1. LED流水灯控制器
任务说明
设计一个LED流水灯控制器:8个LED按顺序点亮形成流水效果,流水方向由按键输入控制(按键按下时反向)。系统时钟50MHz,通过分频产生合适的流水刷新速率(约10Hz),使用异步复位。
模块实现
module led_waterfall (
input clk, // 50MHz
input rst_n, // 异步复位,低有效
input dir_sw, // 方向控制:0=正向,1=反向
output reg [7:0] led // LED输出
);
// 分频参数:50MHz/10Hz = 5_000_000
localparam CNT_MAX = 5_000_000 - 1;
reg [22:0] cnt;
reg tick;
// 时钟分频产生10Hz脉冲
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= 23'd0;
tick <= 1'b0;
end else if (cnt == CNT_MAX) begin
cnt <= 23'd0;
tick <= 1'b1;
end else begin
cnt <= cnt + 1'b1;
tick <= 1'b0;
end
end
// 流水移位控制
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
led <= 8'b0000_0001; // 复位后LED0亮
else if (tick) begin
if (!dir_sw) begin
// 正向:左移循环
led <= {led[6:0], led[7]};
end else begin
// 反向:右移循环
led <= {led[0], led[7:1]};
end
end
end
endmodule
仿真测试平台
`timescale 1ns/1ps
module tb_led_waterfall;
reg clk, rst_n, dir_sw;
wire [7:0] led;
led_waterfall uut (
.clk(clk), .rst_n(rst_n),
.dir_sw(dir_sw), .led(led)
);
initial begin
clk = 0;
forever #10 clk = ~clk; // 50MHz
end
initial begin
// 初始化
rst_n = 0; dir_sw = 0;
#100 rst_n = 1;
// 观察正向流水(约需5.2ms观察2次移位)
#5_200_000;
// 切换反向
dir_sw = 1;
#5_200_000;
// 再次切换正向
dir_sw = 0;
#5_200_000;
$display("Test completed.");
$stop;
end
// 监控LED变化
always @(posedge clk) begin
if (uut.tick)
$display("Time=%0t, dir=%b, led=%b", $time, dir_sw, led);
end
endmodule
运行与验证
仿真工具:Vivado/VCS/ModelSim均可,无需额外库。
- 将两个文件加入工程,
tb_led_waterfall设为顶层仿真模块; - 运行仿真,观察
led信号变化; - 通过标准:复位后
led=0000_0001;每500ms(仿真时间)LED左移一位,依次为0000_0010、0000_0100…循环;切换dir_sw=1后方向反转,LED右移循环;切换回0后恢复左移。
预期仿真输出片段:
Time=5000100000, dir=0, led=00000010
Time=10000100000, dir=0, led=00000100
Time=15000100000, dir=0, led=00001000
Time=20000100000, dir=1, led=00000100 // 反向开始
Time=25000100000, dir=1, led=00000010
常见问题
- 仿真时间过长:若需快速验证,可将
CNT_MAX临时改为较小值(如499),同时相应缩短testbench中的延时; - LED不变化:检查分频计数器位宽是否足够(23位),以及
tick信号是否在比较相等时拉高; - 方向切换延迟:方向信号在
tick有效沿采样,切换后最多等待一个分频周期才生效,属正常行为。