最近在做一个紫光同创的FPGA项目,要跑RISC-V软核,从SD卡加载程序,外挂DDR3。现在卡在启动顺序上:是先初始化DDR3再读SD卡,还是先读SD卡再初始化DDR3?如果顺序反了,有时候启动会卡死,有时候DDR3读写数据错乱。有没有大佬分享下国产FPGA上这套流程的标准做法?另外,SD卡读取用SPI模式还是SD模式更稳?
2026年了,FPGA工程师用国产紫光FPGA做RISC-V软核,SD卡启动和DDR3初始化顺序怎么安排才不翻车?
3 个回答

先给结论:先把 DDR3 初始化跑通,再读 SD 卡。原因很直接,RISC-V 软核本身在 BRAM 里也能跑,但你的程序一旦超过 BRAM 容量,或者要用到堆栈、全局变量、DMA 缓冲,就全靠 DDR3 撑着。顺序反了的话,软核可能已经跳去执行 SD 卡里读出来的代码,但 DDR3 控制器还没 ready,一访问就 hang 或者数据错乱,这跟你描述的现象完全吻合。
我建议的流程是:上电后先做 PLL 锁定、复位释放,然后初始化 DDR3 控制器,做一次简单的写读校验(比如写 0xA5A5 读回来比对),确认内存可用后再去初始化 SD 卡。SD 卡这边,强烈推荐 SPI 模式,理由很简单:紫光这个平台上的 SD 模式驱动要处理 4-bit 总线、CMD 响应超时、CRC 校验,调试成本高;SPI 模式只要把时钟分频降到 400kHz 以下,用 GPIO 模拟或者硬核 SPI 都行,稳定多了。
有一个容易踩的坑是 SD 卡上电时序:卡供电和 FPGA 的 I/O 电压要一起上,而且 SD 卡初始化命令要在至少 74 个时钟周期之后才能发,不然卡不响应。另外,复位信号最好用外部 RC 或者专用复位芯片,别只靠 FPGA 内部上电复位,不然 DDR3 和 SD 卡可能在不同时刻才 ready,导致竞态。
如果你实在想省时间,也可以先读 SD 卡到 BRAM 里的固定地址,等 DDR3 初始化完再搬过去,但这样代码要分段,启动逻辑复杂不少,个人感觉没必要。你现在的卡死是每次必现还是偶发?如果是偶发,优先查 DDR3 的时序约束有没有满足,特别是读写延迟参数。

你这个问题其实拆成两层看:第一层是「软核跑起来需要什么」,第二层是「SD 卡里的代码什么时候能被信任」。很多人一上来就纠结顺序,但忽略了根因——软核在 DDR3 没就绪时访问内存,本身就是未定义行为。
我做过类似的国产 FPGA 项目,当时的做法是:用片上 BRAM 放一个极小的 bootloader(大概 2KB 以内),上电后软核先执行 bootloader,里面只做三件事:初始化 DDR3、等 DDR3 校验通过、然后通过 SPI 读 SD 卡的头几个扇区到 DDR3 的固定地址,最后跳转过去。这样顺序天然就是 DDR3 在前,SD 卡在后。如果你直接把 SD 卡读取放在软核启动的第一条指令,那等于赌 DDR3 在你读卡期间已经稳定,这赌注太脆了。
关于 SPI vs SD 模式,我多说两句。SD 模式带宽高,适合后续可能要热加载大文件,但它的初始化握手比 SPI 严格得多:要发 CMD0、CMD8、ACMD41 这些,而且 ACAD 41 的 HCS 位如果设错,卡直接不认。SPI 模式虽然慢(一般 1-2 MB/s 撑死),但对时序宽容,只要时钟频率别太高,基本上不会出错。你如果只是启动加载,SPI 绰绰有余。
还有一个细节,紫光的 DDR3 IP 生成时有个选项叫「初始化完成信号」,有些版本叫 init_done 或者 calib_done,你一定要把这个信号拉出来,接到软核的中断或者轮询寄存器里。别用固定延时去等,因为 DDR3 训练时间随温度和电压漂移,延时短了没训练完,延时长浪费启动时间。把 init_done 作为 SD 卡读使能的 gate,这样就彻底根除了顺序问题。
最后提醒一下:如果你用的是紫光 PDS 里的软核(比如 Cortex-M1 类似物),它的 memory map 可能默认把 DDR3 映射在 0x80000000,而 SD 卡读出来的数据要写到这个地址。注意 cache 问题——如果软核开了 D-cache,DMA 写入的数据可能读不到,需要做 cache invalidate。这个坑我印象很深,调了两天才发现是 cache 一致性问题。
你现在的软核是 PDS 自带的还是自己写的 RTL?如果是自己写的,那 DDR3 控制器和 AXI 总线的时钟域关系也要检查一下,跨时钟域没处理好也会偶发错乱。

顺序就一句话:先 DDR3 后 SD 卡,别反。SPI 模式稳如老狗,SD 模式调试到你怀疑人生。你那个偶发卡死八成是 DDR3 没完全就绪就去访问了,加个 init_done 信号再使能 SD 读取就行。
发表回答
登录后可在本页底部提交回答
