aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/rust.yml12
m---------stm32-data0
-rw-r--r--stm32-metapac/Cargo.toml5
-rw-r--r--stm32-metapac/build.rs380
-rw-r--r--stm32-metapac/gen/.vscode/settings.json3
-rw-r--r--stm32-metapac/gen/Cargo.toml12
-rw-r--r--stm32-metapac/gen/src/assets/build.rs16
-rw-r--r--stm32-metapac/gen/src/assets/lib_inner.rs6
-rw-r--r--stm32-metapac/gen/src/lib.rs477
-rw-r--r--stm32-metapac/gen/src/main.rs21
-rw-r--r--stm32-metapac/src/lib.rs3
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]
14regex = "1.4.6" 14stm32-metapac-gen = { path = "./gen" }
15chiptool = { git = "https://github.com/embassy-rs/chiptool", rev = "86b77165078065058098e981d49d2dd213b2feba" }
16serde = { version = "1.0.123", features = [ "derive" ]}
17serde_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 @@
1use regex::Regex;
2use serde::Deserialize;
3use std::collections::{HashMap, HashSet};
4use std::env; 1use std::env;
5use std::fmt::Write as _;
6use std::fs;
7use std::fs::File;
8use std::io::Write;
9use std::path::Path;
10use std::path::PathBuf; 2use std::path::PathBuf;
11 3use stm32_metapac_gen::*;
12use chiptool::{generate, ir, transform};
13
14#[derive(Debug, Eq, PartialEq, Clone, Deserialize)]
15pub 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)]
29pub struct Package {
30 pub name: String,
31 pub package: String,
32}
33
34#[derive(Debug, Eq, PartialEq, Clone, Deserialize)]
35pub 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)]
48pub struct Pin {
49 pub pin: String,
50 pub signal: String,
51 pub af: Option<String>,
52}
53
54struct 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
63impl 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
81fn make_table(out: &mut String, name: &str, data: &Vec<Vec<String>>) {
82 write!(
83 out,
84 "#[macro_export]
85macro_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
108fn 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
132fn main() { 5fn 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]
2name = "stm32-metapac-gen"
3version = "0.1.0"
4edition = "2018"
5
6
7[dependencies]
8regex = "1.4.6"
9chiptool = { git = "https://github.com/embassy-rs/chiptool", rev = "b905099423280cdc39a96bc51f0b669ddb443797" }
10serde = { version = "1.0.123", features = [ "derive" ]}
11serde_yaml = "0.8.15"
12syn = { 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 @@
1use std::env;
2
3fn 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
2mod inner;
3
4pub mod common;
5
6pub 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 @@
1use regex::Regex;
2use serde::Deserialize;
3use std::collections::{HashMap, HashSet};
4use std::env;
5use std::fmt::Write as _;
6use std::fs;
7use std::fs::File;
8use std::io::Write;
9use std::path::Path;
10use std::path::PathBuf;
11
12use chiptool::{generate, ir, transform};
13
14#[derive(Debug, Eq, PartialEq, Clone, Deserialize)]
15pub 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)]
29pub struct Package {
30 pub name: String,
31 pub package: String,
32}
33
34#[derive(Debug, Eq, PartialEq, Clone, Deserialize)]
35pub 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)]
48pub struct Pin {
49 pub pin: String,
50 pub signal: String,
51 pub af: Option<String>,
52}
53
54struct 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
63impl 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
81fn 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
105fn make_table(out: &mut String, name: &str, data: &Vec<Vec<String>>) {
106 write!(
107 out,
108 "#[macro_export]
109macro_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
132pub struct Options {
133 pub chips: Vec<String>,
134 pub out_dir: PathBuf,
135 pub data_dir: PathBuf,
136}
137
138pub 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
473fn 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 @@
1use std::path::PathBuf;
2use stm32_metapac_gen::*;
3
4fn 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
6include!(concat!(env!("OUT_DIR"), "/pac.rs")); 6// GEN CUT HERE
7include!(concat!(env!("OUT_DIR"), "/src/lib_inner.rs"));