aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/Cargo.toml4
-rw-r--r--embassy-stm32/build.rs98
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/macros.rs11
-rw-r--r--embassy-stm32/src/xspi/enums.rs364
-rw-r--r--embassy-stm32/src/xspi/mod.rs1425
-rw-r--r--examples/stm32h7rs/src/bin/xspi_memory_mapped.rs448
7 files changed, 2349 insertions, 3 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 8204a0fea..4d0555d4a 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -73,7 +73,7 @@ rand_core = "0.6.3"
73sdio-host = "0.5.0" 73sdio-host = "0.5.0"
74critical-section = "1.1" 74critical-section = "1.1"
75#stm32-metapac = { version = "16" } 75#stm32-metapac = { version = "16" }
76stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-4a964af03b298de30ff9f84fcfa890bcab4ce609" } 76stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-a7a30c9d54e7415709c463a537501691784672db" }
77 77
78vcell = "0.1.3" 78vcell = "0.1.3"
79nb = "1.0.0" 79nb = "1.0.0"
@@ -102,7 +102,7 @@ proc-macro2 = "1.0.36"
102quote = "1.0.15" 102quote = "1.0.15"
103 103
104#stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} 104#stm32-metapac = { version = "16", default-features = false, features = ["metadata"]}
105stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-4a964af03b298de30ff9f84fcfa890bcab4ce609", default-features = false, features = ["metadata"] } 105stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-a7a30c9d54e7415709c463a537501691784672db", default-features = false, features = ["metadata"] }
106 106
107[features] 107[features]
108default = ["rt"] 108default = ["rt"]
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 798133162..b4e61878c 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -62,7 +62,13 @@ fn main() {
62 // generate one singleton per peripheral (with many exceptions...) 62 // generate one singleton per peripheral (with many exceptions...)
63 for p in METADATA.peripherals { 63 for p in METADATA.peripherals {
64 if let Some(r) = &p.registers { 64 if let Some(r) = &p.registers {
65 if r.kind == "adccommon" || r.kind == "sai" || r.kind == "ucpd" || r.kind == "otg" || r.kind == "octospi" { 65 if r.kind == "adccommon"
66 || r.kind == "sai"
67 || r.kind == "ucpd"
68 || r.kind == "otg"
69 || r.kind == "octospi"
70 || r.kind == "xspi"
71 {
66 // TODO: should we emit this for all peripherals? if so, we will need a list of all 72 // TODO: should we emit this for all peripherals? if so, we will need a list of all
67 // possible peripherals across all chips, so that we can declare the configs 73 // possible peripherals across all chips, so that we can declare the configs
68 // (replacing the hard-coded list of `peri_*` cfgs below) 74 // (replacing the hard-coded list of `peri_*` cfgs below)
@@ -116,6 +122,7 @@ fn main() {
116 "peri_usb_otg_fs", 122 "peri_usb_otg_fs",
117 "peri_usb_otg_hs", 123 "peri_usb_otg_hs",
118 "peri_octospi2", 124 "peri_octospi2",
125 "peri_xspi2",
119 ]); 126 ]);
120 cfgs.declare_all(&["mco", "mco1", "mco2"]); 127 cfgs.declare_all(&["mco", "mco1", "mco2"]);
121 128
@@ -1098,6 +1105,72 @@ fn main() {
1098 (("octospim", "P2_NCS"), quote!(crate::ospi::NSSPin)), 1105 (("octospim", "P2_NCS"), quote!(crate::ospi::NSSPin)),
1099 (("octospim", "P2_CLK"), quote!(crate::ospi::SckPin)), 1106 (("octospim", "P2_CLK"), quote!(crate::ospi::SckPin)),
1100 (("octospim", "P2_NCLK"), quote!(crate::ospi::NckPin)), 1107 (("octospim", "P2_NCLK"), quote!(crate::ospi::NckPin)),
1108 (("xspi", "IO0"), quote!(crate::xspi::D0Pin)),
1109 (("xspi", "IO1"), quote!(crate::xspi::D1Pin)),
1110 (("xspi", "IO2"), quote!(crate::xspi::D2Pin)),
1111 (("xspi", "IO3"), quote!(crate::xspi::D3Pin)),
1112 (("xspi", "IO4"), quote!(crate::xspi::D4Pin)),
1113 (("xspi", "IO5"), quote!(crate::xspi::D5Pin)),
1114 (("xspi", "IO6"), quote!(crate::xspi::D6Pin)),
1115 (("xspi", "IO7"), quote!(crate::xspi::D7Pin)),
1116 (("xspi", "IO8"), quote!(crate::xspi::D8Pin)),
1117 (("xspi", "IO9"), quote!(crate::xspi::D9Pin)),
1118 (("xspi", "IO10"), quote!(crate::xspi::D10Pin)),
1119 (("xspi", "IO11"), quote!(crate::xspi::D11Pin)),
1120 (("xspi", "IO12"), quote!(crate::xspi::D12Pin)),
1121 (("xspi", "IO13"), quote!(crate::xspi::D13Pin)),
1122 (("xspi", "IO14"), quote!(crate::xspi::D14Pin)),
1123 (("xspi", "IO15"), quote!(crate::xspi::D15Pin)),
1124 (("xspi", "DQS0"), quote!(crate::xspi::DQS0Pin)),
1125 (("xspi", "DQS1"), quote!(crate::xspi::DQS1Pin)),
1126 (("xspi", "NCS1"), quote!(crate::xspi::NCSPin)),
1127 (("xspi", "NCS2"), quote!(crate::xspi::NCSPin)),
1128 (("xspi", "CLK"), quote!(crate::xspi::CLKPin)),
1129 (("xspi", "NCLK"), quote!(crate::xspi::NCLKPin)),
1130 (("xspim", "P1_IO0"), quote!(crate::xspi::D0Pin)),
1131 (("xspim", "P1_IO1"), quote!(crate::xspi::D1Pin)),
1132 (("xspim", "P1_IO2"), quote!(crate::xspi::D2Pin)),
1133 (("xspim", "P1_IO3"), quote!(crate::xspi::D3Pin)),
1134 (("xspim", "P1_IO4"), quote!(crate::xspi::D4Pin)),
1135 (("xspim", "P1_IO5"), quote!(crate::xspi::D5Pin)),
1136 (("xspim", "P1_IO6"), quote!(crate::xspi::D6Pin)),
1137 (("xspim", "P1_IO7"), quote!(crate::xspi::D7Pin)),
1138 (("xspim", "P1_IO8"), quote!(crate::xspi::D8Pin)),
1139 (("xspim", "P1_IO9"), quote!(crate::xspi::D9Pin)),
1140 (("xspim", "P1_IO10"), quote!(crate::xspi::D10Pin)),
1141 (("xspim", "P1_IO11"), quote!(crate::xspi::D11Pin)),
1142 (("xspim", "P1_IO12"), quote!(crate::xspi::D12Pin)),
1143 (("xspim", "P1_IO13"), quote!(crate::xspi::D13Pin)),
1144 (("xspim", "P1_IO14"), quote!(crate::xspi::D14Pin)),
1145 (("xspim", "P1_IO15"), quote!(crate::xspi::D15Pin)),
1146 (("xspim", "P1_DQS0"), quote!(crate::xspi::DQS0Pin)),
1147 (("xspim", "P1_DQS1"), quote!(crate::xspi::DQS1Pin)),
1148 (("xspim", "P1_NCS1"), quote!(crate::xspi::NCSPin)),
1149 (("xspim", "P1_NCS2"), quote!(crate::xspi::NCSPin)),
1150 (("xspim", "P1_CLK"), quote!(crate::xspi::CLKPin)),
1151 (("xspim", "P1_NCLK"), quote!(crate::xspi::NCLKPin)),
1152 (("xspim", "P2_IO0"), quote!(crate::xspi::D0Pin)),
1153 (("xspim", "P2_IO1"), quote!(crate::xspi::D1Pin)),
1154 (("xspim", "P2_IO2"), quote!(crate::xspi::D2Pin)),
1155 (("xspim", "P2_IO3"), quote!(crate::xspi::D3Pin)),
1156 (("xspim", "P2_IO4"), quote!(crate::xspi::D4Pin)),
1157 (("xspim", "P2_IO5"), quote!(crate::xspi::D5Pin)),
1158 (("xspim", "P2_IO6"), quote!(crate::xspi::D6Pin)),
1159 (("xspim", "P2_IO7"), quote!(crate::xspi::D7Pin)),
1160 (("xspim", "P2_IO8"), quote!(crate::xspi::D8Pin)),
1161 (("xspim", "P2_IO9"), quote!(crate::xspi::D9Pin)),
1162 (("xspim", "P2_IO10"), quote!(crate::xspi::D10Pin)),
1163 (("xspim", "P2_IO11"), quote!(crate::xspi::D11Pin)),
1164 (("xspim", "P2_IO12"), quote!(crate::xspi::D12Pin)),
1165 (("xspim", "P2_IO13"), quote!(crate::xspi::D13Pin)),
1166 (("xspim", "P2_IO14"), quote!(crate::xspi::D14Pin)),
1167 (("xspim", "P2_IO15"), quote!(crate::xspi::D15Pin)),
1168 (("xspim", "P2_DQS0"), quote!(crate::xspi::DQS0Pin)),
1169 (("xspim", "P2_DQS1"), quote!(crate::xspi::DQS1Pin)),
1170 (("xspim", "P2_NCS1"), quote!(crate::xspi::NCSPin)),
1171 (("xspim", "P2_NCS2"), quote!(crate::xspi::NCSPin)),
1172 (("xspim", "P2_CLK"), quote!(crate::xspi::CLKPin)),
1173 (("xspim", "P2_NCLK"), quote!(crate::xspi::NCLKPin)),
1101 (("hspi", "IO0"), quote!(crate::hspi::D0Pin)), 1174 (("hspi", "IO0"), quote!(crate::hspi::D0Pin)),
1102 (("hspi", "IO1"), quote!(crate::hspi::D1Pin)), 1175 (("hspi", "IO1"), quote!(crate::hspi::D1Pin)),
1103 (("hspi", "IO2"), quote!(crate::hspi::D2Pin)), 1176 (("hspi", "IO2"), quote!(crate::hspi::D2Pin)),
@@ -1190,6 +1263,29 @@ fn main() {
1190 peri = format_ident!("{}", "OCTOSPI1"); 1263 peri = format_ident!("{}", "OCTOSPI1");
1191 } 1264 }
1192 1265
1266 // XSPIM is special
1267 if p.name == "XSPIM" {
1268 if pin.signal.starts_with("P1") {
1269 peri = format_ident!("{}", "XSPI1");
1270 } else if pin.signal.starts_with("P2") {
1271 peri = format_ident!("{}", "XSPI2");
1272 } else {
1273 panic! {"malformed XSPIM pin: {:?}", pin}
1274 }
1275 }
1276
1277 // XSPI NCS pin to CSSEL mapping
1278 if pin.signal.ends_with("NCS1") {
1279 g.extend(quote! {
1280 sel_trait_impl!(crate::xspi::NCSEither, #peri, #pin_name, 0);
1281 })
1282 }
1283 if pin.signal.ends_with("NCS2") {
1284 g.extend(quote! {
1285 sel_trait_impl!(crate::xspi::NCSEither, #peri, #pin_name, 1);
1286 })
1287 }
1288
1193 g.extend(quote! { 1289 g.extend(quote! {
1194 pin_trait_impl!(#tr, #peri, #pin_name, #af); 1290 pin_trait_impl!(#tr, #peri, #pin_name, #af);
1195 }) 1291 })
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 4d7aac81f..226293a9d 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -127,6 +127,8 @@ pub mod usart;
127pub mod usb; 127pub mod usb;
128#[cfg(iwdg)] 128#[cfg(iwdg)]
129pub mod wdg; 129pub mod wdg;
130#[cfg(xspi)]
131pub mod xspi;
130 132
131// This must go last, so that it sees all the impl_foo! macros defined earlier. 133// This must go last, so that it sees all the impl_foo! macros defined earlier.
132pub(crate) mod _generated { 134pub(crate) mod _generated {
diff --git a/embassy-stm32/src/macros.rs b/embassy-stm32/src/macros.rs
index 2c181a254..7526bb180 100644
--- a/embassy-stm32/src/macros.rs
+++ b/embassy-stm32/src/macros.rs
@@ -60,6 +60,17 @@ macro_rules! pin_trait_impl {
60 }; 60 };
61} 61}
62 62
63#[allow(unused_macros)]
64macro_rules! sel_trait_impl {
65 (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $sel:expr) => {
66 impl crate::$mod::$trait<crate::peripherals::$instance $(, crate::$mod::$mode)?> for crate::peripherals::$pin {
67 fn sel(&self) -> u8 {
68 $sel
69 }
70 }
71 };
72}
73
63// ==================== 74// ====================
64 75
65macro_rules! dma_trait { 76macro_rules! dma_trait {
diff --git a/embassy-stm32/src/xspi/enums.rs b/embassy-stm32/src/xspi/enums.rs
new file mode 100644
index 000000000..c96641180
--- /dev/null
+++ b/embassy-stm32/src/xspi/enums.rs
@@ -0,0 +1,364 @@
1//! Enums used in Xspi configuration.
2#[derive(Copy, Clone)]
3pub(crate) enum XspiMode {
4 IndirectWrite,
5 IndirectRead,
6 #[expect(dead_code)]
7 AutoPolling,
8 #[expect(dead_code)]
9 MemoryMapped,
10}
11
12impl Into<u8> for XspiMode {
13 fn into(self) -> u8 {
14 match self {
15 XspiMode::IndirectWrite => 0b00,
16 XspiMode::IndirectRead => 0b01,
17 XspiMode::AutoPolling => 0b10,
18 XspiMode::MemoryMapped => 0b11,
19 }
20 }
21}
22
23/// Xspi lane width
24#[derive(Copy, Clone)]
25pub enum XspiWidth {
26 /// None
27 NONE,
28 /// Single lane
29 SING,
30 /// Dual lanes
31 DUAL,
32 /// Quad lanes
33 QUAD,
34 /// Eight lanes
35 OCTO,
36}
37
38impl Into<u8> for XspiWidth {
39 fn into(self) -> u8 {
40 match self {
41 XspiWidth::NONE => 0b00,
42 XspiWidth::SING => 0b01,
43 XspiWidth::DUAL => 0b10,
44 XspiWidth::QUAD => 0b11,
45 XspiWidth::OCTO => 0b100,
46 }
47 }
48}
49
50/// Wrap Size
51#[allow(missing_docs)]
52#[derive(Copy, Clone)]
53pub enum WrapSize {
54 None,
55 _16Bytes,
56 _32Bytes,
57 _64Bytes,
58 _128Bytes,
59}
60
61impl Into<u8> for WrapSize {
62 fn into(self) -> u8 {
63 match self {
64 WrapSize::None => 0x00,
65 WrapSize::_16Bytes => 0x02,
66 WrapSize::_32Bytes => 0x03,
67 WrapSize::_64Bytes => 0x04,
68 WrapSize::_128Bytes => 0x05,
69 }
70 }
71}
72
73/// Memory Type
74#[allow(missing_docs)]
75#[derive(Copy, Clone)]
76pub enum MemoryType {
77 Micron,
78 Macronix,
79 Standard,
80 MacronixRam,
81 HyperBusMemory,
82 HyperBusRegister,
83}
84
85impl Into<u8> for MemoryType {
86 fn into(self) -> u8 {
87 match self {
88 MemoryType::Micron => 0x00,
89 MemoryType::Macronix => 0x01,
90 MemoryType::Standard => 0x02,
91 MemoryType::MacronixRam => 0x03,
92 MemoryType::HyperBusMemory => 0x04,
93 MemoryType::HyperBusRegister => 0x04,
94 }
95 }
96}
97
98/// Xspi memory size.
99#[allow(missing_docs)]
100#[derive(Copy, Clone)]
101pub enum MemorySize {
102 _1KiB,
103 _2KiB,
104 _4KiB,
105 _8KiB,
106 _16KiB,
107 _32KiB,
108 _64KiB,
109 _128KiB,
110 _256KiB,
111 _512KiB,
112 _1MiB,
113 _2MiB,
114 _4MiB,
115 _8MiB,
116 _16MiB,
117 _32MiB,
118 _64MiB,
119 _128MiB,
120 _256MiB,
121 _512MiB,
122 _1GiB,
123 _2GiB,
124 _4GiB,
125 Other(u8),
126}
127
128impl Into<u8> for MemorySize {
129 fn into(self) -> u8 {
130 match self {
131 MemorySize::_1KiB => 9,
132 MemorySize::_2KiB => 10,
133 MemorySize::_4KiB => 11,
134 MemorySize::_8KiB => 12,
135 MemorySize::_16KiB => 13,
136 MemorySize::_32KiB => 14,
137 MemorySize::_64KiB => 15,
138 MemorySize::_128KiB => 16,
139 MemorySize::_256KiB => 17,
140 MemorySize::_512KiB => 18,
141 MemorySize::_1MiB => 19,
142 MemorySize::_2MiB => 20,
143 MemorySize::_4MiB => 21,
144 MemorySize::_8MiB => 22,
145 MemorySize::_16MiB => 23,
146 MemorySize::_32MiB => 24,
147 MemorySize::_64MiB => 25,
148 MemorySize::_128MiB => 26,
149 MemorySize::_256MiB => 27,
150 MemorySize::_512MiB => 28,
151 MemorySize::_1GiB => 29,
152 MemorySize::_2GiB => 30,
153 MemorySize::_4GiB => 31,
154 MemorySize::Other(val) => val,
155 }
156 }
157}
158
159/// Xspi Address size
160#[derive(Copy, Clone)]
161pub enum AddressSize {
162 /// 8-bit address
163 _8bit,
164 /// 16-bit address
165 _16bit,
166 /// 24-bit address
167 _24bit,
168 /// 32-bit address
169 _32bit,
170}
171
172impl Into<u8> for AddressSize {
173 fn into(self) -> u8 {
174 match self {
175 AddressSize::_8bit => 0b00,
176 AddressSize::_16bit => 0b01,
177 AddressSize::_24bit => 0b10,
178 AddressSize::_32bit => 0b11,
179 }
180 }
181}
182
183/// Time the Chip Select line stays high.
184#[allow(missing_docs)]
185#[derive(Copy, Clone)]
186pub enum ChipSelectHighTime {
187 _1Cycle,
188 _2Cycle,
189 _3Cycle,
190 _4Cycle,
191 _5Cycle,
192 _6Cycle,
193 _7Cycle,
194 _8Cycle,
195}
196
197impl Into<u8> for ChipSelectHighTime {
198 fn into(self) -> u8 {
199 match self {
200 ChipSelectHighTime::_1Cycle => 0,
201 ChipSelectHighTime::_2Cycle => 1,
202 ChipSelectHighTime::_3Cycle => 2,
203 ChipSelectHighTime::_4Cycle => 3,
204 ChipSelectHighTime::_5Cycle => 4,
205 ChipSelectHighTime::_6Cycle => 5,
206 ChipSelectHighTime::_7Cycle => 6,
207 ChipSelectHighTime::_8Cycle => 7,
208 }
209 }
210}
211
212/// FIFO threshold.
213#[allow(missing_docs)]
214#[derive(Copy, Clone)]
215pub enum FIFOThresholdLevel {
216 _1Bytes,
217 _2Bytes,
218 _3Bytes,
219 _4Bytes,
220 _5Bytes,
221 _6Bytes,
222 _7Bytes,
223 _8Bytes,
224 _9Bytes,
225 _10Bytes,
226 _11Bytes,
227 _12Bytes,
228 _13Bytes,
229 _14Bytes,
230 _15Bytes,
231 _16Bytes,
232 _17Bytes,
233 _18Bytes,
234 _19Bytes,
235 _20Bytes,
236 _21Bytes,
237 _22Bytes,
238 _23Bytes,
239 _24Bytes,
240 _25Bytes,
241 _26Bytes,
242 _27Bytes,
243 _28Bytes,
244 _29Bytes,
245 _30Bytes,
246 _31Bytes,
247 _32Bytes,
248}
249
250impl Into<u8> for FIFOThresholdLevel {
251 fn into(self) -> u8 {
252 match self {
253 FIFOThresholdLevel::_1Bytes => 0,
254 FIFOThresholdLevel::_2Bytes => 1,
255 FIFOThresholdLevel::_3Bytes => 2,
256 FIFOThresholdLevel::_4Bytes => 3,
257 FIFOThresholdLevel::_5Bytes => 4,
258 FIFOThresholdLevel::_6Bytes => 5,
259 FIFOThresholdLevel::_7Bytes => 6,
260 FIFOThresholdLevel::_8Bytes => 7,
261 FIFOThresholdLevel::_9Bytes => 8,
262 FIFOThresholdLevel::_10Bytes => 9,
263 FIFOThresholdLevel::_11Bytes => 10,
264 FIFOThresholdLevel::_12Bytes => 11,
265 FIFOThresholdLevel::_13Bytes => 12,
266 FIFOThresholdLevel::_14Bytes => 13,
267 FIFOThresholdLevel::_15Bytes => 14,
268 FIFOThresholdLevel::_16Bytes => 15,
269 FIFOThresholdLevel::_17Bytes => 16,
270 FIFOThresholdLevel::_18Bytes => 17,
271 FIFOThresholdLevel::_19Bytes => 18,
272 FIFOThresholdLevel::_20Bytes => 19,
273 FIFOThresholdLevel::_21Bytes => 20,
274 FIFOThresholdLevel::_22Bytes => 21,
275 FIFOThresholdLevel::_23Bytes => 22,
276 FIFOThresholdLevel::_24Bytes => 23,
277 FIFOThresholdLevel::_25Bytes => 24,
278 FIFOThresholdLevel::_26Bytes => 25,
279 FIFOThresholdLevel::_27Bytes => 26,
280 FIFOThresholdLevel::_28Bytes => 27,
281 FIFOThresholdLevel::_29Bytes => 28,
282 FIFOThresholdLevel::_30Bytes => 29,
283 FIFOThresholdLevel::_31Bytes => 30,
284 FIFOThresholdLevel::_32Bytes => 31,
285 }
286 }
287}
288
289/// Dummy cycle count
290#[allow(missing_docs)]
291#[derive(Copy, Clone)]
292pub enum DummyCycles {
293 _0,
294 _1,
295 _2,
296 _3,
297 _4,
298 _5,
299 _6,
300 _7,
301 _8,
302 _9,
303 _10,
304 _11,
305 _12,
306 _13,
307 _14,
308 _15,
309 _16,
310 _17,
311 _18,
312 _19,
313 _20,
314 _21,
315 _22,
316 _23,
317 _24,
318 _25,
319 _26,
320 _27,
321 _28,
322 _29,
323 _30,
324 _31,
325}
326
327impl Into<u8> for DummyCycles {
328 fn into(self) -> u8 {
329 match self {
330 DummyCycles::_0 => 0,
331 DummyCycles::_1 => 1,
332 DummyCycles::_2 => 2,
333 DummyCycles::_3 => 3,
334 DummyCycles::_4 => 4,
335 DummyCycles::_5 => 5,
336 DummyCycles::_6 => 6,
337 DummyCycles::_7 => 7,
338 DummyCycles::_8 => 8,
339 DummyCycles::_9 => 9,
340 DummyCycles::_10 => 10,
341 DummyCycles::_11 => 11,
342 DummyCycles::_12 => 12,
343 DummyCycles::_13 => 13,
344 DummyCycles::_14 => 14,
345 DummyCycles::_15 => 15,
346 DummyCycles::_16 => 16,
347 DummyCycles::_17 => 17,
348 DummyCycles::_18 => 18,
349 DummyCycles::_19 => 19,
350 DummyCycles::_20 => 20,
351 DummyCycles::_21 => 21,
352 DummyCycles::_22 => 22,
353 DummyCycles::_23 => 23,
354 DummyCycles::_24 => 24,
355 DummyCycles::_25 => 25,
356 DummyCycles::_26 => 26,
357 DummyCycles::_27 => 27,
358 DummyCycles::_28 => 28,
359 DummyCycles::_29 => 29,
360 DummyCycles::_30 => 30,
361 DummyCycles::_31 => 31,
362 }
363 }
364}
diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs
new file mode 100644
index 000000000..44c10b961
--- /dev/null
+++ b/embassy-stm32/src/xspi/mod.rs
@@ -0,0 +1,1425 @@
1//! XSPI Serial Peripheral Interface
2//!
3
4#![macro_use]
5
6pub mod enums;
7
8use core::marker::PhantomData;
9
10use embassy_embedded_hal::{GetConfig, SetConfig};
11use embassy_hal_internal::PeripheralType;
12pub use enums::*;
13
14use crate::dma::{word, ChannelAndRequest};
15use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed};
16use crate::mode::{Async, Blocking, Mode as PeriMode};
17use crate::pac::xspi::vals::*;
18use crate::pac::xspi::Xspi as Regs;
19#[cfg(xspim_v1)]
20use crate::pac::xspim::Xspim;
21use crate::rcc::{self, RccPeripheral};
22use crate::{peripherals, Peri};
23
24/// XPSI driver config.
25#[derive(Clone, Copy)]
26pub struct Config {
27 /// Fifo threshold used by the peripheral to generate the interrupt indicating data
28 /// or space is available in the FIFO
29 pub fifo_threshold: FIFOThresholdLevel,
30 /// Indicates the type of external device connected
31 pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface
32 /// Defines the size of the external device connected to the XSPI corresponding
33 /// to the number of address bits required to access the device
34 pub device_size: MemorySize,
35 /// Sets the minimum number of clock cycles that the chip select signal must be held high
36 /// between commands
37 pub chip_select_high_time: ChipSelectHighTime,
38 /// Enables the free running clock
39 pub free_running_clock: bool,
40 /// Sets the clock level when the device is not selected
41 pub clock_mode: bool,
42 /// Indicates the wrap size corresponding to the external device configuration
43 pub wrap_size: WrapSize,
44 /// Specified the prescaler factor used for generating the external clock based
45 /// on the AHB clock. 0 = Fkernel, 1 = Fkernel/2, 2 = Fkernel/3 etc.
46 pub clock_prescaler: u8,
47 /// Allows the delay of 1/2 cycle the data sampling to account for external
48 /// signal delays
49 pub sample_shifting: bool,
50 /// Allows hold to 1/4 cycle the data
51 pub delay_hold_quarter_cycle: bool,
52 /// Enables the transaction boundary feature and defines the boundary to release
53 /// the chip select
54 pub chip_select_boundary: u8,
55 /// Enables communication regulation feature. Chip select is released when the other
56 /// XSpi requests access to the bus
57 pub max_transfer: u8,
58 /// Enables the refresh feature, chip select is released every refresh + 1 clock cycles
59 pub refresh: u32,
60}
61
62impl Default for Config {
63 fn default() -> Self {
64 Self {
65 fifo_threshold: FIFOThresholdLevel::_16Bytes, // 32 bytes FIFO, half capacity
66 memory_type: MemoryType::Micron,
67 device_size: MemorySize::Other(0),
68 chip_select_high_time: ChipSelectHighTime::_5Cycle,
69 free_running_clock: false,
70 clock_mode: false,
71 wrap_size: WrapSize::None,
72 clock_prescaler: 0,
73 sample_shifting: false,
74 delay_hold_quarter_cycle: false,
75 chip_select_boundary: 0, // Acceptable range 0 to 31
76 max_transfer: 0,
77 refresh: 0,
78 }
79 }
80}
81
82/// XSPI transfer configuration.
83pub struct TransferConfig {
84 /// Instruction width (IMODE)
85 pub iwidth: XspiWidth,
86 /// Instruction Id
87 pub instruction: Option<u32>,
88 /// Number of Instruction Bytes
89 pub isize: AddressSize,
90 /// Instruction Double Transfer rate enable
91 pub idtr: bool,
92
93 /// Address width (ADMODE)
94 pub adwidth: XspiWidth,
95 /// Device memory address
96 pub address: Option<u32>,
97 /// Number of Address Bytes
98 pub adsize: AddressSize,
99 /// Address Double Transfer rate enable
100 pub addtr: bool,
101
102 /// Alternate bytes width (ABMODE)
103 pub abwidth: XspiWidth,
104 /// Alternate Bytes
105 pub alternate_bytes: Option<u32>,
106 /// Number of Alternate Bytes
107 pub absize: AddressSize,
108 /// Alternate Bytes Double Transfer rate enable
109 pub abdtr: bool,
110
111 /// Data width (DMODE)
112 pub dwidth: XspiWidth,
113 /// Data buffer
114 pub ddtr: bool,
115
116 /// Number of dummy cycles (DCYC)
117 pub dummy: DummyCycles,
118}
119
120impl Default for TransferConfig {
121 fn default() -> Self {
122 Self {
123 iwidth: XspiWidth::NONE,
124 instruction: None,
125 isize: AddressSize::_8bit,
126 idtr: false,
127
128 adwidth: XspiWidth::NONE,
129 address: None,
130 adsize: AddressSize::_8bit,
131 addtr: false,
132
133 abwidth: XspiWidth::NONE,
134 alternate_bytes: None,
135 absize: AddressSize::_8bit,
136 abdtr: false,
137
138 dwidth: XspiWidth::NONE,
139 ddtr: false,
140
141 dummy: DummyCycles::_0,
142 }
143 }
144}
145
146/// Error used for Xspi implementation
147#[derive(Debug)]
148#[cfg_attr(feature = "defmt", derive(defmt::Format))]
149pub enum XspiError {
150 /// Peripheral configuration is invalid
151 InvalidConfiguration,
152 /// Operation configuration is invalid
153 InvalidCommand,
154 /// Size zero buffer passed to instruction
155 EmptyBuffer,
156}
157
158/// XSPI driver.
159pub struct Xspi<'d, T: Instance, M: PeriMode> {
160 _peri: Peri<'d, T>,
161 clk: Option<Peri<'d, AnyPin>>,
162 d0: Option<Peri<'d, AnyPin>>,
163 d1: Option<Peri<'d, AnyPin>>,
164 d2: Option<Peri<'d, AnyPin>>,
165 d3: Option<Peri<'d, AnyPin>>,
166 d4: Option<Peri<'d, AnyPin>>,
167 d5: Option<Peri<'d, AnyPin>>,
168 d6: Option<Peri<'d, AnyPin>>,
169 d7: Option<Peri<'d, AnyPin>>,
170 d8: Option<Peri<'d, AnyPin>>,
171 d9: Option<Peri<'d, AnyPin>>,
172 d10: Option<Peri<'d, AnyPin>>,
173 d11: Option<Peri<'d, AnyPin>>,
174 d12: Option<Peri<'d, AnyPin>>,
175 d13: Option<Peri<'d, AnyPin>>,
176 d14: Option<Peri<'d, AnyPin>>,
177 d15: Option<Peri<'d, AnyPin>>,
178 ncs: Option<Peri<'d, AnyPin>>,
179 // TODO: allow switching between multiple chips
180 ncs_alt: Option<Peri<'d, AnyPin>>,
181 // false if ncs == NCS1, true if ncs == NCS2
182 // (ncs_alt will be the opposite to ncs).
183 _cssel_swap: bool,
184 dqs0: Option<Peri<'d, AnyPin>>,
185 dqs1: Option<Peri<'d, AnyPin>>,
186 dma: Option<ChannelAndRequest<'d>>,
187 _phantom: PhantomData<M>,
188 config: Config,
189 width: XspiWidth,
190}
191
192impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> {
193 /// Enter memory mode.
194 /// The Input `read_config` is used to configure the read operation in memory mode
195 pub fn enable_memory_mapped_mode(
196 &mut self,
197 read_config: TransferConfig,
198 write_config: TransferConfig,
199 ) -> Result<(), XspiError> {
200 // Use configure command to set read config
201 self.configure_command(&read_config, None)?;
202
203 let reg = T::REGS;
204 while reg.sr().read().busy() {}
205
206 reg.ccr().modify(|r| {
207 r.set_dqse(false);
208 });
209
210 // Set wrting configurations, there are separate registers for write configurations in memory mapped mode
211 reg.wccr().modify(|w| {
212 w.set_imode(WccrImode::from_bits(write_config.iwidth.into()));
213 w.set_idtr(write_config.idtr);
214 w.set_isize(WccrIsize::from_bits(write_config.isize.into()));
215
216 w.set_admode(WccrAdmode::from_bits(write_config.adwidth.into()));
217 w.set_addtr(write_config.addtr);
218 w.set_adsize(WccrAdsize::from_bits(write_config.adsize.into()));
219
220 w.set_dmode(WccrDmode::from_bits(write_config.dwidth.into()));
221 w.set_ddtr(write_config.ddtr);
222
223 w.set_abmode(WccrAbmode::from_bits(write_config.abwidth.into()));
224 w.set_dqse(true);
225 });
226
227 reg.wtcr().modify(|w| w.set_dcyc(write_config.dummy.into()));
228
229 // Enable memory mapped mode
230 reg.cr().modify(|r| {
231 r.set_fmode(Fmode::B_0X3);
232 r.set_tcen(false);
233 });
234 Ok(())
235 }
236
237 /// Quit from memory mapped mode
238 pub fn disable_memory_mapped_mode(&mut self) {
239 let reg = T::REGS;
240
241 reg.cr().modify(|r| {
242 r.set_fmode(Fmode::B_0X0);
243 r.set_abort(true);
244 r.set_dmaen(false);
245 r.set_en(false);
246 });
247
248 // Clear transfer complete flag
249 reg.fcr().write(|w| w.set_ctcf(true));
250
251 // Re-enable ospi
252 reg.cr().modify(|r| {
253 r.set_en(true);
254 });
255 }
256
257 fn new_inner(
258 peri: Peri<'d, T>,
259 d0: Option<Peri<'d, AnyPin>>,
260 d1: Option<Peri<'d, AnyPin>>,
261 d2: Option<Peri<'d, AnyPin>>,
262 d3: Option<Peri<'d, AnyPin>>,
263 d4: Option<Peri<'d, AnyPin>>,
264 d5: Option<Peri<'d, AnyPin>>,
265 d6: Option<Peri<'d, AnyPin>>,
266 d7: Option<Peri<'d, AnyPin>>,
267 d8: Option<Peri<'d, AnyPin>>,
268 d9: Option<Peri<'d, AnyPin>>,
269 d10: Option<Peri<'d, AnyPin>>,
270 d11: Option<Peri<'d, AnyPin>>,
271 d12: Option<Peri<'d, AnyPin>>,
272 d13: Option<Peri<'d, AnyPin>>,
273 d14: Option<Peri<'d, AnyPin>>,
274 d15: Option<Peri<'d, AnyPin>>,
275 clk: Option<Peri<'d, AnyPin>>,
276 ncs_cssel: u8,
277 ncs: Option<Peri<'d, AnyPin>>,
278 ncs_alt: Option<Peri<'d, AnyPin>>,
279 dqs0: Option<Peri<'d, AnyPin>>,
280 dqs1: Option<Peri<'d, AnyPin>>,
281 dma: Option<ChannelAndRequest<'d>>,
282 config: Config,
283 width: XspiWidth,
284 dual_quad: bool,
285 ) -> Self {
286 // Enable the interface
287 match T::SPI_IDX {
288 1 => crate::pac::PWR.csr2().modify(|r| r.set_en_xspim1(true)),
289 2 => crate::pac::PWR.csr2().modify(|r| r.set_en_xspim2(true)),
290 _ => unreachable!(),
291 };
292
293 #[cfg(xspim_v1)]
294 {
295 // RCC for xspim should be enabled before writing register
296 crate::pac::RCC.ahb5enr().modify(|w| w.set_iomngren(true));
297
298 // Disable XSPI peripheral first
299 T::REGS.cr().modify(|w| {
300 w.set_en(false);
301 });
302
303 // XSPI IO Manager has been enabled before
304 T::SPIM_REGS.cr().modify(|w| {
305 w.set_muxen(false);
306 w.set_req2ack_time(0xff);
307 });
308 }
309
310 // System configuration
311 rcc::enable_and_reset::<T>();
312 while T::REGS.sr().read().busy() {}
313
314 // Device configuration
315 T::REGS.dcr1().modify(|w| {
316 w.set_devsize(config.device_size.into());
317 w.set_mtyp(Mtyp::from_bits(config.memory_type.into()));
318 w.set_csht(Csht::from_bits(config.chip_select_high_time.into()));
319 w.set_frck(false);
320 w.set_ckmode(Ckmode::from_bits(config.clock_mode.into()));
321 });
322
323 T::REGS.dcr2().modify(|w| {
324 w.set_wrapsize(Wrapsize::from_bits(config.wrap_size.into()));
325 });
326
327 T::REGS.dcr3().modify(|w| {
328 w.set_csbound(Csbound::from_bits(config.chip_select_boundary.into()));
329 #[cfg(xspi_v1)]
330 {
331 w.set_maxtran(Maxtran::from_bits(config.max_transfer.into()));
332 }
333 });
334
335 T::REGS.dcr4().modify(|w| {
336 w.set_refresh(Refresh::from_bits(config.refresh.into()));
337 });
338
339 T::REGS.cr().modify(|w| {
340 w.set_fthres(Fthres::from_bits(config.fifo_threshold.into()));
341 });
342
343 // Wait for busy flag to clear
344 while T::REGS.sr().read().busy() {}
345
346 T::REGS.dcr2().modify(|w| {
347 w.set_prescaler(config.clock_prescaler);
348 });
349
350 // Wait for busy flag to clear after changing prescaler, during calibration
351 while T::REGS.sr().read().busy() {}
352
353 T::REGS.cr().modify(|w| {
354 w.set_dmm(dual_quad);
355
356 assert!(ncs_alt.is_none(), "ncs_alt TODO");
357 let cssel = if ncs_cssel == 0 { Cssel::B_0X0 } else { Cssel::B_0X1 };
358 w.set_cssel(cssel);
359 });
360
361 T::REGS.tcr().modify(|w| {
362 w.set_sshift(config.sample_shifting);
363 w.set_dhqc(config.delay_hold_quarter_cycle);
364 });
365
366 // Enable peripheral
367 T::REGS.cr().modify(|w| {
368 w.set_en(true);
369 });
370
371 // Free running clock needs to be set after peripheral enable
372 if config.free_running_clock {
373 T::REGS.dcr1().modify(|w| {
374 w.set_frck(config.free_running_clock);
375 });
376 }
377
378 Self {
379 _peri: peri,
380 clk,
381 d0,
382 d1,
383 d2,
384 d3,
385 d4,
386 d5,
387 d6,
388 d7,
389 d8,
390 d9,
391 d10,
392 d11,
393 d12,
394 d13,
395 d14,
396 d15,
397 ncs,
398 ncs_alt,
399 _cssel_swap: ncs_cssel == 1,
400 dqs0,
401 dqs1,
402 dma,
403 _phantom: PhantomData,
404 config,
405 width,
406 }
407 }
408
409 // Function to configure the peripheral for the requested command
410 fn configure_command(&mut self, command: &TransferConfig, data_len: Option<usize>) -> Result<(), XspiError> {
411 // Check that transaction doesn't use more than hardware initialized pins
412 if <enums::XspiWidth as Into<u8>>::into(command.iwidth) > <enums::XspiWidth as Into<u8>>::into(self.width)
413 || <enums::XspiWidth as Into<u8>>::into(command.adwidth) > <enums::XspiWidth as Into<u8>>::into(self.width)
414 || <enums::XspiWidth as Into<u8>>::into(command.abwidth) > <enums::XspiWidth as Into<u8>>::into(self.width)
415 || <enums::XspiWidth as Into<u8>>::into(command.dwidth) > <enums::XspiWidth as Into<u8>>::into(self.width)
416 {
417 return Err(XspiError::InvalidCommand);
418 }
419
420 T::REGS.cr().modify(|w| {
421 w.set_fmode(0.into());
422 });
423
424 // Configure alternate bytes
425 if let Some(ab) = command.alternate_bytes {
426 T::REGS.abr().write(|v| v.set_alternate(ab));
427 T::REGS.ccr().modify(|w| {
428 w.set_abmode(CcrAbmode::from_bits(command.abwidth.into()));
429 w.set_abdtr(command.abdtr);
430 w.set_absize(CcrAbsize::from_bits(command.absize.into()));
431 })
432 }
433
434 // Configure dummy cycles
435 T::REGS.tcr().modify(|w| {
436 w.set_dcyc(command.dummy.into());
437 });
438
439 // Configure data
440 if let Some(data_length) = data_len {
441 T::REGS.dlr().write(|v| {
442 v.set_dl((data_length - 1) as u32);
443 })
444 } else {
445 T::REGS.dlr().write(|v| {
446 v.set_dl((0) as u32);
447 })
448 }
449
450 // Configure instruction/address/data modes
451 T::REGS.ccr().modify(|w| {
452 w.set_imode(CcrImode::from_bits(command.iwidth.into()));
453 w.set_idtr(command.idtr);
454 w.set_isize(CcrIsize::from_bits(command.isize.into()));
455
456 w.set_admode(CcrAdmode::from_bits(command.adwidth.into()));
457 w.set_addtr(command.addtr);
458 w.set_adsize(CcrAdsize::from_bits(command.adsize.into()));
459
460 w.set_dmode(CcrDmode::from_bits(command.dwidth.into()));
461 w.set_ddtr(command.ddtr);
462 });
463
464 // Set information required to initiate transaction
465 if let Some(instruction) = command.instruction {
466 if let Some(address) = command.address {
467 T::REGS.ir().write(|v| {
468 v.set_instruction(instruction);
469 });
470
471 T::REGS.ar().write(|v| {
472 v.set_address(address);
473 });
474 } else {
475 // Double check requirements for delay hold and sample shifting
476 // if let None = command.data_len {
477 // if self.config.delay_hold_quarter_cycle && command.idtr {
478 // T::REGS.ccr().modify(|w| {
479 // w.set_ddtr(true);
480 // });
481 // }
482 // }
483
484 // warn!("instruction: {:#x}", instruction);
485 T::REGS.ir().write(|v| {
486 v.set_instruction(instruction);
487 });
488 }
489 } else {
490 if let Some(address) = command.address {
491 T::REGS.ar().write(|v| {
492 v.set_address(address);
493 });
494 } else {
495 // The only single phase transaction supported is instruction only
496 return Err(XspiError::InvalidCommand);
497 }
498 }
499
500 Ok(())
501 }
502
503 /// Function used to control or configure the target device without data transfer
504 pub fn blocking_command(&mut self, command: &TransferConfig) -> Result<(), XspiError> {
505 // Wait for peripheral to be free
506 while T::REGS.sr().read().busy() {}
507
508 // Need additional validation that command configuration doesn't have data set
509 self.configure_command(command, None)?;
510
511 // Transaction initiated by setting final configuration, i.e the instruction register
512 while !T::REGS.sr().read().tcf() {}
513 T::REGS.fcr().write(|w| {
514 w.set_ctcf(true);
515 });
516
517 Ok(())
518 }
519
520 /// Blocking read with byte by byte data transfer
521 pub fn blocking_read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), XspiError> {
522 if buf.is_empty() {
523 return Err(XspiError::EmptyBuffer);
524 }
525
526 // Wait for peripheral to be free
527 while T::REGS.sr().read().busy() {}
528
529 // Ensure DMA is not enabled for this transaction
530 T::REGS.cr().modify(|w| {
531 w.set_dmaen(false);
532 });
533
534 // self.configure_command(&transaction, Some(buf.len()))?;
535 self.configure_command(&transaction, Some(buf.len())).unwrap();
536
537 let current_address = T::REGS.ar().read().address();
538 let current_instruction = T::REGS.ir().read().instruction();
539
540 // For a indirect read transaction, the transaction begins when the instruction/address is set
541 T::REGS
542 .cr()
543 .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectRead.into())));
544 if T::REGS.ccr().read().admode() == CcrAdmode::B_0X0 {
545 T::REGS.ir().write(|v| v.set_instruction(current_instruction));
546 } else {
547 T::REGS.ar().write(|v| v.set_address(current_address));
548 }
549
550 for idx in 0..buf.len() {
551 while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
552 buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut W).read_volatile() };
553 }
554
555 while !T::REGS.sr().read().tcf() {}
556 T::REGS.fcr().write(|v| v.set_ctcf(true));
557
558 Ok(())
559 }
560
561 /// Blocking write with byte by byte data transfer
562 pub fn blocking_write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), XspiError> {
563 if buf.is_empty() {
564 return Err(XspiError::EmptyBuffer);
565 }
566
567 // Wait for peripheral to be free
568 while T::REGS.sr().read().busy() {}
569
570 T::REGS.cr().modify(|w| {
571 w.set_dmaen(false);
572 });
573
574 self.configure_command(&transaction, Some(buf.len()))?;
575
576 T::REGS
577 .cr()
578 .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into())));
579
580 for idx in 0..buf.len() {
581 while !T::REGS.sr().read().ftf() {}
582 unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) };
583 }
584
585 while !T::REGS.sr().read().tcf() {}
586 T::REGS.fcr().write(|v| v.set_ctcf(true));
587
588 Ok(())
589 }
590
591 /// Set new bus configuration
592 pub fn set_config(&mut self, config: &Config) {
593 // Wait for busy flag to clear
594 while T::REGS.sr().read().busy() {}
595
596 // Disable DMA channel while configuring the peripheral
597 T::REGS.cr().modify(|w| {
598 w.set_dmaen(false);
599 });
600
601 // Device configuration
602 T::REGS.dcr1().modify(|w| {
603 w.set_devsize(config.device_size.into());
604 w.set_mtyp(Mtyp::from_bits(config.memory_type.into()));
605 w.set_csht(Csht::from_bits(config.chip_select_high_time.into()));
606 w.set_frck(false);
607 w.set_ckmode(match config.clock_mode {
608 true => Ckmode::B_0X1,
609 false => Ckmode::B_0X0,
610 });
611 });
612
613 T::REGS.dcr2().modify(|w| {
614 w.set_wrapsize(Wrapsize::from_bits(config.wrap_size.into()));
615 });
616
617 T::REGS.dcr3().modify(|w| {
618 w.set_csbound(Csbound::from_bits(config.chip_select_boundary.into()));
619 #[cfg(xspi_v1)]
620 {
621 w.set_maxtran(Maxtran::from_bits(config.max_transfer.into()));
622 }
623 });
624
625 T::REGS.dcr4().modify(|w| {
626 w.set_refresh(Refresh::from_bits(config.refresh.into()));
627 });
628
629 T::REGS.cr().modify(|w| {
630 w.set_fthres(Fthres::from_bits(config.fifo_threshold.into()));
631 });
632
633 // Wait for busy flag to clear
634 while T::REGS.sr().read().busy() {}
635
636 T::REGS.dcr2().modify(|w| {
637 w.set_prescaler(config.clock_prescaler);
638 });
639
640 T::REGS.tcr().modify(|w| {
641 w.set_sshift(config.sample_shifting);
642 w.set_dhqc(config.delay_hold_quarter_cycle);
643 });
644
645 // Enable peripheral
646 T::REGS.cr().modify(|w| {
647 w.set_en(true);
648 });
649
650 // Free running clock needs to be set after peripheral enable
651 if config.free_running_clock {
652 T::REGS.dcr1().modify(|w| {
653 w.set_frck(config.free_running_clock);
654 });
655 }
656
657 self.config = *config;
658 }
659
660 /// Get current configuration
661 pub fn get_config(&self) -> Config {
662 self.config
663 }
664}
665
666impl<'d, T: Instance> Xspi<'d, T, Blocking> {
667 /// Create new blocking XSPI driver for a single spi external chip
668 pub fn new_blocking_singlespi(
669 peri: Peri<'d, T>,
670 clk: Peri<'d, impl CLKPin<T>>,
671 d0: Peri<'d, impl D0Pin<T>>,
672 d1: Peri<'d, impl D1Pin<T>>,
673 ncs: Peri<'d, impl NCSEither<T>>,
674 config: Config,
675 ) -> Self {
676 Self::new_inner(
677 peri,
678 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
679 new_pin!(d1, AfType::input(Pull::None)),
680 None,
681 None,
682 None,
683 None,
684 None,
685 None,
686 None,
687 None,
688 None,
689 None,
690 None,
691 None,
692 None,
693 None,
694 new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
695 ncs.sel(),
696 new_pin!(
697 ncs,
698 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
699 ),
700 None,
701 None,
702 None,
703 None,
704 config,
705 XspiWidth::SING,
706 false,
707 )
708 }
709
710 /// Create new blocking XSPI driver for a dualspi external chip
711 pub fn new_blocking_dualspi(
712 peri: Peri<'d, T>,
713 clk: Peri<'d, impl CLKPin<T>>,
714 d0: Peri<'d, impl D0Pin<T>>,
715 d1: Peri<'d, impl D1Pin<T>>,
716 ncs: Peri<'d, impl NCSEither<T>>,
717 config: Config,
718 ) -> Self {
719 Self::new_inner(
720 peri,
721 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
722 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
723 None,
724 None,
725 None,
726 None,
727 None,
728 None,
729 None,
730 None,
731 None,
732 None,
733 None,
734 None,
735 None,
736 None,
737 new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
738 ncs.sel(),
739 new_pin!(
740 ncs,
741 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
742 ),
743 None,
744 None,
745 None,
746 None,
747 config,
748 XspiWidth::DUAL,
749 false,
750 )
751 }
752
753 /// Create new blocking XSPI driver for a quadspi external chip
754 pub fn new_blocking_quadspi(
755 peri: Peri<'d, T>,
756 clk: Peri<'d, impl CLKPin<T>>,
757 d0: Peri<'d, impl D0Pin<T>>,
758 d1: Peri<'d, impl D1Pin<T>>,
759 d2: Peri<'d, impl D2Pin<T>>,
760 d3: Peri<'d, impl D3Pin<T>>,
761 ncs: Peri<'d, impl NCSEither<T>>,
762 config: Config,
763 ) -> Self {
764 Self::new_inner(
765 peri,
766 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
767 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
768 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
769 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
770 None,
771 None,
772 None,
773 None,
774 None,
775 None,
776 None,
777 None,
778 None,
779 None,
780 None,
781 None,
782 new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
783 ncs.sel(),
784 new_pin!(
785 ncs,
786 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
787 ),
788 None,
789 None,
790 None,
791 None,
792 config,
793 XspiWidth::QUAD,
794 false,
795 )
796 }
797
798 /// Create new blocking XSPI driver for two quadspi external chips
799 pub fn new_blocking_dualquadspi(
800 peri: Peri<'d, T>,
801 clk: Peri<'d, impl CLKPin<T>>,
802 d0: Peri<'d, impl D0Pin<T>>,
803 d1: Peri<'d, impl D1Pin<T>>,
804 d2: Peri<'d, impl D2Pin<T>>,
805 d3: Peri<'d, impl D3Pin<T>>,
806 d4: Peri<'d, impl D4Pin<T>>,
807 d5: Peri<'d, impl D5Pin<T>>,
808 d6: Peri<'d, impl D6Pin<T>>,
809 d7: Peri<'d, impl D7Pin<T>>,
810 ncs: Peri<'d, impl NCSEither<T>>,
811 config: Config,
812 ) -> Self {
813 Self::new_inner(
814 peri,
815 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
816 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
817 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
818 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
819 new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
820 new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
821 new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
822 new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
823 None,
824 None,
825 None,
826 None,
827 None,
828 None,
829 None,
830 None,
831 new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
832 ncs.sel(),
833 new_pin!(
834 ncs,
835 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
836 ),
837 None,
838 None,
839 None,
840 None,
841 config,
842 XspiWidth::QUAD,
843 true,
844 )
845 }
846
847 /// Create new blocking XSPI driver for xspi external chips
848 pub fn new_blocking_xspi(
849 peri: Peri<'d, T>,
850 clk: Peri<'d, impl CLKPin<T>>,
851 d0: Peri<'d, impl D0Pin<T>>,
852 d1: Peri<'d, impl D1Pin<T>>,
853 d2: Peri<'d, impl D2Pin<T>>,
854 d3: Peri<'d, impl D3Pin<T>>,
855 d4: Peri<'d, impl D4Pin<T>>,
856 d5: Peri<'d, impl D5Pin<T>>,
857 d6: Peri<'d, impl D6Pin<T>>,
858 d7: Peri<'d, impl D7Pin<T>>,
859 ncs: Peri<'d, impl NCSEither<T>>,
860 config: Config,
861 ) -> Self {
862 Self::new_inner(
863 peri,
864 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
865 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
866 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
867 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
868 new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
869 new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
870 new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
871 new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
872 None,
873 None,
874 None,
875 None,
876 None,
877 None,
878 None,
879 None,
880 new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
881 ncs.sel(),
882 new_pin!(
883 ncs,
884 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
885 ),
886 None,
887 None,
888 None,
889 None,
890 config,
891 XspiWidth::OCTO,
892 false,
893 )
894 }
895}
896
897impl<'d, T: Instance> Xspi<'d, T, Async> {
898 /// Create new blocking XSPI driver for a single spi external chip
899 pub fn new_singlespi(
900 peri: Peri<'d, T>,
901 clk: Peri<'d, impl CLKPin<T>>,
902 d0: Peri<'d, impl D0Pin<T>>,
903 d1: Peri<'d, impl D1Pin<T>>,
904 ncs: Peri<'d, impl NCSEither<T>>,
905 dma: Peri<'d, impl XDma<T>>,
906 config: Config,
907 ) -> Self {
908 Self::new_inner(
909 peri,
910 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
911 new_pin!(d1, AfType::input(Pull::None)),
912 None,
913 None,
914 None,
915 None,
916 None,
917 None,
918 None,
919 None,
920 None,
921 None,
922 None,
923 None,
924 None,
925 None,
926 new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
927 ncs.sel(),
928 new_pin!(
929 ncs,
930 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
931 ),
932 None,
933 None,
934 None,
935 new_dma!(dma),
936 config,
937 XspiWidth::SING,
938 false,
939 )
940 }
941
942 /// Create new blocking XSPI driver for a dualspi external chip
943 pub fn new_dualspi(
944 peri: Peri<'d, T>,
945 clk: Peri<'d, impl CLKPin<T>>,
946 d0: Peri<'d, impl D0Pin<T>>,
947 d1: Peri<'d, impl D1Pin<T>>,
948 ncs: Peri<'d, impl NCSEither<T>>,
949 dma: Peri<'d, impl XDma<T>>,
950 config: Config,
951 ) -> Self {
952 Self::new_inner(
953 peri,
954 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
955 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
956 None,
957 None,
958 None,
959 None,
960 None,
961 None,
962 None,
963 None,
964 None,
965 None,
966 None,
967 None,
968 None,
969 None,
970 new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
971 ncs.sel(),
972 new_pin!(
973 ncs,
974 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
975 ),
976 None,
977 None,
978 None,
979 new_dma!(dma),
980 config,
981 XspiWidth::DUAL,
982 false,
983 )
984 }
985
986 /// Create new blocking XSPI driver for a quadspi external chip
987 pub fn new_quadspi(
988 peri: Peri<'d, T>,
989 clk: Peri<'d, impl CLKPin<T>>,
990 d0: Peri<'d, impl D0Pin<T>>,
991 d1: Peri<'d, impl D1Pin<T>>,
992 d2: Peri<'d, impl D2Pin<T>>,
993 d3: Peri<'d, impl D3Pin<T>>,
994 ncs: Peri<'d, impl NCSEither<T>>,
995 dma: Peri<'d, impl XDma<T>>,
996 config: Config,
997 ) -> Self {
998 Self::new_inner(
999 peri,
1000 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1001 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1002 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1003 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1004 None,
1005 None,
1006 None,
1007 None,
1008 None,
1009 None,
1010 None,
1011 None,
1012 None,
1013 None,
1014 None,
1015 None,
1016 new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1017 ncs.sel(),
1018 new_pin!(
1019 ncs,
1020 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
1021 ),
1022 None,
1023 None,
1024 None,
1025 new_dma!(dma),
1026 config,
1027 XspiWidth::QUAD,
1028 false,
1029 )
1030 }
1031
1032 /// Create new blocking XSPI driver for two quadspi external chips
1033 pub fn new_dualquadspi(
1034 peri: Peri<'d, T>,
1035 clk: Peri<'d, impl CLKPin<T>>,
1036 d0: Peri<'d, impl D0Pin<T>>,
1037 d1: Peri<'d, impl D1Pin<T>>,
1038 d2: Peri<'d, impl D2Pin<T>>,
1039 d3: Peri<'d, impl D3Pin<T>>,
1040 d4: Peri<'d, impl D4Pin<T>>,
1041 d5: Peri<'d, impl D5Pin<T>>,
1042 d6: Peri<'d, impl D6Pin<T>>,
1043 d7: Peri<'d, impl D7Pin<T>>,
1044 ncs: Peri<'d, impl NCSEither<T>>,
1045 dma: Peri<'d, impl XDma<T>>,
1046 config: Config,
1047 ) -> Self {
1048 Self::new_inner(
1049 peri,
1050 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1051 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1052 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1053 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1054 new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1055 new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1056 new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1057 new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1058 None,
1059 None,
1060 None,
1061 None,
1062 None,
1063 None,
1064 None,
1065 None,
1066 new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1067 ncs.sel(),
1068 new_pin!(
1069 ncs,
1070 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
1071 ),
1072 None,
1073 None,
1074 None,
1075 new_dma!(dma),
1076 config,
1077 XspiWidth::QUAD,
1078 true,
1079 )
1080 }
1081
1082 /// Create new blocking XSPI driver for xspi external chips
1083 pub fn new_xspi(
1084 peri: Peri<'d, T>,
1085 clk: Peri<'d, impl CLKPin<T>>,
1086 d0: Peri<'d, impl D0Pin<T>>,
1087 d1: Peri<'d, impl D1Pin<T>>,
1088 d2: Peri<'d, impl D2Pin<T>>,
1089 d3: Peri<'d, impl D3Pin<T>>,
1090 d4: Peri<'d, impl D4Pin<T>>,
1091 d5: Peri<'d, impl D5Pin<T>>,
1092 d6: Peri<'d, impl D6Pin<T>>,
1093 d7: Peri<'d, impl D7Pin<T>>,
1094 ncs: Peri<'d, impl NCSEither<T>>,
1095 dma: Peri<'d, impl XDma<T>>,
1096 config: Config,
1097 ) -> Self {
1098 Self::new_inner(
1099 peri,
1100 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1101 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1102 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1103 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1104 new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1105 new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1106 new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1107 new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1108 None,
1109 None,
1110 None,
1111 None,
1112 None,
1113 None,
1114 None,
1115 None,
1116 new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1117 ncs.sel(),
1118 new_pin!(
1119 ncs,
1120 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
1121 ),
1122 None,
1123 None,
1124 None,
1125 new_dma!(dma),
1126 config,
1127 XspiWidth::OCTO,
1128 false,
1129 )
1130 }
1131
1132 /// Blocking read with DMA transfer
1133 pub fn blocking_read_dma<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), XspiError> {
1134 if buf.is_empty() {
1135 return Err(XspiError::EmptyBuffer);
1136 }
1137
1138 // Wait for peripheral to be free
1139 while T::REGS.sr().read().busy() {}
1140
1141 self.configure_command(&transaction, Some(buf.len()))?;
1142
1143 let current_address = T::REGS.ar().read().address();
1144 let current_instruction = T::REGS.ir().read().instruction();
1145
1146 // For a indirect read transaction, the transaction begins when the instruction/address is set
1147 T::REGS
1148 .cr()
1149 .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectRead.into())));
1150 if T::REGS.ccr().read().admode() == CcrAdmode::B_0X0 {
1151 T::REGS.ir().write(|v| v.set_instruction(current_instruction));
1152 } else {
1153 T::REGS.ar().write(|v| v.set_address(current_address));
1154 }
1155
1156 let transfer = unsafe {
1157 self.dma
1158 .as_mut()
1159 .unwrap()
1160 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default())
1161 };
1162
1163 T::REGS.cr().modify(|w| w.set_dmaen(true));
1164
1165 transfer.blocking_wait();
1166
1167 finish_dma(T::REGS);
1168
1169 Ok(())
1170 }
1171
1172 /// Blocking write with DMA transfer
1173 pub fn blocking_write_dma<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), XspiError> {
1174 if buf.is_empty() {
1175 return Err(XspiError::EmptyBuffer);
1176 }
1177
1178 // Wait for peripheral to be free
1179 while T::REGS.sr().read().busy() {}
1180
1181 self.configure_command(&transaction, Some(buf.len()))?;
1182 T::REGS
1183 .cr()
1184 .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into())));
1185
1186 let transfer = unsafe {
1187 self.dma
1188 .as_mut()
1189 .unwrap()
1190 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default())
1191 };
1192
1193 T::REGS.cr().modify(|w| w.set_dmaen(true));
1194
1195 transfer.blocking_wait();
1196
1197 finish_dma(T::REGS);
1198
1199 Ok(())
1200 }
1201
1202 /// Asynchronous read from external device
1203 pub async fn read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), XspiError> {
1204 if buf.is_empty() {
1205 return Err(XspiError::EmptyBuffer);
1206 }
1207
1208 // Wait for peripheral to be free
1209 while T::REGS.sr().read().busy() {}
1210
1211 self.configure_command(&transaction, Some(buf.len()))?;
1212
1213 let current_address = T::REGS.ar().read().address();
1214 let current_instruction = T::REGS.ir().read().instruction();
1215
1216 // For a indirect read transaction, the transaction begins when the instruction/address is set
1217 T::REGS
1218 .cr()
1219 .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectRead.into())));
1220 if T::REGS.ccr().read().admode() == CcrAdmode::B_0X0 {
1221 T::REGS.ir().write(|v| v.set_instruction(current_instruction));
1222 } else {
1223 T::REGS.ar().write(|v| v.set_address(current_address));
1224 }
1225
1226 let transfer = unsafe {
1227 self.dma
1228 .as_mut()
1229 .unwrap()
1230 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default())
1231 };
1232
1233 T::REGS.cr().modify(|w| w.set_dmaen(true));
1234
1235 transfer.await;
1236
1237 finish_dma(T::REGS);
1238
1239 Ok(())
1240 }
1241
1242 /// Asynchronous write to external device
1243 pub async fn write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), XspiError> {
1244 if buf.is_empty() {
1245 return Err(XspiError::EmptyBuffer);
1246 }
1247
1248 // Wait for peripheral to be free
1249 while T::REGS.sr().read().busy() {}
1250
1251 self.configure_command(&transaction, Some(buf.len()))?;
1252 T::REGS
1253 .cr()
1254 .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into())));
1255
1256 let transfer = unsafe {
1257 self.dma
1258 .as_mut()
1259 .unwrap()
1260 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default())
1261 };
1262
1263 T::REGS.cr().modify(|w| w.set_dmaen(true));
1264
1265 transfer.await;
1266
1267 finish_dma(T::REGS);
1268
1269 Ok(())
1270 }
1271}
1272
1273impl<'d, T: Instance, M: PeriMode> Drop for Xspi<'d, T, M> {
1274 fn drop(&mut self) {
1275 self.clk.as_ref().map(|x| x.set_as_disconnected());
1276 self.d0.as_ref().map(|x| x.set_as_disconnected());
1277 self.d1.as_ref().map(|x| x.set_as_disconnected());
1278 self.d2.as_ref().map(|x| x.set_as_disconnected());
1279 self.d3.as_ref().map(|x| x.set_as_disconnected());
1280 self.d4.as_ref().map(|x| x.set_as_disconnected());
1281 self.d5.as_ref().map(|x| x.set_as_disconnected());
1282 self.d6.as_ref().map(|x| x.set_as_disconnected());
1283 self.d7.as_ref().map(|x| x.set_as_disconnected());
1284 self.d8.as_ref().map(|x| x.set_as_disconnected());
1285 self.d9.as_ref().map(|x| x.set_as_disconnected());
1286 self.d10.as_ref().map(|x| x.set_as_disconnected());
1287 self.d11.as_ref().map(|x| x.set_as_disconnected());
1288 self.d12.as_ref().map(|x| x.set_as_disconnected());
1289 self.d13.as_ref().map(|x| x.set_as_disconnected());
1290 self.d14.as_ref().map(|x| x.set_as_disconnected());
1291 self.d15.as_ref().map(|x| x.set_as_disconnected());
1292 self.ncs.as_ref().map(|x| x.set_as_disconnected());
1293 self.ncs_alt.as_ref().map(|x| x.set_as_disconnected());
1294 self.dqs0.as_ref().map(|x| x.set_as_disconnected());
1295 self.dqs1.as_ref().map(|x| x.set_as_disconnected());
1296
1297 rcc::disable::<T>();
1298 }
1299}
1300
1301fn finish_dma(regs: Regs) {
1302 while !regs.sr().read().tcf() {}
1303 regs.fcr().write(|v| v.set_ctcf(true));
1304
1305 regs.cr().modify(|w| {
1306 w.set_dmaen(false);
1307 });
1308}
1309
1310/// XSPI I/O manager instance trait.
1311#[cfg(xspim_v1)]
1312pub(crate) trait SealedXspimInstance {
1313 const SPIM_REGS: Xspim;
1314 const SPI_IDX: u8;
1315}
1316
1317/// XSPI instance trait.
1318pub(crate) trait SealedInstance {
1319 const REGS: Regs;
1320}
1321
1322/// XSPI instance trait.
1323#[cfg(xspim_v1)]
1324#[allow(private_bounds)]
1325pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + SealedXspimInstance {}
1326
1327/// XSPI instance trait.
1328#[cfg(not(xspim_v1))]
1329#[allow(private_bounds)]
1330pub trait Instance: SealedInstance + PeripheralType + RccPeripheral {}
1331
1332pin_trait!(D0Pin, Instance);
1333pin_trait!(D1Pin, Instance);
1334pin_trait!(D2Pin, Instance);
1335pin_trait!(D3Pin, Instance);
1336pin_trait!(D4Pin, Instance);
1337pin_trait!(D5Pin, Instance);
1338pin_trait!(D6Pin, Instance);
1339pin_trait!(D7Pin, Instance);
1340pin_trait!(D8Pin, Instance);
1341pin_trait!(D9Pin, Instance);
1342pin_trait!(D10Pin, Instance);
1343pin_trait!(D11Pin, Instance);
1344pin_trait!(D12Pin, Instance);
1345pin_trait!(D13Pin, Instance);
1346pin_trait!(D14Pin, Instance);
1347pin_trait!(D15Pin, Instance);
1348pin_trait!(DQS0Pin, Instance);
1349pin_trait!(DQS1Pin, Instance);
1350pin_trait!(NCSPin, Instance);
1351pin_trait!(CLKPin, Instance);
1352pin_trait!(NCLKPin, Instance);
1353dma_trait!(XDma, Instance);
1354
1355/// Trait for either NCS1 or NCS2 pins
1356pub trait NCSEither<T: Instance>: NCSPin<T> {
1357 /// Get the CSSEL for this NCS pin
1358 fn sel(&self) -> u8;
1359}
1360
1361// Hard-coded the xspi index, for SPIM
1362#[cfg(xspim_v1)]
1363impl SealedXspimInstance for peripherals::XSPI1 {
1364 const SPIM_REGS: Xspim = crate::pac::XSPIM;
1365 const SPI_IDX: u8 = 1;
1366}
1367
1368// Some cubedb files are missing XSPI2, for example STM32H7R3Z8
1369#[cfg(all(xspim_v1, peri_xspi2))]
1370impl SealedXspimInstance for peripherals::XSPI2 {
1371 const SPIM_REGS: Xspim = crate::pac::XSPIM;
1372 const SPI_IDX: u8 = 2;
1373}
1374
1375#[cfg(xspim_v1)]
1376foreach_peripheral!(
1377 (xspi, $inst:ident) => {
1378 impl SealedInstance for peripherals::$inst {
1379 const REGS: Regs = crate::pac::$inst;
1380 }
1381
1382 impl Instance for peripherals::$inst {}
1383 };
1384);
1385
1386#[cfg(not(xspim_v1))]
1387foreach_peripheral!(
1388 (xspi, $inst:ident) => {
1389 impl SealedInstance for peripherals::$inst {
1390 const REGS: Regs = crate::pac::$inst;
1391 }
1392
1393 impl Instance for peripherals::$inst {}
1394 };
1395);
1396
1397impl<'d, T: Instance, M: PeriMode> SetConfig for Xspi<'d, T, M> {
1398 type Config = Config;
1399 type ConfigError = ();
1400 fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
1401 self.set_config(config);
1402 Ok(())
1403 }
1404}
1405
1406impl<'d, T: Instance, M: PeriMode> GetConfig for Xspi<'d, T, M> {
1407 type Config = Config;
1408 fn get_config(&self) -> Self::Config {
1409 self.get_config()
1410 }
1411}
1412
1413/// Word sizes usable for XSPI.
1414#[allow(private_bounds)]
1415pub trait Word: word::Word {}
1416
1417macro_rules! impl_word {
1418 ($T:ty) => {
1419 impl Word for $T {}
1420 };
1421}
1422
1423impl_word!(u8);
1424impl_word!(u16);
1425impl_word!(u32);
diff --git a/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs b/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs
new file mode 100644
index 000000000..88d914180
--- /dev/null
+++ b/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs
@@ -0,0 +1,448 @@
1#![no_main]
2#![no_std]
3
4//! For Nucleo STM32H7S3L8 MB1737, has MX25UW25645GXDI00
5//!
6//! TODO: Currently this only uses single SPI, pending flash chip documentation for octo SPI.
7
8use defmt::info;
9use embassy_executor::Spawner;
10use embassy_stm32::gpio::{Level, Output, Speed};
11use embassy_stm32::mode::Blocking;
12use embassy_stm32::time::Hertz;
13use embassy_stm32::xspi::{
14 AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, Instance, MemorySize, MemoryType, TransferConfig,
15 WrapSize, Xspi, XspiWidth,
16};
17use embassy_stm32::Config;
18use embassy_time::Timer;
19use {defmt_rtt as _, panic_probe as _};
20
21#[embassy_executor::main]
22async fn main(_spawner: Spawner) {
23 // RCC config
24 let mut config = Config::default();
25 {
26 use embassy_stm32::rcc::*;
27 config.rcc.hse = Some(Hse {
28 freq: Hertz(24_000_000),
29 mode: HseMode::Oscillator,
30 });
31 config.rcc.pll1 = Some(Pll {
32 source: PllSource::HSE,
33 prediv: PllPreDiv::DIV3,
34 mul: PllMul::MUL150,
35 divp: Some(PllDiv::DIV2),
36 divq: None,
37 divr: None,
38 });
39 config.rcc.sys = Sysclk::PLL1_P; // 600 Mhz
40 config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 Mhz
41 config.rcc.apb1_pre = APBPrescaler::DIV2; // 150 Mhz
42 config.rcc.apb2_pre = APBPrescaler::DIV2; // 150 Mhz
43 config.rcc.apb4_pre = APBPrescaler::DIV2; // 150 Mhz
44 config.rcc.apb5_pre = APBPrescaler::DIV2; // 150 Mhz
45 config.rcc.voltage_scale = VoltageScale::HIGH;
46 }
47
48 // Initialize peripherals
49 let p = embassy_stm32::init(config);
50
51 let spi_config = embassy_stm32::xspi::Config {
52 fifo_threshold: FIFOThresholdLevel::_4Bytes,
53 memory_type: MemoryType::Macronix,
54 delay_hold_quarter_cycle: true,
55 // memory_type: MemoryType::Micron,
56 // delay_hold_quarter_cycle: false,
57 device_size: MemorySize::_32MiB,
58 chip_select_high_time: ChipSelectHighTime::_2Cycle,
59 free_running_clock: false,
60 clock_mode: false,
61 wrap_size: WrapSize::None,
62 // 300mhz / (4+1) = 60mhz. Unsure the limit, need to find a MX25UW25645GXDI00 datasheet.
63 clock_prescaler: 3,
64 sample_shifting: false,
65 chip_select_boundary: 0,
66 max_transfer: 0,
67 refresh: 0,
68 };
69
70 let mut cor = cortex_m::Peripherals::take().unwrap();
71
72 // Not necessary, but recommended if using XIP
73 cor.SCB.enable_icache();
74 cor.SCB.enable_dcache(&mut cor.CPUID);
75
76 let xspi = embassy_stm32::xspi::Xspi::new_blocking_xspi(
77 p.XSPI2, p.PN6, p.PN2, p.PN3, p.PN4, p.PN5, p.PN8, p.PN9, p.PN10, p.PN11, p.PN1, spi_config,
78 );
79
80 let mut flash = FlashMemory::new(xspi).await;
81
82 let flash_id = flash.read_id();
83 info!("FLASH ID: {=[u8]:x}", flash_id);
84
85 let mut wr_buf = [0u8; 8];
86 for i in 0..8 {
87 wr_buf[i] = 0x90 + i as u8;
88 }
89 let mut rd_buf = [0u8; 8];
90 flash.erase_sector(0).await;
91 flash.write_memory(0, &wr_buf, true).await;
92 flash.read_memory(0, &mut rd_buf, true);
93 info!("WRITE BUF: {=[u8]:#X}", wr_buf);
94 info!("READ BUF: {=[u8]:#X}", rd_buf);
95 flash.enable_mm().await;
96 info!("Enabled memory mapped mode");
97
98 let first_u32 = unsafe { *(0x70000000 as *const u32) };
99 assert_eq!(first_u32, 0x93929190);
100 info!("first_u32 {:08x}", first_u32);
101
102 let second_u32 = unsafe { *(0x70000004 as *const u32) };
103 assert_eq!(second_u32, 0x97969594);
104 info!("second_u32 {:08x}", first_u32);
105
106 flash.disable_mm().await;
107 info!("Disabled memory mapped mode");
108
109 info!("DONE");
110 // Output pin PE3
111 let mut led = Output::new(p.PE3, Level::Low, Speed::Low);
112
113 loop {
114 led.toggle();
115 Timer::after_millis(1000).await;
116 }
117}
118
119const MEMORY_PAGE_SIZE: usize = 8;
120
121const CMD_READ: u8 = 0x0B;
122const _CMD_QUAD_READ: u8 = 0x6B;
123
124const CMD_WRITE_PG: u8 = 0x02;
125const _CMD_QUAD_WRITE_PG: u8 = 0x32;
126
127const CMD_READ_ID: u8 = 0x9F;
128const CMD_READ_ID_OCTO: u16 = 0x9F60;
129
130const CMD_ENABLE_RESET: u8 = 0x66;
131const CMD_RESET: u8 = 0x99;
132
133const CMD_WRITE_ENABLE: u8 = 0x06;
134
135const CMD_CHIP_ERASE: u8 = 0xC7;
136const CMD_SECTOR_ERASE: u8 = 0x20;
137const CMD_BLOCK_ERASE_32K: u8 = 0x52;
138const CMD_BLOCK_ERASE_64K: u8 = 0xD8;
139
140const CMD_READ_SR: u8 = 0x05;
141const CMD_READ_CR: u8 = 0x35;
142
143const CMD_WRITE_SR: u8 = 0x01;
144const CMD_WRITE_CR: u8 = 0x31;
145
146/// Implementation of access to flash chip.
147///
148/// Chip commands are hardcoded as it depends on used chip.
149/// This targets a MX25UW25645GXDI00.
150pub struct FlashMemory<I: Instance> {
151 xspi: Xspi<'static, I, Blocking>,
152}
153
154impl<I: Instance> FlashMemory<I> {
155 pub async fn new(xspi: Xspi<'static, I, Blocking>) -> Self {
156 let mut memory = Self { xspi };
157
158 memory.reset_memory().await;
159 memory.enable_octo();
160 memory
161 }
162
163 async fn qpi_mode(&mut self) {
164 // Enter qpi mode
165 self.exec_command(0x38).await;
166
167 // Set read param
168 let transaction = TransferConfig {
169 iwidth: XspiWidth::QUAD,
170 dwidth: XspiWidth::QUAD,
171 instruction: Some(0xC0),
172 ..Default::default()
173 };
174 self.enable_write().await;
175 self.xspi.blocking_write(&[0x30_u8], transaction).unwrap();
176 self.wait_write_finish();
177 }
178
179 pub async fn disable_mm(&mut self) {
180 self.xspi.disable_memory_mapped_mode();
181 }
182
183 pub async fn enable_mm(&mut self) {
184 self.qpi_mode().await;
185
186 let read_config = TransferConfig {
187 iwidth: XspiWidth::SING,
188 isize: AddressSize::_8bit,
189 adwidth: XspiWidth::SING,
190 adsize: AddressSize::_24bit,
191 dwidth: XspiWidth::SING,
192 instruction: Some(CMD_READ as u32),
193 dummy: DummyCycles::_8,
194 ..Default::default()
195 };
196
197 let write_config = TransferConfig {
198 iwidth: XspiWidth::SING,
199 isize: AddressSize::_8bit,
200 adwidth: XspiWidth::SING,
201 adsize: AddressSize::_24bit,
202 dwidth: XspiWidth::SING,
203 instruction: Some(CMD_WRITE_PG as u32),
204 dummy: DummyCycles::_0,
205 ..Default::default()
206 };
207 self.xspi.enable_memory_mapped_mode(read_config, write_config).unwrap();
208 }
209
210 fn enable_octo(&mut self) {
211 let cr = self.read_cr();
212 // info!("Read cr: {:x}", cr);
213 self.write_cr(cr | 0x02);
214 // info!("Read cr after writing: {:x}", cr);
215 }
216
217 pub fn disable_octo(&mut self) {
218 let cr = self.read_cr();
219 self.write_cr(cr & (!(0x02)));
220 }
221
222 async fn exec_command_4(&mut self, cmd: u8) {
223 let transaction = TransferConfig {
224 iwidth: XspiWidth::QUAD,
225 adwidth: XspiWidth::NONE,
226 // adsize: AddressSize::_24bit,
227 dwidth: XspiWidth::NONE,
228 instruction: Some(cmd as u32),
229 address: None,
230 dummy: DummyCycles::_0,
231 ..Default::default()
232 };
233 self.xspi.blocking_command(&transaction).unwrap();
234 }
235
236 async fn exec_command(&mut self, cmd: u8) {
237 let transaction = TransferConfig {
238 iwidth: XspiWidth::SING,
239 adwidth: XspiWidth::NONE,
240 // adsize: AddressSize::_24bit,
241 dwidth: XspiWidth::NONE,
242 instruction: Some(cmd as u32),
243 address: None,
244 dummy: DummyCycles::_0,
245 ..Default::default()
246 };
247 // info!("Excuting command: {:x}", transaction.instruction);
248 self.xspi.blocking_command(&transaction).unwrap();
249 }
250
251 pub async fn reset_memory(&mut self) {
252 self.exec_command_4(CMD_ENABLE_RESET).await;
253 self.exec_command_4(CMD_RESET).await;
254 self.exec_command(CMD_ENABLE_RESET).await;
255 self.exec_command(CMD_RESET).await;
256 self.wait_write_finish();
257 }
258
259 pub async fn enable_write(&mut self) {
260 self.exec_command(CMD_WRITE_ENABLE).await;
261 }
262
263 pub fn read_id(&mut self) -> [u8; 3] {
264 let mut buffer = [0; 3];
265 let transaction: TransferConfig = TransferConfig {
266 iwidth: XspiWidth::SING,
267 isize: AddressSize::_8bit,
268 adwidth: XspiWidth::NONE,
269 // adsize: AddressSize::_24bit,
270 dwidth: XspiWidth::SING,
271 instruction: Some(CMD_READ_ID as u32),
272 ..Default::default()
273 };
274 // info!("Reading id: 0x{:X}", transaction.instruction);
275 self.xspi.blocking_read(&mut buffer, transaction).unwrap();
276 buffer
277 }
278
279 pub fn read_id_8(&mut self) -> [u8; 3] {
280 let mut buffer = [0; 3];
281 let transaction: TransferConfig = TransferConfig {
282 iwidth: XspiWidth::OCTO,
283 isize: AddressSize::_16bit,
284 adwidth: XspiWidth::OCTO,
285 address: Some(0),
286 adsize: AddressSize::_32bit,
287 dwidth: XspiWidth::OCTO,
288 instruction: Some(CMD_READ_ID_OCTO as u32),
289 dummy: DummyCycles::_4,
290 ..Default::default()
291 };
292 info!("Reading id: {:#X}", transaction.instruction);
293 self.xspi.blocking_read(&mut buffer, transaction).unwrap();
294 buffer
295 }
296
297 pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) {
298 let transaction = TransferConfig {
299 iwidth: XspiWidth::SING,
300 adwidth: XspiWidth::SING,
301 adsize: AddressSize::_24bit,
302 dwidth: XspiWidth::SING,
303 instruction: Some(CMD_READ as u32),
304 dummy: DummyCycles::_8,
305 // dwidth: XspiWidth::QUAD,
306 // instruction: Some(CMD_QUAD_READ as u32),
307 // dummy: DummyCycles::_8,
308 address: Some(addr),
309 ..Default::default()
310 };
311 if use_dma {
312 self.xspi.blocking_read(buffer, transaction).unwrap();
313 } else {
314 self.xspi.blocking_read(buffer, transaction).unwrap();
315 }
316 }
317
318 fn wait_write_finish(&mut self) {
319 while (self.read_sr() & 0x01) != 0 {}
320 }
321
322 async fn perform_erase(&mut self, addr: u32, cmd: u8) {
323 let transaction = TransferConfig {
324 iwidth: XspiWidth::SING,
325 adwidth: XspiWidth::SING,
326 adsize: AddressSize::_24bit,
327 dwidth: XspiWidth::NONE,
328 instruction: Some(cmd as u32),
329 address: Some(addr),
330 dummy: DummyCycles::_0,
331 ..Default::default()
332 };
333 self.enable_write().await;
334 self.xspi.blocking_command(&transaction).unwrap();
335 self.wait_write_finish();
336 }
337
338 pub async fn erase_sector(&mut self, addr: u32) {
339 self.perform_erase(addr, CMD_SECTOR_ERASE).await;
340 }
341
342 pub async fn erase_block_32k(&mut self, addr: u32) {
343 self.perform_erase(addr, CMD_BLOCK_ERASE_32K).await;
344 }
345
346 pub async fn erase_block_64k(&mut self, addr: u32) {
347 self.perform_erase(addr, CMD_BLOCK_ERASE_64K).await;
348 }
349
350 pub async fn erase_chip(&mut self) {
351 self.exec_command(CMD_CHIP_ERASE).await;
352 }
353
354 async fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) {
355 assert!(
356 (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32,
357 "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}",
358 len,
359 addr
360 );
361
362 let transaction = TransferConfig {
363 iwidth: XspiWidth::SING,
364 adsize: AddressSize::_24bit,
365 adwidth: XspiWidth::SING,
366 dwidth: XspiWidth::SING,
367 instruction: Some(CMD_WRITE_PG as u32),
368 // dwidth: XspiWidth::QUAD,
369 // instruction: Some(CMD_QUAD_WRITE_PG as u32),
370 address: Some(addr),
371 dummy: DummyCycles::_0,
372 ..Default::default()
373 };
374 self.enable_write().await;
375 if use_dma {
376 self.xspi.blocking_write(buffer, transaction).unwrap();
377 } else {
378 self.xspi.blocking_write(buffer, transaction).unwrap();
379 }
380 self.wait_write_finish();
381 }
382
383 pub async fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) {
384 let mut left = buffer.len();
385 let mut place = addr;
386 let mut chunk_start = 0;
387
388 while left > 0 {
389 let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize;
390 let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left };
391 let chunk = &buffer[chunk_start..(chunk_start + chunk_size)];
392 self.write_page(place, chunk, chunk_size, use_dma).await;
393 place += chunk_size as u32;
394 left -= chunk_size;
395 chunk_start += chunk_size;
396 }
397 }
398
399 fn read_register(&mut self, cmd: u8) -> u8 {
400 let mut buffer = [0; 1];
401 let transaction: TransferConfig = TransferConfig {
402 iwidth: XspiWidth::SING,
403 isize: AddressSize::_8bit,
404 adwidth: XspiWidth::NONE,
405 adsize: AddressSize::_24bit,
406 dwidth: XspiWidth::SING,
407 instruction: Some(cmd as u32),
408 address: None,
409 dummy: DummyCycles::_0,
410 ..Default::default()
411 };
412 self.xspi.blocking_read(&mut buffer, transaction).unwrap();
413 // info!("Read w25q64 register: 0x{:x}", buffer[0]);
414 buffer[0]
415 }
416
417 fn write_register(&mut self, cmd: u8, value: u8) {
418 let buffer = [value; 1];
419 let transaction: TransferConfig = TransferConfig {
420 iwidth: XspiWidth::SING,
421 isize: AddressSize::_8bit,
422 instruction: Some(cmd as u32),
423 adsize: AddressSize::_24bit,
424 adwidth: XspiWidth::NONE,
425 dwidth: XspiWidth::SING,
426 address: None,
427 dummy: DummyCycles::_0,
428 ..Default::default()
429 };
430 self.xspi.blocking_write(&buffer, transaction).unwrap();
431 }
432
433 pub fn read_sr(&mut self) -> u8 {
434 self.read_register(CMD_READ_SR)
435 }
436
437 pub fn read_cr(&mut self) -> u8 {
438 self.read_register(CMD_READ_CR)
439 }
440
441 pub fn write_sr(&mut self, value: u8) {
442 self.write_register(CMD_WRITE_SR, value);
443 }
444
445 pub fn write_cr(&mut self, value: u8) {
446 self.write_register(CMD_WRITE_CR, value);
447 }
448}