【前言】
MCU不便是单核运行,多核心的MCU大量出现,比如恩智浦的N947就是最近一段时间比较多的,还有STM32的H7x5,H7x7等,当然国产的有ESP32,树莓派的RP2040等。双核之间通过共享内存就可以实现最简单的数据通信,他们之间的同步是基于中断来实现的,今天就MCXN947来实现双核之间的数据交换。
【N947双核简介】
MCX Nx4x系列微控制器将两个Arm Cortex M33内核、一个CoolFlux BSP32、一个PowerQuad DSP协处理器、 一个NPU,以及多个运行频率达150MHz的高速连接功能相结合。为了支持各种应用,MCX N系列配备了高级串行 外设、定时器、高精度模拟和最先进的安全功能。所有的MCX Nx4x产品都包含双区闪存,支持在内部闪存进行 边读边写的操作。MCX Nx4x系列还支持大型外部串行存储器配置。 MCX Nx4x 是 一 个 双 核 微 控 制 器 系 列 MCU 。 CPU0 是 主 Cortex-M33 ( r0p4-00rel0 版 ) 处 理 器 , 支 持 TrustZone-M、浮点单元(FPU)和内存保护单元(MPU)。 MCX Nx4x芯片还包含第二个Cortex-M33实体CPU1,CPU1作为一个副CM33,旨在从主处理器上分担部分工作, 以支持特殊的专用应用。此实体的配置不包括MPU、FPU、DSP、ETM、Trustzone-M、安全属性单元(SAU) 及协处理器接口。两个内核均支持SYSTICK。 Cortex-M33使用两个32位总线接口(代码总线和系统总线),改变了Harvard存储器架构。总线接口按地址范 围激活,且可以在一个特定的总线端口上同时进行指令获取和操作数数据引用。(传统的哈佛架构严格地将指令 获取和操作数数据引用分离,将它们分配到特定的总线端口,不考虑访问地址。)代码总线通常用于指令获取和 对PC相关数据的访问,而系统总线通常用于对片上和片外存储器的操作数数据的引用以及外设访问。此总线结构 完全支持指令获取和数据访问的并发执行,但Cortex-M33的实现可以在每个总线上都生成这两种类型的引用。
【N947双核的基本机制】
CPU0是先启动的主核,作为主内核。CPU1作为从内核,在芯片启动时保持在复位状态,从而最大限度地降低功 耗。因此,要在CPU1中运行或调试应用程序,必须在CPU0上执行一些代码来初始化CPU1。 在双核运行模式下,CPU0和CPU1需要相互通信。MCX Nx 4x提供了一个CPU间的 Mailbox模块,通过使用两种 机制来实现这种通信:
• 中断机制 第一种机制是对另一个CPU触发中断并向其发送一条简单的消息。要对另一个CPU生成中断,可以使用 Cortex_M33(CPU0)中断集(IRQ0SET)或Cortex_M33(CPU1)中断集(IRQ1SET)来设置任意一个中断 标志(最多32个)。另一个CPU读取Cortex_M33(CPU0)中断集(IRQ0SET)和Cortex_M33(CPU1)中 断集(IRQ1SET),以确定它必须执行的操作。如果不止一个IRQn字段为1,则接收中断的CPU会读取所有这些 为1的IRQn字段。接收到中断请求的CPU在完成请求的服务或操作后,在Cortex_M33 (CPU0) Interrupt Clear (IRQ0CLR)或Cortex_M33 (CPU1) Interrupt Clear (IRQ1CLR)的一个或多个字段中写入1来将其清除。 要查看操作是否完成,发送请求的CPU可以检查IRQn,或者接收原始中断的CPU可以使用同一机制来反向通知 发送请求的CPU。用户可根据其具体应用确定Cortex_M33 (CPU0) Interrupt (IRQ0)和Cortex_M33 (CPU1) Interrupt (IRQ1)中每个位的具体含义,并为每个请求位提供附加信息。例如,两个CPU都可以使用预定义的存 储区位置来保存有关中断请求的详细信息。
• 互斥机制 第二种机制是基于单比特资源分配请求的。MUTEX[EX]位定义了一个互斥请求,读取该位可获得资源的状态。 如果此资源是可用的,则可以为读取该寄存器的进程保留该资源。用户可定义该资源的控制,并在获得这个资 源的访问权限后,向MUTEX[EX]写入1,以向其他处理器发出信号,表明共享资源现在可用。有关更多信息,请 参阅《MCX Nx 4x参考手册》中的MUTEX[EX]说明。
总之,CPU间的 Mailbox提供了以下功能:
• 提供处理器间的通信,允许多个CPU以简便的方式共享资源并相互通信。
• 使每个CPU能够生成多达32个用户定义的中断。
• 使每个CPU能够使用MUTEX寄存器位来获取共享资源。
【CPU间的Mailbox模块】
CPU间的 Mailbox是一个AHB从外设,配备一个单时钟域和复位信号。它有一个互斥寄存器,每个CPU有三个32位 的寄存器(IRQ0/1、IRQ0/1SET、IRQ0/1CLR)。
在其数据文档中有图示:
【双核通信的配置】
MAILBOX的初始配置按照以下方式完成:
• 通过向SYSCON AHBCLKCTRLSET寄存器中的位31写入1,来启用MAILBOX模块的时钟。
• 通过向PRESETCTRLCLR0寄存器中写入位31(MAILBOX),清除SYSCON寄存器中的外设复位。
• 在NVIC中启用中断,以便当内核在正常模式下接收到中断时能够进行处理。
/* Init Mailbox */ MAILBOX_Init(MAILBOX); /* Enable mailbox interrupt */ NVIC_EnableIRQ(MAILBOX_IRQn);
在MAILBOX_Init(MAILBOX)中的函数原型如下:
/*! * @brief Initializes the MAILBOX module. * * This function enables the MAILBOX clock only. * * @param base MAILBOX peripheral base address. */ static inline void MAILBOX_Init(MAILBOX_Type *base) { #if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) CLOCK_EnableClock(kCLOCK_Mailbox); #endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ #if !(defined(FSL_FEATURE_MAILBOX_HAS_NO_RESET) && FSL_FEATURE_MAILBOX_HAS_NO_RESET) /* Reset the MAILBOX module */ RESET_PeripheralReset(kMAILBOX_RST_SHIFT_RSTn); #endif }
【双核数据交互】
在初始好MAILBOX之后,CPU0和CPU1就可以通过向另一个内核发送消息来进行通信,这个消息 能自动生成一个中断。该消息包含在32位寄存器IRQn中,并由用户按需确定。因此,该寄存器的每个位都可以进 行不同的配置。向该寄存器的多个位写入1会生成多个中断。
• CPU0向CPU1发送消息
1. CPU0通过将消息写入接收内核(CPU1)的IRQ1SET寄存器,来将消息写入副mailbox寄存器。
注:写入IRQSET寄存器会将1写入IRQ的对应位中。
1. 在CPU1中生成一个可以在mailbox中断处理程序中处理的中断。
2. CPU1读取其IRQ1寄存器中的消息。
3. CPU1通过将值写入IRQ1CLR,来清除IRQ1寄存器中的相应位。
• CPU1向CPU0发送消息
1. CPU1通过向接收内核(CPU0)的IRQ0SET寄存器写入消息,来将消息写入主mailbox寄存器。
注:写入IRQSET寄存器会将1写入IRQ的对应位中。
1. 在CPU0中生成一个可以在mailbox中断处理程序中处理的中断。
2. CPU0读取其IRQ0寄存器中的消息。
3. CPU0通过将值写入IRQ0CLR,来清除IRQ0寄存器中的相应位。
下面的图示展示了双核通信的整个过程:
在用户手册中,IRQ是一个32位的寄存器,可写可读:
双核的通信的数据基础就是通过IRQ这个寄存器来实现,我们可以向这个寄存器写入一个32位的整数据来实现数据交互,当然在32位MCU中,我们也可以向这个寄存器来写入一个约定的指针地址,通过强转,实现更多丰富的数据传输,比如强转为数组,或者是程序的入口。
【体验】
在N947的示例中有两个示例,一个是中断实现双核的数据交换,实现msg的交替相加
示例在driver_examples下的mailbox:
【总结】
通过上面的分析,其实N947的双核通信就明白,通过向IRQ写入一个32位的整数,通过这个整数指向的内存地址,就可以实现丰富的数据交互功能!