ARM926EJ-S处理器核心架构解析:从MMU、缓存到AM1806系统设计实战
2026/7/22 17:15:06 网站建设 项目流程

1. ARM926EJ-S处理器:嵌入式领域的经典计算核心

在嵌入式系统开发领域,尤其是那些对成本、功耗和实时性有严苛要求的工业控制、消费电子和网络设备中,选择一颗合适的处理器核心是项目成败的关键。从业十几年,我经手过不少基于ARM架构的项目,而ARM9系列,特别是ARM926EJ-S,绝对算得上是“老兵”中的常青树。它可能没有Cortex-A系列那么高的主频和炫酷的多核特性,但其稳定、可靠、易于掌控的特性,使其在大量成熟且对成本敏感的产品中依然占据着重要地位。

AM1806这颗由德州仪器(TI)推出的SoC,其心脏正是一颗ARM926EJ-S。它不仅仅是一个CPU,更是一个完整的子系统,包含了缓存、内存管理单元(MMU)、总线接口以及专用的内部存储器。理解这个子系统,是驾驭整个AM1806芯片、进行底层驱动开发、操作系统移植乃至性能优化的基础。本文将带你深入ARM926EJ-S的内核,从指令集、内存管理一直剖析到它与SoC内其他模块如何高效协同工作,其中会穿插大量实际调试和配置中的“干货”与“避坑指南”。

2. ARM926EJ-S核心架构深度解析

ARM926EJ-S属于ARMv5TEJ架构的处理器,是ARM9家族的代表。它的设计目标非常明确:在有限的硅片面积和功耗预算下,为运行多任务操作系统(如Linux、Windows CE)提供足够的性能支撑。其“哈佛架构”设计——即指令和数据拥有独立的总线和缓存,是它高效执行的关键。

2.1 核心组件与总线接口

ARM926EJ-S核心子系统是一个高度集成的模块,其组成远不止一个算术逻辑单元(ALU)。我们可以将其拆解为几个关键部分:

  1. 整数核心(ARM9EJ-S Core):这是执行指令的引擎,采用5级流水线(取指、译码、执行、存储/写回),支持ARM和Thumb指令集,并包含了用于Java字节码加速的Jazelle技术硬件扩展。
  2. 内存管理单元(MMU):这是运行现代操作系统的基石。它负责将程序使用的“虚拟地址”转换为实际的“物理地址”,同时进行内存访问权限检查。AM1806的MMU支持1MB(段)、64KB(大页)、4KB(小页)和1KB(微页)多种页面大小,提供了极大的灵活性。
  3. 缓存与写缓冲区
    • 指令缓存(I-Cache):16KB,4路组相联。用于缓存频繁执行的指令,极大减少从低速外部存储器取指的开销。
    • 数据缓存(D-Cache):16KB,4路组相联。支持“写通”和“写回”两种策略,可由MMU按内存区域配置。写回策略能显著提升对同一地址多次写入的性能。
    • 写缓冲区:包含一个16字(64字节)的数据缓冲区和4个地址缓冲区。当CPU向可缓存或缓冲区使能的内存区域写入数据时,数据会先进入写缓冲区,CPU无需等待实际写入内存完成即可继续执行,从而隐藏存储延迟。
  4. 协处理器15(CP15):这是程序员控制核心功能的关键接口。通过MRC(从协处理器读到ARM寄存器)和MCR(从ARM寄存器写到协处理器)指令,在特权模式下(如SVC、SYS)可以配置和控制MMU、缓存、TCM(尽管AM1806未实现TCM)以及系统功能。
  5. AHB总线接口:ARM926EJ-S通过两个独立的AMBA AHB总线与外界通信——一个用于指令取指(I-AHB),一个用于数据访问(D-AHB)。这种分离进一步避免了指令和数据流在总线上的竞争。在AM1806中,这两个接口最终连接到SoC内部的交换中心资源(SCR)网络。

实操心得:在启动代码或Bootloader中,最早需要操作的往往就是CP15。例如,在使能MMU或缓存之前,必须无效化(Invalidate)整个TLB和缓存,否则可能因为缓存中残留的旧地址映射或数据,导致程序跑飞。一个常见的操作序列是:关闭MMU和缓存 -> 无效化TLB -> 无效化I-Cache和D-Cache -> 配置页表 -> 使能MMU和缓存。

