前言
FSM是电路设计中非常常见的结构,对于这样常见的结构的正确输写非常重要, 良好的代码风格对于阅读理解以及后期维护也非常重要。本文总结了目前常见的两种输写FSM的方法
介绍
FSM目前主要有两种架构
- Moore FSM
- Mealy FSM
要让电路顺序地执行计算,最简单的方法就是生成一个counter, 然后根据counter值去执行相应的操作或计算。但这种方法只能用于非常简单的控制,且非常不容易维护以及后期功能修改。所以实际上大家都会用标准的FSM来实现复杂控制。
Moore FSM架构
Moore FSM是目前的主流写法, 它由三块组成:
- state register : 由DFF构成,将Next state Logic产生的state存入current register
- next state logic : 由组合逻辑组成,根据输入及目前state,产生next state
- output logic : 组合逻辑, 根据目前state产生输出
假设输入为w_i, 输出为z_o, 当输入连续2个cycle为高时,则输出为1个cycle高
下面是state diagram
三段式编码
/**********************************************
Description : 3 always block for moore fsm (BEST)
**********************************************/
module simple_fsm (
clk,
rst_n,
w_i,
z_o
);
input clk;
input rst_n;
input w_i;
output z_o;
parameter IDLE = 2'b00;
parameter S0 = 2'b01;
parameter S1 = 2'b10;
reg [1:0] curr_state;
reg [1:0] next_state;
reg z_o;
// state reg
always@(posedge clk or negedge rst_n)
begin
if (~rst_n) curr_state <= IDLE;
else curr_state <= next_state;
end
// next state logic
always@(*)
begin
case (curr_state)
IDLE : if (w_i) next_state = S0;
else next_state = IDLE;
S0 : if (w_i) next_state = S1;
else next_state = IDLE;
S1 : if (w_i) next_state = S1;
else next_state = IDLE;
default : next_state = IDLE;
endcase
end
// output logic
always@(*)
begin
case (curr_state)
IDLE : z_o = 1'b0;
S0 : z_o = 1'b0;
S1 : z_o = 1'b1;
default : z_o = 1'b0;
endcase
end
endmodule
下面是相应的TB
/**********************************************
Description : testbench for FSM
**********************************************/
module simple_fsm_tb;
reg clk = 1'b1;
reg rst_n = 1'b1;
reg w_i = 1'b0;
wire z_o;
// clk
always #10 clk = ~clk;
event after_rst;
// rst_n
initial begin
#6; // 6ns
rst_n = 1'b0;
#30; // 36ns
rst_n = 1'b1;
->after_rst;
end
// w_i
initial begin
@(after_rst);
repeat(2)@(posedge clk); // 60ns
w_i <= 1'b1;
@(posedge clk); // 80 ns
w_i <= 1'b0;
@(posedge clk); // 100 ns
w_i <= 1'b1;
repeat(2)@(posedge clk); // 140 ns
w_i <= 1'b0;
@(posedge clk); // 160 ns
w_i <= 1'b1;
repeat(3)@(posedge clk); // 220 ns
w_i <= 1'b0;
end
initial begin
$fsdbDumpfile("simple_fsm.fsdb");
$fsdbDumpvars(0, simple_fsm_tb);
end
simple_fsm u_simple_fsm (
.clk (clk),
.rst_n (rst_n),
.w_i (w_i),
.z_o (z_o)
);
endmodule
仿真波形如下:
两段式编码
代码如下:
module simple_fsm (
clk,
rst_n,
w_i,
z_o
);
input clk;
input rst_n;
input w_i;
output z_o;
parameter IDLE = 2'b00;
parameter S0 = 2'b01;
parameter S1 = 2'b10;
reg [1:0] curr_state;
reg z_o;
// state reg + next state logic
always@(posedge clk or negedge rst_n)
begin
if (~rst_n)
curr_state <= IDLE;
else
case (curr_state)
IDLE : if (w_i) curr_state <= S0;
else curr_state <= IDLE;
S0 : if (w_i) curr_state <= S1;
else curr_state <= IDLE;
S1 : if (w_i) curr_state <= S1;
else curr_state <= IDLE;
default : curr_state <= IDLE;
endcase
end
// output logic
always@(*)
begin
case (curr_state)
IDLE : z_o = 1'b0;
S0 : z_o = 1'b0;
S1 : z_o = 1'b1;
default : z_o = 1'b0;
endcase
end
endmodule
其他
还有其他二段式,或者一段式,但都不推荐使用。
从条理上讲三段式是最清晰的, 易于理解,便于维护
Mealy FSM
架构如下
默认它的输出行为与Moore有点不一样,输出会早一拍
原来Moore FSM需要3个state, 改用Mealy FSM只需要2个State
注意上图中z_o与w_i有关, 所以上图在s0是不存在w_i/~z_o的状态
三段式编码
为了使输出与Moore FSM相同,需要打一拍
module simple_fsm (
clk,
rst_n,
w_i,
z_o
);
input clk;
input rst_n;
input w_i;
output z_o;
parameter IDLE = 2'b00;
parameter S0 = 2'b01;
reg [1:0] curr_state;
reg [1:0] next_state;
reg z;
reg z_o;
// state reg
always@(posedge clk or negedge rst_n)
begin
if (~rst_n)
curr_state <= IDLE;
else
curr_state <= next_state;
end
// next state logic
always@(*)
begin
case (curr_state)
IDLE : if (w_i) next_state = S0;
else next_state = IDLE;
S0 : if (w_i) next_state = S0;
else next_state = IDLE;
default : next_state = IDLE;
endcase
end
// output logic
always@(*)
case (curr_state)
IDLE : if (w_i) z = 1'b0;
else z = 1'b0;
S0 : if (w_i) z = 1'b1;
else z = 1'b0;
default : z = 1'b0;
endcase
// mealy output to delay 1 clk for moore
always@(posedge clk or negedge rst_n)
begin
if (~rst_n)
z_o <= 1'b0;
else
z_o <= z;
end
endmodule
两段式编码
module simple_fsm (
clk,
rst_n,
w_i,
z_o
);
input clk;
input rst_n;
input w_i;
output z_o;
parameter IDLE = 2'b00;
parameter S0 = 2'b01;
reg [1:0] curr_state;
reg z;
reg z_o;
// state reg + next state logic
always@(posedge clk or negedge rst_n)
begin
if (~rst_n)
curr_state <= IDLE;
else
case (curr_state)
IDLE : if (w_i) curr_state <= S0;
else curr_state <= IDLE;
S0 : if (w_i) curr_state <= S0;
else curr_state <= IDLE;
default : curr_state <= IDLE;
endcase
end
// output logic
always@(*)
begin
case (curr_state)
IDLE : if (w_i) z = 1'b0;
else z = 1'b0;
S0 : if (w_i) z = 1'b1;
else z = 1'b0;
default : z = 1'b0;
endcase
end
// mealy output to delay 1 clk for moore
always@(posedge clk or negedge rst_n)
begin
if (~rst_n)
z_o <= 1'b0;
else
z_o <= z;
end
endmodule
有利于后端的推荐风格
为了timing更好,常会在Moore FSM的输出端加一级DFF
架构如下
两段式
module simple_fsm (
clk,
rst_n,
w_i,
z_o
);
input clk;
input rst_n;
input w_i;
output z_o;
parameter IDLE = 2'b00;
parameter S0 = 2'b01;
parameter S1 = 2'b10;
reg [1:0] curr_state;
reg z_o;
// state reg + next state logic
always@(posedge clk or negedge rst_n)
begin
if (~rst_n)
curr_state <= IDLE;
else
case (curr_state)
IDLE : if (w_i) curr_state <= S0;
else curr_state <= IDLE;
S0 : if (w_i) curr_state <= S1;
else curr_state <= IDLE;
S1 : if (w_i) curr_state <= S1;
else curr_state <= IDLE;
default : curr_state <= IDLE;
endcase
end
// output logic
always@(posedge clk or negedge rst_n)
begin
if (~rst_n)
z_o <= 1'b0;
else
case (curr_state)
IDLE : z_o <= 1'b0;
S0 : z_o <= 1'b0;
S1 : z_o <= 1'b1;
default : z_o <= 1'b0;
endcase
end
endmodule
三段式
代码如下:
module simple_fsm (
clk,
rst_n,
w_i,
z_o
);
input clk;
input rst_n;
input w_i;
output z_o;
parameter IDLE = 2'b00;
parameter S0 = 2'b01;
parameter S1 = 2'b10;
reg [1:0] curr_state;
reg [1:0] next_state;
reg z_o;
// state reg
always@(posedge clk or negedge rst_n)
begin
if (~rst_n)
curr_state <= IDLE;
else
curr_state <= next_state;
end
// next state logic
always@(*)
begin
case (curr_state)
IDLE : if (w_i) next_state = S0;
else next_state = IDLE;
S0 : if (w_i) next_state = S1;
else next_state = IDLE;
S1 : if (w_i) next_state = S2;
else next_state = IDLE;
default : next_state = IDLE;
endcase
end
// output logic
always@(posedge clk or negedge rst_n)
begin
if (~rst_n)
z_o <= 1'b0;
else
case (curr_state)
IDLE : z_o <= 1'b0;
S0 : z_o <= 1'b0;
S1 : z_o <= 1'b1;
default : z_o <= 1'b0;
endcase
end
endmodule
注: 本文是在真OO无双原版上的简化,如需要了解更详细信息,请阅读原文
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。