Проектирование учебного процессора с фиксированной запятой в САПр Quartus II
компании Altera
в статье предлагается на основе системы команд из работы [1] и модели процессора с управляющим автоматом на шесть состояний, позволяющим проводить вычисления с фиксированной запятой, реализованной в системе MATLAB/Simulink [2], разработать процессор в базисе ПлИС Stratix III компании Altera с использованием САПр Quartus II. особенностью модели процессора, представленного в работе [2], является распределенная система управления функциональными блоками, то есть каждый блок имеет свой локальный управляющий сигнал (шину), которым управляет цифровой автомат. основные функциональные блоки проектируемого процессора описаны на языке VHDL, код которого был сгенерирован в автоматическом режиме с помощью Simulink HDL Coder системы MATLAB/Simulink.
Андрей строгонов, д. т. н.
andreis@hotmail. ru Артем БуСЛОВ Сергей ДАВыДОВ
На рис. 1 представлена электрическая схема процессора в САПР ПЛИС Quartus II версии 8.1. Проектируемый процессор состоит из следующих функциональных блоков: управляющий автомат (блок CPU_Controller, пример 1); регистр специального назначения (РСН, блок PC_Incrementer, пример 2), он необходим для обеспечения «прыжковых» команд, таких как JMP, JMPZ, CALL и RET; счетчик команд (блок Program_ Counter, пример 3); память программ — ПЗУ процессора (блок Instruction_ROM, пример 4); регистр инструкций (блок Instruction_Register, пример 5); два регистра общего назначения (РОН, блок RegisterA, пример 6); АЛУ процессора (блок ALU, пример 7). Регистры на D-триггерах (четыре 8- (блок dff8) и один 16разрядный (блок dff16)), тактируемые фронтом синхросигнала, разработаны в дополнение к [2] с использованием мегафункции LPM_FF.
На рис. 2, 3 показаны временные диаграммы работы процессора с управляющим автоматом в САПР Quartus II. Осуществляется тестирование команд MOV A,12; MOV B,23; ADD A,B (рис. 2) и команд JMPZ11, JMP7 (рис. 3).
VHDL является языком со строгим контролем типов. Поэтому бывает необходимо преобразовать сигнал одного типа в сигнал другого типа. Даже при выполнении простых действий, если типы объектов не совпадают, может потребоваться обращение к функции преобразования типов. Различают два вида преобразования типа: переход типа и вызов функции преобразования типа. Переход типа применяется для преобразования тесно свя-
занных типов или подтипов. Если типы не тесно связанные, то необходимо выполнить вызов функции преобразования типа.
Пакет numeric_std содержит стандартный набор арифметических, логических функций и функций сравнения для работы с типами signed, unsigned, integer, std_ulogic, std_logic, std_logic_vector. В пакете numeric_std есть функция преобразования векторного типа to_unsigned с операндами arg (тип integer), size (тип natural, битовая ширина) и типом результата unsigned. Тип unsigned интерпретируется как двоичное представление числа без знака, а тип signed обозначает двоичные числа со знаком в дополнительном коде. Например: CPU_state_temp : = to_unsigned(3, 8);. Целое десятичное число 3 преобразуется в 8-разрядное двоичное число без знака, которое присваивается переменной CPU_state_temp. А переменная CPU_state_temp объявлена как массив двоичных чисел без знака: VARIABLE CPU_state_temp : unsigned(7 DOWNTO 0);. Это явное преобразование тесно связанных между собой типов.
Или IR_func <= std_logic_vector(to_ unsigned(3, 2));. Десятичное число 3 преобразуется в двоичное число “11” типа unsigned, затем тип unsigned неявно преобразуется в тип std_logic_vector. Сигналу IR_func будет назначено двоичное число “11”.
При генерации кода языка VHDL блока АЛУ используется дополнительная функция tmw_to_signed, которая преобразует двоичное число типа unsigned в двоичное число типа signed (пример 7) с шириной битовой шины типа integer, что необходимо для обес-
печения операции вычитания (вызов функции преобразования типа):
FUNCTION tmw_to_signed(arg: unsigned; width: integer) RETURN signed IS --SUB A,B
ina_1 := tmw_to_signed(unsigned(inA), 9) - tmw_to_signed(unsigned (inB), 9);
Оператор srl, введенный в стандарте VHDL93, осуществляет операцию логического сдвига одномерного массива (левый оператор) с элементами типа bit вправо, на число, указанное правым оператором типа integer. Например, одномерный массив main_opcode_temp типа unsigned(15 DOWNTO 0), представляющий собой 16-битовое двоичное число, сдвигается вправо на 8 бит. Например: main_opcode_ temp := unsigned(IR_in); cr := main_opcode_ temp srl 8;.
Логический оператор AND (тип левого операнда, правого и тип результата unsigned) используется для выделения 8-разрядного сигнала (операнда) address_data_next из 16-разрядной команды процессора путем логического умножения сигнала major_ opcode_temp с маской to_unsigned(255, 16). Например:
-- IR_in <8..1>
c_uint := main_opcode_temp AND to_unsigned(255, 16);
IF c_uint(15 DOWNTO 8) /= “00000000” THEN address_data_next <= “11111111”;
ELSE
address_data_next <= c_uint(7 DOWNTO 0);
END IF;
В примере 3 используется оператор конкатенации &, который предопределен для всех
Рис. 1. Схема процессора с управляющим автоматом в САПР Quartus II
)и Щъ ярч їм,о* la.tnt ш.&ч га,flu м,о* га,An. &<>*, m.tnt w.pn. »a«
№ч Ш; ll-J Г ~ і "Г ' • -Г "1 г ~ —г— ч і 1 ' -I' • • т ■ -Т -
L^C dc
1*1 ск.еч
fti №Mi Г 1 [ [ І і
-1
тагів,_гк “1
0>5 ЕЭ «Wjpftn ІЇ IMS Ї І Ї ISM ї 5 ї IW ( a ї 1144 І і
t>22 Ш rt® І ї НОМ к К 1363 ї 1 ■ 'Ї '&ПГ- fl V IWS ї В
01 njtr_[fT_1E 3 X 1СЭ5 X С ' "ПЯГХ & —ЧПРГГ- 1 ' X X ' р
^5G El "ч_АІ а С—
ts>G5 Э ■ х —іг ЇЙЇІЇЇ 36 ЇЛІЖ
0 ч^_ВІ 3 X » J (, = : : ■
ц>83 [3 n*S_B2 . X И
0*92 0 HAAjMI г ї 11 X J5
^Ifll 3 ! ' и
t£№ Ш #S*_rc t
Э «Яде і ї і і ї X 'з X і
t^l'35 [sJ JdkfcjKjwar и
Si Tjr3C_iU і і і і * і X
0 toKjrtfr ІГ 1 Ї 1 : J 1 1 V 1 К і X ! ї Т~ \ ї 1 X 1 І 1
Рис. 2. Временные диаграммы работы процессора с управляющим автоматом в САПР Quartus II. Тестирование команд MOV A,12; MOV B,23; ADD A,B
TMCrw Щйгя яці™ mAru М3 Dm Ш0т іИш 1 06 ui 1.1 ш 1.И<.
№•
1>'П' А
1*1
я «Я II 1—1 1
чиї
г
3 F*_m i_ X ЇМ І J _XL«i X _JL . . _Xїм . . X ї ,.1_
-_fZL 3 п» кИЗК s X из X и X ж ; ЇМ X . і х гаї
ц>И 3 H IW ) ч x m л з КЗ £ 3 X WS ! “В
-jfK 3 Ч*А1 !
Э 1 Ь ї A X ■ ! * ї !j ї U
*у7* 3 -Ч 61 6
•Н 3 'i'v 31
9 efliAjxi « X я X 3S
^І31 3 ІЧ_В лі H
цИШ а & і T" к S ■—“
•ш В adil'-jpc 3 К ID X 11 і 7 X —5 X і
іііи 0 sddH j-: jw £ X 7 X і
ви:? 3 Krtjij s ! 1 і LjLJL_ ї
j-а 3 hrn ґаа ІЇ ~т у і it і y 1 і x -r x і : і - К і Т ї і І Н !
Рис. 3. Временные диаграммы работы процессора с управляющим автоматом в САПР Quaгtus II. Тестирование команд JMPZ11, JMP7
одноразмерных массивов. Этот оператор выстраивает массивы путем комбинирования с их операндами. Оператор & используется для добавления одиночного элемента в конец массива РС^а!ие типа unsigned(7 DOWNTO 0):
-- increment PC
ain := resize(PC_value & ‘О’ & ‘О’ & ‘О’ & ‘О’ & ‘О’ & ‘О’ & ‘О’, 16); гт_О := ain + 128;
IF (ainJX15) /= ‘О’) OR (зш_О(14 DOWNTO 7) = “11111111”) THEN PC_value_next <= “11111111”;
ELSE
PC_value_next <= 8Іп_О(14 DOWNTO 7) + (“О” & (гт_О(6))); END IF;
Функция изменения размера resize (тип левого оператора unsigned, количество позиций типа natural, тип результата unsigned) позволяет из 8-разрядного сигнала PC_value сконструировать локальную переменную ain типа unsigned(15 DOWNTO О). Функция ‘+’ позволяет осуществить арифметическую операцию сложения, если тип левого оператора unsigned, а правого — integer с результатом unsigned.
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
ENTITY CPU_Controller IS PORT ( clk : IN std_logic; clk_enable : IN std_logic; reset : IN std_logic; master_rst : IN std_logic;
IR_in : IN std_logic_vector(15 DOWNTO 0);
Reg_A : IN std_logic_vector(7 DOWNTO 0); ALU_func : OUT std_logic_vector(3 DOWNTO 0); IR_func : OUT std_logic_vector(1 DOWNTO 0); PC_inc_func : OUT std_logic_vector(1 DOWNTO 0); PC_func : OUT std_logic_vector(1 DOWNTO 0); addr_inc : OUT std_logic_vector(7 DOWNTO 0); IM_read : OUT std_logic;
RegA_func : OUT std_logic_vector(2 DOWNTO 0); RegB_func : OUT std_logic_vector(2 DOWNTO 0); Reg_OutA : OUT std_logic_vector(7 DOWNTO 0); Reg_OutB : OUT std_logic_vector(7 DOWNTO 0)); END CPU_Controller;
ARCHITECTURE fsm_SFHDL OF CPU_Controller IS
SIGNAL CPU_state : unsigned(7 DOWNTO 0);
SIGNAL major_opcode : unsigned(3 DOWNTO 0); SIGNAL main_opcode : unsigned(15 DOWNTO 0); SIGNAL minor_opcode : unsigned(3 DOWNTO 0); SIGNAL address_data : unsigned(7 DOWNTO 0); SIGNAL CPU_state_next : unsigned(7 DOWNTO 0);
SIGNAL major_opcode_next : unsigned(3 DOWNTO 0);
SIGNAL main_opcode_next : unsigned(15 DOWNTO 0);
SIGNAL minor_opcode_next : unsigned(3 DOWNTO 0);
SIGNAL address_data_next : unsigned(7 DOWNTO 0);
BEGIN
initialize_CPU_Controller : PROCESS (reset, clk)
-- local variables
BEGIN IF reset = ‘1’ THEN CPU_state <= to_unsigned(0, 8); main_opcode <= to_unsigned(0, 16); major_opcode <= to_unsigned(0, 4); minor_opcode <= to_unsigned(0, 4); address_data <= to_unsigned(0, 8);
ELSIF clk’EVENT AND clk= ‘1’ THEN IF clk_enable= ‘1’ THEN CPU_state <= CPU_state_next; major_opcode <= major_opcode_next; main_opcode <= main_opcode_next; minor_opcode <= minor_opcode_next; address_data <= address_data_next;
END IF;
END IF;
END PROCESS initialize_CPU_Controller;
CPU_Controller : PROCESS (CPU_state, major_opcode, main_opcode,
minor_opcode,
address_data, master_rst, IR_in, Reg_A)
-- local variables
VARIABLE c_uint : unsigned(15 DOWNTO 0);
VARIABLE b_c_uint : unsigned(3 DOWNTO 0);
VARIABLE cr : unsigned(15 DOWNTO 0);
VARIABLE CPU_state_temp : unsigned(7 DOWNTO 0); VARIABLE major_opcode_temp : unsigned(3 DOWNTO 0); VARIABLE main_opcode_temp : unsigned(15 DOWNTO 0); VARIABLE reg_a_0 : unsigned(7 DOWNTO 0);
BEGIN
minor_opcode_next <= minor_opcode; address_data_next <= address_data;
CPU_state_temp := CPU_state; major_opcode_temp := major_opcode; main_opcode_temp := main_opcode;
-- CPU Controller
-- 16-bit Instruction Encoding:
-------------minor_opcode----------
-- NOP: ООООО ООО <ОООООООО>
-- JMP: 00000 001 <8-bit>
-- JMPZ: 00000 010 <8-bit>
-- CALL: 00000 011 <8-bit>
-- MOV A,xx: 00000 100 <8-bit>
-- MOV B,xx: 00000 101 <8-bit>
-- RET: 00000 110 <00000000>
-- MOV A,B: 00000 110 <00000001>
-- MOV B,A: 00000 110 <00000010>
-- XCHG A,B: 00000 110 <00000011>
-- ADD A,B: 00000 110 <00000100>
-- SUB A,B: 00000 110 <00000101>
-- AND A,B: 00000 110 <00000110>
-- OR A,B: 00000 110 <00000111>
-- XOR A,B: 00000 110 <00001000>
-- DEC A: 00000 110 <00001001>
IF master_rst /= ‘0’ THEN CPU_state_temp := to_unsigned(0, 8);
END IF;
PC_inc_func <= std_logic_vector(to_unsigned(0, 2));
IR_func <= std_logic_vector(to_unsigned(3, 2));
PC_func <= std_logic_vector(to_unsigned(3, 2));
IM_read <= ‘0’;
addr_inc <= std_logic_vector(to_unsigned(0, 8));
Reg_OutA <= std_logic_vector(to_unsigned(0, 8));
Reg_OutB <= std_logic_vector(to_unsigned(0, 8));
RegA_func <= std_logic_vector(to_unsigned(4, З));
RegB_func <= std_logic_vector(to_unsigned(4, З));
ALU_func <= std_logic_vector(to_unsigned(9, 4));
-- NOP
-- main_code: <16..1>
-- major_opcode: <16..9>
-- minor_opcode: <12..9>
-- address_data: <8..1>
CASE CPU_state_temp IS WHEN “00000000” =>
--%%%%%%%%%%%%%%%%%%%%%%%%%%%
-- RESETTING OUTPUTS --%%%%%%%%%%%%%%%%%%%%%%%%%%% PC_inc_func <= std_logic_vector(to_unsigned(0, 2)); PC_func <= std_logic_vector(to_unsigned(0, 2));
IR_func <= std_logic_vector(to_unsigned(0, 2));
RegA_func <= std_logic_vector(to_unsigned(0, З)); RegB_func <= std_logic_vector(to_unsigned(0, З)); CPU_state_temp := to_unsigned(1, 8);
--%%%%%%%%%%%%%%%%%%%%%%%%%%%
-- FETCH
--%%%%%%%%%%%%%%%%%%%%%%%%%%% WHEN “00000001” =>
-- Read from IM (ROM)
IM_read <= ‘1’;
-- PC increment PC+1
PC_func <= std_logic_vector(to_unsigned(2, 2));
-- store into IR
IR_func <= std_logic_vector(to_unsigned(1, 2)); CPU_state_temp := to_unsigned(2, 8);
WHEN “00000010” =>
-- Read from IR
IR_func <= std_logic_vector(to_unsigned(2, 2));
-- Accommodating for the ‘unit delay’ from IR_out to IR_in CPU_state_temp := to_unsigned(3, 8);
WHEN “00000011” =>
-- IR_in <16..1>
main_opcode_temp := unsigned(IR_in);
-- IR_in <16..9>
cr := main_opcode_temp srl 8;
IF cr(15 DOWNTO 4) /= “000000000000” THEN major_opcode_temp := “1111”;
ELSE
major_opcode_temp := ст(З DOWNTO 0);
END IF;
-- for instructions NOP,JMP,JMPZ,CALL,MOV A,xx MOV B,xx,RET
-- IR_in <12..9>
b_c_uint := major_opcode_temp AND to_unsigned(15, 4); minor_opcode_next <= b_c_uint;
-- IR_in <8..1>
c_uint := main_opcode_temp AND to_unsigned(255, 16);
IF c_uint(15 DOWNTO 8) /= “00000000” THEN address_data_next <= “11111111”;
ELSE
address_data_next <= c_uint(7 DOWNTO 0);
END IF;
-- Go to the decode stage CPU_state_temp := to_unsigned(4, 8);
—%%%%%%%%%%%%%%%%%%%%%%%%%
-- DECODE AND EXECUTE --%%%%%%%%%%%%%%%%%%%%%%%%% WHEN “00000100” =>
CASE minor_opcode IS WHEN “0000” =>
-- NOP
CPU_state_temp := to_unsigned(1, 8);
WHEN “0001” =>
-- JMP
addr_inc <= std_logic_vector(address_data); PC_inc_func <= std_logic_vector(to_unsigned(1, 2)); PC_func <= std_logic_vector(to_unsigned(1, 2)); CPU_state_temp := to_unsigned(1, 8);
WHEN “0010” =>
--JMPZ reg_a_0 := unsigned(Reg_A);
IF reg_a_0 = 0 THEN
addr_inc <= std_logic_vector(address_data); PC_inc_func <= std_logic_vector(to_unsigned(1, 2)); PC_func <= std_logic_vector(to_unsigned(1, 2)); END IF;
CPU_state_temp := to_unsigned(1, 8);
WHEN “0011” =>
-- CALL
addr_inc <= std_logic_vector(address_data); PC_inc_func <= std_logic_vector(to_unsigned(1, 2)); PC_func <= std_logic_vector(to_unsigned(1, 2)); CPU_state_temp := to_unsigned(1, 8);
WHEN “0100” =>
--MOV A,xx
Reg_OutA <= std_logic_vector(address_data); RegA_func <= std_logic_vector(to_unsigned(1, 3)); CPU_state_temp := to_unsigned(1, 8);
WHEN “0101” =>
--MOV B,xx
Reg_OutB <= std_logic_vector(address_data); RegB_func <= std_logic_vector(to_unsigned(1, 3)); CPU_state_temp := to_unsigned(1, 8);
WHEN “0110” =>
CASE address_data IS WHEN “00000000” =>
--RET
PC_inc_func <= std_logic_vector(to_unsigned(2, 2)); PC_func <= std_logic_vector(to_unsigned(2, 2)); CPU_state_temp := to_unsigned(5, 8);
WHEN “00000001” =>
--MOV A,B
ALU_func <= std_logic_vector(to_unsigned(0, 4)); RegA_func <= std_logic_vector(to_unsigned(2, 3)); RegB_func <= std_logic_vector(to_unsigned(2, 3)); CPU_state_temp := to_unsigned(5, 8);
WHEN “00000010” =>
--MOV B,A
ALU_func <= std_logic_vector(to_unsigned(1, 4)); RegA_func <= std_logic_vector(to_unsigned(2, 3)); RegB_func <= std_logic_vector(to_unsigned(2, 3)); CPU_state_temp := to_unsigned(5, 8);
WHEN “00000011” =>
--XCHG A,B
ALU_func <= std_logic_vector(to_unsigned(2, 4)); RegA_func <= std_logic_vector(to_unsigned(2, 3)); RegB_func <= std_logic_vector(to_unsigned(2, 3)); CPU_state_temp := to_unsigned(5, 8);
WHEN “00000100” =>
--ADD A,B
ALU_func <= std_logic_vector(to_unsigned(3, 4)); RegA_func <= std_logic_vector(to_unsigned(2, 3)); RegB_func <= std_logic_vector(to_unsigned(2, 3)); CPU_state_temp := to_unsigned(5, 8);
WHEN “00000101” =>
--SUB A,B
ALU_func <= std_logic_vector(to_unsigned(4, 4)); RegA_func <= std_logic_vector(to_unsigned(2, 3)); RegB_func <= std_logic_vector(to_unsigned(2, 3)); CPU_state_temp := to_unsigned(5, 8);
WHEN “00000110” =>
--AND A,B
ALU_func <= std_logic_vector(to_unsigned(5, 4)); RegA_func <= std_logic_vector(to_unsigned(2, 3)); RegB_func <= std_logic_vector(to_unsigned(2, 3)); CPU_state_temp := to_unsigned(5, 8);
WHEN “00000111” =>
--OR A,B
ALU_func <= std_logic_vector(to_unsigned(4 4));
RegA_func <= std_logic_vector(to_unsigned(2, 3)); RegB_func <= std_logic_vector(to_unsigned(2, 3)); CPU_state_temp := to_unsigned(5, 8);
WHEN “00001000” =>
--XOR A,B
ALU_func <= std_logic_vector(to_unsigned(7, 4)); RegA_func <= std_logic_vector(to_unsigned(2, 3)); RegB_func <= std_logic_vector(to_unsigned(2, 3)); CPU_state_temp := to_unsigned(5, 8);
WHEN “00001001” =>
--DEC A
ALU_func <= std_logic_vector(to_unsigned(8, 4)); RegA_func <= std_logic_vector(to_unsigned(2, 3)); CPU_state_temp := to_unsigned(5, 8);
WHEN OTHERS =>
NULL;
END CASE;
WHEN OTHERS =>
NULL;
END CASE;
WHEN “00000101” =>
RegA_func <= std_logic_vector(to_unsigned(2, 3)); RegB_func <= std_logic_vector(to_unsigned(2, 3)); CPU_state_temp := to_unsigned(1, 8);
WHEN OTHERS =>
NULL;
END CASE;
CPU_state_next <= CPU_state_temp; major_opcode_next <= major_opcode_temp; main_opcode_next <= main_opcode_temp;
END PROCESS CPU_Controller;
END fsm_SFHDL;
Пример 1. Код языка VHDL управляющего автомата проектируемого процессора, сгенерированный в автоматическом режиме с помощью Simulink HDL Coder системы MATLAB/Simulink
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
ENTITY PC_Incrementer IS PORT (
clk : IN std_logic; clk_enable : IN std_logic; reset : IN std_logic;
func : IN std_logic_vector(1 DOWNTO 0); addr : IN std_logic_vector(7 DOWNTO 0);
PC_curr : IN std_logic_vector(7 DOWNTO 0);
PC_next : OUT std_logic_vector(7 DOWNTO 0);
Temp : OUT std_logic_vector(7 DOWNTO 0));
END PC_Incrementer;
ARCHITECTURE fsm_SFHDL OF PC_Incrementer IS
SIGNAL PC_Temp : unsigned(7 DOWNTO 0);
SIGNAL PC_Temp_next : unsigned(7 DOWNTO 0);
BEGIN
initialize_PC_Incrementer : PROCESS (reset, clk)
-- local variables BEGIN
IF reset = ‘1’ THEN
PC_Temp <= to_unsigned(0, 8);
ELSIF clk’EVENT AND clk= ‘1’ THEN IF clk_enable= ‘1’ THEN
PC_Temp <= PC_Temp_next;
END IF;
END IF;
END PROCESS initialize_PC_Incrementer;
PC_Incrementer : PROCESS (PC_Temp, func, addr, PC_curr)
-- local variables
VARIABLE PC_Temp_temp : unsigned(7 DOWNTO 0); BEGIN
PC_Temp_temp := PC_Temp;
-- func = 0 => reset PC_Inc
-- func = 1 => store into PC_Inc when JMP, JMPZ, CALL -- func = 2 => load from PC_Inc when RET PC_next <= PC_curr;
Temp <= std_logic_vector(to_unsigned(0, 8));
CASE func IS WHEN “00” =>
-- reset PC_Inc
PC_next <= std_logic_vector(to_unsigned(0, 8)); WHEN “01” =>
-- store into PC_Inc when JMP, JMPZ, CALL PC_next <= addr;
PC_Temp_temp := unsigned(PC_curr);
Temp <= std_logic_vector(PC_Temp_temp); WHEN “10” =>
-- load from PC_Inc when RET PC_next <= std_logic_vector(PC_Temp);
WHEN OTHERS =>
NULL;
END CASE;
PC_Temp_next <= PC_Temp_temp;
END PROCESS PC_Incrementer;
END fsm_SFHDL;
Пример 2. Регистр специального назначения процессора на языке VHDL
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
ENTITY Program_Counter IS PORT ( clk : IN std_logic; clk_enable : IN std_logic; reset : IN std_logic;
func : IN std_logic_vector(1 DOWNTO 0); addr_in : IN std_logic_vector(7 DOWNTO 0); addr_out : OUT std_logic_vector(7 DOWNTO 0));
END Program_Counter;
ARCHITECTURE fsm_SFHDL OF Program_Counter IS
SIGNAL PC_value : unsigned(7 DOWNTO 0);
SIGNAL PC_value_next : unsigned(7 DOWNTO 0);
BEGIN
initialize_Program_Counter : PROCESS (reset, clk)
-- local variables BEGIN IF reset = ‘1’ THEN
PC_value <= to_unsigned(0, 8);
ELSIF clk’EVENT AND clk= ‘1’ THEN IF clk_enable= ‘1’ THEN PC_value <= PC_value_next;
END IF;
END IF;
END PROCESS initialize_Program_Counter;
Program_Counter : PROCESS (PC_value, func, addr_in)
-- local variables
VARIABLE ain : unsigned(15 DOWNTO 0);
VARIABLE ain_0 : unsigned(15 DOWNTO 0);
BEGIN
PC_value_next <= PC_value;
-- Program Counter
-- func = 0 => reset PC
-- func = 1 => load PC
-- func = 2 => increment PC
addr_out <= std_logic_vector(PC_value);
CASE func IS WHEN “00” =>
-- reset
PC_value_next <= to_unsigned(0, 8);
WHEN “01” =>
-- store into PC
PC_value_next <= unsigned(addr_in);
WHEN “10” =>
-- increment PC
ain := resize(PC_value & ‘0’ & ‘0’ & ‘0’ & ‘0’ & ‘0’ & ‘0’ & ‘0’, 16); ain_0 := ain + 128;
IF (ain_0(15) /= ‘0’) OR (ain_0( 14 DOWNTO 7) = “11111111”)
THEN
PC_value_next <= “11111111”;
ELSE
PC_value_next <= ain_0(14 DOWNTO 7) + (“0” & (ain_0(6))); END IF;
WHEN OTHERS =>
NULL;
END CASE;
END PROCESS Program_Counter;
END fsm_SFHDL;
Пример З. Счетчик команд процессора на языке VHDL
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
ENTITY Instruction_ROM IS PORT ( clk : IN std_logic; clk_enable : IN std_logic; reset : IN std_logic;
addr : IN std_logic_vector(7 DOWNTO 0); read : IN std_logic;
instr_out : OUT std_logic_vector(15 DOWNTO 0));
END Instruction_ROM;
ARCHITECTURE fsm_SFHDL OF Instruction_ROM IS -- TMW_TO_SIGNED
FUNCTION tmw_to_signed(arg: unsigned; width: integer) RETURN
signed IS
BEGIN
IF arg(arg’right) = ‘U’ OR arg(arg’right) = ‘X’ THEN RETURN to_signed(1, width);
END IF;
RETURN to_signed(to_integer(arg), width);
END FUNCTION;
TYPE T_UFIX_16_256 IS ARRAY (255 DOWNTO 0) of unsigned (15 DOWNTO 0);
SIGNAL data : T_UFIX_16_256;
SIGNAL data_next : T_UFIX_16_256;
BEGIN
initialize_Instruction_ROM : PROCESS (reset, clk)
-- local variables VARIABLE b_0 : INTEGER;
BEGIN IF reset = ‘1’ THEN FOR b IN 0 TO 255 LOOP data(b) <= to_unsigned(0, 16);
END LOOP;
ELSIF clk’EVENT AND clk= ‘1’ THEN IF clk_enable= ‘1’ THEN FOR b_0 IN 0 TO 255 LOOP data(b_0) <= data_next(b_0);
END LOOP;
END IF;
END IF;
END PROCESS initialize_Instruction_ROM;
Instruction_ROM : PROCESS (data, addr, read)
-- local variables
VARIABLE data_temp : T_UFIX_16_256;
BEGIN FOR b IN 0 TO 255 LOOP data_temp(b) := data(b);
END LOOP;
data_temp(0) := to_unsigned(1036, 16); data_temp(1) := to_unsigned(1303, 16); data_temp(2) := to_unsigned(1540, 16); data_temp(3) := to_unsigned(1545, 16); data_temp(4) := to_unsigned(1358, 16); data_temp(5) := to_unsigned(1539, 16); data_temp(6) := to_unsigned(1541, 16); data_temp(7) := to_unsigned(1545, 16); data_temp(8) := to_unsigned(1542, 16); data_temp(9) := to_unsigned(523, 16); data_temp(10) := to_unsigned(263, 16); data_temp(11) := to_unsigned(1037, 16); data_temp(12) := to_unsigned(1397, 16); data_temp(13) := to_unsigned(1543, 16); data_temp(14) := to_unsigned(1539, 16); data_temp(15) := to_unsigned(1544, 16); data_temp(16) := to_unsigned(277, 16); data_temp(17) := to_unsigned(1135, 16); data_temp(18) := to_unsigned(1480, 16); data_temp(19) := to_unsigned(1542, 16); data_temp(20) := to_unsigned(1536, 16); data_temp(21) := to_unsigned(785, 16); data_temp(22) := to_unsigned(0, 16);
IF read = ‘1’ THEN instr_out <= std_logic_vector(data_temp(to_integer(tmw_to signed(unsigned(addr) + 1, 32) - 1)));
ELSE
instr_out <= std_logic_vector(to_unsigned(0, 16));
END IF;
FOR c IN 0 TO 255 LOOP data_next(c) <= data_temp(c);
END LOOP;
END PROCESS Instruction_ROM;
END fsm_SFHDL;
Пример 4. Память программ процессора на языке VHDL
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
ENTITY Instruction_Register IS PORT ( clk : IN std_logic; clk_enable : IN std_logic; reset : IN std_logic;
func : IN std_logic_vector(1 DOWNTO 0);
IR_in : IN std_logic_vector(15 DOWNTO 0);
IR_out : OUT std_logic_vector(15 DOWNTO 0)); END Instruction_Register;
ARCHITECTURE fsm_SFHDL OF Instruction_Register IS SIGNAL IR_value : unsigned(15 DOWNTO 0);
SIGNAL IR_value_next : unsigned(15 DOWNTO 0); BEGIN
initialize_Instruction_Register : PROCESS (reset, clk)
-- local variables BEGIN IF reset = ‘1’ THEN IR_value <= to_unsigned(0, 16);
ELSIF clk’EVENT AND clk= ‘1’ THEN IF clk_enable= ‘1’ THEN IR_value <= IR_value_next;
END IF;
END IF;
END PROCESS initialize_Instruction_Register;
Instruction_Register : PROCESS (IR_value, func, IR_in)
-- local variables BEGIN IR_value_next <= IR_value;
-- A 16-bit Instruction Register with the following func: -- func == 0 => reset -- func == 1 => store into IR -- func == 2 => read from IR;
-- otherwise, preserve old value and return 0 IR_out <= std_logic_vector(to_unsigned(0, 16));
CASE func IS WHEN “00” =>
-- reset
IR_value_next <= to_unsigned(0, 16);
WHEN “01” =>
-- store into IR
IR_value_next <= unsigned(IR_in);
WHEN “10” =>
-- read IR
IR_out <= std_logic_vector(IR_value);
WHEN OTHERS =>
NULL;
END CASE;
END PROCESS Instruction_Register;
END fsm_SFHDL;
Пример 5. Блок регистра инструкций процессора на языке VHDL
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
ENTITY RegisterA IS PORT ( clk : IN std_logic; clk_enable : IN std_logic; reset : IN std_logic;
func : IN std_logic_vector(2 DOWNTO 0);
Reg_in_A_1 : IN std_logic_vector(7 DOWNTO 0);
Reg_in_A_2 : IN std_logic_vector(7 DOWNTO 0);
Reg_out_A : OUT std_logic_vector(7 DOWNTO 0));
END RegisterA;
ARCHITECTURE fsm_SFHDL OF RegisterA IS
SIGNAL Reg_value : unsigned(7 DOWNTO 0);
SIGNAL Reg_value_next : unsigned(7 DOWNTO 0);
BEGIN
initialize_RegisterA : PROCESS (reset, clk)
-- local variables BEGIN IF reset = ‘1’ THEN
Reg_value <= to_unsigned(0, 8);
ELSIF clk’EVENT AND clk= ‘1’ THEN IF clk_enable= ‘1’ THEN
Reg_value <= Reg_value_next;
END IF;
END IF;
END PROCESS initialize_RegisterA;
RegisterA : PROCESS (Reg_value, func, Reg_in_A_1, Reg_in_A_2) -- local variables BEGIN
Reg_value_next <= Reg_value;
-- func == 0 => reset;
-- func == 1 => store into RegisterA from port 1;
-- func == 2 => store into RegisterA from port 2;
-- func == 3 => read from RegisterA;
-- HDL specific fimath
Reg_out_A <= std_logic_vector(Reg_value);
CASE func IS WHEN “000” =>
-- reset
Reg_value_next <= to_unsigned(0, 8);
WHEN “001” =>
-- store into Reg_A from port 1 Reg_value_next <= unsigned(Reg_in_A_1);
WHEN “010” =>
-- store into Reg_A from port 2 Reg_value_next <= unsigned(Reg_in_A_2);
WHEN “011” =>
-- read Reg_A
Reg_out_A <= std_logic_vector(Reg_value);
WHEN OTHERS =>
NULL;
END CASE;
END PROCESS RegisterA;
END fsm_SFHDL;
Пример 6. Блок регистра общего назначения A процессора на языке VHDL
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
ENTITY ALU IS PORT ( clk : IN std_logic; clk_enable : IN std_logic; reset : IN std_logic;
func : IN std_logic_vector(3 DOWNTO 0); inA : IN std_logic_vector(7 DOWNTO 0); inB : IN std_logic_vector(7 DOWNTO 0); outA : OUT std_logic_vector(7 DOWNTO 0); outB : OUT std_logic_vector(7 DOWNTO 0));
END ALU;
ARCHITECTURE fsm_SFHDL OF ALU IS -- TMW_TO_SIGNED
FUNCTION tmw_to_signed(arg: unsigned; width: integer) RETURN signed IS BEGIN
IF arg(arg’right) = ‘U’ OR arg(arg’right) = ‘X’ THEN RETURN to_signed(1, width);
END IF;
RETURN to_signed(to_integer(arg), width);
END FUNCTION;
-- TMW_TO_UNSIGNED
FUNCTION tmw_to_unsigned(arg: signed; width: integer) RETURN unsigned IS
CONSTANT ARG_LEFT: INTEGER := ARG’LENGTH-1; ALIAS XARG: SIGNED(ARG_LEFT downto 0) is ARG; VARIABLE result : unsigned(width-1 DOWNTO 0);
VARIABLE argSize : integer;
BEGIN
IF XARG(XARG’high-1) = ‘U’ OR arg(arg’right) = ‘X’ THEN RETURN to_unsigned(1, width);
END IF;
IF (ARG_LEFT < width-1) THEN result := (OTHERS => XARG(ARG_LEFT)); result(ARG_LEFT downto 0) := unsigned(XARG);
ELSE
result(width-1 downto 0) := unsigned(XARG(width-1 downto 0)); END IF;
RETURN result;
END FUNCTION;
BEGIN
ALU : PROCESS (func, inA, inB)
-- local variables
VARIABLE X_temp : unsigned(7 DOWNTO 0);
VARIABLE ina_0 : unsigned(7 DOWNTO 0);
VARIABLE ina_1 : signed(8 DOWNTO 0);
VARIABLE ina_2 : signed(8 DOWNTO 0);
BEGIN
-- This 8-bit ALU supports the following operations:
-- MOV, XCHG, ADD, SUB, AND, OR, XOR, DEC
-- func = 0 => MOV A,B
-- func = 1 => MOV B,A
-- func = 2 => XCHG A,B
-- func = 3 => ADD A,B
-- func = 4 => SUB A,B
-- func = 5 => AND A,B
-- func = 6 => OR A,B
-- func = 7 => XOR A,B
-- func = 8 => DEC A
-- Simply pass the inA, when there is no designated func outA <= inA;
-- Simply pass the inB, when there is no designated func outB <= inB;
CASE func IS WHEN “0000” =>
--MOV A,B outA <= inB;
WHEN “0001” =>
--MOV B,A outB <= inA;
WHEN “0010” =>
--XCHG A,B
X_temp := unsigned(inB); outB <= inA;
outA <= std_logic_vector(X_temp);
WHEN “0011” =>
--ADD A,B
ina_0 := unsigned(inA) + unsigned(inB); outA <= std_logic_vector(ina_0);
WHEN “0100” =>
--SUB A,B
ina_1 := tmw_to_signed(unsigned(inA), 9) - tmw_to_signed (unsigned(inB), 9);
IF ina_1(8) = ‘1’ THEN outA <= “00000000”;
ELSE
outA <= std_logic_vector(resize(unsigned(ina_1(7 DOWNTO 0)), 8));
END IF;
WHEN “0101” =>
--AND A,B
outA <= std_logic_vector(tmw_to_unsigned(tmw_to_ signed(unsigned(inA), 32) AND tmw_to_signed(unsigned(inB), 32), 8)); WHEN “0110” =>
--OR A,B
outA <= std_logic_vector(tmw_to_unsigned(tmw_to_ signed(unsigned(inA), 32) OR tmw_to_signed(unsigned(inB), 32), 8)); WHEN “0111” =>
--XOR A,B
outA <= std_logic_vector(tmw_to_unsigned(tmw_to_ signed(unsigned(inA), 32) XOR tmw_to_signed(unsigned(inB), 32), 8)); WHEN “1000” =>
--DEC A
ina_2 := tmw_to_signed(unsigned(inA), 9) - 1;
IF ina_2(8) = ‘1’ THEN outA <= “00000000”;
ELSE
outA <= std_logic_vector(resize(unsigned(ina_2(7 DOWNTO 0)), 8));
END IF;
WHEN OTHERS =>
NULL;
END CASE;
END PROCESS ALU;
END fsm_SFHDL;
Пример 7. Блок АЛУ процессора на языке VHDL
Процессор, позволяющий проводить вычисления в формате с фиксированной запятой, код языка которого был получен с использованием Simulink HDL Coder системы визуального имитационного моделирования MATLAB/ Simulink [2], показал свою работоспособность в САПР Quartus II компании Altera. Процессор может быть успешно размещен в ПЛИС Stratix III EP3SL50F484C2 и занимает менее 1% ресурсов адаптивных таблиц перекодировок (ALUT, 209) для реализации комбинационной логики и менее 1% ресурсов последовательностной логики (регистров, 105).
Автоматически сгенерированный код языка VHDL c использованием Simulink HDL Coder системы MATLAB/Simulink позволяет значительно ускорить процесс разработки пользовательских процессорных ядер, для реализации их в базисе ПЛИС.
К недостаткам этого кода следует отнести наличие достаточно большого числа явных преобразований тесно связанных между собой типов, что определяется форматом представления исходных данных системы MATLAB/Simulink. ■
Литература
1. Тарасов И. Проектирование конфигурируемых процессоров на базе ПЛИС. Часть I // Компоненты и технологии. 2006. № 2.
2. Строгонов А. Проектирование учебного процессора с фиксированной запятой в системе MATLAB/Simulink // Компоненты и технологии. 2009. № 7.
3. Cуворова Е. А., Шейнин Ю. Е. Проектирование цифровых систем на VHDL. СПб.: БХВ-Петербург, 2003.
4. Поляков А. К. Языки VHDL и VERILOG в проектировании цифровой аппаратуры. М.: СОЛОН-Пресс, 2003.
5. Уэйкерли Дж. Ф. Проектирование цифровых устройств. Том 2. М.: Постмаркет, 2002.