Skip to content

Commit

Permalink
all platforms: update Mycelium dependencies (#343)
Browse files Browse the repository at this point in the history
This updates our dependencies on Mycelium crates to
hawkw/mycelium@ba56bb4.

This picks up the new `maitake` timer API, as well as the upstream
change hawkw/mycelium#487 to allow raw access to the IDT in the Mycelium
x86_64 HAL, which we need to do an async UART driver (see #337).

While updating everything to use the new timer API, I also made some
tweaks to our existing timer code, including implementing 32-bit timer
rollover for the D1's TIMER0 (our current timestamp source on that
platform). This was an ancient TODO item since basically forever.

Fixes #337

---------

Co-authored-by: Anatol Ulrich <[email protected]>
  • Loading branch information
hawkw and Anatol Ulrich authored Dec 27, 2024
1 parent 5f4bc9e commit 4b26795
Show file tree
Hide file tree
Showing 22 changed files with 279 additions and 158 deletions.
33 changes: 22 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 25 additions & 18 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,22 @@ default-members = [
# this isn't actually a crate
exclude = ["source/notes"]

### workspace dependencies ###

[workspace.package]
edition = "2021"
repository = "https://github.com/tosc-rs/mnemos"
homepage = "https://mnemos.dev"
license = "MIT OR Apache-2.0"

[workspace.dependencies]
cordyceps = { version = "0.3", default-features = false }
hal-core = { version = "0.1.0" }
maitake = { version = "0.1.0", default-features = false }
miette = "7.2"
mycelium-alloc = { version = "0.1.0", features = ["buddy", "bump"] }
mycelium-bitfield = { version = "0.1.5" }
mycelium-util = { version = "0.1.0" }

### profile settings ###

Expand Down Expand Up @@ -150,13 +158,12 @@ debug = "line-tables-only"
### patches ###

[patch.crates-io.maitake]
git = "https://github.com/hawkw/mycelium.git"
rev = "13d0722429ef201f38e4ea47ea22d88f3f72c10e"

[patch.crates-io.mycelium-util]
git = "https://github.com/hawkw/mycelium.git"
rev = "13d0722429ef201f38e4ea47ea22d88f3f72c10e"
git = "https://github.com/hawkw/mycelium"
rev = "ba56bb4d02f46fb59754b7b88bddc2e8ca99c1f5"

[patch.crates-io.mycelium-alloc]
git = "https://github.com/hawkw/mycelium"
rev = "ba56bb4d02f46fb59754b7b88bddc2e8ca99c1f5"
# Use the `mycelium-bitfield` crate from the Mycelium monorepo rather than
# crates.io.
# NOTE: this patch, unlike the patches for `maitake` and `mycelium-util`, (which
Expand All @@ -165,24 +172,24 @@ rev = "13d0722429ef201f38e4ea47ea22d88f3f72c10e"
# since it's already in our dependency tree as a transitive dep of `maitake` ---
# having both a Git dep and a crates.io dep seems unfortunate.
[patch.crates-io.mycelium-bitfield]
git = "https://github.com/hawkw/mycelium.git"
rev = "13d0722429ef201f38e4ea47ea22d88f3f72c10e"
git = "https://github.com/hawkw/mycelium"
rev = "ba56bb4d02f46fb59754b7b88bddc2e8ca99c1f5"

[patch.crates-io.mycelium-util]
git = "https://github.com/hawkw/mycelium"
rev = "ba56bb4d02f46fb59754b7b88bddc2e8ca99c1f5"

[patch.crates-io.cordyceps]
git = "https://github.com/hawkw/mycelium.git"
rev = "13d0722429ef201f38e4ea47ea22d88f3f72c10e"
git = "https://github.com/hawkw/mycelium"
rev = "ba56bb4d02f46fb59754b7b88bddc2e8ca99c1f5"

[patch.crates-io.hal-core]
git = "https://github.com/hawkw/mycelium.git"
rev = "13d0722429ef201f38e4ea47ea22d88f3f72c10e"
git = "https://github.com/hawkw/mycelium"
rev = "ba56bb4d02f46fb59754b7b88bddc2e8ca99c1f5"

[patch.crates-io.hal-x86_64]
git = "https://github.com/hawkw/mycelium.git"
rev = "13d0722429ef201f38e4ea47ea22d88f3f72c10e"

[patch.crates-io.mycelium-alloc]
git = "https://github.com/hawkw/mycelium.git"
rev = "13d0722429ef201f38e4ea47ea22d88f3f72c10e"
git = "https://github.com/hawkw/mycelium"
rev = "ba56bb4d02f46fb59754b7b88bddc2e8ca99c1f5"

[patch.crates-io.bbq10kbd]
git = "https://github.com/hawkw/bbq10kbd"
Expand Down
1 change: 0 additions & 1 deletion platforms/allwinner-d1/board-configs/lichee-rv.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[kernel]
max_drivers = 16
timer_granularity = { secs = 0, nanos = 333 }

[services.spawnulator]
enabled = true
Expand Down
79 changes: 79 additions & 0 deletions platforms/allwinner-d1/d1-core/src/timer.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,92 @@
use crate::plic::{Plic, Priority};
use core::sync::atomic::{AtomicU32, Ordering};
pub use d1_pac::timer::tmr_ctrl::{
TMR_CLK_PRES_A as TimerPrescaler, TMR_CLK_SRC_A as TimerSource, TMR_MODE_A as TimerMode,
};
use d1_pac::TIMER;
use kernel::maitake::time::Clock;

pub struct Timers {
pub timer0: Timer0,
pub timer1: Timer1,
}

static TIMER0_ROLLOVERS: AtomicU32 = AtomicU32::new(0);

impl Timer0 {
const INITIAL_VALUE: u32 = u32::MAX;

pub fn into_maitake_clock(mut self, plic: &Plic) -> Clock {
use d1_pac::Interrupt;
self.set_prescaler(TimerPrescaler::P8); // 24M / 8: 3.00M ticks/s
self.set_mode(TimerMode::PERIODIC);

// Clear any previous interrupt flag.
let _ = self.get_and_clear_interrupt();

// Register the interrupt handler for when the timer rolls over.
unsafe {
plic.register(Interrupt::TIMER0, Self::maitake_timer_interrupt);
plic.activate(Interrupt::TIMER0, Priority::P1).unwrap();
}

// Start the timer counting down from u32::MAX;.
self.reset();

Clock::new(core::time::Duration::from_nanos(333), || {
let timer0 = unsafe {
// Safety: we are just reading the current value and will not be
// concurrently mutating the timer.
Self::steal()
};
// Since timer 0 is counting *down*, we have to subtract its current
// value from the intial value to get an i[ncreasing timestamp for
// Maitake. As timer 0 is a 32-bit timer, this forms the lower half
// of our 64-bit timestamp.
let lo = (Self::INITIAL_VALUE - timer0.current_value()) as u64;
// The higher half of the 64-bit timestamp is the current value of
// the 32-bit timer rollover counter --- i.e. the number of times
// that timer 0 has counted down to 0.
let hi = TIMER0_ROLLOVERS.load(Ordering::Relaxed) as u64;
// Combine the two halves to form the full 64-bit timestamp.
(hi << 32) | lo
})
.named("CLOCK_D1_TIMER0")
}

fn reset(&mut self) {
self.start_counter(Self::INITIAL_VALUE);
// N.B. that we probably don't *have* to reset the IRQ_EN bit every time
// it fires, but let's make sure it's enabled just in case...
self.set_interrupt_en(true);
}

/// Handle a TIMER0 interrupt when TIMER0 is used as the maitake timer.
fn maitake_timer_interrupt() {
let mut timer0 = unsafe {
// Safety: we need to do this to be an ISR lol
Self::steal()
};

// Clear the interrupt flag
let _ = timer0.get_and_clear_interrupt();

// Increment the rollover counter
TIMER0_ROLLOVERS.fetch_add(1, Ordering::Relaxed);

// Wait for the interrupt to clear to avoid repeat interrupts
let timers = unsafe { &*TIMER::PTR };
while timers.tmr_irq_sta.read().tmr0_irq_pend().bit_is_set() {}

// RESET THE CLOCK!
timer0.reset();
}

unsafe fn steal() -> Self {
Self { _x: () }
}
}

mod sealed {
use d1_pac::{
generic::Reg,
Expand Down
36 changes: 11 additions & 25 deletions platforms/allwinner-d1/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ pub fn kernel_entry(config: mnemos_config::MnemosConfig<PlatformConfig>) -> ! {

pub struct D1 {
pub kernel: &'static Kernel,
pub timers: Timers,
pub timer1: mnemos_d1_core::timer::Timer1,
pub plic: Plic,
pub dmac: Dmac,
_uart: Uart,
Expand Down Expand Up @@ -192,10 +192,13 @@ impl D1 {
kernel_settings: KernelSettings,
service_settings: KernelServiceSettings,
) -> Self {
let timer0_clock = timers.timer0.into_maitake_clock(&plic);
let k = unsafe {
Box::into_raw(Kernel::new(kernel_settings).expect("cannot initialize kernel"))
.as_ref()
.unwrap()
Box::into_raw(
Kernel::new(kernel_settings, timer0_clock).expect("cannot initialize kernel"),
)
.as_ref()
.unwrap()
};

k.initialize_default_services(service_settings);
Expand Down Expand Up @@ -240,7 +243,7 @@ impl D1 {
kernel: k,
_uart: uart,
_spim: spim,
timers,
timer1: timers.timer1,
plic,
dmac,
i2c0_int,
Expand Down Expand Up @@ -293,7 +296,7 @@ impl D1 {
pub fn run(self) -> ! {
let Self {
kernel: k,
timers,
mut timer1,
plic,
dmac: _,
_uart,
Expand All @@ -308,19 +311,9 @@ impl D1 {
//
// In the future, we probably want to rework this to use the RTC timer for
// both purposes, as this will likely play better with sleep power usage.
let Timers {
mut timer0,
mut timer1,
} = timers;

// NOTE: if you change the timer frequency, make sure you update
// initialize_kernel() below to correct the kernel timer wheel
// granularity setting!
timer0.set_prescaler(TimerPrescaler::P8); // 24M / 8: 3.00M ticks/s
timer1.set_prescaler(TimerPrescaler::P8);
timer0.set_mode(TimerMode::PERIODIC);
timer1.set_mode(TimerMode::SINGLE_COUNTING);
let _ = timer0.get_and_clear_interrupt();
let _ = timer1.get_and_clear_interrupt();

unsafe {
Expand All @@ -344,22 +337,16 @@ impl D1 {
}
}

timer0.start_counter(0xFFFF_FFFF);

loop {
// Tick the scheduler
let start = timer0.current_value();
let tick = k.tick();

// Timer is downcounting
let elapsed = start.wrapping_sub(timer0.current_value());
let turn = k.timer().force_advance_ticks(elapsed.into());
let turn = k.timer().turn();

// If there is nothing else scheduled, and we didn't just wake something up,
// sleep for some amount of time
if turn.expired == 0 && !tick.has_remaining {
let wfi_start = timer0.current_value();

// TODO(AJM): Sometimes there is no "next" in the timer wheel, even though there should
// be. Don't take lack of timer wheel presence as the ONLY heuristic of whether we
// should just wait for SOME interrupt to occur. For now, force a max sleep of 100ms
Expand All @@ -384,8 +371,7 @@ impl D1 {
timer1.stop();

// Account for time slept
let elapsed = wfi_start.wrapping_sub(timer0.current_value());
let _turn = k.timer().force_advance_ticks(elapsed.into());
let _turn = k.timer().turn();
}
}
}
Expand Down
Loading

0 comments on commit 4b26795

Please sign in to comment.