aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp
diff options
context:
space:
mode:
authorpennae <[email protected]>2023-05-06 11:36:07 +0200
committerpennae <[email protected]>2023-05-06 17:23:41 +0200
commit8e4d65e163bd484efc4fb31d20b14e6ac4a88de7 (patch)
tree0d1623f5b7711993ee549041881601193e782984 /embassy-rp
parent2873cb93ee3111d984c35287ea9d98f1218d1024 (diff)
rp/pio: configure state machines with Config struct
the many individual sets aren't very efficient, and almost no checks were done to ensure that the configuration written to the hardware was actually valid. this adresses both of these.
Diffstat (limited to 'embassy-rp')
-rw-r--r--embassy-rp/src/pio.rs438
1 files changed, 217 insertions, 221 deletions
diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs
index 8d0547907..a1b7cf1c5 100644
--- a/embassy-rp/src/pio.rs
+++ b/embassy-rp/src/pio.rs
@@ -6,9 +6,13 @@ use core::task::{Context, Poll};
6 6
7use atomic_polyfill::{AtomicU32, AtomicU8}; 7use atomic_polyfill::{AtomicU32, AtomicU8};
8use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; 8use embassy_cortex_m::interrupt::{Interrupt, InterruptExt};
9use embassy_embedded_hal::SetConfig;
9use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; 10use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
10use embassy_sync::waitqueue::AtomicWaker; 11use embassy_sync::waitqueue::AtomicWaker;
12use fixed::types::extra::U8;
13use fixed::FixedU32;
11use pac::io::vals::Gpio0ctrlFuncsel; 14use pac::io::vals::Gpio0ctrlFuncsel;
15use pac::pio::vals::SmExecctrlStatusSel;
12use pio::{SideSet, Wrap}; 16use pio::{SideSet, Wrap};
13 17
14use crate::dma::{Channel, Transfer, Word}; 18use crate::dma::{Channel, Transfer, Word};
@@ -39,8 +43,12 @@ const NEW_AW: AtomicWaker = AtomicWaker::new();
39const PIO_WAKERS_INIT: Wakers = Wakers([NEW_AW; 12]); 43const PIO_WAKERS_INIT: Wakers = Wakers([NEW_AW; 12]);
40static WAKERS: [Wakers; 2] = [PIO_WAKERS_INIT; 2]; 44static WAKERS: [Wakers; 2] = [PIO_WAKERS_INIT; 2];
41 45
46#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
47#[cfg_attr(feature = "defmt", derive(defmt::Format))]
48#[repr(u8)]
42pub enum FifoJoin { 49pub enum FifoJoin {
43 /// Both TX and RX fifo is enabled 50 /// Both TX and RX fifo is enabled
51 #[default]
44 Duplex, 52 Duplex,
45 /// Rx fifo twice as deep. TX fifo disabled 53 /// Rx fifo twice as deep. TX fifo disabled
46 RxOnly, 54 RxOnly,
@@ -48,8 +56,11 @@ pub enum FifoJoin {
48 TxOnly, 56 TxOnly,
49} 57}
50 58
51#[derive(PartialEq)] 59#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
60#[cfg_attr(feature = "defmt", derive(defmt::Format))]
61#[repr(u8)]
52pub enum ShiftDirection { 62pub enum ShiftDirection {
63 #[default]
53 Right = 1, 64 Right = 1,
54 Left = 0, 65 Left = 0,
55} 66}
@@ -62,6 +73,15 @@ pub enum Direction {
62 Out = 1, 73 Out = 1,
63} 74}
64 75
76#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
77#[cfg_attr(feature = "defmt", derive(defmt::Format))]
78#[repr(u8)]
79pub enum StatusSource {
80 #[default]
81 TxFifoLevel = 0,
82 RxFifoLevel = 1,
83}
84
65const RXNEMPTY_MASK: u32 = 1 << 0; 85const RXNEMPTY_MASK: u32 = 1 << 0;
66const TXNFULL_MASK: u32 = 1 << 4; 86const TXNFULL_MASK: u32 = 1 << 4;
67const SMIRQ_MASK: u32 = 1 << 8; 87const SMIRQ_MASK: u32 = 1 << 8;
@@ -477,6 +497,202 @@ fn assert_consecutive<'d, PIO: Instance>(pins: &[&Pin<'d, PIO>]) {
477 } 497 }
478} 498}
479 499
500#[derive(Clone, Copy, Default, Debug)]
501#[cfg_attr(feature = "defmt", derive(defmt::Format))]
502#[non_exhaustive]
503pub struct ExecConfig {
504 pub side_en: bool,
505 pub side_pindir: bool,
506 pub jmp_pin: u8,
507 pub wrap_top: u8,
508 pub wrap_bottom: u8,
509}
510
511#[derive(Clone, Copy, Default, Debug)]
512#[cfg_attr(feature = "defmt", derive(defmt::Format))]
513pub struct ShiftConfig {
514 pub threshold: u8,
515 pub direction: ShiftDirection,
516 pub auto_fill: bool,
517}
518
519#[derive(Clone, Copy, Default, Debug)]
520#[cfg_attr(feature = "defmt", derive(defmt::Format))]
521pub struct PinConfig {
522 pub sideset_count: u8,
523 pub set_count: u8,
524 pub out_count: u8,
525 pub in_base: u8,
526 pub sideset_base: u8,
527 pub set_base: u8,
528 pub out_base: u8,
529}
530
531#[derive(Clone, Copy, Debug)]
532pub struct Config<'d, PIO: Instance> {
533 // CLKDIV
534 pub clock_divider: FixedU32<U8>,
535 // EXECCTRL
536 pub out_en_sel: u8,
537 pub inline_out_en: bool,
538 pub out_sticky: bool,
539 pub status_sel: StatusSource,
540 pub status_n: u8,
541 exec: ExecConfig,
542 origin: Option<u8>,
543 // SHIFTCTRL
544 pub fifo_join: FifoJoin,
545 pub shift_in: ShiftConfig,
546 pub shift_out: ShiftConfig,
547 // PINCTRL
548 pins: PinConfig,
549 in_count: u8,
550 _pio: PhantomData<&'d mut PIO>,
551}
552
553impl<'d, PIO: Instance> Default for Config<'d, PIO> {
554 fn default() -> Self {
555 Self {
556 clock_divider: 1u8.into(),
557 out_en_sel: Default::default(),
558 inline_out_en: Default::default(),
559 out_sticky: Default::default(),
560 status_sel: Default::default(),
561 status_n: Default::default(),
562 exec: Default::default(),
563 origin: Default::default(),
564 fifo_join: Default::default(),
565 shift_in: Default::default(),
566 shift_out: Default::default(),
567 pins: Default::default(),
568 in_count: Default::default(),
569 _pio: Default::default(),
570 }
571 }
572}
573
574impl<'d, PIO: Instance> Config<'d, PIO> {
575 pub fn get_exec(&self) -> ExecConfig {
576 self.exec
577 }
578 pub unsafe fn set_exec(&mut self, e: ExecConfig) {
579 self.exec = e;
580 }
581
582 pub fn get_pins(&self) -> PinConfig {
583 self.pins
584 }
585 pub unsafe fn set_pins(&mut self, p: PinConfig) {
586 self.pins = p;
587 }
588
589 /// Configures this state machine to use the given program, including jumping to the origin
590 /// of the program. The state machine is not started.
591 ///
592 /// `side_set` sets the range of pins affected by side-sets. The range must be consecutive.
593 /// Side-set pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be
594 /// effective.
595 pub fn use_program(&mut self, prog: &LoadedProgram<'d, PIO>, side_set: &[&Pin<'d, PIO>]) {
596 assert!((prog.side_set.bits() - prog.side_set.optional() as u8) as usize == side_set.len());
597 assert_consecutive(side_set);
598 self.exec.side_en = prog.side_set.optional();
599 self.exec.side_pindir = prog.side_set.pindirs();
600 self.exec.wrap_bottom = prog.wrap.target;
601 self.exec.wrap_top = prog.wrap.source;
602 self.pins.sideset_count = prog.side_set.bits();
603 self.pins.sideset_base = side_set.first().map_or(0, |p| p.pin());
604 self.origin = Some(prog.origin);
605 }
606
607 pub fn set_jmp_pin(&mut self, pin: &Pin<'d, PIO>) {
608 self.exec.jmp_pin = pin.pin();
609 }
610
611 /// Sets the range of pins affected by SET instructions. The range must be consecutive.
612 /// Set pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be
613 /// effective.
614 pub fn set_set_pins(&mut self, pins: &[&Pin<'d, PIO>]) {
615 assert!(pins.len() <= 5);
616 assert_consecutive(pins);
617 self.pins.set_base = pins.first().map_or(0, |p| p.pin());
618 self.pins.set_count = pins.len() as u8;
619 }
620
621 /// Sets the range of pins affected by OUT instructions. The range must be consecutive.
622 /// Out pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be
623 /// effective.
624 pub fn set_out_pins(&mut self, pins: &[&Pin<'d, PIO>]) {
625 assert_consecutive(pins);
626 self.pins.out_base = pins.first().map_or(0, |p| p.pin());
627 self.pins.out_count = pins.len() as u8;
628 }
629
630 /// Sets the range of pins used by IN instructions. The range must be consecutive.
631 /// In pins must configured as inputs using [`StateMachine::set_pin_dirs`] to be
632 /// effective.
633 pub fn set_in_pins(&mut self, pins: &[&Pin<'d, PIO>]) {
634 assert_consecutive(pins);
635 self.pins.in_base = pins.first().map_or(0, |p| p.pin());
636 self.in_count = pins.len() as u8;
637 }
638}
639
640impl<'d, PIO: Instance, const SM: usize> SetConfig for StateMachine<'d, PIO, SM> {
641 type Config = Config<'d, PIO>;
642
643 fn set_config(&mut self, config: &Self::Config) {
644 // sm expects 0 for 65536, truncation makes that happen
645 assert!(config.clock_divider <= 65536, "clkdiv must be <= 65536");
646 assert!(config.clock_divider >= 1, "clkdiv must be >= 1");
647 assert!(config.out_en_sel < 32, "out_en_sel must be < 32");
648 assert!(config.status_n < 32, "status_n must be < 32");
649 // sm expects 0 for 32, truncation makes that happen
650 assert!(config.shift_in.threshold <= 32, "shift_in.threshold must be <= 32");
651 assert!(config.shift_out.threshold <= 32, "shift_out.threshold must be <= 32");
652 let sm = Self::this_sm();
653 unsafe {
654 sm.clkdiv().write(|w| w.0 = config.clock_divider.to_bits() << 8);
655 sm.execctrl().write(|w| {
656 w.set_side_en(config.exec.side_en);
657 w.set_side_pindir(config.exec.side_pindir);
658 w.set_jmp_pin(config.exec.jmp_pin);
659 w.set_out_en_sel(config.out_en_sel);
660 w.set_inline_out_en(config.inline_out_en);
661 w.set_out_sticky(config.out_sticky);
662 w.set_wrap_top(config.exec.wrap_top);
663 w.set_wrap_bottom(config.exec.wrap_bottom);
664 w.set_status_sel(match config.status_sel {
665 StatusSource::TxFifoLevel => SmExecctrlStatusSel::TXLEVEL,
666 StatusSource::RxFifoLevel => SmExecctrlStatusSel::RXLEVEL,
667 });
668 w.set_status_n(config.status_n);
669 });
670 sm.shiftctrl().write(|w| {
671 w.set_fjoin_rx(config.fifo_join == FifoJoin::RxOnly);
672 w.set_fjoin_tx(config.fifo_join == FifoJoin::TxOnly);
673 w.set_pull_thresh(config.shift_out.threshold);
674 w.set_push_thresh(config.shift_in.threshold);
675 w.set_out_shiftdir(config.shift_out.direction == ShiftDirection::Right);
676 w.set_in_shiftdir(config.shift_in.direction == ShiftDirection::Right);
677 w.set_autopull(config.shift_out.auto_fill);
678 w.set_autopush(config.shift_in.auto_fill);
679 });
680 sm.pinctrl().write(|w| {
681 w.set_sideset_count(config.pins.sideset_count);
682 w.set_set_count(config.pins.set_count);
683 w.set_out_count(config.pins.out_count);
684 w.set_in_base(config.pins.in_base);
685 w.set_sideset_base(config.pins.sideset_base);
686 w.set_set_base(config.pins.set_base);
687 w.set_out_base(config.pins.out_base);
688 });
689 if let Some(origin) = config.origin {
690 pio_instr_util::exec_jmp(self, origin);
691 }
692 }
693 }
694}
695
480impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { 696impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
481 #[inline(always)] 697 #[inline(always)]
482 fn this_sm() -> crate::pac::pio::StateMachine { 698 fn this_sm() -> crate::pac::pio::StateMachine {
@@ -504,16 +720,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
504 unsafe { PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0 } 720 unsafe { PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0 }
505 } 721 }
506 722
507 pub fn set_clkdiv(&mut self, div_x_256: u32) {
508 unsafe {
509 Self::this_sm().clkdiv().write(|w| w.0 = div_x_256 << 8);
510 }
511 }
512
513 pub fn get_clkdiv(&self) -> u32 {
514 unsafe { Self::this_sm().clkdiv().read().0 >> 8 }
515 }
516
517 pub fn clkdiv_restart(&mut self) { 723 pub fn clkdiv_restart(&mut self) {
518 let mask = 1u8 << SM; 724 let mask = 1u8 << SM;
519 unsafe { 725 unsafe {
@@ -521,26 +727,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
521 } 727 }
522 } 728 }
523 729
524 /// Configures this state machine to use the given program, including jumping to the origin
525 /// of the program. The state machine is not started.
526 pub fn use_program(&mut self, prog: &LoadedProgram<'d, PIO>, side_set: &[&Pin<'d, PIO>]) {
527 assert!((prog.side_set.bits() - prog.side_set.optional() as u8) as usize == side_set.len());
528 assert_consecutive(side_set);
529 unsafe {
530 Self::this_sm().execctrl().modify(|w| {
531 w.set_side_en(prog.side_set.optional());
532 w.set_side_pindir(prog.side_set.pindirs());
533 w.set_wrap_bottom(prog.wrap.target);
534 w.set_wrap_top(prog.wrap.source);
535 });
536 Self::this_sm().pinctrl().modify(|w| {
537 w.set_sideset_count(prog.side_set.bits());
538 w.set_sideset_base(side_set.first().map_or(0, |p| p.pin()));
539 });
540 pio_instr_util::exec_jmp(self, prog.origin);
541 }
542 }
543
544 fn with_paused(&mut self, f: impl FnOnce(&mut Self)) { 730 fn with_paused(&mut self, f: impl FnOnce(&mut Self)) {
545 let enabled = self.is_enabled(); 731 let enabled = self.is_enabled();
546 self.set_enable(false); 732 self.set_enable(false);
@@ -591,43 +777,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
591 }); 777 });
592 } 778 }
593 779
594 pub fn set_jmp_pin(&mut self, pin: u8) {
595 unsafe {
596 Self::this_sm().execctrl().modify(|w| w.set_jmp_pin(pin));
597 }
598 }
599
600 pub fn get_jmp_pin(&mut self) -> u8 {
601 unsafe { Self::this_sm().execctrl().read().jmp_pin() }
602 }
603
604 pub fn set_fifo_join(&mut self, join: FifoJoin) {
605 let (rx, tx) = match join {
606 FifoJoin::Duplex => (false, false),
607 FifoJoin::RxOnly => (true, false),
608 FifoJoin::TxOnly => (false, true),
609 };
610 unsafe {
611 Self::this_sm().shiftctrl().modify(|w| {
612 w.set_fjoin_rx(rx);
613 w.set_fjoin_tx(tx)
614 });
615 }
616 }
617 pub fn get_fifo_join(&self) -> FifoJoin {
618 unsafe {
619 let r = Self::this_sm().shiftctrl().read();
620 // Ignores the invalid state when both bits are set
621 if r.fjoin_rx() {
622 FifoJoin::RxOnly
623 } else if r.fjoin_tx() {
624 FifoJoin::TxOnly
625 } else {
626 FifoJoin::Duplex
627 }
628 }
629 }
630
631 pub fn clear_fifos(&mut self) { 780 pub fn clear_fifos(&mut self) {
632 // Toggle FJOIN_RX to flush FIFOs 781 // Toggle FJOIN_RX to flush FIFOs
633 unsafe { 782 unsafe {
@@ -641,159 +790,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
641 } 790 }
642 } 791 }
643 792
644 pub fn set_pull_threshold(&mut self, threshold: u8) {
645 unsafe {
646 Self::this_sm().shiftctrl().modify(|w| w.set_pull_thresh(threshold));
647 }
648 }
649
650 pub fn get_pull_threshold(&self) -> u8 {
651 unsafe { Self::this_sm().shiftctrl().read().pull_thresh() }
652 }
653 pub fn set_push_threshold(&mut self, threshold: u8) {
654 unsafe {
655 Self::this_sm().shiftctrl().modify(|w| w.set_push_thresh(threshold));
656 }
657 }
658
659 pub fn get_push_threshold(&self) -> u8 {
660 unsafe { Self::this_sm().shiftctrl().read().push_thresh() }
661 }
662
663 pub fn set_out_shift_dir(&mut self, dir: ShiftDirection) {
664 unsafe {
665 Self::this_sm()
666 .shiftctrl()
667 .modify(|w| w.set_out_shiftdir(dir == ShiftDirection::Right));
668 }
669 }
670 pub fn get_out_shiftdir(&self) -> ShiftDirection {
671 unsafe {
672 if Self::this_sm().shiftctrl().read().out_shiftdir() {
673 ShiftDirection::Right
674 } else {
675 ShiftDirection::Left
676 }
677 }
678 }
679
680 pub fn set_in_shift_dir(&mut self, dir: ShiftDirection) {
681 unsafe {
682 Self::this_sm()
683 .shiftctrl()
684 .modify(|w| w.set_in_shiftdir(dir == ShiftDirection::Right));
685 }
686 }
687 pub fn get_in_shiftdir(&self) -> ShiftDirection {
688 unsafe {
689 if Self::this_sm().shiftctrl().read().in_shiftdir() {
690 ShiftDirection::Right
691 } else {
692 ShiftDirection::Left
693 }
694 }
695 }
696
697 pub fn set_autopull(&mut self, auto: bool) {
698 unsafe {
699 Self::this_sm().shiftctrl().modify(|w| w.set_autopull(auto));
700 }
701 }
702
703 pub fn is_autopull(&self) -> bool {
704 unsafe { Self::this_sm().shiftctrl().read().autopull() }
705 }
706
707 pub fn set_autopush(&mut self, auto: bool) {
708 unsafe {
709 Self::this_sm().shiftctrl().modify(|w| w.set_autopush(auto));
710 }
711 }
712
713 pub fn is_autopush(&self) -> bool {
714 unsafe { Self::this_sm().shiftctrl().read().autopush() }
715 }
716
717 pub fn get_addr(&self) -> u8 {
718 unsafe { Self::this_sm().addr().read().addr() }
719 }
720
721 /// Set the range of out pins affected by a set instruction.
722 pub fn set_set_range(&mut self, base: u8, count: u8) {
723 assert!(base + count < 32);
724 unsafe {
725 Self::this_sm().pinctrl().modify(|w| {
726 w.set_set_base(base);
727 w.set_set_count(count)
728 });
729 }
730 }
731
732 /// Get the range of out pins affected by a set instruction. Returns (base, count).
733 pub fn get_set_range(&self) -> (u8, u8) {
734 unsafe {
735 let r = Self::this_sm().pinctrl().read();
736 (r.set_base(), r.set_count())
737 }
738 }
739
740 pub fn set_in_base_pin(&mut self, base: &Pin<PIO>) {
741 unsafe {
742 Self::this_sm().pinctrl().modify(|w| w.set_in_base(base.pin()));
743 }
744 }
745
746 pub fn get_in_base(&self) -> u8 {
747 unsafe {
748 let r = Self::this_sm().pinctrl().read();
749 r.in_base()
750 }
751 }
752
753 pub fn set_out_range(&mut self, base: u8, count: u8) {
754 assert!(base + count < 32);
755 unsafe {
756 Self::this_sm().pinctrl().modify(|w| {
757 w.set_out_base(base);
758 w.set_out_count(count)
759 });
760 }
761 }
762
763 /// Get the range of out pins affected by a set instruction. Returns (base, count).
764 pub fn get_out_range(&self) -> (u8, u8) {
765 unsafe {
766 let r = Self::this_sm().pinctrl().read();
767 (r.out_base(), r.out_count())
768 }
769 }
770
771 pub fn set_out_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&Pin<PIO>]) {
772 let count = pins.len();
773 assert!(count >= 1);
774 let start = pins[0].pin() as usize;
775 assert!(start + pins.len() <= 32);
776 for i in 0..count {
777 assert!(pins[i].pin() as usize == start + i, "Pins must be sequential");
778 }
779 self.set_out_range(start as u8, count as u8);
780 }
781
782 pub fn set_set_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&Pin<PIO>]) {
783 let count = pins.len();
784 assert!(count >= 1);
785 let start = pins[0].pin() as usize;
786 assert!(start + pins.len() <= 32);
787 for i in 0..count {
788 assert!(pins[i].pin() as usize == start + i, "Pins must be sequential");
789 }
790 self.set_set_range(start as u8, count as u8);
791 }
792
793 pub fn get_current_instr() -> u32 {
794 unsafe { Self::this_sm().instr().read().0 }
795 }
796
797 pub fn exec_instr(&mut self, instr: u16) { 793 pub fn exec_instr(&mut self, instr: u16) {
798 unsafe { 794 unsafe {
799 Self::this_sm().instr().write(|w| w.set_instr(instr)); 795 Self::this_sm().instr().write(|w| w.set_instr(instr));