Zedboard---实验六秒计数

Zedboard—实验六秒计数

本节实验将继续使用PmodSSD来实现秒计数,并且详细演示了了仿真调试代码的过程。

实现过程:
1. 需要知道什么时候开始计数;
2. 让计数器可以保持计数值;
3. 将计数值送入数码管显示;

产生秒脉冲

使用上节实验中产生的毫秒脉冲,在此基础上计数100次实现秒脉冲。

integer ms_count = 0;
reg sec_pulse;
always @(posedge clk)
  begin
    sec_pulse <= 0;
    if (ms_pulse)
      if (ms_count == 999)
        begin
          ms_count <= 0;
          sec_pulse <= 1;
        end
      else
        ms_count <= ms_count+1;
  end

同时产生秒脉冲信号sec_pulse使得每隔一秒,计数器加一。代码如下:

reg [7:0] sec_count = 0;
    always @ (posedge clk)
        if (sec_pulse)
            sec_count <= sec_count +1;

这个仿真带来一个问题,我们能够看到毫秒脉冲ms_pulse信号。但是秒脉冲信号sec_pulse呢?这需要仿真一秒时间来观察脉冲,需要花一定的时间,同时存储所有的数据也会占用大量的硬盘空间。一秒就是100000000个时钟周期 。无论如何在仿真的时候都应该减少计数。

仿真类似上述的长计数情况,是一个通用的需求。接下来介绍top文件中的一个参数。

加速仿真

大家想象一个常数使用在设计中。这个参数值在仿真模块中永远不会变。不同的模块中的实例可以有不同的参数值。

所要介绍的参数能够保持计数器的计数值。该参数默认的值为100000。在该默认值作用下,每个1ms能够产生一个ms_pulse信号。参数声明在模块端口声明之前。

module top
#(
  parameter ms_limit = 100000
) (
   input clk,
   input [7:0] switch,
   output reg [7:0] led,
   output reg [6:0] ssd,
   output reg ssdcat
   );

接下来改变计数器声明count赋值ms_limit-1。这个改变其实没有影响原本设计的功能。

  always @(posedge clk)
    begin
      ms_pulse <= 0;
      if (count == ms_limit-1)
    begin
      count <= 0;
      ms_pulse <= 1;
    end
      else
    count <= count+1;
    end

修改测试文件,将ms_limit的默认值改为100。这将使仿真时,每经过100时钟周期产生一个ms_limit脉冲,而不是之前的100000。

  top #(.ms_limit(100)) top
    (
     .clk(clk),
     .switch(switch),
     .led(led)
     );

再次运行仿真,观察秒计数器,是不是每隔1个毫秒计数一次。

使用之前驱动ssd输出的代码:

  wire [3:0]  digit;
  always @(posedge clk)
    case (digit)
      0: ssd <= 7'b1111110;
      1: ssd <= 7'b0110000;
      2: ssd <= 7'b1101101;
      3: ssd <= 7'b1111001;
      4: ssd <= 7'b0110011;
      5: ssd <= 7'b1011011;
      6: ssd <= 7'b1011111;
      7: ssd <= 7'b1110000;
      8: ssd <= 7'b1111111;
      9: ssd <= 7'b1110011;
      10: ssd <= 7'b1110111;
      11: ssd <= 7'b0011111;
      12: ssd <= 7'b1001110;
      13: ssd <= 7'b0111101;
      14: ssd <= 7'b1001111;
      15: ssd <= 7'b1000111;
    endcase

  assign digit = ssdcat ? sec_count[7:4] : sec_count[3:0];

当然,要确保这些信号在使用前有过声明。 例化到FPGA中,观察每秒钟数码管显示是否正确。

输出正确吗?

虽然已经仿真了设计,但是输出正确与否很难通过观察数码管的二进制值来判断。至少很难直观的检查出结果。

7段数码管模块

这就需要一个七段数码管显示的模块。将其应用于test bench,可以使输出的二进制值代替为十进制数字显示值。模块输出输入代码如下:

module ssd_digit
   input enable,
   input [6:0] ssd,
   output reg [3:0] value
   );

