Verilog三段式狀态機的寫法,标準示例和仿真。
第一段:同步狀态轉移。第一個always塊格式化描述次态寄存器遷移到現态寄存器。
第二段:目前狀态判斷接下來的狀态。組合邏輯always子產品,描述狀态轉移條件判斷用current_state
第三段:次态描述輸出。同步時序always子產品,描述次态寄存器輸出(有誤,見下一篇文章)
注意:
三段式并不是一定要寫為3個always塊,如果狀态機更複雜,就不止3段了。
- 三段always子產品中,第一個和第三個always子產品是同步時序always子產品,用非阻塞指派(“ <= ”);第二個always子產品是組合邏輯always子產品,用阻塞指派(“ = ”)。
- 第二部分為組合邏輯always子產品,為了抑制warning資訊,對于always的敏感清單建議采用always@(*)的方式。
- 第二部分,組合邏輯always子產品,裡面判斷條件一定要包含所有情況!可以用else保證包含完全。
- 第二部分,組合邏輯電平要維持超過一個clock,仿真時注意。
- 需要注意:第二部分case中的條件應該為目前态(current_state)。
- 第三部分case中的條件應該為次态(next_state)。
-
編碼原則,binary和gray-code适用于觸發器資源較少,組合電路資源豐富的情況(CPLD),對于FPGA,适用one-hot code。這樣不但充分利用FPGA豐富的觸發器資源,還因為隻需比較一個bit,速度快,組合電路簡單。
示例1. 狀态轉移圖:
(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正常。
仿真發現狀态跳轉不對: