mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-28 03:43:23 +00:00
Implement a high precision gettime based on tsc
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
ba08895fc3
commit
715072b9f3
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -804,6 +804,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"component",
|
"component",
|
||||||
"jinux-frame",
|
"jinux-frame",
|
||||||
|
"jinux-util",
|
||||||
"log",
|
"log",
|
||||||
"spin 0.9.8",
|
"spin 0.9.8",
|
||||||
]
|
]
|
||||||
@ -816,6 +817,7 @@ dependencies = [
|
|||||||
"jinux-frame",
|
"jinux-frame",
|
||||||
"jinux-rights",
|
"jinux-rights",
|
||||||
"jinux-rights-proc",
|
"jinux-rights-proc",
|
||||||
|
"ktest",
|
||||||
"pod",
|
"pod",
|
||||||
"typeflags-util",
|
"typeflags-util",
|
||||||
]
|
]
|
||||||
|
@ -1,11 +1,23 @@
|
|||||||
|
use core::sync::atomic::AtomicU64;
|
||||||
use x86::cpuid::cpuid;
|
use x86::cpuid::cpuid;
|
||||||
|
|
||||||
|
/// The frequency of tsc. The unit is Hz.
|
||||||
|
pub(crate) static TSC_FREQ: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
|
const TSC_DEADLINE_MODE_SUPPORT: u32 = 1 << 24;
|
||||||
|
|
||||||
|
/// Determine if the current system supports tsc_deadline mode.
|
||||||
|
pub fn is_tsc_deadline_mode_supported() -> bool {
|
||||||
|
let cpuid = cpuid!(1);
|
||||||
|
(cpuid.ecx & TSC_DEADLINE_MODE_SUPPORT) > 0
|
||||||
|
}
|
||||||
|
|
||||||
/// Determine TSC frequency via CPUID. If the CPU does not support calculating TSC frequency by
|
/// Determine TSC frequency via CPUID. If the CPU does not support calculating TSC frequency by
|
||||||
/// CPUID, the function will return None. The unit of the return value is KHz.
|
/// CPUID, the function will return None. The unit of the return value is KHz.
|
||||||
///
|
///
|
||||||
/// Ref: function `native_calibrate_tsc` in linux `arch/x86/kernel/tsc.c`
|
/// Ref: function `native_calibrate_tsc` in linux `arch/x86/kernel/tsc.c`
|
||||||
///
|
///
|
||||||
pub fn tsc_freq() -> Option<u32> {
|
pub fn determine_tsc_freq_via_cpuid() -> Option<u32> {
|
||||||
// Check the max cpuid supported
|
// Check the max cpuid supported
|
||||||
let cpuid = cpuid!(0);
|
let cpuid = cpuid!(0);
|
||||||
let max_cpuid = cpuid.eax;
|
let max_cpuid = cpuid.eax;
|
||||||
|
@ -12,6 +12,8 @@ pub mod qemu;
|
|||||||
pub(crate) mod tdx_guest;
|
pub(crate) mod tdx_guest;
|
||||||
pub(crate) mod timer;
|
pub(crate) mod timer;
|
||||||
|
|
||||||
|
use core::{arch::x86_64::_rdtsc, sync::atomic::Ordering};
|
||||||
|
|
||||||
use kernel::apic::ioapic;
|
use kernel::apic::ioapic;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
|
||||||
@ -50,6 +52,17 @@ pub(crate) fn interrupts_ack() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the frequency of TSC. The unit is Hz.
|
||||||
|
pub fn tsc_freq() -> u64 {
|
||||||
|
kernel::tsc::TSC_FREQ.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads the current value of the processor’s time-stamp counter (TSC).
|
||||||
|
pub fn read_tsc() -> u64 {
|
||||||
|
// Safety: It is safe to read a time-related counter.
|
||||||
|
unsafe { _rdtsc() }
|
||||||
|
}
|
||||||
|
|
||||||
fn enable_common_cpu_features() {
|
fn enable_common_cpu_features() {
|
||||||
use x86_64::registers::{control::Cr4Flags, model_specific::EferFlags, xcontrol::XCr0Flags};
|
use x86_64::registers::{control::Cr4Flags, model_specific::EferFlags, xcontrol::XCr0Flags};
|
||||||
let mut cr4 = x86_64::registers::control::Cr4::read();
|
let mut cr4 = x86_64::registers::control::Cr4::read();
|
||||||
|
@ -1,55 +1,45 @@
|
|||||||
use core::arch::x86_64::_rdtsc;
|
|
||||||
use core::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
|
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
|
use core::arch::x86_64::_rdtsc;
|
||||||
|
use core::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||||
|
|
||||||
use log::info;
|
use log::info;
|
||||||
use spin::Once;
|
use spin::Once;
|
||||||
use trapframe::TrapFrame;
|
use trapframe::TrapFrame;
|
||||||
use x86::cpuid::cpuid;
|
|
||||||
use x86::msr::{wrmsr, IA32_TSC_DEADLINE};
|
use x86::msr::{wrmsr, IA32_TSC_DEADLINE};
|
||||||
|
|
||||||
use crate::arch::kernel::apic::ioapic::IO_APIC;
|
use crate::arch::kernel::apic::ioapic::IO_APIC;
|
||||||
|
use crate::arch::kernel::tsc::is_tsc_deadline_mode_supported;
|
||||||
|
use crate::arch::x86::kernel::apic::{DivideConfig, APIC_INSTANCE};
|
||||||
|
use crate::arch::x86::kernel::tsc::{determine_tsc_freq_via_cpuid, TSC_FREQ};
|
||||||
|
use crate::config::TIMER_FREQ;
|
||||||
use crate::trap::IrqLine;
|
use crate::trap::IrqLine;
|
||||||
use crate::{
|
|
||||||
arch::x86::kernel::{
|
|
||||||
apic::{DivideConfig, APIC_INSTANCE},
|
|
||||||
tsc::tsc_freq,
|
|
||||||
},
|
|
||||||
config::TIMER_FREQ,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
if tsc_mode_support() {
|
init_tsc_freq();
|
||||||
info!("[Timer]: Enable APIC-TSC deadline mode.");
|
if is_tsc_deadline_mode_supported() {
|
||||||
tsc_mode_init();
|
info!("[Timer]: Enable APIC TSC deadline mode.");
|
||||||
|
init_tsc_mode();
|
||||||
} else {
|
} else {
|
||||||
info!("[Timer]: Enable APIC-periodic mode.");
|
info!("[Timer]: Enable APIC periodic mode.");
|
||||||
periodic_mode_init();
|
init_periodic_mode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tsc_mode_support() -> bool {
|
|
||||||
let tsc_rate = tsc_freq();
|
|
||||||
if tsc_rate.is_none() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let cpuid = cpuid!(0x1);
|
|
||||||
// bit 24
|
|
||||||
cpuid.ecx & 0x100_0000 != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) static APIC_TIMER_CALLBACK: Once<Arc<dyn Fn() + Sync + Send>> = Once::new();
|
pub(super) static APIC_TIMER_CALLBACK: Once<Arc<dyn Fn() + Sync + Send>> = Once::new();
|
||||||
|
|
||||||
fn tsc_mode_init() {
|
fn init_tsc_freq() {
|
||||||
|
let tsc_freq = determine_tsc_freq_via_cpuid()
|
||||||
|
.map_or(determine_tsc_freq_via_pit(), |freq| freq as u64 * 1000);
|
||||||
|
TSC_FREQ.store(tsc_freq, Ordering::Relaxed);
|
||||||
|
info!("TSC frequency:{:?} Hz", tsc_freq);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_tsc_mode() {
|
||||||
let mut apic_lock = APIC_INSTANCE.get().unwrap().lock();
|
let mut apic_lock = APIC_INSTANCE.get().unwrap().lock();
|
||||||
// Enable tsc deadline mode.
|
// Enable tsc deadline mode.
|
||||||
apic_lock.set_lvt_timer(super::TIMER_IRQ_NUM.load(Ordering::Relaxed) as u64 | (1 << 18));
|
apic_lock.set_lvt_timer(super::TIMER_IRQ_NUM.load(Ordering::Relaxed) as u64 | (1 << 18));
|
||||||
|
drop(apic_lock);
|
||||||
let tsc_step = {
|
let tsc_step = TSC_FREQ.load(Ordering::Relaxed) / TIMER_FREQ;
|
||||||
let tsc_rate = tsc_freq().unwrap() as u64;
|
|
||||||
info!("TSC frequency:{:?} Hz", tsc_rate * 1000);
|
|
||||||
tsc_rate * 1000 / TIMER_FREQ
|
|
||||||
};
|
|
||||||
|
|
||||||
let callback = move || unsafe {
|
let callback = move || unsafe {
|
||||||
let tsc_value = _rdtsc();
|
let tsc_value = _rdtsc();
|
||||||
@ -61,7 +51,54 @@ fn tsc_mode_init() {
|
|||||||
APIC_TIMER_CALLBACK.call_once(|| Arc::new(callback));
|
APIC_TIMER_CALLBACK.call_once(|| Arc::new(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn periodic_mode_init() {
|
/// When kernel cannot get the TSC frequency from CPUID, it can leverage
|
||||||
|
/// the PIT to calculate this frequency.
|
||||||
|
fn determine_tsc_freq_via_pit() -> u64 {
|
||||||
|
let mut irq = IrqLine::alloc_specific(super::TIMER_IRQ_NUM.load(Ordering::Relaxed)).unwrap();
|
||||||
|
irq.on_active(pit_callback);
|
||||||
|
let mut io_apic = IO_APIC.get().unwrap().get(0).unwrap().lock();
|
||||||
|
debug_assert_eq!(io_apic.interrupt_base(), 0);
|
||||||
|
io_apic.enable(2, irq.clone()).unwrap();
|
||||||
|
drop(io_apic);
|
||||||
|
|
||||||
|
super::pit::init();
|
||||||
|
|
||||||
|
x86_64::instructions::interrupts::enable();
|
||||||
|
static IS_FINISH: AtomicBool = AtomicBool::new(false);
|
||||||
|
static FREQUENCY: AtomicU64 = AtomicU64::new(0);
|
||||||
|
while !IS_FINISH.load(Ordering::Acquire) {
|
||||||
|
x86_64::instructions::hlt();
|
||||||
|
}
|
||||||
|
x86_64::instructions::interrupts::disable();
|
||||||
|
drop(irq);
|
||||||
|
return FREQUENCY.load(Ordering::Acquire);
|
||||||
|
|
||||||
|
fn pit_callback(trap_frame: &TrapFrame) {
|
||||||
|
static mut IN_TIME: u64 = 0;
|
||||||
|
static mut TSC_FIRST_COUNT: u64 = 0;
|
||||||
|
// Set a certain times of callbacks to calculate the frequency.
|
||||||
|
const CALLBACK_TIMES: u64 = TIMER_FREQ / 10;
|
||||||
|
unsafe {
|
||||||
|
if IN_TIME < CALLBACK_TIMES || IS_FINISH.load(Ordering::Acquire) {
|
||||||
|
// drop the first entry, since it may not be the time we want
|
||||||
|
if IN_TIME == 0 {
|
||||||
|
TSC_FIRST_COUNT = _rdtsc();
|
||||||
|
}
|
||||||
|
IN_TIME += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut io_apic = IO_APIC.get().unwrap().get(0).unwrap().lock();
|
||||||
|
io_apic.disable(2).unwrap();
|
||||||
|
drop(io_apic);
|
||||||
|
let tsc_count = _rdtsc();
|
||||||
|
let freq = (tsc_count - TSC_FIRST_COUNT) * (TIMER_FREQ / CALLBACK_TIMES);
|
||||||
|
FREQUENCY.store(freq, Ordering::Release);
|
||||||
|
}
|
||||||
|
IS_FINISH.store(true, Ordering::Release);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_periodic_mode() {
|
||||||
let mut apic_lock = APIC_INSTANCE.get().unwrap().lock();
|
let mut apic_lock = APIC_INSTANCE.get().unwrap().lock();
|
||||||
let mut irq = IrqLine::alloc_specific(super::TIMER_IRQ_NUM.load(Ordering::Relaxed)).unwrap();
|
let mut irq = IrqLine::alloc_specific(super::TIMER_IRQ_NUM.load(Ordering::Relaxed)).unwrap();
|
||||||
irq.on_active(init_function);
|
irq.on_active(init_function);
|
||||||
@ -108,7 +145,6 @@ fn periodic_mode_init() {
|
|||||||
apic_lock.set_timer_init_count(ticks);
|
apic_lock.set_timer_init_count(ticks);
|
||||||
apic_lock.set_lvt_timer(super::TIMER_IRQ_NUM.load(Ordering::Relaxed) as u64 | (1 << 17));
|
apic_lock.set_lvt_timer(super::TIMER_IRQ_NUM.load(Ordering::Relaxed) as u64 | (1 << 17));
|
||||||
apic_lock.set_timer_div_config(DivideConfig::Divide64);
|
apic_lock.set_timer_div_config(DivideConfig::Divide64);
|
||||||
|
|
||||||
info!(
|
info!(
|
||||||
"APIC Timer ticks count:{:x}, remain ticks: {:x},Timer Freq:{} Hz",
|
"APIC Timer ticks count:{:x}, remain ticks: {:x},Timer Freq:{} Hz",
|
||||||
ticks, remain_ticks, TIMER_FREQ
|
ticks, remain_ticks, TIMER_FREQ
|
||||||
|
@ -7,6 +7,7 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
jinux-frame = { path = "../../../framework/jinux-frame" }
|
jinux-frame = { path = "../../../framework/jinux-frame" }
|
||||||
|
jinux-util = { path = "../../libs/jinux-util" }
|
||||||
component = { path = "../../libs/comp-sys/component" }
|
component = { path = "../../libs/comp-sys/component" }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
spin = "0.9.4"
|
spin = "0.9.4"
|
||||||
|
218
services/comps/time/src/clocksource.rs
Normal file
218
services/comps/time/src/clocksource.rs
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
//! This module provides abstractions for hardware-assisted timing mechanisms, encapsulated by the `ClockSource` struct.
|
||||||
|
//! A `ClockSource` can be constructed from any counter with a stable frequency, enabling precise time measurements to be taken
|
||||||
|
//! by retrieving instances of `Instant`.
|
||||||
|
//!
|
||||||
|
//! The `ClockSource` module is a fundamental building block for timing in systems that require high precision and accuracy.
|
||||||
|
//! It can be integrated into larger systems to provide timing capabilities, or used standalone for time tracking and elapsed time measurements.
|
||||||
|
|
||||||
|
use alloc::sync::Arc;
|
||||||
|
use core::{cmp::max, ops::Add, time::Duration};
|
||||||
|
use jinux_frame::sync::SpinLock;
|
||||||
|
use jinux_util::coeff::Coeff;
|
||||||
|
|
||||||
|
use crate::NANOS_PER_SECOND;
|
||||||
|
|
||||||
|
/// `ClockSource` is an abstraction for hardware-assisted timing mechanisms.
|
||||||
|
/// A `ClockSource` can be created based on any counter that operates at a stable frequency.
|
||||||
|
/// Users are able to measure time by retrieving `Instant` from this source.
|
||||||
|
///
|
||||||
|
/// # Implementation
|
||||||
|
/// The `ClockSource` relies on obtaining the frequency of the counter and the method for reading the cycles in order to measure time.
|
||||||
|
/// The **cycles** here refer the counts of the base time counter.
|
||||||
|
/// Additionally, the `ClockSource` also holds a last recorded instant, which acts as a reference point for subsequent time retrieval.
|
||||||
|
/// To prevent numerical overflow during the calculation of `Instant`, this last recorded instant **must be periodically refreshed**.
|
||||||
|
/// The maximum interval for these updates must be determined at the time of the `ClockSource` initialization.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// Suppose we have a counter called `counter` which have the frequency `counter.freq`, and the method to read its cycles called `read_counter()`.
|
||||||
|
/// We can create a corresponding `ClockSource` and use it as follows:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// // here we set the max_delay_secs = 10
|
||||||
|
/// let max_delay_secs = 10;
|
||||||
|
/// // create a clocksource named counter_clock
|
||||||
|
/// let counter_clock = ClockSource::new(counter.freq, max_delay_secs, Arc::new(read_counter));
|
||||||
|
/// // read an instant.
|
||||||
|
/// let instant = counter_clock.read_instant();
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// If using this `ClockSource`, you must ensure its internal instant will be updated
|
||||||
|
/// at least once within a time interval of not more than `max_delay_secs.
|
||||||
|
pub struct ClockSource {
|
||||||
|
read_cycles: Arc<dyn Fn() -> u64 + Sync + Send>,
|
||||||
|
base: ClockSourceBase,
|
||||||
|
coeff: Coeff,
|
||||||
|
last_instant: SpinLock<Instant>,
|
||||||
|
last_cycles: SpinLock<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClockSource {
|
||||||
|
/// Create a new `ClockSource` instance.
|
||||||
|
/// Require basic information of based time counter, including the function for reading cycles, the frequency
|
||||||
|
/// and the maximum delay seconds to update this `ClockSource`.
|
||||||
|
/// The `ClockSource` also calculates a reliable `Coeff` based on the counter's frequency and the maximum delay seconds.
|
||||||
|
/// This `Coeff` is used to convert the number of cycles into the duration of time that has passed for those cycles.
|
||||||
|
pub fn new(
|
||||||
|
freq: u64,
|
||||||
|
max_delay_secs: u64,
|
||||||
|
read_cycles: Arc<dyn Fn() -> u64 + Sync + Send>,
|
||||||
|
) -> Self {
|
||||||
|
let base = ClockSourceBase::new(freq, max_delay_secs);
|
||||||
|
// Too big `max_delay_secs` will lead to a low resolution Coeff.
|
||||||
|
debug_assert!(max_delay_secs < 600);
|
||||||
|
let coeff = Coeff::new(NANOS_PER_SECOND as u64, freq, max_delay_secs * freq);
|
||||||
|
Self {
|
||||||
|
read_cycles,
|
||||||
|
base,
|
||||||
|
coeff,
|
||||||
|
last_instant: SpinLock::new(Instant::zero()),
|
||||||
|
last_cycles: SpinLock::new(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cycles_to_nanos(&self, cycles: u64) -> u64 {
|
||||||
|
self.coeff * cycles
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use the instant cycles to calculate the instant.
|
||||||
|
/// It first calculates the difference between the instant cycles and the last recorded cycles stored in the clocksource.
|
||||||
|
/// Then `ClockSource` will convert the passed cycles into passed time and calculate the current instant.
|
||||||
|
fn calculate_instant(&self, instant_cycles: u64) -> Instant {
|
||||||
|
let delta_nanos = {
|
||||||
|
let delta_cycles = instant_cycles - self.last_cycles();
|
||||||
|
self.cycles_to_nanos(delta_cycles)
|
||||||
|
};
|
||||||
|
let duration = Duration::from_nanos(delta_nanos);
|
||||||
|
self.last_instant() + duration
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use an input instant to update the internal instant in the `ClockSource`.
|
||||||
|
fn update_last_instant(&self, instant: &Instant) {
|
||||||
|
*self.last_instant.lock() = *instant;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use an input cycles to update the internal instant in the `ClockSource`.
|
||||||
|
fn update_last_cycles(&self, cycles: u64) {
|
||||||
|
*self.last_cycles.lock() = cycles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// read current cycles of the `ClockSource`.
|
||||||
|
pub fn read_cycles(&self) -> u64 {
|
||||||
|
(self.read_cycles)()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the last instant recorded in the `ClockSource`.
|
||||||
|
pub fn last_instant(&self) -> Instant {
|
||||||
|
return *self.last_instant.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the last cycles recorded in the `ClockSource`.
|
||||||
|
pub fn last_cycles(&self) -> u64 {
|
||||||
|
return *self.last_cycles.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the maximum delay seconds for updating of the `ClockSource`.
|
||||||
|
pub fn max_delay_secs(&self) -> u64 {
|
||||||
|
self.base.max_delay_secs()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the reference to the generated cycles coeff of the `ClockSource`.
|
||||||
|
pub fn coeff(&self) -> &Coeff {
|
||||||
|
&self.coeff
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the frequency of the counter used in the `ClockSource`.
|
||||||
|
pub fn freq(&self) -> u64 {
|
||||||
|
self.base.freq()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calibrate the recorded `Instant` to zero, and record the instant cycles.
|
||||||
|
pub(crate) fn calibrate(&self, instant_cycles: u64) {
|
||||||
|
self.update_last_cycles(instant_cycles);
|
||||||
|
self.update_last_instant(&Instant::zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the instant to update the internal instant in the `ClockSource`.
|
||||||
|
pub(crate) fn update(&self) {
|
||||||
|
let instant_cycles = self.read_cycles();
|
||||||
|
let instant = self.calculate_instant(instant_cycles);
|
||||||
|
self.update_last_cycles(instant_cycles);
|
||||||
|
self.update_last_instant(&instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read the instant corresponding to the current time.
|
||||||
|
/// When trying to read an instant from the clocksource, it will use the reading method to read instant cycles.
|
||||||
|
/// Then leverage it to calculate the instant.
|
||||||
|
pub(crate) fn read_instant(&self) -> Instant {
|
||||||
|
let instant_cycles = self.read_cycles();
|
||||||
|
self.calculate_instant(instant_cycles)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Instant` captures a specific moment, storing the duration of time
|
||||||
|
/// elapsed since a reference point (typically the system boot time).
|
||||||
|
/// The `Instant` is expressed in seconds and the fractional part is expressed in nanoseconds.
|
||||||
|
#[derive(Debug, Default, Copy, Clone)]
|
||||||
|
pub struct Instant {
|
||||||
|
secs: u64,
|
||||||
|
nanos: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instant {
|
||||||
|
pub const fn zero() -> Self {
|
||||||
|
Self { secs: 0, nanos: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(secs: u64, nanos: u32) -> Self {
|
||||||
|
Self { secs, nanos }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the seconds recorded in the Instant.
|
||||||
|
pub fn secs(&self) -> u64 {
|
||||||
|
self.secs
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the nanoseconds recorded in the Instant.
|
||||||
|
pub fn nanos(&self) -> u32 {
|
||||||
|
self.nanos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add<Duration> for Instant {
|
||||||
|
type Output = Instant;
|
||||||
|
|
||||||
|
fn add(self, other: Duration) -> Self::Output {
|
||||||
|
let mut secs = self.secs + other.as_secs();
|
||||||
|
let mut nanos = self.nanos + other.subsec_nanos();
|
||||||
|
if nanos >= NANOS_PER_SECOND {
|
||||||
|
secs += 1;
|
||||||
|
nanos -= NANOS_PER_SECOND;
|
||||||
|
}
|
||||||
|
Instant::new(secs, nanos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The basic properties of `ClockSource`.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
struct ClockSourceBase {
|
||||||
|
freq: u64,
|
||||||
|
max_delay_secs: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClockSourceBase {
|
||||||
|
fn new(freq: u64, max_delay_secs: u64) -> Self {
|
||||||
|
let max_delay_secs = max(2, max_delay_secs);
|
||||||
|
ClockSourceBase {
|
||||||
|
freq,
|
||||||
|
max_delay_secs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn max_delay_secs(&self) -> u64 {
|
||||||
|
self.max_delay_secs
|
||||||
|
}
|
||||||
|
|
||||||
|
fn freq(&self) -> u64 {
|
||||||
|
self.freq
|
||||||
|
}
|
||||||
|
}
|
@ -2,15 +2,30 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
use component::{init_component, ComponentInitError};
|
extern crate alloc;
|
||||||
use core::sync::atomic::Ordering::Relaxed;
|
|
||||||
use rtc::{get_cmos, is_updating, read, CENTURY_REGISTER};
|
|
||||||
|
|
||||||
|
use alloc::sync::Arc;
|
||||||
|
use component::{init_component, ComponentInitError};
|
||||||
|
use core::{sync::atomic::Ordering::Relaxed, time::Duration};
|
||||||
|
use jinux_frame::sync::Mutex;
|
||||||
|
use spin::Once;
|
||||||
|
|
||||||
|
use clocksource::ClockSource;
|
||||||
|
use rtc::{get_cmos, is_updating, CENTURY_REGISTER};
|
||||||
|
|
||||||
|
pub use clocksource::Instant;
|
||||||
|
|
||||||
|
mod clocksource;
|
||||||
mod rtc;
|
mod rtc;
|
||||||
|
mod tsc;
|
||||||
|
|
||||||
|
pub const NANOS_PER_SECOND: u32 = 1_000_000_000;
|
||||||
|
pub static VDSO_DATA_UPDATE: Once<Arc<dyn Fn(Instant, u64) + Sync + Send>> = Once::new();
|
||||||
|
|
||||||
#[init_component]
|
#[init_component]
|
||||||
fn time_init() -> Result<(), ComponentInitError> {
|
fn time_init() -> Result<(), ComponentInitError> {
|
||||||
rtc::init();
|
rtc::init();
|
||||||
|
tsc::init();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,6 +38,7 @@ pub struct SystemTime {
|
|||||||
pub hour: u8,
|
pub hour: u8,
|
||||||
pub minute: u8,
|
pub minute: u8,
|
||||||
pub second: u8,
|
pub second: u8,
|
||||||
|
pub nanos: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SystemTime {
|
impl SystemTime {
|
||||||
@ -35,6 +51,7 @@ impl SystemTime {
|
|||||||
hour: 0,
|
hour: 0,
|
||||||
minute: 0,
|
minute: 0,
|
||||||
second: 0,
|
second: 0,
|
||||||
|
nanos: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +105,55 @@ impl SystemTime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) static READ_TIME: Mutex<SystemTime> = Mutex::new(SystemTime::zero());
|
||||||
|
pub(crate) static START_TIME: Once<SystemTime> = Once::new();
|
||||||
|
|
||||||
/// get real time
|
/// get real time
|
||||||
pub fn get_real_time() -> SystemTime {
|
pub fn get_real_time() -> SystemTime {
|
||||||
read()
|
read()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read() -> SystemTime {
|
||||||
|
update_time();
|
||||||
|
*READ_TIME.lock()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// read year,month,day and other data
|
||||||
|
/// ref: https://wiki.osdev.org/CMOS#Reading_All_RTC_Time_and_Date_Registers
|
||||||
|
fn update_time() {
|
||||||
|
let mut last_time: SystemTime;
|
||||||
|
|
||||||
|
let mut lock = READ_TIME.lock();
|
||||||
|
|
||||||
|
lock.update_from_rtc();
|
||||||
|
|
||||||
|
last_time = *lock;
|
||||||
|
|
||||||
|
lock.update_from_rtc();
|
||||||
|
|
||||||
|
while *lock != last_time {
|
||||||
|
last_time = *lock;
|
||||||
|
lock.update_from_rtc();
|
||||||
|
}
|
||||||
|
let register_b: u8 = get_cmos(0x0B);
|
||||||
|
|
||||||
|
lock.convert_bcd_to_binary(register_b);
|
||||||
|
lock.convert_12_hour_to_24_hour(register_b);
|
||||||
|
lock.modify_year();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the `START_TIME`, which is the actual time when doing calibrate.
|
||||||
|
pub fn read_start_time() -> SystemTime {
|
||||||
|
*START_TIME.get().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the monotonic time from the tsc clocksource.
|
||||||
|
pub fn read_monotonic_time() -> Duration {
|
||||||
|
let instant = tsc::read_instant();
|
||||||
|
Duration::new(instant.secs(), instant.nanos())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the tsc clocksource.
|
||||||
|
pub fn default_clocksource() -> Arc<ClockSource> {
|
||||||
|
tsc::CLOCK.get().unwrap().clone()
|
||||||
|
}
|
||||||
|
@ -1,15 +1,9 @@
|
|||||||
use core::sync::atomic::AtomicU8;
|
use core::sync::atomic::AtomicU8;
|
||||||
use core::sync::atomic::Ordering::Relaxed;
|
use core::sync::atomic::Ordering::Relaxed;
|
||||||
|
|
||||||
use crate::SystemTime;
|
|
||||||
|
|
||||||
use jinux_frame::arch::x86::device::cmos::{get_century_register, CMOS_ADDRESS, CMOS_DATA};
|
use jinux_frame::arch::x86::device::cmos::{get_century_register, CMOS_ADDRESS, CMOS_DATA};
|
||||||
use jinux_frame::sync::Mutex;
|
|
||||||
|
|
||||||
pub(crate) static CENTURY_REGISTER: AtomicU8 = AtomicU8::new(0);
|
pub(crate) static CENTURY_REGISTER: AtomicU8 = AtomicU8::new(0);
|
||||||
|
|
||||||
static READ_TIME: Mutex<SystemTime> = Mutex::new(SystemTime::zero());
|
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
let Some(century_register) = get_century_register() else {
|
let Some(century_register) = get_century_register() else {
|
||||||
return;
|
return;
|
||||||
@ -26,34 +20,3 @@ pub fn is_updating() -> bool {
|
|||||||
CMOS_ADDRESS.write(0x0A);
|
CMOS_ADDRESS.write(0x0A);
|
||||||
CMOS_DATA.read() & 0x80 != 0
|
CMOS_DATA.read() & 0x80 != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read() -> SystemTime {
|
|
||||||
update_time();
|
|
||||||
*READ_TIME.lock()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// read year,month,day and other data
|
|
||||||
/// ref: https://wiki.osdev.org/CMOS#Reading_All_RTC_Time_and_Date_Registers
|
|
||||||
fn update_time() {
|
|
||||||
let mut last_time: SystemTime;
|
|
||||||
|
|
||||||
let mut lock = READ_TIME.lock();
|
|
||||||
|
|
||||||
lock.update_from_rtc();
|
|
||||||
|
|
||||||
last_time = *lock;
|
|
||||||
|
|
||||||
lock.update_from_rtc();
|
|
||||||
|
|
||||||
while *lock != last_time {
|
|
||||||
last_time = *lock;
|
|
||||||
|
|
||||||
lock.update_from_rtc();
|
|
||||||
}
|
|
||||||
|
|
||||||
let register_b: u8 = get_cmos(0x0B);
|
|
||||||
|
|
||||||
lock.convert_bcd_to_binary(register_b);
|
|
||||||
lock.convert_12_hour_to_24_hour(register_b);
|
|
||||||
lock.modify_year();
|
|
||||||
}
|
|
||||||
|
72
services/comps/time/src/tsc.rs
Normal file
72
services/comps/time/src/tsc.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
//! This module provide a instance of `ClockSource` based on TSC.
|
||||||
|
//!
|
||||||
|
//! Use `init` to initialize this module.
|
||||||
|
use alloc::sync::Arc;
|
||||||
|
use core::time::Duration;
|
||||||
|
use jinux_frame::arch::{read_tsc, x86::tsc_freq};
|
||||||
|
use jinux_frame::timer::Timer;
|
||||||
|
use spin::Once;
|
||||||
|
|
||||||
|
use crate::clocksource::{ClockSource, Instant};
|
||||||
|
use crate::{START_TIME, VDSO_DATA_UPDATE};
|
||||||
|
|
||||||
|
/// A instance of TSC clocksource.
|
||||||
|
pub static CLOCK: Once<Arc<ClockSource>> = Once::new();
|
||||||
|
|
||||||
|
const MAX_DELAY_SECS: u64 = 60;
|
||||||
|
|
||||||
|
/// Init tsc clocksource module.
|
||||||
|
pub(super) fn init() {
|
||||||
|
init_clock();
|
||||||
|
calibrate();
|
||||||
|
init_timer();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_clock() {
|
||||||
|
CLOCK.call_once(|| {
|
||||||
|
Arc::new(ClockSource::new(
|
||||||
|
tsc_freq(),
|
||||||
|
MAX_DELAY_SECS,
|
||||||
|
Arc::new(read_tsc),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calibrate the TSC and system time based on the RTC time.
|
||||||
|
fn calibrate() {
|
||||||
|
let clock = CLOCK.get().unwrap();
|
||||||
|
let cycles = clock.read_cycles();
|
||||||
|
clock.calibrate(cycles);
|
||||||
|
START_TIME.call_once(crate::read);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read an `Instant` of tsc clocksource.
|
||||||
|
pub(super) fn read_instant() -> Instant {
|
||||||
|
let clock = CLOCK.get().unwrap();
|
||||||
|
clock.read_instant()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_clocksource(timer: Arc<Timer>) {
|
||||||
|
let clock = CLOCK.get().unwrap();
|
||||||
|
clock.update();
|
||||||
|
|
||||||
|
// Update vdso data.
|
||||||
|
if VDSO_DATA_UPDATE.is_completed() {
|
||||||
|
VDSO_DATA_UPDATE.get().unwrap()(clock.last_instant(), clock.last_cycles());
|
||||||
|
}
|
||||||
|
// Setting the timer as `clock.max_delay_secs() - 1` is to avoid
|
||||||
|
// the actual delay time is greater than the maximum delay seconds due to the latency of execution.
|
||||||
|
timer.set(Duration::from_secs(clock.max_delay_secs() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_timer() {
|
||||||
|
let timer = Timer::new(update_clocksource).unwrap();
|
||||||
|
// The initial timer should be set as `clock.max_delay_secs() >> 1` or something much smaller than `max_delay_secs`.
|
||||||
|
// This is because the initialization of this timer occurs during system startup.
|
||||||
|
// Afterwards, the system will undergo additional initialization processes, during which time interrupts are disabled.
|
||||||
|
// This results in the actual trigger time of the timer being delayed by about 5 seconds compared to the set time.
|
||||||
|
// TODO: This is a temporary handle, and should be modified in the future.
|
||||||
|
timer.set(Duration::from_secs(
|
||||||
|
CLOCK.get().unwrap().max_delay_secs() >> 1,
|
||||||
|
));
|
||||||
|
}
|
@ -3,8 +3,10 @@ use core::time::Duration;
|
|||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
use jinux_time::read_monotonic_time;
|
||||||
|
|
||||||
mod system_time;
|
mod system_time;
|
||||||
use jinux_frame::timer::read_monotonic_milli_seconds;
|
|
||||||
pub use system_time::SystemTime;
|
pub use system_time::SystemTime;
|
||||||
|
|
||||||
pub type clockid_t = i32;
|
pub type clockid_t = i32;
|
||||||
@ -76,13 +78,8 @@ pub fn now_as_duration(clock_id: &ClockID) -> Result<Duration> {
|
|||||||
match clock_id {
|
match clock_id {
|
||||||
ClockID::CLOCK_MONOTONIC
|
ClockID::CLOCK_MONOTONIC
|
||||||
| ClockID::CLOCK_MONOTONIC_COARSE
|
| ClockID::CLOCK_MONOTONIC_COARSE
|
||||||
| ClockID::CLOCK_MONOTONIC_RAW => {
|
| ClockID::CLOCK_MONOTONIC_RAW
|
||||||
let time_ms = read_monotonic_milli_seconds();
|
| ClockID::CLOCK_BOOTTIME => Ok(read_monotonic_time()),
|
||||||
|
|
||||||
let seconds = time_ms / 1000;
|
|
||||||
let nanos = time_ms % 1000 * 1_000_000;
|
|
||||||
Ok(Duration::new(seconds, nanos as u32))
|
|
||||||
}
|
|
||||||
ClockID::CLOCK_REALTIME | ClockID::CLOCK_REALTIME_COARSE => {
|
ClockID::CLOCK_REALTIME | ClockID::CLOCK_REALTIME_COARSE => {
|
||||||
let now = SystemTime::now();
|
let now = SystemTime::now();
|
||||||
now.duration_since(&SystemTime::UNIX_EPOCH)
|
now.duration_since(&SystemTime::UNIX_EPOCH)
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
|
use jinux_time::{read_monotonic_time, read_start_time};
|
||||||
|
use time::{Date, Month, PrimitiveDateTime, Time};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use time::{Date, Month, PrimitiveDateTime, Time};
|
|
||||||
|
|
||||||
/// This struct corresponds to `SystemTime` in Rust std.
|
/// This struct corresponds to `SystemTime` in Rust std.
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
@ -25,9 +26,13 @@ impl SystemTime {
|
|||||||
|
|
||||||
/// Returns the current system time
|
/// Returns the current system time
|
||||||
pub fn now() -> Self {
|
pub fn now() -> Self {
|
||||||
let system_time = jinux_time::get_real_time();
|
let start = read_start_time();
|
||||||
|
|
||||||
// The get real time result should always be valid
|
// The get real time result should always be valid
|
||||||
convert_system_time(system_time).unwrap()
|
convert_system_time(start)
|
||||||
|
.unwrap()
|
||||||
|
.checked_add(read_monotonic_time())
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a duration to self. If the result does not exceed inner bounds return Some(t), else return None.
|
/// Add a duration to self. If the result does not exceed inner bounds return Some(t), else return None.
|
||||||
@ -73,7 +78,12 @@ fn convert_system_time(system_time: jinux_time::SystemTime) -> Result<SystemTime
|
|||||||
Ok(date) => date,
|
Ok(date) => date,
|
||||||
Err(_) => return_errno_with_message!(Errno::EINVAL, "Invalid system date"),
|
Err(_) => return_errno_with_message!(Errno::EINVAL, "Invalid system date"),
|
||||||
};
|
};
|
||||||
let time_ = match Time::from_hms(system_time.hour, system_time.minute, system_time.second) {
|
let time_ = match Time::from_hms_nano(
|
||||||
|
system_time.hour,
|
||||||
|
system_time.minute,
|
||||||
|
system_time.second,
|
||||||
|
system_time.nanos.try_into().unwrap(),
|
||||||
|
) {
|
||||||
Ok(time_) => time_,
|
Ok(time_) => time_,
|
||||||
Err(_) => return_errno_with_message!(Errno::EINVAL, "Invalid system time"),
|
Err(_) => return_errno_with_message!(Errno::EINVAL, "Invalid system time"),
|
||||||
};
|
};
|
||||||
|
@ -12,5 +12,5 @@ typeflags-util = { path = "../typeflags-util" }
|
|||||||
jinux-rights-proc = { path = "../jinux-rights-proc" }
|
jinux-rights-proc = { path = "../jinux-rights-proc" }
|
||||||
jinux-rights = { path = "../jinux-rights" }
|
jinux-rights = { path = "../jinux-rights" }
|
||||||
bitvec = { version = "1.0", default-features = false, features = ["alloc"] }
|
bitvec = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||||
|
ktest = { path = "../../../framework/libs/ktest" }
|
||||||
[features]
|
[features]
|
||||||
|
139
services/libs/jinux-util/src/coeff.rs
Normal file
139
services/libs/jinux-util/src/coeff.rs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
//! This module provides an abstraction `Coeff` to server for efficient and accurate calculation
|
||||||
|
//! of fraction multiplication.
|
||||||
|
|
||||||
|
use core::ops::Mul;
|
||||||
|
|
||||||
|
use ktest::if_cfg_ktest;
|
||||||
|
|
||||||
|
/// A `Coeff` is used to do a fraction multiplication operation with an unsigned integer.
|
||||||
|
/// It can achieve accurate and efficient calculation and avoid numeric overflow at the same time.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// Let's say we want to multiply a fraction (23456 / 56789) with the target integer `a`,
|
||||||
|
/// which will be no larger than `1_000_000_000`, we can use the following code snippets
|
||||||
|
/// to get an accurate result.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let a = input();
|
||||||
|
/// let coeff = Coeff::new(23456, 56789, 1_000_000_000);
|
||||||
|
/// let result = coeff * a;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # How it works
|
||||||
|
/// `Coeff` is used in the calculation of a fraction value multiplied by an integer.
|
||||||
|
/// Here is a simple example of such calculation:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// let result = (a / b) * c;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// In this equation, `a`, `b`, `c` and `result` are all integers. To acquire a more precise result, we will
|
||||||
|
/// generally calculate `a * c` first and then divide the multiplication result with `b`.
|
||||||
|
/// However, this simple calculation above has two complications:
|
||||||
|
/// - The calculation of `a * c` may overflow if they are too large.
|
||||||
|
/// - The division operation is much more expensive than integer multiplication, which can easily create performance bottlenecks.
|
||||||
|
///
|
||||||
|
/// `Coeff` is implemented to address these two issues. It can be used to replace the fraction in this calculation.
|
||||||
|
/// For example, a `Coeff` generated from (a / b) can modify the calculation above to ensure that:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// coeff * c ~= (a / b) * c
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// In principle, `Coeff` actually turns the multiplication and division into a combination of multiplication and bit operation.
|
||||||
|
/// When creating a `Coeff`, it needs to know the numerator and denominator of the represented fraction
|
||||||
|
/// and the max multiplier it will be multiplied by. Then, a `mult` and a `shift` will be chosen to achieve the replacement of calculation.
|
||||||
|
/// Taking the previous calculation as an example again, `coeff * c` will turn into `mult * c >> shift`, ensuring that:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// mult * c >> shift ~= (a / b) * c
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// and
|
||||||
|
///
|
||||||
|
/// `mult * c` will not result in numeric overflow (i.e., `mult * c` will stay below MAX_U64).
|
||||||
|
///
|
||||||
|
/// This is how `Coeff` achieves accuracy and efficiency at the same time.
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct Coeff {
|
||||||
|
mult: u32,
|
||||||
|
shift: u32,
|
||||||
|
max_multiplier: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Coeff {
|
||||||
|
/// Create a new coeff, which is essentially equivalent to (`numerator` / `denominator`) when being multiplied to an integer;
|
||||||
|
/// Here users should make sure the multiplied integer should not be larger than `max_multiplier`.
|
||||||
|
pub fn new(numerator: u64, denominator: u64, max_multiplier: u64) -> Self {
|
||||||
|
let mut shift_acc: u32 = 32;
|
||||||
|
// Too large `max_multiplier` will make the generated coeff imprecise
|
||||||
|
debug_assert!(max_multiplier < (1 << 40));
|
||||||
|
let mut tmp = max_multiplier >> 32;
|
||||||
|
// Counts the number of 0 in front of the `max_multiplier`.
|
||||||
|
// `shift_acc` indicates the maximum number of bits `mult` can have.
|
||||||
|
while tmp > 0 {
|
||||||
|
tmp >>= 1;
|
||||||
|
shift_acc -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try the `shift` from 32 to 0.
|
||||||
|
let mut shift = 32;
|
||||||
|
let mut mult = 0;
|
||||||
|
while shift > 0 {
|
||||||
|
mult = numerator << shift;
|
||||||
|
mult += denominator / 2;
|
||||||
|
mult /= denominator;
|
||||||
|
if (mult >> shift_acc) == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
shift -= 1;
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
mult: mult as u32,
|
||||||
|
shift,
|
||||||
|
max_multiplier,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the `mult` of the Coeff.
|
||||||
|
/// Only used for the VdsoData and will be removed in the future.
|
||||||
|
pub fn mult(&self) -> u32 {
|
||||||
|
self.mult
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the `shift` of the Coeff.
|
||||||
|
/// Only used for the VdsoData and will be removed in the future.
|
||||||
|
pub fn shift(&self) -> u32 {
|
||||||
|
self.shift
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<u64> for Coeff {
|
||||||
|
type Output = u64;
|
||||||
|
fn mul(self, rhs: u64) -> Self::Output {
|
||||||
|
debug_assert!(rhs <= self.max_multiplier);
|
||||||
|
(rhs * self.mult as u64) >> self.shift
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul<u32> for Coeff {
|
||||||
|
type Output = u32;
|
||||||
|
fn mul(self, rhs: u32) -> Self::Output {
|
||||||
|
debug_assert!(rhs as u64 <= self.max_multiplier);
|
||||||
|
((rhs as u64 * self.mult as u64) >> self.shift) as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[if_cfg_ktest]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use ktest::ktest;
|
||||||
|
#[ktest]
|
||||||
|
fn calculation() {
|
||||||
|
let coeff = Coeff::new(23456, 56789, 1_000_000_000);
|
||||||
|
assert!(coeff * 0 as u64 == 0);
|
||||||
|
assert!(coeff * 100 as u64 == 100 * 23456 / 56789);
|
||||||
|
assert!(coeff * 1_000_000_000 as u64 == 1_000_000_000 * 23456 / 56789);
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
|
pub mod coeff;
|
||||||
pub mod dup;
|
pub mod dup;
|
||||||
pub mod id_allocator;
|
pub mod id_allocator;
|
||||||
pub mod safe_ptr;
|
pub mod safe_ptr;
|
||||||
|
Reference in New Issue
Block a user