本文主要是介绍NES(FC) FPGA游戏卡开发笔记(9)---- 游戏rom加载方案的初步设计和系统架构,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
游戏机上电和Reset
RESET中断
ROM容量只有8KB或者16KB的情况
分析一下飞狼游戏的rom
游戏卡是否有能力知道游戏机的复位状态
游戏卡是否可以让游戏机复位
游戏机在不插卡上电的情况是处于什么状态
游戏机(或者说执行代码)是否可以向FPGA交互信息
总线冲突
系统架构的设计
Power on或者第一次reset
rom加载
加载完成切换游戏运行
玩家在游戏期间按reset键
有存档功能的游戏的存档加载
设计的核心机制
总结
首先列出下面的一些需要考虑的问题:
1)游戏机上电或者reset之后,第一条指令在哪儿,PC(program counter)的入口点是哪里?
2)游戏卡是否有能力知道游戏机的复位状态。
3)游戏机(或者说执行代码)是否可以向FPGA传递信息。通过发送信息,让FPGA知道其状态,或者了解FPGA的状态。
4)总线冲突。让游戏或者menu的rom正确加载到指定RAM芯片,需要在没有总线冲突的情况下才能实现。
5)游戏机在不插卡上电的情况是处于什么状态。
6) 游戏卡是否可以让游戏机复位。
上面的这些条件决定了FPGA的设计方案和ROM加载步骤。
游戏机上电和Reset
RESET中断
首先从游戏机原理图看,CPU的RST管脚连接复位按钮,玩家在按复位键之后,收到一个复位低电平。
游戏机上电或者按复位键之后,CPU在完成内部的一些初始化之后(这两者的区别是初始化的内容有有些不同)。最终会触发一个reset中断。
从6502用户手册可以知道,res中断触发之后,cpu会执行一条间接跳转指令。跳转的地址(两个地址)存放在ROM区的FFFC-FFFD(小段格式)。CPU的第一条指令就是从该地址执行的地方获取。(类似于cortex-m3的cpu把复位中断入口放在0x00000004的位置,ARM7和ARM9不一样,CPU上电之后,直接从0x0获取指令)
6502一共三个中断,中断向量地址如下,位置就是PRG-ROM区的最高地址区。
$FFFA = NMI
$FFFC = RESET
$FFFE = IRQ/BRK
ROM容量只有8KB或者16KB的情况
如果游戏卡的ROM容量比较小,只有8KB或者16KB,会这么样?如果如果是8KB,必须把它加载(映射)到0xE000的地方吗?
利用mirror机制,游戏正常加载到0x8000地址,从0x8000-0x9FFF区域是游戏PRG-ROM区。上面每8KB区域都是一样的内容,访问0x8000和访问0xA000都是一样的。
对于8K ROM,bin文件的最后3个word存放的就是三个中断向量入口地址。
所以PC的JMP指令从0xFFFC-0xFFFD总能获取到正确的入口地址。
分析一下飞狼游戏的rom
前面的地址只是WinHex的偏移地址。实际的加载地址在ROM区,第二个黄色标记就是FFFC-FFFD(reset中断向量地址)指向的内容0xBFD8就是跳转地址(小端格式)。
跳转地址就是第一个黄色标记的地址。把内容反汇编之后如下:
78 : SEI ;关中断EE FB BF : INC 0xBFFB ;0xBFFB指向内容加1D8 : CLD ;clear decimal flag4C 00 C0 : JMP 0xC000 ;跳转到0xc000
最终跳转到0xC000地址。
上图是该游戏的另外一个bank,同样reset中断地址是0xBFD8
游戏卡是否有能力知道游戏机的复位状态
可以。
通常游戏卡不需要知道游戏机的复位状态。但可以利用CPU的M2管脚来监测其状态。
CPU M2管脚是个时钟信号输出管脚,一般的游戏卡接这根线是空置状态。
该管脚在CPU处于复位态的时候输出低电平,当处于正常态的时候是时钟信号输出。
Implementing Mappers In Hardware - NESdev Wiki
上面这个连接的文章介绍了在设计实现游戏卡主要注意的硬件电路知识。非常具有参考价值。
其中一节Recovering !RESET signal from M2,说明了如何设计电路来实现复位检测功能。
这是一个真正的FPGA游戏卡的RESET检测原理图。就是利用一个二极管,RC网络电路实现该功能。在M2低电平的时候,输入是低电平,当M2是时钟信号输出是,对电容进行充放电,但能保持电容的输出电压是高电平。
该FPGA卡是用这个复位检测电路实现FPGA的复位。
在我的方案中,可以利用这个电路实现CPU复活检测IO输入,具体复活信号收到之后的操作看具体实现。
另外,下面的连接介绍了不同机器(包括各种山寨机的M2时钟信号输出是不一样的)
CPU pinout - NESdev Wiki
在RC电路设计的时候需要考虑这一点,满足充放电的高电平。(上图FPGA卡是5V输入,这点也与我的方案不同,我是采用3.3V输入。该设计者采用的FPGA管脚IO可以接受5V电平,我选择的FPGA是3.3v)
游戏卡是否可以让游戏机复位
不能,游戏卡槽的所有管脚不具有这样的功能。
游戏机在不插卡上电的情况是处于什么状态
相当于游戏机的卡槽管脚处于悬空状态,PC指针执行的位置是随机的,可能取出的是有效的指令,也可能是无效的。所以游戏机处于乱跑的状态。
游戏机(或者说执行代码)是否可以向FPGA交互信息
参考MMC控制器的实现,可以知道CPU可以设置MMC的寄存器,读取寄存器,通过这种方式,其原理就是控制地址总线和数据总线的每个pin的信息来进行信息的传递。
在FPGA设计中,菜单程序选择某个游戏,需要把游戏编号(或者索引号)通知FPGA,FPGA加载该索引号的游戏。完成之后,FPGA需要通知菜单程序加载完成,可以开始运行游戏了。
两者之间交互的内容和交互方式在具体设计中进行细化。
总线冲突
在整个FPGA方案中,核心的一点是如何避免总线冲突。一个RAM不要由两个CPU(或者说FPGA和游戏机CPU)同时访问。
在FC游戏机中,那个ROM区选择是有CPU控制,或者通过MMC控制,同一时间只有一个设备会访问。但在FPGA中,RAM区即能被FC CPU访问,也能被FPGA(或者MCU)访问。如何避免这种冲突是设计的关键。原则就是在某一时刻只能有一个设备访问RAM芯片。
在加载menu程序到prg-ram之后,选择某个游戏加载,这时候地址总线是选中menu所在的区域,但游戏也需要加载到该区域,就会产生总线冲突。
系统架构的设计
这是整个系统的框图
整个系统和之前的考虑的不同是多了一个CPLD,要避免总线冲突用一个FPGA是做不到这一点的。因为FPGA每次配置的时候是失去了RAM的控制的,而有局部配置功能的FPGA都是高端系列,不在可选范围之内。
结合框图说明加载的具体流程和设计实现思想。
Power on或者第一次reset
1)游戏机power on之后,CPLD加载配置,MCU固件运行,配置FPGA,大概可以在1秒内完成,具体需要看实际情况。
2)游戏卡槽的总线是通过FPGA连接两块RAM,FPGA控制其处于未连接状态,RAM先是连接到FPGA,这样配置完成之后,MCU可以把menu程序加载到PRG-RAM和CHR-RAM。
3)另外通过CPLD,把loader程序加载到SAVE_RAM,控制SAVE_RAM的访问地址为0x6000(或者0x4000?),
4)因为menu加载是通过SPI由tf卡读取之后加载到RAM区,在完成之后,FPGA才能把RAM的总线控制器切换到CPU。在完成之前,需要一直按住reset键。
5)加载完成之后,FPGA把RAM的总线切换到CPU,mapper设置为255(menu游戏的mapper号),reset读取0xFFFC-FFFD处的menu程序的入口地址。开始运行menu程序。
rom加载
1)在玩家通过menu菜单(列出TF卡上的游戏列表),选择某个游戏之后。
2)把选取游戏的索引号通知FPGA,
3)切换到SAVE_RAM的loader程序,loader程序通过CPLD通知MCU,PRG-RAM和CHR-RAM可以切换到FPGA了,
4)MCU通知FPGA断开与CPU的总线连接,并有FPGA进行接管。
5)MCU从SD卡根据索引号读取游戏加载通过SPI接口传输到有FPGA控制的RAM区,
6)FPGA的mapper号切换为该游戏的mapper号。
加载完成切换游戏运行
在SAVE_RAM中的loader程序不停询问CPLD,是否FPGA已经加载完成。FPGA加载完成之后,可以通过MCU通知CPLD。
loader判读FPGA加载完成之后,执行JMP 0xFFFC的指令,游戏开始运行。
玩家在游戏期间按reset键
FPGA通过RESET检测电路检测到RESET键已经按下,需要加载menu菜单。FPGA控制RAM的访问总线与CPU断开,并通知MCU,MCU下载menu程序,不要下载新的FPGA配置(因为每个mapper配置程序中都包含了mapper255),并把FPGA的mapper配置为255。
MCU控制CPLD映射存放在SAVE_RAM的loader程序的地址为0x6000。重复之前的流程。
有存档功能的游戏的存档加载
在加载游戏的时候把最新的存档加载到SAVE_RAM区,
玩家可以通过菜单把存档游戏转存到tf卡。
设计的核心机制
1)menu菜单程序和loader程序存放在不同的RAM区,loader程序运行在0x6000-8000直接,不会与PRG ROM区重叠。
2)有CPLD来进行辅助控制实现该设计。
总结
这样整个系统控制流程就设计好了,具体模块与模块之间地址线和数据线,控制的连接,需要哪些线,需要做哪些逻辑完成地址映射,传递什么信息,通过什么方式传统,这些详细的设计在之后一个一个进行细化。
其他的考虑:
SAVE_RAM暂时考虑32KB,8KB作为存档,其他作为loader代码区。另外也可以利用CPLD的RAM资源实现。
CPU的选择,雅特力AT32F403ARCT7虽然有两个外接片选,但因为一共64个管脚封装的原因,总线地址只能是访问512KB,不能满足1MB的要求,所以还是用SPI的数据传输方式。
利用CPU的XMC可以加快游戏加载过程,不过在布线和FPGA管脚资源的使用上会导致设计难度加大。CPU可以考虑该系列的1MB内置flash的版本,可以考虑把FPGA配置代码存放在片内flash。省了外接存放BIOS的falsh芯片。
这篇关于NES(FC) FPGA游戏卡开发笔记(9)---- 游戏rom加载方案的初步设计和系统架构的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!