mirror of https://codeberg.org/topola/topola.git
222 lines
8.1 KiB
Rust
222 lines
8.1 KiB
Rust
// SPDX-FileCopyrightText: 2024 Topola contributors
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
//! Module for handling Specctra's mesadata - design rules, as well as layers
|
|
//! or net properties
|
|
|
|
use bimap::BiBTreeMap;
|
|
use std::collections::BTreeMap;
|
|
|
|
use crate::{
|
|
rules::{AccessRules, Conditions},
|
|
structure::Pcb,
|
|
};
|
|
|
|
/// Trait for managing the Specctra's mesadata
|
|
///
|
|
/// This trait implements generic function for accessing or modifying different
|
|
/// compounds of board parts like nets or layers
|
|
pub trait AccessMesadata: AccessRules {
|
|
/// Renames a layer based on its index.
|
|
fn bename_layer(&mut self, layer: usize, layername: String);
|
|
|
|
/// Retrieves the name of a layer by its index.
|
|
fn layer_layername(&self, layer: usize) -> Option<&str>;
|
|
|
|
/// Retrieves the index of a layer by its name.
|
|
fn layername_layer(&self, layername: &str) -> Option<usize>;
|
|
|
|
/// Renames a net based on its index.
|
|
fn bename_net(&mut self, net: usize, netname: String);
|
|
|
|
/// Retrieves the name of a net by its index.
|
|
fn net_netname(&self, net: usize) -> Option<&str>;
|
|
|
|
/// Retrieves the index of a net by its name.
|
|
fn netname_net(&self, netname: &str) -> Option<usize>;
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
/// [`SpecctraRule`] represents the basic routing constraints used by an auto-router, such as
|
|
/// the Topola auto-router, in a PCB design process. This struct defines two key design
|
|
/// rules: the width of the trace and the minimum clearance between electrical features.
|
|
pub struct SpecctraRule {
|
|
/// Specifies the width of the trace (or conductor) in millimeters.
|
|
/// This value ensures that the traces meet electrical
|
|
/// and mechanical requirements, such as current-carrying capacity or signal integrity.
|
|
pub width: f64,
|
|
/// Defines the minimum clearance (spacing) between traces, pads,
|
|
/// or other conductive features on the PCB. Adequate clearance is important for
|
|
/// preventing electrical shorts or interference between signals, and is often
|
|
/// dictated by manufacturing constraints or voltage considerations.
|
|
pub clearance: f64,
|
|
}
|
|
|
|
impl SpecctraRule {
|
|
fn from_dsn(rule: &super::structure::Rule) -> Self {
|
|
Self {
|
|
width: rule.width as f64,
|
|
clearance: rule.clearances[0].value as f64, // picks the generic clearance only for now
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
/// [`SpecctraMesadata`] holds the metadata required by the Specctra auto-router to
|
|
/// understand and enforce design rules across various net classes and layers in a PCB layout.
|
|
/// This struct encapsulates information about rules for individual nets, net classes,
|
|
/// layers, and their corresponding relationships.
|
|
pub struct SpecctraMesadata {
|
|
/// The default routing rule applied globally if no specific net class rule is defined.
|
|
structure_rule: SpecctraRule,
|
|
|
|
// net class name -> rule
|
|
/// A map from net class names to their specific `SpecctraRule` constraints.
|
|
/// These rules are applied to all nets belonging to the respective net clas
|
|
class_rules: BTreeMap<String, SpecctraRule>,
|
|
|
|
// layername <-> layer for Layout
|
|
/// A bidirectional map between layer indices and layer names, allowing translation
|
|
/// between index-based layers in the layout and user-defined layer names.
|
|
pub layer_layername: BiBTreeMap<usize, String>,
|
|
|
|
// netname <-> net for Layout
|
|
/// A bidirectional map between network indices and network names in the PCB layout,
|
|
/// providing an easy way to reference nets by name or index.
|
|
pub net_netname: BiBTreeMap<usize, String>,
|
|
|
|
// net -> netclass
|
|
/// A map that associates network indices with their respective net class names.
|
|
/// This is used to apply net class-specific routing rules to each net.
|
|
net_netclass: BTreeMap<usize, String>,
|
|
}
|
|
|
|
impl SpecctraMesadata {
|
|
/// Creates a [`SpecctraMesadata`] instance from a given `Pcb` reference.
|
|
///
|
|
/// This function extracts the necessary metadata from the `Pcb` struct, such as
|
|
/// layer-to-layer name mappings, net-to-net name mappings, and net class rules.
|
|
pub fn from_pcb(pcb: &Pcb) -> Self {
|
|
let layer_layername = BiBTreeMap::from_iter(
|
|
pcb.structure
|
|
.layers
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(index, layer)| (index, layer.name.clone())),
|
|
);
|
|
|
|
// assign IDs to all nets named in pcb.network
|
|
let net_netname = {
|
|
let mut tmp: Vec<_> = pcb
|
|
.network
|
|
.classes
|
|
.iter()
|
|
.flat_map(|class| &class.nets)
|
|
.chain(pcb.network.nets.iter().map(|net| &net.name))
|
|
.collect();
|
|
// deduplicate net names
|
|
tmp.sort_unstable();
|
|
tmp.dedup();
|
|
|
|
BiBTreeMap::from_iter(tmp.into_iter().cloned().enumerate())
|
|
};
|
|
|
|
let mut net_netclass = BTreeMap::new();
|
|
let class_rules = BTreeMap::from_iter(
|
|
pcb.network
|
|
.classes
|
|
.iter()
|
|
.inspect(|class| {
|
|
for netname in &class.nets {
|
|
// this can't panic because we did iterate over
|
|
// `pcb.network.classes[].nets` above
|
|
let net = net_netname.get_by_right(netname).unwrap();
|
|
net_netclass.insert(*net, class.name.clone());
|
|
}
|
|
})
|
|
.map(|class| (class.name.clone(), SpecctraRule::from_dsn(&class.rule))),
|
|
);
|
|
|
|
let mut structure_rule = super::structure::Rule {
|
|
width: 0.0,
|
|
clearances: Vec::new(),
|
|
};
|
|
// workaround for differing syntax
|
|
// collapse multiple rule entries into a single one
|
|
for rule in &pcb.structure.rules {
|
|
if let Some(width) = rule.width {
|
|
structure_rule.width = width
|
|
}
|
|
structure_rule
|
|
.clearances
|
|
.extend_from_slice(&rule.clearances);
|
|
}
|
|
|
|
Self {
|
|
structure_rule: SpecctraRule::from_dsn(&structure_rule),
|
|
class_rules,
|
|
layer_layername,
|
|
net_netname,
|
|
net_netclass,
|
|
}
|
|
}
|
|
|
|
/// Retrieves the Specctra routing rule associated with a specified net ID.
|
|
///
|
|
/// This function looks up the routing rule for a given net ID. It first checks if the net is
|
|
/// associated with a net class. If a net class is found, it retrieves the corresponding rule
|
|
/// from the class rules. If no class is associated, or if the class does not have a defined rule,
|
|
/// it defaults to the general structure rule.
|
|
///
|
|
pub fn get_rule(&self, net: usize) -> &SpecctraRule {
|
|
self.net_netclass
|
|
.get(&net)
|
|
.and_then(|netclass| self.class_rules.get(netclass))
|
|
.unwrap_or(&self.structure_rule)
|
|
}
|
|
}
|
|
|
|
impl AccessRules for SpecctraMesadata {
|
|
fn clearance(&self, conditions1: &Conditions<'_>, conditions2: &Conditions<'_>) -> f64 {
|
|
let clr1 = self.get_rule(conditions1.net).clearance;
|
|
let clr2 = self.get_rule(conditions2.net).clearance;
|
|
|
|
f64::max(clr1, clr2)
|
|
}
|
|
|
|
fn largest_clearance(&self, _maybe_net: Option<usize>) -> f64 {
|
|
self.class_rules
|
|
.values()
|
|
.map(|rule| rule.clearance)
|
|
.reduce(f64::max)
|
|
.unwrap_or(0.0)
|
|
}
|
|
}
|
|
|
|
impl AccessMesadata for SpecctraMesadata {
|
|
fn bename_layer(&mut self, layer: usize, layername: String) {
|
|
self.layer_layername.insert(layer, layername);
|
|
}
|
|
|
|
fn layer_layername(&self, layer: usize) -> Option<&str> {
|
|
self.layer_layername.get_by_left(&layer).map(|s| s.as_str())
|
|
}
|
|
|
|
fn layername_layer(&self, layername: &str) -> Option<usize> {
|
|
self.layer_layername.get_by_right(layername).copied()
|
|
}
|
|
|
|
fn bename_net(&mut self, net: usize, netname: String) {
|
|
self.net_netname.insert(net, netname);
|
|
}
|
|
|
|
fn net_netname(&self, net: usize) -> Option<&str> {
|
|
self.net_netname.get_by_left(&net).map(|s| s.as_str())
|
|
}
|
|
|
|
fn netname_net(&self, netname: &str) -> Option<usize> {
|
|
self.net_netname.get_by_right(netname).copied()
|
|
}
|
|
}
|