将数码管七段编码值对应为十进制显示值:

  always @(*)
    if (enable)
      case (ssd)
        7'b1111110: value = 0;
        7'b0110000: value = 1;
        7'b1101101: value = 2;
        7'b1111001: value = 3;
        7'b0110011: value = 4;
        7'b1011011: value = 5;
        7'b1011111: value = 6;
        7'b1110000: value = 7;
        7'b1111111: value = 8;
        7'b1110011: value = 9;
        7'b1110111: value = 10;
        7'b0011111: value = 11;
        7'b1001110: value = 12;
        7'b0111101: value = 13;
        7'b1001111: value = 14;
        7'b1000111: value = 15;
      endcase

Test bench中例化模块

这里需要两个数码显示模块,并且需要8根线连接输出数码模块。同时,还需要enable使能信号,实际上就是ssdcat信号。

  wire [7:0] digits;
  ssd_digit PmodSSD0
    (
     .enable(~ssdcat),
     .ssd(ssd),
     .value(digits[3:0])
     );

  ssd_digit PmodSSD1
    (
     .enable(ssdcat),
     .ssd(ssd),
     .value(digits[7:4])
     );

自我检测

运行仿真并且观察输出值,检查输出是否正确。在实际工程中,为了方便每次修改代码并验证代码正确性。需要编写一个能够自我检测的仿真测试程序。这样每次代码修改完后,都不必例化到FPGA中验证。如果它通过了测试文件,就说明代码是正确的。

秒计数模块

这个模块需要输入时钟信号,同时输出八位计数值。应用上节中缩短仿真时间的方法,在仿真文件中修改参数值:

