fix(ps2): 鼠标 dy 方向取负(PS/2 正 dy = 上,软件正 dy = 下);键盘补 W/A/S/D 别名
上板实测:鼠标已能操作(说明“奇校验 + 帧按位的下标对齐”那个修复就是根因),
但上下是反的、左右正确 —— 这是两套约定的差异,不是解码错:
* 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 用例
This commit is contained in:
@@ -6,9 +6,13 @@
|
|||||||
// 键盘只要产出**同样格式**的事件,软件一行都不用改。
|
// 键盘只要产出**同样格式**的事件,软件一行都不用改。
|
||||||
//
|
//
|
||||||
// ↑ ↓ ← → 光标移动(dx/dy = ±8,恰好是软件里的"一格")
|
// ↑ ↓ ← → 光标移动(dx/dy = ±8,恰好是软件里的"一格")
|
||||||
|
// W A S D 同上(别名,与 tools/play_saolei.tcl 一致)
|
||||||
// Enter / 空格 左键(开格)
|
// Enter / 空格 左键(开格)
|
||||||
// 左 Shift / F / C 右键(软件里右键 = 插旗)
|
// 左 Shift / F / C 右键(软件里右键 = 插旗)
|
||||||
//
|
//
|
||||||
|
// 方向约定跟软件对齐:**dy = +8 是向下**(与 cpu21_pointer 的 BTND、cpu21_ps2_mouse 一致),
|
||||||
|
// 所以 ↑ 发 dy = 248(= -8 的补码)。
|
||||||
|
//
|
||||||
// PS/2 键盘发扫描码(集 2):
|
// PS/2 键盘发扫描码(集 2):
|
||||||
// 0xF0 前缀 -> 下一个码是"松开"(break);0xE0 前缀 -> 下一个码是扩展键
|
// 0xF0 前缀 -> 下一个码是"松开"(break);0xE0 前缀 -> 下一个码是扩展键
|
||||||
// 方向键是扩展键:↑ = E0 75、↓ = E0 72、← = E0 6B、→ = E0 74
|
// 方向键是扩展键:↑ = 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'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'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
|
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: ;
|
default: ;
|
||||||
endcase
|
endcase
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -19,7 +19,8 @@
|
|||||||
// 所以字节 = frame_q[8:1](frame_q[1] 就是 d0),校验 = frame_q[9]。
|
// 所以字节 = frame_q[8:1](frame_q[1] 就是 d0),校验 = frame_q[9]。
|
||||||
// 注意 PS/2 是**奇校验**:data + parity 9 位里 1 的个数为奇数。
|
// 注意 PS/2 是**奇校验**:data + parity 9 位里 1 的个数为奇数。
|
||||||
// 2) 解析:byte0 = 标志(bit0 左键、bit1 右键、bit3 固定 1),byte1 = dx,
|
// 2) 解析:byte0 = 标志(bit0 左键、bit1 右键、bit3 固定 1),byte1 = dx,
|
||||||
// byte2 = dy,bit7 为符号位;
|
// byte2 = dy,bit7 为符号位;dx 正 = 右,**dy 正 = 上**(PS/2 的原生方向),
|
||||||
|
// 而软件里正 dy = 下,所以上报前把 dy 取负,让鼠标和板载按键/键盘方向一致;
|
||||||
// 3) 上报:当 dx/dy 非 0 或按键状态变化时产生一个事件(单位与
|
// 3) 上报:当 dx/dy 非 0 或按键状态变化时产生一个事件(单位与
|
||||||
// cpu21_pointer 相同,即原始计数 = 1/8 格);
|
// cpu21_pointer 相同,即原始计数 = 1/8 格);
|
||||||
// 4) 本模块只做"接收",不实现主机→鼠标的命令发送(0xF4 使能上报)。多数
|
// 4) 本模块只做"接收",不实现主机→鼠标的命令发送(0xF4 使能上报)。多数
|
||||||
@@ -131,8 +132,10 @@ module cpu21_ps2_mouse (
|
|||||||
end
|
end
|
||||||
8'd2: begin
|
8'd2: begin
|
||||||
byte_cnt_q <= 8'd0;
|
byte_cnt_q <= 8'd0;
|
||||||
ev_dx_o <= pkt_q[15:8]; // byte1 = dx
|
ev_dx_o <= pkt_q[15:8]; // byte1 = dx(正 = 右,与软件一致)
|
||||||
ev_dy_o <= frame_q[8:1]; // byte2 = dy
|
// 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=中键
|
// 与 cpu21_mmio / mmio.h 一致:bit0=左键, bit1=右键, bit2=中键
|
||||||
ev_btn_o <= {2'b0, pkt_q[2], pkt_q[1], pkt_q[0]};
|
ev_btn_o <= {2'b0, pkt_q[2], pkt_q[1], pkt_q[0]};
|
||||||
ev_valid_o <= 1'b1;
|
ev_valid_o <= 1'b1;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
//
|
//
|
||||||
// 发 → 键(E0 74)应产生 dx=+8;发 ↑(E0 75)应产生 dy=-8;
|
// 发 → 键(E0 74)应产生 dx=+8;发 ↑(E0 75)应产生 dy=-8;
|
||||||
// 发 Enter(5A)应产生 btn=1(开格);发 左Shift(12)应产生 btn=2(插旗);
|
// 发 Enter(5A)应产生 btn=1(开格);发 左Shift(12)应产生 btn=2(插旗);
|
||||||
|
// 发 W(1D)应产生 dy=-8(W/A/S/D 别名);
|
||||||
// 松开码(F0 xx)不应产生事件;校验位错的帧应被整帧丢掉。
|
// 松开码(F0 xx)不应产生事件;校验位错的帧应被整帧丢掉。
|
||||||
//
|
//
|
||||||
// PS/2 键盘发扫描码(集 2):0xF0 前缀 = 下一个码是"松开",0xE0 前缀 = 扩展键,
|
// PS/2 键盘发扫描码(集 2):0xF0 前缀 = 下一个码是"松开",0xE0 前缀 = 扩展键,
|
||||||
@@ -152,8 +153,13 @@ module tb_ps2_kbd;
|
|||||||
send_byte(8'h12);
|
send_byte(8'h12);
|
||||||
#(100_000);
|
#(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);
|
$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 ===");
|
$display("[kbd] === FAIL ===");
|
||||||
else
|
else
|
||||||
$display("[kbd] === ALL PASS ===");
|
$display("[kbd] === ALL PASS ===");
|
||||||
|
|||||||
@@ -11,6 +11,11 @@
|
|||||||
// 再拉低时钟。一帧 11 位 = 起始 0 + 8 位数据(LSB 先) + **奇校验** + 停止 1。
|
// 再拉低时钟。一帧 11 位 = 起始 0 + 8 位数据(LSB 先) + **奇校验** + 停止 1。
|
||||||
// 奇校验 = 校验位让 data + parity 共 9 位里 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 或其它仿真器都行):
|
// 跑法(Vivado 的 xvlog/xelab/xsim 或其它仿真器都行):
|
||||||
// xvlog srcs/rtl/cpu21_ps2_mouse.v srcs/tb/tb_ps2_mouse.v
|
// xvlog srcs/rtl/cpu21_ps2_mouse.v srcs/tb/tb_ps2_mouse.v
|
||||||
// xelab -top tb_ps2_mouse -snapshot ps2sim
|
// 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);
|
$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 (n_ev == 1) begin
|
||||||
if (($signed(ev_dx) != 8) || ($signed(ev_dy) != -8) || (ev_btn != 5'b00000)) 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;
|
ok = 0;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if (n_ev == 2) begin
|
if (n_ev == 2) begin
|
||||||
if (($signed(ev_dx) != -3) || ($signed(ev_dy) != 5) || (ev_btn[0] != 1'b1)) 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;
|
ok = 0;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -125,16 +130,16 @@ module tb_ps2_mouse;
|
|||||||
end
|
end
|
||||||
rx_after_bad = rx_count;
|
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'h08);
|
send_byte(8'h08);
|
||||||
send_byte(8'hF8);
|
|
||||||
#(100_000);
|
#(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'h09);
|
||||||
send_byte(8'hFD);
|
send_byte(8'hFD);
|
||||||
send_byte(8'h05);
|
send_byte(8'hFB);
|
||||||
#(100_000);
|
#(100_000);
|
||||||
|
|
||||||
$display("[ps2] rx_count=%0d events=%0d", rx_count, n_ev);
|
$display("[ps2] rx_count=%0d events=%0d", rx_count, n_ev);
|
||||||
|
|||||||
Reference in New Issue
Block a user