Skip to content

Commit 2ab11b3

Browse files
[draft] Re-organizing using a proc-macro to support more devices
1 parent 3898d36 commit 2ab11b3

File tree

13 files changed

+564
-68
lines changed

13 files changed

+564
-68
lines changed

atsamd-hal-macros/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "atsamd-hal-macros"
3+
version = "0.16.0"
4+
edition = "2021"
5+
6+
[lib]
7+
proc-macro = true
8+
9+
[dependencies]
10+
litrs = "0.4.1"
11+
12+
[build-dependencies]
13+
serde = { version = "1.0.195", features = ["derive"] }
14+
serde_yaml = "0.9.30"

atsamd-hal-macros/build.rs

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
use std::{
2+
collections::{BTreeMap, BTreeSet, HashMap},
3+
path::PathBuf,
4+
};
5+
6+
use serde;
7+
8+
pub type PinCollection = BTreeSet<String>;
9+
10+
#[derive(serde::Deserialize, Debug)]
11+
#[serde(deny_unknown_fields)]
12+
#[serde(rename_all = "kebab-case")]
13+
struct Devices {
14+
pin_groups: HashMap<String, PinCollection>,
15+
families: HashMap<String, Family>,
16+
}
17+
18+
#[derive(serde::Deserialize, Debug)]
19+
#[serde(deny_unknown_fields)]
20+
struct Family {
21+
pins: HashMap<String, PinCollection>,
22+
peripherals: Vec<Peripheral>,
23+
}
24+
25+
#[derive(Debug)]
26+
struct Peripheral {
27+
name: String,
28+
variant: Option<String>,
29+
count: Option<u32>,
30+
only: Option<Vec<String>>,
31+
}
32+
33+
impl<'de> serde::Deserialize<'de> for Peripheral {
34+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
35+
where
36+
D: serde::Deserializer<'de>,
37+
{
38+
#[derive(serde::Deserialize, Debug)]
39+
#[serde(deny_unknown_fields)]
40+
struct PeripheralBody {
41+
#[serde(default)]
42+
variant: Option<String>,
43+
#[serde(default)]
44+
only: Option<Vec<String>>,
45+
#[serde(default)]
46+
count: Option<u32>,
47+
}
48+
49+
struct Visitor;
50+
51+
impl<'de> serde::de::Visitor<'de> for Visitor {
52+
type Value = Peripheral;
53+
54+
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
55+
write!(formatter, "a string or a peripheral mapping")
56+
}
57+
58+
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
59+
where
60+
E: serde::de::Error,
61+
{
62+
Ok(Peripheral {
63+
name: v.to_string(),
64+
variant: None,
65+
count: None,
66+
only: None,
67+
})
68+
}
69+
70+
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
71+
where
72+
E: serde::de::Error,
73+
{
74+
Ok(Peripheral {
75+
name: v,
76+
variant: None,
77+
count: None,
78+
only: None,
79+
})
80+
}
81+
82+
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
83+
where
84+
A: serde::de::MapAccess<'de>,
85+
{
86+
if let Some((name, body)) = map.next_entry::<String, PeripheralBody>()? {
87+
if map
88+
.next_entry::<serde_yaml::Value, serde_yaml::Value>()?
89+
.is_none()
90+
{
91+
Ok(Peripheral {
92+
name,
93+
variant: body.variant,
94+
only: body.only,
95+
count: body.count,
96+
})
97+
} else {
98+
Err(serde::de::Error::invalid_length(
99+
2,
100+
&"expected peripheral map to have a single element",
101+
))
102+
}
103+
} else {
104+
Err(serde::de::Error::invalid_length(
105+
0,
106+
&"expected peripheral map to have a single element",
107+
))
108+
}
109+
}
110+
}
111+
112+
deserializer.deserialize_any(Visitor)
113+
}
114+
}
115+
116+
fn normalize_pins(pin_groups: &HashMap<String, PinCollection>, pins: &mut PinCollection) -> bool {
117+
let mut new = Vec::new();
118+
let mut changed = false;
119+
loop {
120+
pins.retain(|pin| {
121+
if let Some(pins) = pin_groups.get(pin) {
122+
new.extend(pins.iter().cloned());
123+
false
124+
} else {
125+
true
126+
}
127+
});
128+
if new.is_empty() {
129+
break;
130+
}
131+
pins.extend(new.drain(..));
132+
changed = true;
133+
}
134+
changed
135+
}
136+
137+
fn main() -> std::io::Result<()> {
138+
println!("cargo:rerun-if-changed=devices.yaml");
139+
println!("cargo:rerun-if-changed=build.rs");
140+
let out_dir: PathBuf = std::env::var("OUT_DIR").unwrap().into();
141+
let manifest_dir: PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into();
142+
143+
let peripheral_mapping = load_peripheral_mapping(manifest_dir)?;
144+
let out = generate_output_file(peripheral_mapping);
145+
146+
std::fs::write(out_dir.join("generated.rs"), out)?;
147+
148+
Ok(())
149+
}
150+
151+
fn generate_output_file(peripheral_mapping: BTreeMap<String, BTreeSet<String>>) -> String {
152+
let mut out =
153+
"fn match_peripheral(s: &str) -> Option<&'static [&'static str]> { match s {\n".to_string();
154+
155+
for (peripheral, devices) in peripheral_mapping {
156+
use std::fmt::Write;
157+
write!(out, "{peripheral:?} => Some(&[").unwrap();
158+
for device in devices {
159+
write!(out, "{device:?},").unwrap();
160+
}
161+
out += "]),\n";
162+
}
163+
out += "_ => None }}";
164+
out
165+
}
166+
167+
fn load_peripheral_mapping(
168+
manifest_dir: PathBuf,
169+
) -> Result<BTreeMap<String, BTreeSet<String>>, std::io::Error> {
170+
let mut devices: Devices =
171+
serde_yaml::from_reader(std::fs::File::open(manifest_dir.join("devices.yaml"))?).unwrap();
172+
loop {
173+
let mut changed = false;
174+
for key in devices
175+
.pin_groups
176+
.keys()
177+
.cloned()
178+
.collect::<Vec<String>>()
179+
.into_iter()
180+
{
181+
let mut cur = std::mem::take(devices.pin_groups.get_mut(&key).unwrap());
182+
changed |= normalize_pins(&devices.pin_groups, &mut cur);
183+
devices.pin_groups.insert(key, cur);
184+
}
185+
if !changed {
186+
break;
187+
}
188+
}
189+
let mut peripheral_mapping: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
190+
let mut peripheral_sub_mapping: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
191+
for (family_name, mut family) in devices.families {
192+
for (device_name, pins) in family.pins.iter_mut() {
193+
normalize_pins(&devices.pin_groups, pins);
194+
for pin in pins.iter() {
195+
peripheral_mapping
196+
.entry(pin.clone())
197+
.or_default()
198+
.insert(device_name.clone());
199+
}
200+
}
201+
for peripheral in family.peripherals {
202+
let variant = peripheral.variant.unwrap_or_else(|| family_name.clone());
203+
for i in 0..peripheral.count.unwrap_or(1) {
204+
let name = if peripheral.count.is_some() {
205+
format!("{}{i}", peripheral.name)
206+
} else {
207+
peripheral.name.clone()
208+
};
209+
let combined_name = format!("{}-{}", name, variant);
210+
let devices = family
211+
.pins
212+
.keys()
213+
.filter(|key| {
214+
peripheral
215+
.only
216+
.as_ref()
217+
.map_or(true, |only| only.contains(key))
218+
})
219+
.cloned();
220+
peripheral_mapping
221+
.entry(name)
222+
.or_default()
223+
.extend(devices.clone());
224+
peripheral_sub_mapping
225+
.entry(combined_name)
226+
.or_default()
227+
.extend(devices);
228+
}
229+
}
230+
}
231+
232+
for (sub_peripheral_key, sub_peripheral) in peripheral_sub_mapping {
233+
assert!(peripheral_mapping
234+
.insert(sub_peripheral_key, sub_peripheral)
235+
.is_none());
236+
}
237+
238+
Ok(peripheral_mapping)
239+
}

