基于状态机的按键消抖实现

摸鱼记录 Day_14   !(^O^)y

review

        在day_13中以按键状态判断为例学习了状态分析基于状态机的按键消抖原理-CSDN博客

        分析得到了下图:

  今日任务:完成此过程   !(^O^)y

小梅哥对应视频:

        15B 基于状态机的按键消抖Verilog实现_哔哩哔哩_bilibili

        15C 基于Verilog系统函数语法的按键抖动模拟与仿真_哔哩哔哩_bilibili

1.  design sources

module key_judge(
                 input clk , 
                 input reset_n,
                 input key,
                 output reg key_flag,
                 output reg key_state);

        // nedge_key pedge_key

         // key_now   0:IDLE   1:FILTER0   2:DOWN   3:FILTER1
        // cnt 20ms/20ns = 1000000 ;

        reg [1:0]key_now;
        reg [19:0] cnt;
        parameter cnt_N = 1000000;
        always@(posedge clk or negedge reset_n ) 
            if(!reset_n) 
              
            else 
                    case(key_now)
                        0:        IDLE                 
                        1:        FILTER0                                 
                        2:        DOWN                  
                        3:        FILTER1

                     endcase

        // 注意cnt清0

module key_judge(
                 input clk , 
                 input reset_n,
                 input key,
                 output reg key_flag,
                 output reg key_state);
                 
        // nedge_key pedge_key
        reg dff_k_0 , dff_k_1 ;
        reg r_key; 
        wire  nedge_key, pedge_key;
        always@(posedge clk )    
            dff_k_0 <= key ;
        always@(posedge clk )    
            dff_k_1 <= dff_k_0 ;
        always@(posedge clk )    
            r_key <= dff_k_1 ;
            
        assign nedge_key = (r_key == 1)&&(dff_k_1 == 0);
        assign pedge_key = (r_key == 0)&&(dff_k_1 == 1);
   
        // key_now   0:IDLE   1:FILTER0   2:DOWN   3:FILTER1
        // cnt 20ms/20ns = 1000000 ;
        reg [1:0]key_now;
        reg [19:0] cnt;
        parameter cnt_N = 1000000;
        always@(posedge clk or negedge reset_n ) 
            if(!reset_n) 
                begin
                    key_now <= 0 ;
                    cnt <= 0;
                    key_flag <= 0;
                    key_state <= 1;
                end
            else 
                begin
                    key_flag <= 0;
                    case(key_now)
                        0:
                           if(!nedge_key) key_now <= 0;
                           else 
                               begin 
                                 cnt <= 0 ;
                                 key_now <= 1; 
                               end
                               
                        1:
                            if(pedge_key) key_now <= 0;
                            else if(cnt >= cnt_N - 1) 
                                begin
                                    cnt <= 0 ;
                                    key_now <= 2;
                                    key_flag <= 1;
                                    key_state <= 0;
                                end
                            else cnt <= cnt + 1'b1;
                            
                        2:
                            if(!pedge_key) key_now <= 2;
                            else
                                begin
                                    cnt <= 0 ;
                                    key_now <= 3;
                                end
                        
                        3:
                            if(nedge_key) key_now <= 2;
                            else if(cnt >= cnt_N - 1)
                                 begin
                                    cnt <= 0 ;
                                    key_now <= 0;
                                    key_flag <= 1;
                                    key_state <= 1;
                                end
                            else cnt <= cnt + 1'b1;    
                        
                    endcase
                end

endmodule

//hhh 在每次状态变化的时候清零一下,就好用了捏  !(^O^)y

2.  key_tb

