nju实验七 状态机及键盘输入

📚 使用须知

  • 本博客内容仅供学习参考
  • 建议理解思路后独立实现
  • 欢迎交流讨论

task : 状态机及键盘输入

实验七 状态机及键盘输入

简单状态机

.

├── build
├── constr
│ └── top.nxdc
├── csrc
│ └── test_our.cpp
├── dump.vcd
├── Makefile
├── MuxKeyInternal.v
├── MuxKeyWithDefault.v
├── obj_dir
├── SimReg.v
├── test_our.cpp
├── top.v
└── vsrc
└── top.v

├── constr

└── top.nxdc

top=top

button BTNL

rst_n BTNC

leds (LD7, LD6, LD5, LD4, LD3, LD2, LD1, LD0)
hex0 (SEG0A, SEG0B, SEG0C, SEG0D, SEG0E, SEG0F, SEG0G, DEC0P)
hex1 (SEG1A, SEG1B, SEG1C, SEG1D, SEG1E, SEG1F, SEG1G, DEC1P)

├── csrc

└── test_our.cpp

#include<nvboard.h>
#include<Vtop.h>

static TOP_NAME dut;
 
void nvboard_bind_all_pins(TOP_NAME* top);
 
static void single_cycle(){
    dut.eval();
}
  
int main(){
	nvboard_bind_all_pins(&dut);
	nvboard_init();
	
	while(1){
		nvboard_update();
		single_cycle();
	}
 
}

├── test_our.cpp

#include "verilated.h"
#include "verilated_vcd_c.h"
#include "obj_dir/Vtop.h"

VerilatedContext* contextp = NULL;
VerilatedVcdC* tfp = NULL;

#define MAX_SIM_TIME 60
int sim_time = 0;

static Vtop* top;

void step_and_dump_wave(){
  top->eval();
  contextp->timeInc(1);
  tfp->dump(contextp->time());
}
void sim_init(){
  contextp = new VerilatedContext;
  tfp = new VerilatedVcdC;
  top = new Vtop;
  contextp->traceEverOn(true);
  top->trace(tfp, 0);
  tfp->open("dump.vcd");
}

void sim_exit(){
  step_and_dump_wave();
  tfp->close();
}

int main() {
  sim_init();

  while (sim_time < MAX_SIM_TIME) {
  	if (sim_time % 2)top->clk ^=1;
  	if (sim_time == 48) top->reset ^= 1;
  	if (sim_time == 2 | sim_time == 6 | sim_time == 22 | sim_time == 44) top->in ^= 1;
  	step_and_dump_wave();
  	sim_time ++;
  } 
  
  sim_exit();
}

├── vsrc

└── top.v

