数据计算
时钟频率是 50MHz 对应着每个周期是1s/50MHz = 0.02us(20ns)1us = 10^(-6)s,0.02us = 2*10^(-8)s = 0.2*10^(-7)s
- 如果要计时0.2s的话,就是
0.2s = 0.02us*10^7也就是要计数10^7个数,即10_000_000,需要占24位 - 如果要计时1s的话,就是
5*10_000_000,需要占26位
代码编写
/***************************************File Name: flow_led.vDescription: LED流水灯,时间间隔1秒Author: ZHJ0125Date: 2022-04-05***************************************/module flow_led(input sys_clk, //系统时钟input sys_rst_n, //系统复位,低电平有效output reg [3:0] led //4个LED灯);//reg definereg [25:0] counter;/***************************************// main code***************************************///计数器对系统时钟计数,计时1秒always @(posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n)counter <= 26'd0;else if(counter < 26'd50_000_000) // 1s//else if(counter < 24'd10) // 24'd10 for modelsimcounter <= counter + 1'b1;elsecounter <= 26'd0;end//通过移位寄存器控制IO口的高低电平,从而改变LED的显示状态always @(posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n)led <= 4'b0001;else if(counter == 26'd50_000_000)//else if(counter == 24'd10) // 24'd10 for modelsimled[3:0] <= {led[2:0], led[3]};// else// led <= led;endendmodule
程序固化
将.sof文件转换成.jic文件,使之可以被烧录至片外Flash。
SignalTap仿真
设置counter等于49999990时触发,软件会截取触发前256周期至触发后1792周期的状态。
实际效果
每1秒变换一次状态,实现流水灯效果。已将代码烧录至Flash,掉电后数据不会丢失。
ModelSim 手动仿真

添加Verilog文件:

创建Testbanch文件:

工程文件目录:
编写Testbanch文件flow_led_tb.v:
`timescale 1ns/1ns // 定义仿真时间单位1ns,仿真时间精度为1nsmodule flow_led_tb(); // 测试模块// parameter defineparameter T = 20; // 时钟周期20ns// reg definereg sys_clk; // 时钟信号reg sys_rst_n; // 复位信号// wire definewire [3:0] led;//***********************************// main code//************************************// 给输入信号初始值initial beginsys_clk = 1'b0;sys_rst_n = 1'b0; // 复位#(T+1) sys_rst_n = 1'b1; // 在第21ns的时候复位信号拉高end// 50MHz的时钟,周期为1/50MHz=20ns,所以每10ns取反一次always #(T/2) sys_clk = ~sys_clk;// 例化flow_led模块flow_led u0_flow_led (.sys_clk (sys_clk ),.sys_rst_n (sys_rst_n),.led (led ));endmodule
修改源文件时间间隔:
//计数器对系统时钟计数,计时1秒always @(posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n)counter <= 26'd0;//*******修改此处语句,将定时时间改为10*0.02us=0.2us=200ns//else if(counter < 26'd50_000_000) // 1selse if(counter < 26'd10) // 26'd10 for modelsimcounter <= counter + 1'b1;elsecounter <= 26'd0;end//通过移位寄存器控制IO口的高低电平,从而改变LED的显示状态always @(posedge sys_clk or negedge sys_rst_n) beginif(!sys_rst_n)led <= 4'b0001;//*******修改此处语句,将定时时间改为10*0.02us=0.2us=200ns//else if(counter == 26'd50_000_000)else if(counter == 26'd10) // 26'd10 for modelsimled[3:0] <= {led[2:0], led[3]};// else// led <= led;end
修改完成后,在ModelSim → Compile → Compile All 进行全编译。
开始仿真,点击Simulate → Start Simulation ...,选择Work → flow_led_tb,会自动跳转至仿真界面。
添加信号源
设置仿真时间为2 us,并开始运行:
我们在源代码里设置的计数器个数为10,计时时间为0.2us(200ns)。仿真时间为2us,理论上我们可以看到2us/0.2us = 10次LED状态改变。
仿真结果如下:

从上面第一张图可以看出:
- 复位信号
sys_rst_n在第21ns,由低电平跳转至了高电平。 - 时钟信号
sys_clk在第10ns开始跳转至高电平,并以20ns为周期进行高低电平的转换。 - 在第230ns,即时钟信号
sys_clk的第11个上升沿,LED状态发生改变,由初始的0001变为0010。 - 以后每隔220ns,即每次满10个时钟周期,就会在下一次时钟上升沿发生LED状态跳转。
从上面第二张图可以看出:
- 每隔220ns,LED状态就会发生跳转,一直持续了整个仿真时间2us。
- 在整个仿真时间2us内,发生了9次LED状态跳变。
我们原本预计在整个仿真时间2us内,可以发生2us/0.2us=10次LED状态改变,但实际上,由于在第21ns复位信号才被拉高,所以第一次LED状态跳转是在第230ns,一共只能发生9次LED状态跳变。
ModelSim 联合仿真
相关配置
联合仿真就是将Quartus和ModelSim联合,使得在Quartus软件中就可以调用ModelSim的仿真功能。
在Quartus软件的Tools→Options→Gerenal→EDA Tool Options选项界面中,设置ModelSim Altera的路径为<Quartus软件安装路径>\modelsim_ase\win32aloem以我的安装路径为例,需要将目录设置为D:\Quartus\install\modelsim_ase\win32aloem。
另外还需要在Assignments→Settings→EDA Tool Setting→Simulation界面进行如下配置:
编写TestBanch
找到Processing→Start→Start TestBench Template Writer,它会自动创建一个Testbanch模板,创建的模板位置可以在控制台看到。
我们直接在Quartus软件中打开这个flow_led.vt文件:
我们在此模板基础上进行修改,以下是我修改的文件内容:
/***************************************// File Name: flow_led.vt// Description: Verilog Test Bench template for design : flow_led// Author: ZHJ0125// Date: "04/05/2022 20:26:52"***************************************/`timescale 1 ns/ 1 ns // 声明仿真时间单位(左)和仿真精度(右)module flow_led_tb(); // 模块名称为flow_led_tbreg sys_clk; // 时钟信号reg sys_rst_n; // 复位信号// wireswire [3:0] led;// 例化设计模块,例化被测模块的目的是把被测模块和激励模块实例化起来,// 并且把被测模块的端口与激励模块的端口进行相应的连接,使得激励可以输入到被测模块。// 左侧带'.'的信号为flow_led模块定义的端口信号,右侧括号内的信号为激励模块中定义的信号。flow_led i1 (// port map - connection between master ports and signals/registers.led(led),.sys_clk(sys_clk),.sys_rst_n(sys_rst_n));initial begin// code that executes only once// insert code here --> beginsys_clk = 1'b0; // 时钟信号初始值sys_rst_n = 1'b0; // 复位信号初始值#100 sys_rst_n = 1'b1; // 在第100ns的时候复位信号拉高#1000 $stop; // 在第1000ns的时候停止仿真,仿真时间为1000ns// --> end//$display("Running testbench");end// 使用initial或always语句产生激励波形always begin// code executes for every event on sensitivity list// insert code here --> begin#10 sys_clk = ~sys_clk;// --> endendendmodule
接着需要回到Assignments→Settings→EDA Tool Setting→Simulation配置界面,配置Testbanch文件:


需要注意的是:
Test bench name填写的内容需要与模板中写的模块名字一致。Top level module in test bench会自动生成,默认与模块名字一致。文件名是模板自动生成的
flow_led.vt文件,不是我们之前手动编写的flow_led_tb.v。开始仿真
准备就绪后,点击
Tools→Run Simulation Tool→RTL Simulation按钮,会自动打开ModelSim的仿真界面。
可以看到:在第100ns时,复位信号
sys_rst_n拉高- LED状态共跳变4次,并且符合流水灯的预期(循环移位)效果。
参考资料
正点原子开拓者FPGA开发指南V1.5.pdf第四和第五章节


