以前我写状态机都是一段式状态机,虽然目前因为项目都很小,所以一段式没觉得有什么不对劲的,但是为了以后的需要,还是转向三段式状态机吧。
三段式是哪三段呢?
1.将“新状态”赋给“当前状态”,非阻塞赋值
2.产生下一个“新状态”(可能包含组合逻辑),阻塞赋值,激励条件为当前状态
3.根据“当前状态”产生输出(可能包含组合逻辑),非阻塞赋值
以一个只有两个状态的路口红绿灯为例写一个三段式状态机:
要用到的重要寄存器:
parameter s1 = 1'b0, s2 = 1'b1; //第一个状态red1和blue2亮,第二个状态red2和blue1亮
output reg state_cur; //现在状态
output reg state_next; //下一个状态
output reg [3:0] cnt; //计数器到10
reg cnt_flag; //计时器计满指示
三段式因为有当前状态和新状态之分,所以要两个寄存器来分别存储,因为红绿灯要使用计数器控制时间,故设置了计数器
第一段:将“新状态”赋给“当前状态”,非阻塞赋值
always@(posedge clk or negedge rst_n) //第一段为单纯次态寄存器迁移到现态寄存器,改变现在状态
begin
if(!rst_n)
state_cur <= s1;
else
state_cur <= state_next;
end
第二段:产生下一个“新状态”,阻塞赋值,注意激励条件
always@(state_cur or rst_n or cnt_flag) //第二段为状态转移条件判断,改变下一个状态
begin
if(!rst_n)
state_next = s2;
else
begin
case(state_cur)
s1:begin
if(cnt_flag)
state_next = s2;
else
state_next = s1;
end
s2:begin
if(cnt_flag)
state_next = s1;
else
state_next = s2;
end
default:state_next = s1;
endcase
end
end
注意激励条件不再是时钟上升沿,否则会出现一直改变状态的死循环错误
第三段:根据“当前状态”产生输出
always@(posedge clk or negedge rst_n) //第三段为现在状态和输出的关系
begin
if(!rst_n)
begin
red1 = 0;
blue1 = 0;
red2 = 0;
blue2 = 0;
end
else
begin
case(state_cur)
s1:begin
red1 = 1;
blue1 = 0;
red2 = 0;
blue2 = 1;
end
s2:begin
red1 = 0;
blue1 = 1;
red2 = 1;
blue2 = 0;
end
default:
begin
red1 = 0;
blue1 = 0;
red2 = 0;
blue2 = 0;
end
endcase
end
end