УДК 621.3
ПРОЕКТИРОВАНИЕ ПРОЦЕССОРА С ИСПОЛЬЗОВАНИЕМ ВЫСОКОУРОВНЕВОГО ЯЗЫКА ОПИСАНИЯ АППАРАТНЫХ СРЕДСТВ УИБЬ
А. В. Строгонов, С. А. Цыбин
В статье рассматривается проектирование процессорного ядра с фиксированной запятой на языке УИБЬ для реализации в базисе программируемых логических интегральных схем с использованием САПР Quartus II
Ключевые слова: микропроцессор, система команд, язык описания аппаратных средств
В данной работе предлагается на основе системы команд из работы [1] и на основе модели процессора с управляющим автоматом на шесть состояний, позволяющим проводить вычисления с фиксированной зяпятой, реализованной в системе Matlab/Simulink [2], разработать процессор в базисе ПЛИС Stratix III компании Altera с использованием САПР Quartus II. Особенностью модели процессора представленного в работе [2] является распределенная система управления функциональными блоками, т.е. каждый блок имеет свой локальный управляющий сигнал (шину), которым управляет цифровой автомат. Основные функциональные блоки проектируемого процессора описаны на языке VHDL, код языка которого был сгенерирован в автоматическом режиме с помощью Simulink HDL Coder системы Matlab/Simulink.
Проектируемый процессор состоит из следующих функциональных блоков: управляющий автомат (блок CPU_Controller, пример); регистр специального назначения (РСН, блок PC_Incrementer), необходим для обеспечения “прыжковых” команд, таких как JMP, JMPZ, CALL и RET; счетчик команд (блок Program_Counter); память программ - ПЗУ процессора (блок Instruc-tion_ROM); регистра инструкций (блок Instruc-tion_Register); двух регистров общего назначения (РОН, блок RegisterA); АЛУ процессора (блок ALU). Регистры на D-триггерах (четыре 8-ми (блок dff8) и один 16-ти разрядный (блок dff 16)), тактируемые фронтом синхросигнала разработаны дополнительно (см. работу [2]) с использованием мегафункции LPM_FF.
На рис. 1 и 2 показаны временные диаграммы работы процессора с управляющим автоматом в САПР Quartus II. Осуществляется тестирование команд MOV A,12; MOV B,23; ADD A,B и команд JMPZ11, JMP7.
Строгонов Андрей Владимирович - ВГТУ, д-р техн. наук, профессор, E-mail: [email protected], тел. (4732) 43-76-95
Цыбин Сергей Александрович - ОАО “Конструкторско-технологический центр “Электроника”, соискатель, E-mail: [email protected]
Язык 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, что необходимо для обеспечения операции вычитания.
Оператор 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).
В примере 3 используется оператор конкатенации & который предопределен для всех одноразмерных массивов. Этот оператор выстраивает массивы путем комбинирования с их операндами.
Функция изменения размера resize (тип левого оператора unsigned, колличество позиций типа natural, тип результата unsigned) позволяет из восьми разрядного сигнала PC_value сконструировать локальную переменную ain типа unsigned(15 DOWNTO 0). Функция ‘+’ позволяет осуществить ариметическую операцию сложения, если тип левого оператора 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, ma-jor_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: -- SUB A,B: -- AND A,B: -- OR A,B:
-- XOR A,B: -- DEC A:
00000 110 <00000100> 00000 110 <00000101> 00000 110 <00000110> 00000 110 <00000111> 00000 110 <00001000> 00000 110 <00001001>
IF master_rst /= 'О' 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 <= 'О'; 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));
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;
Пример. Код языка VHDL управляющего автомата проектируемого процессора, сгенерированный в автоматическом режиме с помощью Simulink HDL Coder системы Matlab/Simulink
Процессор, позволяющий проводить вычисления в формате с фиксированной зяпятой, код языка которого был получен с использованием 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.
Name
dk elk ena im read reset master_res El instr_rom El instr El instr_dff_16 El reg_A1 El reg_A2 El reg_Et El reg_B2 El reg_A_out El neg_B_out El addrjnc El addr_pc El addr_pc_incr El Func_alu El Funcjnstr
•З’ 1
D^3
ИМ
: 103* X ! 121! X ! 1540 X X 1M5 :
#22 : X 1036 : X 1303 : X 1M0 I ! 1545
#35 i 1036 Ї 121! Ї ! 1540 0 1S45
56 i
#65 i 12 2E 1 X 3 3 35 ;
#74 : X 23 E
#83 i ! 22
#92 i 1 X 35
#101 i 22
#110 :
#119 i X i X
#128 i
#137 3 3 і і
#142 ° ї 3 X 2 з X X 2 2 X 1
Рис. 1. Временные диаграммы ра-бот000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000 ы процессора с управляющим автоматом в САПР Quartus II. Тестирование команд MOV A,12; MOV B,23; ADD
A,B
Name 780.0 ns 820.0 ns 860.0 ns 900.0 ns 940.0 ns 980.0 ns 1.02 us 1.06 us 1.1 us 1.14 L
g£»0 elk clk_ena im_read reset master res Ш instr_rom Ш instr [+1 instr dff 16 0 reg_A1 ІЗ reg_A2 ІЗ reg_B1 13 reg_B2 Ы reg A out 13 reg В out 13 addrjnc [3 addr_pc [3 addr_pcjncr 13 Гипс alu [3 Funcjnstr
E>1
■#■2 _J
!S»3 і
I# 4
#5 X E22 x 263 X і 1545 X X 1И2
#22 1542 E22 X 263 X 1545 1542
#39 1542 Ї X 523 X 263 1545 2
0
#65 43 X 34 X 34 33 з X 3
#74 0
#83 34
#32 43 a X 3
#101 34
#110 0 - 0
#119 9 1 1 8
#128 0 0
#137 a X 5 8 Э
#142 3 X 2 X 2 3 X 1
Рис. 2. Временные диаграммы работы процессора с управляющим автоматом в САПР РиагШБ II. Тестирование
команд ЖР211, ЖР7
Литература
1. Тарасов И. Проектирование конфигурируемых процессоров на базе ПЛИС. Часть I // Компоненты и технологии. 2006. N2.
2. Строгонов А. Проектирование учебного процессора с фиксированной запятой в системе МайаЬ/81тиИпк // Компоненты и технологии. 2009. N7.
3. Суворова Е.А., Шейнин Ю.Е. Проектирование цифровых систем на 'УГГОЬ. - СПб.: БХВ-Петербург, 2003. - 576 с.
4. Поляков А. К. Язки 'УГГОЬ и \ЕЫЬОО в проектировании цифровой аппаратуры. - М.: СОЛОН-Пресс, 2003. - 320 с.
5. Дж. Ф. Уэйкерли. Проектирование цифровых устройств, том 2. Москва: Постмаркет, 2002. -528 с.
Воронежский государственный технический университет ОАО “Конструкторско-технологический центр “Электроника”
DESIGNING OF THE PROCESSOR WITH USE HARDWARE DESCRIPTION LANGUAGE VHDL
A.V. Strogonov, S.A. Tsybin In article designing a processor kernel from the fixed comma in language VHDL for realization in basis of programmed logic integrated circuits with use EDA Quartus II is considered
Key words: microprocessor, system of the commands, hardware description language