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
Диаграмма состояний этого конечного автомата упрощённо выглядит следующим образом:
Регистр nextToggleState хранит индекс следующего состояния, в которое конечный автомат должен перейти из состояния RESET_HIGH по переднему фронту сигнала nToggleIN.
Блок-схема устройства получилась следующей (скачать BDF):
Перечитав эту статью, я спросил себя, а где же в этом 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>