Skip to content

Commit fc5334d

Browse files
committed
Async GPIO implementation
1 parent cafe827 commit fc5334d

File tree

15 files changed

+500
-0
lines changed

15 files changed

+500
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ members = [
55
"e310x-hal",
66
"hifive1",
77
"hifive1-examples",
8+
"hifive1-async-examples",
89
]
910
default-members = [
1011
"e310x",

e310x-hal/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,15 @@ nb = "1.0.0"
1919
portable-atomic = { version = "1.9", default-features = false }
2020
riscv = { workspace = true, features = ["critical-section-single-hart"] }
2121

22+
# Async HAL dependencies
23+
riscv-rt = { workspace = true, optional = true }
24+
embedded-hal-async = { version = "1.0.0", optional = true }
25+
critical-section = { workspace = true, optional = true }
26+
2227
[features]
2328
g002 = ["e310x/g002"]
2429
v-trap = ["e310x/v-trap"]
30+
async = ["riscv-rt", "embedded-hal-async", "critical-section"]
2531

2632
[package.metadata.docs.rs]
2733
features = ["g002"]

e310x-hal/src/asynch.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//! Asynchronous HAL for the E310x family of microcontrollers
2+
//!
3+
//! This is an implementation of the [`embedded-hal-async`] traits for the E310x
4+
//! family of microcontrollers.
5+
6+
#![deny(missing_docs)]
7+
8+
pub mod digital;
9+
pub mod prelude;

e310x-hal/src/asynch/digital.rs

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
//! # Digital I/O
2+
//! # Note
3+
//!
4+
//! Implementation of the Async Embedded HAL I/O functionality.
5+
//!
6+
7+
macro_rules! gpio_async {
8+
($GPIOX:ident, [
9+
$($PXi:ident: ($pxi:ident, $i:expr, $handle:ident),)+
10+
]) => {
11+
use core::cell::RefCell;
12+
use core::task::{Poll, Waker};
13+
use core::future::poll_fn;
14+
use critical_section::Mutex;
15+
use crate::gpio::*;
16+
use crate::gpio::gpio0::*;
17+
use e310x::$GPIOX;
18+
use embedded_hal::digital::{Error, ErrorKind, ErrorType, InputPin};
19+
use embedded_hal_async::digital::Wait;
20+
21+
/// Error type for wait trait.
22+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23+
pub enum DigitalError {
24+
/// Error indicating that a wait operation was already in progress.
25+
AlreadyWaiting,
26+
/// Other errors.
27+
Other,
28+
}
29+
30+
const N_PINS: usize = 32;
31+
static PIN_WAKERS: Mutex<RefCell<[Option<Waker>; N_PINS]>> =
32+
Mutex::new(RefCell::new([const{None}; N_PINS]));
33+
34+
impl Error for DigitalError {
35+
fn kind(&self) -> ErrorKind {
36+
ErrorKind::Other
37+
}
38+
}
39+
40+
/// Interrupt handler for GPIO pins.
41+
fn on_irq(pin_n: usize) {
42+
let gpio_block = unsafe { Gpio0::steal() };
43+
let pin_mask = 1 << pin_n;
44+
45+
// Disable the interrupt for the pin
46+
unsafe{
47+
gpio_block.high_ie().modify(|r, w| w.bits(r.bits() &! pin_mask));
48+
gpio_block.low_ie().modify(|r, w| w.bits(r.bits() &! pin_mask));
49+
gpio_block.rise_ie().modify(|r, w| w.bits(r.bits() &! pin_mask));
50+
gpio_block.fall_ie().modify(|r, w| w.bits(r.bits() &! pin_mask));
51+
}
52+
53+
// Wake the pin if possible
54+
critical_section::with(|cs| {
55+
let mut pin_wakers = PIN_WAKERS.borrow_ref_mut(cs);
56+
if let Some(pinwaker) = pin_wakers[pin_n].take() {
57+
pinwaker.wake();
58+
}
59+
});
60+
61+
// Clear pending pin interrupts
62+
unsafe{
63+
gpio_block.high_ip().write(|w| w.bits(pin_mask));
64+
gpio_block.low_ip().write(|w| w.bits(pin_mask));
65+
gpio_block.rise_ip().write(|w| w.bits(pin_mask));
66+
gpio_block.fall_ip().write(|w| w.bits(pin_mask));
67+
}
68+
}
69+
70+
/// GPIO
71+
$(
72+
impl<MODE> ErrorType for $PXi<Input<MODE>> {
73+
type Error = DigitalError;
74+
}
75+
/// Wait trait implementation
76+
impl<MODE> Wait for $PXi<Input<MODE>> {
77+
#[inline]
78+
async fn wait_for_high(&mut self) -> Result<(), Self::Error> {
79+
// If the pin is already high, no need to wait.
80+
if self.is_high().unwrap() {
81+
return Ok(());
82+
}
83+
84+
// Prevent concurrent waiters.
85+
if critical_section::with(|cs| {
86+
PIN_WAKERS.borrow_ref(cs)[$i].is_some()
87+
}){
88+
return Err(DigitalError::AlreadyWaiting);
89+
}
90+
91+
// Clear previous high interrupts for the pin.
92+
self.clear_interrupt(EventType::High);
93+
94+
// Enable the high interrupt for the pin.
95+
self.enable_interrupt(EventType::High);
96+
97+
// Await until an interrupt indicates that the pin has transitioned high.
98+
poll_fn(|cx| {
99+
if !self.is_interrupt_enabled(EventType::High) {
100+
Poll::Ready(Ok(()))
101+
} else {
102+
critical_section::with(|cs| {
103+
let mut pinwaker = PIN_WAKERS.borrow_ref_mut(cs);
104+
pinwaker[$i] = Some(cx.waker().clone());
105+
});
106+
Poll::Pending
107+
}
108+
}).await
109+
}
110+
111+
#[inline]
112+
async fn wait_for_low(&mut self) -> Result<(), Self::Error> {
113+
// If the pin is already low, no need to wait.
114+
if self.is_low().unwrap() {
115+
return Ok(());
116+
}
117+
118+
// Prevent concurrent waiters.
119+
if critical_section::with(|cs| {
120+
PIN_WAKERS.borrow_ref(cs)[$i].is_some()
121+
}){
122+
return Err(DigitalError::AlreadyWaiting);
123+
}
124+
125+
// Clear previous low interrupts for the pin.
126+
self.clear_interrupt(EventType::Low);
127+
128+
// Enable the low interrupt for the pin.
129+
self.enable_interrupt(EventType::Low);
130+
131+
// Await until an interrupt indicates that the pin has transitioned high.
132+
poll_fn(|cx| {
133+
if !self.is_interrupt_enabled(EventType::Low) {
134+
Poll::Ready(Ok(()))
135+
} else {
136+
critical_section::with(|cs| {
137+
let mut pinwaker = PIN_WAKERS.borrow_ref_mut(cs);
138+
pinwaker[$i] = Some(cx.waker().clone());
139+
});
140+
Poll::Pending
141+
}
142+
}).await
143+
}
144+
145+
#[inline]
146+
async fn wait_for_rising_edge(&mut self) -> Result<(), Self::Error> {
147+
// Prevent concurrent waiters.
148+
if critical_section::with(|cs| {
149+
PIN_WAKERS.borrow_ref(cs)[$i].is_some()
150+
}){
151+
return Err(DigitalError::AlreadyWaiting);
152+
}
153+
154+
// Clear previous rising edge interrupts for the pin.
155+
self.clear_interrupt(EventType::Rise);
156+
157+
// Enable the rising edge interrupt for the pin.
158+
self.enable_interrupt(EventType::Rise);
159+
160+
// Await until an interrupt indicates that the pin has transitioned high.
161+
poll_fn(|cx| {
162+
if !self.is_interrupt_enabled(EventType::Rise) {
163+
Poll::Ready(Ok(()))
164+
} else {
165+
critical_section::with(|cs| {
166+
let mut pinwaker = PIN_WAKERS.borrow_ref_mut(cs);
167+
pinwaker[$i] = Some(cx.waker().clone());
168+
});
169+
Poll::Pending
170+
}
171+
}).await
172+
}
173+
174+
#[inline]
175+
async fn wait_for_falling_edge(&mut self) -> Result<(), Self::Error> {
176+
// Prevent concurrent waiters.
177+
if critical_section::with(|cs| {
178+
PIN_WAKERS.borrow_ref(cs)[$i].is_some()
179+
}){
180+
return Err(DigitalError::AlreadyWaiting);
181+
}
182+
183+
// Clear previous falling edge interrupts for the pin.
184+
self.clear_interrupt(EventType::Fall);
185+
186+
// Enable the falling edge interrupt for the pin.
187+
self.enable_interrupt(EventType::Fall);
188+
189+
// Await until an interrupt indicates that the pin has transitioned high.
190+
poll_fn(|cx| {
191+
if !self.is_interrupt_enabled(EventType::Fall) {
192+
Poll::Ready(Ok(()))
193+
} else {
194+
critical_section::with(|cs| {
195+
let mut pinwaker = PIN_WAKERS.borrow_ref_mut(cs);
196+
pinwaker[$i] = Some(cx.waker().clone());
197+
});
198+
Poll::Pending
199+
}
200+
}).await
201+
}
202+
203+
#[inline]
204+
async fn wait_for_any_edge(&mut self) -> Result<(), Self::Error> {
205+
// Prevent concurrent waiters.
206+
if critical_section::with(|cs| {
207+
PIN_WAKERS.borrow_ref(cs)[$i].is_some()
208+
}){
209+
return Err(DigitalError::AlreadyWaiting);
210+
}
211+
212+
// Clear previous rising and falling edge interrupts for the pin.
213+
self.clear_interrupt(EventType::BothEdges);
214+
215+
// Enable the rising and falling edge interrupts for the pin.
216+
self.enable_interrupt(EventType::BothEdges);
217+
218+
// Await until an interrupt indicates that the pin has transitioned high.
219+
poll_fn(|cx| {
220+
if !self.is_interrupt_enabled(EventType::BothEdges) {
221+
Poll::Ready(Ok(()))
222+
} else {
223+
critical_section::with(|cs| {
224+
let mut pinwaker = PIN_WAKERS.borrow_ref_mut(cs);
225+
pinwaker[$i] = Some(cx.waker().clone());
226+
});
227+
Poll::Pending
228+
}
229+
}).await
230+
}
231+
}
232+
233+
/// Pin Interrupt Handler
234+
#[riscv_rt::external_interrupt(e310x::interrupt::ExternalInterrupt::$handle)]
235+
fn $pxi() {
236+
on_irq($i);
237+
}
238+
)+
239+
}
240+
}
241+
242+
gpio_async!(Gpio0, [
243+
Pin0: (pin0, 0, GPIO0),
244+
Pin1: (pin1, 1, GPIO1),
245+
Pin2: (pin2, 2, GPIO2),
246+
Pin3: (pin3, 3, GPIO3),
247+
Pin4: (pin4, 4, GPIO4),
248+
Pin5: (pin5, 5, GPIO5),
249+
Pin6: (pin6, 6, GPIO6),
250+
Pin7: (pin7, 7, GPIO7),
251+
Pin8: (pin8, 8, GPIO8),
252+
Pin9: (pin9, 9, GPIO9),
253+
Pin10: (pin10, 10, GPIO10),
254+
Pin11: (pin11, 11, GPIO11),
255+
Pin12: (pin12, 12, GPIO12),
256+
Pin13: (pin13, 13, GPIO13),
257+
Pin14: (pin14, 14, GPIO14),
258+
Pin15: (pin15, 15, GPIO15),
259+
Pin16: (pin16, 16, GPIO16),
260+
Pin17: (pin17, 17, GPIO17),
261+
Pin18: (pin18, 18, GPIO18),
262+
Pin19: (pin19, 19, GPIO19),
263+
Pin20: (pin20, 20, GPIO20),
264+
Pin21: (pin21, 21, GPIO21),
265+
Pin22: (pin22, 22, GPIO22),
266+
Pin23: (pin23, 23, GPIO23),
267+
Pin24: (pin24, 24, GPIO24),
268+
Pin25: (pin25, 25, GPIO25),
269+
Pin26: (pin26, 26, GPIO26),
270+
Pin27: (pin27, 27, GPIO27),
271+
Pin28: (pin28, 28, GPIO28),
272+
Pin29: (pin29, 29, GPIO29),
273+
Pin30: (pin30, 30, GPIO30),
274+
Pin31: (pin31, 31, GPIO31),
275+
]);

e310x-hal/src/asynch/prelude.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
//! Prelude
2+
pub use embedded_hal_async::digital::Wait as _eha_Wait;

e310x-hal/src/gpio.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,7 @@ macro_rules! gpio {
558558
}
559559
}
560560