`timescale 1ns/1ns
module model
  #(
    parameter ms_limit = 100000
    )
  (
   input clk,
   output [7:0] seconds
   );

  integer counter = 0;
  always @(posedge clk)
    counter <= counter+1;

  assign seconds = counter / (ms_limit * 1000);

endmodule

仿真文件中例化代码:

  wire [7:0] model_seconds;
  model
    #(.ms_limit(100))
  model
    (
     .clk(clk),
     .seconds(model_seconds)
     );

比较结果

接下来就是比较仿真结果的正确性。

  always @(posedge clk)
    begin
      num_checks = num_checks+1;
      if (digits != model_seconds)
        begin
          $display("ERROR: digits value %0x does not match expected value %0x at time %0fns",
               digits,model_seconds,$realtime);
          num_errors = num_errors+1;
        end
    end 

测试代码与RTL的编写是由区别的。注意上面代码中的num_checknum_errors的赋值用的是阻塞式赋值。这是为了在每个时钟沿都检测LED输出以及计数值。如果使用非阻塞式赋值,只能在每个时钟周期计数一个检查结果,由于不知道那个模块是先运行的,因此掩盖了运行错误。

仿真20秒(由于缩短了仿真时间,其实是毫秒)后停止。将重复语句替换为延时语句:

  initial
    begin
      wait (model_seconds == 20);
      $display("Simulation complete at time %0fns.",$realtime);
      if (num_errors > 0)
        $display("*** Simulation FAILED %0d/%0d",num_errors,num_checks);
      else
        $display("*** Simulation PASSED %0d/%0d",num_errors,num_checks);
      $finish;
    end

运行仿真,显示错误如下:

run all
ERROR: digits value 0 does not match expected value 1 at time 1000000.000000ns
ERROR: digits value 0 does not match expected value 1 at time 1000010.000000ns
ERROR: digits value 0 does not match expected value 1 at time 1000020.000000ns
ERROR: digits value 11 does not match expected value 1 at time 1001010.000000ns
ERROR: digits value 0 does not match expected value 1 at time 1002010.000000ns
ERROR: digits value 11 does not match expected value 1 at time 1003010.000000ns
ERROR: digits value 0 does not match expected value 1 at time 1004010.000000ns
ERROR: digits value 11 does not match expected value 1 at time 1005010.000000ns
...
ERROR: digits value 33 does not match expected value 13 at time 19995010.000000ns
ERROR: digits value 11 does not match expected value 13 at time 19996010.000000ns
ERROR: digits value 33 does not match expected value 13 at time 19997010.000000ns
ERROR: digits value 11 does not match expected value 13 at time 19998010.000000ns
ERROR: digits value 33 does not match expected value 13 at time 19999010.000000ns
Simulation complete at time 19999990.000000ns.
*** Simulation FAILED 18136/4000000
$finish called at time : 19999990 ns : File "/home/pete/tutorial6/tutorial6.srcs/sim_1/new/bench.v" Line 85

4000000比较中有18136个错误。在波形图具体分析错误,观察1000000ns仿真时刻的时序:

波形图1

sec_countmodel_seconds计数值并不一致,并且时间延迟了几个时钟周期。分析之前产生ms_pulse的代码:

  integer     count = 0;
  reg         ms_pulse = 0;
  always @(posedge clk)
    begin
      ms_pulse <= 0;
      if (count == ms_limit-1)
    begin
      count <= 0;
      ms_pulse <= 1;
    end
      else
    count <= count+1;
    end

注意ms_pulse是如何由0变为1的。对其赋值做以下修改:

  integer count = 0;
  wire ms_pulse = count == ms_limit-1;
  always @(posedge clk)
    if (ms_pulse)
      count <= 0;
    else
      count <= count+1;

countms_limit-1的比较结果赋值给ms_pulse。这样不仅使得代码更简洁,并且减少了sec_count的延迟。

波形图2

但是计数值仍然延迟于sec_pulse。修改代码,消除计数延迟:

  integer ms_count = 0;
  wire sec_pulse = ms_count == 999;
  always @(posedge clk)
    if (ms_pulse)
      if (sec_pulse)
    ms_count <= 0;
      else
    ms_count <= ms_count+1;

仿真波形图如下:

波形图3

可以看到计数值的更新对齐了sec_pluse,但是sec_pulse不再是一个脉冲,而引起sec_count计数值的频繁计数。修改代码限制sec_pulse的产生:将逻辑运算(ms_count==999 && ms_pulse)的值赋给sec_pulse

  wire sec_pulse = ms_count == 999 && ms_pulse;

仿真波形图:

波形图4

脉冲信号都对齐了,七段数码管解码输出还有一个时钟的延迟。

计数延迟一个周期,改变秒输出为寄存器类型,并且在每个上升沿对其赋值:

  always @(posedge clk)
    seconds <= counter / (ms_limit * 1000);

还要确保修改了输出声明seconds为reg。

运行仿真:

波形图6


继续查找错误

提示错误:

ERROR: digits value 11 does not match expected value 1 at time 1001000.000000ns

锁定到仿真图中的时间点:

波形图6

注意到digit数码管信号在ssdcat上升沿产生了一个错误值`h11。这是由于ssdcat片选信号与数码管显示信号的改变是同时发生的。对ssdcat信号进行延时错开数码管显示刷新时刻。代码修改如下:

  reg  ms_pulse_delay = 0;
  always @(posedge clk)
    ms_pulse_delay <= ms_pulse;

  initial ssdcat = 0;
  always @(posedge clk)
    if (ms_pulse_delay) ssdcat <= ~ssdcat;

仿真波形:

波形图7


附件
top.v

