aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpennae <[email protected]>2023-07-28 18:45:57 +0200
committerpennae <[email protected]>2023-07-28 19:33:02 +0200
commitcbc8871a0bb40eb5fec82e7c27ed4c0127844c3c (patch)
tree333ab4bc2d1893d42529c6a378ea51c42f8c8d22
parentd752a3f9808ec9be64c720d3f80f152f0b7507df (diff)
rp: relocate programs implicitly during load
this removed the RelocatedProgram construction step from pio uses. there's not all that much to be said for the extra step because the origin can be set on the input program itself, and the remaining information exposed by RelocatedProgram can be exposed from LoadedProgram instead (even though it's already available on the pio_asm programs, albeit perhaps less convenient). we do lose access to the relocated instruction iterator, but we also cannot think of anything this iterator would actually be useful for outside of program loading.
-rw-r--r--cyw43-pio/src/lib.rs8
-rw-r--r--embassy-rp/src/lib.rs2
-rw-r--r--embassy-rp/src/pio.rs60
-rw-r--r--embassy-rp/src/relocate.rs5
-rw-r--r--examples/rp/src/bin/pio_async.rs10
-rw-r--r--examples/rp/src/bin/pio_dma.rs4
-rw-r--r--examples/rp/src/bin/pio_hd44780.rs7
-rw-r--r--examples/rp/src/bin/pio_uart.rs41
-rw-r--r--examples/rp/src/bin/pio_ws2812.rs4
-rw-r--r--tests/rp/src/bin/pio_irq.rs4
-rw-r--r--tests/rp/src/bin/pio_multi_load.rs126
11 files changed, 200 insertions, 71 deletions
diff --git a/cyw43-pio/src/lib.rs b/cyw43-pio/src/lib.rs
index dca30c74d..830a5b44a 100644
--- a/cyw43-pio/src/lib.rs
+++ b/cyw43-pio/src/lib.rs
@@ -8,7 +8,6 @@ use cyw43::SpiBusCyw43;
8use embassy_rp::dma::Channel; 8use embassy_rp::dma::Channel;
9use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate}; 9use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate};
10use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine}; 10use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine};
11use embassy_rp::relocate::RelocatedProgram;
12use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef}; 11use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef};
13use fixed::FixedU32; 12use fixed::FixedU32;
14use pio_proc::pio_asm; 13use pio_proc::pio_asm;
@@ -88,8 +87,6 @@ where
88 ".wrap" 87 ".wrap"
89 ); 88 );
90 89
91 let relocated = RelocatedProgram::new(&program.program);
92
93 let mut pin_io: embassy_rp::pio::Pin<PIO> = common.make_pio_pin(dio); 90 let mut pin_io: embassy_rp::pio::Pin<PIO> = common.make_pio_pin(dio);
94 pin_io.set_pull(Pull::None); 91 pin_io.set_pull(Pull::None);
95 pin_io.set_schmitt(true); 92 pin_io.set_schmitt(true);
@@ -102,7 +99,8 @@ where
102 pin_clk.set_slew_rate(SlewRate::Fast); 99 pin_clk.set_slew_rate(SlewRate::Fast);
103 100
104 let mut cfg = Config::default(); 101 let mut cfg = Config::default();
105 cfg.use_program(&common.load_program(&relocated), &[&pin_clk]); 102 let loaded_program = common.load_program(&program.program);
103 cfg.use_program(&loaded_program, &[&pin_clk]);
106 cfg.set_out_pins(&[&pin_io]); 104 cfg.set_out_pins(&[&pin_io]);
107 cfg.set_in_pins(&[&pin_io]); 105 cfg.set_in_pins(&[&pin_io]);
108 cfg.set_set_pins(&[&pin_io]); 106 cfg.set_set_pins(&[&pin_io]);
@@ -142,7 +140,7 @@ where
142 sm, 140 sm,
143 irq, 141 irq,
144 dma: dma.into_ref(), 142 dma: dma.into_ref(),
145 wrap_target: relocated.wrap().target, 143 wrap_target: loaded_program.wrap.target,
146 } 144 }
147 } 145 }
148 146
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs
index ebec9fec6..45156458d 100644
--- a/embassy-rp/src/lib.rs
+++ b/embassy-rp/src/lib.rs
@@ -33,7 +33,7 @@ pub mod watchdog;
33// TODO: move `pio_instr_util` and `relocate` to inside `pio` 33// TODO: move `pio_instr_util` and `relocate` to inside `pio`
34pub mod pio; 34pub mod pio;
35pub mod pio_instr_util; 35pub mod pio_instr_util;
36pub mod relocate; 36pub(crate) mod relocate;
37 37
38// Reexports 38// Reexports
39pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; 39pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef};
diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs
index 464988b27..3de398af7 100644
--- a/embassy-rp/src/pio.rs
+++ b/embassy-rp/src/pio.rs
@@ -11,7 +11,7 @@ use fixed::types::extra::U8;
11use fixed::FixedU32; 11use fixed::FixedU32;
12use pac::io::vals::Gpio0ctrlFuncsel; 12use pac::io::vals::Gpio0ctrlFuncsel;
13use pac::pio::vals::SmExecctrlStatusSel; 13use pac::pio::vals::SmExecctrlStatusSel;
14use pio::{SideSet, Wrap}; 14use pio::{Program, SideSet, Wrap};
15 15
16use crate::dma::{Channel, Transfer, Word}; 16use crate::dma::{Channel, Transfer, Word};
17use crate::gpio::sealed::Pin as SealedPin; 17use crate::gpio::sealed::Pin as SealedPin;
@@ -734,23 +734,67 @@ pub struct InstanceMemory<'d, PIO: Instance> {
734 734
735pub struct LoadedProgram<'d, PIO: Instance> { 735pub struct LoadedProgram<'d, PIO: Instance> {
736 pub used_memory: InstanceMemory<'d, PIO>, 736 pub used_memory: InstanceMemory<'d, PIO>,
737 origin: u8, 737 pub origin: u8,
738 wrap: Wrap, 738 pub wrap: Wrap,
739 side_set: SideSet, 739 pub side_set: SideSet,
740}
741
742#[derive(Clone, Copy, PartialEq, Eq, Debug)]
743#[cfg_attr(feature = "defmt", derive(defmt::Format))]
744pub enum LoadError {
745 /// Insufficient consecutive free instruction space to load program.
746 InsufficientSpace,
747 /// Loading the program would overwrite an instruction address already
748 /// used by another program.
749 AddressInUse(usize),
740} 750}
741 751
742impl<'d, PIO: Instance> Common<'d, PIO> { 752impl<'d, PIO: Instance> Common<'d, PIO> {
743 pub fn load_program<const SIZE: usize>(&mut self, prog: &RelocatedProgram<SIZE>) -> LoadedProgram<'d, PIO> { 753 /// Load a PIO program. This will automatically relocate the program to
754 /// an available chunk of free instruction memory if the program origin
755 /// was not explicitly specified, otherwise it will attempt to load the
756 /// program only at its origin.
757 pub fn load_program<const SIZE: usize>(&mut self, prog: &Program<SIZE>) -> LoadedProgram<'d, PIO> {
744 match self.try_load_program(prog) { 758 match self.try_load_program(prog) {
745 Ok(r) => r, 759 Ok(r) => r,
746 Err(at) => panic!("Trying to write already used PIO instruction memory at {}", at), 760 Err(e) => panic!("Failed to load PIO program: {:?}", e),
747 } 761 }
748 } 762 }
749 763
764 /// Load a PIO program. This will automatically relocate the program to
765 /// an available chunk of free instruction memory if the program origin
766 /// was not explicitly specified, otherwise it will attempt to load the
767 /// program only at its origin.
750 pub fn try_load_program<const SIZE: usize>( 768 pub fn try_load_program<const SIZE: usize>(
751 &mut self, 769 &mut self,
752 prog: &RelocatedProgram<SIZE>, 770 prog: &Program<SIZE>,
771 ) -> Result<LoadedProgram<'d, PIO>, LoadError> {
772 match prog.origin {
773 Some(origin) => self
774 .try_load_program_at(prog, origin)
775 .map_err(|a| LoadError::AddressInUse(a)),
776 None => {
777 // naively search for free space, allowing wraparound since
778 // PIO does support that. with only 32 instruction slots it
779 // doesn't make much sense to do anything more fancy.
780 let mut origin = 0;
781 while origin < 32 {
782 match self.try_load_program_at(prog, origin as _) {
783 Ok(r) => return Ok(r),
784 Err(a) => origin = a + 1,
785 }
786 }
787 Err(LoadError::InsufficientSpace)
788 }
789 }
790 }
791
792 fn try_load_program_at<const SIZE: usize>(
793 &mut self,
794 prog: &Program<SIZE>,
795 origin: u8,
753 ) -> Result<LoadedProgram<'d, PIO>, usize> { 796 ) -> Result<LoadedProgram<'d, PIO>, usize> {
797 let prog = RelocatedProgram::new_with_origin(prog, origin);
754 let used_memory = self.try_write_instr(prog.origin() as _, prog.code())?; 798 let used_memory = self.try_write_instr(prog.origin() as _, prog.code())?;
755 Ok(LoadedProgram { 799 Ok(LoadedProgram {
756 used_memory, 800 used_memory,
@@ -760,7 +804,7 @@ impl<'d, PIO: Instance> Common<'d, PIO> {
760 }) 804 })
761 } 805 }
762 806
763 pub fn try_write_instr<I>(&mut self, start: usize, instrs: I) -> Result<InstanceMemory<'d, PIO>, usize> 807 fn try_write_instr<I>(&mut self, start: usize, instrs: I) -> Result<InstanceMemory<'d, PIO>, usize>
764 where 808 where
765 I: Iterator<Item = u16>, 809 I: Iterator<Item = u16>,
766 { 810 {
diff --git a/embassy-rp/src/relocate.rs b/embassy-rp/src/relocate.rs
index 9cb279ccd..b35b4ed72 100644
--- a/embassy-rp/src/relocate.rs
+++ b/embassy-rp/src/relocate.rs
@@ -41,11 +41,6 @@ pub struct RelocatedProgram<'a, const PROGRAM_SIZE: usize> {
41} 41}
42 42
43impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> { 43impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> {
44 pub fn new(program: &Program<PROGRAM_SIZE>) -> RelocatedProgram<PROGRAM_SIZE> {
45 let origin = program.origin.unwrap_or(0);
46 RelocatedProgram { program, origin }
47 }
48
49 pub fn new_with_origin(program: &Program<PROGRAM_SIZE>, origin: u8) -> RelocatedProgram<PROGRAM_SIZE> { 44 pub fn new_with_origin(program: &Program<PROGRAM_SIZE>, origin: u8) -> RelocatedProgram<PROGRAM_SIZE> {
50 RelocatedProgram { program, origin } 45 RelocatedProgram { program, origin }
51 } 46 }
diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs
index c001d6440..a6d6144be 100644
--- a/examples/rp/src/bin/pio_async.rs
+++ b/examples/rp/src/bin/pio_async.rs
@@ -8,7 +8,6 @@ use embassy_executor::Spawner;
8use embassy_rp::bind_interrupts; 8use embassy_rp::bind_interrupts;
9use embassy_rp::peripherals::PIO0; 9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; 10use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine};
11use embassy_rp::relocate::RelocatedProgram;
12use fixed::traits::ToFixed; 11use fixed::traits::ToFixed;
13use fixed_macro::types::U56F8; 12use fixed_macro::types::U56F8;
14use {defmt_rtt as _, panic_probe as _}; 13use {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,
29 ".wrap", 28 ".wrap",
30 ); 29 );
31 30
32 let relocated = RelocatedProgram::new(&prg.program);
33 let mut cfg = Config::default(); 31 let mut cfg = Config::default();
34 cfg.use_program(&pio.load_program(&relocated), &[]); 32 cfg.use_program(&pio.load_program(&prg.program), &[]);
35 let out_pin = pio.make_pio_pin(pin); 33 let out_pin = pio.make_pio_pin(pin);
36 cfg.set_out_pins(&[&out_pin]); 34 cfg.set_out_pins(&[&out_pin]);
37 cfg.set_set_pins(&[&out_pin]); 35 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,
65 ".wrap", 63 ".wrap",
66 ); 64 );
67 65
68 let relocated = RelocatedProgram::new(&prg.program);
69 let mut cfg = Config::default(); 66 let mut cfg = Config::default();
70 cfg.use_program(&pio.load_program(&relocated), &[]); 67 cfg.use_program(&pio.load_program(&prg.program), &[]);
71 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); 68 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
72 cfg.shift_in.auto_fill = true; 69 cfg.shift_in.auto_fill = true;
73 cfg.shift_in.direction = ShiftDirection::Right; 70 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,
96 "irq 3 [15]", 93 "irq 3 [15]",
97 ".wrap", 94 ".wrap",
98 ); 95 );
99 let relocated = RelocatedProgram::new(&prg.program);
100 let mut cfg = Config::default(); 96 let mut cfg = Config::default();
101 cfg.use_program(&pio.load_program(&relocated), &[]); 97 cfg.use_program(&pio.load_program(&prg.program), &[]);
102 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); 98 cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
103 sm.set_config(&cfg); 99 sm.set_config(&cfg);
104} 100}
diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs
index 9ab72e1f3..86e5017ac 100644
--- a/examples/rp/src/bin/pio_dma.rs
+++ b/examples/rp/src/bin/pio_dma.rs
@@ -8,7 +8,6 @@ use embassy_executor::Spawner;
8use embassy_futures::join::join; 8use embassy_futures::join::join;
9use embassy_rp::peripherals::PIO0; 9use embassy_rp::peripherals::PIO0;
10use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; 10use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection};
11use embassy_rp::relocate::RelocatedProgram;
12use embassy_rp::{bind_interrupts, Peripheral}; 11use embassy_rp::{bind_interrupts, Peripheral};
13use fixed::traits::ToFixed; 12use fixed::traits::ToFixed;
14use fixed_macro::types::U56F8; 13use fixed_macro::types::U56F8;
@@ -46,9 +45,8 @@ async fn main(_spawner: Spawner) {
46 ".wrap", 45 ".wrap",
47 ); 46 );
48 47
49 let relocated = RelocatedProgram::new(&prg.program);
50 let mut cfg = Config::default(); 48 let mut cfg = Config::default();
51 cfg.use_program(&common.load_program(&relocated), &[]); 49 cfg.use_program(&common.load_program(&prg.program), &[]);
52 cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed(); 50 cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed();
53 cfg.shift_in = ShiftConfig { 51 cfg.shift_in = ShiftConfig {
54 auto_fill: true, 52 auto_fill: true,
diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs
index 8aedd24b6..d80c5c24b 100644
--- a/examples/rp/src/bin/pio_hd44780.rs
+++ b/examples/rp/src/bin/pio_hd44780.rs
@@ -14,7 +14,6 @@ use embassy_rp::pio::{
14 Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, 14 Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
15}; 15};
16use embassy_rp::pwm::{self, Pwm}; 16use embassy_rp::pwm::{self, Pwm};
17use embassy_rp::relocate::RelocatedProgram;
18use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef}; 17use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef};
19use embassy_time::{Duration, Instant, Timer}; 18use embassy_time::{Duration, Instant, Timer};
20use {defmt_rtt as _, panic_probe as _}; 19use {defmt_rtt as _, panic_probe as _};
@@ -127,9 +126,8 @@ impl<'l> HD44780<'l> {
127 126
128 sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); 127 sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
129 128
130 let relocated = RelocatedProgram::new(&prg.program);
131 let mut cfg = Config::default(); 129 let mut cfg = Config::default();
132 cfg.use_program(&common.load_program(&relocated), &[&e]); 130 cfg.use_program(&common.load_program(&prg.program), &[&e]);
133 cfg.clock_divider = 125u8.into(); 131 cfg.clock_divider = 125u8.into();
134 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); 132 cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
135 cfg.shift_out = ShiftConfig { 133 cfg.shift_out = ShiftConfig {
@@ -201,9 +199,8 @@ impl<'l> HD44780<'l> {
201 "# 199 "#
202 ); 200 );
203 201
204 let relocated = RelocatedProgram::new(&prg.program);
205 let mut cfg = Config::default(); 202 let mut cfg = Config::default();
206 cfg.use_program(&common.load_program(&relocated), &[&e]); 203 cfg.use_program(&common.load_program(&prg.program), &[&e]);
207 cfg.clock_divider = 8u8.into(); // ~64ns/insn 204 cfg.clock_divider = 8u8.into(); // ~64ns/insn
208 cfg.set_jmp_pin(&db7); 205 cfg.set_jmp_pin(&db7);
209 cfg.set_set_pins(&[&rs, &rw]); 206 cfg.set_set_pins(&[&rs, &rw]);
diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs
index ca1c7f394..5fddbe292 100644
--- a/examples/rp/src/bin/pio_uart.rs
+++ b/examples/rp/src/bin/pio_uart.rs
@@ -222,8 +222,8 @@ mod uart {
222 mut common, sm0, sm1, .. 222 mut common, sm0, sm1, ..
223 } = Pio::new(pio, Irqs); 223 } = Pio::new(pio, Irqs);
224 224
225 let (tx, origin) = PioUartTx::new(&mut common, sm0, tx_pin, baud, None); 225 let tx = PioUartTx::new(&mut common, sm0, tx_pin, baud);
226 let (rx, _) = PioUartRx::new(&mut common, sm1, rx_pin, baud, Some(origin)); 226 let rx = PioUartRx::new(&mut common, sm1, rx_pin, baud);
227 227
228 PioUart { tx, rx } 228 PioUart { tx, rx }
229 } 229 }
@@ -240,7 +240,6 @@ mod uart_tx {
240 use embassy_rp::gpio::Level; 240 use embassy_rp::gpio::Level;
241 use embassy_rp::peripherals::PIO0; 241 use embassy_rp::peripherals::PIO0;
242 use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; 242 use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine};
243 use embassy_rp::relocate::RelocatedProgram;
244 use embedded_io::asynch::Write; 243 use embedded_io::asynch::Write;
245 use embedded_io::Io; 244 use embedded_io::Io;
246 use fixed::traits::ToFixed; 245 use fixed::traits::ToFixed;
@@ -256,9 +255,8 @@ mod uart_tx {
256 mut sm_tx: StateMachine<'a, PIO0, 0>, 255 mut sm_tx: StateMachine<'a, PIO0, 0>,
257 tx_pin: impl PioPin, 256 tx_pin: impl PioPin,
258 baud: u64, 257 baud: u64,
259 origin: Option<u8>, 258 ) -> Self {
260 ) -> (Self, u8) { 259 let prg = pio_proc::pio_asm!(
261 let mut prg = pio_proc::pio_asm!(
262 r#" 260 r#"
263 .side_set 1 opt 261 .side_set 1 opt
264 262
@@ -272,17 +270,14 @@ mod uart_tx {
272 jmp x-- bitloop [6] ; Each loop iteration is 8 cycles. 270 jmp x-- bitloop [6] ; Each loop iteration is 8 cycles.
273 "# 271 "#
274 ); 272 );
275 prg.program.origin = origin;
276 let tx_pin = common.make_pio_pin(tx_pin); 273 let tx_pin = common.make_pio_pin(tx_pin);
277 sm_tx.set_pins(Level::High, &[&tx_pin]); 274 sm_tx.set_pins(Level::High, &[&tx_pin]);
278 sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]); 275 sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]);
279 276
280 let relocated = RelocatedProgram::new(&prg.program);
281
282 let mut cfg = Config::default(); 277 let mut cfg = Config::default();
283 278
284 cfg.set_out_pins(&[&tx_pin]); 279 cfg.set_out_pins(&[&tx_pin]);
285 cfg.use_program(&common.load_program(&relocated), &[&tx_pin]); 280 cfg.use_program(&common.load_program(&prg.program), &[&tx_pin]);
286 cfg.shift_out.auto_fill = false; 281 cfg.shift_out.auto_fill = false;
287 cfg.shift_out.direction = ShiftDirection::Right; 282 cfg.shift_out.direction = ShiftDirection::Right;
288 cfg.fifo_join = FifoJoin::TxOnly; 283 cfg.fifo_join = FifoJoin::TxOnly;
@@ -290,18 +285,7 @@ mod uart_tx {
290 sm_tx.set_config(&cfg); 285 sm_tx.set_config(&cfg);
291 sm_tx.set_enable(true); 286 sm_tx.set_enable(true);
292 287
293 // The 4 state machines of the PIO each have their own program counter that starts taking 288 Self { sm_tx }
294 // instructions at an offset (origin) of the 32 instruction "space" the PIO device has.
295 // It is up to the programmer to sort out where to place these instructions.
296 // From the pio_asm! macro you get a ProgramWithDefines which has a field .program.origin
297 // which takes an Option<u8>.
298 //
299 // When you load more than one RelocatedProgram into the PIO,
300 // you load your first program at origin = 0.
301 // The RelocatedProgram has .code().count() which returns a usize,
302 // for which you can then use as your next program's origin.
303 let offset = relocated.code().count() as u8 + origin.unwrap_or_default();
304 (Self { sm_tx }, offset)
305 } 289 }
306 290
307 pub async fn write_u8(&mut self, data: u8) { 291 pub async fn write_u8(&mut self, data: u8) {
@@ -329,7 +313,6 @@ mod uart_rx {
329 use embassy_rp::gpio::Level; 313 use embassy_rp::gpio::Level;
330 use embassy_rp::peripherals::PIO0; 314 use embassy_rp::peripherals::PIO0;
331 use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; 315 use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine};
332 use embassy_rp::relocate::RelocatedProgram;
333 use embedded_io::asynch::Read; 316 use embedded_io::asynch::Read;
334 use embedded_io::Io; 317 use embedded_io::Io;
335 use fixed::traits::ToFixed; 318 use fixed::traits::ToFixed;
@@ -345,9 +328,8 @@ mod uart_rx {
345 mut sm_rx: StateMachine<'a, PIO0, 1>, 328 mut sm_rx: StateMachine<'a, PIO0, 1>,
346 rx_pin: impl PioPin, 329 rx_pin: impl PioPin,
347 baud: u64, 330 baud: u64,
348 origin: Option<u8>, 331 ) -> Self {
349 ) -> (Self, u8) { 332 let prg = pio_proc::pio_asm!(
350 let mut prg = pio_proc::pio_asm!(
351 r#" 333 r#"
352 ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and 334 ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and
353 ; break conditions more gracefully. 335 ; break conditions more gracefully.
@@ -369,10 +351,8 @@ mod uart_rx {
369 push ; important in case the TX clock is slightly too fast. 351 push ; important in case the TX clock is slightly too fast.
370 "# 352 "#
371 ); 353 );
372 prg.program.origin = origin;
373 let relocated = RelocatedProgram::new(&prg.program);
374 let mut cfg = Config::default(); 354 let mut cfg = Config::default();
375 cfg.use_program(&common.load_program(&relocated), &[]); 355 cfg.use_program(&common.load_program(&prg.program), &[]);
376 356
377 let rx_pin = common.make_pio_pin(rx_pin); 357 let rx_pin = common.make_pio_pin(rx_pin);
378 sm_rx.set_pins(Level::High, &[&rx_pin]); 358 sm_rx.set_pins(Level::High, &[&rx_pin]);
@@ -387,8 +367,7 @@ mod uart_rx {
387 sm_rx.set_config(&cfg); 367 sm_rx.set_config(&cfg);
388 sm_rx.set_enable(true); 368 sm_rx.set_enable(true);
389 369
390 let offset = relocated.code().count() as u8 + origin.unwrap_or_default(); 370 Self { sm_rx }
391 (Self { sm_rx }, offset)
392 } 371 }
393 372
394 pub async fn read_u8(&mut self) -> u8 { 373 pub async fn read_u8(&mut self) -> u8 {
diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs
index 3de2bd48d..bc87016ec 100644
--- a/examples/rp/src/bin/pio_ws2812.rs
+++ b/examples/rp/src/bin/pio_ws2812.rs
@@ -12,7 +12,6 @@ use embassy_rp::peripherals::PIO0;
12use embassy_rp::pio::{ 12use embassy_rp::pio::{
13 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, 13 Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine,
14}; 14};
15use embassy_rp::relocate::RelocatedProgram;
16use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; 15use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef};
17use embassy_time::{Duration, Timer}; 16use embassy_time::{Duration, Timer};
18use fixed::types::U24F8; 17use fixed::types::U24F8;
@@ -73,8 +72,7 @@ impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> {
73 cfg.set_out_pins(&[&out_pin]); 72 cfg.set_out_pins(&[&out_pin]);
74 cfg.set_set_pins(&[&out_pin]); 73 cfg.set_set_pins(&[&out_pin]);
75 74
76 let relocated = RelocatedProgram::new(&prg); 75 cfg.use_program(&pio.load_program(&prg), &[&out_pin]);
77 cfg.use_program(&pio.load_program(&relocated), &[&out_pin]);
78 76
79 // Clock config, measured in kHz to avoid overflows 77 // Clock config, measured in kHz to avoid overflows
80 // TODO CLOCK_FREQ should come from embassy_rp 78 // TODO CLOCK_FREQ should come from embassy_rp
diff --git a/tests/rp/src/bin/pio_irq.rs b/tests/rp/src/bin/pio_irq.rs
index 45004424a..bdea63eaa 100644
--- a/tests/rp/src/bin/pio_irq.rs
+++ b/tests/rp/src/bin/pio_irq.rs
@@ -9,7 +9,6 @@ use embassy_executor::Spawner;
9use embassy_rp::bind_interrupts; 9use embassy_rp::bind_interrupts;
10use embassy_rp::peripherals::PIO0; 10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{Config, InterruptHandler, Pio}; 11use embassy_rp::pio::{Config, InterruptHandler, Pio};
12use embassy_rp::relocate::RelocatedProgram;
13use {defmt_rtt as _, panic_probe as _}; 12use {defmt_rtt as _, panic_probe as _};
14 13
15bind_interrupts!(struct Irqs { 14bind_interrupts!(struct Irqs {
@@ -35,9 +34,8 @@ async fn main(_spawner: Spawner) {
35 "irq wait 1", 34 "irq wait 1",
36 ); 35 );
37 36
38 let relocated = RelocatedProgram::new(&prg.program);
39 let mut cfg = Config::default(); 37 let mut cfg = Config::default();
40 cfg.use_program(&common.load_program(&relocated), &[]); 38 cfg.use_program(&common.load_program(&prg.program), &[]);
41 sm.set_config(&cfg); 39 sm.set_config(&cfg);
42 sm.set_enable(true); 40 sm.set_enable(true);
43 41
diff --git a/tests/rp/src/bin/pio_multi_load.rs b/tests/rp/src/bin/pio_multi_load.rs
new file mode 100644
index 000000000..356f16795
--- /dev/null
+++ b/tests/rp/src/bin/pio_multi_load.rs
@@ -0,0 +1,126 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4#[path = "../common.rs"]
5mod common;
6
7use defmt::info;
8use embassy_executor::Spawner;
9use embassy_rp::bind_interrupts;
10use embassy_rp::peripherals::PIO0;
11use embassy_rp::pio::{Config, InterruptHandler, LoadError, Pio};
12use {defmt_rtt as _, panic_probe as _};
13
14bind_interrupts!(struct Irqs {
15 PIO0_IRQ_0 => InterruptHandler<PIO0>;
16});
17
18#[embassy_executor::main]
19async fn main(_spawner: Spawner) {
20 let p = embassy_rp::init(Default::default());
21 let pio = p.PIO0;
22 let Pio {
23 mut common,
24 mut sm0,
25 mut sm1,
26 mut sm2,
27 irq_flags,
28 ..
29 } = Pio::new(pio, Irqs);
30
31 // load with explicit origin works
32 let prg1 = pio_proc::pio_asm!(
33 ".origin 4"
34 "nop",
35 "nop",
36 "nop",
37 "nop",
38 "nop",
39 "nop",
40 "nop",
41 "irq 0",
42 "nop",
43 "nop",
44 );
45 let loaded1 = common.load_program(&prg1.program);
46 assert_eq!(loaded1.origin, 4);
47 assert_eq!(loaded1.wrap.source, 13);
48 assert_eq!(loaded1.wrap.target, 4);
49
50 // load without origin chooses a free space
51 let prg2 = pio_proc::pio_asm!("nop", "nop", "nop", "nop", "nop", "nop", "nop", "irq 1", "nop", "nop",);
52 let loaded2 = common.load_program(&prg2.program);
53 assert_eq!(loaded2.origin, 14);
54 assert_eq!(loaded2.wrap.source, 23);
55 assert_eq!(loaded2.wrap.target, 14);
56
57 // wrapping around the end of program space automatically works
58 let prg3 =
59 pio_proc::pio_asm!("nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "irq 2",);
60 let loaded3 = common.load_program(&prg3.program);
61 assert_eq!(loaded3.origin, 24);
62 assert_eq!(loaded3.wrap.source, 3);
63 assert_eq!(loaded3.wrap.target, 24);
64
65 // check that the programs actually work
66 {
67 let mut cfg = Config::default();
68 cfg.use_program(&loaded1, &[]);
69 sm0.set_config(&cfg);
70 sm0.set_enable(true);
71 while !irq_flags.check(0) {}
72 sm0.set_enable(false);
73 }
74 {
75 let mut cfg = Config::default();
76 cfg.use_program(&loaded2, &[]);
77 sm1.set_config(&cfg);
78 sm1.set_enable(true);
79 while !irq_flags.check(1) {}
80 sm1.set_enable(false);
81 }
82 {
83 let mut cfg = Config::default();
84 cfg.use_program(&loaded3, &[]);
85 sm2.set_config(&cfg);
86 sm2.set_enable(true);
87 while !irq_flags.check(2) {}
88 sm2.set_enable(false);
89 }
90
91 // instruction memory is full now. all loads should fail.
92 {
93 let prg = pio_proc::pio_asm!(".origin 0", "nop");
94 match common.try_load_program(&prg.program) {
95 Err(LoadError::AddressInUse(0)) => (),
96 _ => panic!("program loaded when it shouldn't"),
97 };
98
99 let prg = pio_proc::pio_asm!("nop");
100 match common.try_load_program(&prg.program) {
101 Err(LoadError::InsufficientSpace) => (),
102 _ => panic!("program loaded when it shouldn't"),
103 };
104 }
105
106 // freeing some memory should allow further loads though.
107 unsafe {
108 common.free_instr(loaded3.used_memory);
109 }
110 {
111 let prg = pio_proc::pio_asm!(".origin 0", "nop");
112 match common.try_load_program(&prg.program) {
113 Ok(_) => (),
114 _ => panic!("program didn't loaded when it shouldn"),
115 };
116
117 let prg = pio_proc::pio_asm!("nop");
118 match common.try_load_program(&prg.program) {
119 Ok(_) => (),
120 _ => panic!("program didn't loaded when it shouldn"),
121 };
122 }
123
124 info!("Test OK");
125 cortex_m::asm::bkpt();
126}