`timescale 1ns / 1ns
module key_tb( );
    reg clk ,reset_n , key;
    wire key_flag ,  key_state;
    key_judge#(.cnt_N(1000))
         key_(
                 .clk(clk) , 
                 . reset_n(reset_n),
                 . key(key),
                 . key_flag(key_flag),
                 . key_state(key_state)
                 );
   initial clk = 1 ;
   always #10 clk = ~clk ;
   initial
    begin
        reset_n = 0 ; #201;
        reset_n = 1 ;
        key = 1 ; #50;
        key = 0 ; #30;
        key = 1 ; #60;#200;
        key = 0 ;#30000;
        key = 1 ; #50;
        key = 0 ; #30;
        key = 1 ; #60;#200;
        key = 0 ;#200;
        key = 1 ;
        #30000;
        $stop;
    end 
    
endmodule

//今天调试发现这个有大概两三个时钟周期的小误差:

          判断上升/下降沿用了打拍的处理

3.  关于 testbench  优化

3.1  Verilog 语法学习

目标:

        学习task定义与调用

               erilog语法之十一:任务(task)和函数(function) - 知乎 (zhihu.com)

        学习random函数

                总结verilog产生随机数的$random和seed - super_star123 - 博客园 (cnblogs.com)

        学习循环repeat

                Verilog 循环语句(while, for, repeat, forever)_verilog while-CSDN博客

task <任务名>;      

        <端口及数据类型声明语句>      

        <语句1>      

        <语句2>      

        .....      

        <语句n>

endtask

举个栗子:

task my_task;    

        input a, b;    

        inout c;    

        output d;    

         …    

        <语句> //执行任务工作相应的语句    

        …    

        c = foo1; //赋初始值    

        d = foo2; //对任务的输出变量赋值

endtask

调用:my_task(in_a , in_b , io_c , o_d);

$random(seed)

给random传入了参数seed,因此random根据seed来产生随机数。seed不同,产生的随机数的序列也不同。而且,每执行一次$random(seed)产生一个随机数,seed也自动更新一次。

建议用deposit的方式在仿真时改变seed的初值,使$random(seed)产生不同的随机数序列

{$random(seed)}     { }取绝对值

repeat

 verilog 四种循环: while,for,repeat, forever

循环语句只能在 always 或 initial 块中使用,但可以包含延迟表达式。

while

while (condition)

        begin
            …
        end

                 while 循环中止条件为 condition 为假。

for

for(initial_assignment; condition ; step_assignment)  

        begin
            …
        end

                 initial_assignment 为初始条件,condition 为终止条件             

                 step_assignment 为改变控制变量的过程赋值语句

                 i = i + 1 不能写成 i++ ,i = i -1 也不能写成 i -- 

repeat

repeat (loop_times)

         begin
            …
         end

                  repeat 的功能是执行固定次数的循环

                  repeat 循环的次数必须是一个常量变量信号

         如果循环次数是变量信号,则循环次数是开始执行 repeat 循环时变量信号的值。即使执行期间,循环次数代表的变量信号值发生了变化,repeat 执行次数也不会改变。

forever

forever

        begin
            …
        end

         forever 语句一旦执行便无限的执行下去

        系统函数 $finish 可退出 forever

3.2  key_tb_new

`timescale 1ns / 1ns
module key_tb_new( );
    reg clk ,reset_n , key;
    wire key_flag ,  key_state;
    key_judge#(.cnt_N(1000))
         key_(
                 .clk(clk) , 
                 . reset_n(reset_n),
                 . key(key),
                 . key_flag(key_flag),
                 . key_state(key_state)
                 );
   initial clk = 1 ;
   always #10 clk = ~clk ;
   initial
    begin
        reset_n = 0 ; 
        key = 1 ;
        #201;
        reset_n = 1 ;
    key_press(2);

        $stop;
    end 
    
 reg [13:0] rand;
 task key_press;
    input[3:0]seed;
    begin
        key = 1 ;
        #30000;
        repeat(10)
            begin
                rand = {$random(seed)} % 10000;
                #rand;
                key=~key;
            end
        key = 0 ;
        #30000;
        repeat(10)
            begin
                rand = {$random(seed)} % 10000;
                #rand;
                key=~key;
            end
         key = 1 ;
        #50000;
    end
endtask
 
    
endmodule

//  摸鱼结束        !(^O^)y

  • 12
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值