wzd5230 发表于 2014-7-30 01:40:25

初学FPGA,跟随特权的视频,但是按键消抖的历程仿真不成功

初学FPGA,跟随特权的视频,这几天正好开始弄特权的那个按键消抖的程序,因为用的不是特权的板子,自己的板子上只有两个按键和两个led显示灯,所以将特权的代码改了下。将代码修改之后烧入芯片中,功能可以实现消抖,因为学习写写testbench,就拿按键消抖的代码在modelsim下进行仿真,发现仿真过不了,以为是自己程序改错了。于是将特权的程序进行modelsim仿真,发现他的程序在modelsim下仿真结果和我的一样。实在不知道什么原因。现贴上特权的verilog程序和我自己写的testbech代码,以及modelsim仿真波形,麻烦帮看看问题出在哪里。在此谢过。

特权的按键检测程序:
//说明:当三个独立按键的某一个被按下后,相应的LED被点亮;
//                再次按下后,LED熄灭,按键控制LED亮灭

module sw_debounce(
                    clk,rst_n,
                        sw1_n,sw2_n,sw3_n,
                           led_d1,led_d2,led_d3
                    );

input   clk;        //主时钟信号,50MHz
input   rst_n;        //复位信号,低有效
input   sw1_n,sw2_n,sw3_n;         //三个独立按键,低表示按下
outputled_d1,led_d2,led_d3;        //发光二极管,分别由按键控制

//---------------------------------------------------------------------------
reg key_rst;

always @(posedge clkor negedge rst_n)
    if (!rst_n) key_rst <= 3'b111;
    else key_rst <= {sw3_n,sw2_n,sw1_n};

reg key_rst_r;       //每个时钟周期的上升沿将low_sw信号锁存到low_sw_r中

always @ ( posedge clkor negedge rst_n )
    if (!rst_n) key_rst_r <= 3'b111;
    else key_rst_r <= key_rst;
   
//当寄存器key_rst由1变为0时,led_an的值变为高,维持一个时钟周期
wire key_an = key_rst_r & ( ~key_rst);

//---------------------------------------------------------------------------
regcnt;        //计数寄存器

always @ (posedge clkor negedge rst_n)
    if (!rst_n) cnt <= 20'd0;        //异步复位
        else if(key_an) cnt <=20'd0;
    else cnt <= cnt + 1'b1;

reg low_sw;