561+
#[cfg(not(feature = "async"))]
561562
impl<MODE> ErrorType for $PXi<Input<MODE>> {
562563
type Error = Infallible;
563564
}

e310x-hal/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,7 @@ pub mod wdog;
6262
#[cfg(feature = "g002")]
6363
pub mod i2c;
6464

65+
#[cfg(feature = "async")]
66+
pub mod asynch;
67+
6568
pub use device::DeviceResources;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[target.'cfg(all(target_arch = "riscv32", target_os = "none"))']
2+
runner = "qemu-system-riscv32 -machine sifive_e,revb=true -nographic -semihosting-config enable=on,target=native -kernel" # Uncomment for QEMU
3+
# runner = "riscv64-unknown-elf-gdb -q -x gdb_init" # Uncomment for hardware (no semihosting)
4+
# runner = "riscv64-unknown-elf-gdb -q -x gdb_init_sh" # Uncomment for hardware (semihosting)
5+
rustflags = [
6+
"-C", "link-arg=-Thifive1-link.x",
7+
"--cfg", "portable_atomic_target_feature=\"zaamo\"",
8+
]
9+
10+
[build]
11+
target = "riscv32imc-unknown-none-elf"

hifive1-async-examples/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "hifive1-async-examples"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
embassy-executor = { version = "0.7.0", features = ["arch-riscv32", "executor-thread"] } #embassy executor for async tasks
8+
hifive1 = { path = "../hifive1", version = "0.13.0", features = ["board-hifive1-revb", "async"] } # Change to your boardW
9+
riscv = { workspace = true }
10+
riscv-rt = { workspace = true, features = [] }
11+
panic-halt = "1.0.0"
12+
13+
[features]
14+
v-trap = ["hifive1/v-trap"]

0 commit comments

Comments
 (0)