理论
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后,发现其完全不起作用,所以这里对其作用产生疑问。
EB配置


经过测试在OsAlarmAutostart选项卡下,
OsTimeUnit可以配置为Nanoseconds和Tick,不使能默认为Tick;
Nanoseconds如何转换为Tick过程如下:




对于TC23x芯片,STM0_T0计数频率为100MHz,则可见1Tick对应10ns
我猜测,因为这个芯片STM0频率固定,在OS配置中只能配置一个HW_COUNTER对应STM0,所以更改有关修改计数频率的参数都没有作用,例如OsSecondsPerTick,OsCounterTicksPerBase;但是MINCYCLE有作用,对应当OsTimeUnit配置为Tick时,OsAlarmCycleTime不得小于MINCYCLE
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: 首次触发 触发 触发 触发 触发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之前,先介绍几个相关寄存器
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
常涉及到的调试寄存器
Class 0
用于 Memory Management Unit(MMU,内存管理单元)相关异常,处理的是基于 虚拟地址、页表和 TLB 的地址转换及访问权限问题
对于未实现 MMU、未启用 MMU 的 TriCore/AURIX 应用,Class 0 通常不会成为常见问题。
在典型AUTOSAR OS 项目中,内存隔离通常更常依赖TriCore Range-Based Memory Protection(范围型内存保护保护)
而不是类似通用操作系统中的完整虚拟内存和页表管理
对应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
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 指向的位置取指并执行,但该地址区域没有 Execute 权限
“地址区域没有Excute权限”指,PC指向的地址不可执行
典型原因有:函数指针、回调函数、Vtable向量表被破坏;栈溢出覆盖了函数返回地址;栈/局部数组越界;调用了已经失效的回调函数地址
检查重点:Task Stack用量;函数指针;回调表
调试重点:A11;PSW.IO;DEADD目的地址是否为外设SFR;查看调用链中是否绕过了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
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下常见原因有:
编译器 CPU Target 配置错误;
工程使用了不匹配芯片型号的库;
Bootloader 和 Application 使用了不同的编译目标;
手写汇编使用了当前芯片未实现的指令;
FPU 选项与实际芯片能力不匹配。
3 OPD
指令本身合法,但该指令中的操作数编码不满足要求
正常C代码中 OPD 较少见,出现时优先怀疑是否进行了手写汇编代码。
4 ALN
CPU 执行数据读写操作时,访问地址不满足该访问宽度所要求的对齐规则
对于地址0x70000001 进行了(uint32 *)0x70000001U 访问,但是该地址不是4字节对齐地址,于是可能触发ALN
常见于 CAN PDU 解析;LIN 报文解析;以太网报文解析
5 MEM
当前内存访问地址、地址计算方式或特殊地址访问方式,违反了 TriCore 架构规则或具体芯片实现限制
访问这个地址的方式就不符合 CPU 规则
比较少见,不做解释
Class 3
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
3 CDU
4 FCU
5 CSU
6 CTYP
7 NEST

进Trap实操
这里注入代码为:
volatile uint32_t v;
v = *(volatile const uint32_t *)(uintptr_t)0x00000000u;
(void)v;可以预料到这是对无效地址NULL进行访问,势必导致Class1 TIN6 MPN(Memory Protection Null Address)空地址访问保护违规
提前在ProtectTrap处打断点,运行程序后果然运行至此


在这里,查看Core Register

其中,A11对应进入Trap时的地址;D15对应TIN类型
右键选择A11对应的地址可以进行跳转

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

即,可以看出确实是我们注入的代码处发生了trap;
uint32 test_value = 0u;
__asm volatile (
"move.aa %%a0, %0"
:"=a"(test_value)
); //把test_value的值赋值给a0寄存器进入Trap后,可以看到D15为7,对应TIN为7 GRWP(Global Register Write Protection)全局地址寄存器写保护违规

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

可见,这里错误操作了A0寄存器
MTCR(CPU_A5,0U); //放置在App2中,因为App2是none-trust application

待完善。。。
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
简介
EB tresos Safety OS consists of the microkernel and the QM-OS
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 AlarmOsCounterTicksPerBase
表示多少个 Counter Tick 对应一个应用层定义的计数单位(Base)
base用于应用层计数,其作为一个时间单位,但其具体时间长度定义看具体情况
OsSecondsPerTick
表示一个硬件 Tick 所代表的时间,单位是秒(s)
栈相关
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