(由于之前的blog已经关闭了,所以将此文章迁移至这里,并非转载)

HPET

This page is not meant as a full description of HPET, only as a lightweight introduction. If you need any information not covered by this article, consult the HPET specification.
这篇文章并没有完全的去说明HPET,只是简单地概述了一下。如果你需要的内容这里没有讲述到,请参考HPET的说明文档。

Preface

HPET, or High Precision Event Timer, is a piece of hardware designed by Intel and Microsoft to replace older PIT and RTC. It consists of (usually 64-bit) main counter (which counts up), as well as from 3 to 32 32 or 64 bit wide comparators. HPET is programmed using memory mapped IO, and the base address of HPET can be found using ACPI.

HPET(High Precision Event Timer,高精度时钟),是Intel和微软为了替换之前PIT或RTC(都可以当定时器用的一个东西)所设计的一个硬件。它由一个主的计数器和比较器组成,计数器一般是64位的,比较器至少有3个最多32个(这个我没考察过,只是翻译),通常比较器是32位或64位的。HPET通过内存映射IO来操作,内存的基地址可以从ACPI中找到。

Detecting HPET using ACPI

The HPET specification defines an ACPI 2.0 table that is to be used to detect presence, address and capabilities of HPET present in the system. If this table doesn’t exist, you should assume there is no HPET and you should fall back to PIT.

HPET规范中规定了会定义一个ACPI 2.0表(在ACPI中),可以通过这个表来检测硬件中是否存在HPET,并且可以获取到基地址以及HPET功能上的一些信息。如果这个表没有,那只能用PIT了。

struct address_structure
{uint8_t address_space_id;    // 0 - system memory, 1 - system I/Ouint8_t register_bit_width;uint8_t register_bit_offset;uint8_t reserved;uint64_t address;
} __attribute__((packed));struct description_table_header
{char signature[4];    // 'HPET' in case of HPET tableuint32_t length;uint8_t revision;uint8_t checksum;char oemid[6];uint64_t oem_tableid;uint32_t oem_revision;uint32_t creator_id;uint32_t creator_revision;
} __attribute__((packed));struct hpet : public description_table_header
{uint8_t hardware_rev_id;uint8_t comparator_count:5;uint8_t counter_size:1;uint8_t reserved:1;uint8_t legacy_replacement:1;pci_vendor_t pci_vendor_id;address_structure address;uint8_t hpet_number;uint16_t minimum_tick;uint8_t page_protection;
} __attribute__((packed));

HPET – timer vs comparators

There is just one up counting main counter in the timer, but interrupt generation is handled by comparators. There’re from 3 to 32 comparators, and the exact amount is indicated by comparator_count field in the above hpet structure. Keep in mind you have to initialize both the main counter and all of the comparators. Also, the routing as well as allowed routing of comparator interrupts is independent, so you have to detect and set it up for each of them individually. More information on this procedure is provided further in the text.

HPET中只有一个通过定时器自加的计数器,通过不同的比较器来产生中断。(可以理解为只有一个基准Timer,并且自加计数器的值,然后通过比较器来比较是否触发中断。)HPET中大约有3-32个比较器,具体的值时候hpet结构体中的comparator_count字段来设置。必须要同时初始化主计数器和所有的比较器。每个比较器所触发的中断都是可以独立配置的,所以一般情况下都需要为每个比较器单独的设置中断。在下文中将会继续说明这部分内容。

HPET operating modes

HPET offers two operating modes: one-shot (also called “non-periodic” by the specification) and periodic mode.

HPET提供两种操作模式:单次触发(也叫非周期触发)和周期触发。

One-shot mode

In non-periodic mode, the OS programs one of timer’s comparator registers with value of main counter that is to trigger an interrupt. If the timer is set to 32 bit mode, it will also generate an interrupt when the counter wraps around. The comparator register’s value is never written by the hardware, you are free to write to it and read from it at any time, therefore you can change at what value in the main counter the interrupt will be generated.

