理论

AUTOSAR OS概述

该OS是一种RTOS实时操作系统,来源于OSEK OS,保留了很多原有特性。

在整个AUTOSAR架构中,可以看出其贯穿一整个BSW

scalability class 可扩展性等级

SC1 基本功能、调度

SC2 带有时间保护Timing Protection

SC3 带有内存保护、OS 应用等

SC4 前者的总和

Counter计数器

Counter是OS系统内部的时间基准,单位为tick,每个tick的长度由人为决定(可以是1ms,也可以是1us)

分为硬件Counter和软件Counter

  • 硬件Counter由MCU硬件Timer触发,通过周期中断增加Counter计数,所以可以通过修改Timer周期来更改tick长度;

  • 软件Counter由人为通过软件(例如OS API调用, IncrementCounter)来增加计数

Counter可以配置以下属性:

最大计数值 MAXALLOWEDVALUE

定义了Counter累加的最大值,当OS Tick达到该值后会归零,OS Tick的数据类型是uint32,不超过该上限即可(uint32最大表示4,294,967,295)
最大值设置为5,则0->1->2->3->4->5->0->....重复

最小周期 MINCYCLE

the minimum allowed number of counter ticks for a cyclic alarm linked to the counter
代表驱动Alarm的最小周期,例如设置为5,则Alarm只允许最短5tick触发一次,类似于界定了Counter的分辨率

计数器类型 OsCounterType

有硬件计时器(连接硬件TIM定时器)和软件计时器(通过软件API计数)

多少时间(秒)代表为一个Tick OsSecondsPerTick

设置为0.001即1ms,说明现在一个Tick的时间即为1ms,即1ms增加一次Counter tick计数

在查看项目os配置中发现,该参数有时候可以不使能(disabled),
则对于硬件Counter(由硬件定时器驱动的),定时器与counter同频,
例如,对应硬件定时器STM为100MHz(10ns),则SecondsPerTick默认为10ns;

如果为软件Counter,其作用机理尚未确定

2026-07-24 Add:

当OsSecondPerTick参数前面有可选项时,我发现即使修改该参数,不会影响Counter运行频率;此时该参数的作用只有展示当前Counter计数频率的作用

多少Tick代表一个Base OsCounterTicksPerBase

这个Base在手册上解释为:how many ticks of the counter represent a known unit of counting
指定了计数器中的多少个 tick对应一个已知的计数基准单位,我认为这个已知的计数基准单位与上面OsSecondsPerTick相对应。
比如当前硬件时钟STM频率为100MHz(10ns),OsSecondsPerTick为1ms,则实现1ms的OS Tick需要的硬件时钟tick为100,000,最大值为4,294,967,295,即uint32

2026-07-24 Add:

当修改OsCounterTicksPerBase后,发现其完全不起作用,所以这里对其作用产生疑问。

我猜测,因为这个芯片STM0频率固定,在OS配置中只能配置一个HW_COUNTER对应STM0,所以更改有关修改计数频率的参数都没有作用,例如OsSecondsPerTick,OsCounterTicksPerBase;但是MINCYCLE有作用,对应当OsTimeUnit配置为Tick时,OsAlarmCycleTime不得小于MINCYCLE

所以通常先配置一个Counter对应硬件STM,在配置一个软件Counter与Alarm对硬件Counter进行分频,作为系统Counter产生节拍。

EB配置

Alarm报警器

像闹钟一样,用于定时驱动业务,可以是:

  • 激活任务 Activating a task

  • 设置事件 Setting an event

  • 驱动用户软件计数器 Incrementing a user-defined software counter

  • 调用Alarm回调函数 Calling an alarm-callback routine

每个Alarm必须由一个Counter驱动,但是一个Counter可以驱动多个Alarm

启动Alarm可以通过:

  • 绝对启动方式 SetAbsAlarm

StatusType SetAbsAlarm ( 
  AlarmType AlarmID, /* Id of the alarm */ 
  TickType start,    /* Absolute counter value in ticks */
  TickType cycle     /* Cycle value */ 
)

Alarm在指定Tick处启动,如果设置为0,则在对应Counter Tick为0时触发Alarm,可以设置周期启动

这里示例为 Counter最大计数值为5,Alarm start为4,cycle为3,触发示例如下所示:

Counter值:   3    [4]    5     0    [1]    2     3    [4]    5     0    [1]    2     3    [4]    5
Tick 时间:   |-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|
AlarmA:         绝对触发            触发              触发               触发              触发

  • 相对激活方式 SetRelAlarm

StatusType SetRelAlarm ( 
  AlarmType AlarmID,   /* Id of the alarm */ 
  TickType increment,  /* Absolute counter value in ticks */ 
  TickType cycle       /* Cycle value */
)

Alarm在当前位置处Tick+increment的Tick处启动,可以设置周期启动

这里示例为 Counter最大计数值为5,Alarm increment为3,cycle为3,触发示例如下所示:

Counter值:   3    [4]    5     0    [1]    2     3    [4]    5     0    [1]    2     3    [4]    5
Tick 时间:   |-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|
API调用:           ^                 |                 |                 |                 |
AlarmB:         首次触发             触发              触发              触发              触发

经过测试在OsAlarmAutostart选项卡下,

OsTimeUnit可以配置为Nanoseconds和Tick,不使能默认为Tick;
Nanoseconds如何转换为Tick过程如下:

对于TC23x芯片,STM0_T0计数频率为100MHz,则可见1Tick对应10ns

将OsTimeUnit修改为Tick后,在其它参数不改变的情况下,CycleTime从1ms变为10ms,则其驱动的软件计数器sysCounter计数周期也变为10ms,进而导致其驱动的Schedule Table从1ms变为10ms的一个单位;进而导致整体任务触发时间变慢了10倍,即原本1ms周期的任务变为10ms周期;

从UDE中监控一个自定义1s全局变量计数可见,从1s计数一次变为10s计数一次。

if(gApp1_Task_10ms_Counter % 100 == 0)
{
    gApp1_Task_1s_Counter++;
}

Task任务

Task是可执行实体Runnable(函数)的容器,Runnable里面实现了很多业务逻辑,分配到不同的Task中,由操作系统调度执行

Task分为基本任务BT与扩展任务ET

Task都具有三种状态:运行状态(Running)、挂起状态(Suspend)和就绪状态(Ready)扩展任务支持事件触发,特有:等待状态(Waiting)

系统启动后,Task未被激活activate,处于挂起状态suspend;
当任务被激活时,等待系统调度,处于就绪状态ready;
当cpu空闲且没有更高优先级任务时,task被系统调度,处于运行状态running;
当被高优先级任务抢占时,回到就绪状态ready;
任务结束后,再次进入挂起状态suspend,等待下次激活;

当该任务为扩展任务时,进入运行状态running后,
随后该扩展任务进入一个无限循环,循环的开头是(扩展任务不一定一直处于循环状态)WaitEvent,
然后任务进入等待状态Waiting,
当等待的事件发生时,进入就绪状态ready等待系统调度...

