当前位置:首页>java>IPC多核开发代码分析

IPC多核开发代码分析

  • 2026-08-26 11:25:09
IPC多核开发代码分析
CPU型号:AM6442
CCS版本:CCS12.1.0
MCU+SDK:mcu_plus_sdk_am64x_08_06_00_43
AM6442内核:两个Cortex-A53,四个Cortex-R5F,一个Cortex-M4F
由于对多核处理器理解的不够透彻,暂定Cortex-A53核心运行Linux系统,Cortex-R5F做算法处理。
硬件功能:EtherCAT接口 * 1,Ethernet接口 * 1,串口 * 1
通过学习多核间通讯熟悉CCS操作流程、库函数、多核协作方式等。
内核M4FSS相关代码
一、初始化相关函数
1、System_init()函数内包含的函数:Dpl_init()和Sciclient_init()
1.1void Dpl_init(void)函数主要完成地址转换功能的配置,其中调用的函数:
1.1.1HwiP_init()    
  • 禁用所有单独的中断
  • 清除所有单独的中断
  • 启用全局中断
  • 必须在系统初始化过程中调用,之后才会调用HwiP_construct API调用。
  • 对于ARM R5F和ARM M4F,这会初始化并启用FIQ和IRQ
1.1.2初始化地址转换模块
AddrTranslateP_Params addrTranslateParams;AddrTranslateP_Params_init(&addrTranslateParams);addrTranslateParams.numRegions = CONFIG_ADDR_TRANSLATE_REGIONS;addrTranslateParams.ratBaseAddr = CONFIG_ADDR_TRANSLATE_RAT_BASE_ADDR;addrTranslateParams.regionConfig = &gAddrTranslateRegionConfig[0];AddrTranslateP_init(&addrTranslateParams);
不同核RAT地址:
MCU_M4FSS0_RAT_CFG  地址:0x44200000 - 0x44200FFF(4K)
gAddrTranslateRegionConfig[0]中的参数:{    .localAddr = 0x80000000u,    .systemAddr = 0x0u,    .size = AddrTranslateP_RegionSize_256M,},
AddrTranslateP_RegionConfig gAddrTranslateRegionConfig[CONFIG_ADDR_TRANSLATE_REGIONS] = {    {        .localAddr = 0x80000000u,        .systemAddr = 0x0u,        .size = AddrTranslateP_RegionSize_256M,    },    {        .localAddr = 0x90000000u,        .systemAddr = 0x10000000u,        .size = AddrTranslateP_RegionSize_256M,    },    {        .localAddr = 0xA0000000u,        .systemAddr = 0xA0000000u,        .size = AddrTranslateP_RegionSize_256M,    },    {        .localAddr = 0xB0000000u,        .systemAddr = 0x20000000u,        .size = AddrTranslateP_RegionSize_256M,    },    {        .localAddr = 0xC0000000u,        .systemAddr = 0x30000000u,        .size = AddrTranslateP_RegionSize_256M,    },    {        .localAddr = 0xD0000000u,        .systemAddr = 0x40000000u,        .size = AddrTranslateP_RegionSize_256M,    },    {        .localAddr = 0x60000000u,        .systemAddr = 0x60000000u,        .size = AddrTranslateP_RegionSize_256M,    },    {        .localAddr = 0x70000000u,        .systemAddr = 0x70000000u,        .size = AddrTranslateP_RegionSize_256M,    },};
gAddrTranslateRegionConfig中定义的内容应该是与图中外部2G内存区域定义相关。(但是为什么顺序做了调整?)
内核R5FSS相关地址映射信息
地址:0x2FFE0000 - 0x2FFE0FFF(4K)
1.1.3日志
/* init debug log zones early *//* Debug log init */DebugP_logZoneEnable(DebugP_LOG_ZONE_ERROR);DebugP_logZoneEnable(DebugP_LOG_ZONE_WARN);/* Initialize linux trace log writer */DebugP_memLogWriterInit(CSL_CORE_ID_M4FSS0_0);
内核地址定义
#define CSL_CORE_ID_M4FSS0_0         (0U)#define CSL_CORE_ID_R5FSS0_0         (1U)#define CSL_CORE_ID_R5FSS0_1         (2U)#define CSL_CORE_ID_R5FSS1_0         (3U)#define CSL_CORE_ID_R5FSS1_1         (4U)#define CSL_CORE_ID_A53SS0_0         (5U)#define CSL_CORE_ID_A53SS0_1         (6U)#define CSL_CORE_ID_MAX              (7U)
1.1.4时钟初始化
ClockP_init();

