1.1 时序逻辑电路设计之计数器 本节导读 时序逻辑电路是指电路任何时刻的稳态输出不仅取决于当前的输入,还与前一时刻输入形成的状态有关。这跟组合逻辑电路相反,组合逻辑的输出只会跟目前的输入成一种函数关系。换句话说,时序逻辑存在储存元件来存储信息,而组合逻辑则没有。 时序逻辑电路分为很多种,本节将以最常用的计数器为例学习简单的时序逻辑电路设计,并比较与组合逻辑电路的区别。此处设计一个计数器,使学习板上的LED状态每500ms翻转一次。学习板上晶振为50MHz,也就是说时钟周期为20ns,这样可以计算得出500ms = 500_000_000ns/20ns = 25_000_000,即需要计数器计数25_000_000次,也就是需要一个至少25位的计数器(225>25_000_000>224)。且每当计数次数达到需要清零并重新计数。 1.1.1 计数器工作原理 计数器的核心元件是触发器,基本功能是对脉冲进行计数,其所能记忆脉冲最大的数目称为该计数器的模/值。计数器常用在分频、定时等处。计数器的种类很多,按照计数方式的不同可以分为二进制计数器、十进制计数器以及任意进制计数器,按照触发器的时钟脉冲信号来源可分为同步计数器与异步计数器。按照计数增减可分为加法计数器、减法计数器以及可逆计数器。 Verilog HDL之所以被称为硬件电路描述语言,就是因为我们不是在类似C一样进行普通的编程,而是在编写一个实际的硬件电路,例如2.2节Intel FPGA开发流程中设计的一个二选一选择器最后就是被综合称为一个真正的选择器。上面提到计数器即为加法器、比较器、寄存器以及选择器构成,如图 3.2 1所示。 图 3.2 1 计数器逻辑电路图 本节硬件电路如图 3.2 2所示,可以看出当控制端输出低电平时LED亮。这里只需要每当计数器值记满后,翻转LED的控制端即可实现LED按照要求亮灭。 图 3.2 2 LED电路图 1.1.2 计数器Verilog实现 由上面分析可以得出本设计的模块接口示意图如图 3.2 3所示,其各端口的功能描述如表所示。 图 3.2 3 计数器模块示意图 xxx计数器模块端口功能描述 建立工程子文件夹后,新建一个名为counter的工程保存在prj文件夹下,并在本工程目录下的rtl文件夹下新建verilog file文件并以counter.v保存。 从图3.10以及表3.10分析可得出端口列表. input Clk50M; //系统时钟,50M input Rst_n; //全局复位,低电平复位 output reg led; //led输出 |
从实验原理中可以看出需要一个计数器,因为计数器是从0开始计数而不是1,因此在计数值计数到25'd24_999_999时清零而不是计数到25'd25_000_000时清零。 reg [24:0]cnt; //定义计数器寄存器 //计数器计数进程 always@(posedge Clk50M or negedge Rst_n) if(Rst_n == 1'b0) cnt <= 25'd0; else if(cnt == 25'd24_999_999) cnt <= 25'd0; else cnt <= cnt + 1'b1; |
当计数器计数到预设的值后就让led取反一次,来达到亮灭的目的。
//led输出控制进程 always@(posedge Clk50M or negedge Rst_n) if(Rst_n == 1'b0) led <= 1'b1; else if(cnt == 25'd24_999_999) led <= ~led; else led <= led; |
1.1.3 仿真及板级验证 将上面的设计内容进行分析和综合直至没有错误以及警告。 为了测试仿真编写测试激励文件,新建counter_tb.v文件并输入以下内容再次进行分析和综合直至没有错误以及警告,保存到testbench文件夹下。这里生成了一个周期为20ns的时钟clk,并且例化了需要测试的counter.v。 `timescale 1ns/1ns `define clock_period 20 module counter_tb; reg clk; reg rst_n; wire led; counter counter0( .Clk50M(clk), .Rst_n(rst_n), .led(led) ); initial clk = 1; always #(`clock_period/2) clk = ~clk; initial begin rst_n = 1'b0; #(`clock_period *200); rst_n = 1'b1; #2000000000; $stop; end endmodule |
设置好仿真脚本后进行功能仿真,可以看到如图 3.2 4所示的波形文件,可以看出高低电平变化的时间均是0.5s也就是500ms,符合既定的设计要求,至此功能仿真结束。 图 3.2 4 功能仿真波形文件 在进行上述的功能仿真时可以发现电脑需要运行的时间较长,这是由于计数器的计数值太大,因此可以将counter.v的cnt计数值修改为24_999来减少仿真时间,这时会发现仿真时间大幅度缩短,且图 3.2 5中高低电平变化时间变为500_000ns,相比500ms缩短了1000倍,也可以说明功能仿真正确。 图 3.2 5 缩小计数值后的功能仿真波形 全编译后进行时序仿真,可以看到如图 3.2 6所示波形图,在这可以看出由于门电路的延迟高低电平变化时间并不严格等于0.5s。 图 3.2 6 时序仿真波形 现进行分配引脚,此处介绍另一种分配引脚的方式,采用tcl文件。首先在File—New中选中Tcl Script File,新建一个tcl文件,如图 3.2 7所示。并输入以下内容后以PIN.tcl名称保存到prj文件夹下。此处由于不同板卡可能会引脚分配略有不同,请根据对应的引脚表来编写。 图 3.2 7 新建tcl文件 set_location_assignment PIN_E1 -to Clk50M set_location_assignment PIN_E16 -to Rst_n set_location_assignment PIN_A2 -to led |
然后单击Tools→Tcl Script,弹出图 3.2 8对话框后选中编写好的PIN.tcl文件,点击Open Files编写的内容就会出现在下面的框图中,此时再点击Run会弹出图 3.2 9对话框,提示已经运行完毕,如图 3.2 10所示。这时可以打开Pin planer查看分配好的引脚。 图 3.2 8 设置Tcl脚本 图 3.2 9 运行Tcl脚本 图 3.2 10脚本运行成功 将图 3.2 11与图 3.2 1进行对比可以看出,综合后的电路与预期设计一致,同样包括前面讲到的加法器、比较器、寄存器以及选择器构成的计数器。下载到开发板中可以看到图 3.2 12现象,LED0以500ms的时间进行闪烁,如果有示波器也可以测量相应引脚波形的频率。 图 3.2 11 RTL viewer 图 3.2 12 实验现象 从以上的波形以及板级现象分析,可以发现本章设计的计数器工作正常,这样就完成了一个基本的时序逻辑──计数器的设计。 本章以计数器为例学习了简单的时序逻辑设计,并学习了基本的Tcl脚本在分配引脚时的用法。请以此为基础,设计出逻辑电路驱动4个LED灯以不同的频率闪烁,并进行仿真以及板级验证。此处闪烁频率周期可分别为250ms、500ms、1s以及2s,在仿真时同样可以与前面做法相同,通过成倍数缩小时钟周期来加快仿真速度。
|