Zedboard---实验六秒计数

来源:互联网 发布:tripmode mac 编辑:程序博客网 时间:2024/06/05 11:44

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/1nsmodule 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 allERROR: digits value 0 does not match expected value 1 at time 1000000.000000nsERROR: digits value 0 does not match expected value 1 at time 1000010.000000nsERROR: digits value 0 does not match expected value 1 at time 1000020.000000nsERROR: digits value 11 does not match expected value 1 at time 1001010.000000nsERROR: digits value 0 does not match expected value 1 at time 1002010.000000nsERROR: digits value 11 does not match expected value 1 at time 1003010.000000nsERROR: digits value 0 does not match expected value 1 at time 1004010.000000nsERROR: 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.000000nsERROR: digits value 11 does not match expected value 13 at time 19996010.000000nsERROR: digits value 33 does not match expected value 13 at time 19997010.000000nsERROR: digits value 11 does not match expected value 13 at time 19998010.000000nsERROR: digits value 33 does not match expected value 13 at time 19999010.000000nsSimulation 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 / 1nsmodule 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 / 1nsmodule 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;    endendmodule

ssd_digit.v

`timescale 1ns / 1nsmodule 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;      endcaseendmodule

model.v

`timescale 1ns/1nsmodule 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 0
原创粉丝点击