3. 基础时序逻辑IP

基础时序逻辑IP:带异步复位的8位寄存器 学习卡片

任务目标

设计一个8位流水灯控制器,支持左移、右移和暂停三种工作模式,通过两个按键进行模式切换。核心逻辑采用同步时序电路实现,输出直接驱动8个LED。

接口定义

信号方向宽度说明
clkinput1系统时钟(50MHz)
rst_ninput1异步复位,低有效
key_leftinput1模式切换按键1(左移/暂停切换)
key_rightinput1模式切换按键2(右移/暂停切换)
led_outoutput8LED输出,高电平点亮

核心实现

按键输入经两级触发器同步后检测下降沿,产生单周期脉冲。模式状态机在按键脉冲驱动下切换:IDLE(暂停)→ LEFT(左移)→ IDLE → RIGHT(右移)→ IDLE,循环往复。移位速度通过计数器分频产生1Hz使能信号控制。

module led_flow_controller (
    input       clk,
    input       rst_n,
    input       key_left,
    input       key_right,
    output reg [7:0] led_out
);

    // 按键同步与下降沿检测
    reg [1:0] key_left_sync;
    reg [1:0] key_right_sync;
    wire key_left_pulse = key_left_sync[1] & ~key_left_sync[0];
    wire key_right_pulse = key_right_sync[1] & ~key_right_sync[0];

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            key_left_sync  <= 2'b11;
            key_right_sync <= 2'b11;
        end else begin
            key_left_sync  <= {key_left_sync[0], key_left};
            key_right_sync <= {key_right_sync[0], key_right};
        end
    end

    // 模式状态机
    localparam IDLE  = 2'd0;
    localparam LEFT  = 2'd1;
    localparam RIGHT = 2'd2;
    reg [1:0] state, next_state;

    always @(*) begin
        next_state = state;
        case (state)
            IDLE: begin
                if (key_left_pulse)  next_state = LEFT;
                else if (key_right_pulse) next_state = RIGHT;
            end
            LEFT: begin
                if (key_left_pulse || key_right_pulse) next_state = IDLE;
            end
            RIGHT: begin
                if (key_left_pulse || key_right_pulse) next_state = IDLE;
            end
            default: next_state = IDLE;
        endcase
    end

    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) state <= IDLE;
        else state <= next_state;
    end

    // 1Hz分频使能(50MHz时钟)
    reg [25:0] cnt;
    wire tick;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) cnt <= 26'd0;
        else if (cnt == 26'd49_999_999) cnt <= 26'd0;
        else cnt <= cnt + 1'b1;
    end
    assign tick = (cnt == 26'd49_999_999);

    // LED移位控制
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            led_out <= 8'b0000_0001;
        end else if (tick) begin
            case (state)
                LEFT:  led_out <= {led_out[6:0], led_out[7]};
                RIGHT: led_out <= {led_out[0], led_out[7:1]};
                default: led_out <= led_out;  // 暂停保持
            endcase
        end
    end

endmodule

仿真验证

编写testbench验证三种模式切换与暂停功能。初始状态为暂停(LED保持0x01),按下key_left后进入左移模式,每个tick周期LED左移一位;再次按下key_left回到暂停;按下key_right进入右移模式。验证时观察LED输出是否按预期方向循环移位,且暂停时输出保持不变。

`timescale 1ns/1ps
module tb_led_flow;

    reg clk = 0;
    reg rst_n = 0;
    reg key_left = 1;
    reg key_right = 1;
    wire [7:0] led_out;

    led_flow_controller uut (
        .clk(clk), .rst_n(rst_n),
        .key_left(key_left), .key_right(key_right),
        .led_out(led_out)
    );

    // 缩短分频周期便于仿真(将50_000_000改为50)
    // 实际综合时恢复为50_000_000
    always #10 clk = ~clk;

    // 按键按下产生下降沿
    task press_key(input reg key);
        begin
            key = 0;
            #100;
            key = 1;
            #100;
        end
    endtask

    initial begin
        #20 rst_n = 1;
        #100;  // 初始暂停,led_out = 0x01

        press_key(key_left);   // 进入左移
        #2000;                 // 观察左移效果

        press_key(key_left);   // 回到暂停
        #500;                  // 观察暂停保持

        press_key(key_right);  // 进入右移
        #2000;                 // 观察右移效果

        $finish;
    end

endmodule

注意:仿真时需将分频参数26'd49_999_999临时改为26'd49(对应50个时钟周期),否则仿真时间过长。综合时恢复原值即可。

验证判定标准

  • 复位后led_out = 8'b0000_0001,处于暂停状态;
  • 按下key_left后,每个tick周期LED高电平位置左移一位,循环移位;
  • 再次按下key_left,LED停止移位并保持当前值;
  • 按下key_right后,LED高电平位置右移一位,循环移位;
  • 任意移位状态下按下任一按键均回到暂停状态。

满足以上条件即验证通过。综合时建议添加按键消抖逻辑(本设计未包含,实际工程中需在按键输入后增加20ms级消抖电路)。