3. 流水灯控制器
任务目标
设计一个8位流水灯控制器,支持左移、右移和暂停三种工作模式,通过两个按键进行模式切换。核心逻辑采用同步时序电路实现,输出直接驱动8个LED。
接口定义
| 信号 | 方向 | 宽度 | 说明 |
|---|---|---|---|
| clk | input | 1 | 系统时钟(50MHz) |
| rst_n | input | 1 | 异步复位,低有效 |
| key_left | input | 1 | 模式切换按键1(左移/暂停切换) |
| key_right | input | 1 | 模式切换按键2(右移/暂停切换) |
| led_out | output | 8 | LED输出,高电平点亮 |
核心实现
按键输入经两级触发器同步后检测下降沿,产生单周期脉冲。模式状态机在按键脉冲驱动下切换: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级消抖电路)。