1 简介
在操作系统中,“Tick” 是一个重要的概念,通常指的是系统时钟的一个周期性中断。Tick 是操作系统内核中用于时间管理的基本单位。它代表了系统时钟每次中断的时间间隔,通常以毫秒为单位。每当系统时钟产生一个 tick 中断时,操作系统会执行一些特定的任务,例如更新系统时间、调度任务等。
Tick 在操作系统中有几个主要作用:
时间管理:Tick 用于跟踪系统的运行时间,操作系统可以通过 tick 来维护系统时间和日期。
任务调度:操作系统使用 tick 来决定何时切换任务。每当发生 tick 中断时,调度器会检查当前运行的任务是否需要被挂起,并选择下一个要运行的任务。周期任务就是由此实现的。
定时器功能:Tick 还用于实现定时器功能,允许程序在特定的时间间隔后执行某些操作。
在大多数操作系统中,tick 是通过硬件定时器实现的。硬件定时器会在预定的时间间隔内产生中断信号,操作系统内核会响应这些中断并执行相应的处理程序。
Tick 的频率(即每秒产生的 tick 数量)对系统性能有重要影响。常见的 tick 频率有100 Hz,适用于大多数实时操作系统;1000 Hz,适用于需要更高时间分辨率的系统。
Tick提供了精确的时间管理和任务调度,允许操作系统在多任务环境中有效地管理资源。过高的 tick 频率可能导致系统开销增加,因为每个 tick 都需要进行上下文切换和中断处理。过低的 tick 频率可能导致任务调度不够及时,影响系统的实时性。
Autosar OS中,Counter就是OS的Tick来源,一般由硬件Timer进行驱动,继而由Counter驱动Alarm或者调度表。如下图是多核系统中OS运行关系示意图。
从图中我们可以看到,由硬件Timer通过中断驱动Counter,Counter中根据周期触发Alarm或者ScheduleTable。
2 功能介绍
2.1 Timer
Counter的触发依赖于硬件定时器(Timer),由Timer产生中断来执行对应的Counter程序,如果把操作系统比作一辆汽车,那Timer就是燃油,Counter就是发动机。
Timer有三种类型:
Periodical Free Running Timer(PFRT):周期自由运行定时器,该定时器为自由运行的的硬件定时器,每次产生中断后需要软件重新装在定时值,具有一定的累计误差,如Aurix TC3XX中的STM;
Periodical Interrupt Timer(PIT):周期中断定时器,该类定时器由硬件以固定周期触发,硬件定时器自动重载;
High Resolution Timer (HRT):高分辨率定时器,该定时器的中断触发频率一般远高于Os Tick所需值,在中断中判断该Counter是只进行累加,还是执行相关的逻辑,得益于其高频率的中断实现高分辨率Tick。
在Vector Microsar中,当配置硬件定时器为STM时,则默认使用PFRT模式,我们在STM原理那章中介绍过,STM是一个启动不停表的定时器,中断是通过额外的比较寄存器实现的,需要软件每次计算重载值。因此在每次中断中进行比较寄存器重载的时候,会出现累计误差。感兴趣的朋友可以去翻阅那篇STM详解。
当配置硬件定时器为GPT时,则默认使用PIT模式,因为Aurix TC3XX芯片中的GPT是硬件自动重载的定时器,使得OS Tick的中断在时间轴上保证绝对均匀。另外GTM定时器也可以作为OS时钟使用,但是Vector没有在OS中实现这一特性,可能考虑到GTM广泛用于电机控制等外设。PFRT和PIT的Tick时间轴如下图所示。
HRT则是通过高频的中断,实现一些高精度需求的Tick值,而在实际使用过程中,我们Task周期的最大公约数一般为1ms,部分系统为0.5ms,因此PIT或者PFRT就满足我们的要求,而高频中断对于系统的负载有所增加。
关于Aurix TC3XX GPT硬件,我们在Aurix TC3XX系列中进行专题介绍。
2.2 Counter配置
我们先来看Vector OS界面中的Counter配置,我们打开OS Configuration界面,展开Counters即可看到Counter的配置,这里使用TC387 4核CPU,每个核都分配了一个硬件Counter。
OsCounterMaxAllowedValue
该参数定义了Counter累加的最大值,当OS Tick达到该值后会归零,该参数没有什么倍频限制,OS Tick的数据类型是uint32,不超过该上限即可。
OsCounterMinCycle
该参数定义了Counter的分辨率,一般设置为1,每进一次Timer中断处理一次Counter的业务。
OsCounterTicksPerBase
Counter对应的硬件时钟的tick数,与下面的OsSecondsPerTick相对应。比如当前GPT的硬件时钟频率为12.5MHz,OsSecondsPerTick为1ms,则实现1ms的OS Tick需要的硬件时钟tick为12500。
OsCounterType
Counter的类型有硬件和软件,如果不想给每个核配独立的硬件定时器,则没有配置定时器的核可以使用软件Counter。
OsSecondsPerTick
该参数与OsCounterMinCycle共同组成Counter的分辨率,计算关系为: OsCounterMinCycle*OsSecondsPerTick的值==所有需要该Counter触发的Alarm的周期的最大公约数,一般系统的最小Tick为1ms;如果有2.5ms等半秒的需求,则Tick需要配置为0.5ms,硬件时钟频率也对应调整。
OsDriverHardwareTimerChannelRef
该Counter对应的硬件Timer类型,支持Aurix TC3XX的GPT和STM。
OsDriverHighResolution
是否设置为HRT时钟,GPT模式下不支持的。
OsDriverIsrRef
该Counter对应的驱动中断引用,硬件Timer会触发该中断,然后该中断执行本Counter的业务逻辑。如果该Timer在OS中使用,注意在MCAL中不要再使用该资源。关于中断的配置,我们在中断的章节会详细描述。
TimeConstants
定义Tick的常量,用户可以借OS Counter用来计数使用。
Counter Accessing Application
访问权限设置,表征哪些OS-Application可以访问。
关于OS Counter的配置就这么多,配置项还是相对比较简单,且容易理解的。OS Counter的参数设计,符合实际的系统调度需求即可。
另外对于Counter的实现,需要OS工程师了解所使用芯片的硬件Timer原理,以及其背后的时钟分频设置。关于TC3XX的GPT和STM我们在芯片系列文章中会进行介绍,这里就不加以赘述了。
2.3 代码分析
此处以GPT为驱动的PIT为例,进行代码解析。
在Vector Microsar OS中,Timer、Counter的相关文件主要是Os_Counter.c、Os_Timer.c、Os_TimerInt.h,以及与驱动相关的如Os_Hal_Timer_GPT.h,然后以及Os_Counter_Lcfg.c等配置生成代码。
2.3.1 主要数据结构
Counter中的首要数据类型是Os_TimerPitConfigType(如果是PFRT,则对应的类型为Os_TimerPfrtConfigType),该类型在Os_Counter_Lcfg.c中定义,包括Counter中的相关配置值,和其他动态数据包括Queue、状态等指针。
/*! Counter configuration data: SystemTimer */
CONST(Os_TimerPitConfigType, OS_CONST) OsCfg_Counter_SystemTimer =
{
/* .SwCounter = */
{
/* .Counter = */
{
/* .Characteristics = */
{
/* .MaxAllowedValue = */ OSMAXALLOWEDVALUE_SystemTimer,
/* .MaxCountingValue = */ OS_TIMERPIT_GETMAXCOUNTINGVALUE(OSMAXALLOWEDVALUE_SystemTimer),
/* .MaxDifferentialValue = */ OS_TIMERPIT_GETMAXDIFFERENTIALVALUE(OSMAXALLOWEDVALUE_SystemTimer),
/* .MinCycle = */ OSMINCYCLE_SystemTimer,
/* .TicksPerBase = */ OSTICKSPERBASE_SystemTimer
},
/* .JobQueue = */
{
/* .Queue = */ OsCfg_Counter_SystemTimer_JobQueueNodes_Dyn,
/* .Dyn = */ &OsCfg_Counter_SystemTimer_JobQueue_Dyn,
/* .QueueSize = */ (Os_PriorityQueueNodeIdxType)OS_CFG_NUM_COUNTER_SYSTEMTIMER_JOBS
},
/* .DriverType = */ OS_TIMERTYPE_PERIODIC_TICK,
/* .Core = */ &OsCfg_Core_OsCore0,
/* .OwnerApplication = */ &OsCfg_App_SystemApplication_OsCore0,
/* .AccessingApplications = */ (OS_APPID2MASK(OsApplication_NonTrusted_Core0) | OS_APPID2MASK(OsApplication_NonTrusted_Core1) | OS_APPID2MASK(OsApplication_NonTrusted_Core2) | OS_APPID2MASK(OsApplication_NonTrusted_Core3) | OS_APPID2MASK(OsApplication_Trusted_Core0) | OS_APPID2MASK(OsApplication_Trusted_Core1) | OS_APPID2MASK(OsApplication_Trusted_Core2) | OS_APPID2MASK(OsApplication_Trusted_Core3) | OS_APPID2MASK(SystemApplication_OsCore0) | OS_APPID2MASK(SystemApplication_OsCore1) | OS_APPID2MASK(SystemApplication_OsCore2) | OS_APPID2MASK(SystemApplication_OsCore3)) /* PRQA S 0410 */ /* MD_MSR_Dir1.1 */
},
/* .Dyn = */ &OsCfg_Counter_SystemTimer_Dyn
},
/* .HwConfig = */ &OsCfg_Hal_TimerPit_SystemTimer
};
配置生成命名为OsCfg_Counter_xxx,比如前面我们Counter命名为SystemTimer,则生成的数据结构为OsCfg_Counter_SystemTimer。
成员变量->SwCounter.Counter.Characteristics定义了我们前面配置中的相关参数值。
成员变量->SwCounter.Counter.JobQueue指向的OsCfg_Counter_SystemTimer_JobQueue_Dyn存储了该Counter的任务队列,我们在运行时监控可以观测到其为引用该Counter的Alarm(此处未配置ScheduleTable,因此任务队列中仅有仅Alarm),对应数据类型为Os_AlarmSetEventConfigType或Os_AlarmActivateTaskConfigType。
其中的成员变量->SwCounter.Dyn指向的OsCfg_Counter_SystemTimer_Dyn中,包含了该Counter的相关动态值如Counter Value等。
2.3.2 Os初始化
作为第一篇Os内部模块分解,这里先简要介绍下Os的启动调用流程,首先在main中会调用Os_Init进行Os相关数据的初始化中。在Os_Api_Init中执行如下内容等:
执行中断的初始化(暂不使能),包括Os_SystemInterruptHandlingInit、Os_CoreInterruptHandlingInit()等;
执行所有栈的初始化(Vector Os每个中断和任务具有独立的栈空间),包括Os_StackInit();
SpinLock的初始化等;
在Os_Init初始化过程中,一般流程都是由主核执行,对于从核来说,经过该函数时一般无实际动作。调用栈如下图所示。
随后main将线程交给了EcuM,在EcuM的初始化最后一步,调用StartOs进行Os的启动,其调用栈如下图所示。
Os接收main后会在Os_Hook中将线程切到InitHook线程,在其中执行Os_CoreInit,Os_CoreInit中包含了Os层面各个资源的初始化,下面列出一些重点内容:
Os_ResourceInit:初始化OsResource;
Os_IocInit:初始化IOC;
Os_AppInit:初始化各个OsApplication,其中还包括Task、Isr、Alarm、Counter等元素初始化;
Os_XSigInit:初始化XSignal;
Os_TpStart:启动时间保护;
Os_BarrierSynchronizeInternal:进行多核同步;
Os_HookCallCallback:调用Startup Hook函数;
Os_AppStart:启动各个App;
线程切到InitHook时,其返回地址为Os_HookWrapperOs_CoreInitHook,进而调用Os_HookReturn,在其中进入任务调度,完成Os的初始化。
2.3.3 Counter初始化
我们回到Os_CoreInit,在Os_CoreInit中进行Os_AppInit,这就进入了我们本章节的Counter的初始化,调用栈如下图所示。
首先在Os_CoreInit过程中会调用Os_TimerPitInit进行相关的软件变量初始化,如Counter初值等;然后在Os_Hal_TimerPitInit(Os_Hal_Timer_GPT.h),进行GPT硬件Timer的硬件配置,包括Timer中断的优先级及中断源配置。
Counter中的Job是在启动时添加的,比如SetRelAlarm,下图为其过程调用关系图。
在EcuM启动第二阶段,会进行RTE相关的启动,其中进行Alarm的设置。在SetRelAlarm的调用中,会将Alarm作为Job添加到引用的Counter的Job Queue中,也就是将自身的Os_AlarmActivateTaskConfigType数据类型添加到->SwCounter.Counter.JobQueue中。
在Os_Trap这级调用中,如果使用了内存保护,则需要通过syscall触发Trap进入系统调用,从而进入Os内核模式,执行相应的逻辑,这里不展开说明了。
2.3.4 Counter运行阶段
运行阶段,由硬件Timer中断进入Counter相关逻辑,其调用关系图如下图所示。
Timer的中断入口函数定义在Os_Timer.c中,其中会调用Os_TimerSwIncrement进行Counter的累加。
FUNC(void, OS_CODE) Os_TimerSwIncrement
(
P2CONST(Os_TimerSwConfigType, AUTOMATIC, OS_CONST) Timer
)
{
/* #10 Perform assertions. */
Os_Assert((Os_StdReturnType)(Timer->Dyn->Value <= Timer->Counter.Characteristics.MaxCountingValue));
/* #20 If the counter reached its maximum value, reset the counter to zero. */
if(OS_UNLIKELY(Timer->Dyn->Value == Timer->Counter.Characteristics.MaxCountingValue))
{
Timer->Dyn->Value = 0;
}
/* #30 Otherwise, increment the counter. */
else
{
(Timer->Dyn->Value)++;
}
/* #40 If the compare value (time stamp of the next job) has been reached: */
if(OS_UNLIKELY(Timer->Dyn->Value == Timer->Dyn->Compare))
{
/* #50 Work of expired jobs. */
Os_CounterWorkJobs(&(Timer->Counter));
}
}
我们可以看到Os_TimerSwIncrement中首先对Counter的Tick进行累加,然后根据Counter的比较值Compare,该值定义了下一个需要激活Alarm的未来Tick值,然后根据比较判断是否执行Job。如果我们的最小周期Alarm为1ms,那每次都会进入Os_CounterWorkJobs进行工作,否则需要按需执行,Compare的值是按需求更新的。
如果有需要激活的Alarm,则会调用Os_CounterWorkJobs。
FUNC(void, OS_CODE) Os_CounterWorkJobs
(
P2CONST(Os_CounterConfigType, AUTOMATIC, OS_CONST) Counter
)
{
P2CONST(Os_JobConfigType, AUTOMATIC, OS_CONST) job;
P2CONST(Os_PriorityQueueConfigType, AUTOMATIC, OS_CONST) jobQueue = &(Counter->JobQueue);
Os_IntStateType interruptState;
uint8 jobCounter = 0u;
/* #10 Suspend interrupts */
Os_IntSuspend(&interruptState);
job = Os_PriorityQueueTopGet(jobQueue);
/* #20 Repeat until queue is empty OR there are no expired jobs in the queue: */
while(OS_LIKELY(job != NULL_PTR))
{
/* #30 Get counter's current value. (We have a continuously changing FRT here.) */
Os_TickType now = Os_CounterGetPhysicalValue(Counter);
/* #40 If the high prio job is expired (likely): */
if(OS_LIKELY(Os_CounterIsFutureValue(Counter, job->Dyn->ExpirationTimestamp, now) == 0u))
{
/* #50 Dequeue the job. */
Os_PriorityQueueDeleteTop(jobQueue);
/* #60 Work the job off. */
Os_JobDo(job);
/* #70 Increment the local job counter. */
jobCounter = jobCounter + 1u;
/* #80 If the maximum number of job executions per interrupt lock is reached: */
if (jobCounter >= MAX_JOB_EXECS_PER_LOCK)
{
jobCounter = 0u;
/* #90 Open interrupts for a short time to allow interrupts of higher priority. */
Os_IntResume(&interruptState);
Os_IntSuspend(&interruptState);
}
}
else
{
break;
}
/* #100 Get high prio job from queue. */
job = Os_PriorityQueueTopGet(jobQueue);
}
/* #110 If the queue still contains jobs: */
if(job != NULL_PTR)
{
/* #120 Set compare value to expiration time of next job. */
Os_CounterSetCompareValue(Counter, job->Dyn->ExpirationTimestamp);
}
/* #130 else */
else
{
/* #140 Set compare value far into the future. */
Os_TickType now = Os_CounterGetPhysicalValue(Counter);
Os_TickType expirationTimestamp;
expirationTimestamp = Os_TimerAdd(
Counter->Characteristics.MaxAllowedValue,
Counter->Characteristics.MaxCountingValue,
now,
Counter->Characteristics.MaxAllowedValue);
Os_CounterSetCompareValue(Counter, expirationTimestamp);
}
/* #150 Resume all interrupts. */
Os_IntResume(&interruptState);
}
我们可以看到,在Os_CounterWorkJobs中会首先使用Os_PriorityQueueTopGet从队列中取出Job,然后判断该Job是否到期。比如我们的Tick是1ms,某个Alarm定义周期10ms,那每10次Tick会调用Os_JobDo执行该Job的Callback函数。
在Os_JobDo(job)中,执行对应的Callback,对于Alarm Callback是Os_AlarmActionActivateTask或Os_AlarmActionSetEvent。
如果到期Job会被从队列中取出来,注意在Callback中,循环Job如周期Alarm会重新添加到队列,只不过会根据周期添加到队列末尾。
最后会进行Counter的Compare值的重载,由于Counter每次不一定执行完所有的Job,因此重载有所区别。
3 小结
本文对AUTOSAR OS中的Counter进行了介绍,对其内部元素和原理进行了详细说明,并对基于Aurix TC3XX芯片的Vector Microsar工具配置及代码进行了解读。下一篇我们将继续介绍Alarm模块,解析Alarm的机制,以及更深入地探讨Counter激活Alarm之间的关联交互。
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_44000419/article/details/144176191
|