module top (
    input button,           // KEY0按钮输入
    input rst_n,            // 复位信号(低电平有效)
    output [7:0] hex0,      // 低位七段数码管(直接输出编码)
    output [7:0] hex1,      // 高位七段数码管
    output [7:0] leds       // LED显示当前状态
);

    // 8位LFSR寄存器(初始种子为00000001)
    reg [7:0] lfsr = 8'b00000001;
    
    
    // 反馈位计算:x4 XOR x3 XOR x2 XOR x0
    wire feedback = lfsr[4] ^ lfsr[3] ^ lfsr[2] ^ lfsr[0];
    
    // LFSR更新逻辑
    always @(negedge button or posedge rst_n) begin
    	if (rst_n) lfsr <= 8'b00000001;
    	else begin
            lfsr <= {feedback, lfsr[7:1]};  // 右移并插入反馈位
            if (lfsr == 8'b0) lfsr <= 8'b00000001; // 防全零锁定
        end
    end
    
    // 数码管显示模块(根据您提供的编码表)
    seg7_decoder seg0 (.bin(lfsr[3:0]), .seg(hex0));
    seg7_decoder seg1 (.bin(lfsr[7:4]), .seg(hex1));
    
    // LED显示当前状态
    assign leds = lfsr;

endmodule

// 根据您提供的编码表实现的七段译码器
module seg7_decoder (
    input [3:0] bin,
    output reg [7:0] seg
);
always @(*) begin
    case(bin)
        4'h0: seg = 8'b00000011; // 0
        4'h1: seg = 8'b10011111; // 1
        4'h2: seg = 8'b00100101; // 2
        4'h3: seg = 8'b00001101; // 3
        4'h4: seg = 8'b10011001; // 4
        4'h5: seg = 8'b01001001; // 5
        4'h6: seg = 8'b01000001; // 6
        4'h7: seg = 8'b00011111; // 7
        4'h8: seg = 8'b00000001; // 8
        4'h9: seg = 8'b00001001; // 9
        4'hA: seg = 8'b00010001; // A(自定义)
        4'hB: seg = 8'b11000001; // B(自定义)
        4'hC: seg = 8'b01100011; // C(自定义)
        4'hD: seg = 8'b10000101; // D(自定义)
        4'hE: seg = 8'b01100001; // E(自定义)
        4'hF: seg = 8'b01110001; // F(自定义)
        default: seg = 8'b11111111; // 全灭
    endcase
end
endmodule

├── MuxKeyInternal.v

module MuxKeyInternal #(NR_KEY = 2, KEY_LEN = 1, DATA_LEN = 1, HAS_DEFAULT = 0) (
  output reg [DATA_LEN-1:0] out,
  input [KEY_LEN-1:0] key,
  input [DATA_LEN-1:0] default_out,
  input [NR_KEY*(KEY_LEN + DATA_LEN)-1:0] lut
);

  localparam PAIR_LEN = KEY_LEN + DATA_LEN;
  wire [PAIR_LEN-1:0] pair_list [NR_KEY-1:0];
  wire [KEY_LEN-1:0] key_list [NR_KEY-1:0];
  wire [DATA_LEN-1:0] data_list [NR_KEY-1:0];

  generate
    for (genvar n = 0; n < NR_KEY; n = n + 1) begin
      assign pair_list[n] = lut[PAIR_LEN*(n+1)-1 : PAIR_LEN*n];
      assign data_list[n] = pair_list[n][DATA_LEN-1:0];
      assign key_list[n]  = pair_list[n][PAIR_LEN-1:DATA_LEN];
    end
  endgenerate

  reg [DATA_LEN-1 : 0] lut_out;
  reg hit;
  integer i;
  always @(*) begin
    lut_out = 0;
    hit = 0;
    for (i = 0; i < NR_KEY; i = i + 1) begin
      lut_out = lut_out | ({DATA_LEN{key == key_list[i]}} & data_list[i]);
      hit = hit | (key == key_list[i]);
    end
    if (!HAS_DEFAULT) out = lut_out;
    else out = (hit ? lut_out : default_out);
  end

endmodule

1.模块参数说明

NR_KEY:键值对的数量
KEY_LEN:键的位宽
DATA_LEN:数据的位宽
HAS_DEFAULT:是否启用默认输出(0/1)

2.端口定义

out:查找结果输出
key:查找键
default:默认输出值
lut:键值对查找表

3.核心实现分析

3.1 查找表解包
●将连续的lut输入分解为NR_KEY个键值对
●每个键值对长度为KEY_LEN + DATA_LEN
●分别存储到key_listdata_list数组
3.2 查找逻辑
●并行比较所有键值对
●使用位掩码技术实现多路选择
●hit信号标记是否找到匹配键
●根据HAS_DEFAULT决定是否使用默认值

├── MuxKeyWithDefault.v

module MuxKeyWithDefault #(NR_KEY = 2, KEY_LEN = 1, DATA_LEN = 1) (
  output [DATA_LEN-1:0] out,
  input [KEY_LEN-1:0] key,
  input [DATA_LEN-1:0] default_out,
  input [NR_KEY*(KEY_LEN + DATA_LEN)-1:0] lut
);
  MuxKeyInternal #(NR_KEY, KEY_LEN, DATA_LEN, 1) i0 (out, key, default_out, lut);