在单次触发模式中,操作系统写一个值到比较器里面,当主计数器达到该值的时候,就会产生一个中断。如果定时器设置为32位模式的情况下,当计数器溢出的时候也会产生一个中断。比较器的寄存器可以随时的进行读写,因为硬件不会操作它。这样你就可以随意的设置定时器了。

Every comparator in HPET must support non-periodic mode.

每个HPET的比较器,必须支持单次触发模式,估计是文档规定的(反正我是没考证过)。

Periodic mode

Periodic mode is more tricky than non-periodic mode. For periodic mode, similarly to one-shot mode, you write a value at which an interrupt shall be generated to the comparator register. When the interrupt is generated, however, the hardware will increase the value in comparator register by the last value written to it! This is a consequence of HPET’s main counter being up-counting.

周期触发模式相比单次触发模式更加方便(tricky这个单次真不好翻译,意会吧)。周期触发模式和单次触发模式的操作很像,都是写比较器的寄存器进行操作。但是,硬件会在最后一次写如寄存器的值后自加这个值,根据HPET计数器。

So, if the main counter’s value is 12345 when we set the timer up, and we write 12456 to comparator (i.e. the interrupt should trigger 111 time units from now), when the interrupt triggers, 12456 will be added to the comparator register, so it will become 24912, which is 12456 time units from the first interrupt. There are two techniques to deal with this problem; they will both be described in later part of the article.

例如,如果主计数器当前是12345,我们写12456到比较器的寄存器中(中断将会在111个周期后触发),当中断触发后,比较器的寄存器会变成24912(这个数来自12456+12456,也就是比较器的寄存器中的值又增加了一次上次写入的值)。有两种方法可以解决这个问题,文本将在后面进行说明。

Comparators are NOT required to support this mode; you must detect this capability when initializing a comparator. More information on this is provided further in the article.

比较器并非必须支持这种模式,所以在初始化比较器时,必须检测是否支持这个功能。具体信息应该咨询硬件架构提供商(自己查文档吧)。

Interrupt routing

HPET supports three interrupt mapping options: “legacy replacement” option, standard option, and FSB option.

HPET支持三种中断映射模式,原始模式,标准模式,FSB模式。

“Legacy replacement” mapping

In this mapping, HPET’s timer (comparator) #0 replaces PIT interrupts, whereas timer #1 replaces RTC’s interrupts (in other words, PIC and RTC will no longer cause interrupts). While the HPET specification provides the following table describing the routing of timers #0 and #1 in this mapping, it is recommended to at least check the routing of IRQ0 and IRQ8 to I/O APIC in ACPI tables.

在原始模式中,定时器(比较器)#0替换PIT的中断,定时器#1替换RTC的中断(意思就是,HPET替换PIC和RTC,并且PIC和RTC不会产生中断)。下表来自HPET的说明,其中规定了中断映射的方式。建议具体确认下IRQ0和IRQ8在IOAPIC和ACPI表的信息。

Timer PIC mapping IOAPIC mapping
0 IRQ0 IRQ2
1 IRQ8 IRQ8
N As per IRQ Routing Field As per IRQ Routing Field

Standard mapping

In standard mapping, each timer has its own interrupt routing control. Allowed I/O APIC inputs are found by reading timer’s capabilities register.

标准模式中,每个定时器(比较器)有自己的中断控制,通过定时器(比较器)的寄存器及IOAPIC进行控制(就是这么个意思,别纠结翻译了)。

FSB mapping

This mapping is almost identical to PCI’s Message Signaled Interrupts. The “Timer N FSB Interrupt Route Register” which defines how FSB interrupts are configured can be found in the specification. FSB interrupts are enabled using “Tn_FSB_EN_CNF” field in timer’s configuration register. This mapping mode will not be further discussed in this

先解释下FSB,就是前端总线的意思。

