1.解析AUTOSAR Startup
首先,从配置上找到工程的启动入口
/* CODE_SETUP */
_CODE_SETUP_START align(4) :> CODE_SETUP
__CODE_SETUP_START = . ;
. = align(4);
_Startup_Code_START = . ;
__Startup_Code_START = . ;
.brsStartup align (4) :> .
_RESET = brsStartupEntry;
_start = brsStartupEntry;
_brsStartupEntry = brsStartupEntry; |
从这个配置可以看到, brsStartupEntry 是这个工程的入口。
然后,仿真设个断点看看:
对应的源码是
/* Description: Entry point for all cores */
BRS_SECTION_CODE ( brsStartup )
BRS_GLOBAL (brsStartupEntry)
BRS_LABEL (brsStartupEntry)
// … …
BRS_BRANCH (brsPreAsmStartupHook) |
继续看仿真
后面它会跳到 brsStartupZeroInitLoop ,那么这个brsStartupZeroInitLoop做什么的呢?
/* Description: Initialize memory blocks and areas with zero */
BRS_GLOBAL (brsStartupZeroInitLoop)
BRS_LABEL (brsStartupZeroInitLoop)
/* read ID of actual running Core into Register 16 */
BRS_READ_COREID (r16)
/* Initialize memory sections with zeros */
#if defined (VLINKGEN_ZERO_INIT_BLOCK_COUNT_STARTUP)
# if (VLINKGEN_ZERO_INIT_BLOCK_COUNT_STARTUP > 1uL )
__as1 (mov _vLinkGen_ZeroInitBlocksArrayStartup , r11)
BRS_LABEL (_startup_block_zero_init_start)
__as1 (mov r11 , r12)
__as2 (addi 12 , r11, r11)
__as1 (ld.w 0 [r12], r13) /* get start address */
__as1 (ld.w 4 [r12], r14) /* get end address */
__as1 (ld.w 8 [r12], r15) /* get core ID */
__as1 (cmp r13 , r14) /* check end of table */
___asm (be _startup_block_zero_init_end )
__as1 (cmp r15 , r16) /* compare core ID */
___asm (bne _startup_block_zero_init_start )
BRS_LABEL (_startup_block_zero_init_loop_start)
__as1 (st.w r0 , 0 [r13])
__as2 (addi 4 , r13, r13)
__as1 (cmp r13 , r14) /* compare to end address */
___asm (bh _startup_block_zero_init_loop_start )
___asm (jr _startup_block_zero_init_start )
BRS_LABEL (_startup_block_zero_init_end)
# endif /*VLINKGEN_ZERO_INIT_BLOCK_COUNT_STARTUP>1uL*/ |
从名字看就是用来初始化内存的,什么内存?就要看看这个 vLinkGen_ZeroInitBlocksArrayStartup 了。
在vLinkGen_InitSections_Lcfg.c里面可以找到
/*! Memory region blocks to be initialized with zeros directly after reset. */
const vLinkGen_MemArea vLinkGen_ZeroInitBlocksArrayStartup [ VLINKGEN_ZERO_INIT_BLOCK_COUNT_STARTUP ] =
{
{
/* .start = */ ( uint32 ) 0xFEBD0000uL , /* LOCAL_RAM_0 */
/* .end = */ ( uint32 ) 0xFEBF0000uL ,
/* .core = */ ( uint32 ) 0uL
},
{
/* .start = */ ( uint32 ) 0uL ,
/* .end = */ ( uint32 ) 0uL ,
/* .core = */ ( uint32 ) 0uL
}
}; |
其实,这个内存就是RH850 MCU的 LOCAL RAM 来的。很巧妙,这段代码通过判断 .start 和 .end 的值来决定是否结束。如果这个结构体数组有好几个元素,它会一直遍历下去。
完了后,这会跳到 _startup_block_zero_init_end ,继续往下
#else
#error "Mandatory define VLINKGEN_ZERO_INIT_ BLOCK_COUNT_STARTUP missing within vLinkGen configuration!"
#endif /*VLINKGEN_ZERO_INIT_BLOCK_COUNT_STARTUP*/
#if defined (VLINKGEN_ZERO_INIT_AREA_COUNT_STARTUP)
# if (VLINKGEN_ZERO_INIT_AREA_COUNT_STARTUP > 1uL )
__as1 (mov _vLinkGen_ZeroInitAreasArrayStartup , r11)
BRS_LABEL (_startup_area_zero_init_start)
__as1 (mov r11 , r12)
__as2 (addi 12 , r11, r11)
__as1 (ld.w 0 [r12], r13) /* get start address */
__as1 (ld.w 4 [r12], r14) /* get end address */
__as1 (ld.w 8 [r12], r15) /* get core ID */
__as1 (cmp r13 , r14) /* check end of table */
___asm (be _startup_area_zero_init_end )
__as1 (cmp r15 , r16) /* compare core ID */
___asm (bne _startup_area_zero_init_start )
BRS_LABEL (_startup_area_zero_init_loop_start)
__as1 (st.w r0 , 0 [r13])
__as2 (addi 4 , r13, r13)
__as1 (cmp r13 , r14) /* compare to end address */
___asm (bh _startup_area_zero_init_loop_start )
___asm (jr _startup_area_zero_init_start )
BRS_LABEL (_startup_area_zero_init_end)
# endif /*VLINKGEN_ZERO_INIT_AREA_COUNT_STARTUP>1uL*/
#else
#error "Mandatory define VLINKGEN_ZERO_INIT _AREA_COUNT_STARTUP missing within vLinkGen configuration!"
#endif /*VLINKGEN_ZERO_INIT_AREA_COUNT_STARTUP*/ |
这个 vLinkGen_ZeroInitAreasArrayStartup 又是干嘛的?
看这个结构体数组
/*! Section groups to be intialized with zeros directly after reset. */
const vLinkGen_MemArea vLinkGen_ZeroInitAreasArrayStartup [ VLINKGEN_ZERO_INIT_AREA_COUNT_STARTUP ] =
{
{
/* .start = */ ( uint32 ) _Startup_Stack_START ,
/* .end = */ ( uint32 ) _Startup_Stack_END ,
/* .core = */ ( uint32 ) 0uL
},
{
/* .start = */ ( uint32 ) 0uL ,
/* .end = */ ( uint32 ) 0uL ,
/* .core = */ ( uint32 ) 0uL
}
}; |
很明显,这是startup stack来的,即系统的栈初始化。
好了,继续往下走
/* ================== */
/* */
/* Description: Jump to Brs_PreMainStartup() (BrsMainStartup.c) */
/* */
/* ================ */
BRS_BRANCH (_Brs_PreMainStartup) |
那么,这个 Brs_PreMainStartup 是什么东西?从上面的注释,可以在 BrsMainStartup.c 找到
FUNCTION DEFINITIONS
* @brief Unified routine for Pre Main() Startup.
* @pre Stack pointer needs to be initilialized in StartUpCode before.
* @param [in] -
* @param [out] -
* @return -
* @context Function is called from assembler startup code
* Called by all cores
* All APIs are called with current Core ID
*/
void Brs_PreMainStartup ( void )
{
BrsHw_PreInitClock ( BrsHw_GetCore ()); /* optional callout to power up the PLL for faster Memory initialization */
BrsHw_PreZeroRamHook ( BrsHw_GetCore ()); /* optional, empty by default */
// … …
main ();
} |
这个是进入 main 函数之前的一些初始化,主要的也是一些RAM等初始化。本文就不细讲了。
到这里,我就找到了整个main函数之前的初始化了。再后面的就是ECUM、OS和BSWM等的初始化了。
2.AUTOSAR架构中的Configurator
你有没有发觉AUTOSAR代码里面有些文件名很是引起你注意,如 xxx_PBcfg.c 、 xxx_Lcfg.c 和 xxx_Cfg.h 。 看起来,这像是配置文件,实际也证明跟AUTOSAR配置有关(更改配置工具如Davinci Configurator上的配置信息)生成代码时,这些文件会有相应的变化。 那么,问题来了,一个xxx_cfg.h或者xxx_cfg.c就搞定了,为什么会有三种配置文件呢? 于是,我要刨根问底了。 cfg还好理解,就是configuration的意思,这个PB和L又是什么意思? 翻了很多AUTOSAR官方文档,终于找到了,其实AUTOSAR Base Software支持以下几种配置类型:
1. Pre-compile time
2. Link time
- module外的const数据 ; 模块编译后和配置数据
3. Post-build time
- 加载Module外部的const数据 . 加载数据到指定的memory段
独立于配置类,可以通过变化点提供单个或多个配置集。如果提供了多个配置集,则在运行时绑定变量点的情况下,将在运行时选择实际使用的配置集。 在许多情况下,一个模块的配置参数将具有不同的配置类别。 例如,提供构建后时间配置参数的模块仍将具有一些可进行预编译时间配置的参数。 这样看来,这个PB就是post-build time了,而L就是Link time了。那么各个配置文件有什么作用和区别呢? Pre-compile time 使用:
- 启用 / 禁用可选功能。这样可以排除不需要的部分源代码。
- 优化性能和代码大小。在大多数情况下,使用 #defines 比访问常量甚至通过指针访问常量的代码效率更高。
生成的代码避免了代码和运行时开销。 Pre-compile的配置要通过两个配置文件( xxx_Cfg.h , xxx_Cfg.c) 实现:
- xxx_Cfg.h 包含如 宏定义和或者 #defines
- xxx_Cfg.c 包含如 const 数据
Nm_cfg.c
#include "Nm_Cfg.h"
/*lint -restore */ CONST(Nm_NmFunctionTableType, NM_CONST) Nm_NmFunctionTable[1] = { /* PRQA S 1514, 1533 */ /* MD_CSL_ObjectOnlyAccessedOnce */
/* Index GetLocalNodeIdentifier GetNodeIdentifier GetPduData GetState NetworkRelease NetworkRequest PassiveStartUp Referable Keys */
{ /* 0 */ CanNm_GetLocalNodeIdentifier, CanNm_GetNodeIdentifier, CanNm_GetPduData, CanNm_GetState, CanNm_NetworkRelease, CanNm_NetworkRequest, CanNm_PassiveStartUp } /* [CanNm] */
}; |
Nm_Cfg.h
/* Global Properties */
#ifndef NM_DEV_ERROR_DETECT
#define NM_DEV_ERROR_DETECT STD_ON
#endif
#ifndef NM_DEV_ERROR_REPORT
#define NM_DEV_ERROR_REPORT STD_ON
#endif #define NM_VERSION_INFO_API STD_OFF |
Nm.c
/* NM Interface version is decimal coded. */
CONST(uint8, NM_CONST) Nm_MainVersion = NM_SW_MAJOR_VERSION;
CONST(uint8, NM_CONST) Nm_SubVersion = NM_SW_MINOR_VERSION;
CONST(uint8, NM_CONST) Nm_ReleaseVersion = NM_SW_PATCH_VERSION; |
Link time
Link time 配置用于:
- 模块配置仅仅对目标代码可用(例如IP Protection和warranty原因)
- 在编译之后但在链接之前创建配置。
例如 Mcu_Lcfg.c
/* QAC Warning: START Msg(2:3211)-2 */
/* Data Structure of RAM setting Configuration */
CONST(Mcu_RamSetting, MCU_VAR) Mcu_GstRamSetting[1] =
{
/* Index: 0 - McuRamInitConfiguration */
{
/* pRamStartAddress */
/* MISRA Violation: START Msg(4:0306)-1 */
/* QAC Warning: START Msg(2:0315)-3 */
/* QAC Warning: START Msg(2:3892)-4 *
(P2VAR(uint8, TYPEDEF, MCU_CONFIG_DATA)) 0xFEBD0000UL,
/* END Msg(2:3892)-4 */
/* END Msg(2:0315)-3 */
/* END Msg(4:0306)-1 */
/* ulRamSectionSize */
0x00000100UL,
/* ucRamInitValue */
0xFFU,
/* enRamWriteSizeSel */
MCU_8BIT_SIZE
}
}; |
Mcu.c
/* Get the pointer to the RAM structure */
LpRamSetting = & Mcu_GstRamSetting [ RamSection ]; |
Post-build time Post-build time会用于
- 数据配置,其中仅定义结构,但在 ECU 生成期间未知内容
- 在 ECU -build时之后(例如,生产end of line,测试和校准期间)可能会更改或必须修改的数据配置
- 跨不同汽车版本(相同应用,不同配置)的 ECU 的可重用性,例如 与豪华版轿车的 ECU 相比,低成本版轿车的 ECU 在总线上传输的信号更少。
例如
|