From 8ebe6e5f2029026594c703820c11d703da2c0334 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 3 May 2023 17:16:35 +0200 Subject: [PATCH 01/12] rp/pio: drop Pio prefix from almost all names it's only any good for PioPin because there it follows a pattern of gpio pin alternate functions being named like that, everything else can just as well be referred to as `pio::Thing` --- embassy-rp/src/pio.rs | 158 ++++++++++++++--------------- embassy-rp/src/pio_instr_util.rs | 20 ++-- examples/rp/src/bin/pio_async.rs | 14 +-- examples/rp/src/bin/pio_hd44780.rs | 4 +- examples/rp/src/bin/ws2812-pio.rs | 10 +- 5 files changed, 103 insertions(+), 103 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 2cf4761a..31273b5d 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -96,18 +96,18 @@ pub(crate) unsafe fn init() { /// Future that waits for TX-FIFO to become writable #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct FifoOutFuture<'a, 'd, PIO: PioInstance, const SM: usize> { - sm_tx: &'a mut PioStateMachineTx<'d, PIO, SM>, +pub struct FifoOutFuture<'a, 'd, PIO: Instance, const SM: usize> { + sm_tx: &'a mut StateMachineTx<'d, PIO, SM>, value: u32, } -impl<'a, 'd, PIO: PioInstance, const SM: usize> FifoOutFuture<'a, 'd, PIO, SM> { - pub fn new(sm: &'a mut PioStateMachineTx<'d, PIO, SM>, value: u32) -> Self { +impl<'a, 'd, PIO: Instance, const SM: usize> FifoOutFuture<'a, 'd, PIO, SM> { + pub fn new(sm: &'a mut StateMachineTx<'d, PIO, SM>, value: u32) -> Self { FifoOutFuture { sm_tx: sm, value } } } -impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoOutFuture<'a, 'd, PIO, SM> { +impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoOutFuture<'a, 'd, PIO, SM> { type Output = (); fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { //debug!("Poll {},{}", PIO::PIO_NO, SM); @@ -127,7 +127,7 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoOutFuture<'a, 'd, } } -impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoOutFuture<'a, 'd, PIO, SM> { +impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoOutFuture<'a, 'd, PIO, SM> { fn drop(&mut self) { unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| { @@ -139,17 +139,17 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoOutFuture<'a, 'd, P /// Future that waits for RX-FIFO to become readable #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct FifoInFuture<'a, 'd, PIO: PioInstance, const SM: usize> { - sm_rx: &'a mut PioStateMachineRx<'d, PIO, SM>, +pub struct FifoInFuture<'a, 'd, PIO: Instance, const SM: usize> { + sm_rx: &'a mut StateMachineRx<'d, PIO, SM>, } -impl<'a, 'd, PIO: PioInstance, const SM: usize> FifoInFuture<'a, 'd, PIO, SM> { - pub fn new(sm: &'a mut PioStateMachineRx<'d, PIO, SM>) -> Self { +impl<'a, 'd, PIO: Instance, const SM: usize> FifoInFuture<'a, 'd, PIO, SM> { + pub fn new(sm: &'a mut StateMachineRx<'d, PIO, SM>) -> Self { FifoInFuture { sm_rx: sm } } } -impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO, SM> { +impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO, SM> { type Output = u32; fn poll(mut self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { //debug!("Poll {},{}", PIO::PIO_NO, SM); @@ -168,7 +168,7 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoInFuture<'a, 'd, } } -impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoInFuture<'a, 'd, PIO, SM> { +impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoInFuture<'a, 'd, PIO, SM> { fn drop(&mut self) { unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| { @@ -180,12 +180,12 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoInFuture<'a, 'd, PI /// Future that waits for IRQ #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct IrqFuture<'a, 'd, PIO: PioInstance> { - pio: PhantomData<&'a PioIrq<'d, PIO, 0>>, +pub struct IrqFuture<'a, 'd, PIO: Instance> { + pio: PhantomData<&'a Irq<'d, PIO, 0>>, irq_no: u8, } -impl<'a, 'd, PIO: PioInstance> Future for IrqFuture<'a, 'd, PIO> { +impl<'a, 'd, PIO: Instance> Future for IrqFuture<'a, 'd, PIO> { type Output = (); fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { //debug!("Poll {},{}", PIO::PIO_NO, SM); @@ -215,7 +215,7 @@ impl<'a, 'd, PIO: PioInstance> Future for IrqFuture<'a, 'd, PIO> { } } -impl<'a, 'd, PIO: PioInstance> Drop for IrqFuture<'a, 'd, PIO> { +impl<'a, 'd, PIO: Instance> Drop for IrqFuture<'a, 'd, PIO> { fn drop(&mut self) { unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| { @@ -225,12 +225,12 @@ impl<'a, 'd, PIO: PioInstance> Drop for IrqFuture<'a, 'd, PIO> { } } -pub struct Pin<'l, PIO: PioInstance> { +pub struct Pin<'l, PIO: Instance> { pin: PeripheralRef<'l, AnyPin>, pio: PhantomData, } -impl<'l, PIO: PioInstance> Pin<'l, PIO> { +impl<'l, PIO: Instance> Pin<'l, PIO> { /// Set the pin's drive strength. #[inline] pub fn set_drive_strength(&mut self, strength: Drive) { @@ -293,11 +293,11 @@ impl<'l, PIO: PioInstance> Pin<'l, PIO> { } } -pub struct PioStateMachineRx<'d, PIO: PioInstance, const SM: usize> { +pub struct StateMachineRx<'d, PIO: Instance, const SM: usize> { pio: PhantomData<&'d PIO>, } -impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineRx<'d, PIO, SM> { +impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { pub fn empty(&self) -> bool { unsafe { PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0 } } @@ -370,11 +370,11 @@ impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineRx<'d, PIO, SM> { } } -pub struct PioStateMachineTx<'d, PIO: PioInstance, const SM: usize> { +pub struct StateMachineTx<'d, PIO: Instance, const SM: usize> { pio: PhantomData<&'d PIO>, } -impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineTx<'d, PIO, SM> { +impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { pub fn empty(&self) -> bool { unsafe { PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0 } } @@ -445,12 +445,12 @@ impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineTx<'d, PIO, SM> { } } -pub struct PioStateMachine<'d, PIO: PioInstance, const SM: usize> { - rx: PioStateMachineRx<'d, PIO, SM>, - tx: PioStateMachineTx<'d, PIO, SM>, +pub struct StateMachine<'d, PIO: Instance, const SM: usize> { + rx: StateMachineRx<'d, PIO, SM>, + tx: StateMachineTx<'d, PIO, SM>, } -impl<'d, PIO: PioInstance, const SM: usize> Drop for PioStateMachine<'d, PIO, SM> { +impl<'d, PIO: Instance, const SM: usize> Drop for StateMachine<'d, PIO, SM> { fn drop(&mut self) { unsafe { PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(1 << SM)); @@ -459,7 +459,7 @@ impl<'d, PIO: PioInstance, const SM: usize> Drop for PioStateMachine<'d, PIO, SM } } -impl<'d, PIO: PioInstance + 'd, const SM: usize> PioStateMachine<'d, PIO, SM> { +impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { #[inline(always)] fn this_sm() -> crate::pac::pio::StateMachine { PIO::PIO.sm(SM) @@ -771,35 +771,35 @@ impl<'d, PIO: PioInstance + 'd, const SM: usize> PioStateMachine<'d, PIO, SM> { } } - pub fn rx(&mut self) -> &mut PioStateMachineRx<'d, PIO, SM> { + pub fn rx(&mut self) -> &mut StateMachineRx<'d, PIO, SM> { &mut self.rx } - pub fn tx(&mut self) -> &mut PioStateMachineTx<'d, PIO, SM> { + pub fn tx(&mut self) -> &mut StateMachineTx<'d, PIO, SM> { &mut self.tx } - pub fn rx_tx(&mut self) -> (&mut PioStateMachineRx<'d, PIO, SM>, &mut PioStateMachineTx<'d, PIO, SM>) { + pub fn rx_tx(&mut self) -> (&mut StateMachineRx<'d, PIO, SM>, &mut StateMachineTx<'d, PIO, SM>) { (&mut self.rx, &mut self.tx) } } -pub struct PioCommon<'d, PIO: PioInstance> { +pub struct Common<'d, PIO: Instance> { instructions_used: u32, pio: PhantomData<&'d PIO>, } -impl<'d, PIO: PioInstance> Drop for PioCommon<'d, PIO> { +impl<'d, PIO: Instance> Drop for Common<'d, PIO> { fn drop(&mut self) { on_pio_drop::(); } } -pub struct PioInstanceMemory<'d, PIO: PioInstance> { +pub struct InstanceMemory<'d, PIO: Instance> { used_mask: u32, pio: PhantomData<&'d PIO>, } -impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { - pub fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory<'d, PIO> +impl<'d, PIO: Instance> Common<'d, PIO> { + pub fn write_instr(&mut self, start: usize, instrs: I) -> InstanceMemory<'d, PIO> where I: Iterator, { @@ -820,16 +820,16 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { used_mask |= mask; } self.instructions_used |= used_mask; - PioInstanceMemory { + InstanceMemory { used_mask, pio: PhantomData, } } - /// Free instruction memory previously allocated with [`PioCommon::write_instr`]. + /// Free instruction memory previously allocated with [`Common::write_instr`]. /// This is always possible but unsafe if any state machine is still using this /// bit of memory. - pub unsafe fn free_instr(&mut self, instrs: PioInstanceMemory) { + pub unsafe fn free_instr(&mut self, instrs: InstanceMemory) { self.instructions_used &= !instrs.used_mask; } @@ -848,8 +848,8 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { } /// Register a pin for PIO usage. Pins will be released from the PIO block - /// (i.e., have their `FUNCSEL` reset to `NULL`) when the [`PioCommon`] *and* - /// all [`PioStateMachine`]s for this block have been dropped. **Other members + /// (i.e., have their `FUNCSEL` reset to `NULL`) when the [`Common`] *and* + /// all [`StateMachine`]s for this block have been dropped. **Other members /// of [`Pio`] do not keep pin registrations alive.** pub fn make_pio_pin(&mut self, pin: impl Peripheral

+ 'd) -> Pin<'d, PIO> { into_ref!(pin); @@ -865,11 +865,11 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { } } -pub struct PioIrq<'d, PIO: PioInstance, const N: usize> { +pub struct Irq<'d, PIO: Instance, const N: usize> { pio: PhantomData<&'d PIO>, } -impl<'d, PIO: PioInstance, const N: usize> PioIrq<'d, PIO, N> { +impl<'d, PIO: Instance, const N: usize> Irq<'d, PIO, N> { pub fn wait<'a>(&'a mut self) -> IrqFuture<'a, 'd, PIO> { IrqFuture { pio: PhantomData, @@ -879,11 +879,11 @@ impl<'d, PIO: PioInstance, const N: usize> PioIrq<'d, PIO, N> { } #[derive(Clone)] -pub struct PioIrqFlags<'d, PIO: PioInstance> { +pub struct IrqFlags<'d, PIO: Instance> { pio: PhantomData<&'d PIO>, } -impl<'d, PIO: PioInstance> PioIrqFlags<'d, PIO> { +impl<'d, PIO: Instance> IrqFlags<'d, PIO> { pub fn check(&self, irq_no: u8) -> bool { assert!(irq_no < 8); self.check_any(1 << irq_no) @@ -916,48 +916,48 @@ impl<'d, PIO: PioInstance> PioIrqFlags<'d, PIO> { } } -pub struct Pio<'d, PIO: PioInstance> { - pub common: PioCommon<'d, PIO>, - pub irq_flags: PioIrqFlags<'d, PIO>, - pub irq0: PioIrq<'d, PIO, 0>, - pub irq1: PioIrq<'d, PIO, 1>, - pub irq2: PioIrq<'d, PIO, 2>, - pub irq3: PioIrq<'d, PIO, 3>, - pub sm0: PioStateMachine<'d, PIO, 0>, - pub sm1: PioStateMachine<'d, PIO, 1>, - pub sm2: PioStateMachine<'d, PIO, 2>, - pub sm3: PioStateMachine<'d, PIO, 3>, +pub struct Pio<'d, PIO: Instance> { + pub common: Common<'d, PIO>, + pub irq_flags: IrqFlags<'d, PIO>, + pub irq0: Irq<'d, PIO, 0>, + pub irq1: Irq<'d, PIO, 1>, + pub irq2: Irq<'d, PIO, 2>, + pub irq3: Irq<'d, PIO, 3>, + pub sm0: StateMachine<'d, PIO, 0>, + pub sm1: StateMachine<'d, PIO, 1>, + pub sm2: StateMachine<'d, PIO, 2>, + pub sm3: StateMachine<'d, PIO, 3>, } -impl<'d, PIO: PioInstance> Pio<'d, PIO> { +impl<'d, PIO: Instance> Pio<'d, PIO> { pub fn new(_pio: impl Peripheral

+ 'd) -> Self { PIO::state().users.store(5, Ordering::Release); PIO::state().used_pins.store(0, Ordering::Release); Self { - common: PioCommon { + common: Common { instructions_used: 0, pio: PhantomData, }, - irq_flags: PioIrqFlags { pio: PhantomData }, - irq0: PioIrq { pio: PhantomData }, - irq1: PioIrq { pio: PhantomData }, - irq2: PioIrq { pio: PhantomData }, - irq3: PioIrq { pio: PhantomData }, - sm0: PioStateMachine { - rx: PioStateMachineRx { pio: PhantomData }, - tx: PioStateMachineTx { pio: PhantomData }, + irq_flags: IrqFlags { pio: PhantomData }, + irq0: Irq { pio: PhantomData }, + irq1: Irq { pio: PhantomData }, + irq2: Irq { pio: PhantomData }, + irq3: Irq { pio: PhantomData }, + sm0: StateMachine { + rx: StateMachineRx { pio: PhantomData }, + tx: StateMachineTx { pio: PhantomData }, }, - sm1: PioStateMachine { - rx: PioStateMachineRx { pio: PhantomData }, - tx: PioStateMachineTx { pio: PhantomData }, + sm1: StateMachine { + rx: StateMachineRx { pio: PhantomData }, + tx: StateMachineTx { pio: PhantomData }, }, - sm2: PioStateMachine { - rx: PioStateMachineRx { pio: PhantomData }, - tx: PioStateMachineTx { pio: PhantomData }, + sm2: StateMachine { + rx: StateMachineRx { pio: PhantomData }, + tx: StateMachineTx { pio: PhantomData }, }, - sm3: PioStateMachine { - rx: PioStateMachineRx { pio: PhantomData }, - tx: PioStateMachineTx { pio: PhantomData }, + sm3: StateMachine { + rx: StateMachineRx { pio: PhantomData }, + tx: StateMachineTx { pio: PhantomData }, }, } } @@ -974,7 +974,7 @@ pub struct State { used_pins: AtomicU32, } -fn on_pio_drop() { +fn on_pio_drop() { let state = PIO::state(); if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { let used_pins = state.used_pins.load(Ordering::Relaxed); @@ -994,7 +994,7 @@ mod sealed { pub trait PioPin {} - pub trait PioInstance { + pub trait Instance { const PIO_NO: u8; const PIO: &'static crate::pac::pio::Pio; const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel; @@ -1011,16 +1011,16 @@ mod sealed { } } -pub trait PioInstance: sealed::PioInstance + Sized + Unpin {} +pub trait Instance: sealed::Instance + Sized + Unpin {} macro_rules! impl_pio { ($name:ident, $pio:expr, $pac:ident, $funcsel:ident) => { - impl sealed::PioInstance for peripherals::$name { + impl sealed::Instance for peripherals::$name { const PIO_NO: u8 = $pio; const PIO: &'static pac::pio::Pio = &pac::$pac; const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel; } - impl PioInstance for peripherals::$name {} + impl Instance for peripherals::$name {} }; } diff --git a/embassy-rp/src/pio_instr_util.rs b/embassy-rp/src/pio_instr_util.rs index 81abdb66..e425cf09 100644 --- a/embassy-rp/src/pio_instr_util.rs +++ b/embassy-rp/src/pio_instr_util.rs @@ -1,8 +1,8 @@ use pio::{InSource, InstructionOperands, JmpCondition, OutDestination, SetDestination}; -use crate::pio::{PioInstance, PioStateMachine}; +use crate::pio::{Instance, StateMachine}; -pub fn set_x(sm: &mut PioStateMachine, value: u32) { +pub fn set_x(sm: &mut StateMachine, value: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::X, bit_count: 32, @@ -12,7 +12,7 @@ pub fn set_x(sm: &mut PioStateMachine(sm: &mut PioStateMachine) -> u32 { +pub fn get_x(sm: &mut StateMachine) -> u32 { const IN: u16 = InstructionOperands::IN { source: InSource::X, bit_count: 32, @@ -22,7 +22,7 @@ pub fn get_x(sm: &mut PioStateMachine(sm: &mut PioStateMachine, value: u32) { +pub fn set_y(sm: &mut StateMachine, value: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::Y, bit_count: 32, @@ -32,7 +32,7 @@ pub fn set_y(sm: &mut PioStateMachine(sm: &mut PioStateMachine) -> u32 { +pub fn get_y(sm: &mut StateMachine) -> u32 { const IN: u16 = InstructionOperands::IN { source: InSource::Y, bit_count: 32, @@ -43,7 +43,7 @@ pub fn get_y(sm: &mut PioStateMachine(sm: &mut PioStateMachine, data: u8) { +pub fn set_pindir(sm: &mut StateMachine, data: u8) { let set: u16 = InstructionOperands::SET { destination: SetDestination::PINDIRS, data, @@ -52,7 +52,7 @@ pub fn set_pindir(sm: &mut PioStateMachine(sm: &mut PioStateMachine, data: u8) { +pub fn set_pin(sm: &mut StateMachine, data: u8) { let set: u16 = InstructionOperands::SET { destination: SetDestination::PINS, data, @@ -61,7 +61,7 @@ pub fn set_pin(sm: &mut PioStateMachine(sm: &mut PioStateMachine, data: u32) { +pub fn set_out_pin(sm: &mut StateMachine, data: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::PINS, bit_count: 32, @@ -70,7 +70,7 @@ pub fn set_out_pin(sm: &mut PioStateMachine

(sm: &mut PioStateMachine, data: u32) { +pub fn set_out_pindir(sm: &mut StateMachine, data: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::PINDIRS, bit_count: 32, @@ -80,7 +80,7 @@ pub fn set_out_pindir(sm: &mut PioStateMachin sm.exec_instr(OUT); } -pub fn exec_jmp(sm: &mut PioStateMachine, to_addr: u8) { +pub fn exec_jmp(sm: &mut StateMachine, to_addr: u8) { let jmp: u16 = InstructionOperands::JMP { address: to_addr, condition: JmpCondition::Always, diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 4e0ab5e3..461ea3ff 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -4,12 +4,12 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Pio, PioCommon, PioIrq, PioPin, PioStateMachine, ShiftDirection}; +use embassy_rp::pio::{Common, Irq, Pio, PioPin, ShiftDirection, StateMachine}; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; -fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachine, pin: impl PioPin) { +fn setup_pio_task_sm0(pio: &mut Common, sm: &mut StateMachine, pin: impl PioPin) { // Setup sm0 // Send data serially to pin @@ -37,7 +37,7 @@ fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachine) { +async fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) { sm.set_enable(true); let mut v = 0x0f0caffa; @@ -48,7 +48,7 @@ async fn pio_task_sm0(mut sm: PioStateMachine<'static, PIO0, 0>) { } } -fn setup_pio_task_sm1(pio: &mut PioCommon, sm: &mut PioStateMachine) { +fn setup_pio_task_sm1(pio: &mut Common, sm: &mut StateMachine) { // Setupm sm1 // Read 0b10101 repeatedly until ISR is full @@ -67,7 +67,7 @@ fn setup_pio_task_sm1(pio: &mut PioCommon, sm: &mut PioStateMachine) { +async fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) { sm.set_enable(true); loop { let rx = sm.rx().wait_pull().await; @@ -75,7 +75,7 @@ async fn pio_task_sm1(mut sm: PioStateMachine<'static, PIO0, 1>) { } } -fn setup_pio_task_sm2(pio: &mut PioCommon, sm: &mut PioStateMachine) { +fn setup_pio_task_sm2(pio: &mut Common, sm: &mut StateMachine) { // Setup sm2 // Repeatedly trigger IRQ 3 @@ -99,7 +99,7 @@ fn setup_pio_task_sm2(pio: &mut PioCommon, sm: &mut PioStateMachine, mut sm: PioStateMachine<'static, PIO0, 2>) { +async fn pio_task_sm2(mut irq: Irq<'static, PIO0, 3>, mut sm: StateMachine<'static, PIO0, 2>) { sm.set_enable(true); loop { irq.wait().await; diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index f76d334e..17b2440c 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -7,7 +7,7 @@ use core::fmt::Write; use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{FifoJoin, Pio, PioPin, PioStateMachine, ShiftDirection}; +use embassy_rp::pio::{FifoJoin, Pio, PioPin, ShiftDirection, StateMachine}; use embassy_rp::pwm::{Config, Pwm}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{into_ref, Peripheral, PeripheralRef}; @@ -64,7 +64,7 @@ async fn main(_spawner: Spawner) { pub struct HD44780<'l> { dma: PeripheralRef<'l, AnyChannel>, - sm: PioStateMachine<'l, PIO0, 0>, + sm: StateMachine<'l, PIO0, 0>, buf: [u8; 40], } diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index c9c701a7..2e6860d8 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -4,18 +4,18 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::pio::{FifoJoin, Pio, PioCommon, PioInstance, PioPin, PioStateMachine, ShiftDirection}; +use embassy_rp::pio::{Common, FifoJoin, Instance, Pio, PioPin, ShiftDirection, StateMachine}; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use embassy_time::{Duration, Timer}; use smart_leds::RGB8; use {defmt_rtt as _, panic_probe as _}; -pub struct Ws2812<'d, P: PioInstance, const S: usize> { - sm: PioStateMachine<'d, P, S>, +pub struct Ws2812<'d, P: Instance, const S: usize> { + sm: StateMachine<'d, P, S>, } -impl<'d, P: PioInstance, const S: usize> Ws2812<'d, P, S> { - pub fn new(mut pio: PioCommon<'d, P>, mut sm: PioStateMachine<'d, P, S>, pin: impl PioPin) -> Self { +impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { + pub fn new(mut pio: Common<'d, P>, mut sm: StateMachine<'d, P, S>, pin: impl PioPin) -> Self { // Setup sm0 // prepare the PIO program From 09f078a1cc33781a01012ebc43e9d20cff53e2a3 Mon Sep 17 00:00:00 2001 From: pennae Date: Thu, 4 May 2023 10:34:20 +0200 Subject: [PATCH 02/12] rp/pio: remove critical section in IrqFuture::poll there's nothing this critical section protects against. both read and write-to-clear are atomic and don't interfere with other irq futures, only potentially with setting/clearing an irq flag from an arm core. neither have ever been synchronized, and both have the same observable effects under atomic writes and critical sections. (for both setting and clearing an irq flag observable differences could only happen if the set/clear happened after the poll read, but before the write. if it's a clear we observe the same effects as sequencing the clear entirely after the poll, and if it's a set we observe the same effects as sequencing the set entirely before the poll) --- embassy-rp/src/pio.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 31273b5d..c2c144c0 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -191,17 +191,10 @@ impl<'a, 'd, PIO: Instance> Future for IrqFuture<'a, 'd, PIO> { //debug!("Poll {},{}", PIO::PIO_NO, SM); // Check if IRQ flag is already set - if critical_section::with(|_| unsafe { - let irq_flags = PIO::PIO.irq(); - if irq_flags.read().0 & (1 << self.irq_no) != 0 { - irq_flags.write(|m| { - m.0 = 1 << self.irq_no; - }); - true - } else { - false + if unsafe { PIO::PIO.irq().read().0 & (1 << self.irq_no) != 0 } { + unsafe { + PIO::PIO.irq().write(|m| m.0 = 1 << self.irq_no); } - }) { return Poll::Ready(()); } From ed843b519b1cfdcc69f9022e3e14fbb9f62972bb Mon Sep 17 00:00:00 2001 From: pennae Date: Thu, 4 May 2023 11:14:21 +0200 Subject: [PATCH 03/12] rp/pio: tighten variance of particle structs all of these exist in 1:1 correspondence to their parent hal objects, so let's make all of their lifetimes invariant. --- embassy-rp/src/pio.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index c2c144c0..d2eedc72 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -181,7 +181,7 @@ impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoInFuture<'a, 'd, PIO, /// Future that waits for IRQ #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct IrqFuture<'a, 'd, PIO: Instance> { - pio: PhantomData<&'a Irq<'d, PIO, 0>>, + pio: PhantomData<&'a mut Irq<'d, PIO, 0>>, irq_no: u8, } @@ -287,7 +287,7 @@ impl<'l, PIO: Instance> Pin<'l, PIO> { } pub struct StateMachineRx<'d, PIO: Instance, const SM: usize> { - pio: PhantomData<&'d PIO>, + pio: PhantomData<&'d mut PIO>, } impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { @@ -364,7 +364,7 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { } pub struct StateMachineTx<'d, PIO: Instance, const SM: usize> { - pio: PhantomData<&'d PIO>, + pio: PhantomData<&'d mut PIO>, } impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { @@ -777,7 +777,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { pub struct Common<'d, PIO: Instance> { instructions_used: u32, - pio: PhantomData<&'d PIO>, + pio: PhantomData<&'d mut PIO>, } impl<'d, PIO: Instance> Drop for Common<'d, PIO> { @@ -788,7 +788,7 @@ impl<'d, PIO: Instance> Drop for Common<'d, PIO> { pub struct InstanceMemory<'d, PIO: Instance> { used_mask: u32, - pio: PhantomData<&'d PIO>, + pio: PhantomData<&'d mut PIO>, } impl<'d, PIO: Instance> Common<'d, PIO> { @@ -859,7 +859,7 @@ impl<'d, PIO: Instance> Common<'d, PIO> { } pub struct Irq<'d, PIO: Instance, const N: usize> { - pio: PhantomData<&'d PIO>, + pio: PhantomData<&'d mut PIO>, } impl<'d, PIO: Instance, const N: usize> Irq<'d, PIO, N> { @@ -873,7 +873,7 @@ impl<'d, PIO: Instance, const N: usize> Irq<'d, PIO, N> { #[derive(Clone)] pub struct IrqFlags<'d, PIO: Instance> { - pio: PhantomData<&'d PIO>, + pio: PhantomData<&'d mut PIO>, } impl<'d, PIO: Instance> IrqFlags<'d, PIO> { @@ -920,6 +920,7 @@ pub struct Pio<'d, PIO: Instance> { pub sm1: StateMachine<'d, PIO, 1>, pub sm2: StateMachine<'d, PIO, 2>, pub sm3: StateMachine<'d, PIO, 3>, + _pio: PhantomData<&'d mut PIO>, } impl<'d, PIO: Instance> Pio<'d, PIO> { @@ -952,6 +953,7 @@ impl<'d, PIO: Instance> Pio<'d, PIO> { rx: StateMachineRx { pio: PhantomData }, tx: StateMachineTx { pio: PhantomData }, }, + _pio: PhantomData, } } } From 5f7ef8bed0d665c2f59351194fbc155203321d24 Mon Sep 17 00:00:00 2001 From: pennae Date: Thu, 4 May 2023 11:55:11 +0200 Subject: [PATCH 04/12] rp/pio: only clear diag bits if they're set otherwise we may lose a bit being raised after it was read, but before it was cleared. --- embassy-rp/src/pio.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index d2eedc72..ea6814fb 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -307,7 +307,9 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { unsafe { let fdebug = PIO::PIO.fdebug(); let ret = fdebug.read().rxstall() & (1 << SM) != 0; - fdebug.write(|w| w.set_rxstall(1 << SM)); + if ret { + fdebug.write(|w| w.set_rxstall(1 << SM)); + } ret } } @@ -316,7 +318,9 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { unsafe { let fdebug = PIO::PIO.fdebug(); let ret = fdebug.read().rxunder() & (1 << SM) != 0; - fdebug.write(|w| w.set_rxunder(1 << SM)); + if ret { + fdebug.write(|w| w.set_rxunder(1 << SM)); + } ret } } @@ -383,7 +387,9 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { unsafe { let fdebug = PIO::PIO.fdebug(); let ret = fdebug.read().txstall() & (1 << SM) != 0; - fdebug.write(|w| w.set_txstall(1 << SM)); + if ret { + fdebug.write(|w| w.set_txstall(1 << SM)); + } ret } } @@ -392,7 +398,9 @@ impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { unsafe { let fdebug = PIO::PIO.fdebug(); let ret = fdebug.read().txover() & (1 << SM) != 0; - fdebug.write(|w| w.set_txover(1 << SM)); + if ret { + fdebug.write(|w| w.set_txover(1 << SM)); + } ret } } From 41ec4170a5ae9920fe31327252ba1bba754b6d9f Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 5 May 2023 19:49:34 +0200 Subject: [PATCH 05/12] rp/pio: add load_program, use_program programs contain information we could pull from them directly and use to validate other configuration of the state machine instead of asking the user to pull them out and hand them to us bit by bit. unfortunately programs do not specify how many in or out bits they use, so we can only handle side-set and wrapping jumps like this. it's still something though. --- embassy-rp/src/pio.rs | 128 ++++++++++++++--------------- examples/rp/src/bin/pio_async.rs | 25 ++---- examples/rp/src/bin/pio_dma.rs | 7 +- examples/rp/src/bin/pio_hd44780.rs | 16 +--- examples/rp/src/bin/ws2812-pio.rs | 13 +-- 5 files changed, 74 insertions(+), 115 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index ea6814fb..f835fc8d 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -9,12 +9,14 @@ use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::io::vals::Gpio0ctrlFuncsel; +use pio::{SideSet, Wrap}; use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; use crate::gpio::{self, AnyPin, Drive, Pull, SlewRate}; use crate::pac::dma::vals::TreqSel; -use crate::{interrupt, pac, peripherals, RegExt}; +use crate::relocate::RelocatedProgram; +use crate::{interrupt, pac, peripherals, pio_instr_util, RegExt}; struct Wakers([AtomicWaker; 12]); @@ -460,6 +462,13 @@ impl<'d, PIO: Instance, const SM: usize> Drop for StateMachine<'d, PIO, SM> { } } +fn assert_consecutive<'d, PIO: Instance>(pins: &[&Pin<'d, PIO>]) { + for (p1, p2) in pins.iter().zip(pins.iter().skip(1)) { + // purposely does not allow wrap-around because we can't claim pins 30 and 31. + assert!(p1.pin() + 1 == p2.pin(), "pins must be consecutive"); + } +} + impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { #[inline(always)] fn this_sm() -> crate::pac::pio::StateMachine { @@ -504,26 +513,26 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { } } - pub fn set_side_enable(&self, enable: bool) { + /// Configures this state machine to use the given program, including jumping to the origin + /// of the program. The state machine is not started. + pub fn use_program(&mut self, prog: &LoadedProgram<'d, PIO>, side_set: &[&Pin<'d, PIO>]) { + assert!((prog.side_set.bits() - prog.side_set.optional() as u8) as usize == side_set.len()); + assert_consecutive(side_set); unsafe { - Self::this_sm().execctrl().modify(|w| w.set_side_en(enable)); + Self::this_sm().execctrl().modify(|w| { + w.set_side_en(prog.side_set.optional()); + w.set_side_pindir(prog.side_set.pindirs()); + w.set_wrap_bottom(prog.wrap.target); + w.set_wrap_top(prog.wrap.source); + }); + Self::this_sm().pinctrl().modify(|w| { + w.set_sideset_count(prog.side_set.bits()); + w.set_sideset_base(side_set.first().map_or(0, |p| p.pin())); + }); + pio_instr_util::exec_jmp(self, prog.origin); } } - pub fn is_side_enabled(&self) -> bool { - unsafe { Self::this_sm().execctrl().read().side_en() } - } - - pub fn set_side_pindir(&mut self, pindir: bool) { - unsafe { - Self::this_sm().execctrl().modify(|w| w.set_side_pindir(pindir)); - } - } - - pub fn is_side_pindir(&self) -> bool { - unsafe { Self::this_sm().execctrl().read().side_pindir() } - } - pub fn set_jmp_pin(&mut self, pin: u8) { unsafe { Self::this_sm().execctrl().modify(|w| w.set_jmp_pin(pin)); @@ -534,23 +543,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { unsafe { Self::this_sm().execctrl().read().jmp_pin() } } - pub fn set_wrap(&self, source: u8, target: u8) { - unsafe { - Self::this_sm().execctrl().modify(|w| { - w.set_wrap_top(source); - w.set_wrap_bottom(target) - }); - } - } - - /// Get wrapping addresses. Returns (source, target). - pub fn get_wrap(&self) -> (u8, u8) { - unsafe { - let r = Self::this_sm().execctrl().read(); - (r.wrap_top(), r.wrap_bottom()) - } - } - pub fn set_fifo_join(&mut self, join: FifoJoin) { let (rx, tx) = match join { FifoJoin::Duplex => (false, false), @@ -667,28 +659,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { pub fn get_addr(&self) -> u8 { unsafe { Self::this_sm().addr().read().addr() } } - pub fn set_sideset_count(&mut self, count: u8) { - unsafe { - Self::this_sm().pinctrl().modify(|w| w.set_sideset_count(count)); - } - } - - pub fn get_sideset_count(&self) -> u8 { - unsafe { Self::this_sm().pinctrl().read().sideset_count() } - } - - pub fn set_sideset_base_pin(&mut self, base_pin: &Pin) { - unsafe { - Self::this_sm().pinctrl().modify(|w| w.set_sideset_base(base_pin.pin())); - } - } - - pub fn get_sideset_base(&self) -> u8 { - unsafe { - let r = Self::this_sm().pinctrl().read(); - r.sideset_base() - } - } /// Set the range of out pins affected by a set instruction. pub fn set_set_range(&mut self, base: u8, count: u8) { @@ -799,8 +769,35 @@ pub struct InstanceMemory<'d, PIO: Instance> { pio: PhantomData<&'d mut PIO>, } +pub struct LoadedProgram<'d, PIO: Instance> { + pub used_memory: InstanceMemory<'d, PIO>, + origin: u8, + wrap: Wrap, + side_set: SideSet, +} + impl<'d, PIO: Instance> Common<'d, PIO> { - pub fn write_instr(&mut self, start: usize, instrs: I) -> InstanceMemory<'d, PIO> + pub fn load_program(&mut self, prog: &RelocatedProgram) -> LoadedProgram<'d, PIO> { + match self.try_load_program(prog) { + Ok(r) => r, + Err(at) => panic!("Trying to write already used PIO instruction memory at {}", at), + } + } + + pub fn try_load_program( + &mut self, + prog: &RelocatedProgram, + ) -> Result, usize> { + let used_memory = self.try_write_instr(prog.origin() as _, prog.code())?; + Ok(LoadedProgram { + used_memory, + origin: prog.origin(), + wrap: prog.wrap(), + side_set: prog.side_set(), + }) + } + + pub fn try_write_instr(&mut self, start: usize, instrs: I) -> Result, usize> where I: Iterator, { @@ -808,11 +805,9 @@ impl<'d, PIO: Instance> Common<'d, PIO> { for (i, instr) in instrs.enumerate() { let addr = (i + start) as u8; let mask = 1 << (addr as usize); - assert!( - self.instructions_used & mask == 0, - "Trying to write already used PIO instruction memory at {}", - addr - ); + if self.instructions_used & mask != 0 { + return Err(addr as usize); + } unsafe { PIO::PIO.instr_mem(addr as usize).write(|w| { w.set_instr_mem(instr); @@ -821,15 +816,14 @@ impl<'d, PIO: Instance> Common<'d, PIO> { used_mask |= mask; } self.instructions_used |= used_mask; - InstanceMemory { + Ok(InstanceMemory { used_mask, pio: PhantomData, - } + }) } - /// Free instruction memory previously allocated with [`Common::write_instr`]. - /// This is always possible but unsafe if any state machine is still using this - /// bit of memory. + /// Free instruction memory. This is always possible but unsafe if any + /// state machine is still using this bit of memory. pub unsafe fn free_instr(&mut self, instrs: InstanceMemory) { self.instructions_used &= !instrs.used_mask; } diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 461ea3ff..9f47c231 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -5,11 +5,10 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Irq, Pio, PioPin, ShiftDirection, StateMachine}; -use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; -fn setup_pio_task_sm0(pio: &mut Common, sm: &mut StateMachine, pin: impl PioPin) { +fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) { // Setup sm0 // Send data serially to pin @@ -22,15 +21,12 @@ fn setup_pio_task_sm0(pio: &mut Common, sm: &mut StateMachine, pi ); let relocated = RelocatedProgram::new(&prg.program); + sm.use_program(&pio.load_program(&relocated), &[]); let out_pin = pio.make_pio_pin(pin); let pio_pins = [&out_pin]; sm.set_out_pins(&pio_pins); - pio.write_instr(relocated.origin() as usize, relocated.code()); - pio_instr_util::exec_jmp(sm, relocated.origin()); sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32); sm.set_set_range(0, 1); - let pio::Wrap { source, target } = relocated.wrap(); - sm.set_wrap(source, target); sm.set_autopull(true); sm.set_out_shift_dir(ShiftDirection::Left); @@ -48,20 +44,16 @@ async fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) { } } -fn setup_pio_task_sm1(pio: &mut Common, sm: &mut StateMachine) { +fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 1>) { // Setupm sm1 // Read 0b10101 repeatedly until ISR is full let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",); let relocated = RelocatedProgram::new(&prg.program); - pio.write_instr(relocated.origin() as usize, relocated.code()); - pio_instr_util::exec_jmp(sm, relocated.origin()); + sm.use_program(&pio.load_program(&relocated), &[]); sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); sm.set_set_range(0, 0); - let pio::Wrap { source, target } = relocated.wrap(); - sm.set_wrap(source, target); - sm.set_autopush(true); sm.set_in_shift_dir(ShiftDirection::Right); } @@ -75,7 +67,7 @@ async fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) { } } -fn setup_pio_task_sm2(pio: &mut Common, sm: &mut StateMachine) { +fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 2>) { // Setup sm2 // Repeatedly trigger IRQ 3 @@ -89,12 +81,7 @@ fn setup_pio_task_sm2(pio: &mut Common, sm: &mut StateMachine) { ".wrap", ); let relocated = RelocatedProgram::new(&prg.program); - pio.write_instr(relocated.origin() as usize, relocated.code()); - - let pio::Wrap { source, target } = relocated.wrap(); - sm.set_wrap(source, target); - - pio_instr_util::exec_jmp(sm, relocated.origin()); + sm.use_program(&pio.load_program(&relocated), &[]); sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); } diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index c664482e..1c4e127c 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_rp::pio::{Pio, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; -use embassy_rp::{pio_instr_util, Peripheral}; +use embassy_rp::Peripheral; use {defmt_rtt as _, panic_probe as _}; fn swap_nibbles(v: u32) -> u32 { @@ -38,11 +38,8 @@ async fn main(_spawner: Spawner) { ); let relocated = RelocatedProgram::new(&prg.program); - common.write_instr(relocated.origin() as usize, relocated.code()); - pio_instr_util::exec_jmp(&mut sm, relocated.origin()); + sm.use_program(&common.load_program(&relocated), &[]); sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32); - let pio::Wrap { source, target } = relocated.wrap(); - sm.set_wrap(source, target); sm.set_autopull(true); sm.set_autopush(true); sm.set_pull_threshold(32); diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 17b2440c..c3466b55 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -123,15 +123,9 @@ impl<'l> HD44780<'l> { embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b11111); let relocated = RelocatedProgram::new(&prg.program); - common.write_instr(relocated.origin() as usize, relocated.code()); - embassy_rp::pio_instr_util::exec_jmp(&mut sm0, relocated.origin()); + sm0.use_program(&common.load_program(&relocated), &[&e]); sm0.set_clkdiv(125 * 256); - let pio::Wrap { source, target } = relocated.wrap(); - sm0.set_wrap(source, target); - sm0.set_side_enable(true); sm0.set_out_pins(&[&db4, &db5, &db6, &db7]); - sm0.set_sideset_base_pin(&e); - sm0.set_sideset_count(2); sm0.set_out_shift_dir(ShiftDirection::Left); sm0.set_fifo_join(FifoJoin::TxOnly); sm0.set_autopull(true); @@ -199,17 +193,11 @@ impl<'l> HD44780<'l> { ); let relocated = RelocatedProgram::new(&prg.program); - common.write_instr(relocated.origin() as usize, relocated.code()); - embassy_rp::pio_instr_util::exec_jmp(&mut sm0, relocated.origin()); - let pio::Wrap { source, target } = relocated.wrap(); + sm0.use_program(&common.load_program(&relocated), &[&e]); sm0.set_clkdiv(8 * 256); // ~64ns/insn - sm0.set_side_enable(false); sm0.set_jmp_pin(db7pin); - sm0.set_wrap(source, target); sm0.set_set_pins(&[&rs, &rw]); sm0.set_out_pins(&[&db4, &db5, &db6, &db7]); - sm0.set_sideset_base_pin(&e); - sm0.set_sideset_count(1); sm0.set_out_shift_dir(ShiftDirection::Left); sm0.set_fifo_join(FifoJoin::TxOnly); diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 2e6860d8..88997054 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -5,7 +5,6 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::pio::{Common, FifoJoin, Instance, Pio, PioPin, ShiftDirection, StateMachine}; -use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use embassy_time::{Duration, Timer}; use smart_leds::RGB8; @@ -45,15 +44,11 @@ impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { let prg = a.assemble_with_wrap(wrap_source, wrap_target); - let relocated = RelocatedProgram::new(&prg); - pio.write_instr(relocated.origin() as usize, relocated.code()); - pio_instr_util::exec_jmp(&mut sm, relocated.origin()); - // Pin config let out_pin = pio.make_pio_pin(pin); - sm.set_set_pins(&[&out_pin]); - sm.set_sideset_base_pin(&out_pin); - sm.set_sideset_count(1); + + let relocated = RelocatedProgram::new(&prg); + sm.use_program(&pio.load_program(&relocated), &[&out_pin]); // Clock config // TODO CLOCK_FREQ should come from embassy_rp @@ -70,8 +65,6 @@ impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { } sm.set_clkdiv((int << 8) | frac); - let pio::Wrap { source, target } = relocated.wrap(); - sm.set_wrap(source, target); // FIFO config sm.set_autopull(true); From 37b460637df0a20885ba8a0fbb0699e2d44ee4ec Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 5 May 2023 20:45:02 +0200 Subject: [PATCH 06/12] rp/pio: add set-pin-{values,dirs} convenience functions these are needed a lot during state machine setup, it makes sense to provide convenience functions for them. --- embassy-rp/src/pio.rs | 60 +++++++++++++++++++++++++++++- examples/rp/src/bin/pio_hd44780.rs | 9 +---- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index f835fc8d..8d054790 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -13,7 +13,7 @@ use pio::{SideSet, Wrap}; use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; -use crate::gpio::{self, AnyPin, Drive, Pull, SlewRate}; +use crate::gpio::{self, AnyPin, Drive, Level, Pull, SlewRate}; use crate::pac::dma::vals::TreqSel; use crate::relocate::RelocatedProgram; use crate::{interrupt, pac, peripherals, pio_instr_util, RegExt}; @@ -54,6 +54,14 @@ pub enum ShiftDirection { Left = 0, } +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum Direction { + In = 0, + Out = 1, +} + const RXNEMPTY_MASK: u32 = 1 << 0; const TXNFULL_MASK: u32 = 1 << 4; const SMIRQ_MASK: u32 = 1 << 8; @@ -533,6 +541,56 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { } } + fn with_paused(&mut self, f: impl FnOnce(&mut Self)) { + let enabled = self.is_enabled(); + self.set_enable(false); + let pincfg = unsafe { Self::this_sm().pinctrl().read() }; + let execcfg = unsafe { Self::this_sm().execctrl().read() }; + unsafe { + Self::this_sm().execctrl().write_clear(|w| w.set_out_sticky(true)); + } + f(self); + unsafe { + Self::this_sm().pinctrl().write_value(pincfg); + Self::this_sm().execctrl().write_value(execcfg); + } + self.set_enable(enabled); + } + + /// Sets pin directions. This pauses the current state machine to run `SET` commands + /// and temporarily unsets the `OUT_STICKY` bit. + pub fn set_pin_dirs(&mut self, dir: Direction, pins: &[&Pin<'d, PIO>]) { + self.with_paused(|sm| { + for pin in pins { + unsafe { + Self::this_sm().pinctrl().write(|w| { + w.set_set_base(pin.pin()); + w.set_set_count(1); + }); + // SET PINDIRS, (dir) + sm.exec_instr(0b111_00000_100_00000 | dir as u16); + } + } + }); + } + + /// Sets pin output values. This pauses the current state machine to run + /// `SET` commands and temporarily unsets the `OUT_STICKY` bit. + pub fn set_pins(&mut self, level: Level, pins: &[&Pin<'d, PIO>]) { + self.with_paused(|sm| { + for pin in pins { + unsafe { + Self::this_sm().pinctrl().write(|w| { + w.set_set_base(pin.pin()); + w.set_set_count(1); + }); + // SET PINS, (dir) + sm.exec_instr(0b111_00000_000_00000 | level as u16); + } + } + }); + } + pub fn set_jmp_pin(&mut self, pin: u8) { unsafe { Self::this_sm().execctrl().modify(|w| w.set_jmp_pin(pin)); diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index c3466b55..40dee1c4 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -7,7 +7,7 @@ use core::fmt::Write; use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{FifoJoin, Pio, PioPin, ShiftDirection, StateMachine}; +use embassy_rp::pio::{Direction, FifoJoin, Pio, PioPin, ShiftDirection, StateMachine}; use embassy_rp::pwm::{Config, Pwm}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{into_ref, Peripheral, PeripheralRef}; @@ -115,12 +115,7 @@ impl<'l> HD44780<'l> { let db6 = common.make_pio_pin(db6); let db7 = common.make_pio_pin(db7); - sm0.set_set_pins(&[&rs, &rw]); - embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b11); - sm0.set_set_pins(&[&e]); - embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b1); - sm0.set_set_pins(&[&db4, &db5, &db6, &db7]); - embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b11111); + sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); let relocated = RelocatedProgram::new(&prg.program); sm0.use_program(&common.load_program(&relocated), &[&e]); From 2873cb93ee3111d984c35287ea9d98f1218d1024 Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 5 May 2023 20:46:10 +0200 Subject: [PATCH 07/12] rp/pio: mark pio_instr_util unsafe none of these are safe. the x/y functions mangle the fifos, the set functions require the state machine to be stopped to be in any way safe, the out functions do both of those things at once. only the jump instruction is marginally safe, but running this on an active program is bound to cause problems. --- embassy-rp/src/pio_instr_util.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/pio_instr_util.rs b/embassy-rp/src/pio_instr_util.rs index e425cf09..25393b47 100644 --- a/embassy-rp/src/pio_instr_util.rs +++ b/embassy-rp/src/pio_instr_util.rs @@ -2,7 +2,7 @@ use pio::{InSource, InstructionOperands, JmpCondition, OutDestination, SetDestin use crate::pio::{Instance, StateMachine}; -pub fn set_x(sm: &mut StateMachine, value: u32) { +pub unsafe fn set_x(sm: &mut StateMachine, value: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::X, bit_count: 32, @@ -12,7 +12,7 @@ pub fn set_x(sm: &mut StateMachine, val sm.exec_instr(OUT); } -pub fn get_x(sm: &mut StateMachine) -> u32 { +pub unsafe fn get_x(sm: &mut StateMachine) -> u32 { const IN: u16 = InstructionOperands::IN { source: InSource::X, bit_count: 32, @@ -22,7 +22,7 @@ pub fn get_x(sm: &mut StateMachine) -> sm.rx().pull() } -pub fn set_y(sm: &mut StateMachine, value: u32) { +pub unsafe fn set_y(sm: &mut StateMachine, value: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::Y, bit_count: 32, @@ -32,7 +32,7 @@ pub fn set_y(sm: &mut StateMachine, val sm.exec_instr(OUT); } -pub fn get_y(sm: &mut StateMachine) -> u32 { +pub unsafe fn get_y(sm: &mut StateMachine) -> u32 { const IN: u16 = InstructionOperands::IN { source: InSource::Y, bit_count: 32, @@ -43,7 +43,7 @@ pub fn get_y(sm: &mut StateMachine) -> sm.rx().pull() } -pub fn set_pindir(sm: &mut StateMachine, data: u8) { +pub unsafe fn set_pindir(sm: &mut StateMachine, data: u8) { let set: u16 = InstructionOperands::SET { destination: SetDestination::PINDIRS, data, @@ -52,7 +52,7 @@ pub fn set_pindir(sm: &mut StateMachine sm.exec_instr(set); } -pub fn set_pin(sm: &mut StateMachine, data: u8) { +pub unsafe fn set_pin(sm: &mut StateMachine, data: u8) { let set: u16 = InstructionOperands::SET { destination: SetDestination::PINS, data, @@ -61,7 +61,7 @@ pub fn set_pin(sm: &mut StateMachine, d sm.exec_instr(set); } -pub fn set_out_pin(sm: &mut StateMachine, data: u32) { +pub unsafe fn set_out_pin(sm: &mut StateMachine, data: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::PINS, bit_count: 32, @@ -70,7 +70,7 @@ pub fn set_out_pin(sm: &mut StateMachine(sm: &mut StateMachine, data: u32) { +pub unsafe fn set_out_pindir(sm: &mut StateMachine, data: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::PINDIRS, bit_count: 32, @@ -80,7 +80,7 @@ pub fn set_out_pindir(sm: &mut StateMachine(sm: &mut StateMachine, to_addr: u8) { +pub unsafe fn exec_jmp(sm: &mut StateMachine, to_addr: u8) { let jmp: u16 = InstructionOperands::JMP { address: to_addr, condition: JmpCondition::Always, From 8e4d65e163bd484efc4fb31d20b14e6ac4a88de7 Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 6 May 2023 11:36:07 +0200 Subject: [PATCH 08/12] rp/pio: configure state machines with Config struct the many individual sets aren't very efficient, and almost no checks were done to ensure that the configuration written to the hardware was actually valid. this adresses both of these. --- embassy-rp/src/pio.rs | 438 ++++++++++++++--------------- examples/rp/Cargo.toml | 2 + examples/rp/src/bin/pio_async.rs | 37 +-- examples/rp/src/bin/pio_dma.rs | 27 +- examples/rp/src/bin/pio_hd44780.rs | 42 +-- examples/rp/src/bin/ws2812-pio.rs | 37 ++- 6 files changed, 299 insertions(+), 284 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 8d054790..a1b7cf1c 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -6,9 +6,13 @@ use core::task::{Context, Poll}; use atomic_polyfill::{AtomicU32, AtomicU8}; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_embedded_hal::SetConfig; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; +use fixed::types::extra::U8; +use fixed::FixedU32; use pac::io::vals::Gpio0ctrlFuncsel; +use pac::pio::vals::SmExecctrlStatusSel; use pio::{SideSet, Wrap}; use crate::dma::{Channel, Transfer, Word}; @@ -39,8 +43,12 @@ const NEW_AW: AtomicWaker = AtomicWaker::new(); const PIO_WAKERS_INIT: Wakers = Wakers([NEW_AW; 12]); static WAKERS: [Wakers; 2] = [PIO_WAKERS_INIT; 2]; +#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] pub enum FifoJoin { /// Both TX and RX fifo is enabled + #[default] Duplex, /// Rx fifo twice as deep. TX fifo disabled RxOnly, @@ -48,8 +56,11 @@ pub enum FifoJoin { TxOnly, } -#[derive(PartialEq)] +#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] pub enum ShiftDirection { + #[default] Right = 1, Left = 0, } @@ -62,6 +73,15 @@ pub enum Direction { Out = 1, } +#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum StatusSource { + #[default] + TxFifoLevel = 0, + RxFifoLevel = 1, +} + const RXNEMPTY_MASK: u32 = 1 << 0; const TXNFULL_MASK: u32 = 1 << 4; const SMIRQ_MASK: u32 = 1 << 8; @@ -477,6 +497,202 @@ fn assert_consecutive<'d, PIO: Instance>(pins: &[&Pin<'d, PIO>]) { } } +#[derive(Clone, Copy, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct ExecConfig { + pub side_en: bool, + pub side_pindir: bool, + pub jmp_pin: u8, + pub wrap_top: u8, + pub wrap_bottom: u8, +} + +#[derive(Clone, Copy, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct ShiftConfig { + pub threshold: u8, + pub direction: ShiftDirection, + pub auto_fill: bool, +} + +#[derive(Clone, Copy, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PinConfig { + pub sideset_count: u8, + pub set_count: u8, + pub out_count: u8, + pub in_base: u8, + pub sideset_base: u8, + pub set_base: u8, + pub out_base: u8, +} + +#[derive(Clone, Copy, Debug)] +pub struct Config<'d, PIO: Instance> { + // CLKDIV + pub clock_divider: FixedU32, + // EXECCTRL + pub out_en_sel: u8, + pub inline_out_en: bool, + pub out_sticky: bool, + pub status_sel: StatusSource, + pub status_n: u8, + exec: ExecConfig, + origin: Option, + // SHIFTCTRL + pub fifo_join: FifoJoin, + pub shift_in: ShiftConfig, + pub shift_out: ShiftConfig, + // PINCTRL + pins: PinConfig, + in_count: u8, + _pio: PhantomData<&'d mut PIO>, +} + +impl<'d, PIO: Instance> Default for Config<'d, PIO> { + fn default() -> Self { + Self { + clock_divider: 1u8.into(), + out_en_sel: Default::default(), + inline_out_en: Default::default(), + out_sticky: Default::default(), + status_sel: Default::default(), + status_n: Default::default(), + exec: Default::default(), + origin: Default::default(), + fifo_join: Default::default(), + shift_in: Default::default(), + shift_out: Default::default(), + pins: Default::default(), + in_count: Default::default(), + _pio: Default::default(), + } + } +} + +impl<'d, PIO: Instance> Config<'d, PIO> { + pub fn get_exec(&self) -> ExecConfig { + self.exec + } + pub unsafe fn set_exec(&mut self, e: ExecConfig) { + self.exec = e; + } + + pub fn get_pins(&self) -> PinConfig { + self.pins + } + pub unsafe fn set_pins(&mut self, p: PinConfig) { + self.pins = p; + } + + /// Configures this state machine to use the given program, including jumping to the origin + /// of the program. The state machine is not started. + /// + /// `side_set` sets the range of pins affected by side-sets. The range must be consecutive. + /// Side-set pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be + /// effective. + pub fn use_program(&mut self, prog: &LoadedProgram<'d, PIO>, side_set: &[&Pin<'d, PIO>]) { + assert!((prog.side_set.bits() - prog.side_set.optional() as u8) as usize == side_set.len()); + assert_consecutive(side_set); + self.exec.side_en = prog.side_set.optional(); + self.exec.side_pindir = prog.side_set.pindirs(); + self.exec.wrap_bottom = prog.wrap.target; + self.exec.wrap_top = prog.wrap.source; + self.pins.sideset_count = prog.side_set.bits(); + self.pins.sideset_base = side_set.first().map_or(0, |p| p.pin()); + self.origin = Some(prog.origin); + } + + pub fn set_jmp_pin(&mut self, pin: &Pin<'d, PIO>) { + self.exec.jmp_pin = pin.pin(); + } + + /// Sets the range of pins affected by SET instructions. The range must be consecutive. + /// Set pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be + /// effective. + pub fn set_set_pins(&mut self, pins: &[&Pin<'d, PIO>]) { + assert!(pins.len() <= 5); + assert_consecutive(pins); + self.pins.set_base = pins.first().map_or(0, |p| p.pin()); + self.pins.set_count = pins.len() as u8; + } + + /// Sets the range of pins affected by OUT instructions. The range must be consecutive. + /// Out pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be + /// effective. + pub fn set_out_pins(&mut self, pins: &[&Pin<'d, PIO>]) { + assert_consecutive(pins); + self.pins.out_base = pins.first().map_or(0, |p| p.pin()); + self.pins.out_count = pins.len() as u8; + } + + /// Sets the range of pins used by IN instructions. The range must be consecutive. + /// In pins must configured as inputs using [`StateMachine::set_pin_dirs`] to be + /// effective. + pub fn set_in_pins(&mut self, pins: &[&Pin<'d, PIO>]) { + assert_consecutive(pins); + self.pins.in_base = pins.first().map_or(0, |p| p.pin()); + self.in_count = pins.len() as u8; + } +} + +impl<'d, PIO: Instance, const SM: usize> SetConfig for StateMachine<'d, PIO, SM> { + type Config = Config<'d, PIO>; + + fn set_config(&mut self, config: &Self::Config) { + // sm expects 0 for 65536, truncation makes that happen + assert!(config.clock_divider <= 65536, "clkdiv must be <= 65536"); + assert!(config.clock_divider >= 1, "clkdiv must be >= 1"); + assert!(config.out_en_sel < 32, "out_en_sel must be < 32"); + assert!(config.status_n < 32, "status_n must be < 32"); + // sm expects 0 for 32, truncation makes that happen + assert!(config.shift_in.threshold <= 32, "shift_in.threshold must be <= 32"); + assert!(config.shift_out.threshold <= 32, "shift_out.threshold must be <= 32"); + let sm = Self::this_sm(); + unsafe { + sm.clkdiv().write(|w| w.0 = config.clock_divider.to_bits() << 8); + sm.execctrl().write(|w| { + w.set_side_en(config.exec.side_en); + w.set_side_pindir(config.exec.side_pindir); + w.set_jmp_pin(config.exec.jmp_pin); + w.set_out_en_sel(config.out_en_sel); + w.set_inline_out_en(config.inline_out_en); + w.set_out_sticky(config.out_sticky); + w.set_wrap_top(config.exec.wrap_top); + w.set_wrap_bottom(config.exec.wrap_bottom); + w.set_status_sel(match config.status_sel { + StatusSource::TxFifoLevel => SmExecctrlStatusSel::TXLEVEL, + StatusSource::RxFifoLevel => SmExecctrlStatusSel::RXLEVEL, + }); + w.set_status_n(config.status_n); + }); + sm.shiftctrl().write(|w| { + w.set_fjoin_rx(config.fifo_join == FifoJoin::RxOnly); + w.set_fjoin_tx(config.fifo_join == FifoJoin::TxOnly); + w.set_pull_thresh(config.shift_out.threshold); + w.set_push_thresh(config.shift_in.threshold); + w.set_out_shiftdir(config.shift_out.direction == ShiftDirection::Right); + w.set_in_shiftdir(config.shift_in.direction == ShiftDirection::Right); + w.set_autopull(config.shift_out.auto_fill); + w.set_autopush(config.shift_in.auto_fill); + }); + sm.pinctrl().write(|w| { + w.set_sideset_count(config.pins.sideset_count); + w.set_set_count(config.pins.set_count); + w.set_out_count(config.pins.out_count); + w.set_in_base(config.pins.in_base); + w.set_sideset_base(config.pins.sideset_base); + w.set_set_base(config.pins.set_base); + w.set_out_base(config.pins.out_base); + }); + if let Some(origin) = config.origin { + pio_instr_util::exec_jmp(self, origin); + } + } + } +} + impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { #[inline(always)] fn this_sm() -> crate::pac::pio::StateMachine { @@ -504,16 +720,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { unsafe { PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0 } } - pub fn set_clkdiv(&mut self, div_x_256: u32) { - unsafe { - Self::this_sm().clkdiv().write(|w| w.0 = div_x_256 << 8); - } - } - - pub fn get_clkdiv(&self) -> u32 { - unsafe { Self::this_sm().clkdiv().read().0 >> 8 } - } - pub fn clkdiv_restart(&mut self) { let mask = 1u8 << SM; unsafe { @@ -521,26 +727,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { } } - /// Configures this state machine to use the given program, including jumping to the origin - /// of the program. The state machine is not started. - pub fn use_program(&mut self, prog: &LoadedProgram<'d, PIO>, side_set: &[&Pin<'d, PIO>]) { - assert!((prog.side_set.bits() - prog.side_set.optional() as u8) as usize == side_set.len()); - assert_consecutive(side_set); - unsafe { - Self::this_sm().execctrl().modify(|w| { - w.set_side_en(prog.side_set.optional()); - w.set_side_pindir(prog.side_set.pindirs()); - w.set_wrap_bottom(prog.wrap.target); - w.set_wrap_top(prog.wrap.source); - }); - Self::this_sm().pinctrl().modify(|w| { - w.set_sideset_count(prog.side_set.bits()); - w.set_sideset_base(side_set.first().map_or(0, |p| p.pin())); - }); - pio_instr_util::exec_jmp(self, prog.origin); - } - } - fn with_paused(&mut self, f: impl FnOnce(&mut Self)) { let enabled = self.is_enabled(); self.set_enable(false); @@ -591,43 +777,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { }); } - pub fn set_jmp_pin(&mut self, pin: u8) { - unsafe { - Self::this_sm().execctrl().modify(|w| w.set_jmp_pin(pin)); - } - } - - pub fn get_jmp_pin(&mut self) -> u8 { - unsafe { Self::this_sm().execctrl().read().jmp_pin() } - } - - pub fn set_fifo_join(&mut self, join: FifoJoin) { - let (rx, tx) = match join { - FifoJoin::Duplex => (false, false), - FifoJoin::RxOnly => (true, false), - FifoJoin::TxOnly => (false, true), - }; - unsafe { - Self::this_sm().shiftctrl().modify(|w| { - w.set_fjoin_rx(rx); - w.set_fjoin_tx(tx) - }); - } - } - pub fn get_fifo_join(&self) -> FifoJoin { - unsafe { - let r = Self::this_sm().shiftctrl().read(); - // Ignores the invalid state when both bits are set - if r.fjoin_rx() { - FifoJoin::RxOnly - } else if r.fjoin_tx() { - FifoJoin::TxOnly - } else { - FifoJoin::Duplex - } - } - } - pub fn clear_fifos(&mut self) { // Toggle FJOIN_RX to flush FIFOs unsafe { @@ -641,159 +790,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { } } - pub fn set_pull_threshold(&mut self, threshold: u8) { - unsafe { - Self::this_sm().shiftctrl().modify(|w| w.set_pull_thresh(threshold)); - } - } - - pub fn get_pull_threshold(&self) -> u8 { - unsafe { Self::this_sm().shiftctrl().read().pull_thresh() } - } - pub fn set_push_threshold(&mut self, threshold: u8) { - unsafe { - Self::this_sm().shiftctrl().modify(|w| w.set_push_thresh(threshold)); - } - } - - pub fn get_push_threshold(&self) -> u8 { - unsafe { Self::this_sm().shiftctrl().read().push_thresh() } - } - - pub fn set_out_shift_dir(&mut self, dir: ShiftDirection) { - unsafe { - Self::this_sm() - .shiftctrl() - .modify(|w| w.set_out_shiftdir(dir == ShiftDirection::Right)); - } - } - pub fn get_out_shiftdir(&self) -> ShiftDirection { - unsafe { - if Self::this_sm().shiftctrl().read().out_shiftdir() { - ShiftDirection::Right - } else { - ShiftDirection::Left - } - } - } - - pub fn set_in_shift_dir(&mut self, dir: ShiftDirection) { - unsafe { - Self::this_sm() - .shiftctrl() - .modify(|w| w.set_in_shiftdir(dir == ShiftDirection::Right)); - } - } - pub fn get_in_shiftdir(&self) -> ShiftDirection { - unsafe { - if Self::this_sm().shiftctrl().read().in_shiftdir() { - ShiftDirection::Right - } else { - ShiftDirection::Left - } - } - } - - pub fn set_autopull(&mut self, auto: bool) { - unsafe { - Self::this_sm().shiftctrl().modify(|w| w.set_autopull(auto)); - } - } - - pub fn is_autopull(&self) -> bool { - unsafe { Self::this_sm().shiftctrl().read().autopull() } - } - - pub fn set_autopush(&mut self, auto: bool) { - unsafe { - Self::this_sm().shiftctrl().modify(|w| w.set_autopush(auto)); - } - } - - pub fn is_autopush(&self) -> bool { - unsafe { Self::this_sm().shiftctrl().read().autopush() } - } - - pub fn get_addr(&self) -> u8 { - unsafe { Self::this_sm().addr().read().addr() } - } - - /// Set the range of out pins affected by a set instruction. - pub fn set_set_range(&mut self, base: u8, count: u8) { - assert!(base + count < 32); - unsafe { - Self::this_sm().pinctrl().modify(|w| { - w.set_set_base(base); - w.set_set_count(count) - }); - } - } - - /// Get the range of out pins affected by a set instruction. Returns (base, count). - pub fn get_set_range(&self) -> (u8, u8) { - unsafe { - let r = Self::this_sm().pinctrl().read(); - (r.set_base(), r.set_count()) - } - } - - pub fn set_in_base_pin(&mut self, base: &Pin) { - unsafe { - Self::this_sm().pinctrl().modify(|w| w.set_in_base(base.pin())); - } - } - - pub fn get_in_base(&self) -> u8 { - unsafe { - let r = Self::this_sm().pinctrl().read(); - r.in_base() - } - } - - pub fn set_out_range(&mut self, base: u8, count: u8) { - assert!(base + count < 32); - unsafe { - Self::this_sm().pinctrl().modify(|w| { - w.set_out_base(base); - w.set_out_count(count) - }); - } - } - - /// Get the range of out pins affected by a set instruction. Returns (base, count). - pub fn get_out_range(&self) -> (u8, u8) { - unsafe { - let r = Self::this_sm().pinctrl().read(); - (r.out_base(), r.out_count()) - } - } - - pub fn set_out_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&Pin]) { - let count = pins.len(); - assert!(count >= 1); - let start = pins[0].pin() as usize; - assert!(start + pins.len() <= 32); - for i in 0..count { - assert!(pins[i].pin() as usize == start + i, "Pins must be sequential"); - } - self.set_out_range(start as u8, count as u8); - } - - pub fn set_set_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&Pin]) { - let count = pins.len(); - assert!(count >= 1); - let start = pins[0].pin() as usize; - assert!(start + pins.len() <= 32); - for i in 0..count { - assert!(pins[i].pin() as usize == start + i, "Pins must be sequential"); - } - self.set_set_range(start as u8, count as u8); - } - - pub fn get_current_instr() -> u32 { - unsafe { Self::this_sm().instr().read().0 } - } - pub fn exec_instr(&mut self, instr: u16) { unsafe { Self::this_sm().instr().write(|w| w.set_instr(instr)); diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 57f6f5c6..d2829df9 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -22,6 +22,8 @@ lorawan = { version = "0.7.3", default-features = false, features = ["default-cr defmt = "0.3" defmt-rtt = "0.4" +fixed = "1.23.1" +fixed-macro = "1.2" #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m = { version = "0.7.6", features = ["inline-asm"] } diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 9f47c231..12484e88 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -2,10 +2,13 @@ #![no_main] #![feature(type_alias_impl_trait)] use defmt::info; +use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Common, Irq, Pio, PioPin, ShiftDirection, StateMachine}; +use embassy_rp::pio::{Common, Config, Irq, Pio, PioPin, ShiftDirection, StateMachine}; use embassy_rp::relocate::RelocatedProgram; +use fixed::traits::ToFixed; +use fixed_macro::types::U56F8; use {defmt_rtt as _, panic_probe as _}; fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) { @@ -21,15 +24,14 @@ fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, ); let relocated = RelocatedProgram::new(&prg.program); - sm.use_program(&pio.load_program(&relocated), &[]); + let mut cfg = Config::default(); + cfg.use_program(&pio.load_program(&relocated), &[]); let out_pin = pio.make_pio_pin(pin); - let pio_pins = [&out_pin]; - sm.set_out_pins(&pio_pins); - sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32); - sm.set_set_range(0, 1); - - sm.set_autopull(true); - sm.set_out_shift_dir(ShiftDirection::Left); + cfg.set_out_pins(&[&out_pin]); + cfg.set_set_pins(&[&out_pin]); + cfg.clock_divider = (U56F8!(125_000_000) / 20 / 200).to_fixed(); + cfg.shift_out.auto_fill = true; + sm.set_config(&cfg); } #[embassy_executor::task] @@ -51,11 +53,12 @@ fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",); let relocated = RelocatedProgram::new(&prg.program); - sm.use_program(&pio.load_program(&relocated), &[]); - sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); - sm.set_set_range(0, 0); - sm.set_autopush(true); - sm.set_in_shift_dir(ShiftDirection::Right); + let mut cfg = Config::default(); + cfg.use_program(&pio.load_program(&relocated), &[]); + cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); + cfg.shift_in.auto_fill = true; + cfg.shift_in.direction = ShiftDirection::Right; + sm.set_config(&cfg); } #[embassy_executor::task] @@ -81,8 +84,10 @@ fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, ".wrap", ); let relocated = RelocatedProgram::new(&prg.program); - sm.use_program(&pio.load_program(&relocated), &[]); - sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); + let mut cfg = Config::default(); + cfg.use_program(&pio.load_program(&relocated), &[]); + cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); + sm.set_config(&cfg); } #[embassy_executor::task] diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 1c4e127c..7f85288b 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -2,11 +2,14 @@ #![no_main] #![feature(type_alias_impl_trait)] use defmt::info; +use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::pio::{Pio, ShiftDirection}; +use embassy_rp::pio::{Config, Pio, ShiftConfig, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::Peripheral; +use fixed::traits::ToFixed; +use fixed_macro::types::U56F8; use {defmt_rtt as _, panic_probe as _}; fn swap_nibbles(v: u32) -> u32 { @@ -38,15 +41,21 @@ async fn main(_spawner: Spawner) { ); let relocated = RelocatedProgram::new(&prg.program); - sm.use_program(&common.load_program(&relocated), &[]); - sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32); - sm.set_autopull(true); - sm.set_autopush(true); - sm.set_pull_threshold(32); - sm.set_push_threshold(32); - sm.set_out_shift_dir(ShiftDirection::Right); - sm.set_in_shift_dir(ShiftDirection::Left); + let mut cfg = Config::default(); + cfg.use_program(&common.load_program(&relocated), &[]); + cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed(); + cfg.shift_in = ShiftConfig { + auto_fill: true, + threshold: 32, + direction: ShiftDirection::Left, + }; + cfg.shift_out = ShiftConfig { + auto_fill: true, + threshold: 32, + direction: ShiftDirection::Right, + }; + sm.set_config(&cfg); sm.set_enable(true); let mut dma_out_ref = p.DMA_CH0.into_ref(); diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 40dee1c4..61c5565d 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -4,11 +4,12 @@ use core::fmt::Write; +use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Direction, FifoJoin, Pio, PioPin, ShiftDirection, StateMachine}; -use embassy_rp::pwm::{Config, Pwm}; +use embassy_rp::pio::{Config, Direction, FifoJoin, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; +use embassy_rp::pwm::{self, Pwm}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{into_ref, Peripheral, PeripheralRef}; use embassy_time::{Duration, Instant, Timer}; @@ -29,7 +30,7 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let _pwm = Pwm::new_output_b(p.PWM_CH7, p.PIN_15, { - let mut c = Config::default(); + let mut c = pwm::Config::default(); c.divider = 125.into(); c.top = 100; c.compare_b = 50; @@ -83,7 +84,6 @@ impl<'l> HD44780<'l> { ) -> HD44780<'l> { into_ref!(dma); - let db7pin = db7.pin(); let Pio { mut common, mut irq0, @@ -118,13 +118,17 @@ impl<'l> HD44780<'l> { sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); let relocated = RelocatedProgram::new(&prg.program); - sm0.use_program(&common.load_program(&relocated), &[&e]); - sm0.set_clkdiv(125 * 256); - sm0.set_out_pins(&[&db4, &db5, &db6, &db7]); - sm0.set_out_shift_dir(ShiftDirection::Left); - sm0.set_fifo_join(FifoJoin::TxOnly); - sm0.set_autopull(true); - sm0.set_pull_threshold(32); + let mut cfg = Config::default(); + cfg.use_program(&common.load_program(&relocated), &[&e]); + cfg.clock_divider = 125u8.into(); + cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); + cfg.shift_out = ShiftConfig { + auto_fill: true, + direction: ShiftDirection::Left, + threshold: 32, + }; + cfg.fifo_join = FifoJoin::TxOnly; + sm0.set_config(&cfg); sm0.set_enable(true); // init to 8 bit thrice @@ -188,13 +192,15 @@ impl<'l> HD44780<'l> { ); let relocated = RelocatedProgram::new(&prg.program); - sm0.use_program(&common.load_program(&relocated), &[&e]); - sm0.set_clkdiv(8 * 256); // ~64ns/insn - sm0.set_jmp_pin(db7pin); - sm0.set_set_pins(&[&rs, &rw]); - sm0.set_out_pins(&[&db4, &db5, &db6, &db7]); - sm0.set_out_shift_dir(ShiftDirection::Left); - sm0.set_fifo_join(FifoJoin::TxOnly); + let mut cfg = Config::default(); + cfg.use_program(&common.load_program(&relocated), &[&e]); + cfg.clock_divider = 8u8.into(); // ~64ns/insn + cfg.set_jmp_pin(&db7); + cfg.set_set_pins(&[&rs, &rw]); + cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); + cfg.shift_out.direction = ShiftDirection::Left; + cfg.fifo_join = FifoJoin::TxOnly; + sm0.set_config(&cfg); sm0.set_enable(true); diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 88997054..d7c4742d 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -3,10 +3,12 @@ #![feature(type_alias_impl_trait)] use defmt::*; +use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; -use embassy_rp::pio::{Common, FifoJoin, Instance, Pio, PioPin, ShiftDirection, StateMachine}; +use embassy_rp::pio::{Common, Config, FifoJoin, Instance, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; use embassy_rp::relocate::RelocatedProgram; use embassy_time::{Duration, Timer}; +use fixed_macro::fixed; use smart_leds::RGB8; use {defmt_rtt as _, panic_probe as _}; pub struct Ws2812<'d, P: Instance, const S: usize> { @@ -43,35 +45,30 @@ impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { a.bind(&mut wrap_source); let prg = a.assemble_with_wrap(wrap_source, wrap_target); + let mut cfg = Config::default(); // Pin config let out_pin = pio.make_pio_pin(pin); let relocated = RelocatedProgram::new(&prg); - sm.use_program(&pio.load_program(&relocated), &[&out_pin]); + cfg.use_program(&pio.load_program(&relocated), &[&out_pin]); - // Clock config + // Clock config, measured in kHz to avoid overflows // TODO CLOCK_FREQ should come from embassy_rp - const CLOCK_FREQ: u32 = 125_000_000; - const WS2812_FREQ: u32 = 800_000; - - let bit_freq = WS2812_FREQ * CYCLES_PER_BIT; - let mut int = CLOCK_FREQ / bit_freq; - let rem = CLOCK_FREQ - (int * bit_freq); - let frac = (rem * 256) / bit_freq; - // 65536.0 is represented as 0 in the pio's clock divider - if int == 65536 { - int = 0; - } - - sm.set_clkdiv((int << 8) | frac); + let clock_freq = fixed!(125_000: U24F8); + let ws2812_freq = fixed!(800: U24F8); + let bit_freq = ws2812_freq * CYCLES_PER_BIT; + cfg.clock_divider = clock_freq / bit_freq; // FIFO config - sm.set_autopull(true); - sm.set_fifo_join(FifoJoin::TxOnly); - sm.set_pull_threshold(24); - sm.set_out_shift_dir(ShiftDirection::Left); + cfg.fifo_join = FifoJoin::TxOnly; + cfg.shift_out = ShiftConfig { + auto_fill: true, + threshold: 24, + direction: ShiftDirection::Left, + }; + sm.set_config(&cfg); sm.set_enable(true); Self { sm } From bdcea84ca106a0bd3a131fa1de4fb35218e9149a Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 6 May 2023 12:23:53 +0200 Subject: [PATCH 09/12] rp/pio: add sm batch operations sometimes state machines need to be started, restarted, or synchronized at exactly the same time. the current interface does not allow this but the hardware does, so let's expose that. --- embassy-rp/src/pio.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index a1b7cf1c..7cf605d8 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -912,6 +912,47 @@ impl<'d, PIO: Instance> Common<'d, PIO> { pio: PhantomData::default(), } } + + pub fn apply_sm_batch(&mut self, f: impl FnOnce(&mut PioBatch<'d, PIO>)) { + let mut batch = PioBatch { + clkdiv_restart: 0, + sm_restart: 0, + sm_enable_mask: 0, + sm_enable: 0, + _pio: PhantomData, + }; + f(&mut batch); + unsafe { + PIO::PIO.ctrl().modify(|w| { + w.set_clkdiv_restart(batch.clkdiv_restart); + w.set_sm_restart(batch.sm_restart); + w.set_sm_enable((w.sm_enable() & !batch.sm_enable_mask) | batch.sm_enable); + }); + } + } +} + +pub struct PioBatch<'a, PIO: Instance> { + clkdiv_restart: u8, + sm_restart: u8, + sm_enable_mask: u8, + sm_enable: u8, + _pio: PhantomData<&'a PIO>, +} + +impl<'a, PIO: Instance> PioBatch<'a, PIO> { + pub fn restart_clockdiv(&mut self, _sm: &mut StateMachine<'a, PIO, SM>) { + self.clkdiv_restart |= 1 << SM; + } + + pub fn restart(&mut self, _sm: &mut StateMachine<'a, PIO, SM>) { + self.clkdiv_restart |= 1 << SM; + } + + pub fn set_enable(&mut self, _sm: &mut StateMachine<'a, PIO, SM>, enable: bool) { + self.sm_enable_mask |= 1 << SM; + self.sm_enable |= (enable as u8) << SM; + } } pub struct Irq<'d, PIO: Instance, const N: usize> { From 374c7513f905fcbf353bee2987e78d68fe8ea303 Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 6 May 2023 17:24:06 +0200 Subject: [PATCH 10/12] rp/pio: mark exec_instr as unsafe because it most definitely is. --- embassy-rp/src/pio.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 7cf605d8..5fed4f1f 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -790,10 +790,8 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { } } - pub fn exec_instr(&mut self, instr: u16) { - unsafe { - Self::this_sm().instr().write(|w| w.set_instr(instr)); - } + pub unsafe fn exec_instr(&mut self, instr: u16) { + Self::this_sm().instr().write(|w| w.set_instr(instr)); } pub fn rx(&mut self) -> &mut StateMachineRx<'d, PIO, SM> { From b38d496d519f87d7f455e6c6b32f5d6379af44a5 Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 6 May 2023 20:53:06 +0200 Subject: [PATCH 11/12] rp/pio: allow wrap-around program loading execution wraps around after the end of instruction memory and wrapping works with this, so we may as well allow program loading across this boundary. could be useful for reusing chunks of instruction memory. --- embassy-rp/src/pio.rs | 11 ++++++----- embassy-rp/src/relocate.rs | 10 +++------- examples/rp/src/bin/pio_hd44780.rs | 3 ++- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 5fed4f1f..fe66d9ae 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -855,13 +855,14 @@ impl<'d, PIO: Instance> Common<'d, PIO> { { let mut used_mask = 0; for (i, instr) in instrs.enumerate() { - let addr = (i + start) as u8; - let mask = 1 << (addr as usize); - if self.instructions_used & mask != 0 { - return Err(addr as usize); + // wrapping around the end of program memory is valid, let's make use of that. + let addr = (i + start) % 32; + let mask = 1 << addr; + if (self.instructions_used | used_mask) & mask != 0 { + return Err(addr); } unsafe { - PIO::PIO.instr_mem(addr as usize).write(|w| { + PIO::PIO.instr_mem(addr).write(|w| { w.set_instr_mem(instr); }); } diff --git a/embassy-rp/src/relocate.rs b/embassy-rp/src/relocate.rs index f36170e0..9cb279cc 100644 --- a/embassy-rp/src/relocate.rs +++ b/embassy-rp/src/relocate.rs @@ -26,11 +26,7 @@ where Some(if instr & 0b1110_0000_0000_0000 == 0 { // this is a JMP instruction -> add offset to address let address = (instr & 0b1_1111) as u8; - let address = address + self.offset; - assert!( - address < pio::RP2040_MAX_PROGRAM_SIZE as u8, - "Invalid JMP out of the program after offset addition" - ); + let address = address.wrapping_add(self.offset) % 32; instr & (!0b11111) | address as u16 } else { instr @@ -62,8 +58,8 @@ impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> { let wrap = self.program.wrap; let origin = self.origin; Wrap { - source: wrap.source + origin, - target: wrap.target + origin, + source: wrap.source.wrapping_add(origin) % 32, + target: wrap.target.wrapping_add(origin) % 32, } } diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 61c5565d..088fd564 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -95,6 +95,7 @@ impl<'l> HD44780<'l> { let prg = pio_proc::pio_asm!( r#" .side_set 1 opt + .origin 20 loop: out x, 24 @@ -148,7 +149,7 @@ impl<'l> HD44780<'l> { // many side sets are only there to free up a delay bit! let prg = pio_proc::pio_asm!( r#" - .origin 7 + .origin 27 .side_set 1 .wrap_target From db9b8eb88f095687ed624171cf79fbacf06c199a Mon Sep 17 00:00:00 2001 From: pennae Date: Sat, 6 May 2023 21:14:00 +0200 Subject: [PATCH 12/12] rp/pio: make sure gpio() asserts are compiled out we'll have to touch pio one way or other if the number of gpio pins ever increases. may as well make sure an assert never fires until that happens. --- embassy-rp/src/pio.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index fe66d9ae..1e41bed3 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -1070,7 +1070,8 @@ fn on_pio_drop() { if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { let used_pins = state.used_pins.load(Ordering::Relaxed); let null = Gpio0ctrlFuncsel::NULL.0; - for i in 0..32 { + // we only have 30 pins. don't test the other two since gpio() asserts. + for i in 0..30 { if used_pins & (1 << i) != 0 { unsafe { pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null));