2.2 操作状态与处理器模式

理解处理器的“状态”和“模式”是理解其异常处理和系统调度的前提。

  • ARM状态与Thumb状态:这是指令集宽度状态。ARM状态执行32位定长指令,性能高;Thumb状态执行16位定长指令,代码密度高(通常能减少30%以上)。使用BXBLX指令(目标地址最低位为1)可在两种状态间切换。在实际项目中,我们通常用Thumb状态编译大部分应用代码以节省Flash空间,而用ARM状态编译对性能敏感的中断服务例程或关键算法循环。
  • 处理器模式:ARM有7种运行模式,不同模式拥有独立的堆栈指针(SP)和部分备份寄存器(如R13, R14),这为操作系统提供了硬件级的隔离和保护。
    • 用户模式(USR):非特权模式,运行普通应用程序。
    • 特权模式:包括系统模式(SYS)管理模式(SVC)中断模式(IRQ)快速中断模式(FIQ)中止模式(ABT)未定义模式(UND)。这些模式可以访问所有系统资源,执行特权指令(如操作CP15)。
    • 上电或复位后,处理器进入管理模式(SVC)。应用程序通过发起“软件中断(SWI)”或使用“SVC”指令来触发异常,从而陷入内核态(SVC模式)以请求操作系统服务。

注意事项:FIQ模式有5个额外的私有寄存器(R8_fiq-R12_fiq),这允许FIQ中断处理程序在进入时无需保存这些寄存器,从而实现了极低延迟的中断响应。在设计实时性要求极高的系统时,应将最紧急、最频繁的中断分配给FIQ。IRQ则用于一般中断。

2.3 异常与中断向量表

当发生异常(如中断、非法指令、内存访问错误)时,处理器会强制跳转到固定的内存地址执行,这些地址构成了“异常向量表”。ARM926EJ-S的异常优先级从高到低为:复位 > 数据中止 > FIQ > IRQ > 预取指中止 > 未定义指令 = SWI。

在AM1806中,通过配置CP15的c1寄存器,将VINITHI信号置为高,使得异常向量表基地址位于0xFFFF0000。这个地址映射到了ARM内部的8KB RAM起始处。这是一个非常重要的设计细节!这意味着在系统启动初期,你需要将异常向量表(通常是一系列跳转指令LDR PC, =Handler_XXX)拷贝或编程到这个RAM区域。

表:ARM异常向量表(VINITHI=1时基地址为0xFFFF0000)

偏移地址异常类型进入模式I位状态F位状态典型处理内容
0x00复位(Reset)SVC1(禁用)1(禁用)初始化堆栈、CP15、时钟、内存控制器,跳转到C入口
0x04未定义指令UND1不变报告错误或进行指令模拟(如软件浮点)
0x08软件中断(SWI)SVC1不变系统调用入口,根据SWI号提供内核服务
0x0C预取指中止ABT1不变处理指令取指失败(如MMU权限错误)
0x10数据中止ABT1不变处理数据访问失败(如MMU权限错误、总线错误)
0x14保留通常放置一条死循环指令(B .)以防误跳转
0x18普通中断(IRQ)IRQ1不变通用外设中断服务例程分发入口
0x1C快速中断(FIQ)FIQ11高优先级、低延迟中断服务,通常处理定时器或通信

避坑指南:在编写启动代码时,务必确保在使能任何中断(清除CPSR的I/F位)之前,正确的异常处理程序地址已经就位于向量表中。否则,一旦发生中断,PC会跳转到一个未初始化的地址,导致系统立即崩溃。此外,向量表中的每条指令恰好占4字节,所以通常是一条跳转指令。在RAM中设置向量表比在ROM中更灵活,因为允许动态修改。

3. 内存管理单元(MMU)与地址转换实战

MMU是现代操作系统的守护神。它主要完成两项工作:地址翻译和访问保护。对于运行Linux这类操作系统,MMU是必须开启的。

3.1 地址转换流程与TLB

ARM926EJ-S的MMU采用两级页表转换。CPU核心发出的是虚拟地址(VA),MMU将其转换为修改后的虚拟地址(MVA),主要用于缓存查找(Cache使用MVA作为索引和标签)。最后,MMU通过查询页表,将MVA转换为物理地址(PA),用于访问实际的内存或外设。

