Код процесора на AHDL (код для стека не показан).
Код:
TITLE "Stupid Simple CPU";
include "Stack";
-- команды для памяти
CONSTANT READ = 1;
CONSTANT WRITE = 2;
-- команды для стеков
CONSTANT NOP = 0;
CONSTANT PUSH = 1;
CONSTANT POP = 2;
CONSTANT SWAP = 3;
SUBDESIGN ssCPU (
-- входная синхронизация
CLK : input;
-- адресная пина
A[15..0] : output;
-- вход данных
Di[7..0] : input;
-- выход данных
Do[7..0] : output;
-- сигналы управления внешней памятью
MEMcs,MEMrd,MEMwr : output;
-- сброс разумеется!
RESET : input;
)
VARIABLE
-- внутренний сигнал сброса
RST : node;
-- код операции памяти
MEM[1..0] : node;
-- код операции стека возвратов
RSTC[1..0] : node;
-- код операции стека данных
DSTC[1..0] : node;
-- счетчик команд
PC[15..0] : DFF;
-- регистр команды
CMDR[7..0] : DFF;
-- буферный регистр
BUF[15..0] : DFF;
-- стеки
RStack : Stack WITH (WIDTH=16, DEPTH=3);
DStack : Stack WITH (WIDTH=16, DEPTH=3);
BEGIN
-- формирование синхронного внутреннего сброса
RST = DFF(RESET,CLK,,);
-- сброс регистров
PC[].clrn = RST;
CMDR[].clrn = RST;
-- синхронизировать ВСЕ!
CMDR[].clk = CLK;
PC[].clk = CLK;
BUF[].clk = CLK;
RStack.clk = CLK;
DStack.clk = CLK;
-- командировки для стеков
RStack.stc[] = RSTC[];
DStack.stc[] = DSTC[];
CASE CMDR[4..0] IS
-- NOP выборка следующей команды (нет операции)
WHEN 0 => A[] = PC[]; PC[] = PC[] + 1; CMDR[] = Di[]; BUF[] = BUF[]; DStack.d[] = DStack.q[]; RStack.d[] = RStack.q[]; MEM[] = READ; RSTC[] = NOP; DSTC[] = NOP;
-- LIT загрузка непосредственного литерала на стек ( --> data )
WHEN 1 => A[] = PC[]; PC[] = PC[] + 1; CMDR[] = 2; BUF[] = (H"00",Di[]); DStack.d[] = DStack.q[]; RStack.d[] = RStack.q[]; MEM[] = READ; RSTC[] = NOP; DSTC[] = NOP;
WHEN 2 => A[] = PC[]; PC[] = PC[] + 1; CMDR[] = 0; DStack.d[] = (Di[],BUF[7..0]); BUF[] = (Di[],BUF[7..0]); RStack.d[] = RStack.q[]; MEM[] = READ; RSTC[] = NOP; DSTC[] = PUSH;
-- CALL вызов попрограммы ( вызов адресного интерпретатора)
WHEN 3 => A[] = PC[]; PC[] = PC[] + 1; CMDR[] = 4; BUF[] = (H"00",Di[]); DStack.d[] = DStack.q[]; RStack.d[] = RStack.q[]; MEM[] = READ; RSTC[] = NOP; DSTC[] = NOP;
WHEN 4 => A[] = PC[]; CMDR[] = 0; PC[] = (Di[],BUF[7..0]); BUF[] = (Di[],BUF[7..0]); RStack.d[] = PC[] + 1; DStack.d[] = DStack.q[]; MEM[] = READ; RSTC[] = PUSH; DSTC[] = NOP;
-- RET возврат из подпрограммы
WHEN 5 => A[] = PC[]; PC[] = RStack.q[]; CMDR[] = 0; BUF[] = BUF[]; DStack.d[] = DStack.q[]; RStack.d[] = RStack.q[]; MEM[] = NOP; RSTC[] = POP; DSTC[] = NOP;
-- NEXT переход на следующее слово (адресная интерпретация)
WHEN 6 => CMDR[] = 7; A[] = RStack.q[]; RStack.d[] = RStack.q[] + 1; DStack.d[] = DStack.q[]; PC[] = PC[]; BUF[] = (H"00",Di[]); MEM[] = READ; RSTC[] = SWAP; DSTC[] = NOP;
WHEN 7 => CMDR[] = 0; A[] = RStack.q[]; RStack.d[] = RStack.q[] + 1; DStack.d[] = DStack.q[]; PC[] = (Di[],BUF[7..0]); BUF[] = (Di[],BUF[7..0]); MEM[] = READ; RSTC[] = SWAP; DSTC[] = NOP;
-- @ извлечь данное по адресу из стека ( addr --> data=mem[addr])
WHEN 8 => CMDR[] = 9; A[] = DStack.q[]; DStack.d[] = DStack.q[] + 1; PC[] = PC[]; BUF[] = (H"00",Di[]); RStack.d[] = RStack.q[]; MEM[] = READ; RSTC[] = NOP; DSTC[] = SWAP;
WHEN 9 => CMDR[] = 10; A[] = DStack.q[]; PC[] = PC[]; BUF[] = (Di[],BUF[7..0]); RStack.d[] = RStack.q[]; MEM[] = READ; RSTC[] = NOP; DSTC[] = NOP;
WHEN 10 => CMDR[] = 0; A[] = PC[]; PC[] = PC[]; BUF[] = BUF[]; DStack.d[] = BUF[]; RStack.d[] = RStack.q[]; MEM[] = NOP; RSTC[] = NOP; DSTC[] = SWAP;
-- ! сохранить даное в памяти по адресу ( data, addr --> )
WHEN 11 => CMDR[] = 12; A[] = DStack.q[]; DStack.d[] = DStack.q[] + 1; PC[] = PC[]; RStack.d[] = RStack.q[]; MEM[] = WRITE; RSTC[] = NOP; DSTC[] = SWAP; Do[] = DStack.qq[7..0];
WHEN 12 => CMDR[] = 13; A[] = DStack.q[]; DStack.d[] = DStack.q[] + 1; PC[] = PC[]; RStack.d[] = RStack.q[]; MEM[] = WRITE; RSTC[] = NOP; DSTC[] = POP; Do[] = DStack.qq[15..8];
WHEN 13 => CMDR[] = 0; A[] = DStack.q[]; PC[] = PC[]; BUF[] = BUF[]; DStack.d[] = DStack.q[]; RStack.d[] = RStack.q[]; MEM[] = NOP; RSTC[] = NOP; DSTC[] = POP;
-- ?BRANCH (IFNZ) переход, если на стеке не ноль
WHEN 14 => A[] = PC[]; IF DFF(DStack.q[] == 0,CLK,,) THEN CMDR[] = 15; PC[] = PC[]; ELSE CMDR[] = 0; PC[] = PC[] + 2; END IF; BUF[] = BUF[]; DStack.d[] = DStack.q[]; RStack.d[] = RStack.q[]; MEM[] = NOP; RSTC[] = NOP; DSTC[] = NOP;
WHEN 15 => CMDR[] = 16; A[] = PC[]; PC[] = PC[] + 1; BUF[] = (H"00",Di[]); DStack.d[] = DStack.q[]; RStack.d[] = RStack.q[]; MEM[] = READ; RSTC[] = NOP; DSTC[] = NOP;
WHEN 16 => CMDR[] = 0; A[] = PC[]; BUF[] = (Di[],BUF[7..0]); PC[] = (Di[],BUF[7..0]); DStack.d[] = DStack.q[]; RStack.d[] = RStack.q[]; MEM[] = READ; RSTC[] = NOP; DSTC[] = NOP;
-- DUP дублирование данного с вершины стека
WHEN 17 => A[] = PC[]; PC[] = PC[] + 1; CMDR[] = Di[]; BUF[] = BUF[]; DStack.d[] = DStack.q[]; MEM[] = READ; DSTC[] = PUSH; RSTC[] = NOP;
-- DROP удаление данного с вершины стека
WHEN 18 => A[] = PC[]; PC[] = PC[] + 1; CMDR[] = Di[]; BUF[] = BUF[]; DStack.d[] = DStack.q[]; MEM[] = READ; DSTC[] = POP; RSTC[] = NOP;
-- OVER копирование второго элемента стека
WHEN 19 => A[] = PC[]; PC[] = PC[] + 1; CMDR[] = Di[]; BUF[] = BUF[]; DStack.d[] = DStack.qq[]; MEM[] = READ; DSTC[] = PUSH; RSTC[] = NOP;
-- SWAP перестановка двух элементов на вершине стека
WHEN 20 => CMDR[] = 21; A[] = PC[]; PC[] = PC[]; BUF[] = DStack.q[]; DStack.d[] = BUF[]; DSTC[] = POP; RStack.d[] = RStack.q[]; RSTC[] = NOP; MEM[] = NOP;
WHEN 21 => CMDR[] = 22; A[] = PC[]; PC[] = PC[]; BUF[] = DStack.q[]; DStack.d[] = BUF[]; DSTC[] = SWAP; RStack.d[] = RStack.q[]; RSTC[] = NOP; MEM[] = NOP;
WHEN 22 => CMDR[] = 0; A[] = PC[]; PC[] = PC[]; BUF[] = DStack.q[]; DStack.d[] = BUF[]; DSTC[] = PUSH; RStack.d[] = RStack.q[]; RSTC[] = NOP; MEM[] = NOP;
-- >R переместить данное со стека данных на стек возвратов
WHEN 23 => CMDR[] = 0; A[] = PC[]; PC[] = PC[]; DStack.d[] = DStack.q[]; BUF[] = BUF[]; RStack.d[] = DStack.q[]; DSTC[] = POP; RSTC[] = PUSH; MEM[] = NOP;
-- R> переместить данное со стека возвратов на стек данных
WHEN 24 => CMDR[] = 0; A[] = PC[]; PC[] = PC[]; DStack.d[] = RStack.q[]; BUF[] = BUF[]; RStack.d[] = RStack.q[]; DSTC[] = PUSH; RSTC[] = POP; MEM[] = NOP;
-- NAND поразрядное логическое "И-НЕ" двух верхних элементов стека ( d1 d1 --> d1 nand d2)
WHEN 25 => CMDR[] = 10; A[] = PC[]; PC[] = PC[]; DStack.d[] = DStack.q[]; BUF[] = !(DStack.q[] and DStack.qq[]); DSTC[] = POP; MEM[] = NOP;
-- ADD сложить два верхних элемента стека (d1 d2 --> d1+d2)
WHEN 26 => CMDR[] = 10; A[] = PC[]; PC[] = PC[]; DStack.d[] = DStack.q[]; BUF[] = !(DStack.q[] + DStack.qq[]); DSTC[] = POP; MEM[] = NOP;
-- 2/ разделить верхний элемент стека на 2
WHEN 27 => CMDR[] = 0; A[] = PC[]; PC[] = PC[]; DStack.d[] = (DStack.q[15],DStack.q[15..1]); BUF[] = BUF[]; DSTC[] = SWAP; MEM[] = NOP;
-- HALT - All other commands
WHEN others => CMDR[] = CMDR[]; A[] = PC[]; BUF[] = BUF[]; DStack.d[] = DStack.q[]; RStack.d[] = RStack.q[]; PC[] = PC[]; MEM[] = NOP; RSTC[] = NOP; DSTC[] = NOP;
END CASE;
-- формирование сигналов для внешней памяти
CASE MEM[] IS
WHEN READ => MEMcs = CLK; MEMrd = CLK; MEMwr = VCC;
WHEN WRITE => MEMcs = CLK; MEMrd = VCC; MEMwr = CLK;
WHEN others => MEMcs = VCC; MEMrd = VCC; MEMwr = VCC;
END CASE;
END;
Процессор 16-разрядный, но с 8-разрядной шиной данных и 16-разрядным адресом (ну, почти как у Z80).
Каждому 8-разрядному коду соответствует некая операция (забитая в оператор CASE)
В нем же определяется, какой код будет следующим.
Если он равен нулю, то следующий код считывается из внешней памяти.
Таким образом любая команда исполняется за несколько тактов - первый - чтение команды, остальные - исполнение. В конце исполнения каждой команды происходит вызов чтения следующей команды.
Если этого не сделать - новая комнда не загрузится и процессор зациклится на каком-нибудь коде. (ветвь others именно такая).
Компилируется сие "чудо" с рабочей частотой ~100Mhz, но никакая внешняя память такую частоту не потянет, поэтому частоту можно смело занижать или подключать к процессору внутреннюю память ПЛИС-а.
По ячейкам процессор занимает ~600LCELL при глубине стеков в 3 элемента.
Система команд обсуждалась здесь:
viewtopic.php?f=3&t=2564