aboutsummaryrefslogtreecommitdiff
path: root/embassy-mspm0/build.rs
diff options
context:
space:
mode:
authori509VCB <[email protected]>2025-03-13 22:10:45 -0500
committeri509VCB <[email protected]>2025-03-13 22:10:45 -0500
commite0cdc356ccd7f9e20c2b5355cc52b7eb7610147b (patch)
tree0c34424508b1ee8d5010dc186247b72fac7aca69 /embassy-mspm0/build.rs
parent38f26137fc67beb874aa73c9a7ab2150d9f3d372 (diff)
Embassy for MSPM0
This adds an embassy hal for the Texas Instruments MSPM0 microcontroller series. So far the GPIO and time drivers have been implemented. I have tested these drivers on the following parts: - C1104 - L1306 - L2228 - G3507 - G3519 The PAC is generated at https://github.com/mspm0-rs
Diffstat (limited to 'embassy-mspm0/build.rs')
-rw-r--r--embassy-mspm0/build.rs616
1 files changed, 616 insertions, 0 deletions
diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs
new file mode 100644
index 000000000..ffbe15c56
--- /dev/null
+++ b/embassy-mspm0/build.rs
@@ -0,0 +1,616 @@
1use std::collections::HashMap;
2use std::io::Write;
3use std::path::{Path, PathBuf};
4use std::process::Command;
5use std::sync::LazyLock;
6use std::{env, fs};
7
8use common::CfgSet;
9use mspm0_metapac::metadata::METADATA;
10use proc_macro2::{Ident, Literal, Span, TokenStream};
11use quote::{format_ident, quote};
12
13#[path = "./build_common.rs"]
14mod common;
15
16fn main() {
17 generate_code();
18}
19
20fn generate_code() {
21 let mut cfgs = common::CfgSet::new();
22 common::set_target_cfgs(&mut cfgs);
23
24 cfgs.declare_all(&["gpio_pb", "gpio_pc", "int_group1"]);
25
26 let mut singletons = Vec::new();
27
28 // Generate singletons for GPIO pins. To only consider pins available on a family, use the name of
29 // the pins from the pincm mappings.
30 for pincm_mapping in METADATA.pincm_mappings.iter() {
31 singletons.push(pincm_mapping.pin.to_string());
32 }
33
34 for peri in METADATA.peripherals {
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
43 // These peripherals are managed internally by the hal.
44 "iomux" | "cpuss" => {}
45
46 _ => singletons.push(peri.name.to_string()),
47 }
48 }
49
50 time_driver(&singletons, &mut cfgs);
51
52 // ========
53 // Write singletons
54 let mut g = TokenStream::new();
55
56 let singleton_tokens: Vec<_> = singletons.iter().map(|s| format_ident!("{}", s)).collect();
57
58 g.extend(quote! {
59 embassy_hal_internal::peripherals_definition!(#(#singleton_tokens),*);
60 });
61
62 g.extend(quote! {
63 embassy_hal_internal::peripherals_struct!(#(#singleton_tokens),*);
64 });
65
66 // ========
67 // Generate GPIO pincm lookup tables.
68 let pincms = METADATA.pincm_mappings.iter().map(|mapping| {
69 let port_letter = mapping.pin.strip_prefix("P").unwrap();
70 let port_base = (port_letter.chars().next().unwrap() as u8 - b'A') * 32;
71 // This assumes all ports are single letter length.
72 // This is fine unless TI releases a part with 833+ GPIO pins.
73 let pin_number = mapping.pin[2..].parse::<u8>().unwrap();
74
75 let num = port_base + pin_number;
76
77 // But subtract 1 since pincm indices start from 0, not 1.
78 let pincm = Literal::u8_unsuffixed(mapping.pincm - 1);
79 quote! {
80 #num => #pincm
81 }
82 });
83
84 g.extend(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."]
86 pub(crate) fn gpio_pincm(pin_port: u8) -> u8 {
87 match pin_port {
88 #(#pincms),*,
89 _ => unreachable!(),
90 }
91 }
92 });
93
94 for pincm_mapping in METADATA.pincm_mappings.iter() {
95 let name = Ident::new(&pincm_mapping.pin, Span::call_site());
96 let port_letter = pincm_mapping.pin.strip_prefix("P").unwrap();
97 let port_letter = port_letter.chars().next().unwrap();
98 let pin_number = Literal::u8_unsuffixed(pincm_mapping.pin[2..].parse::<u8>().unwrap());
99
100 let port = Ident::new(&format!("Port{}", port_letter), Span::call_site());
101
102 // TODO: Feature gate pins that can be used as NRST
103
104 g.extend(quote! {
105 impl_pin!(#name, crate::gpio::Port::#port, #pin_number);
106 });
107 }
108
109 // Generate timers
110 for peripheral in METADATA
111 .peripherals
112 .iter()
113 .filter(|p| p.name.starts_with("TIM"))
114 {
115 let name = Ident::new(&peripheral.name, Span::call_site());
116 let timers = &*TIMERS;
117
118 let timer = timers.get(peripheral.name).expect("Timer does not exist");
119 assert!(timer.bits == 16 || timer.bits == 32);
120 let bits = if timer.bits == 16 {
121 quote! { Bits16 }
122 } else {
123 quote! { Bits32 }
124 };
125
126 g.extend(quote! {
127 impl_timer!(#name, #bits);
128 });
129 }
130
131 // Generate interrupt module
132 let interrupts: Vec<Ident> = METADATA
133 .interrupts
134 .iter()
135 .map(|interrupt| Ident::new(interrupt.name, Span::call_site()))
136 .collect();
137
138 g.extend(quote! {
139 embassy_hal_internal::interrupt_mod! {
140 #(#interrupts),*
141 }
142 });
143
144 let group_interrupt_enables = METADATA
145 .interrupts
146 .iter()
147 .filter(|interrupt| interrupt.name.contains("GROUP"))
148 .map(|interrupt| {
149 let name = Ident::new(interrupt.name, Span::call_site());
150
151 quote! {
152 crate::interrupt::typelevel::#name::enable();
153 }
154 });
155
156 // Generate interrupt enables for groups
157 g.extend(quote! {
158 pub fn enable_group_interrupts(_cs: critical_section::CriticalSection) {
159 use crate::interrupt::typelevel::Interrupt;
160
161 unsafe {
162 #(#group_interrupt_enables)*
163 }
164 }
165 });
166
167 let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
168 let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string();
169 fs::write(&out_file, g.to_string()).unwrap();
170 rustfmt(&out_file);
171}
172
173fn time_driver(singletons: &[String], cfgs: &mut CfgSet) {
174 // Timer features
175 for (timer, desc) in TIMERS.iter() {
176 if desc.bits != 16 {
177 continue;
178 }
179
180 let name = timer.to_lowercase();
181 cfgs.declare(&format!("time_driver_{}", name));
182 }
183
184 let time_driver = match env::vars()
185 .map(|(a, _)| a)
186 .filter(|x| x.starts_with("CARGO_FEATURE_TIME_DRIVER_"))
187 .get_one()
188 {
189 Ok(x) => Some(
190 x.strip_prefix("CARGO_FEATURE_TIME_DRIVER_")
191 .unwrap()
192 .to_ascii_lowercase(),
193 ),
194 Err(GetOneError::None) => None,
195 Err(GetOneError::Multiple) => panic!("Multiple time-driver-xxx Cargo features enabled"),
196 };
197
198 // Verify the selected timer is available
199 let singleton = match time_driver.as_ref().map(|x| x.as_ref()) {
200 None => "",
201 Some("timg0") => "TIMG0",
202 Some("timg1") => "TIMG1",
203 Some("timg2") => "TIMG2",
204 Some("timg3") => "TIMG3",
205 Some("timg4") => "TIMG4",
206 Some("timg5") => "TIMG5",
207 Some("timg6") => "TIMG6",
208 Some("timg7") => "TIMG7",
209 Some("timg8") => "TIMG8",
210 Some("timg9") => "TIMG9",
211 Some("timg10") => "TIMG10",
212 Some("timg11") => "TIMG11",
213 Some("timg14") => "TIMG14",
214 Some("tima0") => "TIMA0",
215 Some("tima1") => "TIMA1",
216 Some("any") => {
217 // Order of timer candidates:
218 // 1. 16-bit, 2 channel
219 // 2. 16-bit, 2 channel with shadow registers
220 // 3. 16-bit, 4 channel
221 // 4. 16-bit with QEI
222 // 5. Advanced timers
223 //
224 // TODO: Select RTC first if available
225 // TODO: 32-bit timers are not considered yet
226 [
227 // 16-bit, 2 channel
228 "TIMG0", "TIMG1", "TIMG2", "TIMG3",
229 // 16-bit, 2 channel with shadow registers
230 "TIMG4", "TIMG5", "TIMG6", "TIMG7", // 16-bit, 4 channel
231 "TIMG14", // 16-bit with QEI
232 "TIMG8", "TIMG9", "TIMG10", "TIMG11", // Advanced timers
233 "TIMA0", "TIMA1",
234 ]
235 .iter()
236 .find(|tim| singletons.contains(&tim.to_string()))
237 .expect("Could not find any timer")
238 }
239 _ => panic!("unknown time_driver {:?}", time_driver),
240 };
241
242 if !singleton.is_empty() {
243 cfgs.enable(format!("time_driver_{}", singleton.to_lowercase()));
244 }
245}
246
247/// rustfmt a given path.
248/// Failures are logged to stderr and ignored.
249fn rustfmt(path: impl AsRef<Path>) {
250 let path = path.as_ref();
251 match Command::new("rustfmt").args([path]).output() {
252 Err(e) => {
253 eprintln!("failed to exec rustfmt {:?}: {:?}", path, e);
254 }
255 Ok(out) => {
256 if !out.status.success() {
257 eprintln!("rustfmt {:?} failed:", path);
258 eprintln!("=== STDOUT:");
259 std::io::stderr().write_all(&out.stdout).unwrap();
260 eprintln!("=== STDERR:");
261 std::io::stderr().write_all(&out.stderr).unwrap();
262 }
263 }
264 }
265}
266
267#[allow(dead_code)]
268struct TimerDesc {
269 bits: u8,
270 /// Is there an 8-bit prescaler
271 prescaler: bool,
272 /// Is there a repeat counter
273 repeat_counter: bool,
274 ccp_channels_internal: u8,
275 ccp_channels_external: u8,
276 external_pwm_channels: u8,
277 phase_load: bool,
278 shadow_load: bool,
279 shadow_ccs: bool,
280 deadband: bool,
281 fault_handler: bool,
282 qei_hall: bool,
283}
284
285/// Description of all timer instances.
286const TIMERS: LazyLock<HashMap<String, TimerDesc>> = LazyLock::new(|| {
287 let mut map = HashMap::new();
288 map.insert(
289 "TIMG0".into(),
290 TimerDesc {
291 bits: 16,
292 prescaler: true,
293 repeat_counter: false,
294 ccp_channels_internal: 2,
295 ccp_channels_external: 2,
296 external_pwm_channels: 2,
297 phase_load: false,
298 shadow_load: false,
299 shadow_ccs: false,
300 deadband: false,
301 fault_handler: false,
302 qei_hall: false,
303 },
304 );
305
306 map.insert(
307 "TIMG1".into(),
308 TimerDesc {
309 bits: 16,
310 prescaler: true,
311 repeat_counter: false,
312 ccp_channels_internal: 2,
313 ccp_channels_external: 2,
314 external_pwm_channels: 2,
315 phase_load: false,
316 shadow_load: false,
317 shadow_ccs: false,
318 deadband: false,
319 fault_handler: false,
320 qei_hall: false,
321 },
322 );
323
324 map.insert(
325 "TIMG2".into(),
326 TimerDesc {
327 bits: 16,
328 prescaler: true,
329 repeat_counter: false,
330 ccp_channels_internal: 2,
331 ccp_channels_external: 2,
332 external_pwm_channels: 2,
333 phase_load: false,
334 shadow_load: false,
335 shadow_ccs: false,
336 deadband: false,
337 fault_handler: false,
338 qei_hall: false,
339 },
340 );
341
342 map.insert(
343 "TIMG3".into(),
344 TimerDesc {
345 bits: 16,
346 prescaler: true,
347 repeat_counter: false,
348 ccp_channels_internal: 2,
349 ccp_channels_external: 2,
350 external_pwm_channels: 2,
351 phase_load: false,
352 shadow_load: false,
353 shadow_ccs: false,
354 deadband: false,
355 fault_handler: false,
356 qei_hall: false,
357 },
358 );
359
360 map.insert(
361 "TIMG4".into(),
362 TimerDesc {
363 bits: 16,
364 prescaler: true,
365 repeat_counter: false,
366 ccp_channels_internal: 2,
367 ccp_channels_external: 2,
368 external_pwm_channels: 2,
369 phase_load: false,
370 shadow_load: true,
371 shadow_ccs: true,
372 deadband: false,
373 fault_handler: false,
374 qei_hall: false,
375 },
376 );
377
378 map.insert(
379 "TIMG5".into(),
380 TimerDesc {
381 bits: 16,
382 prescaler: true,
383 repeat_counter: false,
384 ccp_channels_internal: 2,
385 ccp_channels_external: 2,
386 external_pwm_channels: 2,
387 phase_load: false,
388 shadow_load: true,
389 shadow_ccs: true,
390 deadband: false,
391 fault_handler: false,
392 qei_hall: false,
393 },
394 );
395
396 map.insert(
397 "TIMG6".into(),
398 TimerDesc {
399 bits: 16,
400 prescaler: true,
401 repeat_counter: false,
402 ccp_channels_internal: 2,
403 ccp_channels_external: 2,
404 external_pwm_channels: 2,
405 phase_load: false,
406 shadow_load: true,
407 shadow_ccs: true,
408 deadband: false,
409 fault_handler: false,
410 qei_hall: false,
411 },
412 );
413
414 map.insert(
415 "TIMG7".into(),
416 TimerDesc {
417 bits: 16,
418 prescaler: true,
419 repeat_counter: false,
420 ccp_channels_internal: 2,
421 ccp_channels_external: 2,
422 external_pwm_channels: 2,
423 phase_load: false,
424 shadow_load: true,
425 shadow_ccs: true,
426 deadband: false,
427 fault_handler: false,
428 qei_hall: false,
429 },
430 );
431
432 map.insert(
433 "TIMG8".into(),
434 TimerDesc {
435 bits: 16,
436 prescaler: true,
437 repeat_counter: false,
438 ccp_channels_internal: 2,
439 ccp_channels_external: 2,
440 external_pwm_channels: 2,
441 phase_load: false,
442 shadow_load: false,
443 shadow_ccs: false,
444 deadband: false,
445 fault_handler: false,
446 qei_hall: true,
447 },
448 );
449
450 map.insert(
451 "TIMG9".into(),
452 TimerDesc {
453 bits: 16,
454 prescaler: true,
455 repeat_counter: false,
456 ccp_channels_internal: 2,
457 ccp_channels_external: 2,
458 external_pwm_channels: 2,
459 phase_load: false,
460 shadow_load: false,
461 shadow_ccs: false,
462 deadband: false,
463 fault_handler: false,
464 qei_hall: true,
465 },
466 );
467
468 map.insert(
469 "TIMG10".into(),
470 TimerDesc {
471 bits: 16,
472 prescaler: true,
473 repeat_counter: false,
474 ccp_channels_internal: 2,
475 ccp_channels_external: 2,
476 external_pwm_channels: 2,
477 phase_load: false,
478 shadow_load: false,
479 shadow_ccs: false,
480 deadband: false,
481 fault_handler: false,
482 qei_hall: true,
483 },
484 );
485
486 map.insert(
487 "TIMG11".into(),
488 TimerDesc {
489 bits: 16,
490 prescaler: true,
491 repeat_counter: false,
492 ccp_channels_internal: 2,
493 ccp_channels_external: 2,
494 external_pwm_channels: 2,
495 phase_load: false,
496 shadow_load: false,
497 shadow_ccs: false,
498 deadband: false,
499 fault_handler: false,
500 qei_hall: true,
501 },
502 );
503
504 map.insert(
505 "TIMG12".into(),
506 TimerDesc {
507 bits: 32,
508 prescaler: false,
509 repeat_counter: false,
510 ccp_channels_internal: 2,
511 ccp_channels_external: 2,
512 external_pwm_channels: 2,
513 phase_load: false,
514 shadow_load: false,
515 shadow_ccs: true,
516 deadband: false,
517 fault_handler: false,
518 qei_hall: false,
519 },
520 );
521
522 map.insert(
523 "TIMG13".into(),
524 TimerDesc {
525 bits: 32,
526 prescaler: false,
527 repeat_counter: false,
528 ccp_channels_internal: 2,
529 ccp_channels_external: 2,
530 external_pwm_channels: 2,
531 phase_load: false,
532 shadow_load: false,
533 shadow_ccs: true,
534 deadband: false,
535 fault_handler: false,
536 qei_hall: false,
537 },
538 );
539
540 map.insert(
541 "TIMG14".into(),
542 TimerDesc {
543 bits: 16,
544 prescaler: true,
545 repeat_counter: false,
546 ccp_channels_internal: 4,
547 ccp_channels_external: 4,
548 external_pwm_channels: 4,
549 phase_load: false,
550 shadow_load: false,
551 shadow_ccs: false,
552 deadband: false,
553 fault_handler: false,
554 qei_hall: false,
555 },
556 );
557
558 map.insert(
559 "TIMA0".into(),
560 TimerDesc {
561 bits: 16,
562 prescaler: true,
563 repeat_counter: true,
564 ccp_channels_internal: 4,
565 ccp_channels_external: 2,
566 external_pwm_channels: 8,
567 phase_load: true,
568 shadow_load: true,
569 shadow_ccs: true,
570 deadband: true,
571 fault_handler: true,
572 qei_hall: false,
573 },
574 );
575
576 map.insert(
577 "TIMA1".into(),
578 TimerDesc {
579 bits: 16,
580 prescaler: true,
581 repeat_counter: true,
582 ccp_channels_internal: 2,
583 ccp_channels_external: 2,
584 external_pwm_channels: 4,
585 phase_load: true,
586 shadow_load: true,
587 shadow_ccs: true,
588 deadband: true,
589 fault_handler: true,
590 qei_hall: false,
591 },
592 );
593
594 map
595});
596
597enum GetOneError {
598 None,
599 Multiple,
600}
601
602trait IteratorExt: Iterator {
603 fn get_one(self) -> Result<Self::Item, GetOneError>;
604}
605
606impl<T: Iterator> IteratorExt for T {
607 fn get_one(mut self) -> Result<Self::Item, GetOneError> {
608 match self.next() {
609 None => Err(GetOneError::None),
610 Some(res) => match self.next() {
611 Some(_) => Err(GetOneError::Multiple),
612 None => Ok(res),
613 },
614 }
615 }
616}