2026-07-17 Q: 当扩展任务刚收到Event唤醒,同时其他优先级更高的任务被激活时,谁先触发?
2026-07-20 A: Event唤醒时,该ET任务首先从Waiting状态标记为Ready状态,受系统调度,等待系统空闲或者抢占别的任务进入Runing状态。

基本任务的示例代码:

TASK(Task1)
{
  Application ...
  TerminateTask();
}

扩展任务示例代码:

TASK (Task2)
{
  EventMaskType ev;
  for ( ; ; )
  {
    WaitEvent(Ev1);
    GetEvent(Task2,&ev);
    ClearEvent(ev);
    Application ...
  }
  TerminateTask();
}

任务激活方式:

直接激活:

StatusType ActivateTask ( TaskType TaskID /* Id of the task to be activated */ )
//启动系统调度,通过优先级决定激活任务是否进行
StatusType ChainTask ( TaskType TaskID /* Id of the task to be activated */ )
//当前任务立刻终止,系统重新调度,通过优先级决定激活任务是否进行

间接激活:通过Alarm, Schedule expiry point激活

任务结束方式:

StatusType TerminateTask(void)
// 任务只能被自己终止,每个任务必须最后都要被终止
StatusType ChainTask ( TaskType TaskID /* Id of the task to be activated */ )
// 当前任务终止,下一个任务连续执行(是否执行取决于系统调度)

注意:每个TASK必须在结尾处调用任务结束方式。

任务符合类别comformance class

分为BCC1,BCC2,ECC1,ECC2

1类与2类的区别在于:2类支持任务多重激活相同优先级可以设置多个任务

Basic类与Extend类区别在于:Extend支持事件触发,即ET

一般情况下,我们的操作系统依赖ECC2级别的任务配置

任务多重激活:

对于每个任务都有一个激活计数器;可以配置允许的最大激活次数;当激活到最大次数后,新的激活将被拒绝;当任务终止后,计数器将减少;

调度机制

整个Autosar OS都是基于优先级进行调度的——高优先级任务抢占低优先级

系统调度策略:

  • 完全可抢占型调度策略 Full Preemptive

默认的调度方式,各个任务之间通过优先级级别决定执行顺序

相同优先级任务按先后顺序执行FIFO

  • 不可抢占型调度策略 None Preemptive

当前任务运行时不可被打断,只有运行结束后系统调度优先级高的任务进行执行

非抢占型任务可以使用API Schedule来允许具有更高的优先级

  • 混合型策略 Mix Preemptive

两种方式混合(一般使用这种方式,初始化任务设置为不可抢占型,其他任务设置为可抢占)

非抢占指的是任务之间不可打断,但中断属于异步触发,可以打断非抢占型任务

Schedule Table调度表

当需要实现任务之间的协同配合,尤其是实现特定的时间关系间隔,就需要依赖调度表

调度表就相当于一个人一周/月内的计划,然后按照这个时间顺序去执行

调度表具有特定长度的时间轴(周期)和一系列在该时间轴上具有特定偏移的触发点(到期/溢出点expiry point)组成的

与Alarm相同,每个调度表必须由一个Counter驱动

可以配置为单次运行循环运行

启动方式(同Alarm)

  • 绝对启动方式 StartScheduleTableAbs

StatusType StartScheduleTableAbs ( 
  ScheduleTableType scheduletableid,
  TickType offset 
)

指定Tick处启动(从Counter为0算起),如果offset设置为4,则在对应Counter Tick为0+4=4时触发调度表

这里示例为调度表长度为10,offset为4,EP0 offset为0,EP1 offset为4,EP0 offset为8,触发示例如下所示:

Counter 值:      2      3     [4]    5     0     1     2     3     4     5     0     1     2     3     4     5     0     1 
                 |------|------|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----
                       
ScheduleTableA:  |------|------0-----1-----2-----3-----4-----5-----6-----7-----8-----9-----0-----1-----2-----3-----4-----5-----
                 |             |                       |                       |           |                       |          
                 |         EP0:Task_A              EP1:Task_B              EP2:Task_C   EP0:Task_A              EP1:Task_B
                 |             |------------------------长度为10---------------------------|
                 |
                 *
  调用StartScheduleTableAbs(ScheduleTableA, 4)

  • 相对激活方式 StartScheduleTableRel

StatusType StartScheduleTableRel ( 
  ScheduleTableType scheduletableid,
  TickType offset 
)

在当前位置处Tick+offset 的Tick处启动

这里示例为调度表长度为10,offset为2,EP0 offset为0,EP1 offset为4,EP0 offset为8,触发示例如下所示:

Counter 值:      2      3     [4]    5     0     1     2     3     4     5     0     1     2     3     4     5     0     1 
                 |------|------|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----|-----
                       
ScheduleTableA:  |------|------0-----1-----2-----3-----4-----5-----6-----7-----8-----9-----0-----1-----2-----3-----4-----5-----
                 |             |                       |                       |           |                       |          
                 |         EP0:Task_A              EP1:Task_B              EP2:Task_C   EP0:Task_A              EP1:Task_B
                 |             |------------------------长度为10---------------------------|
                 |
                 *
  调用StartScheduleTableRel(ScheduleTableA, 2)

调度表的同步机制(较难,先略过)

Resource资源

调度器资源scheduler resource (RES_SCHEDULER) 用于短暂提升任务的优先级

ISR中断

支持两类中断

  • Category1 ISR 一类中断

    • 执行不经过OS管理,不可关闭(2026-07-21 add: 可以通过suspend/disableAllInterrupts暂停/关闭)

    • 不能使用OS API(启动/终止Task)

    • 中断优先级非常高,执行速度非常快

    • 外设等的中断,如DMA、SPI、PWM等与OS并非直接关联的

  • Category2 ISR: 二类中断

    • 可以使用OS API(启动/终止Task,触发Event,调用协议栈等)

    • 可以提供资源保护,进行中断栈监控

中断嵌套

  • 一类中断优先级比二类高,可以打断二类中断

  • 二类中断之间根据优先级高低被打断,即可以实现中断嵌套

我没有在Autosar Interrupt、EB OS文档中找到相关内容,
但种种迹象使我认为二类中断可以被嵌套的
而且中断优先级不可以设置相同

2026-07-21 solved: 阅读Safety_OS_document_TRICORE发现

If an interrupt service routine is being executed, and an interrupt source with a higher level is triggered, then the processor accepts the interrupt request and preempts the currently executing interrupt service routine...
...
In the case of the TriCore you can select the interrupt level of an ISR with the option OsTricoreIrqLevel.

可见允许嵌套中断运行

中断优先级只允许在配置中修改,程序运行时不允许修改。

错误处理及Hook函数

Hook钩子函数,就是操作系统执行到某处调用该函数,该函数的实现由用户来定义,用以将用户的操作意图插入到某个操作系统的执行步骤中,类似穿针引线,因此称为钩子函数

  • ErrorHook

处理OS运行过程中的一般性错误时被触发,当该服务返回的 StatusType不是E_OK时,或者当其他系统服务检测到错误条件时触发

void ErrorHook ( StatusType Error /* the error code */ )

