有限状态机FSM的分类:
1)、 Moore型状态机:状态机的输出只与当前的状态有关系。
2)、 Mealy型状态机:状态机的输出不仅与当前的状态有关,还与当前的输入有关。
有限状态机的写法根据always块的数量分一段式、两段式和三段式(本文第三段采用的是assign输出,always输出亦可),其中三段式状态机(包含 次态逻辑、状态寄存器、输出逻辑三部分)可以清晰完整的显示出状态机的结构, 代码清晰,便于编写,易于维护。下面以三段式为例编写代码。
一、自动售卖机实现
设计一个自动售卖饮料机,设饮料售价为2.5元,可使用5角和一元硬币,有找零功能。
- 代码编写前,先画出状态转化图,注明各个参数含义
module drink_status_moore(clk,reset,half.one,out,cout);
input clk;
input reset;
input half;
input one;
output out;
output cout;
parameter [2:0] s0 = 3'b000, //3'd0,参数声明
s1 = 3'b001, //3'd1
s2 = 3'b010, //3'd2
s3 = 3'b011, //3'd3
s4 = 3'b100, //3'd4
s5 = 3'b101, //3'd5
s6 = 3'b110; //3'd6
reg [2:0] curr_state; //内部信号声明
reg [2:0] next_state;
//first segement: state transfer---描述状态寄存器、时序逻辑
always @(posedge clk, negedge reset) begin
if(!reset)
curr_state <= s0;
else
curr_state <= next_state;
end
//second segment: transfer condition--描述次态逻辑、组合逻辑
always @(curr_state,one,half)begin //等价 always@(*)
case(curr_state)
s0:begin
if(half) next_state = s1;
else if(one) next_state = s2;
else next_state = s0;
end
s1:begin
if(half) next_state = s2;
else if(one) next_state = s3;
else next_state = s1;
end
s2:begin
if(half) next_state = s3;
else if(one) next_state = s4;
else next_state = s2;
end
s3:begin
if(half) next_state = s4;
else if(one) next_state = s5;
else next_state = s3;
end
s4:begin
if(half) next_state = s5;
else if(one) next_state = s6;
else next_state = s4;
end
s5:begin
next_state = s0;
end
s6:begin
next_state = s0;
end
default: next_state = s0;
endcase
end
//third segement: state output---状态输出(描述输出逻辑)
assign out = ((curr_state == s5) || (curr_state == s6))?1:0;
assign cout = (curr_state == s6)?1:0; //assign可简化输出
endmodule
上述Moore FSM的Testbench:
`timescale 1ns/1ns
module drink_status_moore_tb();
reg half;
reg one;
reg clk;
reg reset;
wire out
wire cout;
initial
begin
clk = 0;
reset = 0; //初始化时钟与复位信号
#20 reset = 1; //拉起reset
end
always #20 clk = ~clk //时钟周期为40ns
initial
begin
half = 0;
one = 0;
#20;
repeat(5)begin
@(posedge clk) half = 1;
@(posedge clk) half = 0;
end
#100;
repeat(3)begin
@(posedge clk) one = 1;
@(posedge clk) one = 0;
end
#200;
$finish;
end
drink_status_moore drink_status_moore_u1(
.clk (clk),
.reset (reset),
.one (one),
.half (half),
.out (out),
.cout(cout));
endmodule
module drink_status_mealy(
input clk, //verilog-2001
input reset,
input half,
input one,
output out,
output cout);
parameter [2:0] s0 = 3'b000, //3'd0
s1 = 3'b001, //3'd1
s2 = 3'b010, //3'd2
s3 = 3'b011, //3'd3
s4 = 3'b100, //3'd4
reg [2:0] curr_state;
reg [2:0] next_state;
//first segement: state transfer---时序逻辑、描述状态寄存器
always @(posedge clk, negedge reset) begin
if(!reset)
curr_state <= s0;
else
curr_state <= next_state;
end
//second segment: transfer condition---组合逻辑、描述次态逻辑
always @(*)begin //等价 always @(curr_state,one,half)
case(curr_state)
s0:begin
if(half) next_state = s1;
else if(one) next_state = s2;
else next_state = s0;
end
s1:begin
if(half) next_state = s2;
else if(one) next_state = s3;
else next_state = s1;
end
s2:begin
if(half) next_state = s3;
else if(one) next_state = s4;
else next_state = s2;
end
s3:begin
if(half) next_state = s4;
else if(one) next_state = s0;
else next_state = s3;
end
s4:begin
if(half) next_state = s0;
else if(one) next_state = s0;
else next_state = s4;
end
default: next_state = s0;
endcase
end
//third segement: state output---状态输出(描述输出逻辑)
assign out = ((curr_state == s4) & (half|one))?1:((curr_state == s3) & (one))?1:0; //等价于if--else if--else,即条件操作符的嵌套使用
assign cout = ((curr_state == s4) & (one))?1:0;
endmodule
注意区别Moore型状态机与Mealy型状态机,一般Moore型状态机的状态数量较Mealy型要多。
二、序列检测器
设计一个序列检测器,检查的序列为“1110010”,当输入的信号依次为“1110010”时,输出信号Y输出一个高电平,否则输出信号Y为低电平。
- 状态转换图如下:
module seq_checker_moore(
input in, clk, reset;
output out;
output [2:0] state; //000-111
parameter s0 = 'd0,
s1 = 'd1,
s2 = 'd2,
s3 = 'd3,
s4 = 'd4,
s5 = 'd5,
s6 = 'd6,
s7 = 'd7,
s8 = 'd8;
reg [2:0] curr_state;
reg [2:0] next_state;
//first segement: state transfer---时序逻辑、描述状态寄存器
always @(posedge clk, negedge reset) begin
if(!reset)
curr_state <= s0;
else
curr_state <= next_state;
end
//second segment: transfer condition---组合逻辑、描述次态逻辑
always @(*)begin //等价 always @(curr_state,in)
case(curr_state)
s0:begin
if(in==0) next_state = s0;
else next_state = s1;
end
s1:begin
if(in==0) next_state = s0;
else next_state = s2;
end
s2:begin
if(in==0) next_state = s0;
else next_state = s3;
end
s3:begin
if(in==0) next_state = s4;
else next_state = s3;
end
s4:begin
if(in==0) next_state = s5;
else next_state = s1;
end
s5:begin
if(in==0) next_state = s0;
else next_state = s6;
end
s6:begin
if(in==0) next_state = s7;
else next_state = s2;
end
s7:begin
if(in==0) next_state = s0;
else next_state = s1;
end
default: next_state = s0;
endcase
end
//third segement: state output---状态输出(描述输出逻辑)
assign out = (curr_state == s7)?1:0;
endmodule
mealy型序列检测器的状态机不再赘述。
以下为上述Moore FSM的Tsetbench:
`timescale 1ns/1ns
module seq_checker_moore_tb();
reg in;
reg clk;
reg reset;
reg [7:0] data[0:7];
wire out
wire [3:0] state;
reg i;
initial
begin
clk = 0;
reset = 0; //初始化时钟与复位信号
#20 reset = 1; //拉起reset
end
always #20 clk = ~clk //时钟周期为40ns
initial
begin
in = 0;
#30 in = 1;
#40 in = 1;
#40 in = 0;
#40 in = 0;
#40 in = 1;
#40 in = 0;
#40 in = 1;
#40 in = 0;
#40 in = 1;
#40 in = 1;
#40;
$finish;
end
initial begin //该模块与仿真无关
$readmemh("data.txt",data);
for(i=0;i<8;i=i+1)begin
#40;
$display("data=%d",data[i]);
end
#200;
$finish;
end
seq_checker_moore seq_ckecker_moore_u1(
.clk (clk),
.reset (reset),
.in (in),
.out (out),
.state(state));
endmodule
三、两段式、三段式框架
- 两段式示列模板如下:
//第一个进程,同步时序always模块,格式化描述次态寄存器迁移到现态寄存器
always @ (posedge clk or negedge rst_n) //异步复位
if(!rst_n)
current_state <= IDLE;
else
current_state <= next_state; //注意,使用的是非阻塞赋值
//第二个进程,组合逻辑always模块,描述状态转移条件判断
always @ (current_state or 输入信号) begin //电平触发 等价posedge @(*)
case(current_state)
S1: if(...)
next_state = S2; //阻塞赋值
out1 <= 1'b1; //注意是非阻塞赋值
...
endcase
end
- 三段式示列模板如下:
//第一个进程,同步时序always模块,格式化描述次态寄存器迁移到现态寄存器
always @ (posedge clk or negedge rst_n) //异步复位
if(!rst_n)
current_state <= IDLE;
else
current_state <= next_state; //注意,使用的是非阻塞赋值
//第二个进程,组合逻辑always模块,描述状态转移条件判断
always @ (current_state or 输入信号) begin //电平触发 等价posedge @(*)
case(current_state)
S1: if(...)
next_state = S2; //阻塞赋值
...
endcase
end
//第三个进程,同步时序always模块,格式化描述次态寄存器输出
always @ (posedge clk or negedge rst_n)
case(next_state or 输入信号)
S1: out1 <= 1'b1; //注意是非阻塞逻辑
S2: out2 <= 1'b0;
...
default:... //default的作用是免除综合工具综合出锁存器
endcase
end