pio: Move interrupt related (en|dis)abling/forcing methods to the statemachine (#447)

* pio: Move interrupt related (en|dis)abling/forcing methods to the statemachine

The SM knows its id using the type system so there's no need for checking it.
This commit also adds a `PioIRQ` enum to select the output IRQ.

* Move the interrupt control to Rx & Tx and make all accesses to inte atomic
* Adjust the ergonomics of IRQ handling.
* Elide lifetimes where they can be (clippy's advice)
This commit is contained in:
Wilfried Chauveau 2022-09-14 21:07:03 +01:00 committed by GitHub
parent 42fc266e67
commit 322bba2cc0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -51,18 +51,6 @@ pub trait PIOExt:
( (
PIO { PIO {
used_instruction_space: 0, used_instruction_space: 0,
interrupts: [
Interrupt {
id: 0,
block: self.deref(),
_phantom: core::marker::PhantomData,
},
Interrupt {
id: 1,
block: self.deref(),
_phantom: core::marker::PhantomData,
},
],
pio: self, pio: self,
}, },
sm0, sm0,
@ -80,7 +68,6 @@ impl PIOExt for PIO1 {}
pub struct PIO<P: PIOExt> { pub struct PIO<P: PIOExt> {
used_instruction_space: u32, // bit for each PIO_INSTRUCTION_COUNT used_instruction_space: u32, // bit for each PIO_INSTRUCTION_COUNT
pio: P, pio: P,
interrupts: [Interrupt<P>; 2],
} }
impl<P: PIOExt> core::fmt::Debug for PIO<P> { impl<P: PIOExt> core::fmt::Debug for PIO<P> {
@ -113,9 +100,20 @@ impl<P: PIOExt> PIO<P> {
self.pio self.pio
} }
/// This PIO's interrupts. /// This PIO0's interrupts.
pub fn interrupts(&self) -> &[Interrupt<P>; 2] { pub fn irq0(&self) -> Interrupt<'_, P, 0> {
&self.interrupts Interrupt {
block: self.pio.deref(),
_phantom: core::marker::PhantomData,
}
}
/// This PIO0's interrupts.
pub fn irq1(&self) -> Interrupt<'_, P, 1> {
Interrupt {
block: self.pio.deref(),
_phantom: core::marker::PhantomData,
}
} }
/// Get raw irq flags. /// Get raw irq flags.
@ -509,6 +507,24 @@ pub struct Stopped;
/// Marker for an initialized and running state machine. /// Marker for an initialized and running state machine.
pub struct Running; pub struct Running;
/// Id for the PIO's IRQ
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PioIRQ {
#[allow(missing_docs)]
Irq0,
#[allow(missing_docs)]
Irq1,
}
impl PioIRQ {
const fn to_index(self) -> usize {
match self {
PioIRQ::Irq0 => 0,
PioIRQ::Irq1 => 1,
}
}
}
impl<SM: ValidStateMachine, State> StateMachine<SM, State> { impl<SM: ValidStateMachine, State> StateMachine<SM, State> {
/// Stops the state machine if it is still running and returns its program. /// Stops the state machine if it is still running and returns its program.
/// ///
@ -1096,7 +1112,7 @@ unsafe impl<SM: ValidStateMachine + Send> Send for Rx<SM> {}
// are added. // are added.
impl<SM: ValidStateMachine> Rx<SM> { impl<SM: ValidStateMachine> Rx<SM> {
fn register_block(&self) -> &pac::pio0::RegisterBlock { fn register_block(&self) -> &pac::pio0::RegisterBlock {
// Safety: The register is unique to this Tx instance. // Safety: The register is unique to this Rx instance.
unsafe { &*self.block } unsafe { &*self.block }
} }
@ -1135,7 +1151,7 @@ impl<SM: ValidStateMachine> Rx<SM> {
} }
/// Enable/Disable the autopush feature of the state machine. /// Enable/Disable the autopush feature of the state machine.
// Safety: This register is read by Tx, this is the only write. // Safety: This register is read by Rx, this is the only write.
pub fn enable_autopush(&mut self, enable: bool) { pub fn enable_autopush(&mut self, enable: bool) {
self.register_block().sm[SM::id()] self.register_block().sm[SM::id()]
.sm_shiftctrl .sm_shiftctrl
@ -1151,6 +1167,49 @@ impl<SM: ValidStateMachine> Rx<SM> {
pub fn is_full(&self) -> bool { pub fn is_full(&self) -> bool {
self.register_block().fstat.read().rxfull().bits() & (1 << SM::id()) != 0 self.register_block().fstat.read().rxfull().bits() & (1 << SM::id()) != 0
} }
/// Enable RX FIFO not empty interrupt.
///
/// This interrupt is raised when the RX FIFO is not empty, i.e. one could read more data from it.
pub fn enable_rx_not_empty_interrupt(&self, id: PioIRQ) {
unsafe {
write_bitmask_set(
self.register_block().sm_irq[id.to_index()]
.irq_inte
.as_ptr(),
1 << SM::id(),
);
}
}
/// Disable RX FIFO not empty interrupt.
pub fn disable_rx_not_empty_interrupt(&self, id: PioIRQ) {
unsafe {
write_bitmask_clear(
self.register_block().sm_irq[id.to_index()]
.irq_inte
.as_ptr(),
1 << SM::id(),
);
}
}
/// Force RX FIFO not empty interrupt.
pub fn force_rx_not_empty_interrupt(&self, id: PioIRQ, state: bool) {
let action = if state {
write_bitmask_set
} else {
write_bitmask_clear
};
unsafe {
action(
self.register_block().sm_irq[id.to_index()]
.irq_intf
.as_ptr(),
1 << SM::id(),
);
}
}
} }
/// PIO TX FIFO handle. /// PIO TX FIFO handle.
@ -1204,7 +1263,7 @@ impl<SM: ValidStateMachine> Tx<SM> {
} }
unsafe { unsafe {
let reg_ptr = self.register_block().txf[SM::id()].as_ptr() as *mut u32; let reg_ptr = self.fifo_address() as *mut u32;
reg_ptr.write_volatile(value); reg_ptr.write_volatile(value);
} }
@ -1238,7 +1297,7 @@ impl<SM: ValidStateMachine> Tx<SM> {
} }
unsafe { unsafe {
let reg_ptr = self.register_block().txf[SM::id()].as_ptr() as *mut u8; let reg_ptr = self.fifo_address() as *mut u8;
reg_ptr.write_volatile(value); reg_ptr.write_volatile(value);
} }
@ -1272,7 +1331,7 @@ impl<SM: ValidStateMachine> Tx<SM> {
} }
unsafe { unsafe {
let reg_ptr = self.register_block().txf[SM::id()].as_ptr() as *mut u16; let reg_ptr = self.fifo_address() as *mut u16;
reg_ptr.write_volatile(value); reg_ptr.write_volatile(value);
} }
@ -1343,35 +1402,69 @@ impl<SM: ValidStateMachine> Tx<SM> {
.write(|w| unsafe { w.sm0_instr().bits(instr) }) .write(|w| unsafe { w.sm0_instr().bits(instr) })
} }
} }
/// Enable TX FIFO not full interrupt.
///
/// This interrupt is raised when the TX FIFO is not full, i.e. one could push more data to it.
pub fn enable_tx_not_full_interrupt(&self, id: PioIRQ) {
unsafe {
write_bitmask_set(
self.register_block().sm_irq[id.to_index()]
.irq_inte
.as_ptr(),
1 << (SM::id() + 4),
);
}
}
/// Disable TX FIFO not full interrupt.
pub fn disable_tx_not_full_interrupt(&self, id: PioIRQ) {
unsafe {
write_bitmask_clear(
self.register_block().sm_irq[id.to_index()]
.irq_inte
.as_ptr(),
1 << (SM::id() + 4),
);
}
}
/// Force TX FIFO not full interrupt.
pub fn force_tx_not_full_interrupt(&self, id: PioIRQ) {
unsafe {
write_bitmask_set(
self.register_block().sm_irq[id.to_index()]
.irq_intf
.as_ptr(),
1 << (SM::id() + 4),
);
}
}
} }
/// PIO Interrupt controller. /// PIO Interrupt controller.
#[derive(Debug)] #[derive(Debug)]
pub struct Interrupt<P: PIOExt> { pub struct Interrupt<'a, P: PIOExt, const IRQ: usize> {
id: u8,
block: *const rp2040_pac::pio0::RegisterBlock, block: *const rp2040_pac::pio0::RegisterBlock,
_phantom: core::marker::PhantomData<P>, _phantom: core::marker::PhantomData<&'a P>,
} }
// Safety: `Interrupt` provides exclusive access to interrupt registers. // Safety: `Interrupt` provides exclusive access to interrupt registers.
unsafe impl<P: PIOExt> Send for Interrupt<P> {} unsafe impl<'a, P: PIOExt, const IRQ: usize> Send for Interrupt<'a, P, IRQ> {}
// Safety: `Interrupt` is marked Send so ensure all accesses remain atomic and no new concurrent // Safety: `Interrupt` is marked Send so ensure all accesses remain atomic and no new concurrent
// accesses are added. // accesses are added.
// `Interrupt` provides exclusive access to `irq_intf` to `irq_inte` for it's state machine, this // `Interrupt` provides exclusive access to `irq_intf` to `irq_inte` for it's state machine, this
// must remain true to satisfy Send. // must remain true to satisfy Send.
impl<P: PIOExt> Interrupt<P> { impl<'a, P: PIOExt, const IRQ: usize> Interrupt<'a, P, IRQ> {
/// Enable interrupts raised by state machines. /// Enable interrupts raised by state machines.
/// ///
/// The PIO peripheral has 4 outside visible interrupts that can be raised by the state machines. Note that this /// The PIO peripheral has 4 outside visible interrupts that can be raised by the state machines. Note that this
/// does not correspond with the state machine index; any state machine can raise any one of the four interrupts. /// does not correspond with the state machine index; any state machine can raise any one of the four interrupts.
pub fn enable_sm_interrupt(&self, id: u8) { pub fn enable_sm_interrupt(&self, id: u8) {
match id { assert!(id < 4, "invalid state machine interrupt number");
0 => self.irq().irq_inte.modify(|_, w| w.sm0().set_bit()), unsafe {
1 => self.irq().irq_inte.modify(|_, w| w.sm1().set_bit()), write_bitmask_set(self.irq().irq_inte.as_ptr(), 1 << (id + 8));
2 => self.irq().irq_inte.modify(|_, w| w.sm2().set_bit()),
3 => self.irq().irq_inte.modify(|_, w| w.sm3().set_bit()),
_ => panic!("invalid state machine interrupt number"),
} }
} }
@ -1379,12 +1472,9 @@ impl<P: PIOExt> Interrupt<P> {
/// ///
/// See [`Self::enable_sm_interrupt`] for info about the index. /// See [`Self::enable_sm_interrupt`] for info about the index.
pub fn disable_sm_interrupt(&self, id: u8) { pub fn disable_sm_interrupt(&self, id: u8) {
match id { assert!(id < 4, "invalid state machine interrupt number");
0 => self.irq().irq_inte.modify(|_, w| w.sm0().clear_bit()), unsafe {
1 => self.irq().irq_inte.modify(|_, w| w.sm1().clear_bit()), write_bitmask_clear(self.irq().irq_inte.as_ptr(), 1 << (id + 8));
2 => self.irq().irq_inte.modify(|_, w| w.sm2().clear_bit()),
3 => self.irq().irq_inte.modify(|_, w| w.sm3().clear_bit()),
_ => panic!("invalid state machine interrupt number"),
} }
} }
@ -1392,14 +1482,17 @@ impl<P: PIOExt> Interrupt<P> {
/// ///
/// Note that this doesn't affect the state seen by the state machine. For that, see [`PIO::force_irq`]. /// Note that this doesn't affect the state seen by the state machine. For that, see [`PIO::force_irq`].
/// ///
///
///
/// See [`Self::enable_sm_interrupt`] for info about the index. /// See [`Self::enable_sm_interrupt`] for info about the index.
pub fn force_sm_interrupt(&self, id: u8) { pub fn force_sm_interrupt(&self, id: u8, set: bool) {
match id { assert!(id < 4, "invalid state machine interrupt number");
0 => self.irq().irq_intf.modify(|_, w| w.sm0().set_bit()), unsafe {
1 => self.irq().irq_intf.modify(|_, w| w.sm1().set_bit()), if set {
2 => self.irq().irq_intf.modify(|_, w| w.sm2().set_bit()), write_bitmask_set(self.irq().irq_intf.as_ptr(), 1 << (id + 8));
3 => self.irq().irq_intf.modify(|_, w| w.sm3().set_bit()), } else {
_ => panic!("invalid state machine interrupt number"), write_bitmask_clear(self.irq().irq_intf.as_ptr(), 1 << (id + 8));
}
} }
} }
@ -1407,51 +1500,42 @@ impl<P: PIOExt> Interrupt<P> {
/// ///
/// Each of the 4 state machines have their own TX FIFO. This interrupt is raised when the TX FIFO is not full, i.e. /// Each of the 4 state machines have their own TX FIFO. This interrupt is raised when the TX FIFO is not full, i.e.
/// one could push more data to it. /// one could push more data to it.
#[deprecated(
since = "0.7.0",
note = "Use the dedicated method on the state machine"
)]
pub fn enable_tx_not_full_interrupt(&self, id: u8) { pub fn enable_tx_not_full_interrupt(&self, id: u8) {
match id { assert!(id < 4, "invalid state machine interrupt number");
0 => self.irq().irq_inte.modify(|_, w| w.sm0_txnfull().set_bit()), unsafe {
1 => self.irq().irq_inte.modify(|_, w| w.sm1_txnfull().set_bit()), write_bitmask_set(self.irq().irq_inte.as_ptr(), 1 << (id + 4));
2 => self.irq().irq_inte.modify(|_, w| w.sm2_txnfull().set_bit()),
3 => self.irq().irq_inte.modify(|_, w| w.sm3_txnfull().set_bit()),
_ => panic!("invalid state machine interrupt number"),
} }
} }
/// Disable TX FIFO not full interrupt. /// Disable TX FIFO not full interrupt.
/// ///
/// See [`Self::enable_tx_not_full_interrupt`] for info about the index. /// See [`Self::enable_tx_not_full_interrupt`] for info about the index.
#[deprecated(
since = "0.7.0",
note = "Use the dedicated method on the state machine"
)]
pub fn disable_tx_not_full_interrupt(&self, id: u8) { pub fn disable_tx_not_full_interrupt(&self, id: u8) {
match id { assert!(id < 4, "invalid state machine interrupt number");
0 => self unsafe {
.irq() write_bitmask_clear(self.irq().irq_inte.as_ptr(), 1 << (id + 4));
.irq_inte
.modify(|_, w| w.sm0_txnfull().clear_bit()),
1 => self
.irq()
.irq_inte
.modify(|_, w| w.sm1_txnfull().clear_bit()),
2 => self
.irq()
.irq_inte
.modify(|_, w| w.sm2_txnfull().clear_bit()),
3 => self
.irq()
.irq_inte
.modify(|_, w| w.sm3_txnfull().clear_bit()),
_ => panic!("invalid state machine interrupt number"),
} }
} }
/// Force TX FIFO not full interrupt. /// Force TX FIFO not full interrupt.
/// ///
/// See [`Self::enable_tx_not_full_interrupt`] for info about the index. /// See [`Self::enable_tx_not_full_interrupt`] for info about the index.
#[deprecated(
since = "0.7.0",
note = "Use the dedicated method on the state machine"
)]
pub fn force_tx_not_full_interrupt(&self, id: u8) { pub fn force_tx_not_full_interrupt(&self, id: u8) {
match id { assert!(id < 4, "invalid state machine interrupt number");
0 => self.irq().irq_intf.modify(|_, w| w.sm0_txnfull().set_bit()), unsafe {
1 => self.irq().irq_intf.modify(|_, w| w.sm1_txnfull().set_bit()), write_bitmask_set(self.irq().irq_intf.as_ptr(), 1 << (id + 4));
2 => self.irq().irq_intf.modify(|_, w| w.sm2_txnfull().set_bit()),
3 => self.irq().irq_intf.modify(|_, w| w.sm3_txnfull().set_bit()),
_ => panic!("invalid state machine interrupt number"),
} }
} }
@ -1459,75 +1543,42 @@ impl<P: PIOExt> Interrupt<P> {
/// ///
/// Each of the 4 state machines have their own RX FIFO. This interrupt is raised when the RX FIFO is not empty, /// Each of the 4 state machines have their own RX FIFO. This interrupt is raised when the RX FIFO is not empty,
/// i.e. one could read more data from it. /// i.e. one could read more data from it.
#[deprecated(
since = "0.7.0",
note = "Use the dedicated method on the state machine"
)]
pub fn enable_rx_not_empty_interrupt(&self, id: u8) { pub fn enable_rx_not_empty_interrupt(&self, id: u8) {
match id { assert!(id < 4, "invalid state machine interrupt number");
0 => self unsafe {
.irq() write_bitmask_set(self.irq().irq_inte.as_ptr(), 1 << id);
.irq_inte
.modify(|_, w| w.sm0_rxnempty().set_bit()),
1 => self
.irq()
.irq_inte
.modify(|_, w| w.sm1_rxnempty().set_bit()),
2 => self
.irq()
.irq_inte
.modify(|_, w| w.sm2_rxnempty().set_bit()),
3 => self
.irq()
.irq_inte
.modify(|_, w| w.sm3_rxnempty().set_bit()),
_ => panic!("invalid state machine interrupt number"),
} }
} }
/// Disable RX FIFO not empty interrupt. /// Disable RX FIFO not empty interrupt.
/// ///
/// See [`Self::enable_rx_not_empty_interrupt`] for info about the index. /// See [`Self::enable_rx_not_empty_interrupt`] for info about the index.
#[deprecated(
since = "0.7.0",
note = "Use the dedicated method on the state machine"
)]
pub fn disable_rx_not_empty_interrupt(&self, id: u8) { pub fn disable_rx_not_empty_interrupt(&self, id: u8) {
match id { assert!(id < 4, "invalid state machine interrupt number");
0 => self unsafe {
.irq() write_bitmask_clear(self.irq().irq_inte.as_ptr(), 1 << id);
.irq_inte
.modify(|_, w| w.sm0_rxnempty().clear_bit()),
1 => self
.irq()
.irq_inte
.modify(|_, w| w.sm1_rxnempty().clear_bit()),
2 => self
.irq()
.irq_inte
.modify(|_, w| w.sm2_rxnempty().clear_bit()),
3 => self
.irq()
.irq_inte
.modify(|_, w| w.sm3_rxnempty().clear_bit()),
_ => panic!("invalid state machine interrupt number"),
} }
} }
/// Force RX FIFO not empty interrupt. /// Force RX FIFO not empty interrupt.
/// ///
/// See [`Self::enable_rx_not_empty_interrupt`] for info about the index. /// See [`Self::enable_rx_not_empty_interrupt`] for info about the index.
#[deprecated(
since = "0.7.0",
note = "Use the dedicated method on the state machine"
)]
pub fn force_rx_not_empty_interrupt(&self, id: u8) { pub fn force_rx_not_empty_interrupt(&self, id: u8) {
match id { assert!(id < 4, "invalid state machine interrupt number");
0 => self unsafe {
.irq() write_bitmask_set(self.irq().irq_intf.as_ptr(), 1 << id);
.irq_intf
.modify(|_, w| w.sm0_rxnempty().set_bit()),
1 => self
.irq()
.irq_intf
.modify(|_, w| w.sm1_rxnempty().set_bit()),
2 => self
.irq()
.irq_intf
.modify(|_, w| w.sm2_rxnempty().set_bit()),
3 => self
.irq()
.irq_intf
.modify(|_, w| w.sm3_rxnempty().set_bit()),
_ => panic!("invalid state machine interrupt number"),
} }
} }
@ -1550,7 +1601,7 @@ impl<P: PIOExt> Interrupt<P> {
} }
fn irq(&self) -> &rp2040_pac::pio0::SM_IRQ { fn irq(&self) -> &rp2040_pac::pio0::SM_IRQ {
&self.register_block().sm_irq[self.id as usize] &self.register_block().sm_irq[IRQ]
} }
} }