I.MX RT1170双核学习(1):双核通信之MU消息单元详解

news2024/12/28 20:52:28

在I.MX RT1170中,它有CM7和CM4核,而消息单元(MU)模块使SoC内的两个处理器能够通过MU接口传递消息以进行通信和协调。

文章目录

  • 1 MU特性
  • 2 功能描述
  • 3 MU通信实例
    • 3.1 轮训实现多核通信
      • 3.1.1 MU_SetFlags和MU_GetFlags
      • 3.1.2 MU_SendMsg和MU_ReceiveMsg
      • 3.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为例进行分析,它实现以下功能:

  1. core 0通过MU模块以轮询模式向core 1发送消息。
  2. core 1通过轮询模式将消息回发给core 0。
  3. 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;
}

这里将GIRnFn的位都清零了,然后根据flag的值再置Fn的位:

在这里插入图片描述

可以看到GIRn是用于中断通知MUA的,这里我们用的是轮询方式,所以清零。对于Fn位来说:

  • Fn的3位分别代表MUBMUA发送的不同标志
  • 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,且此时修改CRFn位也是无效的,我们需要等待其自动清零后才能置标志位。

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;

发送之前我们需要等待对应MUSR寄存器的[23:20]位的TEn(发送寄存器空)标志,四个位就对应四个通道。当消息发送到另一核后,该位会置0,当该位置1时,表示我们可以继续发送数据了。

在这里插入图片描述

  • 上图为MUA寄存器的说明,MUB类似

接着我们只要将数据写入TR寄存器即可,四个通道各有一个32位的TR寄存器:

在这里插入图片描述

来看一下MUA中的TR0寄存器的说明,TR1~TR3类似:

在这里插入图片描述

  • 写入MUATR0寄存器的数据会反映在MUBRR0中,这些寄存器都不是双缓冲的,所以数据会覆盖
  • TR0会清除MUASR中的TE0位,并置MUBSR中的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发来数据后,会置MUBSR中的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_GeneralPurposeRegister20

SRC->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_GeneralPurposeRegister20