`timescale 1ns / 1ns
module top
#(
  parameter ms_limit = 100000
) (
   input clk,
   input [7:0] switch,
   output reg [7:0] led,
   output reg [6:0] ssd,
   output reg ssdcat
   );

  always @(posedge clk) led <= switch;

  wire [3:0]  digit;
  always @(posedge clk)
    case (digit)
      0: ssd <= 7'b1111110;
      1: ssd <= 7'b0110000;
      2: ssd <= 7'b1101101;
      3: ssd <= 7'b1111001;
      4: ssd <= 7'b0110011;
      5: ssd <= 7'b1011011;
      6: ssd <= 7'b1011111;
      7: ssd <= 7'b1110000;
      8: ssd <= 7'b1111111;
      9: ssd <= 7'b1110011;
      10: ssd <= 7'b1110111;
      11: ssd <= 7'b0011111;
      12: ssd <= 7'b1001110;
      13: ssd <= 7'b0111101;
      14: ssd <= 7'b1001111;
      15: ssd <= 7'b1000111;
    endcase

  integer count = 0;
  wire ms_pulse = count == ms_limit-1;
  always @(posedge clk)
    if (ms_pulse)
      count <= 0;
    else
      count <= count+1;

  reg  ssdcat_pre = 0;
  always @(posedge clk)
    if (ms_pulse)
      ssdcat_pre <= ~ssdcat_pre;

  initial ssdcat = 0;
  always @(posedge clk)
    ssdcat <= ssdcat_pre;

  integer ms_count = 0;
  wire sec_pulse = ms_count == 999 && ms_pulse;
  always @(posedge clk)
    if (ms_pulse)
      if (sec_pulse)
    ms_count <= 0;
      else
    ms_count <= ms_count+1;

  reg [7:0] sec_count = 0;
  always @(posedge clk)
    if (sec_pulse)
      sec_count <= sec_count+1;

  assign digit = ssdcat_pre ? sec_count[7:4] : sec_count[3:0];

endmodule

bench.v

`timescale 1ns / 1ns
module bench;
  reg clk = 1;
  always #5 clk = ~clk;

  reg [7:0] switch;
  wire [7:0] led;
  wire       ssdcat;
  wire [6:0] ssd;

  top #(.ms_limit(100)) top
    (
     .clk(clk),
     .switch(switch),
     .led(led),
     .ssd(ssd),
     .ssdcat(ssdcat)
     );

  wire [7:0] digits;
  ssd_digit PmodSSD0
    (
     .enable(~ssdcat),
     .ssd(ssd),
     .value(digits[3:0])
     );

  ssd_digit PmodSSD1
    (
     .enable(ssdcat),
     .ssd(ssd),
     .value(digits[7:4])
     );

  wire [7:0] model_seconds;
  model
    #(.ms_limit(100))
  model
    (
     .clk(clk),
     .seconds(model_seconds)
     );

  always @(posedge clk)
    switch <= $random;

  reg [7:0] expected_led;
  always @(posedge clk)
    expected_led <= switch;

  integer num_checks = 0;
  integer num_errors = 0;
  always @(posedge clk)
    begin
      num_checks = num_checks+1;
      if (expected_led != led)
    begin
      if (num_errors < 100)
        $display("ERROR: led value %0x does not match expected value %0x at time %0.0fns",
             led,expected_led,$realtime);
      num_errors = num_errors+1;
    end
    end 

  reg [3:0] check_digits;
  reg [3:0] check_model_digits;
  always @(posedge clk)
    begin
      check_digits = ssdcat ? digits[7:4] : digits[3:0];
      check_model_digits = ssdcat ? model_seconds[7:4] : model_seconds[3:0];
      num_checks = num_checks+1;
      if (check_digits != check_model_digits)
    begin
      if (num_errors < 100)
        $display("ERROR: check_digits value %0x does not match expected check_model_digits value %0x at time %0.0fns",
             check_digits,check_model_digits,$realtime);
      num_errors = num_errors+1;
    end
    end 

  initial
    begin
      wait (model_seconds == 20);
      $display("Simulation complete at time %0fns.",$realtime);
      if (num_errors > 0)
    $display("*** Simulation FAILED %0d/%0d",num_errors,num_checks);
      else
    $display("*** Simulation PASSED %0d/%0d",num_errors,num_checks);
      $finish;
    end

endmodule

ssd_digit.v

`timescale 1ns / 1ns
module ssd_digit
  (
   input enable,
   input [6:0] ssd,
   output reg [3:0] value
   );

  always @(*)
    if (enable)
      case (ssd)
    7'b1111110: value = 0;
    7'b0110000: value = 1;
    7'b1101101: value = 2;
    7'b1111001: value = 3;
    7'b0110011: value = 4;
    7'b1011011: value = 5;
    7'b1011111: value = 6;
    7'b1110000: value = 7;
    7'b1111111: value = 8;
    7'b1110011: value = 9;
    7'b1110111: value = 10;
    7'b0011111: value = 11;
    7'b1001110: value = 12;
    7'b0011001: value = 12;
    7'b0111101: value = 13;
    7'b1001111: value = 14;
    7'b1000111: value = 15;
    default: value = 'bx;
      endcase

endmodule

model.v

`timescale 1ns/1ns
module model
  #(
    parameter ms_limit = 100000
    )
  (
   input clk,
   output reg [7:0] seconds
   );

  integer counter = 0;
  always @(posedge clk)
    counter <= counter+1;

  always @(posedge clk)
    seconds <= counter / (ms_limit * 1000);

endmodule

原文链接

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值