状态机,是我大二在实验室学习时,学长给我们安排的任务之一。那个时候,我还没有学过《数电》,一直搞不明白状态机的意思,做了一周,都没有结果。
任务是检测序列,检测到1011时输出一个高电平。
我们都知道verilog语言依靠不同的always语句块实现了硬件电路的并行执行,但是在工程中,我们不仅要处理并行执行电路,偶尔也会遇到需要串行执行的电路要求。刚开始学习FPGA的话,可能会想到我们可以利用很多很多的使能信号实现,但是这样维护的成本大大增加。状态机就可以完美的实现这一功能。先简单介绍一下状态机的基本概念。
状态机的基本要素是输入、输出和状态。输入是引起状态变化的条件,输出是状态变化引起的变化。状态就是字面理解的意思了,状态机,通俗的说就是因为输入导致状态在不断的变化的硬件电路。
Moore型状态机的输出仅与状态有关,与输入无关。
Mealy型状态机的输入不仅与状态有关,还与输入条件有关。
一段式写法,输入、输出和状态在一个always语句块中,后期不容易维护。
module pro(
input clk,
input rst_n,
input data,
output cntout
);
parameter INITIALSTATE = 5'b00_001,
STATE_0 = 5'b00_010,
STATE_1 = 5'b00_100,
STATE_2 = 5'b01_000,
STATE_3 = 5'b10_000;
//————————————————————————————————————————————
//一段式写法, 既把输入、输出和状态写在一个always语句块
reg[4:0] state_ce; //状态寄存器
reg cnt;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt <= 1'b0;
state_ce <= INITIALSTATE;
end
else
case(state_ce)
INITIALSTATE: begin
cnt <= 1'b0;
if(data == 1'b1)
state_ce <= STATE_0;
else
state_ce <= INITIALSTATE;
end
STATE_0: if(data == 1'b0)
state_ce <= STATE_1;
else
state_ce <= STATE_0;
STATE_1: if(data == 1'b1)
state_ce <= STATE_2;
else
state_ce <= INITIALSTATE;
STATE_2: if(data == 1'b1)
state_ce <= STATE_3;
else
state_ce <= STATE_1;
STATE_3: begin
state_ce <= INITIALSTATE;
cnt <= 1'b1;
end
endcase
end
assign cntout = cnt;
endmodule
二段式写法,分成了组合逻辑和时序逻辑。时序逻辑里作状态的转移,组合逻辑里作输入条件判断和输出。但是组合逻辑输出容易出现毛刺问题。
module pro(
input clk,
input rst_n,
input data,
output cntout
);
parameter INITIALSTATE = 5'b00_001,
STATE_0 = 5'b00_010,
STATE_1 = 5'b00_100,
STATE_2 = 5'b01_000,
STATE_3 = 5'b10_000;
reg[4:0] state_ce;
reg[4:0] state_nt;
reg cnt;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
state_ce <= INITIALSTATE;
else
state_ce <= state_nt;
end
always @ (state_ce or data) begin
case(state_ce)
INITIALSTATE: begin
cnt = 1'b0;
if(data == 1'b1)
state_nt = STATE_0;
else
state_nt = INITIALSTATE;
end
STATE_0: begin
cnt = 1'b0;
if(data == 1'b0)
state_nt = STATE_1;
else
state_nt = STATE_0;
end
STATE_1: begin
cnt = 1'b0;
if(data == 1'b1)
state_nt = STATE_2;
else
state_nt = INITIALSTATE;
end
STATE_2: begin
if(data == 1'b1)
state_nt= STATE_3;
else
state_nt =STATE_1;
end
STATE_3: begin
cnt = 1'b1;
state_nt = INITIALSTATE;
end
endcase
end
assign cntout = cnt;
endmodule
三段式写法,又将输出写作时序逻辑输出,解决了二段式中毛刺的问题。
module pro(
input clk,
input rst_n,
input data,
output cntout
);
parameter INITIALSTATE = 5'b00_001,
STATE_0 = 5'b00_010,
STATE_1 = 5'b00_100,
STATE_2 = 5'b01_000,
STATE_3 = 5'b10_000;
reg[4:0] state_ce;
reg[4:0] state_nt;
reg cnt;
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
state_ce <= INITIALSTATE;
else
state_ce <= state_nt;
end
always @ (state_ce or data) begin
case(state_ce)
INITIALSTATE: if(data == 1'b1)
state_nt = STATE_0;
else
state_nt = INITIALSTATE;
STATE_0: if(data == 1'b0)
state_nt = STATE_1;
else
state_nt = STATE_0;
STATE_1: if(data == 1'b1)
state_nt = STATE_2;
else
state_nt = INITIALSTATE;
STATE_2: if(data == 1'b1)
state_nt = STATE_3;
else
state_nt = STATE_2;
STATE_3: state_nt = INITIALSTATE;
endcase
end
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
cnt <= 'd0;
else if(state_nt == STATE_3)
cnt <= 1'b1;
else
cnt <= 1'b0;
end
assign cntout = cnt;
endmodule