1434: rp pio IV (the voyage home) r=Dirbaio a=pennae

this should hopefully be the last entry in this series. after this we'll have a reasonably safe interface to pio, both for configuration and at runtime. pio now looks very much like the other peripherals (though not exactly, seeing how state machines can't be constructed from a config but only have it applied to them later). the generated code for `StateMachine::set_config` is still larger than we'd like (almost 300 bytes at Oz), but it's a great step up in safety from the previous interface at approximately the same code space cost.

Co-authored-by: pennae <github@quasiparticle.net>
This commit is contained in:
bors[bot] 2023-05-08 09:17:51 +00:00 committed by GitHub
commit 79c60f4a7d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 572 additions and 504 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
use pio::{InSource, InstructionOperands, JmpCondition, OutDestination, SetDestination};
use crate::pio::{PioInstance, PioStateMachine};
use crate::pio::{Instance, StateMachine};
pub fn set_x<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, value: u32) {
pub unsafe fn set_x<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>, value: u32) {
const OUT: u16 = InstructionOperands::OUT {
destination: OutDestination::X,
bit_count: 32,
@ -12,7 +12,7 @@ pub fn set_x<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM
sm.exec_instr(OUT);
}
pub fn get_x<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>) -> u32 {
pub unsafe fn get_x<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>) -> u32 {
const IN: u16 = InstructionOperands::IN {
source: InSource::X,
bit_count: 32,
@ -22,7 +22,7 @@ pub fn get_x<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM
sm.rx().pull()
}
pub fn set_y<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, value: u32) {
pub unsafe fn set_y<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>, value: u32) {
const OUT: u16 = InstructionOperands::OUT {
destination: OutDestination::Y,
bit_count: 32,
@ -32,7 +32,7 @@ pub fn set_y<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM
sm.exec_instr(OUT);
}
pub fn get_y<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>) -> u32 {
pub unsafe fn get_y<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>) -> u32 {
const IN: u16 = InstructionOperands::IN {
source: InSource::Y,
bit_count: 32,
@ -43,7 +43,7 @@ pub fn get_y<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM
sm.rx().pull()
}
pub fn set_pindir<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, data: u8) {
pub unsafe fn set_pindir<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>, data: u8) {
let set: u16 = InstructionOperands::SET {
destination: SetDestination::PINDIRS,
data,
@ -52,7 +52,7 @@ pub fn set_pindir<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PI
sm.exec_instr(set);
}
pub fn set_pin<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, data: u8) {
pub unsafe fn set_pin<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>, data: u8) {
let set: u16 = InstructionOperands::SET {
destination: SetDestination::PINS,
data,
@ -61,7 +61,7 @@ pub fn set_pin<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO,
sm.exec_instr(set);
}
pub fn set_out_pin<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, data: u32) {
pub unsafe fn set_out_pin<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>, data: u32) {
const OUT: u16 = InstructionOperands::OUT {
destination: OutDestination::PINS,
bit_count: 32,
@ -70,7 +70,7 @@ pub fn set_out_pin<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<P
sm.tx().push(data);
sm.exec_instr(OUT);
}
pub fn set_out_pindir<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, data: u32) {
pub unsafe fn set_out_pindir<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>, data: u32) {
const OUT: u16 = InstructionOperands::OUT {
destination: OutDestination::PINDIRS,
bit_count: 32,
@ -80,7 +80,7 @@ pub fn set_out_pindir<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachin
sm.exec_instr(OUT);
}
pub fn exec_jmp<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, to_addr: u8) {
pub unsafe fn exec_jmp<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>, to_addr: u8) {
let jmp: u16 = InstructionOperands::JMP {
address: to_addr,
condition: JmpCondition::Always,

View file

@ -26,11 +26,7 @@ where
Some(if instr & 0b1110_0000_0000_0000 == 0 {
// this is a JMP instruction -> add offset to address
let address = (instr & 0b1_1111) as u8;
let address = address + self.offset;
assert!(
address < pio::RP2040_MAX_PROGRAM_SIZE as u8,
"Invalid JMP out of the program after offset addition"
);
let address = address.wrapping_add(self.offset) % 32;
instr & (!0b11111) | address as u16
} else {
instr
@ -62,8 +58,8 @@ impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> {
let wrap = self.program.wrap;
let origin = self.origin;
Wrap {
source: wrap.source + origin,
target: wrap.target + origin,
source: wrap.source.wrapping_add(origin) % 32,
target: wrap.target.wrapping_add(origin) % 32,
}
}

View file

@ -22,6 +22,8 @@ lorawan = { version = "0.7.3", default-features = false, features = ["default-cr
defmt = "0.3"
defmt-rtt = "0.4"
fixed = "1.23.1"
fixed-macro = "1.2"
#cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] }
cortex-m = { version = "0.7.6", features = ["inline-asm"] }

View file

@ -2,14 +2,16 @@
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::info;
use embassy_embedded_hal::SetConfig;
use embassy_executor::Spawner;
use embassy_rp::peripherals::PIO0;
use embassy_rp::pio::{Pio, PioCommon, PioIrq, PioPin, PioStateMachine, ShiftDirection};
use embassy_rp::pio_instr_util;
use embassy_rp::pio::{Common, Config, Irq, Pio, PioPin, ShiftDirection, StateMachine};
use embassy_rp::relocate::RelocatedProgram;
use fixed::traits::ToFixed;
use fixed_macro::types::U56F8;
use {defmt_rtt as _, panic_probe as _};
fn setup_pio_task_sm0(pio: &mut PioCommon<PIO0>, sm: &mut PioStateMachine<PIO0, 0>, pin: impl PioPin) {
fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) {
// Setup sm0
// Send data serially to pin
@ -22,22 +24,18 @@ fn setup_pio_task_sm0(pio: &mut PioCommon<PIO0>, sm: &mut PioStateMachine<PIO0,
);
let relocated = RelocatedProgram::new(&prg.program);
let mut cfg = Config::default();
cfg.use_program(&pio.load_program(&relocated), &[]);
let out_pin = pio.make_pio_pin(pin);
let pio_pins = [&out_pin];
sm.set_out_pins(&pio_pins);
pio.write_instr(relocated.origin() as usize, relocated.code());
pio_instr_util::exec_jmp(sm, relocated.origin());
sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32);
sm.set_set_range(0, 1);
let pio::Wrap { source, target } = relocated.wrap();
sm.set_wrap(source, target);
sm.set_autopull(true);
sm.set_out_shift_dir(ShiftDirection::Left);
cfg.set_out_pins(&[&out_pin]);
cfg.set_set_pins(&[&out_pin]);
cfg.clock_divider = (U56F8!(125_000_000) / 20 / 200).to_fixed();
cfg.shift_out.auto_fill = true;
sm.set_config(&cfg);
}
#[embassy_executor::task]
async fn pio_task_sm0(mut sm: PioStateMachine<'static, PIO0, 0>) {
async fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) {
sm.set_enable(true);
let mut v = 0x0f0caffa;
@ -48,26 +46,23 @@ async fn pio_task_sm0(mut sm: PioStateMachine<'static, PIO0, 0>) {
}
}
fn setup_pio_task_sm1(pio: &mut PioCommon<PIO0>, sm: &mut PioStateMachine<PIO0, 1>) {
fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 1>) {
// Setupm sm1
// Read 0b10101 repeatedly until ISR is full
let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",);
let relocated = RelocatedProgram::new(&prg.program);
pio.write_instr(relocated.origin() as usize, relocated.code());
pio_instr_util::exec_jmp(sm, relocated.origin());
sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32);
sm.set_set_range(0, 0);
let pio::Wrap { source, target } = relocated.wrap();
sm.set_wrap(source, target);
sm.set_autopush(true);
sm.set_in_shift_dir(ShiftDirection::Right);
let mut cfg = Config::default();
cfg.use_program(&pio.load_program(&relocated), &[]);
cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
cfg.shift_in.auto_fill = true;
cfg.shift_in.direction = ShiftDirection::Right;
sm.set_config(&cfg);
}
#[embassy_executor::task]
async fn pio_task_sm1(mut sm: PioStateMachine<'static, PIO0, 1>) {
async fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) {
sm.set_enable(true);
loop {
let rx = sm.rx().wait_pull().await;
@ -75,7 +70,7 @@ async fn pio_task_sm1(mut sm: PioStateMachine<'static, PIO0, 1>) {
}
}
fn setup_pio_task_sm2(pio: &mut PioCommon<PIO0>, sm: &mut PioStateMachine<PIO0, 2>) {
fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 2>) {
// Setup sm2
// Repeatedly trigger IRQ 3
@ -89,17 +84,14 @@ fn setup_pio_task_sm2(pio: &mut PioCommon<PIO0>, sm: &mut PioStateMachine<PIO0,
".wrap",
);
let relocated = RelocatedProgram::new(&prg.program);
pio.write_instr(relocated.origin() as usize, relocated.code());
let pio::Wrap { source, target } = relocated.wrap();
sm.set_wrap(source, target);
pio_instr_util::exec_jmp(sm, relocated.origin());
sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32);
let mut cfg = Config::default();
cfg.use_program(&pio.load_program(&relocated), &[]);
cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
sm.set_config(&cfg);
}
#[embassy_executor::task]
async fn pio_task_sm2(mut irq: PioIrq<'static, PIO0, 3>, mut sm: PioStateMachine<'static, PIO0, 2>) {
async fn pio_task_sm2(mut irq: Irq<'static, PIO0, 3>, mut sm: StateMachine<'static, PIO0, 2>) {
sm.set_enable(true);
loop {
irq.wait().await;

View file

@ -2,11 +2,14 @@
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::info;
use embassy_embedded_hal::SetConfig;
use embassy_executor::Spawner;
use embassy_futures::join::join;
use embassy_rp::pio::{Pio, ShiftDirection};
use embassy_rp::pio::{Config, Pio, ShiftConfig, ShiftDirection};
use embassy_rp::relocate::RelocatedProgram;
use embassy_rp::{pio_instr_util, Peripheral};
use embassy_rp::Peripheral;
use fixed::traits::ToFixed;
use fixed_macro::types::U56F8;
use {defmt_rtt as _, panic_probe as _};
fn swap_nibbles(v: u32) -> u32 {
@ -38,18 +41,21 @@ async fn main(_spawner: Spawner) {
);
let relocated = RelocatedProgram::new(&prg.program);
common.write_instr(relocated.origin() as usize, relocated.code());
pio_instr_util::exec_jmp(&mut sm, relocated.origin());
sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32);
let pio::Wrap { source, target } = relocated.wrap();
sm.set_wrap(source, target);
sm.set_autopull(true);
sm.set_autopush(true);
sm.set_pull_threshold(32);
sm.set_push_threshold(32);
sm.set_out_shift_dir(ShiftDirection::Right);
sm.set_in_shift_dir(ShiftDirection::Left);
let mut cfg = Config::default();
cfg.use_program(&common.load_program(&relocated), &[]);
cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed();
cfg.shift_in = ShiftConfig {
auto_fill: true,
threshold: 32,
direction: ShiftDirection::Left,
};
cfg.shift_out = ShiftConfig {
auto_fill: true,
threshold: 32,
direction: ShiftDirection::Right,
};
sm.set_config(&cfg);
sm.set_enable(true);
let mut dma_out_ref = p.DMA_CH0.into_ref();

View file

@ -4,11 +4,12 @@
use core::fmt::Write;
use embassy_embedded_hal::SetConfig;
use embassy_executor::Spawner;
use embassy_rp::dma::{AnyChannel, Channel};
use embassy_rp::peripherals::PIO0;
use embassy_rp::pio::{FifoJoin, Pio, PioPin, PioStateMachine, ShiftDirection};
use embassy_rp::pwm::{Config, Pwm};
use embassy_rp::pio::{Config, Direction, FifoJoin, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine};
use embassy_rp::pwm::{self, Pwm};
use embassy_rp::relocate::RelocatedProgram;
use embassy_rp::{into_ref, Peripheral, PeripheralRef};
use embassy_time::{Duration, Instant, Timer};
@ -29,7 +30,7 @@ async fn main(_spawner: Spawner) {
let p = embassy_rp::init(Default::default());
let _pwm = Pwm::new_output_b(p.PWM_CH7, p.PIN_15, {
let mut c = Config::default();
let mut c = pwm::Config::default();
c.divider = 125.into();
c.top = 100;
c.compare_b = 50;
@ -64,7 +65,7 @@ async fn main(_spawner: Spawner) {
pub struct HD44780<'l> {
dma: PeripheralRef<'l, AnyChannel>,
sm: PioStateMachine<'l, PIO0, 0>,
sm: StateMachine<'l, PIO0, 0>,
buf: [u8; 40],
}
@ -83,7 +84,6 @@ impl<'l> HD44780<'l> {
) -> HD44780<'l> {
into_ref!(dma);
let db7pin = db7.pin();
let Pio {
mut common,
mut irq0,
@ -95,6 +95,7 @@ impl<'l> HD44780<'l> {
let prg = pio_proc::pio_asm!(
r#"
.side_set 1 opt
.origin 20
loop:
out x, 24
@ -115,27 +116,20 @@ impl<'l> HD44780<'l> {
let db6 = common.make_pio_pin(db6);
let db7 = common.make_pio_pin(db7);
sm0.set_set_pins(&[&rs, &rw]);
embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b11);
sm0.set_set_pins(&[&e]);
embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b1);
sm0.set_set_pins(&[&db4, &db5, &db6, &db7]);
embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b11111);
sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
let relocated = RelocatedProgram::new(&prg.program);
common.write_instr(relocated.origin() as usize, relocated.code());
embassy_rp::pio_instr_util::exec_jmp(&mut sm0, relocated.origin());
sm0.set_clkdiv(125 * 256);
let pio::Wrap { source, target } = relocated.wrap();
sm0.set_wrap(source, target);
sm0.set_side_enable(true);
sm0.set_out_pins(&[&db4, &db5, &db6, &db7]);
sm0.set_sideset_base_pin(&e);
sm0.set_sideset_count(2);
sm0.set_out_shift_dir(ShiftDirection::Left);
sm0.set_fifo_join(FifoJoin::TxOnly);
sm0.set_autopull(true);
sm0.set_pull_threshold(32);
let mut cfg = Config::default();
cfg.use_program(&common.load_program(&relocated), &[&e]);
cfg.clock_divider = 125u8.into();
cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
cfg.shift_out = ShiftConfig {
auto_fill: true,
direction: ShiftDirection::Left,
threshold: 32,
};
cfg.fifo_join = FifoJoin::TxOnly;
sm0.set_config(&cfg);
sm0.set_enable(true);
// init to 8 bit thrice
@ -155,7 +149,7 @@ impl<'l> HD44780<'l> {
// many side sets are only there to free up a delay bit!
let prg = pio_proc::pio_asm!(
r#"
.origin 7
.origin 27
.side_set 1
.wrap_target
@ -199,19 +193,15 @@ impl<'l> HD44780<'l> {
);
let relocated = RelocatedProgram::new(&prg.program);
common.write_instr(relocated.origin() as usize, relocated.code());
embassy_rp::pio_instr_util::exec_jmp(&mut sm0, relocated.origin());
let pio::Wrap { source, target } = relocated.wrap();
sm0.set_clkdiv(8 * 256); // ~64ns/insn
sm0.set_side_enable(false);
sm0.set_jmp_pin(db7pin);
sm0.set_wrap(source, target);
sm0.set_set_pins(&[&rs, &rw]);
sm0.set_out_pins(&[&db4, &db5, &db6, &db7]);
sm0.set_sideset_base_pin(&e);
sm0.set_sideset_count(1);
sm0.set_out_shift_dir(ShiftDirection::Left);
sm0.set_fifo_join(FifoJoin::TxOnly);
let mut cfg = Config::default();
cfg.use_program(&common.load_program(&relocated), &[&e]);
cfg.clock_divider = 8u8.into(); // ~64ns/insn
cfg.set_jmp_pin(&db7);
cfg.set_set_pins(&[&rs, &rw]);
cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
cfg.shift_out.direction = ShiftDirection::Left;
cfg.fifo_join = FifoJoin::TxOnly;
sm0.set_config(&cfg);
sm0.set_enable(true);

View file

@ -3,19 +3,20 @@
#![feature(type_alias_impl_trait)]
use defmt::*;
use embassy_embedded_hal::SetConfig;
use embassy_executor::Spawner;
use embassy_rp::pio::{FifoJoin, Pio, PioCommon, PioInstance, PioPin, PioStateMachine, ShiftDirection};
use embassy_rp::pio_instr_util;
use embassy_rp::pio::{Common, Config, FifoJoin, Instance, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine};
use embassy_rp::relocate::RelocatedProgram;
use embassy_time::{Duration, Timer};
use fixed_macro::fixed;
use smart_leds::RGB8;
use {defmt_rtt as _, panic_probe as _};
pub struct Ws2812<'d, P: PioInstance, const S: usize> {
sm: PioStateMachine<'d, P, S>,
pub struct Ws2812<'d, P: Instance, const S: usize> {
sm: StateMachine<'d, P, S>,
}
impl<'d, P: PioInstance, const S: usize> Ws2812<'d, P, S> {
pub fn new(mut pio: PioCommon<'d, P>, mut sm: PioStateMachine<'d, P, S>, pin: impl PioPin) -> Self {
impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> {
pub fn new(mut pio: Common<'d, P>, mut sm: StateMachine<'d, P, S>, pin: impl PioPin) -> Self {
// Setup sm0
// prepare the PIO program
@ -44,41 +45,30 @@ impl<'d, P: PioInstance, const S: usize> Ws2812<'d, P, S> {
a.bind(&mut wrap_source);
let prg = a.assemble_with_wrap(wrap_source, wrap_target);
let relocated = RelocatedProgram::new(&prg);
pio.write_instr(relocated.origin() as usize, relocated.code());
pio_instr_util::exec_jmp(&mut sm, relocated.origin());
let mut cfg = Config::default();
// Pin config
let out_pin = pio.make_pio_pin(pin);
sm.set_set_pins(&[&out_pin]);
sm.set_sideset_base_pin(&out_pin);
sm.set_sideset_count(1);
// Clock config
let relocated = RelocatedProgram::new(&prg);
cfg.use_program(&pio.load_program(&relocated), &[&out_pin]);
// Clock config, measured in kHz to avoid overflows
// TODO CLOCK_FREQ should come from embassy_rp
const CLOCK_FREQ: u32 = 125_000_000;
const WS2812_FREQ: u32 = 800_000;
let bit_freq = WS2812_FREQ * CYCLES_PER_BIT;
let mut int = CLOCK_FREQ / bit_freq;
let rem = CLOCK_FREQ - (int * bit_freq);
let frac = (rem * 256) / bit_freq;
// 65536.0 is represented as 0 in the pio's clock divider
if int == 65536 {
int = 0;
}
sm.set_clkdiv((int << 8) | frac);
let pio::Wrap { source, target } = relocated.wrap();
sm.set_wrap(source, target);
let clock_freq = fixed!(125_000: U24F8);
let ws2812_freq = fixed!(800: U24F8);
let bit_freq = ws2812_freq * CYCLES_PER_BIT;
cfg.clock_divider = clock_freq / bit_freq;
// FIFO config
sm.set_autopull(true);
sm.set_fifo_join(FifoJoin::TxOnly);
sm.set_pull_threshold(24);
sm.set_out_shift_dir(ShiftDirection::Left);
cfg.fifo_join = FifoJoin::TxOnly;
cfg.shift_out = ShiftConfig {
auto_fill: true,
threshold: 24,
direction: ShiftDirection::Left,
};
sm.set_config(&cfg);
sm.set_enable(true);
Self { sm }