Всем привет, дорогие хабровчане! Сегодня я хочу поделиться своей «больной» идеей реализовать калькулятор на ПЛИС на основе конечного автомата. Почему больной? Потому что уж очень мудрёно получается: всё-таки реализация автоматов на ПЛИС – дорогая практика в смысле ресурсов. Почему хочу поделиться? Потому что вишенкой на торте в этом проекте является автоматическая генерация кода с помощью такого мощного средства, как HDL Coder в MATLAB, что в купе со Stateflow очень интересно смотрится: создание железного кода на основе графического составления графа системы – ни это ли верх мечтаний разработчика, которому необходимо реализовать сложнейший граф с кучей разных переходов и условий ?!

Итак, задачу перед собой я поставил следующую: у меня есть «китайский» кит с FPGA Spartan 6 на борту и старенький клавиатурный интерфейс PS/2. Я собираюсь залить проект калькулятора-автомата на ПЛИС вместе с выбранным интерфейсом и с клавиатуры осуществлять ввод данных. Вывод результата и текущего ввода будем наблюдать на 8-ми cемисегментных дисплеях, которые также имеются на отладочной плате.

В первой части мы познакомимся с пакетом Stateflow, как собиралась модель в SIMULINK и сгенерируем HDL-описание. Во второй части мы немного скорректируем проект для получения синтезируемого HDL кода.

Рис. 1 Отладочная плата
Рис. 1 Отладочная плата

Немного ликбеза о конечных автоматах

Конечным автоматом называется модель устройства, имеющего один вход, один выход и обладающего конечным набором состояний. Переход в следующее состояние зависит от состояния, в котором находится устройство в данный момент, и от входного воздействия. Отсюда и деление на автоматы Мили и Мура. Автоматы Мура в отличие от автоматов Мили определяют следующее состояние, исходя только из текущего состояния, в то время как в автоматах Мили учитывается также и входное воздействие.

Рис. 2 Синхронный автомат Мили
Рис. 2 Синхронный автомат Мили
Рис. 3 Синхронный автомат Мура
Рис. 3 Синхронный автомат Мура

Блоки F и G являются строго комбинационными. Блок памяти состояний построен на последовательностной логике.

Описание автоматов осуществляется с помощью графов и таблиц. Графы используются для наглядности логики переходов, в то время как таблицы используются для описания таких переходов в ЭВМ. Автомат считается полным, если для каждой вершины определены переходы в любое из возможных состояний, описываемых моделью автомата. В противном случае автомат считается частичным.

Знакомство со Stateflow

Пакет прост и интуитивен, поэтому предлагаю начать наглядное знакомство. Итак, работа в данном пакете начинается с открытия Simulink и использования блочка chart (рис. 4). В данном блоке описывается вся внетабличная логика автомата как посредством самого инструмента Stateflow, так и Simulink вместе с MATLAB. Состояния характеризуются блоком State (рис. 5). Предусмотрено, что логика состояния может выполняться в 3-х разных режимах: при входе, при выходе из состояния, либо выполняться постоянно. Переход из состояния в состояние осуществляется посредством направленных графовых стрелок (рис. 6).

Рис. 4 Основной блок описания логики автомата
Рис. 4 Основной блок описания логики автомата
Рис. 5 Блок состояния
Рис. 5 Блок состояния
Рис. 6 Переходы графа
Рис. 6 Переходы графа

Как говорилось выше, пакет Stateflow отличает конечные автоматы Мура и Мили. Соответственно исполненные структуры при генерации HDL кода также повторяются. Чтобы показать какую структуру мы хотим реализовать в «железе», мы должны собирать граф, как показано на рис. 7.

Рис. 7 Различие структур при сборке конечного автомата
Рис. 7 Различие структур при сборке конечного автомата

Также важно отметить, что можно задавать как последовательно выполняющиеся состояния, так и параллельно выполняющиеся процессы, что мы увидим далее, а сейчас давайте перейдём непосредственно к сборке модели.

Граф алгоритма и сборка нашего проекта

