Проектирование учебного процессора с фиксированной запятой в САПР Quartus II компании Altera
На рис. 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.
Рис. 1. Схема процессора с управляющим автоматом в САПР Quartus II
На рис. 2, 3 показаны временные диаграммы работы процессора с управляющим автоматом в САПР Quartus II. Осуществляется тестирование команд MOV A,12; MOV B,23; ADD A,B (рис. 2) и команд JMPZ11, JMP7 (рис. 3).
Рис. 2. Временные диаграммы работы процессора с управляющим автоматом в САПР Quartus II. Тестирование команд MOV A,12; MOV B,23; ADD A,B
Рис. 3. Временные диаграммы работы процессора с управляющим автоматом в САПР Quartus II. Тестирование команд JMPZ11, JMP7
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 используется оператор конкатенации &, который предопределен для всех одноразмерных массивов. Этот оператор выстраивает массивы путем комбинирования с их операндами. Оператор & используется для добавления одиночного элемента в конец массива PC_value типа unsigned(7 DOWNTO 0):
— 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;
Функция изменения размера resize (тип левого оператора unsigned, количество позиций типа natural, тип результата unsigned) позволяет из 8-разрядного сигнала PC_value сконструировать локальную переменную ain типа unsigned(15 DOWNTO 0). Функция ‘+’ позволяет осуществить арифметическую операцию сложения, если тип левого оператора unsigned, а правого — integer с результатом unsigned.
LIBRARY ieee;
USE ieee.std_logk_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: 00000 000 <00000000>
— 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, 3));
RegB_func <= std_logic_vector(to_unsigned(4, 3));
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, 3));
RegB_func <= std_logic_vector(to_unsigned(0, 3));
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 := cr(3 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(6, 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
КОМПОНЕНТЫ И ТЕХНОЛОГИИ • № 11 ‘2009
www.kit-e.ru
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 <= am_0(14 DOWNTO 7) + («0» & (ain_0(6)));
END IF;
WHEN OTHERS =>
NULL;
END CASE;
END PROCESS Program_Counter;
END fsm_SFHDL;
Пример 3. Счетчик команд процессора
на языке 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.
Литература
- Тарасов И. Проектирование конфигурируемых процессоров на базе ПЛИС. Часть I // Компоненты и технологии. 2006. № 2.
- Строгонов А. Проектирование учебного процессора с фиксированной запятой в системе MATLAB/Simulink // Компоненты и технологии. 2009. № 7.
- Cуворова Е. А., Шейнин Ю. Е. Проектирование цифровых систем на VHDL. СПб.: БХВ-Петербург, 2003.
- Поляков А. К. Языки VHDL и VERILOG в проектировании цифровой аппаратуры. М.: СОЛОН-Пресс, 2003.
- Уэйкерли Дж. Ф. Проектирование цифровых устройств. Том 2. М.: Постмаркет, 2002.