进入ErrorHook之后,可以通过OSErrorGetServiceId() 获取具体报错的系统服务,并通过OSError_x1_x2 (x1为系统服务名称,x2为参数名称)来获取具体发生错误的ISR或Task

  • ProtectionHook

系统出现严重错误时调用,例如超出最长执行时间或超出内存保护边界,根据返回值,操作系统将杀死该任务或类别Cat2 ISR,尝试重启或关闭系统;
出现栈溢出、系统异常都会流经这里。并且AUTOSAR OS中的内存保护、时间保护等异常,都会调用该钩子函数,因此这里是集中处理系统异常的地方

  • StartupHook

在系统启动时由内核调用,此时系统已经初始化但调度器尚未激活。在钩子执行期间,所有中断都被禁用

  • ShutdownHook

每当ShutdownOS时调用,无论是应用程序显式调用还是系统隐式调用,都会触发。它可以用于重新初始化或错误日志记录等目的。当钩子返回后,系统将关闭所有服务

  • PreTaskHook

内核即将进入新的任务之前被调用,此时GetTaskID返回是当前任务,GetTaskState返回即将进入任务的状态为RUNNING

注意:PreTaskHook只要任务进入Running状态就会调用一次,无论是任务第一次启动时,还是从Waiting状态返回时,还是获取CPU调度时,都会被调用

  • PostTaskHook

离开当前任务上下文后在内核上下文中被调用。由于抢占、任务终止或任务等待事件都可以引起该函数;此时GetTaskID返回是 即将进入的任务,GetTaskState返回即将进入任务的状态为RUNNING

注意:PostTaskHook只要任务离开Running状态就会调用一次,无论是任务终止时(terminate, chain),还是进入Waiting状态时,还是被更高优先级任务抢占时,都会被调用

  • PreISRHook

在进入中断服务例程的用户代码之前调用

不同于Pre/PostTaskHook,Pre/PostISRHook只会被调用一次

  • PostISRHook

在退出中断服务例程的用户代码之后调用

不同于Pre/PostTaskHook,Pre/PostISRHook只会被调用一次

TriCore的异常处理机制Trap

TriCore的异常处理机制称为Trap系统,T界定了8种Trap通用类型Trap Class Number(TCN),每个类都有自己对应的Trap Handler函数;在每个类中,具体trap记录在Trap Identification Number(TIN)中,对应寄存器为D[15];

Traps具体分为同步异步类,硬件软件触发类,具体如下表所示:

同步Trap

Synchronous traps are associated with the execution or attempted execution of specific instructions, or with an attempt to access a virtual address that requires the intervention of the memory-management system. The instruction causing the trap is known precisely. The trap is taken immediately and serviced before execution can proceed beyond that instruction.

同步Trap与某条指令的执行,或试图执行该指令,具有直接且明确的关联;
引发Trap的指令可以被精确确定
Trap会立刻被接受,并且在CPU执行后续指令之前完成处理。

在同步Trap后通常可以通过通用寄存器中A11 (return address)的值找到那条指令的地址

异步Trap

Asynchronous traps are similar to interrupts, in that they are associated with hardware conditions detected externally and signaled back to the core. Some result indirectly from instructions that have been previously executed, but the direct association with those instructions has been lost. Others, such as the Non-Maskable Interrupt (NMI), are external events. The difference between an asynchronous trap and an interrupt is that asynchronous traps are routed via the trap vector instead of the interrupt vector. They can not be masked and they do not change the current CPU interrupt priority number.

异步Trap与中断类似,由CPU Core外部(看门狗、外设等)检测到的硬件条件引起,并有外部模块反馈给CPU Core。
有些异步Trap是由之前执行过的指令引起的,但当Trap到来时,已经无法准确、直接关联到触发的那条指令
(例如,数据访问错误,错误消息在访问结束后一段时间才发生,接收到异常时以及不在当时触发数据访问的那条指令处了)
异步Trap与中断区别在于前者不会被屏蔽,不会改变CPU中断优先级号,而且两者的入口不同。

Trap的优先级:异步 Trap > 同步 Trap > 普通中断

Trap与中断的区别在于

  • Trap始终有效,不可软件屏蔽,级别高于中断

  • 向量表不同,Trap使用BTV,中断使用BIV

  • Trap侧重于异常、故障、系统服务和不可屏蔽事件;中断强调可调度、带优先级的异步外部事件

在介绍Trap之前,先介绍几个相关寄存器

GPR (General Purpose Registers) 通用寄存器
程序平常计算和传参使用
16 Data registers (DGPRs), 16组数据寄存器 D[0] to D[15]
16 Address registers (AGPRs), 16组地址寄存器 A[0] to A[15]

CSFR (Core Special Function Registers ) 内核特殊功能寄存器组
控制内核工作,作为内核状态仪表盘,监测内核运行状态
包含常见寄存器有PCXI/PCX,PSW,PC,BIV,BTV,ISP,ICR,FCX/LCX
PCXI (Previous Context Information and Pointer Register) 先前上下文信息和指针寄存器,保存/恢复上下文的CSA信息
PSW (Program Status Word) 程序状态字寄存器
PC (Program Counter)程序计数器寄存器,当前运行指令的地址
BIV(Base Interrupt Vector Table Pointer)中断向量表基地址寄存器
BTV(Base Trap Vector Table Pointer)Trap向量表基地址寄存器
ISP(Interrupt Stack Pointer)中断栈指针
ICR(Interrupt Control Register)中断控制寄存器,当前CPU优先级,中断使能
FCX (Free Context[CSA] List Head Pointer Register) 空闲CSA列表头指针,永远指向空闲的CSA
LCX(Free Context[CSA]List Limit)指向最后一个可用的CSA,当FCX与LCX相同时,系统报Trap

常涉及到的调试寄存器

D15

Data Register 15

Trap 入口时保存 TIN

A11

Address Register 11

Trap返回地址;对同步 Trap,通常用于定位出错指令

BTV

Base Trap Vector Table Pointer

Trap Vector Table 基地址

PSW

Program Status Word

CPU 权限模式、保护集、栈状态等

PCXI

Previous Context Information Register

CSA 上下文链信息

DEADD

Data Error Address Register

数据访问错误的目标地址信息

DSTR

Data Synchronous Trap Register

同步数据访问 Trap 状态

DATR

Data Asynchronous Trap Register

异步数据访问 Trap 状态

Class 0 Virtual Address Trap

TIN

Trap

英文全写

含义

0

VAF

Virtual Address Fill

虚拟地址映射缺失 / TLB 填充 Trap

1

VAP

Virtual Address Protection

虚拟地址页保护 Trap

用于 Memory Management Unit(MMU,内存管理单元)相关异常,处理的是基于 虚拟地址、页表和 TLB 的地址转换及访问权限问题

对于未实现 MMU、未启用 MMU 的 TriCore/AURIX 应用,Class 0 通常不会成为常见问题

在典型AUTOSAR OS 项目中,内存隔离通常更常依赖TriCore Range-Based Memory Protection MPU范围型内存保护保护)
而不是类似通用操作系统中的完整虚拟内存和页表管理

MMU通常在 ARM Cortex-A 核的汽车 SoC 中设计

