当前位置: 首页 > 科技观察

一文看懂Linux时间子系统的硬件架构

时间:2023-03-17 21:34:16 科技观察

从硬件架构图中可以看出以下特点:每个CPU核心都包含自己的本地定时器,相互独立。每个本地定时器都支持中断生成。中断类型为PPI,是CPU的私有中断。GIC负责分配给指定的CPU。这些中断可用于生成系统事件。LocalTimer的中断如下:1.SecurePhysicalTimer事件(ID29,即上述设备节点中的13,29=16+13)2.Non-securePhysicalTimer事件(ID30,即上述设备节点中abovedevice14innode,30=16+14)3.VirtualTimerevent(ID27)4.HypervisorTimerevent(ID26)系统中有一个always-powereddomain,提供系统计数器和定时所有核心的所有定时器都基于系统计数器提供的计数器值,因此理论上所有本地定时器都基于相同的时间参考。为什么强调系统计数器一直供电,必须独立?这是因为在系统运行过程中,一些核可能会进入休眠状态以节省能量,本地定时器也可能因此而被关闭。在特定的时间唤醒CPU,唤醒后得到正确的时间。同时,系统计数器也支持休眠模式。它的休眠并没有关闭,只是频率降低了。通常,定时器的频率为1~50MHz。假设它以10MHz运行,将其降低到1MHz。那么,系统计数器会在运行时,计数器不再加1,而是加10,这样时间精度就不会丢失。系统计数器的实现标准是:1.至少56位宽度。2、频率为1-50MHz。3、溢流时间至少40年。4.arm对精度没有特殊要求,但是推荐最小值是24小时,误差不要超过10s。5、从0开始计数,正常情况下每个时钟脉冲加1,节能模式除外。系统计数器可以通过总线地址映射被所有内核访问,而本地定时器则通过CP15协处理器的运行被相应的CPU内核访问。软件架构的底层是硬件和驱动层。每个cpu核心都有自己的cpu本地定时器。此外,SOC内部必须有一个全局计数器。中间层是linux内核层。内核抽象出时钟源(clocksource)、时钟事件设备(clock_event_device)和时钟设备(tick_device)进行时间管理。分为两部分:右侧实现计时功能。linux内核有各种时间线,包括realtimeclock、monotonicclock、monotonicrawclock等,clocksource提供了一个单调递增的定时器来产生ticks,为timeline提供时钟源。Timekeeper是内核提供时间服务的基本模块,负责选择和维护最优的时钟源。计时功能在左侧实现。时钟事件管理可以产生事件或触发中断定时器。(一般来说,每个CPU都形成了自己的小系统,也需要管理自己的时钟事件。)tick设备是基于时钟事件设备工作的。cpu根据tick设备管理自己的调度、进程统计等。低精度定时器和高精度定时器都是基于tickdevice生成的定时器设备。它们的事件和周期信号的关系在上图中大体介绍。顶层是linux应用层。基于计时设备的是时间管理库timelib,基于定时器设备的是计时管理库timerlib。数据结构clocksource:来自于系统的时序要求。换句话说,系统需要知道现在是xx小时、xx分钟、xx秒、xx纳秒、xx、xx、xx、xx。本地定时器clocksource相关的配置信息:staticstructclocksourceclocksource_counter={.name="arch_sys_counter",.rating=400,.read=arch_counter_read,.mask=CLOCKSOURCE_MASK(56),.flags=CLOCK_SOURCE_IS_CONTINUOUS,};clock_event_device:来自系统的定时需求(即timer)。即从当前时间点开始,xxx纳秒后通知我做某事。localtimer的clock_event_device相关配置信息:staticvoid__arch_timer_setup(unsignedtype,structclock_event_device*clk){clk->features=CLOCK_EVT_FEAT_ONESHOT;if(type==ARCH_TIMER_TYPE_CP15){if(arch_timer_c3stop)clk->ST_FEAT_FEAT_CLK=CLO"arch_sys_timer";clk->rating=450;clk->cpumask=cpumask_of(smp_processor_id());clk->irq=arch_timer_ppi[arch_timer_uses_ppi];switch(arch_timer_uses_ppi){...caseARCH_TIMER_PHYS_NONSECURE_PPI:caseARCH_clP_TIMER_>set_state_shutdown=arch_timer_shutdown_phys;clk->set_state_oneshot_stopped=arch_timer_shutdown_phys;clk->set_next_event=arch_timer_set_next_event_phys;break;default:BUG();}}系统计数器clock_event_device相关配置信息如下,进入硬件定时器后,用于唤醒CPU。staticstructclock_event_deviceclockevent_sysctr={.name="i.MXsystemcountertimer",.features=CLOCK_EVT_FEAT_ONESHOT|CLOCK_EVT_FEAT_DYNIRQ,.set_state_oneshot=sysctr_set_state_oneshot,.set_next_event=sysctr_set_next_event,.set_state_shutdown=sysctr_set_state_shutdown,.rating=200,};tick_device是clock_event_device的子类。structtick_device{structclock_event_device*evtdev;enumtick_device_modemode;};tick设备的的的模式模式:enuumtick_device_mode{tickdev_mode_mode_periodic,tickdev_mode_oneshot,tickdev_mode_oneshot,}会将CPU推入空闲状态。当CPU休眠时,可以关闭本地定时器硬件。这会导致本地定时器无法唤醒CPU。为了在进入idle后唤醒CPU,有两种方案,一种是通过hrtimer的软件方案,一种是硬件方案。这里只介绍硬件方案。通常,永远在线的硬件定时器被用作唤醒源。它不属于任何CPU。它使用SPI类型的中断来唤醒CPU并处理软件定时器。