深入解析TI EMIFB SDRAM控制器:调度、同步与低功耗实战

📅 2026/7/22 12:51:48 👁️ 阅读次数 📝 编程学习
深入解析TI EMIFB SDRAM控制器:调度、同步与低功耗实战

1. 项目概述与核心挑战

在嵌入式系统开发中,外部SDRAM(同步动态随机存取存储器)是扩展系统内存、运行复杂应用的关键。然而,直接与SDRAM芯片打交道是件麻烦事,你需要精确控制行激活、列选通、预充电、刷新等一系列时序命令,任何一个环节出错都可能导致数据丢失或系统崩溃。因此,SDRAM控制器应运而生,它充当了CPU与物理SDRAM颗粒之间的“智能翻译官”和“交通警察”。

这个“警察”的工作远不止发号施令那么简单。在一个多主控(Multi-master)的复杂SoC(片上系统)里,比如同时有CPU、DSP、DMA控制器等多个模块都需要访问内存,SDRAM控制器面临的挑战是三维的:效率、正确性和功耗。效率低下会导致系统性能瓶颈;正确性出问题,轻则数据错误,重则系统死锁;功耗失控则直接关系到设备的续航和散热。德州仪器(TI)的EMIFB(External Memory Interface B)控制器,就是应对这些挑战的一个经典工业级解决方案。它不仅仅是一个连接器,更是一个集成了复杂调度算法、电源管理状态机和健壮性保障机制的内存子系统核心。

本文将深入拆解EMIFB控制器的三大核心难题:命令饥饿、竞争条件和电源管理。我会结合手册中的技术细节和实际工程经验,解释这些现象为何会发生,EMIFB是如何设计机制来应对的,以及我们在实际配置和编程中需要注意哪些“坑”。无论你是正在调试一块新的核心板,还是希望优化现有系统的内存性能与功耗,这些从芯片手册字里行间和调试实践中总结出的经验,或许能帮你少走些弯路。

2. 命令饥饿:当低优先级请求永远在排队

命令饥饿(Command Starvation)是SDRAM控制器调度算法中一个经典且棘手的问题。想象一下,你在一个繁忙的十字路口,交警一直给主干道(高优先级读请求)放行,结果支路(低优先级写请求)上的车永远过不去。在EMIFB中,这种情况具体表现为两种:

2.1 高优先级读阻塞低优先级写

SDRAM的物理特性决定了读操作通常比写操作对系统性能(尤其是处理器流水线)的影响更直接、更关键。一次缓存未命中导致的读等待,可能让CPU流水线停滞数十个周期。因此,许多内存控制器(包括EMIFB)的默认或可配置策略会优先处理读请求。

问题场景:假设一个视频编码器(Master A)正在通过DMA向SDRAM中持续写入大量的原始视频帧数据(低优先级、大流量的写操作)。同时,CPU(Master B)需要频繁读取指令和数据(高优先级、随机性的读操作)。如果控制器的调度器设计是严格的读优先,那么CPU的读请求可能会不断插队,导致DMA的写请求队列被长时间阻塞。虽然CPU性能得到了保障,但DMA的缓冲区可能因此溢出,导致视频数据丢失。

EMIFB的解决机制:优先级提升计数器EMIFB并没有采用简单的静态优先级。它在BPRIO(Peripheral Bus Burst Priority Register)寄存器中提供了一个关键的PRIO_RAISE位域。这个机制就像一个“公平性计时器”:

  1. 计数:控制器会统计自上次调度以来,已经连续执行了多少次传输(transfers)。
  2. 触发:当连续传输次数达到PRIO_RAISE设定的阈值时,调度器会临时提升命令FIFO中最老的那个命令的优先级,无论它是读还是写。
  3. 执行:这个被“饿”了很久的老命令会被立即调度执行。
  4. 重置:执行完成后,计数器重置,调度逻辑恢复正常。

实操配置与心得

  • 阈值设置PRIO_RAISE的值需要权衡。设置太小(如4或8),会频繁打断高优先级流的连续性,降低整体带宽利用率;设置太大(如64或128),低优先级请求等待时间过长,可能引发上游主控的超时错误。没有绝对最优值,需要结合你的具体应用场景(各主控的带宽需求、延迟敏感度)进行性能剖析(Profiling)后确定。一个常见的起步策略是将其设置为典型高优先级突发(Burst)长度的2-4倍。
  • 监控:EMIFB提供了性能计数器寄存器(PC1,PC2,PCC,PCMRS),你可以配置它们来统计特定主控或区域的读/写停顿周期数。这是诊断是否存在命令饥饿的黄金指标。如果发现某个主控的写停顿计数器异常高,可能就是饥饿现象的证据。

