ARC Core是一个符合AUTOSAR标准的嵌入式开发平台,它包含三个组件:支持代码(Arctic Core)、开发平台(Arctic Studio)和配置工具(BSW Builder)。
Arctic Core是一个开放源码(GPL)的嵌入式系统平台,它基于模块化设计,符合AUTOSAR标准,目前实现了以下内容:
其实现路线图如下(图 11):
Arctic Studio是一个集成了gcc编译器的开发环境(powerpc-eabispe),同时包括了C语言编辑器CDT,在Professional版本中包含ECU的配置工具。
Arctic Studio对源代码进行统一管理,它会自动创建用于构建Arctic Core的Makefile的基本模板,但用户仍需要编辑自己工程内的Makefile。构建系统模型如图 12所示。
图 12 构建系统
BSW Builder是一个付费的AUTOSAR中BSW的配置工具,它支持图形化的ECU级编辑(如图 13所示)和操作系统级编辑(如图 14所示)。
图 13 BSW Builder
图 14 BSW Builder操作系统编辑
通过基于arxml数据的配置,可以生成和C语言代码的文件。
本文档的目的在于分析Arctic Core结构,从而获得它的运行方式及RTE与OS的关系,为RTE的实现做准备。
分析主要由以下方式进行:
图 21 将检出项目转换为C项目
图 22 转换为C/C++项目
含义 |
变量 |
编译器位置 |
CROSS_COMPILE=/opt/powerpc-eabispe/bin/powerpc-eabispe- |
板级支持包位置 |
BOARDDIR=<arc-stable/boards/任意一个目录名 需要注意的是:如果想以后要仿真调试,需要选择带有sim的目标板(如mpc5554sim) |
目录外编译位置 |
BDIR=../${ProjName} |
图 23 拷贝版本库中的示例
配置信息(如Os_Cfg.c)可以使用手工填写,也可以使用BSW Builder生成。下面的例子就是用BSW Builder写一个简单的OSEK操作系统应用:
图 24 新建C语言项目
图 25 New Ecu Configuration
图 26 添加OS对象
#define USE_DEBUG
#include "Trace.h"
#define USE_SIMPLE_PRINTF
#include "simple_printf.h"
// Tasks
void Task_1(void)
{
simple_printf("Task_1 Started!\n");
for (;;)
{
WaitEvent(EVENT_MASK_Event_1);
ClearEvent(EVENT_MASK_Event_1);
simple_printf("Hello World\n");
}
}
// Hooks
void ErrorHook(StatusType Error)
{
dbg_printf("Error Code = %d\n", Error);
}
void PostTaskHook(void)
{
TaskType currTask;
GetTaskID(&currTask);
TickType alarmTick;
GetAlarm(ALARM_ID_Alarm_1, &alarmTick);
dbg_printf("<%d> Task %d Switch finished\n", alarmTick, currTask);
}
void PreTaskHook(void)
{
TaskType currTask;
GetTaskID(&currTask);
dbg_printf("[%d] Task %d Get in\n", GetOsTick(), currTask);
}
ProtectionReturnType ProtectionHook(StatusType FatalError)
{
dbg_printf("Error Code = %d\n", FatalError);
return PRO_KILLAPPL;
}
void ShutdownHook(StatusType Error)
{
dbg_printf("OS Shutdown!\n");
}
void StartupHook(void)
{
dbg_printf("OS Startup!\n");
}
代码编写完成后,检查makefile是否包含了config目录中的全部代码,如下所示:
# Our object files
obj-y += simple_main.o
# OS object files.
obj-y += Os_Cfg.o
并检查build_config.mk文件中是否包含了所有用到的库文件。如下所示:
MOD_USE+=KERNEL MCU T32_TERM RAMLOG SIMPLE_PRINTF RAMLOG
完成后使用Build Project进行编译。
图 27 构建项目
整个编译过程会显示在Console视图中。下面我们分析make的全过程。
首先是提示信息:
行号 |
内容 |
1 |
**** Build of configuration Default for project test **** |
2 |
make all |
3 |
Building for system/kernel ../test |
4 |
BOARDDIR: mpc5516it |
5 |
ARCH_FAM/ARCH: / |
6 |
cygwin warning: MS-DOS style path detected: E:\codes\ARCStudio\arc-stable Preferred POSIX equivalent is: /cygdrive/e/codes/ARCStudio/arc-stable CYGWIN environment variable option "nodosfilewarning" turns off this warning. Consult the user's guide for more details about POSIX paths: http://cygwin.com/cygwin-ug-net/using.html#using-pathnames |
7 |
|
8 |
|
9 |
|
10 |
|
11 |
然后将各模块C语言原文件编译成目标文件:
12 |
==========[ system/kernel ]=========== |
|
13 |
make -r (使用内建build规则) -C system/kernel/obj_mpc5516it (切换目录到obj_mpc5516it) -f /cygdrive/e/codes/ARCStudio/arc-stable/scripts/rules.mk (使用rules.mk作为一个Makefile) ROOTDIR=/cygdrive/e/codes/ARCStudio/arc-stable SUBDIR=system/kernel all |
|
14 |
make[1]: Entering directory `/cygdrive/e/codes/ARCStudio/arc-stable/system/kernel/obj_mpc5516it' (make[1]开始) |
|
15 |
>> CC event.c |
|
16 |
/opt/powerpc-eabispe/bin/powerpc-eabispe-gcc -c (编译) -B/libexec/gcc:/opt (指定编译器cc1的位置) -mno-eabi (Embedded Applications Binary Interface:堆栈16位对齐,不从main开始执行) -msdata=none (把所有全局初始化数据放在.data节,非初始化全局数据放在.bss节) -mmultiple (使用多字指令) -msoft-float (使用浮点库) -mcpu=8540 (cpu名) -mstrict-align (内存必须对齐) -gdwarf-2 (使用DWARF v2作为调试标准) -D_PPC (前缀) -O0 (不优化) -std=gnu99 (c语言格式=gnu99) -MMD (不连接) -Wall (所有警告) -Winline (inline警告) -fno-strict-aliasing -fno-builtin (不识别内建类型) -o event.o (生成event.o) |
-I . -I /cygdrive/e/codes/ARCStudio/arc-stable/drivers/include -I /cygdrive/e/codes/ARCStudio/arc-stable/arch/ppc/mpc55xx/kernel -I /cygdrive/e/codes/ARCStudio/arc-stable/arch/ppc/mpc55xx/drivers -I /cygdrive/e/codes/ARCStudio/arc-stable/arch/ppc/mpc55xx/config -I /cygdrive/e/codes/ARCStudio/arc-stable/arch/mpc55xx -I /cygdrive/e/codes/ARCStudio/arc-stable/include/ppc -I /cygdrive/e/codes/ARCStudio/arc-stable/include -I /cygdrive/e/codes/ARCStudio/arc-stable/include/ppc -I ../include -DUSE_DBG_PRINTF -DUSE_KERNEL -DUSE_MCU -DCFG_PPC -DCFG_BOOKE -DCFG_E200Z1 -DCFG_MPC55XX -DCFG_MPC5516 -DCFG_BRD_MPC5516IT -Dmpc55xx -Dppc -Dmpc5516 -DCC_KERNEL /cygdrive/e/codes/ARCStudio/arc-stable/system/kernel/event.c |
17 |
>> CC init.c |
|
19 |
>> CC trusted.c |
|
21 |
>> CC arch.c |
|
23 |
>> CC task.c |
|
25 |
>> CC task_i.c |
|
27 |
>> CC resource.c |
|
29 |
>> CC swap.c |
|
31 |
>> CC alarm.c |
|
33 |
>> CC sched_table.c |
|
35 |
>> CC counter.c |
|
37 |
>> CC com_internal.c |
|
39 |
>> CC create.c |
|
41 |
>> CC Frt.c |
|
43 |
>> CC stack.c |
|
45 |
>> CC isr.c |
|
47 |
>> CC int_ctrl.c |
将编译成功后产生的所有目标文件压缩到库中:
49 |
>> AR /cygdrive/e/codes/ARCStudio/arc-stable/libs/libkernel_mpc5516.a |
50 |
/opt/powerpc-eabispe/bin/powerpc-eabispe-ar (将前面编译的.o文件压缩成.a文件) -r -o /cygdrive/e/codes/ARCStudio/arc-stable/libs/libkernel_mpc5516.a arch_krn.o event.o init.o trusted.o arch.o task.o task_i.o resource.o swap.o alarm.o sched_table.o counter.o com_internal.o create.o Frt.o stack.o isr.o int_ctrl.o 2> /dev/null |
51 |
`/cygdrive/e/codes/ARCStudio/arc-stable/libs/libkernel_mpc5516.a' -> `/cygdrive/e/codes/ARCStudio/arc-stable/binaries/libkernel_mpc5516.a' (拷贝) |
52 |
make[1]: Leaving directory `/cygdrive/e/codes/ARCStudio/arc-stable/system/kernel/obj_mpc5516it' (make[1]完成) |
编译目录外用户定义目标文件:
53 |
==========[ ../test ]=========== |
|
54 |
make -r -C ../test/obj_mpc5516it -f /cygdrive/e/codes/ARCStudio/arc-stable/scripts/rules.mk ROOTDIR=/cygdrive/e/codes/ARCStudio/arc-stable SUBDIR=../test all |
|
55 |
make[1]: Entering directory `/cygdrive/e/codes/ARCStudio/test/obj_mpc5516it' (make开始) |
|
56 |
>> CC simple_main.c |
|
57 |
/opt/powerpc-eabispe/bin/powerpc-eabispe-gcc -c -B/libexec/gcc:/opt -mno-eabi -msdata=none -mmultiple -msoft-float -mcpu=8540 -mno-eabi -mstrict-align -gdwarf-2 -D_PPC -O0 -std=gnu99 -MMD -Wall -Winline -fno-strict-aliasing -fno-builtin -o simple_main.o |
-I ../config/mpc5516it -I ../config -I ../config -I /cygdrive/e/codes/ARCStudio/arc-stable/system/kernel/obj_mpc5516it -I /cygdrive/e/codes/ARCStudio/arc-stable/system/kernel/include -I /cygdrive/e/codes/ARCStudio/arc-stable/system/EcuM -I /cygdrive/e/codes/ARCStudio/arc-stable /arch/ppc/mpc55xx/delivery/mpc5500_h7f/include -I /cygdrive/e/codes/ARCStudio/arc-stable/communication/ComM -I /cygdrive/e/codes/ARCStudio/arc-stable/include -I /cygdrive/e/codes/ARCStudio/arc-stable/kernel/test -I /cygdrive/e/codes/ARCStudio/arc-stable/kernel/include -I /cygdrive/e/codes/ARCStudio/arc-stable/arch/ppc/mpc55xx/kernel -I /cygdrive/e/codes/ARCStudio/arc-stable/arch/ppc/mpc55xx/drivers -I /cygdrive/e/codes/ARCStudio/arc-stable/boards/mpc5516it/config -I /cygdrive/e/codes/ARCStudio/arc-stable/drivers/Dem -I /cygdrive/e/codes/ARCStudio/arc-stable/drivers/test -I /cygdrive/e/codes/ARCStudio/arc-stable/boards/generic -I /cygdrive/e/codes/ARCStudio/arc-stable/include -I /cygdrive/e/codes/ARCStudio/arc-stable/include/ppc -I ../include -DUSE_DBG_PRINTF -DUSE_KERNEL -DUSE_MCU -DUSE_KERNEL -DUSE_MCU -DUSE_T32_TERM -DUSE_RAMLOG -DUSE_SIMPLE_PRINTF -DUSE_RAMLOG -DCFG_PPC -DCFG_BOOKE -DCFG_E200Z1 -DCFG_MPC55XX -DCFG_MPC5516 -DCFG_BRD_MPC5516IT -Dmpc55xx -Dppc -Dmpc5516 /cygdrive/e/codes/ARCStudio/test/simple_main.c |
58 |
>> CC Os_Cfg.c |
|
60 |
>> CC EcuM.c |
|
62 |
>> CC EcuM_Cfg.c |
|
64 |
>> CC EcuM_Callout_template.c |
|
66 |
>> CC Mcu.c |
|
68 |
>> CC Mcu_Cfg.c |
|
70 |
>> CC Det.c |
|
72 |
>> CC arc.c |
|
74 |
>> CC newlib_port.c |
将目录外目标文件和库文件进行连接:
77 |
/opt/powerpc-eabispe/bin/powerpc-eabispe-ld (链接) -T /cygdrive/e/codes/ARCStudio/arc-stable/arch/ppc/mpc55xx/scripts/linkscript_gcc.ldf (使用linkscript_gcc.ldf作为链接脚本) -o simple.elf (输出为elf文件) (设置符号搜索路径) -L"/opt/powerpc-eabispe/lib/gcc/powerpc-eabispe/4.1.2/http://www.cnblogs.com/http://www.cnblogs.com/powerpc-eabispe/lib" -L"/opt/powerpc-eabispe/lib/gcc/powerpc-eabispe/4.1.2" --start-group (设置链接文件) simple_main.o Os_Cfg.o crt0.o EcuM.o EcuM_Cfg.o EcuM_Callout_template.o Mcu.o Mcu_Cfg.o Det.o xtoa.o arc.o ramlog.o printf.o newlib_port.o Mcu_Exceptions.o -lgcc -lc /cygdrive/e/codes/ARCStudio/arc-stable/libs/libkernel_mpc5516.a --end-group -M > simple.map(输出映射文件) |
78 |
Image size: (decimal) text:91596 bytes data:2680 bytes bss :31812 bytes ROM: ~94276 bytes RAM: ~34492 bytes |
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
>>>>>>> DONE <<<<<<<<< |
85 |
`simple.elf' -> `/cygdrive/e/codes/ARCStudio/arc-stable/binaries/simple.elf' |
86 |
make[1]: Leaving directory `/cygdrive/e/codes/ARCStudio/test/obj_mpc5516it' |
调试有多种方式,最好的是把程序直接下载到目标板,使用OCD方式进行。如果没有目标板,只有使用仿真器进行。
(注意:下载的仿真器是评估版,只能调试60分钟。如果需要调试超过60分钟,请双击下面的图标"T32.rar"下载)
Arctic Core目录结构分为半,一半为标准的OS和外设支持文件,另一半为用户定制项目的配置文件和任务及其支持文件。如图 31所示:
图 31 Arctic Core项目目录结构
标准库中个目录详细信息如下:
目录名 |
概述 |
所包含的内容 |
arch |
CPU构架所支持的基本数据结构、内部驱动和启动代码 |
arm,ppc |
boards |
目标板对于CPU构架的集成配置情况,每一块目标板都有自己的目录与之对应 |
et_stm32_stamp,generic,mpc5516it,mpc551xsim,mpc5554sim,mpc5567qrtech |
common |
最基本的库,字符串处理,内核调试支持和状态解释文件 |
arc.c,newlib_port.c,printf.c,ramlog.c,strace.c,xtoa.c |
communication |
基本通信模块,支持Can和Lin总线 |
CanIf,Com,ComM,Lin,PduR |
components |
包含各个应用特有的AUTOSAR构件,由于构件在库文件中,所以可以复用 |
每个应用特有一个或多个 |
diagnostic |
目前诊断模块中只有Det(Development Error Tracer)驱动可用,它操纵detCbk_t跟踪错误 |
Dem,Det |
include |
基本的类型文件和所有函数的原型 |
|
memory |
内存管理,目前没有实现 |
NvmM |
peripherals |
特定目标板的外设控制 |
Fls_SST25xx.c |
rte |
运行时环境支持,目前没有实现,但是可以从include/rte.h看到所有函数的原型 |
rte.c |
system |
操作系统,用于管理ECU,内存和实时内核的基本代码 |
EcuM,kernel,mm,SchM,WdgM |
用户目录中结构详细信息如下:
目录名 |
概述 |
所包含的内容 |
makefile |
本项目中所有需要编译和链接的目标文件的编译信息,整个项目的链接信息 |
obj-y(目标文件),inc-y(包含头文件),libitem-y(库文件),ldcmdfile-y(link文件),build-exe-y(可执行文件) |
build_config.mk |
在整个项目编译过程中,库文件需要包含的目标文件 |
如MOD_USE+=KERNEL T32_TERM SIMPLE_PRINTF |
Task.c |
任务体的实现 |
各个任务的任务体函数 |
Hook.c |
钩子的实现 |
|
config |
对于操作系统和外设的配置 |
|
Rte |
运行时环境(任务体直接调用运行时环境的主函数) |
rte.c,rte.h |
整个项目依赖关系如图 32所示,从makefile中我们也可以看出整个编译首先是从用户目录开始的。
基于AUTOSAR的项目不需要自己编写的Task,而是由Rte中的Runnable实现。整个操作系统启动后首先进入自动生成的Task,再由Task调用Runnable的任务体。
任务和资源的管理在Task中由Rte自动生成实现,Alarm和Event都被封装起来自动调用,Runnable中的程序只能调用Rte提供的或BSW的函数,通信由Rte层进行转换。Rte是如何封装任务API的将在后面内容中说明。
操作系统和通信又基于目标板端口读写和中断实现,它们全部基于特定的CPU构架。
图 32 Arctic Core项目依赖关系
程序的二进制布局由linker file描述,在Arctic Core中linker file被放在了arch\ppc\ mpc55xx\scripts\linkscript_gcc.ldf文件中。需要了解程序从何处开始运行,就需要找到程序入口,在linker file中程序入口由ENTRY(symbol)指令进行指定。
在linkscript_gcc.ldf中入口点为:
ENTRY(_start)
_start符号在\arch\ppc\crt0.sx第24行。它的代码如下:
_start:
// Set up the reserved registers in EABI: r1,r2 and r13()
// r1, stack pointer
lis r1,__SP_INIT@h
ori r1,r1,__SP_INIT@l
// r13, base of .sdata
lis r13,_SDA_BASE_@h
ori r13,r13,_SDA_BASE_@l
// r2, base of .sdata2 and .sbss2
lis r2,_SDA2_BASE_@h
ori r2,r2,_SDA2_BASE_@l
// make space for initial backchain..
subi r1,r1,16
// Copy initialized data from ROM to RAM
lis r3,__DATA_ROM@h
ori r3,r3,__DATA_ROM@l
lis r4,__DATA_RAM@h
ori r4,r4,__DATA_RAM@l
lis r5,__DATA_END@h
ori r5,r5,__DATA_END@l
cmplw r3,r4
beq skip_data
cmplw r4,r5
beq skip_data
subi r3,r3,1
subi r4,r4,1
1:
lbzu r6,1(r3)
stbu r6,1(r4)
cmplw r4,r5
bne+ 1b
skip_data:
# Clear uninitialized data( holds both bss and sbss )
lis r3,__BSS_START@h
ori r3,r3,__BSS_START@l
lis r4,__BSS_END@h
ori r4,r4,__BSS_END@l
cmplw r3,r4
beq 3f
li r0,0
subi r3,r3,1
2:
stbu r0,1(r3)
cmplw r3,r4
bne+ 2b
3:
# Call main() with argc set to 1 and argv ignored
li r3,1
bl main
# Call exit() with the return value from main() as argument
b exit
.globl _exit
_exit:
b _exit
.end
这段代码主要完成了以下工作:
其流程如图 41所示:
图 41 CPU初始化流程
在此,必须要对目标cpu的内存结构进行一个说明。
MEMORY
{
rcw(R) : ORIGIN = 0x00000000, LENGTH = 0x8
flash(R) : ORIGIN = 0x00000008, LENGTH = 0x100000
ram(RW) : ORIGIN = 0x40000000, LENGTH = 0x100000
}
整个内存被分为三个部分,rcw存放启动时的基本数据,占8个字节,内容如下:
.section ".rcw","ax"
.global _resetconfiguration
_resetconfiguration:
1 .byte 0x00 #no watchdog
2 .byte 0x5A #Boot identifier
3 .byte 0x00
4 .byte 0x00
5-8 .long _start
然后是flash区,放到flash区的信息有以下几个节:
节名 |
所存放的内容 |
.text |
代码 |
.fls_rom |
flash布局静态变量 __FLS_ERASE_ROM__ __FLS_WRITE_ROM__ __FLS_END_ROM__ |
.exception_tbl |
异常表(中断) |
.rodata |
只读数据 |
各段的基地址 |
_SDA2_BASE_ __TEXT_END __DATA_ROM __DATA_RAM __SDATA_START__ _SDA_BASE_ __DATA_END |
最后是1MB的ram区,用于存放数据和堆栈,.data,.sdata,.bss和.sbss这几个节的内容都放在ram区中,但要注意的是.bss是没有初始值的数据,需要在程序中进行初始化,而上面那段启动程序把所有未初始化的数据都初始化为0。这个过程叫做flash解压。
硬件系统启动后,调用main()函数,这个函数在\system\kernel\init.c第238行,它的函数体如下:
int main(void)
{
EcuM_Init();
}
仅调用了一个函数,而且没有return任何内容。
EcuM_Init()函数启动Ecu管理程序,它在system\EcuM\EcuM.c第51行,其函数体如下:
void EcuM_Init( void )
{
internal_data.current_state = ECUM_STATE_STARTUP_ONE;
// Initialize drivers that are needed to determine PostBuild configuration
EcuM_AL_DriverInitZero();
// Initialize the OS
InitOS();
// Enable interrupts
IntCtrl_Init();
// Determine PostBuild configuration
internal_data.config = EcuM_DeterminePbConfiguration();
// Check consistency of PB configuration
// TODO
// Initialize drivers needed before the OS-starts
EcuM_AL_DriverInitOne(internal_data.config);
// Determine the reset/wakeup reason
// TODO Mcu_ResetType type = Mcu_GetResetReason();
// Set default shutdown target
internal_data.shutdown_target = internal_data.config->EcuMDefaultShutdownTarget;
internal_data.shutdown_mode = internal_data.config->EcuMDefaultShutdownMode;
// Set default application mode
internal_data.app_mode = internal_data.config->EcuMDefaultAppMode;
internal_data.initiated = TRUE;
// Start this baby up
StartOS(internal_data.app_mode);
}
它主要做了以下几件事情:
名称 |
类型 |
内容 |
initiated; |
boolean |
ECU是否已经启动 |
config; |
EcuM_ConfigType* |
ECU上各个硬件部分的控制信息,如: Ecu Default Shutdown Target,Ecu Default Shutdown Mode, Ecu Default App Mode |
shutdown_target; |
EcuM_StateType |
是一个枚举变量,有以下选项: ECUM_STATE_APP_RUN = 0x32, ECUM_STATE_SHUTDOWN = 0x40, ECUM_STATE_WAKEUP = 0x20, ECUM_SUBSTATE_MASK = 0x0F, ECUM_STATE_WAKEUP_WAKESLEEP = 0x25, ECUM_STATE_WAKEUP_ONE = 0x21, ECUM_STATE_OFF = 0x80, ECUM_STATE_STARTUP = 0x10, ECUM_STATE_PREP_SHUTDOWN = 0x44, ECUM_STATE_RUN = 0x30, ECUM_STATE_STARTUP_TWO = 0x12, ECUM_STATE_WAKEUP_TTII = 0x26, ECUM_STATE_WAKEUP_VALIDATION = 0x22, ECUM_STATE_GO_SLEEP = 0x49, ECUM_STATE_STARTUP_ONE = 0x11, ECUM_STATE_WAKEUP_TWO = 0x24, ECUM_STATE_SLEEP = 0x50, ECUM_STATE_WAKEUP_REACTION = 0x23, ECUM_STATE_APP_POST_RUN = 0x33, ECUM_STATE_GO_OFF_TWO = 0x4e, ECUM_STATE_RESET = 0x90, ECUM_STATE_GO_OFF_ONE = 0x4d |
shutdown_mode; |
uint8 |
关闭模式 |
app_mode; |
AppModeType |
应用模式,操作系统会根据该模式启动不同的功能。 |
current_state; |
EcuM_StateType |
Ecu当前状态 |
3.1) 初始化全局变量sys_t os_sys,这个变量用于表示操作系统内部的状态信息,它的类型如下:
名称 |
类型 |
内容 |
curr_application; |
app_t* |
由于OSEK操作系统可以支持多个应用程序,所以需要标识当前应用 |
curr_pcb; |
pcb_t* |
当前pcb |
pcb_list; |
pcb_t* |
pcb表(链表) |
int_nest_cnt; |
uint32 |
中断嵌套计数 |
int_stack; |
void* |
中断堆栈 |
tick; |
TickType |
当前时钟tick值 |
scheduler_lock; |
int |
是否可以调度 |
*hooks |
struct os_conf_global_hooks_s |
所有钩子的指针 |
param1; |
uint32_t |
用于 错误提示的参数 |
param2; |
uint32_t |
- |
param3; |
uint32_t |
- |
serviceId; |
uint32_t |
- |
task_cnt; |
uint32_t |
任务数量 |
pcb_head; |
TAILQ_HEAD(tailq2,pcb_s) |
pcb头部 |
ready_head; |
TAILQ_HEAD(tailq,pcb_s) |
ready队列头部(每次取ready队列第一个值) |
3.2) 初始化计数器
3.3) 初始化调度表
3.4) 在ram中建立pcb,并把rom中的pcb解压到ram中,并串接成链。
4) 建立中断表:
IntCtrl_Init()函数通过spr(Special Purpose Register)指定中断表的位置。异常表在\arch\ppc\mpc55xx\drivers\Mcu_Exceptions.sx中,其内容如下:
# Force this jump table to this address to match the
# value written to z1 IVPR
.section ".exception_tbl","ax"
.balign 0x0800 //TODO: 1000 eller 800?
.global exception_tbl
# The .skip directive aligns the branch instructions
# to the irq vector offsets
exception_tbl:
b exception_IVOR0
.skip +0xc
b exception_IVOR1
.skip +0xc
b exception_IVOR2
.skip +0xc
b exception_IVOR3
.skip +0xc
b exception_IVOR4
.skip +0xc
b exception_IVOR5
.skip +0xc
b exception_IVOR6
.skip +0xc
b exception_IVOR7
.skip +0xc
b exception_IVOR8
.skip +0xc
b exception_IVOR9
.skip +0xc
b dec_exception
//b exception_IVOR10
.skip +0xc
b exception_IVOR11
.skip +0xc
b exception_IVOR12
.skip +0xc
b exception_IVOR13
.skip +0xc
b exception_IVOR14
.skip +0xc
b bad_int
从表中可以看出所有的异常都没有指定跳转的函数,而是只指定了符号,所以IntCtrl_Init()的作用就是填充完整这个异常处理表。填充过程为:不断调用类似于下面语句的宏来指定异常处理程序的位置。
asm volatile (" mtspr " "63" ",%[_val]" : : [_val] "r" ((uint32)exception_tbl))
在StartOS函数中,应该根据不同的App模式来启动调度。但是ArcticCore 2.0并没有实现App模式的检查,其函数体如下:
void StartOS(AppModeType Mode)
{
/* Check link file */
if (TEST_DATA != test_data)
{
noooo();
}
if (test_bss != 0)
{
noooo();
}
os_start();
/** @req OS424 */
assert(0);
}
这里有两个小技巧,第一是检查数据段是否初始化正确,test_data和test_bss的定义如下:
#define TEST_DATA 12345
int test_data = TEST_DATA;
int test_bss = 0;
如果没有初始化正确,就进入了noooo()死循环,从而触发看门狗reset。
二是检查os_strat()是否被退出,如果退出,则会调用assert(0)从而引发可捕获的异常。
StratOS()这个系统API调用了os_start()内部函数,它在\system\kernel\init.c第170行,函数体如下:
static void os_start(void)
{
pcb_t *tmp_pcb;
assert(init_os_called);
/* find highest prio process and run it */
tmp_pcb = os_find_top_prio_proc();
/* TODO: fix ugly */
/* Call the startup hook */
extern struct os_conf_global_hooks_s os_conf_global_hooks;
os_sys.hooks = &os_conf_global_hooks;
if (os_sys.hooks->StartupHook != NULL)
{
os_sys.hooks->StartupHook();
}
/* handle autostart */
for (int j = 0; j < Oil_GetAlarmCnt(); j++)
{
alarm_obj_t *alarmPtr;
alarmPtr = Oil_GetAlarmObj(j);
if (alarmPtr->autostart.active)
{
alarm_autostart_t *autoPtr = &alarmPtr->autostart;
SetAbsAlarm(j, autoPtr->alarmtime, autoPtr->cycletime);
}
}
// Activate the systick interrupt
{
uint32_t sys_freq = McuE_GetSystemClock();
Frt_Init();
Frt_Start(sys_freq / OsTickFreq);
}
// Swap in prio proc.
{
// FIXME: Do this in a more structured way.. setting os_sys.curr_pcb manually is not the way to go..
os_sys.curr_pcb = tmp_pcb;
// NOTE! We don't go for os_swap_context() here..
// first arg(NULL) is dummy only
os_swap_context_to(NULL, tmp_pcb);
// We should not return here
assert(0);
}
}
这个函数首先获得处于ready队列优先级最高的任务,然后切换到该任务,其间又调用了用户的StartupHook(),启动Alarm、Counter和系统Tick。
首次任务切换后操作系统就开始正式运行,至此系统启动完毕。
操作系统的运行核心就在于如何组织任务抢占CPU的时间片。在OSEK OS中,任务被触发执行可能的情况有四种:
其中第3种情况是最为常见的,因为任务通常是以周期执行的形式体现。
操作系统在启动时将7号中断(时钟中断)的服务程序设置为了一个叫做OsTick的函数,这个过程由在前文中提到的Frt_Init()函数内完成,其函数体如下:
void Frt_Init(void)
{
TaskType tid;
tid = Os_CreateIsr(OsTick, 6/*prio*/, "OsTick");
IntCtrl_AttachIsr2(tid, NULL, 7);
}
系统每过一段时间,时钟计数器达到一个固定的值之后,就会触发一个时钟中断,并调用OsTick()。
OsTick()将各个计数器加一个增量,再检查基于计数器的报警器是否需要报警,如果需要,则触发相应的事件,然后切换任务。它的函数体如下:
void OsTick(void)
{
// if not used, os_tick_counter < 0
if (Os_Arc_OsTickCounter >= 0)
{
OsCounterType *c_p = Oil_GetCounter(Os_Arc_OsTickCounter);
os_sys.tick++;
IncCounter(c_p);
check_alarms(c_p);
Os_SchTblCheck(c_p);
}
}
含有RTE应用程序的操作系统可分为五个部分:内核、任务体、构件、RTE和基础软件,RTE在Arctic Core中是一个API的转换层,构件只能运行自己的算法或调用Rte中的函数,每一个项目都有用户自己定义的Rte模块将构件的调用转换为BSW的API。整个过程如图 42所示:
图 42 RTE与操作系统关系
内核调度器先分派一个任务开始运行,这个任务首先运行RTE跟踪和诊断程序,在Arctic Core中只实现了KickDog,然后调用构件的runnable入口函数。与如下代码类似:
void bTask25(void)
{
dbg_printf("task25\n");
WdgM_UpdateAliveCounter(WDBG_ALIVE_LOOP_BLINK_COMPONENT);
blinker_component_main();
TerminateTask();
}
进入到Component中,执行一些算法,这些算法可以是自己写的,也可以用matlab生成。算法中如果需要调用底层硬件或发送接收数据,则需要调用RTE提供的函数。如下列代码所示:
int blinker_component_main(void)
{
// 转换小灯状态
if (blinkerStatus == STD_LOW)
{
blinkerStatus = STD_HIGH;
}
else
{
blinkerStatus = STD_LOW;
}
// 通过RTE将信号送给小灯
RTE_blinker_blink(blinkerStatus);
return 1;
}
RTE函数再调用BSW函数,从而实现对硬件的调用和管理。如下所示:
void RTE_blinker_blink(uint8 arg)
{
Dio_WriteChannel(LED_CHANNEL, arg);
}
转换过程还应该实现对硬件资源的互斥使用和诊断管理。但是Arctic Core目前没有实现。
OSEK OS对AUTOSAR运行体的运行提供了很大程度上的支持。它提供了任务、计数器、报警器、事件、资源、中断和通信这6个基本对象。
因为与本文无关,系统中还有很多重要的数据结构如pcb的分析和OIL语言的使用分析没有放到文章中。
Arctic Core对于RTE的支持还处于初步阶段,我们从前文看到的RTE只是AUTOSAR RTE的一部分。