这种模式就是大多数使用的PCI消息的方式触发中断(其实本质是PCI内存写事件触发的中断,大体参考下而已)。根据文档来具体操作每个定时器的中断配置寄存器的配置。使用“Tn_FSB_EN_CNF”寄存器来控制是否使用FSB中断模式。本文中将不对这种方式进行讲解(外文就这么写的,我也不补充了,如果有心情,会以后更新)。

HPET registers

The following table and field descriptions can also be found in the specification. “Offset” means offset from the address defined in address field of hpet struct. The following table skips reserved registers defined in the specification.

下表所描述的寄存器信息可以在HPET文档中找到。“Offset”指的事hpet结构体在内存中的偏移(根据我的经验 ,一般是指物理内存)。下表中已经将没用的寄存器给去掉了。

Offset Register Type
0x000 – 000×7 General Capabilities and ID Register Read only
0x010 – 0x017 General Configuration Register Read/write
0x020 – 0x027 General Interrupt Status Register Read/write clear
0x0F0 – 0x0F7 Main Counter Value Register Read/write
(0x100 + 0x20 * N) – (0x107 + 0x20 * N) Timer N Configuration and Capability Register Read/write
(0x108 + 0x20 * N) – (0x10F + 0x20 * N) Timer N Comparator Value Register Read/write
(0x110 + 0x20 * N) – (0x117 + 0x20 * N) Timer N FSB Interrupt Route Register Read/write

General Capabilities and ID Register

Bits Name Description
63:32 COUNTER_CLK_PERIOD Main counter tick period in femptoseconds (10^-15 seconds). Must not be zero, must be less or equal to 0x05F5E100, or 100 nanoseconds.
31-16 VENDOR_ID This field should be interpreted similarly to PCI’s vendor ID.

15 LEG_RT_CAP If this bit is 1, HPET is capable of using “legacy replacement” mapping.|
|14 |Reserved| –|
|13 |COUNT_SIZE_CAP |If this bit is 1, HPET main counter is capable of operating in 64 bit mode.
|12:8| NUM_TIM_CAP |The amount of timers – 1.|
|7:0 |REV_ID |Indicates which revision of the function is implemented; must not be 0.|

General Configuration Register

Bits Name Description
63:2 Reserved
1 LEG_RT_CNF 0 – “legacy replacement” mapping is disabled1 – “legacy replacement” mapping is enabled
0 ENABLE_CNF Overall enable.0 – main counter is halted, timer interrupts are disabled1 – main counter is running, timer interrupts are allowed if enabled

General Interrupt Status Register

Bits Name Description
63:32 Reserved
n Tn_INT_STS The functionality is dependent of whether edge or level-triggered mode is used for timer #n.For level-triggered: the default value is 0. When a corresponding timer interrupt is active, this bit is set. If it is set, software can clear it by writing 1 to this bit. Writes of 0 have no effect.For edge-triggered: this bit should be ignored. It is always set to 0.

Main Counter Value Register

Bits 63:0 of this register are called MAIN_COUNTER_VAL. Writes to this register should only be done when the counter is halted (ENABLE_CNF = 0). Reads will return current value of the main counter. 32-bit counters will always return 0 for the upper 32 bits. If 32 bit reads are performed on 64 bit counter, consult 2.4.7 in the specification for instructions how to do it safely. It is recommended to use 32 bit counter when on 32-bit only software.

只有在计数器不使能(停止)的情况下才可以操作 MAIN_COUNTER_VAL的0-63位。可以直接从这些寄存器中读取出当前的值。在32位计数器模式下,前32位将都是0。如果一定要使用32位的方式去操作一个64位的计数器,在HPET文档的2.4.7节中将会说明如何才能安全的进行操作。再次建议在如果要使用32位的方式去操作,尽量将计数器配置为32位模式。

Timer N Configuration and Capability Register

