Андрей СТРОГОНОВ, д. т. н.
andreis@hotmail. ru Сергей ДАВЫДОВ
Воспользуемся системой команд синхронного процессора, рассмотренного в работе [3], и спроектируем микропроцессорное ядро с использованием конвейерной архитектуры [4]. Типовой конвейер микропроцессора содержит пять стадий [5]: выборка инструкции; декодирование инструкции; адресация и выборка операнда из ОЗУ; выполнение арифметических операций; сохранение результата операции. Каждый этап команды рассматривается как каскад конвейера. Таким образом, можно организовать наложение команд, при котором новая команда будет начинать выполняться в первый момент каждого такта. Благодаря использованию внутреннего параллелизма потока команд конвейерная обработка позволяет существенно снизить в среднем время выполнения одной команды. Пропускная способность машины с конвейерной обработкой определяется числом команд, пропущенных через конвейер за единицу времени.
Для реализации процессора необходима память, в которой будут храниться команды микропроцессора (память программ) и инструкции для управляющего автомата. Проектируемая память имеет асинхронный сигнал сброса reset, состоит из двух массивов памяти емкостью 4096 бит. Далее приведен код языка VHDL асинхронной памяти (пример 1). ПЗУ разделено на 2 области и обладает двумя адресными шинами — addr_cmd [15..0] и addr_avt [15..0]. По шине avt_out [15..0] передаются инструкции управляющего автомата, а по шине сmd_out [15..0] — команды микропроцессора.
Разработаем для микропроцессорного ядра управляющий автомат на девять состояний
Проектирование микропроцессорных ядер с конвейерной архитектурой
для реализации в базисе ПЛИС фирмы Altera
В работах [1, 2] с использованием системы команд из работы [3] показаны примеры проектирования микропроцессорных ядер для реализации в базисе ПЛИС фирмы Altera с использованием как мегафункций асинхронного ОЗУ/ПЗУ САПР ПЛИС Quartus II, так и асинхронного ПЗУ на языке VHDL. Общим недостатком работ [1, 2] является отсутствие управляющего автомата.
(рис. 1). Использование управляющего автомата соответствует современной концепции синхронного кодирования при реализации цифровых устройств в базисе ПЛИС.
data_temp(8) := “0010000000000000”; data_temp(9) := “0011000000000000”; data_temp(10) := “0100000000000000”; data_temp(11) := “1001000000000000”;
avt_out <= std_logic_vector(data_temp(to_integer(unsigned (addr_avt))));
END PROCESS;
END a
Пример 1. Код языка VHDL-блока асинхронной памяти
Управляющий автомат имеет вход синхронизации clk и асинхронного сброса rst, который устанавливает автомат в начальное состояние INST. В начальном состоянии INST по шине instr [15..0] происходит загрузка из ПЗУ инструкции для управляющего автомата в регистр инструкций, в котором выделяются разряды [15..12] (шина instr [15..12]) для декодирования движений по веткам автомата и разряды [11..9] (шина instr [ 11..9]) для декодирования логико-арифметических операций.
На выходе автомата ip [15..0] с помощью битов контроля (внутренний сигнал control_signal) формируется адрес команды, хранящийся в ПЗУ. Высокий уровень сигнала instr_en разрешает получение новой инструкции из ПЗУ для управляющего автомата и увеличивает содержимое счетчика на единицу. Сигнал num_state [3..0] показывает номер состояния, в котором находится управляющий автомат.
Следующее состояние, в которое переходит автомат по переднему фронту синхроимпульса clk, — это ReadInst. В состоянии ReadInst происходит чтение полученной инструкции и выбор следующего состояния автомата. В состояниях INST и ReadInst в АЛУ не должно выполнять логико-арифметических операций. Автомат реализует команду NOP.
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
ENTITY rom_syn IS
PORT (addr_cmd : IN std_logic_vector(15 DOWNTO 0); addr_avt : IN std_logic_vector(15 DOWNTO 0); cmd_out : OUT std_logic_vector(15 DOWNTO 0); avt_out : OUT std_logic_vector(15 DOWNTO 0));
END rom_syn;
ARCHITECTURE a OF rom_syn IS
TYPE T_UFIX_16_256 IS ARRAY (255 DOWNTO 0) of unsigned (15 DOWNTO 0);
BEGIN
PROCESS (addr_cmd)
VARIABLE b : INTEGER;
VARIABLE c : INTEGER;
VARIABLE data_temp : T_UFIX_16_256;
BEGIN
FOR b IN 128 TO 255 LOOP data_temp(b) := to_unsigned(0, 16);
END LOOP;
data_temp(128) := to_unsigned(0, 16); data_temp(129) := to_unsigned(1165, 16); data_temp(130) := to_unsigned(1358, 16); data_temp(131) := to_unsigned(1540, 16); data_temp(132) := to_unsigned(1541, 16); data_temp(133) := to_unsigned(1542, 16); data_temp(134) := to_unsigned(1543, 16); data_temp(135) := to_unsigned(1544, 16); data_temp(136) := to_unsigned(1545, 16); data_temp(137) := to_unsigned(1539, 16); data_temp(138) := to_unsigned(1546, 16); data_temp(139) := to_unsigned(1547, 16); cmd_out <= std_logic_vector(data_temp(to_integer(unsigned (addr_cmd))));
END PROCESS;
PROCESS (addr_avt)
VARIABLE data_temp : T_UFIX_16_256;
BEGIN
FOR c IN 0 TO 255 LOOP data_temp(c) := to_unsigned(0, 16);
END LOOP;
ita_temp(0) =“0000000000000000”
ita_temp(1) =“1000000000000000”
ta_temp(2) =“1000001000000000”
ita_temp(3) := “1000010000000000”;
ta_temp(4) := “1000011000000000”;
ita_temp(5) := “1000100000000000”;
ta_temp(6) := “1000101000000000”;
ta_temp(7) := “0001000000000000”;
instr_15__12
"1000"
"000"
Рис. 1. Блок-схема управляющего автомата микропроцессорного ядра на 9 состояний
С приходом фронта синхроимпульса автомат перейдет в одну из возможных веток (всего возможны 4 ветки), например, в состояние MovA, если на шине instr_ 15_12 присутствует код “0000” (ветка с состояниями MovA, MovB, XCHG для выполнения АЛУ трех регистровых операций пересылки), в состояние ALU, если “1000” (в этом состоянии АЛУ выполняет шесть логикоарифметических операций), в состояние RegA, если “1001” (ветка с состояниями RegA, RegB, АЛУ выполняет загрузку РОН А и В с входного порта) или в состояние JUMP, если на шине instr_15_12 присутствует любое другое значение. В состоянии JUMP, в зависимости от кода на шине instr_
15_ 12, автомат может «перепрыгнуть» в другое состояние (возможные переходы показаны пунктирными линиями), при этом АЛУ не выполняет операций, а автомат реализует команду NOP.
В состоянии MovA происходит непосредственная загрузка в регистр A операнда, заданного младшим байтом команды.
Следующим состоянием, в котором произойдет загрузка операнда в регистр B, будет MovB. В состоянии XCHG произойдет обмен содержимого в регистрах A и B. После этого автомат возвращается в состояние INST и читает следующую инструкцию instr из памяти. В состоянии ALU код на шине instr_11_9 выбирает логико-арифметическую операцию, которая будет выполнена в АЛУ. В состояниях RegA и RegB происходит загрузка данных в регистры А и В с входного порта. VHDL-описание проектируемого автомата с использованием двухпроцессорного шаблона показано в примере 2:
ARCHITECTURE behave OF Control IS -- Definition of the state names
TYPE state_type IS (Inst, ReadInst, MovA, MovB, XCHG, ALU, JUMP, RegA, RegB);
SIGNAL state, next_state : state_type;
Signal control_signal: std_logic_vector(15 downto 0);
BEGIN
-- State process PROCESS(clk, rst)
BEGIN
IF rst = ‘1’ THEN state <= Inst;
ELSIF clk’event and clk=T THEN state <= next_state;
END IF;
END PROCESS;
-- Logic Process PROCESS(state)
BEGIN CASE state IS -- Instruction WHEN Inst =>
control_signal <= “0000000010000000”;
-- 128(D) NOP num_state <= “0000”; instr_en <= ‘1’; next_state <= ReadInst;
-- Read Instruction
WHEN ReadInst =>
control_signal <= “0000000010000000”;
-- 128(D) NOP num_state <= “0001”; instr_en <= ‘0’;
IF instr_15_12 = “0000” THEN next_state <=MovA;
ELSIF instr_15_12 = “1000” THEN next_state <=ALU;
ELSIF instr_15_12 = “1001” THEN next_state<=RegA;
ELSE next_state <= JUMP;
END IF;
-- MovA
WHEN MovA =>
control_signal <= “0000000010000001”;
-- 129(D) MOV A,xx next_state <= MovB; num_state <= “0010”; instr_en <= ‘0’;
-- MovB
WHEN MovB =>
control_signal <= “0000000010000010”;
-- 130(D) MOV B,xx next_state <= XCHG; num_state <= “0011”; instr_en <= ‘0’;
-- XCHG
WHEN XCHG => next_state <= Inst; num_state <= “0100”; instr_en <= ‘0’;
control_signal <= “0000000010001001”;
-- 137(D) XCHG A,B -- ALU
WHEN ALU => instr_en <= ‘0’; num_state <= “0101”;
IF instr_11_9 = “000” THEN control_signal <= “0000000010000011”;
-- 131(D) ADD A,B next_state <= INST;
ELSIF instr_11_9 = “001” THEN control_signal <= “0000000010000100”;
-- 132(D) SUB A,B next_state <= INST;
ELSIF instr_11_9 = “010” THEN control_signal <= “0000000010000101”;
-- 133(D) AND A,B next_state <= INST;
ELSIF instr_11_9 = “011” THEN control_signal <= “0000000010000110”;
-- 134(D) OR A,B next_state <= INST;
ELSIF instr_11_9= “100” THEN control_signal <= “0000000010000111”;
-- 135(D) XOR A,B next_state <= INST;
ELSIF instr_11_9 = “101” THEN control_signal <= “0000000010001000”;
-- 136(D) DEC A next_state <= INST;
END IF;
-- RegA
WHEN RegA => next_state <= RegB; num_state <= “0110”; instr_en <= ‘0’;
control_signal <= “0000000010001010”;
-- 138(D) MOV A,indata -- RegB
WHEN RegB => next_state <= INST; num_state <= “0111”; instr_en <= ‘0’;
control_signal <= “0000000010001011”;
-- 139(D) MOV B,indata -- JUMP
WHEN JUMP =>
control_signal <= “0000000010000000”;
-- 128(D) NOP instr_en <= ‘0’; num_state <= “1000”;
IF instr_15_12 = “0001” THEN next_state <= MovA; ELSIF instr_15_12 = “0010” THEN next_state <= MovB; ELSIF instr_15_12 = “0011” THEN next_state <= ALU; ELSIF instr_15_12 = “0100” THEN next_state <= XCHG; ELSIF instr_15_12 = “0110” THEN next_state <= RegA; ELSIF instr_15_12 = “0111” THEN next_state <= RegB; ELSE next_state <= Inst;
END IF;
END case;
END process; ip <= control_signal;
END behave;
Пример 2. Фрагмент кода языка VHDL управляющего автомата
Синхронное АЛУ выполняет различные логико-арифметические операции над операндами, значения которых сохраняются в регистрах-защелках А и В. В этом блоке реализованы следующие команды (команды JMPZ, CALL, RET не поддерживаются [1], добавлены две новые команды с кодом 1546(D) (MOV A, indata) и 1547(D) (MOV B, indata) для загрузки РОН А и В с входного порта): Mov A,xx; Mov B,xx; XCHG A,B; ADD A,B; SUB A,B; AND A,B; OR A,B; XOR A,B; DEC A; Reg A; Reg B (пример 3).
signal regA,regB,indata: std_logic_vector(7 downto 0);
BEGIN
PROCESS (clk,res)
BEGIN regA<=a; regB<=b; indata<=input; if (res = ‘1’) then
regA <=“00000000”; regB <=“00000000”; elsif (clk’event and clk=‘1’) then case conv_integer(cmd) is
when 1024 to 1279 => regA<=cmd(7 downto 0); enaa<=‘1’; enab<=‘0’; when 1280 to 1535 => regB<=cmd(7 downto 0); enab<=‘1’; enaa<=‘0’; when 1537=>regA<=regB; enaa<=‘1’; enab<=‘0’; when 1538=>regB<=regA; enaa<=‘0’; enab<=‘1’; when 1539=>regA<=regB; regB<=regA; enaa<=‘1’; enab<=‘1’;
when 1540=>regA<=regA+regB; enaa<=‘1’; enab<=‘0’; when 1541=>regA<=regA-regB; enaa<=‘1’; enab<=‘0’;
Рис. 2. Схема микропроцессорного ядра с конвейерной архитектурой в графическом редакторе САПР ПЛИС Quartus II
when 1542=>regA<=regA and regB;
enaa<=‘1’; enab<=‘0’;
when 1543=>regA<=regA or regB;
enaa<=‘1’; enab<=‘0’;
when 1544=>regA<=regA xor regB;
enaa<=‘1’; enab<=‘0’;
when 1545=>regA<=regA-1; enaa<=‘1’; enab<=‘0’; when 1546=>regA<=indata; enaa<=‘1’; enab<=‘0’; when 1547=>regB<=indata; enaa<=‘0’; enab<=‘1’; when others=> dataa<=regA; datab<=regB; enaa<=‘0’; enab<=‘0’; end case; end if;
dataa<=regA; datab<=regB; end process;
Пример 3. Фрагмент кода языка VHDL блока АЛУ
На рис. 2 показана схема микропроцессорного ядра с конвейерной архитектурой. В первом состоянии управляющего автомата происходит чтение инструкции из ПЗУ (іш^ [ 15..0]) и выделение из нее полей — і^г_15_12[3..0] и і^г_11_9 [2..0]. Адрес этой инструкции для автомата формирует счетчик (шина рс [ 15..0]), прибавляющий 1 к предыдущему адресу, когда автомат выполнит цикл команд и вернется в состояние 1№Т. Автомат для каждого своего состояния вырабатывает адрес нужной команды, хранящейся в ПЗУ программ
(шина ip [ 15..0]), с помощью битов контроля (сигнал control_signal). Эта команда по шине команд cmd_out [15..0] передается в АЛУ, где выполняется требуемая операция, результаты помещаются в регистры. Схема содержит 2 восьмиразрядных регистра (регистр - защелка) общего назначения А и В, данные из которых попадают в АЛУ для выполнения следующей операции.
На рис. 3 приведены временные диаграммы работы микропроцессорного ядра. В начальном состоянии управляющего ав-
Рис. 3. Временные диаграммы работы микропроцессорного ядра с конвейерной архитектурой в векторном редакторе САПР ПЛИС Quaгtus II
ПЛИС
томата (INST) происходит запись инструкции в блок выделения полей (блок instreg), и из ПЗУ программ извлекается команда NOP c кодом 0, при которой нет операций. С приходом переднего фронта синхросигнала clk автомат переключается в состояние ReadInst, в котором читается полученная инструкция и выбирается следующее состояние.
Во втором состоянии выполняется команда Mov A,xx, которая загружает в регистр А значение, заданное младшим байтом команды. Из ПЗУ программ была получена команда 48D(H) (1165(D)) и в регистр А было загружено число 8D(H), или 141 в десятичной системе. Согласно схеме на рис. 1 следующее состояние, которое принимает автомат, — состояние номер 3 (MovB). В этом состоянии выполняется команда Mov B,xx. Команда загружает в регистр В значение, заданное младшим байтом команды. Тестирование команды пересылки Mov B,xx показано на рис. 3. Из ПЗУ была получена команда 54Е(Н), и в регистр В было загружено число 4Е(Н), или 78 в десятичной системе. Согласно схеме на рис. 1 следующее состояние автомата — XCHG. Из ПЗУ была извлечена команда 603(H), и регистры А и В обменялись значени-
ями. При этом на шинах instr_15_12 = “0000” и instr_11_9 = “000”.
Код команды ADD — 604(H), или 1540(D). При этом на шинах instr_15_12 = “1000”, а instr_11_9 = “000”. Значения регистров А и В были сложены, и результат помещен в регистр А. Команда SUB A, B выполнила вычитание значений в регистрах А и В, результат помещен в регистр А (код команды — 605(H)). Команда AND A, B, выполняющая операцию побитного логического И значений в регистрах А и В, также показана на рис. 3. Команда «логическое И» с кодом 606(H) работает верно. Результат команды был помещен в регистр А. Команда «логическое ИЛИ» с кодом 607(H) выполняет операцию «побитное логическое ИЛИ» (команда OR A, B). Результат выполнения команды помещен в регистр А. Команда XOR A, B выполняет побитное логическое исключающее ИЛИ значений в регистрах А и В. Результат помещен в регистр А (код 608(H)).
Особенностью разработанного микропроцессорного ядра с конвейерной архитектурой является использование управляющего автомата и наличие двух блоков памяти: для хранения команд и для хранения инструкций управляющего автомата. При этом АЛУ
выполняет только логико-арифметические операции, а прыжковые команды типа JMP реализует управляющий автомат. Проект микропроцессора на языке VHDL может быть успешно размещен в ПЛИС APEX20KE (EP20K160EB356-1), при этом общее число задействованных ресурсов составляет 70%, с рабочей тактовой частотой до 33 МГц. ■
Литература
1. Строгонов А. Проектирование учебного процессора для реализации в базисе ПЛИС // Компоненты и технологии. 2009. № 3.
2. Строгонов А., Буслов А. Проектирование учебного процессора для реализации в базисе ПЛИС с использованием системы MATLAB/Simulink // Компоненты и технологии. 2009. № 5.
3. Тарасов И. Проектирование конфигурируемых процессоров на базе ПЛИС. Часть II // Компоненты и технологии. 2006. № 3.
4. Linder M., Schmid M. University of Ulster at Jor-danstown, University of Applied Sciences, Augsburg. Master of Engineering VLSI Design Project Report. Processor Implementation in VHD. Submitted: 06/07/07.
5. Каршенбойм И. Микропроцессор своими руками-5 // Компоненты и технологии. 2007. № 6.