• 架构学习之通用定时器


     人与人之间的偏见和隔阂就如同一座大山,让人互相看不到真实的样子。OK,准备好plan B。

                                                                                                                                      ----- 佚名

    本文翻译自文档Learn the architecture_Generic Timer。

    1 Overview

            本文档介绍通用定时器,A系列PE的定时器框架。本文档介绍现代SOC中定时器框架的不同组件,并介绍对软件有用的编程接口。

            本文档的目标人群为基于ARM系统写低层软件进行初始化或使用timer的开发者。文档的使用者通常工作在低层代码。

            在文档的最后,你可以检查一下你的知识。你将学习到构成timer子系统的不同组件的名称和作用。你将能够在裸机上写代码建立起timer。你将能够基于实现的框架特性,描述哪个timer存在。

    2 在开始之前

            我们假定你对ARM异常级别非常熟悉。如果不熟悉,你可以先阅读ARM v8-A Exception model指导。

            文档包括用C或汇编写的代码例子。如果你对ARM汇编语法不熟悉,可以阅读Arm v8-A Instruction Set Architecture。例子要求Arm Development Studio。如果你还没有它,可以下载一个评估版。

    3 什么是通用定时器为

            通用定时器为Arm core提供标准定时器。通用定时器包括一个系统counter和一组每core定时器,如下图所示:

            系统counter为一直on的设备,它提供一个固定频率的系统计数。在系统中system count值对所有core都是广播的,让core对时间的流逝有共同的看法。系统计数值为56~64位,通常频率为1MHZ~50MHZ。

            NOTE:通用定时器仅测量时间的流逝。它不会报告时间或日期。通常一个SOC也为时间和日期包含一个RTC。

            每个core有一组timer。这些定时器是比较器,它们与系统计数器提供的广播系统计数进行比较。软件可以配置timer在将来某个点产生中断或event。软件也可以使用系统计数增加时间戳,因为系统计数对所有core提供公共参考点。

            在本指导中,我们将解释timer和系统计数器的操作和配置。

    4 处理器定时器

            下图描述了处理器定时器:

    计数和频率

            系统寄存器CNTPCT_EL0报告当前系统计数值。

            CNTPCT_EL0的读取可以被预测。这意味着它们可以被无序的读取。在某些情况下这特别重要,比如比较时间戳。当计数器读的时序很重要时,可以使用ISB,如下代码所示:

            CNTPCT_EL0报告系统计数的频率。但是,该寄存器不是由硬件进行populate。该寄存器在最高异常级别是可写的,在所有异常级别为可读。运行在EL3的firmware,初始化寄存器作为早期系统初始化的一部分。更多级别的软件,像OS,可以使用该寄存器获取频率。

    定时器寄存器

            每个定时器有三个如下系统寄存器:

    寄存器

    作用

    _CTL_EL

    控制寄存器

    _CVAL_EL

    比较器值

    _TVAL_EL

    timer值

            在寄存器名中,表明访问哪个timer。下列表显示可能的值:

    Timer

    寄存器前缀

    EL

    EL1 physical timer

    CNTP

    EL0

    EL1 virtual timer

    CNTV

    EL0

    Non-secure EL2 physical timer

    CNTHP

    EL2

    Non-secure EL2 virtual timer

    CNTHV

    EL2

    EL3 physical timer

    CNTPS

    EL1

    Secure EL2 physical timer

    CNTHPS

    EL2

    Secure EL2 virtual timer

    CNTHVS

    EL2

            比如CNTP_CVAL_EL0为EL1 physical timer的比较器寄存器。

    自测试

            EL3 physical timer和non-secure EL2 virtual timer的控制器寄存器名称是什么?

    访问定时器

            对于一些定时器,可以配置哪些异常级别可以访问定时器:

            EL1 physical and virtual timer: EL0访问这些定时器由CNTKCTL_EL1控制。

            EL2 physical and virtual timer: 当HCR_EL2.{TGE,E2H}={1,1}时,EL0访问这些定时器由CNTKCTL_EL2控制。

            这些定时器作为支持Armv8.1-A Virtualization Host Extension的一部分被添加。哪个不在本文档中介绍?EL3 physical timer:S.EL1和S.EL2访问定时器由SCR_EL3.ST控制。

    配置定时器

            这里有两种方式来配置定时器,或使用CVAL寄存器,或使用TVAL寄存器。

            比较器寄存器CVAL为64位寄存器。软件写值到这个寄存器,当计数达到或超过时,定时器触发,你将看到:

            定时器寄存器TVAL为32位寄存器。当软件写TVAL时,处理器读取当前系统计时,增加所写值,并populate CVAL:

            在软件中你可以看到CVAL的值。如果你读当前系统计数,写1000到TVAL,然后读取CVAL,你将看到CVAL为1000+系统计数。这个值是合适的,因为在指令时序时时间仍会前进。

            读回TVAL将它减少到0,但系统计时仍增加。TVAL报告一个signed值,将在定时器fire后将继续减少,这允许软件决定该定时器在多久之前fire。TVAL和CVAL给软件两种不同的方式如何使用定时器。如果软件需要在时钟的x ticks中需要一个定时器时,软件能写X到TVAL。相反,当系统计数达到y时如果软件想要一个event,软件写y到CVAL。

            记住TVAL和CVAL为两种不同方式对相同的定时器编程。它们非不同的定时器。

    中断

            定时器可以被配置为产生一个中断。一个core定时器的中断仅能被传递给该core。这意味着一个core的定时器不能产生一个目标为不同core的中断。

            中断的产生由CTL寄存器控制,使用这些域:

    1. ENABLE - 使能定时器
    2. IMASK - 中断屏蔽。使能或禁用中断的产生
    3. ISTATUS - 当ENABLE=1时,报告是否中断fire(CVAL <= 系统计数)

            为产生一个中断,软件必须设置ENABLE为1并清IMASK为0。当定时器fire(CVAL <= 系统计数)时,产生一个中断信号。在Armv8-A系统中,中断控制器通常为GIC。

            中断ID(INTID)用于由SBSA定义的每个定时器,如下:

    Timer

    SBSA推荐的INTID

    EL1 physical timer

    30

    EL1 virtual timer

    27

    Non-secure EL2 physical timer

    26

    Non-secure EL2 virtual timer

    28

    EL3 physical timer

    29

    Secure EL2 physical timer

    20

    Secure EL2 virtual timer

    19

            定时器产生的中断为电平敏感的方式。这意味着,一旦定时器fire条件满足时,定时器将持续产生中断,直到下列条件产生:

    1. IMASK被设置为1,这将屏蔽中断
    2. ENABLE被设置为0,这将禁用中断
    3. TVAL或CVAL被写,因此fire条件不再满足

            当写定时器的中断handler时,软件在GIC中deactivate中断之前清除中断非常重要。否则GIC将重发相同的信号。

            GIC的操作和配置超出本文档的范围。

    定时器虚拟化

            早期我们在一个处理器上引入不同的定时器。这些定时器被分成两组:virtual定时器和physical定时器。

            physical定时器,像EL3 physical timer, CNTPS, 与系统计数提供的计数值进行比较。该值被当作physical count,并由CNTPCT_EL0报告。

            virtual定时器,像EL1 virtual timer, CNTV,与虚拟计数进行比较。该虚拟计数值由下面来计算:

            偏移值由寄存器CNTVOFF_EL2指定,这仅由EL2或EL3访问。该配置由下图呈示:

     

            虚拟计数允许hypervisor将虚拟时间显示给虚拟机。比如,当虚拟机没有被调度到时,一个hypervisor可能使用offset隐藏流逝过的时间。这意味着虚拟计数由虚拟机的当前时间决定,而不是wall clock时间。

    Event stream

            通用定时器也可以用于产生event stream,作为event机制的等待的一部分。WFE指令将core置于低功耗状态,可以通过event将core唤醒。

            WFE机制的细节不在本文档介绍范围。

            这里有几种方式产生event,包括:

    1. 在不同的core上执行SEV指令(Send Event)
    2. 清除core的Global Exclusive Monitor
    3. 从core的通用定时器使用event stream

            通用定时器可以被配置以固定间隔产生一系列event。一个使用该配置产生超时。当等待资源有效时WFE通常会被使用,且当等待不会太久时。来自定时器的event stream意味着core保持在低功耗状态的最大时间达到。

            从physical count CNTPCT_EL0或从virtual count CNTPVT_EL0产生的event stream:

    CNTKCTL_EL1 - 控制CNTVCT_EL0产生的event stream

     CNTKCTL_EL2 - 控制CNTPCT_EL0产生的event stream

            对于每个寄存器,控制如下:

    1. EVENTEN使能或禁用event的产生
    2. EVNTI控制event的速率
    3. EVNTDIR控制何时产生event

            控制EVNTI指定0~15范围的bit位置。当在选择的位置的位修改,产生一个event。比如,如果EVNTI被设置为3时,当bit[3]的计数变化,产生一个event。

            控制EVNTDIR控制当选择位从1到0或从0到1时,是否产生event。

    总结表

            该表总结本节讨论的不同定时器的信息:

    定时器

    寄存器

    通常用于

    trappable

    使用counter

    INTID

    EL1 physical timer

    CNTP_<>_EL0

    EL0/EL1

    EL2

    CNTPCT_EL0

    30

    EL1 virtual timer

    CNTHP_<>_EL2

    NS.EL2

    CNTPCT_EL0

    27

    Non-secure EL2 physical timer

    CNTHPS_<>_EL2

    S.EL2

    CNTPCT_EL0

    26

    Non-secure EL2 virtual timer

    CNTPS_<>_EL1

    S.EL1/EL3

    EL3

    CNTPCT_EL0

    28

    EL3 physical timer

    CNTV_<>_EL0

    EL0/1

    CNTPCT_EL0

    29

    Secure EL2 physical timer

    CNTHV_<>_EL2

    NS.EL2

    CNTPCT_EL0

    20

    Secure EL2 virtual timer

    CNTHVS_<>_EL2

    S.EL2

    CNTPCT_EL0

    19

    5 系统计数器

            在“什么是通用定时器”章节,我们介绍了系统计数器。系统计数器产生会发送给系统中所有core的系统计数值,如下图所示:

     

            SOC实现负责系统计数器的设计。通常,在系统启动时系统计数器要求做初始化。Arm为系统计数器提供推荐的寄存器接口,但你需要检查你的SOC实现获取详细实现。

            一个physical system count值被广播到所有core上。这意味着所有的core共享相同的流逝时间。考虑如下例子:

    1. 设备A读取当前系统计数并将其作为时间戳添加到message中,然后将message发送给设备B;
    2. 当设备B接受到message,它会比较时间戳和当前系统计数

            在这个例子中,设备B看到的系统计数值不可能比message中时间戳更早。

            系统计数器测量真实时间。这意味着它不会被电源管理技术如DVFS或将core置于低功耗状态。计数必须以固定频率继续增加。实际上,这要求系统计数器一直处于开启的电源域。

            为了节省电源,系统计数器可以修改速率来更新计数。比如,系统计数器可以以每10个tick更新计数。当连接的core处于低功耗状态时这非常有用。系统计数仍需要反映时间的推进,但电源可以通过广播更少计时更新达到节省的目的。

    Counter scale

            将系统计数进行缩放的选项在Armv8.4-A引入的。代替时钟的每个tick增加一,计数可以每次增加x,在系统初始化时软件配置x。该特性允许计数有效的更快或更慢增加计数。

            为了支持缩放,系统计时器内部将计数器值扩展到88位,如你所看到下图所示:

            计数是由88位固定值表示,其中64位为整数部分,24位为分数部分。计数的整数部分由连接的处理器的CNTPCT_EL0报告。分数部分被内部的系统计数器使用。

            增量部分来自32位称为CNTSCR的寄存器,它的格式如下所示:

            增加值分成8位的整数部分和24位的分数部分。

            当缩放被使能,每次tick时计数增加CNTSCR。比如,如果CNTSCR被设置为0x01800000, 这意味着每次tick时计数增加1.5(其中整数部分0x1,分数部分为0x800000)。这由下表表示:

    tick

    内部计数器值

    整数部分/分数部分

    呈现的计数器值

    通过CNTPCT_EL0可见

    0

    0x0000_0000_0000_0000_0000_00

    0x0000_0000_0000_0000

    1

    0x0000_0000_0000_0001_8000_00

    0x0000_0000_0000_0001

    2

    0x0000_0000_0000_0003_0000_00

    0x0000_0000_0000_0003

    3

    0x0000_0000_0000_0004_8000_00

    0x0000_0000_0000_0004

    4

    0x0000_0000_0000_0006_0000_00

    0x0000_0000_0000_0006

    5

    0x0000_0000_0000_0007_8000_00

    0x0000_0000_0000_0007

    6

    0x0000_0000_0000_0009_0000_00

    0x0000_0000_0000_0009

            当系统计时器被禁用时也可以配置缩放。当计数器正在运行时,修改缩放使用或禁用,或缩放因子,它会导致不可知的计数值返回。

    基础编程

            在本节假定系统计时器实现了Arm推荐的寄存器接口。

            系统计数器提供两个寄存器组:CNTControlBase和CNTReadBase。

            寄存器组CNTControlBase用于配置系统计数器,且它可以安全访问支持trustzone的系统。寄存器组中的寄存器如下表所示:

    寄存器

    描述

    CNTCR

    控制寄存器,包括:

    1. 计数器使能
    2. 计数器缩放使能
    3. 更新频率选择
    4. Halt-on-debug控制

    CNTSCR

    当使能缩放时增加值

    CNTID

    ID寄存器,报告哪些特性被实现

    CNTSR

    状态寄存器。报告定时器是运行还是停止

    CNTCV

    报告当前计数值。

    返回计数的整数部分

    CNTFID

    报告有效的更新频率

            为了使能系统计数器,软件必须选择一个更新的频率并设置计数器使能。

            CNTReadBase为仅包含CNTCV寄存器的CNTControlBase的拷贝。这意味着CNTReadBase仅报告当前系统计数值。但不像CNTControlBase,CNTReadBase可以访问非安全访问。这意味着非安全软件可以读取当前计数,但不能配置系统计时器。

    6 外部定时器

            在“什么是通用计时器”章节,我们介绍了在处理器中的定时器。系统也可以包含额外的外部定时器。下图描述了一个例子:

     

            这些定时器的编程接口为内部定时器的镜像,但这些定时器通过内存映射寄存器被访问。这些寄存器的位置由SOC实现决定,并由你工作的SOC数据手册报告。

    来自外部内存映射的定时器的中断通常以共享外设中断SPI传递。

     

     

     

     

     

     

  • 相关阅读:
    想进BAT当架构师?BAT面试知识点扫盲祝你一臂之力!
    【笔记:模拟MOS集成电路】二级运算放大器频率响应与品质因子Q和阻尼因子ξ的讨论
    [EECS][Design Lab 3][All Carrot, No Stick] 实现目标线性轨迹运动 实现状态机的级联 距离控制实现避障
    【区块链 | Compound】5.剖析DeFi借贷产品之Compound:延伸篇
    安装PYG
    mysql 数据备份 --chatGPT
    hookZz,Dobby,xHook,consoleDebugger
    LabVIEW 应用程序视窗始终置于顶层
    Android使用Kotlin封装MMKVUtils
    MySQL 8.0新的GTID持久化线程和GTID恢复方式
  • 原文地址:https://blog.csdn.net/flyingnosky/article/details/126310866