МАРСОХОД

Open Source Hardware Project

Проекты Altera Quartus Prime для платы Марсоход3

Verilog State Machine Framework

FSM, Finite State Machine

Рискну предложить почтенной публике мое новейшее "изобретение": VSMF, Verilog State Machine Framework.
Я делаю его для одного из наших внутренних проектов. Пока это не полностью завершенная работа, но скорее демонстрация идеи.

VSMF - это такая даже не библиотека, а набор макросов для упрощенного описания машин состояния на языке Verilog HDL.

Не знаю кому как, но мне бывает трудно описать машину состояния (FSM, Finite State Machine), если состояний много и много разных условий переходов. Хоть я и поклонник Verilog, но здесь на мой взгляд он не очень удобен.

Думаю Вы и сами без труда можете нагуглить статьи типа How to write FSM in Verilog? или Verilog HDL Templates for State Machines

Информации по машинам состояния в интернете много. Примеров много. Но писать FSM все равно не легко.
Если логика у машины состояния сложная, то зачастую у разработчика возникает мысль: "а не вставить ли в ПЛИС какой нибудь софт процессор, чтоб потом на нормальном языке всю логику описать".

Другие идут еще дальше - делают транслятор с традиционных языков типа C/C++ в Verilog или VHDL.
Не плохая мысль, кстати.

Я довольно долго работал над этим вопросом и вот придумал VSMF.

Итак, машина состояний.
Один из способов представления машины состояний - это набор связанных регистров, в каждый момент времени только один регистр хранит бит "1", остальные в это же время хранят "0". Переход из одного состояния в другое состояние - это передача "единички" от одного регистра к другому. Получается одинокая "единичка" путешествует по регистрам состояния, как эстафетная палочка от одного бегуна к другому.

Существует несколько проблем при описании FSM на Verilog:

  1. желательно зараннее сразу продумать все возможные состояния в системе, по возможности желательно задать им имена;
  2. нужно сразу четко понимать направления переходов между состояниями;
  3. нужно сразу продумывать какие действия система производит в каждом из состояний.

Кажется логичным и правильным брать листок бумаги чертить кружочки со стрелочками и долго на это смотреть и обдумывать алгоритмы.

А теперь представьте себе программиста на языке C или Pascal. Они вообще когда нибудь задумываются сколько строчек кода будет в их программе? А ведь условно говоря каждая строчка кода - это как бы новое состояние, новое действие системы.

С самого начала моих изысканий мне хотелось придумать псевдо язык программирования, может быть даже чем-то похожий на язык бейсик. Мне хотелось, что бы мой псевдо язык программирования позволял построчное исполнение "программы" - условно говоря, одна строка программы - это одно состояние машины FSM. При этом хотелось бы использовать какие-то стандартные методы, не изобретать свой компилятор.

После нескольких неудачных попыток пришла мысль сделать набор специальных макросов Verilog. Макросы верилог действуют примерно так же, как и в других языках. Например, на языке C/C++ можно написать 

#define MAGIC_NUMBER 123

и везде, где в тексте программы встретится слово MAGIC_NUMBER оно будет заменено на 123. Так же и в Verilog HDL есть макросы, которые позволяют авто подстановку текста перед компиляцией. В верилог можно написать

`define MAGIC_NUMBER 123

в коде программы Verilog для макроподстановки нужно писать `MAGIC_NUMBER - везде этот макрос будет заменен на 123. Не буду здесь углубляться в тонкости синтаксиса (хотя они есть). Просто скажу, что макросы могут быть сложными, многострочными и принимать параметры. Воспользовавшись этими знаниями я написал несколько макросов Verilog для своего псевдо языка программирования Verilog State Machine Framework.

Вот сразу посмотрите на такой код:

`xprogram(64);
`xstep;
`xstep;
`xstep;
`xstep;
....

И этот макро код будет развернут препроцессором компилятора Verilog HDL и будет представлять примерно такую схему:

xprogram VSMF

На самом деле схема будет немного сложнее, но для простоты понимания я нарисовал ее вот так.

Представьте себе, что макрос `xprogram() создает самый первый регистр машины состояний RS0, тот, который после сброса содержит начальную "единичку". Каждый следующий макрос `xstep автоматически(!) создает последовательно соединенные регистры RS1, RS2, RS3, RS4... В момент сброса сигналом reset все регистры состояния начиная с RS1 будут содержать ноль. После того, как сигнал reset будет снят, "единица" из регистра RS0 начнет свое путешествие по регистрам RS1, RS2, RS3 и так далее с каждым импульсом тактовой частоты. Таким образом, используя вот только эти два макроса уже можно получить несколько поочередных состояний и специально их именовать не нужно.

Теперь, между строк с макросами `xstep можно вписывать какие-то строки, описывающие полезные действия, выполняемые именно на этом шаге. Имя регистра текущего состояния описывается макросом `R. Каждая новый вызов макроса `xstep переопределяет макрос `R. Если хотите, то можно выполнить какое нибудь действие прямо на нужном шаге программы как-то вот так:

`xprogram(64);
`xstep;
`xstep;
`xstep;

reg A;
always @(posedge clk)
if( `R ) A <= .....

