rp/pio: wrap sm rx, tx in structs and allow splitting

this *finally* allows sound implementions of bidirectional transfers
without blocking. the futures previously allowed only a single direction
to be active at any given time, and the dma transfers didn't take a
mutable reference and were thus unsound.
This commit is contained in:
pennae 2023-05-03 12:49:55 +02:00
parent 77f7830da3
commit c44c108db5
6 changed files with 204 additions and 169 deletions

View File

@ -97,13 +97,13 @@ pub(crate) unsafe fn init() {
/// Future that waits for TX-FIFO to become writable /// Future that waits for TX-FIFO to become writable
#[must_use = "futures do nothing unless you `.await` or poll them"] #[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct FifoOutFuture<'a, 'd, PIO: PioInstance, const SM: usize> { pub struct FifoOutFuture<'a, 'd, PIO: PioInstance, const SM: usize> {
sm: &'a mut PioStateMachine<'d, PIO, SM>, sm_tx: &'a mut PioStateMachineTx<'d, PIO, SM>,
value: u32, value: u32,
} }
impl<'a, 'd, PIO: PioInstance, const SM: usize> FifoOutFuture<'a, 'd, PIO, SM> { impl<'a, 'd, PIO: PioInstance, const SM: usize> FifoOutFuture<'a, 'd, PIO, SM> {
pub fn new(sm: &'a mut PioStateMachine<'d, PIO, SM>, value: u32) -> Self { pub fn new(sm: &'a mut PioStateMachineTx<'d, PIO, SM>, value: u32) -> Self {
FifoOutFuture { sm, value } FifoOutFuture { sm_tx: sm, value }
} }
} }
@ -112,7 +112,7 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoOutFuture<'a, 'd,
fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
//debug!("Poll {},{}", PIO::PIO_NO, SM); //debug!("Poll {},{}", PIO::PIO_NO, SM);
let value = self.value; let value = self.value;
if self.get_mut().sm.try_push_tx(value) { if self.get_mut().sm_tx.try_push(value) {
Poll::Ready(()) Poll::Ready(())
} else { } else {
WAKERS[PIO::PIO_NO as usize].fifo_out()[SM].register(cx.waker()); WAKERS[PIO::PIO_NO as usize].fifo_out()[SM].register(cx.waker());
@ -140,12 +140,12 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoOutFuture<'a, 'd, P
/// Future that waits for RX-FIFO to become readable /// Future that waits for RX-FIFO to become readable
#[must_use = "futures do nothing unless you `.await` or poll them"] #[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct FifoInFuture<'a, 'd, PIO: PioInstance, const SM: usize> { pub struct FifoInFuture<'a, 'd, PIO: PioInstance, const SM: usize> {
sm: &'a mut PioStateMachine<'d, PIO, SM>, sm_rx: &'a mut PioStateMachineRx<'d, PIO, SM>,
} }
impl<'a, 'd, PIO: PioInstance, const SM: usize> FifoInFuture<'a, 'd, PIO, SM> { impl<'a, 'd, PIO: PioInstance, const SM: usize> FifoInFuture<'a, 'd, PIO, SM> {
pub fn new(sm: &'a mut PioStateMachine<'d, PIO, SM>) -> Self { pub fn new(sm: &'a mut PioStateMachineRx<'d, PIO, SM>) -> Self {
FifoInFuture { sm } FifoInFuture { sm_rx: sm }
} }
} }
@ -153,7 +153,7 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoInFuture<'a, 'd,
type Output = u32; type Output = u32;
fn poll(mut self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(mut self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
//debug!("Poll {},{}", PIO::PIO_NO, SM); //debug!("Poll {},{}", PIO::PIO_NO, SM);
if let Some(v) = self.sm.try_pull_rx() { if let Some(v) = self.sm_rx.try_pull() {
Poll::Ready(v) Poll::Ready(v)
} else { } else {
WAKERS[PIO::PIO_NO as usize].fifo_in()[SM].register(cx.waker()); WAKERS[PIO::PIO_NO as usize].fifo_in()[SM].register(cx.waker());
@ -293,10 +293,163 @@ impl<'l, PIO: PioInstance> Pin<'l, PIO> {
} }
} }
pub struct PioStateMachine<'d, PIO: PioInstance, const SM: usize> { pub struct PioStateMachineRx<'d, PIO: PioInstance, const SM: usize> {
pio: PhantomData<&'d PIO>, pio: PhantomData<&'d PIO>,
} }
impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineRx<'d, PIO, SM> {
pub fn empty(&self) -> bool {
unsafe { PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0 }
}
pub fn full(&self) -> bool {
unsafe { PIO::PIO.fstat().read().rxfull() & (1u8 << SM) != 0 }
}
pub fn level(&self) -> u8 {
unsafe { (PIO::PIO.flevel().read().0 >> (SM * 8 + 4)) as u8 & 0x0f }
}
pub fn stalled(&self) -> bool {
unsafe {
let fdebug = PIO::PIO.fdebug();
let ret = fdebug.read().rxstall() & (1 << SM) != 0;
fdebug.write(|w| w.set_rxstall(1 << SM));
ret
}
}
pub fn underflowed(&self) -> bool {
unsafe {
let fdebug = PIO::PIO.fdebug();
let ret = fdebug.read().rxunder() & (1 << SM) != 0;
fdebug.write(|w| w.set_rxunder(1 << SM));
ret
}
}
pub fn pull(&mut self) -> u32 {
unsafe { PIO::PIO.rxf(SM).read() }
}
pub fn try_pull(&mut self) -> Option<u32> {
if self.empty() {
return None;
}
Some(self.pull())
}
pub fn wait_pull<'a>(&'a mut self) -> FifoInFuture<'a, 'd, PIO, SM> {
FifoInFuture::new(self)
}
pub fn dma_pull<'a, C: Channel, W: Word>(
&'a mut self,
ch: PeripheralRef<'a, C>,
data: &'a mut [W],
) -> Transfer<'a, C> {
unsafe {
let pio_no = PIO::PIO_NO;
let p = ch.regs();
p.write_addr().write_value(data.as_ptr() as u32);
p.read_addr().write_value(PIO::PIO.rxf(SM).ptr() as u32);
p.trans_count().write_value(data.len() as u32);
compiler_fence(Ordering::SeqCst);
p.ctrl_trig().write(|w| {
// Set RX DREQ for this statemachine
w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8 + 4));
w.set_data_size(W::size());
w.set_chain_to(ch.number());
w.set_incr_read(false);
w.set_incr_write(true);
w.set_en(true);
});
compiler_fence(Ordering::SeqCst);
}
Transfer::new(ch)
}
}
pub struct PioStateMachineTx<'d, PIO: PioInstance, const SM: usize> {
pio: PhantomData<&'d PIO>,
}
impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineTx<'d, PIO, SM> {
pub fn empty(&self) -> bool {
unsafe { PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0 }
}
pub fn full(&self) -> bool {
unsafe { PIO::PIO.fstat().read().txfull() & (1u8 << SM) != 0 }
}
pub fn level(&self) -> u8 {
unsafe { (PIO::PIO.flevel().read().0 >> (SM * 8)) as u8 & 0x0f }
}
pub fn stalled(&self) -> bool {
unsafe {
let fdebug = PIO::PIO.fdebug();
let ret = fdebug.read().txstall() & (1 << SM) != 0;
fdebug.write(|w| w.set_txstall(1 << SM));
ret
}
}
pub fn overflowed(&self) -> bool {
unsafe {
let fdebug = PIO::PIO.fdebug();
let ret = fdebug.read().txover() & (1 << SM) != 0;
fdebug.write(|w| w.set_txover(1 << SM));
ret
}
}
pub fn push(&mut self, v: u32) {
unsafe {
PIO::PIO.txf(SM).write_value(v);
}
}
pub fn try_push(&mut self, v: u32) -> bool {
if self.full() {
return false;
}
self.push(v);
true
}
pub fn wait_push<'a>(&'a mut self, value: u32) -> FifoOutFuture<'a, 'd, PIO, SM> {
FifoOutFuture::new(self, value)
}
pub fn dma_push<'a, C: Channel, W: Word>(&'a mut self, ch: PeripheralRef<'a, C>, data: &'a [W]) -> Transfer<'a, C> {
unsafe {
let pio_no = PIO::PIO_NO;
let p = ch.regs();
p.read_addr().write_value(data.as_ptr() as u32);
p.write_addr().write_value(PIO::PIO.txf(SM).ptr() as u32);
p.trans_count().write_value(data.len() as u32);
compiler_fence(Ordering::SeqCst);
p.ctrl_trig().write(|w| {
// Set TX DREQ for this statemachine
w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8));
w.set_data_size(W::size());
w.set_chain_to(ch.number());
w.set_incr_read(true);
w.set_incr_write(false);
w.set_en(true);
});
compiler_fence(Ordering::SeqCst);
}
Transfer::new(ch)
}
}
pub struct PioStateMachine<'d, PIO: PioInstance, const SM: usize> {
rx: PioStateMachineRx<'d, PIO, SM>,
tx: PioStateMachineTx<'d, PIO, SM>,
}
impl<'d, PIO: PioInstance, const SM: usize> Drop for PioStateMachine<'d, PIO, SM> { impl<'d, PIO: PioInstance, const SM: usize> Drop for PioStateMachine<'d, PIO, SM> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
@ -333,59 +486,6 @@ impl<'d, PIO: PioInstance + 'd, const SM: usize> PioStateMachine<'d, PIO, SM> {
unsafe { PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0 } unsafe { PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0 }
} }
pub fn is_tx_empty(&self) -> bool {
unsafe { PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0 }
}
pub fn is_tx_full(&self) -> bool {
unsafe { PIO::PIO.fstat().read().txfull() & (1u8 << SM) != 0 }
}
pub fn is_rx_empty(&self) -> bool {
unsafe { PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0 }
}
pub fn is_rx_full(&self) -> bool {
unsafe { PIO::PIO.fstat().read().rxfull() & (1u8 << SM) != 0 }
}
pub fn tx_level(&self) -> u8 {
unsafe {
let flevel = PIO::PIO.flevel().read().0;
(flevel >> (SM * 8)) as u8 & 0x0f
}
}
pub fn rx_level(&self) -> u8 {
unsafe {
let flevel = PIO::PIO.flevel().read().0;
(flevel >> (SM * 8 + 4)) as u8 & 0x0f
}
}
pub fn push_tx(&mut self, v: u32) {
unsafe {
PIO::PIO.txf(SM).write_value(v);
}
}
pub fn try_push_tx(&mut self, v: u32) -> bool {
if self.is_tx_full() {
return false;
}
self.push_tx(v);
true
}
pub fn pull_rx(&mut self) -> u32 {
unsafe { PIO::PIO.rxf(SM).read() }
}
pub fn try_pull_rx(&mut self) -> Option<u32> {
if self.is_rx_empty() {
return None;
}
Some(self.pull_rx())
}
pub fn set_clkdiv(&mut self, div_x_256: u32) { pub fn set_clkdiv(&mut self, div_x_256: u32) {
unsafe { unsafe {
Self::this_sm().clkdiv().write(|w| w.0 = div_x_256 << 8); Self::this_sm().clkdiv().write(|w| w.0 = div_x_256 << 8);
@ -671,92 +771,14 @@ impl<'d, PIO: PioInstance + 'd, const SM: usize> PioStateMachine<'d, PIO, SM> {
} }
} }
pub fn wait_push<'a>(&'a mut self, value: u32) -> FifoOutFuture<'a, 'd, PIO, SM> { pub fn rx(&mut self) -> &mut PioStateMachineRx<'d, PIO, SM> {
FifoOutFuture::new(self, value) &mut self.rx
} }
pub fn tx(&mut self) -> &mut PioStateMachineTx<'d, PIO, SM> {
pub fn wait_pull<'a>(&'a mut self) -> FifoInFuture<'a, 'd, PIO, SM> { &mut self.tx
FifoInFuture::new(self)
} }
pub fn rx_tx(&mut self) -> (&mut PioStateMachineRx<'d, PIO, SM>, &mut PioStateMachineTx<'d, PIO, SM>) {
pub fn has_tx_stalled(&self) -> bool { (&mut self.rx, &mut self.tx)
unsafe {
let fdebug = PIO::PIO.fdebug();
let ret = fdebug.read().txstall() & (1 << SM) != 0;
fdebug.write(|w| w.set_txstall(1 << SM));
ret
}
}
pub fn has_tx_overflowed(&self) -> bool {
unsafe {
let fdebug = PIO::PIO.fdebug();
let ret = fdebug.read().txover() & (1 << SM) != 0;
fdebug.write(|w| w.set_txover(1 << SM));
ret
}
}
pub fn has_rx_stalled(&self) -> bool {
unsafe {
let fdebug = PIO::PIO.fdebug();
let ret = fdebug.read().rxstall() & (1 << SM) != 0;
fdebug.write(|w| w.set_rxstall(1 << SM));
ret
}
}
pub fn has_rx_underflowed(&self) -> bool {
unsafe {
let fdebug = PIO::PIO.fdebug();
let ret = fdebug.read().rxunder() & (1 << SM) != 0;
fdebug.write(|w| w.set_rxunder(1 << SM));
ret
}
}
pub fn dma_push<'a, C: Channel, W: Word>(&'a self, ch: PeripheralRef<'a, C>, data: &'a [W]) -> Transfer<'a, C> {
unsafe {
let pio_no = PIO::PIO_NO;
let p = ch.regs();
p.read_addr().write_value(data.as_ptr() as u32);
p.write_addr().write_value(PIO::PIO.txf(SM).ptr() as u32);
p.trans_count().write_value(data.len() as u32);
compiler_fence(Ordering::SeqCst);
p.ctrl_trig().write(|w| {
// Set TX DREQ for this statemachine
w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8));
w.set_data_size(W::size());
w.set_chain_to(ch.number());
w.set_incr_read(true);
w.set_incr_write(false);
w.set_en(true);
});
compiler_fence(Ordering::SeqCst);
}
Transfer::new(ch)
}
pub fn dma_pull<'a, C: Channel, W: Word>(&'a self, ch: PeripheralRef<'a, C>, data: &'a mut [W]) -> Transfer<'a, C> {
unsafe {
let pio_no = PIO::PIO_NO;
let p = ch.regs();
p.write_addr().write_value(data.as_ptr() as u32);
p.read_addr().write_value(PIO::PIO.rxf(SM).ptr() as u32);
p.trans_count().write_value(data.len() as u32);
compiler_fence(Ordering::SeqCst);
p.ctrl_trig().write(|w| {
// Set RX DREQ for this statemachine
w.set_treq_sel(TreqSel(pio_no * 8 + SM as u8 + 4));
w.set_data_size(W::size());
w.set_chain_to(ch.number());
w.set_incr_read(false);
w.set_incr_write(true);
w.set_en(true);
});
compiler_fence(Ordering::SeqCst);
}
Transfer::new(ch)
} }
} }
@ -921,10 +943,22 @@ impl<'d, PIO: PioInstance> Pio<'d, PIO> {
irq1: PioIrq { pio: PhantomData }, irq1: PioIrq { pio: PhantomData },
irq2: PioIrq { pio: PhantomData }, irq2: PioIrq { pio: PhantomData },
irq3: PioIrq { pio: PhantomData }, irq3: PioIrq { pio: PhantomData },
sm0: PioStateMachine { pio: PhantomData }, sm0: PioStateMachine {
sm1: PioStateMachine { pio: PhantomData }, rx: PioStateMachineRx { pio: PhantomData },
sm2: PioStateMachine { pio: PhantomData }, tx: PioStateMachineTx { pio: PhantomData },
sm3: PioStateMachine { pio: PhantomData }, },
sm1: PioStateMachine {
rx: PioStateMachineRx { pio: PhantomData },
tx: PioStateMachineTx { pio: PhantomData },
},
sm2: PioStateMachine {
rx: PioStateMachineRx { pio: PhantomData },
tx: PioStateMachineTx { pio: PhantomData },
},
sm3: PioStateMachine {
rx: PioStateMachineRx { pio: PhantomData },
tx: PioStateMachineTx { pio: PhantomData },
},
} }
} }
} }

