Данный конспект собран в качестве сопровождающего материала к курсам по ПЛИС. Поскольку курсы регулярно проводятся на базе ПЛИС различных производителей (как правило либо AMD(prev.Xilinx) либо IntelFPGA (prev.Altera)), то иллюстрации с логическими схемами, полученными в САПР, будут приведены в смешанном формате (некоторые из САПР Vivado, другие из САПР Quartus).
Базовым средством моделирования является ModelSim (либо QuestaSim), однако и для Xsim даются комментарии.
Все предложения по модернизациям и исправлению ошибок просьба направлять в Telegram: https://t.me/suggestmenews_bot
Использование графического представления схем из логических элементов может быстро перегрузиться и усложнить восприятие даже с использованием обозначений некоторых абстрактных элементов с заранее известным поведением. Для улучшения эффективности разработки логических схем были созданы системы автоматического проектирования (САПР), которые позволили использовать языки описания аппаратуры (англ. Hardware Description Language, HDL). То есть, были разработаны инструменты, благодаря которым описания поведения схем стало превращаться в схемы. Этот процесс называется синтезом. Синтез является одним из этапов компиляции проектов под ПЛИС; ступени компиляции проектов мы рассмотрим немного позднее.
Итак, для получения схемы теперь достаточно её описать. Для описания схем в рамках данного конспекта будет рассматриваться язык VHDL, а именно его наиболее актуальную версию VHDL 2008. \
!NB Важный факт о VHDL: язык является нечувствительным к регистру, то есть объекты с именами Var1
и VAR1
— это один и тот же объект.
Структура описания схемы на VHDL выглядит следующим образом:
Рассмотрим каждый из элементов отдельно на примере описания элемента И, представленного ниже. Если текст начинается с двойного дефиса — это комментарий к коду.
Библиотека IEEE — это набор стандартных объектов языка описания, которые утверждены международным институтом электроинженерии. STD_LOGIC_1164 подключается для того, чтобы воспользоваться необходимыми типами данных и возможными операциями над ними. В VHDL один из наиболее распространенных является тип std_logic. Его название буквально означает "стандартная логика". Этот тип определяет возможные логические значения, такие как логический ноль и логическую единицу. Подробнее об этом типе будет описано в следующих разделах.
Поле entity предназначено для описания программу как черного ящика, как ее мог бы видеть пользователь этой схемы. В ней содержится поле port, в нем определяются входы, выходы и их параметры. В данном примере система имеет два однобитных входа и один выход, все инициализированы типом std_logic.Обратите внимание - так как запись по сути является перечислением в скобках, то после последней записи отсутствует точка с запятой - это частая орфографическая ошибка у начинающих.
Блок архитектура (architecture) своим названием отражает свое назначение: в нем производится описание архитектуры программы. Внутри архитектуры между ключевыми слова begin и end описывается поведение схемы. В представленном коде демонстрируется простое описание путем присвоения в выход логической схемы результата логической операции И над двумя входами схемы. В языке VHDL символы "<=" подразумевают операцию присвоения, называемой неблокирующее присваивание (Nonblocking Assignment).
В результате анализа данного описания САПР Vivado превратит его в следующую схему:Результат компиляции кода
В языке VHDL существует набор зарезервированных слов, которые обозначают логические операции, вот некоторые из них:
конструкция в VHDL | описание |
---|---|
not
|
НЕ |
and |
И |
or |
ИЛИ |
xor |
Исключающее ИЛИ |
... 1 architecture mux_struct of mux1 is 2 begin 3 y <= x0 and x1; 4 y2 <= (x0 and not(a)) or (x1 and a); 5 end mux_struct;
Каждый объект, создаваемый при описании схемы на VHDL, имеет свое имя, которое ему придумывает разработчик. В этой задаче основным правилом является запрет на использование зарезервированных слов, то есть слов, которые являются частью языка VHDL, а, следовательно, использование этих же слов для именования объектов может вызвать неоднозначное поведение инструментов анализа и синтеза схем вплоть до ошибок компиляции, из текстовых описаний которых очень трудно понять в чем же дело. НЕ ИСПОЛЬЗУЙТЕ эти слова для именования объектов в описании схемы.
Согласно стандарту языка VHDL STD 1076-2008 зарезервированы следующие слова:
abs | access | after | alias | all |
and | architecture | array | assert | assume |
assume_guarantee | attribute | begin | block | body |
buffer | bus | case | component | configuration |
constant | context | cover | default | disconnect |
downto | else | elsif | end | entity |
exit | fairness | file | for | force |
function | generate | generic | group | guarded |
if | impure | in | inertial | inout |
is | label | library | linkage | literal |
loop | map | mod | nand | new |
next | nor | not | null | of |
on | open | or | others | out |
package | parameter | port | postponed | procedure |
process | property | protected | pure | range |
record | register | reject | release | rem |
report | restrict | restrict_guarantee | return | rol |
ror | select | sequence | severity | signal |
shared | sla | sll | sra | srl |
strong | subtype | then | to | transport |
type | unaffected | units | until | use |
variable | vmode | vprop | vunit | wait |
when | while | with | xnor | xor |
Помимо непосредственного описания взаимодействия между портами существуют тип объектов под названием signal. Данные объекты инициализируются в архитектуре до начала описания самой архитектуры. Пример применения объектов типа сигнал приведен ниже:
Сигнал обычно называют аналогией "провода" — при помощи таких объектов как при помощи проводов можно соединять между собой операции над входами и другие компоненты или выходы.
!NB на примере кода выше можно заметить, что сначала "провод" подключается к выходу, а затем только к операции логического И между входными портами. Результат синтеза этого кода не будет ничем отличаться от предыдущего примера: это демонстрирует главный нюанс, который часто путает начинающих: VHDL — это язык описания аппаратуры, а не язык программирования; порядок строк в этом примере не влияет на результат. Об этом говорит само название вида присвоения которое используется при "передаче" данных справа от выражения в объект слева: процедура "передачи" не блокирует выполнение строк ниже, а значит они выполняются "одновременно".
1 my_bus_out1(0) <= '1';
2 my_bus_out1(3 downto 1) <= (others => '0');
3 my_bus_out1 (6 downto 4) <= "010";
Тип данных std_logic_vector необходим прежде всего для интерпретации физического сигнала. Такой тип не подходит для математических операций. Внутри самого блока мы можем оперировать исключительно двоичными значения, для этого в VHDL используются различные типы данных, в том числе и из других библиотек ieee:
Рассмотрим основные типы данных.
Эти типы данных предназначены для возможности проведения математических операций. Подключаются эти типы данных с использованием библиотеки numeric_std, в которой определены результаты математических операций для данных типов.
Типы unsigned и signed - это двоичные типы данных, которые по способу инициализации схожи с std_logic_vector. Unsigned является беззнаковым типом данных, в то время как signed - знаковым.
<...> signal sum1 : unsigned(7 downto 0) :="10110100"; --8bit unsigned signal sum2 : signed(3 downto 0) :=4x"F"; --4bit signed <...>
При инициализации сигнала данного типа его битность четко определена в скобках; в примере сигналы sum1 и sum2 однозначно определены как шины на 8 и 4 бит соответственно. После указания типа данных и его битности появилась еще одна запись с так называемым "моржовым" оператором (двоеточие равно). Данная запись указывает значение сигнала в нулевой момент времени. В верхней строке показан пример инициализации в двоичном виде, а инициализация сигнала sum2 показана с использованием шестнадцатеричного числа F.
Тип данных integer является целочисленным типом данных, однако, при инициализации его битность по умолчанию 32, если только не указаны ограничения при создании сигнала:
<...> signal count1 : integer; --32bit signal count2 : integer range 0 to 3; --2bit <...>Запись range 0 to 3 сообщает, что создаваемый сигнал не может принимать значения вне указанного диапазона [0;3]. Исходя из этих данных синтезатор определит, что для данного диапазона достаточно двух бит.
В языке VHDL определены и другие, менее популярные типы данных. К примеру, positive — это подвид типа integer, который может принимать только положительные значения от 1 до 232. Тип natural — подтип integer, который может принимать неотрицательные значения: от 0 до 232.
При описании различных схем существуют ситуации, где используются конкретные числа, с которыми выполняются математические или логические операции. Так как использование так называемых "магических чисел" является плохой практикой, существуют константы. Пример использования константы:
1 library ieee; 2 use ieee.std_logic_1164.all; 3 use ieee.numeric_std.all; 4 5 entity const_use is 6 port( 7 s1 : in std_logic; 8 s2 : in std_logic_vector(7 downto 0); 9 s3 : out std_logic 10 ); 11 end; 12 13 architecture rtl of const_use is ; 14 constant s2_check : std_logic_vector(7 downto 0) :="11001010"; 15 16 begin 17 18 process(all) 19 begin 20 if s1 = '1' then 21 s2 <= s2_check; 22 else 23 s2 <= (others =>'0'); 24 end if; 25 end process; 26 end rtl;
В строке 14 продемонстрирован пример создания константы и инициализация её значения. Аналогично сигналам константы могут быть любого типа данных
При работе с данными зачастую неудобно создавать множество сигналов или констант с индексом в имени. На помощь приходит возможность создания массивов. Для создания массива необходимо создать тип данных, а затем создать сигнал или константу с этим типом данных. Для этого используется команда type:
type <type_name> is array (range) of <type>;
В данной команде <type_name> — это название типа данных, range - это размер массива, а <type> — это указание типа данных каждого из элементов массива. Рассмотрим пример:
<..> type my_array is array (0 to 5) of unsigned(7 downto 0); --creating array signal const_arr : my_array; -- creating array signal
Здесь создается тип my_array, этот тип подразумевает, что это массив из 6 значений (от 0 до 5) данных типа unsigned(7 downto 0).
Литература:
Signed and Unsigned in VHDL, Nandland: https://www.nandland.com/vhdl/examples/example-signed-unsigned.html
Data type conversion in VHDL, Doulos: https://www.doulos.com/knowhow/vhdl/vhdl-vector-arithmetic-using-numeric_std/
VHDL Arrays, Nandland: https://www.nandland.com/vhdl/examples/example-array-type-vhdl.html
Помимо простого присвоения значения сигналам существуют еще два вида:
Присвоение значения по условию аналогично оператору if-else
в высокоуровневых языках программирования. Синтаксис выглядит следующим образом:
1 library ieee; 2 use ieee.std_logic_1164.all; 3 entity mux_mod is 4 port( 5 a_data : in std_logic_vector(3 downto 0); 6 b_data : in std_logic_vector(3 downto 0); 7 sel : in std_logic; 8 q_data : out std_logic_vector(3 downto 0) 9 ); 10 end entity; 11 architecture rtl of mux_mod is 12 begin 13 q_data <= a_data when sel = '0' else b_data; 14 end rtl;
Запись в строке 13 является присвоением по условию: на выход q_data
подаются значения со входа a_data
только при условии нулевого значения входа sel
, во всех других случаях на выход поступят значения со входа b_data
. Как можно догадаться такое описание соответствует поведению схемы мультиплексора:
Инструменты компиляции Quartus отображают несколько нюансов схемы: во-первых, т.к. сигналы многобитные, то на каждый бит информации свой мультиплексор, поэтому отрисована "пачка" мультиплексоров друг за другом. Во-вторых, сигнал sel
инвертирован, что подчеркивает пустая окружность на мультиплексоре. И, наконец, в-третьих, однобитный sel
управляет четырьмя мультиплексорами, поэтому он "размножен" на все из них.
Данная конструкция может каскадироваться, т.е. возможность множественного выбора как по бинарному дереву. Дополним схему выше еще одним входом условия и еще одним входом данных, например:
1 library ieee; 2 use ieee.std_logic_1164.all; 3 entity mux_mod2 is 4 port( 5 a_data : in std_logic_vector(3 downto 0); 6 b_data : in std_logic_vector(3 downto 0); 6 c_data : in std_logic_vector(3 downto 0); 7 sel0 : in std_logic; 7 sel1 : in std_logic; 8 q_data : out std_logic_vector(3 downto 0) 9 ); 10 end entity; 11 architecture rtl of mux_mod is 12 signal sels : std_logic_vector(1 downto 0); 13 begin 14 q_data <= a_data when sels = "00" else b_data 15 when sels = "01" else c_data; 16 sels <= sel1 & sel0; -- операция конкатенации 17 end rtl;
Данное описание синтезируется в следующую схему:
Теперь на схеме видно, что выход q_data определяется сразу двумя мультиплексорами
!NB Обратите внимание: каскадирование происходит путем подключения выхода мультиплексора ко входу другого. Получается, что при различных комбинациях управляющих сигналов данные будут проходить разные по количеству элементов пути. Принято считать, что у сигнала a_data выше приоритет, отсюда и название этого вида мультиплексирования — с приоритетом.
Присвоение значения по выбору аналогично оператору switch-case
в высокоуровневых языках программирования. Синтаксис выглядит следующим образом:
1 library ieee; 2 use ieee.std_logic_1164.all; 3 entity mux_mod is 4 port( 5 a_data : in std_logic_vector(3 downto 0); 6 b_data : in std_logic_vector(3 downto 0); 7 sel : in std_logic; 8 q_data : out std_logic_vector(3 downto 0) 9 ); 10 end entity; 11 architecture rtl of mux_mod is 12 begin 13 with sel select 14 q_data <= a_data when '0', 15 b_data when others; 16 end rtl;
Запись в строках 14-15 является присвоением в порт q_data
по выбору sel
. Результат компиляции будет аналогичным первому варианту в начале страницы.
В случае же увеличения событий управления количество "выборов" увеличивается, запись выглядит следующим образом:
1 library ieee; 2 use ieee.std_logic_1164.all; 3 entity mux_mod2 is 4 port( 5 a_data : in std_logic_vector(3 downto 0); 6 b_data : in std_logic_vector(3 downto 0); 6 c_data : in std_logic_vector(3 downto 0); 7 sel0 : in std_logic; 7 sel1 : in std_logic; 8 q_data : out std_logic_vector(3 downto 0) 9 ); 10 end entity; 11 architecture rtl of mux_mod is 12 signal sels : std_logic_vector(1 downto 0); 13 begin 14 with (sels) select 15 q_data <= a_data when "00", 16 b_data when "01", 17 c_data when others; 18 sels <= sel1 & sel0; -- операция конкатенации 19 end rtl;
В таком описании схема уже не имеет приоритетов между состояниями селектора, поэтому данное описание синтезируется в следующую схему:
DATA
по порядковому номеру SEL
. Перед тем как двинуться далее стоит рассмотреть инструмент, используемый для моделирования описываемой схемы с целью проверки и отладки перед тем как запустить ее на отладочной плате.
Для проведения моделирования одним из наиболее популярных инструментов является Modelsim. В версиях Quartus после 18.0 доступен его аналог — Questasim, который хотя и называется по другому, имеет абсолютно тот же внешний вид и набор команд, дополненный набором продвинутых возможностей.
Графический интерфейс инструмента позволяет произвести моделирование с использованием всего нескольких кнопок. Рассмотрим этот инструмент подробнее.
Начальное окно Modelsim
Запустить инструмент можно прямо из меню Пуск.
Важно! По умолчанию бесплатная версия называется Modelsim Starter Edition. Однако, с инструментов Quartus устанавливается автоматически еще и платная версия и ею воспользоваться уже не получится.
Графический интерфейс представляет из себя следующие поля:
Консоль имеет два назначения:
Консоль поддерживает выполнение команд с использованиям языка TCL (этот язык часто встречается и в других инструментах разработки).
Для начала стоит достаточно знать, что строки, начинающиеся со знака #, являются комментариями. С этого знака как правило начинается вывод информационных сообщений (информация об ошибках, предупреждениях о проблемах и статусе моделирования и компиляции).
Панель библиотек (library) содержит наборы компонентов, в том числе vendor-specific, которые могут пригодиться при моделировании. Пользователи могут создавать свои библиотеки со своими модулями, ссылаясь на них на этапе подготовки к моделированию.
Панель меню и управления процессом моделирования содержит все инструменты, которые пригодятся при анализе процесса моделирования.
Инструмент Modelsim имеет следующую идеологию проведения моделирования
При использовании графического интерфейса начать лучше всего со смены рабочей директории, чтобы временные файлы моделирования хранились в том же месте, где и исходные файлы. Для этого необходимо нажать File -> Change directory и выбрать директорию, в которой находятся файлы, с которыми в дальнейшем будет вестись работа.
Смена директории через графический интерфейс
Работа через графический интерфейс позволяет сразу перейти к компиляции файлов через меню Compile выбираем Compile... При компиляции первого исходного файла Modelsim предложит автоматически создать библиотеку work, в которую будут компилироваться моделируемые исходные файлы. Если данный вопрос не возник, то, скорее всего, в этой директории уже ранее создавалась рабочая библиотека и компилируемый файл будет в нее сразу добавлен.
Элементы графического интерфейса при компиляции исходных файлов
При компиляции в консоли появится информация о статусе компиляции и результатах анализа файла:
Пример вывода информации в консоль после компиляции исходного файла
После успешной компиляции исходного кода необходимо найти файл в списке библиотеки work, нажать по нему правой клавишей мыши и выбрать Simulate. После этого инструмент моделирования перейдет в режим моделирования, перестроив порядок некоторых инструментов.
Меню перехода в моделирование
Графический интерфейс в режиме моделирования
В режиме моделирования графический интерфейс оставляет консоль, но теперь раскрывает следующие панели:
Главная задача моделирования — сымитировать необходимые воздействия на входы схемы (подать логические нули или единицы), запустить временной промежуток моделирования и проанализировать поведение выходов/внутренних сигналов на предмет ошибок.
Один из способов выполнить данную задачу — представить все объекты в графическом виде как в логическом анализаторе/осциллографе и визуально проинспектировать поведение.
Для этого необходимо выделить все объекты, нажать правой кнопкой мыши и выбрать Add Wave. После этого шага должно появиться окно или вкладка Wave.
Добавление на временную диаграмму объектов в графическом интерфейсе Modelsim
Обновленное состояние окна моделирования с вкладкой Wave
На этом этапе можно начать управление моделированием и обозначение воздействий на входы схемы:
Этапы установления значения 10 на входе А
На изображении ниже приведен результат запуска нескольких шагов моделирования, изменения значения на одном из входов согласно изображениям выше и запуску еще нескольких шагов моделирования.
Временная диаграмма Modelsim после нескольких шагов и воздействий на входы
Последовательное изменение значений на входах при помощи Force и дальнейшее выполнение моделирования через команду Run позволит проследить за поведением сигналов и сделать выводы о работоспособности hdl-описания схемы.
Стоит обратить внимание, что каждое взаимодействие через графический интерфейс повторено командой в консоли. Таким образом, можно не зная языка TCL скопировать все эти строки в один текстовый файл и всего лишь вызовом одного файла сразу выполнять те шаги, которые приходится постоянно друг за другом выполнять мышью или клавиатурой. Например:
Чтобы выполнить такой файл необходимо в консоли Modelsim вызвать команду: do do_compile.do
Конструкция process — это дополнительная процедура, внутри которой можно использовать высокоуровневые операторы для описания логики.
Эта конструкция используется в коде следующим образом:
!NB: использование конструкции process не обязательно означает, что код реализует последовательную логику. Ниже будут рассмотрены примеры
В рамках конструкции process доступно использование привычной для высокоуровневых языков программирования — условного ветвления
if-else конструкция по своей логике близка к мультиплексору, ко входу адреса которого подключено условие ветвления (равенство cmd='1'), а в обоих ветках оператора описаны нулевое значение для выхода и первое значение. На схеме это будет выглядеть следующим образом:
Схема получилась комбинационная: внутри конструкции process покрыты все варианты комбинаций входов и выходов. Условия ветвления можно каскадировать используя последовательность if-elsif-else
Подобная вложенность ветвления может преобразиться как в мультиплексор с четырьмя входами данных, так и в каскад мультиплексоров.
!NB: Хотя защелка распространена в электронике, её использование в ПЛИС стараются избегать, а некоторые инструменты синтеза даже считают использование защелок ошибкой
Когда в конструкции process будут описаны не все ветви условного оператора, возникает вопрос: поведение какого логического элемента данное описание имитирует?
Warning (10631): VHDL Process Statement warning at simple_process.vhd(13): inferring latch(es) for signal or variable "q", which holds its previous value in one or more paths through the process
По аналогии с защелкой в условии ветвления можно использовать функцию rising_edge(clk) — эта функция возвращает состояние истины только в момент положительного перепада фронта сигнала clk. Простое описание триггера может выглядеть следующим образом:
Одна из наиболее стандартных визуализаций последовательной логики — это сдвиговый регистр: цепь последовательно соединенных друг за другом триггеров, фактически задерживающие распространение изменяющегося сигнала на входе первого триггера на время, равное длительности периода тактовой частоты умноженной на количество регистров в цепи. Подробнее можно поиграться со схемой на сайте: https://www.falstad.com/circuit/, выбрал Схемы ->Последовательные схемы -> Сдвиговые регистры -> SIPO. Пример такой схемы показан ниже ( можно не обращать внимания на буферы входов и выходов — это просто компоненты представления схемы после Technology map)
Поведение компонентов схемы можно описать временной диаграммой:
С каждым тактом генератора изменение на входе распространяется к выходу схемы, пока, наконец, через 5 тактов не проявится.
Простая версия исходного кода может выглядеть следующим образом:
В данном случае передача данных из "провода" а1 в "провод" а2 происходит только положительному фронту, что архитектурно подразумевает наличие триггера между этими проводами.
Выше приведена схема потому что визуальное представление на RTL Viewer имеет менее визуально очевидные межсоединения:
Здесь уже изображена "пачка" триггеров, назначенных на каждый "слой" из изображения выше. Эту же запись можно проинтерпретировать как попросту многобитное слово, внутри которого производится сдвиг каждого бита вправо или влево, а крайние значения отвечают за вход и выход. Пример такого решения с возможностью параметризации:
Сами по себе языки описания аппаратуры имеют в себе как подмножество синтезируемых конструкций, т.е. тех, которые превращаются в схемы, так и набор не синтезируемых конструкций: эти конструкции могут быть использованы для описания поведения, которое будет выполняться при моделировании, но оно не имеет возможности превратиться в схему из логических элементов.
Тестбенч (testbench) - это код, написанный на языке описания аппаратуры (как правило), используемый для создания воздействий на тестируемый код каких-либо воздействий и анализа результатов этих воздействий. По изначальной идее тестбенч - это стол, на котором стоит проверочное оборудование (генератор, блок питания, осциллограф, вольтметр и др.), которое настраивается под конкретную задачу, и на этот стол разработчик располагает элемент изделия, скажем, электрическую плату питания. Эту плату разработчик подключает проводами с одной стороны к оборудованию с другой стороны в соответствующие разъемы платы. После этого оборудование по очереди запускается с целью выявить потенциально возможные огрехи работы платы (плата не запускается, плата запускается и ничего не делает и др.).
картинка тестбенчка из презы елисея
Аналогично необходимо подготовить "стол" для проведения тестирования разрабатываемого кода.
Текст кода инициализации тестбенча концептуально требует следующего: в архитектуре рядом с сигналами объявить о подключаемом компоненте (тестируемом коде), создать сигналы-"проводники", которыми будет произведено "подключение" ко входам и выходам тестируемого кода и в теле архитектуры произвести подключение. После этих операций можно писать сам код воздействий на блок.
Напишем тестбенч для следующего кода:
Как и у любого другого кода, у тестбенча неизменными остаются главные компоненты: объявление библиотек, описание entity и описание архитектуры.
library ieee;
use ieee.std_logic_1164.all;
entity no_sig_tb is
end entity;
architecture tb of no_sig_tb is
begin
end tb;
Однако, поскольку тестбенч подразумевает собой обертку вокруг тестируемого кода, входов и выходов у тестбенча может не быть, поэтому блок сущности можно оставить пустым. Добавим компонент и сигналы в архитектуру
library ieee;
use ieee.std_logic_1164.all;
entity no_sig_tb is
end entity;
architecture tb of no_sig_tb is
component no_sig is
port(
a1 : in std_logic;
a2 : in std_logic;
o1 : out std_logic
);
end component;
signal a1_sig : std_logic :='0';
signal a2_sig : std_logic:='0';
signal o1_sig : std_logic:='0';
begin
end tb;
конструкцией component производится объявление о том, какой внешний вид схемы, которую в данном тестбенче планируется подключать для работы, по сути это entity кода выше; сигналов понадобится столько же сколько и портов. Подключение компонента производится через создание уникальной копии компонента, instance, к которой описывается подключение сигналов.
Так же хорошее объяснение построения тестового окружения можно посмотреть по ссылке: https://fpgatutorial.com/how-to-write-a-basic-testbench-using-vhdl/
Конечный автомат (так же встречается машина с конечным набором состояний, англ. finite state machine, fsm) — это концепция описания поведения последовательной логики с целью абстрагирования от структуры последовательных схем.
Термин исходит из теории автоматов (раздела дискретной математики), в которой существует понятие абстрактный автомат (abstract machine) — это теоретическая модель функционирования компьютерных систем. По аналогии с математическими функциями, абстрактный автомат также имеет набор входов, выходов, состояний и правил взаимодействия между входами, выходами и переходами:
Математическая функция \( y=f(x) \) | Абстрактный автомат |
---|---|
Аргументы (\( x \)) | множество входов |
функция (\( y \)) | множество выходов |
уравнение зависимости (\( f(x) \)) | модель поведения |
Можно заметить, что описание переходов КА напоминает поведение последовательной логики, а описание выходов схемы — комбинационной логики. Именно эта связь и используется при абстракции описания последовательных схем при помощи КА. Исторически сам автомат представляется в виде черной коробки, на входах которой в каждый момент времени t имеется некоторое значение. Это значение преобразуется через функции переходов в значение следующего состояния КА и сигналов для выходных портов Y, определяемых также через функции.
Концепция конечных автоматов широко применяется в программировании, отлично зарекомендовав себя как модель абстракции над последовательной логикой. Общее представление конечного автомата выглядит следующим образом:
[здесь картинка из понга/харрисов где состояние регистр выходы]
Самостоятельное составление подобной структуры по абстрактному описанию вызывает большие проблемы, поэтому в языках описания аппаратуры продумана последовательность использования конструкций, которые позволяют инструментам компиляции и моделирования не только представить в виде последовательной логики, но и извлечь информацию в виде графа конечного автомата и предоставить его пользователю для визуальной верификации корректности описанного поведения.
Для описания КА в языке VHDL используется конструкция case, собственный тип данных и rising_edge(clk): каждый "случай" по сути своей подразумевает наличие "состояния", в котором может находиться схема, а благодаря тактовому сигналу реализуется дискретность времени. Конструкция case использует инициализированный сигнал любого типа в качестве входного аргумента ветвления и в зависимости от его текущего значения выполняет одну из ветвей. Хотя использование этого оператора соответствует логике поведения КА, у него имеется ряд ограничений:
Описание конечного автомата на VHDL принято использовать путем создания собственного типа данных, относящегося к перечисляемым (аналог enum), и затем создание сигнала этого типа для дальнейшего использования в конструкции case. Полученная реализация для описанного ранее конечного автомата будет выглядеть следующим образом:
Многие современные инструменты работы с HDL умеют распознавать правильно описанные конечные автоматы и изображать по ним графы переходов. Далее будут рассмотрены следующие варианты, которые несложно использовать:
Самый простой и быстрый инструмент — это редактор VS Code с установленным в нем расширением Teros HDL. Для визуализации конечного автомата необходимо нажать на соответствующую клавишу
Распознавание конечного автомата в инструментах моделирования — это дополнительная возможность, поэтому требует использование скриптовых команд
vcom -work work passwd_fsm.vhd +cover=f
В отчете компиляции появится следующее дополнительное сообщение:
# ** Note: (vcom-143) Recognized 1 FSM in architecture body "passwd_fsm(rtl)".
vsim -fsmdebug work.passwd_fsm
В результате после запуска моделирования необходимо включить вкладку FSM через меню View, перейти на нее и лицезреть КА, найденные в коде. Нажав на один из них (в текущем случае на единственный) справа откроется диаграмма переходов