计数器与分频器实验

实验目的

(1) 熟悉计数器的功能特性和分频器应用;

(2) 用HDL语言设计二进制计数器和时钟分频器。

(3) 熟悉仿真软件的使用。

参考设计

流水灯实验中用按键手动产生时钟信号,本实验使用系统的连续时钟,使流水灯能自动移动。由于系统时钟的频率比较高,进入到VirtualBoard模块的CLOCK信号的频率是10MHz;如果直接用它作为移位寄存器的时钟,人的肉眼将无法分辨出LED灯的移动。所以需要设计一个分频器将时钟频率降下来。

例 1给出了一个自动流水灯的参考设计。它使用二进制计数器作为分频器,将10MHz系统时钟CLOCK分频后作为移位寄存器的时钟。认真阅读并理解参考设计的代码,计算出送给移位寄存器的clk信号的周期(频率的倒数)。

例 1. 自动流水灯参考设计的核心代码
logic  [22:0] count;
always_ff @(posedge CLOCK or posedge reset)
begin
    if(reset)
        count <= 0;
    else
        count <= count+1;
end

assign clk = count[22];

logic [7:0]q;
always_ff @ (posedge clk or posedge reset)
	if (reset)
		q <= 1;
	else
		q <= {q[6:0], q[7]};

注:完整代码可从开源项目托管网站下载,下载方法见下载实验材料

编译后下载到实验板上,观察流水灯的自动移动。注意需开启虚拟面板的自动刷新,如图 1所示。远程实验系统默认的自动刷新时间间隔为1000毫秒。

Snipaste_2019-09-22_00-08-32
图 1. 自动流水灯的虚拟面板

思考为什么观察到的流水灯移动会跳跃,如何才能观察到LED灯的匀速移动(提示:需根据clk信号的周期恰当设置刷新间隔)。

实验任务1:分频器

用HDL设计一个偶数分频器,要求写成独立模块,端口声明如下,其中参数RATIO用来声明分频数。

module ClockDivider
#(parameter RATIO = 8) // RATIO为分频数,应大于1且为偶数
(
    input ClkIn,Reset,
    output reg ClkOut
);

在VirtualBoard模块中实例化ClockDivider模块,代替参考设计中的二进制分频器。ClkIn连接10MHz系统时钟CLOCK,ClkOut作为移位寄存器的时钟。实例化时传递合适的RATIO参数值,使ClkOut输出频率为1Hz。最终构成1秒钟移动一位的自动流水灯,运行效果如图 2

自动流水灯
图 2. 时钟频率为1Hz的自动流水灯运行效果

实验任务2:仿真

到目前为止,设计是否正确是通过加载到FPGA芯片进行验证的。对于简单的实验项目,这种验证方法是可行的,而且经过虚拟实验软件的可视化,能达到较好的学习效果。但是对于复杂的工程项目,不可能通过实验系统进行验证。仿真是一种工业界通行的验证方法,后面CPU设计实验的最后阶段也可以通过仿真查找设计错误。

(1)熟悉ModelSim仿真软件

关于ModelSim的用法请阅读ModelSim仿真入门,其中介绍了用ModelSim仿真本实验项目参考设计的过程。

(2)理解Testbench

例 2给出了仿真计数器的Testbench。

例 2. 计数器仿真的Testbench
`timescale 1ns/100ps
module tb_top;
   reg reset, clock;
   wire [19:0]pb;
   assign pb[0] = reset;
   
   VirtualBoard UUT (.CLOCK(clock), .PB(pb)); (1)
   
   initial begin
      reset=1'b1;
      clock=1'b0; 
   end
   initial #150 reset = 1'b0;
   always #50 clock = ~clock;
   initial #1000000 $stop; (2)
   
endmodule
1 被仿真的设计模块是VirtualBoard。
2 运行仿真的时长为1000000ns,即1ms。

(3)仿真任务1设计的分频器

由于testbench中指定的仿真时长只有1ms,不足以观察分频器ClkOut的周期性变化。可以在仿真暂停后继续运行仿真;也可以修改testbench,延长停止仿真前的时长;或者临时修改设计代码,改为较小的分频数以便能观察到ClkOut的周期性变化。