endmodule

├── SimReg.v

module SimReg #(
    parameter WIDTH = 4,          // 寄存器位宽
    parameter INIT_VALUE = 0      // 初始值
) (
    input clk,                   // 时钟信号
    input reset,                 // 异步复位
    input [WIDTH-1:0] din,       // 数据输入
    output reg [WIDTH-1:0] dout, // 数据输出
    input wen                    // 写使能
);

    // 寄存器存储
    reg [WIDTH-1:0] reg_data = INIT_VALUE;
    
    always @(posedge clk or posedge reset) begin
        if (reset) begin
            reg_data <= INIT_VALUE;  // 异步复位
        end else if (wen) begin      // 写使能有效时更新
            reg_data <= din;
        end
    end
    
    // 持续输出
    assign dout = reg_data;

endmodule

├── top.v

module top
(
  input   clk, in, reset,
  output reg out
);

parameter[3:0] S0 = 0, S1 = 1, S2 = 2, S3 = 3,
          S4 = 4, S5 = 5, S6 = 6, S7 = 7, S8 = 8;

wire [3:0] din, dout;
wire wen;

SimReg#(4,0) state(clk, reset, din, dout, wen);

assign wen = 1;

MuxKeyWithDefault#(9, 4, 1) outMux(.out(out), .key(dout), .default_out(0), .lut({
  S0, 1'b0,
  S1, 1'b0,
  S2, 1'b0,
  S3, ~in,
  S4, ~in,
  S5, 1'b0,
  S6, 1'b0,
  S7, in,
  S8, in
}));

MuxKeyWithDefault#(9, 4, 4) stateMux(.out(din), .key(dout), .default_out(S0), .lut({
  S0, in ? S5 : S1,
  S1, in ? S5 : S2,
  S2, in ? S5 : S3,
  S3, in ? S5 : S4,
  S4, in ? S5 : S4,
  S5, in ? S6 : S1,
  S6, in ? S7 : S1,
  S7, in ? S8 : S1,
  S8, in ? S8 : S1
}));

endmodule

波形仿真

(1)编译
verilator -Wall --trace -cc top.v --exe main.cpp
(2)生成可执行文件
 make -C obj_dir -f Vtop.mk Vtop
(3)生成波形
./obj_dir/Vtop
(4)查看波形
gtkwave dump.vcd

摩尔型

image

米里型

image

特性 摩尔型(Moore) 米里型(Mealy)
输出决定因素 仅取决于当前状态 取决于当前状态和输入
输出时序 输出在状态变化后一个时钟周期才改变 输入变化后输出可能立即改变
状态图表示 输出写在状态圈内 输出写在转移箭头上
实现复杂度 通常需要更多状态 通常可以用较少状态实现相同功能

PS/2键盘控制器的设计

.

├── build
├── constr
│ └── top.nxdc
├── csrc
│ └── test_our.cpp
├── dump.vcd
├── Makefile
├── key_decoder.v
├── ps2_keyboard.v
├── bcd7seg.v
├── obj_dir
├── test_our.cpp
├── top.v
└── vsrc
└── top.v

├── constr

└── top.nxdc

top=top

ps2_clk PS2_CLK
ps2_data PS2_DAT

seg0 (DEC0P, SEG0G, SEG0F, SEG0E, SEG0D, SEG0C, SEG0B, SEG0A)
seg1 (DEC1P, SEG1G, SEG1F, SEG1E, SEG1D, SEG1C, SEG1B, SEG1A)
seg2 (DEC2P, SEG2G, SEG2F, SEG2E, SEG2D, SEG2C, SEG2B, SEG2A)
seg3 (DEC3P, SEG3G, SEG3F, SEG3E, SEG3D, SEG3C, SEG3B, SEG3A)
seg4 (DEC6P, SEG6G, SEG6F, SEG6E, SEG6D, SEG6C, SEG6B, SEG6A)
seg5 (DEC7P, SEG7G, SEG7F, SEG7E, SEG7D, SEG7C, SEG7B, SEG7A)

