1use std::fs;
2use std::fs::{read_dir, read_to_string};
3use std::io;
4use std::path::{Path, PathBuf};
5
6pub fn run(input_dir: impl AsRef<Path>, output_file: impl AsRef<Path>) {
10 let mut files = vec![];
12 visit_dirs(input_dir.as_ref(), &mut files).unwrap();
13
14 let output_file = output_file.as_ref();
15
16 let output_dir = output_file.parent().unwrap();
17
18 let mut configs: Vec<proc_macro2::TokenStream> = vec![];
19 for file in files {
20 let string = read_to_string(&file).expect(
21 "Algorithm definition file could not be read. This is a bug. Please report it.",
22 );
23
24 let yaml: Result<serde_yaml::Value, _> = serde_yaml::from_str(&string);
25
26 match yaml {
27 Ok(chip) => {
28 let chip = extract_chip_family(&chip, output_dir);
29 configs.push(chip);
30 }
31 Err(e) => {
32 panic!("Failed to parse target file: {:?} because:\n{}", file, e);
33 }
34 }
35 }
36
37 let include_stream = if configs.is_empty() {
38 quote::quote! {}
39 } else {
40 quote::quote! {
41 #[allow(unused_imports)]
42 use jep106::JEP106Code;
43 use crate::config::{Chip, RawFlashAlgorithm, FlashRegion, MemoryRegion, RamRegion, SectorDescription, FlashProperties};
44
45 use std::borrow::Cow;
46 }
47 };
48
49 let target_count = configs.len();
50
51 let stream = quote::quote! {
52 #include_stream
53 use crate::config::ChipFamily;
54
55 #[allow(clippy::unreadable_literal)]
56 pub const TARGETS: [ChipFamily;#target_count] = [
57 #(#configs,)*
58 ];
59
60 pub fn get_targets() -> &'static [ChipFamily] {
61 &TARGETS
62 }
63 };
64
65 fs::write(output_file, stream.to_string()).expect("Writing build.rs output failed.");
66}
67
68fn visit_dirs(dir: &Path, targets: &mut Vec<PathBuf>) -> io::Result<()> {
70 if dir.is_dir() {
71 for entry in read_dir(dir)? {
72 let entry = entry?;
73 let path = entry.path();
74 if path.is_dir() {
75 visit_dirs(&path, targets)?;
76 } else {
77 targets.push(path.to_owned());
78 }
79 }
80 }
81 Ok(())
82}
83
84fn quote_option<T: quote::ToTokens>(option: Option<T>) -> proc_macro2::TokenStream {
86 if let Some(value) = option {
87 quote::quote! {
88 Some(#value)
89 }
90 } else {
91 quote::quote! {
92 None
93 }
94 }
95}
96
97fn extract_algorithms(
99 chip: &serde_yaml::Value,
100 output_dir: &Path,
101) -> Vec<proc_macro2::TokenStream> {
102 let algorithm_iter = chip
104 .get("flash_algorithms")
105 .unwrap()
106 .as_mapping()
107 .unwrap()
108 .iter();
109
110 algorithm_iter
111 .map(|(_name, algorithm)| {
112 let name = algorithm
114 .get("name")
115 .unwrap()
116 .as_str()
117 .unwrap()
118 .to_ascii_lowercase();
119 let description = algorithm
120 .get("description")
121 .unwrap()
122 .as_str()
123 .unwrap()
124 .to_ascii_lowercase();
125 let default = algorithm.get("default").unwrap().as_bool().unwrap();
126 let instructions: Vec<u8> =
127 base64::decode(algorithm.get("instructions").unwrap().as_str().unwrap()).unwrap();
128 let pc_init =
129 quote_option(algorithm.get("pc_init").unwrap().as_u64().map(|v| v as u32));
130 let pc_uninit = quote_option(
131 algorithm
132 .get("pc_uninit")
133 .unwrap()
134 .as_u64()
135 .map(|v| v as u32),
136 );
137 let pc_program_page =
138 algorithm.get("pc_program_page").unwrap().as_u64().unwrap() as u32;
139 let pc_erase_sector =
140 algorithm.get("pc_erase_sector").unwrap().as_u64().unwrap() as u32;
141 let pc_erase_all = quote_option(
142 algorithm
143 .get("pc_erase_all")
144 .unwrap()
145 .as_u64()
146 .map(|v| v as u32),
147 );
148 let data_section_offset = algorithm
149 .get("data_section_offset")
150 .unwrap()
151 .as_u64()
152 .unwrap() as u32;
153
154 let flash_properties = algorithm.get("flash_properties").unwrap();
155
156 let range = flash_properties.get("address_range").unwrap();
157 let start = range.get("start").unwrap().as_u64().unwrap() as u32;
158 let end = range.get("end").unwrap().as_u64().unwrap() as u32;
159 let page_size = flash_properties.get("page_size").unwrap().as_u64().unwrap() as u32;
160 let erased_byte_value = flash_properties
161 .get("erased_byte_value")
162 .unwrap()
163 .as_u64()
164 .unwrap() as u8;
165 let program_page_timeout = flash_properties
166 .get("program_page_timeout")
167 .unwrap()
168 .as_u64()
169 .unwrap() as u32;
170 let erase_sector_timeout = flash_properties
171 .get("erase_sector_timeout")
172 .unwrap()
173 .as_u64()
174 .unwrap() as u32;
175
176 let sectors = extract_sectors(&flash_properties);
178
179 let mut algorithm_file_name = name.replace(" ", "_");
182
183 algorithm_file_name.push_str(".bin");
184
185 let algorithm_path = output_dir.join(&algorithm_file_name);
186
187 fs::write(&algorithm_path, &instructions).unwrap();
188
189 let algorithm = quote::quote! {
191 RawFlashAlgorithm {
192 name: Cow::Borrowed(#name),
193 description: Cow::Borrowed(#description),
194 default: #default,
195 instructions: Cow::Borrowed(include_bytes!(#algorithm_file_name)),
196 pc_init: #pc_init,
197 pc_uninit: #pc_uninit,
198 pc_program_page: #pc_program_page,
199 pc_erase_sector: #pc_erase_sector,
200 pc_erase_all: #pc_erase_all,
201 data_section_offset: #data_section_offset,
202 flash_properties: FlashProperties {
203 address_range: #start..#end,
204 page_size: #page_size,
205 erased_byte_value: #erased_byte_value,
206 program_page_timeout: #program_page_timeout,
207 erase_sector_timeout: #erase_sector_timeout,
208 sectors: Cow::Borrowed(&[
209 #(#sectors,)*
210 ])
211 },
212 }
213 };
214
215 algorithm
216 })
217 .collect()
218}
219
220fn extract_sectors(region: &serde_yaml::Value) -> Vec<proc_macro2::TokenStream> {
221 match region.get("sectors") {
222 Some(sectors) => {
223 let iter = sectors.as_sequence().unwrap().iter();
224
225 iter.map(|sector| {
226 let size = sector.get("size").unwrap().as_u64().unwrap() as u32;
227 let address = sector.get("address").unwrap().as_u64().unwrap() as u32;
228
229 quote::quote! {
230 SectorDescription {
231 size: #size,
232 address: #address,
233 }
234 }
235 })
236 .collect()
237 }
238 None => vec![],
241 }
242}
243
244fn extract_memory_map(chip: &serde_yaml::Value) -> Vec<proc_macro2::TokenStream> {
246 let memory_map_iter = chip
248 .get("memory_map")
249 .unwrap()
250 .as_sequence()
251 .unwrap()
252 .iter();
253
254 memory_map_iter
255 .filter_map(|memory_region| {
256 memory_region
258 .get("Ram")
259 .map(|region| {
260 let range = region.get("range").unwrap();
261 let start = range.get("start").unwrap().as_u64().unwrap() as u32;
262 let end = range.get("end").unwrap().as_u64().unwrap() as u32;
263 let is_boot_memory = region.get("is_boot_memory").unwrap().as_bool().unwrap();
264
265 quote::quote! {
266 MemoryRegion::Ram(RamRegion {
267 range: #start..#end,
268 is_boot_memory: #is_boot_memory,
269 })
270 }
271 })
272 .or_else(|| {
273 memory_region.get("Flash").map(|region| {
274 let range = region.get("range").unwrap();
275 let start = range.get("start").unwrap().as_u64().unwrap() as u32;
276 let end = range.get("end").unwrap().as_u64().unwrap() as u32;
277 let is_boot_memory =
278 region.get("is_boot_memory").unwrap().as_bool().unwrap();
279
280 quote::quote! {
281 MemoryRegion::Flash(FlashRegion {
282 range: #start..#end,
283 is_boot_memory: #is_boot_memory,
284 })
285 }
286 })
287 })
288 })
289 .collect()
290}
291
292fn extract_variants(chip_family: &serde_yaml::Value) -> Vec<proc_macro2::TokenStream> {
294 let variants_iter = chip_family
296 .get("variants")
297 .unwrap()
298 .as_sequence()
299 .unwrap()
300 .iter();
301
302 variants_iter
303 .map(|variant| {
304 let name = variant.get("name").unwrap().as_str().unwrap();
305 let part = quote_option(
306 variant
307 .get("part")
308 .and_then(|v| v.as_u64().map(|v| v as u16)),
309 );
310
311 let memory_map = extract_memory_map(&variant);
313
314 let flash_algorithms = variant
315 .get("flash_algorithms")
316 .unwrap()
317 .as_sequence()
318 .unwrap();
319 let flash_algorithm_names = flash_algorithms.iter().map(|a| a.as_str().unwrap());
320 quote::quote! {
321 Chip {
322 name: Cow::Borrowed(#name),
323 part: #part,
324 memory_map: Cow::Borrowed(&[
325 #(#memory_map,)*
326 ]),
327 flash_algorithms: Cow::Borrowed(&[
328 #(Cow::Borrowed(#flash_algorithm_names),)*
329 ]),
330 }
331 }
332 })
333 .collect()
334}
335
336fn extract_chip_family(
338 chip_family: &serde_yaml::Value,
339 output_dir: &Path,
340) -> proc_macro2::TokenStream {
341 let algorithms = extract_algorithms(&chip_family, output_dir);
343
344 let variants = extract_variants(&chip_family);
346
347 let name = chip_family
348 .get("name")
349 .unwrap()
350 .as_str()
351 .unwrap()
352 .to_ascii_lowercase();
353 let core = chip_family
354 .get("core")
355 .unwrap()
356 .as_str()
357 .unwrap()
358 .to_ascii_lowercase();
359 let manufacturer = quote_option(extract_manufacturer(&chip_family));
360
361 let chip_family = quote::quote! {
363 ChipFamily {
364 name: Cow::Borrowed(#name),
365 manufacturer: #manufacturer,
366 flash_algorithms: Cow::Borrowed(&[
367 #(#algorithms,)*
368 ]),
369 variants: Cow::Borrowed(&[
370 #(#variants,)*
371 ]),
372 core: Cow::Borrowed(#core),
373 }
374 };
375
376 chip_family
377}
378
379fn extract_manufacturer(chip: &serde_yaml::Value) -> Option<proc_macro2::TokenStream> {
381 chip.get("manufacturer").and_then(|manufacturer| {
382 let cc = manufacturer.get("cc").map(|v| v.as_u64().unwrap() as u8);
383 let id = manufacturer.get("id").map(|v| v.as_u64().unwrap() as u8);
384
385 if cc.is_some() && id.is_some() {
387 Some(quote::quote! {
388 JEP106Code {
389 cc: #cc,
390 id: #id,
391 }
392 })
393 } else {
394 None
395 }
396 })
397}