Собственно, перед началом сборки я долго думал о том, как мне работать с точкой: с одной стороны, я могу выводить только 4 знака после запятой на свои семисегментные дисплеи и при этом, потратив большой ресурс на разрядность числа – 50 разрядов точки – это величина, которая требовалась, чтобы получить чистую тысячную единицу. С другой стороны, самые простые калькуляторы могут давать намного больше цифр после запятой, чем 4, что потребовало бы от меня значительно усложнить модель и я бы отошел от простой идеи.

Рис. 8 Модель в Simulink
Рис. 8 Модель в Simulink

Чтобы спроектировать и ввести в работу любое устройство, необходимо создать его сырую копию, т. е. прототип. Это может быть как натурный объект, так и его математическая модель. Легко догадаться, что проще и быстрее создать последнее, т. е. создать цифрового двойника. В моём случае необходимо было промоделировать ввод с клавиатуры, интерфейс ps/2, работу самого автомата и засветку семисегментных дисплеев.

Итого, в состав модели вошли:

  1. Простейший контроллер ps/2 интерфейса – Controller_psD2;

  2. Калькулятор на основе конечного автомата – Chart;

  3. Семисегментная панель — controlLedPanal.

В теле автомата калькулятора можно найти 7 параллельных процессов (пунктирные границы блока state) рис. 9.

Рис. 9 Структура автомата
Рис. 9 Структура автомата

Важнейшими процессами в структуре, приведенной на рис. 9, являются:

  1. evalBody – процесс, в котором описан граф переходов самого калькулятора (рис. 10).

  2. LEDDISPLAY – процесс, в котором описан граф засветки семисегментных дисплеев (рис. 11).

  3. numDecode – процесс, который проверяет код нажатой кнопки с клавиатуры и интерпретирует в цифру или арифметический знак.

  4. interfaceProc – процесс, который собирает 8-битный кадр, приходящий от клавиатуры. Вызывает функцию собранную в Simulink (рис. 12).

Рис. 10 Граф калькулятора
Рис. 10 Граф калькулятора
Рис. 11 Граф засветки семисегментных дисплеев
Рис. 11 Граф засветки семисегментных дисплеев
Рис. 12 Блок сборки кадра, приходящего от клавиатуры на основе блока Simulink  Function
Рис. 12 Блок сборки кадра, приходящего от клавиатуры на основе блока Simulink Function

Остальные процессы в той или иной степени отвечают за «сборку» цифр в число и обратно. Здесь же есть так называемые MATLAB Function, которые можно вызывать в теле конечного автомата, когда необходимо описать логику работы с помощью скрипта. И также для удобства используются Simulink Function, которые тоже используются внутри автомата.

Работа модели состоит в следующем: блок контроллера клавиатуры на вход конечного автомата последовательно отправляет 8-битный код символа нажатой клавиши, по линии dataIn, биты которого выставляются по переднему фронту тактового сигнала clockS1, который также поступает на вход конечного автомата (рис. 13).

Рис. 13 Осциллограммы интерфейса ps/2 в Simulink. На входе dataIn выставлена единица.
Рис. 13 Осциллограммы интерфейса ps/2 в Simulink. На входе dataIn выставлена единица.

Модель предполагает вводной и выводной регистр. В вводной регистр записывается очередная цифра, введенная с клавиатуры, а в выводной регистр записываются результаты арифметических операций и каждая вторая вводимая цифра. Последовательно введенный код декодируется в цифру, цифра записывается в вводной сдвиговый регистр и далее калькулятор ждёт следующей команды. Если приходит код цифры — всё повторяется, если приходит код арифметической операции, то из вводного регистра цифры сдвигаются в выводной регистр, а в процессе evalBody осуществляется переход в состояние операции, соответствующей пришедшему коду. В таком состоянии происходит комплектация числа из цифр, находящихся в выходном регистре с помощью MATLAB функции buildNum. Следующий шаг опять будет зависеть от кода: если это будет арифметический символ, то выполнится соответствующее действие.

Рис. 14 Операция сложения
Рис. 14 Операция сложения
Рис. 15 Результат сложения
Рис. 15 Результат сложения

Использование HDL Coder при генерации железного кода

