extern crate avr_mcu;
use std::path::Path;
fn main() {
let crate_root = Path::new(env!("CARGO_MANIFEST_DIR"));
let mcus = if cfg!(feature = "all_mcus") {
avr_mcu::microcontrollers().to_owned()
} else {
if cfg!(arch = "avr") {
let current_mcu = avr_mcu::current::mcu()
.expect("no target cpu set");
vec![current_mcu]
} else {
avr_mcu::microcontrollers().to_owned()
}
};
gen::all(&crate_root.join("src").join("gen"), &mcus).unwrap();
}
mod gen {
use avr_mcu::*;
use std::collections::{HashMap, HashSet};
use std::fs::{self, File};
use std::io::prelude::*;
use std::io;
use std::path::Path;
pub fn all(path: &Path, mcus: &[Mcu]) -> Result<(), io::Error> {
fs::create_dir_all(path)?;
let mut module_names = Vec::new();
for mcu in mcus.iter() {
let module_name = self::mcu_module_name(mcu);
let module_path = path.join(format!("{}.rs", module_name));
eprintln!("generating module for {}", mcu.device.name);
generate_mcu_module(mcu, &module_path)?;
module_names.push(module_name);
}
generate_entry_module(path, &module_names)
}
fn generate_entry_module(output_path: &Path, module_names: &[String]) -> Result<(), io::Error> {
let mut mod_rs = File::create(output_path.join("mod.rs"))?;
writeln!(mod_rs, "// Device definitions")?;
for module_name in module_names {
writeln!(mod_rs, "pub mod {};", module_name)?;
}
writeln!(mod_rs)?;
const CURRENT_MOD_SUMMARY: &'static str = "Contains definitions for the current AVR device";
writeln!(mod_rs, "/// {}", CURRENT_MOD_SUMMARY)?;
writeln!(mod_rs, "///")?;
writeln!(mod_rs, "/// **NOTE**: We are showing the ATmega328 here, even though the library")?;
writeln!(mod_rs, "/// is not targeting a real AVR device. If you compile this library for")?;
writeln!(mod_rs, "/// a specific AVR MCU, the module for that device will aliased here.")?;
writeln!(mod_rs, "// If we are targeting a non-AVR device, just pick the ATmega328p so")?;
writeln!(mod_rs, "// that users can see what the API would look like")?;
writeln!(mod_rs, "//")?;
writeln!(mod_rs, "// Note that we reexport rather than alias so that we can add a note about")?;
writeln!(mod_rs, "// this behaviour to the documentation.")?;
writeln!(mod_rs, "#[cfg(not(target_arch = \"avr\"))]")?;
writeln!(mod_rs, "pub mod current {{ pub use super::atmega328::*; }}")?;
writeln!(mod_rs)?;
writeln!(mod_rs, "/// {}", CURRENT_MOD_SUMMARY)?;
writeln!(mod_rs, "// If we are targeting AVR, lookup the current device's module")?;
writeln!(mod_rs, "// and alias it to the `current` module.")?;
writeln!(mod_rs, "#[cfg(target_arch = \"avr\")]")?;
writeln!(mod_rs, "pub mod current {{")?;
writeln!(mod_rs, " // NOTE: 'target_cpu' is a cfg flag specific to the avr-rust fork")?;
for module_name in module_names {
writeln!(mod_rs, " #[cfg(target_cpu = \"{}\")] pub use super::{} as current;",
module_name, module_name)?;
}
writeln!(mod_rs, "}}")?;
Ok(())
}
fn generate_mcu_module(mcu: &Mcu, path: &Path) -> Result<(), io::Error> {
let mut file = File::create(path)?;
self::mcu_module_doc(mcu, &mut file)?;
writeln!(file)?;
self::mcu_module_code(mcu, &mut file)?;
Ok(())
}
fn mcu_module_name(mcu: &Mcu) -> String {
mcu.device.name.to_lowercase()
}
pub fn mcu_module_doc(mcu: &Mcu, w: &mut Write)
-> Result<(), io::Error> {
writeln!(w, "//! The AVR {} microcontroller", mcu.device.name)?;
writeln!(w, "//!")?;
writeln!(w, "//! # Variants")?;
writeln!(w, "//! | | Pinout | Mcuage | Operating temperature | Operating voltage | Max speed |")?;
writeln!(w, "//! |--------|--------|---------|-----------------------|-------------------|-----------|")?;
for variant in mcu.variants.iter() {
let pinout_label = variant.pinout.as_ref().map(|p| p.replace('_', "-").to_owned()).unwrap_or_else(|| String::new());
let speed_mhz = variant.speed_max_hz / 1_000_000;
writeln!(w, "//! | {} | {} | {} | {}°C - {}°C | {}V - {}V | {} MHz |",
variant.name, pinout_label,
variant.package, variant.temperature_min,
variant.temperature_max, variant.voltage_min, variant.voltage_max,
speed_mhz)?;
}
writeln!(w, "//!")?;
Ok(())
}
pub fn mcu_module_code(mcu: &Mcu, w: &mut Write)
-> Result<(), io::Error> {
let registers = ordered_registers(mcu);
let register_bitfields = documentable_bitfields(®isters);
writeln!(w, "#![allow(non_upper_case_globals)]")?;
writeln!(w)?;
for register in registers.iter() {
let ty = integer_type(register.size);
if !register.caption.is_empty() {
let mut caption = register.caption.trim().to_owned();
if !caption.ends_with('.') { caption.push('.') }
writeln!(w, "/// {}", caption)?;
} else {
writeln!(w, "/// {} register", register.name)?;
}
let mut bitfields = register_bitfields.iter().filter_map(|&(reg,bitfield)| {
if reg == register { Some(bitfield) } else { None }
}).peekable();
if bitfields.peek().is_some() {
writeln!(w, "///")?;
writeln!(w, "/// Bitfields:")?;
writeln!(w, "///")?;
writeln!(w, "/// | Name | Mask (binary) |")?;
writeln!(w, "/// | ---- | ------------- |")?;
while let Some(bitfield) = bitfields.next() {
writeln!(w, "/// | {} | {:b} |", bitfield.name, bitfield.mask)?;
}
}
writeln!(w, "pub const {}: *mut {} = {:#X} as *mut {};",
register.name, ty, register.offset, ty)?;
writeln!(w)?;
}
for (register, bitfield) in register_bitfields {
let ty = integer_type(bitfield.size);
writeln!(w, "/// Bitfield on register {}", register.name)?;
writeln!(w, "pub const {}: *mut {} = {:#X} as *mut {};",
bitfield.name, ty, bitfield.mask, ty)?;
writeln!(w)?;
}
Ok(())
}
fn ordered_registers(mcu: &Mcu) -> Vec<Register> {
let mut unique_registers = self::unique_registers(mcu);
insert_high_low_variants(&mut unique_registers);
let mut registers: Vec<Register> = unique_registers.into_iter().map(|a| a.1).collect();
registers.sort_by_key(|r| r.offset);
registers
}
fn insert_high_low_variants(registers: &mut HashMap<String, Register>) {
let wide_registers: Vec<_> = registers.values()
.filter(|r| r.size == 2)
.cloned()
.collect();
for r in wide_registers {
let (high, low) = high_low_variants(&r);
if !registers.contains_key(&high.name) {
registers.insert(high.name.clone(), high);
}
if !registers.contains_key(&low.name) {
registers.insert(low.name.clone(), low);
}
}
}
fn high_low_variants(r: &Register) -> (Register, Register) {
assert_eq!(2, r.size, "only 16-bit registers have high low variants");
(
Register { name: r.name.clone() + "H",
caption: r.caption.clone() + " high byte",
offset: r.offset + 1,
size: r.size / 2,
mask: None,
bitfields: Vec::new(), rw: r.rw.clone() },
Register { name: r.name.clone() + "L",
caption: r.caption.clone() + " low byte",
offset: r.offset + 0,
size: r.size / 2,
mask: None,
bitfields: Vec::new(), rw: r.rw.clone() },
)
}
fn unique_registers(mcu: &Mcu) -> HashMap<String, Register> {
let mut result = HashMap::new();
for module in mcu.modules.iter() {
for register_group in module.register_groups.iter() {
for register in register_group.registers.iter() {
let r: Register = if let Some(ref existing) = result.remove(®ister.name) {
register.union(existing)
} else {
register.clone()
};
result.insert(r.name.clone(), r);
}
}
}
result
}
fn integer_type(byte_count: u32) -> &'static str {
match byte_count {
1 => "u8",
2 => "u16",
4 => "u32",
_ => panic!("failed to get type of {}", byte_count),
}
}
fn documentable_bitfields(registers: &[Register]) -> Vec<(&Register, &Bitfield)> {
let register_names: HashSet<&str> = registers.iter().map(|r| &r.name[..]).collect();
let mut history: HashMap<&str, Vec<(&Register, &Bitfield)>> = HashMap::new();
for register in registers.iter(){
for bitfield in register.bitfields.iter() {
let bitfields = history.entry(&bitfield.name).
or_insert_with(|| Vec::new());
bitfields.push((register, bitfield));
}
}
let mut register_bitfields: Vec<_> = history.into_iter().map(|(_, register_bitfields)| register_bitfields).collect();
register_bitfields.sort_by_key(|register_bitfields| ®ister_bitfields[0].0.name);
let unique_bitfields = register_bitfields.into_iter().filter_map(|register_bitfields| {
if register_bitfields.len() == 1 {
Some(register_bitfields.into_iter().next().unwrap())
} else {
None
}
});
let bitfields = unique_bitfields.filter(|&(register, bitfield)| {
let full_mask = match register.size {
1 => 0xff,
2 => 0xffff,
_ => panic!("register is too large"),
};
bitfield.mask != full_mask &&
bitfield.name != register.name &&
!register_names.contains(&bitfield.name[..])
});
bitfields.collect()
}
}