`xstep;
....

Здесь присвоение нового значения в регистр A будет происходить именно на шаге программы RS3. На мой взгляд это уже интересно и как-то облегчает жизнь разработчика, но хочется пойти дальше.

Я ввожу еще один макрос `xvar(name,numbits). Этот макрос объявляет специальную переменную для всей программы.
Почему нужна особая переменная, почему не использовать обычный reg языка Verilog? На самом деле обычный регистр reg вполне подходит, но проблема состоит в том, что присвоение разных значений в регистр при разных условиях требует мультиплексоров. Описание этих мультиплексоров (в Verilog они описываются с помощью if-else или case-endcase) - это не простое занятие при описании сложных машин состояний.

Мой макрос `xvar позволяет автоматизировать процесс присвоения значений в регистр во время разных состояний. То есть, мультиплекросы сами собой будут созданы макросом.

Ну и естественно, нужен еще макрос для присвоения значений в переменную. Это макрос `xset(var,new_value).
Теперь мой псевдо язык позволит написать что-то вот такое:

`xprogram(64);
`xvar(counter,16);
`xstep;
`xstep;
`xset(counter,0);
`xstep;
`xstep;
`xset(counter,counter+1);
`xstep;

Здесь в объявленную переменную "counter" присвоение идет из двух мест программы, из двух разных состояний.
В состоянии RS2 в counter будет записан ноль, а во время состояния RS4 значение counter будет увеличено на единицу.

Если не пользоваться макросами VSMF, придется писать примерно вот такой Verilog код:

reg [15:0]counter;
always @(posedge clk)
  if(RS2)
    counter <= 0;
  else
  if(RS4)
    counter <= counter+1;

В этом коде все прекрасно, но при описании сложных машин состояний, когда точно еще не определился с количеством состояний, с их именами, да еще до конца не уверен в какой момент какое действие выполняется - такие конструкции приходится многократно переписывать.

Мне кажется, что описание программы макросами VSMF будет проще.

Хотелось бы отметить, что на одном шаге программы можно делать несколько присвоений в разные переменные, но нельзя делать несколько присвоений в один регистр. Ну вот такой у меня язык программирования. Вот так можно написать:

`xprogram(64);
  `xvar(counter,16);
  `xvar(xxx,8);
