use crate::netlist::{NetNode, Netlist};
use crate::parser::{Circuit, Port};
use crate::silicon::model::SiliconTarget;
use super::naming::{dedupe_signal_names, derive_signal_name, sanitize_ident};
pub use super::render::generate_pins_rs;
pub(crate) use super::select::select_mcu_component;
use super::select::{is_power_rail, parse_gpio_number};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PinMapping {
pub pin_number: u32,
pub pin_label: String,
pub signal_name: String,
pub net_name: String,
pub remote_component: Option<String>,
pub remote_port: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct McuInfo {
pub component_id: String,
pub name: String,
pub chip: String,
pub pins: Vec<PinMapping>,
}
struct McuRef<'a> {
circuit: &'a Circuit,
netlist: &'a Netlist,
id: &'a str,
name: &'a str,
silicon: Option<&'static SiliconTarget>,
}
struct NetLink {
net_name: String,
remote_component: Option<String>,
remote_port: Option<String>,
signal_name: String,
}
pub fn extract_mcu_pins(
circuit: &Circuit,
netlist: &Netlist,
target_chip: Option<&str>,
) -> Option<McuInfo> {
let mcu_comp = select_mcu_component(circuit, target_chip)?;
let mcu_id = mcu_comp.component_id.as_deref().unwrap_or("U1");
let mcu_name = mcu_comp.name.as_deref().unwrap_or(mcu_id);
let chip_name = target_chip
.map(str::to_string)
.or_else(|| mcu_comp.kind.clone())
.unwrap_or_else(|| mcu_name.to_string());
let mcu = McuRef {
circuit,
netlist,
id: mcu_id,
name: mcu_name,
silicon: crate::silicon::find_target(&chip_name),
};
let mut pin_mappings: Vec<PinMapping> = circuit
.ports()
.filter_map(|port| mcu.map_port(port))
.collect();
dedupe_signal_names(&mut pin_mappings);
Some(McuInfo {
component_id: mcu_id.to_string(),
name: mcu_name.to_string(),
chip: chip_name,
pins: pin_mappings,
})
}
fn port_label<'a>(port: &'a Port, port_id: &'a str) -> &'a str {
port.name
.as_deref()
.or_else(|| {
port.port_hints
.as_ref()
.and_then(|h| h.first().map(String::as_str))
})
.unwrap_or(port_id)
}
impl McuRef<'_> {
fn is_mcu(&self, component_id: Option<&str>) -> bool {
component_id == Some(self.id) || component_id == Some(self.name)
}
fn owns(&self, port: &Port) -> bool {
self.is_mcu(port.source_component_id.as_deref())
|| port
.port_id
.as_deref()
.map(|pid| pid.starts_with(self.id) || pid.starts_with(self.name))
.unwrap_or(false)
}
fn map_port(&self, port: &Port) -> Option<PinMapping> {
if !self.owns(port) {
return None;
}
let port_id = port.port_id.as_deref()?;
let pin_label = port_label(port, port_id);
if self
.silicon
.is_some_and(|t| t.find_pin(pin_label).is_none())
{
return None;
}
let pin_number = parse_gpio_number(pin_label, port.pin_number)?;
let link = self.net_link(port_id, pin_label);
let skip = is_power_rail(&link.net_name)
|| is_power_rail(pin_label)
|| pin_label.starts_with("NC");
(!skip).then(|| PinMapping {
pin_number,
pin_label: pin_label.to_string(),
signal_name: link.signal_name,
net_name: link.net_name,
remote_component: link.remote_component,
remote_port: link.remote_port,
})
}
fn net_link(&self, port_id: &str, pin_label: &str) -> NetLink {
let node = self
.netlist
.port_to_net
.get(port_id)
.and_then(|&idx| self.netlist.nets.get(idx));
match node {
Some(node) => self.connected_link(node, pin_label),
None => NetLink {
net_name: format!("unconnected_{pin_label}"),
remote_component: None,
remote_port: None,
signal_name: sanitize_ident(pin_label),
},
}
}
fn connected_link(&self, node: &NetNode, pin_label: &str) -> NetLink {
let net_name = node.display_name().to_string();
let remote = node
.endpoints
.iter()
.find(|ep| !self.is_mcu(ep.component_id.as_deref()));
let remote_component = remote
.and_then(|ep| ep.component_id.as_deref())
.map(|id| self.component_name(id));
let remote_port = remote.and_then(|ep| ep.name.clone());
let signal_name = derive_signal_name(
&net_name,
pin_label,
remote_component.as_deref(),
remote_port.as_deref(),
);
NetLink {
net_name,
remote_component,
remote_port,
signal_name,
}
}
fn component_name(&self, id: &str) -> String {
self.circuit
.components()
.find(|c| c.component_id.as_deref() == Some(id))
.and_then(|c| c.name.clone())
.unwrap_or_else(|| id.to_string())
}
}