From a4a000feb992b660ebb18a4cbdd934786316a704 Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Tue, 23 Sep 2025 13:25:32 +0200 Subject: [PATCH] feat(autorouter/ratsnest): Have ratlines across layers --- src/autorouter/autorouter.rs | 2 +- src/autorouter/conncomps.rs | 154 ++++++++++++++++++++++++++++------- src/autorouter/ratsnest.rs | 44 +++++----- src/bimapset.rs | 4 + src/board/mod.rs | 7 +- tests/common/mod.rs | 4 +- 6 files changed, 163 insertions(+), 52 deletions(-) diff --git a/src/autorouter/autorouter.rs b/src/autorouter/autorouter.rs index 6c3070e..af169ff 100644 --- a/src/autorouter/autorouter.rs +++ b/src/autorouter/autorouter.rs @@ -73,7 +73,7 @@ pub struct Autorouter { impl Autorouter { pub fn new(board: Board) -> Result { - let ratsnest = Ratsnest::new(board.layout())?; + let ratsnest = Ratsnest::new(&board)?; Ok(Self { board, ratsnest }) } diff --git a/src/autorouter/conncomps.rs b/src/autorouter/conncomps.rs index db823c0..f9b475c 100644 --- a/src/autorouter/conncomps.rs +++ b/src/autorouter/conncomps.rs @@ -2,51 +2,149 @@ // // SPDX-License-Identifier: MIT +use std::collections::BTreeSet; + use derive_getters::Getters; use petgraph::unionfind::UnionFind; -use specctra_core::rules::AccessRules; +use specctra_core::mesadata::AccessMesadata; use crate::{ - drawing::{graph::PrimitiveIndex, primitive::GetJoints}, + board::Board, + drawing::{dot::FixedDotIndex, graph::PrimitiveIndex, primitive::GetJoints}, + geometry::GenericNode, graph::GetIndex, - layout::Layout, }; #[derive(Clone, Getters)] -pub struct Conncomps { +pub struct ConncompsWithPrincipalLayer { unionfind: UnionFind, } -impl Conncomps { - pub fn new(layout: &Layout) -> Self { - let mut unionfind = UnionFind::new(layout.drawing().geometry().dot_index_bound()); +impl ConncompsWithPrincipalLayer { + pub fn new(board: &Board, principal_layer: usize) -> Self { + let mut principally_visited_pins = BTreeSet::new(); + let mut unionfind = UnionFind::new(board.layout().drawing().geometry().dot_index_bound()); - for primitive in layout.drawing().primitive_nodes() { - match primitive { - PrimitiveIndex::FixedSeg(seg) => { - let joints = layout.drawing().primitive(seg).joints(); - unionfind.union(joints.0.index(), joints.1.index()); + for node in board + .layout() + .drawing() + .layer_primitive_nodes(principal_layer) + { + Self::unionize_primitive_endpoint_dots(board, &mut unionfind, node); + + if let Some(pinname) = board.node_pinname(&GenericNode::Primitive(node)) { + principally_visited_pins.insert(pinname.clone()); + } + } + + /*for layer in 0..board.layout().drawing().layer_count() { + if layer != principal_layer { + for primitive in board.layout().drawing().layer_primitive_nodes(layer) { + if let Some(pinname) = board.node_pinname(&GenericNode::Primitive(primitive)) { + if !principally_visited_pins.contains(pinname) { + Self::unionize_by_primitive(board, &mut unionfind, primitive); + } + } } - PrimitiveIndex::LoneLooseSeg(seg) => { - let joints = layout.drawing().primitive(seg).joints(); - unionfind.union(joints.0.index(), joints.1.index()); + } + }*/ + + //TODO for pinname in board.pins() if !principally_visited_pins.contains(pinname) for node in board.pinname_nodes() unionize to first found element + + for pinname in board.pinnames() { + if principally_visited_pins.contains(pinname) { + let mut iter = board.pinname_nodes(pinname); + let Some(first_fixed_dot) = iter.find_map(|node| { + if let GenericNode::Primitive(PrimitiveIndex::FixedDot(first_fixed_dot)) = node + { + Some(first_fixed_dot) + } else { + None + } + }) else { + continue; + }; + + for node in board.pinname_nodes(pinname) { + if let GenericNode::Primitive(primitive) = node { + Self::unionize_to_common(board, &mut unionfind, primitive, first_fixed_dot); + } } - PrimitiveIndex::SeqLooseSeg(seg) => { - let joints = layout.drawing().primitive(seg).joints(); - unionfind.union(joints.0.index(), joints.1.index()); - } - PrimitiveIndex::FixedBend(bend) => { - let joints = layout.drawing().primitive(bend).joints(); - unionfind.union(joints.0.index(), joints.1.index()); - } - PrimitiveIndex::LooseBend(bend) => { - let joints = layout.drawing().primitive(bend).joints(); - unionfind.union(joints.0.index(), joints.1.index()); - } - _ => (), } } Self { unionfind } } + + fn unionize_primitive_endpoint_dots( + board: &Board, + unionfind: &mut UnionFind, + primitive: PrimitiveIndex, + ) { + match primitive { + PrimitiveIndex::FixedSeg(seg) => { + let joints = board.layout().drawing().primitive(seg).joints(); + unionfind.union(joints.0.index(), joints.1.index()); + } + PrimitiveIndex::LoneLooseSeg(seg) => { + let joints = board.layout().drawing().primitive(seg).joints(); + unionfind.union(joints.0.index(), joints.1.index()); + } + PrimitiveIndex::SeqLooseSeg(seg) => { + let joints = board.layout().drawing().primitive(seg).joints(); + unionfind.union(joints.0.index(), joints.1.index()); + } + PrimitiveIndex::FixedBend(bend) => { + let joints = board.layout().drawing().primitive(bend).joints(); + unionfind.union(joints.0.index(), joints.1.index()); + } + PrimitiveIndex::LooseBend(bend) => { + let joints = board.layout().drawing().primitive(bend).joints(); + unionfind.union(joints.0.index(), joints.1.index()); + } + _ => (), + } + } + + fn unionize_to_common( + board: &Board, + unionfind: &mut UnionFind, + primitive: PrimitiveIndex, + common: FixedDotIndex, + ) { + match primitive { + PrimitiveIndex::FixedDot(dot) => { + unionfind.union(common.index(), dot.index()); + } + PrimitiveIndex::LooseDot(dot) => { + unionfind.union(common.index(), dot.index()); + } + PrimitiveIndex::FixedSeg(seg) => { + let joints = board.layout().drawing().primitive(seg).joints(); + unionfind.union(common.index(), joints.0.index()); + unionfind.union(common.index(), joints.1.index()); + } + PrimitiveIndex::LoneLooseSeg(seg) => { + let joints = board.layout().drawing().primitive(seg).joints(); + unionfind.union(common.index(), joints.0.index()); + unionfind.union(common.index(), joints.1.index()); + } + PrimitiveIndex::SeqLooseSeg(seg) => { + let joints = board.layout().drawing().primitive(seg).joints(); + unionfind.union(common.index(), joints.0.index()); + unionfind.union(common.index(), joints.1.index()); + } + PrimitiveIndex::FixedBend(bend) => { + let joints = board.layout().drawing().primitive(bend).joints(); + unionfind.union(common.index(), joints.0.index()); + unionfind.union(common.index(), joints.1.index()); + } + PrimitiveIndex::LooseBend(bend) => { + let joints = board.layout().drawing().primitive(bend).joints(); + unionfind.union(common.index(), joints.0.index()); + unionfind.union(common.index(), joints.1.index()); + } + _ => (), + } + } } diff --git a/src/autorouter/ratsnest.rs b/src/autorouter/ratsnest.rs index b3df1b9..079e9e6 100644 --- a/src/autorouter/ratsnest.rs +++ b/src/autorouter/ratsnest.rs @@ -11,22 +11,20 @@ use enum_dispatch::enum_dispatch; use geo::Point; use petgraph::{data::Element, prelude::StableUnGraph}; use spade::{handles::FixedVertexHandle, HasPosition, InsertionError, Point2}; +use specctra_core::mesadata::AccessMesadata; use crate::{ - autorouter::conncomps::Conncomps, + autorouter::conncomps::ConncompsWithPrincipalLayer, + board::Board, drawing::{ band::BandTermsegIndex, dot::FixedDotIndex, graph::{GetMaybeNet, MakePrimitiveRef, PrimitiveIndex}, primitive::MakePrimitiveShape, - rules::AccessRules, }, geometry::shape::AccessShape, graph::{GenericIndex, GetIndex, MakeRef}, - layout::{ - poly::{MakePolygon, PolyWeight}, - Layout, - }, + layout::poly::{MakePolygon, PolyWeight}, triangulation::{GetTrianvertexNodeIndex, Triangulation}, }; @@ -107,8 +105,8 @@ pub struct Ratsnest { } impl Ratsnest { - pub fn new(layout: &Layout) -> Result { - let conncomps = Conncomps::new(layout); + pub fn new(board: &Board) -> Result { + let conncomps = ConncompsWithPrincipalLayer::new(board, 0); let mut this = Self { graph: StableUnGraph::default(), @@ -116,16 +114,16 @@ impl Ratsnest { let mut triangulations = BTreeMap::new(); - for layer in 0..layout.drawing().layer_count() { + for layer in 0..board.layout().drawing().layer_count() { let mut handle_ratvertex_weight = |maybe_net: Option, vertex: RatvertexIndex, pos: Point| { if let Some(net) = maybe_net { triangulations - .entry((layer, net)) + .entry(net) .or_insert_with(|| { Triangulation::new(RatvertexToHandleMap::new( - layout.drawing().geometry().dot_index_bound(), - layout.drawing().geometry().compound_index_bound(), + board.layout().drawing().geometry().dot_index_bound(), + board.layout().drawing().geometry().compound_index_bound(), )) }) .add_vertex(RatvertexWeight { vertex, pos })?; @@ -133,30 +131,36 @@ impl Ratsnest { Ok(()) }; - for node in layout.drawing().layer_primitive_nodes(layer) { + for node in board.layout().drawing().layer_primitive_nodes(layer) { if let PrimitiveIndex::FixedDot(dot) = node { // Dots that are parts of polys are ignored because ratlines // should only go to their centerpoints. - if layout.drawing().compounds(dot).next().is_none() { + if board.layout().drawing().compounds(dot).next().is_none() { handle_ratvertex_weight( - layout.drawing().primitive(dot).maybe_net(), + board.layout().drawing().primitive(dot).maybe_net(), RatvertexIndex::FixedDot(dot), - node.primitive_ref(layout.drawing()).shape().center(), + node.primitive_ref(board.layout().drawing()) + .shape() + .center(), )?; } } } - for poly in layout.layer_poly_nodes(layer) { + for poly in board.layout().layer_poly_nodes(layer) { handle_ratvertex_weight( - layout.drawing().compound_weight(poly.into()).maybe_net(), + board + .layout() + .drawing() + .compound_weight(poly.into()) + .maybe_net(), RatvertexIndex::Poly(poly), - poly.ref_(layout).shape().center(), + poly.ref_(board.layout()).shape().center(), )?; } } - for ((_layer, _net), triangulation) in triangulations { + for (_net, triangulation) in triangulations { let mut map = Vec::new(); for element in petgraph::algo::min_spanning_tree(&triangulation) { diff --git a/src/bimapset.rs b/src/bimapset.rs index 3d25c1c..3de9dfb 100644 --- a/src/bimapset.rs +++ b/src/bimapset.rs @@ -97,4 +97,8 @@ impl BiBTreeMapSet { None } } + + pub fn keys(&self) -> impl Iterator + '_ { + self.key_to_values.keys() + } } diff --git a/src/board/mod.rs b/src/board/mod.rs index c3dc11a..77d17f1 100644 --- a/src/board/mod.rs +++ b/src/board/mod.rs @@ -180,6 +180,11 @@ impl Board { poly } + /// Returns an iterator over all the pin names. + pub fn pinnames(&self) -> impl Iterator + '_ { + self.pinname_nodes.keys() + } + /// Returns an iterator over the set of all nodes associated with a given /// pin name. pub fn pinname_nodes(&self, pinname: &str) -> impl Iterator + '_ { @@ -215,7 +220,7 @@ impl Board { self.band_bandname.get_by_right(bandname) } - /// Creates band between the two nodes + /// Registers that a band is between the two nodes. pub fn try_set_band_between_nodes( &mut self, recorder: &mut BoardDataEdit, diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 661fd6c..2b778df 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -6,7 +6,7 @@ use std::{fs::File, io::BufReader}; use topola::{ autorouter::{ - conncomps::Conncomps, + conncomps::ConncompsWithPrincipalLayer, history::{History, HistoryError}, invoker::{Invoker, InvokerError}, Autorouter, @@ -174,7 +174,7 @@ pub fn assert_that_all_single_layer_groundless_ratlines_are_autorouted( autorouter: &mut Autorouter, layername: &str, ) { - let conncomps = Conncomps::new(autorouter.board().layout()); + let conncomps = ConncompsWithPrincipalLayer::new(autorouter.board().layout()); for ratline in autorouter.ratsnest().graph().edge_indices() { let (origin_dot, destination_dot) = ratline.ref_(autorouter).endpoint_dots();