feat(autorouter/ratsnest): Have ratlines across layers

This commit is contained in:
Mikolaj Wielgus 2025-09-23 13:25:32 +02:00
parent d74a06b2ea
commit a4a000feb9
6 changed files with 163 additions and 52 deletions

View File

@ -73,7 +73,7 @@ pub struct Autorouter<M> {
impl<M: AccessMesadata> Autorouter<M> { impl<M: AccessMesadata> Autorouter<M> {
pub fn new(board: Board<M>) -> Result<Self, InsertionError> { pub fn new(board: Board<M>) -> Result<Self, InsertionError> {
let ratsnest = Ratsnest::new(board.layout())?; let ratsnest = Ratsnest::new(&board)?;
Ok(Self { board, ratsnest }) Ok(Self { board, ratsnest })
} }

View File

@ -2,51 +2,149 @@
// //
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
use std::collections::BTreeSet;
use derive_getters::Getters; use derive_getters::Getters;
use petgraph::unionfind::UnionFind; use petgraph::unionfind::UnionFind;
use specctra_core::rules::AccessRules; use specctra_core::mesadata::AccessMesadata;
use crate::{ use crate::{
drawing::{graph::PrimitiveIndex, primitive::GetJoints}, board::Board,
drawing::{dot::FixedDotIndex, graph::PrimitiveIndex, primitive::GetJoints},
geometry::GenericNode,
graph::GetIndex, graph::GetIndex,
layout::Layout,
}; };
#[derive(Clone, Getters)] #[derive(Clone, Getters)]
pub struct Conncomps { pub struct ConncompsWithPrincipalLayer {
unionfind: UnionFind<usize>, unionfind: UnionFind<usize>,
} }
impl Conncomps { impl ConncompsWithPrincipalLayer {
pub fn new(layout: &Layout<impl AccessRules>) -> Self { pub fn new(board: &Board<impl AccessMesadata>, principal_layer: usize) -> Self {
let mut unionfind = UnionFind::new(layout.drawing().geometry().dot_index_bound()); 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() { for node in board
match primitive { .layout()
PrimitiveIndex::FixedSeg(seg) => { .drawing()
let joints = layout.drawing().primitive(seg).joints(); .layer_primitive_nodes(principal_layer)
unionfind.union(joints.0.index(), joints.1.index()); {
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 } Self { unionfind }
} }
fn unionize_primitive_endpoint_dots(
board: &Board<impl AccessMesadata>,
unionfind: &mut UnionFind<usize>,
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<impl AccessMesadata>,
unionfind: &mut UnionFind<usize>,
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());
}
_ => (),
}
}
} }

View File

@ -11,22 +11,20 @@ use enum_dispatch::enum_dispatch;
use geo::Point; use geo::Point;
use petgraph::{data::Element, prelude::StableUnGraph}; use petgraph::{data::Element, prelude::StableUnGraph};
use spade::{handles::FixedVertexHandle, HasPosition, InsertionError, Point2}; use spade::{handles::FixedVertexHandle, HasPosition, InsertionError, Point2};
use specctra_core::mesadata::AccessMesadata;
use crate::{ use crate::{
autorouter::conncomps::Conncomps, autorouter::conncomps::ConncompsWithPrincipalLayer,
board::Board,
drawing::{ drawing::{
band::BandTermsegIndex, band::BandTermsegIndex,
dot::FixedDotIndex, dot::FixedDotIndex,
graph::{GetMaybeNet, MakePrimitiveRef, PrimitiveIndex}, graph::{GetMaybeNet, MakePrimitiveRef, PrimitiveIndex},
primitive::MakePrimitiveShape, primitive::MakePrimitiveShape,
rules::AccessRules,
}, },
geometry::shape::AccessShape, geometry::shape::AccessShape,
graph::{GenericIndex, GetIndex, MakeRef}, graph::{GenericIndex, GetIndex, MakeRef},
layout::{ layout::poly::{MakePolygon, PolyWeight},
poly::{MakePolygon, PolyWeight},
Layout,
},
triangulation::{GetTrianvertexNodeIndex, Triangulation}, triangulation::{GetTrianvertexNodeIndex, Triangulation},
}; };
@ -107,8 +105,8 @@ pub struct Ratsnest {
} }
impl Ratsnest { impl Ratsnest {
pub fn new(layout: &Layout<impl AccessRules>) -> Result<Self, InsertionError> { pub fn new(board: &Board<impl AccessMesadata>) -> Result<Self, InsertionError> {
let conncomps = Conncomps::new(layout); let conncomps = ConncompsWithPrincipalLayer::new(board, 0);
let mut this = Self { let mut this = Self {
graph: StableUnGraph::default(), graph: StableUnGraph::default(),
@ -116,16 +114,16 @@ impl Ratsnest {
let mut triangulations = BTreeMap::new(); 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 = let mut handle_ratvertex_weight =
|maybe_net: Option<usize>, vertex: RatvertexIndex, pos: Point| { |maybe_net: Option<usize>, vertex: RatvertexIndex, pos: Point| {
if let Some(net) = maybe_net { if let Some(net) = maybe_net {
triangulations triangulations
.entry((layer, net)) .entry(net)
.or_insert_with(|| { .or_insert_with(|| {
Triangulation::new(RatvertexToHandleMap::new( Triangulation::new(RatvertexToHandleMap::new(
layout.drawing().geometry().dot_index_bound(), board.layout().drawing().geometry().dot_index_bound(),
layout.drawing().geometry().compound_index_bound(), board.layout().drawing().geometry().compound_index_bound(),
)) ))
}) })
.add_vertex(RatvertexWeight { vertex, pos })?; .add_vertex(RatvertexWeight { vertex, pos })?;
@ -133,30 +131,36 @@ impl Ratsnest {
Ok(()) 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 { if let PrimitiveIndex::FixedDot(dot) = node {
// Dots that are parts of polys are ignored because ratlines // Dots that are parts of polys are ignored because ratlines
// should only go to their centerpoints. // 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( handle_ratvertex_weight(
layout.drawing().primitive(dot).maybe_net(), board.layout().drawing().primitive(dot).maybe_net(),
RatvertexIndex::FixedDot(dot), 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( handle_ratvertex_weight(
layout.drawing().compound_weight(poly.into()).maybe_net(), board
.layout()
.drawing()
.compound_weight(poly.into())
.maybe_net(),
RatvertexIndex::Poly(poly), 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(); let mut map = Vec::new();
for element in petgraph::algo::min_spanning_tree(&triangulation) { for element in petgraph::algo::min_spanning_tree(&triangulation) {

View File

@ -97,4 +97,8 @@ impl<K: Eq + Ord + Clone, V: Eq + Ord + Clone> BiBTreeMapSet<K, V> {
None None
} }
} }
pub fn keys(&self) -> impl Iterator<Item = &K> + '_ {
self.key_to_values.keys()
}
} }

View File

@ -180,6 +180,11 @@ impl<M: AccessMesadata> Board<M> {
poly poly
} }
/// Returns an iterator over all the pin names.
pub fn pinnames(&self) -> impl Iterator<Item = &String> + '_ {
self.pinname_nodes.keys()
}
/// Returns an iterator over the set of all nodes associated with a given /// Returns an iterator over the set of all nodes associated with a given
/// pin name. /// pin name.
pub fn pinname_nodes(&self, pinname: &str) -> impl Iterator<Item = NodeIndex> + '_ { pub fn pinname_nodes(&self, pinname: &str) -> impl Iterator<Item = NodeIndex> + '_ {
@ -215,7 +220,7 @@ impl<M: AccessMesadata> Board<M> {
self.band_bandname.get_by_right(bandname) 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( pub fn try_set_band_between_nodes(
&mut self, &mut self,
recorder: &mut BoardDataEdit, recorder: &mut BoardDataEdit,

View File

@ -6,7 +6,7 @@ use std::{fs::File, io::BufReader};
use topola::{ use topola::{
autorouter::{ autorouter::{
conncomps::Conncomps, conncomps::ConncompsWithPrincipalLayer,
history::{History, HistoryError}, history::{History, HistoryError},
invoker::{Invoker, InvokerError}, invoker::{Invoker, InvokerError},
Autorouter, Autorouter,
@ -174,7 +174,7 @@ pub fn assert_that_all_single_layer_groundless_ratlines_are_autorouted(
autorouter: &mut Autorouter<impl AccessMesadata>, autorouter: &mut Autorouter<impl AccessMesadata>,
layername: &str, layername: &str,
) { ) {
let conncomps = Conncomps::new(autorouter.board().layout()); let conncomps = ConncompsWithPrincipalLayer::new(autorouter.board().layout());
for ratline in autorouter.ratsnest().graph().edge_indices() { for ratline in autorouter.ratsnest().graph().edge_indices() {
let (origin_dot, destination_dot) = ratline.ref_(autorouter).endpoint_dots(); let (origin_dot, destination_dot) = ratline.ref_(autorouter).endpoint_dots();