• 状态机的Verilog写法


      “硬件设计很讲究并行设计思想,虽然用Verilog描述的电路大都是并行实现的,但是对于实际的工程应用,往往需要让硬件来实现一些具有一定顺序的工作,这就要用到状态机思想。什么是状态机呢?简单的说,就是通过不同的状态迁移来完成一些特定的顺序逻辑。硬件的并行性决定了用Verilog描述的硬件实现(臂如不同的always语句)都是并行执行的,那么如果希望分多个时间完成一个任务,怎么办?也许可以用多个使能信号来衔接多个不同的模块,但是这样做多少显得繁琐。状态机的提出会大大简化这一工作。”

    ——特权同学《深入浅出玩转FPGA》

      一、状态机分类:

      1.Moore型:状态机的状态变化仅和当前状态有关(特权同学《深入浅出玩转FPGA》);时序逻辑电路的输出只取决于当前状态(夏宇闻《Verilog数字系统设计》)。设计高速电路时常用此类状态机,把状态变化直接用作输出。

      2.Mealy型:状态机的状态变化不仅与当前的状态有关,还取决于当前的输入条件(特权同学《深入浅出玩转FPGA》);时序逻辑的输出不但取决于状态还取决于输入(夏宇闻《Verilog数字系统设计》)。平常使用较多的是此类状态机。

      “其实这几种状态机之间,只要做一些改变,便可以从一种形式转变为另一种形式。把状态机精确的分为这类或那类,其实并不重要,重要的是设计者如何把握输出的结构能满足设计的整体目标,包括定时的准确性和灵活性。”

    ——夏宇闻《Verilog数字系统设计》

      二、状态机编码:

       状态机的参数定义采用的都是独热码,和格雷码相比,虽然独热码多用了触发器,但所用组合电路可以省一些,因而使电路的速度和可靠性有显著提高,而总的单元数并无显著增加。采用独热编码后有了多余的状态,就有一些不可达到的状态。为此在case语句的最后需要增加default分支向。这可以用默认项表示该项,也可以用确定项表示,以确保回到初始状态。一般综合器都可以通过综合指令的控制来合理地处理默认项。

      三、实例分析

      状态机一般有三种不同的写法,即一段式、两段式和三段式的状态机写法,他们在速度、面积、代码可维护性等各个方面互有优劣,不要对任何一种写法给出“一棍子打死”的定论。手头上刚好有一个状态机的例子,借此记录一下三种状态机的Verilog写法。

      要求:

      售货机里有价值4元的脉动饮料,支持1元和2元硬币。请设计一个状态机,检测投入的硬币,当累计投入币值大于等于脉动价格时,售货机自动找零并弹出1瓶脉动饮料。硬币和商品都是一个一个的进出,不会出现一次性投很多个硬币弹出很多瓶脉动的情况。

    信号 含义
    clk 时钟信号
    rst_n 复位信号
    in 输入信号,币值,有1和2两种,投钱
    out 输出信号,币值,有1和2两种,找零
    out_vld 输出信号,脉动,为1则输出1瓶脉动

      状态转移图:

      根据要求,我们先把状态转移图画出来,绘画软件:Visio,如果没有安装也可以用wps自带应用的“流程图”功能:

      testbench:

      1 `timescale 1ns/1ps  //时间精度
      2 `define    Clock 20 //时钟周期
      3 
      4 module FSM_3_tb;
      5 //--------------------< 端口 >------------------------------------------
      6 reg                     clk                 ;
      7 reg                     rst_n               ;
      8 reg  [1:0]              in                  ;
      9 wire [1:0]              out                 ;
     10 wire                    out_vld             ;
     11 
     12 //----------------------------------------------------------------------
     13 //--   模块例化
     14 //----------------------------------------------------------------------
     15 FSM_3 u_FSM_3
     16 (
     17     .clk                (clk                ),
     18     .rst_n              (rst_n              ),
     19     .in                 (in                 ),
     20     .out                (out                ),
     21     .out_vld            (out_vld            )
     22 );
     23 
     24 //----------------------------------------------------------------------
     25 //--   状态机名称查看器
     26 //----------------------------------------------------------------------
     27 localparam S0           = 4'b0001           ;
     28 localparam S1           = 4'b0010           ;
     29 localparam S2           = 4'b0100           ;
     30 localparam S3           = 4'b1000           ;
     31 //2字符16位
     32 reg [15:0]              state_name          ;
     33 
     34 always@(*)begin
     35     case(u_FSM_3.state_c)
     36         S0:     state_name = "S0";
     37         S1:     state_name = "S1";
     38         S2:     state_name = "S2";
     39         S3:     state_name = "S3";
     40         default:state_name = "S0";
     41     endcase
     42 end
     43 
     44 //----------------------------------------------------------------------
     45 //--   时钟信号和复位信号
     46 //----------------------------------------------------------------------
     47 initial begin
     48     clk = 1;
     49     forever
     50     #(`Clock/2) clk = ~clk;
     51 end
     52 
     53 initial begin
     54     rst_n = 0; #(`Clock*20+1);
     55     rst_n = 1;
     56 end
     57 
     58 //----------------------------------------------------------------------
     59 //--   设计输入信号
     60 //----------------------------------------------------------------------
     61 initial begin
     62     #1;
     63     in = 0;
     64     #(`Clock*20+1); //初始化完成
     65 //情况1--------------------------
     66     in = 1;         //1块钱
     67     #(`Clock*1);
     68     in = 0;
     69     #(`Clock*1);
     70     in = 1;         //1块钱
     71     #(`Clock*1);
     72     in = 0;
     73     #(`Clock*1);
     74     in = 1;         //1块钱
     75     #(`Clock*1);
     76     in = 0;
     77     #(`Clock*1);
     78     in = 1;         //1块钱
     79     #(`Clock*1);
     80     in = 0;
     81     #(`Clock*10);
     82 //情况2--------------------------
     83     in = 1;         //1块钱
     84     #(`Clock*1);
     85     in = 0;
     86     #(`Clock*1);
     87     in = 1;         //1块钱
     88     #(`Clock*1);
     89     in = 0;
     90     #(`Clock*1);
     91     in = 1;         //1块钱
     92     #(`Clock*1);
     93     in = 0;
     94     #(`Clock*1);
     95     in = 2;         //2块钱
     96     #(`Clock*1);
     97     in = 0;
     98     #(`Clock*10);
     99 //情况3--------------------------
    100     in = 1;         //1块钱
    101     #(`Clock*1);
    102     in = 0;
    103     #(`Clock*1);
    104     in = 1;         //1块钱
    105     #(`Clock*1);
    106     in = 0;
    107     #(`Clock*1);
    108     in = 2;         //2块钱
    109     #(`Clock*1);
    110     in = 0;
    111     #(`Clock*10);
    112 //情况4--------------------------
    113     in = 1;         //1块钱
    114     #(`Clock*1);
    115     in = 0;
    116     #(`Clock*1);
    117     in = 2;         //2块钱
    118     #(`Clock*1);
    119     in = 0;
    120     #(`Clock*1);
    121     in = 2;         //2块钱
    122     #(`Clock*1);
    123     in = 0;
    124     #(`Clock*10);
    125 //情况5--------------------------
    126     in = 2;         //2块钱
    127     #(`Clock*1);
    128     in = 0;
    129     #(`Clock*1);
    130     in = 2;         //2块钱
    131     #(`Clock*1);
    132     in = 0;
    133     #(`Clock*10);
    134 
    135 
    136     $stop;
    137 end
    138 
    139 
    140 endmodule


      1. 一段式状态机

      只定义一个转移状态:state,总体结构是一段always时序逻辑,用于描述状态转移和输出。由于是时序逻辑能够自动保持,所以可以省略else。但建议在初始状态时(例如下文的S0),else处赋一下初始值。

     1 //======================================================================
     2 // --- 名称 : FSM_1
     3 // --- 作者 : xianyu_FPGA
     4 // --- 日期 : 2018-12-15
     5 // --- 描述 : 售货机练习,采用一段式状态机
     6 //======================================================================
     7 
     8 module FSM_1
     9 //---------------------<端口声明>---------------------------------------
    10 (
    11 input                   clk                 ,
    12 input                   rst_n               ,
    13 input      [1:0]        in                  ,
    14 output reg [1:0]        out                 ,
    15 output reg              out_vld
    16 );
    17 //---------------------<信号定义>---------------------------------------
    18 reg  [3:0]              state               ;
    19 //---------------------<状态机参数>-------------------------------------
    20 localparam S0           = 4'b0001           ;
    21 localparam S1           = 4'b0010           ;
    22 localparam S2           = 4'b0100           ;
    23 localparam S3           = 4'b1000           ;
    24 
    25 //----------------------------------------------------------------------
    26 //--   状态机第1段
    27 //----------------------------------------------------------------------
    28 always@(posedge clk or negedge rst_n)begin
    29     if(!rst_n)begin
    30         state   <= S0;
    31         out     <= 0 ;
    32         out_vld <= 0 ;
    33     end
    34     else begin
    35         case(state)
    36             S0: begin
    37                 if(in==1)begin
    38                     state   <= S1;
    39                 end
    40                 else if(in==2)begin
    41                     state   <= S2;
    42                 end
    43                 else begin
    44                     out     <= 0 ;
    45                     out_vld <= 0 ;
    46                 end
    47             end
    48             S1: begin
    49                 if(in==1)begin
    50                     state   <= S2;
    51                 end
    52                 else if(in==2)begin
    53                     state   <= S3;
    54                 end
    55             end
    56             S2: begin
    57                 if(in==1)begin
    58                     state   <= S3;
    59                 end
    60                 else if(in==2)begin
    61                     state   <= S0;
    62                     out_vld <= 1 ;
    63                 end
    64             end
    65             S3: begin
    66                 if(in==1)begin
    67                     state   <= S0;
    68                     out_vld <= 1 ;
    69                 end
    70                 else if(in==2)begin
    71                     state   <= S0;
    72                     out     <= 1 ;
    73                     out_vld <= 1 ;
    74                 end
    75             end
    76             default:state   <= S0;
    77         endcase
    78     end
    79 end
    80 
    81 
    82 
    83 endmodule

    仿真波形如下:

    结论:波形和预想一致!

      2. 二段式状态机:

      二段式状态机,第一段用时序逻辑描述state_c(现态)和state_n(次态),第二段用组合逻辑描述状态转移和输出。由于是组合逻辑,为避免产生锁存器,else处一定要写上 if 中说使用了的信号。

     1 //======================================================================
     2 // --- 名称 : FSM_2
     3 // --- 作者 : xianyu_FPGA
     4 // --- 日期 : 2018-12-15
     5 // --- 描述 : 售货机练习,采用二段式状态机
     6 //======================================================================
     7 
     8 module FSM_2
     9 //---------------------<端口声明>---------------------------------------
    10 (
    11 input                   clk                 ,
    12 input                   rst_n               ,
    13 input      [1:0]        in                  ,
    14 output reg [1:0]        out                 ,
    15 output reg              out_vld
    16 );
    17 //---------------------<信号定义>---------------------------------------
    18 reg  [3:0]              state_c             ;
    19 reg  [3:0]              state_n             ;
    20 //---------------------<状态机参数>-------------------------------------
    21 localparam S0           = 4'b0001           ;
    22 localparam S1           = 4'b0010           ;
    23 localparam S2           = 4'b0100           ;
    24 localparam S3           = 4'b1000           ;
    25 
    26 //----------------------------------------------------------------------
    27 //--   状态机第1段
    28 //----------------------------------------------------------------------
    29 always@(posedge clk or negedge rst_n)begin
    30     if(!rst_n)
    31         state_c <= S0;
    32     else
    33         state_c <= state_n;
    34 end
    35 
    36 //----------------------------------------------------------------------
    37 //--   状态机第2段
    38 //----------------------------------------------------------------------
    39 always@(*)begin
    40     case(state_c)
    41         S0: begin
    42             if(in==1)begin
    43                 state_n = S1;
    44             end
    45             else if(in==2)begin
    46                 state_n = S2;
    47             end
    48             else begin
    49                 state_n = state_c;
    50                 out     = 0 ;
    51                 out_vld = 0 ;
    52             end
    53         end
    54         S1: begin
    55             if(in==1)begin
    56                 state_n = S2;
    57             end
    58             else if(in==2)begin
    59                 state_n = S3;
    60             end
    61             else begin
    62                 state_n = state_c;
    63             end
    64         end
    65         S2: begin
    66             if(in==1)begin
    67                 state_n = S3;
    68             end
    69             else if(in==2)begin
    70                 state_n = S0;
    71                 out_vld = 1 ;
    72             end
    73             else begin
    74                 state_n = state_c;
    75                 out_vld = 0;
    76             end
    77         end
    78         S3: begin
    79             if(in==1)begin
    80                 state_n = S0;
    81                 out_vld = 1 ;
    82             end
    83             else if(in==2)begin
    84                 state_n = S0;
    85                 out     = 1 ;
    86                 out_vld = 1 ;
    87             end
    88             else begin
    89                 state_n = state_c;
    90                 out     = 0;
    91                 out_vld = 0;
    92             end
    93         end
    94         default:state_n = S0;
    95     endcase
    96 end
    97 
    98 
    99 endmodule

    仿真波形如下所示:

    结论:波形和预想一致!但是产生了毛刺,这也是二段式状态机的缺点。

    毛刺产生原因:状态机通常包含主控时序进程、主控组合进程和辅助进程三个部分。其中,主控组合进程的任务是根据外部输入的控制信号和当前状态的状态值确定下一 状态的取向,并确定对外输出内容和对内部其他组合或时序进程输出控制信号的内容。一方面,由于有组合逻辑进程的存在,状态机输出信号会出现毛刺——竞争冒险现象;另一方面,如果状态信号是多位值的,则在电路中对应了多条信号线。由于存在传输延迟,各信号线上的值发生改变的时间则存在先后,从而使得状态迁移时在初始状态和目的状态之间出现临时状态——毛刺。

       简单理解为:state_n 会因为组合逻辑原因不断出现临时状态,这些状态是无效的,而输出也因为组合逻辑原因产生这些临时状态,即毛刺。

      3. 三段式状态机

      三段式状态机,第一段用时序逻辑描述state_c(现态)和state_n(次态),第二段用组合逻辑描述状态转移,第三段用时序逻辑描述输出,第三段可以是多个always块。

     1 //======================================================================
     2 // --- 名称 : FSM_3
     3 // --- 作者 : xianyu_FPGA
     4 // --- 日期 : 2018-12-15
     5 // --- 描述 : 售货机练习,采用三段式状态机
     6 //======================================================================
     7 
     8 module FSM_3
     9 //---------------------<端口声明>---------------------------------------
    10 (
    11 input                   clk                 ,
    12 input                   rst_n               ,
    13 input      [1:0]        in                  ,
    14 output reg [1:0]        out                 ,
    15 output reg              out_vld
    16 );
    17 //---------------------<信号定义>---------------------------------------
    18 reg  [3:0]              state_c             ;
    19 reg  [3:0]              state_n             ;
    20 //---------------------<状态机参数>-------------------------------------
    21 localparam S0           = 4'b0001           ;
    22 localparam S1           = 4'b0010           ;
    23 localparam S2           = 4'b0100           ;
    24 localparam S3           = 4'b1000           ;
    25 
    26 //----------------------------------------------------------------------
    27 //--   状态机第1段
    28 //----------------------------------------------------------------------
    29 always @(posedge clk or negedge rst_n)begin
    30     if(!rst_n)
    31         state_c <= S0;
    32     else
    33         state_c <= state_n;
    34 end
    35 
    36 //----------------------------------------------------------------------
    37 //--   状态机第2段
    38 //----------------------------------------------------------------------
    39 always @(*)begin
    40     case(state_c)
    41         S0: begin
    42             if(in==1)
    43                 state_n = S1;
    44             else if(in==2)
    45                 state_n = S2;
    46             else
    47                 state_n = state_c;
    48         end
    49         S1: begin
    50             if(in==1)
    51                 state_n = S2;
    52             else if(in==2)
    53                 state_n = S3;
    54             else
    55                 state_n = state_c;
    56         end
    57         S2: begin
    58             if(in==1)
    59                 state_n = S3;
    60             else if(in==2)
    61                 state_n = S0;
    62             else
    63                 state_n = state_c;
    64         end
    65         S3: begin
    66             if(in==1 || in==2)      // in != 0也行
    67                 state_n = S0;
    68             else
    69                 state_n = state_c;
    70         end
    71         default:state_n = S0;
    72     endcase
    73 end
    74 
    75 //----------------------------------------------------------------------
    76 //--   状态机第3段
    77 //----------------------------------------------------------------------
    78 //找零钱
    79 always @(posedge clk or negedge rst_n)begin
    80     if(!rst_n)
    81         out <= 0;     
    82     else if(state_c==S3 && in==2)
    83         out <= 1;
    84     else
    85         out <= 0;
    86 end
    87 
    88 //输出脉动
    89 always @(posedge clk or negedge rst_n)begin
    90     if(rst_n==1'b0)
    91         out_vld <= 0;
    92     else if((state_c==S2 && in==2) || (state_c==S3 && in!=0))
    93         out_vld <= 1;
    94     else
    95         out_vld <= 0;
    96 end
    97 
    98 
    99 endmodule

    仿真波形如下所示:

    结论:波形和预想一致!这也是较多书籍推荐的写法。

      4. 一段式和三段式结合的状态机(by 威三学院FPGA教程)

      V3学院状态机,只定义一个转移状态:state。第一段用时序逻辑描述state状态转移,第二段用时序逻辑描述输出,第二段可以是多个always块。由于是时序逻辑能够自动保持,所以可以省略else。这种状态机的优点是既消除了组合逻辑可能产生的毛刺,又减少了代码量。

     1 //======================================================================
     2 // --- 名称 : FSM_V3
     3 // --- 作者 : xianyu_FPGA
     4 // --- 日期 : 2019-06-12
     5 // --- 描述 : 售货机练习,采用V3学院的状态机
     6 //======================================================================
     7 
     8 module FSM_V3
     9 //---------------------<端口声明>---------------------------------------
    10 (
    11 input                   clk                 ,
    12 input                   rst_n               ,
    13 input      [1:0]        in                  ,
    14 output reg [1:0]        out                 ,
    15 output reg              out_vld
    16 );
    17 //---------------------<信号定义>---------------------------------------
    18 reg  [3:0]              state               ;
    19 //---------------------<状态机参数>-------------------------------------
    20 localparam S0           = 4'b0001           ;
    21 localparam S1           = 4'b0010           ;
    22 localparam S2           = 4'b0100           ;
    23 localparam S3           = 4'b1000           ;
    24 
    25 //----------------------------------------------------------------------
    26 //--   状态机
    27 //----------------------------------------------------------------------
    28 always @(posedge clk or negedge rst_n)begin
    29     if(!rst_n)
    30         state <= S0;
    31     else begin
    32         case(state)
    33             S0: begin
    34                 if(in==1)
    35                     state <= S1;
    36                 else if(in==2)
    37                     state <= S2;
    38             end
    39             S1: begin
    40                 if(in==1)
    41                     state <= S2;
    42                 else if(in==2)
    43                     state <= S3;
    44             end
    45             S2: begin
    46                 if(in==1)
    47                     state <= S3;
    48                 else if(in==2)
    49                     state <= S0;
    50             end
    51             S3: begin
    52                 if(in==1 || in==2)      // in != 0也行
    53                     state <= S0;
    54             end
    55             default:state <= S0;
    56         endcase
    57     end
    58 end
    59 
    60 //----------------------------------------------------------------------
    61 //--   输出
    62 //----------------------------------------------------------------------
    63 //找零钱
    64 always @(posedge clk or negedge rst_n)begin
    65     if(!rst_n)
    66         out <= 0;     
    67     else if(state==S3 && in==2)
    68         out <= 1;
    69     else
    70         out <= 0;
    71 end
    72 
    73 //输出脉动
    74 always @(posedge clk or negedge rst_n)begin
    75     if(rst_n==1'b0)
    76         out_vld <= 0;
    77     else if((state==S2 && in==2) || (state==S3 && in!=0))
    78         out_vld <= 1;
    79     else
    80         out_vld <= 0;
    81 end
    82 
    83 
    84 endmodule

    仿真波形如下所示:

    结论:波形和预想一致!

    四、状态机名称查看器

      可以看到,我的Modelsim波形中出现了一个信号state_name,里面显示了状态机的名称,这是怎么做到的呢?方法有很多种,这里介绍两种。

      1. testbench法

      testbench里增加一段参数转ASCII码的代码,如下所示:

     1 //----------------------------------------------------------------------
     2 //--   状态机名称查看器
     3 //----------------------------------------------------------------------
     4 localparam S0           = 4'b0001           ;
     5 localparam S1           = 4'b0010           ;
     6 localparam S2           = 4'b0100           ;
     7 localparam S3           = 4'b1000           ;
     8 //2字符16位
     9 reg [15:0]              state_name          ;
    10 
    11 always@(*)begin
    12     case(u_FSM_3.state_c)
    13         S0:     state_name = "S0";
    14         S1:     state_name = "S1";
    15         S2:     state_name = "S2";
    16         S3:     state_name = "S3";
    17         default:state_name = "S0";
    18     endcase
    19 end

      在Modelsim中点击信号state_name,右键选择用ASSIC码查看就可以看到状态机的名称,而不再是头疼的的0001、0010等字符。编写时注意一下位宽,一个ASSIC码字符宽度是8位,例如“S0”有2个字符则需要16位宽。

       2. do文件法(tcl文件也是一样的)

      首先你得学会怎么使用Modelsim的自动化脚本仿真,那么我们只要再do文件中加入这段代码即可:

     1 # ======================================================================
     2 # ==   状态机名称查看器
     3 # ======================================================================
     4 
     5 # 结构体设置
     6 virtual type {
     7     {4'b0001 S0}
     8     {4'b0010 S1}
     9     {4'b0100 S2}
    10     {4'b1000 S3}
    11 } fsm_type;
    12 
    13 # 结构体和信号名关联,命名为state_name
    14 virtual function {(fsm_type)/fsm_tb/u_fsm/state} state_name

    参考资料:

    [1]小梅哥FPGA教程

    [2]威三学院FPGA教程

    [3]吴厚航. 深入浅出玩转FPGA[M]. 北京航空航天大学出版社, 2013.

    [4]夏宇闻. Verilog数字系统设计教程.第3版[M]. 北京航空航天大学出版社, 2013.

    [5]韩彬, 于潇宇, 张雷鸣. FPGA设计技巧与案例开发详解[M]. 电子工业出版社, 2014.

  • 相关阅读:
    FTP 协议和 HTTP 协议的比较
    HttpURLConnection的post请求,什么时候发出,writeData存在什么地方
    装饰器
    函数参数以及名称空间作用域
    函数的调用
    函数的返回值
    定义函数的三种方式
    函数
    day05
    day04
  • 原文地址:https://www.cnblogs.com/xianyufpga/p/11006113.html
Copyright © 2020-2023  润新知