转换过程简述

  1. CPU发出虚拟地址(VA)。
  2. VA被转换为MVA(对于单任务系统,通常MVA=VA)。
  3. MMU用MVA查询TLB(快表)。TLB是页表项(PTE)的缓存,命中则直接获得物理地址和权限信息。
  4. 若TLB未命中,则MMU启动“页表遍历”硬件过程,从内存中的两级页表里查找对应的PTE,将其加载到TLB,并完成转换。
  5. 根据PTE中的权限位(AP)和域(Domain)设置,检查此次访问是否合法。若非法,则触发“预取指中止”或“数据中止”异常。
  6. 若合法,则发出物理地址(PA)进行访问。

TLB管理:TLB是性能关键。ARM926EJ-S提供通过CP15寄存器c8来管理TLB的指令:

  • Invalidate Entire TLB:使整个TLB失效。通常在修改页表(如创建新映射、修改权限)后必须执行。
  • Invalidate TLB Entry by MVA:根据MVA使特定的TLB条目失效,更精细。
  • Lockdown:通过CP15寄存器c10,可以将关键的TLB条目锁定,防止被换出,保证实时性。

3.2 页表配置与内存属性

页表条目(PTE)不仅包含物理页帧号,还定义了内存区域的属性,这对缓存和缓冲行为有决定性影响:

  • C位(Cachable)B位(Bufferable)
    • C=1, B=1Write-Back模式。写入操作先到D-Cache,仅当缓存行被替换时才写回内存。性能最高。
    • C=1, B=0Write-Through模式。写入同时更新D-Cache和内存。保证一致性,性能稍低。
    • C=0, B=1Non-cachable but Bufferable。数据不缓存,但可进入写缓冲区。适用于外设寄存器映射的内存区域(如UART的TX寄存器),写入可以缓冲以提高效率。
    • C=0, B=0Strongly-ordered。数据不缓存也不缓冲,访问严格按程序顺序执行。用于必须立即生效的硬件控制寄存器。

实操心得:在配置AM1806的内存映射时,需要特别注意不同内存区域的C/B位设置。例如:

  • 片上RAM(0x8000 0000):通常设置为C=1,B=1(Write-Back),以获得最佳性能。
  • DDR2 SDRAM(0xC000 0000):同样设置为C=1,B=1
  • 外设寄存器区域(如0x01C0 0000开始的CFG空间)必须设置为C=0, B=0C=0, B=1绝对不能设置为可缓存,否则对寄存器的读写可能被缓存或合并,导致无法正确控制硬件,引发难以调试的时序错误。
  • EMIFA/NOR Flash:如果从XIP(就地执行),则指令区域C=1,B=0,数据区域C=0,B=1。如果只是存储数据,则C=0,B=1

4. 缓存与写缓冲区机制详解

缓存是弥补CPU与主存速度差距的关键。ARM926EJ-S的16KB I-Cache和D-Cache采用4路组相联结构,行大小为8字(32字节)。

4.1 缓存工作模式与维护

  • I-Cache:通常是只读的,工作在“直读”模式。当CPU取指未命中时,缓存控制器会从内存读取一整行(32字节)填充缓存。
  • D-Cache:支持两种策略,由MMU页表项的C/B位控制:
    • Write-Through:写命中时,同时更新缓存和主存;写未命中时,可能采用“写分配”或“写不分配”策略(通常由CP15配置)。
    • Write-Back:写命中时,只更新缓存,并将该行标记为“脏”(Dirty);写未命中时,通常采用“写分配”策略,即先加载整行到缓存再修改。当脏行需要被替换时,才写回主存。

缓存一致性维护是嵌入式开发中的难点。在以下场景,必须手动管理缓存:

  1. DMA操作:当外设(如EDMA3)直接与内存交换数据,而该内存区域是可缓存的时,就会出现问题。CPU可能只在缓存中有数据的最新副本,而DMA从内存读到的是旧数据;或者DMA将新数据写入内存,但CPU缓存中的是旧数据。解决方案是:在DMA读取前,清理(Clean)对应内存区域的D-Cache(将脏数据写回内存);在DMA写入后,无效化(Invalidate)对应区域的D-Cache,迫使CPU从内存重新读取。
  2. 自修改代码:如果程序修改了正在执行的指令(如JIT编译),需要先清理D-Cache(确保修改写回内存),然后无效化I-Cache,最后执行一条ISB(指令同步屏障)指令。
  3. 启动初期:在使能缓存前,必须无效化整个I-Cache和D-Cache。

