数逻实验1——手搓一个可控流水灯
设计文件flowing_water_lights.v
第一行
1 | timescale 1ns / 1ps |
timescale指令定义了仿真时间单位和时间精度。这里时间单位为1纳秒,精度为1皮秒。
简单理解,这句话告诉计算机我们的设计是在以纳秒为单位进行计时,精度达到皮秒。
模块声明与参数部分
1 | module flowing_water_lights_v2 |
module flowing_water_lights_v2:这一行定义了我们的模块名字是 flowing_water_lights_v2,相当于在写一个小程序(模块),名字叫“流水灯程序”。
#(…) 是用来定义参数的部分:
parameter 表示这是一个可以改变的值,用来控制一些特定功能。
FREQ0, FREQ1, FREQ2, FREQ3 这些都是频率值,用来控制灯的闪烁速度(可以想象成灯亮灭的速度)。数字 32’d 是表示这是32位的十进制数。例如,32’d1000000 就是 1000000 的十进制数。
时间周期和频率
如果时钟信号 clk 为 100 MHz,即每秒钟有 100,000,000 个时钟周期,如果FREQ0的大小为1000000,那么灯在一秒钟亮100次,闪烁的速度相对较快。
输入输出端口
1 | ( |
- input 和 output 就是模块和外部连接的“接口”。模块需要通过这些接口接受信号或控制某些输出。
输入信号:
clk (时钟信号):电路会根据这个信号的节奏来同步工作,就像心跳一样。每次心跳(时钟周期),电路都会执行一次操作。
rst (复位信号):按下这个信号,电路会重置,一切会回到初始状态,相当于“重启”。
button (按钮信号):你可以用它来控制灯的启动和停止。
dir_set (方向设置):决定灯的流动方向是从左到右,还是从右到左。
freq_set (频率设置):这是一个2位的信号,可以选择我们刚才定义的4个不同的频率值(FREQ0 到 FREQ3),来控制灯的闪烁速度。
输出信号:led:这是一个8位的输出信号,控制8个LED灯的亮灭。每一个bit(位)控制一个灯。如果某个bit是1,灯就会亮;如果是0,灯就灭。led[7:0] 表示我们有8个灯。
1 | reg running; // 用来表示灯的当前状态,运行还是停止 |
reg 是寄存器的缩写,表示可以存储数据的地方。
wire 是线网,用于连接信号。
各个变量的作用:
- running:这个变量表示灯的当前状态。它会保存“灯是亮着的”还是“灯是停着的”的信息。
- button_press:这个信号用来检测按键什么时候被按下(准确地说,是检测按键的上升沿,也就是从未按下到按下的那一瞬间)。
- btn_sync:这是一个三级同步器,用来减少按键抖动带来的影响。因为按键按下时可能会抖动,信号会不稳定,所以需要“同步”来保证信号的可靠性。
- counter:这是一个计数器,用来计算时间。比如说,1秒钟我们可以数1000000次,等到计数满了,就让灯变化一次。
- freq_val:存储当前选择的频率值,用来控制灯的闪烁速度。
- tick:每当计数器数到某个值时,这个信号会产生一个“脉冲”,用来让灯变化(流动)。
按键同步与去抖
1 | always @(posedge clk or posedge rst) begin |
按键上升沿检测
1 | assign button_press = ~btn_sync[2] & btn_sync[1]; |
运行状态切换
1 | always @(posedge clk or posedge rst) begin |
- 这段代码控制灯的运行状态。如果按下复位键,灯会停止。
- 如果检测到按键被按下(button_press为真),则将灯的状态取反:如果灯当前在运行,那么按下按键后它会停止;如果灯停止,按下按键后它会启动。
选择闪烁频率
1 | always @(*) begin |
- 这段代码根据freq_set选择不同的频率值。
- case语句表示不同的情况:当freq_set是2’b00时,选择FREQ0;当是2’b01时,选择FREQ1,以此类推。
计数器与tick信号
1 | always @(posedge clk or posedge rst) begin |
- 当rst信号有效时,计数器counter和tock信号被清零。
- 当计数器达到freq_val设定的频率值时,计数器清零,并且tick信号置为1,表示一个周期完成。
- 只有在running状态下,计数器会递增。
LED流动控制
1 | always @(posedge clk or posedge rst) begin |
- 当tock信号触发时,LED灯会根据dir_set信号的值决定流动方向:
- 如果dir_set为高电平,LED从左向右流动(位移操作<<)。
- 如果dir_set为低电平,LED从右向左流动(位移操作>>)
结束
1 | endmodule |
总结
这个Verilog代码实现了一个控制8个LED灯的流水灯模块。可以通过按键启动或停止,同时支持不同的频率设置和流动方向的选择。
总体的代码如下:
1 | `timescale 1ns / 1ps |
仿真文件 testbench.v
1 | `timescale 1ns / 1ps |
约束文件(是学校的板子)
1 | set_property -dict {PACKAGE_PIN Y18 IOSTANDARD LVCMOS33} [get_ports clk] |
-------------本文结束感谢您的阅读-------------