AUTOSAR OS模块详解(五) 中断ISR

AUTOSAR OS模块详解(五) 中断ISR

本文主要介绍AUTOSAR OS的中断ISR,并对基于英飞凌Aurix TC3XX系列芯片的Vector Microsar代码和配置进行部分讲解。

1 简介

对于嵌入式系统来说,实时性是一个非常重要的特性,相比较于桌面端系统,嵌入式系统往往部署在各种硬件设备上,这些硬件设备的控制往往都要求毫秒甚至微秒级的反馈。而在车规级控制器中,有些硬件功能的实时性要求就更为严苛。

关于Aurix TC3XX系列芯片的中断系统,我们已经专门出过相关文章进行介绍,本文就从纯软件的角度,解析Autosar Os中的中断定义及处理逻辑。

2 功能介绍

2.1 中断分类

在Autosar Os中,中断分为两类,就叫做一类中断二类中断。其中一类中断的执行不经过Os的调度管理,中断发生后直接到达中断业务逻辑,对于Os来说是无感的,而二类中断则是需要Os进行调度管理的,有专门的程序控制块。除此之外,二者还具有以下区别:

  • 一类中断的优先级恒高于二类优先级(配置强限制);

  • 临界区层级不同,例如SuspendOsInterrupt只关闭Os中断而不关闭一类中断,SuspendAllInterrupt则关闭所有中断;

  • 因为一类中断不被Os管理,因此一类中断的处理过程中不需要切换线程和保存上下文;

    一般情况下,对于外设等的中断,如DMA、SPI、PWM等与OS并非直接关联的,建议设置为一类中断,省去了Os调度的消耗;而对于CAN、Eth等与协议栈MainFunction等关联性较强的中断,甚至需要Os资源提供数据保护的,建议使用二类中断。

2.2 中断源

在Vector DaVinci工具中配置中断,要区别于在EB中配置中断,在EB中每个模块都详细列出来,而在DaVinci工具中,还需要配置一个中断源,顾名思义,该中断源用于指定该中断连接到哪个硬件中断信号上。中断源填写的是所使用中断的SRC寄存器偏移地址,具体需要查询芯片用户手册,下面我们以CAN中断为例介绍如何查询。

我们这里配置使用CAN0INT0,关于CAN中断及其连接,读者可以翻阅本公众号发布的CAN模块的介绍,此处不进行赘述。

我们来到所使用芯片的UserMannual中,注意是系列手册,不是family手册,这里我的芯片是TC387,使用的TC38x-UserManual。打开16.4 IR章节中的TC38x Specific Service Request Control (SRC) registers

这里我们可以看到两个表,一个是Table 262 Register Address Space - SRC,里面说明了SRC寄存器地址范围,另一个是Table 263 Register Overview - SRC (ascending Offset Address),里面说明了所有该芯片各个中断的SRC寄存器偏移地址,也就是我们需要查阅的目标。

在这里插入图片描述

我们在第二个表中搜索CAN可以找到如下条目:

在这里插入图片描述

我们需要查找的是CAN0INT0,这里将x、y代入得出偏移地址为0x5B0,即1456,后续我们在中断源配置中需要填入该值。

另外值得一提的是Os Counter的中断源是不需要我们填的,我们在Counter配置中选定了硬件定时器后工具会自动进行配置。

2.3 中断配置

OS配置这里我们选择Os Counter作为示例进行演示,虽然该中断有些配置项是OS自动配置的,但是配置项和其他我们添加的配置也是一样的。

在这里插入图片描述

OsIsrShortName

中断的ShortName和其他元素的不太一样,我们自行添加的中断没有Special Function Name那项配置的话,Os会使用ISR这个宏对ShortName进行展开,进行中断入口函数的声明,如ISR(CanIsr_0)展开成Os_Isr_CanIsr_0。

OsIsrCategory

中断类型,一类还是二类。一类的话无法由OS控制,所以下面的中断嵌套和中断使能都无法勾选。另外如果我们要配置Trap向量表(Vector OS支持用户配置部分Trap,这个我们以后在Os异常处理中介绍),这里不能选为二类。

