Verilog-实现基于状态机的序列检测--一段式状态机、二段式状态机、三段式状态机

一、状态机的基本概念

1.为什么要使用状态机

  硬件设计需要并行设计思想,而用Verilog描述的电路大多都是并行实现的,但是对于实际的项目工程,往往需要让硬件来做一些具有顺序的工作,这就要用到状态机的思想。状态机简单的来说就是通过不同的状态迁移来完成一些特定的顺序逻辑

2. 构成状态机的基本要素

  • 输入:引发状态变化的条件
  • 输出:状态变化后引起的变化
  • 状态:S0、S1、S2、S3

以博主< https://www.cnblogs.com/zqh1126/p/16992755.html >“浅谈Moore型和Mealy型以及序列检测状态图” 中的序列检测图为例。

image

二、关于一段式、二段式、三段式有效状态机状态机

有限状态机:高效的顺序控制模块模型

1. 一段式状态机

  将所有的逻辑(输入、输出、状态)在一个always块里进行描述,这种写法看起来简单,但对于复杂的状态会容易出错,并且在大型项目中这些代码是不利于维护的。

2. 二段式状态机

  将时序逻辑和组合逻辑划分开来,时序逻辑进行当前状态和下一状态的切换,组合逻辑实现输入、输出以及状态的判断,这种写法相对容易维护,不过组合逻辑输出容易出现毛刺等问题。

3. 三段式状态机

  代码易维护,时序逻辑的输出解决了二段式写法中组合逻辑的毛刺问题,但是三段式消耗的资源相对多一点,并且三段式从输入到输出比一段式和二段式会延时一个时钟周期。

三、三种状态机的Verilog实现

输入序列为:010101101 ; 凡收到输入序列101时,输出为1

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


2. 二段式状态机代码实现

二段式状态机代码
`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


3. 三段式状态机代码实现

三段式状态机代码
`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

posted @ 2023-01-13 22:14  anzg256  阅读(434)  评论(0编辑  收藏  举报