HPET: Difference between revisions

3,139 bytes added ,  29 days ago
m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
(timer N registers)
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(15 intermediate revisions by 8 users not shown)
Line 1:
{{In Progress}}
 
:''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 [http://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf HPET specification].''
 
'''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-bit or 64 -bit wide comparators. HPET is programmed using memory mapped IO, and the base address of HPET can be found using [[ACPI]].
==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]].
 
==Detecting HPET using ACPI==
 
The [http://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf 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]] or the [[APIC timer]].
 
<sourcesyntaxhighlight lang="cpp">
struct address_structure
{
{
uint8_t address_space_id; // 0 - system memory, 1 - system I/O
uint8_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 table
uint32_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_tuint16_t pci_vendor_id;
address_structure address;
uint8_t hpet_number;
uint16_t minimum_tick;
uint8_t page_protection;
} __attribute__((packed));
</syntaxhighlight>
</source>
 
==HPET - timer vs comparators==
Line 55 ⟶ 51:
==HPET operating modes==
 
HPET offers threetwo operating modes; as of now, this article describes two of them: one-shot (also called "non-periodic" by the specification) and periodic mode.
 
===One-shot mode===
Line 103 ⟶ 99:
===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 article.
 
==HPET registers==
Line 114 ⟶ 110:
!Type
|-
|0x000 - 000x70x007
|General Capabilities and ID Register
|Read only
Line 152 ⟶ 148:
|63:32
|COUNTER_CLK_PERIOD
|Main counter tick period in femptosecondsfemtoseconds (10^-15 seconds). Must not be zero, must be less or equal to 0x05F5E100, or 100 nanoseconds.
|-
|31-16
Line 295 ⟶ 291:
 
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.
 
==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.
 
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.
 
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.
 
==Using timers==
 
===One-shot mode===
 
To enable one-shot mode:
 
<syntaxhighlight lang="cpp">
// "time" is time in femtoseconds 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);
</syntaxhighlight>
 
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:
 
<syntaxhighlight lang="cpp">
// "time" is time in femtoseconds 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);
</syntaxhighlight>
 
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?
 
Let's take a look at quote from the HPET specification:
 
<blockquote>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.</blockquote>
<blockquote>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.</blockquote>
 
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.
 
==See also==
* [[IOAPIC]]
* [[APIC]]
* [[APIC timer]]
* [[PIT]]
* [[RTC]]
 
==External links==
* [http://en.wikipedia.org/wiki/High_Precision_Event_Timer HPET article on Wikipedia]
* [http://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf Intel's HPET Specification v1.0a]
 
 
[[Category:Common Devices]]
[[Category:Interrupts]]
[[Category:Timers]]