2.2 同行不同页的访问阻塞

这是SDRAM架构带来的另一个特定饥饿场景。SDRAM内部被分为多个Bank(库),每个Bank可以独立地打开一行(Page)。访问已打开行(页命中,Page Hit)的速度远快于访问关闭行(页缺失,Page Miss),因为后者需要先预充电关闭当前行,再激活新行。

问题场景:某个主控程序(例如一个矩阵运算内核)正在密集访问Bank 0的Row X,产生了连续的页命中,速度极快。此时,另一个请求需要访问Bank 0的Row Y。由于Bank 0当前的行激活(Active)状态被Row X占用,访问Row Y的请求(它需要发出一条预充电命令关闭Row X,再发激活命令打开Row Y)就会被持续阻塞,直到对Row X的密集访问流暂停。

EMIFB的应对逻辑: EMIFB的调度器同样会受到PRIO_RAISE机制的影响。当对同一Bank同一行的访问流持续了PRIO_RAISE次传输后,调度器会强制插入对最老命令的处理。如果最老的命令正好是那个需要访问其他行的请求,它就会被执行,从而打破“一行独大”的局面,保证了不同行地址请求的基本公平性。

注意:这个机制主要缓解的是因调度策略导致的“饿死”,对于因SDRAM物理时序(如tRCtRAS)限制而必须等待的情况,控制器无能为力,这是硬件特性。

3. 竞争条件:看不见的数据一致性陷阱

竞争条件(Race Condition)在多主控、多级流水线的内存系统中是一个隐蔽的“幽灵”。它不一定会让系统立刻崩溃,但会导致间歇性的、难以复现的数据错误,是嵌入式系统中最令人头疼的Bug之一。

3.1 软件消息传递中的经典竞争

手册中给出的例子非常典型:Master A通过SDRAM中的一个缓冲区传递软件消息给Master B。

  1. Master A向缓冲区写入新消息。
  2. Master A不等待写完成确认,就直接通知Master B“数据准备好了”。
  3. Master B立即发起读请求,试图读取该消息。

风险点:由于写操作在EMIFB控制器和SDRAM总线层面存在延迟(写入FIFO、调度、传输到SDRAM颗粒并生效),Master B的读请求可能被调度到Master A的写请求之前执行,或者读到了缓存中的旧数据。结果就是Master B读到了过时的(stale)数据,导致通信失败。

3.2 EMIFB的硬件局限与软件屏障(Barrier)解决方案

关键在于,EMIFB作为从设备(Slave),它不会主动向发起写操作的主控(如CPU)发送“写完成”事件。这与一些带有写回(Write-back)确认的高级总线协议不同。

因此,手册给出了一个必须由软件实现的内存屏障(Memory Barrier)写同步(Write Synchronization)操作,作为变通方案(Workaround):

// Master A 发送消息 *message_buffer = new_message; // 1. 执行需要的写操作 // 2. 执行一次到EMIFB SDRAM状态寄存器的虚写(Dummy Write) volatile uint32_t *sdram_status_reg = (volatile uint32_t *)EMIFB_SDSTAT_REG_ADDR; *sdram_status_reg = 0; // 虚写,目的是冲刷CPU写缓冲区,并确保写请求到达EMIFB // 3. 执行一次��同一状态寄存器的虚读(Dummy Read) uint32_t dummy = *sdram_status_reg; // 虚读,确保之前的写操作已在EMIFB和SDRAM层面完成 // 4. 现在可以安全地通知Master B signal_master_b_data_ready();

原理解析与实操要点

  1. 虚写(Dummy Write):其核心作用是冲刷(Flush)CPU的写缓冲区(Write Buffer)。现代CPU为了性能,会将写操作暂存在缓冲区中,并非立即发出。这条写指令强制CPU将包括message_buffer写入在内的所有未决写操作提交到总线。
  2. 虚读(Dummy Read):这是关键同步点。读操作是“获取”数据的,CPU必须等待读响应。当对EMIFB的同一个从设备(这里就是EMIFB的状态寄存器)发起读操作时,EMIFB的接口逻辑会确保所有之前提交给它的、目标地址在该从设备范围内的写操作都已完成,才会处理这条读请求。因此,当虚读完成时,意味着之前对message_buffer(同样映射在EMIFB地址空间)的写操作肯定已经完成。
  3. 为什么选择状态寄存器?因为它是一个总是可读写的、不会产生副作用(Side-effect)的寄存器,对其进行虚读写不会影响SDRAM的正常状态。