├── csrc

└── test_our.cpp

#include <nvboard.h>
#include <Vtop.h>

static TOP_NAME dut;

void nvboard_bind_all_pins(Vtop* top);

static void single_cycle() {
  dut.clk = 0; dut.eval();
  dut.clk = 1; dut.eval();
}

static void reset(int n) {
  dut.rst = 1;
  while (n-- > 0) single_cycle();
  dut.rst = 0;
}


int main() {
  nvboard_bind_all_pins(&dut);
  nvboard_init();

  reset(10);

  while(1) {
    nvboard_update();
    single_cycle();
  }
}

├── test_our.cpp

#include "verilated.h"
#include "verilated_vcd_c.h"
#include "Vtop.h"

vluint64_t sim_time = 0;
const vluint64_t MAX_SIM_TIME = 10000;  // 足够长的仿真时间

Vtop *top;
VerilatedVcdC *tfp;

// 精确时序参数(与Verilog测试一致)
const int CLK_HALF_PERIOD = 5;   // 系统时钟半周期=5ns (100MHz)
const int PS2_HALF_PERIOD = 30;  // PS/2时钟半周期=30ns (~16.7kHz)

void eval_and_dump() {
    top->eval();
    tfp->dump(sim_time);
    sim_time++;
}

void sim_init() {
    Verilated::traceEverOn(true);
    top = new Vtop;
    tfp = new VerilatedVcdC;
    top->trace(tfp, 99);
    tfp->open("ps2_waveform.vcd");
}

void sim_exit() {
    tfp->close();
    top->final();
    delete top;
    delete tfp;
}

// 精确模拟PS/2时钟和数据时序
void ps2_clock_edge(bool falling) {
    if (falling) {
        // PS/2时钟下降沿(数据变化)
        top->ps2_clk = 0;
        for (int i = 0; i < PS2_HALF_PERIOD/CLK_HALF_PERIOD; i++) {
            top->clk = !top->clk;
            eval_and_dump();
        }
    } else {
        // PS/2时钟上升沿(数据稳定)
        top->ps2_clk = 1;
        for (int i = 0; i < PS2_HALF_PERIOD/CLK_HALF_PERIOD; i++) {
            top->clk = !top->clk;
            eval_and_dump();
        }
    }
}

// 精确模拟PS/2字节发送(与Verilog测试完全一致)
void ps2_send_byte(uint8_t byte) {
    // 计算奇校验位(与Verilog相同的算法)
    uint8_t parity = 0;
    for (int i = 0; i < 8; i++) {
        parity ^= (byte >> i) & 0x1;
    }
    parity = ~parity & 0x1;
    
    // 发送开始位(0)
    top->ps2_data = 0;
    ps2_clock_edge(true);  // 下降沿
    ps2_clock_edge(false); // 上升沿
    
    // 发送数据位(LSB first)
    for (int i = 0; i < 8; i++) {
        top->ps2_data = (byte >> i) & 0x1;
        ps2_clock_edge(true);
        ps2_clock_edge(false);
    }
    
    // 发送奇校验位
    top->ps2_data = parity;
    ps2_clock_edge(true);
    ps2_clock_edge(false);
    
    // 发送停止位(1)
    top->ps2_data = 1;
    ps2_clock_edge(true);
    ps2_clock_edge(false);
    
    // 总线空闲(与Verilog测试一致)
    for (int i = 0; i < 10; i++) {
        top->clk = !top->clk;
        eval_and_dump();
    }
}

