diff options
| author | Dario Nieuwenhuis <[email protected]> | 2021-06-10 03:49:41 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-06-10 03:49:41 +0200 |
| commit | bd7425a571ef276f6ce2ec9bd22f6cd50f5a0f3e (patch) | |
| tree | dbfa4e15c981cd89a4a8997d691b9dbb18fa2858 | |
| parent | 08bd140c6de80453b76d95c4beb3799105a510ac (diff) | |
| parent | c5a418a9a61c00385b3be8e128d7f438e7cd8990 (diff) | |
Merge pull request #228 from embassy-rs/metapac2
stm32-metapac: add new codegen, allows pregenerating the entire pac
| -rw-r--r-- | .github/workflows/rust.yml | 12 | ||||
| m--------- | stm32-data | 0 | ||||
| -rw-r--r-- | stm32-metapac/Cargo.toml | 5 | ||||
| -rw-r--r-- | stm32-metapac/build.rs | 380 | ||||
| -rw-r--r-- | stm32-metapac/gen/.vscode/settings.json | 3 | ||||
| -rw-r--r-- | stm32-metapac/gen/Cargo.toml | 12 | ||||
| -rw-r--r-- | stm32-metapac/gen/src/assets/build.rs | 16 | ||||
| -rw-r--r-- | stm32-metapac/gen/src/assets/lib_inner.rs | 6 | ||||
| -rw-r--r-- | stm32-metapac/gen/src/lib.rs | 477 | ||||
| -rw-r--r-- | stm32-metapac/gen/src/main.rs | 21 | ||||
| -rw-r--r-- | stm32-metapac/src/lib.rs | 3 |
11 files changed, 561 insertions, 374 deletions
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b48b9feb3..8807114ba 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml | |||
| @@ -112,3 +112,15 @@ jobs: | |||
| 112 | toolchain: stable | 112 | toolchain: stable |
| 113 | - name: Check fmt | 113 | - name: Check fmt |
| 114 | run: for i in embassy-*; do (cd $i; cargo fmt -- --check); done | 114 | run: for i in embassy-*; do (cd $i; cargo fmt -- --check); done |
| 115 | |||
| 116 | metapac_gen: | ||
| 117 | runs-on: ubuntu-latest | ||
| 118 | steps: | ||
| 119 | - uses: actions/checkout@v2 | ||
| 120 | with: | ||
| 121 | submodules: true | ||
| 122 | - uses: actions-rs/toolchain@v1 | ||
| 123 | with: | ||
| 124 | toolchain: stable | ||
| 125 | - name: Generate pregenerated metapac | ||
| 126 | run: cd stm32-metapac/gen; cargo run --release | ||
diff --git a/stm32-data b/stm32-data | |||
| Subproject f07c793bbe81b01b2f3668177648f024ec1c8fb | Subproject 679a1e238835ac67597c6327cc6fb7e29bec095 | ||
diff --git a/stm32-metapac/Cargo.toml b/stm32-metapac/Cargo.toml index e7f0b22e9..4a434b17e 100644 --- a/stm32-metapac/Cargo.toml +++ b/stm32-metapac/Cargo.toml | |||
| @@ -11,10 +11,7 @@ cortex-m-rt = { version = "0.6.8", optional = true } | |||
| 11 | # BEGIN BUILD DEPENDENCIES | 11 | # BEGIN BUILD DEPENDENCIES |
| 12 | # These are removed when generating the pre-generated crate using the tool at gen/. | 12 | # These are removed when generating the pre-generated crate using the tool at gen/. |
| 13 | [build-dependencies] | 13 | [build-dependencies] |
| 14 | regex = "1.4.6" | 14 | stm32-metapac-gen = { path = "./gen" } |
| 15 | chiptool = { git = "https://github.com/embassy-rs/chiptool", rev = "86b77165078065058098e981d49d2dd213b2feba" } | ||
| 16 | serde = { version = "1.0.123", features = [ "derive" ]} | ||
| 17 | serde_yaml = "0.8.15" | ||
| 18 | # END BUILD DEPENDENCIES | 15 | # END BUILD DEPENDENCIES |
| 19 | 16 | ||
| 20 | [features] | 17 | [features] |
diff --git a/stm32-metapac/build.rs b/stm32-metapac/build.rs index 8118d8072..b44d98f3a 100644 --- a/stm32-metapac/build.rs +++ b/stm32-metapac/build.rs | |||
| @@ -1,136 +1,10 @@ | |||
| 1 | use regex::Regex; | ||
| 2 | use serde::Deserialize; | ||
| 3 | use std::collections::{HashMap, HashSet}; | ||
| 4 | use std::env; | 1 | use std::env; |
| 5 | use std::fmt::Write as _; | ||
| 6 | use std::fs; | ||
| 7 | use std::fs::File; | ||
| 8 | use std::io::Write; | ||
| 9 | use std::path::Path; | ||
| 10 | use std::path::PathBuf; | 2 | use std::path::PathBuf; |
| 11 | 3 | use stm32_metapac_gen::*; | |
| 12 | use chiptool::{generate, ir, transform}; | ||
| 13 | |||
| 14 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize)] | ||
| 15 | pub struct Chip { | ||
| 16 | pub name: String, | ||
| 17 | pub family: String, | ||
| 18 | pub line: String, | ||
| 19 | pub core: String, | ||
| 20 | pub flash: u32, | ||
| 21 | pub ram: u32, | ||
| 22 | pub gpio_af: String, | ||
| 23 | pub packages: Vec<Package>, | ||
| 24 | pub peripherals: HashMap<String, Peripheral>, | ||
| 25 | pub interrupts: HashMap<String, u32>, | ||
| 26 | } | ||
| 27 | |||
| 28 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize)] | ||
| 29 | pub struct Package { | ||
| 30 | pub name: String, | ||
| 31 | pub package: String, | ||
| 32 | } | ||
| 33 | |||
| 34 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize)] | ||
| 35 | pub struct Peripheral { | ||
| 36 | pub address: u32, | ||
| 37 | #[serde(default)] | ||
| 38 | pub kind: Option<String>, | ||
| 39 | #[serde(default)] | ||
| 40 | pub block: Option<String>, | ||
| 41 | #[serde(default)] | ||
| 42 | pub clock: Option<String>, | ||
| 43 | #[serde(default)] | ||
| 44 | pub pins: Vec<Pin>, | ||
| 45 | } | ||
| 46 | |||
| 47 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize)] | ||
| 48 | pub struct Pin { | ||
| 49 | pub pin: String, | ||
| 50 | pub signal: String, | ||
| 51 | pub af: Option<String>, | ||
| 52 | } | ||
| 53 | |||
| 54 | struct BlockInfo { | ||
| 55 | /// usart_v1/USART -> usart | ||
| 56 | module: String, | ||
| 57 | /// usart_v1/USART -> v1 | ||
| 58 | version: String, | ||
| 59 | /// usart_v1/USART -> USART | ||
| 60 | block: String, | ||
| 61 | } | ||
| 62 | |||
| 63 | impl BlockInfo { | ||
| 64 | fn parse(s: &str) -> Self { | ||
| 65 | let mut s = s.split("/"); | ||
| 66 | let module = s.next().unwrap(); | ||
| 67 | let block = s.next().unwrap(); | ||
| 68 | assert!(s.next().is_none()); | ||
| 69 | let mut s = module.split("_"); | ||
| 70 | let module = s.next().unwrap(); | ||
| 71 | let version = s.next().unwrap(); | ||
| 72 | assert!(s.next().is_none()); | ||
| 73 | Self { | ||
| 74 | module: module.to_string(), | ||
| 75 | version: version.to_string(), | ||
| 76 | block: block.to_string(), | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | fn make_table(out: &mut String, name: &str, data: &Vec<Vec<String>>) { | ||
| 82 | write!( | ||
| 83 | out, | ||
| 84 | "#[macro_export] | ||
| 85 | macro_rules! {} {{ | ||
| 86 | ($($pat:tt => $code:tt;)*) => {{ | ||
| 87 | macro_rules! __{}_inner {{ | ||
| 88 | $(($pat) => $code;)* | ||
| 89 | ($_:tt) => {{}} | ||
| 90 | }} | ||
| 91 | ", | ||
| 92 | name, name | ||
| 93 | ) | ||
| 94 | .unwrap(); | ||
| 95 | |||
| 96 | for row in data { | ||
| 97 | write!(out, " __{}_inner!(({}));\n", name, row.join(",")).unwrap(); | ||
| 98 | } | ||
| 99 | |||
| 100 | write!( | ||
| 101 | out, | ||
| 102 | " }}; | ||
| 103 | }}" | ||
| 104 | ) | ||
| 105 | .unwrap(); | ||
| 106 | } | ||
| 107 | |||
| 108 | fn find_reg_for_field<'c>( | ||
| 109 | rcc: &'c ir::IR, | ||
| 110 | reg_prefix: &str, | ||
| 111 | field_name: &str, | ||
| 112 | ) -> Option<(&'c str, &'c str)> { | ||
| 113 | rcc.fieldsets.iter().find_map(|(name, fieldset)| { | ||
| 114 | if name.starts_with(reg_prefix) { | ||
| 115 | fieldset | ||
| 116 | .fields | ||
| 117 | .iter() | ||
| 118 | .find_map(|field| { | ||
| 119 | if field_name == field.name { | ||
| 120 | return Some(field.name.as_str()); | ||
| 121 | } else { | ||
| 122 | None | ||
| 123 | } | ||
| 124 | }) | ||
| 125 | .map(|n| (name.as_str(), n)) | ||
| 126 | } else { | ||
| 127 | None | ||
| 128 | } | ||
| 129 | }) | ||
| 130 | } | ||
| 131 | 4 | ||
| 132 | fn main() { | 5 | fn main() { |
| 133 | let dir = "../stm32-data/data"; | 6 | let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap()); |
| 7 | let data_dir = PathBuf::from("../stm32-data/data"); | ||
| 134 | 8 | ||
| 135 | println!("cwd: {:?}", env::current_dir()); | 9 | println!("cwd: {:?}", env::current_dir()); |
| 136 | 10 | ||
| @@ -142,248 +16,16 @@ fn main() { | |||
| 142 | .unwrap() | 16 | .unwrap() |
| 143 | .to_ascii_uppercase(); | 17 | .to_ascii_uppercase(); |
| 144 | 18 | ||
| 145 | let chip_path = Path::new(&dir) | 19 | gen(Options { |
| 146 | .join("chips") | 20 | out_dir: out_dir.clone(), |
| 147 | .join(&format!("{}.yaml", chip_name)); | 21 | data_dir: data_dir.clone(), |
| 148 | let chip = fs::read(chip_path).unwrap(); | 22 | chips: vec![chip_name.clone()], |
| 149 | let chip: Chip = serde_yaml::from_slice(&chip).unwrap(); | ||
| 150 | |||
| 151 | let mut ir = ir::IR::new(); | ||
| 152 | |||
| 153 | let mut dev = ir::Device { | ||
| 154 | interrupts: Vec::new(), | ||
| 155 | peripherals: Vec::new(), | ||
| 156 | }; | ||
| 157 | |||
| 158 | // Load RCC register for chip | ||
| 159 | let rcc = chip.peripherals.iter().find_map(|(name, p)| { | ||
| 160 | if name == "RCC" { | ||
| 161 | p.block.as_ref().map(|block| { | ||
| 162 | let bi = BlockInfo::parse(block); | ||
| 163 | let rcc_reg_path = Path::new(&dir) | ||
| 164 | .join("registers") | ||
| 165 | .join(&format!("{}_{}.yaml", bi.module, bi.version)); | ||
| 166 | serde_yaml::from_reader(File::open(rcc_reg_path).unwrap()).unwrap() | ||
| 167 | }) | ||
| 168 | } else { | ||
| 169 | None | ||
| 170 | } | ||
| 171 | }); | 23 | }); |
| 172 | 24 | ||
| 173 | let mut peripheral_versions: HashMap<String, String> = HashMap::new(); | 25 | println!( |
| 174 | let mut pin_table: Vec<Vec<String>> = Vec::new(); | 26 | "cargo:rustc-link-search={}/src/chips/{}", |
| 175 | let mut interrupt_table: Vec<Vec<String>> = Vec::new(); | 27 | out_dir.display(), |
| 176 | let mut peripherals_table: Vec<Vec<String>> = Vec::new(); | 28 | chip_name.to_ascii_lowercase() |
| 177 | let mut peripheral_pins_table: Vec<Vec<String>> = Vec::new(); | ||
| 178 | let mut peripheral_rcc_table: Vec<Vec<String>> = Vec::new(); | ||
| 179 | |||
| 180 | let dma_base = chip | ||
| 181 | .peripherals | ||
| 182 | .get(&"DMA".to_string()) | ||
| 183 | .unwrap_or_else(|| chip.peripherals.get(&"DMA1".to_string()).unwrap()) | ||
| 184 | .address; | ||
| 185 | let dma_stride = 0x400; | ||
| 186 | |||
| 187 | let gpio_base = chip.peripherals.get(&"GPIOA".to_string()).unwrap().address; | ||
| 188 | let gpio_stride = 0x400; | ||
| 189 | |||
| 190 | for (name, p) in &chip.peripherals { | ||
| 191 | let mut ir_peri = ir::Peripheral { | ||
| 192 | name: name.clone(), | ||
| 193 | array: None, | ||
| 194 | base_address: p.address, | ||
| 195 | block: None, | ||
| 196 | description: None, | ||
| 197 | interrupts: HashMap::new(), | ||
| 198 | }; | ||
| 199 | |||
| 200 | if let Some(block) = &p.block { | ||
| 201 | let bi = BlockInfo::parse(block); | ||
| 202 | |||
| 203 | for pin in &p.pins { | ||
| 204 | let mut row = Vec::new(); | ||
| 205 | row.push(name.clone()); | ||
| 206 | row.push(bi.module.clone()); | ||
| 207 | row.push(bi.block.clone()); | ||
| 208 | row.push(pin.pin.clone()); | ||
| 209 | row.push(pin.signal.clone()); | ||
| 210 | if let Some(ref af) = pin.af { | ||
| 211 | row.push(af.clone()); | ||
| 212 | } | ||
| 213 | peripheral_pins_table.push(row); | ||
| 214 | } | ||
| 215 | |||
| 216 | let mut peripheral_row = Vec::new(); | ||
| 217 | peripheral_row.push(bi.module.clone()); | ||
| 218 | peripheral_row.push(name.clone()); | ||
| 219 | peripherals_table.push(peripheral_row); | ||
| 220 | |||
| 221 | if let Some(old_version) = | ||
| 222 | peripheral_versions.insert(bi.module.clone(), bi.version.clone()) | ||
| 223 | { | ||
| 224 | if old_version != bi.version { | ||
| 225 | panic!( | ||
| 226 | "Peripheral {} has multiple versions: {} and {}", | ||
| 227 | bi.module, old_version, bi.version | ||
| 228 | ); | ||
| 229 | } | ||
| 230 | } | ||
| 231 | ir_peri.block = Some(format!("{}::{}", bi.module, bi.block)); | ||
| 232 | |||
| 233 | match bi.module.as_str() { | ||
| 234 | "gpio" => { | ||
| 235 | let port_letter = name.chars().skip(4).next().unwrap(); | ||
| 236 | let port_num = port_letter as u32 - 'A' as u32; | ||
| 237 | assert_eq!(p.address, gpio_base + gpio_stride * port_num); | ||
| 238 | |||
| 239 | for pin_num in 0..16 { | ||
| 240 | let pin_name = format!("P{}{}", port_letter, pin_num); | ||
| 241 | pin_table.push(vec![ | ||
| 242 | pin_name.clone(), | ||
| 243 | name.clone(), | ||
| 244 | port_num.to_string(), | ||
| 245 | pin_num.to_string(), | ||
| 246 | format!("EXTI{}", pin_num), | ||
| 247 | ]); | ||
| 248 | } | ||
| 249 | } | ||
| 250 | "dma" => { | ||
| 251 | let dma_num = if name == "DMA" { | ||
| 252 | 0 | ||
| 253 | } else { | ||
| 254 | let dma_letter = name.chars().skip(3).next().unwrap(); | ||
| 255 | dma_letter as u32 - '1' as u32 | ||
| 256 | }; | ||
| 257 | assert_eq!(p.address, dma_base + dma_stride * dma_num); | ||
| 258 | } | ||
| 259 | |||
| 260 | _ => {} | ||
| 261 | } | ||
| 262 | |||
| 263 | if let Some(clock) = &p.clock { | ||
| 264 | if let Some(rcc) = &rcc { | ||
| 265 | // Workaround for clock registers being split on some chip families. Assume fields are | ||
| 266 | // named after peripheral and look for first field matching and use that register. | ||
| 267 | let en = find_reg_for_field(&rcc, clock, &format!("{}EN", name)); | ||
| 268 | let rst = find_reg_for_field(&rcc, clock, &format!("{}RST", name)); | ||
| 269 | |||
| 270 | match (en, rst) { | ||
| 271 | (Some((enable_reg, enable_field)), Some((reset_reg, reset_field))) => { | ||
| 272 | peripheral_rcc_table.push(vec![ | ||
| 273 | name.clone(), | ||
| 274 | enable_reg.to_ascii_lowercase(), | ||
| 275 | reset_reg.to_ascii_lowercase(), | ||
| 276 | format!("set_{}", enable_field.to_ascii_lowercase()), | ||
| 277 | format!("set_{}", reset_field.to_ascii_lowercase()), | ||
| 278 | ]); | ||
| 279 | } | ||
| 280 | (None, Some(_)) => { | ||
| 281 | println!("Unable to find enable register for {}", name) | ||
| 282 | } | ||
| 283 | (Some(_), None) => { | ||
| 284 | println!("Unable to find reset register for {}", name) | ||
| 285 | } | ||
| 286 | (None, None) => { | ||
| 287 | println!("Unable to find enable and reset register for {}", name) | ||
| 288 | } | ||
| 289 | } | ||
| 290 | } | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 | dev.peripherals.push(ir_peri); | ||
| 295 | } | ||
| 296 | |||
| 297 | for (name, &num) in &chip.interrupts { | ||
| 298 | dev.interrupts.push(ir::Interrupt { | ||
| 299 | name: name.clone(), | ||
| 300 | description: None, | ||
| 301 | value: num, | ||
| 302 | }); | ||
| 303 | |||
| 304 | interrupt_table.push(vec![name.to_ascii_uppercase()]); | ||
| 305 | } | ||
| 306 | |||
| 307 | ir.devices.insert("".to_string(), dev); | ||
| 308 | |||
| 309 | let mut extra = format!( | ||
| 310 | "pub fn GPIO(n: usize) -> gpio::Gpio {{ | ||
| 311 | gpio::Gpio(({} + {}*n) as _) | ||
| 312 | }} | ||
| 313 | pub fn DMA(n: usize) -> dma::Dma {{ | ||
| 314 | dma::Dma(({} + {}*n) as _) | ||
| 315 | }}", | ||
| 316 | gpio_base, gpio_stride, dma_base, dma_stride, | ||
| 317 | ); | 29 | ); |
| 318 | |||
| 319 | let peripheral_version_table = peripheral_versions | ||
| 320 | .iter() | ||
| 321 | .map(|(kind, version)| vec![kind.clone(), version.clone()]) | ||
| 322 | .collect(); | ||
| 323 | |||
| 324 | make_table(&mut extra, "pins", &pin_table); | ||
| 325 | make_table(&mut extra, "interrupts", &interrupt_table); | ||
| 326 | make_table(&mut extra, "peripherals", &peripherals_table); | ||
| 327 | make_table(&mut extra, "peripheral_versions", &peripheral_version_table); | ||
| 328 | make_table(&mut extra, "peripheral_pins", &peripheral_pins_table); | ||
| 329 | make_table(&mut extra, "peripheral_rcc", &peripheral_rcc_table); | ||
| 330 | |||
| 331 | for (module, version) in peripheral_versions { | ||
| 332 | println!("loading {} {}", module, version); | ||
| 333 | |||
| 334 | let regs_path = Path::new(&dir) | ||
| 335 | .join("registers") | ||
| 336 | .join(&format!("{}_{}.yaml", module, version)); | ||
| 337 | |||
| 338 | let mut peri: ir::IR = serde_yaml::from_reader(File::open(regs_path).unwrap()).unwrap(); | ||
| 339 | |||
| 340 | transform::expand_extends::ExpandExtends {} | ||
| 341 | .run(&mut peri) | ||
| 342 | .unwrap(); | ||
| 343 | |||
| 344 | let prefix = module; | ||
| 345 | transform::map_names(&mut peri, |s, k| match k { | ||
| 346 | transform::NameKind::Block => format!("{}::{}", prefix, s), | ||
| 347 | transform::NameKind::Fieldset => format!("{}::regs::{}", prefix, s), | ||
| 348 | transform::NameKind::Enum => format!("{}::vals::{}", prefix, s), | ||
| 349 | _ => s.to_string(), | ||
| 350 | }) | ||
| 351 | .unwrap(); | ||
| 352 | |||
| 353 | ir.merge(peri); | ||
| 354 | } | ||
| 355 | |||
| 356 | // Cleanups! | ||
| 357 | transform::sort::Sort {}.run(&mut ir).unwrap(); | ||
| 358 | transform::Sanitize {}.run(&mut ir).unwrap(); | ||
| 359 | |||
| 360 | let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); | ||
| 361 | |||
| 362 | let items = generate::render(&ir).unwrap(); | ||
| 363 | let mut file = File::create(out.join("pac.rs")).unwrap(); | ||
| 364 | let data = items.to_string().replace("] ", "]\n"); | ||
| 365 | |||
| 366 | // Remove inner attributes like #![no_std] | ||
| 367 | let re = Regex::new("# *! *\\[.*\\]").unwrap(); | ||
| 368 | let data = re.replace_all(&data, ""); | ||
| 369 | file.write_all(data.as_bytes()).unwrap(); | ||
| 370 | file.write_all(extra.as_bytes()).unwrap(); | ||
| 371 | |||
| 372 | let mut device_x = String::new(); | ||
| 373 | |||
| 374 | for (name, _) in &chip.interrupts { | ||
| 375 | write!( | ||
| 376 | &mut device_x, | ||
| 377 | "PROVIDE({} = DefaultHandler);\n", | ||
| 378 | name.to_ascii_uppercase() | ||
| 379 | ) | ||
| 380 | .unwrap(); | ||
| 381 | } | ||
| 382 | |||
| 383 | File::create(out.join("device.x")) | ||
| 384 | .unwrap() | ||
| 385 | .write_all(device_x.as_bytes()) | ||
| 386 | .unwrap(); | ||
| 387 | println!("cargo:rustc-link-search={}", out.display()); | ||
| 388 | println!("cargo:rerun-if-changed=build.rs"); | 30 | println!("cargo:rerun-if-changed=build.rs"); |
| 389 | } | 31 | } |
diff --git a/stm32-metapac/gen/.vscode/settings.json b/stm32-metapac/gen/.vscode/settings.json new file mode 100644 index 000000000..23fd35f0e --- /dev/null +++ b/stm32-metapac/gen/.vscode/settings.json | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | { | ||
| 2 | "editor.formatOnSave": true | ||
| 3 | } \ No newline at end of file | ||
diff --git a/stm32-metapac/gen/Cargo.toml b/stm32-metapac/gen/Cargo.toml new file mode 100644 index 000000000..9151c821f --- /dev/null +++ b/stm32-metapac/gen/Cargo.toml | |||
| @@ -0,0 +1,12 @@ | |||
| 1 | [package] | ||
| 2 | name = "stm32-metapac-gen" | ||
| 3 | version = "0.1.0" | ||
| 4 | edition = "2018" | ||
| 5 | |||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | regex = "1.4.6" | ||
| 9 | chiptool = { git = "https://github.com/embassy-rs/chiptool", rev = "b905099423280cdc39a96bc51f0b669ddb443797" } | ||
| 10 | serde = { version = "1.0.123", features = [ "derive" ]} | ||
| 11 | serde_yaml = "0.8.15" | ||
| 12 | syn = { version = "1.0", features = ["full","extra-traits"] } | ||
diff --git a/stm32-metapac/gen/src/assets/build.rs b/stm32-metapac/gen/src/assets/build.rs new file mode 100644 index 000000000..c16bd6407 --- /dev/null +++ b/stm32-metapac/gen/src/assets/build.rs | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | use std::env; | ||
| 2 | |||
| 3 | fn main() { | ||
| 4 | let _chip_name = env::vars_os() | ||
| 5 | .map(|(a, _)| a.to_string_lossy().to_string()) | ||
| 6 | .find(|x| x.starts_with("CARGO_FEATURE_STM32")) | ||
| 7 | .expect("No stm32xx Cargo feature enabled") | ||
| 8 | .strip_prefix("CARGO_FEATURE_") | ||
| 9 | .unwrap() | ||
| 10 | .to_ascii_lowercase(); | ||
| 11 | |||
| 12 | #[cfg(feature = "rt")] | ||
| 13 | println!("cargo:rustc-link-search=src/chips/{}", _chip_name); | ||
| 14 | |||
| 15 | println!("cargo:rerun-if-changed=build.rs"); | ||
| 16 | } | ||
diff --git a/stm32-metapac/gen/src/assets/lib_inner.rs b/stm32-metapac/gen/src/assets/lib_inner.rs new file mode 100644 index 000000000..b7729cb3a --- /dev/null +++ b/stm32-metapac/gen/src/assets/lib_inner.rs | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | // GEN PATHS HERE | ||
| 2 | mod inner; | ||
| 3 | |||
| 4 | pub mod common; | ||
| 5 | |||
| 6 | pub use inner::*; | ||
diff --git a/stm32-metapac/gen/src/lib.rs b/stm32-metapac/gen/src/lib.rs new file mode 100644 index 000000000..c4090ccaf --- /dev/null +++ b/stm32-metapac/gen/src/lib.rs | |||
| @@ -0,0 +1,477 @@ | |||
| 1 | use regex::Regex; | ||
| 2 | use serde::Deserialize; | ||
| 3 | use std::collections::{HashMap, HashSet}; | ||
| 4 | use std::env; | ||
| 5 | use std::fmt::Write as _; | ||
| 6 | use std::fs; | ||
| 7 | use std::fs::File; | ||
| 8 | use std::io::Write; | ||
| 9 | use std::path::Path; | ||
| 10 | use std::path::PathBuf; | ||
| 11 | |||
| 12 | use chiptool::{generate, ir, transform}; | ||
| 13 | |||
| 14 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize)] | ||
| 15 | pub struct Chip { | ||
| 16 | pub name: String, | ||
| 17 | pub family: String, | ||
| 18 | pub line: String, | ||
| 19 | pub core: String, | ||
| 20 | pub flash: u32, | ||
| 21 | pub ram: u32, | ||
| 22 | pub gpio_af: String, | ||
| 23 | pub packages: Vec<Package>, | ||
| 24 | pub peripherals: HashMap<String, Peripheral>, | ||
| 25 | pub interrupts: HashMap<String, u32>, | ||
| 26 | } | ||
| 27 | |||
| 28 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize)] | ||
| 29 | pub struct Package { | ||
| 30 | pub name: String, | ||
| 31 | pub package: String, | ||
| 32 | } | ||
| 33 | |||
| 34 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize)] | ||
| 35 | pub struct Peripheral { | ||
| 36 | pub address: u32, | ||
| 37 | #[serde(default)] | ||
| 38 | pub kind: Option<String>, | ||
| 39 | #[serde(default)] | ||
| 40 | pub block: Option<String>, | ||
| 41 | #[serde(default)] | ||
| 42 | pub clock: Option<String>, | ||
| 43 | #[serde(default)] | ||
| 44 | pub pins: Vec<Pin>, | ||
| 45 | } | ||
| 46 | |||
| 47 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize)] | ||
| 48 | pub struct Pin { | ||
| 49 | pub pin: String, | ||
| 50 | pub signal: String, | ||
| 51 | pub af: Option<String>, | ||
| 52 | } | ||
| 53 | |||
| 54 | struct BlockInfo { | ||
| 55 | /// usart_v1/USART -> usart | ||
| 56 | module: String, | ||
| 57 | /// usart_v1/USART -> v1 | ||
| 58 | version: String, | ||
| 59 | /// usart_v1/USART -> USART | ||
| 60 | block: String, | ||
| 61 | } | ||
| 62 | |||
| 63 | impl BlockInfo { | ||
| 64 | fn parse(s: &str) -> Self { | ||
| 65 | let mut s = s.split("/"); | ||
| 66 | let module = s.next().unwrap(); | ||
| 67 | let block = s.next().unwrap(); | ||
| 68 | assert!(s.next().is_none()); | ||
| 69 | let mut s = module.split("_"); | ||
| 70 | let module = s.next().unwrap(); | ||
| 71 | let version = s.next().unwrap(); | ||
| 72 | assert!(s.next().is_none()); | ||
| 73 | Self { | ||
| 74 | module: module.to_string(), | ||
| 75 | version: version.to_string(), | ||
| 76 | block: block.to_string(), | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | fn find_reg_for_field<'c>( | ||
| 82 | rcc: &'c ir::IR, | ||
| 83 | reg_prefix: &str, | ||
| 84 | field_name: &str, | ||
| 85 | ) -> Option<(&'c str, &'c str)> { | ||
| 86 | rcc.fieldsets.iter().find_map(|(name, fieldset)| { | ||
| 87 | if name.starts_with(reg_prefix) { | ||
| 88 | fieldset | ||
| 89 | .fields | ||
| 90 | .iter() | ||
| 91 | .find_map(|field| { | ||
| 92 | if field_name == field.name { | ||
| 93 | return Some(field.name.as_str()); | ||
| 94 | } else { | ||
| 95 | None | ||
| 96 | } | ||
| 97 | }) | ||
| 98 | .map(|n| (name.as_str(), n)) | ||
| 99 | } else { | ||
| 100 | None | ||
| 101 | } | ||
| 102 | }) | ||
| 103 | } | ||
| 104 | |||
| 105 | fn make_table(out: &mut String, name: &str, data: &Vec<Vec<String>>) { | ||
| 106 | write!( | ||
| 107 | out, | ||
| 108 | "#[macro_export] | ||
| 109 | macro_rules! {} {{ | ||
| 110 | ($($pat:tt => $code:tt;)*) => {{ | ||
| 111 | macro_rules! __{}_inner {{ | ||
| 112 | $(($pat) => $code;)* | ||
| 113 | ($_:tt) => {{}} | ||
| 114 | }} | ||
| 115 | ", | ||
| 116 | name, name | ||
| 117 | ) | ||
| 118 | .unwrap(); | ||
| 119 | |||
| 120 | for row in data { | ||
| 121 | write!(out, " __{}_inner!(({}));\n", name, row.join(",")).unwrap(); | ||
| 122 | } | ||
| 123 | |||
| 124 | write!( | ||
| 125 | out, | ||
| 126 | " }}; | ||
| 127 | }}" | ||
| 128 | ) | ||
| 129 | .unwrap(); | ||
| 130 | } | ||
| 131 | |||
| 132 | pub struct Options { | ||
| 133 | pub chips: Vec<String>, | ||
| 134 | pub out_dir: PathBuf, | ||
| 135 | pub data_dir: PathBuf, | ||
| 136 | } | ||
| 137 | |||
| 138 | pub fn gen(options: Options) { | ||
| 139 | let generate_opts = generate::Options { | ||
| 140 | common_path: syn::parse_str("crate::common").unwrap(), | ||
| 141 | }; | ||
| 142 | |||
| 143 | let out_dir = options.out_dir; | ||
| 144 | let data_dir = options.data_dir; | ||
| 145 | |||
| 146 | fs::create_dir_all(out_dir.join("src/peripherals")).unwrap(); | ||
| 147 | fs::create_dir_all(out_dir.join("src/chips")).unwrap(); | ||
| 148 | |||
| 149 | println!("cwd: {:?}", env::current_dir()); | ||
| 150 | |||
| 151 | let mut all_peripheral_versions: HashSet<(String, String)> = HashSet::new(); | ||
| 152 | |||
| 153 | for chip_name in &options.chips { | ||
| 154 | let chip_path = data_dir.join("chips").join(&format!("{}.yaml", chip_name)); | ||
| 155 | println!("chip_path: {:?}", chip_path); | ||
| 156 | let chip = fs::read(chip_path).unwrap(); | ||
| 157 | let chip: Chip = serde_yaml::from_slice(&chip).unwrap(); | ||
| 158 | |||
| 159 | let mut ir = ir::IR::new(); | ||
| 160 | |||
| 161 | let mut dev = ir::Device { | ||
| 162 | interrupts: Vec::new(), | ||
| 163 | peripherals: Vec::new(), | ||
| 164 | }; | ||
| 165 | |||
| 166 | // Load RCC register for chip | ||
| 167 | let rcc = chip.peripherals.iter().find_map(|(name, p)| { | ||
| 168 | if name == "RCC" { | ||
| 169 | p.block.as_ref().map(|block| { | ||
| 170 | let bi = BlockInfo::parse(block); | ||
| 171 | let rcc_reg_path = data_dir | ||
| 172 | .join("registers") | ||
| 173 | .join(&format!("{}_{}.yaml", bi.module, bi.version)); | ||
| 174 | serde_yaml::from_reader(File::open(rcc_reg_path).unwrap()).unwrap() | ||
| 175 | }) | ||
| 176 | } else { | ||
| 177 | None | ||
| 178 | } | ||
| 179 | }); | ||
| 180 | |||
| 181 | let mut peripheral_versions: HashMap<String, String> = HashMap::new(); | ||
| 182 | let mut pin_table: Vec<Vec<String>> = Vec::new(); | ||
| 183 | let mut interrupt_table: Vec<Vec<String>> = Vec::new(); | ||
| 184 | let mut peripherals_table: Vec<Vec<String>> = Vec::new(); | ||
| 185 | let mut peripheral_pins_table: Vec<Vec<String>> = Vec::new(); | ||
| 186 | let mut peripheral_rcc_table: Vec<Vec<String>> = Vec::new(); | ||
| 187 | |||
| 188 | let dma_base = chip | ||
| 189 | .peripherals | ||
| 190 | .get(&"DMA".to_string()) | ||
| 191 | .unwrap_or_else(|| chip.peripherals.get(&"DMA1".to_string()).unwrap()) | ||
| 192 | .address; | ||
| 193 | let dma_stride = 0x400; | ||
| 194 | |||
| 195 | let gpio_base = chip.peripherals.get(&"GPIOA".to_string()).unwrap().address; | ||
| 196 | let gpio_stride = 0x400; | ||
| 197 | |||
| 198 | for (name, p) in &chip.peripherals { | ||
| 199 | let mut ir_peri = ir::Peripheral { | ||
| 200 | name: name.clone(), | ||
| 201 | array: None, | ||
| 202 | base_address: p.address, | ||
| 203 | block: None, | ||
| 204 | description: None, | ||
| 205 | interrupts: HashMap::new(), | ||
| 206 | }; | ||
| 207 | |||
| 208 | if let Some(block) = &p.block { | ||
| 209 | let bi = BlockInfo::parse(block); | ||
| 210 | |||
| 211 | for pin in &p.pins { | ||
| 212 | let mut row = Vec::new(); | ||
| 213 | row.push(name.clone()); | ||
| 214 | row.push(bi.module.clone()); | ||
| 215 | row.push(bi.block.clone()); | ||
| 216 | row.push(pin.pin.clone()); | ||
| 217 | row.push(pin.signal.clone()); | ||
| 218 | if let Some(ref af) = pin.af { | ||
| 219 | row.push(af.clone()); | ||
| 220 | } | ||
| 221 | peripheral_pins_table.push(row); | ||
| 222 | } | ||
| 223 | |||
| 224 | let mut peripheral_row = Vec::new(); | ||
| 225 | peripheral_row.push(bi.module.clone()); | ||
| 226 | peripheral_row.push(name.clone()); | ||
| 227 | peripherals_table.push(peripheral_row); | ||
| 228 | |||
| 229 | if let Some(old_version) = | ||
| 230 | peripheral_versions.insert(bi.module.clone(), bi.version.clone()) | ||
| 231 | { | ||
| 232 | if old_version != bi.version { | ||
| 233 | panic!( | ||
| 234 | "Peripheral {} has multiple versions: {} and {}", | ||
| 235 | bi.module, old_version, bi.version | ||
| 236 | ); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | ir_peri.block = Some(format!("{}::{}", bi.module, bi.block)); | ||
| 240 | |||
| 241 | match bi.module.as_str() { | ||
| 242 | "gpio" => { | ||
| 243 | let port_letter = name.chars().skip(4).next().unwrap(); | ||
| 244 | let port_num = port_letter as u32 - 'A' as u32; | ||
| 245 | assert_eq!(p.address, gpio_base + gpio_stride * port_num); | ||
| 246 | |||
| 247 | for pin_num in 0..16 { | ||
| 248 | let pin_name = format!("P{}{}", port_letter, pin_num); | ||
| 249 | pin_table.push(vec![ | ||
| 250 | pin_name.clone(), | ||
| 251 | name.clone(), | ||
| 252 | port_num.to_string(), | ||
| 253 | pin_num.to_string(), | ||
| 254 | format!("EXTI{}", pin_num), | ||
| 255 | ]); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | "dma" => { | ||
| 259 | let dma_num = if name == "DMA" { | ||
| 260 | 0 | ||
| 261 | } else { | ||
| 262 | let dma_letter = name.chars().skip(3).next().unwrap(); | ||
| 263 | dma_letter as u32 - '1' as u32 | ||
| 264 | }; | ||
| 265 | assert_eq!(p.address, dma_base + dma_stride * dma_num); | ||
| 266 | } | ||
| 267 | _ => {} | ||
| 268 | } | ||
| 269 | |||
| 270 | if let Some(clock) = &p.clock { | ||
| 271 | if let Some(rcc) = &rcc { | ||
| 272 | // Workaround for clock registers being split on some chip families. Assume fields are | ||
| 273 | // named after peripheral and look for first field matching and use that register. | ||
| 274 | let en = find_reg_for_field(&rcc, clock, &format!("{}EN", name)); | ||
| 275 | let rst = find_reg_for_field(&rcc, clock, &format!("{}RST", name)); | ||
| 276 | |||
| 277 | match (en, rst) { | ||
| 278 | (Some((enable_reg, enable_field)), Some((reset_reg, reset_field))) => { | ||
| 279 | peripheral_rcc_table.push(vec![ | ||
| 280 | name.clone(), | ||
| 281 | enable_reg.to_ascii_lowercase(), | ||
| 282 | reset_reg.to_ascii_lowercase(), | ||
| 283 | format!("set_{}", enable_field.to_ascii_lowercase()), | ||
| 284 | format!("set_{}", reset_field.to_ascii_lowercase()), | ||
| 285 | ]); | ||
| 286 | } | ||
| 287 | (None, Some(_)) => { | ||
| 288 | println!("Unable to find enable register for {}", name) | ||
| 289 | } | ||
| 290 | (Some(_), None) => { | ||
| 291 | println!("Unable to find reset register for {}", name) | ||
| 292 | } | ||
| 293 | (None, None) => { | ||
| 294 | println!("Unable to find enable and reset register for {}", name) | ||
| 295 | } | ||
| 296 | } | ||
| 297 | } | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | dev.peripherals.push(ir_peri); | ||
| 302 | } | ||
| 303 | |||
| 304 | for (name, &num) in &chip.interrupts { | ||
| 305 | dev.interrupts.push(ir::Interrupt { | ||
| 306 | name: name.clone(), | ||
| 307 | description: None, | ||
| 308 | value: num, | ||
| 309 | }); | ||
| 310 | |||
| 311 | interrupt_table.push(vec![name.to_ascii_uppercase()]); | ||
| 312 | } | ||
| 313 | |||
| 314 | ir.devices.insert("".to_string(), dev); | ||
| 315 | |||
| 316 | let mut extra = format!( | ||
| 317 | "pub fn GPIO(n: usize) -> gpio::Gpio {{ | ||
| 318 | gpio::Gpio(({} + {}*n) as _) | ||
| 319 | }} | ||
| 320 | pub fn DMA(n: usize) -> dma::Dma {{ | ||
| 321 | dma::Dma(({} + {}*n) as _) | ||
| 322 | }}", | ||
| 323 | gpio_base, gpio_stride, dma_base, dma_stride, | ||
| 324 | ); | ||
| 325 | |||
| 326 | let peripheral_version_table = peripheral_versions | ||
| 327 | .iter() | ||
| 328 | .map(|(kind, version)| vec![kind.clone(), version.clone()]) | ||
| 329 | .collect(); | ||
| 330 | |||
| 331 | make_table(&mut extra, "pins", &pin_table); | ||
| 332 | make_table(&mut extra, "interrupts", &interrupt_table); | ||
| 333 | make_table(&mut extra, "peripherals", &peripherals_table); | ||
| 334 | make_table(&mut extra, "peripheral_versions", &peripheral_version_table); | ||
| 335 | make_table(&mut extra, "peripheral_pins", &peripheral_pins_table); | ||
| 336 | make_table(&mut extra, "peripheral_rcc", &peripheral_rcc_table); | ||
| 337 | |||
| 338 | for (module, version) in peripheral_versions { | ||
| 339 | all_peripheral_versions.insert((module.clone(), version.clone())); | ||
| 340 | write!( | ||
| 341 | &mut extra, | ||
| 342 | "#[path=\"../../peripherals/{}_{}.rs\"] pub mod {};\n", | ||
| 343 | module, version, module | ||
| 344 | ) | ||
| 345 | .unwrap(); | ||
| 346 | } | ||
| 347 | |||
| 348 | // Cleanups! | ||
| 349 | transform::sort::Sort {}.run(&mut ir).unwrap(); | ||
| 350 | transform::Sanitize {}.run(&mut ir).unwrap(); | ||
| 351 | |||
| 352 | let chip_dir = out_dir | ||
| 353 | .join("src/chips") | ||
| 354 | .join(chip_name.to_ascii_lowercase()); | ||
| 355 | fs::create_dir_all(&chip_dir).unwrap(); | ||
| 356 | |||
| 357 | let items = generate::render(&ir, &generate_opts).unwrap(); | ||
| 358 | let mut file = File::create(chip_dir.join("pac.rs")).unwrap(); | ||
| 359 | let data = items.to_string().replace("] ", "]\n"); | ||
| 360 | |||
| 361 | // Remove inner attributes like #![no_std] | ||
| 362 | let re = Regex::new("# *! *\\[.*\\]").unwrap(); | ||
| 363 | let data = re.replace_all(&data, ""); | ||
| 364 | file.write_all(data.as_bytes()).unwrap(); | ||
| 365 | file.write_all(extra.as_bytes()).unwrap(); | ||
| 366 | |||
| 367 | let mut device_x = String::new(); | ||
| 368 | |||
| 369 | for (name, _) in &chip.interrupts { | ||
| 370 | write!( | ||
| 371 | &mut device_x, | ||
| 372 | "PROVIDE({} = DefaultHandler);\n", | ||
| 373 | name.to_ascii_uppercase() | ||
| 374 | ) | ||
| 375 | .unwrap(); | ||
| 376 | } | ||
| 377 | |||
| 378 | File::create(chip_dir.join("device.x")) | ||
| 379 | .unwrap() | ||
| 380 | .write_all(device_x.as_bytes()) | ||
| 381 | .unwrap(); | ||
| 382 | } | ||
| 383 | |||
| 384 | for (module, version) in all_peripheral_versions { | ||
| 385 | println!("loading {} {}", module, version); | ||
| 386 | |||
| 387 | let regs_path = Path::new(&data_dir) | ||
| 388 | .join("registers") | ||
| 389 | .join(&format!("{}_{}.yaml", module, version)); | ||
| 390 | |||
| 391 | let mut ir: ir::IR = serde_yaml::from_reader(File::open(regs_path).unwrap()).unwrap(); | ||
| 392 | |||
| 393 | transform::expand_extends::ExpandExtends {} | ||
| 394 | .run(&mut ir) | ||
| 395 | .unwrap(); | ||
| 396 | |||
| 397 | transform::map_names(&mut ir, |s, k| match k { | ||
| 398 | transform::NameKind::Block => format!("{}", s), | ||
| 399 | transform::NameKind::Fieldset => format!("regs::{}", s), | ||
| 400 | transform::NameKind::Enum => format!("vals::{}", s), | ||
| 401 | _ => s.to_string(), | ||
| 402 | }) | ||
| 403 | .unwrap(); | ||
| 404 | |||
| 405 | transform::sort::Sort {}.run(&mut ir).unwrap(); | ||
| 406 | transform::Sanitize {}.run(&mut ir).unwrap(); | ||
| 407 | |||
| 408 | let items = generate::render(&ir, &generate_opts).unwrap(); | ||
| 409 | let mut file = File::create( | ||
| 410 | out_dir | ||
| 411 | .join("src/peripherals") | ||
| 412 | .join(format!("{}_{}.rs", module, version)), | ||
| 413 | ) | ||
| 414 | .unwrap(); | ||
| 415 | let data = items.to_string().replace("] ", "]\n"); | ||
| 416 | |||
| 417 | // Remove inner attributes like #![no_std] | ||
| 418 | let re = Regex::new("# *! *\\[.*\\]").unwrap(); | ||
| 419 | let data = re.replace_all(&data, ""); | ||
| 420 | file.write_all(data.as_bytes()).unwrap(); | ||
| 421 | } | ||
| 422 | |||
| 423 | // Generate src/lib_inner.rs | ||
| 424 | const PATHS_MARKER: &[u8] = b"// GEN PATHS HERE"; | ||
| 425 | let librs = include_bytes!("assets/lib_inner.rs"); | ||
| 426 | let i = bytes_find(librs, PATHS_MARKER).unwrap(); | ||
| 427 | let mut paths = String::new(); | ||
| 428 | for chip_name in &options.chips { | ||
| 429 | let x = chip_name.to_ascii_lowercase(); | ||
| 430 | write!( | ||
| 431 | &mut paths, | ||
| 432 | "#[cfg_attr(feature=\"{}\", path = \"chips/{}/pac.rs\")]", | ||
| 433 | x, x | ||
| 434 | ) | ||
| 435 | .unwrap(); | ||
| 436 | } | ||
| 437 | let mut contents: Vec<u8> = Vec::new(); | ||
| 438 | contents.extend(&librs[..i]); | ||
| 439 | contents.extend(paths.as_bytes()); | ||
| 440 | contents.extend(&librs[i + PATHS_MARKER.len()..]); | ||
| 441 | fs::write(out_dir.join("src").join("lib_inner.rs"), &contents).unwrap(); | ||
| 442 | |||
| 443 | // Generate src/lib.rs | ||
| 444 | const CUT_MARKER: &[u8] = b"// GEN CUT HERE"; | ||
| 445 | let librs = include_bytes!("../../src/lib.rs"); | ||
| 446 | let i = bytes_find(librs, CUT_MARKER).unwrap(); | ||
| 447 | let mut contents: Vec<u8> = Vec::new(); | ||
| 448 | contents.extend(&librs[..i]); | ||
| 449 | contents.extend(b"include!(\"lib_inner.rs\");\n"); | ||
| 450 | fs::write(out_dir.join("src").join("lib.rs"), contents).unwrap(); | ||
| 451 | |||
| 452 | // Generate src/common.rs | ||
| 453 | fs::write( | ||
| 454 | out_dir.join("src").join("common.rs"), | ||
| 455 | generate::COMMON_MODULE, | ||
| 456 | ) | ||
| 457 | .unwrap(); | ||
| 458 | |||
| 459 | // Generate Cargo.toml | ||
| 460 | const BUILDDEP_BEGIN: &[u8] = b"# BEGIN BUILD DEPENDENCIES"; | ||
| 461 | const BUILDDEP_END: &[u8] = b"# END BUILD DEPENDENCIES"; | ||
| 462 | |||
| 463 | let mut contents = include_bytes!("../../Cargo.toml").to_vec(); | ||
| 464 | let begin = bytes_find(&contents, BUILDDEP_BEGIN).unwrap(); | ||
| 465 | let end = bytes_find(&contents, BUILDDEP_END).unwrap() + BUILDDEP_END.len(); | ||
| 466 | contents.drain(begin..end); | ||
| 467 | fs::write(out_dir.join("Cargo.toml"), contents).unwrap(); | ||
| 468 | |||
| 469 | // Generate build.rs | ||
| 470 | fs::write(out_dir.join("build.rs"), include_bytes!("assets/build.rs")).unwrap(); | ||
| 471 | } | ||
| 472 | |||
| 473 | fn bytes_find(haystack: &[u8], needle: &[u8]) -> Option<usize> { | ||
| 474 | haystack | ||
| 475 | .windows(needle.len()) | ||
| 476 | .position(|window| window == needle) | ||
| 477 | } | ||
diff --git a/stm32-metapac/gen/src/main.rs b/stm32-metapac/gen/src/main.rs new file mode 100644 index 000000000..bb76b9167 --- /dev/null +++ b/stm32-metapac/gen/src/main.rs | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | use std::path::PathBuf; | ||
| 2 | use stm32_metapac_gen::*; | ||
| 3 | |||
| 4 | fn main() { | ||
| 5 | let out_dir = PathBuf::from("out"); | ||
| 6 | let data_dir = PathBuf::from("../../stm32-data/data"); | ||
| 7 | |||
| 8 | let chips = std::fs::read_dir(data_dir.join("chips")) | ||
| 9 | .unwrap() | ||
| 10 | .filter_map(|res| res.unwrap().file_name().to_str().map(|s| s.to_string())) | ||
| 11 | .filter(|s| s.ends_with(".yaml")) | ||
| 12 | .filter(|s| !s.starts_with("STM32L1")) // cursed gpio stride | ||
| 13 | .map(|s| s.strip_suffix(".yaml").unwrap().to_string()) | ||
| 14 | .collect(); | ||
| 15 | |||
| 16 | gen(Options { | ||
| 17 | out_dir, | ||
| 18 | data_dir, | ||
| 19 | chips, | ||
| 20 | }) | ||
| 21 | } | ||
diff --git a/stm32-metapac/src/lib.rs b/stm32-metapac/src/lib.rs index cf8ff3789..5dd2682fb 100644 --- a/stm32-metapac/src/lib.rs +++ b/stm32-metapac/src/lib.rs | |||
| @@ -3,4 +3,5 @@ | |||
| 3 | #![allow(unused)] | 3 | #![allow(unused)] |
| 4 | #![allow(non_camel_case_types)] | 4 | #![allow(non_camel_case_types)] |
| 5 | 5 | ||
| 6 | include!(concat!(env!("OUT_DIR"), "/pac.rs")); | 6 | // GEN CUT HERE |
| 7 | include!(concat!(env!("OUT_DIR"), "/src/lib_inner.rs")); | ||
