Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ andes-riscv = { version = "0.1.1" }
riscv = { version = "0.11", features = ["critical-section-single-hart"] }

embedded-hal = { version = "1.0.0" }
embassy-time-driver = { version = "0.1.0", features = [
embassy-time-driver = { version = "0.2.0", features = [
"tick-hz-1_000_000",
], optional = true }
embassy-time-queue-utils = { version = "0.1.0", optional = true }
embassy-sync = { version = "0.6.1" }
embassy-futures = { version = "0.1.1" }
embassy-hal-internal = { version = "0.2.0", default-features = false }
embassy-time = { version = "0.3.2", optional = true }
embassy-time = { version = "0.4.0", optional = true }
embassy-usb-driver = { version = "0.1.0" }
portable-atomic = "1"
critical-section = "1.1.3"

static_assertions = "1"
Expand Down Expand Up @@ -61,7 +63,7 @@ default = ["rt", "embassy", "time"]
rt = ["dep:hpm-riscv-rt", "hpm-metapac/rt"]
defmt = ["dep:defmt", "embassy-usb-driver/defmt"]
time = ["dep:embassy-time"]
embassy = ["dep:embassy-time-driver"]
embassy = ["dep:embassy-time-driver", "dep:embassy-time-queue-utils"]
usb-pin-reuse-hpm5300 = []

mcan = ["dep:mcan"]
Expand Down
11 changes: 5 additions & 6 deletions examples/hpm5300evk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,18 @@ embedded-graphics = "0.8.1"
embedded-hal = "1.0.0"
embedded-hal-async = "1.0.0"
embedded-io = "0.6.1"
embedded-hal-bus = "0.2.0"
embedded-hal-bus = "0.3.0"

embassy-time = { version = "0.3.0", features = ["tick-hz-1_000_000"] }
embassy-executor = { version = "0.6.3", features = [
embassy-time = { version = "0.4.0", features = ["tick-hz-1_000_000"] }
embassy-executor = { version = "0.7.0", features = [
"nightly",
"integrated-timers",
"arch-riscv32",
"executor-thread",
] }
embassy-embedded-hal = "0.2.0"
embassy-embedded-hal = "0.3.0"
embassy-sync = "0.6.1"

embassy-usb = { version = "0.3.0", features = [
embassy-usb = { version = "0.4.0", features = [
"defmt",
"max-handler-count-8",
"max-interface-count-8",
Expand Down
9 changes: 4 additions & 5 deletions examples/hpm6200evk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,20 @@ assign-resources = "0.4.1"
heapless = "0.8.0"

# embassy dependencies
embassy-time = { version = "0.3.0", features = ["tick-hz-1_000_000"] }
embassy-executor = { version = "0.6.3", features = [
embassy-time = { version = "0.4.0", features = ["tick-hz-1_000_000"] }
embassy-executor = { version = "0.7.0", features = [
# "nightly",
"integrated-timers",
"arch-riscv32",
"executor-thread",
] }
embassy-embedded-hal = "0.2.0"
embassy-embedded-hal = "0.3.0"
embassy-sync = "0.6.1"

# embedded-hal ecosystem
embedded-hal = "1.0.0"
embedded-hal-async = "1.0.0"
embedded-io = "0.6.1"
embedded-hal-bus = "0.2.0"
embedded-hal-bus = "0.3.0"
riscv = "0.12.1"
andes-riscv = "0.1.2"

Expand Down
5 changes: 5 additions & 0 deletions examples/hpm6300evk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ embedded-hal = "1.0.0"
panic-halt = "1.0.0"
riscv = { version = "0.11.1", features = ["critical-section-single-hart"] }
andes-riscv = "0.1.2"
embassy-executor = { version = "0.7.0", features = [
"nightly",
"arch-riscv32",
"executor-thread",
] }

[profile.release]
strip = false # symbols are not flashed to the microcontroller, so don't strip them.
Expand Down
13 changes: 6 additions & 7 deletions examples/hpm6750evkmini/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ edition = "2021"
# defmt = "0.3.8"
# defmt-rtt = "0.4.1"
# no defmt
hpm-hal = { path = "../..", features = [
hpm-hal = { path = "../../", features = [
"rt",
"embassy",
"hpm6750",
"chrono",
"time",
], no-default-features = true }
], default-features = false }

embedded-hal = "1.0.0"

Expand All @@ -29,10 +29,9 @@ embedded-hal = "1.0.0"
panic-halt = "1.0.0"
riscv = { version = "0.11.1", features = ["critical-section-single-hart"] }
riscv-semihosting = "0.1.0"
embassy-time = { version = "0.3.0", features = ["tick-hz-1_000_000"] }
embassy-executor = { version = "0.6.3", features = [
embassy-time = { version = "0.4.0", features = ["tick-hz-1_000_000"] }
embassy-executor = { version = "0.7.0", features = [
# "nightly",
"integrated-timers",
"arch-riscv32",
"executor-thread",
] }
Expand All @@ -41,8 +40,8 @@ embedded-io = "0.6.1"
futures-util = { version = "0.3.30", default-features = false }
assign-resources = "0.4.1"
embedded-graphics = "0.8.1"
embedded-hal-bus = { version = "0.2.0", features = ["async"] }
embassy-usb = { version = "0.3.0", features = [
embedded-hal-bus = { version = "0.3.0", features = ["async"] }
embassy-usb = { version = "0.4.0", features = [
"max-handler-count-8",
"max-interface-count-8",
] }
Expand Down
9 changes: 4 additions & 5 deletions examples/hpm6e00evk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,23 @@ embedded-hal = "1.0.0"
riscv = { version = "0.11.1", features = ["critical-section-single-hart"] }
heapless = "0.8.0"

embassy-time = { version = "0.3.0", features = ["tick-hz-1_000_000"] }
embassy-executor = { version = "0.6.3", features = [
embassy-time = { version = "0.4.0", features = ["tick-hz-1_000_000"] }
embassy-executor = { version = "0.7.0", features = [
# "nightly",
"integrated-timers",
"arch-riscv32",
"executor-thread",
] }
embedded-io = "0.6.1"
embedded-graphics = "0.8.1"
embassy-usb = { version = "0.3.0", features = [
embassy-usb = { version = "0.4.0", features = [
"defmt",
"max-handler-count-8",
"max-interface-count-8",
] }
futures-util = { version = "0.3", default-features = false }
tinygif = "0.0.4"
assign-resources = "0.4.1"
embedded-hal-bus = "0.2.0"
embedded-hal-bus = "0.3.0"
embassy-sync = "0.6.1"
usbd-hid = "0.8"
static_cell = "2"
Expand Down
156 changes: 61 additions & 95 deletions src/embassy/time_driver_mchtmr.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
//! Embassy time driver using machine timer(mchtmr)

use core::cell::Cell;
use core::sync::atomic::{AtomicU32, AtomicU8, Ordering};
use core::{mem, ptr};
use core::cell::{Cell, RefCell};
use core::task::Waker;

use critical_section::{CriticalSection, Mutex};
use embassy_time_driver::AlarmHandle;
use embassy_time_driver::{Driver, TICK_HZ};
use embassy_time_queue_utils::Queue;
use hpm_metapac::sysctl::vals;
use hpm_metapac::{MCHTMR, SYSCTL};
use portable_atomic::{AtomicU64, Ordering};

use crate::sysctl::ClockConfig;
use crate::{interrupt, pac};

pub const ALARM_COUNT: usize = 1;

// Alarm state structure to manage individual alarms
struct AlarmState {
timestamp: Cell<u64>,

// This is really a Option<(fn(*mut ()), *mut ())>
// but fn pointers aren't allowed in const yet
callback: Cell<*const ()>,
ctx: Cell<*mut ()>,
}

unsafe impl Send for AlarmState {}
Expand All @@ -29,26 +24,32 @@ impl AlarmState {
const fn new() -> Self {
Self {
timestamp: Cell::new(u64::MAX),
callback: Cell::new(ptr::null()),
ctx: Cell::new(ptr::null_mut()),
}
}
}

pub struct MachineTimerDriver {
alarm_count: AtomicU8,
alarms: Mutex<[AlarmState; ALARM_COUNT]>,
period: AtomicU32,
// Machine timer based time driver implementation
pub(crate) struct MachineTimerDriver {
// Total number of ticks since system start
ticks: AtomicU64,
// Number of allocated alarms
alarm: Mutex<AlarmState>,
queue: Mutex<RefCell<Queue>>,
}

// Constant initialization for alarm states
#[allow(clippy::declare_interior_mutable_const)]
const ALARM_STATE_NEW: AlarmState = AlarmState::new();

// Macro to create a static driver instance
embassy_time_driver::time_driver_impl!(static DRIVER: MachineTimerDriver = MachineTimerDriver {
period: AtomicU32::new(1), // avoid div by zero
alarm_count: AtomicU8::new(0),
alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]),
ticks: AtomicU64::new(0),
alarm: Mutex::new(ALARM_STATE_NEW),
queue: Mutex::new(RefCell::new(Queue::new()))
});

impl MachineTimerDriver {
// Initialize the machine timer driver
fn init(&'static self) {
// FIXME: The name in SDK is MCHTMR0
#[cfg(hpm67)]
Expand All @@ -62,9 +63,6 @@ impl MachineTimerDriver {
};

let cnt_per_second = crate::sysctl::clocks().get_freq(&mchtmr_cfg).0 as u64;
let cnt_per_tick = cnt_per_second / embassy_time_driver::TICK_HZ;

self.period.store(cnt_per_tick as u32, Ordering::Relaxed);

// make sure mchtmr will not be gated on "wfi"
// Design consideration: use WAIT is also useful to enter low-power mode
Expand All @@ -87,103 +85,71 @@ impl MachineTimerDriver {
MCHTMR.mtimecmp().write_value(u64::MAX - 1);
}

#[inline(always)]
// Machine timer interrupt handler
fn on_interrupt(&self) {
unsafe {
riscv::register::mie::clear_mtimer();
}

critical_section::with(|cs| {
self.trigger_alarm(cs);
// Increment global tick counter
let current_ticks = self.ticks.fetch_add(1, Ordering::Relaxed);
self.check_and_trigger_alarm(current_ticks, cs);
})
}

#[inline(always)]
fn trigger_alarm(&self, cs: CriticalSection) {
let alarm = &self.alarms.borrow(cs)[0];
alarm.timestamp.set(u64::MAX);

// Call after clearing alarm, so the callback can set another alarm.

// safety:
// - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`.
// - other than that we only store valid function pointers into alarm.callback
let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
f(alarm.ctx.get());
// Check if an alarm is due and trigger it if necessary
#[inline]
fn check_and_trigger_alarm(&self, current_time: u64, cs: CriticalSection) {
let alarm = &self.alarm.borrow(cs);
let alarm_timestamp = alarm.timestamp.get();

// Check if alarm is scheduled and due
if alarm_timestamp != u64::MAX && current_time >= alarm_timestamp {
let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(current_time);
while !self.set_alarm(cs, next) {
next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
}
}
}

fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState {
// safety: we're allowed to assume the AlarmState is created by us, and
// we never create one that's out of bounds.
unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) }
// Set alarm timestamp
fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
if self.now() >= timestamp {
// Alarm time has passed, cannot set
return false;
}
self.alarm.borrow(cs).timestamp.set(timestamp);
if self.now() >= timestamp {
self.alarm.borrow(cs).timestamp.set(u64::MAX);
return false;
}
true
}
}

impl embassy_time_driver::Driver for MachineTimerDriver {
// Implement the Driver trait for MachineTimerDriver
impl Driver for MachineTimerDriver {
// Get current system time in ticks
fn now(&self) -> u64 {
MCHTMR.mtime().read() / self.period.load(Ordering::Relaxed) as u64
self.ticks.load(Ordering::Relaxed)
}

unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
if x < ALARM_COUNT as u8 {
Some(x + 1)
} else {
None
}
});

match id {
Ok(id) => Some(AlarmHandle::new(id)),
Err(_) => None,
}
}

fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
fn schedule_wake(&self, at: u64, waker: &Waker) {
critical_section::with(|cs| {
let alarm = self.get_alarm(cs, alarm);

alarm.callback.set(callback as *const ());
alarm.ctx.set(ctx);
})
}

fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
critical_section::with(|cs| {
let _n = alarm.id();

let alarm = self.get_alarm(cs, alarm);
alarm.timestamp.set(timestamp);
let mut queue = self.queue.borrow(cs).borrow_mut();

let t = self.now();
if timestamp <= t {
// If alarm timestamp has passed the alarm will not fire.
// Disarm the alarm and return `false` to indicate that.
unsafe {
riscv::register::mie::clear_mtimer();
if queue.schedule_wake(at, waker) {
let mut next = queue.next_expiration(self.now());
while !self.set_alarm(cs, next) {
next = queue.next_expiration(self.now());
}

alarm.timestamp.set(u64::MAX);

return false;
}

let safe_timestamp = timestamp
.saturating_add(1)
.overflowing_mul(self.period.load(Ordering::Relaxed) as u64)
.0;

MCHTMR.mtimecmp().write_value(safe_timestamp);
unsafe {
riscv::register::mie::set_mtimer();
}

true
})
}
}

// Core local interrupts are handled in CORE_LOCAL
// Machine timer interrupt handler (to be implemented in your interrupt vector)
#[interrupt]
fn MachineTimer() {
DRIVER.on_interrupt();
Expand Down