-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add basic Embassy support #53
base: main
Are you sure you want to change the base?
Conversation
@qwandor I must ask to wait with a merge as I've just discovered some strange issues with this PR: a couple of fresh new GD32F130C8 are starting to reboot after a few alarm cycles on any timer/channel instance. I tried to fully erase my GD32F130G8's flash, and uploaded an example, now it's reproducing on this chip as well. Moreover, openocd is not able to program the GD32F130G8 anymore, however probe-rs is working just fine. Alas, no meaningful ideas what's going on so far, I'll keep investigation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird! Good luck. I haven't finished reviewing this yet, but some comments in the meantime...
Cargo.toml
Outdated
"dep:embassy-time", | ||
"dep:embassy-time-driver", | ||
] | ||
time-driver-tim1=["embassy"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: please format these lines with a space before and after the =
to be consistent with the rest of the file.
Cargo.toml
Outdated
"executor-thread", | ||
"integrated-timers", | ||
], optional = true } | ||
embassy-time = { version = "0.3.2", optional = true } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need this dependency? The documentation for embassy-time-driver
says "If you are writing a driver, you should depend only on this crate, not on the main embassy-time crate.".
Cargo.toml
Outdated
@@ -26,6 +27,14 @@ embedded-io = "0.6.1" | |||
gd32f1 = { version = "0.9.1", features = ["critical-section"] } | |||
nb = "1.1.0" | |||
void = { version = "1.0.2", default-features = false, optional = true } | |||
embassy-executor = { version = "0.6", features = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this needed for the implementation or just for the example? If it's only for the example then you can move it to the dev-dependencies
section below.
@qwandor Thank you for pointing those out, I've fixed the issues you mentioned. Also I realized that my MCUs are restarting due to the free watchdog timer timeout. According to the GD32F1x0 user manual, the watchdog timer is always running which makes me confused why the examples were running before without chip reset. |
#[cfg(feature = "time-driver-tim14")] | ||
const ALARM_COUNT: usize = 1; | ||
|
||
fn timer() -> &'static RegisterBlock { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function shouldn't be safe, because it can allow creating aliases to timer register blocks which may already be owned by other parts of the program.
Or better, rather than constructing the reference here, use the Timer
passed in to EmbassyTimeDriver::init
, as that way you know you have ownership of it.
callback: Cell<Option<(fn(*mut ()), *mut ())>>, | ||
} | ||
|
||
unsafe impl Send for AlarmState {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is sound then please add a safety comment explaining how AlarmState
is guaranteed to meet the safety requirements of Send
.
((period as u64) << 15) + ((counter ^ ((period & 1) << 15)) as u64) | ||
} | ||
|
||
const ALARM_STATE_NEW: AlarmState = AlarmState::new(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this constant, rather than just calling AlarmState::new()
where you need it? Having both seems like unneccesary duplication.
} | ||
|
||
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean the AlarmHandle
? This is a safe method, so you can't assume anything about what arguments it is called with. Either make this method unsafe
and document the safety requirement, or use a safe method below to panic if the handle is out of bounds rather than causing undefined behaviour.
let period = self.period.load(Ordering::Relaxed); | ||
compiler_fence(Ordering::Acquire); | ||
// Timer1 has a 32-bit counter, while other timers resolution is limited to 16 bits | ||
let counter = r.cnt().read().cnt().bits() as u32; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you use .into()
here rather than as u32
? That way it's clear that no bits are being discarded.
fn now(&self) -> u64 { | ||
let r = timer(); | ||
let period = self.period.load(Ordering::Relaxed); | ||
compiler_fence(Ordering::Acquire); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would using Ordering::Acquire
for the period load operation on the previous line have an equivalent effect?
} | ||
|
||
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { | ||
critical_section::with(|_| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this need a critical section, when it's only accessing atomics?
Resolves #49
Embassy time driver implementation is mostly a copy-pasta of the STM32 driver except it's not implementing RTC functionality required for low-power/deep-sleep scenarios in order to keep it simple (AFAIK current HAL implementation also lacks of RTC support).
This PR brings a set of new features:
embassy
- enables Embassy support in generaltime-driver-tim<1, 2, 14>
- enables Embassy time driver support for a specified general-purpose timer instanceBoth features are required to use the Embassy executor, so any of the
time-driver-tim<1, 2, 14>
features enables theembassy
feature in general.Despite that the driver uses
unsafe
access to the selected timer's registry, in order to guard an access to aTIMERx
peripheral, the driver initialization requires to move atimer<1, 2, 14>
so it couldn't be used anywhere else causing undefined behavior, ex:After that the
p.timer1
could not be moved, to create aTimer::timer1
, for example.Please take into account that Embassy support comes with a price, as a binary relying on the
embassy
feature and using theasync
executor would have additional ~2KiB added to it's size (also an extra memory would be required for the tasks arena):