OsIsrEnableNesting

是否可以嵌套,如果可以嵌套,则该中断可以被高优先级中断打断。

OsIsrInitialEnableInterruptSource

是否由OS使能该中断,一般默认选择开启,OS会在初始化后使能该中断,无需用户手动操作。一类中断此处无法配置,所以一类的初始化和使能需要用户在初始化中实现。

OsIsrInterruptMapping

中断映射,我们知道TC3XX系列芯片可以将中断路由到DMA中,这里可以选择DMA进行路由。

OsIsrInterruptPriority

中断优先级,与硬件相关,这里即TC3XX的中断优先级,数字越大优先级越高,中断入口代码在向量表中按优先级排布。

OsIsrInterruptSource

中断源,即前文提到的硬件中断关联,Os Counter的中断源是根据用户选的定时器自动计算的。这里为了演示配置方法,我们查询手册,我选的定时器是STM0(默认Compare0中断),其中断源在手册中为0x300,即上述配置中的768。

在这里插入图片描述

OsIsrInterruptType

中断类型,这里配置为EXTERNAL即可,如果是配置Trap的话,则要配置为EXCEPTION。

OsIsrMemoryProtectionIdentifier

内存保护的识别号,Task和Isr的内存保护我们一般不单独配置,因为芯片没有那么多的Set足够每个Task或者Isr单独配置,一般是继承Application的识别号。

OsIsrSpecialFunctionName

指定的中断函数,因为Counter的中断函数是Vector内置的,所以这里强行配置了Os_TimerPfrtIsr,这个函数我们在Counter章节中也介绍过。

OsIsrStackSize

二类中断栈大小,一般情况下1024就够用了,甚至偏多,除非是用户手写代码。一类中断不由OS管理,这里配置了也没用。需要注意的是一类中断在进入时直接使用当前线程的栈,如果使用较多则需要注意。

OsIsrUsesFpu

是否使用浮点运算单元,在ARM等芯片中,FPU这类协处理器是具有独立的寄存器的,因此Task/Isr切换的过程中,是需要保存FPU上下文的。TriCore内核芯片FPU没有单独的上下文,这里不使能即可。

Cross-core channel receiver interrupt

多核通道接收中断,Vector中利用TC3XX的软中断,实现X-Signal跨核通信,对应的中断这里需要配置使能。

Timing Protection Enabled

是否使用时间保护,在SC2和SC4中,该配置可以用于中断时间监控。

2.4 中断向量表

在TC3XX中断文章中,我们介绍过,该系列芯片的中断向量表是一段指定的连续Flash地址,按照优先级进行中断入口函数的排布。

在这里插入图片描述

在Vector代码中,该向量表由OS维护,统一进行中断入口的管理,包括一类中断。

在Os_Hal_Entry_Lcfg.c中我们可以看到该向量表,是以Memmap包裹的特殊宏定义。

Memmap的功能是将该段代码以OS_INTVEC_CORE0_CODE的section装载到指定内存中,并且中断向量表基址寄存器的初始化也会使用该代码段的地址标签。

每个中断优先级都以32字节对齐分配了一定的空间,用来存放中断入口,进行中断函数跳转。未配置的中断则使用Os_Hal_UnhandledInterruptEntry展开,用于异常触发时的处理。

#define OS_START_SEC_INTVEC_CORE0_CODE
#include "Os_MemMap_OsSections.h"

Os_Hal_InterruptSectionDeclaration(0)
Os_Hal_UnhandledInterruptEntry(0, 0)
...
Os_Hal_Cat2InterruptEntry(0, 80, OS_CFG_HAL_ISR2_DISABLE_LEVEL, OsCfg_Isr_CounterIsr_SystemTimer)
...
Os_Hal_Cat2InterruptEntry(0, 85, OS_CFG_HAL_ISR2_DISABLE_LEVEL, OsCfg_Isr_CanIsr_0)
...

