当前位置: 首页 > news >正文

痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里实现可靠Flash IAP的方法


  大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是双核i.MXRT1180下XIP应用里实现可靠Flash IAP的方法

  近期有一个 RT1180 客户在咨询关于双核应用下 Flash IAP 实现的问题,其应用场景是:主核 CM33 运行在 FlexSPI XIP Flash 代码区域,从核 CM7 运行在 ITCM 里且需要对同一颗 Flash 的数据区域进行 IAP 操作。因为目前大部分 NOR Flash 都不支持 Read-While-Write 特性,因此在从核 CM7 执行 IAP 操作过程中,Flash 不能够被读访问,这时候我们就需要特别保证主核 CM33 程序执行不出问题。这显然是一个很有意思的话题,痞子衡今天就来和大家深入讨论一下解决方案。

  • Note:本文虽以 RT1180 为例,但所述方法对其他双核芯片(如 RT1170、RT700)也同样适用。

一、双核通信的三种方法

  因为今天这个话题涉及双核架构,有必要先简单和大家谈一谈双核间通信的三种方式及其优缺点,注意这里仅指双核间传递消息数据(侧重通知),而不涉及所谓信号量 semaphore 概念(侧重互斥,比如两个核抢同一个“硬件锁寄存器”,谁先抢到,谁就独占某个共享资源)。

1.1 共享内存

  第一种方法就是最常见的共享内存,只要是两个核均能访问的 SRAM,Register 等均可以用作消息数据传递。比如定义如下结构体 g_sharedMsg 存储两个 msg 数据,core0 发出的消息存入 core0_msg,core1 发出消息存入 core1_msg,在两个核的工程链接文件里,均将 .shared_mem 段指向同一个物理地址即可(需要是 Non‑Cacheable 属性的内存,且是 4 字节对齐地址)。

// 对 ARM Cortex‑M 来说,4 字节对齐的 uint32_t 读写是原子的,不会出现“读到一半新、一半旧”的撕裂问题
typedef struct _shared_msg
{uint32_t core0_msg;uint32_t core1_msg;
} shared_msg_t;__attribute__((section(".shared_mem")))
volatile shared_msg_t g_sharedMsg;

  这种方法的优点是非常通用,不依赖任何硬件,且消息数据量不限(取决于内存大小);缺点是消息数据需要内核以 polling 方式获取,消息交互实时性不高。

1.2 硬件MU模块

  第二种方法就是借助专用于双核通信的 Messaging Unit (MU) 模块,这是一个硬件模块,双核 MCU 里一般都会有,其提供了中断驱动的消息传递机制。这种方法的优点是实时性最佳,消息交互通过中断驱动,不需要内核去 polling;缺点是依赖专用硬件模块,且消息寄存器数量有限,比如 RT1180 上 MU 一次最多传递 4 个 uint32_t 数据。

1.3 硬件通信模块

  最后一种方法就是借助一般通信外设模块,比如 GPIO/UART/SPI/I2C 等,两个内核各控制一个通信外设,片外通过 pin 脚将两个外设相连。这种方法的优点是实时性也不错,消息交互也可通过通信外设自身中断驱动,并且消息数据量也不限;缺点是消耗硬件通信外设,且占用外部 pin 脚。

二、MU模块及其驱动简介

  综合比较,本文选取了第二种方法即借助于片内专用 MU 模块实现双核通信,下面简单介绍一下 MU 模块的基本特性和驱动用法。RT1180 内部共有两个 MU 模块,每个 MU 内部包含两个子模块 MUA 和 MUB,分别对应主核 CM33 (Processor A)和从核 CM7 (Processor B),MUA 与 MUB 子模块之间通过内部总线相连。

  每个 MUA/B 模块均提供了 4 个 32-bit 的消息寄存器(发送通过 TR0-3,接收通过 RR0-3)用于数据传递,以及相应的中断机制。比如当 Processor A 向 MUA_TR0 写入数据时,Processor B 可以通过 MUB_RR0 读取该数据,同时会触发 Processor B 的中断;反之亦然。

  下面是一个基于 SDK v25.12 里 fsl_mu 驱动代码的简单示例,两个内核各自初始化自己的 MU 子模块,这里定义了两个 MU_CMD,通过 MUx_TR0/RR0 交互,CM7 先通过 MUB 发出 MU_CMD_ECHO 消息,CM33 通过 MUA 中断得到该消息后立刻返回 MU_CMD_ACK 消息,CM7 收到返回消息即结束。

