十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

STM32MP257 OpenAMP代码生成后编译报错:CubeMX配置与排障全解析

STM32MP257 OpenAMP代码生成后编译报错:CubeMX配置与排障全解析 如果你搜到这篇文章估计已经踩进了同一个坑CubeMX 6.18.1配置STM32MP257OpenAMP组件勾上代码生成成功结果一编译就是几十条错误。我最近在调STM32MP257异构多核主核是两个Cortex-A35跑Linux从核是一个Cortex-M33跑裸机/FreeRTOS核间通信走OpenAMP。CubeMX 6.18.1里把OpenAMP相关组件拖进去点点共享内存然后生成代码——这一步非常顺利代码被生成到了 c:/users/administrator/desktop/apfn 里边。接下来在STM32CubeIDE里导入工程点Build问题就来了头文件找不到、metal_init未定义、内存区域溢出各种错误叠在一起。说得难听一点生成的代码能不能编过感觉就像开盲盒。这篇文章不是简单丢一个能用的补丁而是把“CubeMX生成OpenAMP代码为什么一编译就炸”这件事从头到尾讲清楚生成过程依赖哪些配置、生成出来的目录结构里谁最关键、三种最常见的报错怎么排查以及我最后跑通的稳定构建流程。不管是刚接触STM32MP2的新手还是被编译错误折磨到无语的老手应该都能找到对应的解决思路。嵌入式领域没有银弹硬件型号、CubeMX版本、固件包版本、工具链版本不同错误字面可能不一样但排障思路是一致的。1. 先说背景CubeMX 6.18.1是怎么把OpenAMP代码“拼”出来的1.1 异构多核里OpenAMP到底解决了什么问题STM32MP257属于ST新一代通用微处理器家族架构上是典型的异构多核Cortex-A35负责跑Linux这类复杂系统Cortex-M33负责跑裸机或RTOS去做实时控制。两个核共享同一片DDR但各自有独立的外设视图和中断控制器。要让它们配合干活就必须有个核间通信机制OpenAMP就是这套机制的标准化实现。OpenAMP的核心思路其实不复杂它把“核间通信”拆成三层。底层是共享内存两个核都能读写的一块区域相当于两家公司共用的会议室中间层是硬件mailbox也就是IPCC外设用来传递“我有话要说”的通知相当于会议室里的电话最上层才是rpmsg协议负责把数据分帧、封装、路由让上层应用感觉在收发明文消息一样。这个模型听着简单落到底层工程上非常敏感共享内存放在哪个地址、资源表resource_table放在哪个位置、主核从核各自如何映射这段地址只要有一个对不上编译阶段可能没事但跑起来就串数据。这正是OpenAMP代码编译难的原因所在。它不是一个普通静态库而是和系统内存布局强耦合的一套代码。生成OpenAMP代码时CubeMX要做的事情比普通外设初始化多得多要生成resource_table结构体、要生成rpmsg_config.h按你选的内存参数定义通道数/缓冲区大小、要自动修改链接脚本加入共享内存区域还要把libmetal和libopenamp的源码抄进工程目录。任何一个环节没做好后续构建就会以各种奇怪的方式失败。1.2 CubeMX生成前这几项配置决定了能不能编过用CubeMX 6.18.1做STM32MP257的OpenAMP工程第一步是在芯片选择界面选对具体型号然后进入双核配置视图。重点在于M33这个Context里的配置不是A35侧。很多人习惯性在Linux侧去翻OpenAMP组件翻半天找不到因为OpenAMP的生成主体在Cortex-M33对应的Software Packs里头。在M33的Context里添加OpenAMP组件时务必确认这几个东西一起被带进来libmetal、rpmsg、IPCC驱动。正常情况下它们是依赖关系会自动勾选但如果固件包版本太旧或者本地库里文件不完整OpenAMP组件可能只是半挂状态生成了资源表文件但没有把libopenamp源码带进工程。这个在后面编译报“找不到openamp/rpmsg.h”时是最容易背锅的原因。另一个关键开关是M33侧选Bare Metal还是FreeRTOS。这不是运行时的区别而是影响OpenAMP初始化代码的生成方式。选FreeRTOS时生成代码里会包含vRingBuffer相关的RTOS适配并且在main.c里自动启动rpmsg服务线程选Bare Metal时OpenAMP的轮询和回调就都得自己处理。选择不同编译依赖和链接到的系统文件也不同中途切换容易留下不匹配的残留文件。配置TrustZone也要注意。MP2系列支持TrustZone如果你开了TrustZone安全启动M33的工程会分成安全和非安全两个镜像OpenAMP的资源表和共享内存区域会落在不同安全属性的存储区。链接脚本里会出现SAU/MPU相关的地址配置编译时候链接脚本选错错误绝不是一两行而是大几十行。建议第一次调OpenAMP先不要开TrustZone把通信链路跑通再考虑安全分区。内存配置面板也值得多看一眼。CubeMX里有一个Memory Configuration的视图里面可以看系统自动分配的共享内存地址和大小。OpenAMP组件默认会占用一片DDR区域作为共享内存这个地址同时会被写进resource_table和A35侧的设备树。你可以在CubeMX里改这个地址但改之前要想清楚A35侧设备树里的reg属性必须同步改否则哪怕编译全过Linux起来后也找不到这块内存。编译错误和运行时错误往往就是在这里分叉的。2. 生成出来的工程结构哪些文件才是编译的关键2.1 从生成目录看OpenAMP的组成CubeMX生成OpenAMP代码以后你会看到整个工程目录的形态和普通单片机工程不太一样。以我的 apfn 工程为例生成目录大致长这样apfn/ ├── CM33/ │ ├── CM33.specs │ ├── Core/ │ │ ├── Inc/ │ │ └── Src/ │ │ ├── main.c │ │ └── openamp.c │ ├── openamp/ │ │ ├── libopenamp/ │ │ │ ├── rpmsg_virtio.c │ │ │ ├── rpmsg_core.c │ │ │ └── rpmsg.c │ │ ├── libmetal/ │ │ │ ├── lib/ │ │ │ └── include/ │ │ ├── resource_table.c │ │ ├── rpmsg_config.h │ │ └── openamp.h │ ├── STM32MP257XX_CM33_FLASH.ld │ └── .project ├── CM33_A35/ ├── DeviceTree/ └── CMakeLists.txt注意这里的关键点openamp目录下必须同时有libopenamp和libmetal两个源码目录。libmetal是OpenAMP的底层抽象库负责处理物理内存映射、缓存一致性、原子操作这些跟硬件相关的细节libopenamp才是真正实现rpmsg协议、virtio传输的代码。编译OpenAMP工程这两个库都要参与构建缺一个就等着各种undefined reference。如果你打开生成目录发现里面没有openamp文件夹或者只有resource_table.c没有libopenamp目录那基本可以确定是CubeMX组件的固件包没有下载完整。处理办法不是手动去网上拉代码而是回CubeMX里重新检查OpenAMP组件的版本让它重新解析组件依赖再生成一次。手动下载的代码版本和CubeMX默认配置不一致后面调试会更痛苦。2.2 链接脚本里那几段内存决定了一半的报错OpenAMP工程的链接脚本是编译容易出问题的高发区而且出问题的方式非常隐蔽。以STM32MP257的M33工程为例链接脚本里通常要划分几类区域M33私有的SRAM用来放向量表、关键数据和RTOS内核用来放代码的Flash区域或者从DDR加载代码方式下DDR的一个段以及专门留给共享内存的区域。这里有一个新手很容易踩的误区以为共享内存只是给openAMP库malloc用的随便放哪都行。实际上共享内存的地址必须和A35侧设备树配置严格一致因为它要被Linux内核里的remoteproc驱动解析映射给用户态程序用。在CubeMX生成时共享内存的基地址已经写进了resource_table.c和rpmsg_config.h如果链接脚本里没有单独把这个区域划分出来或者区域的LENGTH设成了0链接器就会把M33的数据段直接铺到这块地址上。编译结果就是一堆“region DDR overflowed”或者“section will not fit”的错误。很多人在网上找解决办法看到有人贴链接脚本就让直接改ld文件加大LENGTH。我不建议直接去改ld。更稳妥的做法是回到CubeMX的Memory Configuration视图里调整内存分配然后重新生成链接脚本。因为CubeMX每次重新生成代码如果检测到ld文件被手动改过很可能直接覆盖掉你改的内容就白干了。而且手改ld只解决编译不等于是解决内存布局的一致性容易埋下运行时的雷。一个简化后的链接脚本示意图如下具体数值每个板子不一样请以CubeMX生成为准MEMORY { M33_SRAM (rw) : ORIGIN 0x30000000, LENGTH 512K FLASH (rx) : ORIGIN 0x2FFC0000, LENGTH 256K DDR_SHMEM (rw) : ORIGIN 0xD0000000, LENGTH 4M } SECTIONS { .resource_table : { . ALIGN(4); KEEP(*(.resource_table)) } DDR_SHMEM }resource_table段单独放是为了让A35侧能找到M33固件里的资源表地址。如果你发现资源表总是被链接器优化掉或者地址不停变化就去检查有没有加KEEP()以及对应的段名是否正确。2.3 编译宏和工具链为什么同一个代码换工具链就炸OpenAMP代码生成时CubeMX是按GNU工具链的路径来组织的。默认的工程配置里会带一组编译宏例如STM32MP257xx、CORE_CM33、USE_OPENAMP如果是FreeRTOS还会加USE_RTOS这类宏。这些宏不是摆设它们直接影响代码里的条件编译分支比如libmetal的原子操作实现在GCC下走__atomic内置函数在ARM Compiler 6下就要换成内联汇编版本。所以一个很常见的场景是同样的源码在STM32CubeIDE里编不过换成命令行arm-none-eabi-gcc反而过了或者反过来。因为IDE的工程配置里自动加了一堆宏和include路径命令行你自己写的CMakeLists并没有完全照搬。如果坚持要我给一个建议那就是先确定你最终用什么工具链跑项目然后在CubeMX的Project Manager里把Toolchain选成一致再去生成代码。不要生成一份GCC的工程拿到Keil里去编译这几乎注定要踩AC6和GCC之间的头文件、宏、内联汇编差异。另外一个跟工具链强相关的是arm-none-eabi-gcc的版本。OpenAMP的libmetal在较新版本中用到了armv8-m架构相关的编译选项。如果GCC版本太低对Cortex-M33的硬件浮点、DSP扩展支持不完整可能在编译libmetal时出一个很奇怪的internal error。所以我建议STM32MP2的开发直接使用STM32CubeIDE自带的GCC工具链不要自己另外折腾一个老版本的arm-none-eabi-gcc省下来的时间足够你调半天Bug。3. 从CubeIDE到命令行两条构建路线3.1 CubeIDE导入工程5分钟跑通CubeMX生成的MP2工程M33子目录下本身是带.project文件的这意味着不需要手动新建工程直接导入即可。操作路径是CubeIDE里 File → Import → General → Existing Projects into Workspace然后选择生成目录里的CM33子目录。导入之后重点检查三个位置。第一是工程属性里的C/C Build → Settings确认当前使用的是STM32CubeIDE自带的GNU工具链通常是stm32cubeide-gcc而不是外部配置的某个编译器。第二是includes路径正常情况下CubeMX已经加好了Core/Inc、openamp/libmetal/include、openamp/libopenamp/include这几条缺一条就按我后面第4章的方法去追。第三是链接脚本在工程属性里的Arm Linker配置里看看有没有选对ld文件。导入后第一次Build很可能会有错误别急。先把错误列表按类型归类确定是宏没定义、头文件找不到、还是链接阶段的符号缺失然后回到设置里去补。IDE的好处是图形界面可以直接看到每条include路径和宏定义适合第一次排障。一个Windows下非常实际的建议把工程放到一个短路径下比如C:\stm32\apfn。CubeMX默认生成到“桌面”这种带空格的路径有些跨工具链的make脚本对空格极其敏感明明路径看着都对就是莫名其妙找不到文件。另外Windows Defender的实时扫描对编译速度影响很大尤其是几万个源文件的OpenAMP工程建议把工程目录加进排除列表。3.2 命令行构建CMake工程怎么组织如果你要在CI里跑构建或者在无图形环境的服务器上编译M33固件那需要自己捋清楚CMake的组织方式。CubeMX对MP2生成的工程老版本可能只给CubeIDE格式6.18.1开始对CMake支持明显好了一些但生成的CMakeLists仍然建议你手工确认一下工具链部分。我自己搭的CMake工程核心结构如下cmake_minimum_required(VERSION 3.20) project(m33_openamp LANGUAGES C ASM) set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_C_COMPILER arm-none-eabi-gcc) add_library(metal STATIC openamp/libmetal/lib/device.c openamp/libmetal/lib/init.c openamp/libmetal/lib/io.c openamp/libmetal/lib/dma.c openamp/libmetal/lib/shmem.c openamp/libmetal/lib/version.c openamp/libmetal/lib/system/generic/condition.c openamp/libmetal/lib/system/generic/irq.c openamp/libmetal/lib/system/generic/sleep.c openamp/libmetal/lib/system/generic/init.c openamp/libmetal/lib/system/generic/io.c ) target_include_directories(metal PUBLIC openamp/libmetal/include ) add_library(openamp STATIC openamp/libopenamp/rpmsg.c openamp/libopenamp/rpmsg_core.c openamp/libopenamp/rpmsg_virtio.c openamp/libopenamp/virtio.c ) target_include_directories(openamp PUBLIC openamp/libopenamp/include ) target_link_libraries(openamp PUBLIC metal) add_executable(m33_openamp.elf Core/Src/main.c Core/Src/openamp.c openamp/resource_table.c ) target_include_directories(m33_openamp.elf PRIVATE Core/Inc openamp ) target_link_libraries(m33_openamp.elf PRIVATE openamp) target_link_options(m33_openamp.elf PRIVATE -T STM32MP257XX_CM33_FLASH.ld -Wl,--gc-sections )注意这里的链接顺序。m33_openamp.elf链接openampopenamp链接metalCMake在处理目标间依赖时会在命令行里生成正确的-lopenamp -lmetal顺序。但如果你是手工写gcc命令而不小心写成-lmetal -lopenamp在旧版本的GNU ld下就可能出现“undefined reference to metal_init” —— 不是因为代码里没有metal_init而是链接器已经扫描完metal库文件还没遇到需要解析的符号。遇到这种符号缺失先查链接顺序永远没错。我用命令行构建时的常用命令cmake -S . -B build -G Ninja cmake --build build --target m33_openamp.elfNinja比MinGW Makefile在Windows上快不少尤其是OpenAMP这种库文件多、编译单元也多的工程。前提是先装好Ninja并确保arm-none-eabi-gcc能在PATH里找到。3.3 构建参数核对表在动手编译之前把下面这张表过一遍能少走很多弯路检查项正确位置常见错误交叉编译器arm-none-eabi-gcc推荐CubeIDE自带版本用了主机gcc或x86 gcc系统名CMAKE_SYSTEM_NAMEGeneric默认宿主系统链接脚本不生效链接脚本指向MX生成的ld文件用了其它型号的ldinclude路径Core/Inc、libmetal/include、libopenamp/include缺一条就找不到头文件编译宏STM32MP257xx、CORE_CM33、USE_OPENAMP宏缺失导致条件编译分支错误链接顺序openamp在metal之前顺序反了出现undefined reference共享内存地址与CubeMX Memory配置一致不一致导致runtime异常但编译过这张表本身就是我最初排障时手写的检查清单后来干脆固化成了流程。每次新建MP2工程先按这个表走一遍再编译成功率能提升一大截。4. 我踩过的三个典型报错以及完整排障过程4.1 错误一fatal error: openamp/rpmsg.h: No such file or directory这个报错出现的场景很典型CubeMX生成代码成功在CubeIDE里打开工程一编译main.c第几行引用的openamp/rpmsg.h直接红灯。看到这个错误第一反应不是去搜这个头文件在哪而是先看看工程里到底有没有这个文件。打开工程目录下的openamp文件夹正常情况下会有libopenamp/include/openamp/rpmsg.h。如果文件在那就是include路径没配好进入工程属性里的 C/C General → Paths and Symbols看Includes列表里有没有openamp/libopenamp/include。如果文件压根不存在那就回到CubeMX检查OpenAMP组件的固件包完整性重新下载对应版本的STM32Cube FW MP2包再生成一次。一个我在Windows上遇到过的特殊变体路径中含有中文或空格导致头文件搜索路径被makefile切碎明明路径存在却还是报No such file。这个浪费了我半小时。解决办法就是把工程移动到纯英文短路径。之后我养成了习惯CubeMX生成工程前先把输出路径设成C:\stm32\project再生成这套问题几乎绝迹。4.2 错误二undefined reference to metal_init / rpmsg_virtio_new这个报错是链接阶段的说明编译已经过了头文件、宏、源文件都能找到但在链接时符号找不到了。第一步去检查libmetal和libopenamp相关源码是否真的被编译进了工程。如果是CubeIDE工程进入项目的Source Location看libmetal和libopenamp目录是否在编译集合里。很多时候因为导入工程时勾选范围不对库源码并没有被加入编译只加了include路径于是头文件找得着符号却编不进去。如果源码确实参与了编译再查链接顺序。手动写CMakeLists时尤其容易犯这个错。GNU ld是单遍扫描链接命令行里库的顺序很讲究。目标文件后面出现的库会被扫描但库之间如果互相引用后面的库不会回头去找前面已经扫过的库。所以在CMake里最稳妥的方式是用target_link_libraries声明依赖关系让CMake自己生成正确的顺序而不是手工去拼一串-l参数。还有一个隐蔽原因libmetal和libopenamp编译时使用的工具链和你主程序的不一致。比如其中一个是用ARM Compiler 6编译出的符号另一个是用GCC编译的双方使用的符号命名规范不同比如符号前缀或类型签名差异链接器就找不到。遇到这种可疑情况直接用nm工具查看生成的静态库里有没有对应符号比如arm-none-eabi-nm build/libmetal.a | grep metal_init如果有符号说明是链接顺序或调用约定的问题如果没有符号说明这个库本身就没编正确。4.3 错误三region DDR_REGION overflowed by xxx bytes / .resource_table will not fit in region这个报错的直观意思是链接脚本里某个内存区域的长度不够了或者是resource_table想要放的区域不存在/长度为零。先说最常见的情况。CubeMX的Memory Configuration里给共享内存分配了4MB那么链接脚本里DDR_SHMEM区域的LENGTH就应该至少是4MB。但如果你在CubeMX里改了共享内存大小生成后ld文件又因为某种原因没有同步更新链接器就会按旧的区域去算导致溢出。这种情况回CubeMX确认Memory配置再重新生成一次基本能解决。另一种情况更隐蔽M33的私有SRAM不够放OpenAMP加FreeRTOS的全部代码链接器把部分初始化数据塞到了共享内存区域然后说共享内存区域溢出。这种场景本质上不是“溢出”而是“你代码量太大该把代码或数据挪到DDR里去”。裸机从SRAM启动没问题但OpenAMPFreeRTOS这种组件组合下把整个M33固件放在内部SRAM通常不太现实。解决思路是调整链接脚本把大段的代码放到DDR上的可执行区域M33内部SRAM只放向量表和RTOS紧缺数据。但这一步必须同步考虑MPU配置否则M33跑在DDR里的代码时会触发总线错误。这个报错在编译期异常显眼因为它会打出整个memory map几百行刷屏。很多新手看一眼就慌。实际上它反而是最好定位的一类问题因为链接器已经把哪些段放不进区域、超了多少字节全打出来了照着memory map里的地址列表去改ld或回CubeMX调内存分配就行。4.4 编译时还容易出现的“非代码问题”OpenAMP工程编译失败有时候跟代码半毛钱关系都没有。我遇到过几次极其离谱的情况展开说说。一次是Windows下编译到一半GCC报internal compiler error: Illegal instruction。刚开始怀疑GCC版本问题重装了三次没用。最后发现是杀毒软件实时扫描把所有临时目录里的.s文件从头到尾扫了一遍导致GCC在调用汇编器时行为异常。把工程目录和C:\Users\user\AppData\Local\Temp加入白名单之后问题消失。一次是编译速度慢到怀疑人生一个工程要编二十多分钟。查下来是工程放在了一台同步网盘目录里每次读写文件都触发云同步。后来把工程从同步目录里移出来编译时间降到三分钟。OpenAMP相关工程源文件多IO密集最怕这种文件系统层面的额外开销。还有一次是链接时报找不到某个.o文件路径看起来没问题最后发现是工程路径中有一个非法字符makefile没有正确转义。这类问题往往报错信息很怪建议第一件事就是把工程路径改成纯字母数字不要带中文、空格、括号、反斜杠混用。路径问题是我在MP2系列上遇到过的最莫名其妙的坑没有之一。
返回列表