MCU+SDK:mcu_plus_sdk_am64x_08_06_00_43AM6442内核:两个Cortex-A53,四个Cortex-R5F,一个Cortex-M4F由于对多核处理器理解的不够透彻,暂定Cortex-A53核心运行Linux系统,Cortex-R5F做算法处理。硬件功能:EtherCAT接口 * 1,Ethernet接口 * 1,串口 * 1通过学习多核间通讯熟悉CCS操作流程、库函数、多核协作方式等。1、System_init()函数内包含的函数:Dpl_init()和Sciclient_init()1.1void Dpl_init(void)函数主要完成地址转换功能的配置,其中调用的函数:- 必须在系统初始化过程中调用,之后才会调用HwiP_construct API调用。
- 对于ARM R5F和ARM M4F,这会初始化并启用FIQ和IRQ
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);
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内存区域定义相关。(但是为什么顺序做了调整?)地址:0x2FFE0000 - 0x2FFE0FFF(4K)/* 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)
该函数注释如下
/** * \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。
1.2 Sciclient_init(CSL_CORE_ID_M4FSS0_0)SCI客户端初始化。具体内容不清楚,传入参数为内核地址。(属于不清楚具体功能的函数)关联函数: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 },};因此该函数实际没做任何处理。
时钟使能和时钟频率设置函数实际并没有做任何处理,可以理解为都是采用的默认值。相关函数使用方法和使用环境后面遇到再补充。(备注一下)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}};
{ IpcNotify_Params notifyParams; int32_t status; /* initialize parameters to default */ IpcNotify_Params_init(¬ifyParams); /* 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(¬ifyParams); DebugP_assert(status==SystemP_SUCCESS);}
{ 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 中配置以控制共享内存大小,
- VRING 共享内存缓冲地址可以配置,可以是 DDR 或内部内存地址。
- 在Linux和NORTOS/RTOS之间,VRING参数固定如下
- 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传递指向该数据的指针。
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", },//};
rpmsgParams.linuxCoreId = CSL_CORE_ID_A53SS0_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.6.4 IPC RP消息的初始化
/* initialize the IPC RP Message module */status = RPMessage_init(&rpmsgParams);DebugP_assert(status==SystemP_SUCCESS);
以上是系统初始化函数的相关代码。基本流程理解了,细节的东西需要使用中学习。
例程中注释有些不是特别清晰,对于初学者不是很友好,需要做大量的功课去查找。另外在整理代码中发现涉及很多不曾接触的领域,如virtio、
VDEVs等。尤其是很多缩写不知道原型是什么,网上查也都是以缩写的形式出现。感觉想学好,学明白还有漫长的路要走。