文章目录1 概要2 存储和系统资源分配2.1 存储分配2.2 系统资源分配3 总结4 其他章节1 概要使用正点原子的stm32mp157mini进行RPMSG多核通信试验。硬件STM32MP157软件STM32CubeIDE / STM32CubeMX远程处理器Remoteproc框架负责根据固件资源表中可用的信息激活 Linux 端的进程间通信IPC本节我们来分析 Linux 下的 Remoteproc 相关驱动了解 Remoteproc 是怎样控制远程处理器的。本小节的内容会涉及到分析代码会比较枯燥乏味如果只想了解 Remotepro使用方法可以直接看本章的最后一小节。本章分为如下几部分(文章末尾会附上链接)资源表存储和系统资源分配Linux 下 Remoteproc 相关 API链接脚本Remoteproc 的使用2 存储和系统资源分配在前面我们介绍了 ST 对 STM32MP157 资源的分配情况下面我们来看在设备树下是怎么配置的我们重点关注 M4 的存储和外设资源分配。2.1 存储分配我们先来了解在设备树下是怎么分配 M4 的存储地址的在 stm32mp157d-atk.dtsi 设备树下可以看到这段代码reserved-memory{#address-cells1;#size-cells1;ranges;mcuram2:mcuram210000000{compatibleshared-dma-pool;reg0x100000000x40000;no-map;};vdev0vring0:vdev0vring010040000{compatibleshared-dma-pool;reg0x100400000x1000;no-map;};vdev0vring1:vdev0vring110041000{compatibleshared-dma-pool;reg0x100410000x1000;no-map;};vdev0buffer:vdev0buffer10042000{compatibleshared-dma-pool;reg0x100420000x4000;no-map;};mcuram:mcuram30000000{compatibleshared-dma-pool;reg0x300000000x40000;no-map;};retram:retram38000000{compatibleshared-dma-pool;reg0x380000000x10000;no-map;};};第 1 行的 reserved-memory 表示该节点下分配的内存都是预留的内存预留的内存区域一般是给特定的驱动程序使用的它和 Linux 内核使用的内存区域不同一般预留的内存功能和Linux 内核的 DMA 或者 CMA 紧密相关如果某个节点的 compatible 属性为 shared-dma-pool则表示该节点内存区域用作一组设备的 DMA 缓冲区共享池此时如果看到节点属性中有 no-map则表示该内存不能被 Linux内核映射为系统内存的一部分需要从系统内存中分离出来如果看到节点属性中有 reusable属性则表示该内存不用从系统内存分离出来当特定驱动不使用这些内存的时候OS 可以使用这些内存。注意的是一个节点不能同时有 no-map 和 reusable 属性因为它们是逻辑上的矛盾关系。我们看到 reserved-memory 节点下的每个子节点都包含 no-map 属性说明这些内存不能被Linux 内核用作系统内存实际上是留给 M4 的系统使用的。设备树中设备节点的名称格式为 node-nameunit-address 例如vdev0vring0 子节点vdev0vring0:vdev0vring010040000{compatibleshared-dma-pool;reg0x100400000x1000;no-map;};vdev0vring0 是子节点的名字node-name 后面的10040000表示vdev0vring0 节点的地址为0x10040000。reg 后面是设备的起始地址和地址的长度。下面我们来看看reserved-memory节点下的每个子节点mcuram2子节点0x10000000 是 SRAM1 的起始地址0x40000 的大小刚好 256KB这段区域是 SRAM1SRAM2 区域这段区域主要用来保存M4固件的代码段和数据段SRAM1代码和SRAM2数据可以从M4工程的链接脚本分散加载文件看出不过也可以通过修改链接脚本重新划分地址范围但是要注意的是链接脚本的地址范围要和设备树配置的范围一致。vdev0vring0、vdev0vring1 和 vdev0buffer 子节点vdev0vring0、vdev0vring1 和 vdev0buffer 子节点刚好在 SRAM3 处即 IPC 缓冲区我们来看看这三个节点怎么分配的。1vdev0vring0 子节点0x10040000 是 vring0 的起始地址地址长度为0x1000即 4KB同样的。2vdev0vring1 是 vring1 的起始地址地址长度也是0x1000即 4KB。这两个节点就是我们前面说的用于发送和接收消息的vring。3vdev0buffer 子节点起始地址为 0x10042000地址长度为 0x4000大小为 16KB这段地址刚好落在SRAM3中这就是设置的共享的内存区域。vdev0vring0、vdev0vring1 和 vdev0buffer 只占用了 SRAM3 的前 24KBSRAM3 有 64KB并未使用完所以如果有需要也可以通过修改设备树和链接脚本来将SRAM3未使用到的地址用作其它功能。mcuram子节点mcuram 子节点起始地址是 0x30000000地址长度为 0x40000大小为 256KB这段地址是RAM aliases 里的SRAM1和SRAM2区域因为RAM aliases和SRAMs的物理地址是一样的所以也需要配置对于A7“可见”的对应区域这段区域对应的是M4“可见”的mcuram2区域。因为mcuram和 mcuram2 的物理地址一样所以这两段存储区域的功能是一样的可以说mcuram 是mcuram2 的别名存储区域这可能就是RAM aliases中aliases别名的由来要注意的是在设备树中mcuram 和 mcuram2 内存段定义必须是一致的。retram子节点retram 子节点的起始地址是 0x38000000地址长度 0x10000 为 64KB属于 RAM aliases里的RETRAM区域此区域和BOOT存储区域的RETRAM区域对应它们是同一个物理地址。RETRAM 用于存放 M4 内核的中断向量表中断向量表从0x00000000 开始默认情况下RETRAM起始地址为0x38000000它会重新映射到0x00000000以执行M4的代码。以上SRAM地址配置如下表4.2.1.1所示。上面的图可能有一点绕大家可以这么理解A7和M4为了方便地址的同一管理对于同一个真实芯片的物理地址在A7和M4中可能有不同的地址映射比如A7是从0X38000000开始且这部分叫做mcuram,而在M4眼中是从0X10000000开始这一部分叫做mcuram2但是其实这两个地址和其涵盖的长度最后都指向同一个真实的存储芯片。2.2 系统资源分配打开内核源码的stm32mp151.dtsi设备树文件找到如下地方mlahb{compatiblesimple-bus;#address-cells1;#size-cells1;dma-ranges0x000000000x380000000x10000,0x100000000x100000000x60000,0x300000000x300000000x60000;m4_rproc:m410000000{compatiblest,stm32mp1-m4;reg0x100000000x40000,0x300000000x40000,0x380000000x10000;resetsscmi0_reset RST_SCMI0_MCU;st,syscfg-holdbootrcc0x10C0x1;st,syscfg-tzrcc0x0000x1;st,syscfg-rsc-tbltamp0x1440xFFFFFFFF;st,syscfg-copro-statetamp0x1480xFFFFFFFF;st,syscfg-pddspwr_mcu0x00x1;statusdisabled;m4_system_resources{compatiblerproc-srm-core;statusdisabled;};};};以上设备树节点中有个m4_system_resources子节点也就是M4的资源管理器它是用于配置M4的外设资源的其中compatible属性中的rproc-srm-core 会匹配到内核源码源码的drivers/remoteproc/rproc_srm_core.c驱动文件如下是rproc_srm_core.c文件的部分代码staticconststructof_device_idrproc_srm_core_match[]{{.compatiblerproc-srm-core,},{},};MODULE_DEVICE_TABLE(of,rproc_srm_core_match);staticstructplatform_driverrproc_srm_core_driver{.proberproc_srm_core_probe,.removerproc_srm_core_remove,.driver{.namerproc-srm-core,.of_match_tableof_match_ptr(rproc_srm_core_match),},};module_platform_driver(rproc_srm_core_driver);当设备和驱动匹配成功以后platform_driver的probe 成员变量所代表的函数rproc_srm_core_probe()被执行通过该函数实现注册rproc 子设备rproc代表一个物理远程处理器设备可以说是一个外设打开内核源码的stm32mp157-m4-srm.dtsi设备树文件如下看到这部分代码m4_rproc表示在前面的m4_rproc节点下追加内容我们看追加了哪些内容m4_rproc{m4_system_resources{#address-cells1;#size-cells0;m4_timers2:timer40000000{compatiblerproc-srm-dev;reg0x400000000x400;clocksrcc TIM2_K;clock-namesint;statusdisabled;};/* 省略部分代码 */m4_adc:adc48003000{compatiblerproc-srm-dev;reg0x480030000x400;clocksrcc ADC12,rcc ADC12_K;clock-namesbus,adc;statusdisabled;};/* 省略部分代码 */m4_ethernet0:ethernet5800a000{compatiblerproc-srm-dev;reg0x5800a0000x2000;clock-namesstmmaceth,mac-clk-tx,mac-clk-rx,ethstp,syscfg-clk;clocksrcc ETHMAC,rcc ETHTX,rcc ETHRX,rcc ETHSTP,rcc SYSCFG;statusdisabled;};};};以上代码配置的就是M4的系统资源即M4配置了哪些外设不过status属性都是disabled也就是虽然配置了外设但是不使能外设。因为stm32mp157d-atk.dtsi文件include了stm32mp157-m4-srm.dtsi文件stm32mp157d-atk.dts文件又include了stm32mp157d-atk.dtsi文件所以可以直接在stm32mp157d-atk.dtsi或stm32mp157d-atk.dts设备树文件中选择使能M4的某个外设。这里说明一下M4和A7有些外设是共享的例如GPIO是共享的资源如果此GPIO没有复用做其它功能只是单纯当做普通的IO使用那么A7和M4都可以访问这些资源。例如在stm32mp157d-atk.dtsi下有配置了一个蜂鸣器和两个led节点它们使用的是GPIO功能这些节点是给A7用的但M4也可以使用leds{compatiblegpio-leds;led1{labelsys-led;gpiosgpioi0GPIO_ACTIVE_LOW;linux,default-triggerheartbeat;default-stateon;statusokay;};led2{labeluser-led;gpiosgpiof3GPIO_ACTIVE_LOW;linux,default-triggernone;default-stateon;statusokay;};beep{labelbeep;gpiosgpioc7GPIO_ACTIVE_LOW;default-stateoff;};};如果是具有单选功能的外设也就是这些外设要么只能单独给A7使用要么只能单独给M4使用如果A7和M4都一起使用该外设就会存在资源争用问题某一方就会出现异常主处理器有一定的优先权一般是协处理器这边出现异常例如如果A7和M4一起占用ADC1来采集数据这个时候M4这边采集到的数据会不准确可能采集不到数据而显示0。对于单选的外设如果A7要使用该外设的话在设备树下一定要配置A7对应的外设节点如果该外设要给M4使用的话设备树下可以不必配置M4相关的节点只需要在固件中配置该外设即可也就是在裸机程序中配置当A7加载和启动固件后M4就可以使用该外设了。如果已经在设备树下配置A7对应的某个外设节点M4想使用此外设的话是否需要将设备树下A7占用的相关节点注释掉呢例如在stm32mp157d-atk.dtsi设备树下有如下节点adc1_in6_pins_b:adc1-in6{pins{pinmuxSTM32_PINMUX(A,5,ANALOG);};};adc{/* ADC1 ADC2 common resources */pinctrl-namesdefault;pinctrl-0adc1_in6_pins_b;vdd-supplyvdd;vdda-supplyvdd;vref-supplyvdd;statusokay;adc1:adc0{/* private resources for ADC1 */st,adc-channels19;st,min-sample-time-nsecs10000;statusokay;};};以上代码段中表示将ADC1分配给A7使用如果M4要使用的话这段代码可以不用注释掉只要确保Linux系统运行以后A7不去操作ADC1那么当加载和运行M4的固件后固件中已经配置了ADC1M4就可以使用ADC1来采集数据了。但是如果此时A7去操作ADC1的话M4这边ADC1采集到的数据就会不准确。所以要么将设备树下A7占用ADC1的节点注释掉这样A7就永远无法使用ADC1了要么保留A7占用的ADC1节点只需要保证Linux系统启动后A7不去操作ADC1这样M4就可以正常使用ADC1了。如果采用前者的方法将A7占用ADC1的相关节点注释掉可以修改如下/* adc1_in6_pins_b: adc1-in6 { pins { pinmux STM32_PINMUX(A, 5, ANALOG); }; }; *//* adc { // * ADC1 ADC2 common resources * pinctrl-names default; pinctrl-0 adc1_in6_pins_b; vdd-supply vdd; vdda-supply vdd; vref-supply vdd; status okay; adc1: adc0 { // * private resources for ADC1 * st,adc-channels 19; st,min-sample-time-nsecs 10000; status okay; }; }; */m4_adc{vref-supplyvrefbuf;statusokay;/* 使能M4的ADC */};也就是将A7占用的ADC1部分注释掉。后面m4_adc节点部分是手动添加的这段可以添加也可以不添加不过按照ST的标准来最好要添加设备树stm32mp157c-dk2-m4-examples.dts是模板文件我们修改设备树的时候可以参考模板文件以上修改的m4_adc节点就是参考此文件来写的。修改好设备树以后执行如下指令重新编译设备树make ARCHarm CROSS_COMPILEarm-none-linux-gnueabihf-dtbs再将编译出来的stm32mp157d-atk.dtb文件拷贝到开发板文件系统的/boot目录下替换掉以前的设备树二进制文件再执行sync指令以同步缓存然后重启开发板重新进入Linux操作系统后A7就不能再去操作ADC1了当加载和启动M4固件后M4可单独访问ADC1。关于这些操作大家在后期操作的时候可以多进行实践。3 总结本章节分析了stm32mp157中的存储和系统资源分配为后续核间通信打下一定基础。4 其他章节【STM32MP157 异核通信框架学习篇】1SMP和AMP架构【STM32MP157 异核通信框架学习篇】2IPCC通信框架【STM32MP157 异核通信框架学习篇】3OpenAMP框架【STM32MP157 异核通信框架学习篇】4驱动文件介绍【STM32MP157 异核通信框架学习篇】5Remoteproc 相关驱动简析 ---- 资源表【STM32MP157 异核通信框架学习篇】6Remoteproc 相关驱动简析 ---- 存储和系统资源分配【STM32MP157 异核通信框架学习篇】7Remoteproc 相关驱动简析 ---- 链接脚本【STM32MP157 异核通信框架学习篇】8Remoteproc 相关驱动简析 ---- 重新划分存储区域【STM32MP157 异核通信框架学习篇】10Linux下Remoteproc相关API (上)【STM32MP157 异核通信框架学习篇】10Linux下Remoteproc相关API (下)【STM32MP157 异核通信框架学习篇】11链接脚本【STM32MP157 异核通信框架学习篇】11链接脚本 – [额外补充: MP157板子存储分配]【STM32MP157 异核通信框架学习篇】12Remoteproc 的使用 [加载和启动M4固件] (上)【STM32MP157 异核通信框架学习篇】12Remoteproc 的使用 [加载和启动M4固件] (下)