重要例外:手册明确指出,对于EDMA(增强型直接内存访问)控制器,不需要此变通方案。因为EDMA传输本身具有完成事件(Completion Event)或中断机制,你可以通过等待EDMA传输完成事件来自然实现同步。这提醒我们,在阅读手册时,一定要注意“适用范围”和“例外情况”。

工程实践中的建议

  • 封装成函数:在驱动层或操作系统层,应将此序列封装成一个函数,如emifb_write_sync(),供所有需要保证写后读顺序的代码调用。
  • 理解编译器屏障:在高级语言中,需要使用volatile关键字来防止编译器优化掉这些“看似无用”的虚读写操作。有时还需要插入编译器屏障(如GCC的__asm__ __volatile__("" ::: "memory")),确保内存操作顺序不被重排。
  • 性能代价:这个序列引入了额外的延迟(几次寄存器访问周期)。在性能关键路径上要谨慎使用,但为了数据正确性,在共享数据同步点上必须使用。

4. 复位与初始化:稳定起航的第一步

EMIFB控制器有两种复位信号,理解它们的区别是系统稳定启动的基础:

  • CHIP_RST:芯片级复位。它重置整个EMIFB模块,包括状态机和所有内存映射寄存器(MMRs)。相当于给控制器做了个“全身格式化”,一切回到出厂状态。
  • MOD_G_RST:模块级全局复位。它只重置控制器的状态机,但保持寄存器的配置值不变。这类似于“重启服务但不改变设置”,用于从某些错误状态中恢复。

关键警告与实操铁律: 手册强调:在CHIP_RST或MOD_G_RST信号有效(asserted)期间,用户软件绝不能访问EMIFB的内存空间或其寄存器。为什么?因为此时控制器的内部逻辑处于不确定状态。强行访问可能导致:

  1. 访问挂起(Hang):总线等待无响应,导致发起访问的主控(如CPU)死锁。
  2. 错误配置:在复位解除、初始化序列自动开始的瞬间,如果软件同时写配置寄存器,可能造成配置冲突或损坏。

正确的初始化流程

  1. 等待复位释放:系统上电或硬复位后,软件需通过查询PSC(电源与睡眠控制器)或全局状态寄存器,确认CHIP_RSTMOD_G_RST均已释放。
  2. 等待PLL锁定:确保给EMIFB提供时钟的PLL已经锁定并输出稳定时钟。
  3. 配置前检查:即使复位完成,EMIFB会自动开始一个基础的SDRAM初始化序列。但手册第19.2.6.5节描述的特定初始化流程仍然必须由软件执行。这通常包括:
    • 配置SDRAM时钟(通过PLL控制器)。
    • 在改变时钟前,先将SDRAM置于自刷新模式(设置SDRFC寄存器的LP_MODE=1,SR_PD=0)。
    • 编程SDCFGSDRFCSDTIM1SDTIM2这四个核心寄存器。特别注意SDCFG中的TIMUNLOCK,它像一把锁,在写时序寄存器前需要先将其置1,写完后清0,防止误修改。
  4. 触发初始化:向SDCFG寄存器的某些字段(如NM,CL,IBANK,PAGESIZE)写入值,会自动触发EMIFB执行完整的SDRAM初始化序列(包括发送预充电、模式寄存器设置命令等)。软件只需配置并写入,无需手动生成SDRAM命令。

5. 电源管理:在性能与功耗间走钢丝

对于电池供电的嵌入式设备,EMIFB和SDRAM的功耗是系统功耗的大头。EMIFB提供了从浅到深的多级功耗管理手段。

5.1 自刷新模式(Self-Refresh)

这是最常用的一种低功耗状态。

  • 原理:控制器通过设置SDRFC寄存器(LP_MODE=1, SR_PD=0),向SDRAM发送命令,使其进入自刷新模式。在此模式下,SDRAM内部的振荡器工作,自动进行刷新以保持数据,但外部时钟和命令总线可以暂停,EMIFB控制器的大部分逻辑也可以进入低功耗状态。
  • 功耗:SDRAM自身功耗显著降低(从几十mA降至几百uA),EMIFB控制器功耗也有下降。
  • 唤醒延迟:唤醒时间较短(通常需要满足tXSR时序,即自刷新退出时间,在SDTIM2中配置),一般在微秒级。
  • 应用场景:系统空闲(Idle)、待机(Standby)等短时休眠状态。

