1. 从“跑得动”到“能固化”:嵌入式存储系统的核心价值
上一章我们点亮了NIOS II软核,让程序在FPGA的片内RAM里跑了起来,那种“Hello World”打印出来的兴奋感,估计很多朋友都体验过。但紧接着,一个现实问题就摆在了面前:关掉电源,再打开,FPGA又变成了一张白纸,程序没了,一切归零。这感觉就像辛辛苦苦搭了个乐高城堡,一碰就散,只能看不能留。问题出在哪?就出在我们缺了一个能“记住”程序的部件——非易失性存储器,也就是我们常说的ROM。
在第一个工程里,我们用的on-chip memory,本质上就是一块高速的SRAM。它的优势是速度快,和NIOS II内核同在一个FPGA芯片内部,通信延迟极低,性能没得说。但它的致命弱点就是“失忆”,断电后数据全部丢失。这决定了它只能作为程序运行时的“工作台”(存放运行代码和变量),而不能作为程序的“仓库”(存放需要固化的最终程序)。一个真正能独立工作的嵌入式系统,必须有一个可靠的“仓库”,确保每次上电,系统都能从同一个起点开始工作。
所以,这一章我们要解决的核心问题,就是为我们的NIOS II系统搭建一个完整的存储体系。这个体系通常由两部分组成:非易失性存储器(如Flash)作为“仓库”,存放需要固化的应用程序;易失性存储器(如SDRAM)作为“工作台”,提供程序运行时所需的高速空间。理解了这一点,我们就从“玩具级”的演示,迈向了“产品级”设计的第一步。接下来,我会带你一步步剖析存储系统的原理,并在SOPC Builder中亲手搭建它。
2. 嵌入式存储系统架构深度解析
2.1 为何需要ROM与RAM的分工协作?
我们可以用电脑来做个类比,方便理解。电脑的硬盘(HDD或SSD)就像ROM,容量大、断电数据不丢失,用来长期存放操作系统和所有软件。但CPU并不会直接去硬盘里取指令执行,因为硬盘速度太慢。所以开机时,系统会把需要运行的程序从硬盘加载到内存(RAM)里,CPU再从内存中高速读取指令执行。内存的读写速度比硬盘快几个数量级,但一断电,里面的内容就清空了。
这个“硬盘-内存”的架构,在嵌入式系统里同样适用,只是器件换成了Flash和SDRAM/SRAM。
- Flash (ROM角色):相当于嵌入式系统的“硬盘”。我们编译好的程序(.elf文件)最终需要烧录到Flash里。它的特点是非易失性、容量相对较大、成本较低,但读写速度慢,尤其是写操作(擦除再编程)非常耗时。
- SDRAM/SRAM (RAM角色):相当于嵌入式系统的“内存”。系统上电后,一段称为“Bootloader”的固化小程序(或硬件逻辑)会自动将Flash中的主要程序代码搬运到SDRAM中。随后,NIOS II CPU从SDRAM中取指执行,所有变量也存放在SDRAM中。它的特点是易失性、速度快,是程序运行的理想场所。
那么,第一个工程里只用片内RAM(On-Chip Memory)行不行?对于极简的、不需要断电保存的演示,可以。但对于实际项目,有三大硬伤:
- 成本高昂:FPGA内部的存储单元(Block RAM或Distributed RAM)是宝贵的逻辑资源,用它们来存大量程序代码极其浪费,会大幅增加芯片成本。
- 容量有限:即便像Cyclone IV EP4CE10这样的入门芯片,其Block RAM也就400多Kbit,存个稍复杂的程序就捉襟见肘。
- 无法固化:根本问题,断电即丢失。
因此,引入外部专用存储器(Flash + SDRAM)是性价比最高、最实用的方案。FPGA片内RAM则应被用作更擅长的角色:高速缓存(Cache)、数据缓冲(FIFO)或寄存器堆,发挥其速度快、可灵活定制的优势。
2.2 主流存储方案选型:SDRAM + EPCS/CFI Flash
在Altera(现Intel FPGA)的NIOS II生态中,最经典、最常用的存储搭配是:SDRAM + EPCS串行配置器件。有时也会用到并行Nor Flash(CFI Flash)。
SDRAM (Synchronous Dynamic RAM):
- 优点:容量大(通常32MB、64MB甚至更大)、成本低、速度快(与系统时钟同步)。
- 缺点:接口时序复杂,需要专用的SDRAM控制器来管理刷新、预充电、行列地址等操作。幸运的是,SOPC Builder提供了现成的、经过验证的SDRAM控制器IP核,我们只需配置参数即可。
- 角色:系统主运行内存。
EPCS (Serial Configuration Device):
- 本质:它其实是一种串行Nor Flash,但被Altera专门用来存储FPGA的配置比特流文件(.sof)。
- 我们的用法:利用其富余的存储空间。FPGA配置完成后,EPCS器件并未被完全占用,剩余空间可以通过Altera提供的“EPCS Serial Flash Controller”IP核来访问,用于存储我们的NIOS II程序。
- 优点:一举两得,既配置了FPGA,又存储了程序,节省了一个单独的Flash芯片,降低了PCB面积和BOM成本。接口简单(SPI),占用FPGA I/O少。
- 缺点:读写速度慢(串行接口),容量有限(常见4Mb, 16Mb, 64Mb)。适合程序体积不大的应用。
CFI Flash (Common Flash Interface):
- 本质:并行Nor Flash,有独立的地址和数据总线。
- 优点:读取速度比EPCS快很多,容量选择范围广。
- 缺点:需要占用大量FPGA I/O引脚,需要额外的控制器IP,并且需要单独烧录程序,增加了生产环节的复杂度。
- 角色:当程序较大,或对启动速度有要求时选用。
对于大多数学习和中等规模的应用,SDRAM + EPCS的组合是首选。本章实战也将采用这个方案。
2.3 系统启动流程(Boot Flow)揭秘
理解了器件,我们再来梳理一下系统从上电到程序运行的完整流程,这对后续调试至关重要:
FPGA配置阶段:开发板通电。FPGA本身是空白的,其内部逻辑由EPCS器件中的**.sof**文件决定。配置电路(通常由一个叫
EPCS的芯片和FPGA上的专用引脚ASDO,DATA0,DCLK,nCSO等构成)自动将.sof文件加载到FPGA中,形成我们设计好的数字电路,包括NIOS II CPU、SDRAM控制器、PLL等。NIOS II程序搬运阶段:FPGA配置完成后,NIOS II CPU开始从复位地址执行指令。这个复位地址指向一段Bootloader代码。这段代码可以:
- 硬件实现:由SOPC Builder中的“Boot Copier”硬件模块自动完成。这是最常用的方式。它能在FPGA配置完成后,自动将存储在EPCS指定偏移地址处的程序代码(.elf)拷贝到SDRAM的基地址处。
- 软件实现:一个极简的软件Bootloader,其本身被放在FPGA片内ROM或EPCS开头,负责后续的拷贝工作。
程序执行阶段:代码搬运完成后,NIOS II CPU跳转到SDRAM的基地址开始执行主程序。从此,系统进入高速运行状态。
整个流程的核心在于“搬运”。我们烧录到板子里的最终文件(.jic)包含了FPGA配置信息(.sof)和NIOS II程序(.elf),它们被“打包”并写入EPCS的不同区域。上电后,硬件或软件Bootloader负责解开这个包裹,把程序放到正确的位置。
注意:在SOPC Builder中配置系统时,我们必须正确设置“Reset Address”和“Exception Address”。通常,“Reset Address”指向Bootloader或SDRAM的起始地址(如果使用硬件Boot Copier),“Exception Address”指向SDRAM中用于处理中断的特定区域。设置错误会导致程序无法启动。
3. 第二个系统:SOPC Builder中的存储控制器搭建实战
理论铺垫完毕,现在打开Quartus II和SOPC Builder,我们开始动手搭建一个完整的、带存储系统的NIOS II系统。
3.1 系统框架设计与组件清单
在开始添加IP之前,我们先规划一下系统需要哪些组件:
- NIOS II Processor:CPU核心,选择经济型(Nios II/e)或标准型(Nios II/s)即可,性能型(Nios II/f)通常需要搭配缓存。
- JTAG UART:调试和打印终端,必不可少。
- System ID Peripheral:系统ID外设,用于Quartus和Nios II EDS校验软件与硬件的一致性,防止版本错乱。
- SDRAM Controller:本章主角之一,连接外部SDRAM芯片。
- EPCS Serial Flash Controller:本章另一主角,用于访问EPCS器件中的程序存储区。
- On-Chip Memory (RAM):仍然需要一小块(例如4KB),用于存放Bootloader代码或中断向量表等关键数据。因为SDRAM控制器初始化需要时间,系统最开始的几条指令必须在初始化好的内存中执行。
- PLL (Phase-Locked Loop):锁相环,用于生成SDRAM控制器所需的不同频率时钟(如100MHz),并调整时钟相位以满足SDRAM芯片的建立保持时间要求。
- Avalon-MM Tri-State Bridge:三态桥,如果需要连接类似CFI Flash这类有双向数据总线的器件时会用到。本章EPCS是SPI接口,不需要此桥。
3.2 SDRAM控制器组件添加与关键配置
在SOPC Builder的组件列表中,找到“Memories and Memory Controllers” -> “SDRAM” -> “SDRAM Controller”,双击添加。
配置界面参数较多,需要根据你开发板上的SDRAM芯片型号手册来填写。这是最容易出错的地方。我们以一颗常见的“W9825G6KH-6”(32MB, 4 Banks, 12行, 9列)为例:
- Presets:选择“Custom”。
- Data Width:16位(根据芯片型号定)。
- Architecture:1个芯片(1 Chip Select)。
- Banks:4。
- Row Address Size:12。
- Column Address Size:9。
- CAS Latency:通常为2或3个时钟周期,查芯片手册“-6”通常对应CL=3。这里填3。
- Initialization Refresh Cycles:保持默认(例如2个)。
- Issue one refresh command every:刷新周期。计算方法是
Refresh Period (ns) / Clock Period (ns)。对于W9825G6KH-6,典型刷新周期是64ms,每个Bank有4096行,所以每行刷新间隔是64ms / 4096 = 15.625us。如果我们的SDRAM时钟是100MHz(周期10ns),那么这个值就是15.625us / 10ns = 1562.5,取整填1563。这一步配置错误会导致系统运行一段时间后随机崩溃。 - Timing:
tRCD(RAS to CAS Delay):芯片手册查表,对于100MHz时钟,可能是20ns,即2个时钟周期(20ns/10ns=2)。tRP(Precharge Period):同样可能是20ns,填2。tRAS(Active to Precharge):典型值45ns或55ns,对于100MHz,可能需要5或6个周期。tWR(Write Recovery):通常1个周期加上tRP,可能需要3个周期。tMRD(Mode Register Set Cycle):固定值,通常2。
实操心得:SDRAM配置是硬件软件联调的第一道坎。务必、务必、务必找到开发板原理图和SDRAM芯片的数据手册(Datasheet)来确认每一个参数。很多开发板提供的例程中,SDRAM控制器的配置是已经调好的,可以直接参考。如果参数配置不当,系统可能根本无法启动,或运行极不稳定。
3.3 EPCS控制器组件添加与配置
在组件列表中,找到“Memories and Memory Controllers” -> “Flash” -> “EPCS Serial Flash Controller”,双击添加。
它的配置相对简单:
- 保持默认的“EPCS/EPCQx1 Serial Flash Controller”即可。
- 注意它的接口是“Avalon Memory Mapped Slave”,它会映射到NIOS II的地址空间,CPU可以通过读写特定地址来访问EPCS。
添加后,我们需要在“System Contents”标签页中,右键点击这个epcs_flash_controller组件,选择“Rename”将其改名为epcs_flash_controller,这样在后续软件编程时更清晰。
3.4 PLL组件添加与配置:时钟与相位的艺术
SDRAM对时钟和时序要求苛刻,FPGA内部产生的时钟直接驱动SDRAM芯片可能无法满足其数据建立(Setup)和保持(Hold)时间。我们需要一个PLL来完成两件事:
- 频率合成:将板载的50MHz晶振时钟,倍频到SDRAM控制器工作的100MHz(或其他所需频率)。
- 相位调整:对输出给SDRAM芯片的时钟(
sdram_clk)进行相位偏移(例如-60度),让FPGA内部的SDRAM控制器在采样SDRAM数据时,正好对准数据稳定的窗口中心,提高时序裕量。
在SOPC Builder中,PLL位于“Bridge and Adapters”下,名为“ALTPLL”。添加后,会弹出Quartus II的PLL配置工具(MegaWizard)。
- 输入时钟
inclk0设为你的板载时钟(如50MHz)。 - 创建一个输出时钟
c0,设为100MHz。 - 关键步骤:在“Phase Shift”选项中,为
c0设置一个负的相位偏移,比如-60度(具体值需要通过时序分析和板级调试确定,-60是一个常用起始值)。这个c0时钟将连接给SDRAM控制器和作为SDRAM芯片的驱动时钟。 - 还可以创建另一个同频(100MHz)但相位为0度的时钟
c1,供NIOS II内核和其他逻辑使用。
配置好后,在SOPC Builder中,需要将PLL的c0输出连接到SDRAM控制器的clk输入,并将c0输出也引到顶层模块,作为输出端口(sdram_clk)连接到FPGA引脚,最终驱动SDRAM芯片的CLK引脚。
3.5 系统集成、地址分配与生成
将所有组件(NIOS II, JTAG UART, System ID, On-Chip RAM, SDRAM Controller, EPCS Controller, PLL)拖入系统,并用Avalon总线连接起来。
接下来是关键的系统地址分配:
- 双击NIOS II处理器,进入配置。
- Reset Vector:这是CPU上电后执行的第一条指令地址。我们必须把它设置到
epcs_flash_controller的地址空间内。因为Bootloader代码(负责搬运程序到SDRAM)就存放在EPCS里。例如,设置为epcs_flash_controller.s1,偏移量(Offset)为0x0。 - Exception Vector:这是异常和中断处理程序的入口地址。我们必须把它设置到
onchip_memory或sdram的地址空间内。因为异常处理需要快速响应,且此时SDRAM应该已经初始化好了。通常设为sdram.s1的某个偏移,如0x20。 - 在SOPC Builder主界面,点击菜单
System->Auto-Assign Base Addresses和Auto-Assign IRQs,让工具自动分配基地址和中断号。 - 检查
onchip_memory的容量,设为4KB或8KB足矣。
最后,点击Generate生成系统。这个过程会编译所有的IP核,并生成一个代表整个系统的HDL文件(.qsys或.sopc文件)和相应的软件头文件(system.h)。
4. 顶层模块设计与引脚分配:连接物理世界
在Quartus II中,创建一个新的顶层Verilog/VHDL文件(或修改已有的),实例化刚才生成的系统模块(例如my_nios_system)。
module top_nios ( input wire clk_50m, // 板载50MHz时钟 input wire rst_n, // 板载复位按键,低有效 // SDRAM接口 output wire sdram_clk, output wire sdram_cke, output wire sdram_cs_n, output wire sdram_ras_n, output wire sdram_cas_n, output wire sdram_we_n, output wire [1:0] sdram_ba, output wire [11:0] sdram_addr, inout wire [15:0] sdram_data, output wire [1:0] sdram_dqm, // EPCS接口 (通常连接到FPGA的专用配置引脚,名称固定) output wire epcs_dclk, output wire epcs_sce, output wire epcs_sdo, input wire epcs_data0 ); // 系统复位信号生成,对异步复位进行同步处理,避免亚稳态 reg [2:0] rst_sync; always @(posedge clk_50m or negedge rst_n) begin if (!rst_n) rst_sync <= 3'b000; else rst_sync <= {rst_sync[1:0], 1'b1}; end wire sys_rst_n = rst_sync[2]; // 高电平有效的系统复位 // 实例化PLL(如果PLL是在SOPC Builder内生成的,它可能已被集成在系统内) // 这里假设PLL是独立模块 wire clk_100m; wire clk_100m_shifted; pll_module u_pll ( .inclk0(clk_50m), .c0(clk_100m), // 100MHz, 0度相位 .c1(clk_100m_shifted) // 100MHz, -60度相位,用于sdram_clk ); // 实例化NIOS II系统 my_nios_system u0 ( .clk_clk(clk_100m), // 系统主时钟 .reset_reset_n(sys_rst_n), // 系统复位 // 连接SDRAM控制器外部接口 .sdram_clk_clk(clk_100m_shifted), // 特别注意!PLL移相时钟给SDRAM控制器 .sdram_addr(sdram_addr), .sdram_ba(sdram_ba), .sdram_cas_n(sdram_cas_n), .sdram_cke(sdram_cke), .sdram_cs_n(sdram_cs_n), .sdram_dq(sdram_data), .sdram_dqm(sdram_dqm), .sdram_ras_n(sdram_ras_n), .sdram_we_n(sdram_we_n), // 连接EPCS控制器外部接口(通常信号名固定) .epcs_dclk(epcs_dclk), .epcs_sce(epcs_sce), .epcs_sdo(epcs_sdo), .epcs_data0(epcs_data0) ); // 将SDRAM控制器的clk输出连接到驱动SDRAM芯片的时钟引脚 assign sdram_clk = clk_100m_shifted; endmodule注意:以上代码是示意性的。实际生成的系统模块端口名需要根据你在SOPC Builder中的命名来调整。PLL也可能被集成在
my_nios_system内部。
接下来进行引脚分配。这是硬件设计的关键一步,错误会导致通信失败。
- 在Quartus II的Pin Planner工具中,根据你的开发板原理图,将顶层模块的端口分配到具体的FPGA引脚上。
- 特别关注:
sdram_clk:必须分配到FPGA的专用时钟输出引脚(如CLKOUTn),这类引脚驱动能力、抖动性能更好。epcs_*系列引脚:通常有固定的、与配置电路连接的引脚,不能随意分配。参考开发板手册或原理图。- SDRAM的其他控制、地址、数据线:分配到普通I/O引脚即可,但要注意同一组总线尽量分配到同一Bank,并参考FPGA手册的I/O标准(如设置为3.3V LVTTL)。
- 为SDRAM接口引脚设置正确的I/O Standard和Current Strength。SDRAM通常使用3.3V LVTTL,驱动电流可以设为8mA或12mA。
完成引脚分配后,进行全编译(Compilation),生成最终的FPGA配置文件(.sof)。
5. 软件工程调试、下载与固化全流程
5.1 在Nios II EDS中创建BSP与应用程序
- 打开Nios II Software Build Tools for Eclipse (SBT)。
File->New->Nios II Application and BSP from Template。- 选择刚才Quartus工程目录下的
.sopcinfo文件(SOPC Builder系统信息文件)。 - 选择软件模板,例如
Hello World。 - 关键步骤:在BSP Editor中配置软件环境。
- 进入
BSP Editor,找到Linker Script部分。我们需要指定程序不同段(Section)的存放位置。 .text(代码段)、.rodata(只读数据段):这些需要断电保存,应链接到epcs_flash_controller的地址空间。但注意,它们的运行地址(Runtime Address)应该在SDRAM中。这需要通过Bootloader来搬运。幸运的是,Altera的BSP默认设置已经帮我们处理好了。检查Linker Section映射,确保.text等的Linker region是epcs_flash_controller,但Memory region可能指向sdram。这表示“代码存储在Flash,但运行时在SDRAM中”。.rwdata(读写数据段)、.bss(未初始化数据段)、.heap、.stack:这些必须链接到sdram的地址空间。- 在
Main标签页,确保hal.linker.enable_alt_load和hal.linker.enable_alt_load_copy_exceptions等与Bootloader相关的选项是开启的。
- 进入
- 生成BSP库。
5.2 编译、下载与在线调试
- 编译应用程序,生成
.elf文件。 - 在Quartus II中,通过USB-Blaster等下载器,将
.sof文件下载到FPGA中。此时FPGA具备了包含SDRAM控制器的硬件系统,但NIOS II的程序还没固化。 - 在Nios II SBT中,右键点击工程,
Run As->Nios II Hardware。这个操作会通过JTAG接口,将.elf文件直接下载到SDRAM中并运行。此时程序是易失的,但可以用于功能调试和验证。你可以在Console窗口看到Hello World输出。
5.3 程序固化到EPCS
在线调试无误后,我们需要将程序永久烧录到EPCS中,实现脱机运行。
- 生成.jic文件:这是将FPGA配置数据(.sof)和NIOS II程序数据(.elf)合并的文件。
- 在Quartus II中,打开
File->Convert Programming Files。 Programming file type选择JTAG Indirect Configuration File (.jic)。Configuration device选择你板载的EPCS芯片型号(如EPCS16)。- 在
Input files to convert部分,点击Add SOF Data,添加你的.sof文件。 - 关键步骤:在
SOF Data的属性中,点击Add ELF Data,将你的NIOS II应用程序的.elf文件添加进去。并需要正确设置.elf文件在EPCS中的偏移地址(Start address)。这个地址必须避开FPGA配置数据占用的区域。通常,FPGA配置数据从0x0开始,大小在.sof文件转换时会给出。程序偏移地址可以设为0x100000(1MB之后),具体需要查阅文档或计算。一个简单的方法是使用BSP设置中epcs_flash_controller的基地址偏移。
- 在Quartus II中,打开
- 点击
Generate生成.jic文件。 - 烧录.jic文件:
- 将开发板断电。
- 在Quartus Programmer中,添加
.jic文件。 - 确保编程器硬件选择正确,勾选
Program/Configure。 - 点击
Start。这个过程会将.jic文件通过JTAG口写入EPCS芯片。
- 验证:烧录完成后,关闭Quartus Programmer和Nios II SBT,然后给开发板断电再上电。此时,FPGA会自动从EPCS加载配置,NIOS II系统启动后,Bootloader会自动将程序从EPCS拷贝到SDRAM并运行。你应该能看到程序(如LED闪烁、串口打印)自动运行,无需电脑连接。
6. 常见问题与排查技巧实录
搭建存储系统的过程坑点不少,这里汇总一些典型问题:
| 问题现象 | 可能原因 | 排查思路与解决方案 |
|---|---|---|
| 系统编译后,下载.sof成功,但Nios II程序无法通过JTAG下载(报错:Unable to read CPU ID) | 1. NIOS II CPU的JTAG调试模块未添加或未连接。 2. 系统时钟或复位不正确,CPU未正常工作。 3. .sof文件与当前SOPC系统不匹配。 | 1. 检查SOPC系统中,NIOS II处理器是否勾选了Include JTAG Debug Module,并确保其等级至少为Level 1。2. 检查顶层模块的时钟和复位信号是否正确连接到系统。用SignalTap II逻辑分析仪抓取CPU的 clk和reset_n信号。3. 重新全编译Quartus工程,并确保Nios II SBT中工程关联的 .sopcinfo文件是最新生成的。 |
| JTAG可以下载并运行程序,但断电重启后程序不运行 | 1. .jic文件生成错误,ELF数据未包含或偏移地址不对。 2. Reset Vector地址设置错误,未指向EPCS控制器。 3. Bootloader未正常工作。 | 1. 检查.jic文件生成步骤,确认添加了正确的.elf文件,并核对EPCS芯片容量和偏移地址是否合理(可用objdump命令查看.elf文件大小)。2. 在SOPC Builder和BSP Editor中双重检查 Reset Vector地址映射。3. 在BSP Editor中,确认 hal.linker.enable_alt_load等Bootloader相关选项已开启。可以尝试在main()函数最开始加一个LED闪烁代码,观察上电瞬间是否有动作,判断Bootloader是否执行。 |
| 程序运行不稳定,随机死机或数据错误 | 1. SDRAM控制器时序参数配置错误(如刷新周期、CAS延迟)。 2. SDRAM时钟相位未调整,时序裕量不足。 3. PCB布线质量差,信号完整性有问题。 4. 电源噪声大。 | 1. 再次严格核对SDRAM芯片手册与控制器配置参数,尤其是刷新相关参数。 2. 尝试调整PLL输出给 sdram_clk的相位偏移(如-45度, -75度),找到稳定窗口。这是一个重要的调试手段。3. 检查PCB上SDRAM时钟线是否等长,数据线是否有匹配电阻。对于成熟开发板,此问题较少。 4. 测量SDRAM供电电压是否稳定,可在电源引脚附近加滤波电容。 |
| 通过JTAG下载程序到SDRAM运行正常,但固化后启动,串口无输出或输出乱码 | 1. 程序在SDRAM中的运行地址与链接脚本中设置的不一致。 2. 初始化 .data段(已初始化全局变量)的代码在搬运过程中出错。3. 系统时钟在固化启动和JTAG调试时不一致(例如PLL未锁定)。 | 1. 检查BSP的链接脚本(.ld文件),确保程序各段(尤其是.data)的加载地址(在EPCS)和运行地址(在SDRAM)映射正确。2. 在 main()函数开头,直接对几个全局变量进行读写测试,判断数据段是否初始化成功。3. 在硬件设计中,确保PLL的 locked信号作为系统的复位释放条件之一,保证时钟稳定后才让NIOS II开始运行。 |
独家避坑技巧:
- 分步验证法:不要试图一步到位。先搭建一个最小系统(NIOS II + JTAG UART + On-Chip RAM),验证软件流程。然后单独验证SDRAM控制器:写一个简单的SDRAM读写测试程序,通过JTAG下载运行,循环读写SDRAM的每一个Bank和地址,对比数据是否正确。最后再集成EPCS和固化流程。
- 利用System Console:Nios II SBT中的System Console工具非常强大。你可以在Quartus编程后,通过它直接读写Avalon总线上的任意外设寄存器,包括SDRAM控制器。你可以用它来手动初始化SDRAM,并读写特定地址,从而在完全不用软件的情况下验证硬件连接和控制器配置是否正确。
- 仔细阅读警告信息:SOPC Builder生成系统和Quartus编译时,会给出很多警告。不要忽略它们!特别是关于时钟域交叉、地址重叠、中断未连接的警告,必须逐一排查解决。
存储系统是嵌入式产品的基石,这一步走稳了,后续的外设驱动、应用开发才能顺利进行。虽然配置过程略显繁琐,但一旦打通,你对系统启动流程、软硬件协同的理解会上一个大台阶。