##1.为什么要使用状态机
硬件设计需要并行设计思想,而用Verilog描述的电路大多都是并行实现的,但是对于实际的项目工程,往往需要让硬件来做一些具有顺序的工作,这就要用到状态机的思想。状态机简单的来说就是通过不同的状态迁移来完成一些特定的顺序逻辑
##2. 构成状态机的基本要素
以博主< https://www.cnblogs.com/zqh1126/p/16992755.html >“浅谈Moore型和Mealy型以及序列检测状态图” 中的序列检测图为例。
有限状态机:高效的顺序控制模块模型
将所有的逻辑(输入、输出、状态)在一个always块里进行描述,这种写法看起来简单,但对于复杂的状态会容易出错,并且在大型项目中这些代码是不利于维护的。
将时序逻辑和组合逻辑划分开来,时序逻辑进行当前状态和下一状态的切换,组合逻辑实现输入、输出以及状态的判断,这种写法相对容易维护,不过组合逻辑输出容易出现毛刺等问题。
代码易维护,时序逻辑的输出解决了二段式写法中组合逻辑的毛刺问题,但是三段式消耗的资源相对多一点,并且三段式从输入到输出比一段式和二段式会延时一个时钟周期。
输入序列为:010101101 ; 凡收到输入序列101时,输出为1
`timescale 1ns/1ps
module fsm_1 (
input clk, //50MHZ
input rstn, //复位信号
input data_in,
output reg data_out
);
//序列检测101--可重叠Moore型--需要四个状态
parameter S0 = 4'b0001,
S1 = 4'b0010,
S2 = 4'b0100,
S3 = 4'b1000;
reg [3:0] c_state;
//fsm 一段式写法 简单状态机可用,复杂的状态容易出错且不利于维护
always @(posedge clk or negedge rstn) begin
if (!rstn) begin
c_state <= S0;
data_out <= 0;
end
else
case(c_state)
S0: begin
if(data_in == 1)begin
c_state <= S1;
data_out <= 0;
end
else begin
c_state <= S0;
data_out <= 0;
end
end
S1: begin
if(data_in == 0)begin
c_state <= S2;
data_out <= 0;
end
else begin
c_state <= S1;
data_out <= 0;
end
end
S2: begin
if(data_in == 1) begin
c_state <= S3;
data_out <= 0;
end
else begin
c_state <= S0;
data_out <= 0;
end
end
S3: begin
data_out <= 1; //Moore型状态机,其输出只和S3状态有关
if (data_in == 1) begin
c_state <= S1;
end
else begin
c_state <= S2;
end
end
default : begin c_state = S0; end
endcase
end
endmodule
Testbench
`timescale 1ns/1ps
module fsm_1_tb;
// Ports
reg clk = 0;
reg rstn = 0;
reg data_in = 0;
wire data_out;
fsm_1 dut (
.clk (clk ),
.rstn (rstn ),
.data_in (data_in ),
.data_out ( data_out)
);
initial begin
#50 rstn = 1;
data_in = 0;
#20 data_in = 1;
#20 data_in = 0;
#20 data_in = 1;
#20 data_in = 0;
#20 data_in = 1;
#20 data_in = 1;
#20 data_in = 0;
#20 data_in = 1;
end
always
#10 clk = ! clk ;
endmodule
`timescale 1ns/1ps
module fsm_2 (
input clk, //50MHZ
input rstn, //复位信号
input data_in,
output reg data_out
);
//序列检测101--可重叠Moore型--需要四个状态
parameter S0 = 4'b0001,
S1 = 4'b0010,
S2 = 4'b0100,
S3 = 4'b1000;
reg [3:0] c_state;
reg [3:0] n_state;
//fsm 二段式写法 写法相对容易维护,不过组合逻辑输出容易出现毛刺等问题
//第一个always块:进行当前状态和下一个状态的切换 时序逻辑
always @(posedge clk or negedge rstn) begin
if(!rstn)
c_state <= S0;
else
c_state <= n_state;
end
//第二个always块:实现输入、输出以及状态跳转判断 组合逻辑 阻塞赋值 =
always @(*) begin
if (!rstn) begin
n_state = S0;
data_out = 0;
end
else
case(c_state)
S0: begin
if(data_in == 1)begin
n_state = S1;
data_out = 0;
end
else begin
n_state = S0;
data_out = 0;
end
end
S1: begin
if(data_in == 0)begin
n_state = S2;
data_out = 0;
end
else begin
n_state = S1;
data_out = 0;
end
end
S2: begin
if(data_in == 1) begin
n_state = S3;
data_out = 0;
end
else begin
n_state = S0;
data_out = 0;
end
end
S3: begin
data_out = 1; //Moore型状态机,其输出只和S3状态有关
if (data_in == 1) begin
n_state = S1;
end
else begin
n_state = S2;
end
end
default : begin n_state = S0; end
endcase
end
endmodule
Testbench
`timescale 1ns / 1ps
module fsm_2_tb;
// Parameters
localparam S0 = 4'b0001;
// Ports
reg clk = 0;
reg rstn = 0;
reg data_in = 0;
wire data_out;
fsm_2 uut (
.clk (clk ),
.rstn (rstn ),
.data_in (data_in ),
.data_out ( data_out)
);
initial begin
begin
#50 rstn = 1;
data_in = 0;
#20 data_in = 1;
#20 data_in = 0;
#20 data_in = 1;
#20 data_in = 0;
#20 data_in = 1;
#20 data_in = 1;
#20 data_in = 0;
#20 data_in = 1;
end
end
always
#10 clk = ! clk ;
endmodule
`timescale 1ns/1ps
module fsm_3 (
input clk, //50MHZ
input rstn, //复位信号
input data_in,
output reg data_out
);
//序列检测101--可重叠Moore型--需要四个状态
parameter S0 = 4'b0001,
S1 = 4'b0010,
S2 = 4'b0100,
S3 = 4'b1000;
reg [3:0] c_state;
reg [3:0] n_state;
//fsm 三段式写法
//代码易维护,时序逻辑的输出解决了二段式写法中组合逻辑的毛刺问题,
//但是三段式消耗的资源相对多一点
//第一个always块:进行当前状态和下一个状态的切换 时序逻辑
always @(posedge clk or negedge rstn) begin
if(!rstn)
c_state <= S0;
else
c_state <= n_state;
end
//第二个always块:实现输入以及状态跳转判断 组合逻辑 阻塞赋值 =
always @(*) begin
if (!rstn) begin
n_state = S0;
end
else
case(c_state)
S0: begin
if(data_in == 1)begin
n_state = S1;
end
else begin
n_state = S0;
end
end
S1: begin
if(data_in == 0)begin
n_state = S2;
end
else begin
n_state = S1;
end
end
S2: begin
if(data_in == 1) begin
n_state = S3;
end
else begin
n_state = S0;
end
end
S3: begin
if (data_in == 1) begin
n_state = S1;
end
else begin
n_state = S2;
end
end
default : begin n_state = S0; end
endcase
end
//第三个always块:实现输出 时序逻辑 非阻塞赋值 <=
always @(posedge clk or negedge rstn) begin
if (!rstn)
data_out <= 1'b0;
else if (n_state == S3)
data_out <= 1'b1;
else
data_out <= 1'b0;
end
endmodule
Testbench
`timescale 1ns/1ps
module fsm_3_tb;
// Parameters
localparam S0 = 4'b0001;
// Ports
reg clk = 0;
reg rstn = 0;
reg data_in = 0;
wire data_out;
fsm_3 uut (
.clk (clk ),
.rstn (rstn ),
.data_in (data_in ),
.data_out ( data_out)
);
initial begin
begin
#50 rstn = 1;
data_in = 0;
#20 data_in = 1;
#20 data_in = 0;
#20 data_in = 1;
#20 data_in = 0;
#20 data_in = 1;
#20 data_in = 1;
#20 data_in = 0;
#20 data_in = 1;
end
end
always
#10 clk = ! clk ;
endmodule