时序电路的状态是一个状态变量集合,这些状态变量在任意时刻的值都包含了为确定电路的未来行为而必需考虑的所有历史信息。
状态机采用VerilogHDL语言编码,建议分为三个always段完成。
三段式建模描述FSM的状态机输出时,只需指定case敏感表为次态寄存器, 然后直接在每个次态的case分支中描述该状态的输出即可,不用考虑状态转移条件。
三段式描述方法虽然代码结构复杂了一些,但是换来的优势是使FSM做到了同步寄存器输出,消除了组合逻辑输出的不稳定与毛刺的隐患,而且更利于时序路径分组,一般来说在FPGA/CPLD等可编程逻辑器件上的综合与布局布线效果更佳。
示列如下:
//第一个进程,同步时序always模块,格式化描述次态寄存器迁移到现态寄存器
always @ (posedge clk or negedge rst_n) //异步复位
if(!rst_n)
current_state <= IDLE;
else
current_state <= next_state;//注意,使用的是非阻塞赋值
//第二个进程,组合逻辑always模块,描述状态转移条件判断
always @ (current_state) //电平触发
begin
next_state = x; //要初始化,使得系统复位后能进入正确的状态
case(current_state)
S1: if(...)
next_state = S2; //阻塞赋值
...
endcase
end
//第三个进程,同步时序always模块,格式化描述次态寄存器输出
always @ (posedge clk or negedge rst_n)
begin
...//初始化
case(next_state)
S1:
out1 <= 1; //注意是非阻塞逻辑
S2:
out2 <= 1;
default:... //default的作用是免除综合工具综合出锁存器。
endcase
end
另附一交通灯状态机实例[转]:
module FSM_3always(out,clock,clk,rst);
output [5:0] out;
output clock;
input clk,rst;
reg [4:0] L_time;
wire clock;
reg [5:0] out;
reg [3:0] CS,NS;
parameter [3:0] //独热码且有零初始状态;
IDLE = 4'b0000,
S1 = 4'b0001,
S2 = 4'b0010,
S3 = 4'b0100,
S4 = 4'b1000;
fenpin fenpin_10(clock,clk,rst);//分频模块调用;
always @ (posedge clock or negedge rst) //时序模块描述状态转移
begin
if(!rst)
CS <= IDLE;
else
CS <= NS;
end
always @ (CS or L_time or rst) //组合模块描述状态转且电平敏感表必须列完整,否则会产生隐含锁存器
begin
NS = 4'bx;
case(CS)
IDLE:begin
if(!rst) NS = IDLE;
else NS = S1;
end
S1: begin
if(L_time==0) NS = S2;
end
S2: begin
if(L_time==0) NS = S3;
end
S3: begin
if(L_time==0) NS = S4;
end
S4: begin
if(L_time==0) NS = S1;
end
default:NS = IDLE;
endcase
end
always @ (posedge clock or negedge rst)
begin
if(!rst)
out <= 6'b010010;
else
begin
case(NS)
IDLE: out <= 6'b010010;
S1: out <= 6'b001100;
S2: out <= 6'b010100;
S3: out <= 6'b100001;
S4: out <= 6'b100010;
default:out <= 6'b010010;
endcase
end
end
always @ (posedge clock or negedge rst) //用于L_time的计数和复位;
begin
if(!rst)
L_time <= 5'bx;
else
begin
L_time <= L_time - 1;
case(CS)
S1: if(L_time==0) L_time <= 5'd2;
S2: if(L_time==0) L_time <= 5'd28;
S3: if(L_time==0) L_time <= 5'd2;
S4: if(L_time==0) L_time <= 5'd18;
default:L_time <= 5'd18; //工作之前也就是S1状态到来之前不断的赋初值
endcase
end
end
endmodule