FPGA 状态机

  随着对FPGA的不断学习,发现状态机在FPGA的逻辑设计中,是个及其重要的概念和能力,是个必须掌握的知识点,本文是结合网上资料及野火的《FPGA Verilog开发实战指南——基于Altera EP4CE10》一书的理解的个人总结。

参考一:https://bbs.elecfans.com/jishu_909019_1_1.html

参考二:https://www.cnblogs.com/wanghuaijun/p/8284697.html

参考三:《FPGA Verilog开发实战指南——基于Altera EP4CE10》

 

一、状态机概念

  将每个情况抽象成各个状态,各状态根据当前状态和输入条件,决定下一状态进入到什么状态,以此达成相应的目的。软件编程中也有状态机的概念,目前本人还没有具体了解。

 

二、状态机类型

  状态机共有Moore、Mealy两种类型,

1. Moore的输出只跟当前状态有关

2. Mealy的输出与当前状态及输入情况有关

 

三、状态机编码

  状态机的每种状态需要编码,以便使用case语句。而编码方式又分为二进制码、格雷码、独热码等多种方式,不同的编码方式根据器件的组合逻辑和寄存器资源情况,比较好的适用情况如下。

截取自《FPGA Verilog开发实战指南——基于Altera EP4CE10》

 

 

四、状态机的编写方式

  状态机有三种编写方式,分别为一段式、二段式、三段式。

1. 一段式:将所有的状态转移、输入、输出都写在一个always语句块中。

2. 二段式:第一段采用同步时序逻辑描述状态转移,第二段采用组合逻辑描述状态转移条件及输出。

3. 三段式:第一段采用同步时序逻辑描述状态转移,第二段采用组合逻辑描述状态转移条件,第三段采用同步时序逻辑,或者组合逻辑描述输出。

4. 新二段式(野火提到的方式):第一段采用同步时序逻辑描述状态转移条件,第二段采用同步时序逻辑,或者组合逻辑描述输出。相当于三段式第一段与第二段合并为第一段,原第三段变为第二段。

注意:编写方式的区分,不以使用多少个always语句块判断,而是以上面的定义的方式来判定。因为组合逻辑容易产生毛刺,所以输出在有选择的情况下,选择同步时序逻辑输出。

 

五、编写格式

