Проектирование микропроцессорных ядер с использованием приложения StateFlow системы MATLAB/Simulink
Основная идея этой работы — заменить описание управляющего автомата (M-файл, табл. 1) микропроцессорного ядра из [1, 2] визуально-графическим автоматом (граф переходов), построенным с помощью приложения (пакет расширения) StateFlow системы MATLAB/Simulink. Далее с помощью приложения Simulink HDL Coder сгенерировать код языка VHDL и затем реализовать модель микропроцессора в базисе ПЛИС Stratix III EP3SL50F484C2 фирмы Altera. Особенностью данного типа ПЛИС семейства Stratix III является структура памяти TriMatrix с рабочими тактовыми частотами до 600 МГц, которая содержит 108 блоков памяти типа M9K (емкость блока — 9216 бит), 6 блоков памяти типа M144K (емкость — 147 456 бит) и 950 блоков памяти MLAB (емкость — 320 бит). Общий объем встроенной памяти ОЗУ — 1836 кбит, 475 К логических элементов (LE), 19 К адаптивных логических модулей (ALM), которые способны работать в различных режимах, и 38 К триггеров.
В таблице 1 приведен M-файл и код управляющего автомата на языке VHDL из [2]. Видно, что по M-файлу сгенерирован асинхронный цифровой автомат с использованием двух операторов выбора CASE, представляющий собой комбинационный дешифратор.
Таблица 1. M-файл и код управляющего автомата на языке VHDL [2]
Как и в работе [2], процессор состоит из следующих блоков (рис. 1): ROM — ПЗУ команд процессора (память программ); COP — блок выделения полей команды (дешифратор команд); ALU — 8-разрядное АЛУ процессора (управляющий автомат); RON — блок регистров общего назначения (8-разрядные регистры А и B); RSN — блок регистров специального назначения, 8-разрядный регистр R (стек команд) для обеспечения выполнения команд обращения к подпрограммам (CALL) и возврата (RET) и 8-разрядный регистр Ip (для хранения значений счетчика команд). На рис. 1 также показана тестовая программа (содержимое файла прошивки ПЗУ такое же, как и в [3]) и система команд из [1].
Рис. 1. Модель микропроцессорного ядра с управляющим автоматом в системе MATLAB/Simulink
Для построения ПЗУ используется функциональный блок Lookup Table (таблица соответствия). Все сигналы в процессоре представлены в формате uint8 (Unsigned integer fixed-point data type, целые числа без знака в формате с фиксированной запятой, с 8-битной шиной), кроме команд (сигналы cmd и InCmd), они представлены в формате uint16.
Управляющий автомат микропроцессора разработан с помощью приложения StateFlow (рис. 2). StateFlow является интерактивным инструментом разработки в области моделирования сложных, управляемых событиями систем. Он тесно интегрирован с MATLAB и Simulink и основан на теории конечных автоматов. Диаграмма StateFlow — графическое представление конечного автомата, где состояния и переходы формируют базовые конструктивные блоки проектируемой системы.
Рис. 2. Управляющий автомат (блок АЛУ), созданный с помощью приложения StateFlow в режиме отладки
StateFlow-диаграмма построена из отдельных объектов, таких как состояние, переход, переход по умолчанию и др. Более подробную информацию можно найти в [4]. Состояние — условие, в котором моделируемая система пребывает некоторое время, в течение этого времени она ведет себя одинаковым образом. В диаграмме переходов состояния представлены прямоугольными полями со скругленными углами. Например, состояние COMM является родителем состояний MOVAB, RET, MOVBA, XCHG, ADD, SUB, END, OR, XOR, DEC. На рис. 3 показаны временные диаграмы работы модели микропроцессорного ядра в системе MATLAB/ Simulink; A, B, R, IP — выходы соответствующих регистров РОН и РСН.
Рис. 3. Временные диаграммы работы модели микропроцессорного ядра в системе MATLAB/Simulink
Переход — это линия со стрелкой, соединяющая один графический объект с другим. В большинстве случаев переход представляет собой скачок системы из одного режима (состояния) в другой. Переход соединяет объект-источник с объектом-адресатом. Объект-источник — это место, где переход начинается, объект-адресат — это место, где переход заканчивается. Переходы по состояниям характеризуются метками. Метка может включать в себя имя события, условие, действие условия и/или действие перехода. Первоначально переходы помечаются символом «?». Метки перехода имеют следующий основной формат: event [condition] {condition_action} / transition_action. Любая часть метки может отсутствовать. Условия — это булевы выражения, которые должны быть истинны для осуществления перехода. Условия заключаются в квадратные скобки — []. Например, условие [InA= =0] должно быть истинным для того, чтобы произошло действие условия и переход стал возможен.
Действия условий следуют за условиями и заключаются в фигурные скобки — {}. Они выполняются тогда, когда условие становится истинным, но перед тем, как переход осуществится. Если ни одно условие не определено, подразумеваемое условие принимается за истинное и действие выполняется. Если условие [InA= =0] истинно, то действие {OutIP=InData} немедленно выполняется.
Линия со стрелкой и точкой на конце — это безусловный переход. Безусловные переходы преимущественно используются для определения, какое последовательное состояние должно стать активным, когда есть неоднозначность между двумя или более ИЛИ-подсостояниями. Безусловные переходы имеют объект-адресат, но у них нет объекта-источника.
На рис. 4 показано микропроцессорное ядро с асинхронным ПЗУ в базисе ПЛИС Stratix III в САПР Quartus II, код языка которого получен с использованием Simulink HDL Coder системы MATLAB/Simulink. ПЗУ, дешифратор команд — комбинационные устройства, регистры RON, RSN и управляющий автомат (ALU) — синхронные последовательностью устройства.
Рис. 4. Микропроцессорное ядро с асинхронным ПЗУ и синхронным управляющим автоматом в базисе ПЛИС Stratix III в САПР ПЛИС Quartus II, код языка которого получен с использованием Simulink HDL Coder системы MATLAB/Simulink
Для описания управляющего автомата микропроцессора (пример) используются перечислимые типы. Перечислимый тип часто применяется для обозначения состояний конечных автоматов [3]. Анализируя код VHDL, можно сделать вывод, что сгенерирован синхронный автомат с асинхронным входом Reset (активный — высокий уровень) и с синхронным сигналом разрешения тактирования clk_enable. Функциональное моделирование работы микропроцессорного ядра с асинхронным ПЗУ и синхронным управляющим автоматом в базисе ПЛИС Stratix III показано на рис. 5.
Рис. 5. Функциональное моделирование работы микропроцессорного ядра с асинхронным ПЗУ и синхронным управляющим автоматом в базисе ПЛИС Stratix III
Для сравнения на рис. 6 показано функциональное моделирование работы микропроцессорного ядра из работы [2] с асинхронным ПЗУ и асинхронным управляющим автоматом в базисе ПЛИС Stratix III.
Рис. 6. Функциональное моделирование работы микропроцессорного ядра из работы [2] с асинхронным ПЗУ и асинхронным управляющим автоматом в базисе ПЛИС Stratix III
Видно, что микропроцессорные ядра работают одинаково, но для микропроцессора с синхронным автоматом требуется большее число тактов на отработку команд, и по-разному выполняется команда с кодом 0600H (RET). На StateFlow-диаграмме можно по-иному организовать выполнение команды вызова и возврата из подпрограммы. Для команды CALL: OutR=InIP и команды RET: OutIP=InR. Сведения по используемым ресурсам ПЛИС представлены в таблице 2.
Таблица 2. Используемые ресурсы ПЛИС Stratix III EP3SL50F484C2
Логические ресурсы |
Максимальная | ||
Проект | Адаптивные таблицы перекодировок ALUT, для реализации комбинационных функций | выделенные триггеры логических элементов | тактовая частота синхросигнала, ‘maxCLI Мгц |
Асинхронное ПЗУ, асинхронный управляющий автомат (M-файл) | 82 | 26 | 360 |
Асинхронное ПЗУ, синхронный управляющий автомат (StateFlow) | 103 | 59 | 275 |
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;
InCmd : IN std_logic_vector(7 DOWNTO 0); InData : IN std_logic_vector(7 DOWNTO 0); InIP : IN std_logic_vector(7 DOWNTO 0); InR : IN std_logic_vector(7 DOWNTO 0); InA : IN std_logic_vector(7 DOWNTO 0); InB : IN std_logic_vector(7 DOWNTO 0); OutIP : OUT std_logic_vector(7 DOWNTO 0); OutR : OUT 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
TYPE T_state_type_is_ALU is (IN_NO_ACTIVE_CHILD, IN_CALL, IN_COMM, IN_INST, IN_JMP, INJMPZ, IN_MOVA, IN_MOVB, IN_NOP, IN_ReadInst);
TYPE T_state_type_is_COMM is (IN_NO_ACTIVE_CHILD, IN_ADD, IN_AND, IN_DEC, IN_MOVAB, IN_MOVBA, IN_OR, IN_RET, IN_SUB, IN_XCHG, IN_XOR); SIGNAL is_ALU : T_state_type_is_ALU; SIGNAL is_COMM : T_state_type_is_COMM; SIGNAL OutIP_reg : unsigned(7 DOWNTO 0); SIGNAL OutR_reg : unsigned(7 DOWNTO 0); SIGNAL OutA_reg : unsigned(7 DOWNTO 0); SIGNAL OutB_reg : unsigned(7 DOWNTO 0); SIGNAL is_ALU_next : T_state_type_is_ALU; SIGNAL is_COMM_next : T_state_type_is_COMM; SIGNAL OutIP_reg_next : unsigned(7 DOWNTO 0); SIGNAL OutR_reg_next : unsigned(7 DOWNTO 0); SIGNAL OutA_reg_next : unsigned(7 DOWNTO 0); SIGNAL OutB_reg_next : unsigned(7 DOWNTO 0);
BEGIN
irntialize_ALU : PROCESS (reset, clk)
— local variables BEGIN
IF reset = ‘1’ THEN
is_COMM <= IN_NO_ACTIVE_CHILD; OutIP_reg <= to_unsigned(0, 8); OutR_reg <= to_unsigned(0, 8); OutA_reg <= to_unsigned(0, 8); OutB_reg <= to_unsigned(0, 8); is_ALU <= IN_ReadInst; ELSIF clk’EVENT AND clk= ‘1’ THEN IF clk_enable= ‘1’ THEN is_ALU <= is_ALU_next; is_COMM <= is_COMM_next; OutIP_reg <= OutIP_reg_next; OutR_reg <= OutR_reg_next; OutA_reg <= OutA_reg_next; OutB_reg <= OutB_reg_next; END IF;
END IF; END PROCESS imtialize_ALU;
ALU : PROCESS (is_ALU, is_COMM, OutIP_reg, OutR_reg, OutA_reg, OutB_reg, InCmd, InData, InIP, InR, InA, InB) — local variables BEGIN
is_ALU_next <= is_ALU; is_COMM_next <= is_COMM; OutIP_reg_next <= OutIP_reg; OutR_reg_next <= OutR_reg; OutA_reg_next <= OutA_reg; OutB_reg_next <= OutB_reg; CASE is_ALU IS
WHEN IN_CALL =>
is_ALU_next <= IN_ReadInst; WHEN IN_COMM =>
is_COMM_next <= IN_NO_ACTIVE_CHILD; is_ALU_next <= IN_INST; OutIP_reg_next <= unsigned(InIP) + 1; WHEN IN_INST =>
is_ALU_next <= IN_ReadInst; WHEN IN_JMP =>
is_ALU_next <= IN_ReadInst; WHEN IN_JMPZ =>
IF unsigned(InA) = 0 THEN
OutIP_reg_next <= unsigned(InData); is_ALU_next <= IN_ReadInst; ELSIF unsigned(InA) /= 0 THEN is_ALU_next <= IN_INST; OutIP_reg_next <= unsigned(InIP) + 1; END IF; WHEN IN_MOVA =>
is_ALU_next <= IN_INST; OutIP_reg_next <= unsigned(InIP) + 1; WHEN IN_MOVB =>
is_ALU_next <= IN_INST; OutIP_reg_next <= unsigned(InIP) + 1; WHEN IN_NOP =>
is_ALU_next <= IN_INST; OutIP_reg_next <= unsigned(InIP) + 1; WHEN IN_ReadInst =>
IF unsigned(InCmd) = 6 THEN is_ALU_next <= IN_COMM; IF unsigned(InData) = 1 THEN is_COMM_next <= IN_MOVAB; OutA_reg_next <= unsigned(InB); ELSIF unsigned(InData) = 0 THEN is_COMM_next <= IN_RET; OutIP_reg_next <= unsigned(InR) — 1; ELSIF unsigned(InData) = 3 THEN is_COMM_next <= IN_XCHG; OutA_reg_next <= unsigned(InB); OutB_reg_next <= unsigned(InA); ELSIF unsigned(InData) = 2 THEN is_COMM_next <= IN_MOVBA; OutB_reg_next <= unsigned(InA); ELSIF unsigned(InData) = 4 THEN is_COMM_next <= IN_ADD;
OutA_reg_next <= unsigned(InA) + unsigned(InB); ELSIF unsigned(InData) = 5 THEN is_COMM_next <= IN_SUB; OutA_reg_next <= unsigned(InA) — unsigned(InB); ELSIF unsigned(InData) = 6 THEN is_COMM_next <= IN_AND; OutA_reg_next <= unsigned(InA AND InB); ELSIF unsigned(InData) = 7 THEN is_COMM_next <= IN_OR; OutA_reg_next <= unsigned(InA OR InB); ELSIF unsigned(InData) = 8 THEN is_COMM_next <= IN_XOR; OutA_reg_next <= unsigned(InA XOR InB); ELSIF unsigned(InData) = 9 THEN is_COMM_next <= IN_DEC; OutA_reg_next <= unsigned(InA) — 1; END IF;
ELSIF unsigned(InCmd) = 5 THEN is_ALU_next <= IN_MOVB; OutB_reg_next <= unsigned(InData); ELSIF unsigned(InCmd) = 4 THEN is_ALU_next <= IN_MOVA; OutA_reg_next <= unsigned(InData); ELSIF unsigned(InCmd) = 3 THEN is_ALU_next <= IN_CALL; OutR_reg_next <= unsigned(InIP) + 1; OutIP_reg_next <= unsigned(InData); ELSIF unsigned(InCmd) = 2 THEN
is_ALU_next <= IN_JMPZ; ELSIF unsigned(InCmd) = 1 THEN is_ALU_next <= IN_JMP; OutIP_reg_next <= unsigned(InData); ELSIF unsigned(InCmd) = 0 THEN
is_ALU_next <= IN_NOP; END IF; WHEN OTHERS =>
is_ALU_next <= IN_ReadInst; END CASE; END PROCESS ALU;
OutIP <= std_logic_vector(OutIP_reg_next); OutR <= std_logic_vector(OutR_reg_next); OutA <= std_logic_vector(OutA_reg_next); OutB <= std_logic_vector(OutB_reg_next); END fsm_SFHDL;
Пример. Код языка VHDL управляющего автомата модели микропроцессорного ядра на StateFlow, полученный с использованием Simulink HDL Coder системы MATLAB/Simulink
Выводы
Приложение Simulink HDL Coder для управляющего автомата микропроцессора построенного с помощью StateFlow системы MATLAB/Simulink, позволяет сгенерировать код языка VHDL синхронного автомата.
Система визуально-имитационного моделирования MATLAB/Simulink с приложениями StateFlow и Simulink HDL Coder может быть эффективно использована для ускоре
ния процесса разработки моделей микропроцессорных ядер. Проект микропроцессора с асинхронным ПЗУ на языке VHDL может быть успешно размещен в ПЛИС Stratix III EP3SL50F484C2, при этом общее число задействованных логических ресурсов составляет менее 1%.
Литература
- Тарасов И. Проектирование конфигурируемых процессоров на базе ПЛИС. Часть II // Компоненты и технологии. 2006. № 3.
- Строгонов А., Буслов А. Проектирование учебного процессора для реализации в базисе ПЛИС с использованием системы MATLAB/Simulink // Компоненты и технологии. 2009. № 5.
- Строгонов А., Цыбин С. Использование различных типов памяти при проектировании учебного микропроцессорного ядра для реализации в базисе ПЛИС // Компоненты и технологии. 2009. № 12.
- Рогачев Г. Н. Stateflow 5.0. Руководство пользователя: www.exponenta.ru.