该函数注释如下

/** * \brief Initialize the clock module * * The API is called during system init to setup a timer to run at a periodic time internval * of 'n' micro seconds. * * 'n' can be configued by the user via SysConfig, default value for 'n' is typically 1000 us * * Using this single timer, the clock API can be used to start multiple 'clock's in units of * clock ticks. */初始化时钟模块。在系统初始化过程中调用该 API,以设置定时器,使其以周期“n”微妙运行。用户可以通过 SysConfig 配置 'n','n' 的默认值通常是 1000 us利用这个单一定时器,时钟 API 可以用来启动多个以时钟刻为单位的“时钟”。

初始化阶段并没有调用SysConfig相关函数,应该是使用默认的周期1ms。

SysConfig 具体函数定义看到后再补充
1.1.5使能中断
HwiP_enable();
1.2 Sciclient_init(CSL_CORE_ID_M4FSS0_0)
SCI客户端初始化。具体内容不清楚,传入参数为内核地址。(属于不清楚具体功能的函数)
1.3PowerClock_init()
关联函数:Module_clockEnable()和Module_clockSetFrequency()
都没有注释,看着比较费劲。通过函数名可以理解是时钟使能和时钟频率设置。
1.3.1Module_clockEnable函数
voidModule_clockEnable(void){    int32_t status;    uint32_t i = 0;    while(gSocModules[i]!=SOC_MODULES_END)    {        status = SOC_moduleClockEnable(gSocModules[i], 1);        DebugP_assertNoLog(status == SystemP_SUCCESS);        i++;    }}其中SOC_MODULES_END = 0xFFFFFFFFu函数SOC_moduleClockEnable定义int32_t SOC_moduleClockEnable(uint32_t moduleId, uint32_t enable)传入参数moduleId:参考tisci_devices列表enable:1使能,0禁止数组初始化如下:uint32_t gSocModules[] = {SOC_MODULES_END,};实际Module_clockEnable没做任何处理
1.3.2Module_clockSetFrequency函数
voidModule_clockSetFrequency(void){    int32_t status;    uint32_t i = 0;    while(gSocModulesClockFrequency[i].moduleId!=SOC_MODULES_END)    {        status = SOC_moduleSetClockFrequency(                    gSocModulesClockFrequency[i].moduleId,                    gSocModulesClockFrequency[i].clkId,                    gSocModulesClockFrequency[i].clkRate                    );        DebugP_assertNoLog(status == SystemP_SUCCESS);        i++;    }}主要是调用函数SOC_moduleSetClockFrequency来设置时钟频率结构体初始化如下:SOC_ModuleClockFrequency gSocModulesClockFrequency[] = {    { SOC_MODULES_END, SOC_MODULES_END, SOC_MODULES_END },};因此该函数实际没做任何处理。
时钟使能和时钟频率设置函数实际并没有做任何处理,可以理解为都是采用的默认值。相关函数使用方法和使用环境后面遇到再补充。(备注一下)
1.4Pinmux_init引脚复用初始化
voidPinmux_init(void){    Pinmux_config(gPinMuxMainDomainCfg, PINMUX_DOMAIN_ID_MAIN);    Pinmux_config(gPinMuxMcuDomainCfg, PINMUX_DOMAIN_ID_MCU);}static Pinmux_PerCfg_t gPinMuxMainDomainCfg[] = {    {PINMUX_END, PINMUX_END}};static Pinmux_PerCfg_t gPinMuxMcuDomainCfg[] = {    {PINMUX_END, PINMUX_END}};
根据初始化内容判断,该函数也没做实质的处理。
1.5 IPC notify模式参数配置
{    IpcNotify_Params notifyParams;    int32_t status;    /* initialize parameters to default */    IpcNotify_Params_init(&notifyParams);    /* specify the core on which this API is called */    notifyParams.selfCoreId = CSL_CORE_ID_M4FSS0_0;    /* list the cores that will do IPC Notify with this core    * Make sure to NOT list 'self' core in the list below    */    notifyParams.numCores = 5;    notifyParams.coreIdList[0] = CSL_CORE_ID_R5FSS0_0;    notifyParams.coreIdList[1] = CSL_CORE_ID_R5FSS0_1;    notifyParams.coreIdList[2] = CSL_CORE_ID_R5FSS1_0;    notifyParams.coreIdList[3] = CSL_CORE_ID_R5FSS1_1;    notifyParams.coreIdList[4] = CSL_CORE_ID_A53SS0_0;    notifyParams.linuxCoreId = CSL_CORE_ID_A53SS0_0;    /* initialize the IPC Notify module */    status = IpcNotify_init(&notifyParams);    DebugP_assert(status==SystemP_SUCCESS);}
主要是配置IPC通讯的内核列表
1.6 IPC RPMessage
{    RPMessage_Params rpmsgParams;    int32_t status;    /* initialize parameters to default */    RPMessage_Params_init(&rpmsgParams);    /* VRING mapping from source core to destination core, '-1' means NO VRING,    r5fss0_0 => {"r5fss0_0":-1,"r5fss0_1":0,"r5fss1_0":1,"r5fss1_1":2,"m4fss0_0":3}    r5fss0_1 => {"r5fss0_0":4,"r5fss0_1":-1,"r5fss1_0":5,"r5fss1_1":6,"m4fss0_0":7}    r5fss1_0 => {"r5fss0_0":8,"r5fss0_1":9,"r5fss1_0":-1,"r5fss1_1":10,"m4fss0_0":11}    r5fss1_1 => {"r5fss0_0":12,"r5fss0_1":13,"r5fss1_0":14,"r5fss1_1":-1,"m4fss0_0":15}    m4fss0_0 => {"r5fss0_0":16,"r5fss0_1":17,"r5fss1_0":18,"r5fss1_1":19,"m4fss0_0":-1}    */    /* TX VRINGs */    rpmsgParams.vringTxBaseAddr[CSL_CORE_ID_R5FSS0_0] = (uintptr_t)gRPMessageVringMem[16];    rpmsgParams.vringTxBaseAddr[CSL_CORE_ID_R5FSS0_1] = (uintptr_t)gRPMessageVringMem[17];    rpmsgParams.vringTxBaseAddr[CSL_CORE_ID_R5FSS1_0] = (uintptr_t)gRPMessageVringMem[18];    rpmsgParams.vringTxBaseAddr[CSL_CORE_ID_R5FSS1_1] = (uintptr_t)gRPMessageVringMem[19];    /* RX VRINGs */    rpmsgParams.vringRxBaseAddr[CSL_CORE_ID_R5FSS0_0] = (uintptr_t)gRPMessageVringMem[3];    rpmsgParams.vringRxBaseAddr[CSL_CORE_ID_R5FSS0_1] = (uintptr_t)gRPMessageVringMem[7];    rpmsgParams.vringRxBaseAddr[CSL_CORE_ID_R5FSS1_0] = (uintptr_t)gRPMessageVringMem[11];    rpmsgParams.vringRxBaseAddr[CSL_CORE_ID_R5FSS1_1] = (uintptr_t)gRPMessageVringMem[15];    /* Other VRING properties */    rpmsgParams.vringSize = IPC_RPMESSAGE_VRING_SIZE;    rpmsgParams.vringNumBuf = IPC_RPMESSAGE_NUM_VRING_BUF;    rpmsgParams.vringMsgSize = IPC_RPMESSAGE_MAX_VRING_BUF_SIZE;    rpmsgParams.linuxResourceTable = &gRPMessage_linuxResourceTable;    rpmsgParams.linuxCoreId = CSL_CORE_ID_A53SS0_0;    /* initialize the IPC RP Message module */    status = RPMessage_init(&rpmsgParams);    DebugP_assert(status==SystemP_SUCCESS);}
typedef struct{    uintptr_t vringTxBaseAddr[CSL_CORE_ID_MAX];  /**< VRING address of transmit rings to each core */    uintptr_t vringRxBaseAddr[CSL_CORE_ID_MAX];  /**< VRING address of receive rings to each core */    uint32_t vringSize;                   /**< Size of memory assigned to one VRING, use \ref RPMESSAGE_VRING_SIZE to find the size needed */    uint16_t vringNumBuf;                 /**< Max number of buffers in one VRING */    uint16_t vringMsgSize;                /**< Size of each message in one VRING */    const RPMessage_ResourceTable *linuxResourceTable;  /**< Linux resoruce table for self core,                                                   * when non-NULL Cortex A* is assumed to run Linux.                                                   * And VRING info  for message exchange with LInux                                                   * is specified in the resource table                                                   */    uint16_t linuxCoreId; /** ID of linux core */} RPMessage_Params;