atsamd-hal-macros/devices.yaml

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
pin-groups:
2+
pins-14: ["pa02", "pa04", "pa05", "pa08", "pa09", "pa14", "pa15", "pa24", "pa25", "pa30", "pa31"]
3+
pins-24: ["pins-14", "pa03", "pa06", "pa07", "pa10", "pa11", "pa16", "pa17", "pa22", "pa23"]
4+
pins-32: ["pins-24", "pa18", "pa19"]
5+
pins-32a: ["pins-32", "pa00", "pa01", "pa27"]
6+
pins-32l: ["pins-32", "pb02", "pb03", "pb04", "pb05"]
7+
pins-48: ["pins-32", "pa12", "pa13", "pa20", "pa21", "pa27", "pb02", "pb03", "pb08", "pb09", "pb10", "pb11"]
8+
pins-48a: ["pins-48", "pa00", "pa01", "pb22", "pb23"]
9+
pins-48l: ["pins-48", "pb00", "pb01", "pb04", "pb05"]
10+
pins-64: ["pins-48a", "pins-48l", "pb06", "pb07", "pb12", "pb13", "pb14", "pb15", "pb16", "pb17", "pb30", "pb31"]
11+
pins-100: ["pins-64", "pb18", "pb19", "pb20", "pb21", "pb24", "pb25", "pc00", "pc01", "pc02", "pc03", "pc05", "pc06", "pc07", "pc10", "pc11", "pc12", "pc13", "pc14", "pc15", "pc16", "pc17", "pc18", "pc19", "pc20", "pc21", "pc24", "pc25", "pc26", "pc27", "pc28"]
12+
pins-128: ["pins-100", "pb26", "pb27", "pb28", "pb29", "pc04", "pc22", "pc23", "pc30", "pc31", "pd00", "pd01", "pd08", "pd09", "pd10", "pd11", "pd12", "pd20", "pd21"]
13+
14+
families:
15+
d11:
16+
pins:
17+
samd11c: ["pins-14", "pa28"]
18+
samd11d: ["pins-24", "pa27", "pa28"]
19+
peripherals:
20+
- serial-numbers
21+
- dsu
22+
- clock
23+
- gclk
24+
- pm
25+
- sysctrl
26+
- wdt
27+
- rtc
28+
- dmac
29+
- eic
30+
- nvmctrl
31+
- port
32+
- evsys
33+
- sercom: { count: 2, only: ["samd11c"] }
34+
- sercom: { count: 3, only: ["samd11d"] }
35+
- tc0
36+
- tc1
37+
- tcc0
38+
- usb
39+
- adc
40+
- ac
41+
- dac
42+
- ptc
43+
44+
d21:
45+
pins:
46+
samd21e: ["pins-32a", "pa28"]
47+
samd21g: ["pins-48a", "pa28"]
48+
samd21j: ["pins-64", "pa28"]
49+
samd21el: ["pins-32l"]
50+
samd21gl: ["pins-48l", "pa28"]
51+
peripherals:
52+
- serial-numbers
53+
- dsu
54+
- clock
55+
- gclk
56+
- pm
57+
- sysctrl
58+
- wdt
59+
- rtc
60+
- dmac
61+
- eic
62+
- nvmctrl
63+
- port
64+
- evsys
65+
- sercom: { count: 4, only: ["samd21e", "samd21el"] }
66+
- sercom: { count: 6, only: ["samd21g", "samd21j", "samd21gl"] }
67+
- i2s: { only: ["samd21e", "samd21g", "samd21j"] }
68+
- tc
69+
- tcc
70+
- usb: { only: ["samd21e", "samd21g", "samd21j"] }
71+
- adc
72+
- ac
73+
- dac
74+
- ptc
75+
76+
d5x:
77+
pins:
78+
samd51g: ["pins-48a"]
79+
samd51j: ["pins-64"]
80+
samd51n: ["pins-100"]
81+
samd51p: ["pins-128"]
82+
same51g: ["pins-48a"]
83+
same51j: ["pins-64"]
84+
same51n: ["pins-100"]
85+
same53j: ["pins-64"]
86+
same53n: ["pins-100"]
87+
same54n: ["pins-100"]
88+
same54p: ["pins-128"]
89+
peripherals:
90+
- serial-numbers
91+
- cmcc
92+
- dsu
93+
- clock
94+
- gclk
95+
- mclk
96+
- rstc
97+
- ramecc
98+
- pm
99+
- supc
100+
- wdt
101+
- rtc
102+
- dmac
103+
- eic
104+
- gmac: { only: ["same53j", "same53n", "same54n", "same54p"] }
105+
- nvmctrl
106+
- icm
107+
- pac
108+
- oscctrl
109+
- osc32kctrl
110+
- freqm
111+
- evsys
112+
- port
113+
- sercom: { count: 6, only: ["samd51g", "samd51j", "same51g", "same51j", "same53j"] }
114+
- sercom: { count: 8, only: ["samd51n", "samd51p", "same51n", "same53n", "same54n", "same54p"] }
115+
- qspi
116+
- usb
117+
- can: { count: 1, only: ["same51g"]}
118+
- can: { count: 2, only: ["same51j", "same51n", "same54n", "same54p"] }
119+
- sdhc: { count: 1, only: ["samd51g", "samd51j", "same51g", "same51n", "same51j", "same53j"] }
120+
- sdhc: { count: 2, only: ["samd51n", "samd51p", "same53n", "same54n", "same54p"] }
121+
- ccl
122+
- aes
123+
- pukcc
124+
- pukcl
125+
- trng
126+
- adc
127+
- ac
128+
- dac
129+
- tc
130+
- tcc
131+
- ptc
132+
- i2s: { only: ["samd51g", "samd51n", "samd51p", "same51g", "same51j", "same51n", "same53j", "same53n", "same54n", "same54p"] }
133+
- pcc
134+
- pdec

0 commit comments

Comments
 (0)