#define OS_STOP_SEC_INTVEC_CORE0_CODE
#include "Os_MemMap_OsSections.h"

在使用Tasking编译器时,一类中断宏按照如下形式展开,我们可以看到其仅仅声明了一个函数名,保存低上下文供中断使用,然后根据用户配置的中断函数进行跳转,并未做其他处理,所以我们说一类中断不经过OS管理

# define Os_Hal_Cat1InterruptEntry(core, level, function) \
  __asm ("  .SECT   \".text.OS_INTVEC_CORE"#core"_CODE\""); \
  __asm ("  .ALIGN 32"); \
  __asm ("  .EXTERN " #function); \
  __asm ("  .GLOBAL osIsrLevel_" #level "_Core" #core); \
  __asm ("osIsrLevel_" #level "_Core" #core": svlcx"); \
  __asm ("  call " #function);\
  __asm ("  rslcx");\
  __asm ("  rfe");\

二类中断的宏展开内容多了一些,仍然声明一个向量表函数名,然后这里extern了一个全局变量,是中断的配置结构体,作为入参传递到Os的二类中断处理函数中,比如CAN中断这里传入的就是前面的OsCfg_Isr_CanIsr_0。

所有的二类中断都要进入Os_Hal_IsrRun函数,在该函数中进行上下文切换,线程处理,然后才能进入实际中断执行代码。所以说二类中断是由OS进行管理的

# define Os_Hal_Cat2InterruptEntry(core, level, systemlevel, isrconfig) \
  __asm ("  .SECT   \".text.OS_INTVEC_CORE"#core"_CODE\""); \
  __asm ("  .ALIGN 32"); \
  __asm ("  .EXTERN " #isrconfig); \
  __asm ("  .EXTERN Os_Hal_IsrRun"); \
  __asm ("  .GLOBAL osIsrLevel_" #level "_Core" #core); \
  __asm ("osIsrLevel_" #level "_Core" #core": svlcx"); \
  __asm ("  movh.a  a4, #@his("#isrconfig")"); \
  __asm ("  lea  a4, [a4]@los("#isrconfig")"); \
  __asm ("  call Os_Hal_IsrRun"); \
  __asm ("  rslcx"); \
  __asm ("  rfe");

2.5 代码分析

2.5.1 主要数据结构

中断Isr的主要数据结构为Os_IsrConfigType,我们到Os_Isr_Lcfg.c文件中可以找到该数据,Isr对应的变量名为 OsCfg_Isr_前缀加上中断Isr的配置名称。

另外如果是Os Counter中断,会特殊一点,类型为Os_TimerIsrConfigType,其中包含一个Os_IsrConfigType和一个Os_CounterConfigType的指针。这里就以Timer中断为例。

CONST(Os_TimerIsrConfigType, OS_CONST) OsCfg_Isr_CounterIsr_SystemTimer =
{
  /* .Isr     = */
  {
    /* .Thread   = */
    {
      /* .ContextConfig         = */ &OsCfg_Hal_Context_CounterIsr_SystemTimer,
      /* .Context               = */ &OsCfg_Hal_Context_OsCore0_Isr_Level5_Dyn,
      /* .Stack                 = */ &OsCfg_Stack_OsCore0_Isr_Core,
      /* .Dyn                   = */ OS_ISR_CASTDYN_ISR_2_THREAD(OsCfg_Isr_CounterIsr_SystemTimer_Dyn),
      /* .OwnerApplication      = */ &OsCfg_App_SystemApplication_OsCore0,
      /* .Core                  = */ &OsCfg_Core_OsCore0,
      /* .IntApiState           = */ &OsCfg_Core_OsCore0_Dyn.IntApiState,
      /* .TimeProtConfig        = */ NULL_PTR,
      /* .MpAccessRightsInitial = */ &OsCfg_Mp_CounterIsr_SystemTimer,
      /* .AccessRights          = */ &OsCfg_AccessCheck_NoAccess,
      /* .Trace                 = */ NULL_PTR,
      /* .FpuContext            = */ NULL_PTR,
      /* .InitialCallContext    = */ OS_CALLCONTEXT_ISR2,
      /* .PreThreadHook         = */ NULL_PTR,
      /* .InitDuringStartUp     = */ FALSE,
      /* .UsesFpu               = */ FALSE
    },
    /* .SourceConfig              = */ &OsCfg_Isr_CounterIsr_SystemTimer_HwConfig,
    /* .IsrId                     = */ CounterIsr_SystemTimer,
    /* .IsEnabledOnInitialization = */ FALSE
  }
  ,
  /* .Counter = */ OS_COUNTER_CASTCONFIG_TIMERPFRT_2_COUNTER(OsCfg_Counter_SystemTimer)
};

该结构体是该Isr的句柄,相当于线程控制块。其中ContextConfig指向的结构体为OsCfg_Hal_Context_CounterIsr_SystemTimer,包含了栈信息等,其中Entry是该Isr的入口函数,ReturnAddress是中断调用栈的栈底Os_TrapIsrEpilogue,中断执行完毕之后在这里切回原有线程。

CONST(Os_Hal_ContextConfigType, OS_CONST) OsCfg_Hal_Context_CounterIsr_SystemTimer =
{
  /* .StackEndAddr     = */ (uint32)(OS_STACK_GETHIGHADDRESS(OsCfg_Stack_OsCore0_Isr_Core_Dyn)+1),
  /* .StackStartAddr   = */ (uint32)OS_STACK_GETLOWADDRESS(OsCfg_Stack_OsCore0_Isr_Core_Dyn),
  /* .ProgramStatus    = */ (uint32)OS_HAL_PSW_IS_MASK | OS_HAL_PSW_CDE_MASK | OS_HAL_PSW_IO_SUPERVISOR | OS_HAL_PSW_S_MASK | OS_HAL_PSW_PRS_PS0,
  /* .Entry            = */ (uint32)&Os_Isr_Os_TimerPfrtIsr,
  /* .ReturnAddress    = */ (uint32)&Os_TrapIsrEpilogue,
  /* .IntStatus        = */ ((uint32)130<<OS_HAL_PCXI_PCPN_BIT_POSITION) | OS_HAL_PCXI_PIE_ENABLED
};

Context所指向的OsCfg_Hal_Context_OsCore0_Isr_Level5_Dyn为该Isr的上下文信息,包括返回地址、CSA等信息,如下图所示。

在这里插入图片描述

Dyn所指向结构体指示了该Isr的各种动态状态量,包括运行状态,运行时优先级等。在实际调试过程中可用于观察该中断状态。

2.5.2 中断Isr初始化阶段

关于Os的初始化,在本系列Counter章节中已经介绍过,这里就不赘述了,与Counter、Task等相同,Isr的初始化也是在Os_AppInit中进行的,其调用栈如下图所示。

在这里插入图片描述

中断的初始化相对于Task要简单,只有一个线程的初始化。

2.5.3 中断使能

中断在完成初始化之后,仅仅是配置了中断状态相关的寄存器此时中断还不能够运行,还需要使能中断。

一类中断的使能是需要用户手动操作的,推荐在EcuM_AL_SetProgrammableInterrupts函数中执行。

对于Os Counter的中断,之前的文章已经介绍过了,会在Timer的初始化中启动Os Tick所使用的中断。

而对于普通的二类中断,则是在Default_Init_Task_Trusted任务中进行使能的(前提是勾选了OsIsrInitialEnableInterruptSource),该任务是Os的内置任务,用户不可配置其中的Rbl,优先级也是建议配置为最高,启动Os之后优先执行。

在该任务中调用Os_InitialEnableInterruptSources函数,随后一路调用到Os_Hal_IntEnableSource函数中,在其中写入中断的SRC寄存器中SRE位域,使能该中断。

2.5.4 中断执行

一类中断的执行不经过Os进行处理,因此一类中断在经过中断向量表之后,直接跳转到其中断执行函数,此处不进行赘述。

对于二类中断,这里以Counter中断CounterIsr_SystemTimer为例,我们将断点打到中断向量表中。

在这里插入图片描述

可以看到该段汇编首先保存了低上下文,以供中断函数使用,然后将0x800255f4作为指针入参传递该Os_Hal_IsrRun函数中,我们在Map里搜索该地址发现它就是前文提到的OsCfg_Isr_CounterIsr_SystemTimer,也就是该中断的句柄。

在这里插入图片描述

然后我们从Os_Hal_IsrRun将其调用时序图画出。

在这里插入图片描述

在进入Os_Hal_IsrRun之后,会直接调用Os_IsrRun,然后进入Os的中断处理函数。所有的二类中断都会经过这个函数,在这里进行线程的切换以及上下文的处理。

首先会通过Os_IntSuspend关闭中断(虽然硬件在进入中断时已经关闭,但对于Os需要保证自身完整性),如果使能了0类中断,接下来会重新打开,以保证0类中断能够进入。

然后使用Os_CoreInterruptedThreadsPush记录中断嵌套次数。

接下来就是通过Os_ThreadSuspendAndStart进行线程的上下文处理了。

{
  Os_StackOverflowCheck();

  /* #15 Store FPU context if needed */
  Os_ThreadStoreFpuContext(Current);                                                                    

  /* #20 Resume memory protection access rights of Next. */
  Os_MpSwitch(Current->Dyn->MpAccessRights, Next->Dyn->MpAccessRights);                                 

  /* #25 Switch current thread pointer. */
  Os_CoreSetThread(Next);                                                                               

  /* #30 Call PreThreadHook. */
  Os_ThreadCallPreThreadHook(Next, CallPreThreadHook);                                                  

  /* #40 Set the current stack pointer. */
  Os_CoreSetCurrentStack(Next->Stack, Next->Core);                                                      

  /* #50 Prepare context of next thread. */
  Os_Hal_ContextInit(Next->ContextConfig, Next->Context);                                               

  /* #60 Setup FPU for next thread. */
  Os_ThreadInitFpu(Next);                                                                               

  /* #70 Set parameter for the entry function, currently only used for hooks (hook thread pointer). */
  Os_Hal_ContextSetParameter(Next->Context, Next);                                                      

  /* #80 Update the Msr of the next thread. */
  Os_Hal_ContextSetUserMsrBits(Next->Context);                                                          

  /* #90 Perform context switch. */
  Os_Hal_ContextCallOnStack(Current->Context, Next->Context);                                           

  /* #100 Restore FPU context if needed */
  Os_ThreadRestoreFpuContext(Current);                                                                  
}                                                                                                       

在这里首先进行当前线程的栈检查,然后保存FPU上下文(如果需要),切换内存保护集,设置线程全局指针等上下文操作,然后在Os_Hal_ContextCallOnStack中进行上下文切换,切换到目标中断线程中。

在Os_Hal_ContextCallOnStack中首先将当前线程的上下文信息进行保存,如PCXI、返回地址等,在Os_Hal_ContextIntRestore中加载中断线程的内容然后完成切换动作,然后我们就来到了实际要处理的中断函数Os_TimerPfrtIsr中。

在这里插入图片描述

到这里Os真正开始处理对应的二类中断函数,也就是用户定义的业务代码。

当中断函数执行完毕之后,返回线程入口地址Os_TrapIsrEpilogue,然后调用Os_IsrEpilogue函数,在其中返回被打断的Task,或者返回被打断的其他中断,这样就切出了该中断线程,至此,该中断处理完成。

3 小结

本文介绍了Autosar Os中的中断Isr定义及其实现,就其类型及工具配置展开了介绍。通过对Vector Os实现机制进行了代码解析,详细介绍了中断Isr的处理过程。

如您有任何问题,欢迎关注公众号【TechLink汽车软件】与我们联系!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值