对应MircoKernal中Trap handle函数为

void MK_HandleVirtualAddressTrap(mk_uint32_t pcxiValue, mk_uint32_t d15Value,mk_uint32_t d11Value)

0 VAF

若 CPU 访问的虚拟地址没有对应 TLB 映射,就可能产生 VAF

CPU 访问一个虚拟地址时,需要先找到 虚拟地址 VA → 物理地址 PA 的映射关系,
该映射通常缓存在 TLB(Translation Lookaside Buffer) 地址转换缓存中。

在Autosar OS应用开发中,VAF 通常不是由业务代码逻辑直接引起的常见问题,优先怀疑Bootloader与Application的地址转换配置不一致等

1 VAP

CPU访问的虚拟地址找到了映射关系,若程序行为不符合PTE权限,就可能进入 VAP

每个PTE(Page Table Entry) 页表项不仅包含虚拟物理的映射,还可包含权限
例如,是否允许读写,执行,允许用户态访问等

在Autosar平台中可以理解为:当前 Task / ISR 所属的执行上下文,试图访问一个页表权限不允许访问的区域

Class 1 Protection Trap

TIN

Trap

英文全写

含义

1

PRIV

Privilege Violation

特权违规

2

MPR

Memory Protection Read

内存保护读违规

3

MPW

Memory Protection Write

内存保护写违规

4

MPX

Memory Protection Execute

内存保护执行违规

5

MPP

Memory Protection Peripheral Access

外设访问保护违规

6

MPN

Memory Protection Null Address

空地址访问保护违规

7

GRWP

Global Register Write Protection

全局地址寄存器写保护违规

TriCore CPU 内部保护机制产生的Trap,包括CPU运行权限保护、范围性内存保护、特殊地址/外设保护

对应MircoKernal中Trap handle函数为

void MK_HandleProtectionTrap(mk_uint32_t pcxiValue, mk_uint32_t d15Value,mk_uint32_t d11Value)

1 PRIV

当前程序运行在User Mode,却执行了该模式不允许执行的特权指令

具体 Task、ISR、Trusted Function 最终运行在哪一种 CPU 模式,取决于 OS 厂商实现和项目配置

在Autosar OS中该情况可能发生在,None-Trusted OS Application直接进行特权操作;普通Task修改核心控制寄存器;应该通过OS Service调用却直接执行....

排查 PSW.IO 查看当时处于何种权限模式

2 MPR

当内存保护启用后,当前程序(Task/ISR)对某个地址执行读操作,但该地址不属于当前 Protection Set 中允许读取的范围

PSW寄存器中的PRS(Protection Register Set) 确定当前使用哪个保护寄存器集,其中对应着当前允许访问的内存范围

在Autosar中该情况可能发生在,App A 读取 App B 的私有 RAM;Task访问了未授权的共享内存;指针越界;野指针;非可信应用(None-Trusted OS Application)读取OS Kernel数据

调试重点:A11查看对应指令;读取 DEADD 查看实际读取地址;查看当前Task属于哪个APP;查看PSW.PRS使用哪个内存集合;内存映射属于Kernel,APP,还是Shared RAM

3 MPW

当内存保护启用后,当前程序(Task/ISR)向某个地址执行写操作,但该地址不属于当前 Protection Set 中允许写入的范围/没有写入的权限

指令对应的EA(Effective Address)有效地址,并不在Write Protection Range内,导致MPW

在Autosar中导致出现的典型原因有,App A 写入 App B 的私有 RAM;Task写入了未授权的共享内存;写入只读常量或Flash区;指针越界;非可信应用写入OS Kernel RAM;

调试重点:A11;DEADD;当前Task属于哪个APP;PSW.PRS;源代码

4 MPX

CPU正在尝试从当前 PC 指向的位置取指并执行,但该地址区域没有 Execution 权限

“地址区域没有Excute权限”指,MPU规定不能从这里Execution(即使这里存放了可执行代码,但不允许Execution)

典型原因有:函数指针、回调函数、Vtable向量表被破坏;栈溢出覆盖了函数返回地址;栈/局部数组越界;调用了已经失效的回调函数地址

检查重点:Task Stack用量;函数指针;回调表

调试重点:A11;PSW.IO;DEADD目的地址是否为外设SFRDEADD没有值);查看调用链中是否绕过了MCAL/OS Service

5 MPP

当前程序运行在User-0 Mode时,尝试读写被定义为Peripheral Segment的地址区域

“被定义为Peripheral Segment的地址区域”指,访问外设——低权限任务直接访问外设寄存器

典型原因有:非可信应用直接操作MCU外设寄存器;应用程序绕过MCAL,直接访问SFR;MCAL API错误放在非特权上下文中执行;外设驱动需要通过Trusted Function访问,但实际直接调用;裸地址调用

6 MPN

程序访问了空指针,触发访问保护

典型原因:指针未初始化;API返回NULL,直接就解引用了;指针无效等

调试重点:A11;DEADD是否为0x000;调用链查看谁传入了NULL

7 GRWP

程序在没有权限的情况下,试图修改全局地址寄存器

AGPR中A[0],A[1],A[8],A[9]为global address 保存一些全局基址,例如全局变量基址,固定数据基址等

Class 2 Instruction Trap

TIN

缩写

英文全写

中文说明

1

IOPC

Illegal Opcode

非法操作码

2

UOPC

Unimplemented Opcode

未实现操作码

3

OPD

Invalid Operand

非法操作数

4

ALN

Data Address Alignment

数据地址对齐错误

5

MEM

Invalid Memory Address

非法内存地址

Class2 对应指令错误,包括指令编码不合法;CPU不支持当前指令;指令操作数编码不合法;指令数据地址不符合对齐规则。

对应的MicroKernal中的handle函数为:

void MK_HandleInstructionTrap(mk_uint32_t pcxiValue, mk_uint32_t d15Value,mk_uint32_t d11Value)

1 IOPC

CPU 从当前程序计数器 PC 指向的位置取出了一段二进制数据,但这段数据不对应当前CPU支持的任何合法指令

CPU 想执行一条指令,但取出来的内容是“乱码”。

2 UOPC

CPU 识别该指令,它是架构中定义的合法指令,但是当前具体芯片或当前 Core 并没有实现该功能

这条指令“语法正确”,但当前硬件没有对应能力
芯片芯片没有 MMU,但代码执行了 MMU 指令;
芯片没有 FPU,但代码执行了浮点运算相关指令;

在Autosar下常见原因有:

  1. 编译器 CPU Target 配置错误;

  2. 工程使用了不匹配芯片型号的库;

  3. Bootloader 和 Application 使用了不同的编译目标;

  4. 手写汇编使用了当前芯片未实现的指令;

  5. FPU 选项与实际芯片能力不匹配。

3 OPD

指令本身合法,但该指令中的操作数编码不满足要求

正常C代码中 OPD 较少见,出现时优先怀疑是否进行了手写汇编代码。

4 ALN

CPU 执行数据读写操作时,访问地址不满足该访问宽度所要求的对齐规则

访问数据宽度

常见对齐要求

8-bit

任意地址

16-bit

地址为 2 的倍数

