3. 流水灯
任务目标
设计一个8位流水灯控制器,实现LED从右向左(或从左向右)依次点亮并循环流动的效果,且流水速度(周期)可通过外部输入调节。本案例以Verilog HDL实现核心逻辑,并提供测试平台用于功能仿真验证。
实现方案
采用有限状态机(FSM)或移位寄存器两种方式均可实现流水灯。本案例选用参数化移位寄存器方案:通过左移操作配合位拼接实现循环移位,流水方向固定为从LED[0]向LED[7]流动(即从左向右点亮)。周期调节通过一个外部输入的时钟分频系数DIV_RATIO实现,该系数越大,流水速度越慢。
Verilog核心代码
module led_water_light #(
parameter N = 8, // LED数量
parameter CNT_WIDTH = 24 // 分频计数器位宽
)(
input wire clk, // 系统时钟(50MHz)
input wire rst_n, // 异步复位,低有效
input wire [CNT_WIDTH-1:0] div_ratio, // 分频系数,决定流水周期
output reg [N-1:0] led // LED输出,1表示点亮
);
// 分频计数器
reg [CNT_WIDTH-1:0] cnt;
wire tick;
// 时钟分频:产生tick脉冲,周期 = div_ratio * 时钟周期
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
cnt <= 0;
else if (cnt == div_ratio - 1)
cnt <= 0;
else
cnt <= cnt + 1'b1;
end
assign tick = (cnt == div_ratio - 1) ? 1'b1 : 1'b0;
// 流水灯移位逻辑
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
led <= 8'b0000_0001; // 初始状态:仅LED[0]亮
else if (tick) begin
// 循环左移:最高位移到最低位
led <= {led[N-2:0], led[N-1]};
end
end
endmodule
测试平台(Testbench)
`timescale 1ns / 1ps
module tb_led_water_light;
reg clk;
reg rst_n;
reg [23:0] div_ratio;
wire [7:0] led;
// 实例化被测模块
led_water_light #(
.N(8),
.CNT_WIDTH(24)
) uut (
.clk(clk),
.rst_n(rst_n),
.div_ratio(div_ratio),
.led(led)
);
// 生成50MHz时钟(周期20ns)
initial clk = 0;
always #10 clk = ~clk;
// 测试激励
initial begin
// 初始化
rst_n = 0;
div_ratio = 24'd5; // 每5个时钟周期移位一次
#30;
rst_n = 1;
// 观察多个流水周期
#2000;
$display("Simulation finished.");
$stop;
end
// 监控LED变化
initial begin
$monitor("Time=%0t, led=%b", $time, led);
end
endmodule
运行与验证
仿真环境:Vivado、ModelSim或Quartus自带的仿真器均可。
- 将核心代码保存为
led_water_light.v,测试平台保存为tb_led_water_light.v,加入工程。 - 运行行为仿真(Behavioral Simulation),观察
led信号波形。
通过条件:
- 复位释放后,
led初始值为0000_0001。 - 每经过
div_ratio个时钟周期,led值左移一位,最高位循环回最低位。 - 当
div_ratio=5时,每100ns(5×20ns)LED状态变化一次,8个状态构成一个完整流水周期(800ns),随后重复。
预期输出(部分):
Time=30, led=00000001
Time=130, led=00000010
Time=230, led=00000100
Time=330, led=00001000
Time=430, led=00010000
Time=530, led=00100000
Time=630, led=01000000
Time=730, led=10000000
Time=830, led=00000001 // 循环回到初始状态
周期调节说明
流水周期 \( T = div\_ratio \times T_{clk} \times 8 \)。例如系统时钟50MHz(\( T_{clk}=20ns \)),当div_ratio=5时,完整流水周期为 \( 5 \times 20ns \times 8 = 800ns \)。实际应用中可通过拨码开关或按键输入不同的div_ratio值来改变流水速度。