1.6.1 uintptr_t类型为32位无符号整型变量。gRPMessageVringMem是8位的二维数组[20][1312]

/* Number of CPUs that are enabled for IPC RPMessage */#define IPC_RPMESSAGE_NUM_CORES           (5U)/* Number of VRINGs for the numner of CPUs that are enabled for IPC */#define IPC_RPMESSAGE_NUM_VRINGS          (IPC_RPMESSAGE_NUM_CORES*(IPC_RPMESSAGE_NUM_CORES-1))//内核数量为5,每个核需要与另外4个核通讯,因此需要建立5*4个VRING环/* Number of a buffers in a VRING, i.e depth of VRING queue */#define IPC_RPMESSAGE_NUM_VRING_BUF       (8U)/* Max size of a buffer in a VRING */#define IPC_RPMESSAGE_MAX_VRING_BUF_SIZE  (128U)/* Size of each VRING is number of buffers x ( size of each buffer + space for data structures of one buffer (32B) )*/#define IPC_RPMESSAGE_VRING_SIZE          RPMESSAGE_VRING_SIZE(IPC_RPMESSAGE_NUM_VRING_BUF, IPC_RPMESSAGE_MAX_VRING_BUF_SIZE)uint8_t gRPMessageVringMem[IPC_RPMESSAGE_NUM_VRINGS][IPC_RPMESSAGE_VRING_SIZE] __attribute__((aligned(128), section(".bss.ipc_vring_mem")));