32-bit

地址为 4 的倍数

更宽数据

需要满足对应更严格的对齐要求

对于地址0x70000001 进行了(uint32 *)0x70000001U 访问,但是该地址不是4字节对齐地址,于是可能触发ALN

常见于 CAN PDU 解析;LIN 报文解析;以太网报文解析

5 MEM

当前内存访问地址、地址计算方式或特殊地址访问方式,违反了 TriCore 架构规则或具体芯片实现限制

访问这个地址的方式就不符合 CPU 规则

比较少见,不做解释

Class 3 Context Trap

TIN

Trap

英文全写

含义

1

FCD

Free Context List Depletion

空闲 CSA 即将耗尽

2

CDO

Call Depth Overflow

调用深度计数溢出

3

CDU

Call Depth Underflow

调用深度计数下溢

4

FCU

Free Context List Underflow

空闲 CSA 完全耗尽

5

CSU

Call Stack Underflow

调用栈下溢

6

CTYP

Context Type

上下文类型错误

7

NEST

Nesting Error

嵌套关系错误

Class3与TriCore的CSA(Contxt save area)机制直接相关

CSA 可以理解为一组固定大小的上下文块,通过链表方式管理
TriCore 使用 CSA 自动保存部分寄存器上下文
相关寄存器包括:FCX, LCX, PCXI, PSW

对应MircoKernal中Trap handle函数为

void MK_HandleContextTrap(mk_uint32_t pcxiValue, mk_uint32_t d15Value,mk_uint32_t d11Value)

1 FCD

CPU 刚完成一次上下文保存操作后,发现空闲 CSA 已经接近耗尽

FCX 到达 LCX 所定义的警戒位置
不代表CSA已经完全没有,FCD是资源即将耗尽的预警

2 CDO

当调用深度计数器(PSW.CDE)启用后,当前调用深度达到最大值,又执行CALL

函数调用嵌套太深

3 CDU

当调用深度计数器(PSW.CDE)启用后,当前调用深度已经为0,又执行RET

CPU 认为没有任何可返回的调用层级,但程序却执行了 RET(函数返回)

4 FCU

CPU 需要保存上下文,但已经没有可用 CSA;或者上下文保存/恢复过程中发生了无法完成的错误。

CSA彻底耗尽了;当系统发生此类Trap后,系统不可恢复!!

5 CSU

CPU 尝试恢复上一个上下文(执行RET),但上下文链中不存在可恢复的前一个节点(PCX=0)

CPU执行函数返回时RET,但是Context为空

通常在用户空间不会造成该问题,一般出现在内核OS的问题。
在项目中,总是进入到CSU Trap中,猜测是MK内核问题,暂时没有影响到程序正常运行

6 CTYP

CPU 在恢复上下文时,期望取得某一种类型的 CSA(Upper/Lower),但实际 CSA 类型不匹配

Upper CSA为系统硬件自动生成 Lower是软件管理的

7 NEST

当CPU执行RFE(return form exception)时,调用深度计数器(PSW.CDE)不为0

Trap 或 ISR Handler 内部的函数调用还没有完全返回(RET),却试图直接从异常返回(ISR/Trap返回)

比较少见

Class 4 Bus Error Trap

TIN

缩写

英文全写

说明

1

PSE

Program Fetch Synchronous Error

程序取指同步错误

2

DSE

Data Access Synchronous Error

数据访问同步错误

3

DAE

Data Access Asynchronous Error

数据访问异步错误

4

CAE

Coprocessor Trap Asynchronous Error

协处理器异步错误

5

PIE

Program Memory Integrity Error

程序存储器完整性错误

6

DIE

Data Memory Integrity Error

数据存储器完整性错误

7

TAE

Temporal Asynchronous Error

时间保护异步错误

对应的MicroKernal中的handle函数为:

void MK_HandlebuserrorTrap(mk_uint32_t pcxiValue, mk_uint32_t d15Value,mk_uint32_t d11Value)

1 PSE

CPU 在取指执行代码时发生错误

指令取指发生总线错误

CPU尝试从不具有取指属性的地址区域取指(比如说从外设寄存器区域、普通数据区域取指)

取指:取指令 Instruction Fetch
它指 CPU 执行程序时的一个步骤
CPU 根据程序计数器 PC 里的地址,从内存或指令缓存中读取下一条要执行的机器指令。

一个典型指令执行流程是:
取指:从内存中取出指令
译码:分析这条指令要做什么
执行:进行计算、访存、跳转等操作
写回:把结果写回寄存器或内存

相比于Class1 MPX:没有权限在该地址执行

相比于Class2 IOPC:取出的内容不是合法的指令地址

调试重点:A11查看从哪个地址取指,是否是有效的code区域;

2 DSE

CPU 执行数据访问指令(Load)时,数据访问发生了同步错误,或者数据读取相关的地址、缓存、总线访问出现异常。

DSPR 地址越界——访问 Data Scratchpad RAM 超出实际内存范围(对 DSPR 执行 Load 或 Store,若访问超出范围,也可能产生 DSE)

C 段全局地址错误——访问 C1000000 到 C7FFFFFF 范围,无法转换成全局地址

外部数据读取总线错误——从外部内存、LMU、外设等区域读取数据时发生总线错误

Overlay系统错误——Overlay 机制在数据读取过程中产生错误

调试重点:A11;DSTR;DEADD

3 DAE

CPU执行数据存储指令(Store)时,数据访问发生了异步错误,通常是Store 操作导致总线错误

发生 Trap 时,CPU 可能已经不在执行原始 Store 指令。

CPU 执行 Store
        ↓
数据进入 Write Buffer
        ↓
CPU 继续执行后续代码
        ↓
总线稍后尝试真正写入目标设备
        ↓
外设 / 外部 RAM / 总线返回错误
        ↓
DAE Trap

调试重点:DATR;DEADD;A11(异步错误不要只根据A11判断原始故障位置)

4 CAE

表示某个协处理器(FPU等)报告了异步错误

未知的协处理器指令;浮点运算异常;协处理器内部错误;协处理器状态错误

5 PIE

CPU 从程序存储器中取指时,检测到不可纠正的程序存储器完整性错误,因此无法安全执行该指令

程序存储器例如:PFlash,PSPR

程序存储器通常按一定大小的Fetch group进行取指和完整性检查,所以不一定能定位到具体哪条指令

调试重点:PIEAR(Program Integrity Error Address Register);PIETR(Program Integrity Error Trap Register)

6 DIE

CPU 访问数据时,检测到不可纠正的数据存储器完整性错误

可能发生在Load;Store;CSA保存/恢复;RAM访问;Flash数据读取

可能是同步也可能是异步(在TC23x中始终为异步)

调试重点:DIEAR(Data Integrity Error Address Register);DIETR(Data Integrity Error Trap Register)

7 TAE

在设置时间保护系统的程序中,时间监控定时器递减到0时触发

Task,ISR等运行超时

调试重点:TPS_TIMERx;TPS_CON

Class 5 Assertion Trap

TIN

缩写

英文全写

说明

1

OVF

Arithmetic Overflow

算术溢出 Trap

2

SOVF