Bits Name Description
63:32 Tn_INT_ROUTE_CAP Timer n Interrupt Routing Capability. If bit X is set in this field, it means that this timer can be mapped to IRQX line of I/O APIC.
31:16 Reserved
15 Tn_FSB_INT_DEL_CAP If this read-only bit is 1, this timer supports FSB interrupt mapping.
14 Tn_FSB_EN_CNF If this bit is set to 1, this timer will use FSB interrupt mapping.
13:9 Tn_INT_ROUTE_CNF This field indicates I/O APIC routing. Allowed values can be determined using Tn_INT_ROUTE_CAP. If an illegal value is written, then value read back from this field will not match the written value.
8 Tn_32MODE_CNF For 64-bit timer, if this field is set, the timer will be forced to work in 32-bit mode. Otherwise it has no effect.
7 Reserved
6 Tn_VAL_SET_CNF This field is used to allow software to directly set periodic timer’s accumulator. Detailed explanation is provided further in the article.
5 Tn_SIZE_CAP If this read-only bit is set to 1, the size of the timer is 64-bit. Otherwise, it’s 32-bit.
4 Tn_PER_INT_CAP If this read-only bit is set to 1, this timer supports periodic mode.
3 Tn_TYPE_CNF If Tn_PER_INT_CAP is 1, then writing 1 to this field enables periodic timer and writing 0 enables non-periodic mode. Otherwise, this bit will be ignored and reading it will always return 0.
2 Tn_INT_ENB_CNF Setting this bit to 1 enables triggering of interrupts. Even if this bit is 0, this timer will still set Tn_INT_STS.
1 Tn_INT_TYPE_CNF 0 – this timer generates edge-triggered interrupts.1 – this timer generates level-triggered interrupts. When the interrupt is generated, Tn_INT_STS is set. If another interrupt occurs before that bit is cleared, the interrupt will remain active.
0 Reserved

Timer N Comparator Value Register

Bits 63:0 (or 31:0, if the timer operates in 32 bit mode) are used to compare with main counter to check if an interrupt should be generated.

比较器使用寄存器中的值同主计数器进行比较(64位模式比较64位,32位模式比较32位),来判断是否产生中断。

Initialization

The following is the procedure you need to perform to initialize main counter and comparators in order to receive interrupts.

下面这段文章将会告诉你如何去初始化主计数器和比较器,并且使其能产生所需要的中断。

General initialization: 1. Find HPET base address in ‘HPET’ ACPI table. 2. Calculate HPET frequency (f = 10^15 / period). 3. Save minimal tick (either from ACPI table or configuration register). 4. Initialize comparators. 5. Set ENABLE_CNF bit.

全局初始化:1.在ACPI的HPET表中,找到HPET的基地址。2.计算HPET的频率(f = 10^15 / period)。3.保存最小的tick(无论是从ACPI表或配置寄存器)。4.初始化比较器。5.设置时能(ENABLE_CNF)位。

Timer N initialization: 1. Determine if timer N is periodic capable, save that information to avoid re-reading it every time. 2. Determine allowed interrupt routing for current timer and allocate an interrupt for it.

定时器N初始化:1.检测定时器N所支持的触发模式,保存这部分信息,避免每次都需要从新读。2.检测所支持的中断模式,并且分配一个中断。

I am enabling the timers only when I actually use them, so there’s no “real” initialization of comparators here.

外文原文的坐着习惯每次都在实际需要使用的时候采取时能具体的定时器,所以下面并没有真正的初始化具体的比较器,应该是具体需要使用的时候才真正的时能。

Keep in mind that allowed interrupt routing may be insane. Namely, you probably want to use some of ISA interrupts – or, at very least, be able to use them at one point unambiguously. Last time I checked VirtualBox allowed mappings for HPET, it allowed every timer to be routed to any of 32 I/O APIC inputs present on the system. Knowing how buggy hardware can be, I wouldn’t be too surprised if there exists a PC with HPET claiming that input #31 is allowed, when there are only 24 I/O APIC inputs. Be aware of this when choosing interrupt routing for timers.