CP15提供了丰富的缓存维护指令(通过c7寄存器操作),可以按虚拟地址(VA)或组/路索引来清理和无效化缓存,非常灵活。

4.2 写缓冲区策略

写缓冲区对于提升写性能至关重要。它像一个先进先出的队列,CPU可以快速将写数据提交给它,然后继续执行,由写缓冲区负责在后台完成对内存或外设的实际写入。

  • 对于可缓存、可缓冲的写回区域:写操作先进入D-Cache。当缓存行被替换时,整行数据(如果脏了)才进入写缓冲区,最终写回内存。
  • 对于不可缓存但可缓冲的区域(如某些外设):每次写操作直接进入写缓冲区。
  • 对于强序区域:写操作绕过写缓冲区,直接访问总线。

常见问题排查:如果你发现对某个外设寄存器的写入操作似乎“延迟”了或者顺序不对,首先要检查该内存区域的B位是否被错误地设为0(强序),或者该外设总线本身是否不支持写缓冲。在某些对时序极其敏感的操作中(如先写命令寄存器再写数据寄存器),可能需要在两次写操作之间插入DSB(数据同步屏障)指令,强制清空写缓冲区,确保前一次写完成后再进行下一次。

5. AM1806系统互联与内存空间规划

AM1806是一个复杂的SoC,ARM926EJ-S作为主控核心,需要通过高效、有序的互联结构与众多外设和存储资源通信。其核心是交换中心资源(SCR)网络。

5.1 系统互联矩阵解析

AM1806的互联结构是一种多层的交叉开关(Crossbar)网络,允许多个主设备(Master)并发地访问不同的从设备(Slave),极大提升了系统整体带宽并降低了延迟。

  • 主设备(Master):能够主动发起读写交易的模块。包括:ARM(通过I/D AHB)、两个EDMA3控制器(共3个传输控制器TC)、HPI、LCD控制器、PRU子系统、USB2.0、uPP、VPIF等。
  • 从设备(Slave):被动响应读写请求的模块。主要是各种存储器和外设寄存器,如:ARM内部RAM/ROM、128KB共享RAM、DDR2控制器、EMIFA接口以及所有外设(如UART、SPI、I2C等)的配置寄存器组。

表:AM1806主要主设备访问权限简表(基于典型配置)

主设备ARM RAM/ROM128KB RAMDDR2EMIFA外设组 (CFG)
ARM (I/D)
EDMA3_0_TC0/TC1
EDMA3_1_TC0
PRU0/PRU1
HPI✓ (部分)
USB2.0, uPP, VPIF
LCDC

从上表可以看出:

  1. ARM内部RAM/ROM是私有的,只有ARM核心和PRU可以访问,这为Bootloader和关键中断向量表提供了安全空间。
  2. 128KB片上RAM和DDR2是共享资源,几乎所有主设备都能访问,是数据交换的主要舞台。
  3. EDMA3是数据搬运的引擎,它可以高效地在内存与外设间、内存与内存间传输数据,解放CPU。
  4. 某些高速外设(如USB、uPP)的DMA只能访问共享RAM和DDR2,不能直接访问低速的EMIFA或外设配置总线,这符合其高带宽特性。

5.2 统一内存映射与地址空间

AM1806采用统一的内存映射,即从所有主设备的视角看,系统中每个物理资源(内存、外设)都出现在一个固定的、统一的地址范围内。这极大简化了软件编程。