while (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_SendMsgNonBlockingMUB发送消息,等这次发送完毕后,再次进入发送空中断则调用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字节后,在下一次进入中断时关闭接收满中断。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1306074.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里云国际版无法远程连接Windows服务器的排查方法

如果您遇到紧急情况&#xff0c;需要尽快登录Windows实例&#xff0c;请参见以下操作步骤&#xff0c;先检查ECS实例的状态&#xff0c;然后通过云助手向Windows实例发送命令或通过VNC登录实例&#xff0c;具体步骤如下&#xff1a; 步骤一&#xff1a;检查ECS实例状态 无论何…

CentOS 7 源码部署 Nginx

文章目录 1. 概述2. 部署示例2.1 下载和解压 Nginx 源码2.2 安装编译依赖包2.3 编译和安装2.4 启动 Nginx2.5 配置防火墙2.6 设置 Nginx 为系统服务2.7 配置访问 3. 扩展知识 1. 概述 Nginx 是一款高性能的开源 Web 服务器软件&#xff0c;广泛应用于互联网领域。本篇博客将介…

pytest-fixtured自动化测试详解

fixture的作用 1.同unittest的setup和teardown,作为测试前后的初始化设置。 fixture的使用 1.作为前置条件使用 2.fixture的的作用范围 1.作为前置条件使用 pytest.fixture() def a():return 3def test_b(a):assert a3 2.fixture的作用范围 首先实例化更高范围的fixture…

Ghidra设置主题和字体大小

文章目录 Ghidra调整主题和字体大小设置主题和全局字体大小调整反汇编页面字体大小 Ghidra调整主题和字体大小 ghidra默认字体小,默认主题白色伤眼,摸索找到了设置主题和字体的方法 设置主题和全局字体大小 启动ghidra后选择edit>theme>configure 下拉框选择主题,右上…

maui下sqlite演示增删改查

数据操作类 有分页 todoitemDatabase.cs&#xff1a; using SQLite; using TodoSQLite.Models;namespace TodoSQLite.Data {public class TodoItemDatabase{SQLiteAsyncConnection Database;public TodoItemDatabase(){}// 初始化数据库连接和表async Task Init(){if (Databa…

科技提升安全,基于YOLOv7【tiny/yolov7/yolov7x】开发构建商超扶梯场景下行人安全行为姿态检测识别系统

在商超等人流量较为密集的场景下经常会报道出现一些行人在扶梯上摔倒、受伤等问题&#xff0c;随着AI技术的快速发展与不断普及&#xff0c;越来越多的商超、地铁等场景开始加装专用的安全检测预警系统&#xff0c;核心工作原理即使AI模型与摄像头图像视频流的实时计算&#xf…

计算机视觉项目实战-驾驶员疲劳检测

&#x1f60a;&#x1f60a;&#x1f60a;欢迎来到本博客&#x1f60a;&#x1f60a;&#x1f60a; 本次博客内容将继续讲解关于OpenCV的相关知识 &#x1f389;作者简介&#xff1a;⭐️⭐️⭐️目前计算机研究生在读。主要研究方向是人工智能和群智能算法方向。目前熟悉深度学…

nrm 的使用 可以快速切换下载(npm)镜像,解决资源下载慢和运行失败

nrm是什么&#xff1f; 介绍 nrm(npm registry manager) 是 npm 的镜像源管理工具. 有时候国外资源太慢,使用 nrm 可以快速的在 npm 源之间切换 安装 npm install -g nrm 基本使用 查看可选择的源 nrm ls 切换到对应的镜像源 nrm use 对应的镜像 删除镜像源 nrm del 名字 …

翻译: LLM大语言模型图像生成原理Image generation

文本生成是许多用户正在使用的&#xff0c;也是所有生成式人工智能工具中影响最大的。但生成式人工智能的一部分兴奋点也在于图像生成。目前也开始出现一些可以生成文本或图像的模型&#xff0c;这些有时被称为多模态模型&#xff0c;因为它们可以在多种模式中操作&#xff0c;…

【Matlab】如何将二阶线性微分方程进行Laplace变换得到传递函数

二阶线性微分方程进行Laplace变换 前言正文代码实现 前言 二阶线性微分方程: 一个二阶线性微分方程通常可以写成如下形式: y ′ ′ ( t ) p ( t ) y ′ ( t ) q ( t ) y ( t ) f ( t ) y^{\prime \prime}(t)p(t) y^{\prime}(t)q(t) y(t)f(t) y′′(t)p(t)y′(t)q(t)y(t)f(…

基于Python+WaveNet+MFCC+Tensorflow智能方言分类—深度学习算法应用(含全部工程源码)(三)

目录 前言引言总体设计系统整体结构图系统流程图 运行环境模块实现1. 数据预处理2. 模型构建1&#xff09;定义模型结构2&#xff09;优化损失函数 3. 模型训练及保存1&#xff09;模型训练2&#xff09;模型保存3&#xff09;映射保存 相关其它博客工程源代码下载其它资料下载…

Redis系列之简单实现watchDog自动续期机制

在分布锁的实际使用中&#xff0c;可能会遇到一种情况&#xff0c;一个业务执行时间很长&#xff0c;已经超过redis加锁的时间&#xff0c;也就是锁已经释放了&#xff0c;但是业务还没执行完成&#xff0c;这时候其它线程还是可以获取锁&#xff0c;那就没保证线程安全 项目环…

文件操作及函数

什么是文件&#xff1f; 在程序设计中&#xff0c;文件有两种&#xff1a;程序文件和数据文件。 程序文件 包括源程序文件&#xff08;.c&#xff09;&#xff0c;目标文件&#xff08;.obj&#xff09;&#xff0c;可执行程序(.exe)。 数据文件 文件的内容不一定是程序&…

mybatis的快速入门以及spring boot整合mybatis(二)

需要用到的SQL脚本&#xff1a; CREATE TABLE dept (id int unsigned PRIMARY KEY AUTO_INCREMENT COMMENT ID, 主键,name varchar(10) NOT NULL UNIQUE COMMENT 部门名称,create_time datetime DEFAULT NULL COMMENT 创建时间,update_time datetime DEFAULT NULL COMMENT 修改…

P1044 [NOIP2003 普及组] 栈——卡特兰数

传送门&#xff1a; P1044 [NOIP2003 普及组] 栈 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P1044 公式一&#xff1a;递推式(注意开 long long &#xff0c;然后 先乘完再除&#xff0c;防止下取整&#xff09; typedef long long ll;…

超过 1450 个 pfSense 服务器因错误链而遭受 RCE 攻击

在线暴露的大约 1450 个 pfSense 实例容易受到命令注入和跨站点脚本漏洞的攻击&#xff0c;这些漏洞如果链接起来&#xff0c;可能使攻击者能够在设备上执行远程代码。 pfSense 是一款流行的开源防火墙和路由器软件&#xff0c;允许广泛的定制和部署灵活性。 它是一种经济高效…

SpringCloud-高级篇(七)

前面在微服务里整合了Seata&#xff0c;下面利用Seata去解决分布式事务的问题&#xff0c;回去学习Seata中的四种解决方案 &#xff1a;首先学习XA模式 &#xff08;1&#xff09;XA模式 RM在前面讲的是资源管理器&#xff0c;在XA标准中RM都是由数据库来实现的&#xff0c;数…

【二分查找】【双指针】LeetCode:2565最少得分子序列

作者推荐 【动态规划】【广度优先】LeetCode2258:逃离火灾 本文涉及的基础知识点 二分查找算法合集 有序向量的二分查找&#xff0c;初始化完成后&#xff0c;向量不会修改。 双指针&#xff1a; 用于计算子字符串是s的字符串的子系列。 题目 给你两个字符串 s 和 t 。 你…

19.java绘图

A.Graphics类 Graphics类是java.awt包中的一个类&#xff0c;它用于在图形用户界面&#xff08;GUI&#xff09;或其他图形应用程序中进行绘制。该类通常与Component的paint方法一起使用&#xff0c;以在组件上进行绘制操作。 一些Graphics类的常见用法和方法&#xff1a; 在组…

Java基础语法之类和对象

类的定义 类就是对一个实体的属性功能进行描述 类的定义格式 首先要用到class关键字&#xff1b; 其次&#xff0c;类名采用大驼峰形式例如Dog ClassName Student…… 最后&#xff0c;类中包含该类的属性&#xff0c;即成员变量&#xff1b;以及该类的功能&#xff0…