上面大意,我理解为瞎乱使用中断路由基本会祸害死人。IOAPIC中断输入是公用的,所以别用重复了,并且一定要规划好怎么映射以及怎么使用,或者说如何复用。

Using timers

One-shot mode

To enable one-shot mode:

下面代码指出如何配置单次触发模式的定时器。

// "time" is time in femptoseconds from now to interrupt
if (time < COUNTER_CLK_PERIOD)
{time = adjust_time(time);
}write_register_64(timer_configuration(n), (ioapic_input << 9) | (1 << 2));
write_register_64(timer_comparator(n), read_register(main_counter) + time);

I hope the above code is obvious. If it’s not, please analyze the meaning of specific fields in registers used above.

外文原文作者懒了,懒得说明每行的具体意思。大体能猜出来就行了。

Periodic mode

To enable periodic mode:

下面代码指出如何配置周期触发模式的定时器。

// "time" is time in femptoseconds from now to interrupt
if (time < COUNTER_CLK_PERIOD)
{time = adjust_time(time);
}write_register_64(timer_configuration(n), (ioapic_input << 9) | (1 << 2) | (1 << 3) | (1 << 6));
write_register_64(timer_comparator(n), read_register(main_counter) + time);
write_register_64(timer_comparator(n), time);

This snippet requires some more comments.

外文原文作者良心发现了,需要给一些注释翻译。

Bit 2 is the same as above, Interrupt Enable. Bit 3 is also quite straightforward – 1 means periodic timer. But we’ve also set bit 6. Why?

bit 2和之前配置的相同,都是为了使能中断。bit3也是需要配置的,配置为1代表的是使用周期模式。但是为何需要配置Bit 6,外文作者引用如下内容去解释。

Let’s take a look at quote from the HPET specification:

外文作者又懒了,直接抄书了。

Timer n Value Set: […] Software uses this read/write bit only for timers that have been set to periodic mode. By writing this bit to a 1, the software is then allowed to directly set a periodic timer’s accumulator.

Software does NOT have to write this bit back to 0 (it automatically clears). Software should not write a 1 to this bit position if the timer is set to non-periodic mode.

This means that next write to timer N comparator register will have the usual meaning, while second next write will write directly to the accumulator. I believe that the wording could’ve been much better.

Bit 6的意思是说第一次写比较器的寄存器是通常的意义,就是写个需要比较的数,到达的时候就触发中断,但是连续的第二次写比较器的寄存器意义就改变了,之前不是说过他触发中断后,会把值增加么?设置完Bit 6后,连续的第二次写比较器的寄存器的值就是增加的值,这样就将之前留下的问题给解决了。

译者注,连续两次写寄存器的代码中间一定要做一些保护,因为有些编译器会进行优化,直接将上面那句在编译阶段就给扔了,所以一定要做一些保护,避免自动优化造成的错误。

结尾,我是Boris,留下邮箱为方便大家交流xuezhe.liu@hotmail.com。有关HPET的问题可以进行咨询及讨论。

本文翻译自HPET – OSDev Wiki。如需原文文档,请参考链接http://wiki.osdev.org/HPET。

See also

IOAPIC
PIT
RTC

External links

HPET article on Wikipedia
Intel’s HPET Specification v1.0a

