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自带的仿真器均可。

  1. 将核心代码保存为led_water_light.v,测试平台保存为tb_led_water_light.v,加入工程。
  2. 运行行为仿真(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值来改变流水速度。

LED0 LED1 LED2 LED3 LED4 LED5 LED6 LED7 当前状态:LED0亮,下一拍左移 → LED1亮