FPGA. Конечные автоматы

В предыдущей статье FPGA. Чтоб работало как часы я немного поэкспериментировал с тактированием программируемых логических интегральных схем, в данной статье собираюсь немного рассказать о машинах состояний (state machines), по-русски в технической литературе называемых конечными автоматами.

FPGA как конечный автомат

При знакомстве с программируемыми логическими интегральными схемами сложно не заметить того, что от микроконтроллеров и ряда других цифровых микросхем, FPGA отличает отсутствие вывода nRESET, то есть сама микросхема никак HDL-программисту не сообщает, что её состояние например "микросхеме только что подали питание". А ведь более-менее сложная логическая схема предполагает стадии предварительной инициализации модулей, поскольку сами по себе модули, из которых состоит схема устройства, ничего не знают о других модулях и их состояниях. В результате получается, что сразу после подачи питания схема оказывается в одном из возможных вариантов её состояния, что является эффектом нежелательным, поэтому этот процесс так или иначе разработчику приходится упорядочивать.

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

Вот, например, как выглядит диаграмма состояний устройства, которое включается и выключается подачей и отключением питания:

Пример диаграммы состояний конечного автомата

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

Общая диаграмма состояний сбрасываемого конечного автомата

На языке Verilog это выглядит следующим образом:

module SomeModule(cklIN, nResetIN, nResetOUT);

input clkIN, nResetIN;

output nResetOUT;


reg nResetOUT = 1'b0;


always @ (posedge clkIN)

if (~nResetIN)

nResetOUT <= 1'b0;

else

nResetOUT <= 1'b1;


endmodule

D-триггер как пример конечного автомата

Простым примером конечного автомата мог быть стать например светодиод, включаемый и выключаемый по нажатию кнопки. Для этого я написал модуль, который назвал LEDStateMachine (скачать):

module LEDStateMachine (clkIN, nResetIN, nToggleIN, stateOUT);

input clkIN, nResetIN, nToggleIN;

output [3:0] stateOUT;


`define RESET_LOW 4'b0000

`define RESET_HIGH 4'b0001

`define TOGGLED_HIGH 4'b0010

`define TOGGLED_LOW 4'b0100

reg [3:0] stateOUT;

reg [3:0] nextToggleState;


always @ (posedge clkIN)

if (~nResetIN) begin

stateOUT <= `RESET_LOW;

nextToggleState <= `TOGGLED_HIGH;

end

else case(stateOUT)

`RESET_LOW: begin

stateOUT <= `RESET_HIGH;

end

`RESET_HIGH: begin

if (nToggleIN)

stateOUT <= `RESET_HIGH;

else

stateOUT <= nextToggleState;

end

`TOGGLED_HIGH: begin

nextToggleState <= `TOGGLED_LOW;

if (nToggleIN)

stateOUT <= `RESET_HIGH;

end

`TOGGLED_LOW: begin

nextToggleState <= `TOGGLED_HIGH;

if (nToggleIN)

stateOUT <= `RESET_HIGH;

end

endcase

endmodule

Диаграмма состояний этого конечного автомата упрощённо выглядит следующим образом:

Диаграмма состояний конечного автомата LEDStateMachine

Регистр nextToggleState хранит индекс следующего состояния, в которое конечный автомат должен перейти из состояния RESET_HIGH по переднему фронту сигнала nToggleIN.

Блок-схема устройства получилась следующей (скачать BDF):

Блок-схема конечного автомата LEDStateMachine

Перечитав эту статью, я спросил себя, а где же в этом Verilog-файле заявленный светодиод. Поскольку на моей плате от LDM-Systems светодиодов 4 штуки, то для данного примера я сконфигурировал модули таким образом, чтобы текущее состояние конечного автомата наглядно отображалось. В результате получилось так, что при смене состояния меняют своё состояние целых два бита - state[1] и state[2], причём полярно - подобно выводам Q и nQ логического элемента D-триггер.

Поэтому я решил, что в модуле явно не хватает регистра, который таки переключал бы именно эти выводы, то есть чтобы конечный автомат LEDStateMachine выполнял бы свою функцию как задумано. Дополненная версия LEDStateMachine1 выглядит следующим образом (скачать):

module LEDStateMachine1 (clkIN, nResetIN, nToggleIN, stateOUT, qOUT);

input clkIN, nResetIN, nToggleIN;

output [3:0] stateOUT;

output [1:0] qOUT;


`define RESET_LOW 4'b0000

`define RESET_HIGH 4'b0001

`define TOGGLED_HIGH 4'b0010

`define TOGGLED_LOW 4'b0100

reg [3:0] stateOUT;

reg [3:0] nextToggleState;

reg [1:0] qOUT;


always @ (posedge clkIN)

if (~nResetIN) begin

stateOUT <= `RESET_LOW;

nextToggleState <= `TOGGLED_HIGH;

qOUT <= 2'b00;

end

else case(stateOUT)

`RESET_LOW: begin

stateOUT <= `RESET_HIGH;

qOUT <= 2'b00;

end

`RESET_HIGH: begin

if (nToggleIN)

stateOUT <= `RESET_HIGH;

else

stateOUT <= nextToggleState;

end

`TOGGLED_HIGH: begin

qOUT <= 2'b01;

nextToggleState <= `TOGGLED_LOW;

if (nToggleIN)

stateOUT <= `RESET_HIGH;

end

`TOGGLED_LOW: begin

qOUT <= 2'b10;

nextToggleState <= `TOGGLED_HIGH;

if (nToggleIN)

stateOUT <= `RESET_HIGH;

end

endcase


endmodule

Автор: Андрей Шаройко <vanyamboe@gmail.com>