Zedboard---实验六秒计数
2017-05-01 12:15
260 查看
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_check和num_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仿真时刻的时序:
sec_count和model_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;
将count和ms_limit-1的比较结果赋值给ms_pulse。这样不仅使得代码更简洁,并且减少了sec_count的延迟。
但是计数值仍然延迟于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;
仿真波形图如下:
可以看到计数值的更新对齐了sec_pluse,但是sec_pulse不再是一个脉冲,而引起sec_count计数值的频繁计数。修改代码限制sec_pulse的产生:将逻辑运算(ms_count==999 && ms_pulse)的值赋给sec_pulse。
wire sec_pulse = ms_count == 999 && ms_pulse;
仿真波形图:
脉冲信号都对齐了,七段数码管解码输出还有一个时钟的延迟。
计数延迟一个周期,改变秒输出为寄存器类型,并且在每个上升沿对其赋值:
always @(posedge clk) seconds <= counter / (ms_limit * 1000);
还要确保修改了输出声明seconds为reg。
运行仿真:
继续查找错误
提示错误:ERROR: digits value 11 does not match expected value 1 at time 1001000.000000ns
锁定到仿真图中的时间点:
注意到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;
仿真波形:
附件
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
原文链接
相关文章推荐
- Zedboard---实验七秒计数
- Zedboard---实验四驱动7段数码管
- zedboard的裸机中断实验(一)
- 实验 使用 vivado zedboard GPIO 开关 开控制 LED
- 实验 使用 vivado zedboard GPIO 开关 开控制 LED
- Zedboard学习(三):PL下流水灯实验 标签: fpgazynqPL 2017-07-05 11:09 21人阅读 评论(0)
- 【51单片机实验】INT0中断计数
- Zedboard学习(三):PL下流水灯实验
- Zedboard---实验二仿真
- 数字计数问题(算法实验)
- 单片机 计数/定时 中断实验
- zedboard(zynqXC7Z020)入门实验之PS_GPIO的使用(MIO)
- Zedboard---实验三阻塞非阻塞
- ZedBoard--(4)嵌入式Linux下的LED实验(PS + PL)
- Zedboard---实验一点亮一盏LED
- zedboard--单独PL实验(四)
- Storm实验 -- 单词计数2
- zedboard--拷贝到SD卡启动的fsbl引导裸跑程序实验(九)
- MOOC清华《面向对象程序设计》第8章:智能指针与引用计数实验
- Storm实验 -- 单词计数3