5.2 掉电模式(Power-Down)

  • 原理:通过设置SDRFC寄存器(LP_MODE=1, SR_PD=1)进入。EMIFB将EMB_SDCKE信号拉低。SDRAM进入掉电模式,内部刷新停止,功耗比自刷新模式更低
  • 风险与机制:数据需要依靠电容保持,时间非常短(通常<100us)。EMIFB会周期性地拉高EMB_SDCKE并发送刷新命令,然后再拉低,以维持数据。这增加了控制器的动态功耗。
  • 应用场景:对功耗极度敏感,且能容忍周期性轻微唤醒抖动的超低功耗场景。使用前必须仔细计算SDRAM的数据保持时间和刷新周期。

5.3 时钟门控(Clock Gating)

这是最极致的省电方式,通过PSC(电源与睡眠控制器)关闭EMIFB模块的输入时钟(VCLK, MCLK等)。

  • 原理:时钟树被关闭,EMIFB内部逻辑完全静态,功耗接近漏电功耗。
  • 前提条件必须先将SDRAM置于自刷新模式。因为关闭时钟后,EMIFB无法再发送任何刷新命令,如果SDRAM不在自刷新状态,数据会丢失。
  • 关键配置:需要设置SDRFC寄存器的MCLKSTOP_EN=1来使能此功能。
  • 唤醒:重新使能时钟后,需要先将MCLKSTOP_EN清0,再让SDRAM退出自刷新模式。
  • 警告:如果外部SDRAM器件需要持续时钟(某些特定型号),则绝对不能使用此模式,否则会导致数据损坏。

5.4 LPSC状态机详解

PSC对EMIFB的时钟控制通过LPSC(局部电源与睡眠控制器)状态机实现,共有五种状态:

  1. Enable:全功能运行状态。
  2. Disable:手动关闭时钟。进入前需软件确保SDRAM进入自刷新且无数据传输。是最彻底的省电状态。
  3. Auto Sleep自动睡眠。这是非常实用的特性。EMIFB在空闲时自动进入Disable状态(时钟关闭),但其总线接口仍在监听。当有新的访问请求到来时,硬件自动唤醒至Enable状态处理请求,处理完毕后又自动回到Disable状态。实现了“随用随开”,在间歇性访问的场景下节能效果极佳。
  4. Auto Wake:从Auto Sleep状态永久唤醒回Enable状态的命令。
  5. Sync Reset:同步复位。此状��只复位状态机,不复位寄存器,行为类似Disable,但在此状态下访问请求不被响应。用于从严重错误中恢复。

电源管理操作流程示例(进入深度睡眠):

// 1. 确保所有进行中的传输完成(软件需协调各主控) // 2. 将SDRAM置于自刷新模式 EMIFB->SDRFC = (EMIFB->SDRFC & ~0x80000000) | 0x80000000; // 设置LP_MODE=1 while(!(EMIFB->SDRFC & 0x80000000)); // 等待自刷新模式确认(根据具体寄存器位) // 3. 使能MCLK停止功能 EMIFB->SDRFC |= 0x40000000; // 设置MCLKSTOP_EN=1 // 4. 通过PSC模块配置EMIFB的LPSC为Disable状态 PSC->MDCTL[EMIFB_MODULE_ID] |= PSC_DISABLE; while((PSC->PTSTAT & (1 << EMIFB_MODULE_ID)) != 0); // 等待模块关闭

6. 实战配置:连接与寄存器计算

理论最终要落地为配置。我们以一个具体的例子来串联所有知识:将EMIFB(运行在133MHz)连接至一颗符合JESD21-C标准的64MB SDR SDRAM芯片(假设为4M x 16bit x 4 banks)。

6.1 硬件连接要点

手册中的图19-11和19-12清晰地展示了16位和32位连接的差异。

  • 数据总线(EMB_D):是点对点连接。32位模式需用两片16位SDRAM并联。
  • 地址/控制总线(EMB_A, EMB_BA, EMB_CS, EMB_RAS, EMB_CAS, EMB_WE, EMB_SDCKE):在多片SDRAM并联时,这些信号是共享的(除了片选CS,在某些扩展情况下可能独立)。
  • 数据掩码(EMB_WE_DQM):用于字节使能,在32位模式下每8位对应一个DQM信号。
  • 时钟(EMB_CLK):必须注意PCB走线的等长控制,以保证时钟与数据/命令信号的时序关系。

