1. 概述

    1. 背景

    ARC Core是一个符合AUTOSAR标准的嵌入式开发平台,它包含三个组件:支持代码(Arctic Core)、开发平台(Arctic Studio)和配置工具(BSW Builder)。

    1. Arctic Core

    Arctic Core是一个开放源码(GPL)的嵌入式系统平台,它基于模块化设计,符合AUTOSAR标准,目前实现了以下内容:

    1. 符合OSEK标准的实时内核
    2. LINCAN总线的通信栈
    3. 外设:ADC, PWM, digital I/O

    其实现路线图如下(图 11):

    图 11 Arctic Core路线图

     

    已经实现

     

    在Arctic Studio Pro中实现

     

    2010年3月实现

     

    2010年5月实现

     

    2010年9月实现

     

    没有计划

    1. Arctic Studio

    Arctic Studio是一个集成了gcc编译器的开发环境(powerpc-eabispe),同时包括了C语言编辑器CDT,在Professional版本中包含ECU的配置工具。

    Arctic Studio对源代码进行统一管理,它会自动创建用于构建Arctic Core的Makefile的基本模板,但用户仍需要编辑自己工程内的Makefile。构建系统模型如图 12所示。

    图 12 构建系统

    1. BSW Builder

    BSW Builder是一个付费的AUTOSAR中BSW的配置工具,它支持图形化的ECU级编辑(如图 13所示)和操作系统级编辑(如图 14所示)。

    图 13 BSW Builder

    图 14 BSW Builder操作系统编辑

    通过基于arxml数据的配置,可以生成和C语言代码的文件。

    1. 目标

    本文档的目的在于分析Arctic Core结构,从而获得它的运行方式RTE与OS的关系,为RTE的实现做准备。

    1. 方法

    分析主要由以下方式进行:

    1. 准备阶段:Arctic Core的获取、编译与调试。
    1. 静态分析:项目的依赖关系及目录结构。
    2. 动态分析:Arctic Core的运行过程,OSEK OSRTE在运行时的关系。
  2. Arctic Core的获取、编译与调试

    1. 获取

      1. Clone Arctic Core

        1. 下载安装Arctic Studiohttp://arccore.com/download/Arctic-Core-Studio)。
        1. 安装过程会附带安装cygwinmercurial,需要确保它们被正确安装。
        2. File->Import中选择Mercurial版本库。并在URL中填入:
          http://arccore.com/hg/arc-stable
          (稳定版)
          http://arccore.com/hg/arc
          (孵化版)
        3. 并选择check out as a project(s) in the workspace完成导入。
      2. Convert to a C/C++ Project

        1. 由于导入完成后项目为一般项目,没有用CDT建立类型和变量索引,所以源码不能进行编译。需要在Project Explorer视图中选择项目,并将其转换为C项目。如图 21所示。

    图 21 将检出项目转换为C项目

    1. 在转换选项中选择Makefile ProjectToolchainEmpty Toolchain。如图 22所示。

    图 22 转换为C/C++项目

    1. 使用

      1. 建立自己的项目

        1. 在菜单中选择File à New à C Project
        1. 新建对话框中Project Type填入Empty Arctic Core ProjectToolchainCore Builder Cygwin Toolchain
        2. Arctic Core source path中填入:${workspace_loc:/arc-stable},其中arc-stable是检出时填写的项目名称。
        3. Advanced settings…中检查下面三个环境变量是否配置正确(C/C++ Build / Environment):

    含义

    变量

    编译器位置

    CROSS_COMPILE=/opt/powerpc-eabispe/bin/powerpc-eabispe-

    板级支持包位置

    BOARDDIR=<arc-stable/boards/任意一个目录名

    需要注意的是:如果想以后要仿真调试,需要选择带有sim的目标板(如mpc5554sim)

    目录外编译位置

    BDIR=../${ProjName}

    1. C/C++ Build / SettingsBinary选项卡中勾选Elf-Parser,编译成功后目录中的elf文件就可以被自动解析。
    2. Project PreferencesProject reference中勾选Arctic Core项目(如arc-stable),编写代码时源代码就可以被自动搜索关联。
    3. 建立C语言文件,并编写代码。(或者也可以拷贝源码库中example下的代码,再进行修改,但要注意的是只拷贝目录下的文件,不要拷贝目录。如图 23所示)

    图 23 拷贝版本库中的示例

    1. 使用BSW Builder编写源代码

    配置信息(如Os_Cfg.c)可以使用手工填写,也可以使用BSW Builder生成。下面的例子就是用BSW Builder写一个简单的OSEK操作系统应用:

    1. 首先新建一个C语言项目

    图 24 新建C语言项目

    1. 建立新的ECU配置文件,这个文件是arxml文件。

    图 25 New Ecu Configuration

    1. Mcu选择MPC551x并确定。
    2. 添加一个OS对象:

    图 26 添加OS对象

    1. 双击OS进入其配置页面。
    2. 勾选所有的hook并添加1Alarm1Counter、一个Event1Task。填入OS tick frequency1000Hz,也就是说1ms运行一次。
    3. 修改CounterTick per base unit1Counter unitOsTick,与系统时钟同步。Max Allowed Value1000,也就是说1秒回卷一次。Min allowed ticks cyclic alarm 10,则表示alarm最短也要10ms才能出发一次。
    4. 修改EventEvent Mask1,表示一号事件。
    5. 修改Task类型为Extended,优先级为1并且Autostart
    6. 修改AlarmAlarm Counter为刚刚创建的Counter_1,并且AutostartAlarm time100表示0.1秒触发一次。Alarm cycle也为100,类型为"相对"。
    7. 添加set Event action,并Set EventEvent_1Set Event TaskTask_1
    8. 完成后选择Generate Stubs…产生Task.c
    9. 回到Overview,在Generator output directory中填入工程目录下的config并在workflow中选择Generator产生Os_Cfg.cOs_Cfg.h
    10. 打开Task.c文件,在文件顶部填入调试输出信息支持头文件:

      #define USE_DEBUG

      #include "Trace.h"

       

      #define USE_SIMPLE_PRINTF

      #include "simple_printf.h"

    11. Task_1中填入简单的调试信息:

      // 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");

          }

      }

    12. 在所有支持的系统Hook中加入相应的调试信息:

      // 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");

      }

    1. 编译

    代码编写完成后,检查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'

     

    1. 调试

    调试有多种方式,最好的是把程序直接下载到目标板,使用OCD方式进行。如果没有目标板,只有使用仿真器进行。

    1. Lauterbach T32 Simulator(Trace32)

      1. http://www.lauterbach.com/frames.html?download.html 上下载PowerPC的仿真器simppc.zip

    (注意:下载的仿真器是评估版,只能调试60分钟。如果需要调试超过60分钟,请双击下面的图标"T32.rar"下载)

    1. 解压simppc.zip,并把t32mppc.exe, config.t32, helpdemo.t32, men8xx.men, men405.men, t32.men, t32.cmm这几个文件拷贝到arc-stable\tools\t32\下。如果使用的是上面的压缩包,则不需要拷贝,可解压后直接运行,并跳过4-7步。
    2. 运行t32mppc.exe
    3. 选择FileàRun Batchfile…,并打开start.cmm
    4. Config对话框中选择合适的目标CPU(与编译有关)和工程目录。
    5. 此时菜单栏会出现Autosar菜单项,选择AutosaràLoad,然后打开编译好的目标文件。
    6. 选择AutosaràRestore Layout整理界面,弹出终端对话框。
    7. 进行调试。
  3. 静态分析

    1. 目录结构

    Arctic Core目录结构分为半,一半为标准的OS和外设支持文件,另一半为用户定制项目的配置文件和任务及其支持文件。如图 31所示:

    图 31 Arctic Core项目目录结构

    1. 标准库

    标准库中个目录详细信息如下:

    目录名

    概述

    所包含的内容

    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

    1. 用户目录

    用户目录中结构详细信息如下:

    目录名

    概述

    所包含的内容

    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

    1. 依赖关系

    整个项目依赖关系如图 32所示,从makefile中我们也可以看出整个编译首先是从用户目录开始的。

    基于AUTOSAR的项目不需要自己编写的Task,而是由Rte中的Runnable实现。整个操作系统启动后首先进入自动生成的Task,再由Task调用Runnable的任务体。

    任务和资源的管理在Task中由Rte自动生成实现,Alarm和Event都被封装起来自动调用,Runnable中的程序只能调用Rte提供的或BSW的函数,通信由Rte层进行转换。Rte是如何封装任务API的将在后面内容中说明。

    操作系统和通信又基于目标板端口读写和中断实现,它们全部基于特定的CPU构架。

    图 32 Arctic Core项目依赖关系

     

  4. 动态分析

    1. 硬件系统启动

    程序的二进制布局由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

    这段代码主要完成了以下工作:

    1. 初始化微处理器寄存器:r1=堆栈基始地址,r13=.sdata基地址,r2=.sdata2.sbss2基地址。
    2. 初始化内存,把程序中的静态数据从rom拷贝到ram
    3. 调用main函数。

    其流程如图 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解压。

    1. 操作系统启动

    硬件系统启动后,调用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);

    }

    它主要做了以下几件事情:

    1. 初始化全局变量internal_datainiternal_data可以看作是ECU的控制块,它是一个EcuM_GobalType结构体,定义如下:

    名称

    类型

    内容

    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当前状态

    1. 启动调试模块(Development Error Tracer
    2. 启动操作系统InitOS()完成以下工作:

      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))

    1. 调用StartOS启动操作系统。

    在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。

    首次任务切换后操作系统就开始正式运行,至此系统启动完毕。

    1. 操作系统运行

    操作系统的运行核心就在于如何组织任务抢占CPU的时间片。在OSEK OS中,任务被触发执行可能的情况有四种:

    1. 被其他任务、中断服务程序调用SetEvent()ActivateTask()触发。任务还可以调用ChainTask()触发切换。
    2. 其他任务释放资源。
    3. 被报警器(Alarm)触发。
    4. 通信通知。

    其中第3种情况是最为常见的,因为任务通常是以周期执行的形式体现。

    1. 周期任务的执行

    操作系统在启动时将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);

        }

    }

    1. RTE与操作系统的关系

    含有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目前没有实现。

  5. 总结

    OSEK OS对AUTOSAR运行体的运行提供了很大程度上的支持。它提供了任务、计数器、报警器、事件、资源、中断和通信这6个基本对象。

    因为与本文无关,系统中还有很多重要的数据结构如pcb的分析和OIL语言的使用分析没有放到文章中。

    Arctic Core对于RTE的支持还处于初步阶段,我们从前文看到的RTE只是AUTOSAR RTE的一部分。

posted on 2010-03-16 19:05  chrihop  阅读(14028)  评论(3编辑  收藏  举报