From 28c696ece28ed3f59b2612326fbf7d44b868000b Mon Sep 17 00:00:00 2001 From: Alex-create-beep <2965312221@qq.com> Date: Wed, 23 Sep 2026 22:50:19 +0800 Subject: [PATCH] =?UTF-8?q?fix(ps2):=20=E9=BC=A0=E6=A0=87=20dy=20=E6=96=B9?= =?UTF-8?q?=E5=90=91=E5=8F=96=E8=B4=9F=EF=BC=88PS/2=20=E6=AD=A3=20dy=20=3D?= =?UTF-8?q?=20=E4=B8=8A=EF=BC=8C=E8=BD=AF=E4=BB=B6=E6=AD=A3=20dy=20=3D=20?= =?UTF-8?q?=E4=B8=8B=EF=BC=89=EF=BC=9B=E9=94=AE=E7=9B=98=E8=A1=A5=20W/A/S/?= =?UTF-8?q?D=20=E5=88=AB=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 上板实测:鼠标已能操作(说明“奇校验 + 帧按位的下标对齐”那个修复就是根因), 但上下是反的、左右正确 —— 这是两套约定的差异,不是解码错: * PS/2 鼠标原生约定:dx 正 = 右、**dy 正 = 上** * saolei.c 的约定:正 dx = 列 +1、**正 dy = 行 +1(向下)**,和 cpu21_pointer 的 BTND = +8 一致 所以 cpu21_ps2_mouse 在拼包时把 dy 取负(dx 不动),三种事件源(板载按键 / PS/2 鼠标 / PS/2 键盘)方向统一。 键盘:另加 W/A/S/D = 方向键(与 tools/play_saolei.tcl 里的别名保持一致),这样用普通 字母键也能测。 自测同步更新(都 ALL PASS): * tb_ps2_mouse:改发“鼠标向上”的 dy=+8,事件里必须得到 dy=-8;向下 dy=-5 -> +5 * tb_ps2_kbd:新增 W(1D) -> dy=-8 用例 --- pipelined-cpu/srcs/rtl/cpu21_ps2_kbd.v | 9 +++++++++ pipelined-cpu/srcs/rtl/cpu21_ps2_mouse.v | 9 ++++++--- pipelined-cpu/srcs/tb/tb_ps2_kbd.v | 8 +++++++- pipelined-cpu/srcs/tb/tb_ps2_mouse.v | 17 +++++++++++------ 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/pipelined-cpu/srcs/rtl/cpu21_ps2_kbd.v b/pipelined-cpu/srcs/rtl/cpu21_ps2_kbd.v index cc3439c..f9fd385 100644 --- a/pipelined-cpu/srcs/rtl/cpu21_ps2_kbd.v +++ b/pipelined-cpu/srcs/rtl/cpu21_ps2_kbd.v @@ -6,9 +6,13 @@ // 键盘只要产出**同样格式**的事件,软件一行都不用改。 // // ↑ ↓ ← → 光标移动(dx/dy = ±8,恰好是软件里的"一格") +// W A S D 同上(别名,与 tools/play_saolei.tcl 一致) // Enter / 空格 左键(开格) // 左 Shift / F / C 右键(软件里右键 = 插旗) // +// 方向约定跟软件对齐:**dy = +8 是向下**(与 cpu21_pointer 的 BTND、cpu21_ps2_mouse 一致), +// 所以 ↑ 发 dy = 248(= -8 的补码)。 +// // PS/2 键盘发扫描码(集 2): // 0xF0 前缀 -> 下一个码是"松开"(break);0xE0 前缀 -> 下一个码是扩展键 // 方向键是扩展键:↑ = E0 75、↓ = E0 72、← = E0 6B、→ = E0 74 @@ -89,6 +93,11 @@ module cpu21_ps2_kbd ( 8'h12: begin ev_dx_o <= 8'd0; ev_dy_o <= 8'd0; ev_btn_o <= 5'b00010; ev_valid_o <= 1'b1; end // 左 Shift 8'h2B: begin ev_dx_o <= 8'd0; ev_dy_o <= 8'd0; ev_btn_o <= 5'b00010; ev_valid_o <= 1'b1; end // F 8'h2E: begin ev_dx_o <= 8'd0; ev_dy_o <= 8'd0; ev_btn_o <= 5'b00010; ev_valid_o <= 1'b1; end // C + // W/A/S/D 也当方向键(和 tools/play_saolei.tcl 里的别名保持一致) + 8'h1D: begin ev_dx_o <= 8'd0; ev_dy_o <= 8'd248; ev_btn_o <= 5'd0; ev_valid_o <= 1'b1; end // W = ↑ + 8'h1B: begin ev_dx_o <= 8'd0; ev_dy_o <= 8'd8; ev_btn_o <= 5'd0; ev_valid_o <= 1'b1; end // S = ↓ + 8'h1C: begin ev_dx_o <= 8'd248; ev_dy_o <= 8'd0; ev_btn_o <= 5'd0; ev_valid_o <= 1'b1; end // A = ← + 8'h23: begin ev_dx_o <= 8'd8; ev_dy_o <= 8'd0; ev_btn_o <= 5'd0; ev_valid_o <= 1'b1; end // D = → default: ; endcase end diff --git a/pipelined-cpu/srcs/rtl/cpu21_ps2_mouse.v b/pipelined-cpu/srcs/rtl/cpu21_ps2_mouse.v index 31b0bda..ae9795a 100644 --- a/pipelined-cpu/srcs/rtl/cpu21_ps2_mouse.v +++ b/pipelined-cpu/srcs/rtl/cpu21_ps2_mouse.v @@ -19,7 +19,8 @@ // 所以字节 = frame_q[8:1](frame_q[1] 就是 d0),校验 = frame_q[9]。 // 注意 PS/2 是**奇校验**:data + parity 9 位里 1 的个数为奇数。 // 2) 解析:byte0 = 标志(bit0 左键、bit1 右键、bit3 固定 1),byte1 = dx, -// byte2 = dy,bit7 为符号位; +// byte2 = dy,bit7 为符号位;dx 正 = 右,**dy 正 = 上**(PS/2 的原生方向), +// 而软件里正 dy = 下,所以上报前把 dy 取负,让鼠标和板载按键/键盘方向一致; // 3) 上报:当 dx/dy 非 0 或按键状态变化时产生一个事件(单位与 // cpu21_pointer 相同,即原始计数 = 1/8 格); // 4) 本模块只做"接收",不实现主机→鼠标的命令发送(0xF4 使能上报)。多数 @@ -131,8 +132,10 @@ module cpu21_ps2_mouse ( end 8'd2: begin byte_cnt_q <= 8'd0; - ev_dx_o <= pkt_q[15:8]; // byte1 = dx - ev_dy_o <= frame_q[8:1]; // byte2 = dy + ev_dx_o <= pkt_q[15:8]; // byte1 = dx(正 = 右,与软件一致) + // byte2 = dy:**PS/2 的正 dy 是鼠标上移**,而软件里正 dy = 光标下移 + // (与 cpu21_pointer 的 BTND = +8 一致),所以这里要取负。 + ev_dy_o <= 8'd0 - frame_q[8:1]; // 与 cpu21_mmio / mmio.h 一致:bit0=左键, bit1=右键, bit2=中键 ev_btn_o <= {2'b0, pkt_q[2], pkt_q[1], pkt_q[0]}; ev_valid_o <= 1'b1; diff --git a/pipelined-cpu/srcs/tb/tb_ps2_kbd.v b/pipelined-cpu/srcs/tb/tb_ps2_kbd.v index 618277e..43c4260 100644 --- a/pipelined-cpu/srcs/tb/tb_ps2_kbd.v +++ b/pipelined-cpu/srcs/tb/tb_ps2_kbd.v @@ -4,6 +4,7 @@ // // 发 → 键(E0 74)应产生 dx=+8;发 ↑(E0 75)应产生 dy=-8; // 发 Enter(5A)应产生 btn=1(开格);发 左Shift(12)应产生 btn=2(插旗); +// 发 W(1D)应产生 dy=-8(W/A/S/D 别名); // 松开码(F0 xx)不应产生事件;校验位错的帧应被整帧丢掉。 // // PS/2 键盘发扫描码(集 2):0xF0 前缀 = 下一个码是"松开",0xE0 前缀 = 扩展键, @@ -152,8 +153,13 @@ module tb_ps2_kbd; send_byte(8'h12); #(100_000); + $display("[kbd] W 键(1D)期望 dy=-8(W/A/S/D 是方向键别名)"); + exp_dx = 0; exp_dy = -8; exp_btn = 0; + send_byte(8'h1D); + #(100_000); + $display("[kbd] rx_count=%0d events=%0d", rx_count, n_ev); - if ((ok == 0) || (n_ev != 4)) + if ((ok == 0) || (n_ev != 5)) $display("[kbd] === FAIL ==="); else $display("[kbd] === ALL PASS ==="); diff --git a/pipelined-cpu/srcs/tb/tb_ps2_mouse.v b/pipelined-cpu/srcs/tb/tb_ps2_mouse.v index 21d3218..5604998 100644 --- a/pipelined-cpu/srcs/tb/tb_ps2_mouse.v +++ b/pipelined-cpu/srcs/tb/tb_ps2_mouse.v @@ -11,6 +11,11 @@ // 再拉低时钟。一帧 11 位 = 起始 0 + 8 位数据(LSB 先) + **奇校验** + 停止 1。 // 奇校验 = 校验位让 data + parity 共 9 位里 1 的个数为奇数(真设备就是这么发的)。 // +// 方向约定:PS/2 的 **dx 正 = 右、dy 正 = 上**(真鼠标往上推,byte2 是正数); +// 而软件(saolei.c / cpu21_pointer)里 **dy 正 = 向下(行号增加)**, +// 所以 cpu21_ps2_mouse 上报前把 dy 取负 —— 本测试就是盯这个约定: +// 发“向上”的 dy,事件里必须得到负的 dy。 +// // 跑法(Vivado 的 xvlog/xelab/xsim 或其它仿真器都行): // xvlog srcs/rtl/cpu21_ps2_mouse.v srcs/tb/tb_ps2_mouse.v // xelab -top tb_ps2_mouse -snapshot ps2sim @@ -95,13 +100,13 @@ module tb_ps2_mouse; $display(" [ps2] event #%0d: dx=%0d dy=%0d btn=%b", n_ev, $signed(ev_dx), $signed(ev_dy), ev_btn); if (n_ev == 1) begin if (($signed(ev_dx) != 8) || ($signed(ev_dy) != -8) || (ev_btn != 5'b00000)) begin - $display(" [ps2] FAIL: 第 1 包应为 dx=+8 dy=-8 btn=00000"); + $display(" [ps2] FAIL: 第 1 包应为 dx=+8 dy=-8 btn=00000(鼠标向上 -> 事件 dy 为负)"); ok = 0; end end if (n_ev == 2) begin if (($signed(ev_dx) != -3) || ($signed(ev_dy) != 5) || (ev_btn[0] != 1'b1)) begin - $display(" [ps2] FAIL: 第 2 包应为 dx=-3 dy=+5 btn=xxxx1"); + $display(" [ps2] FAIL: 第 2 包应为 dx=-3 dy=+5 btn=xxxx1(鼠标向下 -> 事件 dy 为正)"); ok = 0; end end @@ -125,16 +130,16 @@ module tb_ps2_mouse; end rx_after_bad = rx_count; - $display("[ps2] packet 1: flag=0x08(no button) dx=+8 dy=-8"); + $display("[ps2] packet 1: flag=0x08(no button) dx=+8 dy=+8(鼠标向右上移)"); + send_byte(8'h08); send_byte(8'h08); send_byte(8'h08); - send_byte(8'hF8); #(100_000); - $display("[ps2] packet 2: flag=0x09(left) dx=-3 dy=+5"); + $display("[ps2] packet 2: flag=0x09(left) dx=-3 dy=-5(向左下移)"); send_byte(8'h09); send_byte(8'hFD); - send_byte(8'h05); + send_byte(8'hFB); #(100_000); $display("[ps2] rx_count=%0d events=%0d", rx_count, n_ev);