论坛型网站 建站,手机百度网页版入口,安徽淮北做网站的公司,wordpress 空白页在I.MX RT1170中#xff0c;它有CM7和CM4核#xff0c;而消息单元(MU)模块使SoC内的两个处理器能够通过MU接口传递消息以进行通信和协调。 文章目录 1 MU特性2 功能描述3 MU通信实例3.1 轮训实现多核通信3.1.1 MU_SetFlags和MU_GetFlags3.1.2 MU_SendMsg和MU_ReceiveMsg3.1.…在I.MX RT1170中它有CM7和CM4核而消息单元(MU)模块使SoC内的两个处理器能够通过MU接口传递消息以进行通信和协调。 文章目录 1 MU特性2 功能描述3 MU通信实例3.1 轮训实现多核通信3.1.1 MU_SetFlags和MU_GetFlags3.1.2 MU_SendMsg和MU_ReceiveMsg3.1.3 调试从核注意事项 3.2 中断实现多核通信 1 MU特性
MU包括以下特性
通过中断或轮询进行的消息控制 中断也可用于从低功耗模式唤醒另一处理器允许一个处理器使用中断向另一个处理器发出信号 对称的处理器接口每一侧支持以下功能 四个通用中断请求在另一核中反映三个通用标志在另一核中反映四个带有可屏蔽中断的接收寄存器四个带有可屏蔽中断的发送寄存器 由于CM7和CM4可能使用不同的时钟MU需要确保在传递消息时两侧的访问是同步的以避免数据传输或通信中的时序问题。 MU通过使用两组对应的寄存器来实现这种同步以确保消息的正确传递和处理。
2 功能描述
主要特性描述描述处理器间中断CM7和CM4各有12个中断源用于向另一处理器发出信号。这些中断可用于RX/TX事件的通知和处理器间的通用信号。MU复位处理器A可通过其对应的控制寄存器(ACR)中的控制位(MUR)对整个MU进行复位。MUR位是自清零位。核间状态和控制通信MU提供了一种方式使两个核能够使用两个处理器的状态和控制寄存器进行通信。一个核的状态寄存器反映了另一个核的状态。控制寄存器用于控制操作例如启用中断和向另一处理器发送中断。核间同步消息传输通过同步机制更新两个核各自的传输和接收满标志实现。注意更新其中一个核的寄存器后被另一个核接收到的过程存在延迟。直接访问共享内存和避免冲突MU在两个核都提供了4个传输寄存器和4个接收寄存器。同时两个核可以直接访问SoC的共享内存资源。为了访问共享内存的冲突(互斥)可以使用MU的中断和传输接收寄存器解决这个问题。支持双核不同频率的时钟MU模块的核心是事件控制机制MU制定了事件更新延迟用于同步MU两侧的访问因为两个核可以使用不同的时钟。内存映射寄存器MU连接在双核各自的外设总线上
3 MU通信实例
MU向双核都提供了32位的状态和控制寄存器用于控制操作(如中断、复位)以及检查另一侧的状态。对于消息传递MU在两个处理器都提供了4个32位的只写传输寄存器和4个32位的只读接收寄存器。这些寄存器用于彼此发送消息它们可以使用MU任一侧的控制和状态寄存器中提供的3个通用标志位进行控制。
通过MU一个核可以传递一个32位的消息给另一个核同时触发对方的中断。MU支持4个双向的通道 下面通过SDK中的代码来看一下MU模块如何使用
3.1 轮训实现多核通信
这里以SDK中的evkmimxrt1170_mu_polling_core为例进行分析它实现以下功能
core 0通过MU模块以轮询模式向core 1发送消息。core 1通过轮询模式将消息回发给core 0。core 0通过轮询模式接收来自core 1发送的消息。
这里core 0为CM7(主核)core 1为CM4(从核)。主核使用的MU模块成为MUA从核使用的MU模块为MUB。
主从核实现
下面一步步分析一下主从核代码的执行过程。
3.1.1 MU_SetFlags和MU_GetFlags
先来看一下初始化过程
主核代码流程从核代码流程初始化主核MU时钟:MU_Init(MUA)-从CM7启动CM4核:设置向量表,复位等:APP_BootCore1()-等待从核准备好:while (BOOT_FLAG ! MU_GetFlags(MUA))初始化从核MU时钟:MU_Init(MUB)-设置主核Flag指示从核已经运行:MU_SetFlags(MUB, BOOT_FLAG);
MU_SetFlags
这里主核在启动从核后等待从核置位而从核启动后则调用MU_SetFlags置位。下面来看一下这个函数
void MU_SetFlags(MU_Type *base, uint32_t flags)
{while (0U ! (base-SR ((uint32_t)MU_SR_FUP_MASK))){}MU_SetFlagsNonBlocking(base, flags);
}static inline void MU_SetFlagsNonBlocking(MU_Type *base, uint32_t flags)
{uint32_t reg base-CR;reg (reg ~((MU_CR_GIRn_MASK | MU_CR_NMI_MASK) | MU_CR_Fn_MASK)) | MU_CR_Fn(flags);base-CR reg;
}先来看一下最终调用的MU_SetFlagsNonBlocking函数
#define MU_CR_GIRn_MASK (0xF0000U)
#define MU_CR_NMI_MASK 0U
#define MU_CR_Fn_MASK (0x7U)
#define MU_CR_Fn(x) (((uint32_t)(((uint32_t)(x)) 0)) 7)
static inline void MU_SetFlagsNonBlocking(MU_Type *base, uint32_t flags)
{uint32_t reg base-CR;reg (reg ~((MU_CR_GIRn_MASK | MU_CR_NMI_MASK) | MU_CR_Fn_MASK)) | MU_CR_Fn(flags);base-CR reg;
}这里将GIRn和Fn的位都清零了然后根据flag的值再置Fn的位 可以看到GIRn是用于中断通知MUA的这里我们用的是轮询方式所以清零。对于Fn位来说
Fn的3位分别代表MUB向MUA发送的不同标志Fn位在MU重置(系统初始化或其它条件)的时候会清零或者直接写000也能清零MUA可以通过其SR寄存器的Fn位来获取MuB发送过来的标志 所以这里的MU_SetFlagsNonBlocking实际上就是置CR寄存器的Fn位。我们在程序中将其置为BOOT_FLAG也就是三个标志位的最低位为1。
#define BOOT_FLAG 0x01U另外在从核设置标志位之前需要等待其MUB-SR寄存器的FUP标志位置0来看一下这个位的定义 也就是说如果之前MUB设置的标志位还没有update到MUA中FUP为1且此时修改CR的Fn位也是无效的我们需要等待其自动清零后才能置标志位。
MU_GetFlags
#define MU_SR_Fn_MASK (0x7U)
#define MU_SR_Fn_SHIFT (0U)
static inline uint32_t MU_GetFlags(MU_Type *base)
{return (base-SR MU_SR_Fn_MASK) MU_SR_Fn_SHIFT;
}前面有提到MUA需要从SR寄存器的低三位获取MUB传来的标志位这个函数就是获取SR的低三位。如果BOOT_FLAG相匹配则程序继续往下执行。
3.1.2 MU_SendMsg和MU_ReceiveMsg
主核代码流程从核代码流程发送消息给MUB:MU_SendMsg(MUA, kMU_MsgReg0, g_msgSend[i]);--接收MUA的消息:MU_ReceiveMsg(MUB, kMU_MsgReg0);-回显收到的消息:MU_SendMsg(MUB, kMU_MsgReg0, g_msgRecv[i]);MU_ReceiveMsg(MUA, kMU_MsgReg0);-
我们知道MU有4个双向的通信通道这里就利用通道0进行主从核的通信主核发从核收然后从核回显信息给主核。
MU_SendMsg
void MU_SendMsg(MU_Type *base, uint32_t regIndex, uint32_t msg)
{while (0U (base-SR (((uint32_t)kMU_Tx0EmptyFlag) regIndex))){}base-TR[regIndex] msg;
}typedef enum _mu_msg_reg_index //regIndex的取值,对应4个通道
{kMU_MsgReg0 0,kMU_MsgReg1,kMU_MsgReg2,kMU_MsgReg3,
} mu_msg_reg_index_t;发送之前我们需要等待对应MU的SR寄存器的[23:20]位的TEn(发送寄存器空)标志四个位就对应四个通道。当消息发送到另一核后该位会置0当该位置1时表示我们可以继续发送数据了。 上图为MUA寄存器的说明MUB类似
接着我们只要将数据写入TR寄存器即可四个通道各有一个32位的TR寄存器 来看一下MUA中的TR0寄存器的说明TR1~TR3类似 写入MUA的TR0寄存器的数据会反映在MUB的RR0中这些寄存器都不是双缓冲的所以数据会覆盖写TR0会清除MUA的SR中的TE0位并置MUB的SR中的RF0(接收满)位对TR0寄存器的任何写操作都将更新所有状态信息。
MU_ReceiveMsg
uint32_t MU_ReceiveMsg(MU_Type *base, uint32_t regIndex)
{while (0U (base-SR (((uint32_t)kMU_Rx0FullFlag) regIndex))){}return base-RR[regIndex];
}前面有提到MUA发来数据后会置MUB的SR中的RF0(接收满)位。 所以我们等待RF0位被置位然后获取消息即可。消息从RR寄存器获取同样地四个寄存器对应四个通道 其中RR0寄存器的描述如下 3.1.3 调试从核注意事项
这篇文章就不说明如何调试双核了后面我会写一篇文章来讲解。
这里主要是双核调试有一个问题我们通常首先启动主核初始化系统然后启动次核运行。在同时调试双核的情况下调试器会启动次核。然后在主核初始化尚未完成的情况下次核可能会提前开始运行。
这里我们使用RT1170的SRC(System Reset Controller)中的GPR(General Purpose Register)指示从核是否可以运行。如下图所示这个寄存器对双核都可见除了第0,1,2,3,4,9个GPR被ROM BootLoader使用外其它的我们可以用来设置标志位这里我们使用GPR20。 次核在启动时应检查并等待SRC-GPR中的标志主核在其初始化工作完成时在SRC-GPR中设置该标志。
主核在启动从核后执行以下代码
#define BOARD_SECONDARY_CORE_GO_FLAG 0xa5a5a5a5u
#define BOARD_SECONDARY_CORE_SRC_GPR kSRC_GeneralPurposeRegister20SRC-GPR[BOARD_SECONDARY_CORE_SRC_GPR] BOARD_SECONDARY_CORE_GO_FLAG;从核在上电后执行以下代码
#define BOARD_SECONDARY_CORE_GO_FLAG 0xa5a5a5a5u
#define BOARD_SECONDARY_CORE_SRC_GPR kSRC_GeneralPurposeRegister20while (BOARD_SECONDARY_CORE_GO_FLAG ! SRC-GPR[BOARD_SECONDARY_CORE_SRC_GPR]){} // 等待主核置位
SRC-GPR[BOARD_SECONDARY_CORE_SRC_GPR] 0x0; // 用完后恢复GPR20的初始值0,防止主从核软件复位后,从核又提前运行3.2 中断实现多核通信
和刚刚轮询实现的功能一样我们来学习一下如何使用中断来收发数据。 这里使用中断的方式实现与刚刚轮询代码一样的功能整体代码类似下面来梳理一下中断需要做的操作
主核
1、使能中断
(1)NVIC使能
NVIC_EnableIRQ(MUA_IRQn);(2)使能中断标志位发送和接收中断
MU_EnableInterrupts(MUA, (kMU_Tx0EmptyInterruptEnable | kMU_Rx0FullInterruptEnable));(3)发送和接收数据
我们打开发送空中断后就调用MU_SendMsgNonBlocking向MUB发送消息等这次发送完毕后再次进入发送空中断则调用MU_DisableInterrupts禁用发送空中断。
同样地等从核MUB发来消息后进入接收满中断然后调用MU_ReceiveMsgNonBlocking接收数据等下次接收满时调用MU_DisableInterrupts关闭接收满中断。
#define MSG_LENGTH 32U
void APP_MU_IRQHandler(void)
{uint32_t flag 0;flag MU_GetStatusFlags(MUA);if ((flag kMU_Tx0EmptyFlag) kMU_Tx0EmptyFlag){if (g_curSend MSG_LENGTH){MU_SendMsgNonBlocking(MUA, kMU_MsgReg0, g_msgSend[g_curSend]);}else{MU_DisableInterrupts(MUA, kMU_Tx0EmptyInterruptEnable);}}if ((flag kMU_Rx0FullFlag) kMU_Rx0FullFlag){if (g_curRecv MSG_LENGTH){g_msgRecv[g_curRecv] MU_ReceiveMsgNonBlocking(MUA, kMU_MsgReg0);}else{MU_DisableInterrupts(MUA, kMU_Rx0FullInterruptEnable);}}SDK_ISR_EXIT_BARRIER;
}从核
整体流程和轮询代码一致另外和主核一样要打开对应的中断现在来看看MUB的中断回调函数
void APP_MU_IRQHandler(void)
{uint32_t flag 0;flag MU_GetStatusFlags(APP_MU);if ((flag kMU_Rx0FullFlag) kMU_Rx0FullFlag){if (g_curRecv MSG_LENGTH){g_msgRecv[g_curRecv] MU_ReceiveMsgNonBlocking(MUB, kMU_MsgReg0);}else{MU_DisableInterrupts(MUB, kMU_Rx0FullInterruptEnable);}}if (((flag kMU_Tx0EmptyFlag) kMU_Tx0EmptyFlag) (g_curRecv MSG_LENGTH)){if (g_curSend MSG_LENGTH){MU_SendMsgNonBlocking(MUB, kMU_MsgReg0, g_msgRecv[g_curSend]);}else{MU_DisableInterrupts(MUB, kMU_Tx0EmptyInterruptEnable);}}SDK_ISR_EXIT_BARRIER;
}同样地在使能发送空中断后这里的中断就一直会被调用但是这里的发送空分支中还判断了(g_curRecv MSG_LENGTH)也就是MUB接收了MUA发送的完整的MSG_LENGTH(32)字节才允许进入这个分支进入后将收到的数据回显给MUA然后在下一次进入发送空中断时关闭中断。
对于接收满中断来说一样收到MSG_LENGTH字节后在下一次进入中断时关闭接收满中断。