Merge pull request #1707 from pennae/rp-pio-load
rp: relocate programs implicitly during load
This commit is contained in:
commit
e3cc0d168c
@ -8,7 +8,6 @@ use cyw43::SpiBusCyw43;
|
|||||||
use embassy_rp::dma::Channel;
|
use embassy_rp::dma::Channel;
|
||||||
use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate};
|
use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate};
|
||||||
use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine};
|
use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine};
|
||||||
use embassy_rp::relocate::RelocatedProgram;
|
|
||||||
use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef};
|
use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef};
|
||||||
use fixed::FixedU32;
|
use fixed::FixedU32;
|
||||||
use pio_proc::pio_asm;
|
use pio_proc::pio_asm;
|
||||||
@ -88,8 +87,6 @@ where
|
|||||||
".wrap"
|
".wrap"
|
||||||
);
|
);
|
||||||
|
|
||||||
let relocated = RelocatedProgram::new(&program.program);
|
|
||||||
|
|
||||||
let mut pin_io: embassy_rp::pio::Pin<PIO> = common.make_pio_pin(dio);
|
let mut pin_io: embassy_rp::pio::Pin<PIO> = common.make_pio_pin(dio);
|
||||||
pin_io.set_pull(Pull::None);
|
pin_io.set_pull(Pull::None);
|
||||||
pin_io.set_schmitt(true);
|
pin_io.set_schmitt(true);
|
||||||
@ -102,7 +99,8 @@ where
|
|||||||
pin_clk.set_slew_rate(SlewRate::Fast);
|
pin_clk.set_slew_rate(SlewRate::Fast);
|
||||||
|
|
||||||
let mut cfg = Config::default();
|
let mut cfg = Config::default();
|
||||||
cfg.use_program(&common.load_program(&relocated), &[&pin_clk]);
|
let loaded_program = common.load_program(&program.program);
|
||||||
|
cfg.use_program(&loaded_program, &[&pin_clk]);
|
||||||
cfg.set_out_pins(&[&pin_io]);
|
cfg.set_out_pins(&[&pin_io]);
|
||||||
cfg.set_in_pins(&[&pin_io]);
|
cfg.set_in_pins(&[&pin_io]);
|
||||||
cfg.set_set_pins(&[&pin_io]);
|
cfg.set_set_pins(&[&pin_io]);
|
||||||
@ -142,7 +140,7 @@ where
|
|||||||
sm,
|
sm,
|
||||||
irq,
|
irq,
|
||||||
dma: dma.into_ref(),
|
dma: dma.into_ref(),
|
||||||
wrap_target: relocated.wrap().target,
|
wrap_target: loaded_program.wrap.target,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ pub mod watchdog;
|
|||||||
// TODO: move `pio_instr_util` and `relocate` to inside `pio`
|
// TODO: move `pio_instr_util` and `relocate` to inside `pio`
|
||||||
pub mod pio;
|
pub mod pio;
|
||||||
pub mod pio_instr_util;
|
pub mod pio_instr_util;
|
||||||
pub mod relocate;
|
pub(crate) mod relocate;
|
||||||
|
|
||||||
// Reexports
|
// Reexports
|
||||||
pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
|
pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
|
||||||
|
@ -11,7 +11,7 @@ use fixed::types::extra::U8;
|
|||||||
use fixed::FixedU32;
|
use fixed::FixedU32;
|
||||||
use pac::io::vals::Gpio0ctrlFuncsel;
|
use pac::io::vals::Gpio0ctrlFuncsel;
|
||||||
use pac::pio::vals::SmExecctrlStatusSel;
|
use pac::pio::vals::SmExecctrlStatusSel;
|
||||||
use pio::{SideSet, Wrap};
|
use pio::{Program, SideSet, Wrap};
|
||||||
|
|
||||||
use crate::dma::{Channel, Transfer, Word};
|
use crate::dma::{Channel, Transfer, Word};
|
||||||
use crate::gpio::sealed::Pin as SealedPin;
|
use crate::gpio::sealed::Pin as SealedPin;
|
||||||
@ -734,23 +734,67 @@ pub struct InstanceMemory<'d, PIO: Instance> {
|
|||||||
|
|
||||||
pub struct LoadedProgram<'d, PIO: Instance> {
|
pub struct LoadedProgram<'d, PIO: Instance> {
|
||||||
pub used_memory: InstanceMemory<'d, PIO>,
|
pub used_memory: InstanceMemory<'d, PIO>,
|
||||||
origin: u8,
|
pub origin: u8,
|
||||||
wrap: Wrap,
|
pub wrap: Wrap,
|
||||||
side_set: SideSet,
|
pub side_set: SideSet,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum LoadError {
|
||||||
|
/// Insufficient consecutive free instruction space to load program.
|
||||||
|
InsufficientSpace,
|
||||||
|
/// Loading the program would overwrite an instruction address already
|
||||||
|
/// used by another program.
|
||||||
|
AddressInUse(usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, PIO: Instance> Common<'d, PIO> {
|
impl<'d, PIO: Instance> Common<'d, PIO> {
|
||||||
pub fn load_program<const SIZE: usize>(&mut self, prog: &RelocatedProgram<SIZE>) -> LoadedProgram<'d, PIO> {
|
/// Load a PIO program. This will automatically relocate the program to
|
||||||
|
/// an available chunk of free instruction memory if the program origin
|
||||||
|
/// was not explicitly specified, otherwise it will attempt to load the
|
||||||
|
/// program only at its origin.
|
||||||
|
pub fn load_program<const SIZE: usize>(&mut self, prog: &Program<SIZE>) -> LoadedProgram<'d, PIO> {
|
||||||
match self.try_load_program(prog) {
|
match self.try_load_program(prog) {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(at) => panic!("Trying to write already used PIO instruction memory at {}", at),
|
Err(e) => panic!("Failed to load PIO program: {:?}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load a PIO program. This will automatically relocate the program to
|
||||||
|
/// an available chunk of free instruction memory if the program origin
|
||||||
|
/// was not explicitly specified, otherwise it will attempt to load the
|
||||||
|
/// program only at its origin.
|
||||||
pub fn try_load_program<const SIZE: usize>(
|
pub fn try_load_program<const SIZE: usize>(
|
||||||
&mut self,
|
&mut self,
|
||||||
prog: &RelocatedProgram<SIZE>,
|
prog: &Program<SIZE>,
|
||||||
|
) -> Result<LoadedProgram<'d, PIO>, LoadError> {
|
||||||
|
match prog.origin {
|
||||||
|
Some(origin) => self
|
||||||
|
.try_load_program_at(prog, origin)
|
||||||
|
.map_err(|a| LoadError::AddressInUse(a)),
|
||||||
|
None => {
|
||||||
|
// naively search for free space, allowing wraparound since
|
||||||
|
// PIO does support that. with only 32 instruction slots it
|
||||||
|
// doesn't make much sense to do anything more fancy.
|
||||||
|
let mut origin = 0;
|
||||||
|
while origin < 32 {
|
||||||
|
match self.try_load_program_at(prog, origin as _) {
|
||||||
|
Ok(r) => return Ok(r),
|
||||||
|
Err(a) => origin = a + 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(LoadError::InsufficientSpace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_load_program_at<const SIZE: usize>(
|
||||||
|
&mut self,
|
||||||
|
prog: &Program<SIZE>,
|
||||||
|
origin: u8,
|
||||||
) -> Result<LoadedProgram<'d, PIO>, usize> {
|
) -> Result<LoadedProgram<'d, PIO>, usize> {
|
||||||
|
let prog = RelocatedProgram::new_with_origin(prog, origin);
|
||||||
let used_memory = self.try_write_instr(prog.origin() as _, prog.code())?;
|
let used_memory = self.try_write_instr(prog.origin() as _, prog.code())?;
|
||||||
Ok(LoadedProgram {
|
Ok(LoadedProgram {
|
||||||
used_memory,
|
used_memory,
|
||||||
@ -760,7 +804,7 @@ impl<'d, PIO: Instance> Common<'d, PIO> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_write_instr<I>(&mut self, start: usize, instrs: I) -> Result<InstanceMemory<'d, PIO>, usize>
|
fn try_write_instr<I>(&mut self, start: usize, instrs: I) -> Result<InstanceMemory<'d, PIO>, usize>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = u16>,
|
I: Iterator<Item = u16>,
|
||||||
{
|
{
|
||||||
|
@ -41,11 +41,6 @@ pub struct RelocatedProgram<'a, const PROGRAM_SIZE: usize> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> {
|
impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> {
|
||||||
pub fn new(program: &Program<PROGRAM_SIZE>) -> RelocatedProgram<PROGRAM_SIZE> {
|
|
||||||
let origin = program.origin.unwrap_or(0);
|
|
||||||
RelocatedProgram { program, origin }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_with_origin(program: &Program<PROGRAM_SIZE>, origin: u8) -> RelocatedProgram<PROGRAM_SIZE> {
|
pub fn new_with_origin(program: &Program<PROGRAM_SIZE>, origin: u8) -> RelocatedProgram<PROGRAM_SIZE> {
|
||||||
RelocatedProgram { program, origin }
|
RelocatedProgram { program, origin }
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ use embassy_executor::Spawner;
|
|||||||
use embassy_rp::bind_interrupts;
|
use embassy_rp::bind_interrupts;
|
||||||
use embassy_rp::peripherals::PIO0;
|
use embassy_rp::peripherals::PIO0;
|
||||||
use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine};
|
use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine};
|
||||||
use embassy_rp::relocate::RelocatedProgram;
|
|
||||||
use fixed::traits::ToFixed;
|
use fixed::traits::ToFixed;
|
||||||
use fixed_macro::types::U56F8;
|
use fixed_macro::types::U56F8;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
@ -29,9 +28,8 @@ fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a,
|
|||||||
".wrap",
|
".wrap",
|
||||||
);
|
);
|
||||||
|
|
||||||
let relocated = RelocatedProgram::new(&prg.program);
|
|
||||||
let mut cfg = Config::default();
|
let mut cfg = Config::default();
|
||||||
cfg.use_program(&pio.load_program(&relocated), &[]);
|
cfg.use_program(&pio.load_program(&prg.program), &[]);
|
||||||
let out_pin = pio.make_pio_pin(pin);
|
let out_pin = pio.make_pio_pin(pin);
|
||||||
cfg.set_out_pins(&[&out_pin]);
|
cfg.set_out_pins(&[&out_pin]);
|
||||||
cfg.set_set_pins(&[&out_pin]);
|
cfg.set_set_pins(&[&out_pin]);
|
||||||
@ -65,9 +63,8 @@ fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a,
|
|||||||
".wrap",
|
".wrap",
|
||||||
);
|
);
|
||||||
|
|
||||||
let relocated = RelocatedProgram::new(&prg.program);
|
|
||||||
let mut cfg = Config::default();
|
let mut cfg = Config::default();
|
||||||
cfg.use_program(&pio.load_program(&relocated), &[]);
|
cfg.use_program(&pio.load_program(&prg.program), &[]);
|
||||||
cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
|
cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
|
||||||
cfg.shift_in.auto_fill = true;
|
cfg.shift_in.auto_fill = true;
|
||||||
cfg.shift_in.direction = ShiftDirection::Right;
|
cfg.shift_in.direction = ShiftDirection::Right;
|
||||||
@ -96,9 +93,8 @@ fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a,
|
|||||||
"irq 3 [15]",
|
"irq 3 [15]",
|
||||||
".wrap",
|
".wrap",
|
||||||
);
|
);
|
||||||
let relocated = RelocatedProgram::new(&prg.program);
|
|
||||||
let mut cfg = Config::default();
|
let mut cfg = Config::default();
|
||||||
cfg.use_program(&pio.load_program(&relocated), &[]);
|
cfg.use_program(&pio.load_program(&prg.program), &[]);
|
||||||
cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
|
cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
|
||||||
sm.set_config(&cfg);
|
sm.set_config(&cfg);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ use embassy_executor::Spawner;
|
|||||||
use embassy_futures::join::join;
|
use embassy_futures::join::join;
|
||||||
use embassy_rp::peripherals::PIO0;
|
use embassy_rp::peripherals::PIO0;
|
||||||
use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection};
|
use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection};
|
||||||
use embassy_rp::relocate::RelocatedProgram;
|
|
||||||
use embassy_rp::{bind_interrupts, Peripheral};
|
use embassy_rp::{bind_interrupts, Peripheral};
|
||||||
use fixed::traits::ToFixed;
|
use fixed::traits::ToFixed;
|
||||||
use fixed_macro::types::U56F8;
|
use fixed_macro::types::U56F8;
|
||||||
@ -46,9 +45,8 @@ async fn main(_spawner: Spawner) {
|
|||||||
".wrap",
|
".wrap",
|
||||||
);
|
);
|
||||||
|
|
||||||
let relocated = RelocatedProgram::new(&prg.program);
|
|
||||||
let mut cfg = Config::default();
|
let mut cfg = Config::default();
|
||||||
cfg.use_program(&common.load_program(&relocated), &[]);
|
cfg.use_program(&common.load_program(&prg.program), &[]);
|
||||||
cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed();
|
cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed();
|
||||||
cfg.shift_in = ShiftConfig {
|
cfg.shift_in = ShiftConfig {
|
||||||
auto_fill: true,
|
auto_fill: true,
|
||||||
|
@ -14,7 +14,6 @@ use embassy_rp::pio::{
|
|||||||
Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
|
Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
|
||||||
};
|
};
|
||||||
use embassy_rp::pwm::{self, Pwm};
|
use embassy_rp::pwm::{self, Pwm};
|
||||||
use embassy_rp::relocate::RelocatedProgram;
|
|
||||||
use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef};
|
use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef};
|
||||||
use embassy_time::{Duration, Instant, Timer};
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
@ -127,9 +126,8 @@ impl<'l> HD44780<'l> {
|
|||||||
|
|
||||||
sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
|
sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
|
||||||
|
|
||||||
let relocated = RelocatedProgram::new(&prg.program);
|
|
||||||
let mut cfg = Config::default();
|
let mut cfg = Config::default();
|
||||||
cfg.use_program(&common.load_program(&relocated), &[&e]);
|
cfg.use_program(&common.load_program(&prg.program), &[&e]);
|
||||||
cfg.clock_divider = 125u8.into();
|
cfg.clock_divider = 125u8.into();
|
||||||
cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
|
cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
|
||||||
cfg.shift_out = ShiftConfig {
|
cfg.shift_out = ShiftConfig {
|
||||||
@ -201,9 +199,8 @@ impl<'l> HD44780<'l> {
|
|||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
|
|
||||||
let relocated = RelocatedProgram::new(&prg.program);
|
|
||||||
let mut cfg = Config::default();
|
let mut cfg = Config::default();
|
||||||
cfg.use_program(&common.load_program(&relocated), &[&e]);
|
cfg.use_program(&common.load_program(&prg.program), &[&e]);
|
||||||
cfg.clock_divider = 8u8.into(); // ~64ns/insn
|
cfg.clock_divider = 8u8.into(); // ~64ns/insn
|
||||||
cfg.set_jmp_pin(&db7);
|
cfg.set_jmp_pin(&db7);
|
||||||
cfg.set_set_pins(&[&rs, &rw]);
|
cfg.set_set_pins(&[&rs, &rw]);
|
||||||
|
@ -222,8 +222,8 @@ mod uart {
|
|||||||
mut common, sm0, sm1, ..
|
mut common, sm0, sm1, ..
|
||||||
} = Pio::new(pio, Irqs);
|
} = Pio::new(pio, Irqs);
|
||||||
|
|
||||||
let (tx, origin) = PioUartTx::new(&mut common, sm0, tx_pin, baud, None);
|
let tx = PioUartTx::new(&mut common, sm0, tx_pin, baud);
|
||||||
let (rx, _) = PioUartRx::new(&mut common, sm1, rx_pin, baud, Some(origin));
|
let rx = PioUartRx::new(&mut common, sm1, rx_pin, baud);
|
||||||
|
|
||||||
PioUart { tx, rx }
|
PioUart { tx, rx }
|
||||||
}
|
}
|
||||||
@ -240,7 +240,6 @@ mod uart_tx {
|
|||||||
use embassy_rp::gpio::Level;
|
use embassy_rp::gpio::Level;
|
||||||
use embassy_rp::peripherals::PIO0;
|
use embassy_rp::peripherals::PIO0;
|
||||||
use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine};
|
use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine};
|
||||||
use embassy_rp::relocate::RelocatedProgram;
|
|
||||||
use embedded_io::asynch::Write;
|
use embedded_io::asynch::Write;
|
||||||
use embedded_io::Io;
|
use embedded_io::Io;
|
||||||
use fixed::traits::ToFixed;
|
use fixed::traits::ToFixed;
|
||||||
@ -256,9 +255,8 @@ mod uart_tx {
|
|||||||
mut sm_tx: StateMachine<'a, PIO0, 0>,
|
mut sm_tx: StateMachine<'a, PIO0, 0>,
|
||||||
tx_pin: impl PioPin,
|
tx_pin: impl PioPin,
|
||||||
baud: u64,
|
baud: u64,
|
||||||
origin: Option<u8>,
|
) -> Self {
|
||||||
) -> (Self, u8) {
|
let prg = pio_proc::pio_asm!(
|
||||||
let mut prg = pio_proc::pio_asm!(
|
|
||||||
r#"
|
r#"
|
||||||
.side_set 1 opt
|
.side_set 1 opt
|
||||||
|
|
||||||
@ -272,17 +270,14 @@ mod uart_tx {
|
|||||||
jmp x-- bitloop [6] ; Each loop iteration is 8 cycles.
|
jmp x-- bitloop [6] ; Each loop iteration is 8 cycles.
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
prg.program.origin = origin;
|
|
||||||
let tx_pin = common.make_pio_pin(tx_pin);
|
let tx_pin = common.make_pio_pin(tx_pin);
|
||||||
sm_tx.set_pins(Level::High, &[&tx_pin]);
|
sm_tx.set_pins(Level::High, &[&tx_pin]);
|
||||||
sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]);
|
sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]);
|
||||||
|
|
||||||
let relocated = RelocatedProgram::new(&prg.program);
|
|
||||||
|
|
||||||
let mut cfg = Config::default();
|
let mut cfg = Config::default();
|
||||||
|
|
||||||
cfg.set_out_pins(&[&tx_pin]);
|
cfg.set_out_pins(&[&tx_pin]);
|
||||||
cfg.use_program(&common.load_program(&relocated), &[&tx_pin]);
|
cfg.use_program(&common.load_program(&prg.program), &[&tx_pin]);
|
||||||
cfg.shift_out.auto_fill = false;
|
cfg.shift_out.auto_fill = false;
|
||||||
cfg.shift_out.direction = ShiftDirection::Right;
|
cfg.shift_out.direction = ShiftDirection::Right;
|
||||||
cfg.fifo_join = FifoJoin::TxOnly;
|
cfg.fifo_join = FifoJoin::TxOnly;
|
||||||
@ -290,18 +285,7 @@ mod uart_tx {
|
|||||||
sm_tx.set_config(&cfg);
|
sm_tx.set_config(&cfg);
|
||||||
sm_tx.set_enable(true);
|
sm_tx.set_enable(true);
|
||||||
|
|
||||||
// The 4 state machines of the PIO each have their own program counter that starts taking
|
Self { sm_tx }
|
||||||
// instructions at an offset (origin) of the 32 instruction "space" the PIO device has.
|
|
||||||
// It is up to the programmer to sort out where to place these instructions.
|
|
||||||
// From the pio_asm! macro you get a ProgramWithDefines which has a field .program.origin
|
|
||||||
// which takes an Option<u8>.
|
|
||||||
//
|
|
||||||
// When you load more than one RelocatedProgram into the PIO,
|
|
||||||
// you load your first program at origin = 0.
|
|
||||||
// The RelocatedProgram has .code().count() which returns a usize,
|
|
||||||
// for which you can then use as your next program's origin.
|
|
||||||
let offset = relocated.code().count() as u8 + origin.unwrap_or_default();
|
|
||||||
(Self { sm_tx }, offset)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn write_u8(&mut self, data: u8) {
|
pub async fn write_u8(&mut self, data: u8) {
|
||||||
@ -329,7 +313,6 @@ mod uart_rx {
|
|||||||
use embassy_rp::gpio::Level;
|
use embassy_rp::gpio::Level;
|
||||||
use embassy_rp::peripherals::PIO0;
|
use embassy_rp::peripherals::PIO0;
|
||||||
use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine};
|
use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine};
|
||||||
use embassy_rp::relocate::RelocatedProgram;
|
|
||||||
use embedded_io::asynch::Read;
|
use embedded_io::asynch::Read;
|
||||||
use embedded_io::Io;
|
use embedded_io::Io;
|
||||||
use fixed::traits::ToFixed;
|
use fixed::traits::ToFixed;
|
||||||
@ -345,9 +328,8 @@ mod uart_rx {
|
|||||||
mut sm_rx: StateMachine<'a, PIO0, 1>,
|
mut sm_rx: StateMachine<'a, PIO0, 1>,
|
||||||
rx_pin: impl PioPin,
|
rx_pin: impl PioPin,
|
||||||
baud: u64,
|
baud: u64,
|
||||||
origin: Option<u8>,
|
) -> Self {
|
||||||
) -> (Self, u8) {
|
let prg = pio_proc::pio_asm!(
|
||||||
let mut prg = pio_proc::pio_asm!(
|
|
||||||
r#"
|
r#"
|
||||||
; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and
|
; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and
|
||||||
; break conditions more gracefully.
|
; break conditions more gracefully.
|
||||||
@ -369,10 +351,8 @@ mod uart_rx {
|
|||||||
push ; important in case the TX clock is slightly too fast.
|
push ; important in case the TX clock is slightly too fast.
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
prg.program.origin = origin;
|
|
||||||
let relocated = RelocatedProgram::new(&prg.program);
|
|
||||||
let mut cfg = Config::default();
|
let mut cfg = Config::default();
|
||||||
cfg.use_program(&common.load_program(&relocated), &[]);
|
cfg.use_program(&common.load_program(&prg.program), &[]);
|
||||||
|
|
||||||
let rx_pin = common.make_pio_pin(rx_pin);
|
let rx_pin = common.make_pio_pin(rx_pin);
|
||||||
sm_rx.set_pins(Level::High, &[&rx_pin]);
|
sm_rx.set_pins(Level::High, &[&rx_pin]);
|
||||||
@ -387,8 +367,7 @@ mod uart_rx {
|
|||||||
sm_rx.set_config(&cfg);
|
sm_rx.set_config(&cfg);
|
||||||
sm_rx.set_enable(true);
|
sm_rx.set_enable(true);
|
||||||
|
|
||||||
let offset = relocated.code().count() as u8 + origin.unwrap_or_default();
|
Self { sm_rx }
|
||||||
(Self { sm_rx }, offset)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_u8(&mut self) -> u8 {
|
pub async fn read_u8(&mut self) -> u8 {
|
||||||
|
@ -12,7 +12,6 @@ use embassy_rp::peripherals::PIO0;
|
|||||||
use embassy_rp::pio::{
|
use embassy_rp::pio::{
|
||||||
Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
|
Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
|
||||||
};
|
};
|
||||||
use embassy_rp::relocate::RelocatedProgram;
|
|
||||||
use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
|
use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
use fixed::types::U24F8;
|
use fixed::types::U24F8;
|
||||||
@ -73,8 +72,7 @@ impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> {
|
|||||||
cfg.set_out_pins(&[&out_pin]);
|
cfg.set_out_pins(&[&out_pin]);
|
||||||
cfg.set_set_pins(&[&out_pin]);
|
cfg.set_set_pins(&[&out_pin]);
|
||||||
|
|
||||||
let relocated = RelocatedProgram::new(&prg);
|
cfg.use_program(&pio.load_program(&prg), &[&out_pin]);
|
||||||
cfg.use_program(&pio.load_program(&relocated), &[&out_pin]);
|
|
||||||
|
|
||||||
// Clock config, measured in kHz to avoid overflows
|
// Clock config, measured in kHz to avoid overflows
|
||||||
// TODO CLOCK_FREQ should come from embassy_rp
|
// TODO CLOCK_FREQ should come from embassy_rp
|
||||||
|
@ -9,7 +9,6 @@ use embassy_executor::Spawner;
|
|||||||
use embassy_rp::bind_interrupts;
|
use embassy_rp::bind_interrupts;
|
||||||
use embassy_rp::peripherals::PIO0;
|
use embassy_rp::peripherals::PIO0;
|
||||||
use embassy_rp::pio::{Config, InterruptHandler, Pio};
|
use embassy_rp::pio::{Config, InterruptHandler, Pio};
|
||||||
use embassy_rp::relocate::RelocatedProgram;
|
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
bind_interrupts!(struct Irqs {
|
bind_interrupts!(struct Irqs {
|
||||||
@ -35,9 +34,8 @@ async fn main(_spawner: Spawner) {
|
|||||||
"irq wait 1",
|
"irq wait 1",
|
||||||
);
|
);
|
||||||
|
|
||||||
let relocated = RelocatedProgram::new(&prg.program);
|
|
||||||
let mut cfg = Config::default();
|
let mut cfg = Config::default();
|
||||||
cfg.use_program(&common.load_program(&relocated), &[]);
|
cfg.use_program(&common.load_program(&prg.program), &[]);
|
||||||
sm.set_config(&cfg);
|
sm.set_config(&cfg);
|
||||||
sm.set_enable(true);
|
sm.set_enable(true);
|
||||||
|
|
||||||
|
126
tests/rp/src/bin/pio_multi_load.rs
Normal file
126
tests/rp/src/bin/pio_multi_load.rs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(type_alias_impl_trait)]
|
||||||
|
#[path = "../common.rs"]
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
use defmt::info;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::bind_interrupts;
|
||||||
|
use embassy_rp::peripherals::PIO0;
|
||||||
|
use embassy_rp::pio::{Config, InterruptHandler, LoadError, Pio};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
bind_interrupts!(struct Irqs {
|
||||||
|
PIO0_IRQ_0 => InterruptHandler<PIO0>;
|
||||||
|
});
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
let p = embassy_rp::init(Default::default());
|
||||||
|
let pio = p.PIO0;
|
||||||
|
let Pio {
|
||||||
|
mut common,
|
||||||
|
mut sm0,
|
||||||
|
mut sm1,
|
||||||
|
mut sm2,
|
||||||
|
irq_flags,
|
||||||
|
..
|
||||||
|
} = Pio::new(pio, Irqs);
|
||||||
|
|
||||||
|
// load with explicit origin works
|
||||||
|
let prg1 = pio_proc::pio_asm!(
|
||||||
|
".origin 4"
|
||||||
|
"nop",
|
||||||
|
"nop",
|
||||||
|
"nop",
|
||||||
|
"nop",
|
||||||
|
"nop",
|
||||||
|
"nop",
|
||||||
|
"nop",
|
||||||
|
"irq 0",
|
||||||
|
"nop",
|
||||||
|
"nop",
|
||||||
|
);
|
||||||
|
let loaded1 = common.load_program(&prg1.program);
|
||||||
|
assert_eq!(loaded1.origin, 4);
|
||||||
|
assert_eq!(loaded1.wrap.source, 13);
|
||||||
|
assert_eq!(loaded1.wrap.target, 4);
|
||||||
|
|
||||||
|
// load without origin chooses a free space
|
||||||
|
let prg2 = pio_proc::pio_asm!("nop", "nop", "nop", "nop", "nop", "nop", "nop", "irq 1", "nop", "nop",);
|
||||||
|
let loaded2 = common.load_program(&prg2.program);
|
||||||
|
assert_eq!(loaded2.origin, 14);
|
||||||
|
assert_eq!(loaded2.wrap.source, 23);
|
||||||
|
assert_eq!(loaded2.wrap.target, 14);
|
||||||
|
|
||||||
|
// wrapping around the end of program space automatically works
|
||||||
|
let prg3 =
|
||||||
|
pio_proc::pio_asm!("nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "irq 2",);
|
||||||
|
let loaded3 = common.load_program(&prg3.program);
|
||||||
|
assert_eq!(loaded3.origin, 24);
|
||||||
|
assert_eq!(loaded3.wrap.source, 3);
|
||||||
|
assert_eq!(loaded3.wrap.target, 24);
|
||||||
|
|
||||||
|
// check that the programs actually work
|
||||||
|
{
|
||||||
|
let mut cfg = Config::default();
|
||||||
|
cfg.use_program(&loaded1, &[]);
|
||||||
|
sm0.set_config(&cfg);
|
||||||
|
sm0.set_enable(true);
|
||||||
|
while !irq_flags.check(0) {}
|
||||||
|
sm0.set_enable(false);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cfg = Config::default();
|
||||||
|
cfg.use_program(&loaded2, &[]);
|
||||||
|
sm1.set_config(&cfg);
|
||||||
|
sm1.set_enable(true);
|
||||||
|
while !irq_flags.check(1) {}
|
||||||
|
sm1.set_enable(false);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut cfg = Config::default();
|
||||||
|
cfg.use_program(&loaded3, &[]);
|
||||||
|
sm2.set_config(&cfg);
|
||||||
|
sm2.set_enable(true);
|
||||||
|
while !irq_flags.check(2) {}
|
||||||
|
sm2.set_enable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// instruction memory is full now. all loads should fail.
|
||||||
|
{
|
||||||
|
let prg = pio_proc::pio_asm!(".origin 0", "nop");
|
||||||
|
match common.try_load_program(&prg.program) {
|
||||||
|
Err(LoadError::AddressInUse(0)) => (),
|
||||||
|
_ => panic!("program loaded when it shouldn't"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let prg = pio_proc::pio_asm!("nop");
|
||||||
|
match common.try_load_program(&prg.program) {
|
||||||
|
Err(LoadError::InsufficientSpace) => (),
|
||||||
|
_ => panic!("program loaded when it shouldn't"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// freeing some memory should allow further loads though.
|
||||||
|
unsafe {
|
||||||
|
common.free_instr(loaded3.used_memory);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let prg = pio_proc::pio_asm!(".origin 0", "nop");
|
||||||
|
match common.try_load_program(&prg.program) {
|
||||||
|
Ok(_) => (),
|
||||||
|
_ => panic!("program didn't loaded when it shouldn"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let prg = pio_proc::pio_asm!("nop");
|
||||||
|
match common.try_load_program(&prg.program) {
|
||||||
|
Ok(_) => (),
|
||||||
|
_ => panic!("program didn't loaded when it shouldn"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Test OK");
|
||||||
|
cortex_m::asm::bkpt();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user