View File

@ -8,7 +8,7 @@ pub fn set_x<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM
bit_count: 32, bit_count: 32,
} }
.encode(); .encode();
sm.push_tx(value); sm.tx().push(value);
sm.exec_instr(OUT); sm.exec_instr(OUT);
} }
@ -19,7 +19,7 @@ pub fn get_x<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM
} }
.encode(); .encode();
sm.exec_instr(IN); sm.exec_instr(IN);
sm.pull_rx() sm.rx().pull()
} }
pub fn set_y<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, value: u32) { pub fn set_y<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, value: u32) {
@ -28,7 +28,7 @@ pub fn set_y<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM
bit_count: 32, bit_count: 32,
} }
.encode(); .encode();
sm.push_tx(value); sm.tx().push(value);
sm.exec_instr(OUT); sm.exec_instr(OUT);
} }
@ -40,7 +40,7 @@ pub fn get_y<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM
.encode(); .encode();
sm.exec_instr(IN); sm.exec_instr(IN);
sm.pull_rx() sm.rx().pull()
} }
pub fn set_pindir<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, data: u8) { pub fn set_pindir<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, data: u8) {
@ -67,7 +67,7 @@ pub fn set_out_pin<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<P
bit_count: 32, bit_count: 32,
} }
.encode(); .encode();
sm.push_tx(data); sm.tx().push(data);
sm.exec_instr(OUT); sm.exec_instr(OUT);
} }
pub fn set_out_pindir<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, data: u32) { pub fn set_out_pindir<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, data: u32) {
@ -76,7 +76,7 @@ pub fn set_out_pindir<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachin
bit_count: 32, bit_count: 32,
} }
.encode(); .encode();
sm.push_tx(data); sm.tx().push(data);
sm.exec_instr(OUT); sm.exec_instr(OUT);
} }

