Всем привет, дорогие хабровчане! Сегодня я хочу поделиться своей «больной» идеей реализовать калькулятор на ПЛИС на основе конечного автомата. Почему больной? Потому что уж очень мудрёно получается: всё-таки реализация автоматов на ПЛИС – дорогая практика в смысле ресурсов. Почему хочу поделиться? Потому что вишенкой на торте в этом проекте является автоматическая генерация кода с помощью такого мощного средства, как HDL Coder в MATLAB, что в купе со Stateflow очень интересно смотрится: создание железного кода на основе графического составления графа системы – ни это ли верх мечтаний разработчика, которому необходимо реализовать сложнейший граф с кучей разных переходов и условий ?!
Итак, задачу перед собой я поставил следующую: у меня есть «китайский» кит с FPGA Spartan 6 на борту и старенький клавиатурный интерфейс PS/2. Я собираюсь залить проект калькулятора-автомата на ПЛИС вместе с выбранным интерфейсом и с клавиатуры осуществлять ввод данных. Вывод результата и текущего ввода будем наблюдать на 8-ми cемисегментных дисплеях, которые также имеются на отладочной плате.
В первой части мы познакомимся с пакетом Stateflow, как собиралась модель в SIMULINK и сгенерируем HDL-описание. Во второй части мы немного скорректируем проект для получения синтезируемого HDL кода.
Немного ликбеза о конечных автоматах
Конечным автоматом называется модель устройства, имеющего один вход, один выход и обладающего конечным набором состояний. Переход в следующее состояние зависит от состояния, в котором находится устройство в данный момент, и от входного воздействия. Отсюда и деление на автоматы Мили и Мура. Автоматы Мура в отличие от автоматов Мили определяют следующее состояние, исходя только из текущего состояния, в то время как в автоматах Мили учитывается также и входное воздействие.
Блоки F и G являются строго комбинационными. Блок памяти состояний построен на последовательностной логике.
Описание автоматов осуществляется с помощью графов и таблиц. Графы используются для наглядности логики переходов, в то время как таблицы используются для описания таких переходов в ЭВМ. Автомат считается полным, если для каждой вершины определены переходы в любое из возможных состояний, описываемых моделью автомата. В противном случае автомат считается частичным.
Знакомство со Stateflow
Пакет прост и интуитивен, поэтому предлагаю начать наглядное знакомство. Итак, работа в данном пакете начинается с открытия Simulink и использования блочка chart (рис. 4). В данном блоке описывается вся внетабличная логика автомата как посредством самого инструмента Stateflow, так и Simulink вместе с MATLAB. Состояния характеризуются блоком State (рис. 5). Предусмотрено, что логика состояния может выполняться в 3-х разных режимах: при входе, при выходе из состояния, либо выполняться постоянно. Переход из состояния в состояние осуществляется посредством направленных графовых стрелок (рис. 6).
Как говорилось выше, пакет Stateflow отличает конечные автоматы Мура и Мили. Соответственно исполненные структуры при генерации HDL кода также повторяются. Чтобы показать какую структуру мы хотим реализовать в «железе», мы должны собирать граф, как показано на рис. 7.
Также важно отметить, что можно задавать как последовательно выполняющиеся состояния, так и параллельно выполняющиеся процессы, что мы увидим далее, а сейчас давайте перейдём непосредственно к сборке модели.
Граф алгоритма и сборка нашего проекта
Собственно, перед началом сборки я долго думал о том, как мне работать с точкой: с одной стороны, я могу выводить только 4 знака после запятой на свои семисегментные дисплеи и при этом, потратив большой ресурс на разрядность числа – 50 разрядов точки – это величина, которая требовалась, чтобы получить чистую тысячную единицу. С другой стороны, самые простые калькуляторы могут давать намного больше цифр после запятой, чем 4, что потребовало бы от меня значительно усложнить модель и я бы отошел от простой идеи.
Чтобы спроектировать и ввести в работу любое устройство, необходимо создать его сырую копию, т. е. прототип. Это может быть как натурный объект, так и его математическая модель. Легко догадаться, что проще и быстрее создать последнее, т. е. создать цифрового двойника. В моём случае необходимо было промоделировать ввод с клавиатуры, интерфейс ps/2, работу самого автомата и засветку семисегментных дисплеев.
Итого, в состав модели вошли:
Простейший контроллер ps/2 интерфейса – Controller_psD2;
Калькулятор на основе конечного автомата – Chart;
Семисегментная панель — controlLedPanal.
В теле автомата калькулятора можно найти 7 параллельных процессов (пунктирные границы блока state) рис. 9.
Важнейшими процессами в структуре, приведенной на рис. 9, являются:
evalBody – процесс, в котором описан граф переходов самого калькулятора (рис. 10).
LEDDISPLAY – процесс, в котором описан граф засветки семисегментных дисплеев (рис. 11).
numDecode – процесс, который проверяет код нажатой кнопки с клавиатуры и интерпретирует в цифру или арифметический знак.
interfaceProc – процесс, который собирает 8-битный кадр, приходящий от клавиатуры. Вызывает функцию собранную в Simulink (рис. 12).
Остальные процессы в той или иной степени отвечают за «сборку» цифр в число и обратно. Здесь же есть так называемые MATLAB Function, которые можно вызывать в теле конечного автомата, когда необходимо описать логику работы с помощью скрипта. И также для удобства используются Simulink Function, которые тоже используются внутри автомата.
Работа модели состоит в следующем: блок контроллера клавиатуры на вход конечного автомата последовательно отправляет 8-битный код символа нажатой клавиши, по линии dataIn, биты которого выставляются по переднему фронту тактового сигнала clockS1, который также поступает на вход конечного автомата (рис. 13).
Модель предполагает вводной и выводной регистр. В вводной регистр записывается очередная цифра, введенная с клавиатуры, а в выводной регистр записываются результаты арифметических операций и каждая вторая вводимая цифра. Последовательно введенный код декодируется в цифру, цифра записывается в вводной сдвиговый регистр и далее калькулятор ждёт следующей команды. Если приходит код цифры — всё повторяется, если приходит код арифметической операции, то из вводного регистра цифры сдвигаются в выводной регистр, а в процессе evalBody осуществляется переход в состояние операции, соответствующей пришедшему коду. В таком состоянии происходит комплектация числа из цифр, находящихся в выходном регистре с помощью MATLAB функции buildNum. Следующий шаг опять будет зависеть от кода: если это будет арифметический символ, то выполнится соответствующее действие.
Использование HDL Coder при генерации железного кода
Вот мы и добрались до генерации HDL кода. Сам по себе процесс сборки кода несложный, если знать некоторые тонкости:
При синтезе не используйте Simulink Function в теле автомата – так мне пришлось выносить интерфейсный блок.
Все вводимые переменные должны иметь тип fixed point и их разрядность должна быть задана. Не должно присутствовать переменных с типом double.
Помните, в HDL нет операции деления (/ – только нацело), извлечения корня и возведения в степень. Поэтому применение данных операторов либо вызовет ошибку, либо приведет к неверному результату.
Для реализации сложных арифметических действий в железе, пользуйтесь библиотекой HDLMathLib, которая содержит синтезируемые IP-ядра для таких действий (рис. 16).
В петлях обратной связи между блоками конечного автомата и Simulink блоками необходимо использовать задержку в 1 такт либо использовать разные скорости.
Итак, для генерации кода я чуть-чуть подправил модель (рис. 17) – здесь вынесены из тела автомата блок формирования кадра и ядро деления, и уместил синтезируемую часть в подсистему (Subsystem). Теперь необходимо в Setting Parameters указать устройство, под которое производится генерация кода и под какой софт. Также во вкладке Report отмечаем все галочки для получения максимально подробного отчёта, даём команду на генерацию кода (рис. 18).
Результаты и выводы
Как результат мы получаем:
Подробнейший отчёт по процессу генерации;
HDL – код проекта;
Код, привязанный к комментариям и соответствующим частям модели;
Сгенерированный TestBench.
Итак, мы получили жирный в смысле ресурсов Verilog код калькулятора для ПЛИС и TestBench под него. Однако, это без этапа оптимизации кода в HDL workflow. Весь процесс был интуитивен и понятен, и самым сложным были только функции сборки числа и обратно. Также мы ушли от написания громоздкого TestBench и смогли создать цифровой двойник нашего устройства – начиная от интерфейса ввода и заканчивая выводом данных на семисегментные дисплеи. Во второй части мы задействуем HDL workflow, оптимизируем этим инструментом код и зальём код в железку! Всем спасибо за внимание!