diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 2cf4761a..1e41bed3 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -6,15 +6,21 @@ 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}; 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::{interrupt, pac, peripherals, RegExt}; +use crate::relocate::RelocatedProgram; +use crate::{interrupt, pac, peripherals, pio_instr_util, RegExt}; struct Wakers([AtomicWaker; 12]); @@ -37,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, @@ -46,12 +56,32 @@ 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, } +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum Direction { + In = 0, + 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; @@ -96,18 +126,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 +157,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 +169,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 +198,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,28 +210,21 @@ 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 mut 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); // 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(()); } @@ -215,7 +238,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 +248,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 +316,11 @@ impl<'l, PIO: PioInstance> Pin<'l, PIO> { } } -pub struct PioStateMachineRx<'d, PIO: PioInstance, const SM: usize> { - pio: PhantomData<&'d PIO>, +pub struct StateMachineRx<'d, PIO: Instance, const SM: usize> { + pio: PhantomData<&'d mut 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 } } @@ -314,7 +337,9 @@ impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineRx<'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 } } @@ -323,7 +348,9 @@ impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineRx<'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 } } @@ -370,11 +397,11 @@ impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineRx<'d, PIO, SM> { } } -pub struct PioStateMachineTx<'d, PIO: PioInstance, const SM: usize> { - pio: PhantomData<&'d PIO>, +pub struct StateMachineTx<'d, PIO: Instance, const SM: usize> { + pio: PhantomData<&'d mut 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 } } @@ -390,7 +417,9 @@ impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineTx<'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 } } @@ -399,7 +428,9 @@ impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineTx<'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 } } @@ -445,12 +476,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 +490,210 @@ 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> { +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"); + } +} + +#[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 { PIO::PIO.sm(SM) @@ -486,16 +720,6 @@ impl<'d, PIO: PioInstance + 'd, const SM: usize> PioStateMachine<'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 { @@ -503,78 +727,54 @@ impl<'d, PIO: PioInstance + 'd, const SM: usize> PioStateMachine<'d, PIO, SM> { } } - pub fn set_side_enable(&self, enable: bool) { + 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().modify(|w| w.set_side_en(enable)); + Self::this_sm().execctrl().write_clear(|w| w.set_out_sticky(true)); } - } - - pub fn is_side_enabled(&self) -> bool { - unsafe { Self::this_sm().execctrl().read().side_en() } - } - - pub fn set_side_pindir(&mut self, pindir: bool) { + f(self); unsafe { - Self::this_sm().execctrl().modify(|w| w.set_side_pindir(pindir)); + Self::this_sm().pinctrl().write_value(pincfg); + Self::this_sm().execctrl().write_value(execcfg); } + self.set_enable(enabled); } - 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)); - } - } - - pub fn get_jmp_pin(&mut self) -> u8 { - 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), - 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 + /// 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 clear_fifos(&mut self) { @@ -590,246 +790,94 @@ impl<'d, PIO: PioInstance + 'd, const SM: usize> PioStateMachine<'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 unsafe fn exec_instr(&mut self, instr: u16) { + Self::this_sm().instr().write(|w| w.set_instr(instr)); } - 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() } - } - 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) { - 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)); - } - } - - 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>, + pio: PhantomData<&'d mut 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>, + pio: PhantomData<&'d mut PIO>, } -impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { - pub fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory<'d, 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 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, { let mut used_mask = 0; 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 - ); + // 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); }); } used_mask |= mask; } self.instructions_used |= used_mask; - PioInstanceMemory { + Ok(InstanceMemory { used_mask, pio: PhantomData, - } + }) } - /// Free instruction memory previously allocated with [`PioCommon::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) { + /// 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; } @@ -848,8 +896,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); @@ -863,13 +911,54 @@ impl<'d, PIO: PioInstance> PioCommon<'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 PioIrq<'d, PIO: PioInstance, const N: usize> { - pio: PhantomData<&'d PIO>, +pub struct PioBatch<'a, PIO: Instance> { + clkdiv_restart: u8, + sm_restart: u8, + sm_enable_mask: u8, + sm_enable: u8, + _pio: PhantomData<&'a PIO>, } -impl<'d, PIO: PioInstance, const N: usize> PioIrq<'d, PIO, N> { +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> { + pio: PhantomData<&'d mut PIO>, +} + +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 +968,11 @@ impl<'d, PIO: PioInstance, const N: usize> PioIrq<'d, PIO, N> { } #[derive(Clone)] -pub struct PioIrqFlags<'d, PIO: PioInstance> { - pio: PhantomData<&'d PIO>, +pub struct IrqFlags<'d, PIO: Instance> { + pio: PhantomData<&'d mut 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,49 +1005,51 @@ 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>, + _pio: PhantomData<&'d mut PIO>, } -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 }, }, + _pio: PhantomData, } } } @@ -974,12 +1065,13 @@ 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); 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)); @@ -994,7 +1086,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 +1103,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..25393b47 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 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 PioStateMachine(sm: &mut PioStateMachine) -> 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 PioStateMachine(sm: &mut PioStateMachine, 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 PioStateMachine(sm: &mut PioStateMachine) -> 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 PioStateMachine(sm: &mut PioStateMachine, 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 PioStateMachine(sm: &mut PioStateMachine, 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 PioStateMachine(sm: &mut PioStateMachine, 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 PioStateMachine

