Skip to content

Commit

Permalink
[draft] Re-organizing using a proc-macro to support more devices
Browse files Browse the repository at this point in the history
  • Loading branch information
TethysSvensson committed Jan 20, 2024
1 parent 3898d36 commit f89c518
Show file tree
Hide file tree
Showing 13 changed files with 565 additions and 68 deletions.
14 changes: 14 additions & 0 deletions atsamd-hal-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "atsamd-hal-macros"
version = "0.16.0"
edition = "2021"

[lib]
proc-macro = true

[dependencies]
litrs = "0.4.1"

[build-dependencies]
serde = { version = "1.0.195", features = ["derive"] }
serde_yaml = "0.9.30"
240 changes: 240 additions & 0 deletions atsamd-hal-macros/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
use std::{
collections::{BTreeMap, BTreeSet, HashMap},
path::PathBuf,
};

use serde;

pub type PinCollection = BTreeSet<String>;

#[derive(serde::Deserialize, Debug)]
#[serde(deny_unknown_fields)]
#[serde(rename_all = "kebab-case")]
struct Devices {
pin_groups: HashMap<String, PinCollection>,
families: HashMap<String, Family>,
}

#[derive(serde::Deserialize, Debug)]
#[serde(deny_unknown_fields)]
struct Family {
pins: HashMap<String, PinCollection>,
peripherals: Vec<Peripheral>,
}

#[derive(Debug)]
struct Peripheral {
name: String,
variant: Option<String>,
count: Option<u32>,
only: Option<Vec<String>>,
}

impl<'de> serde::Deserialize<'de> for Peripheral {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(serde::Deserialize, Debug)]
#[serde(deny_unknown_fields)]
struct PeripheralBody {
#[serde(default)]
variant: Option<String>,
#[serde(default)]
only: Option<Vec<String>>,
#[serde(default)]
count: Option<u32>,
}

struct Visitor;

impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = Peripheral;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "a string or a peripheral mapping")
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Peripheral {
name: v.to_string(),
variant: None,
count: None,
only: None,
})
}

fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(Peripheral {
name: v,
variant: None,
count: None,
only: None,
})
}

fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
if let Some((name, body)) = map.next_entry::<String, PeripheralBody>()? {
if map
.next_entry::<serde_yaml::Value, serde_yaml::Value>()?
.is_none()
{
Ok(Peripheral {
name,
variant: body.variant,
only: body.only,
count: body.count,
})
} else {
Err(serde::de::Error::invalid_length(
2,
&"expected peripheral map to have a single element",
))
}
} else {
Err(serde::de::Error::invalid_length(
0,
&"expected peripheral map to have a single element",
))
}
}
}

deserializer.deserialize_any(Visitor)
}
}

fn normalize_pins(pin_groups: &HashMap<String, PinCollection>, pins: &mut PinCollection) -> bool {
let mut new = Vec::new();
let mut changed = false;
loop {
pins.retain(|pin| {
if let Some(pins) = pin_groups.get(pin) {
new.extend(pins.iter().cloned());
false
} else {
true
}
});
if new.is_empty() {
break;
}
pins.extend(new.drain(..));
changed = true;
}
changed
}

fn main() -> std::io::Result<()> {
println!("cargo:rerun-if-changed=devices.yaml");
println!("cargo:rerun-if-changed=build.rs");
let out_dir: PathBuf = std::env::var("OUT_DIR").unwrap().into();
let manifest_dir: PathBuf = std::env::var("CARGO_MANIFEST_DIR").unwrap().into();

let peripheral_mapping = load_peripheral_mapping(manifest_dir)?;
let out = generate_output_file(peripheral_mapping);

std::fs::write(out_dir.join("generated.rs"), out)?;

Ok(())
}

fn generate_output_file(peripheral_mapping: BTreeMap<String, BTreeSet<String>>) -> String {
let mut out =
"pub fn match_peripheral(s: &str) -> Option<&'static [&'static str]> { match s {\n"
.to_string();

for (peripheral, devices) in peripheral_mapping {
use std::fmt::Write;
write!(out, "{peripheral:?} => Some(&[").unwrap();
for device in devices {
write!(out, "{device:?},").unwrap();
}
out += "]),\n";
}
out += "_ => None }}";
out
}

