use std::collections::BTreeMap;
use crate::silicon::model::{parse_port_pin, SiliconTarget};
use crate::silicon::PeripheralSignal;
use super::groups::{categorize_pins, field_names};
use super::pins::{McuInfo, PinMapping};
fn comment_text(s: &str) -> String {
s.chars()
.map(|c| {
if c.is_control() {
c.escape_default().to_string()
} else {
c.to_string()
}
})
.collect()
}
fn pin_type(pins: &[PinMapping]) -> &'static str {
match pins.iter().map(|p| p.pin_number).max().unwrap_or(0) {
0..=0xFF => "u8",
0x100..=0xFFFF => "u16",
_ => "u32",
}
}
pub fn generate_pins_rs(mcu: &McuInfo, board_name: &str) -> String {
let mut buf = String::new();
let ty = pin_type(&mcu.pins);
render_header(&mut buf, mcu, board_name);
render_gpio(&mut buf, mcu, ty);
let groups = categorize_pins(&mcu.pins);
for (group_name, pins) in &groups {
render_group(&mut buf, group_name, pins, ty);
}
render_board(&mut buf, &groups);
buf
}
fn render_header(buf: &mut String, mcu: &McuInfo, board_name: &str) {
buf.push_str("//! Generated by Fusible — Electronic Linter & Hardware Bridge.\n");
buf.push_str("//! DO NOT EDIT MANUALLY. Changes will be overwritten on next build.\n");
buf.push_str("//!\n");
buf.push_str(&format!("//! Board: {}\n", comment_text(board_name)));
buf.push_str(&format!(
"//! Microcontroller: {} ({})\n",
comment_text(&mcu.chip),
comment_text(&mcu.name)
));
buf.push_str("\n#![allow(dead_code)]\n\n");
}
fn render_gpio(buf: &mut String, mcu: &McuInfo, ty: &str) {
buf.push_str("/// Hardware GPIO pin constants extracted from the circuit netlist.\n");
buf.push_str("pub mod gpio {\n");
let target = crate::silicon::find_target(&mcu.chip);
for pin in &mcu.pins {
let af_doc = target.and_then(|t| af_doc(t, pin)).unwrap_or_default();
let port_doc = if parse_port_pin(&pin.pin_label).is_some() {
format!(" = `{}` (port * 16 + pin)", comment_text(&pin.pin_label))
} else {
String::new()
};
buf.push_str(&format!(
" /// Pin {}{} (hardware net `{}`){}.\n",
pin.pin_number,
port_doc,
comment_text(&pin.net_name),
af_doc
));
buf.push_str(&format!(
" pub const {}: {ty} = {};\n",
pin.signal_name, pin.pin_number
));
}
buf.push_str("}\n\n");
}
fn af_doc(target: &SiliconTarget, pin: &PinMapping) -> Option<String> {
let spec = target.find_pin(&pin.pin_label)?;
let sig = PeripheralSignal::parse(&pin.signal_name)
.or_else(|| PeripheralSignal::parse(&pin.net_name))?;
let af = spec.get_af(&sig)?;
Some(format!(" [{}: AF{}]", af.af_name, af.af_number))
}
fn render_group(buf: &mut String, group_name: &str, pins: &[&PinMapping], ty: &str) {
let struct_name = format!("{group_name}Pins");
let const_name = format!("{}_PINS", group_name.to_ascii_uppercase());
let fields = field_names(pins, group_name);
buf.push_str(&format!("/// Peripheral pin mapping for `{group_name}`.\n"));
buf.push_str("#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n");
buf.push_str(&format!("pub struct {struct_name} {{\n"));
for field_name in &fields {
buf.push_str(&format!(" pub {field_name}: {ty},\n"));
}
buf.push_str("}\n\n");
buf.push_str(&format!(
"/// Default `{group_name}` hardware configuration.\n"
));
buf.push_str(&format!(
"pub const {const_name}: {struct_name} = {struct_name} {{\n"
));
for (pin, field_name) in pins.iter().zip(&fields) {
buf.push_str(&format!(" {field_name}: gpio::{},\n", pin.signal_name));
}
buf.push_str("};\n\n");
}
fn render_board(buf: &mut String, groups: &BTreeMap<String, Vec<&PinMapping>>) {
buf.push_str("/// Complete board pin assignment container.\n");
buf.push_str("#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n");
buf.push_str("pub struct BoardPins {\n");
for group_name in groups.keys() {
let field_name = group_name.to_ascii_lowercase();
let struct_name = format!("{group_name}Pins");
buf.push_str(&format!(" pub {field_name}: {struct_name},\n"));
}
buf.push_str("}\n\n");
buf.push_str("/// Verified hardware board pin assignments.\n");
buf.push_str("pub const BOARD_PINS: BoardPins = BoardPins {\n");
for group_name in groups.keys() {
let field_name = group_name.to_ascii_lowercase();
let const_name = format!("{}_PINS", group_name.to_ascii_uppercase());
buf.push_str(&format!(" {field_name}: {const_name},\n"));
}
buf.push_str("};\n");
}