Sticky Arithmetic Overflow

粘滞算术溢出 Trap

Class 5 通常不是硬件自动在每次溢出后立即产生,而是由软件显式执行特定检查指令后触发。

需要执行指令为:TRAPV(Trap on overflow); TRAPSV(Trap on sticky overflow)

对应的MicroKernal中的handle函数为:

void MK_HandleassertionTrap(mk_uint32_t pcxiValue, mk_uint32_t d15Value,mk_uint32_t d11Value)

1 OVF

当算术运算产生溢出,并且 PSW.V 溢出标志位为 1 时,执行 TRAPV 会触发 OVF Trap

2 SOVF

当某次算术运算曾经发生过溢出,并且该溢出状态被保存在 Sticky Overflow 位 PSW.SV 中时,执行 TRAPSV 会触发 SOVF Trap

Class 6 System Call

与前面Trap不同,该类通常由软件主动执行SYSCALL指令而触发的,用于从受限上下文请求系统服务;

该类Trap不一定表示故障,本质上是TriCore提供的一种执行系统服务的机制

TIN值取决于执行SYSCALL所带的参数,范围是8位无符号数

SYSCALL(0xFF)   //则D15(TIN)就为0xff

执行结束后A11指向SYSCALL后的下一条指令,作为服务完成后的地址

通常,SYSCALL维护于OS中,可作为 Non-Trusted Application 请求 OS Kernel、Trusted Function 或其他特权服务的底层机制。它本身通常不是故障,而是一次受控权限切换

对应的MicroKernal中的handle函数为:

void MK_TricoreSyscall(mk_uint32_t pcxiValue, mk_uint32_t d15Value)

Class 7 NMI(Non-Maskable Interrupt)

不可屏蔽中断,与普通中断不同,NMI通常用于报告必须被处理的严重系统事件:

例如:外部安全信号、看门狗超时、电源故障、严重硬件故障

对应的MicroKernal中的handle函数为:

void MK_HandleNmi(mk_uint32_t pcxiValue, mk_uint32_t d15Value,mk_uint32_t d11Value)

Trap注入

Class 0

在TC2xx/TC3xx中MMU不支持,Class0Trap被保留,不会被硬件真实触发

Class1 TIN1 PRIV(Privilege Violation)特权违规

操作核心寄存器,这里放置在App2中(none-trust application),属于User-1权限,没有操作核心存储器的权限

MTCR(CPU_A5,0U);  //向A5寄存器写入0值

成功触发TIN1 Trap

Class1 TIN2/3 内存保护读/写

在App3中读取App1中的变量

App3为QM应用,在OS中通过MPU设置了不可写但可读取App1(ASIL-D)的变量

可以正常读取,但在写入时,跳转至Trap中

A11为写入变量的代码运行地址;

同时DEADD寄存器,显示操作变量的地址

对应的变量即为gApp1_Task_1s_Counter

Class1 TIN4 MPX(Memory Protection Execute)内存保护执行违规

构建了一个函数指针,指向DSPR区域(RAM区域没有执行权限只有读取和写入权限),并执行

进入Trap后发现 A11并不是执行函数指针的位置,而是函数指针指向的非法地址

所以这可能造成不太好排查到底是哪里开始执行的

Class1 TIN5 MPP(Memory Protection Peripheral Access)外设访问保护违规

None-trusted Application默认外设权限为User-1,具有一般外设访问权限(定时器、IO)

这里程序运行至app3_10ms时,通过UDE认为修改当前外设权限为User-0,

当在app3_10ms Task中访问STM寄存器时,即触发Trap

通过A11定位到触发Trap的代码地址为:

即可看出这里读取了STM定时器的值

Class1 TIN6 MPN(Memory Protection Null Address)空地址访问保护违规

volatile uint32_t v;
v = *(volatile const uint32_t *)(uintptr_t)0x00000000u;
(void)v;

可以预料到这是对无效地址NULL进行访问,

提前在ProtectTrap处打断点,运行程序后果然运行至此

在这里,查看Core Register

其中,A11对应进入Trap时的地址;D15对应TIN类型

右键选择A11对应的地址可以进行跳转

可以看出是在App1.c 364行代码处出现了问题,对应源码为:

即,可以看出确实是我们注入的代码处发生了trap;

Class1 TIN7 GRWP(Global Register Write Protection)全局地址寄存器写保护违规

uint32 test_value = 0u;
__asm volatile (
  "move.aa %%a0, %0"
  :"=a"(test_value)
);  //把test_value的值赋值给a0寄存器

进入Trap后,可以看到D15为7,对应TIN为7

跳转到A11地址处对应的代码为:

可见,这里错误操作了A0寄存器

Class 2 TIN1 IOPC(Illegal Opcode)非法操作码

在PFlash中,任意找到一处含有非法操作码的区域(illegal Instruction)作为测试目的地址

运行后即可进入对应Trap

A11地址为非法操作码区域,但看不到从哪里调用这个非法目的地址的

Class 2 TIN2/3/5

这里在常规编程中,几乎遇不到操作码有效,但不支持本CPU(一般发生在不支持FPU但启用了FPU编程)

Class 2 TIN4 ALN

这里对一个没有4字节对齐的地址进行了uin32取指

A11为取指处代码

DEADD为取指目的地址

Class3 TIN1

通过不断递归(前提确保栈足够大),每递归一次消耗一个CSA,当FCX=LCX时,跳入该Trap

A11为最新的一次函数调用处,

跳入Trap后 也是要消耗CSA的,所以FCX的值已经超过了LCX(出现了溢出情况)

然后结束Trap后 系统因为没有可用CSA 导致宕机

Class 4 TIN1 PSE(Program Fetch Synchronous Error)

这里为了触发程序取指错误,把函数指针地址指向外设区内即可

A11的值为访问的目的地址,也同样看不到哪里调用的

Class 4 TIN2 DSE(Data Access Synchronous Error)

为了触发数据访问错误,读取地址超过DSPR地址(0x70000000~0x7002DFFF)范围即可

A11指向取数据的代码位置

DEADD为取数据的错误地址

Class 4 TIN3 DAE(Data Access Asynchronous Error)数据访问异步错误

尝试向PFlash区域写入数值,触发总线异步访问错误

可观察到A11的值并不对应实际触发Store指令处

DEADD为错误写入的地址

DART寄存器显示写总线错误

Class 4 TIN4 CAE(Coprocessor Trap Asynchronous Error)

触发协处理器错误最简单就用FPU触发除零错误即可

在运行至div指令前还需要在FPU_TRAP_CONF寄存器中使能FZE(Divided by Zero Trap),才会触发Trap

进入Trap后发现A11已经不是触发Trap的地址

因为该trap为异步trap,但是可以通过FPU_TRAP_PC寄存器看到具体的地址

Class 4 TIN5/6 通常发生在烧录pflash失败,发生概率较小; TIN7要使能时间保护功能,暂未使用故不做测试

Class 5 Assertion TIN1

造成一个有符号数溢出即可触发

运行至

才进入Trap

注意:该Trap检查的PSW.V指的是算数溢出标志,是对于有符号类型而言的