Вот мы и добрались до генерации HDL кода. Сам по себе процесс сборки кода несложный, если знать некоторые тонкости:

  1. При синтезе не используйте Simulink Function в теле автомата – так мне пришлось выносить интерфейсный блок.

  2. Все вводимые переменные должны иметь тип fixed point и их разрядность должна быть задана. Не должно присутствовать переменных с типом double.

  3. Помните, в HDL нет операции деления (/ – только нацело), извлечения корня и возведения в степень. Поэтому применение данных операторов либо вызовет ошибку, либо приведет к неверному результату.

  4. Для реализации сложных арифметических действий в железе, пользуйтесь библиотекой HDLMathLib, которая содержит синтезируемые IP-ядра для таких действий (рис. 16).

  5. В петлях обратной связи между блоками конечного автомата и Simulink блоками необходимо использовать задержку в 1 такт либо использовать разные скорости.

Рис. 16 Содержимое библиотеки арифметических IP-ядер
Рис. 16 Содержимое библиотеки арифметических IP-ядер

Итак, для генерации кода я чуть-чуть подправил модель (рис. 17) – здесь вынесены из тела автомата блок формирования кадра и ядро деления, и уместил синтезируемую часть в подсистему (Subsystem). Теперь необходимо в Setting Parameters указать устройство, под которое производится генерация кода и под какой софт. Также во вкладке Report отмечаем все галочки для получения максимально подробного отчёта, даём команду на генерацию кода (рис. 18).

Рис. 17 Изменения изначальной модели под генерацию кода
Рис. 17 Изменения изначальной модели под генерацию кода
Рис. 18 Генерируем код
Рис. 18 Генерируем код

Результаты и выводы

Как результат мы получаем:

  1. Подробнейший отчёт по процессу генерации;

  2. HDL – код проекта;

  3. Код, привязанный к комментариям и соответствующим частям модели;

  4. Сгенерированный TestBench.

Рис. 19 Часть отчёта: затраченные ресурсы
Рис. 19 Часть отчёта: затраченные ресурсы
Рис. 20. Соответствие частей кода с элементами модели
Рис. 20. Соответствие частей кода с элементами модели

Итак, мы получили жирный в смысле ресурсов Verilog код калькулятора для ПЛИС и TestBench под него. Однако, это без этапа оптимизации кода в HDL workflow. Весь процесс был интуитивен и понятен, и самым сложным были только функции сборки числа и обратно. Также мы ушли от написания громоздкого TestBench и смогли создать цифровой двойник нашего устройства – начиная от интерфейса ввода и заканчивая выводом данных на семисегментные дисплеи. Во второй части мы задействуем HDL workflow, оптимизируем этим инструментом код и зальём код в железку! Всем спасибо за внимание!

Комментарии (6)


  1. Thealik
    25.10.2021 16:17
    +1

    В качестве альтернативы Stateflow можно попробовать Synthezza. Может оказаться, что Synthezza дает лучшее соотношение скорость/площадь. Однако, тесной связи с MATLAB у неё нет.
    https://www.synthezza.com/demo-series


  1. NumLock
    26.10.2021 01:41

    Можно для проекта синтезировать проц. и функции калькулятора запихать как подпрограммы в коде. Интересно проц. намного больше места займёт на кристалле или будет соизмеримо?


    1. VolodjaT
      26.10.2021 08:29

      А зачем тогда FPGA если решать задачу императивно?


      1. NumLock
        26.10.2021 14:48

        В realtime системе, где время отклика критично, есть смысл функции выражать в железе. Но это калькулятор, где время выполнения не критично. Удобно создать микропрограмму с последующим дополнением функций (например финансовые функции с рекурсией), чем синтезировать весь HDL снова.


        1. VolodjaT
          27.10.2021 00:44

          А зачем тогда не realtime на fpga? Их смысл только в этом. А прошивать софт процессоры не особо даст навыков для fpga


  1. lexxsu
    26.10.2021 09:14
    +1

    Статья ради статьи, работа ради работы.

    Пишите напрямую в HDL и делайте тест на Matlab, как вариант сравните написанное вручную и автоматически. А так, студент не понимает как написанное выше представляет собой цифровую схему.