Autosar Os MCU 多核 启动_core_sec-程序员宅基地

技术标签: infineon  单片机  

1.1 core0 main 之前MCU 干了什么

1.1.2 链接文件指定入口函数

mcu 启动的地址有很多方式,这里介绍链接文件指定启动位置。使用ENTRY 指定一个symbol (不知道咋翻译)

ENTRY(cstart)

ENTRY 是 编译器给的link文件命令。

The first instruction to execute in a program is called the entry point. You can use the ENTRY
linker script command to set the entry point. The argument is a symbol name:
ENTRY(<symbol>)
There are several ways to set the entry point. The linker will set the entry point by trying each
of the following methods in order, and stopping when one of them succeeds:
 the ’-e’ <entry> command-line option;
 the ENTRY(<symbol>) command in a linker script;
 the value of the symbol start , if defined;
 the address of the first byte of the . text section, if present;
 The address 0.

这里面我们就可以在代码里定义一个函数。函数名字叫做

void cstart(void)

这里面 函数的链接位置,也是通过链接文件来指定。这里不赘述,一般都是”从头开始“。

这里我们要注意一点。cstart 每个core 都可以来调用。我们来看一下cstart 的实现。

void cstart(void)
{
    
   unsigned int coreID;
   coreID = __MFCR(0xFE1C);

   if(coreID == 0u)
  {
    
       Ifx_Ssw_jumpToFunction(__StartUpSoftware);
  }
   if(coreID == 1u)
  {
    
       Ifx_Ssw_jumpToFunction(__Core1_start);
  }
   if(coreID == 2u)
  {
    
       Ifx_Ssw_jumpToFunction(__Core2_start);
  }
   if(coreID == 3u)
  {
    
       Ifx_Ssw_jumpToFunction(__Core3_start);
  }
   if(coreID == 4u)
  {
    
       Ifx_Ssw_jumpToFunction(__Core4_start);
  }
   if(coreID == 5u)
  {
    
       Ifx_Ssw_jumpToFunction(__Core5_start);
  }
}

1.1.3 __StartUpSoftware

由前面可以知道cstart 是上电后,MCU 指定的第一个函数入口。

第一句有个指令 __MFCR

Move from core register

就是说把core 寄存器 0xFE1C 的数 拿给coreID这个 变量。

这个寄存器是什么呢?

在这里插入图片描述
所以当不同的core 来执行 该指令的时候,返回值就是该core的coreID.

到了这里我们开始启动core0。

void __StartUpSoftware(void)

首先这里对A1 寄存器进行了初始化。

Ifx_Ssw_setAddressReg(a1, __SDATA2(0));

实际上就是把SDATA2(0) 数值 set 给 a1 寄存器。那么 SDATA2(0) 是什么呢?

#define __SDATA1(cpu)     __A0_MEM
#define __SDATA2(cpu)     __A1_MEM
#define __SDATA3(cpu)     __A8_MEM
#define __SDATA4(cpu)     __A9_MEM

那么问题来了 __A1_MEM 又是哪里来的呢?

这个又涉及到了链接文件。

 CORE_SEC(.sdata2) : FLAGS(arsl)
  {
    
      *(.srodata)
      *(.srodata.*)
  } > default_rom
  _SMALL_DATA2_ = SIZEOF(CORE_SEC(.sdata2)) ? ADDR(CORE_SEC(.sdata2)) : (ADDR(CORE_SEC(.sdata2)) & 0xF0000000) + 32k ;
  __A1_MEM = _SMALL_DATA2_;  

这个是什么意思呢?

这段代码是用于计算SMALL_DATA2_的值。首先,它使用SIZEOF(CORE_SEC(.sdata2))来获取.sdata2段的大小,然后判断该大小是否为0。如果不为0,则将ADDR(CORE_SEC(.sdata2))赋值给SMALL_DATA2_;否则,将ADDR(CORE_SEC(.sdata2))与0xF0000000进行按位与运算,再加上32k,最后将结果赋值给SMALL_DATA2_。
所以也就是看有没有内容链接到 这个sec,如果没有,那么就只需要知道 CORE_SEC 的地址就可以了。

这里 default_rom 是 0x8000xxxx, 所以最终计算下来是

0x80008000

也就是说A1 寄存器现在的数值是0x80008000。
下面看一下这些寄存器分别的介绍。
在这里插入图片描述
设置完A1 寄存器的global register 地址之后。set 另外一个寄存器。
在这里插入图片描述
这里设置固定值

0x00000980u == 1001 1000 0000b

看一下具体含义。

  • Call depth counting is enabled

  • Write permission to global registers A[0], A[1], A[8], A[9] is enabled.

  • Supervisor ModeEnables access to all peripheral devices. It enables read/write access to coreregisters and protected peripheral devices. Tasks at this level may disableinterrupts.

什么意思呢?简言之,开启栈深度counter, 开启牛逼权限,开启一些寄存器的读写权限。

有了这些权限之后。判断一下,本次启动是因为软件重启还是硬件复位。不同的reset 方式 对应的启动流程是不一样的。

这里是通过寄存器来判断,具体不同bit 含义如下。不做赘述。

typedef struct _Ifx_SCU_RSTCON_Bits
{
    
   Ifx_UReg_32Bit ESR0:2;            /**< \brief [1:0] ESR0 Reset Request Trigger Reset Configuration - ESR0 (rw) */
   Ifx_UReg_32Bit ESR1:2;            /**< \brief [3:2] ESR1 Reset Request Trigger Reset Configuration - ESR1 (rw) */
   Ifx_UReg_32Bit reserved_4:2;      /**< \brief [5:4] \internal Reserved */
   Ifx_UReg_32Bit SMU:2;             /**< \brief [7:6] SMU Reset Request Trigger Reset Configuration - SMU (rw) */
   Ifx_UReg_32Bit SW:2;              /**< \brief [9:8] SW Reset Request Trigger Reset Configuration - SW (rw) */
   Ifx_UReg_32Bit STM0:2;            /**< \brief [11:10] STM0 Reset Request Trigger Reset Configuration - STM0 (rw) */
   Ifx_UReg_32Bit STM1:2;            /**< \brief [13:12] STM1 Reset Request Trigger Reset Configuration (If Product has STM1) - STM1 (rw) */
   Ifx_UReg_32Bit STM2:2;            /**< \brief [15:14] STM2 Reset Request Trigger Reset Configuration (If Product has STM2) - STM2 (rw) */
   Ifx_UReg_32Bit STM3:2;            /**< \brief [17:16] STM3 Reset Request Trigger Reset Configuration (If Product has STM3) - STM3 (rw) */
   Ifx_UReg_32Bit STM4:2;            /**< \brief [19:18] STM4 Reset Request Trigger Reset Configuration (If Product has STM4) - STM4 (rw) */
   Ifx_UReg_32Bit STM5:2;            /**< \brief [21:20] STM5 Reset Request Trigger Reset Configuration (If Product has STM5) - STM5 (rw) */
   Ifx_UReg_32Bit reserved_22:10;    /**< \brief [31:22] \internal Reserved */
} Ifx_SCU_RSTCON_Bits;

我们这里说 硬件从零上电的过程。

总结一下:在phase0 主要是初始化core 寄存器,以及赋予一些操作权限。

1.1.4 __StartUpSoftware_Phase2

这里主要对mcu 电源,内部内存进行一些自检。还是要看用户有没有进行配置,进行写自检代码,否则没有实际代码执行。

   /* Power and EVRC configurations */
   IFX_CFG_SSW_CALLOUT_PMS_INIT();

   /* LBIST Tests and evaluation */
   IFX_CFG_SSW_CALLOUT_LBIST();

   /* MONBIST Tests and evaluation */
   IFX_CFG_SSW_CALLOUT_MONBIST();

总结一下:mcu 自检操作。

1.1.5 __StartUpSoftware_Phase3PowerOnResetPath

这里主要是进行上下文初始化。包含 stack 与 CSA。也就是 上下文切换,占空间的 ram 地址。

我们先来分析一下链接文件对stack,CSA 的指定地址。

CORE_ID = CPU0;
SECTIONS
{
    
   CORE_SEC(.ustack) (LCF_DSPR0_START + LCF_USTACK0_OFFSET):
  {
     PROVIDE(__USTACK0_END = .);   . = . + LCF_USTACK0_SIZE;    PROVIDE(__USTACK0 = .); }
   
   CORE_SEC(.istack) (LCF_DSPR0_START + LCF_ISTACK0_OFFSET):
  {
     PROVIDE(__ISTACK0_END = .);   . = . + LCF_ISTACK0_SIZE;    PROVIDE(__ISTACK0 = .); }
   
   CORE_SEC(.csa) (LCF_DSPR0_START + LCF_CSA0_OFFSET):
  {
     PROVIDE(__CSA0_END = .);   . = . + LCF_CSA0_SIZE;    PROVIDE(__CSA0 = .); }
}

看这个之前先了解一下PROVIDE 是干什么的。简单来说就是通过链接文件来定义了一下symbol,可以给程序使用。程序如果没有定义,就用这里面的定义。具体如下(翻译麻烦,不翻译了。)。

In some cases, it is desirable for a linker script to define a symbol only if it is referenced and
is not defined by any object included in the link. For example, traditional linkers defined the
symbol etext. However, ANSI C requires the user to be able to use etext as a function name
without encountering an error. The PROVIDE keyword may be used to define a symbol, such
as etext, only if it is referenced but not defined. The syntax is PROVIDE( = <
expression>).
Here is an example of using PROVIDE to define etext:
SECTIONS
{
.text :
{
∗(.text)
etext = .;
PROVIDE(etext = .);
}
}
In this example, if the program defines _etext (with a leading underscore), the linker will give
a multiple definition error. If, on the other hand, the program defines etext (with no leading
underscore), the linker will silently use the definition in the program. If the program references
etext but does not define it, the linker will use the definition in the linker script.

在set好A10 寄存器 (也就是SP 指针)后,这里加了一行汇编。

IFX_SSW_INLINE void Ifx_Ssw_DSYNC(void)
{
    
   __asm__ volatile ("dsync" : : : "memory");
}

什么意思呢:就是保证所有的数据都搞定了,在开始访问下一个数据,也就是说保证做CSA前A10搞好了。

To ensure memory coherency, a prior to any access to an active CSA memory location.
DSYNC instruction must be executed

随之而来初始化CSA。

具体CSA 是什么,这里直接copy一下之前写过的文章。

上下文查看

上下文保存了一些数据寄存器,地址寄存器以及程序状态字和链接字。上下文分为高级context和低级context, 高级context自动保存,低级context需要用户手动保存。这里以高级context为例。看一下怎么查看context使用情况。

连接好调试器,打开CPU 寄存器,这里会有高级context, 低级context以及一起其他的系统寄存器。

从芯片系统架构手册可知上下文的数据结构和内容如下图。
在这里插入图片描述
我们在调试器打开CPU 寄存器能找到PCXI,这个是链接字。这里以链接字为0x00370B70 为例。
在这里插入图片描述
通过取中间可用的信息来计算链接字对应RAM 空间位置。具体计算方式来自英飞凌系统架构手册,比较详细的介绍。实际上就是数据段加上偏移量。填零。
在这里插入图片描述
微软自带计算器 计算出 ram位置这里计算出来的是0x7002DC00 是上文我们提到的core0 的 ram空间。文档搬运工。。。
在这里插入图片描述
这时候可以使用调试器我这里使用的是Lauterbach可以查看内存信息。dump… 工具搬运工。。。

在这里插入图片描述
通过英飞凌系统架构手册里面对context的解析,可以看到上下文使用情况,以及具体数据。需要对每一个寄存器进行调查 可以查看手册。

上下文在任务切换过程中总是保存或者读取。新任务抢占老任务,老任务的上下文会被抢占,新任务结束,继续执行老任务,老任务的上下文会被重读恢复读取使用。后进先出的过程。那么下面看一下在哪些情况下上下文会被操作。
在这里插入图片描述
和上面说的一样,高级context保存是系统自动保存。在中断发生,进入trap,函数调用,换句话说 当系统发生需要使用另一个context的时候。高级context就会被自动保存。然而lower context则需要相应的指令去保存。这里提一下,lower 和 upper 对应的地址,对应的链接字,大小格式,都是一样的。比如配置了64个上下文位置,那么这是lower + upper 一共可以用的空间。所以可以通过程序状态字去查看这个context具体指的是lower or upper.

FCX, PCX 这两个分别的 free context List 和 Previous context List 对应的就是用过了的和可用的链接字的位置。那么问题来了,会不会存在用超了的现象,用超了会有措施吗。这里就到提到LCX 这个寄存器,指向的是最后的某个context 所以 当系统认为的 free context 和 LCX 相等时 就需要注意了。Context 马上就要超了。

举个梨子

当前在用的context时CSA2, 那么FCX 指向下一个可用的context就是CSA3, 在CSA3 里面指向的链接字就是CSA4,以此类推 直到LCX和最后一个。手册搬运工。。。

往前的话,上一个使用了的context的链接字就是CSA1.
在这里插入图片描述
新建一个context后,前一个用过了的 和 下一个可用的 context都会被更新,但是下一个可用后面的链表还是原来的样子。当CSA3被new出来之后就变成了这样。
在这里插入图片描述
自此 SP 指针 与 CSA 都已经被初始化完毕。

执行一次

isync 来确保 指令的操作完整性。

All preceding instructions are executed by the CPU. Then the pipeline is flushed before the next instruction is executed.

1.1.5 __StartUpSoftware_Phase4

开启内部看门狗,没什么好说的。

&MODULE_SCU.WDTCPU[0]

1.1.6 __StartUpSoftware_Phase5

如果有配置smu, 这里就可以开启SMU 模块,来监控芯片状态。

1.1.7 __StartUpSoftware_Phase6.

这里是和用户main 最接近的一次。
这里面执行两个事情。

  • 启动下一个core
void Ifx_Ssw_startCore(Ifx_CPU *cpu, unsigned int programCounter)
{
    
   /* Set the PC */
   cpu->PC.B.PC = (unsigned int)programCounter >> 1U;

   /* release boot halt mode if required */
   Ifx_CPU_SYSCON syscon;
   syscon = cpu->SYSCON;

   if (syscon.B.BHALT)
  {
    
       syscon.B.BHALT = 0U;
       cpu->SYSCON    = syscon;
  }

}
  • 运行至本core的main 函数
void __Core0_start(void)

所以这里之后真正的多核开始了并行。一步一步来。

1.2 多核启动

1.2.1 core0 start

  • 重新加载关闭看门狗

  • 通过写寄存器来决定是否开启P/D cache

/** \brief 920C, CPUx Program Control 0 */
#define CPU_PCON0 0x920C
/** \brief 9040, CPUx Data Memory Control Register */
#define CPU_DCON0 0x9040

然后对其他几个通用寄存器进行初始化。初始化方式和上面类似。这里不一一赘述。

/* Set A0 Pointer to access global variables with small data addressing */
   Ifx_Ssw_setAddressReg(a0, __SDATA1(0));

   /* These to be un commented if A8 and A9 are required to be initialized */
   Ifx_Ssw_setAddressReg(a8, __SDATA3(0));
   Ifx_Ssw_setAddressReg(a9, __SDATA4(0));

随之而来的就是 初始化中断向量表。注意这里中断向量表可以说是每个core都可以存在一个。互不干扰。

Os_InitializeVectorTable();

这个中断向量表是配置在os 里面的中断。里面包含中断服务函数,与优先级。在os 的实现代码里面是汇编。部分如下。详细可查看文件:Os_vectors.c

__asm__("\n\
 .file \"Os_vectors.s\"\n\
 .section \".os_interrupt_code.osinterrupts\", \"ax\", @progbits\n\
 #==========================================\n\
 # Os_InterruptVectorTable0\n\
 #==========================================\n\
 .align 13\n\
 .global Os_InterruptVectorTable0 ;# Start of the table\n\
Os_InterruptVectorTable0:\n\

初始化了中断向量表,后面就需要立即给中断进行栈分配。

Ifx_Ssw_MTCR(CPU_ISP, (unsigned int)__ISTACK(0));

注意这里中断有自己独立的栈空间。和前面任务栈一个意思。

由于看门狗是自动起的。下面需要进行ram 初始化,这里先关闭看门狗,如下操作。

   Ifx_Ssw_setCpuEndinitInline(&MODULE_SCU.WDTCPU[0], cpuWdtPassword);

   /* CPU and safety watchdogs are enabled by default,
    * C initialization functions are not servicing the watchdogs.
    */
   Ifx_Ssw_disableCpuWatchdog(&MODULE_SCU.WDTCPU[0], cpuWdtPassword);
   Ifx_Ssw_disableSafetyWatchdog(safetyWdtPassword);

前面已经对中断,对栈空间,对通用寄存器进行了初始化与配置。

下面开始对ram 进行初始化。ram初始化主要有两个方面。一个是从对应的falsh 拿数值放到ram 里面。

一个是初始化直接是0. 那么怎么,哪里,如何从flash 里拿到对应的ram呢?CPU 怎么知道的。

前面有个文章已经进行说明。也是通过链接文件的方式。copytabble 方式。

这里给出接口函数。

IFX_SSW_INLINE void Ifx_Ssw_C_InitInline(void)
{
    
   Ifx_Ssw_CTablePtr pBlockDest, pBlockSrc;
   unsigned int      uiLength, uiCnt;
   unsigned int     *pTable;
   /* clear table */
   pTable = (unsigned int *)&__clear_table;

   while (pTable)
  {
    
       pBlockDest.uiPtr = (unsigned int *)*pTable++;
       uiLength         = *pTable++;

       /* we are finished when length == -1 */
       if (uiLength == 0xFFFFFFFF)
      {
    
           break;
      }

       uiCnt = uiLength / 8;

       while (uiCnt--)
      {
    
           *pBlockDest.ullPtr++ = 0;
      }

       if (uiLength & 0x4)
      {
    
           *pBlockDest.uiPtr++ = 0;
      }

       if (uiLength & 0x2)
      {
    
           *pBlockDest.usPtr++ = 0;
      }

       if (uiLength & 0x1)
      {
    
           *pBlockDest.ucPtr = 0;
      }
  }

   /* copy table */
   pTable = (unsigned int *)&__copy_table;

   while (pTable)
  {
    
       pBlockSrc.uiPtr  = (unsigned int *)*pTable++;
       pBlockDest.uiPtr = (unsigned int *)*pTable++;
       uiLength         = *pTable++;

       /* we are finished when length == -1 */
       if (uiLength == 0xFFFFFFFF)
      {
    
           break;
      }

       uiCnt = uiLength / 8;

       while (uiCnt--)
      {
    
           *pBlockDest.ullPtr++ = *pBlockSrc.ullPtr++;
      }

       if (uiLength & 0x4)
      {
    
           *pBlockDest.uiPtr++ = *pBlockSrc.uiPtr++;
      }

       if (uiLength & 0x2)
      {
    
           *pBlockDest.usPtr++ = *pBlockSrc.usPtr++;
      }

       if (uiLength & 0x1)
      {
    
           *pBlockDest.ucPtr = *pBlockSrc.ucPtr;
      }
  }
}

ram已经初始化完毕。可以开启看门狗。

Ifx_Ssw_enableCpuWatchdog(&MODULE_SCU.WDTCPU[0], cpuWdtPassword);

终于到了core0 的main 函数。

void core0_main (void)

好这里我们等一下,前面说到,这个函数做了两个事情,一个是运行至core0的main. 还有一个是 __Core1_start。

对了现在两个核独立的跑了,我们需要同步分析一下另外一个core.

1.2.2 __Core1_start

首先我们看一下start core 的接口函数什么样的。

void Ifx_Ssw_startCore(Ifx_CPU *cpu, unsigned int programCounter)
{
    
   /* Set the PC */
   cpu->PC.B.PC = (unsigned int)programCounter >> 1U;

   /* release boot halt mode if required */
   Ifx_CPU_SYSCON syscon;
   syscon = cpu->SYSCON;

   if (syscon.B.BHALT)
  {
    
       syscon.B.BHALT = 0U;
       cpu->SYSCON    = syscon;
  }

}

其实就是把对应的core的 寄存器 置为,进而让核运行起来。然后第二个参数,就是即将运行的函数。

Ifx_Strict_32Bit BHALT:1;         /**< \brief [24:24] Boot Halt - BHALT (rw) */

分析一下

void __Core1_start(void)

这里与前面分析的Core0 一致。只是不需要对全局ram进行初始化。因为前面已经初始化了。

还有不同的点就是这里可能会拉起后面的core. 如core2.

然后运行至自己的main函数。即:

void core1_main (void)
{
    
 volatile unsigned short LoopFlag = 1U;
 unsigned short cpuWdtPassword;
 #if ((defined IFX_CFG_SSW_ENABLE_TRICORE0) && (IFX_CFG_SSW_ENABLE_TRICORE0 == 0))
 unsigned short safetyWdtPassword;
 #endif
 ENABLE();
 /*
  * !!WATCHDOG1 IS DISABLED HERE!!
  * Enable the watchdog in the demo if it is required and also service the watchdog periodically
  * */

 #if ((defined IFX_CFG_SSW_ENABLE_TRICORE0) && (IFX_CFG_SSW_ENABLE_TRICORE0 == 0))
 safetyWdtPassword = Ifx_Ssw_getSafetyWatchdogPassword();
 Ifx_Ssw_disableSafetyWatchdog(safetyWdtPassword);
 #endif

 cpuWdtPassword = Ifx_Ssw_getCpuWatchdogPassword(&MODULE_SCU.WDTCPU[1]);
 Ifx_Ssw_disableCpuWatchdog(&MODULE_SCU.WDTCPU[1], cpuWdtPassword);

 main();

 while(LoopFlag == 1U)
{
    

}
}

所以从上面也能看出来,多核的启动是一个拉着一个的,

不是说core0 直接把所有都拉起来的。

后面的每一个core 怎么拉起来, 这里就不介绍了,和core1被拉起来的方式一致。

那么也就是说,我们说到了 两个main 函数。

core0 和 core1 都已经运行到了自己的main 函数。这里就涉及到同步的概念了,先期的core 要等一会 暂时还没有启动的core. 是什么意思呢?我们继续说。

1.3 各自main函数

1.3.1 core0 的main 函数

extern int main(void);
void core0_main (void)
{
    
 volatile unsigned short LoopFlag = 1U;
 unsigned short cpuWdtPassword;
 unsigned short safetyWdtPassword;


 ENABLE();
 /*
  * !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!!
  * Enable the watchdog in the demo if it is required and also service the watchdog periodically
  * */
 cpuWdtPassword = Ifx_Ssw_getCpuWatchdogPassword(&MODULE_SCU.WDTCPU[0]);
 safetyWdtPassword = Ifx_Ssw_getSafetyWatchdogPassword();
 Ifx_Ssw_disableCpuWatchdog(&MODULE_SCU.WDTCPU[0], cpuWdtPassword);
 Ifx_Ssw_disableSafetyWatchdog(safetyWdtPassword);

 main();

 while (LoopFlag == 1U)
{
    

}
}

不难看出基操后面跟了个main() 然后就是while 死循环了。由此猜想。走完这个main. 可能就被os 接管了。换句话说,分析到这里,基本和autosar 没有一毛钱关系,除了,前面的中断向量表。不过那个中断其实mcal也可以实现。只是为了配合os 所以才有的。好 现在我们来分析这个

int main(void)

这实际是个宏展开。我来给大家展开一下就一目了然了。

extern int main(void);

int main(void)
{
    
(
  {
    
     unsigned newval = (0x200 | (({
     unsigned res; __asm__ volatile ("mfcr %0," "0xfe04" : "=d" (res) : : "memory"); res; })));
      __asm__ volatile ("mtcr " "0xfe04" ",%0" : : "d" (newval) : "memory");
  }
);
 
 __asm__ volatile ("isync" : : : "memory");
     
   Os_StartCoreGate();
   
   inner_main();
   
   return 0;
     
}
     
void inner_main(void)
{
    
   Dem_SetOperationCycleState(0u, 0);
   /*Invoking the ECUM Init for ECU Initialization, never return */
   EcuM_Init();
}

这里也看出在EcuM_Init() 的时候,才真正运行到Autosar 协议栈。

好,那我们看看前面是什么。

有一个函数:

FUNC(void, OS_CODE) Os_StartCoreGate(void) {
    
 uint32 core = OS_MFCR(0xfe1c);

 /* Prevent secondary cores starting before given permission in Os_Cbk_StartCore() */
 while ((core == 1U) && (Os_StartBlock[0] != 0xa55a5aa5U)) {
    OS_NOP();}
 while ((core == 2U) && (Os_StartBlock[1] != 0xa55a5aa5U)) {
    OS_NOP();}
 while ((core == 3U) && (Os_StartBlock[2] != 0xa55a5aa5U)) {
    OS_NOP();}
 while ((core == 4U) && (Os_StartBlock[3] != 0xa55a5aa5U)) {
    OS_NOP();}
 while ((core == 5U) && (Os_StartBlock[4] != 0xa55a5aa5U)) {
    OS_NOP();}
}

这是在干啥呢。哦。这里貌似在同步每一个core. 好不着急,我们继续往下看。

void inner_main(void)
{
    
   Dem_SetOperationCycleState(0u, 0);
   /*Invoking the ECUM Init for ECU Initialization, never return */
   EcuM_Init();
}

暂不介绍Dem 模块,这里进行了EcuM_Init().

当然这里会对mcu外设,等寄存器进行初始化。这里我们先不说,后面有专门的文章来介绍EcuM的相关说明。我们只说EcuM 是怎么拉起来Os的。

在EcuM 初始化list0,list1 之后,会调用

EcuM_Prv_StartSlaveCores();
void EcuM_Prv_StartSlaveCores( void )
{
    
/*local variables*/
   StatusType dataStatus_chr = E_NOT_OK;
   uint16 cntrLoopCtr_u16;
/*Starting all the OS Cores in a loop*/


       for( cntrLoopCtr_u16=0; cntrLoopCtr_u16<ECUM_CFG_NUM_OS_CORES ; cntrLoopCtr_u16++ )
      {
    
           StartCore( cntrLoopCtr_u16, &dataStatus_chr);
           if(dataStatus_chr != E_OK)
          {
    
               /* StartCore Failed*/

               EcuM_ErrorHook(ECUM_E_START_CORE_FAILED);

          }
      }

}

也就是说会对后面的每一个core进行

StartCore( cntrLoopCtr_u16, &dataStatus_chr);

这里就是Autosar Os对应的start core.

FUNC(void, OS_CODE) Os_StartCore(CoreIdType CoreID, Os_StatusRefType Status) {
    
 *Status = E_OK; /* [$UKS 1628] */
 if (CoreID >= 3U) {
    
   *Status = E_OS_ID; /* [$UKS 1629] */
} else if (*Os_const_coreconfiguration[CoreID].state > 1U) {
    
   *Status = E_OS_ACCESS; /* [$UKS 1631] */
} else if (*Os_const_coreconfiguration[CoreID].state != 0U) {
    
   *Status = E_OS_STATE; /* [$UKS 1632] [$UKS 1633] */
} else {
    
   /* OK */
}
 if (*Status == E_OK) {
    
   /* [$UKS 1634] */
  (*Os_const_coreconfiguration[CoreID].state) = 1U;  /* Started */
   if ((CoreIdType)OS_MFCR(0xfe1c) != CoreID) {
    
     *Status = Os_Cbk_StartCore(CoreID);
  }
}
 return;
} /* StartCore */

这里可以发现,在起core的时候有callback函数。那这个对应的是什么呢。

FUNC(StatusType, OS_CALLOUT_CODE) Os_Cbk_StartCore(uint16 CoreID) {
    
 StatusType ret = E_OS_ID;

 if (CoreID == 1U) {
    
   Os_StartBlock[0] = 0xa55a5aa5U;
#ifndef _lint /* Skip lint checks on compiler-specific ornamentations */
   CPU1_PC.U = (uint32)cstart;
   CPU1_SYSCON.B.BHALT = 0U;
#endif
   ret = E_OK;

} else if (CoreID == 2U) {
    
   Os_StartBlock[1] = 0xa55a5aa5U;
#ifndef _lint /* Skip lint checks on compiler-specific ornamentations */
   CPU2_PC.U = (uint32)cstart;
   CPU2_SYSCON.B.BHALT = 0U;
#endif
   ret = E_OK;

} else if (CoreID == 3U) {
    
   Os_StartBlock[2] = 0xa55a5aa5U;
#ifndef _lint /* Skip lint checks on compiler-specific ornamentations */
   CPU3_PC.U = (uint32)cstart;
   CPU3_SYSCON.B.BHALT = 0U;
#endif
   ret = E_OK;

} else if (CoreID == 4U) {
    
   Os_StartBlock[3] = 0xa55a5aa5U;
#ifndef _lint /* Skip lint checks on compiler-specific ornamentations */
   CPU4_PC.U = (uint32)cstart;
   CPU4_SYSCON.B.BHALT = 0U;
#endif
   ret = E_OK;

} else if (CoreID == 5U) {
    
   Os_StartBlock[4] = 0xa55a5aa5U;
#ifndef _lint /* Skip lint checks on compiler-specific ornamentations */
   CPU5_PC.U = (uint32)cstart;
   CPU5_SYSCON.B.BHALT = 0U;
#endif
   ret = E_OK;
} else {
    
   /* Not an expected core! */
}
 return ret;
}

这里是不是豁然开朗,其实core本身已经启动了。这里os 同步一下。给每一个Os_StartBlock[] 进行赋值。

也就是说当core0 走了EcuM_Init 到这里之后,才可以说 来识别每一个core 同步。core0 来给每一个core进行赋值。

这样各自core 就不会卡在 下面函数里了。

FUNC(void, OS_CODE) Os_StartCoreGate(void) {
    
 uint32 core = OS_MFCR(0xfe1c);

 /* Prevent secondary cores starting before given permission in Os_Cbk_StartCore() */
 while ((core == 1U) && (Os_StartBlock[0] != 0xa55a5aa5U)) {
    OS_NOP();}
 while ((core == 2U) && (Os_StartBlock[1] != 0xa55a5aa5U)) {
    OS_NOP();}
 while ((core == 3U) && (Os_StartBlock[2] != 0xa55a5aa5U)) {
    OS_NOP();}
 while ((core == 4U) && (Os_StartBlock[3] != 0xa55a5aa5U)) {
    OS_NOP();}
 while ((core == 5U) && (Os_StartBlock[4] != 0xa55a5aa5U)) {
    OS_NOP();}
}

当每一个core同步之后。除了core0 其他的core都跑到各自的

inner_main();

因为那些core暂时没有配置EcuM 所以就直接到了while(1)

也就是说,等着os 来进行调度。

core0 这时候也进入了

EcuM_Prv_StartOS();

即:

StartOS(x)
{
    Os_StackBase[OS_MFCR(0xfe1c)] = Os_GetSP();
if (Os_StartOS(x)) {
    while(Os_Cbk_Idle()) {
    } /* [$UKS 161] */;
for(;;)
{
    } /* [$UKS 16] */
}
}

到这里为止 多核系统启动完毕。从裸机让Autosar Os 进行接管。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_48120109/article/details/134044616

智能推荐

海康威视网络摄像头开发流程(五)------- 直播页面测试_ezuikit 测试的url-程序员宅基地

文章浏览阅读3.8k次。1、将下载好的萤石js插件,添加到SoringBoot项目中。位置可参考下图所示。(容易出错的地方,在将js插件在html页面引入时,发生路径错误的问题)所以如果对页面中引入js的路径不清楚,可参考下图所示存放路径。2、将ezuikit.js引入到demo-live.html中。(可直接将如下代码复制到你创建的html页面中)<!DOCTYPE html><html lan..._ezuikit 测试的url

如何确定组态王与多动能RTU的通信方式_组态王ua-程序员宅基地

文章浏览阅读322次。第二步,在弹出的对话框选择,设备驱动—>PLC—>莫迪康—>ModbusRTU—>COM,根据配置软件选择的协议选期期,这里以此为例,然后点击“下一步”。第四步,把使用虚拟串口打勾(GPRS设备),根据需要选择要生成虚拟口,这里以选择KVCOM1为例,然后点击“下一步”设备ID即Modbus地址(1-255) 使用DTU时,为下485接口上的设备地址。第六步,Modbus的从机地址,与配置软件相同,这里以1为例,点击“下一步“第五步,Modbus的从机地址,与配置软件相同,这里以1为例,点击“下一步“_组态王ua

npm超详细安装(包括配置环境变量)!!!npm安装教程(node.js安装教程)_npm安装配置-程序员宅基地

文章浏览阅读9.4k次,点赞22次,收藏19次。安装npm相当于安装node.js,Node.js已自带npm,安装Node.js时会一起安装,npm的作用就是对Node.js依赖的包进行管理,也可以理解为用来安装/卸载Node.js需要装的东西_npm安装配置

火车头采集器AI伪原创【php源码】-程序员宅基地

文章浏览阅读748次,点赞21次,收藏26次。大家好,小编来为大家解答以下问题,python基础训练100题,python入门100例题,现在让我们一起来看看吧!宝子们还在新手村练级的时候,不单要吸入基础知识,夯实自己的理论基础,还要去实际操作练练手啊!由于文章篇幅限制,不可能将100道题全部呈现在此除了这些,下面还有我整理好的基础入门学习资料,视频和讲解文案都很齐全,用来入门绝对靠谱,需要的自提。保证100%免费这不,贴心的我爆肝给大家整理了这份今天给大家分享100道Python练习题。大家一定要给我三连啊~

Linux Ubuntu 安装 Sublime Text (无法使用 wget 命令,使用安装包下载)_ubuntu 安装sumlime text打不开-程序员宅基地

文章浏览阅读1k次。 为了在 Linux ( Ubuntu) 上安装sublime,一般大家都会选择常见的教程或是 sublime 官网教程,然而在国内这种方法可能失效。为此,需要用安装包安装。以下就是使用官网安装包安装的教程。打开 sublime 官网后,点击右上角 download, 或是直接访问点击打开链接,即可看到各个平台上的安装包。选择 Linux 64 位版并下载。下载后,打开终端,进入安装..._ubuntu 安装sumlime text打不开

CrossOver for Mac 2024无需安装 Windows 即可以在 Mac 上运行游戏 Mac运行exe程序和游戏 CrossOver虚拟机 crossover运行免安装游戏包-程序员宅基地

文章浏览阅读563次,点赞13次,收藏6次。CrossOver24是一款类虚拟机软件,专为macOS和Linux用户设计。它的核心技术是Wine,这是一种在Linux和macOS等非Windows操作系统上运行Windows应用程序的开源软件。通过CrossOver24,用户可以在不购买Windows授权或使用传统虚拟机的情况下,直接在Mac或Linux系统上运行Windows软件和游戏。该软件还提供了丰富的功能,如自动配置、无缝集成和实时传输等,以实现高效的跨平台操作体验。

随便推点

一个用聊天的方式让ChatGPT写的线程安全的环形List_为什么gpt一写list就卡-程序员宅基地

文章浏览阅读1.7k次。一个用聊天的方式让ChatGPT帮我写的线程安全的环形List_为什么gpt一写list就卡

Tomcat自带的设置编码Filter-程序员宅基地

文章浏览阅读336次。我们在前面的文章里曾写过Web应用中乱码产生的原因和处理方式,旧文回顾:深度揭秘乱码问题背后的原因及解决方式其中我们提到可以通过Filter的方式来设置请求和响应的encoding,来解..._filterconfig selectencoding

javascript中encodeURI和decodeURI方法使用介绍_js encodeur decodeurl-程序员宅基地

文章浏览阅读651次。转自:http://www.jb51.net/article/36480.htmencodeURI和decodeURI是成对来使用的,因为浏览器的地址栏有中文字符的话,可以会出现不可预期的错误,所以可以encodeURI把非英文字符转化为英文编码,decodeURI可以用来把字符还原回来_js encodeur decodeurl

Android开发——打包apk遇到The destination folder does not exist or is not writeable-程序员宅基地

文章浏览阅读1.9w次,点赞6次,收藏3次。前言在日常的Android开发当中,我们肯定要打包apk。但是今天我打包的时候遇到一个很奇怪的问题Android The destination folder does not exist or is not writeable,大意是目标文件夹不存在或不可写。出现问题的原因以及解决办法上面有说报错的中文大意是:目标文件夹不存在或不可写。其实问题就在我们的打包界面当中图中标红的Desti..._the destination folder does not exist or is not writeable

Eclipse配置高大上环境-程序员宅基地

文章浏览阅读94次。一、配置代码编辑区的样式 <1>打开Eclipse,Help —> Install NewSoftware,界面如下: <2>点击add...,按下图所示操作: name:随意填写,Location:http://eclipse-color-th..._ecplise高大上设置

Linux安装MySQL-5.6.24-1.linux_glibc2.5.x86_64.rpm-bundle.tar_linux mysql 安装 mysql-5.6.24-1.linux_glibc2.5.x86_6-程序员宅基地

文章浏览阅读2.8k次。一,下载mysql:http://dev.mysql.com/downloads/mysql/; 打开页面之后,在Select Platform:下选择linux Generic,如果没有出现Linux的选项,请换一个浏览器试试。我用的谷歌版本不可以,换一个别的浏览器就行了,如果还是不行,需要换一个翻墙的浏览器。 二,下载完后解压缩并放到安装文件夹下: 1、MySQL-client-5.6.2_linux mysql 安装 mysql-5.6.24-1.linux_glibc2.5.x86_64.rpm-bundle