6.2 核心寄存器配置计算

配置的核心是四个寄存器:SDCFG,SDRFC,SDTIM1,SDTIM2。所有计算基于133MHz时钟周期(约7.52ns)。

1. SDRAM配置寄存器(SDCFG)根据芯片手册,我们确定参数:

  • NM(数据宽度):0 (32位)
  • CL(CAS延迟):2 (2个时钟周期)
  • IBANK(内部Bank数):2 (4 banks)
  • PAGESIZE(页大小):1 (512 words)
  • TIMUNLOCK:仅在需要修改SDTIM1/2时临时置1,配置完成后应清0。

2. SDRAM刷新控制寄存器(SDRFC)关键计算是REFRESH_RATE。假设SDRAM规格要求64ms(tREF)内刷新8192行。

  • 刷新间隔 = 64ms / 8192 = 7.8μs
  • REFRESH_RATE= 刷新间隔 / EMB_CLK周期 = 7.8μs / 7.52ns ≈ 1037.4
  • 寄存器值需为整数,且必须大于等于计算值,通常向上取整。我们取1038 = 0x40E。
  • 初始不上电管理,故LP_MODE=0,MCLKSTOP_EN=0,SR_PD忽略。

3. SDRAM时序寄存器1(SDTIM1)此寄存器配置SDRAM的主动命令时序。公式统一为:T_XXX = ceil(tXXX / tCK) - 1ceil表示向上取整,因为寄存器值代表最小周期数减1。 以tRCD(行到列延迟)为例,手册值为20ns:

  • T_RCD= ceil(20ns / 7.52ns) - 1 = ceil(2.66) - 1 = 3 - 1 =2

下表为完整计算示例(假设所有时序参数来自某型号SDRAM):

寄存器字段SDRAM参数数据手册值(ns)计算公式寄存器值
T_RFCtRFC66ceil(66/7.52)-1 = 9-18
T_RPtRP20ceil(20/7.52)-1 = 3-12
T_RCDtRCD20ceil(20/7.52)-1 = 3-12
T_WRtWR15ceil(15/7.52)-1 = 2-11
T_RAStRAS44ceil(44/7.52)-1 = 6-15
T_RCtRC66ceil(66/7.52)-1 = 9-18
T_RRDtRRD15ceil(15/7.52)-1 = 2-11

4. SDRAM时序寄存器2(SDTIM2)此寄存器配置自刷新和时钟使能时序。

  • T_XSR(自刷新退出时间):tXSR=75nsT_XSR = ceil(75/7.52)-1 = 10-1 = 9
  • T_CKE(CKE脉冲宽度):tCKE=38nsT_CKE = ceil(38/7.52)-1 = 6-1 = 5
  • T_RAS_MAX:此行激活最长时间,通常是一个非常大的值(如100K个刷新周期),用于超时保护。计算方式不同:T_RAS_MAX = (tRAS_MAX / 刷新间隔) - 1。假设tRAS_MAX=100us,刷新间隔7.8μs,则约为12.8,取整后T_RAS_MAX = 13

6.3 配置代码示例与陷阱

