diff options
| -rw-r--r-- | cyw43-pio/src/lib.rs | 8 | ||||
| -rw-r--r-- | embassy-rp/src/lib.rs | 2 | ||||
| -rw-r--r-- | embassy-rp/src/pio.rs | 60 | ||||
| -rw-r--r-- | embassy-rp/src/relocate.rs | 5 | ||||
| -rw-r--r-- | examples/rp/src/bin/pio_async.rs | 10 | ||||
| -rw-r--r-- | examples/rp/src/bin/pio_dma.rs | 4 | ||||
| -rw-r--r-- | examples/rp/src/bin/pio_hd44780.rs | 7 | ||||
| -rw-r--r-- | examples/rp/src/bin/pio_uart.rs | 41 | ||||
| -rw-r--r-- | examples/rp/src/bin/pio_ws2812.rs | 4 | ||||
| -rw-r--r-- | tests/rp/src/bin/pio_irq.rs | 4 | ||||
| -rw-r--r-- | tests/rp/src/bin/pio_multi_load.rs | 126 |
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; | |||
| 8 | use embassy_rp::dma::Channel; | 8 | use embassy_rp::dma::Channel; |
| 9 | use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate}; | 9 | use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate}; |
| 10 | use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine}; | 10 | use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine}; |
| 11 | use embassy_rp::relocate::RelocatedProgram; | ||
| 12 | use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef}; | 11 | use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef}; |
| 13 | use fixed::FixedU32; | 12 | use fixed::FixedU32; |
| 14 | use pio_proc::pio_asm; | 13 | use 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` |
| 34 | pub mod pio; | 34 | pub mod pio; |
| 35 | pub mod pio_instr_util; | 35 | pub mod pio_instr_util; |
| 36 | pub mod relocate; | 36 | pub(crate) mod relocate; |
| 37 | 37 | ||
| 38 | // Reexports | 38 | // Reexports |
| 39 | pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; | 39 | pub 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; | |||
| 11 | use fixed::FixedU32; | 11 | use fixed::FixedU32; |
| 12 | use pac::io::vals::Gpio0ctrlFuncsel; | 12 | use pac::io::vals::Gpio0ctrlFuncsel; |
| 13 | use pac::pio::vals::SmExecctrlStatusSel; | 13 | use pac::pio::vals::SmExecctrlStatusSel; |
| 14 | use pio::{SideSet, Wrap}; | 14 | use pio::{Program, SideSet, Wrap}; |
| 15 | 15 | ||
| 16 | use crate::dma::{Channel, Transfer, Word}; | 16 | use crate::dma::{Channel, Transfer, Word}; |
| 17 | use crate::gpio::sealed::Pin as SealedPin; | 17 | use crate::gpio::sealed::Pin as SealedPin; |
| @@ -734,23 +734,67 @@ pub struct InstanceMemory<'d, PIO: Instance> { | |||
| 734 | 734 | ||
| 735 | pub struct LoadedProgram<'d, PIO: Instance> { | 735 | pub 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))] | ||
| 744 | pub 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 | ||
| 742 | impl<'d, PIO: Instance> Common<'d, PIO> { | 752 | impl<'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 | ||
| 43 | impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> { | 43 | impl<'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; | |||
| 8 | use embassy_rp::bind_interrupts; | 8 | use embassy_rp::bind_interrupts; |
| 9 | use embassy_rp::peripherals::PIO0; | 9 | use embassy_rp::peripherals::PIO0; |
| 10 | use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; | 10 | use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; |
| 11 | use embassy_rp::relocate::RelocatedProgram; | ||
| 12 | use fixed::traits::ToFixed; | 11 | use fixed::traits::ToFixed; |
| 13 | use fixed_macro::types::U56F8; | 12 | use fixed_macro::types::U56F8; |
| 14 | use {defmt_rtt as _, panic_probe as _}; | 13 | 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, | |||
| 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; | |||
| 8 | use embassy_futures::join::join; | 8 | use embassy_futures::join::join; |
| 9 | use embassy_rp::peripherals::PIO0; | 9 | use embassy_rp::peripherals::PIO0; |
| 10 | use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; | 10 | use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; |
| 11 | use embassy_rp::relocate::RelocatedProgram; | ||
| 12 | use embassy_rp::{bind_interrupts, Peripheral}; | 11 | use embassy_rp::{bind_interrupts, Peripheral}; |
| 13 | use fixed::traits::ToFixed; | 12 | use fixed::traits::ToFixed; |
| 14 | use fixed_macro::types::U56F8; | 13 | use 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 | }; |
| 16 | use embassy_rp::pwm::{self, Pwm}; | 16 | use embassy_rp::pwm::{self, Pwm}; |
| 17 | use embassy_rp::relocate::RelocatedProgram; | ||
| 18 | use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef}; | 17 | use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef}; |
| 19 | use embassy_time::{Duration, Instant, Timer}; | 18 | use embassy_time::{Duration, Instant, Timer}; |
| 20 | use {defmt_rtt as _, panic_probe as _}; | 19 | use {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; | |||
| 12 | use embassy_rp::pio::{ | 12 | use 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 | }; |
| 15 | use embassy_rp::relocate::RelocatedProgram; | ||
| 16 | use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; | 15 | use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; |
| 17 | use embassy_time::{Duration, Timer}; | 16 | use embassy_time::{Duration, Timer}; |
| 18 | use fixed::types::U24F8; | 17 | use 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; | |||
| 9 | use embassy_rp::bind_interrupts; | 9 | use embassy_rp::bind_interrupts; |
| 10 | use embassy_rp::peripherals::PIO0; | 10 | use embassy_rp::peripherals::PIO0; |
| 11 | use embassy_rp::pio::{Config, InterruptHandler, Pio}; | 11 | use embassy_rp::pio::{Config, InterruptHandler, Pio}; |
| 12 | use embassy_rp::relocate::RelocatedProgram; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 14 | 13 | ||
| 15 | bind_interrupts!(struct Irqs { | 14 | bind_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"] | ||
| 5 | mod common; | ||
| 6 | |||
| 7 | use defmt::info; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_rp::bind_interrupts; | ||
| 10 | use embassy_rp::peripherals::PIO0; | ||
| 11 | use embassy_rp::pio::{Config, InterruptHandler, LoadError, Pio}; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | bind_interrupts!(struct Irqs { | ||
| 15 | PIO0_IRQ_0 => InterruptHandler<PIO0>; | ||
| 16 | }); | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async 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 | } | ||
