FPGA 中,基于时序编写状态机,可以完成几乎所有的逻辑控制任务。状态机就是用来管理电路的逻辑状态。
状态机通常有三种:一段式、二段式、三段式。可以写出四段式,五段式,但一般来说,常用的不超过三段。写成几段看实际情况,一般很简单的状态机写一段式,稍复杂的用二段式,特别复杂的用三段式。
1. 一段式状态机
module detect_1(
input clk_i,
input rst_n_i,
output out_o
);
reg out_r;
//状态声明和状态编码
reg [1:0] state;
parameter [1:0] S0=2'b00;
parameter [1:0] S1=2'b01;
parameter [1:0] S2=2'b10;
parameter [1:0] S3=2'b11;
always@(posedge clk_i)
begin
if(!rst_n_i)begin
state<=0;
out_r<=1'b0;
end
else
case(state)
S0 :
begin
out_r<=1'b0;
state<= S1;
end
S1 :
begin
out_r<=1'b1;
state<= S2;
end
S2 :
begin
out_r<=1'b0;
state<= S3;
end
S3 :
begin
out_r<=1'b1;
end
endcase
end
assign out_o=out_r;
endmodule
通常来说一段式状态机不推荐,但是简单的代码没必要严格使用多段式。简单状态机是可以用的。
2. 二段式状态机
...
//时序逻辑:描述状态转换
always@(posedge clk_i)
begin
if(!rst_n_i)
Current_state<=0;
else
Current_state<=Next_state;
end
//组合逻辑:描述下一状态和输出
always@(*)
begin
case(Current_state)
S0 :
begin
out_r=1'b0;
Next_state= S1;
end
S1 :
begin
out_r=1'b1;
Next_state= S2;
end
S2 :
begin
out_r=1'b0;
Next_state= S3;
end
S3 :
begin
out_r=1'b1;
Next_state=Next_state;
end
endcase
end
assign out_o=out_r;
...
两段式状态机采用两个 always 块实现状态机的功能,一个 always 采用同步时序逻辑描述状态转移,另一个 always 采用组合逻辑来判断状态条件。
推荐使用至少两段式的状态机。
3. 三段式状态机
...
//时序逻辑:描述状态转换
always@(posedge clk_i)
begin
if(!rst_n_i)
Current_state<=0;
else
Current_state<=Next_state;
end
//组合逻辑:描述下一状态
always@(*)
begin
case(Current_state)
S0:
Next_state = S1;
S1:
Next_state = S2;
S2:
Next_state = S3;
S3:
Next_state = Next_state;
default :
Next_state = S0;
endcase
end
//输出逻辑:让输出 out,经过寄存器 out_r 锁存后输出,消除毛刺
always@(*)
begin
case(Current_state)
S0,S2:
out_r<=1'b0;
S1,S3:
out_r<=1'b1;
default :
out_r<=out_r;
endcase
end
assign out_o=out_r;
...
三段式状态机有3个always块,第一个同步时序逻辑描述状态转移,第二个采用组合逻辑描述状态转移条件和规律,第三个 描述电路的输出。
一般使输出信号经过寄存器缓存之后再输出,消除电路毛刺。这是比较推荐的形式。
对比三种状态机的写法和原理图,三段式是最简洁高效的形式,两段式次之,一段式最繁杂。因此一般推荐使用两段式以上的状态机。