实现PS/2键盘接口控制器,接收11位帧数据,解码扫描码,支持奇偶校验。
PS/2接收器结构:
ps2_clk → 2级FF同步 → 下降沿检测
│
ps2_data → 2级FF同步 → 移位寄存器
│
bit_cnt → 状态机
帧: [start=0][d0..d7][parity][stop=1]
奇校验: ^(data[7:0]) ^ parity = 1// PS/2 Keyboard - PS/2键盘扫描码解码器
// 接收11-bit帧: 1起始位 + 8数据位 + 1校验位 + 1停止位
module ps2_keyboard(
input wire clk,
input wire rst,
input wire ps2_clk, // PS/2 clock from keyboard
input wire ps2_data, // PS/2 data from keyboard
output reg [7:0] scan_code,
output reg valid, // New scan code received
output reg parity_err // Parity error detected
);
// Synchronize PS/2 signals to system clock
reg ps2_clk_sync [0:2];
reg ps2_data_sync [0:2];
always @(posedge clk or posedge rst) begin
if (rst) begin
ps2_clk_sync[0] <= 1'b1;
ps2_clk_sync[1] <= 1'b1;
ps2_clk_sync[2] <= 1'b1;
ps2_data_sync[0] <= 1'b1;
ps2_data_sync[1] <= 1'b1;
ps2_data_sync[2] <= 1'b1;
end else begin
ps2_clk_sync[0] <= ps2_clk;
ps2_clk_sync[1] <= ps2_clk_sync[0];
ps2_clk_sync[2] <= ps2_clk_sync[1];
ps2_data_sync[0] <= ps2_data;
ps2_data_sync[1] <= ps2_data_sync[0];
ps2_data_sync[2] <= ps2_data_sync[1];
end
end
// Detect falling edge of PS/2 clock
wire ps2_clk_falling = ps2_clk_sync[2] & ~ps2_clk_sync[1];
reg [3:0] bit_cnt;
reg [7:0] shift_reg;
reg parity_bit;
always @(posedge clk or posedge rst) begin
if (rst) begin
scan_code <= 8'd0;
valid <= 1'b0;
parity_err <= 1'b0;
bit_cnt <= 4'd0;
shift_reg <= 8'd0;
parity_bit <= 1'b0;
end else begin
valid <= 1'b0;
if (ps2_clk_falling) begin
case (bit_cnt)
4'd0: begin
// Start bit - should be 0
bit_cnt <= bit_cnt + 4'd1;
end
4'd1, 4'd2, 4'd3, 4'd4,
4'd5, 4'd6, 4'd7, 4'd8: begin
// Data bits (LSB first)
shift_reg <= {ps2_data_sync[1], shift_reg[7:1]};
bit_cnt <= bit_cnt + 4'd1;
end
4'd9: begin
// Parity bit (odd parity)
parity_bit <= ps2_data_sync[1];
bit_cnt <= bit_cnt + 4'd1;
end
4'd10: begin
// Stop bit - should be 1
scan_code <= shift_reg;
// Check odd parity: data bits XOR parity should be 1
parity_err <= ~(^shift_reg ^ parity_bit);
valid <= 1'b1;
bit_cnt <= 4'd0;
end
default: bit_cnt <= 4'd0;
endcase
end
end
end
endmodule
测试:发送扫描码0x1C(Enter)和0x5A,均正确接收且奇校验通过(parity_err=0)。
本实验所有Verilog代码已通过Verilator编译验证,功能行为正确。测试用例覆盖核心功能路径,确保设计满足规格要求。
PS/2虽是老接口(1987年IBM PS/2),但协议简单仍在嵌入式教学和工业控制中使用。USB HID协议本质上也是扫描码的演变。2024年很多工业主板仍保留PS/2接口因为其中断驱动特性在BIOS层面更可靠。
尝试修改代码中的关键参数,观察仿真结果变化:
💡 Verilator会在位宽不匹配时给出Warning,这是学习的好机会
在现有基础上增加新功能:
🔧 增量开发:每次只改一个地方,验证通过后再改下一个
| 语法 | 说明 | 示例 |
|---|---|---|
reg [7:0] | 8位寄存器 | reg [7:0] data; |
wire | 组合逻辑连线 | wire valid = cnt > 5; |
always @(posedge clk) | 时序逻辑 | 上升沿触发 |
always @(*) | 组合逻辑 | 敏感列表自动推导 |
localparam | 局部常量 | localparam DIV = 50000000; |
case | 多分支选择 | 注意default分支 |
$display | 仿真打印 | 不可综合,仅仿真用 |
本设计在典型FPGA上的资源占用估算:LUT约20-120个,FF约30-100个,无BRAM/DSP依赖(特殊模块除外)。时钟频率可达50-100MHz+。Verilator仿真速度约5-10M周期/秒。