int main() {
    sim_init();
    
    // 初始化(与Verilog测试完全一致)
    top->clk = 0;
    top->reset_n = 0;
    top->ps2_clk = 1;
    top->ps2_data = 1;
    top->nextdata_n = 1;
    
    // 复位周期(20ns后释放)
    for (int i = 0; i < 4; i++) {
        top->clk = !top->clk;
        eval_and_dump();
    }
    top->reset_n = 1;
    for (int i = 0; i < 4; i++) {
        top->clk = !top->clk;
        eval_and_dump();
    }
    
    // 精确重现Verilog测试序列
    printf("=== 精确重现Verilog测试波形 ===\n");
    
    // 1. 发送'A'键按下(0x1C)
    printf("[1] 发送'A'键按下(0x1C)\n");
    ps2_send_byte(0x1C);
    
    // 读取数据(20ns后请求,再20ns后释放)
    for (int i = 0; i < 4; i++) {
        top->clk = !top->clk;
        eval_and_dump();
    }
    printf("[2] 读取数据\n");
    top->nextdata_n = 0;
    for (int i = 0; i < 4; i++) {
        top->clk = !top->clk;
        eval_and_dump();
    }
    top->nextdata_n = 1;
    
    // 2. 发送Break code(0xF0)
    printf("[3] 发送Break code(0xF0)\n");
    ps2_send_byte(0xF0);
    
    // 读取数据
    for (int i = 0; i < 4; i++) {
        top->clk = !top->clk;
        eval_and_dump();
    }
    top->nextdata_n = 0;
    for (int i = 0; i < 4; i++) {
        top->clk = !top->clk;
        eval_and_dump();
    }
    top->nextdata_n = 1;
    
    // 3. 发送'A'键释放(0x1C)
    printf("[4] 发送'A'键释放(0x1C)\n");
    ps2_send_byte(0x1C);
    
    // 读取数据
    for (int i = 0; i < 4; i++) {
        top->clk = !top->clk;
        eval_and_dump();
    }
    top->nextdata_n = 0;
    for (int i = 0; i < 4; i++) {
        top->clk = !top->clk;
        eval_and_dump();
    }
    top->nextdata_n = 1;
    
    // 4. 发送'S'键按下(0x1B)
    printf("[5] 发送'S'键按下(0x1B)\n");
    ps2_send_byte(0x1B);
    
    // 保持按下状态(发送两次)
    for (int i = 0; i < 20; i++) {
        top->clk = !top->clk;
        eval_and_dump();
    }
    ps2_send_byte(0x1B);
    for (int i = 0; i < 20; i++) {
        top->clk = !top->clk;
        eval_and_dump();
    }
    ps2_send_byte(0x1B);
    
    // 5. 发送Break code + 'S'键释放
    printf("[6] 发送'S'键释放(0xF0 0x1B)\n");
    ps2_send_byte(0xF0);
    ps2_send_byte(0x1B);
    
    // 完成仿真
    while (sim_time < MAX_SIM_TIME) {
        top->clk = !top->clk;
        eval_and_dump();
    }
    
    sim_exit();
    printf("=== 仿真完成,波形已保存到ps2_waveform.vcd ===\n");
    return 0;
}

├── vsrc

└── top.v

module top (
    input clk,
    input rst,
    input ps2_clk, ps2_data,

    output [7:0] seg0,
    output [7:0] seg1,

    output [7:0] seg2,
    output [7:0] seg3,

    output [7:0] seg4,
    output [7:0] seg5
);

/* ps2_keyboard interface signals */
wire [7:0] data;
wire ready, overflow;
wire nextdata_n = 1'b0;

ps2_keyboard inst(
    .clk(clk),
    .clrn(~rst),
    .ps2_clk(ps2_clk),
    .ps2_data(ps2_data),
    .data(data),
    .ready(ready),
    .nextdata_n(nextdata_n),
    .overflow(overflow)
);

reg seg_en;
reg [7:0] cnt;
reg [1:0] state;
reg [7:0] curdata;
reg [7:0] ascii;

