- cpu21_mmio.v: sel_fb 原写成 addr[12] && !addr[11](那是 0x1000-0x17FF), 导致 0x2000+ 的显存读写全不命中, 而鼠标/显示寄存器写反而落进显存。 改为 bit13=1 && bit12=0 && bit11=0 后扫雷画面才正常: 修复前仿真 fb_we 只有 6 次写(画面全空), 修复后开始界面 1246 次、进关卡后 2775 次 - cpu21_board_top.v: 新增 POINTER_SAMPLE_DIV 参数(默认仍为上板的 50000), 仿真传 50 即可让按键在几十微秒内被识别(defparam 对 generate 块内实例无效) - saolei/tools/elf2hex.py: .text.isr1/2/3 之间的空洞(0 字)填 NOP —— objcopy 留下的 0x00000000 在本 CPU 上等同自跳指令, PC 掉进去就出不来 - saolei/tools/fb2png.py + srcs/tb/tb_saolei.v: 无 VGA 显示器的调试台 —— 跑板级顶层、 按键当鼠标、把 40x30 显存 dump 成文本, 再按 cpu21_vga.v 的字库/调色板还原 PNG, 并附 ASCII 图 + RAM 变量视图 + PC 热点直方图
82 lines
3.1 KiB
Python
82 lines
3.1 KiB
Python
#!/usr/bin/env python3
|
||
"""elf2hex.py —— 把 objcopy 出来的裸二进制转成 ROM 需要的两份文件。
|
||
|
||
用法: python tools/elf2hex.py <input.bin> <output.hex> [ROM_WORDS] [output.vh]
|
||
|
||
产出:
|
||
1) <output.hex> 每行一个 32 位字($readmemh 格式,供 srcs/rom/ 下的
|
||
测试平台加载,例如拷成 srcs/rom/saolei.hex)
|
||
2) <output.vh>(可选) rom[i] = 32'h...; 语句,供
|
||
pipelined-cpu/srcs/board/cpu21_board_top.v 里 `include
|
||
固化进片内 ROM(未用到的地址填 NOP 0x00000013)
|
||
(未用到的地址填 NOP 0x00000013)
|
||
|
||
字序:输入的 .bin 是按字节小端排列的机器码,这里按小端拼成 32 位字。
|
||
"""
|
||
|
||
import sys
|
||
|
||
NOP = 0x00000013
|
||
|
||
|
||
def main():
|
||
if len(sys.argv) < 3:
|
||
print("用法: python tools/elf2hex.py <input.bin> <output.hex> [ROM_WORDS] [output.vh]")
|
||
return 2
|
||
|
||
src = sys.argv[1]
|
||
dst = sys.argv[2]
|
||
words_total = int(sys.argv[3]) if len(sys.argv) > 3 else 8192
|
||
vh_path = sys.argv[4] if len(sys.argv) > 4 else None
|
||
|
||
with open(src, "rb") as f:
|
||
data = f.read()
|
||
|
||
if len(data) % 4 != 0:
|
||
print("警告:二进制长度 %d 不是 4 的倍数,尾部会被补 0" % len(data))
|
||
data += b"\x00" * (4 - len(data) % 4)
|
||
|
||
words = []
|
||
for i in range(0, len(data), 4):
|
||
words.append(int.from_bytes(data[i:i + 4], "little"))
|
||
|
||
# 段间空洞填 NOP:objcopy 出来的 .bin 在 .text.isr1 / .text.isr2 / .text.isr3
|
||
# 这些"绝对地址段"之间会留下 0x00000000,而本 CPU 把 0x00000000 译成自跳指令
|
||
# (beq x0,x0,0 形式),PC 一旦落进去就永远出不来(仿真里已复现:PC 热点全在
|
||
# 0x194-0x1FF / 0x218-0x2FF 这些空洞地址上)。这里把"最后一条非零字之前"的
|
||
# 0 字统一换成 NOP,让 PC 即使误入也能顺势走过。
|
||
last_nz = -1
|
||
for i, w in enumerate(words):
|
||
if w != 0:
|
||
last_nz = i
|
||
filled = 0
|
||
for i in range(last_nz):
|
||
if words[i] == 0:
|
||
words[i] = NOP
|
||
filled += 1
|
||
|
||
if len(words) > words_total:
|
||
print("错误:程序 %d 个字,超过 ROM 容量 %d 个字(改大 ROM_WORDS 或精简程序)"
|
||
% (len(words), words_total))
|
||
return 1
|
||
|
||
with open(dst, "w", encoding="ascii") as f:
|
||
for w in words:
|
||
f.write("%08X\n" % w)
|
||
|
||
if vh_path:
|
||
with open(vh_path, "w", encoding="utf-8") as f:
|
||
f.write(" // 由 tools/elf2hex.py 生成,请勿手工编辑\n")
|
||
f.write(" // 源文件:%s(%d 个字,其余填 NOP)\n" % (src, len(words)))
|
||
for addr in range(words_total):
|
||
w = words[addr] if addr < len(words) else NOP
|
||
f.write(" rom[%d] = 32'h%08X;\n" % (addr, w))
|
||
|
||
print("已生成 %s(%d 个字)%s" % (dst, len(words),
|
||
("、%s(%d 个字,NOP 填充)" % (vh_path, words_total)) if vh_path else ""))
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|