void emifb_sdram_init(void) { volatile uint32_t *emifb_base = (volatile uint32_t*)EMIFB_BASE_ADDR; // 0. 确保EMIFB已退出复位,PLL时钟稳定 // 1. 解锁时序寄存器(如果需要配置SDTIM) emifb_base[SDCFG_OFFSET] = (emifb_base[SDCFG_OFFSET] & ~0x8000) | 0x8000; // 设置TIMUNLOCK=1 // 2. 配置时序寄存器(必须在TIMUNLOCK=1时进行) emifb_base[SDTIM1_OFFSET] = (8 << 25) | (2 << 22) | (2 << 19) | (1 << 16) | (5 << 11) | (8 << 6) | (1 << 3); emifb_base[SDTIM2_OFFSET] = (13 << 25) | (9 << 19) | (5 << 16); // 假设高8位为T_RAS_MAX // 3. 锁定时序寄存器,并同时配置SDCFG其他参数(写SDCFG会触发初始化序列!) uint32_t sdcfg_val = 0; sdcfg_val |= (0 << 14); // NM=0, 32-bit sdcfg_val |= (2 << 9); // CL=2 sdcfg_val |= (2 << 4); // IBANK=2, 4 banks sdcfg_val |= (1 << 0); // PAGESIZE=1, 512 words // TIMUNLOCK位在本次写入时被清0,完成锁定 emifb_base[SDCFG_OFFSET] = sdcfg_val; // 4. 配置刷新控制寄存器 emifb_base[SDRFC_OFFSET] = 0x0000040E; // LP_MODE=0, MCLKSTOP_EN=0, REFRESH_RATE=0x40E // 5. 等待SDRAM初始化序列完成。通常需要延时若干毫秒,或轮询某个状态位(如果提供)。 // 根据具体SDRAM芯片要求,可能需要额外的延时,例如等待200us以上。 delay_ms(1); }

配置陷阱提醒

  • 顺序很重要:必须先解锁(TIMUNLOCK=1),再配SDTIM,最后在配SDCFG其他位时同时锁定(TIMUNLOCK=0)。错误的顺序会导致配置不生效。
  • 触发初始化:对SDCFGNM,CL,IBANK,PAGESIZE等字段的写操作会自动触发硬件初始化序列。软件只需配置,无需模拟SDRAM上电时序。
  • 延时等待:初始化序列需要时间。手册可能未明确说明完成时间,保险做法是参照SDRAM芯片手册的上电初始化要求,在配置后加入足够的软件延时(通常几百微秒到几毫秒)。

7. 调试与问题排查实录

即使配置完全按照手册,在实际硬件上仍可能遇到问题。以下是一些常见故障和排查思路:

问题1:系统在访问SDRAM时随机挂起或数据错误。

  • 排查思路
    1. 时序参数:首先复查SDTIM1/2的所有计算值。确保所有值都满足SDRAM芯片手册的“最小值”要求T_RC必须大于等于T_RAS + T_RP。使用示波器测量EMB_CLK频率是否准确为133MHz?时钟抖动是否在允许范围内?
    2. 硬件连接:检查PCB上SDRAM的电源、参考电压(VREF)是否稳定。用示波器检查命令、地址、数据线的信号完整性,是否存在过冲、振铃或时序裕量不足(Setup/Hold Time Violation)。特别注意时钟线与数据线的等长。
    3. 命令饥饿/竞争:如果挂起发生在多主控频繁访问时,考虑是否触发了命令饥饿。尝试调整BPRIO寄存器中的PRIO_RAISE值。检查软件中共享数据区的访问是否缺少了必要的写同步屏障(dummy write/read)。
    4. 复位与初始化:确认系统启动时,软件是在EMIFB和PLL完全稳定后才进行配置的。可以在配置前后读取REVID寄存器,验证EMIFB模块可正常访问。

问题2:进入低功耗模式后,唤醒系统数据丢失。

  • 排查思路
    1. 自刷新入口:确认在关闭时钟(MCLKSTOP_EN)或进入深度睡眠前,是否成功将SDRAM置入了自刷新模式?检查SDRFC寄存器的LP_MODE位是否已置位,并等待其确认(如果寄存器有状态位)。
    2. 唤醒时序:唤醒后,在让SDRAM退出自刷新模式(LP_MODE=0)之前,是否已稳定提供了时钟并等待了足够时间(满足tXSR)?检查SDTIM2中的T_XSR配置是否正确。
    3. 电源完整性:在低功耗模式下,SDRAM的供电电压是否仍保持在数据保持所需的最小电压以上?检查电源管理芯片的配置。

问题3:性能不达预期,带宽利用率低。

  • 排查思路
    1. 调度器观察:利用EMIFB的性能计数器(PC1,PC2)统计读/写命令的排队周期、Bank冲突次数等。如果Bank冲突频繁,可以考虑优化软件的数据布局,尽量让连续访问分布在不同的Bank上。
    2. 突发长度:确保主控(如DMA、CPU Cache)发起的访问请求使用了最优的突发长度(Burst Length)。EMIFB通常支持最大长度的突发传输,充分利用它。
    3. 仲裁优先级:检查BPRIO寄存器中是否为实时性要求最高的主控设置了合理的优先级。但要注意避免因此导致其他主控被饿死。

调试EMIFB这类底层控制器,三分靠代码,七分靠仪器和耐心。逻辑分析仪或带有高级触发功能的示波器是必备的,可以抓取SDRAM命令总线上的真实时序,与数据手册和寄存器配置进行比对,这是定位硬件时序问题的终极手段。软件层面,养成在关键数据通路添加内存屏障的习惯,以及合理配置优先级提升机制,能从根本上避免许多间歇性故障。