Verilog三段式状态机的写法,标准示例和仿真。
第一段:同步状态转移。第一个always块格式化描述次态寄存器迁移到现态寄存器。
第二段:当前状态判断接下来的状态。组合逻辑always模块,描述状态转移条件判断用current_state
第三段:次态描述输出。同步时序always模块,描述次态寄存器输出(有误,见下一篇文章)
注意:
三段式并不是一定要写为3个always块,如果状态机更复杂,就不止3段了。
(1)fsm.v文件:
module fsm(//go,ws,clk,rst_n,rd,ds);
input go,
input ws,
input clk,
input rst_n,
output reg rd,
output reg ds,
output reg [1:0] current_state,
output reg [1:0] next_state,
output reg [3:0] led
);
//reg rd,ds;
parameter [1:0] IDLE = 2’b00;
parameter [1:0] READ = 2’b01;
parameter [1:0] DLY = 2’b10;
parameter [1:0] DONE = 2’b11;
//reg [1:0] current_state,next_state;
//next state logic
always @(posedge clk or negedge rst_n)begin
if(!rst_n) current_state <= IDLE;
else current_state <= next_state;//
end
//state register
always @(current_state or go or ws)begin
next_state = 2’bx;
case(current_state)
IDLE : if(go) next_state = READ;
else next_state = IDLE;
READ : next_state = DLY;
DLY : if(ws) next_state = READ;
else next_state = DONE;
DONE : next_state = IDLE;
endcase
end
//Output logic
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rd <= 1’b0;
ds <= 1’b0;
end
else begin
rd <= 1’b0;
ds <= 1’b0;
led <= 4’b0000;
case(next_state)//注意是next_state.
IDLE: if(go) begin rd <= 1’b1; led <= 4’b0001; end
READ: begin rd <= 1’b1; led <= 4’b0010; end
DLY: if(ws) begin rd <= 1’b1; led <= 4’b0100; end
else begin ds <= 1’b1; led <= 4’b1000; end
DONE: begin ds <= 1’b1; led <= 4’b1000; end
endcase
end
end
Endmodule
(2)fsm_test.v文件:
`timescale 1ns/1ps
module fsm_test();
reg clk;
reg rst_n;
reg go;
reg ws;
wire rd;
wire ds;
wire [3:0] led;
wire [1:0] current_state;
wire [1:0] next_state;
fsm u1(
.go(go),
.ws(ws),
.clk(clk),
.rst_n(rst_n),
.rd(rd),
.ds(ds),
.current_state(current_state),
.next_state(next_state),
.led(led)
);
initial begin
clk =1’b0;
rst_n = 1’b1;
#50 rst_n =1’b0;
#50 rst_n =1’b1;
go = 1’b0;
ws = 1’b0;
#100 go = 1’b1;
#2000 $stop;//仿真2000ns后停止。
end
always #25 clk = ~clk;
Endmodule
仿真波形如下:
状态和输出对得上。需要注意第三个always块里的state用的是next_state.
原问题:当第三段使用current_state判断时,状态和输出对不上。改为next_state正常。
仿真发现状态跳转不对: