aboutsummaryrefslogtreecommitdiff
path: root/cyw43-pio
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-05-30 22:43:40 +0200
committerDario Nieuwenhuis <[email protected]>2023-05-30 22:43:40 +0200
commitb3bbe5eb2d0f7ec6c8cae6e0486cb46a433fe11a (patch)
tree620783af45d908710b53bf792f4a449a274c98df /cyw43-pio
parentf5d0d28ac3cfcb74eaa59bbe984b7969a0743724 (diff)
parentc327c6cd6fc3c11cfaf83cf64591940d401c5f6b (diff)
Merge remote-tracking branch 'cyw43/master' into cyw43
Diffstat (limited to 'cyw43-pio')
-rw-r--r--cyw43-pio/Cargo.toml17
-rw-r--r--cyw43-pio/src/lib.rs229
2 files changed, 246 insertions, 0 deletions
diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml
new file mode 100644
index 000000000..a7af2c8e2
--- /dev/null
+++ b/cyw43-pio/Cargo.toml
@@ -0,0 +1,17 @@
1[package]
2name = "cyw43-pio"
3version = "0.1.0"
4edition = "2021"
5
6[features]
7# If disabled, SPI runs at 31.25MHz
8# If enabled, SPI runs at 62.5MHz, which is 25% higher than 50Mhz which is the maximum according to the CYW43439 datasheet.
9overclock = []
10
11[dependencies]
12cyw43 = { path = "../cyw43" }
13embassy-rp = { version = "0.1.0", features = ["unstable-traits", "nightly", "unstable-pac", "time-driver"] }
14pio-proc = "0.2"
15pio = "0.2.1"
16fixed = "1.23.1"
17defmt = { version = "0.3", optional = true } \ No newline at end of file
diff --git a/cyw43-pio/src/lib.rs b/cyw43-pio/src/lib.rs
new file mode 100644
index 000000000..dca30c74d
--- /dev/null
+++ b/cyw43-pio/src/lib.rs
@@ -0,0 +1,229 @@
1#![no_std]
2#![allow(incomplete_features)]
3#![feature(async_fn_in_trait)]
4
5use core::slice;
6
7use cyw43::SpiBusCyw43;
8use embassy_rp::dma::Channel;
9use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate};
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};
13use fixed::FixedU32;
14use pio_proc::pio_asm;
15
16pub struct PioSpi<'d, CS: Pin, PIO: Instance, const SM: usize, DMA> {
17 cs: Output<'d, CS>,
18 sm: StateMachine<'d, PIO, SM>,
19 irq: Irq<'d, PIO, 0>,
20 dma: PeripheralRef<'d, DMA>,
21 wrap_target: u8,
22}
23
24impl<'d, CS, PIO, const SM: usize, DMA> PioSpi<'d, CS, PIO, SM, DMA>
25where
26 DMA: Channel,
27 CS: Pin,
28 PIO: Instance,
29{
30 pub fn new<DIO, CLK>(
31 common: &mut Common<'d, PIO>,
32 mut sm: StateMachine<'d, PIO, SM>,
33 irq: Irq<'d, PIO, 0>,
34 cs: Output<'d, CS>,
35 dio: DIO,
36 clk: CLK,
37 dma: impl Peripheral<P = DMA> + 'd,
38 ) -> Self
39 where
40 DIO: PioPin,
41 CLK: PioPin,
42 {
43 #[cfg(feature = "overclock")]
44 let program = pio_asm!(
45 ".side_set 1"
46
47 ".wrap_target"
48 // write out x-1 bits
49 "lp:"
50 "out pins, 1 side 0"
51 "jmp x-- lp side 1"
52 // switch directions
53 "set pindirs, 0 side 0"
54 "nop side 1" // necessary for clkdiv=1.
55 "nop side 0"
56 // read in y-1 bits
57 "lp2:"
58 "in pins, 1 side 1"
59 "jmp y-- lp2 side 0"
60
61 // wait for event and irq host
62 "wait 1 pin 0 side 0"
63 "irq 0 side 0"
64
65 ".wrap"
66 );
67 #[cfg(not(feature = "overclock"))]
68 let program = pio_asm!(
69 ".side_set 1"
70
71 ".wrap_target"
72 // write out x-1 bits
73 "lp:"
74 "out pins, 1 side 0"
75 "jmp x-- lp side 1"
76 // switch directions
77 "set pindirs, 0 side 0"
78 "nop side 0"
79 // read in y-1 bits
80 "lp2:"
81 "in pins, 1 side 1"
82 "jmp y-- lp2 side 0"
83
84 // wait for event and irq host
85 "wait 1 pin 0 side 0"
86 "irq 0 side 0"
87
88 ".wrap"
89 );
90
91 let relocated = RelocatedProgram::new(&program.program);
92
93 let mut pin_io: embassy_rp::pio::Pin<PIO> = common.make_pio_pin(dio);
94 pin_io.set_pull(Pull::None);
95 pin_io.set_schmitt(true);
96 pin_io.set_input_sync_bypass(true);
97 pin_io.set_drive_strength(Drive::_12mA);
98 pin_io.set_slew_rate(SlewRate::Fast);
99
100 let mut pin_clk = common.make_pio_pin(clk);
101 pin_clk.set_drive_strength(Drive::_12mA);
102 pin_clk.set_slew_rate(SlewRate::Fast);
103
104 let mut cfg = Config::default();
105 cfg.use_program(&common.load_program(&relocated), &[&pin_clk]);
106 cfg.set_out_pins(&[&pin_io]);
107 cfg.set_in_pins(&[&pin_io]);
108 cfg.set_set_pins(&[&pin_io]);
109 cfg.shift_out.direction = ShiftDirection::Left;
110 cfg.shift_out.auto_fill = true;
111 //cfg.shift_out.threshold = 32;
112 cfg.shift_in.direction = ShiftDirection::Left;
113 cfg.shift_in.auto_fill = true;
114 //cfg.shift_in.threshold = 32;
115
116 #[cfg(feature = "overclock")]
117 {
118 // 125mhz Pio => 62.5Mhz SPI Freq. 25% higher than theoretical maximum according to
119 // data sheet, but seems to work fine.
120 cfg.clock_divider = FixedU32::from_bits(0x0100);
121 }
122
123 #[cfg(not(feature = "overclock"))]
124 {
125 // same speed as pico-sdk, 62.5Mhz
126 // This is actually the fastest we can go without overclocking.
127 // According to data sheet, the theoretical maximum is 100Mhz Pio => 50Mhz SPI Freq.
128 // However, the PIO uses a fractional divider, which works by introducing jitter when
129 // the divider is not an integer. It does some clocks at 125mhz and others at 62.5mhz
130 // so that it averages out to the desired frequency of 100mhz. The 125mhz clock cycles
131 // violate the maximum from the data sheet.
132 cfg.clock_divider = FixedU32::from_bits(0x0200);
133 }
134
135 sm.set_config(&cfg);
136
137 sm.set_pin_dirs(Direction::Out, &[&pin_clk, &pin_io]);
138 sm.set_pins(Level::Low, &[&pin_clk, &pin_io]);
139
140 Self {
141 cs,
142 sm,
143 irq,
144 dma: dma.into_ref(),
145 wrap_target: relocated.wrap().target,
146 }
147 }
148
149 pub async fn write(&mut self, write: &[u32]) -> u32 {
150 self.sm.set_enable(false);
151 let write_bits = write.len() * 32 - 1;
152 let read_bits = 31;
153
154 #[cfg(feature = "defmt")]
155 defmt::trace!("write={} read={}", write_bits, read_bits);
156
157 unsafe {
158 pio_instr_util::set_x(&mut self.sm, write_bits as u32);
159 pio_instr_util::set_y(&mut self.sm, read_bits as u32);
160 pio_instr_util::set_pindir(&mut self.sm, 0b1);
161 pio_instr_util::exec_jmp(&mut self.sm, self.wrap_target);
162 }
163
164 self.sm.set_enable(true);
165
166 self.sm.tx().dma_push(self.dma.reborrow(), write).await;
167
168 let mut status = 0;
169 self.sm
170 .rx()
171 .dma_pull(self.dma.reborrow(), slice::from_mut(&mut status))
172 .await;
173 status
174 }
175
176 pub async fn cmd_read(&mut self, cmd: u32, read: &mut [u32]) -> u32 {
177 self.sm.set_enable(false);
178 let write_bits = 31;
179 let read_bits = read.len() * 32 + 32 - 1;
180
181 #[cfg(feature = "defmt")]
182 defmt::trace!("write={} read={}", write_bits, read_bits);
183
184 unsafe {
185 pio_instr_util::set_y(&mut self.sm, read_bits as u32);
186 pio_instr_util::set_x(&mut self.sm, write_bits as u32);
187 pio_instr_util::set_pindir(&mut self.sm, 0b1);
188 pio_instr_util::exec_jmp(&mut self.sm, self.wrap_target);
189 }
190
191 // self.cs.set_low();
192 self.sm.set_enable(true);
193
194 self.sm.tx().dma_push(self.dma.reborrow(), slice::from_ref(&cmd)).await;
195 self.sm.rx().dma_pull(self.dma.reborrow(), read).await;
196
197 let mut status = 0;
198 self.sm
199 .rx()
200 .dma_pull(self.dma.reborrow(), slice::from_mut(&mut status))
201 .await;
202 status
203 }
204}
205
206impl<'d, CS, PIO, const SM: usize, DMA> SpiBusCyw43 for PioSpi<'d, CS, PIO, SM, DMA>
207where
208 CS: Pin,
209 PIO: Instance,
210 DMA: Channel,
211{
212 async fn cmd_write(&mut self, write: &[u32]) -> u32 {
213 self.cs.set_low();
214 let status = self.write(write).await;
215 self.cs.set_high();
216 status
217 }
218
219 async fn cmd_read(&mut self, write: u32, read: &mut [u32]) -> u32 {
220 self.cs.set_low();
221 let status = self.cmd_read(write, read).await;
222 self.cs.set_high();
223 status
224 }
225
226 async fn wait_for_event(&mut self) {
227 self.irq.wait().await;
228 }
229}