fn load_peripheral_mapping(
manifest_dir: PathBuf,
) -> Result<BTreeMap<String, BTreeSet<String>>, std::io::Error> {
let mut devices: Devices =
serde_yaml::from_reader(std::fs::File::open(manifest_dir.join("devices.yaml"))?).unwrap();
loop {
let mut changed = false;
for key in devices
.pin_groups
.keys()
.cloned()
.collect::<Vec<String>>()
.into_iter()
{
let mut cur = std::mem::take(devices.pin_groups.get_mut(&key).unwrap());
changed |= normalize_pins(&devices.pin_groups, &mut cur);
devices.pin_groups.insert(key, cur);
}
if !changed {
break;
}
}
let mut peripheral_mapping: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
let mut peripheral_sub_mapping: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
for (family_name, mut family) in devices.families {
for (device_name, pins) in family.pins.iter_mut() {
normalize_pins(&devices.pin_groups, pins);
for pin in pins.iter() {
peripheral_mapping
.entry(pin.clone())
.or_default()
.insert(device_name.clone());
}
}
for peripheral in family.peripherals {
let variant = peripheral.variant.unwrap_or_else(|| family_name.clone());
for i in 0..peripheral.count.unwrap_or(1) {
let name = if peripheral.count.is_some() {
format!("{}{i}", peripheral.name)
} else {
peripheral.name.clone()
};
let combined_name = format!("{}-{}", name, variant);
let devices = family
.pins
.keys()
.filter(|key| {
peripheral
.only
.as_ref()
.map_or(true, |only| only.contains(key))
})
.cloned();
peripheral_mapping
.entry(name)
.or_default()
.extend(devices.clone());
peripheral_sub_mapping
.entry(combined_name)
.or_default()
.extend(devices);
}
}
}

for (sub_peripheral_key, sub_peripheral) in peripheral_sub_mapping {
assert!(peripheral_mapping
.insert(sub_peripheral_key, sub_peripheral)
.is_none());
}

Ok(peripheral_mapping)
}
134 changes: 134 additions & 0 deletions atsamd-hal-macros/devices.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
pin-groups:
pins-14: ["pa02", "pa04", "pa05", "pa08", "pa09", "pa14", "pa15", "pa24", "pa25", "pa30", "pa31"]
pins-24: ["pins-14", "pa03", "pa06", "pa07", "pa10", "pa11", "pa16", "pa17", "pa22", "pa23"]
pins-32: ["pins-24", "pa18", "pa19"]
pins-32a: ["pins-32", "pa00", "pa01", "pa27"]
pins-32l: ["pins-32", "pb02", "pb03", "pb04", "pb05"]
pins-48: ["pins-32", "pa12", "pa13", "pa20", "pa21", "pa27", "pb02", "pb03", "pb08", "pb09", "pb10", "pb11"]
pins-48a: ["pins-48", "pa00", "pa01", "pb22", "pb23"]
pins-48l: ["pins-48", "pb00", "pb01", "pb04", "pb05"]
pins-64: ["pins-48a", "pins-48l", "pb06", "pb07", "pb12", "pb13", "pb14", "pb15", "pb16", "pb17", "pb30", "pb31"]
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"]
pins-128: ["pins-100", "pb26", "pb27", "pb28", "pb29", "pc04", "pc22", "pc23", "pc30", "pc31", "pd00", "pd01", "pd08", "pd09", "pd10", "pd11", "pd12", "pd20", "pd21"]

families:
d11:
pins:
samd11c: ["pins-14", "pa28"]
samd11d: ["pins-24", "pa27", "pa28"]
peripherals:
- serial-numbers
- dsu
- clock
- gclk
- pm
- sysctrl
- wdt
- rtc
- dmac
- eic
- nvmctrl
- port
- evsys
- sercom: { count: 2, only: ["samd11c"] }
- sercom: { count: 3, only: ["samd11d"] }
- tc0
- tc1
- tcc0
- usb
- adc
- ac
- dac
- ptc

d21:
pins:
samd21e: ["pins-32a", "pa28"]
samd21g: ["pins-48a", "pa28"]
samd21j: ["pins-64", "pa28"]
samd21el: ["pins-32l"]
samd21gl: ["pins-48l", "pa28"]
peripherals:
- serial-numbers
- dsu
- clock
- gclk
- pm
- sysctrl
- wdt
- rtc
- dmac
- eic
- nvmctrl
- port
- evsys
- sercom: { count: 4, only: ["samd21e", "samd21el"] }
- sercom: { count: 6, only: ["samd21g", "samd21j", "samd21gl"] }
- i2s: { only: ["samd21e", "samd21g", "samd21j"] }
- tc
- tcc
- usb: { only: ["samd21e", "samd21g", "samd21j"] }
- adc
- ac
- dac
- ptc

d5x:
pins:
samd51g: ["pins-48a"]
samd51j: ["pins-64"]
samd51n: ["pins-100"]
samd51p: ["pins-128"]
same51g: ["pins-48a"]
same51j: ["pins-64"]
same51n: ["pins-100"]
same53j: ["pins-64"]
same53n: ["pins-100"]
same54n: ["pins-100"]
same54p: ["pins-128"]
peripherals:
- serial-numbers
- cmcc
- dsu
- clock
- gclk
- mclk
- rstc
- ramecc
- pm
- supc
- wdt
- rtc
- dmac
- eic
- gmac: { only: ["same53j", "same53n", "same54n", "same54p"] }
- nvmctrl
- icm
- pac
- oscctrl
- osc32kctrl
- freqm
- evsys
- port
- sercom: { count: 6, only: ["samd51g", "samd51j", "same51g", "same51j", "same53j"] }
- sercom: { count: 8, only: ["samd51n", "samd51p", "same51n", "same53n", "same54n", "same54p"] }
- qspi
- usb
- can: { count: 1, only: ["same51g"]}
- can: { count: 2, only: ["same51j", "same51n", "same54n", "same54p"] }
- sdhc: { count: 1, only: ["samd51g", "samd51j", "same51g", "same51n", "same51j", "same53j"] }
- sdhc: { count: 2, only: ["samd51n", "samd51p", "same53n", "same54n", "same54p"] }
- ccl
- aes
- pukcc
- pukcl
- trng
- adc
- ac
- dac
- tc
- tcc
- ptc
- i2s: { only: ["samd51g", "samd51n", "samd51p", "same51g", "same51j", "same51n", "same53j", "same53n", "same54n", "same54p"] }
- pcc
- pdec
Loading

0 comments on commit f89c518

Please sign in to comment.