关于缓存数量和缓存大小在IPC RPMessage章节中有相关描述:

  • 在 NORTOS 和 RTOS 之间,下面可以在 RP Message 中配置以控制共享内存大小,
    • 最大消息大小,默认为128字节
    • VRING 中的缓冲区数量,默认是 8 条消息
    • VRING 共享内存缓冲地址可以配置,可以是 DDR 或内部内存地址。
  • 在Linux和NORTOS/RTOS之间,VRING参数固定如下
    • 最大消息是512B
    • VRING 中消息数量为 256
    • VRING 共享内存地址由 Linux 设备树中的值确定,并放置在 DDR。
AM64x MCU+ SDK地址:安装目录下--->../ti/mcu_plus_sdk_am64x_08_06_00_43/docs/api_guide_am64x/DRIVERS_IPC_RPMESSAGE_PAGE.html
章节中给出的“重要使用指南”
  • 强烈建议参考IPC示例,以理解IPC应用中链接器命令文件的设置。examples/drivers/ipc
  • 当 Linux 与 RTOS/NORTOS 同时运行时,请按照以下作确保 NORTOS/RTOS 能够与 Linux 通信。
    • 在开始与Linux通信前,务必先调用RPMessage_waitForLinuxReady()。
    • 另外,对于在NORTOS/RTOS上创建的任何RPMessage点,请务必用RPMessage_announce()向Linux公告。
    • 确保按照Linux设备树中提到的,分配用于VRING的共享内存在Linux和CPU之间。另外,务必将此部分标记为非缓存
    • 如果CPU代码在DDR里跑不完,记得在DDR里为代码/数据部分设置一个MPU条目。这可以标记为缓存。
    • 同样,请参阅Linux设备树,了解DDR和MSMC中NORTOS/RTOS应用可以执行的空间。
  • 确保在每个CPU的链接器命令文件中,将用于NORTOS/RTOS共享的VRING共享内存分配到一个公共内存部分,并且在R5F MPU中标记该部分为非缓存。
  • Syscfg 的最大消息大小限制为 1152 字节,缓冲区最大数量限制为 16。推荐的方法是将缓冲区数量和消息大小控制在这个限制内。
  • 如果需要传递较大的消息,数据应保存在共享内存中,并通过IPC传递指向该数据的指针。
