`timescale 1ns/1ns module tb_lm(); reg k_1; // 显示的图像1 reg k_2; // 显示的图像2 reg key; // 物理按键 开关显示, 该按键异步的, 每次按下 等于 reg key_1; // 物理按键的随时钟同步变化信号 reg key_2; // reg is_show; // 互斥变化的 reg [4:0] flag_time; reg flag; // 状态改变周期 现在500纳秒改变一次状态, 显示相反的图像 reg sys_clk; reg sys_rst; reg [7:0]x; reg [7:0]y; always #10 sys_clk = ~sys_clk; initial begin sys_clk <= 1'b0; sys_rst <= 1'b0; #200 sys_rst <= 1'b1; // 延迟2000纳秒, 现在x 应该是0 #2000 key <= 1'b1; #120 key <= 1'b0; #20 key <= 1'b1; #80 key <= 1'b0; #20 key <= 1'b1; #90 key <= 1'b0; #20 // 延迟4000纳秒观察 {k_2, k_1} <= 2'b10; #3900 {k_2, k_1} <= 2'b01; #3800 key <= 1'b1; #20 key <= 1'b0; #20 key <= 1'b1; #20 key <= 1'b0; #20 key <= 1'b1; #90 key <= 1'b0; #200 key <= 1'b1; #120 key <= 1'b0; #20 key <= 1'b1; #120 key <= 1'b0; #20 key <= 1'b1; #150 key <= 1'b0; end // 显示的时候计时 always @(posedge sys_clk or negedge sys_rst) begin if (sys_rst == 1'b0) begin flag_time <= 5'd0; end else if (flag_time < (5'd25 - 5'd1) && is_show) begin flag_time <= flag_time + 5'd1; end else begin flag_time <= 5'b0; end end always @(posedge sys_clk or negedge sys_rst) begin if (sys_rst == 1'b0) begin {k_2, k_1} <= 2'b11; x <= 8'b00000000; y <= 8'b11111111; // 所有的都显示算了 end else if ({k_2, k_1} == 2'b10 && is_show) begin case (flag) 1'b0: x <= 8'b1010_1010; 1'b1: x <= 8'b0101_0101; endcase end else if ({k_2, k_1} == 2'b01 && is_show) begin case (flag) 1'b0: x <= 8'b1111_0000; 1'b1: x <= 8'b0000_1111; endcase end else begin x <= 8'b00000000; end end // 每 显示500ns 改变显示的图像 always @(posedge sys_clk or negedge sys_rst) begin if (sys_rst == 1'b0) begin flag <= 1'b0; end else if (flag_time == (5'd25 - 5'd1)) begin flag <= !flag; end else begin end end // 异步信号, 通过比较上次时钟周期的状态和当前时钟周期的状态得到当前是否按下了 always @(posedge sys_clk or negedge sys_rst) begin if (sys_rst == 1'b0) begin key <= 1'b0; key_1 <= 1'b0; key_2 <= 1'b0; end else begin key_1 <= key; key_2 <= key_1; end end reg [4:0] click_time; // max: 20 reg is_down; // 消抖之后的按键状态, 是一个稳定的状态, 没稳定之前 is_down 一直是上次状态, 稳定之后视key最新的值为定 always @(posedge sys_clk or negedge sys_rst) begin if (sys_rst == 1'b0) begin is_down <= 1'b0; click_time <= 5'd0; end if (key_1 != key_2) begin click_time <= 5'd0; end else begin // 如果一直相同持续时间达到20个时钟周期 if (click_time == (5'd20 - 5'd1)) begin // 当前的key就是可用的, 返回到外部也就是 is_down is_down <= key_1; end else begin click_time <= click_time + 5'd1; end end end // 检查稳定之后的按键是否按下了 wire is_click; reg is_down_1; reg is_down_2; assign is_click = is_down_1 && !is_down_2; always @(posedge sys_clk or negedge sys_rst) begin if (sys_rst == 1'b0) begin is_down_1 <= 1'b0; is_down_2 <= 1'b0; end else begin is_down_1 <= is_down; is_down_2 <= is_down_1; end end always @(posedge sys_clk or negedge sys_rst) begin if (sys_rst == 1'b0) begin is_show <= 1'b0; end else if (is_click) begin is_show <= !is_show; end else begin end end lm u_lm( .x (x), .y (y) ); endmodule