key_decoder inst2(
    .clk(clk),
    .kbd_data(curdata),
    .ascii(ascii)
);

wire [7:0] cnt_units = cnt % 10;  // 个位数
wire [7:0] cnt_tens = cnt / 10;   // 十位数

bcd7seg bcd7seg0(.en(seg_en), .b(curdata[3:0]), .h(seg0));
bcd7seg bcd7seg1(.en(seg_en), .b(curdata[7:4]), .h(seg1));

bcd7seg bcd7seg2(.en(seg_en), .b(ascii[3:0]), .h(seg2));
bcd7seg bcd7seg3(.en(seg_en), .b(ascii[7:4]), .h(seg3));


bcd7seg bcd7seg4(.en(1), .b(cnt_units[3:0]), .h(seg4));
bcd7seg bcd7seg5(.en(1), .b(cnt_tens[3:0]), .h(seg5));

always @(posedge clk) begin
    if (rst == 0 && ready) begin
        $display("keyboard: %x", data);
        if (state == 2'b00) begin
            seg_en <= 1'b1;
            if (cnt < 99) cnt <= cnt + 8'd1;
            else cnt <= 0;
            curdata <= data;
            state <= 2'b01;
        end else if (state == 2'b01) begin
            if (data == 8'hf0) begin
                state <= 2'b10;
                seg_en <= 1'b0;
            end
        end else if (state == 2'b10) begin
            state <= 2'b00;
        end
    end
end


initial begin
    seg_en = 1'b0;
    cnt = 0;
    state = 0;
end

endmodule

├── key_decoder.v

module key_decoder (
    input clk,
    input [7:0] kbd_data,
    output reg [7:0] ascii
);

always @(posedge clk) begin
    case (kbd_data)
        8'h1c: ascii <= 8'h61; // a
        8'h32: ascii <= 8'h62; // b
        8'h21: ascii <= 8'h63; // c
        8'h23: ascii <= 8'h64; // d
        8'h24: ascii <= 8'h65; // e
        8'h2b: ascii <= 8'h66; // f
        8'h34: ascii <= 8'h67; // g
        8'h33: ascii <= 8'h68; // h
        8'h43: ascii <= 8'h69; // i
        8'h3b: ascii <= 8'h6a; // j
        8'h42: ascii <= 8'h6b; // k
        8'h4b: ascii <= 8'h6c; // l
        8'h3a: ascii <= 8'h6d; // m
        8'h31: ascii <= 8'h6e; // n
        8'h44: ascii <= 8'h6f; // o
        8'h4d: ascii <= 8'h70; // p
        8'h15: ascii <= 8'h71; // q
        8'h2d: ascii <= 8'h72; // r
        8'h1b: ascii <= 8'h73; // s
        8'h2c: ascii <= 8'h74; // t
        8'h3c: ascii <= 8'h75; // u
        8'h2a: ascii <= 8'h76; // v
        8'h1d: ascii <= 8'h77; // w
        8'h22: ascii <= 8'h78; // x
        8'h35: ascii <= 8'h79; // y
        8'h1a: ascii <= 8'h7a; // z
        8'h45: ascii <= 8'h30; // 0
        8'h16: ascii <= 8'h31; // 1
        8'h1e: ascii <= 8'h32; // 2
        8'h26: ascii <= 8'h33; // 3
        8'h25: ascii <= 8'h34; // 4
        8'h2e: ascii <= 8'h35; // 5
        8'h36: ascii <= 8'h36; // 6
        8'h3d: ascii <= 8'h37; // 7
        8'h3e: ascii <= 8'h38; // 8
        8'h46: ascii <= 8'h39; // 9
        default: ascii <= 8'h00;
    endcase
end

endmodule

├── ps2_keyboard.v

接收键盘送来的数据
image
image
image

├── bcd7seg.v

module bcd7seg(
  input [3:0] b,
  input en,
  output reg [7:0] h
);

  always @(*) begin
    if (!en) begin
      h = 8'b11111111;
    end else case(b)
      4'h0: h = 8'b00000011; // 0
      4'h1: h = 8'b10011111; // 1
      4'h2: h = 8'b00100101; // 2
      4'h3: h = 8'b00001101; // 3
      4'h4: h = 8'b10011001; // 4
      4'h5: h = 8'b01001001; // 5
      4'h6: h = 8'b01000001; // 6
      4'h7: h = 8'b00011111; // 7
      4'h8: h = 8'b00000001; // 8
      4'h9: h = 8'b00001001; // 9
      4'hA: h = 8'b00010001; // A
      4'hB: h = 8'b11000001; // B
      4'hC: h = 8'b01100011; // C
      4'hD: h = 8'b10000101; // D
      4'hE: h = 8'b01100001; // E
      4'hF: h = 8'b01110001; // F
      default: h = 8'b11111111; // 全灭
    endcase
  end

endmodule

├── top.v

module top(
    input clk,
    input reset_n,
    input ps2_clk,
    input ps2_data,
    input nextdata_n,
    output [7:0] ascii_out,
    output ready,
    output key_pressed,
    output overflow
);

    // 内部连接信号
    wire [7:0] scan_code;
    
    // PS/2键盘接收模块
    ps2_keyboard keyboard_receiver (
        .clk(clk),
        .clrn(reset_n),
        .ps2_clk(ps2_clk),
        .ps2_data(ps2_data),
        .data(scan_code),
        .ready(ready),
        .nextdata_n(nextdata_n),  // 持续读取数据
        .overflow(overflow)
    );
    
    // 扫描码转ASCII解码模块
    key_decoder decoder (
        .clk(clk),
        .rst_n(reset_n),
        .scan_code(scan_code),
        .data_ready(ready),
        .ascii(ascii_out),
        .key_pressed(key_pressed)
    );

endmodule

波形仿真

(1)编译
verilator -Wall --trace -cc top.v --exe main.cpp
(2)生成可执行文件
 make -C obj_dir -f Vtop.mk Vtop
(3)生成波形
./obj_dir/Vtop
(4)查看波形
gtkwave dump.vcd

image

接入NVBoard

make
cd build
./top

效果

高级选做内容

●支持ShiftCTRL等组合键,在LED上显示组合键是否按下的状态指示
●支持Shift键与字母/数字键同时按下,相互不冲突
●支持输入大写字符,显示对应的ASCII

always @(posedge clk) begin
    if (rst) begin
        shift_state <= 1'b0;
        ctrl_state <= 1'b0;
        state <= 2'b00;
        seg_en <= 1'b0;
        cnt <= 0;
    end
    else if (ready) begin
        $display("keyboard: %x", data);
        case (state)
            2'b00: begin
                if (data == 8'hf0) begin
                    // 释放前缀,进入状态01
                    state <= 2'b01;
                    seg_en <= 1'b0;
                end
                else if (data == 8'h12 || data == 8'h59) begin // Shift键按下
                    shift_state <= 1'b1;
                    state <= 2'b10;
                    seg_en <= 1'b0;
                end
                else if (data == 8'h14) begin // Ctrl键按下
                    ctrl_state <= 1'b1;
                    state <= 2'b10;
                    seg_en <= 1'b0;
                end
                else begin // 普通键按下
                    seg_en <= 1'b1;
                    if (cnt < 99) cnt <= cnt + 8'd1;
                    else cnt <= 0;
                    curdata <= data;
                    state <= 2'b10;
                end
            end
            
            2'b01: begin // 处理键释放
                if (data == 8'h12 || data == 8'h59) begin // Shift键释放
                    shift_state <= 1'b0;
                end
                else if (data == 8'h14) begin // Ctrl键释放
                    ctrl_state <= 1'b0;
                end
                state <= 2'b00;
            end
            
            2'b10: begin // 完成按键处理
                if (data == 8'hf0) begin
                    // 释放前缀,进入状态01
                    state <= 2'b01;
                    seg_en <= 1'b0;
                end
                else state <= 2'b00;
            end
            default: ;
        endcase
    end
end


initial begin
    seg_en = 1'b0;
    cnt = 0;
    state = 0;
    shift_state = 0;
    ctrl_state = 0;
end
module key_decoder (
    input clk,
    input shift,        // Shift键状态
    input [7:0] kbd_data,
    output reg [7:0] ascii
);

always @(posedge clk) begin
    case (kbd_data)
        // 字母键 (支持Shift)
        8'h1c: ascii <= shift ? 8'h41 : 8'h61; // A/a
        8'h32: ascii <= shift ? 8'h42 : 8'h62; // B/b
        8'h21: ascii <= shift ? 8'h43 : 8'h63; // C/c
        8'h23: ascii <= shift ? 8'h44 : 8'h64; // D/d
        8'h24: ascii <= shift ? 8'h45 : 8'h65; // E/e
        8'h2b: ascii <= shift ? 8'h46 : 8'h66; // F/f
        8'h34: ascii <= shift ? 8'h47 : 8'h67; // G/g
        8'h33: ascii <= shift ? 8'h48 : 8'h68; // H/h
        8'h43: ascii <= shift ? 8'h49 : 8'h69; // I/i
        8'h3b: ascii <= shift ? 8'h4A : 8'h6a; // J/j
        8'h42: ascii <= shift ? 8'h4B : 8'h6b; // K/k
        8'h4b: ascii <= shift ? 8'h4C : 8'h6c; // L/l
        8'h3a: ascii <= shift ? 8'h4D : 8'h6d; // M/m
        8'h31: ascii <= shift ? 8'h4E : 8'h6e; // N/n
        8'h44: ascii <= shift ? 8'h4F : 8'h6f; // O/o
        8'h4d: ascii <= shift ? 8'h50 : 8'h70; // P/p
        8'h15: ascii <= shift ? 8'h51 : 8'h71; // Q/q
        8'h2d: ascii <= shift ? 8'h52 : 8'h72; // R/r
        8'h1b: ascii <= shift ? 8'h53 : 8'h73; // S/s
        8'h2c: ascii <= shift ? 8'h54 : 8'h74; // T/t
        8'h3c: ascii <= shift ? 8'h55 : 8'h75; // U/u
        8'h2a: ascii <= shift ? 8'h56 : 8'h76; // V/v
        8'h1d: ascii <= shift ? 8'h57 : 8'h77; // W/w
        8'h22: ascii <= shift ? 8'h58 : 8'h78; // X/x
        8'h35: ascii <= shift ? 8'h59 : 8'h79; // Y/y
        8'h1a: ascii <= shift ? 8'h5A : 8'h7a; // Z/z
        
        // 数字键 (支持Shift切换符号)
        8'h16: ascii <= shift ? 8'h21 : 8'h31; // 1/!
        8'h1e: ascii <= shift ? 8'h40 : 8'h32; // 2/@
        8'h26: ascii <= shift ? 8'h23 : 8'h33; // 3/#
        8'h25: ascii <= shift ? 8'h24 : 8'h34; // 4/$
        8'h2e: ascii <= shift ? 8'h25 : 8'h35; // 5/%
        8'h36: ascii <= shift ? 8'h5E : 8'h36; // 6/^
        8'h3d: ascii <= shift ? 8'h26 : 8'h37; // 7/&
        8'h3e: ascii <= shift ? 8'h2A : 8'h38; // 8/*
        8'h46: ascii <= shift ? 8'h28 : 8'h39; // 9/(
        8'h45: ascii <= shift ? 8'h29 : 8'h30; // 0/)
        default: ascii <= 8'h00;
    endcase
end

endmodule
posted @ 2025-11-21 21:36  mo686  阅读(58)  评论(0)    收藏  举报