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

智能推荐

练习7-3 将数组中的数逆序存放 (20分) 本题要求编写程序,将给定的n个整数存入数组中,将数组中的这n个数逆序存放,再按顺序输出数组中的元素。_题目内容:编写程序,将给定的n个整数存入数组中,将数组中的这n个数逆序存放并输出-程序员宅基地

文章浏览阅读4w次,点赞19次,收藏42次。本题要求编写程序,将给定的n个整数存入数组中,将数组中的这n个数逆序存放,再按顺序输出数组中的元素。输入格式:输入在第一行中给出一个正整数n(1≤n≤10)。第二行输入n个整数,用空格分开。输出格式:在一行中输出这n个整数的处理结果,相邻数字中间用一个空格分开,行末不得有多余空格。输入样例:410 8 1 2输出样例:2 1 8 10#include<stdio.h>int main(){ int n; scanf("%d",&n); in_题目内容:编写程序,将给定的n个整数存入数组中,将数组中的这n个数逆序存放并输出

MovingPandas时空轨迹数据探索和分析——上海市出租车数据集_上海出租车数据-程序员宅基地

文章浏览阅读2.5k次,点赞24次,收藏54次。MovingPandas是一个基于Python的地理数据处理库,专门用于处理移动物体的轨迹数据。本文用MovingPandas对上海市出租车轨迹数据进行探索和分析。_上海出租车数据

fprintf()、fscanf()、fwrite()、fread()用法与区别-程序员宅基地

文章浏览阅读1.3k次,点赞2次,收藏31次。其中,fprintf()和fscanf()的用法类似于printf()和scanf(),只是输出和输入的地方改成了文件流。类似于scanf()函数,但是scanf()用于从标准输入设备(键盘)中读取数据,而fscanf()用于从文件中读取数据。返回值:成功读取并匹配的输入项数,如果没有匹配到任何数据,则返回0,如果发生错误,则返回一个负数。fprintf():按照指定的格式将数据输出到指定的文件流中。fread():从指定的文件流中读取指定数量的数据块。fwrite():将指定的数据块写入指定的文件流中。_fprintf

Windows 10搭建FTP 服务器保姆教学_win10ftr服务器-程序员宅基地

文章浏览阅读1k次。windows10搭建FTP服务器保姆教程:Ⅰ。我们需要打开windows的FTP服务器功能。Ⅱ。我们需要切断防火墙对FTP服务器的拦截。Ⅲ。新建一个本地用户。Ⅳ。新建一个存放传输文件的文件夹Ⅴ。搭建FTP服务器。_win10ftr服务器

Unity插件——Odin 学习笔记(一)_unity odin-程序员宅基地

文章浏览阅读2.6w次,点赞27次,收藏108次。本文章是为了记录学习Unity插件Odin,使用该插件可以让我们更快速便捷的开发Unity工具前言Unity原生编辑编辑器的方法有两种——IMGUI和新的UIElement1.UIElement我之前做过介绍,开发模式类似于HTML+CSS,如果有类似经验的人开发及相对容易,但是就目前而言不太适合编辑器开发,我觉得比较适合取代UGUI进行UI开发2.IMGUI入门比较简单,使用Edit..._unity odin

python 数据类型介绍_python中数据类型的作用-程序员宅基地

文章浏览阅读1.8k次。在 Python 中,数据类型是区分数据的种类和存储方式的标识符。它定义了数据的取值范围、占用空间大小、可操作特性等。Python 中常见的数据类型包括数字、字符串、列表、元组、集合和字典等。数据类型在编程中的作用主要有以下几个方面:不同的数据类型需要占用不同的内存空间,因此在内存空间的管理上,数据类型具有重要的作用。例如,在处理大量数据时,选择合适的数据类型可以有效地减少内存占用,提高程序的执行效率。数据类型定义了数据的表达方式和可操作性,使得程序能够对不同类型的数据进行有效的处理。_python中数据类型的作用

随便推点

Javase面向对象8:设计模式、枚举、注解、单元测试、包装类_如何对枚举类的valueof方法进行单元测试-程序员宅基地

文章浏览阅读90次。目录1. 笔记1.1 设计模式1.2 枚举1.3 注解1.4 单元测试2. 练习2.1 枚举类型常用方法示例代码2.2 枚举类实现接口2.3 注解示例代码1. 笔记1.1 设计模式1. 设计模式 ★ 23种-->就是前辈智慧的结晶(换一种方式实现功能) 1.1 单例模式 一个类对外只提供一个对象! a. 构造器私有化(不让外界new对象) b. 在本类内实例_如何对枚举类的valueof方法进行单元测试

PyCharm入门教程——保存和还原更改_pycharm您的本地更改将被合并覆盖。 提交、存储或还原您的更改以继续-程序员宅基地

文章浏览阅读4.9k次。PyCharm最新版本下载JetBrains PyCharm是一种Python IDE,其带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具。此外,该IDE提供了一些高级功能,以用于Django框架下的专业Web开发。PyCharm会自动保存您在文件中所做的更改。保存由各种事件触发,例如编译、运行、调试、执行版本控制操作、关闭文件或项目或完全退出IDE。实际事件是预定义的,..._pycharm您的本地更改将被合并覆盖。 提交、存储或还原您的更改以继续

机器学习-决策树:python “AttributeError: '_csv.reader' object has no attribute'next'”_attributeerror: '_csv.writer' object has no attrib-程序员宅基地

文章浏览阅读532次。(1)reader.next()改为next(reader)(2)open(‘AllElectronics.csv’, ‘rb’)中的rb改成rt_attributeerror: '_csv.writer' object has no attribute 'writeheader

银行卡验证工具类分享_private static card card = card.getinstance();-程序员宅基地

文章浏览阅读4.9k次。用于验证的请求接口:https://ccdcapi.alipay.com/validateAndCacheCardInfo.json?_input_charset=utf-8&cardNo=6217002430035835629&cardBinCheck=true返回:{“bank”:”CCB”,”validated”:true,”cardType”:”DC”,”key”:”621700243003_private static card card = card.getinstance();

fatal error C1083: 无法打开包括文件: “Eigen\Dense”: No such file or directory_c1083无法打开包括文件: “eigen/dense”: no such file or dire-程序员宅基地

文章浏览阅读7.9k次,点赞18次,收藏30次。参考资料:CSDN、博客园等网上众多资料Eigen库,哎,感觉自己好蠢~我的包含路径是到你的路径…\Eigen我include<Eigen\Dense>标红,其实只要include就行,因为你的路径已经包含到Eigenl了这里要记一下的是你的包含路径到你要找的库的前一级用图来解释一下吧!我的Eigen库路径如下先来一波错误示范:在这里我们要使用Eigen下的Dense..._c1083无法打开包括文件: “eigen/dense”: no such file or directoryh:\work\6

科研绘图学习笔记1_科研笔记1:科研绘图-程序员宅基地

文章浏览阅读182次。在选定对应的色轮配色方案后,我们可根据它提供的 HEX 颜色码或 R、G、B 值进行配色的拾取,从而完成插图颜色的选择。ColorBrewer 2.0 是一个专业的在线配色方案网站,它提供了大量的颜色搭配主题,这些主题是众多绘图工具(如 Matplotlib、ggplot2 等)内置的绘图颜色主题。要避免出现文字较少、图表较多的情况,即无须将原始数据和中间处理过程涉及的插图全部展示在论文中,而应在有复杂和多维数据的情况下,提高精选插图的能力,而非简单地堆砌插图。在科研论文配图绘制的过程中应用较少。_科研笔记1:科研绘图

推荐文章

热门文章

相关标签