#include "fsl_mu.h"
typedef enum {MU_CMD_ECHO  = 0xA5A50001,  // CM7 -> CM33MU_CMD_ACK   = 0xA5A50002,  // CM33 -> CM7
} mu_cmd_t;// 下列代码应用于 CM33 工程
void mc_cm33_init(void)
{MU_Init(MU1_MUA);MU_EnableInterrupts(MU1_MUA, kMU_Rx0FullInterruptEnable);NVIC_EnableIRQ(MU1_IRQn);
}
void MU1_IRQHandler(void)
{uint32_t flag = MU_GetStatusFlags(MU1_MUA);if ((flag & kMU_Rx0FullFlag) == kMU_Rx0FullFlag){uint32_t msg = MU_ReceiveMsgNonBlocking(MU1_MUA, kMU_MsgReg0);if (msg == MU_CMD_ECHO){MU_SendMsgNonBlocking(MU1_MUA, kMU_MsgReg0, MU_CMD_ACK);}}
}// 下列代码应用于 CM7 工程
void mc_cm7_init(void)
{MU_Init(MU1_MUB);MU_EnableInterrupts(MU1_MUB, kMU_Rx0FullInterruptEnable);NVIC_EnableIRQ(MU1_IRQn);MU_SendMsgNonBlocking(MU1_MUB, kMU_MsgReg0, MU_CMD_ECHO);
}
void MU1_IRQHandler(void)
{uint32_t flag = MU_GetStatusFlags(MU1_MUB);if ((flag & kMU_Rx0FullFlag) == kMU_Rx0FullFlag){uint32_t msg = MU_ReceiveMsgNonBlocking(MU1_MUB, kMU_MsgReg0);if (msg == MU_CMD_ACK){// Do something}}
}

三、双核管理驱动MCMGR用法

  由于 MU 模块仅仅是最底层的裸消息数据传输,不带协议、不带缓冲、不带管理,这里不建议直接在应用程序里大量使用,因为代码易写错、维护成本高。NXP 官方提供了一个更高级的双核管理驱动 MCMGR(Multicore Manager),它是对 MU 模块的进一步封装,提供了更加便捷的双核通信接口。

  我们将上一节里的代码示例实现用 MCMGR 驱动改写,这时候完全不用碰 MU 寄存器与驱动,代码变得更加清晰易读。MCMGR 驱动的核心是事件(Event)机制,每个事件都有一个 ID 类型和关联的回调函数,当一个核向另一个核发送事件时,接收端会触发相应的回调函数。

#include "mcmgr.h"#define MCMGR_EVENT_ECHO   (1U)  // CM7 -> CM33
#define MCMGR_EVENT_ACK    (2U)  // CM33 -> CM7// 下列代码应用于 CM33 工程
void mc_cm33_init(void)
{(void)MCMGR_Init();MCMGR_RegisterEvent(kMCMGR_RemoteApplicationEvent,cm33_mc_cb,NULL);
}
static void cm33_mc_cb(mcmgr_core_t coreNum, uint16_t eventData, void *context)
{if (eventData == MCMGR_EVENT_ECHO){MCMGR_TriggerEvent(kMCMGR_Core1, kMCMGR_RemoteApplicationEvent, MCMGR_EVENT_ACK);}
}// 下列代码应用于 CM7 工程
void mc_cm7_init(void)
{(void)MCMGR_Init();MCMGR_RegisterEvent(kMCMGR_RemoteApplicationEvent,cm7_mc_cb,NULL);MCMGR_TriggerEvent(kMCMGR_Core0, kMCMGR_RemoteApplicationEvent, MCMGR_EVENT_ECHO);
}
static void cm7_mc_cb(mcmgr_core_t coreNum, uint16_t eventData, void *context)
{if (eventData == MCMGR_EVENT_ACK){// Do something}
}

  MCMGR 驱动一共定义了 9 种不同类型的事件,其中 kMCMGR_RemoteApplicationEvent 是用于用户自定义跨核事件的唯一正确类型。

typedef enum _mcmgr_event_type_t
{kMCMGR_RemoteCoreUpEvent = 1,       // 内部事件,核状态管理kMCMGR_RemoteCoreDownEvent,         // 内部事件,核状态管理kMCMGR_RemoteExceptionEvent,        // 内部事件kMCMGR_StartupDataEvent,            // 启动数据kMCMGR_FeedStartupDataEvent,kMCMGR_RemoteRPMsgEvent,            // RPMsg 专用,IPCkMCMGR_RemoteApplicationEvent,      // 唯一用户事件kMCMGR_FreeRtosMessageBuffersEvent, // 系统用,Buffer 回收kMCMGR_EventTableLength
} mcmgr_event_type_t;

四、XIP应用里实现Flash IAP的方法

  前面铺垫了那么多,看到这里相信你肯定已经对这个客户需求如何解决有了答案,其实也不复杂。我们可以直接基于 SDK multicore_examples/hello_world 例程来修改实现,该例程 CM33 是 XIP,CM7 在 ITCM 执行,跟客户情况一致,我们只需要在此基础上加上 MCMGR 和 IAP 代码,核心思想是 CM7 做 Flash IAP 之前必须先给 CM33 发 notify 消息让其跳转到 RAM 里驻留等待(脱离 XIP),得到 CM33 发来的 ready 消息时,CM7 才能真正开始做 Flash IAP,等 IAP 结束 CM7 再通知 CM33 重返 XIP,这里最大的注意点是 CM33 端 RAM 驻留函数设计与 FlexSPI 状态清理。

CM7 端:> MCMGR_EVENT_FLASH_IAP_NOTIFY:通知CM33即将做IAP< MCMGR_EVENT_FLASH_IAP_READY:得到来自CM33的ready信号后开始做IAP> MCMGR_EVENT_FLASH_IAP_PASS:IAP成功后返回结果给CM33> MCMGR_EVENT_FLASH_IAP_FAIL:IAP失败后返回结果给CM33CM33 端:< MCMGR_EVENT_FLASH_IAP_NOTIFY:当接收到来自CM7的notify信号后,清除FLEXSPI cache等待bus idle> MCMGR_EVENT_FLASH_IAP_READY:此时已驻留进RAM loop中,给CM7发ready信号< MCMGR_EVENT_FLASH_IAP_PASS:得到来自CM7的IAP成功结果,直接重返XIP< MCMGR_EVENT_FLASH_IAP_FAIL:得到来自CM7的IAP失败结果,需重新初始化FLEXSPI重返XIP(TBD)

  具体实现代码如下,目前主要支持 RT1180,未来计划把 RT1170 和 RT700 也加上。

  • 项目地址:https://github.com/JayHeng/func-imxrt-flexspi-dual-core-flash-operation

  至此,双核i.MXRT1180下XIP应用里实现可靠Flash IAP的方法痞子衡便介绍完毕了,掌声在哪里~~~

欢迎订阅

文章会同时发布到我的 博客园主页、CSDN主页、知乎主页、微信公众号 平台上。

微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。

http://www.jsqmd.com/news/657601/

相关文章:

  • 终极指南:5步将Deebot扫地机器人接入Home Assistant实现智能家居控制
  • 《数据库系统概论》实战解析:从DAC到MAC,构建企业级数据安全防线
  • 从零开始:使用VT2710板卡实现RS485通信的完整流程(含代码示例)
  • 5分钟上手gprMax:FDTD电磁仿真与地质雷达模拟完整指南
  • 3步解锁Windows 10/11的HEIC缩略图预览功能:告别iPhone照片的空白图标
  • 国内订阅 Claude Pro:用 Apple 礼品卡走 iPhone 内购的实践记录
  • 【笔试真题】- 电信-2026.04.11
  • FastAdmin Shopro与uni-app分销商城的功能定制与二次开发详解
  • 基于模块化解析架构的B站多媒体资源批量下载方案
  • 手把手教你用Wireshark抓包分析CPRI/eCPRI协议:从光模块信号到IQ数据映射实战
  • C++20 线程管理新选择:从 std::thread 到 std::jthread 的实战迁移指南
  • 工控机与GPIO:工业控制系统的“神经末梢”与“大脑”协同
  • S32K3 MCAL实战:手把手教你改造LPUART中断,搞定BLE/WiFi模组不定长数据接收
  • Java开发者必看!转型AI,薪资翻倍,学习路线全解析!
  • cv_unet_image-colorization镜像标准化:符合OCI规范,支持Kubernetes集群化部署
  • 别再让net::ERR_INCOMPLETE_CHUNKED_ENCODING中断你的数据导出!Spring Boot + Nginx实战排查指南
  • 避坑指南:在Cadence里做拉扎维习题仿真时,DC、AC和Tran仿真电源设置千万别搞混
  • Oracle学工系统SQL注入实战:从WAF拦截到SRC漏洞挖掘
  • nli-distilroberta-base生产环境:中小企业低成本部署NLI服务的完整方案
  • 我转行AI大模型了!从推荐算法到AI大模型:30岁工程师的转行抉择与高薪机遇!
  • 【表面粗糙度】基于粒子群PSO算法优化-BP神经网络的表面粗糙度研究附Matlab代码
  • 北京伯爵官方售后网点2026年4月核验报告(实地模拟考察版) - 速递信息
  • Matlab自动化技巧:利用M脚本批量清理Simulink模型中的无效模块与悬空信号线
  • Spring事务事件监听:@TransactionalEventListener的实战场景与核心机制剖析
  • 别再只爬静态数据了!从QQ音乐vKey获取,聊聊如何应对前端加密的API
  • Unity_脚本驱动Spine动画状态与皮肤动态切换实战
  • NLP 词嵌入:从Word2Vec到BERT 技术演进与实践
  • STM32+SHT30温湿度传感器实战:手把手教你用IIC通信实现环境监测
  • 失业了可以死磕的网站
  • netdisk-fast-download如何提升你的下载速度