对于无符号数的运算超出了其范围,例如uint32,发生的叫做回绕

Class 6 System Call

在任意位置处放置

运行至此即可触发syscall Trap

因为MicroKernel自定义了处理函数,这里d15作为该函数的形参可以看到是我们设置的0xFF值

SYSCALL一般对应着系统的行为,这里制造0xFF后,MircoKernel找不到对应的处理流程,触发异常,终止程序

Class7 NMI

在项目中好像和SMU有关 不太了解 后续补充


EB Safety OS

名词定义

MicroKernel 微内核
Microkernel 是经过安全设计的最小可信内核,主要负责最基础、最关键的系统功能,通常具有功能安全要求,例如:发动机控制,制动和底盘控制,电池管理系统,ADAS,域控制器

ASIL Automotive Safety Integrity Level 汽车安全完整性等级
ISO 26262

QM-OS
EB tresos Safety OS is divided into two parts. The QM-OS is the part without ASIL allocation according to ISO26262.
EB tresos Safety OS 被划分为两个部分。QM-OS 是其中按照 ISO 26262 未被分配 ASIL 的部分。
可以理解为EB tresos Safety OS架构中,负责实现“非安全关键 AUTOSAR OS 功能”的传统操作系统部分

Category API

完整性分类

实现位置

典型调用方式

安全含义

Category 1

Microkernel

应用 → Microkernel

完全由安全内核处理

Category 2

Microkernel Wrapper + QM-OS

应用 → Wrapper → QM-OS Thread → QM-OS

内核控制进入QM-OS的过程

Category 3

QM-OS

应用 → QM-OS

不属于Microkernel的安全实现范围

简介

EB tresos Safety OS consists of the microkernel and the QM-OS

The microkernel is the part of EB tresos Safety OS that manages the executable objects of an AUTOSAR sys-tem.....

Safety OS Service 安全系统服务

integrity category 1 完整性类别1 由标准ASIL-D开发 非常安全 可以在任何Task,ISR中调用

integrity category 2 完整性类别2 由QM提供 基本安全 可以在任何Task,ISR中调用,但该功能无法保证达到任何安全完整性等级

integrity category 3 完整性类别3 由QM提供 不安全 禁止在有安全等级的Task,ISR中调用

MicroKernel

Memory Protection内存保护

MK_RSA_<name>
MK_RLA_<name>
MK_BAS_<name>
MK_RDA_<name>

在OsTaskMemoryRegionRef中定义内存保护类

结束后需要在TriboardTC234.mak中添加对应的地址对应

  • MR_8toF

    • 0x8000,0000~0xFFFF,FFF8

  • MR_AllRam

    • 0x5000,0000~0x7FFF,FFF8

  • MR_OvrRam

    • App3结尾~0x700A,2000

  • MR_Read

    • 0x0000,0000~0xFFFF,FFF8

  • App1

    • App1范围 ASIL-D

  • App2

    • App2范围 ASIL-B

  • App3

    • App3范围QM

在启用内存保护后 我尝试在QM.c文件中实现一个栈溢出注入函数,执行放到app3_10ms任务中,通过ude修改全局变量调用开启,

但是发现进入trap,原因是写权限保护;

因为在QM.c文件中实现的栈注入函数需要写入app3的栈,但这个函数没有权限(如果需要授权,必须在eb中修改声明这个栈注入函数为trust Function, 并利用CallTrustedFunction 这个api调用)

所以就把这个注入的实现放到了app3文件中,这样就不用很麻烦了

MkIdleFunction自定义配置

空闲函数,即当CPU空闲的时候运行在这个函数内;这个函数必须是无限循环的;

如果要自定义这个函数需要在EB OsMicroKernel中修改对应的函数名称

这里我配置成了自定义函数名称Mk_Idle_Custom

Mk_gen_config.h中修改

Mk_gen_user.h中添加

同时请不要忘记如果要在Idle函数中调用外设,例如定时器等,请将Idle函数外设权限设置为User1/Supervisor

MK_Idle_Custom(void)的实现,必须包含循环!!!!!

void MK_Idle_Custom(void)
{
    for (;;)
    {
       /* endless loop */
       /* add what you want */
    }
}

不可用API

  • Pre/PostTaskHook

  • Pre/PostISRHook

Micro Kernel下错误代码请参考Mk_error.h

EB配置

OsOS

  • OsScalabilityClass

操作系统扩展类别SC1-4,目前项目用的是SC3

  • OsStackMonitoring

使能栈监控

  • OsStatus

决定内核怎么处理错误

STANDARD 对于静态错误 隔离有问题的任务或应用

EXTENDED 系统服务应该返回特定的错误代码

  • OsUserGetServiceID

貌似和用户自定义服务程序有关(需要自己编译源码)

  • OsUseParameterAccess

自定义参数

  • OsUseResScheduler

决定是否生成特殊资源 RES_SCHEDULER

用于task禁止任务抢占、保护调度器临界区的资源

  • OsCC

comformance class (BCC1 BCC2 ECC1 ECC2)

对于基本任务和扩展任务,其中2类支持多次激活,1类不支持

  • OsTrace

使能traceHook, Debug&Trace 模块,可以查看任务、ISR、调度器和 OS 服务按什么时序运行

  • OsExtra_Runtime_Checks

启用内核运行过程中的额外一致性和内部状态检查

  • OsStartupChecks

启动时执行一系列额外的检查

  • OsServiceTrace

控制是否通过 ORTI(OSEK Run-Time Interface) 跟踪 OS 服务调用

使用调试器才可以看到

  • OsSourceOptimization

使能重新编译并优化 OS 源码

  • OsStackOptimization

控制是否在多个任务之间复用任务栈

NO 每个任务拥有独立的栈空间 可以清楚地观察每个任务调用栈大小 是否有栈溢出风险

WITHIN_APPLICATIONS 允许同一个Application内的任务共享栈空间 在不同时间复用同一块内存

GLOBAL 允许来自不同 Application 的任务共享栈空间

共享可以节省栈空间但是可能存在兼容性问题

  • OsProtection

在支持内存保护的微控制器上启用完整、部分或关闭内存保护功能

如果开发阶段遇到断点问题,可以先配置为部分关闭,若还不行,则全部关闭

在运行时最好开启全部内存保护功能(如果运行不信任的应用程序)

  • OsUseLastError

使能后可以通过ORTI查看最后一个错误

  • OsTracebuffer

用于定义 OS Trace Buffer 的大小,即系统最多可以保存多少条 Trace 记录

  • OsSchedule

NON 非抢占调度方式

FULL 全抢占调度方式

MIXED 两种类型都存在

  • OsTimestampTimer

用于配置时间戳的定时器用于

CPU Load 测量;任务或 ISR 执行时间统计;任务到达率(Arrival Rate)监控等

可复用已经存在的定时器,时间戳功能不一定需要单独占用一个硬件定时器,

如果CPU提供专用的Timer,该项将不可配置

  • OsTrappingKernel

使能后通过Systrap机制进入系统内核(例如开启任务,修改寄存器等),可以进行内存保护

一般用在非信任任务中,防止出现非法访问、越界访问等行为

