2、多核架构基础:CPU子系统与存储体系

各位同学,今天我们来聊聊AURIX多核架构里最核心的部分——CPU子系统和存储体系。说实话,我第一次接触TC1.6和TC1.8内核时,也被那一堆缩写搞得头晕:PMU、DSPR、DLMU、SPM、SRI、SPB、LMB……嗯,别急,我们一个一个拆开看。

我个人习惯把多核架构想象成一个团队协作的场景。每个CPU核心就像一名工程师,而存储器和总线系统就是他们的办公桌和文件传递通道。搞懂了这些,你写代码时心里就有底了。

2.1 CPU子系统:TC1.6与TC1.8内核

AURIX家族里,TC1.6和TC1.8是两代核心。TC1.6是经典款,TC1.8是升级版。我在项目中遇到过不少选型困惑,这里直接说结论:

  • TC1.6:32位架构,主频最高300MHz,支持单精度浮点。适合大多数工业控制场景。
  • TC1.8:同样是32位,但主频能到400MHz,增加了双精度浮点单元。如果你做伺服驱动或机器人控制,我建议优先考虑TC1.8。

为什么会这样?因为TC1.8的流水线深度做了优化,分支预测更准。说白了,就是跑循环和条件判断时,它比TC1.6少浪费几个时钟周期。别小看这几个周期,在1ms的控制周期里,可能就是生与死的区别。

核心要点:每个CPU核心都有自己的Trap处理单元和中断控制器。多核编程时,中断亲和性设置是个坑——我曾经把两个核心的中断绑到了同一个优先级上,结果系统直接死锁。嗯,后来我学乖了,每个核心的中断向量表独立配置。

2.2 程序存储器:PMU与Flash

程序存储器这块,PMU(Program Memory Unit)是Flash的控制器。你想想看,Flash本身读写速度慢,但CPU又需要快速取指令,怎么办?PMU就是中间的缓冲层。

我记得第一次调Flash读取时序时,发现代码跑飞了。查了半天,原来是PMU的预取缓冲没开。默认情况下,PMU的预取是关闭的,你需要手动配置寄存器。代码示例如下:

/* 使能PMU预取缓冲 */
IfxPmu_enablePrefetch(&MODULE_PMU, TRUE);

/* 配置等待状态:根据主频调整 */
IfxPmu_setWaitstate(&MODULE_PMU, 2);  /* 2个等待周期 */

这里有个避坑指南:千万不要在Flash操作期间执行中断服务函数。我曾经在擦除Flash扇区时,一个中断进来,直接导致擦除失败,数据全乱。后来我都是把Flash操作放在临界区里,关中断执行。

2.3 数据存储器:DSPR、DLMU、SPM

数据存储器这块,AURIX的设计很有意思。它把内存分成了好几块,各有各的用途:

存储器类型 容量(典型值) 访问速度 用途
DSPR(本地数据RAM) 64KB~128KB 1个时钟周期 每个核心私有,存栈和局部变量
DLMU(本地内存单元) 32KB~64KB 2个时钟周期 多核共享数据,带硬件一致性
SPM(共享程序内存) 8KB~16KB 1个时钟周期 存放中断向量和关键代码

我个人习惯把实时性要求高的变量放在DSPR里。比如电流环的PI参数,我直接定义到DSPR段:

/* 将变量分配到DSPR */
__attribute__((section(".data.dspr"))) float pi_kp = 1.5f;
__attribute__((section(".data.dspr"))) float pi_ki = 0.01f;

DLMU呢?它适合放多核之间需要交换的数据。但要注意,DLMU虽然带硬件一致性,但写操作还是需要加锁。我见过有人直接用DLMU做无锁队列,结果在高负载下数据错乱——嗯,硬件一致性只保证单个变量的原子性,不保证复合操作的原子性。

小技巧:SPM虽然小,但速度极快。我把中断服务函数和临界区代码都放在SPM里。这样中断响应时间能缩短30%以上。配置方法很简单,链接脚本里指定段即可。

2.4 总线系统:SRI、SPB、LMB

总线系统是AURIX的血管。三条总线各司其职:

  • SRI(系统资源互联):64位宽,主总线。连接CPU、Flash、DMA等高速设备。说白了,这是高速公路。
  • SPB(外设总线):32位宽,连接各种外设(CAN、SPI、I2C等)。这是城市道路。
  • LMB(本地内存总线):每个核心私有,连接DSPR和SPM。这是小区内部路。

我在项目中遇到过总线冲突的问题。两个核心同时访问同一个外设寄存器,结果SPB总线仲裁导致其中一个核心被阻塞了200ns。200ns啊,在1MHz的PWM周期里,这误差足以让电机抖动。解决方案?把外设访问分散到不同的时间片,或者用DMA来搬运数据。

警告:SRI总线支持多主设备并发访问,但不要让两个核心同时写同一个32位变量。虽然SRI有原子操作指令,但如果你用普通的store指令,数据可能被撕裂。记住:多核共享变量,一定要用原子操作或锁。

2.5 DMA控制器

DMA控制器,说白了就是数据搬运工。AURIX的DMA有64个通道,支持链式传输和循环模式。我一般用它来做三件事:

  1. ADC结果搬运:ADC转换完,DMA自动把结果搬到内存,CPU只管算。
  2. PWM比较值更新:DMA定时把新的占空比数据写到PWM寄存器,实现无抖动更新。
  3. 通信数据收发:CAN或SPI的数据,DMA直接搬,CPU零参与。

配置DMA其实不难,但有个细节要注意——传输宽度对齐。我曾经配了一个8位到32位的传输,结果数据错位了。后来发现DMA的源地址和目标地址必须按传输宽度对齐。代码示例:

/* DMA通道配置:从ADC结果寄存器搬运到内存 */
IfxDma_Dma_ChannelConfig dmaConfig;
IfxDma_Dma_initChannelConfig(&dmaConfig, &MODULE_DMA);

dmaConfig.transferCount = 8;          /* 传输8次 */
dmaConfig.srcAddress = (uint32)&ADC_RESULT;
dmaConfig.destAddress = (uint32)&adcBuffer;
dmaConfig.srcAddressCircular = FALSE;
dmaConfig.destAddressCircular = TRUE; /* 循环缓冲区 */
dmaConfig.transferSize = IfxDma_ChannelTransferSize_32bit;

IfxDma_Dma_initChannel(&dmaChannel, &dmaConfig);

经验之谈:DMA中断不要开得太频繁。我见过有人每个DMA传输完成都进中断,结果CPU被中断淹没了。建议用DMA的链式传输,一次配置多个传输块,只在最后一个块完成时触发中断。这样效率高得多。

知识体系总览

下面这张图是我自己画的,把整个多核架构的存储和总线关系理清楚了。你保存下来,写代码时对照着看:

AURIX多核架构存储与总线关系图 CPU0 (TC1.6/1.8) LMB总线 CPU1 (TC1.6/1.8) LMB总线 CPU2 (TC1.6/1.8) LMB总线 DSPR0 SPM0 DSPR1 SPM1 DSPR2 SPM2 SRI总线(64位,主总线) DLMU(共享数据RAM) PMU + Flash(程序存储) DMA控制器 SPB总线(32位,外设总线) CAN SPI I2C GPT ADC CPU核心 私有存储器 SRI总线 共享资源 SPB总线 外设

好了,这一章的内容就到这里。记住:多核编程的核心就是理解每个核心能访问什么、不能访问什么,以及数据怎么在核心之间流动。下一章我们会深入讲中断系统和Trap处理,那才是真正考验多核设计能力的地方。


专注资料整理