Fixes for hpet counter read, choose better PIT divisor

This commit is contained in:
Jeremy Soller
2022-11-14 13:58:55 -07:00
parent 2052cc8cdc
commit 33aa263deb
9 changed files with 77 additions and 41 deletions

View File

@@ -1,4 +1,5 @@
use crate::acpi::hpet::Hpet;
use super::pit;
const LEG_RT_CNF: u64 = 2;
const ENABLE_CNF: u64 = 1;
@@ -14,7 +15,7 @@ pub(crate) const MAIN_COUNTER_OFFSET: usize = 0xF0;
// const NUM_TIMER_CAP_MASK: u64 = 0x0f00;
const LEG_RT_CAP: u64 = 0x8000;
const T0_CONFIG_CAPABILITY_OFFSET: usize = 0x100;
const T0_COMPARATOR_OFFSET: usize = 0x108;
pub(crate) const T0_COMPARATOR_OFFSET: usize = 0x108;
const PER_INT_CAP: u64 = 0x10;
@@ -35,10 +36,8 @@ pub unsafe fn init(hpet: &mut Hpet) -> bool {
return false;
}
let counter_clk_period_fs = capability >> 32;
let desired_fs_period: u64 = 2_250_286 * 1_000_000;
let clk_periods_per_kernel_tick: u64 = desired_fs_period / counter_clk_period_fs;
let period_fs = capability >> 32;
let divisor = (pit::RATE as u64 * 1_000_000) / period_fs;
let t0_capabilities = hpet.base_address.read_u64(T0_CONFIG_CAPABILITY_OFFSET);
if t0_capabilities & PER_INT_CAP == 0 {
@@ -51,9 +50,9 @@ pub unsafe fn init(hpet: &mut Hpet) -> bool {
let t0_config_word: u64 = TN_VAL_SET_CNF | TN_TYPE_CNF | TN_INT_ENB_CNF;
hpet.base_address.write_u64(T0_CONFIG_CAPABILITY_OFFSET, t0_config_word);
// set accumulator value
hpet.base_address.write_u64(T0_COMPARATOR_OFFSET, counter + clk_periods_per_kernel_tick);
hpet.base_address.write_u64(T0_COMPARATOR_OFFSET, counter + divisor);
// set interval
hpet.base_address.write_u64(T0_COMPARATOR_OFFSET, clk_periods_per_kernel_tick);
hpet.base_address.write_u64(T0_COMPARATOR_OFFSET, divisor);
// Enable interrupts from the HPET
{

View File

@@ -10,7 +10,14 @@ const ACCESS_LATCH: u8 = 0b00 << 4;
const ACCESS_LOHI: u8 = 0b11 << 4;
const MODE_2: u8 = 0b010 << 1;
const CHAN0_DIVISOR: u16 = 2685;
// 1 / (1.193182 MHz) = 838,095,110 femtoseconds ~= 838.095 ns
pub const PERIOD_FS: u128 = 838_095_110;
// 461 / (1.193182 MHz) = 3863618.00071 ns ~= 386 us or 2.588 kHz
pub const CHAN0_DIVISOR: u16 = 461;
// Calculated interrupt period in nanoseconds based on divisor and period
pub const RATE: u128 = (CHAN0_DIVISOR as u128 * PERIOD_FS) / 1_000_000;
pub unsafe fn init() {
COMMAND.write(SELECT_CHAN0 | ACCESS_LOHI | MODE_2);

View File

@@ -4,15 +4,13 @@ use alloc::vec::Vec;
use crate::{interrupt, interrupt_stack};
use crate::context::timeout;
use crate::device::{local_apic, ioapic, pic};
use crate::device::{local_apic, ioapic, pic, pit};
use crate::device::serial::{COM1, COM2};
use crate::ipi::{ipi, IpiKind, IpiTarget};
use crate::scheme::debug::{debug_input, debug_notify};
use crate::scheme::serio::serio_input;
use crate::{context, time};
pub const PIT_RATE: u128 = 2_250_286;
//resets to 0 in context::switch()
#[thread_local]
pub static PIT_TICKS: AtomicUsize = AtomicUsize::new(0);
@@ -140,7 +138,7 @@ interrupt_stack!(pit_stack, |_stack| {
// Saves CPU time by not sending IRQ event irq_trigger(0);
{
*time::OFFSET.lock() += PIT_RATE;
*time::OFFSET.lock() += pit::RATE;
}
eoi(0);
@@ -268,7 +266,7 @@ interrupt!(lapic_error, || {
interrupt!(calib_pit, || {
{
*time::OFFSET.lock() += PIT_RATE;
*time::OFFSET.lock() += pit::RATE;
}
eoi(0);

View File

@@ -3,14 +3,28 @@ use super::device::{hpet, pit};
pub fn counter() -> u128 {
if let Some(ref hpet) = *ACPI_TABLE.hpet.read() {
let capability = unsafe { hpet.base_address.read_u64(hpet::CAPABILITY_OFFSET) };
let period_fs = (capability >> 32) as u128;
//TODO: handle rollover?
//TODO: improve performance
// Current count
let counter = unsafe { hpet.base_address.read_u64(hpet::MAIN_COUNTER_OFFSET) };
(counter as u128 * period_fs) / 1_000_000
// Comparator holds next interrupt count
let comparator = unsafe { hpet.base_address.read_u64(hpet::T0_COMPARATOR_OFFSET) };
// Get period in femtoseconds
let capability = unsafe { hpet.base_address.read_u64(hpet::CAPABILITY_OFFSET) };
let period_fs = capability >> 32;
// Calculate divisor
let divisor = (pit::RATE as u64 * 1_000_000) / period_fs;
// Calculate last interrupt
let last_interrupt = comparator.saturating_sub(divisor);
// Calculate ticks since last interrupt
let elapsed = counter.saturating_sub(last_interrupt);
// Calculate nanoseconds since last interrupt
(elapsed as u128 * period_fs as u128) / 1_000_000
} else {
// 1.193182 MHz PIT is approximately 838.095 nanoseconds
let period_ns = 838;
let counter = unsafe { pit::read() };
counter as u128 * period_ns
// Read ticks since last interrupt
let elapsed = unsafe { pit::read() };
// Calculate nanoseconds since last interrupt
(elapsed as u128 * pit::PERIOD_FS) / 1_000_000
}
}

View File

@@ -1,4 +1,5 @@
use crate::acpi::hpet::Hpet;
use super::pit;
const LEG_RT_CNF: u64 = 2;
const ENABLE_CNF: u64 = 1;
@@ -14,7 +15,7 @@ pub(crate) const MAIN_COUNTER_OFFSET: usize = 0xF0;
// const NUM_TIMER_CAP_MASK: u64 = 0x0f00;
const LEG_RT_CAP: u64 = 0x8000;
const T0_CONFIG_CAPABILITY_OFFSET: usize = 0x100;
const T0_COMPARATOR_OFFSET: usize = 0x108;
pub(crate) const T0_COMPARATOR_OFFSET: usize = 0x108;
const PER_INT_CAP: u64 = 0x10;
@@ -35,10 +36,8 @@ pub unsafe fn init(hpet: &mut Hpet) -> bool {
return false;
}
let counter_clk_period_fs = capability >> 32;
let desired_fs_period: u64 = 2_250_286 * 1_000_000;
let clk_periods_per_kernel_tick: u64 = desired_fs_period / counter_clk_period_fs;
let period_fs = capability >> 32;
let divisor = (pit::RATE as u64 * 1_000_000) / period_fs;
let t0_capabilities = hpet.base_address.read_u64(T0_CONFIG_CAPABILITY_OFFSET);
if t0_capabilities & PER_INT_CAP == 0 {
@@ -51,9 +50,9 @@ pub unsafe fn init(hpet: &mut Hpet) -> bool {
let t0_config_word: u64 = TN_VAL_SET_CNF | TN_TYPE_CNF | TN_INT_ENB_CNF;
hpet.base_address.write_u64(T0_CONFIG_CAPABILITY_OFFSET, t0_config_word);
// set accumulator value
hpet.base_address.write_u64(T0_COMPARATOR_OFFSET, counter + clk_periods_per_kernel_tick);
hpet.base_address.write_u64(T0_COMPARATOR_OFFSET, counter + divisor);
// set interval
hpet.base_address.write_u64(T0_COMPARATOR_OFFSET, clk_periods_per_kernel_tick);
hpet.base_address.write_u64(T0_COMPARATOR_OFFSET, divisor);
// Enable interrupts from the HPET
{

View File

@@ -10,7 +10,14 @@ const ACCESS_LATCH: u8 = 0b00 << 4;
const ACCESS_LOHI: u8 = 0b11 << 4;
const MODE_2: u8 = 0b010 << 1;
const CHAN0_DIVISOR: u16 = 2685;
// 1 / (1.193182 MHz) = 838,095,110 femtoseconds ~= 838.095 ns
pub const PERIOD_FS: u128 = 838_095_110;
// 461 / (1.193182 MHz) = 3863618.00071 ns ~= 386 us or 2.588 kHz
pub const CHAN0_DIVISOR: u16 = 461;
// Calculated interrupt period in nanoseconds based on divisor and period
pub const RATE: u128 = (CHAN0_DIVISOR as u128 * PERIOD_FS) / 1_000_000;
pub unsafe fn init() {
COMMAND.write(SELECT_CHAN0 | ACCESS_LOHI | MODE_2);

View File

@@ -4,15 +4,13 @@ use alloc::vec::Vec;
use crate::{interrupt, interrupt_stack};
use crate::context::timeout;
use crate::device::{local_apic, ioapic, pic};
use crate::device::{local_apic, ioapic, pic, pit};
use crate::device::serial::{COM1, COM2};
use crate::ipi::{ipi, IpiKind, IpiTarget};
use crate::scheme::debug::{debug_input, debug_notify};
use crate::scheme::serio::serio_input;
use crate::{context, time};
pub const PIT_RATE: u128 = 2_250_286;
//resets to 0 in context::switch()
#[thread_local]
pub static PIT_TICKS: AtomicUsize = AtomicUsize::new(0);
@@ -140,7 +138,7 @@ interrupt_stack!(pit_stack, |_stack| {
// Saves CPU time by not sending IRQ event irq_trigger(0);
{
*time::OFFSET.lock() += PIT_RATE;
*time::OFFSET.lock() += pit::RATE;
}
eoi(0);
@@ -268,7 +266,7 @@ interrupt!(lapic_error, || {
interrupt!(calib_pit, || {
{
*time::OFFSET.lock() += PIT_RATE;
*time::OFFSET.lock() += pit::RATE;
}
eoi(0);

View File

@@ -3,14 +3,28 @@ use super::device::{hpet, pit};
pub fn counter() -> u128 {
if let Some(ref hpet) = *ACPI_TABLE.hpet.read() {
let capability = unsafe { hpet.base_address.read_u64(hpet::CAPABILITY_OFFSET) };
let period_fs = (capability >> 32) as u128;
//TODO: handle rollover?
//TODO: improve performance
// Current count
let counter = unsafe { hpet.base_address.read_u64(hpet::MAIN_COUNTER_OFFSET) };
(counter as u128 * period_fs) / 1_000_000
// Comparator holds next interrupt count
let comparator = unsafe { hpet.base_address.read_u64(hpet::T0_COMPARATOR_OFFSET) };
// Get period in femtoseconds
let capability = unsafe { hpet.base_address.read_u64(hpet::CAPABILITY_OFFSET) };
let period_fs = capability >> 32;
// Calculate divisor
let divisor = (pit::RATE as u64 * 1_000_000) / period_fs;
// Calculate last interrupt
let last_interrupt = comparator.saturating_sub(divisor);
// Calculate ticks since last interrupt
let elapsed = counter.saturating_sub(last_interrupt);
// Calculate nanoseconds since last interrupt
(elapsed as u128 * period_fs as u128) / 1_000_000
} else {
// 1.193182 MHz PIT is approximately 838.095 nanoseconds
let period_ns = 838;
let counter = unsafe { pit::read() };
counter as u128 * period_ns
// Read ticks since last interrupt
let elapsed = unsafe { pit::read() };
// Calculate nanoseconds since last interrupt
(elapsed as u128 * pit::PERIOD_FS) / 1_000_000
}
}

View File

@@ -164,7 +164,7 @@ pub unsafe fn switch() -> bool {
// Set old context as not running and update CPU time
from_context_guard.running = false;
from_context_guard.cpu_time += switch_time - from_context_guard.switch_time;
from_context_guard.cpu_time += switch_time.saturating_sub(from_context_guard.switch_time);
// Set new context as running and set switch time
to_context.running = true;