(sm: &mut PioStateMachine, 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 PioStateMachin sm.exec_instr(OUT); } -pub fn exec_jmp(sm: &mut PioStateMachine, 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, 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/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 4e0ab5e3..12484e88 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -2,14 +2,16 @@ #![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::{Pio, PioCommon, PioIrq, PioPin, PioStateMachine, ShiftDirection}; -use embassy_rp::pio_instr_util; +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(pio: &mut PioCommon, sm: &mut PioStateMachine, 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,22 +24,18 @@ 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,26 +46,23 @@ 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<'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.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); + 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] -async fn pio_task_sm1(mut sm: PioStateMachine<'static, PIO0, 1>) { +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 +70,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<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 2>) { // Setup sm2 // Repeatedly trigger IRQ 3 @@ -89,17 +84,14 @@ 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_dma.rs b/examples/rp/src/bin/pio_dma.rs index c664482e..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::{pio_instr_util, Peripheral}; +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,18 +41,21 @@ 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.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); - 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 f76d334e..088fd564 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::{FifoJoin, Pio, PioPin, PioStateMachine, ShiftDirection}; -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; @@ -64,7 +65,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], } @@ -83,7 +84,6 @@ impl<'l> HD44780<'l> { ) -> HD44780<'l> { into_ref!(dma); - let db7pin = db7.pin(); let Pio { mut common, mut irq0, @@ -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 @@ -115,27 +116,20 @@ 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); - common.write_instr(relocated.origin() as usize, relocated.code()); - embassy_rp::pio_instr_util::exec_jmp(&mut sm0, relocated.origin()); - 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); - 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 @@ -155,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 @@ -199,19 +193,15 @@ 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.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); + 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 c9c701a7..d7c4742d 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -3,19 +3,20 @@ #![feature(type_alias_impl_trait)] use defmt::*; +use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; -use embassy_rp::pio::{FifoJoin, Pio, PioCommon, PioInstance, PioPin, PioStateMachine, ShiftDirection}; -use embassy_rp::pio_instr_util; +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: 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 @@ -44,41 +45,30 @@ impl<'d, P: PioInstance, const S: usize> Ws2812<'d, P, S> { a.bind(&mut wrap_source); 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()); + let mut cfg = Config::default(); // 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); - // Clock config + let relocated = RelocatedProgram::new(&prg); + cfg.use_program(&pio.load_program(&relocated), &[&out_pin]); + + // 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 pio::Wrap { source, target } = relocated.wrap(); - sm.set_wrap(source, target); + 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 }