aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2025-03-24 00:27:16 +0000
committerGitHub <[email protected]>2025-03-24 00:27:16 +0000
commitf15a11f4d69ea905e9feb944969f6b1d1f134e63 (patch)
tree269a1b4235aaee507e4db02c19d9dfba0646c5e7
parentc9a07977971933b1d833093fea8c30ca1c6cccdf (diff)
parent73c0bad8ad398c072215c8ff4382dffbe1a6a057 (diff)
Merge pull request #3994 from i509VCB/mspm0-generate-singletons
mspm0: generate all singletons
-rw-r--r--embassy-mspm0/Cargo.toml9
-rw-r--r--embassy-mspm0/build.rs397
-rw-r--r--embassy-mspm0/src/lib.rs19
-rw-r--r--embassy-mspm0/src/time_driver.rs3
4 files changed, 322 insertions, 106 deletions
diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml
index e63a86103..28a8e7724 100644
--- a/embassy-mspm0/Cargo.toml
+++ b/embassy-mspm0/Cargo.toml
@@ -45,14 +45,14 @@ cortex-m = "0.7.6"
45critical-section = "1.2.0" 45critical-section = "1.2.0"
46 46
47# mspm0-metapac = { version = "" } 47# mspm0-metapac = { version = "" }
48mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-9faa5239a8eab04946086158f2a7fdff5a6a179d" } 48mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-119240dd23ef5748d2a7bef219ca298d37ba604a" }
49 49
50[build-dependencies] 50[build-dependencies]
51proc-macro2 = "1.0.94" 51proc-macro2 = "1.0.94"
52quote = "1.0.40" 52quote = "1.0.40"
53 53
54# mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } 54# mspm0-metapac = { version = "", default-features = false, features = ["metadata"] }
55mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-9faa5239a8eab04946086158f2a7fdff5a6a179d", default-features = false, features = ["metadata"] } 55mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-119240dd23ef5748d2a7bef219ca298d37ba604a", default-features = false, features = ["metadata"] }
56 56
57[features] 57[features]
58default = ["rt"] 58default = ["rt"]
@@ -107,7 +107,10 @@ time-driver-timg9 = ["_time-driver"]
107time-driver-timg10 = ["_time-driver"] 107time-driver-timg10 = ["_time-driver"]
108## Use TIMG11 as time driver 108## Use TIMG11 as time driver
109time-driver-timg11 = ["_time-driver"] 109time-driver-timg11 = ["_time-driver"]
110# TODO: Support TIMG12 and TIMG13 110## Use TIMG12 as time driver
111time-driver-timg12 = ["_time-driver"]
112## Use TIMG13 as time driver
113time-driver-timg13 = ["_time-driver"]
111## Use TIMG14 as time driver 114## Use TIMG14 as time driver
112time-driver-timg14 = ["_time-driver"] 115time-driver-timg14 = ["_time-driver"]
113## Use TIMA0 as time driver 116## Use TIMA0 as time driver
diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs
index 8c6dd4a5c..39d8b2f8a 100644
--- a/embassy-mspm0/build.rs
+++ b/embassy-mspm0/build.rs
@@ -1,4 +1,5 @@
1use std::collections::HashMap; 1use std::cmp::Ordering;
2use std::collections::{BTreeSet, HashMap};
2use std::io::Write; 3use std::io::Write;
3use std::path::{Path, PathBuf}; 4use std::path::{Path, PathBuf};
4use std::process::Command; 5use std::process::Command;
@@ -23,48 +24,128 @@ fn generate_code() {
23 24
24 cfgs.declare_all(&["gpio_pb", "gpio_pc", "int_group1"]); 25 cfgs.declare_all(&["gpio_pb", "gpio_pc", "int_group1"]);
25 26
26 let mut singletons = Vec::new(); 27 let mut singletons = get_singletons(&mut cfgs);
27 28
28 // Generate singletons for GPIO pins. To only consider pins available on a family, use the name of 29 time_driver(&mut singletons, &mut cfgs);
29 // the pins from the pincm mappings. 30
30 for pincm_mapping in METADATA.pincm_mappings.iter() { 31 let mut g = TokenStream::new();
31 singletons.push(pincm_mapping.pin.to_string()); 32
33 g.extend(generate_singletons(&singletons));
34 g.extend(generate_pincm_mapping());
35 g.extend(generate_pin());
36 g.extend(generate_timers());
37 g.extend(generate_interrupts());
38 g.extend(generate_peripheral_instances());
39 g.extend(generate_pin_trait_impls());
40
41 let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
42 let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string();
43 fs::write(&out_file, g.to_string()).unwrap();
44 rustfmt(&out_file);
45}
46
47#[derive(Debug, Clone)]
48struct Singleton {
49 name: String,
50
51 cfg: Option<TokenStream>,
52}
53
54impl PartialEq for Singleton {
55 fn eq(&self, other: &Self) -> bool {
56 self.name == other.name
32 } 57 }
58}
33 59
34 for peri in METADATA.peripherals { 60impl Eq for Singleton {}
35 match peri.kind {
36 // Specially generated.
37 "gpio" => match peri.name {
38 "GPIOB" => cfgs.enable("gpio_pb"),
39 "GPIOC" => cfgs.enable("gpio_pc"),
40 _ => (),
41 },
42 61
43 // These peripherals are managed internally by the hal. 62impl PartialOrd for Singleton {
44 "iomux" | "cpuss" => {} 63 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
64 Some(self.cmp(other))
65 }
66}
45 67
46 _ => singletons.push(peri.name.to_string()), 68impl Ord for Singleton {
47 } 69 fn cmp(&self, other: &Self) -> Ordering {
70 self.name.cmp(&other.name)
48 } 71 }
72}
49 73
50 time_driver(&singletons, &mut cfgs); 74fn get_singletons(cfgs: &mut common::CfgSet) -> Vec<Singleton> {
75 let mut singletons = Vec::<Singleton>::new();
76
77 for peripheral in METADATA.peripherals {
78 // Some peripherals do not generate a singleton, but generate a singleton for each pin.
79 let skip_peripheral_singleton = match peripheral.kind {
80 "gpio" => {
81 // Also enable ports that are present.
82 match peripheral.name {
83 "GPIOB" => cfgs.enable("gpio_pb"),
84 "GPIOC" => cfgs.enable("gpio_pc"),
85 _ => (),
86 }
87
88 true
89 }
51 90
52 // ======== 91 // Each channel gets a singleton, handled separately.
53 // Write singletons 92 "dma" => true,
54 let mut g = TokenStream::new();
55 93
56 let singleton_tokens: Vec<_> = singletons.iter().map(|s| format_ident!("{}", s)).collect(); 94 // These peripherals do not exist as singletons, and have no signals but are managed
95 // by the HAL.
96 "iomux" | "cpuss" => true,
57 97
58 g.extend(quote! { 98 _ => false,
59 embassy_hal_internal::peripherals_definition!(#(#singleton_tokens),*); 99 };
60 });
61 100
62 g.extend(quote! { 101 if !skip_peripheral_singleton {
63 embassy_hal_internal::peripherals_struct!(#(#singleton_tokens),*); 102 singletons.push(Singleton {
64 }); 103 name: peripheral.name.to_string(),
104 cfg: None,
105 });
106 }
107
108 let mut signals = BTreeSet::new();
109
110 // Pick out each unique signal. There may be multiple instances of each signal due to
111 // iomux mappings.
112 for pin in peripheral.pins {
113 let signal = if peripheral.name.starts_with("GPIO")
114 || peripheral.name.starts_with("VREF")
115 || peripheral.name.starts_with("RTC")
116 {
117 pin.signal.to_string()
118 } else {
119 format!("{}_{}", peripheral.name, pin.signal)
120 };
121
122 // We need to rename some signals to become valid Rust identifiers.
123 let signal = make_valid_identifier(&signal);
124 signals.insert(signal);
125 }
126
127 singletons.extend(signals);
128 }
129
130 // DMA channels get their own singletons
131 for dma_channel in METADATA.dma_channels.iter() {
132 singletons.push(Singleton {
133 name: format!("DMA_CH{}", dma_channel.number),
134 cfg: None,
135 });
136 }
65 137
66 // ======== 138 singletons.sort_by(|a, b| a.name.cmp(&b.name));
67 // Generate GPIO pincm lookup tables. 139 singletons
140}
141
142fn make_valid_identifier(s: &str) -> Singleton {
143 let name = s.replace('+', "_P").replace("-", "_N");
144
145 Singleton { name, cfg: None }
146}
147
148fn generate_pincm_mapping() -> TokenStream {
68 let pincms = METADATA.pincm_mappings.iter().map(|mapping| { 149 let pincms = METADATA.pincm_mappings.iter().map(|mapping| {
69 let port_letter = mapping.pin.strip_prefix("P").unwrap(); 150 let port_letter = mapping.pin.strip_prefix("P").unwrap();
70 let port_base = (port_letter.chars().next().unwrap() as u8 - b'A') * 32; 151 let port_base = (port_letter.chars().next().unwrap() as u8 - b'A') * 32;
@@ -81,7 +162,7 @@ fn generate_code() {
81 } 162 }
82 }); 163 });
83 164
84 g.extend(quote! { 165 quote! {
85 #[doc = "Get the mapping from GPIO pin port to IOMUX PINCM index. This is required since the mapping from IO to PINCM index is not consistent across parts."] 166 #[doc = "Get the mapping from GPIO pin port to IOMUX PINCM index. This is required since the mapping from IO to PINCM index is not consistent across parts."]
86 pub(crate) fn gpio_pincm(pin_port: u8) -> u8 { 167 pub(crate) fn gpio_pincm(pin_port: u8) -> u8 {
87 match pin_port { 168 match pin_port {
@@ -89,9 +170,11 @@ fn generate_code() {
89 _ => unreachable!(), 170 _ => unreachable!(),
90 } 171 }
91 } 172 }
92 }); 173 }
174}
93 175
94 for pincm_mapping in METADATA.pincm_mappings.iter() { 176fn generate_pin() -> TokenStream {
177 let pin_impls = METADATA.pincm_mappings.iter().map(|pincm_mapping| {
95 let name = Ident::new(&pincm_mapping.pin, Span::call_site()); 178 let name = Ident::new(&pincm_mapping.pin, Span::call_site());
96 let port_letter = pincm_mapping.pin.strip_prefix("P").unwrap(); 179 let port_letter = pincm_mapping.pin.strip_prefix("P").unwrap();
97 let port_letter = port_letter.chars().next().unwrap(); 180 let port_letter = port_letter.chars().next().unwrap();
@@ -101,78 +184,19 @@ fn generate_code() {
101 184
102 // TODO: Feature gate pins that can be used as NRST 185 // TODO: Feature gate pins that can be used as NRST
103 186
104 g.extend(quote! { 187 quote! {
105 impl_pin!(#name, crate::gpio::Port::#port, #pin_number); 188 impl_pin!(#name, crate::gpio::Port::#port, #pin_number);
106 });
107 }
108
109 // Generate timers
110 for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with("TIM")) {
111 let name = Ident::new(&peripheral.name, Span::call_site());
112 let timers = &*TIMERS;
113
114 let timer = timers.get(peripheral.name).expect("Timer does not exist");
115 assert!(timer.bits == 16 || timer.bits == 32);
116 let bits = if timer.bits == 16 {
117 quote! { Bits16 }
118 } else {
119 quote! { Bits32 }
120 };
121
122 g.extend(quote! {
123 impl_timer!(#name, #bits);
124 });
125 }
126
127 // Generate interrupt module
128 let interrupts: Vec<Ident> = METADATA
129 .interrupts
130 .iter()
131 .map(|interrupt| Ident::new(interrupt.name, Span::call_site()))
132 .collect();
133
134 g.extend(quote! {
135 embassy_hal_internal::interrupt_mod! {
136 #(#interrupts),*
137 }
138 });
139
140 let group_interrupt_enables = METADATA
141 .interrupts
142 .iter()
143 .filter(|interrupt| interrupt.name.contains("GROUP"))
144 .map(|interrupt| {
145 let name = Ident::new(interrupt.name, Span::call_site());
146
147 quote! {
148 crate::interrupt::typelevel::#name::enable();
149 }
150 });
151
152 // Generate interrupt enables for groups
153 g.extend(quote! {
154 pub fn enable_group_interrupts(_cs: critical_section::CriticalSection) {
155 use crate::interrupt::typelevel::Interrupt;
156
157 unsafe {
158 #(#group_interrupt_enables)*
159 }
160 } 189 }
161 }); 190 });
162 191
163 let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); 192 quote! {
164 let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); 193 #(#pin_impls)*
165 fs::write(&out_file, g.to_string()).unwrap(); 194 }
166 rustfmt(&out_file);
167} 195}
168 196
169fn time_driver(singletons: &[String], cfgs: &mut CfgSet) { 197fn time_driver(singletons: &mut Vec<Singleton>, cfgs: &mut CfgSet) {
170 // Timer features 198 // Timer features
171 for (timer, desc) in TIMERS.iter() { 199 for (timer, _) in TIMERS.iter() {
172 if desc.bits != 16 {
173 continue;
174 }
175
176 let name = timer.to_lowercase(); 200 let name = timer.to_lowercase();
177 cfgs.declare(&format!("time_driver_{}", name)); 201 cfgs.declare(&format!("time_driver_{}", name));
178 } 202 }
@@ -192,7 +216,7 @@ fn time_driver(singletons: &[String], cfgs: &mut CfgSet) {
192 }; 216 };
193 217
194 // Verify the selected timer is available 218 // Verify the selected timer is available
195 let singleton = match time_driver.as_ref().map(|x| x.as_ref()) { 219 let selected_timer = match time_driver.as_ref().map(|x| x.as_ref()) {
196 None => "", 220 None => "",
197 Some("timg0") => "TIMG0", 221 Some("timg0") => "TIMG0",
198 Some("timg1") => "TIMG1", 222 Some("timg1") => "TIMG1",
@@ -228,14 +252,181 @@ fn time_driver(singletons: &[String], cfgs: &mut CfgSet) {
228 "TIMA0", "TIMA1", 252 "TIMA0", "TIMA1",
229 ] 253 ]
230 .iter() 254 .iter()
231 .find(|tim| singletons.contains(&tim.to_string())) 255 .find(|tim| singletons.iter().any(|s| s.name == **tim))
232 .expect("Could not find any timer") 256 .expect("Could not find any timer")
233 } 257 }
234 _ => panic!("unknown time_driver {:?}", time_driver), 258 _ => panic!("unknown time_driver {:?}", time_driver),
235 }; 259 };
236 260
237 if !singleton.is_empty() { 261 if !selected_timer.is_empty() {
238 cfgs.enable(format!("time_driver_{}", singleton.to_lowercase())); 262 cfgs.enable(format!("time_driver_{}", selected_timer.to_lowercase()));
263 }
264
265 // Apply cfgs to each timer and it's pins
266 for singleton in singletons.iter_mut() {
267 if singleton.name.starts_with("TIM") {
268 // Remove suffixes for pin singletons.
269 let name = if singleton.name.contains("_CCP") {
270 singleton.name.split_once("_CCP").unwrap().0
271 } else if singleton.name.contains("_FAULT") {
272 singleton.name.split_once("_FAULT").unwrap().0
273 } else if singleton.name.contains("_IDX") {
274 singleton.name.split_once("_IDX").unwrap().0
275 } else {
276 &singleton.name
277 };
278
279 let feature = format!("time-driver-{}", name.to_lowercase());
280
281 if singleton.name.contains(selected_timer) {
282 singleton.cfg = Some(quote! { #[cfg(not(all(feature = "time-driver-any", feature = #feature)))] });
283 } else {
284 singleton.cfg = Some(quote! { #[cfg(not(feature = #feature))] });
285 }
286 }
287 }
288}
289
290fn generate_singletons(singletons: &[Singleton]) -> TokenStream {
291 let singletons = singletons
292 .iter()
293 .map(|s| {
294 let cfg = s.cfg.clone().unwrap_or_default();
295
296 let ident = format_ident!("{}", s.name);
297
298 quote! {
299 #cfg
300 #ident
301 }
302 })
303 .collect::<Vec<_>>();
304
305 quote! {
306 embassy_hal_internal::peripherals_definition!(#(#singletons),*);
307 embassy_hal_internal::peripherals_struct!(#(#singletons),*);
308 }
309}
310
311fn generate_timers() -> TokenStream {
312 // Generate timers
313 let timer_impls = METADATA
314 .peripherals
315 .iter()
316 .filter(|p| p.name.starts_with("TIM"))
317 .map(|peripheral| {
318 let name = Ident::new(&peripheral.name, Span::call_site());
319 let timers = &*TIMERS;
320
321 let timer = timers.get(peripheral.name).expect("Timer does not exist");
322 assert!(timer.bits == 16 || timer.bits == 32);
323 let bits = if timer.bits == 16 {
324 quote! { Bits16 }
325 } else {
326 quote! { Bits32 }
327 };
328
329 quote! {
330 impl_timer!(#name, #bits);
331 }
332 });
333
334 quote! {
335 #(#timer_impls)*
336 }
337}
338
339fn generate_interrupts() -> TokenStream {
340 // Generate interrupt module
341 let interrupts: Vec<Ident> = METADATA
342 .interrupts
343 .iter()
344 .map(|interrupt| Ident::new(interrupt.name, Span::call_site()))
345 .collect();
346
347 let group_interrupt_enables = METADATA
348 .interrupts
349 .iter()
350 .filter(|interrupt| interrupt.name.contains("GROUP"))
351 .map(|interrupt| {
352 let name = Ident::new(interrupt.name, Span::call_site());
353
354 quote! {
355 crate::interrupt::typelevel::#name::enable();
356 }
357 });
358
359 // Generate interrupt enables for groups
360 quote! {
361 embassy_hal_internal::interrupt_mod! {
362 #(#interrupts),*
363 }
364
365 pub fn enable_group_interrupts(_cs: critical_section::CriticalSection) {
366 use crate::interrupt::typelevel::Interrupt;
367
368 unsafe {
369 #(#group_interrupt_enables)*
370 }
371 }
372 }
373}
374
375fn generate_peripheral_instances() -> TokenStream {
376 let mut impls = Vec::<TokenStream>::new();
377
378 for peripheral in METADATA.peripherals {
379 let peri = format_ident!("{}", peripheral.name);
380
381 // Will be filled in when uart implementation is finished
382 let _ = peri;
383 let tokens = match peripheral.kind {
384 // "uart" => Some(quote! { impl_uart_instance!(#peri); }),
385 _ => None,
386 };
387
388 if let Some(tokens) = tokens {
389 impls.push(tokens);
390 }
391 }
392
393 quote! {
394 #(#impls)*
395 }
396}
397
398fn generate_pin_trait_impls() -> TokenStream {
399 let mut impls = Vec::<TokenStream>::new();
400
401 for peripheral in METADATA.peripherals {
402 for pin in peripheral.pins {
403 let key = (peripheral.kind, pin.signal);
404
405 let pin_name = format_ident!("{}", pin.pin);
406 let peri = format_ident!("{}", peripheral.name);
407 let pf = pin.pf;
408
409 // Will be filled in when uart implementation is finished
410 let _ = pin_name;
411 let _ = peri;
412 let _ = pf;
413
414 let tokens = match key {
415 // ("uart", "TX") => Some(quote! { impl_uart_tx_pin!(#peri, #pin_name, #pf); }),
416 // ("uart", "RX") => Some(quote! { impl_uart_rx_pin!(#peri, #pin_name, #pf); }),
417 // ("uart", "CTS") => Some(quote! { impl_uart_cts_pin!(#peri, #pin_name, #pf); }),
418 // ("uart", "RTS") => Some(quote! { impl_uart_rts_pin!(#peri, #pin_name, #pf); }),
419 _ => None,
420 };
421
422 if let Some(tokens) = tokens {
423 impls.push(tokens);
424 }
425 }
426 }
427
428 quote! {
429 #(#impls)*
239 } 430 }
240} 431}
241 432
diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs
index 3128bb3c7..1df85a520 100644
--- a/embassy-mspm0/src/lib.rs
+++ b/embassy-mspm0/src/lib.rs
@@ -8,6 +8,25 @@ pub(crate) mod fmt;
8pub mod gpio; 8pub mod gpio;
9pub mod timer; 9pub mod timer;
10 10
11/// Operating modes for peripherals.
12pub mod mode {
13 trait SealedMode {}
14
15 /// Operating mode for a peripheral.
16 #[allow(private_bounds)]
17 pub trait Mode: SealedMode {}
18
19 /// Blocking mode.
20 pub struct Blocking;
21 impl SealedMode for Blocking {}
22 impl Mode for Blocking {}
23
24 /// Async mode.
25 pub struct Async;
26 impl SealedMode for Async {}
27 impl Mode for Async {}
28}
29
11#[cfg(feature = "_time-driver")] 30#[cfg(feature = "_time-driver")]
12mod time_driver; 31mod time_driver;
13 32
diff --git a/embassy-mspm0/src/time_driver.rs b/embassy-mspm0/src/time_driver.rs
index 937ce58d4..e80e89e55 100644
--- a/embassy-mspm0/src/time_driver.rs
+++ b/embassy-mspm0/src/time_driver.rs
@@ -12,6 +12,9 @@ use mspm0_metapac::tim::{Counterregs16, Tim};
12use crate::peripherals; 12use crate::peripherals;
13use crate::timer::SealedTimer; 13use crate::timer::SealedTimer;
14 14
15#[cfg(any(time_driver_timg12, time_driver_timg13))]
16compile_error!("TIMG12 and TIMG13 are not supported by the time driver yet");
17
15// Currently TIMG12 and TIMG13 are excluded because those are 32-bit timers. 18// Currently TIMG12 and TIMG13 are excluded because those are 32-bit timers.
16#[cfg(time_driver_timg0)] 19#[cfg(time_driver_timg0)]
17type T = peripherals::TIMG0; 20type T = peripherals::TIMG0;