HPET(High Precision Event Timer)简要说明相关推荐

  1. 官档翻译-启用High Precision Event Timer (HPET) Functionality

    原文地址:http://doc.dpdk.org/guides/linux_gsg/enable_func.html#enabling-additional-functionality BIOS准备 ...

  2. [dpdk] TSC , HPET, Timer, Event Timer,RDTSCP

    关于dpdk timer跨越CPU core调度的准确性问题 首先dpdk的timer接口里边使用 cpu cycle来比较时间.根据之前的内容 [dpdk] dpdk --lcores参数 当一个E ...

  3. Linux内核深入理解定时器和时间管理(6):x86_64 相关的时钟源(kvm-clock,tsc,acpi_pm,hpet)

    Linux内核深入理解定时器和时间管理 x86_64 相关的时钟源(kvm-clock,tsc,acpi_pm,hpet) rtoax 2021年3月 在原文基础上,增加5.10.13内核源码相关内容 ...

  4. SylixOS x86 HPET 定时器驱动

    HPET(High Precision Event Timer) 俗称高精度定时器,最低时钟频率为10MHZ,而且定义了比较严格的精确度(间隔 >= 1 毫秒的允许 +-0.05% 的误差,间隔 ...

  5. Linux 内核引导选项简介 *********很多常用的受益匪浅

    内核引导选项大体上可以分为两类:一类与设备无关.另一类与设备有关.与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项.比如,如果你想知道可以向 AHA154 ...

  6. Linux启动引导参数grub

    内核引导参数大体上可以分为两类:一类与设备无关.另一类与设备有关.与设备有关的引导参数多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导参数.比如,如果你想知道可以向 AHA154 ...

  7. Linux内核引导选项

    概述 内核引导选项大体上可以分为两类:一类与设备无关.另一类与设备有关.与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项.比如,如果你想知道可以向 AHA ...

  8. linux 内核启动参数

    Linux 内核引导选项简介 作者:金步国 版权声明 本文作者是一位开源理念的坚定支持者,所以本文虽然不是软件,但是遵照开源的精神发布. 无担保:本文作者不保证作品内容准确无误,亦不承担任何由于使用此 ...

  9. Linux 内核引导参数简介

    概述 内核引导参数大体上可以分为两类:一类与设备无关.另一类与设备有关.与设备有关的引导参数多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导参数.比如,如果你想知道可以向 AHA ...

最新文章

  1. C#遍历得到指定文件夹下的所有文件和子文件夹
  2. get 和post 方式请求数据
  3. 复旦大学肖仰华教授:知识图谱落地的基本原则与最佳实践
  4. Tomcat5+Mssql server 2000数据库连接池配置
  5. Faster-RCNN+ZF用自己的数据集训练模型(Python版本)
  6. 漫画:IT公司年终总结会开崩了...
  7. web开发为什么用java的多_java与php做web开发 最大的区别在那 为什么好多用java的...
  8. php 跨域解决方案
  9. 数据结构_静态链表(C语言)
  10. 我的世界服务器雪球菜单无限雪球,我的世界:雪球玩法?关于指令的高端玩法,操作简单老玩家都在用...
  11. 达拉斯大学计算机硕士专业排名,美国名校之德克萨斯大学达拉斯分校研究生专业排名榜单 优势专业你选对了...
  12. 各类排序算法汇总及动画演示(C语言)
  13. 国内有没有 全球自由公开化开源免费项目软件开发的社区?
  14. 响应式Web设计:HTML5和CSS3实战(读书笔记)
  15. ffmpeg学习笔记
  16. 怎么优化自己网站的关键词_新网站seo必做步骤
  17. 消灭该死的重复 下(1)布尔运算 boolean
  18. 给swagger的接口添加描述
  19. 发现个好东西pAppLocale,转码神器啊
  20. CentOS服务器的加固方案

热门文章

  1. 无分类编址CIDR(构造超网)
  2. python文件操作入门
  3. Gitlab-runner 构建失败问题排查
  4. java实现生成验证码图片
  5. 分享一个超大文件编辑器(WINDOWS 文本编辑器)
  6. vscode 终端运行yarn 报错 “因为在此系统上禁止运行脚本”
  7. 前端实现高效的海报系统
  8. 移动APP集成支付宝--服务器端设计
  9. 用C语言编写程序,任意输入一个字符串,将其中的字符按从小到大的顺序重排
  10. Excel下的数据挖掘:学生成绩统计分析实战之前言