5.1 一段式:

  1 //采用Mealy型状态机,一段式写法
  2 //1. 空闲状态----所有灯熄灭
  3 //2. 状态S1----------点亮LED[0]
  4 //3. 状态S2----------点亮LED[1]
  5 //4. 状态S3----------点亮LED[2]
  6 module st(
  7 input clk,
  8 input rst_n,
  9 
 10 input [1:0]key,
 11 
 12 output reg [2:0]led
 13 );
 14 
 15 parameter    IDLE = 4'b0001;
 16 parameter     S1 = 4'b0010;
 17 parameter     S2 = 4'b0100;
 18 parameter    S3 = 4'b1000;
 19 
 20 reg [3:0] state;
 21 
 22 //状态转移、状态转移条件、输出都写在一个always语句块中
 23 always @(posedge clk or negedge rst_n)
 24     if(!rst_n)
 25         begin
 26             state <= IDLE;
 27             led <= 3'b111;
 28         end
 29     else
 30         begin
 31             case(state)
 32                 IDLE:    if(key == 2'b01)
 33                             begin
 34                                 state <= S1;
 35                                 led <= 3'b110;
 36                             end
 37                         else if(key == 2'b10)
 38                             begin
 39                                 state <= S2;
 40                                 led <= 3'b101;
 41                             end
 42                         else if(key == 2'b11)
 43                             begin
 44                                 state <= S3;
 45                                 led <= 3'b011;
 46                             end
 47                         else 
 48                             begin
 49                                 state <= IDLE;
 50                                 led <= 3'b111;
 51                             end
 52                 
 53                 S1:        if(key == 2'b01)
 54                             begin
 55                                 state <= S2;
 56                                 led <= 3'b101;
 57                             end
 58                         else if(key == 2'b10)
 59                             begin
 60                                 state <= S3;
 61                                 led <= 3'b011;
 62                             end
 63                         else if(key == 2'b11)
 64                             begin
 65                                 state <= IDLE;
 66                                 led <= 3'b111;
 67                             end
 68                         else
 69                             begin
 70                                 state <= S1;
 71                                 led <= 3'b110;
 72                             end
 73                             
 74                 S2:        if(key == 2'b01)
 75                             begin
 76                                 state <= S3;
 77                                 led <= 3'b011;
 78                             end
 79                         else if(key == 2'b10 || key == 2'b11)
 80                             begin
 81                                 state <= IDLE;
 82                                 led <= 3'b111;
 83                             end
 84                         else 
 85                             begin
 86                                 state <= S2;
 87                                 led <= 3'b101;
 88                             end
 89                 
 90                             
 91                 S3:        if(key != 2'b00)
 92                             begin
 93                                 state <= IDLE;
 94                                 led <= 3'b111;
 95                             end
 96                         else
 97                             begin
 98                                 state <= S3;
 99                                 led <= 3'b011;
100                             end
101                 
102                 default:    begin
103                                 state <= IDLE;
104                                 led <= 3'b111;
105                             end
106                 
107             endcase
108         end
109 
110 
111 endmodule
View Code

 

5.2 二段式:

  1 //采用Mealy型状态机,二段式写法
  2 //1. 空闲状态----所有灯熄灭
  3 //2. 状态S1----------点亮LED[0]
  4 //3. 状态S2----------点亮LED[1]
  5 //4. 状态S3----------点亮LED[2]
  6 module st(
  7 input clk,
  8 input rst_n,
  9 
 10 input [1:0]key,
 11 
 12 output reg [2:0]led
 13 );
 14 
 15 parameter    IDLE = 4'b0001;
 16 parameter     S1 = 4'b0010;
 17 parameter     S2 = 4'b0100;
 18 parameter    S3 = 4'b1000;
 19 
 20 reg [3:0] currnt_state;
 21 reg [3:0] next_state;
 22 
 23 //二段式,第一段,将状态转移写入一个always语句块中
 24 always @(posedge clk or negedge rst_n)
 25     if(!rst_n)
 26         currnt_state <= IDLE;
 27     else
 28         currnt_state <= next_state;
 29         
 30 //二段式,第二段,将状态转移条件、输出写入一个always语句块中,采用组合逻辑,即电平触发,组合逻辑方式输出不可避免产生毛刺
 31 always @(currnt_state or key)    
 32     case(currnt_state)
 33         IDLE:    if(key == 2'b01)
 34                     begin
 35                         next_state <= S1;
 36                         led <= 3'b110;
 37                     end
 38                 else if(key == 2'b10)
 39                     begin
 40                         next_state <= S2;
 41                         led <= 3'b101;
 42                     end
 43                 else if(key == 2'b11)
 44                     begin
 45                         next_state <= S3;
 46                         led <= 3'b011;
 47                     end
 48                 else 
 49                     begin
 50                         next_state <= IDLE;
 51                         led <= 3'b111;
 52                     end
 53         
 54         S1:        if(key == 2'b01)
 55                     begin
 56                         next_state <= S2;
 57                         led <= 3'b101;
 58                     end
 59                 else if(key == 2'b10)
 60                     begin
 61                         next_state <= S3;
 62                         led <= 3'b011;
 63                     end
 64                 else if(key == 2'b11)
 65                     begin
 66                         next_state <= IDLE;
 67                         led <= 3'b111;
 68                     end
 69                 else
 70                     begin
 71                         next_state <= S1;
 72                         led <= 3'b110;
 73                     end
 74                     
 75         S2:        if(key == 2'b01)
 76                     begin
 77                         next_state <= S3;
 78                         led <= 3'b011;
 79                     end
 80                 else if(key == 2'b10 || key == 2'b11)
 81                     begin
 82                         next_state <= IDLE;
 83                         led <= 3'b111;
 84                     end
 85                 else 
 86                     begin
 87                         next_state <= S2;
 88                         led <= 3'b101;
 89                     end
 90         
 91                     
 92         S3:        if(key != 2'b00)
 93                     begin
 94                         next_state <= IDLE;
 95                         led <= 3'b111;
 96                     end
 97                 else
 98                     begin
 99                         next_state <= S3;
100                         led <= 3'b011;
101                     end
102         
103         default:    begin
104                         next_state <= IDLE;
105                         led <= 3'b111;
106                     end
107         
108     endcase
109     
110 endmodule            
View Code

 

5.3 三段式:

  1 //采用Mealy型状态机,三段式写法
  2 //1. 空闲状态----所有灯熄灭
  3 //2. 状态S1----------点亮LED[0]
  4 //3. 状态S2----------点亮LED[1]
  5 //4. 状态S3----------点亮LED[2]
  6 module st(
  7 input clk,
  8 input rst_n,
  9 
 10 input [1:0]key,
 11 
 12 output reg [2:0]led
 13 );
 14 
 15 parameter    IDLE = 4'b0001;
 16 parameter     S1 = 4'b0010;
 17 parameter     S2 = 4'b0100;
 18 parameter    S3 = 4'b1000;
 19 
 20 reg [3:0] currnt_state;
 21 reg [3:0] next_state;
 22 
 23 //三段式,第一段,将状态转移写入一个always语句块中
 24 always @(posedge clk or negedge rst_n)
 25     if(!rst_n)
 26         currnt_state <= IDLE;
 27     else
 28         currnt_state <= next_state;
 29         
 30 //三段式,第二段,将状态转移条件写入一个always语句块中,采用组合逻辑,即电平触发
 31 always @(currnt_state or key)    
 32     case(currnt_state)
 33         IDLE:    if(key == 2'b01)
 34                     begin
 35                         next_state <= S1;
 36                     end
 37                 else if(key == 2'b10)
 38                     begin
 39                         next_state <= S2;
 40                     end
 41                 else if(key == 2'b11)
 42                     begin
 43                         next_state <= S3;
 44                     end
 45                 else 
 46                     begin
 47                         next_state <= IDLE;
 48                     end
 49         
 50         S1:        if(key == 2'b01)
 51                     begin
 52                         next_state <= S2;
 53                     end
 54                 else if(key == 2'b10)
 55                     begin
 56                         next_state <= S3;
 57                     end
 58                 else if(key == 2'b11)
 59                     begin
 60                         next_state <= IDLE;
 61                     end
 62                 else
 63                     begin
 64                         next_state <= S1;
 65                     end
 66                     
 67         S2:        if(key == 2'b01)
 68                     begin
 69                         next_state <= S3;
 70                     end
 71                 else if(key == 2'b10 || key == 2'b11)
 72                     begin
 73                         next_state <= IDLE;
 74                     end
 75                 else 
 76                     begin
 77                         next_state <= S2;
 78                     end
 79         
 80                     
 81         S3:        if(key != 2'b00)
 82                     begin
 83                         next_state <= IDLE;
 84                     end
 85                 else
 86                     begin
 87                         next_state <= S3;
 88                     end
 89         
 90         default:    begin
 91                         next_state <= IDLE;
 92                     end
 93         
 94     endcase
 95     
 96 //三段式,第三段,采用同步时序逻辑,或者组合逻辑描述输出,因为组合逻辑容易产生毛刺,所以这里使用同步时序输出
 97 always @(posedge clk or negedge rst_n)
 98     case(currnt_state)
 99         IDLE:        led <= 3'b111;
100         S1:            led <= 3'b110;
101         S2:            led <= 3'b101;
102         S3:            led <= 3'b011;
103         default:    led <= 3'b111;
104     endcase
105         
106 endmodule        
View Code

 

 5.4 野火提到的新式二段式:

  1 //采用Mealy型状态机,野火提到的二段式新写法
  2 //1. 空闲状态----所有灯熄灭
  3 //2. 状态S1----------点亮LED[0]
  4 //3. 状态S2----------点亮LED[1]
  5 //4. 状态S3----------点亮LED[2]
  6 module st(
  7 input clk,
  8 input rst_n,
  9 
 10 input [1:0]key,
 11 
 12 output reg [2:0]led
 13 );
 14 
 15 parameter    IDLE = 4'b0001;
 16 parameter     S1 = 4'b0010;
 17 parameter     S2 = 4'b0100;
 18 parameter    S3 = 4'b1000;
 19 
 20 reg [3:0] state;
 21 
 22         
 23 //二段式,第一段,将状态转移、状态转移条件写入一个always语句块中,采用时序逻辑
 24 always @(posedge clk or negedge rst_n)
 25     if(!rst_n)
 26         state <= IDLE;    
 27     else case(state)
 28             IDLE:    if(key == 2'b01)
 29                         begin
 30                             state <= S1;
 31                         end
 32                     else if(key == 2'b10)
 33                         begin
 34                             state <= S2;
 35                         end
 36                     else if(key == 2'b11)
 37                         begin
 38                             state <= S3;
 39                         end
 40                     else 
 41                         begin
 42                             state <= IDLE;
 43                         end
 44             
 45             S1:        if(key == 2'b01)
 46                         begin
 47                             state <= S2;
 48                         end
 49                     else if(key == 2'b10)
 50                         begin
 51                             state <= S3;
 52                         end
 53                     else if(key == 2'b11)
 54                         begin
 55                             state <= IDLE;
 56                         end
 57                     else
 58                         begin
 59                             state <= S1;
 60                         end
 61                         
 62             S2:        if(key == 2'b01)
 63                         begin
 64                             state <= S3;
 65                         end
 66                     else if(key == 2'b10 || key == 2'b11)
 67                         begin
 68                             state <= IDLE;
 69                         end
 70                     else 
 71                         begin
 72                             state <= S2;
 73                         end
 74             
 75                         
 76             S3:        if(key != 2'b00)
 77                         begin
 78                             state <= IDLE;
 79                         end
 80                     else
 81                         begin
 82                             state <= S3;
 83                         end
 84             
 85             default:    begin
 86                             state <= IDLE;
 87                         end
 88         
 89     endcase
 90     
 91 //二段式,第二段,采用同步时序逻辑,或者组合逻辑描述输出,因为组合逻辑容易产生毛刺,所以这里使用同步时序输出
 92 always @(posedge clk or negedge rst_n)
 93     case(state)
 94         IDLE:        led <= 3'b111;
 95         S1:            led <= 3'b110;
 96         S2:            led <= 3'b101;
 97         S3:            led <= 3'b011;
 98         default:    led <= 3'b111;
 99     endcase            
100         
101 endmodule        
View Code

 

六、实验

  以《FPGA Verilog开发实战指南——基于Altera EP4CE10》状态机一章后面的拓展训练为例,可知共有六个状态,如下

 

代码(未实现可乐的输出及找零,若要实现,再加两个always语句块输出即可):

  1 //采用Mealy型状态机,三段式写法
  2 module state(
  3     input clk,                    //系统输入时钟
  4     input rst_n,                //系统复位信号,低电平有效
  5     
  6     input m_half,               //0.5元
  7     input m_one,                //1元
  8 
  9     output reg  [3:0]led        //4个LED灯控制
 10 
 11 );
 12 
 13 //状态编码使用独热码方式
 14 parameter   IDLE    =   7'b000_0001;    //空闲状态    
 15 parameter   STATE1  =   7'b000_0010;    //0.5元
 16 parameter   STATE2  =   7'b000_0100;    //1元
 17 parameter   STATE3  =   7'b000_1000;    //1.5元
 18 parameter   STATE4  =   7'b001_0000;    //2元
 19 parameter   STATE5  =   7'b010_0000;    //2.5元
 20 parameter   STATE6  =   7'b100_0000;    //3元
 21 
 22 parameter    SYS_FRQ        =    50;                                        //时钟输入频率,50MHz
 23 parameter    STATE_TIME     =   24'd10_000_000;                            //状态停留时间,单位为us,这里是10秒
 24 parameter    LED_FLOW_TIME    =    18'd200_000;                        //LED流水灯亮间隔时间,单位us,这里是0.2s
 25 
 26 parameter    STATE_CNT_TIME    =    STATE_TIME * SYS_FRQ;                //状态停留,所需要得时钟周期数
 27 parameter    LED_FLOW_CNT_TIME    =    LED_FLOW_TIME * SYS_FRQ;        //LED流水灯,每个灯亮的时钟周期数
 28 
 29 reg [28:0]    state4_cnt;                 //STATE4 10s计数器
 30 reg [28:0]    state5_cnt;                 //STATE5 10s计数器
 31 reg [28:0]    state6_cnt;                 //STATE6 10s计数器
 32 
 33 reg [23:0]    flow_cnt;                 //0.2s计数器
 34 reg led_flow_flag;                     //流水灯流向标志,0--从左往右,1--从右往左
 35 reg [1:0]led_flow_cnt;                 //流水灯计数器
 36 
 37 reg [6:0]   current_state;             //当前状态
 38 reg [6:0]   next_state;                 //下一个状态
 39 
 40 
 41 
 42 wire key_m_one;
 43 wire key_m_half;
 44 
 45 wire [1:0]   coin;
 46 
 47 
 48 //将两个按键拼接在一起,方便后面判断
 49 assign  coin    =   {key_m_one, key_m_half};
 50 
 51 //一元投币按键滤波,key_flag为高则表示按键按下
 52 key_filter    key1(
 53 .clk(clk),
 54 .rst_n(rst_n),
 55 .key_in(m_one),
 56 .key_flag(key_m_one)
 57 
 58 );
 59 
 60 //0.5元投币按键滤波,key_flag为高则表示按键按下
 61 key_filter    key2(
 62 .clk(clk),
 63 .rst_n(rst_n),
 64 .key_in(m_half),
 65 .key_flag(key_m_half)
 66 
 67 );
 68 
 69 
 70 
 71 //三段式,第一段,采用同步时序逻辑描述状态转移
 72 always  @(posedge clk or negedge rst_n)
 73     if(!rst_n)
 74         current_state   <=  IDLE;
 75      else
 76         current_state   <= next_state;
 77 
 78 
 79 //三段式,第二段,采用组合逻辑描述状态转移条件,电平触发
 80 always  @(*)
 81 begin
 82     //state1_6_flag <= 1'b1;
 83     case(current_state)
 84             IDLE    :  if(coin ==  2'b01)
 85                             next_state  <=  STATE1;
 86                         else if(coin == 2'b10)
 87                             next_state  <=  STATE2;
 88                         else
 89                             next_state  <=  IDLE;
 90             
 91             STATE1  :   if(coin ==  2'b01)
 92                             next_state  <=  STATE2;
 93                         else if(coin == 2'b10)
 94                             next_state  <=  STATE3;
 95                         else
 96                             next_state  <=  STATE1;
 97                       
 98             STATE2  :     if(coin ==  2'b01)
 99                             next_state  <=  STATE3;
100                         else if(coin == 2'b10)                        
101                             next_state  <=  STATE4;                                                    
102                         else
103                             next_state  <=  STATE2;
104                             
105             STATE3  :     if(coin ==  2'b01)                        
106                             next_state  <=  STATE4;                                                    
107                         else if(coin == 2'b10)
108                             next_state  <=  STATE5;
109                         else
110                             next_state  <=  STATE3;
111                            
112             STATE4  :    if(coin ==  2'b01)                        
113                             next_state  <=  STATE5;                                                    
114                         else if(coin == 2'b10)
115                             next_state  <=  STATE6;
116                         else if(state4_cnt == STATE_CNT_TIME - 1'b1)            //状态4,若一直持续10s,则状态跳转到空闲状态                        
117                             next_state <=    IDLE;                                            
118                         else                        
119                             next_state  <=  STATE4;                    
120 
121             STATE5  :  if(coin ==  2'b01 || coin == 2'b10)                        
122                             next_state  <=  STATE6;                            
123                         else if(state5_cnt == STATE_CNT_TIME - 1'b1)        //状态5,若一直持续10s,则状态跳转到空闲状态                        
124                             next_state <= IDLE;                            
125                         else                        
126                             next_state  <=  STATE5;                            
127 
128             STATE6  :  if(state6_cnt == STATE_CNT_TIME - 1'b1)                   //状态5,若一直持续10s,则状态跳转到空闲状态                        
129                             next_state  <=  IDLE;                               
130                         else                        
131                             next_state <= STATE6;                            
132             
133             default    :    next_state    <=    IDLE;
134                          
135     endcase
136 end
137 
138 //三段式,第三段,采用同步时序逻辑,或者组合逻辑描述输出,因为组合逻辑容易产生毛刺,所以这里使用同步时序输出
139 always    @(posedge clk or negedge rst_n)
140     if(!rst_n)
141         led <= 4'b1111;    //野火EP4CE10 Pro开发板,LED为高时熄灭
142     else
143         case(current_state)
144             IDLE    :    led    <= 4'b1111;
145             STATE1    :    led <= 4'b1110;
146             STATE2    :    led <= 4'b1100;
147             STATE3    :    led    <= 4'b1000;
148             STATE4    :    led <= 4'b0000;
149             STATE5    :    if(state5_cnt == 29'd0)        //刚进入状态5时,给LED赋初值,以便实现流水效果
150                             led <= 4'b1110;
151                         else if(flow_cnt == LED_FLOW_CNT_TIME - 1'b1)
152                             led <= {led[2:0],led[3]};
153                         else
154                             led <= led;
155                             
156             STATE6    :    if(state6_cnt == 29'd0)
157                             led <= 4'b1110;                    //刚进入状态5时,给LED赋初值,以便实现流水效果
158                         else if(led_flow_flag == 1'b0)        //从右往左
159                             begin
160                                 if(flow_cnt == LED_FLOW_CNT_TIME - 1'b1)
161                                     led <= {led[2:0], led[3]};    
162                                 else
163                                     led <= led;
164                             end
165                         else if(led_flow_flag == 1'b1)        //从左往右
166                             begin
167                                 if(flow_cnt == LED_FLOW_CNT_TIME - 1'b1)
168                                     led <= {led[0], led[3:1]};
169                                 else
170                                     led <= led;
171                             end
172                         else
173                             led <= led;
174                         
175         endcase
176 
177 
178 
179 //流水灯每个灯亮计数器,只有状态5~6用到
180 always @(posedge clk or negedge rst_n)
181     if(!rst_n)
182         flow_cnt <= 24'b0;
183     else if(flow_cnt == LED_FLOW_CNT_TIME - 1'b1)
184             flow_cnt <= 24'b0;
185     else
186         flow_cnt <= flow_cnt + 1'b1;
187 
188 
189 //流水灯单方向流水灯计数器,只有状态5~6用到
190 always @(posedge clk or negedge rst_n)
191     if(!rst_n)
192         led_flow_cnt <= 2'b0;
193     else if(flow_cnt == LED_FLOW_CNT_TIME - 1'b1)
194         begin
195             if(led_flow_cnt == 2'b11)
196                 led_flow_cnt <= 2'b0;
197             else
198                 led_flow_cnt <= led_flow_cnt + 1'b1;
199         end
200         
201     else 
202         led_flow_cnt <= led_flow_cnt;
203         
204 
205 //状态4计数器
206 always @(posedge clk or negedge rst_n)
207 if(!rst_n)
208     state4_cnt <= 29'b0;
209 else if(current_state == STATE4)    
210     begin
211         if(state4_cnt == STATE_CNT_TIME - 1'b1)
212             state4_cnt <= 29'b0;
213         else
214             state4_cnt <= state4_cnt + 1'b1;
215     end
216 else
217         state4_cnt <= 29'b0;
218 
219     
220 //状态5计数器
221 always @(posedge clk or negedge rst_n)
222 if(!rst_n)
223     state5_cnt <= 29'b0;
224 else if(current_state == STATE5)    
225     begin
226         if(state5_cnt == STATE_CNT_TIME - 1'b1)
227             state5_cnt <= 29'b0;
228         else
229             state5_cnt <= state5_cnt + 1'b1;
230     end
231 else
232     state5_cnt <= 29'b0;
233 
234 
235 //状态6计数器
236 always @(posedge clk or negedge rst_n)
237 if(!rst_n)
238     state6_cnt <= 29'b0;
239 else if(current_state == STATE6)    
240     begin
241         if(state6_cnt == STATE_CNT_TIME - 1'b1)
242             state6_cnt <= 29'b0;
243         else
244             state6_cnt <= state6_cnt + 1'b1;
245     end
246 
247 else
248     state6_cnt <= 29'b0;
249 
250 
251 //状态6流向标志,流水方向边界,分别为四个LED灯的两边,即LED[0]、LED[3],在这两个地方需要转换标志,以改变其流向
252 always @(posedge clk or negedge rst_n)
253     if(!rst_n)
254         led_flow_flag <= 1'b0;
255     else if(led[0] == 1'b0)
256         led_flow_flag <= 1'b0;
257     else if(led[3] == 1'b0)
258         led_flow_flag <= 1'b1;
259     else
260         led_flow_flag <= led_flow_flag;
261 
262 
263 endmodule
View Code

 

按键滤波代码:

 1 //按键滤波,低为按键按下,输出高表示按键按下
 2 module key_filter    #(
 3 parameter    CNT_MAX    =    20'd999_999 //20ms延时时间
 4 
 5 )(
 6 input    clk,
 7 input    rst_n,
 8 input     key_in,
 9 
10 
11 output reg key_flag
12 
13 );
14 
15 reg    [19:0]    cnt_20ms;    //20ms计数器
16 
17 //检测按键为低时,开始重零计时
18 always @(posedge clk or negedge rst_n)
19     if(!rst_n)
20         cnt_20ms <= 20'b0;
21     else if(key_in == 1'b1)
22         cnt_20ms <= 20'b0;
23     else if(cnt_20ms == CNT_MAX && key_in == 1'b0)
24         cnt_20ms <= cnt_20ms;
25     else
26         cnt_20ms <= cnt_20ms + 1'b1;
27 
28 //一旦延时20ms还是检测为低时,则表示一次有效按键按下
29 always @(posedge clk or negedge rst_n)
30     if(!rst_n)
31         key_flag <= 1'b0;
32     else if(cnt_20ms == CNT_MAX - 1'b1)
33         key_flag <= 1'b1;
34     else
35         key_flag <= 1'b0;
36 
37 
38 endmodule
View Code

 

总结:

1.  在状态机比较复杂时,最好使用三段式,或者新式二段式,这样比较清晰明了,从以上代码就可以看出

2.  输出采用时序逻辑,这样可以消除组合逻辑不可避免的毛刺

3.  带有按键输入的,一定要进行消抖,不然其状态及其不稳定,写这篇文章已体验多次

4. 另外,代码中的0.2s计数器,其实跟状态不是同步的,所以在流水灯亮第一颗灯时,其时间大概率不会是0.2s,对于要求高的,需要将0.2s与状态同步

5. 状态4~6都各自使用了一个10s的计数器,这样代码会比较容易实现,如果都用同一个,本人是没有写出来,越写越乱,与其这么乱,不如就加两个计数器,简单明了

6. 以上代码,包括编写格式都实际上机验证,可以直接验证。编写格式代码,需要加上按键消抖模块

 

posted @ 2021-05-17 23:04  秋水寒林  阅读(775)  评论(0编辑  收藏  举报