关键内存区域划分(举例)

  • 0x0000 0000 - 0x01BF FFFF: 通常未使用或保留。注意:ARM的VINITHI复位后为1,向量表在0xFFFF0000,而非0x00000000
  • 0x01C0 0000 - 0x01FF FFFF:外设配置空间(CFG)。所有外设(如UART、SPI、Timer、PLL等)的寄存器都映射到这个区域。访问这些地址就是读写外设寄存器。
  • 0x8000 0000 - 0x8001 FFFF:ARM内部8KB RAM。通常用于存放异常向量表和关键的栈或数据。
  • 0x8002 0000 - 0x8003 FFFF:ARM内部64KB ROM。可能存放TI的Bootloader或安全启动代码。
  • 0x8000 0000 - 0x8001 FFFF(另一视图):128KB共享片上RAM。这是一个独立的物理内存,地址可能与ARM内部RAM重叠或相邻,具体需查数据手册。它是系统中性能最高的内存,常被用作关键数据缓冲区或实时任务栈。
  • 0xC000 0000 - 0xDFFF FFFF:DDR2/mDDR SDRAM控制器映射区域。这是主程序运行和数据存储的主要区域。
  • 0xE000 0000 - 0xE00F FFFF:EMIFA接口映射区域。连接外部NOR Flash、NAND Flash或异步SRAM。

配置要点:在编写链接脚本(Linker Script)时,必须根据这个内存映射来规划代码和数据的存放位置。例如,.text(代码段)通常放在DDR2中;.data(初始化数据)和.bss(未初始化数据)也放在DDR2;而中断栈或需要极快访问的数据可以放到128KB共享RAM中。Bootloader的初始阶段代码可能需要链接到内部RAM执行。

6. 内存保护单元(MPU)的应用与配置

在复杂的多主设备系统中,防止一个失控的主设备(如某个DMA引擎或协处理器)错误地覆盖关键内存区域至关重要。AM1806集成了两个MPU:MPU1保护128KB共享RAM,MPU2保护DDR2区域。

6.1 MPU工作原理

MPU不像MMU那样使用页表,它通过一组可编程的地址范围寄存器属性寄存器来工作。每个范围可以设置:

  1. 起始地址和结束地址:定义受保护的内存区域。
  2. 允许的访问者ID(AID):AM1806的每个主设备都有一个固定的“特权ID”(见输入材料表5-3)。例如,ARM的ID是0,HPI的ID是3。在MPPA寄存器中,可以为每个范围设置一个位图,指定哪些ID可以访问。
  3. 访问权限:针对“超级用户”和“用户”模式,独立设置读(R)、写(W)、执行(X)权限。

当一个主设备发起访问时,MPU会检查地址落在哪个(或哪些)范围内,然后检查该主设备的ID是否在允许列表中,最后检查访问类型是否符合权限。任何一步失败,都会触发保护错误中断(MPU_PROT_ERR_INT)或地址错误中断(MPU_ADDR_ERR_INT),并记录违规的详细信息(地址、ID、操作类型)。

6.2 实战配置示例

假设在一个系统中,我们需要保护一段位于DDR2中、用于关键通信数据的区域(0xC1000000 - 0xC100FFFF),只允许ARM核心(ID=0)和EDMA3_0_TC0(ID继承自其CC)进行读写,不允许任何设备执行代码,且不允许HPI(ID=3)访问。

配置MPU2(保护DDR2)的步骤可能如下:

  1. 选择一个可编程范围(例如范围0)。
  2. 设置范围起始地址寄存器(MPSAR0)为0xC1000000
  3. 设置范围结束地址寄存器(MPEAR0)为0xC100FFFF
  4. 配置内存保护页属性寄存器(MPPA0):
    • AID0位(对应ARM)置1。
    • AID?位(对应EDMA3_0_CC0/TC0,需查手册确定具体位)置1。
    • AID3位(对应HPI)清0。
    • SR(超级用户读)和SW(超级用户写)置1。
    • SX(超级用户执行)、URUWUX全部清0。
  5. 使能MPU2。

这样,如果HPI试图访问该区域,或者任何设备试图从该区域取指,MPU都会立即产生中断,系统可以在中断服务程序中记录错误、复位外设或采取其他安全措施。

重要提示:MPU的配置通常在系统初始化早期、使能任何DMA引擎之前完成。它是对系统稳定性的最后一道硬件防线。在调试DMA相关的内存覆盖问题时,如果怀疑是非法访问,配置MPU来监控可疑区域是非常有效的定位手段。同时,要留意MPU的粒度(MPU1为1KB,MPU2为64KB),确保保护范围对齐。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询