并给出了各个内核通讯的示例代码。
1.6.2 
rpmsgParams.linuxResourceTable = &gRPMessage_linuxResourceTable;
gRPMessage_linuxResourceTable参数说明:定义:typedef struct{    RPMessage_RscHdr base;       /**< Header Information */    uint32_t offset[2];      /**< offset to VDEV and TRACE entries */    RPMessage_RscVdev vdev;    /**< VDEV entry */    RPMessage_RscVring  vring0;    /**< TX VRING  */    RPMessage_RscVring  vring1;    /**< RX VRING */    RPMessage_RscTrace trace;    /**< Trace entry  */} RPMessage_ResourceTable;初始化:const RPMessage_ResourceTable gRPMessage_linuxResourceTable __attribute__ ((section (".resource_table"), aligned (4096))) ={    {        1U,         /* we're the first version that implements this */        2U,         /* number of entries, MUST be 2 */        { 0U, 0U, } /* reserved, must be zero */    },  //定义中也有说明,这部分值不可修改    /* offsets to the entries */    {        offsetof(RPMessage_ResourceTable, vdev),          offsetof(RPMessage_ResourceTable, trace),    },//计算vdev和trace的偏移量。//VDEV 是一种 资源类型标识,用于在资源表(Resource Table) 中声明一个 VirtIO 设备//trace 是一种 rpmsg 服务 或 rpmsg 通道,用于系统调试和追踪信息传输。    /* vdev entry */    {        RPMESSAGE_RSC_TYPE_VDEV, RPMESSAGE_RSC_VIRTIO_ID_RPMSG,        0U, 1U, 0U, 0U, 0U, 2U, { 0U, 0U },    },//后面的数据基本是默认值,主要是前面两个参数的定义如下:/** \brief trace type */#define RPMESSAGE_RSC_TYPE_TRACE       (2U)/** \brief VDEV type */#define RPMESSAGE_RSC_TYPE_VDEV        (3U)#define RPMESSAGE_RSC_VIRTIO_ID_RPMSG  (7U)//查到一篇资料是这样定义的:enum fw_resource_type { /* 资源条目的类型 */  RSC_CARVEOUT = 0, /* 请求分配物理上连续的内存区域 */  RSC_DEVMEM = 1, /* 请求 iommu_map 一个基于内存的外设 */  RSC_TRACE = 2, /* 宣布跟踪缓冲区的可用性,远程处理器将在其中写入日志 */   RSC_VDEV = 3, /* 声明对 Virtio 设备的支持,并作为其 Virtio 标头。 */   RSC_LAST = 4, /* 把这个放在标准资源的末尾 */  RSC_VENDOR_START = 128, /* 供应商特定资源类型范围的开始 */  RSC_VENDOR_END = 512, /* 供应商特定资源类型范围的末尾 */ }; //而RPMESSAGE_RSC_VIRTIO_ID_RPMSG定义为7与 VirtIO 设备类型 ID 的分配有关//VirtIO 标准为不同类型的虚拟设备定义了唯一的设备 ID,而 7 就是为 VirtIO 设备类型 "rpmsg" 所保留的 ID    /* the two vrings */    { RPMESSAGE_RSC_VRING_ADDR_ANY, 4096U, 256U, 1U, 0U },    { RPMESSAGE_RSC_VRING_ADDR_ANY, 4096U, 256U, 2U, 0U },//两个vring配置,实际地址由linux系统给定,数据对齐4096,缓存256,发送环1,接收环2    {        (RPMESSAGE_RSC_TRACE_INTS_VER0 | RPMESSAGE_RSC_TYPE_TRACE),        (uint32_t)gDebugMemLog, DebugP_MEM_LOG_SIZE,        0, "trace:m4fss0_0",    },//};
1.6.3 
rpmsgParams.linuxCoreId = CSL_CORE_ID_A53SS0_0;
关于核ID的定义:
#define CSL_CORE_ID_M4FSS0_0         (0U)#define CSL_CORE_ID_R5FSS0_0         (1U)#define CSL_CORE_ID_R5FSS0_1         (2U)#define CSL_CORE_ID_R5FSS1_0         (3U)#define CSL_CORE_ID_R5FSS1_1         (4U)#define CSL_CORE_ID_A53SS0_0         (5U)#define CSL_CORE_ID_A53SS0_1         (6U)#define CSL_CORE_ID_MAX              (7U)

1.6.4 IPC RP消息的初始化

/* initialize the IPC RP Message module */status = RPMessage_init(&rpmsgParams);DebugP_assert(status==SystemP_SUCCESS);

以上是系统初始化函数的相关代码。基本流程理解了,细节的东西需要使用中学习。

例程中注释有些不是特别清晰,对于初学者不是很友好,需要做大量的功课去查找。另外在整理代码中发现涉及很多不曾接触的领域,如virtio、

VDEVs等。尤其是很多缩写不知道原型是什么,网上查也都是以缩写的形式出现。感觉想学好,学明白还有漫长的路要走。

最新文章

随机文章