关闭后直接通过普通函数调用进入内核,调用时间快,资源少

  • OsGenerateSWCD

使能后生成描述 OS API 的 SWCD 文件,以便软件组件通过 RTE 访问部分 OS 服务

只有SWC通过RTE调用OS API时才有用

OsCounter

OsCounterMaxAllowedValue

表示 OS 系统计数器允许达到的最大值,单位通常是 Tick

如果MaxALlowedValue为5,则

0->1->2->3->4->5->0->....

OsCounterMinCycle

表示由该Counter驱动的周期性 Alarm 所允许配置的最小周期,单位为 Counter Tick

如果CounterMinCycle为5,则

只允许Alarm的cycle不得小于5(间隔不得小于5tick)

即最短5个周期触发一次循环

Counter Tick:
0         5        10        15        20
|---------|---------|---------|---------|
        Alarm     Alarm     Alarm

OsCounterTicksPerBase

表示多少个 Counter Tick 对应一个应用层定义的计数单位(Base)

base用于应用层计数,其作为一个时间单位,但其具体时间长度定义看具体情况

OsSecondsPerTick

表示一个硬件 Tick 所代表的时间,单位是秒(s)

栈空间

在MicroKernel下,不同Task/ISR的栈空间由MK_threadStack<x>_slot<x>数组表示

编译后对应的地址可以在map文件中找到

在EB OS配置中ISR_GTM的栈空间配置为1024字节,对应上图的MK_threadStack0_slot10数组,该数组是32位无符号数,共256个元素;

分配到.bss段中,0x70005880~0x70005C80 可以注意到MK_threadStack0_slot10数组中前后两个元素是不被用作栈空间的,

这里猜测是因为系统用于作为内存保护判断的界限。

栈优化

OsStackOptimization

No: 每个任务都有自己独立的栈空间,当需要监控每个任务的栈使用量时必须设置

With_Application: 相同的App下共享栈空间

Global: 不同App之间共享栈空间,最高效的RAM使用方式,但是可能会误触发内存保护

在项目中由于栈优化配置为Global,导致在审查stack占用的时候,不同App中运行的程序占用的栈出现了乱飞现象(例如:App3中的函数实际跑到了GTM中断中)

当一个Task为Extended扩展任务时,他的栈必须是私有的Private;因为扩展任务等待时必须保持栈的完整。

OS_UserGetStackInfo

os_result_t OS_UserGetStackInfo(
    os_taskorisr_t      id,
    os_stackinfo_t     *out
);

其中,id可以为:

  • OS_TaskToTOI(OS_NULLTASK) 用于查询当前Task

若目标 Task 正在运行(running),不能通过任务上下文保存区得到实时 SP

因为实时SP在CPU寄存器中,并且API调用本身会改变栈

所以函数不会修改 out->stackPointer ,保持原样

调用者可以在调用函数前自行读取并保存当前Stack Pointer

OS_GetTaskSp为系统调用,可以看出当任务不在running状态,

但是可以是ready状态时,能调用当前栈指针

  • OS_TaskToTOI(task_id) 用于查询指定Task

  • OS_IsrToTOI(isr_id) 用于查询指定ISR

  • OS_IsrToTOI(OS_NULLISR) 用于查询当前ISR

如果当前不在ISR中,返回 全局 kernel stack 信息

注意:

一般情况下,多个 ISR 会共用全局kernel stack

对于配置为private stack的ISR环境,

通过指定OS_IsrToTOI(isr_id) 才能获取真正的private stack大小

  • OS_TOI_CURRENTCONTEXT 用于查询当前位置上下文信息

这种情况下,SP在手册中写的是始终为NULL,但通过查看系统源码发现并非NULL

现在还不明确,OS_TOI_CURRENTCONTEXT 与 查询当前Task/ISR 有何不同

但可以明确的是,不能通过这个模式获得调用者当时的 SP 寄存器值。

其中,返回变量类型os_stackinfo_t为:

stackBase     栈基地址指针
stackPointer  当前栈指针位置
stackLen      栈总长度
stackClean    栈空间剩余大小
stackStatus   栈状态标识(整型)
isrStackBase  ISR栈基地址(只对ISR查询有意义)
isrStackLen   ISR栈长度(只对ISR查询有意义)

函数返回值为:

  • OS_E_OK

  • OS_E_NOFUNC

传入 OS_TaskToTOI(OS_NULLTASK),但当前不存在运行中的 Task

使用条件:

  • Task

  • Category 2 ISR

OS_StackCheck

os_int_t OS_StackCheck(void)

用于当前调用位置处的栈检查

返回1(栈溢出),0(正常),-1(栈下溢)

内核源码:

os_int_t OS_StackCheck(void)
{
    os_int_t answer = 0;
    os_stackinfo_t info;
    
    // 尽量接近调用 OS_StackCheck() 入口时的真实栈位置,为了后续检查栈情况使用
    info.stackPointer = (os_stackinfoptr_t)OS_GetCurrentSp();

    if ( OS_UserGetStackInfo(OS_TOI_CURRENTCONTEXT, &info) == OS_E_OK )
    {
        answer = info.stackStatus;
    }

    return answer;
}

但是函数调用会占据栈空间,导致API调用后SP已经发生变化,所以在API前对SP进行采样通常更接近真正希望检查的点

对于在ISR中调用:

注释这里的意思为:

ISR 中预先记录的 SP,不能作为最终由 OS 返回的 ISR 栈 SP;

OS 查询过程需要在 ISR 栈上继续执行,并且 OS 会根据实际 ISR的

kernel stack 模型重新采样和填充 out->stackPointer。

OS_GetUnusedIsrStack/UsedIsrStack

os_size_t OS_GetUnusedIsrStack(void)
os_size_t OS_GetUsedIsrStack(void)

获取当前ISR中剩余/已使用栈大小

注意:只能在ISR中调用

返回栈字节数

OS_GetUnusedTaskStack/GetUsedTaskStack

os_size_t OS_GetUnusedTaskStack( os_taskid_t t /* TaskID */ )
os_size_t OS_GetUsedTaskStack( os_taskid_t t /* TaskID */ )

获取某Task中剩余/已使用栈大小

可以在任意时刻调用,指定task即可

返回栈字节数

OS_GetCurrentStackArea

void OS_GetCurrentStackArea(void **begin, void **end)

获取当前栈起始与结束地址

可以在task和CAT2 ISR中调用

返回栈头和尾指针的指针

标准配置宏

STD_ON 标准ON

STD_OFF 标准OFF

E_NOT_OK 状态不好

E_OK 状态好

STD_HIGH 标准高

STD_LOW 标准低

STD_ACTIVE 活动状态

STD_IDLE 空闲状态

错误标志

E_OK 成功

E_OS_ID 所调用的task/alarm/...不存在

E_OS_RESOURCE 当前task占据资源

E_OS_LIMIT 所调用的task已经到达激活上限

E_OS_ACCESS 当前task不属于extened task

参考链接

  1. AUTOSAR OS模块详解(四) Task&Event - TechLink汽车软件的文章 - 知乎

山和山不相遇,人与人要相逢