ARM9 SDRAM初始化与模式寄存器编程实战解析
1. 项目概述搞嵌入式开发尤其是基于ARM9这类老牌处理器的系统SDRAM的配置绝对是个绕不开的“硬骨头”。你可能在数据手册里见过一堆时序图、寄存器位定义和初始化代码但真到自己动手对着手册一行行敲代码时还是会觉得云里雾里为什么非要等200微秒预充电和自动刷新到底在干什么那个看起来像天书一样的模式寄存器地址0x08111800是怎么算出来的如果配置错了轻则系统跑飞重则根本点不亮连最基本的调试信息都出不来。我当年在调一块基于Freescale现NXPMC9328MX1的开发板时就曾在这个问题上卡了整整一周。板子上的SDRAM是两颗Micron的MT48LC16M16A2-7E组成32位总线。按照参考手册的示例代码配置后系统就是无法正常启动串口一片死寂。最后发现问题就出在模式寄存器地址的计算上——我错误地理解了地址映射关系导致写入SDRAM的配置值完全不对。这段经历让我深刻意识到SDRAM的初始化不仅仅是“抄代码”更需要理解其背后的硬件原理和控制器的工作机制。本文将以MC9328MX1的SDRAM控制器为例彻底拆解SDRAM初始化和模式寄存器编程的每一个步骤。我不会只给你看手册上的流程图和代码片段而是会结合我踩过的坑告诉你每个操作背后的“为什么”以及在实际调试中如何验证和排查问题。无论你是在移植U-Boot、编写裸机程序还是仅仅想理解内存控制器的工作原理这篇文章都能给你提供从理论到实践的完整路径。2. SDRAM核心原理与初始化逻辑拆解在深入代码之前我们必须先搞清楚SDRAM这颗“心脏”是怎么跳动的。它不像SRAM或Flash那样上电即用其内部结构是由电容阵列组成的存储单元电荷会随时间泄露因此需要定期刷新来保持数据。同时为了达到高速度它的操作被严格同步到时钟边沿并且引入了Bank、行、列寻址以及突发传输等复杂机制。2.1 为什么需要如此复杂的初始化你可以把SDRAM想象成一个巨大的、结构精密的仓库。上电瞬间这个仓库处于未知的混乱状态仓库管理员控制逻辑没上班货架存储阵列上的货物电荷可能残存搬运通道内部信号通路也不稳定。初始化序列就是一套强制性的“开业准备流程”上电稳定≥200µs给仓库供电并等待所有内部电路电压泵、锁相环、偏置电路达到稳定工作状态。这段时间是物理要求必须满足。所有Bank预充电将所有仓库的各个楼层Bank的当前工作通道关闭使其回到“待命”状态。这是因为SDRAM的读写操作是以“激活打开某一行然后读写该行中不同列”的方式进行的。操作结束后必须关闭当前行为下一次操作做准备。初始化时我们不知道各个Bank处于什么状态所以发一个全局预充电命令让一切归零。执行8次或更多自动刷新由于电容漏电仓库里的货物数据会慢慢消失。刷新操作就是定期把每个货架存储行上的货物读出来再原样写回去以保持数据。但刚上电时电容里的电荷是随机的可能是0可能是1也可能是不稳定的中间值。执行多次刷新操作就是为了用确定的值通常是刷新操作本身的电平去覆盖这些随机电荷使整个存储阵列达到一个已知的、稳定的空白状态。JEDEC规范通常要求至少8次刷新。配置模式寄存器MRS这是最关键的一步。告诉仓库管理员我们今后的工作模式取货时从下单到拿到货要等几个时钟周期CAS Latency一次搬运多少件货物Burst Length是按顺序搬运还是隔一个搬一个Burst Type以及送货写操作是单件送还是一次送一批Write Burst Mode。这些参数必须与主控MCU的能力和系统时钟频率严格匹配否则通信会彻底错乱。MC9328MX1的SDRAM控制器SDRAMC为我们分担了一部分工作。如手册所述硬件会自动处理步骤1和2它通过一个专用的SD_RST信号在系统复位信号m_rst解除的200µs前就先解除SDRAM的复位并在这200µs内保持时钟稳定、命令线处于NOP无操作状态。这意味着在我们的软件开始运行之前SDRAM已经完成了上电稳定等待。我们的代码只需要从步骤3预充电开始执行。2.2 控制器操作模式SMODE是关键桥梁手册中反复提到的SMODE字段是软件与SDRAM物理命令之间的翻译官。SDRAMC有几种特殊的操作模式通过配置SMODE来进入预充电命令模式Precharge Command在此模式下对SDRAM地址空间的任何访问读或写都不会执行真正的数据传输而是由控制器翻译成一条“预充电所有Bank”的命令发送给SDRAM颗粒。自动刷新模式Auto-Refresh在此模式下对SDRAM地址空间的访问会被翻译成“自动刷新”命令。设置模式寄存器模式Set Mode Register在此模式下对SDRAM地址空间的访问会被翻译成“加载模式寄存器”命令。此时访问的地址总线上的值就是我们要写入模式寄存器的配置数据。正常模式Normal默认模式在此模式下访问SDRAM才会执行真正的读写操作。我们的初始化代码本质上就是按顺序将控制器切换到不同的SMODE然后通过对特定地址进行“虚假”的访问来触发控制器发出对应的物理命令。这解释了为什么示例代码init_sdram中在设置了PRE_ALL_CMD模式后会执行两条ld指令——它们并非读取数据而是为了触发预充电命令。3. 初始化流程的代码级详解让我们结合手册中的Code Example 24-2逐行解析这个初始化序列。假设我们使用CSD0片选0其映射的SDRAM阵列基地址为0x08000000。3.1 步骤分解与地址映射预充电所有Bank操作将控制器的SMODE设置为预充电命令模式然后对SDRAM地址空间进行一次访问。关键细节手册提到访问时需要将地址位A10置1。在SDRAM标准中A10线在发送预充电命令时如果为高表示“预充电所有Bank”如果为低则表示只预充电由BA0/BA1指定的Bank。初始化时我们当然希望全部预充电。在MC9328MX1的地址映射中A10对应的是内部地址的某一位。对于0x08000000开始的阵列进行一次“预充电所有”访问的地址通常是基地址 | (1 10)。在示例代码中这个地址被直接包含在SDRAM_ARRAY_0这个标签所代表的值里例如0x08000000而A101这个条件可能已经在硬件或初始化常量中隐含处理。更常见的做法是在设置SMODE后直接读取基地址即可控制器会自行处理A10信号。ldr r2, CSD0_CONTROL_REG_BASE 加载SDRAM控制器寄存器基地址 ldr r3, PRE_ALL_CMD_VALUE SMODE 预充电所有命令模式 str r3, [r2] 配置控制器0CSD0进入预充电模式 ldr r4, SDRAM_ARRAY_0_BASE 加载SDRAM阵列0的基地址例如0x08000000 ldr r0, [r4] 执行一次“读”访问。注意此处不关心读回的数据目的是触发控制器发出预充电命令注意很多新手会在这里困惑ld r3, (0,r4)这条指令不是“加载数据”吗是的但从CPU角度看它发起了一次总线访问。SDRAM控制器在SMODE被设置为特殊模式时会拦截这次访问并将其“翻译”成对应的SDRAM命令此处是PRE-ALL而不是进行真实的数据传输。因此r3寄存器中读回的值是无意义的。执行8次自动刷新操作将SMODE设置为自动刷新模式然后对SDRAM地址空间进行8次访问。关键细节每次访问触发一次刷新命令。刷新操作没有地址概念所以访问哪个地址都可以通常就使用SDRAM的基地址。等待时间tRP预充电命令周期时间在命令之间由控制器自动满足。ldr r3, AUTO_REF_CMD_VALUE SMODE 自动刷新模式 str r3, [r2] 配置控制器进入自动刷新模式 mov r6, #8 需要8次刷新 1: ldr r0, [r4] 对阵列基地址进行8次访问触发8次刷新命令 subs r6, r6, #1 bne 1b设置模式寄存器MRS操作这是最复杂的一步。将SMODE设置为设置模式寄存器模式。然后向一个特定的、计算好的地址进行一次写或读访问。这次访问提供的地址值其低位部分对应到SDRAM的地址引脚A0-A12/BA0/BA1等就是我们要写入模式寄存器的数据。关键细节这个“特定的地址”就是本文的重中之重下一章会详细计算。示例代码中MODE_REG_VAL0这个标签指向的就是这个计算好的地址值如0x08111800ld r3, (0,r3)指令将这个值加载到地址总线上从而完成模式寄存器的写入。ldr r3, SET_MODE_REG_CMD_VALUE SMODE 设置模式寄存器模式 str r3, [r2] 配置控制器进入MRS模式 ldr r3, MODE_REG_VAL0 加载计算好的模式寄存器地址值 ldr r0, [r3] 发起访问地址总线上的值即为配置参数写入模式寄存器返回正常模式操作将SMODE设置回正常模式。此后对SDRAM地址空间的访问就是真实的读写操作了。ldr r3, NORMAL_MODE_VALUE str r3, [r2]3.2 实操心得与避坑指南顺序是铁律预充电-刷新-设置模式寄存器-正常操作。这个顺序绝对不能错也不能省略任何一步。我曾尝试过跳过几次刷新系统在大多数简单测试中能跑但运行大型程序或长时间后必然死机原因是存储单元未达到稳定状态。时间参数虽然控制器帮我们处理了tRP、tRRC等时序但在编写初始化代码时仍需确保指令执行速度不会快于SDRAM颗粒要求的最短时间间隔。在MC9328MX1这类处理器上用汇编循环或C语言空循环进行几次刷新访问其间隔通常远大于SDRAM要求的最小值所以一般没问题。但在更高主频的CPU上可能需要插入nop或精确延时。多阵列Chip Select处理如果系统连接了多片SDRAM使用CSD0和CSD1需要对每个片选控制的阵列独立执行完整的初始化序列预充电、刷新、设置模式寄存器。示例代码中对(0,r2)和(4,r2)的分别操作就是分别配置CSD0和CSD1对应的控制器寄存器。验证方法初始化完成后如何验证SDRAM工作正常一个简单有效的方法是进行“走马灯”测试向SDRAM的一段连续地址写入一个特定的模式如0xAA55AA55然后读回来比较。再写入另一个模式如0x55AA55AA再读回。如果读写一致基本说明初始化成功。更严格的测试包括地址线测试、数据总线测试和完整性压力测试。4. 模式寄存器MRS编程深度解析模式寄存器配置是SDRAM性能与稳定性的基石。配置错误SDRAM要么完全无法工作要么会在高负载下出现随机错误。4.1 模式寄存器位域详解以手册中256Mbit (16Mx16) SDRAM为例其模式寄存器通过地址线A12-A0及可能的BAx输入。核心位域包括位域 (示例)名称功能描述常见设置与原因M2, M1, M0突发长度 (BL)定义连续读/写的字数。011 (8)这是与MC9328MX1的ARM920T内核缓存线长度8个字匹配的标准设置。突发传输能最大化总线效率。M3突发类型 (BT)0顺序突发1交错突发。0 (顺序)顺序突发0,1,2,3...更符合CPU访问内存的线性特性。交错突发在特定DMA场景可能有用但MC9328MX1不支持。M6, M5, M4CAS延迟 (CL)从发出读命令到数据出现在总线上所需的时钟周期数。010 (2 clocks)或011 (3 clocks)。这取决于SDRAM芯片的规格和系统时钟频率。100MHz下许多-7E速度等级的芯片可跑CL2但为稳定起见CL3更保险。必须查阅具体SDRAM芯片的数据手册。M9写突发模式 (WB)0突发写1单点写。1 (单点写)至关重要MC9328MX1的SDRAM控制器只支持单点写操作即每个写命令只写一个数据。如果错误设置为0突发写控制器无法在突发中途发送“终止突发”命令会导致后续写入数据丢失或覆盖。其他 (M8, M7, M10, M11, M12, M13)保留位必须设置为0。通常接地或设为0。对于示例中的Micron MT48LC16M16A2-7E芯片在100MHz、非Bank交错模式下我们需要的配置是BL 8 (011)BT 0 (顺序)CL 2 (010) // 假设芯片支持CL2 100MHzWB 1 (单点写)保留位 0将这些二进制值按位填入模式寄存器位得到一串二进制数。对于该芯片模式寄存器映射到地址线A12-A0见手册Table 24-39。假设我们关心A12-A0那么A0, A1, A2 对应 BL: 0, 1, 1 (二进制011 注意LSB在A0)A3 对应 BT: 0A4, A5, A6 对应 CL: 0, 1, 0 (二进制010)A7, A8: 保留位 0A9 对应 WB: 1A10, A11, A12: 保留位 0因此A[12:0]的二进制值为0000 0010 0011 1从A12到A0。为了方便我们通常按A12是最高位来排列b0000_0010_0011_1。将其转换为十六进制并注意位宽这是一个13位的值但我们需要将其放入一个32位地址的某些位上。这就引出了最令人困惑的部分——地址计算。4.2 地址计算从位模式到物理地址这是整个过程中最容易出错的一环。我们得到的模式寄存器位模式例如0000_0010_0011_1并不是直接作为CPU访问的地址。它需要被放置到MCU外部地址总线连接SDRAM芯片的地址线的特定引脚上。而MCU内部地址总线到外部地址引脚的映射又取决于内存配置密度、位宽、是否Bank交错。手册中的Table 24-38就是这个映射关系的“解码表”。它告诉我们对于特定的内存配置如16Mx16, IAM0内部地址总线A‘x的哪些位最终会连接到SDRAM芯片的哪些地址引脚对应模式寄存器位Mx。计算流程以示例1 16Mx16, IAM0为例确定配置我们的系统是两颗16Mx16的SDRAM组成32位宽总容量32MB。对应手册Table 24-38中的“16Mx16Bit x 2 Chips (64 Mbyte)”行。注意这里64MB是比特位除以8得到8MB不对这里描述有歧义。应根据芯片实际容量判断。MT48LC16M16A2是16Mbit x 16即32Mbit每颗两颗共64Mbit等于8MB。但手册表格标题的“64 Mbyte”疑似笔误应为“64 Mbit”或“8 Mbyte”。我们以实际连接和Table 24-34/24-38的映射关系为准。查找映射行在Table 24-38中找到“16Mx16Bit x 2 Chips”这一行。它有两行分别对应CSD0和CSD1实际上它展示了内部地址位A‘31-A’0与SDRAM地址引脚/模式寄存器位Mx的对应关系。我们需要关注的是A‘23-A’12这一段的映射因为A‘31-A’24是片选基地址0x08A‘11-A’0在初始化访问时通常被忽略或为0。插入模式寄存器位从映射行我们看到以CSD0部分为例A‘23 对应 M13 (保留位我们设为0)A‘22 对应 M12 (保留位0)A‘21 对应 M11 (保留位0)A‘20 对应 M10 (保留位0)A‘19 对应 M9 (WB1)A‘18 对应 M8 (保留位0)A‘17 对应 M7 (保留位0)A‘16 对应 M6 (CL bit20)A‘15 对应 M5 (CL bit11)A‘14 对应 M4 (CL bit00)A‘13 对应 M3 (BT0)A‘12 对应 M2 (BL bit20)A‘11 对应 M1 (BL bit11)A‘10 对应 M0 (BL bit01)A‘9-A’0 在初始化访问时通常为0。组合地址片选基地址CSD0: A‘31-A’24 0x08模式寄存器位部分: A‘23-A’10 0000_0001_0001_1000(从M13到M0: 0,0,0,0,1,0,0,0,1,1,0,0,0,1? 等一下这里需要仔细核对) 根据我们之前的位模式(M13-M0): 0,0,0,0,1,0,0,0,1,1,0,0,0,1? 不对。我们有的位是M91, M6..M4010, M30, M2..M0011。 映射关系A‘19(M9)1, A‘16(M6)0, A‘15(M5)1, A‘14(M4)0, A‘13(M3)0, A‘12(M2)0, A‘11(M1)1, A‘10(M0)1。其他M位M13,12,11,10,8,7都是保留位0。 所以 A‘23-A’10 的二进制是A‘23(M13)0, A‘22(M12)0, A‘21(M11)0, A‘20(M10)0, A‘19(M9)1, A‘18(M8)0, A‘17(M7)0, A‘16(M6)0, A‘15(M5)1, A‘14(M4)0, A‘13(M3)0, A‘12(M2)0, A‘11(M1)1, A‘10(M0)1。 即0000_1000_0100_0011(从A‘23到A‘10)。转换为十六进制0x0843。低10位A‘9-A’0: 0x000因此完整的内部地址为0x08000000(基址) 0x00084300(模式寄存器位左移对齐到地址线)? 不对需要看地址位对应关系。A‘23-A’10是内部地址的第23位到第10位。所以这个值0x0843实际上占据了内部地址的 [23:10] 位。 计算0x0843左移10位因为A‘10是第10位0x0843 10 0x210C00。 再加上基地址0x08000000得到0x08110C00。 但手册示例1给出的结果是0x08111800。这说明我的计算或者对映射关系的理解有误。这正是实际调试中最容易出问题的地方为什么会出现差异手册Table 24-38的展示方式非常容易让人误解。它并不是一个简单的移位操作。表中的“X”表示“无关位”而有些模式寄存器位被映射到了内部地址的非连续位置。例如在“16Mx16Bit”行M13和M12被映射到了A‘23和A‘22但M11-M0被映射到了A‘21-A’10吗仔细看在“CSD0”部分M11对应A‘21M10对应A‘20…M0对应A‘10。这看起来是连续的。那么问题可能出在我对模式寄存器位值的推导有误。手册示例中的值0x08111800包含了其他我们忽略的固定位例如可能A‘24的某个固定值。最可靠的方法直接使用手册提供的示例值进行验算和推导。示例1中模式寄存器值最终被转换为地址0x08111800。我们反推一下0x08111800减去基地址0x080000000x00111800。0x00111800的二进制0000_0000_0001_0001_0001_1000_0000_0000。 关注A‘23-A’10位即bit23-bit10:bit23:0, bit22:0, bit21:0, bit20:1, bit19:0, bit18:0, bit17:0, bit16:1, bit15:0, bit14:0, bit13:0, bit12:1, bit11:1, bit10:0。 即0000_1000_0100_0110(从A‘23到A‘10)。这和我们之前根据模式寄存器位计算出的0000_1000_0100_0011在A‘10位不同我们是1这里是0。这说明M0BL最低位在地址计算中可能被置为了0或者我的位映射理解有偏差。面对这种复杂情况实操中的建议是优先使用芯片厂商或BSP提供的参考代码如果目标芯片和SDRAM型号与参考设计一致直接使用其计算好的地址常量是最安全、最快捷的。严格对照数据手册和控制器手册如果必须自己计算拿出Table 24-38和SDRAM数据手册的模式寄存器映射表画一个从内部地址位到SDRAM地址引脚的详细映射图一位一位地核对。利用已知正确值反推验证像上面一样用手册给出的正确地址反推模式寄存器位在地址中的实际位置总结出规律。编写验证脚本对于复杂的映射可以写一个简单的Python或C脚本输入模式寄存器各比特值、内存配置、基地址自动计算出待写入的地址。这能极大减少人为错误。4.3 模式寄存器编程步骤总结确定SDRAM芯片型号获取其数据手册。确定系统参数系统时钟频率、总线宽度32位、是否Bank交错IAM。查阅芯片数据手册找到模式寄存器MRS的位定义表。确定在目标频率下支持的CAS LatencyCL。确定突发长度BL通常为8、突发类型BT顺序、写突发模式WB必须为1。组合模式寄存器值根据步骤3得到一组二进制位M0-M13等。查阅MCU控制器手册找到对应内存配置密度、位宽、IAM的地址映射表如Table 24-38。进行地址计算将步骤4得到的模式寄存器位按照步骤5的映射关系“填入”内部地址总线A‘的相应位置。结合片选基地址得到最终的32位物理地址MODE_REG_VAL。在代码中使用在初始化序列的“设置模式寄存器”阶段将控制器设为MRS模式然后向步骤6计算出的地址执行一次加载或存储操作。5. 常见问题排查与调试技巧即使严格按照手册操作SDRAM初始化仍可能失败。以下是我在实践中总结的排查清单5.1 系统毫无反应串口无输出检查电源和时钟首先确认SDRAM的VDD、VDDQ供电是否正常时钟信号SDCLK是否有波形频率是否正确。检查硬件连接对照原理图检查地址线、数据线、控制线RAS、CAS、WE、CS、DQM是否有短路、断路、虚焊。特别是数据线的高位D16-D31在32位系统中也必须连接。检查初始化代码是否执行在初始化代码的开头设置一个GPIO引脚翻转其电平用示波器查看。确保CPU已经执行到了SDRAM初始化部分。检查配置寄存器值确认写入SDRAM控制器SDCTL的配置值行/列地址位数、CAS延迟、刷新率等与硬件连接完全匹配。一个常见的错误是“ROW”和“COL”配置反了。5.2 可以执行简单代码但运行大程序或复杂任务时死机刷新率配置错误检查SDRAM控制器的刷新计数器REFR设置。刷新间隔过长会导致数据丢失。计算公式为刷新周期 (2^REFR) / 系统时钟频率。必须满足SDRAM芯片要求的最大刷新间隔如64ms内刷新8192行。CAS Latency不匹配这是最隐蔽的问题之一。如果CL设置得比SDRAM芯片在當前频率下能支持的值更小可能在低速访问时正常一旦总线繁忙时序收紧就会出错。务必根据芯片数据手册的频率-CL对应表来设置。在不确定时设置为更大的CL值如CL3更稳妥。模式寄存器地址计算错误如上一章所述这是重灾区。如果地址算错SDRAM会工作在完全错误的模式下。使用前面提到的“走马灯”测试或更完整的内存测试来验证。时不满足虽然MCU控制器处理了大部分时序但PCB布线引起的信号完整性问题过冲、振铃、串扰可能导致时序裕量不足。在高速系统100MHz中需要检查PCB的等长设计和端接电阻。5.3 数据读写不稳定某些位总是错误检查DQM信号数据掩码信号DQM必须正确连接和配置。对于32位系统DQM0-DQM3通常分别对应字节0-3。检查地址线连接特别是地址线A10它在预充电命令中有特殊作用。确保其连接正确。进行总线测试编写测试程序依次让每根数据线写0读0、写1读1检查是否有固定为高或低的位。对地址线进行“地址线粘连”测试如写入地址A却在地址B读到数据。电源噪声SDRAM对电源纹波比较敏感。用示波器检查SDRAM电源引脚上的噪声是否在芯片允许范围内。必要时增加去耦电容。5.4 使用调试器如JTAG进行排查如果支持JTAG调试可以单步跟踪初始化代码观察每一步操作后SDRAM控制器寄存器的值是否按预期变化。内存窗口查看在初始化完成后尝试通过调试器查看SDRAM地址空间如0x08000000。如果能看到可读写的内存可能是随机值说明初始化基本成功。如果读取失败或全是0xFF/0x00则说明通信未建立。设置数据观察点Watchpoint对SDRAM的某个地址设置写观察点然后运行程序。如果程序触发了该观察点说明CPU成功写入了SDRAM这是一个好迹象。6. 同步闪存SyncFlash的特别注意事项MC9328MX1还支持一种特殊的存储器——SyncFlash。它兼容SDRAM接口但本质是NOR Flash。初始化比SDRAM简单得多主要区别在于无需复杂的预充电/刷新序列上电后等待复位信号RESET_SF拉高并稳定至少100µs即可。刷新必须禁用在SDRAM控制器中必须关闭自动刷新功能。因为SyncFlash将刷新命令解释为其他控制命令开启刷新会导致不可预料的行为。模式寄存器可配可不配SyncFlash在初始化时会从内部的非易失性配置寄存器加载默认模式通常可以工作。如需更改配置方法与SDRAM相同。引导设备限制如果要从SyncFlash启动它必须连接到CSD1不能是CSD0。在配置SyncFlash时最关键的一步就是在SDRAM控制器的配置寄存器中将刷新使能位通常是REFR相关的字段设置为禁用。其他步骤与SDRAM类似但少了刷新循环。7. 总结与进阶思考SDRAM初始化和模式寄存器编程是嵌入式底层开发中的一项基本功。它要求开发者不仅会写代码更要理解硬件时序、总线协议和地址映射。通过剖析MC9328MX1的这个案例我们可以将这套方法论迁移到其他平台比如STM32的FMC控制器、i.MX系列的MMDC等。我个人最大的体会是文档和参考代码是你的朋友但绝不能替代思考。手册中的表格和示例代码提供了蓝图但你必须理解每一行代码、每一个寄存器位背后的硬件行为。当遇到问题时系统地、分层地排查从电源时钟到硬件连接再到软件配置最后考虑信号完整性等物理层因素。对于更复杂的系统如DDR3/LPDDR4初始化序列和寄存器配置呈指数级增长通常会由BootROM或初级引导程序完成。但理解SDRAM的基本原理依然是驾驭这些现代内存技术的基础。下次当你看到U-Boot源码中那长长的dram_init()函数时希望你能会心一笑因为你知道它正在完成的正是我们今天讨论的这套“唤醒”内存的精密仪式。