Forth http://fforum.winglion.ru/ |
|
И еще один 4-хбитный ФОРТ-процессор... http://fforum.winglion.ru/viewtopic.php?f=3&t=1517 |
Страница 1 из 3 |
Автор: | WingLion [ Пн сен 22, 2008 21:42 ] |
Заголовок сообщения: | И еще один 4-хбитный ФОРТ-процессор... |
Новые версии фoрт-процессоров рождаются так быстро, что продолжение старой темы теряет всякий смысл. А по сему, начинаю новую тему о старом. Исходные предпосылки: 1. Язык VHDL для меня до сих пор является темным лесом (с редкими просветами), а по сему, для описания процессора я буду использовать старый добрый AHDL, с которым у меня проблем нету. 2. Разрядность процессора должна быть настраиваемой, поэтому первые строчки исходника: Код: 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; Величина WIDTH формально может быть любой, но для отработки схемы выбрано число 16, как достаточно разумное при имеющихся ресурсах ПЛИС. 3. Для проверки работы форт-процессора в железе используется Альтеровский КИТ: DE1 Syclone-II 2C20 В Питере эта плата весной стоила $186, и вполне доставабельна, хотя сейчас уже имеет смысл смотреть в сторону КИТ-ов на Cyclone-III. Схему для тестирования процессора я здесь приводить пока не буду - это отдельная большая тема, которую с мешать с процессором не стоит. 4. Итак ИДЕЯ N1 - схема процессора должна быть очень простой и прозрачной для понимания. Это достигается использованием опыта прежнего "процессоростроения" и использованием наиболее удачных идей. Как показывает практика, наибольшую сложность вызывает построение декодера команд, поэтому начну именно с него, и сделаю его так, чтобы реализация всей логики досталась САПР-у, а не разработчику. Самый простой декодер для 4-хбитного процессора, это оператор CASE. Поэтому, записываю: Код: 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; 5. В регистр команд загружается сразу целое слово, и для того, чтобы разбить его на четверки битов используется самый простой метод - аппаратный сдвиг. Код: 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; Вместо IF для декодера команды в процессоре используется CASE, поэтому записываем в CASE первые действия. Здесь же надо сразу отметить появление "спец-команды" по загрузке регистра команд, которая на поверку оказывается той самой пресловутой командой NOP, которую хочется исключить, но в данном случае, смысла этого исключения нет, ибо возникнет другая проблема - а с чем же совмещать загрузку команд? Расписываю: Код: 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; особого смысла сокращать данную запись я не вижум поэтому и не буду этого делать. Так лучше видно, что делает каждая команда. А прямым кодированием команды занимается САПР при компиляции проекта. Кроме того, сдесь сразу видно, каким образом в регистр команд попадает следующая группа команд. Именно для этого в регистре команд на 4 бита больше чем ширина слова, и при загрузке команды в последнюю тетраду записываются нули, которые и обеспечивают загрузку новой команду после того, как все предыдущие исполнены. 6. Стеки. Вписать реализацию стеков прямо в файл процессора - можно, но не нужно. Для этого в AHDL существует механизм инклюдов, поэтому просто добавляю в после строчки с TITLE: Код: include "stack.inc"; а в секции VARIABLE вставляю два стека: Код: RStack : stack with(WIDTH=WIDTH,DEPTH=DEPTH); DStack : stack with(WIDTH=WIDTH,DEPTH=DEPTH); PC[WIDTH-1..0] : DFF; TR[WIDTH-1..0] : DFF; как видно, вместе со стеками я добавил и два регистра, которые формально являются вершинами соответствующих стеков. TR - вершина для стека данных, PC - вершина стека возвратов. Заклинания в скобках при описании стеков - являются передачей параметров - WIDTH - ширина стека в битах, DEPTH - глубина в словах. Синхронизация для всех элементов общая. Итак свожу все воедино и записываю нулевой вариант TDF-файла: Код: 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; Скелет процессора готов. Осталось только наполнить оператор CASE действиями, и все будет как надо! На этом я сейчас закончу описание построения процессора. Продолжение будет в следующих постах. А пока мне пора спать ложиться... |
Автор: | WingLion [ Пн сен 22, 2008 22:21 ] |
Заголовок сообщения: | |
Хм... а время еще детское, можно и еще пописать... С учетом того, что начал в 20.00, можно примерно проследить, сколько надо времени на написание подобного процессора. Итак, продолжаю. Очевидно (не для всех, но для меня), что 16 команд для процессора мало, поэтому в декодер команд надо сразу же заложить возможность префиксов, и это в данной схеме делается очень просто: Код: 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"00000000",RCMD[WIDTH-5..4]); WHEN 6 => -- команда 6 RCMD[] = (B"00000000",RCMD[WIDTH-5..4]); WHEN 7 => -- команда 7 RCMD[] = (B"00000000",RCMD[WIDTH-5..4]); WHEN 8,9,10,11,12,13,14,15 => остальные команды RCMD[] = (B"0000",RCMD[WIDTH-1..4]); END CASE; PREF[] = RCMD[7..4]; для команд с префиксами я выбрал три кода - 5,6 и 7. Как видно, регистр команд в момент исполнения этих команд сдвигается не на 4, а на 8 бит и, таким образом из очереди исключаются четыре бита, используемых префиксами, как код префиксной команды. Время исполнения команды с префиксом - ровно один такт, и единственное неудобство в том, что такая команда не должна быть последней в слове, иначе, префикс будет исполняться с командой 0. Впрочем это можно и использовать (иногда, если повезет). Увеличение же длины команды - ничто против увеличения длины программы, из-за извращений которые придется делать при сокращенном наборе команд для получения нужных действий. Для того, чтобы определиться с типами префиксов я использую предыдущую наработку (версию 6, где все было так же, но команды с префиксами не структурированы). Итак префиксы имеют следующие типы: PREFIX типа 5 - DROP - команды с уменьшением использования стека данных в этот тип попадают все двухоперандные арифметико-логические команды, а так же, с этим префиксом могут быть закодированы специальные команды загрузки спец-регистров или вывода в порты. PREFIX типа 6 - SWAP - команды с неизменением использования стека данных этот префикс для команд с одним опрандом PREFIX типа 7 - DUP - команды с увеличением использования стека данных префикс команд загрузки специальных операндов, а так же может быть использован для ввода данных из спец-портов DROP 0, SWAP 0, DUP 0 исполняют соответствующие команды Форта, и исключительный случай с попаданием префикса на конец командного слова получает дополнительный шанс на использование. Для команд с префиксами используются дополнительные схемы ALU5, ALU6, ALU7, которые вводятся в исходный код как инклюды: Код: include "alu_5.inc": include "alu_6.inc": include "alu_7.inc": Если не хочется использовать префиксы, их можно легко исключить из исходника процессора, но это уже будет другой процессор. Вписываю сделанные дополнения в общий файл: Код: TITLE "FORTH-CPU version 7";
include "stack.inc"; include "alu_5.inc": include "alu_6.inc": include "alu_7.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; PREF[3..0]: NODE; ALU5 : alu_5; ALU6 : alu_6; ALU7 : alu_7; 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; |
Автор: | WingLion [ Пн сен 22, 2008 23:20 ] |
Заголовок сообщения: | |
начинаю наполнение оператора CASE действиями. Пока без оглядки на необходимость реализации команд перехода, вызовов, возвратов, условных переходов... 1. PC и TR переопределяются как регистры с DFFE. 2. определяются действия по умолчанию: Код: DEFAULTS
RStack.cmd[] = GND; -- всегда nop DStack.cmd[] = GND; -- всегда nop PC[].ena = GND; -- PC чаще не меняется TR[].ena = GND; -- TR чаще не меняется END DEFAULTS; ALU5.cmd[] = PREF[]; ALU5.op1[] = TR[]; ALU5.op2[] = DStack.do[]; ALU6.cmd[] = PREF[]; ALU6.op1[] = TR[]; ALU6.op2[] = DStack.do[]; ALU7.cmd[] = PREF[]; ALU7.op1[] = TR[]; ALU7.op2[] = DStack.do[]; CASE CMD[3..0] IS WHEN 0 => -- команда 0 RCMD[] = DI[]; PC[] = PC[] + 1; PC[].ena = VCC; WHEN 1 => -- команда 1 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = PC[]; WHEN 2 => -- команда 2 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = PC[]; WHEN 3 => -- команда 3 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = PC[]; WHEN 4 => -- команда 4 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = PC[]; WHEN 5 => -- команда 5 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = PC[]; WHEN 6 => -- команда 6 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = PC[]; WHEN 7 => -- команда 7 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = PC[]; WHEN 8,9,10,11,12,13,14,15 => остальные команды RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = PC[]; END CASE; А вот теперь - точно спать! |
Автор: | WingLion [ Пн сен 22, 2008 23:23 ] |
Заголовок сообщения: | |
И вот, результат на сегодня: Код: TITLE "FORTH-CPU version 7";
include "stack.inc"; include "alu_5.inc": include "alu_6.inc": include "alu_7.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] : DFFE; TR[WIDTH-1..0] : DFFE; CMD[3..0] : NODE; PREF[3..0]: NODE; ALU5 : alu_5; ALU6 : alu_6; ALU7 : alu_7; BEGIN DEFAULTS RStack.cmd[] = GND; -- всегда nop DStack.cmd[] = GND; -- всегда nop PC[].ena = GND; -- PC не меняется TR[].ena = GND; -- TR не меняется END DEFAULTS; RCMD[].clk = clk; RStack.clk = clk; DStack.clk = clk; PC[].clk = clk; TR[].clk = clk; ALU5.cmd[] = PREF[]; ALU5.op1[] = TR[]; ALU5.op2[] = DStack.do[]; ALU6.cmd[] = PREF[]; ALU6.op1[] = TR[]; ALU6.op2[] = DStack.do[]; ALU7.cmd[] = PREF[]; ALU7.op1[] = TR[]; ALU7.op2[] = DStack.do[]; CASE CMD[3..0] IS WHEN 0 => -- команда 0 RCMD[] = DI[]; PC[] = PC[] + 1; PC[].ena = VCC 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]); TR[] = ALU5.rez[]; TR[].ena = VCC; WHEN 6 => -- команда 6 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); TR[] = ALU6.rez[]; TR[].ena = VCC; WHEN 7 => -- команда 7 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); TR[] = ALU7.rez[]; TR[].ena = VCC; WHEN 8,9,10,11,12,13,14,15 => остальные команды RCMD[] = (B"0000",RCMD[WIDTH-1..4]); END CASE; CMD[] = RCMD[3..0]; END; |
Автор: | WingLion [ Вт сен 23, 2008 07:51 ] |
Заголовок сообщения: | |
Для того, чтобы данный файл компилился, надо сделать к нему простейшие варианты стека и трех ALU: Код: TITLE "Stack on LCELL"; PARAMETERS ( WIDTH = 16, DEPTH = 8 ); SUBDESIGN Stack ( CLK : input; DI[WIDTH-1..0] : input; DO[WIDTH-1..0] : output; CMD[1..0] : input; ) VARIABLE REGS[DEPTH-1..0][WIDTH-1..0] : DFFE; CMDR[5..0] : NODE; BEGIN CASE CMD[] IS WHEN 0 => CMDR[] = B"000000"; -- NOP WHEN 1 => CMDR[] = B"111111"; -- PUSH WHEN 2 => CMDR[] = B"000111"; -- POP WHEN 3 => CMDR[] = B"000001"; -- SWAP_TOP END CASE; REGS[][].clk = CLK; REGS[0][].ena = CMDR0; REGS[1][].ena = CMDR1; REGS[DEPTH-1..2][].ena = CMDR2; DO[] = REGS[0][]; IF CMDR3 THEN REGS[0][] = DI[]; ELSE REGS[0][] = REGS[1][]; END IF; IF CMDR4 THEN REGS[1][] = REGS[0][]; ELSE REGS[1][] = REGS[2][]; END IF; FOR i IN 2 TO DEPTH-2 GENERATE IF CMDR5 THEN REGS[i][] = REGS[i-1][]; ELSE REGS[i][] = REGS[i+1][]; END IF; END GENERATE; IF CMDR5 THEN REGS[DEPTH-1][] = REGS[DEPTH-2][]; ELSE REGS[DEPTH-1][] = 0; END IF; END; Код: TITLE "ALU DROP"; PARAMETERS ( WIDTH=16 ); SUBDESIGN alu_5 ( op1[WIDTH-1..0] : input; op2[WIDTH-1..0] : input; cmd[3..0] : input; res[WIDTH-1..0] : output; ) VARIABLE logic[WIDTH-1..0] : LCELL; BEGIN logic[] = op2[]; res[] = logic[]; END; Код: TITLE "ALU SWAP"; PARAMETERS ( WIDTH=16 ); SUBDESIGN alu_6 ( op1[WIDTH-1..0] : input; op2[WIDTH-1..0] : input; cmd[3..0] : input; res[WIDTH-1..0] : output; ) VARIABLE logic[WIDTH-1..0] : LCELL; BEGIN logic[] = op2[]; res[] = logic[]; END; Код: TITLE "ALU DUP"; PARAMETERS ( WIDTH=16 ); SUBDESIGN alu_7 ( op1[WIDTH-1..0] : input; op2[WIDTH-1..0] : input; cmd[3..0] : input; res[WIDTH-1..0] : output; ) VARIABLE logic[WIDTH-1..0] : LCELL; BEGIN logic[] = op2[]; res[] = logic[]; END; И главный файл (с последними исправлениями): Код: TITLE "FORTH-CPU version 7";
include "stack.inc"; include "alu_5.inc"; include "alu_6.inc"; include "alu_7.inc"; constant nop = 0; constant drop = 2; constant dup = 1; constant swap = 3; 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] : DFFE; TR[WIDTH-1..0] : DFFE; CMD[3..0] : NODE; PREF[3..0]: NODE; ALU5 : alu_5 with(WIDTH=WIDTH); ALU6 : alu_6 with(WIDTH=WIDTH); ALU7 : alu_7 with(WIDTH=WIDTH); BEGIN DEFAULTS RStack.cmd[] = GND; -- по умолчанию nop DStack.cmd[] = GND; -- по умолчанию nop PC[].ena = GND; -- PC не меняется TR[].ena = GND; -- TR не меняется RD = VCC; WR = VCC; CS = VCC; END DEFAULTS; RCMD[].clk = clk; RStack.clk = clk; DStack.clk = clk; PC[].clk = clk; TR[].clk = clk; ALU5.cmd[] = PREF[]; ALU5.op1[] = TR[]; ALU5.op2[] = DStack.do[]; ALU6.cmd[] = PREF[]; ALU6.op1[] = TR[]; ALU6.op2[] = DStack.do[]; ALU7.cmd[] = PREF[]; ALU7.op1[] = TR[]; ALU7.op2[] = DStack.do[]; CASE CMD[3..0] IS WHEN 0 => -- команда 0 NOP RCMD[] = DI[]; PC[] = PC[] + 1; PC[].ena = VCC; 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]); TR[] = ALU5.res[]; TR[].ena = VCC; DStack.di[] = TR[]; DStack.cmd[] = drop; WHEN 6 => -- команда 6 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); TR[] = ALU6.res[]; TR[].ena = VCC; DStack.di[] = TR[]; DStack.cmd[] = dup; WHEN 7 => -- команда 7 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); TR[] = ALU7.res[]; TR[].ena = VCC; DStack.di[] = TR[]; DStack.cmd[] = swap; WHEN 8,9,10,11,12,13,14,15 => -- остальные команды RCMD[] = (B"0000",RCMD[WIDTH-1..4]); END CASE; CMD[] = RCMD[3..0]; ADR[] = PC[]; DO[] = TR[]; END; Вставляется все эти коды в соответствующие файлы, в Quartus-II для стека и трех ALU сначала генерятся инклюд-файлы для AHDL (меню File -> Create/Update -> Create AHDL Include фор current file) p.s. ночью я спал, а эти файлы добавил как проснулся (стек не писал заново, а просто скопировал из предыдущей версии, т.к. там он давно отлажен и работает, и на повтор уже сделанного время терять не хочется). Сейчас ухожу на работу... вернусь вечерком и продолжу... |
Автор: | WingLion [ Вт сен 23, 2008 22:40 ] |
Заголовок сообщения: | |
Итак, я продолжаю начатую тему. Во первых, оставлю заглушки для ALU так как есть. Их можно достаточно легко наполнять операциями по мере необходимости. Во вторых, определимся, какие операции нужны в первую очередь и какие будут появляться в программах чаще всего. Первая необходимая команда - загрузка литерала - LIT - для ее выполнения нужно сделать следующее: Код: PC[] = PC[] + 1; PC[].ena = VCC; TR[] = DI[]; TR[].ena = VCC; DStack.di[] = TR[]; DStack.cmd[] = push; Замечу, что адрес загружаемых данных подготовлен предыдущей операцией NOP, а здесь вычисляется адрес сладующего за литералом слова. Если в слове окажется несколько литералов подряд, то они и загрузятся подряд за несколько тактов. Следующая необходимая команда - CALL: Код: PC[] = DI[]; PC[].ena = VCC; RStack.cmd[] = push; RStack.di[] = PC[] + 1; И, конечно же, команда RET: Код: PC[] = RStack.do[]; PC[].ena = VCC; RStack.cmd[] = pop; помимо этих действий в командах CALL и RET необходимо сбрасывать очередь команд. Это можно сделать как программно (занулив все команды после RET и CALL) или же аппаратно, записав: Код: RCMD[] = 0; с точки зрения схемотехники этот вариант приведет к некоторому усложнению схемы, с другой стороны, программа может выполнить еще несколько действий после того, как изменится счетчик команд, что может оказаться довольно полезно. Например, команда CALL-LIT-RET загрузит непосредственное данное из адреса, который попадет в PC по CALL и управление тут же вернется на следующий адрес после точки вызова. Сие полезное свойство стоит оставить, поэтому принудительное зануление команд после CALL и RET не делаю. В случае RET сие состояние дает возможность получить непосредственную команду NEXT для адресного интерпретатора: команду RET-CALL, которая загружает в PC адрес из стека возвратов, тут же берет из него следующий адрес для загрузки в PC, a увеличеный на единицу адрес интерпретаяции возвращает в стек возвратов. Итак, команды: Код: 0 - NOP 1 - LIT 2 - CALL 3 - RET Следующая необходимая команда - условный переход - IF Код: CASE 4=>
IF (TR[] == 0) THEN PC[] = PC[] + 1; ELSE PC[] = DI[]; END IF; PC[].ena = VCC; Здесь замечу, что PC[] изменяется сразу же при любом значении в TR[], поэтому команды, непосредственно следующие за IF, в том же слове, будут исполнены независимо от исхода сравнения. Это может быть полезно для реализации, например "правильного IF", который при исполнении удаляет верхний элемент стека, т.е. это команда IF-DROP, a команда IF-NOP - оставит верхний элемент стека без изменения. |
Автор: | WingLion [ Вт сен 23, 2008 23:39 ] |
Заголовок сообщения: | |
Теперь о командах @ и ! для загрузки адреса используется действия, подобные команде call, a для следующее действие по разименованию или присвоению производится дополнительными командами, которые формально из набора выпадают, так как "теряют смысл" без предварительной подготовки. Возможно, я этого смысла просто не нашел, но для упрощения схемы я эти команды ввожу и никак не маскирую. Код: CASE 8 => -- @ -- call* PC[] = TR[]; PC[].ena = VCC; RStack.di[] = PC[]; RStack.cmd[] = push; -- сохраняется неизменное(!) значение PC CASE 9 => -- продолжение @ - ret-@ PC[] = RStack.do[]; PC[].ena = VCC; RStack.cmd[] = pop; TR[] = DI[]; TR[].ena = VCC; CASE 10 => -- ! -- call* PC[] = TR[]; PC[].ena = VCC; RStack.di[] = PC[]; RStack.cmd[] = push; -- сохраняется неизменное(!) значение PC TR[] = DStack.do[]; TR[].ena = VCC; DRStack.cmd[] = pop; CASE 11 => -- продолжение ! ret-! PC[] = RStack.do[]; PC[].ena = VCC; RStack.cmd[] = pop; TR[] = DStack.do[]; TR[].ena = VCC; DRStack.cmd[] = pop; Лишние занятые коды, конечно не особенно приятны, но пока я их оставляю. Обойти их можно некоторым усложнением схемы и введением 5-битной внутренней команды, которая бы прямо дешифровалась из 4-хбитной команды, но я этого здесь делать не буду, так как формально две лишние команды мало что меняют, ибо есть префиксы, а с ними достаточное количество дополнительных команд. Еще пара нужных команд - >R и R>: Код: CASE 12 => -- >R
RStack.di[] = TR[]; RStack.cmd[] = push; TR[] = DStack.do[]; TR[].ena = VCC; DStack.cmd[] = pop; CASE 13 => -- R> DStack.di[] = TR[]; DStack.cmd[] = push; TR[] = RStack.do[]; TR[].ena = VCC; RStack.cmd[] = pop; |
Автор: | WingLion [ Ср сен 24, 2008 00:16 ] |
Заголовок сообщения: | |
Теперь свожу все дополнения в общий файл, заодно добавляю пропущенные операции по установке сигналов WE и RD и CS для памяти: Код: TITLE "FORTH-CPU version 7";
include "stack.inc"; include "alu_5.inc"; include "alu_6.inc"; include "alu_7.inc"; constant nop = 0; constant drop = 2; constant dup = 1; constant swap = 3; 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] : DFFE; TR[WIDTH-1..0] : DFFE; CMD[3..0] : NODE; PREF[3..0]: NODE; ALU5 : alu_5 with(WIDTH=WIDTH); ALU6 : alu_6 with(WIDTH=WIDTH); ALU7 : alu_7 with(WIDTH=WIDTH); BEGIN DEFAULTS RStack.cmd[] = GND; -- по умолчанию nop DStack.cmd[] = GND; -- по умолчанию nop PC[].ena = GND; -- PC не меняется TR[].ena = GND; -- TR не меняется RD = VCC; WR = VCC; CS = VCC; END DEFAULTS; RCMD[].clk = clk; RStack.clk = clk; DStack.clk = clk; PC[].clk = clk; TR[].clk = clk; ALU5.cmd[] = PREF[]; ALU5.op1[] = TR[]; ALU5.op2[] = DStack.do[]; ALU6.cmd[] = PREF[]; ALU6.op1[] = TR[]; ALU6.op2[] = DStack.do[]; ALU7.cmd[] = PREF[]; ALU7.op1[] = TR[]; ALU7.op2[] = DStack.do[]; CASE CMD[3..0] IS WHEN 0 => -- команда 0 NOP RCMD[] = DI[]; RD = GND; PC[] = PC[] + 1; PC[].ena = VCC; WHEN 1 => -- команда 1 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = PC[] + 1; PC[].ena = VCC; TR[] = DI[]; TR[].ena = VCC; RD = GND; DStack.di[] = TR[]; DStack.cmd[] = push; WHEN 2 => -- команда 2 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = DI[]; PC[].ena = VCC; RD = GND; RStack.cmd[] = push; RStack.di[] = PC[] + 1; WHEN 3 => -- команда 3 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = RStack.do[]; PC[].ena = VCC; RStack.cmd[] = pop; WHEN 4 => -- команда 4 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); IF (TR[] == 0) THEN PC[] = PC[] + 1; ELSE PC[] = DI[]; RD = GND; END IF; PC[].ena = VCC; WHEN 5 => -- команда 5 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); TR[] = ALU5.res[]; TR[].ena = VCC; DStack.di[] = TR[]; DStack.cmd[] = drop; WHEN 6 => -- команда 6 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); TR[] = ALU6.res[]; TR[].ena = VCC; DStack.di[] = TR[]; DStack.cmd[] = dup; WHEN 7 => -- команда 7 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); TR[] = ALU7.res[]; TR[].ena = VCC; DStack.di[] = TR[]; DStack.cmd[] = swap; CASE 8 => -- @ -- call* RCMD[] = (RCMD[WIDTH-1..4],B"1001"); PC[] = TR[]; PC[].ena = VCC; RStack.di[] = PC[]; RStack.cmd[] = push; -- сохраняется неизменное(!) значение PC CASE 9 => -- продолжение @ - ret-@ RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = RStack.do[]; PC[].ena = VCC; RStack.cmd[] = pop; TR[] = DI[]; TR[].ena = VCC; RD = GND; CASE 10 => -- ! -- call* RCMD[] = (RCMD[WIDTH-1..4],B"1011"); PC[] = TR[]; PC[].ena = VCC; RStack.di[] = PC[]; RStack.cmd[] = push; -- сохраняется неизменное(!) значение PC TR[] = DStack.do[]; TR[].ena = VCC; DRStack.cmd[] = pop; CASE 11 => -- продолжение ! ret-! WR = GND; RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = RStack.do[]; PC[].ena = VCC; RStack.cmd[] = pop; TR[] = DStack.do[]; TR[].ena = VCC; DRStack.cmd[] = pop; CASE 12 => -- >R RCMD[] = (B"0000",RCMD[WIDTH-1..4]); RStack.di[] = TR[]; RStack.cmd[] = push; TR[] = DStack.do[]; TR[].ena = VCC; DStack.cmd[] = pop; CASE 13 => -- R> RCMD[] = (B"0000",RCMD[WIDTH-1..4]); DStack.di[] = TR[]; DStack.cmd[] = push; TR[] = RStack.do[]; TR[].ena = VCC; RStack.cmd[] = pop; WHEN 14,15 => -- остальные команды RCMD[] = (B"0000",RCMD[WIDTH-1..4]); END CASE; CMD[] = RCMD[3..0]; ADR[] = PC[]; DO[] = TR[]; END; Что интересно, даже при занятых двух лишних кодах остались две резервные команды 14 и 15, которые вообще ничего не делают, кроме сдвига регистра команд на следующую четверку бит. |
Автор: | WingLion [ Ср сен 24, 2008 00:58 ] |
Заголовок сообщения: | |
ИТАК, получившаяся система команд следующая: 0 - NOP: нет операции - "ничего не делает" загружает в регистр команд слово по адресу из PC, PC увеличивается на единицу 1 - LIT: загрузка литерала - ( --> dd) загружает в регистр TR слово по адресу из PC, PC увеличивается на единицу 2 - CALL: вызов подпрограммы по непосредственному адресу - PC+1 заталкивается в стек возвратов, в PC загружается слово по адресу из PC 3 - RET: возврат из подпрограммы - Выталкивается из стека возвратов последнее значение и вписывается в PC 4 - IF: условный переход ( --> ) ЕСЛИ регистр TR содержит нуль PC увеличивается на единичку (переходит на адрес следующий после адреса перехода) ИНАЧЕ в PC загружается слово по адресу из PC 5 - DROP* префикс команды типа drop - (dd2 dd1 --> dd2 ) - пока исполняет чистый DROP независимо следующего кода 6 - DUP* префикс команды типа dup - ( dd1 --> dd1 dd2 ) - пока исполняет чистый DUP независимо следующего кода 7 - SWAP* префикс команды типа swap - ( dd1 dd2 --> dd1 dd2 ) - пока исполняет чистый SWAP независимо следующего кода 8 - @ разыменование (dd --> mem) - (слово из памяти по адресу из TR записывается в TR) сначала PC запоминается в стеке возвртов, а в PC записывается адрес из TR, принудительно устанавливается следующий исполняемый код команды - 9 для продолжения исполнения команды @ 9 - ret-@ продолжение рзыменования - происходит считывание слова из адреса в PC и запись его в TR, a в PC возвращается значение из стека возвратов чистое использование этого кода как команды - сомнительно, поэтому в будущем код будет маскирован и, возможно в чистом виде будет исполнять что-то полезное 10 - ! присводение (dd aa --> ) - (слово dd записывается в память по адресу аа) сначала сначала PC запоминается в стеке возвртов, а в PC записывается адрес из TR, принудительно устанавливается следующий исполняемый код команды - 11 для продолжения исполнения команды ! 11 - ret-! продолжение присвоения - происходит запись слова из TR в память по адресу из PC, a в PC возвращается значение из стека возвратов чистое использование этого кода как команды - сомнительно, поэтому в будущем код будет маскирован и, возможно в чистом виде будет исполнять что-то полезное 12 - >R на-Эр - (dd -->) слово из TR заталкивается на стек возвратов, в TR записывается слово вытолкнутое из стека данных 13 - R> с-Эр - (--> dd) слово из TR заталкивается на стек данных, в TR записывается слово вытолкнутое из стека возвратов 15 - NOP* резервный код 16 - NOP* резервный код |
Автор: | WingLion [ Ср сен 24, 2008 07:47 ] |
Заголовок сообщения: | |
И еще одна ссылочка: http://winglion.ru/Forth-CPU/fcpu7l.zip (22kb) - все файлы форт-процессора, в том числе файлы проекта для Quartus-а. |
Автор: | WingLion с работы [ Ср сен 24, 2008 13:46 ] |
Заголовок сообщения: | |
работаю над отладкой проца |
Автор: | WingLion [ Ср сен 24, 2008 19:02 ] |
Заголовок сообщения: | |
продолжаю... 1. Ввожу в в схему сигнал сброса, который устанавливает PC и регистр команд в ноль. 2. Делаю исправления ошибок, обнаруженных при попытке компиляции проекта. Проверяю работу команд процессора в симуляторе. Команды подаю вручную на входы 1. команда 0 - NOP 5 РАЗ ПОДРЯД - ЗАГРУЗКА КОМАНДЫ - СНОВА NOP - счетчик команд увеличивается каждый такт, пока не встречается первая не-NOP команда. 2. команды 000F и FEFE - проверяется сдвиг в регистре команд и загрузка следующей команды после исполнение последовaтельности нe-NOP 3. команда 0001 - LIT - загрузка операнда - операнд AA55 агружается в TR на следующем такте после загрузки в регистр команд кода 0001 исполняется код 1, затем 0 - загрузка следующей команды без лишних ожиданий 4. команда 0002 - CALL - переход по адресу 0222 происходит на следующем такте в момент загрузки адреса перехода. 5. команда 3А11 - однословная подпрограмма с загрузкой двух слов - данных и адреса - данные записывются по адресу, после чего происходит возврат из подпрогряммы. проверяется ход изполнения, пока адрес в PC не возврщается к точке вызова подпрограммы - на адрес 000Б 6. проверка префиксных команд - код 4657 - обнаруживается неправильная работа команды 7 - код после префикса в регистре команд надо пропустить (он используется как команда для ALU одновременно с моментом исполнения кода 7). Исправляю: Код: RCMD[] = (B"0000",RCMD[WIDTH-1..4]);
в командах 5,6,7 заменяется на: RCMD[] = (B"0000000",RCMD[WIDTH-1..8]); |
Автор: | WingLion [ Ср сен 24, 2008 19:09 ] |
Заголовок сообщения: | |
исходник с последними исправлениями: Код: TITLE "FORTH-CPU version 7";
include "stack.inc"; include "alu_5.inc"; include "alu_6.inc"; include "alu_7.inc"; constant nop = 0; constant pop = 2; constant push = 1; constant swap = 3; 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; -- управляющие сигналы ОЗУ RESET : input; ) VARIABLE RCMD[WIDTH-1..0] : DFF; RStack : stack with(WIDTH=WIDTH,DEPTH=DEPTH); DStack : stack with(WIDTH=WIDTH,DEPTH=DEPTH); PC[WIDTH-1..0] : DFFE; TR[WIDTH-1..0] : DFFE; CMD[3..0] : NODE; PREF[3..0]: NODE; ALU5 : alu_5 with(WIDTH=WIDTH); ALU6 : alu_6 with(WIDTH=WIDTH); ALU7 : alu_7 with(WIDTH=WIDTH); BEGIN DEFAULTS RStack.cmd[] = GND; -- по умолчанию nop DStack.cmd[] = GND; -- по умолчанию nop PC[].ena = GND; -- PC не меняется TR[].ena = GND; -- TR не меняется RD = VCC; WR = VCC; CS = VCC; END DEFAULTS; (RCMD[].clrn,PC[].clrn) = DFF(RESET,CLK,,); RCMD[].clk = clk; RStack.clk = clk; DStack.clk = clk; PC[].clk = clk; TR[].clk = clk; ALU5.cmd[] = PREF[]; ALU5.op1[] = TR[]; ALU5.op2[] = DStack.do[]; ALU6.cmd[] = PREF[]; ALU6.op1[] = TR[]; ALU6.op2[] = DStack.do[]; ALU7.cmd[] = PREF[]; ALU7.op1[] = TR[]; ALU7.op2[] = DStack.do[]; PREF[] = RCMD[7..4]; CASE CMD[3..0] IS WHEN 0 => -- команда 0 NOP RCMD[] = DI[]; RD = GND; PC[] = PC[] + 1; PC[].ena = VCC; WHEN 1 => -- команда 1 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = PC[] + 1; PC[].ena = VCC; TR[] = DI[]; TR[].ena = VCC; RD = GND; DStack.di[] = TR[]; DStack.cmd[] = push; WHEN 2 => -- команда 2 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = DI[]; PC[].ena = VCC; RD = GND; RStack.cmd[] = push; RStack.di[] = PC[] + 1; WHEN 3 => -- команда 3 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = RStack.do[]; PC[].ena = VCC; RStack.cmd[] = pop; WHEN 4 => -- команда 4 RCMD[] = (B"0000",RCMD[WIDTH-1..4]); IF (TR[] == 0) THEN PC[] = PC[] + 1; ELSE PC[] = DI[]; RD = GND; END IF; PC[].ena = VCC; WHEN 5 => -- команда 5 DUP* RCMD[] = (B"00000000",RCMD[WIDTH-1..8]); TR[] = ALU5.res[]; TR[].ena = VCC; DStack.di[] = TR[]; DStack.cmd[] = push; WHEN 6 => -- команда 6 DROP* RCMD[] = (B"00000000",RCMD[WIDTH-1..8]); TR[] = ALU6.res[]; TR[].ena = VCC; DStack.di[] = TR[]; DStack.cmd[] = pop; WHEN 7 => -- команда 7 SWAP* RCMD[] = (B"00000000",RCMD[WIDTH-1..8]); TR[] = ALU7.res[]; TR[].ena = VCC; DStack.di[] = TR[]; DStack.cmd[] = swap; WHEN 8 => -- @ -- call* RCMD[] = (RCMD[WIDTH-1..4],B"1001"); PC[] = TR[]; PC[].ena = VCC; RStack.di[] = PC[]; RStack.cmd[] = push; -- сохраняется неизменное(!) значение PC WHEN 9 => -- продолжение @ - ret-@ RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = RStack.do[]; PC[].ena = VCC; RStack.cmd[] = pop; TR[] = DI[]; TR[].ena = VCC; RD = GND; WHEN 10 => -- ! -- call* RCMD[] = (RCMD[WIDTH-1..4],B"1011"); PC[] = TR[]; PC[].ena = VCC; RStack.di[] = PC[]; RStack.cmd[] = push; -- сохраняется неизменное(!) значение PC TR[] = DStack.do[]; TR[].ena = VCC; DStack.cmd[] = pop; WHEN 11 => -- продолжение ! ret-! WR = GND; RCMD[] = (B"0000",RCMD[WIDTH-1..4]); PC[] = RStack.do[]; PC[].ena = VCC; RStack.cmd[] = pop; TR[] = DStack.do[]; TR[].ena = VCC; DStack.cmd[] = pop; WHEN 12 => -- >R RCMD[] = (B"0000",RCMD[WIDTH-1..4]); RStack.di[] = TR[]; RStack.cmd[] = push; TR[] = DStack.do[]; TR[].ena = VCC; DStack.cmd[] = pop; WHEN 13 => -- R> RCMD[] = (B"0000",RCMD[WIDTH-1..4]); DStack.di[] = TR[]; DStack.cmd[] = push; TR[] = RStack.do[]; TR[].ena = VCC; RStack.cmd[] = pop; WHEN 14,15 => -- остальные команды RCMD[] = (B"0000",RCMD[WIDTH-1..4]); END CASE; CMD[] = RCMD[3..0]; ADR[] = PC[]; DO[] = TR[]; END; |
Автор: | WingLion [ Чт сен 25, 2008 07:30 ] |
Заголовок сообщения: | |
Во первых, исправленый полный зип с проектом: http://winglion.ru/Forth-CPU/fcpu7last.zip (изменения есть во всех исходных файлах! в особенности исходники ALU_i) обновленная система команд (расширено описание команд с префиксами и сами они изменились): Код: 0 - NOP: нет операции - "ничего не делает" загружает в регистр команд слово по адресу из PC, PC увеличивается на единицу
1 - LIT: загрузка литерала - ( --> dd) загружает в регистр TR слово по адресу из PC, PC увеличивается на единицу 2 - CALL: вызов подпрограммы по непосредственному адресу - PC+1 заталкивается в стек возвратов, в PC загружается слово по адресу из PC 3 - RET: возврат из подпрограммы - Выталкивается из стека возвратов последнее значение и вписывается в PC 4 - IF: условный переход ( --> ) ЕСЛИ регистр TR содержит нуль PC увеличивается на единичку (переходит на адрес следующий после адреса перехода) ИНАЧЕ в PC загружается слово по адресу из PC 5 - DUP* префикс команды типа dup - ( dd --> dd2 dd1) - глубина используемого стека увеличивается набор команд с префиксом DUP*: DUP-0 -- DUP ( dd --> dd dd) DUP-1 -- OVER ( dd1 dd2 --> d1 dd2 dd1) DUP-3 -- TRUE ( dd --> dd FFFF) DUP-4 -- FALSE ( dd --> dd 0) DUP-5 -- 1 ( dd --> dd 1) DUP-5...15 -- зарезервированы (пока эквивалентны DUP-5) 6 - DROP* префикс команды типа dup - ( dd1 --> dd1 dd2 ) - глубина используемого стека уменьшается набор команд с префиксом DROP*: DROP-0 -- DROP ( dd2 dd1 --> dd2) DROP-2 -- ADD ( dd2 dd1 --> dd2+dd1) DROP-3 -- SUB ( dd2 dd1 --> dd2-dd1) DROP-4 -- MUL ( dd2 dd1 --> dd2*dd1) DROP-5 -- AND ( dd2 dd1 --> dd2 and dd1) DROP-6 -- OR ( dd2 dd1 --> dd2 or dd1) DROP-7 -- XOR ( dd2 dd1 --> dd2 xor dd1) DROP-8..15 -- зарезервированы (пока эквивалентны DROP-0) 7 - SWAP* префикс команды типа swap - ( dd1 dd2 --> dd1 dd2 ) - глубина используемого стека не меняется набор команд с префиксом SWAP*: SWAP-0 -- SWAP (dd2 dd1 --> dd1 dd2) SWAP-1 -- INC (dd2 dd1 --> dd2 dd1+1) SWAP-2 -- DEC (dd2 dd1 --> dd2 dd1-1) SWAP-3 -- NEG (dd2 dd1 --> dd2 - dd1) SWAP-4..15 -- зарезервированы (пока эквивалентны SWAP-0) 8 - @ разыменование (dd --> mem) - (слово из памяти по адресу из TR записывается в TR) сначала PC запоминается в стеке возвртов, а в PC записывается адрес из TR, принудительно устанавливается следующий исполняемый код команды - 9 для продолжения исполнения команды @ 9 - ret-@ продолжение рзыменования - происходит считывание слова из адреса в PC и запись его в TR, a в PC возвращается значение из стека возвратов чистое использование этого кода как команды - сомнительно, поэтому в будущем код будет маскирован и, возможно в чистом виде будет исполнять что-то полезное 10 - ! присводение (dd aa --> ) - (слово dd записывается в память по адресу аа) сначала сначала PC запоминается в стеке возвртов, а в PC записывается адрес из TR, принудительно устанавливается следующий исполняемый код команды - 11 для продолжения исполнения команды ! 11 - ret-! продолжение присвоения - происходит запись слова из TR в память по адресу из PC, a в PC возвращается значение из стека возвратов чистое использование этого кода как команды - сомнительно, поэтому в будущем код будет маскирован и, возможно в чистом виде будет исполнять что-то полезное 12 - >R на-Эр - (dd -->) слово из TR заталкивается на стек возвратов, в TR записывается слово вытолкнутое из стека данных 13 - R> с-Эр - (--> dd) слово из TR заталкивается на стек данных, в TR записывается слово вытолкнутое из стека возвратов 15 - NOP* резервный код 16 - NOP* резервный код |
Автор: | WingLion [ Чт сен 25, 2008 23:13 ] |
Заголовок сообщения: | |
Очередное дополнение - введена команда MOVE - код 14: Код: WHEN 14 => -- MOVE PC[] = RStack.do[]; RStack.cmd[] = swap; PC[].ena = VCC; RStack.di[] = PC[] + 1; XR = !XR; IF !XR THEN TR[] = DI[]; TR[].ena = VCC; RD = GND; DStack.di[] = TR[]; DStack.cmd[] = push; ELSE DStack.cmd[] = pop; TR[] = DStack.do[] - 1; WR = GND; TR[].ena = VCC; END IF; IF (TR[] == 0) and !XR THEN RCMD[] = (B"0000",RCMD[WIDTH-1..4]); ELSE RCMD[] = RCMD[]; END IF; WHEN 15 => RCMD[] = (B"0000",RCMD[WIDTH-1..4]); END CASE; картинка с симуляцией работы этой команды (и некоторых других): подпрограмма пересылки блока выглядит так: Код: CALL move
move: LIT (адрес1) >R LIT (num) JNZ-MOVE-R>-RET (адрес2) адрес1 заталкивается в стек возвратов, на вершину стека данных ложится количество пересылок, делается "переход" по адресу, "откуда брать данные" с отложенными командами снятия со стека возвратов адреса-приемника и возвратом в вызывающую программу num слов пересылаются с адреса2 на адрес1 цикл пересылки состоит из двух тактов: 1. TR -> DStack, mem(PC) -> TR, PC+1 -> RStack, RStack -> PC, 2. TR -> mem(PC), PC+1 -> RStack, RStack -> PC, DStack-1 -> TR В первом такте проверяется условие TR==0, если оно выполняется, регистр команд сдвигается на 4 бита и выполняются следующие команды. Иначе, регистр команд сохраняется, и происходит продолжение выполнения пересылки. Таким образом цикл пересылки крутится до тех пор, пока число на стеке данных не обнулится. Кроме того, в начале происходит проверка - если длина пересылаемого блока равна нулю, то ничего не делается. |
Страница 1 из 3 | Часовой пояс: UTC + 3 часа [ Летнее время ] |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |