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

智能推荐

HTML5 游戏开发快速提升_html5游戏开发 slg游戏-程序员宅基地

文章浏览阅读3.1k次。带你快速掌握html5游戏开发基本功,助你快速进阶游戏开发_html5游戏开发 slg游戏

Windows下kafka生成消费者报错:Missing required argument “[zookeeper]“_missing required argument "[zookeeper]-程序员宅基地

文章浏览阅读4.3k次。.\kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic kafka-test-topic --from-beginning上面的语句就是在Windos下 kafka生成消费者的命令行,这命令行是我从百度上找的,但一直是报错。仔细看报错是缺少一个参数–zookeeper,然后我W3Cshool关于kafka的教程上看到Linux系统下生成消费者的命令行。如下:bin/kafka-console-consumer.sh._missing required argument "[zookeeper]

Qt Creator中,include路径包含过程(或如何找到对应的头文件)_qt include路径-程序员宅基地

文章浏览阅读5.9w次,点赞11次,收藏29次。Qt Creator中,include路径包含过程(或如何找到对应的头文件)利用Qt Creator开发程序时,需要包含利用#include来添加头文件。大家都知道,#include 对于后者,路径比较直观,容易理解。如#include "lyc/daniel.h",路径在当前目录的lyc文件夹下。(文件包含是可以嵌套的。)下面重点追溯一下Qt的标准库头文件的路径包含情况。_qt include路径

Fortify代码扫描问题及修复_input validation and representation-程序员宅基地

文章浏览阅读1.6w次,点赞8次,收藏39次。静态代码扫描常见问题及修复风险类型原因Code Correctness: Erroneous String Compare字符串的对比使用错误方法Cross-Site ScriptingWeb浏览器发送非法数据,导致浏览器执行恶意代码Dead Code: Expression is Always true表达式的判断总是trueDead Code: Unused Method没有使用的方法HTTP Response Splitting含有未验证的数据_input validation and representation

探索Svelte SVG Patterns:创新SVG图案生成器-程序员宅基地

文章浏览阅读278次,点赞4次,收藏9次。探索Svelte SVG Patterns:创新SVG图案生成器项目地址:https://gitcode.com/catchspider2002/svelte-svg-patterns该项目[[链接]][1]是一个基于Svelte框架构建的SVG图案生成工具,由开发者catchspider2002维护。它允许用户通过简单的交互式界面创建独特且可自定义的SVG图案,这些图案可以用于网页背景、图标...

光场深度估计(Light Field Depth Estimation)-程序员宅基地

文章浏览阅读1.8w次,点赞23次,收藏94次。本文将介绍光场领域进行深度估计的相关研究。In this post, I’ll introduce some depth estimation algorithms using Light field information. Here is some of the code.研究生阶段的研究方向是光场深度信息的恢复。再此做一些总结,以便于让大家了解光场数据处理的一般步骤以及深度估计的相关..._光场深度估计

随便推点

项目管理中冲突的六种解决方法_项目 冲突 解决的案例-程序员宅基地

文章浏览阅读2.8w次,点赞3次,收藏17次。解决项目冲突的主要责任在于项目经理,项目经理可以使用以下六种方法来解决冲突:面对/解决问题(confronting/problem solving):通过审查备选方案,把冲突当作需要解决的问题来处理;需要以“取舍”的态度进行公开对话。问题解决就是冲突各方一起积极地定义问题、收集问题的信息、制定解决方案,最后直到选择一个最合适的方案来解决冲突,此时为双赢或多赢。但在这个过程中,需要公开地协商_项目 冲突 解决的案例

VM虚拟机安装Windows XP Service Pack 3 (x86)_windows_xp_professional_with_service_pack_3_x86-程序员宅基地

文章浏览阅读2.9k次,点赞2次,收藏3次。Windows版本  Windows XP发行于2001年10月25日,于2014年4月8日停止支持,XP取自Experience。Windows 7发行于2009年10月22日。  Windows XP Service Pack 3 (x86):SP(service pack)是指服务补丁包,01年发布的XP不带补丁,第一次补丁版本叫SP2,14年最后一个补丁版本叫SP3。下载ISO  操作系统站:https://msdn.itellyou.cn/。值得一提,MSDN是原装系统,跟ghost系统_windows_xp_professional_with_service_pack_3_x86

vue-cli指定版本安装_vue-cli-service 选哪个版本-程序员宅基地

文章浏览阅读1.2w次,点赞16次,收藏32次。安装新的版本前,需要先把之前安装的版本卸载掉。vue卸载:npm uninstall vue-cli -g(3.0以下版本卸载)npm uninstall -g @vue/cli(3.0以上版本卸载)vue安装:npm install -g @vue/cli (安装的是最新版)npm install [email protected] (指定版本安装【指定版本为3.0以下版本】,其中2.9.6为版本号)npm install -g @vue/[email protected](指定版本安装【指定版本为3.0以上版本】,_vue-cli-service 选哪个版本

Verilog SPI-Flash读写总线控制模块_qspi flash verilog-程序员宅基地

文章浏览阅读3.6k次,点赞2次,收藏59次。`timescale 1ns / 1ps//////////////////////////////////////////////////////////////////////////////////// Company: // Engineer: // // Create Date: 2019/11/26 11:55:56// Design Name: // Module Name: spi_driver// Project Name: // Target Devices: //._qspi flash verilog

Python实例1:题目:有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?_pycharm有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?-程序员宅基地

文章浏览阅读1w次,点赞6次,收藏29次。这是一个相对比较简单的数学题,首先我们先对题目进行分析分析:三位数,个位、十位、百位分别都可以是1、2、3、4这四个数字,但要求互不相同且无重复,就要求个位不等于十位,个位不等于百位,十位不等于百位分析完毕后,我们可以就可以开始编写代码了!方法1:首先是相对比较基础的,利用for循环嵌套附加if判断筛选实现i = 0for x in range(1, 5): for y in range(1, 5): for z in range(1, 5): _pycharm有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?

Android 中常用的跨进程通信和跨线程通信方法总结_android 跨线程消息-程序员宅基地

文章浏览阅读708次。优点:简化了线程间通信:EventBus提供了一种简单的方式来进行线程间通信,开发人员无需手动处理线程切换和消息传递的细节。松耦合的组件通信:通过EventBus,组件之间可以进行松耦合的通信,发送者和接收者之间没有直接的依赖关系。线程切换方便:EventBus允许在发布事件时指定事件的接收线程,从而方便地在不同线程之间切换。1.优点:网络通信能力:Socket通信可以在网络层面上进行进程间通信,适用于跨网络的通信需求。有多个进程和线程就会涉及到跨进程通信或跨线程通信,用户状态同步,UI刷新等。_android 跨线程消息

推荐文章

热门文章

相关标签