一、修 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 路径
75 lines
2.8 KiB
Verilog
75 lines
2.8 KiB
Verilog
`timescale 1ns / 1ps
|
||
|
||
// -----------------------------------------------------------------------------
|
||
// cpu21_board_top 的仿真测试(不上板,纯 RTL 仿真)
|
||
//
|
||
// 检查内容:
|
||
// 1) 复位释放后 CPU 能从片内 ROM 取指(ROM 由 cpu21_board_rom.vh 初始化);
|
||
// 2) 程序通过 MMIO 写 LED,板载 LED 端口上能看到 16 位跑马灯序列。
|
||
//
|
||
// 默认的 board_demo 程序带约 20 ms 软件延时,仿真只需确认第一次写入;
|
||
// 若要完整验证跑马灯序列,可生成一份短延时的 ROM 并用 -d BOARD_ROM_FAST 编译:
|
||
// xvlog -d BOARD_ROM_FAST board/cpu21_board_top.v <rtl/*.v> testbench/tb_board_top.v
|
||
// -----------------------------------------------------------------------------
|
||
module tb_board_top;
|
||
reg clk100 = 1'b0;
|
||
reg rst_n = 1'b0;
|
||
reg go = 1'b0;
|
||
reg [2:0] irq_sw = 3'b000;
|
||
|
||
wire [15:0] led;
|
||
wire led_we;
|
||
|
||
always #5 clk100 = ~clk100; // 100 MHz
|
||
|
||
cpu21_board_top #(
|
||
.LED_ADDR(32'h0000_1000)
|
||
) dut (
|
||
.clk100(clk100),
|
||
.rst_n (rst_n),
|
||
.go (go),
|
||
.irq_sw(irq_sw),
|
||
.led (led),
|
||
.led_we(led_we),
|
||
// 扫雷扩展端口:本测试平台不复位测试这些功能,输入显式接 0/1,
|
||
// 避免悬空(z)传到中断控制器里影响跑马灯的比对结果。
|
||
.btnu (1'b0),
|
||
.btnd (1'b0),
|
||
.btnl (1'b0),
|
||
.btnr (1'b0),
|
||
.sw_flag (1'b0),
|
||
.hid_sel (1'b0),
|
||
.ps2_clk (1'b1),
|
||
.ps2_data(1'b1),
|
||
.an (),
|
||
.seg (),
|
||
.dp (),
|
||
.vga_r (),
|
||
.vga_g (),
|
||
.vga_b (),
|
||
.vga_hs (),
|
||
.vga_vs ()
|
||
);
|
||
|
||
integer changes = 0;
|
||
reg [15:0] led_prev = 16'hxxxx;
|
||
|
||
always @(posedge clk100) begin
|
||
if (led !== led_prev) begin
|
||
changes = changes + 1;
|
||
led_prev = led;
|
||
$display("t=%t LED=%04h led_we=%b", $time, led, led_we);
|
||
end
|
||
end
|
||
|
||
initial begin
|
||
$timeformat(-9, 3, " ns", 12); // %t 按 ns 显示(默认按精度 ps 显示)
|
||
$display("NOTE: 复位后观察 LED(默认程序首次写入应为 0001)");
|
||
repeat (10) @(posedge clk100);
|
||
rst_n = 1'b1;
|
||
repeat (30000) @(posedge clk100); // 300 us(CPU 时钟为 clk100/2 = 50 MHz)
|
||
$display("FINAL: LED 变化次数=%0d 当前 LED=%04h", changes, led);
|
||
$finish;
|
||
end
|
||
endmodule
|