数据计算
时钟频率是 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.v
Description: LED流水灯,时间间隔1秒
Author: ZHJ0125
Date: 2022-04-05
***************************************/
module flow_led(
input sys_clk, //系统时钟
input sys_rst_n, //系统复位,低电平有效
output reg [3:0] led //4个LED灯
);
//reg define
reg [25:0] counter;
/***************************************
// main code
***************************************/
//计数器对系统时钟计数,计时1秒
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
counter <= 26'd0;
else if(counter < 26'd50_000_000) // 1s
//else if(counter < 24'd10) // 24'd10 for modelsim
counter <= counter + 1'b1;
else
counter <= 26'd0;
end
//通过移位寄存器控制IO口的高低电平,从而改变LED的显示状态
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
led <= 4'b0001;
else if(counter == 26'd50_000_000)
//else if(counter == 24'd10) // 24'd10 for modelsim
led[3:0] <= {led[2:0], led[3]};
// else
// led <= led;
end
endmodule
程序固化
将.sof
文件转换成.jic
文件,使之可以被烧录至片外Flash。
SignalTap仿真
设置counter等于49999990时触发,软件会截取触发前256周期至触发后1792周期的状态。
实际效果
每1秒变换一次状态,实现流水灯效果。已将代码烧录至Flash,掉电后数据不会丢失。
ModelSim 手动仿真
添加Verilog文件:
创建Testbanch文件:
工程文件目录:
编写Testbanch文件flow_led_tb.v
:
`timescale 1ns/1ns // 定义仿真时间单位1ns,仿真时间精度为1ns
module flow_led_tb(); // 测试模块
// parameter define
parameter T = 20; // 时钟周期20ns
// reg define
reg sys_clk; // 时钟信号
reg sys_rst_n; // 复位信号
// wire define
wire [3:0] led;
//***********************************
// main code
//************************************
// 给输入信号初始值
initial begin
sys_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) begin
if(!sys_rst_n)
counter <= 26'd0;
//*******修改此处语句,将定时时间改为10*0.02us=0.2us=200ns
//else if(counter < 26'd50_000_000) // 1s
else if(counter < 26'd10) // 26'd10 for modelsim
counter <= counter + 1'b1;
else
counter <= 26'd0;
end
//通过移位寄存器控制IO口的高低电平,从而改变LED的显示状态
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!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 modelsim
led[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_tb
reg sys_clk; // 时钟信号
reg sys_rst_n; // 复位信号
// wires
wire [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 --> begin
sys_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;
// --> end
end
endmodule
接着需要回到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第四和第五章节