1. LED流水灯控制器

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均可,无需额外库。

  1. 将两个文件加入工程,tb_led_waterfall设为顶层仿真模块;
  2. 运行仿真,观察led信号变化;
  3. 通过标准:复位后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有效沿采样,切换后最多等待一个分频周期才生效,属正常行为。