always @(posedge clkor negedge rst_n)
    if (!rst_n) low_sw <= 3'b111;
    else if (cnt == 20'hfffff)         //满20ms,将按键值锁存到寄存器low_sw中       cnt == 20'hfffff
      low_sw <= {sw3_n,sw2_n,sw1_n};
      
//---------------------------------------------------------------------------
reg low_sw_r;       //每个时钟周期的上升沿将low_sw信号锁存到low_sw_r中

always @ ( posedge clkor negedge rst_n )
    if (!rst_n) low_sw_r <= 3'b111;
    else low_sw_r <= low_sw;
   
//当寄存器low_sw由1变为0时,led_ctrl的值变为高,维持一个时钟周期
wire led_ctrl = low_sw_r & ( ~low_sw);

reg d1;
reg d2;
reg d3;

always @ (posedge clk or negedge rst_n)
    if (!rst_n) begin
      d1 <= 1'b0;
      d2 <= 1'b0;
      d3 <= 1'b0;
      end
    else begin                //某个按键值变化时,LED将做亮灭翻转
      if ( led_ctrl ) d1 <= ~d1;       
      if ( led_ctrl ) d2 <= ~d2;
      if ( led_ctrl ) d3 <= ~d3;
      end

assign led_d3 = d1 ? 1'b1 : 1'b0;                //LED翻转输出
assign led_d2 = d2 ? 1'b1 : 1'b0;
assign led_d1 = d3 ? 1'b1 : 1'b0;

endmodule

自己写的testbench代码
`timescale 10 ns/ 1 ps
module sw_debounce_vlg_tst();
// constants                                          
// general purpose registers
reg eachvec;
// test vector input registers
reg clk;
reg rst_n;
reg sw1_n;
reg sw2_n;
reg sw3_n;
// wires                                             
wire led_d1;
wire led_d2;
wire led_d3;

// assign statements (if any)                        
sw_debounce i1 (
// port map - connection between master ports and signals/registers   
        .clk(clk),
        .led_d1(led_d1),
        .led_d2(led_d2),
        .led_d3(led_d3),
        .rst_n(rst_n),
        .sw1_n(sw1_n),
        .sw2_n(sw2_n),
        .sw3_n(sw3_n)
);
initial
begin
clk = 1'b0;
forever
begin
    #10 clk = ~clk;
end
end               

initial
begin
    rst_n = 1'b0;
    #100;
    rst_n = 1'b1;
end            

initial
begin
    sw1_n = 1'b1;
    sw2_n = 1'b1;
    sw3_n = 1'b1;
    #1000000
    sw1_n = 1'b0;
    sw2_n = 1'b0;
    sw3_n = 1'b0;
    #7000000
    sw1_n = 1'b1;
    sw2_n = 1'b1;
    sw3_n = 1'b1;
    #3000000
    $stop;
end
endmodule

下面是特权的代码的仿真波形,模拟一下按键的按下,有30mS的按下延时

DepravedLucien 发表于 2014-7-30 09:34:50

你测下你仿真的时钟 clk,应该不是50MHz,而是5Mhz吧

wzd5230 发表于 2014-7-30 12:14:29

DepravedLucien 发表于 2014-7-30 09:34
你测下你仿真的时钟 clk,应该不是50MHz,而是5Mhz吧

你是正确的,我clk波形时钟错了,改成50MHz。现在仿真通过了

wzd5230 发表于 2014-7-30 12:26:48

DepravedLucien 发表于 2014-7-30 09:34
你测下你仿真的时钟 clk,应该不是50MHz,而是5Mhz吧

你好,我这边还有一个疑问,我自己按照特权的思路自己写的按键消抖程序,我下载到开发板上,可以控制led灯翻转,但是仿真也过不了。我找了两个多小时。在代码中间加上了一个测试输出引脚,用来输出clk/4的方波,但是什么波形都没有。麻烦帮看一下哪里出问题了。谢谢了。

按键检测消抖代码:
module key_scan(
        key_in,
        led_out,
        clk,
        rst_n,
        test_pin);

        /* 端口声明 */
        input key_in;                                                                        /* 两个按键输入,低电平有效*/
        input clk;                                                                                          /* 时钟输入 */
        input rst_n;                                                                                  /* 复位输入,低电平有效复位 */
        output led_out;                                                                /* led驱动输出,高电平驱动led发光 */
        output test_pin;
       
        reg led_out;
        reg test_pin;
       
        initial
        begin
    test_pin <= 1'b0;
end
        /*************************************************/
        /* 模块功能实现 */
       
        /* 在clk时钟下,保存当前clk上升沿时key的输入值 */
        reg temp1_key_in;               
        always@(posedge clk or negedge rst_n)
        begin
                if(!rst_n)
                        temp1_key_in <= 2'b11;
                else
                        temp1_key_in <= key_in;
        end
       
        /* 在clk时钟下,保存上一次的clk上升沿时key的输入值 */
        reg temp2_key_in;
        always@(posedge clk or negedge rst_n)
        begin
                if(!rst_n)
                        temp2_key_in <= 2'b11;
                else
                        temp2_key_in <= temp1_key_in;
        end
       
        /* 判断出在clk时钟下,按键在两次时钟采样值不同的按键,即被按下的按键 */
        wire temp3_key_in;
        assign temp3_key_in = temp2_key_in & (~temp1_key_in);

        /* 产生20ms的计时时间 */
        parameter COUNT_TOP_20MS = 20'hF4240;                                /* 定义20mS计时的定时器上限 */
        reg count;
        always@(posedge clk or negedge rst_n)
        begin
                if(!rst_n)
                        count <= 20'b0;
                else
                begin
                        if(count == COUNT_TOP_20MS || temp3_key_in != 2'b00)
                                count <= 20'b0;
                        else
                                count <= count + 1'b1;
                end
        end
       
        /* test pin,it should output wave */
        reg cnt;
        always@(posedge clk or negedge rst_n)
        begin
    if(!rst_n)
      cnt <= 2'b00;
    else
    begin
      cnt <= cnt + 2'b1;
      test_pin = (cnt==2'b11)?(~test_pin):(test_pin);
    end
end
       
        /* 以20mS为时间基准,记录当前20mS时的按键输入值 */
        reg temp4_key_in;
        always@(posedge clk or negedge rst_n)
        begin
                if(!rst_n)
                        temp4_key_in <= 2'b11;
                else if(count == (COUNT_TOP_20MS-1))
                begin
                        temp4_key_in <= key_in;
                end
        end
       
        /* 以20mS为时间基准,记录前一个20mS时的按键输入值 */
        reg temp5_key_in;
        always@(posedge clk or negedge rst_n)
        begin
                if(!rst_n)
                        temp5_key_in <= 2'b11;
                else if(count == (COUNT_TOP_20MS-1))
                begin
                        temp5_key_in <= temp4_key_in;
                end
        end
       
        /* 根据两次20mS前后的采样值来判断按键在去抖动之后的按键值 */
        wire temp6_key_in;
        assign temp6_key_in = temp5_key_in & (~temp4_key_in);
       
        /* 根据按键的去抖动之后的输入值来判断led驱动引脚输出什么电平 */
        always@(posedge clk or negedge rst_n)
        begin
                if(!rst_n)
                        led_out <= 2'b00;                                                                /* 复位时将输入低电平,led灯熄灭 */
                else
                begin
                        case(temp6_key_in)
                                2'b11:led_out <= ~led_out;
                                2'b10:led_out <= ~led_out;
                                2'b01:led_out <= ~led_out;
                                default:;
                        endcase
                end
        end

endmodule
       

testbench代码
// Copyright (C) 1991-2009 Altera Corporation
// Your use of Altera Corporation's design tools, logic functions
// and other software and tools, and its AMPP partner logic
// functions, and any output files from any of the foregoing
// (including device programming or simulation files), and any
// associated documentation or information are expressly subject
// to the terms and conditions of the Altera Program License
// Subscription Agreement, Altera MegaCore Function License
// Agreement, or other applicable license agreement, including,
// without limitation, that your use is for the sole purpose of
// programming logic devices manufactured by Altera and sold by
// Altera or its authorized distributors.Please refer to the
// applicable agreement for further details.

// *****************************************************************************
// This file contains a Verilog test bench template that is freely editable to
// suit user's needs .Comments are provided in each section to help the user   
// fill out necessary details.                                                
// *****************************************************************************
// Generated on "07/30/2014 00:13:51"
                                                                              
// Verilog Test Bench template for design : key_scan
//
// Simulation tool : ModelSim (Verilog)
//

`timescale 10 ns/ 1 ps
module key_scan_vlg_tst();
// constants                                          
// general purpose registers
reg eachvec;
// test vector input registers
reg clk;
reg key_in;
reg rst_n;
// wires                                             
wire led_out;
wire test_pin;

// assign statements (if any)                        
key_scan i1 (
// port map - connection between master ports and signals/registers   
        .clk(clk),
        .key_in(key_in),
        .led_out(led_out),
        .rst_n(rst_n),
        .test_pin(test_pin)
);
initial
begin
clk = 1'b0;
rst_n = 1'b1;
forever
begin
#1 clk <= ~clk;
end
end

initial
begin
key_in <= 2'b11;
#1000000
key_in <= 2'b01;
#3000000
key_in <= 2'b11;
#3000000
$stop;
end                                                   
endmodule


以下下是我的仿真波形:


下面是我clk的放大波形,这个没有弄错了。clk周期刚好是20ns

Nuker 发表于 2014-7-30 12:49:25

这个debounce的思路就按键输入的降沿复位cnt,当cnt溢出时再次检查按键输入,如果还是低电平,LED翻转
cnt溢出的时间为(10×2)*(2**20) = 20971520
那么按键按下保持的时间至少要大于这个值
所以,testbench第54行改为
#23000000
就可以看到LED翻转了

DepravedLucien 发表于 2014-7-30 13:54:29

wzd5230 发表于 2014-7-30 12:26
你好,我这边还有一个疑问,我自己按照特权的思路自己写的按键消抖程序,我下载到开发板上,可以控制led ...

1、代码中 为 test_pin赋初值不使用initial语句,在always里面赋值;
2、testbench中复位有效一段时间,你现在复位一直为1;

wzd5230 发表于 2014-7-30 22:50:49

DepravedLucien 发表于 2014-7-30 13:54
1、代码中 为 test_pin赋初值不使用initial语句,在always里面赋值;
2、testbench中复位有效一段时间, ...

我找到错误了,是我的按键消抖代码有问题,我不理解的是那个复位信号为什么在testbench中要让其初始复位一段时间,一直让其是1‘b1(即不复位)的话,为什么那个测试引脚无法输出1/4的clk频率的信号,只要在testbench中加上复位,这个测试信号就可以正常输出。

DepravedLucien 发表于 2014-8-1 09:14:02

wzd5230 发表于 2014-7-30 22:50
我找到错误了,是我的按键消抖代码有问题,我不理解的是那个复位信号为什么在testbench中要让其初始复位 ...

因为它是仿真啊,如果你是下载到芯片上,可能没问题,仿真的话就得为其赋初值,否则它会是不确定状态,就是你仿真波形看到的“红色”的线,它的状态是X,所以你的功能无法继续跑下去

wzd5230 发表于 2014-8-1 14:01:45

DepravedLucien 发表于 2014-8-1 09:14
因为它是仿真啊,如果你是下载到芯片上,可能没问题,仿真的话就得为其赋初值,否则它会是不确定状态,就 ...

哦,知道了,非常感谢你的回答,谢谢你!!

wzd5230 发表于 2014-8-2 09:02:48

DepravedLucien 发表于 2014-8-1 09:14
因为它是仿真啊,如果你是下载到芯片上,可能没问题,仿真的话就得为其赋初值,否则它会是不确定状态,就 ...

又来麻烦你了,我用jtag方式下载程序但是有时候会出现程序没有运行的情况,或者运行不正常,重新下载一次就可以了,不知道你有遇到这个问题吗?就是驱动数码管的程序,然后用到了外部50MHz时钟。没有特殊的地方。

DepravedLucien 发表于 2014-8-6 17:56:03

wzd5230 发表于 2014-8-2 09:02
又来麻烦你了,我用jtag方式下载程序但是有时候会出现程序没有运行的情况,或者运行不正常,重新下载一次 ...


你使用的是开发板么?有没有例程,例程每次下载会有这个问题么?
这个我觉得需要确认是程序下载的问题,还是程序本身设计的问题;
可以通过signaltap来调试
也有可能是usb blaster的问题

wzd5230 发表于 2014-8-6 21:18:01

DepravedLucien 发表于 2014-8-6 17:56
你使用的是开发板么?有没有例程,例程每次下载会有这个问题么?
这个我觉得需要确认是程序下载的问题, ...

使用的是开发板,之前没有遇到过这种情况,就上次自己写的程序,烧进去出现两次,后面再烧又没有这个现象了。
我用的是usb blaster。后面使用中我再观察吧,如果遇到问题我再和你交流。
非常感谢
页: [1]
查看完整版本: 初学FPGA,跟随特权的视频,但是按键消抖的历程仿真不成功