View File

@ -42,7 +42,7 @@ async fn pio_task_sm0(mut sm: PioStateMachine<'static, PIO0, 0>) {
let mut v = 0x0f0caffa; let mut v = 0x0f0caffa;
loop { loop {
sm.wait_push(v).await; sm.tx().wait_push(v).await;
v ^= 0xffff; v ^= 0xffff;
info!("Pushed {:032b} to FIFO", v); info!("Pushed {:032b} to FIFO", v);
} }
@ -70,7 +70,7 @@ fn setup_pio_task_sm1(pio: &mut PioCommon<PIO0>, sm: &mut PioStateMachine<PIO0,
async fn pio_task_sm1(mut sm: PioStateMachine<'static, PIO0, 1>) { async fn pio_task_sm1(mut sm: PioStateMachine<'static, PIO0, 1>) {
sm.set_enable(true); sm.set_enable(true);
loop { loop {
let rx = sm.wait_pull().await; let rx = sm.rx().wait_pull().await;
info!("Pulled {:032b} from FIFO", rx); info!("Pulled {:032b} from FIFO", rx);
} }
} }

View File

@ -60,9 +60,10 @@ async fn main(_spawner: Spawner) {
} }
let mut din = [0u32; 29]; let mut din = [0u32; 29];
loop { loop {
let (rx, tx) = sm.rx_tx();
join( join(
sm.dma_push(dma_out_ref.reborrow(), &dout), tx.dma_push(dma_out_ref.reborrow(), &dout),
sm.dma_pull(dma_in_ref.reborrow(), &mut din), rx.dma_pull(dma_in_ref.reborrow(), &mut din),
) )
.await; .await;
for i in 0..din.len() { for i in 0..din.len() {

View File

@ -139,14 +139,14 @@ impl<'l> HD44780<'l> {
sm0.set_enable(true); sm0.set_enable(true);
// init to 8 bit thrice // init to 8 bit thrice
sm0.push_tx((50000 << 8) | 0x30); sm0.tx().push((50000 << 8) | 0x30);
sm0.push_tx((5000 << 8) | 0x30); sm0.tx().push((5000 << 8) | 0x30);
sm0.push_tx((200 << 8) | 0x30); sm0.tx().push((200 << 8) | 0x30);
// init 4 bit // init 4 bit
sm0.push_tx((200 << 8) | 0x20); sm0.tx().push((200 << 8) | 0x20);
// set font and lines // set font and lines
sm0.push_tx((50 << 8) | 0x20); sm0.tx().push((50 << 8) | 0x20);
sm0.push_tx(0b1100_0000); sm0.tx().push(0b1100_0000);
irq0.wait().await; irq0.wait().await;
sm0.set_enable(false); sm0.set_enable(false);
@ -216,7 +216,7 @@ impl<'l> HD44780<'l> {
sm0.set_enable(true); sm0.set_enable(true);
// display on and cursor on and blinking, reset display // display on and cursor on and blinking, reset display
sm0.dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await; sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await;
Self { Self {
dma: dma.map_into(), dma: dma.map_into(),
@ -240,6 +240,6 @@ impl<'l> HD44780<'l> {
// set cursor to 1:15 // set cursor to 1:15
self.buf[38..].copy_from_slice(&[0x80, 0xcf]); self.buf[38..].copy_from_slice(&[0x80, 0xcf]);
self.sm.dma_push(self.dma.reborrow(), &self.buf).await; self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await;
} }
} }

View File

@ -87,7 +87,7 @@ impl<'d, P: PioInstance, const S: usize> Ws2812<'d, P, S> {
pub async fn write(&mut self, colors: &[RGB8]) { pub async fn write(&mut self, colors: &[RGB8]) {
for color in colors { for color in colors {
let word = (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8); let word = (u32::from(color.g) << 24) | (u32::from(color.r) << 16) | (u32::from(color.b) << 8);
self.sm.wait_push(word).await; self.sm.tx().wait_push(word).await;
} }
} }
} }