2. PRU硬件架构与内存映射
PRU(Programmable Real-time Unit)是TI Sitara AM335x/AM57x等SoC内部独立于ARM Cortex-A核的实时协处理器子系统。理解PRU,关键不是把它当成“另一个CPU”,而是把它看成一套紧耦合的实时执行环境:每个PRU核拥有自己的指令RAM、数据RAM、寄存器组和常量表,同时通过共享内存与外设接口和系统其余部分交换数据。本节围绕PRU核心、指令RAM、数据RAM、共享内存、外设接口、中断控制器与常量表七个对象,梳理它们的地址空间与访问关系。
一、PRU核心与双核结构
一个PRU-ICSS(Industrial Communication SubSystem)通常包含两个PRU核,记为PRU0和PRU1。每个核是32位RISC结构,指令字长32位,绝大多数指令单周期执行,主频与PRU子系统时钟一致(AM335x上典型为200MHz,即5ns一个周期)。核内包含通用寄存器R0~R31、程序计数器PC、以及一组用于状态和事件控制的寄存器。
两个核之间不是主从关系,而是对等并行。它们各自独立取指、独立执行,但共享同一片数据RAM和部分外设资源。这种结构决定了PRU编程的两个基本约束:一是实时性由指令周期数直接决定,二是双核共享资源时必须显式处理互斥。例如PRU0负责EtherCAT帧收发,PRU1负责GPIO同步采样,两者通过共享内存中的标志位或事件寄存器协调,而不是靠操作系统调度。
二、指令RAM与数据RAM
每个PRU核有独立的指令RAM,容量通常为8KB(AM335x),用于存放PRU固件。指令RAM在PRU本地地址空间中映射到0x0000_0000起始处,ARM侧则通过PRU_ICSS_IRAM窗口访问。固件加载流程是:ARM将编译生成的二进制镜像写入指令RAM,然后释放PRU核复位,PRU从地址0开始取指。
数据RAM每个核通常为8KB,本地映射到0x0000_0000之外的独立区域(如PRU0为0x0000_0000,PRU1为0x0000_2000,具体以TRM为准)。数据RAM用于存放变量、缓冲区、栈和查找表。由于容量有限,PRU程序必须精打细算:一个EtherCAT过程数据缓冲区可能就占去数KB,剩余空间要留给状态机和临时变量。
三、共享内存与外设接口
共享内存(Shared RAM)是PRU与ARM、PRU与PRU之间交换数据的主要通道,典型容量为12KB,映射在PRU本地地址空间的高端区域(如0x0001_0000附近)。ARM通过L3/L4互连访问同一片物理内存,因此双方看到的是同一份数据。共享内存的访问需要关注两点:一是字节序与对齐,PRU为小端,ARM通常也是小端,但结构体对齐要显式控制;二是一致性,PRU写入后ARM读取前,需通过内存屏障或事件通知确保可见。
外设接口方面,PRU-ICSS集成了专用外设:ECAP、UART、eCAP、MII接口、GPIO以及PRU-ICSS IEP(工业以太网外设)。这些外设通过PRU本地总线映射到固定地址,PRU可以用LBCO/SBCO指令直接读写,而不经过ARM。例如MII的发送FIFO和接收FIFO就映射在PRU地址空间,PRU0可以逐字节搬运以太网帧,实现微秒级确定性收发。
四、中断控制器与常量表
PRU-ICSS的中断控制器(INTC)负责把外设事件、系统事件和PRU核间事件汇聚、映射到PRU的R31寄存器。PRU没有传统意义上的中断向量表,而是通过R31的位域读取中断状态,再用XIN/XOUT指令或条件跳转响应。典型配置是:将MII接收事件映射到R31的某一位,PRU主循环轮询该位,一旦置位就进入收包处理。这种“事件驱动+轮询”的混合模式,比纯轮询更省周期,又比ARM中断更可预测。
常量表(Constant Table)是PRU的一个特色机制,通过LDI指令配合常量表索引,可以快速访问一组预定义的系统资源,如C0~C31。其中C0~C15通常映射到PRU本地寄存器或外设,C16~C31可映射到共享内存或系统事件。常量表的价值在于:用一条指令就能完成对外设寄存器的读写,无需先计算地址再加载,从而压缩关键路径的指令数。
五、地址空间与访问方式的实际含义
把上述对象放到一张地址图上,PRU本地地址空间大致分为三段:低端是指令RAM和数据RAM,中段是外设寄存器,高端是共享内存。PRU用LBBO/SBBO访问本地数据RAM,用LBCO/SBCO访问常量表映射的外设,用普通加载/存储访问共享内存。ARM侧则通过PRU_ICSS_CFG、PRU_ICSS_IRAM、PRU_ICSS_SHARED_RAM等窗口访问同一批资源。
实际工程中,一个常见的初始化顺序是:ARM先使能PRU-ICSS时钟,配置PRU_ICSS_CFG中的PRUSS_CFG寄存器,将PRU固件写入指令RAM,设置共享内存中的初始参数,然后清除PRU_CONTROL中的复位位,让PRU0和PRU1开始运行。PRU启动后,先读取共享内存中的配置结构体,再初始化常量表映射,最后进入主循环。这个顺序如果颠倒,比如PRU先跑而共享内存还没写好,就会出现参数为随机值的问题。
六、常量表与中断的配合
常量表常被用来做“外设快捷方式”。例如把C16配置为指向PRU-ICSS IEP的全局配置寄存器,PRU就可以用LDI R1, C16加偏移的方式读取IEP计数器,而不必先算基地址。中断方面,INTC把多个系统事件映射到R31的位,PRU主循环用QBBS/QBBC判断某位是否置位,再跳转到对应处理段。这种设计让PRU可以在一个循环里同时处理收包、定时和GPIO事件,而不会因为中断嵌套打乱时序。
总结来看,PRU硬件架构的核心是“本地存储+共享内存+常量表外设+事件位”四件套。指令RAM和数据RAM保证执行速度,共享内存解决与ARM的数据交换,外设接口和常量表提供低延迟I/O,INTC和R31提供事件响应。把这四者的地址和访问方式记清楚,后续写PRU固件、调实时性、排查数据不一致时,才能快速定位到是本地RAM、共享内存还是外设映射出了问题。