feat(board): 支持 PS/2 键盘(与鼠标共用板载 USB HID 口),并修好 PS/2 帧校验
一、修 PS/2 接收器的真 bug —— 这就是上板“鼠标毫无反应”的根因
鼠标和键盘两个接收模块原来是同一个写法错误:
* 帧用整体右移 {data, frame_q[10:1]},判 frame_q[10] 时停止位还没移进来,判到的
其实是校验位;取数据 frame_q[8:1] 也因此整体错一位
* 校验位按“偶校验”判,而 PS/2 是“奇校验”
两者叠加让真设备发来的帧 100% 被丢掉(上板表现为 rx_count 恒为 0,看起来像硬件
或跳线问题)。现在按“位的下标”存帧(frame_q[i] = 第 i 位,所以字节 = frame_q[8:1]、
校验 = frame_q[9]),停止位直接在当前拍的 ps2_data 上判,校验按奇校验。
二、新增 PS/2 键盘解码 cpu21_ps2_kbd.v
扫描码集 2:E0 = 扩展前缀、F0 = 松开前缀,只对“按下”产生事件,并映射成与鼠标
完全相同的事件格式(↑↓←→ = dy/dx ±8,正好一格;Enter/空格 = 左键;左 Shift/F/C
= 右键 = 插旗),所以 saolei 的 C 代码一行都不用改;按住方向键 = 连续移动(用的是
键盘自己的重复码)。
三、顶层 cpu21_board_top:用 SW15 选设备
同一根 PS/2 线上只会插一个设备,但两个解码器都在听:键盘的扫描码会凑巧组成
“合法的鼠标包”,两路同时生效会让光标乱跳。因此新增端口 hid_sel(SW15):
0 = 键盘(默认),1 = 鼠标;诊断 LED 改显示“当前选中设备”收到的字节数。
四、其它
* XDC:hid_sel -> V10(SW15),引脚号在官方母版里核对过
* tb_board_top 补 hid_sel 端口,避免悬空
* 新增自测 srcs/tb/tb_ps2_mouse.v / tb_ps2_kbd.v:含“故意发一个校验位错的帧,
必须丢帧”的用例,两个都 ALL PASS
* README / saolei/README:新增“PS/2 键鼠”一节(接线方式、SW15、LED 诊断口诀、
键位表),修正显存格式(每格 2 字节、0x2000-0x2FFF)、扫雷难度与验证状态、
修好 riscv 工具链 -Prefix 路径
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
// rst_n (C12) -> 复位按键 CPU_RESETN,低有效,经复位同步器
|
||||
// go (N17) -> 按键 BTNC:ecall 停机后按下继续运行
|
||||
// irq_sw[2:0] -> 拨码开关 SW[2:0]:3 路中断源
|
||||
// hid_sel (V10) -> 拨码开关 SW[15]:选 PS/2 设备(0 = 键盘,1 = 鼠标)
|
||||
// sw_flag (R15) -> 拨码开关 SW[3]:插旗模式(软件读 INPUT 寄存器)
|
||||
// led[15:0] -> 板载 LED[15:0]:LED[2:0] = 三路中断指示,从该路中断
|
||||
// 请求出现开始点亮,一直到它的 ISR 执行 uret 返回后才熄灭;
|
||||
// LED[15:3] 熄灭
|
||||
@@ -52,10 +54,13 @@ module cpu21_board_top #(
|
||||
// 取大于 2 的值只是把实际时钟变慢,XDC 的 cpu_clk 仍按最坏情况 50 MHz 约束,
|
||||
// 所以不会掩盖时序违例(约束偏严)。
|
||||
parameter integer CLK_DIV_INTRUPT = 32,
|
||||
// 鼠标事件源:0 = 板上 5 向按键(免外设,默认);1 = PS/2 鼠标(Pmod JA)
|
||||
parameter integer USE_PS2_MOUSE = 1, // 1 = 启用板载 USB HID 口(PS/2 键鼠),板载按键同时有效
|
||||
// PS/2 输入(板载 USB HID 口,也可改接 Pmod JA):0 = 关闭(只剩板上 5 向按键);
|
||||
// 1 = 开启。开启后同时例化鼠标与键盘两个解码器(它们共用同一根 PS/2 线),
|
||||
// 用 SW[15](端口 hid_sel)选哪个生效:0 = 键盘(默认),1 = 鼠标。
|
||||
// 不能两个同时生效:键盘的扫描码会凑巧组成合法的鼠标包,让光标乱跳。
|
||||
parameter integer USE_PS2_MOUSE = 1,
|
||||
// 调试:1 = 用 LED 高位显示 PS/2 诊断信息:
|
||||
// LED[15:6] = PS/2 收到的字节数低 10 位(一动鼠标就跳 = 收发都通了)
|
||||
// LED[15:6] = 当前选中设备收到的字节数低 10 位(按一下键/动一下鼠标就跳 = 收发都通)
|
||||
// LED[5] = ps2_clk 有边沿活动(时钟在动就快速翻转)
|
||||
// LED[4] = ps2_data 当前电平
|
||||
// LED[3] = ps2_clk 当前电平
|
||||
@@ -74,6 +79,7 @@ module cpu21_board_top #(
|
||||
input wire btnl, // P17:光标左
|
||||
input wire btnr, // M17:光标右
|
||||
input wire sw_flag, // R15:SW[3],插旗模式(软件读 INPUT 寄存器)
|
||||
input wire hid_sel, // V10:SW[15],选 PS/2 设备(0 = 键盘,1 = 鼠标)
|
||||
output wire [15:0] led, // LED[15:0]
|
||||
output wire led_we, // LED16_B:MMIO 写入指示
|
||||
// 七段数码管(共阳极:位选/段选都是低有效)
|
||||
@@ -167,7 +173,8 @@ module cpu21_board_top #(
|
||||
wire [31:0] data_wdata;
|
||||
wire [ 3:0] data_wstrb;
|
||||
wire data_we;
|
||||
wire [15:0] ps2_rx_count; // PS/2 收到的字节数(用于 LED 调试显示)
|
||||
wire [15:0] ps2_rx_count; // PS/2 鼠标收到的字节数(用于 LED 调试显示)
|
||||
wire [15:0] kbd_rx_count; // PS/2 键盘收到的字节数(先声明:LED 调试段要用)
|
||||
wire [31:0] led_data; // LED_ADDR 写入值,同时作为七段数码管显示数据
|
||||
wire led_valid;
|
||||
// 扫雷扩展外设的连线(先声明,例化在下面)
|
||||
@@ -366,9 +373,12 @@ module cpu21_board_top #(
|
||||
end
|
||||
end
|
||||
|
||||
// 当前选中的 PS/2 设备收到的字节数(SW[15] 选谁就显示谁)
|
||||
wire [15:0] hid_rx_sel = hid_sel ? ps2_rx_count : kbd_rx_count;
|
||||
|
||||
// LED[15:3] 平时熄灭;DEBUG_PS2_RX_LED=1 时显示 PS/2 诊断信息(含义见参数注释)
|
||||
assign led = (DEBUG_PS2_RX_LED != 0)
|
||||
? {ps2_rx_count[9:0], ps2_edge_cnt[0], ps2_data_s2, ps2_clk_s2, irq_led_q}
|
||||
? {hid_rx_sel[9:0], ps2_edge_cnt[0], ps2_data_s2, ps2_clk_s2, irq_led_q}
|
||||
: {{13{1'b0}}, irq_led_q};
|
||||
|
||||
reg [23:0] led_we_cnt;
|
||||
@@ -477,6 +487,12 @@ module cpu21_board_top #(
|
||||
wire [7:0] ps2_dx, ps2_dy;
|
||||
wire [4:0] ps2_btn;
|
||||
|
||||
// PS/2 键盘:与鼠标共用同一根 PS/2 线(板上只有一个 USB HID 口)。
|
||||
// 线上只有一个设备在说话,"是哪一个"用 SW[15] 告诉硬件(hid_sel)。
|
||||
wire kbd_valid;
|
||||
wire [7:0] kbd_dx, kbd_dy;
|
||||
wire [4:0] kbd_btn;
|
||||
|
||||
generate
|
||||
if (USE_PS2_MOUSE != 0) begin : g_ps2
|
||||
cpu21_ps2_mouse u_ps2 (
|
||||
@@ -492,20 +508,41 @@ module cpu21_board_top #(
|
||||
.ev_btn_o (ps2_btn),
|
||||
.rx_count_o (ps2_rx_count)
|
||||
);
|
||||
|
||||
cpu21_ps2_kbd u_kbd (
|
||||
.clk (clk_cpu),
|
||||
.reset (reset),
|
||||
.ps2_clk_i (ps2_clk),
|
||||
.ps2_data_i (ps2_data),
|
||||
.ev_valid_o (kbd_valid),
|
||||
.ev_dx_o (kbd_dx),
|
||||
.ev_dy_o (kbd_dy),
|
||||
.ev_btn_o (kbd_btn),
|
||||
.rx_count_o (kbd_rx_count)
|
||||
);
|
||||
end else begin : g_no_ps2
|
||||
assign ps2_valid = 1'b0;
|
||||
assign ps2_dx = 8'd0;
|
||||
assign ps2_dy = 8'd0;
|
||||
assign ps2_btn = 5'd0;
|
||||
assign ps2_rx_count = 16'd0;
|
||||
assign kbd_valid = 1'b0;
|
||||
assign kbd_dx = 8'd0;
|
||||
assign kbd_dy = 8'd0;
|
||||
assign kbd_btn = 5'd0;
|
||||
assign kbd_rx_count = 16'd0;
|
||||
end
|
||||
endgenerate
|
||||
|
||||
// 3) 合并:同一拍只有一方产生事件(两个源相互独立),优先 PS/2
|
||||
assign ev_valid = ps2_valid | ptr_valid;
|
||||
assign ev_dx = ps2_valid ? ps2_dx : ptr_dx;
|
||||
assign ev_dy = ps2_valid ? ps2_dy : ptr_dy;
|
||||
assign ev_btn = ps2_valid ? ps2_btn : ptr_btn;
|
||||
// 3) 只放行 SW[15] 选中的那一路 PS/2 设备(另一个解码器虽然也在听,但事件被挡住)
|
||||
wire m_valid = ps2_valid & hid_sel; // SW[15] = 1 -> PS/2 鼠标
|
||||
wire k_valid = kbd_valid & ~hid_sel; // SW[15] = 0 -> PS/2 键盘
|
||||
|
||||
// 4) 合并:三个源相互独立,同一拍只会有一方产生事件;优先 PS/2
|
||||
assign ev_valid = m_valid | k_valid | ptr_valid;
|
||||
assign ev_dx = m_valid ? ps2_dx : k_valid ? kbd_dx : ptr_dx;
|
||||
assign ev_dy = m_valid ? ps2_dy : k_valid ? kbd_dy : ptr_dy;
|
||||
assign ev_btn = m_valid ? ps2_btn : k_valid ? kbd_btn : ptr_btn;
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
Reference in New Issue
Block a user