`xstep;
`xstep;
  `xset(counter,counter+1);
  `xset(xxx,(xxx<<1) | 8`h01 );
`xstep;
`xstep;
...

К сожалению, я не смог придумать абсолютно элегантного способа представления и создания переменнных с помощью макросов Verilog. Макрос `xvar зараннее генерирует особый регистр для каждого(!) шага программы, а потом ообъединяет их логическим "ИЛИ". При этом появляется избыточное количество объявленных регистров с первоначальным значением ноль, и уже компилятор Verilog потом оптимизируя схему устройства выбрасывает всю избыточную логику. При этом, возникает необходимость зараннее объявлять максимально возможное число шагов в программе в макросе `xprogram(max_num_steps). Не очень удобно, но хоть так.

Идем дальше.
Для нормальных программ нужна возможность условного исполнения и условных переходов - читай переходов между состояниями.

С условным исполнением просто. Вводим макрос `xset_if( variable, value, condition ). В переменную variable будет присвоено значение value, если condition не равен нулю.

С условными переходами несколько сложнее.
Во-первых, нужно как-то обозначить место или состояние куда переходить. Макрос `xstep не именует состояние. Поэтому добавляю специальный макрос `xstate(label). Этот макрос не только создает регистр для хранения состояния, но и делает возможным переход в это состояние из других участков программы.

xstate

Макрос `xstate создает регистр у которого на входе данных есть многовходовый элемент ИЛИ для приема "единицы" состояния не только от предыдущего шага, но и из произвольного места в программе.

Макрос `xgoto(label) передает "единицу" не следующему шагу, а указанному в параметре состоянию.
Соответственно макрос `xgoto_if(label,condition) осуществляет условный переход к нужному состоянию в программе.

Давайте теперь сделаем очень простой проект для платы Марсоход3, который будет демонстрировать возможности псевдо языка VSMF. Опять будем использовать только кнопочки и светодиодики - исключительно с целью сделать простой и понятный проект.

project top module ALtera Quartus II
К модулю simple_sm идут сигналы тактовой частоты clk, сигнал сброса reset, сигналы от кнопочек. Из модуля simple_sm идет восьмибитная шина на светодиоды платы.

Код машины состояний выглядит вот так:

module simple_sm(
  input wire clk,
  input wire reset,
  input wire key0,
  input wire key1,
  output wire [7:0]leds
);

`include "vsmf.v"

`xprogram(64);
`xvar(leds_reg, 8);
`xvar(counter, 32);
`xstep;
`xstep;
`xstep;
  `xset(leds_reg,1);
`xstep;
`xstate(start);
  `xset( counter, 0 );
`xstate(label_cycle);
  `xset(counter, (counter + 1) );
`xstep;
  `xgoto_if(label_cycle,(counter<200000));
`xstep;
  `xgoto_if(shift_right,(key0==0));
`xstep;
  `xset(leds_reg, {leds_reg[6:0], leds_reg[7]} );
  `xgoto(start);
`xstate(shift_right);
  `xset(leds_reg, {leds_reg[0],leds_reg[7:1]} );
  `xgoto(start);

assign leds = leds_reg;

endmodule

Программа делает следующее: горит один светодиодик из восьми и перемещается по кругу влево. Если нажать кнопочку key0, то светодиодик начинает перемещаться вправо.

Чтобы пояснить программу я раскрасил ее в разные цвета.
Текст выделенный красным показывает внутренний цикл по счетчику counter. Здесь реализована обычная программная задержка, как ее обычно пишут в микроконтроллерах, всяких ардуинах и т.д. От этой задержки зависит как быстро будет моргать светодиодик.

Синим цветом показаны присвоения в переменную leds_reg из разных состояний FSM.

Так же обратите внимание, что переход на одну метку программы start может происходить из разных мест программы. У меня одно состояние без имени делает циклический сдвиг содержимого регистра leds_reg влево, а состояние shift_right сдвигает содержимое этого же регистра вправо.

Таким образом, использование макросов VSMF позволяет использовать Verilog HDL для описания последовательно исполняемых программ.

Надеюсь Verilog State Machine Framework покажется вам интересной идеей и вы сможете использовать его в своих проектах.

Здесь можно скачать весь проект для Altera Quartus II для платы Марсоход3: 

  Здесь в архиве есть все необходимые файлы.

Видеодемонстрация проекта в плате Марсоход3:

Снимал видео в довольно темном месте, чтоб хоть чуть чуть было видно бегающий огонек светодиода. Из-за этого видео немного шумит. Так получилось...

 

Комментарии  

0 #19 valery_17 30.11.2016 09:33
О чудо! Батник сгенерировал правильные файлы и все прошло чисто, спасибо большое.
+1 #18 Leka 29.11.2016 10:58
https://marsohod.org/forum/proekty-polzovatelej/4045-sistema-upravleniya-sinkhronnym-mnogofaznym-elektrod?start=90#5143
0 #17 Leka 29.11.2016 10:54
Как сам делаю, можно посмотреть в проектах, которые выкладывал, например m16.
+2 #16 Leka 29.11.2016 10:52
Я не создаю новый проект в Квартусе.
У меня топ-модуль всегда именован top. Создаю новую папку, копирую *.v, top.qsf, top.qpf(если нужна иконка для заруска Квартуса). Правлю в исходниках имя топового модуля на top. Правлю в редакторе top.qsf, выбрасывая все лишнее, и оставляя только необходимый минимум (это до 13.1, в старших версиях м/б другой файл, не помню).
0 #15 valery_17 29.11.2016 10:32
Я тоже в исходном проекте удалил все, оставил только верилоговский модуль, и он прошел также без ошибок. Но
если задавать новый проект с нуля в том же самом квартусе, такой же верилоговский проект, то ошибки есть и много.Такое ощущение что при создании проекта нужно какие-то насторойки регулировать.
0 #14 Leka 29.11.2016 09:38
Пропустил через IcarusVerilog, удалил пустые строки, скормил это Квартусу 13.1 - ошибок не было.
Никаких графических топовых модулей.
0 #13 valery_17 28.11.2016 13:32
Предупреждение по параллелньой компиляции ни причем, но почему то с точно таким же модулем simple_sm.v в качестве топового, и 2-х включенных vsmf.v и stepdef.v- так у меня построен проект, выходят пресловутые ошибки, ссылающиеся на макросы. Неужели необходимо создавать топовый графический модуль?
0 #12 valery_17 28.11.2016 11:15
В quartus 15.1 данный проект компилируется без ошибок.
Но когда создаю свой проект- верилоговский файл в качестве топ модуля, аналогичный описанному в этой статье, выходит предупреждение:
20028 Parallel compilation is not licensed and has been disabled
и за ним те же ошибки что были раньше.
0 #11 valery_17 26.11.2016 17:14
Мне макросы надо будет глубже изучить да и вспомогательные среды, еще раз спасибо за помощь.
0 #10 Leka 26.11.2016 14:28
Еще вариант - переписать макросы под препроцессор Си (и делать вывод в файл *.v)
Чтобы отлаживать дизайн со сложными макросами, надо видеть текст после препроцессора.
0 #9 Leka 26.11.2016 14:21
После "\" не д/б пробелов (и других невидимых символов), сталкивался с тем, что некоторые препроцессоры неправильно работают в этом случае.
Но в любом случае очень желательно иметь вывод препроцессора в файл, и уже этот файл скармливать Квартусу - проще будет локализовывать ошибки синтеза по номеру строки.
0 #8 valery_17 26.11.2016 13:18
Спасибо за вариант, пока буду думать над тем как это запустить в самом квартусе без использования дополнительных программ. Вот если вытащить строчку
`include "vsmf.v" в самое начало, до описания модуля остается 1 непонятная ошибка:
near text "generate"; expecting a description
хотя там вроде правильно все написано.
0 #7 Leka 26.11.2016 11:28
Для:
...  `xset(leds_reg, {leds_reg[6:0], leds_reg[7]} );
...  `xset(leds_reg, {leds_reg[0],le ds_reg[7:1]} );
надо заключить фигурные скобки в круглые:
...  `xset(leds_reg, ({leds_reg[6:0] , leds_reg[7]}) );
...  `xset(leds_reg, ({leds_reg[0],le ds_reg[7:1]}) );
Тогда последняя версия IcarusVerilog без ошибок дает вывод препроцессора с опцией -E.
Выходной файл препроцессора IcarusVerilog можно попробовать дать Квартусу для синтеза (сам еще не пробовал).
0 #6 valery_17 25.11.2016 10:32
Спасибо большое за статью, и идею, хотелось бы использовать в микро проектах, но пока столкнулся с проблемой:
пробовал запускать в quartus 11.1, на этапе сборки проекта ругается на превышение количества вложенных ifndef- более 50, уменьшил до 44, уже другая ошибка:
10108 missing Compiler Directive, и др. синтаксические ошибки 10170 и т.д.
пробовал в quartus 15/64bit, также куча синтактических ошибок, хотя в файлах с макросами ничего не менял.
Кто нибудь еще пробовал были такие проблемы?, или у меня что-то не так...
0 #5 nckm 26.10.2015 17:26
Цитирую ilya.ryasanov:
А как насчет SystemVerilog ? Там есть возможность определить свои типы данных состояния для конечного автомата

Как мне кажется SystemVerilog пока не очень распространен. Хотелось сделать как можно проще..
0 #4 nckm 26.10.2015 17:24
Цитирую Leka:
Язык макросов забыл напрочь (лет 5 назад делал подобное), в Квартусе можно увидеть Верилоговский код после препроцессора? Раньше для этих целей использовал, кажется, IcarusVerilog, но он не понимает многие конструкции SV.

сам искал параметры командной строки для quartus_map чтобы посмотреть текст после препроцессора и не нашел.. У icarus verilog опция -E. Но к сожалению, этот проект нельзя использовать с icarus - не компилирует двойной астериск. Нашел подходящие конструкции для икаруса, но тогда их квартус не понимает. Просто беда.
0 #3 Leka 26.10.2015 10:26
Язык макросов забыл напрочь (лет 5 назад делал подобное), в Квартусе можно увидеть Верилоговский код после препроцессора? Раньше для этих целей использовал, кажется, IcarusVerilog, но он не понимает многие конструкции SV.
0 #2 ilya.ryasanov 26.10.2015 02:39
А как насчет SystemVerilog ? Там есть возможность определить свои типы данных состояния для конечного автомата
0 #1 Leka 25.10.2015 23:44
Макросами многое можно закодировать. Но использование макросов оправдано, если цель - попытаться привлечь энтузиастов полностью открытым проектом, для которого не надо изучать/изобрет ать другой язык. Для дела и/или внутреннего использования удобнее использовать какой-либо свой препроцессор, но тогда совсем нет шансов привлечь энтузиастов. Имхо.

Добавить комментарий


Защитный код
Обновить


GitHub YouTube Twitter
Вы здесь: Начало Проекты Проект Марсоход3 Verilog State Machine Framework