diff --git a/src/arch/x86/device/hpet.rs b/src/arch/x86/device/hpet.rs index 40d7d4e..e159680 100644 --- a/src/arch/x86/device/hpet.rs +++ b/src/arch/x86/device/hpet.rs @@ -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 { diff --git a/src/arch/x86/device/pit.rs b/src/arch/x86/device/pit.rs index fdf54a1..7cd74a5 100644 --- a/src/arch/x86/device/pit.rs +++ b/src/arch/x86/device/pit.rs @@ -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); diff --git a/src/arch/x86/interrupt/irq.rs b/src/arch/x86/interrupt/irq.rs index 96845fb..79729cd 100644 --- a/src/arch/x86/interrupt/irq.rs +++ b/src/arch/x86/interrupt/irq.rs @@ -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); diff --git a/src/arch/x86/time.rs b/src/arch/x86/time.rs index ca1fa5e..5e1ab17 100644 --- a/src/arch/x86/time.rs +++ b/src/arch/x86/time.rs @@ -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 } } diff --git a/src/arch/x86_64/device/hpet.rs b/src/arch/x86_64/device/hpet.rs index 40d7d4e..e159680 100644 --- a/src/arch/x86_64/device/hpet.rs +++ b/src/arch/x86_64/device/hpet.rs @@ -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 { diff --git a/src/arch/x86_64/device/pit.rs b/src/arch/x86_64/device/pit.rs index fdf54a1..7cd74a5 100644 --- a/src/arch/x86_64/device/pit.rs +++ b/src/arch/x86_64/device/pit.rs @@ -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); diff --git a/src/arch/x86_64/interrupt/irq.rs b/src/arch/x86_64/interrupt/irq.rs index 96845fb..79729cd 100644 --- a/src/arch/x86_64/interrupt/irq.rs +++ b/src/arch/x86_64/interrupt/irq.rs @@ -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); diff --git a/src/arch/x86_64/time.rs b/src/arch/x86_64/time.rs index ca1fa5e..5e1ab17 100644 --- a/src/arch/x86_64/time.rs +++ b/src/arch/x86_64/time.rs @@ -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 } } diff --git a/src/context/switch.rs b/src/context/switch.rs index f4a3ee0..6ba12b6 100644 --- a/src/context/switch.rs +++ b/src/context/switch.rs @@ -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;