NES 烧录卡N8 Pro SystemVerilog实现 — 系统架构分析

📅 2026/7/5 2:07:42
NES 烧录卡N8 Pro SystemVerilog实现 — 系统架构分析
NES 烧录卡N8 Pro SystemVerilog实现 — 系统架构分析基于examples/mappers/base_sv与examples/mappers/255目录下的 SystemVerilog 源码1. 项目概述本项目是一款基于 FPGA 的 NES/FC 烧录卡固件实现采用MCU FPGA双芯片架构。FPGA 侧使用 SystemVerilog 实现所有 NES 硬件模拟逻辑包括 CPU/PPU 总线解码、Mapper 映射、内存管理、DMA、金手指、即时存档Save State以及音频 DAC 等功能。MCU 通过 SPI 接口与 FPGA 通信负责 ROM 加载、配置下发和游戏菜单等上层逻辑。2. 顶层文件结构examples/mappers/ ├── base_sv/ # 基础系统不可修改 │ ├── top.sv # 顶层模块 复位/VRAM/DAC 等辅助模块 │ ├── defs.sv # 全局宏定义与 include │ ├── structs.sv # 所有结构体定义CpuBus, PpuBus, MapIn, MapOut, PiBus, DmaBus, SysCfg, SSTBus, MemCtrl │ ├── base_io.sv # 基础 I/OFIFO、状态寄存器、MCU 通信 │ ├── pi.sv # 外设接口SPI 协议解析 地址译码 │ ├── sys_cfg.sv # 系统配置寄存器Mapper 索引、掩码、音量、按键等 │ ├── dma.sv # DMA 控制器MCU 通过 FPGA 访问卡带内存 │ ├── ggc.sv # 金手指Game Genie / Cheat引擎 │ ├── sst.sv # 即时存档Save State控制器 寄存器嗅探器 │ ├── map_255.sv # Mapper 255系统菜单专用 Mapper │ ├── map_nom.sv # 空 Mapper不支持的卡带回退 │ └── clocks.sdc # 时序约束 └── 255/ └── map_hub.sv # Mapper 路由中心当前仅挂载 map_nom3. 系统总体架构┌─────────────────────────────────────────────────────────────────────┐ │ NES 主机侧 │ │ ┌──────────┐ ┌──────────┐ │ │ │ CPU Bus │ │ PPU Bus │ (NES 6502 / PPU 信号) │ │ └────┬─────┘ └────┬─────┘ │ └───────┼──────────────┼──────────────────────────────────────────────┘ │ │ ┌───────▼──────────────▼──────────────────────────────────────────────┐ │ FPGA (top.sv) │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌──────────────────────────┐ │ │ │ 总线解码 │ │ 数据总线 │ │ Mapper 引擎 │ │ │ │ cpu_dir/oe │ │ 多路选择器 │ │ ┌────────┐ ┌─────────┐ │ │ │ │ ppu_dir/oe │ │ (MUX/优先级)│ │ │Map 255 │ │ Map Hub │ │ │ │ └─────────────┘ └─────────────┘ │ │(系统菜单)│ │(游戏逻辑)│ │ │ │ │ └────────┘ └────┬────┘ │ │ │ ┌─────────────────────────────────┐ │ │ │ │ │ │ 外设子系统 │ │ ┌───────────────┘ │ │ │ │ ┌───────┐ ┌─────┐ ┌─────────┐ │ │ │ map_nom (回退) │ │ │ │ │Pi Bus │ │DMA │ │ Base IO │ │ └──────────────────────────┘ │ │ │ │(SPI) │ │ │ │(FIFO) │ │ │ │ │ └───┬───┘ └──┬──┘ └────┬────┘ │ ┌──────────────────────────┐ │ │ └──────┼────────┼─────────┼──────┘ │ 内存控制 │ │ │ │ │ │ │ ┌─────┐┌─────┐┌─────┐ │ │ │ ┌──────▼────────▼─────────▼──────┐ │ │ PRG ││ CHR ││ SRM │ │ │ │ │ 辅助功能 │ │ │ ROM ││ ROM ││ RAM │ │ │ │ │ ┌───────┐ ┌──────┐ ┌───────┐ │ │ └──┬──┘└──┬──┘└──┬──┘ │ │ │ │ │ GGC │ │ SST │ │ DAC │ │ └─────┼──────┼──────┼──────┘ │ │ │ │金手指 │ │存档 │ │音频 │ │ │ │ │ │ │ │ └───────┘ └──────┘ └───────┘ │ │ │ │ │ │ └────────────────────────────────┘ │ │ │ │ └────────────────────────────────────────────┼──────┼──────┼─────────┘ │ │ │ ┌───────▼──────▼──────▼───────┐ │ 外部存储 (SRAM / Flash) │ │ PRG ROM / CHR ROM / SRAM │ └─────────────────────────────┘ ┌───────────────────────────────────────────────────────────┐ │ MCU (ARM) │ │ - SPI 通信 → PI Bus │ │ - ROM 加载 (DMA) │ │ - 配置下发 (sys_cfg) │ │ - 游戏菜单 / OS │ └───────────────────────────────────────────────────────────┘4. 核心结构体定义structs.sv结构体用途关键字段CpuBusCPU 总线信号data[7:0],addr[15:0],rw,m2,m3PpuBusPPU 总线信号data[7:0],addr[13:0],oe,weMemCtrl内存控制接口dati[7:0],addr[22:0],ce,oe,we,async_ioMapInMapper 输入cpu,ppu,cfg,sst,prg_do,chr_do,srm_do,sys_rst,map_rstMapOutMapper 输出prg,chr,srm(MemCtrl),ciram_a10/ce,irq,snd[15:0],sst_diPiBusMCU↔FPGA 外设总线dato[7:0],addr[31:0],we,oe,act,map(PiMap)DmaBusDMA 控制mem(MemCtrl),req_prg/chr/srm,pi_di[7:0]SysCfg系统配置map_idx,prg/chr/srm_msk,master_vol,ct_*控制位,mir_*镜像模式SSTBus即时存档总线addr[12:0],act,we,dato[7:0]5. 各子系统详解5.1 顶层模块top.sv顶层模块是整个 FPGA 的核心负责NES 总线接口直接对接 NES 主机的 CPU 和 PPU 信号线双向数据总线、地址线、读写控制、M2 时钟外部存储接口PRG ROM22-bit 地址、CHR ROM22-bit 地址、SRAM 的控制信号生成数据总线多路复用根据地址空间和设备优先级选择正确的数据源驱动到 CPU/PPU 数据总线Mapper 路由根据cfg.map_idx选择系统 Mapper255或游戏 Mappermap_hub地址掩码根据SysCfg中的掩码配置对 PRG/CHR/SRM 地址进行范围限制M2 同步通过 16 级移位寄存器对 M2 时钟进行同步产生we_ok和ce_ok信号确保时序正确总线冲突处理当 CPU 写入与 PRG ROM 输出冲突时执行 AND 操作模拟 NES 原始硬件行为地址空间划分CPU 侧地址范围功能0x0000-0x3FFF未使用开放总线0x4000-0x40FF内部寄存器FIFO、状态、金手指、存档0x4100-0x41FFMapper 255 寄存器区0x5000-0x5FFFSRAM 区Mapper 2550x6000-0x7FFF应用 Bank / SRAM0x8000-0xFFFFPRG ROM5.2 外设接口pi.sv— SPI 通信协议MCU 通过 SPI 与 FPGA 通信pi_io模块实现 SPI 从机协议SPI 时钟域独立于系统时钟使用spi_clk边沿采样命令格式0xA0内存写MCU → FPGA0xA1内存读FPGA → MCU地址传输命令后跟 4 字节地址32-bit然后进行数据传输地址译码pi_io_map地址段功能[24:23]0PRG RAM8MB[24:23]1CHR RAM8MB[24:23]2SRAM8MB[24:23]3, [21:16]0系统寄存器64KB[24:23]3, [21:16]1FIFO64KB系统寄存器内ce_ggc(金手指)、ce_cfg(配置)、ce_sst(存档)5.3 系统配置sys_cfg.sv通过 PI Bus 的ce_cfg区域16 字节寄存器配置系统参数寄存器内容scfg[0]Mapper 索引低 8 位scfg[1]PRG/SRM 掩码低 4 位 PRG高 4 位 SRMscfg[2]Mapper 索引高 4 位 CHR 掩码低 4 位scfg[3]主音量scfg[4]Mapper 自定义配置镜像模式、CHR RAM 等scfg[5-6]存档按键保存/加载scfg[7]控制位复位延迟、存档使能、金手指、Famicom 模式、解锁scfg[8]菜单按键scfg[9]跳线器5.4 基础 I/Obase_io.sv提供 CPU 侧的寄存器接口地址0x40xxFIFO 数据寄存器(0x40F0)双向 FIFOMCU↔CPU 通信通道2KB 深度FIFO 状态寄存器(0x40F1)FIFO 空/满标志状态寄存器(0x40FF)MCU 状态、FPGA 配置状态、解锁状态、Strobe 位FIFO 实现为基于双端口 RAM 的环形缓冲区支持同时读写。5.5 DMA 控制器dma.svMCU 通过 DMA 直接访问卡带内存PRG/CHR/SRAM无需 CPU 参与当 PI Bus 访问 PRG/CHR/SRAM 地址空间时DMA 接管内存控制信号DMA 优先级高于 Mapper 输出通过top.sv中的 MUX 实现async_io 1DMA 访问不受 M2 时序约束5.6 Mapper 引擎5.6.1 Mapper 路由机制top.sv 中: mao (map_rst | sst.act) ? map_out_255 : map_out_game;Mapper 255系统菜单专用在复位或存档操作时自动激活Map Hub游戏运行时使用根据cfg.map_idx路由到具体 Mapper 实现5.6.2 Mapper 255系统菜单功能丰富的系统级 Mapper支持PRG ROM 映射系统 ROM 固定在0x7E0000起始地址RAM 区0x5000-0x5FFF16 字节窗口低 12 位直连应用 Bank0x6000-0x7FFF16 个可切换 Bank每 Bank 8KBCHR ROM/RAM支持 PPU 关闭时内部 VRAM 访问用于存档时 PPU 画面冻结内部 VRAM双端口 RAM 实现的名表/属性表渲染引擎支持 CPU 侧写入和 PPU 侧读取三种模式标准STD、安全SAF、测试TST属性表自动计算和镜像定时器1KHz 精度的毫秒计时器供菜单使用5.6.3 Map Nominal空 Mapper不支持的 Mapper 回退方案简单的地址直通映射无 Bank 切换LED 闪烁提示用户当前 Mapper 不支持支持基本的 SRAM0x6000-0x7FFF和 PRG ROM0x8000-0xFFFF5.7 金手指引擎ggc.sv实现 Game Genie 兼容的金手指功能8 个独立插槽每个插槽存储一组作弊码每条作弊码 4 字节{地址低, 地址高, 比较数据, 替换数据}匹配条件CPU 读取0x8000-0xFFFF区域时地址匹配且数据比较通过当code[3] code[2]时禁用数据比较仅地址匹配通过 PI Bus 的ce_ggc区域32 字节配置5.8 即时存档控制器sst.sv实现 NES 游戏的即时存档Save State功能存档触发机制sst_sw按键触发通过手柄按键组合触发保存/加载/菜单按键可配置外部按钮支持 FDS 切换按钮触发NMI 劫持通过拦截 NMI 向量读取0xFFFA实现游戏暂停存档数据布局128 字节地址空间偏移内容0x00-0x7FMapper 寄存器128 字节0x80-0xBFPPU/APU 寄存器嗅探捕获0xC0-0xFFOAM 数据256 字节通过地址复用0x100Mapper 内存数据寄存器嗅探器sst_sniffer实时监听 CPU 对 PPU/APU 寄存器的写操作自动捕获PPU 控制寄存器$2000PPU 掩码寄存器$2001滚动地址$2005VRAM 地址$2006OAM 数据调色板数据$3F00-$3F1FAPU 寄存器$4000-$401F5.9 音频 DACdac_dstop.sv 内Delta-Sigma 调制器实现的音频 DAC16-bit 输入8-bit 主音量控制使用 M2 时钟二分频作为调制时钟一阶 Sigma-Delta 调制输出 1-bit PWM 信号支持扩展音频Mapper 可通过mao.snd[15:0]输出音频数据5.10 VRAM 控制ppu_vram_ctrltop.sv 内PPU 视频内存管理标准模式由 Mapper 控制 CIRAM 片选和 A10镜像模式四屏镜像模式使用 FPGA 内部 4KB RAM 实现第四屏 VRAM支持水平/垂直/单屏/四屏镜像切换5.11 复位控制sys_rst_ctrl检测 M2 时钟停止CPU 复位产生系统复位信号map_rst_ctrl系统复位后延迟产生 Mapper 复位支持可配置延迟快速复位 vs 返回菜单6. 数据流路径6.1 CPU 读取 PRG ROMNES CPU addr[15] → prg.ce → prg_addr_msk → 外部 PRG ROM → prg_dat → cpu_dat (通过 MUX 选择)6.2 PPU 读取 CHR ROMNES PPU addr → chr.ce → chr_addr_msk → 外部 CHR ROM → chr_dat → ppu_dat (通过 MUX 选择)6.3 MCU 写入 ROMDMA 路径MCU → SPI → pi_io → PiBus[PRG区域] → DMA 接管 → prg_mem 控制信号 → 外部 SRAM/Flash6.4 金手指替换CPU 读取 0x8000 → ggc 匹配地址和数据 → ggc_do 替换 cpu_dat优先级高于 PRG ROM 输出7. 时钟域时钟频率用途clk50 MHzFPGA 主时钟所有同步逻辑m2~1.79 MHzNES CPU 时钟由主机提供spi_clk可变MCU↔FPGA 通信跨时钟域处理M2 → CLK通过m2_st移位寄存器同步SPI → CLK通过pi.act脉冲信号握手FIFO 使用同一clk驱动双端口避免跨时钟域问题8. Mapper 加载机制1. MCU 通过 SPI 将 .RBF 文件Quartus 二进制加载到 FPGA 2. MCU 通过 PI Bus 写入 sys_cfg 寄存器设置 map_idx 3. FPGA 根据 map_idx 在 map_hub 中选择对应的 Mapper 实现 4. MAPROUT.BIN 文件维护 Mapper 编号到 .RBF 文件的映射表 5. 0xFF 表示不支持的 Mapper9. 关键设计特点模块化 Mapper 架构MapIn/MapOut 结构体标准化接口新增 Mapper 只需实现标准模板动态重配置通过 PI Bus 运行时修改内存掩码、镜像模式等参数总线冲突模拟精确模拟 NES 硬件的总线冲突行为AND 操作M2 同步设计所有 CPU 侧操作严格同步到 M2 时钟的稳定窗口非侵入式存档通过寄存器嗅探实现即时存档无需修改游戏代码双芯片协作MCU 处理上层逻辑FPGA 处理实时硬件模拟分工明确10. 模块实例化关系图top ├── ppu_vram_ctrl_inst (ppu_vram_ctrl) │ └── ppu_ram_inst (ppu_ram) ├── m255 (map_255) ← 系统菜单 Mapper │ ├── timer_inst (timer) │ └── vram_inst (vram) │ ├── atr_inst (atr_ctrl) │ ├── ntb_ram (ram_dp) │ └── atr_ram (ram_dp) ├── map_hub ← 游戏 Mapper 路由 │ └── mnom (map_nom) ← 当前仅挂载空 Mapper ├── pi_io_inst (pi_io) ← SPI 外设接口 │ └── pi_io_map_inst (pi_io_map) ├── io_inst (base_io) ← 基础 I/O FIFO │ ├── fifo_a (fifo) │ │ └── fifo_ram (ram_dp) │ └── fifo_b (fifo) │ └── fifo_ram (ram_dp) ├── sys_cfg_inst (sys_cfg) ← 系统配置 ├── dma_io_inst (dma_io) ← DMA 控制器 ├── sys_rst_inst (sys_rst_ctrl) ← 系统复位 ├── map_rst_inst (map_rst_ctrl) ← Mapper 复位 ├── ggc_inst (ggc) ← 金手指 [条件编译 GGC_ON] │ └── gg0~gg7 (gg_slot) × 8 ├── sst_inst (sst_controller) ← 即时存档 [条件编译 SST_ON] │ ├── sst_sw_inst (sst_sw) │ ├── joy_inst1 (joy_rdr) │ └── sniffer_inst (sst_sniffer) │ ├── regs_mem (ram_dp) │ └── oam_mem (ram_dp) └── dac_inst (dac_ds) ← 音频 DAC [条件编译 SND_ON]文档生成时间2026-07-04