Новые версии фoрт-процессоров рождаются так быстро, что продолжение старой темы теряет всякий смысл.
А по сему, начинаю новую трему о старом.

Исходные предпосылки: 

1. Язык VHDL для меня до сих пор является темным лесом (с редкими просветами), а по сему, для описания процессора я буду использовать старый добрый AHDL, с которым у меня проблем нету.

2. Разрядность процессора должна быть настраиваемой, поэтому первые строчки исходника:

[code]
TITLE "FORTH-CPU version 7"

PARAMETERS (
        WIDTH = 16,
        DEPTH = 8
);
SUBDESIGN fcpu (
        CLK : input;  -- ясное дело - синхронизация!
        DI[WIDTH-1..0] : input; -- данные, считываемые из ОЗУ
        DO[WIDTH-1..0] : output; -- данные для запппписи в ОЗУ
        ADR[WIDTH-1..0] : output; -- адрес ОЗУ
        RD,WR,CS : output; -- управляющие сигналы ОЗУ
)
VARIABLE
BEGIN
END;
[/code]

Величина WIDTH формально может быть любой, но для отработки схемы выбрано число 16, как достаточно разумное при имеющихся ресурсах ПЛИС.

3. Для проверки работы форт-процессора в железе используется Альтеровский КИТ:  [url=http://www.terasic.com.tw/cgi-bin/page/archive.pl?Language=English&CategoryNo=39&No=83][b]DE1 Syclone-II 2C20[/b][/url]  В Питере эта плата весной стоила $186, и вполне доставабельна, хотя сейчас уже имеет смысл смотреть в сторону КИТ-ов на Cyclone-III.

Схему для тестирования процессора я здесь приводить пока не буду - это отдельная большая тема, которую с мешать с процессором не стоит.

4. Итак [b]ИДЕЯ N1[/b] - схема процессора должна быть очень простой и прозрачной для понимания. Это достигается использованием опыта прежнего "процессоростроения" и использованием наиболее удачных идей. Как показывает практика, наибольшую сложность вызывает построение декодера команд, поэтому начну именно с него, и сделаю его так, чтобы реализация всей логики досталась САПР-у, а не разработчику.
Самый простой декодер для 4-хбитного процессора, это оператор [b]CASE[/b]. Поэтому, записываю:
[code]
CASE CMD[3..0] IS
    WHEN 0 => -- команда 0
    WHEN 1 => -- команда 1
    WHEN 2 => -- команда 2
    WHEN 3 => -- команда 3
    WHEN 4 => -- команда 4
    WHEN 5 => -- команда 5
    WHEN 6 => -- команда 6
    WHEN 7 => -- команда 7
    WHEN 8,9,10,11,12,13,14,15 => остальные команды
END CASE;
[/code]

5. В [b]регистр команд [/b]загружается сразу целое слово, и для того, чтобы разбить его на четверки битов используется самый простой метод - аппаратный сдвиг.

[code]VARIABLE
    RCMD[WIDTH+3..0] : DFF;
BEGIN
    RCMD[].clk = CLK;
    IF CMD_LOAD THEN
        RCMD[] = DI[];
    ELSE
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
    END IF;
END;[/code]

Вместо IF для декодера команды в процессоре используется CASE, поэтому записываем в CASE первые действия.
Здесь же надо сразу отметить появление "спец-команды" по загрузке регистра команд, которая на поверку оказывается той самой пресловутой командой NOP, которую хочется исключить, но в данном случае, смысла этого исключения нет, ибо возникнет другая проблема - а с чем же совмещать загрузку команд?

Расписываю:

[code]CASE CMD[3..0] IS
    WHEN 0 => -- команда 0
        RCMD[] = DI[];
    WHEN 1 => -- команда 1
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
    WHEN 2 => -- команда 2
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
    WHEN 3 => -- команда 3
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
    WHEN 4 => -- команда 4
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
    WHEN 5 => -- команда 5
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
    WHEN 6 => -- команда 6
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
    WHEN 7 => -- команда 7
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
    WHEN 8,9,10,11,12,13,14,15 => остальные команды
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
END CASE;

[/code]

особого смысла сокращать данную запись я не вижум поэтому и не буду этого делать.
Так лучше видно, что делает каждая команда. А прямым кодированием команды занимается САПР при компиляции проекта.
Кроме того, сдесь сразу видно, каким образом в регистр команд попадает следующая группа команд. 
Именно для этого в регистре команд на 4 бита больше чем ширина слова, и при загрузке команды в 
последнюю тетраду записываются нули, которые и обеспечивают загрузку новой команду после того, 
как все предыдущие исполнены.

6. [b]Стеки[/b]. Вписать реализацию стеков прямо в файл процессора - можно, но не нужно. Для этого в AHDL существует механизм инклюдов, поэтому просто добавляю в  после строчки с TITLE:

[code]include "stack.inc";
[/code]

а в секции VARIABLE вставляю два стека:
[code]
    RStack : stack with(WIDTH=WIDTH,DEPTH=DEPTH);
    DStack : stack with(WIDTH=WIDTH,DEPTH=DEPTH);
    PC[WIDTH-1..0] : DFF;
    TR[WIDTH-1..0] : DFF;
[/code]

как видно, вместе со стеками я добавил и два регистра, которые формально являются вершинами соответствующих стеков.
TR - вершина для стека данных, PC - вершина стека возвратов. Заклинания в скобках при описании стеков - являются передачей параметров - WIDTH - ширина стека в битах, DEPTH - глубина в словах.
Синхронизация для всех элементов общая. 

Итак свожу все воедино и записываю нулевой вариант  TDF-файла:

[code]
TITLE "FORTH-CPU version 7"

include "stack.inc";

PARAMETERS (
        WIDTH = 16,
        DEPTH = 8
);
SUBDESIGN fcpu (
        CLK : input;  -- ясное дело - синхронизация!
        DI[WIDTH-1..0] : input; -- данные, считываемые из ОЗУ
        DO[WIDTH-1..0] : output; -- данные для запппписи в ОЗУ
        ADR[WIDTH-1..0] : output; -- адрес ОЗУ
        RD,WR,CS : output; -- управляющие сигналы ОЗУ
)


VARIABLE
    RCMD[WIDTH-1..0] : DFF;
    RStack : stack with(WIDTH=WIDTH,DEPTH=DEPTH);
    DStack : stack with(WIDTH=WIDTH,DEPTH=DEPTH);
    PC[WIDTH-1..0] : DFF;
    TR[WIDTH-1..0] : DFF;
    CMD[3..0] : NODE;
BEGIN
    RCMD[].clk = clk;
    RStack.clk = clk;
    DStack.clk = clk;
    PC[].clk = clk;
    TR[].clk = clk;

CASE CMD[3..0] IS
    WHEN 0 => -- команда 0
        RCMD[] = DI[];
    WHEN 1 => -- команда 1
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
    WHEN 2 => -- команда 2
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
    WHEN 3 => -- команда 3
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
    WHEN 4 => -- команда 4
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
    WHEN 5 => -- команда 5
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
    WHEN 6 => -- команда 6
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
    WHEN 7 => -- команда 7
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
    WHEN 8,9,10,11,12,13,14,15 => остальные команды
        RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
END CASE;

CMD[] = RCMD[3..0];

END;[/code]

Скелет процессора готов. Осталось только наполнить оператор CASE действиями, и все будет как надо!

На этом я сейчас закончу описание построения процессора. Продолжение будет в следующих постах. 
А пока мне пора спать ложиться...