feat(autorouter/autorouter): Impl. rudimentary multilayer reconfigurator (not yet functional)

This commit is contained in:
Mikolaj Wielgus 2025-10-21 18:00:01 +02:00
parent 0985e0a427
commit b7fd6a0cab
12 changed files with 266 additions and 34 deletions

View File

@ -28,10 +28,12 @@ allowed_scopes = [
"autorouter/invoker", "autorouter/invoker",
"autorouter/measure_length", "autorouter/measure_length",
"autorouter/multilayer_autoroute", "autorouter/multilayer_autoroute",
"autorouter/planar_reconfigurator", "autorouter/multilayer_reconfigurator",
"autorouter/planar_reconfigurer", "autorouter/multilayer_reconfigurer",
"autorouter/place_via", "autorouter/place_via",
"autorouter/planar_autoroute", "autorouter/planar_autoroute",
"autorouter/planar_reconfigurator",
"autorouter/planar_reconfigurer",
"autorouter/planner", "autorouter/planner",
"autorouter/pointroute", "autorouter/pointroute",
"autorouter/presorter", "autorouter/presorter",

View File

@ -105,7 +105,6 @@ impl SpecctraMesadata {
.enumerate() .enumerate()
.map(|(index, layer)| (index, layer.name.clone())), .map(|(index, layer)| (index, layer.name.clone())),
); );
let layer_count = pcb.structure.layers.len();
// assign IDs to all nets named in pcb.network // assign IDs to all nets named in pcb.network
let net_netname = { let net_netname = {

View File

@ -12,10 +12,9 @@ use thiserror::Error;
use crate::{ use crate::{
autorouter::{ autorouter::{
multilayer_autoroute::{MultilayerAutorouteExecutionStepper, MultilayerAutorouteOptions}, multilayer_autoroute::MultilayerAutorouteOptions,
planar_reconfigurator::PlanarAutorouteReconfigurator, multilayer_reconfigurator::MultilayerAutorouteReconfigurator,
planner::Planner, planar_reconfigurator::PlanarAutorouteReconfigurator, ratsnests::Ratsnests,
ratsnests::Ratsnests,
}, },
board::{AccessMesadata, Board}, board::{AccessMesadata, Board},
drawing::{band::BandTermsegIndex, graph::MakePrimitiveRef}, drawing::{band::BandTermsegIndex, graph::MakePrimitiveRef},
@ -119,16 +118,10 @@ impl<M: AccessMesadata> Autorouter<M> {
&mut self, &mut self,
selection: &PinSelection, selection: &PinSelection,
options: MultilayerAutorouteOptions, options: MultilayerAutorouteOptions,
) -> Result<MultilayerAutorouteExecutionStepper, AutorouterError> { ) -> Result<MultilayerAutorouteReconfigurator, AutorouterError> {
let planner = Planner::new( MultilayerAutorouteReconfigurator::new(
self,
&self.selected_ratlines(selection, options.planar.principal_layer),
);
MultilayerAutorouteExecutionStepper::new(
self, self,
self.selected_ratlines(selection, options.planar.principal_layer), self.selected_ratlines(selection, options.planar.principal_layer),
planner.plan().clone(),
options, options,
) )
} }

View File

@ -9,7 +9,8 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
autorouter::{ autorouter::{
multilayer_autoroute::{MultilayerAutorouteExecutionStepper, MultilayerAutorouteOptions}, multilayer_autoroute::MultilayerAutorouteOptions,
multilayer_reconfigurator::MultilayerAutorouteReconfigurator,
planar_reconfigurator::PlanarAutorouteReconfigurator, planar_reconfigurator::PlanarAutorouteReconfigurator,
}, },
board::{edit::BoardEdit, AccessMesadata}, board::{edit::BoardEdit, AccessMesadata},
@ -49,7 +50,7 @@ pub enum Command {
#[enum_dispatch(GetDebugOverlayData)] #[enum_dispatch(GetDebugOverlayData)]
pub enum ExecutionStepper<M> { pub enum ExecutionStepper<M> {
MultilayerAutoroute(MultilayerAutorouteExecutionStepper), MultilayerAutoroute(MultilayerAutorouteReconfigurator),
PlanarAutoroute(PlanarAutorouteReconfigurator), PlanarAutoroute(PlanarAutorouteReconfigurator),
TopoAutoroute(ng::AutorouteExecutionStepper<M>), TopoAutoroute(ng::AutorouteExecutionStepper<M>),
PlaceVia(PlaceViaExecutionStepper), PlaceVia(PlaceViaExecutionStepper),

View File

@ -32,7 +32,7 @@ use super::{
execution::{Command, ExecutionStepper}, execution::{Command, ExecutionStepper},
history::{History, HistoryError}, history::{History, HistoryError},
measure_length::MeasureLengthExecutionStepper, measure_length::MeasureLengthExecutionStepper,
multilayer_autoroute::MultilayerAutorouteExecutionStepper, multilayer_reconfigurator::MultilayerAutorouteReconfigurator,
place_via::PlaceViaExecutionStepper, place_via::PlaceViaExecutionStepper,
planar_reconfigurator::PlanarAutorouteReconfigurator, planar_reconfigurator::PlanarAutorouteReconfigurator,
remove_bands::RemoveBandsExecutionStepper, remove_bands::RemoveBandsExecutionStepper,

View File

@ -12,6 +12,8 @@ pub mod history;
pub mod invoker; pub mod invoker;
pub mod measure_length; pub mod measure_length;
pub mod multilayer_autoroute; pub mod multilayer_autoroute;
pub mod multilayer_reconfigurator;
pub mod multilayer_reconfigurer;
pub mod place_via; pub mod place_via;
pub mod planar_autoroute; pub mod planar_autoroute;
pub mod planar_reconfigurator; pub mod planar_reconfigurator;

View File

@ -11,7 +11,6 @@ use crate::{
autorouter::{ autorouter::{
anterouter::{Anterouter, AnterouterOptions, AnterouterPlan}, anterouter::{Anterouter, AnterouterOptions, AnterouterPlan},
invoker::GetDebugOverlayData, invoker::GetDebugOverlayData,
planar_autoroute::PlanarAutorouteContinueStatus,
planar_reconfigurator::{PlanarAutorouteReconfigurator, PlanarReconfiguratorStatus}, planar_reconfigurator::{PlanarAutorouteReconfigurator, PlanarReconfiguratorStatus},
ratline::RatlineUid, ratline::RatlineUid,
Autorouter, AutorouterError, PlanarAutorouteOptions, Autorouter, AutorouterError, PlanarAutorouteOptions,
@ -20,7 +19,7 @@ use crate::{
drawing::graph::PrimitiveIndex, drawing::graph::PrimitiveIndex,
geometry::{edit::Edit, primitive::PrimitiveShape}, geometry::{edit::Edit, primitive::PrimitiveShape},
router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper}, router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper},
stepper::{Abort, EstimateProgress, ReconfiguratorStatus, Step}, stepper::{Abort, EstimateProgress, ReconfiguratorStatus, Reconfigure, Step},
}; };
#[derive(Clone, Copy, Debug, Deserialize, Serialize)] #[derive(Clone, Copy, Debug, Deserialize, Serialize)]
@ -32,8 +31,7 @@ pub struct MultilayerAutorouteOptions {
pub struct MultilayerAutorouteExecutionStepper { pub struct MultilayerAutorouteExecutionStepper {
planar: PlanarAutorouteReconfigurator, planar: PlanarAutorouteReconfigurator,
anteroute_edit: BoardEdit, anteroute_edit: BoardEdit,
// TODO: Obviously, we need something more sophisticated here. options: MultilayerAutorouteOptions,
planar_autoroute_reconfiguration_count: u64,
} }
impl MultilayerAutorouteExecutionStepper { impl MultilayerAutorouteExecutionStepper {
@ -43,14 +41,14 @@ impl MultilayerAutorouteExecutionStepper {
plan: AnterouterPlan, plan: AnterouterPlan,
options: MultilayerAutorouteOptions, options: MultilayerAutorouteOptions,
) -> Result<Self, AutorouterError> { ) -> Result<Self, AutorouterError> {
let mut assigner = Anterouter::new(plan); let mut anterouter = Anterouter::new(plan);
let mut anteroute_edit = BoardEdit::new(); let mut anteroute_edit = BoardEdit::new();
assigner.anteroute(autorouter, &mut anteroute_edit, &options.anterouter); anterouter.anteroute(autorouter, &mut anteroute_edit, &options.anterouter);
Ok(Self { Ok(Self {
planar: PlanarAutorouteReconfigurator::new(autorouter, ratlines, options.planar)?, planar: PlanarAutorouteReconfigurator::new(autorouter, ratlines, options.planar)?,
anteroute_edit, anteroute_edit,
planar_autoroute_reconfiguration_count: 0, options: options.clone(),
}) })
} }
} }
@ -70,12 +68,9 @@ impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, PlanarReconfigura
// FIXME: Unnecessary large clone. // FIXME: Unnecessary large clone.
Ok(ControlFlow::Break(Some(self.anteroute_edit.clone()))) Ok(ControlFlow::Break(Some(self.anteroute_edit.clone())))
} }
Ok(ControlFlow::Continue(ReconfiguratorStatus::Reconfigured(result))) => { Ok(ControlFlow::Continue(ReconfiguratorStatus::Reconfigured(result))) => Ok(
self.planar_autoroute_reconfiguration_count += 1; ControlFlow::Continue(ReconfiguratorStatus::Reconfigured(result)),
Ok(ControlFlow::Continue(ReconfiguratorStatus::Reconfigured( ),
result,
)))
}
x => x, x => x,
} }
} }
@ -87,6 +82,25 @@ impl<M: AccessMesadata> Abort<Autorouter<M>> for MultilayerAutorouteExecutionSte
} }
} }
impl<M: AccessMesadata> Reconfigure<Autorouter<M>> for MultilayerAutorouteExecutionStepper {
type Configuration = AnterouterPlan;
type Output = Result<(), AutorouterError>;
fn reconfigure(
&mut self,
autorouter: &mut Autorouter<M>,
plan: AnterouterPlan,
) -> Result<(), AutorouterError> {
self.planar.abort(autorouter);
autorouter.board.apply_edit(&self.anteroute_edit.reverse());
let mut anterouter = Anterouter::new(plan);
let mut anteroute_edit = BoardEdit::new();
anterouter.anteroute(autorouter, &mut anteroute_edit, &self.options.anterouter);
Ok(())
}
}
impl EstimateProgress for MultilayerAutorouteExecutionStepper { impl EstimateProgress for MultilayerAutorouteExecutionStepper {
type Value = f64; type Value = f64;

View File

@ -0,0 +1,151 @@
// SPDX-FileCopyrightText: 2025 Topola contributors
//
// SPDX-License-Identifier: MIT
use std::ops::ControlFlow;
use specctra_core::mesadata::AccessMesadata;
use crate::{
autorouter::{
invoker::GetDebugOverlayData,
multilayer_autoroute::{MultilayerAutorouteExecutionStepper, MultilayerAutorouteOptions},
multilayer_reconfigurer::MultilayerReconfigurer,
planar_reconfigurator::PlanarReconfiguratorStatus,
planner::Planner,
ratline::RatlineUid,
Autorouter, AutorouterError,
},
board::edit::BoardEdit,
drawing::graph::PrimitiveIndex,
geometry::primitive::PrimitiveShape,
router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper},
stepper::{Abort, EstimateProgress, ReconfiguratorStatus, Reconfigure, Step},
};
pub type MultilayerReconfiguratorStatus = ReconfiguratorStatus<(), PlanarReconfiguratorStatus>;
pub struct MultilayerAutorouteReconfigurator {
stepper: MultilayerAutorouteExecutionStepper,
reconfigurer: MultilayerReconfigurer,
options: MultilayerAutorouteOptions,
// TODO: Obviously, we need something more sophisticated here.
planar_autoroute_reconfiguration_count: u64,
}
impl MultilayerAutorouteReconfigurator {
pub fn new(
autorouter: &mut Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineUid>,
options: MultilayerAutorouteOptions,
) -> Result<Self, AutorouterError> {
let planner = Planner::new(autorouter, &ratlines);
let reconfigurer = MultilayerReconfigurer::new(autorouter, ratlines.clone(), &options);
Ok(Self {
stepper: MultilayerAutorouteExecutionStepper::new(
autorouter,
ratlines,
planner.plan().clone(),
options,
)?,
reconfigurer,
options,
planar_autoroute_reconfiguration_count: 0,
})
}
fn reconfigure<M: AccessMesadata>(
&mut self,
autorouter: &mut Autorouter<M>,
) -> Result<ControlFlow<Option<BoardEdit>, MultilayerReconfiguratorStatus>, AutorouterError>
{
loop {
let Some(plan) = self.reconfigurer.next_configuration(autorouter) else {
return Ok(ControlFlow::Break(None));
};
match self.stepper.reconfigure(autorouter, plan) {
Ok(_) => {
return Ok(ControlFlow::Continue(
ReconfiguratorStatus::Reconfigured(()),
))
}
Err(AutorouterError::NothingToUndoForReconfiguration) => continue,
Err(err) => return Err(err),
}
}
}
}
impl<M: AccessMesadata> Step<Autorouter<M>, Option<BoardEdit>, MultilayerReconfiguratorStatus>
for MultilayerAutorouteReconfigurator
{
type Error = AutorouterError;
fn step(
&mut self,
autorouter: &mut Autorouter<M>,
) -> Result<ControlFlow<Option<BoardEdit>, MultilayerReconfiguratorStatus>, AutorouterError>
{
match self.stepper.step(autorouter) {
Ok(ControlFlow::Break(maybe_edit)) => Ok(ControlFlow::Break(maybe_edit)),
Ok(ControlFlow::Continue(ReconfiguratorStatus::Running(status))) => {
Ok(ControlFlow::Continue(ReconfiguratorStatus::Running(
ReconfiguratorStatus::Running(status),
)))
}
Ok(ControlFlow::Continue(ReconfiguratorStatus::Reconfigured(status))) => {
self.planar_autoroute_reconfiguration_count += 1;
if self.planar_autoroute_reconfiguration_count >= 100 {
self.planar_autoroute_reconfiguration_count = 0;
self.reconfigure(autorouter)
} else {
Ok(ControlFlow::Continue(ReconfiguratorStatus::Running(
ReconfiguratorStatus::Reconfigured(status),
)))
}
}
Err(_) => self.reconfigure(autorouter),
}
}
}
impl<M: AccessMesadata> Abort<Autorouter<M>> for MultilayerAutorouteReconfigurator {
fn abort(&mut self, autorouter: &mut Autorouter<M>) {
self.stepper.abort(autorouter)
}
}
impl EstimateProgress for MultilayerAutorouteReconfigurator {
type Value = f64;
fn estimate_progress_value(&self) -> f64 {
// TODO.
self.stepper.estimate_progress_value()
}
fn estimate_progress_maximum(&self) -> f64 {
// TODO.
self.stepper.estimate_progress_maximum()
}
}
impl GetDebugOverlayData for MultilayerAutorouteReconfigurator {
fn maybe_thetastar(&self) -> Option<&ThetastarStepper<Navmesh, f64>> {
self.stepper.maybe_thetastar()
}
fn maybe_navcord(&self) -> Option<&Navcord> {
self.stepper.maybe_navcord()
}
fn ghosts(&self) -> &[PrimitiveShape] {
self.stepper.ghosts()
}
fn obstacles(&self) -> &[PrimitiveIndex] {
self.stepper.obstacles()
}
}

View File

@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: 2025 Topola contributors
//
// SPDX-License-Identifier: MIT
use std::time::SystemTime;
use specctra_core::mesadata::AccessMesadata;
use crate::autorouter::{
anterouter::AnterouterPlan, multilayer_autoroute::MultilayerAutorouteOptions, planner::Planner,
ratline::RatlineUid, Autorouter,
};
pub struct MultilayerReconfigurer {
original_ratlines: Vec<RatlineUid>,
}
impl MultilayerReconfigurer {
pub fn new(
autorouter: &Autorouter<impl AccessMesadata>,
ratlines: Vec<RatlineUid>,
options: &MultilayerAutorouteOptions,
) -> Self {
Self {
original_ratlines: ratlines,
}
}
pub fn next_configuration(
&mut self,
autorouter: &Autorouter<impl AccessMesadata>,
) -> Option<AnterouterPlan> {
let planner = Planner::new_from_layer_map(
autorouter,
&self.original_ratlines,
self.original_ratlines
.iter()
.enumerate()
.map(|(_, ratline)| (*ratline, Self::crude_random_bit()))
.collect(),
);
Some(planner.plan().clone())
}
fn crude_random_bit() -> usize {
let duration_since_epoch = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
let timestamp_nanos = duration_since_epoch.as_nanos();
(timestamp_nanos % 2) as usize
}
}

View File

@ -29,20 +29,24 @@ use super::{
PlanarAutorouteOptions, PlanarAutorouteOptions,
}; };
#[derive(Clone, Debug)]
pub struct PlanarAutorouteConfiguration { pub struct PlanarAutorouteConfiguration {
pub ratlines: Vec<RatlineUid>, pub ratlines: Vec<RatlineUid>,
} }
#[derive(Clone, Debug)]
pub struct PlanarAutorouteCosts { pub struct PlanarAutorouteCosts {
pub lengths: Vec<f64>, pub lengths: Vec<f64>,
} }
#[derive(Clone, Debug)]
pub struct PlanarAutorouteConfigurationResult { pub struct PlanarAutorouteConfigurationResult {
pub configuration: PlanarAutorouteConfiguration, pub configuration: PlanarAutorouteConfiguration,
pub costs: PlanarAutorouteCosts, pub costs: PlanarAutorouteCosts,
} }
/// Represents the current status of the autoroute operation. /// The current status of the autoroute operation.
#[derive(Clone, Copy, Debug)]
pub enum PlanarAutorouteContinueStatus { pub enum PlanarAutorouteContinueStatus {
/// The autoroute is currently running and in progress. /// The autoroute is currently running and in progress.
Running, Running,

View File

@ -28,12 +28,24 @@ pub struct Planner {
impl Planner { impl Planner {
pub fn new(autorouter: &Autorouter<impl AccessMesadata>, ratlines: &[RatlineUid]) -> Self { pub fn new(autorouter: &Autorouter<impl AccessMesadata>, ratlines: &[RatlineUid]) -> Self {
let mut plan = AnterouterPlan { Self::new_from_layer_map(
layer_map: ratlines autorouter,
ratlines,
ratlines
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, ratline)| (*ratline, i % 2)) .map(|(i, ratline)| (*ratline, i % 2))
.collect(), .collect(),
)
}
pub fn new_from_layer_map(
autorouter: &Autorouter<impl AccessMesadata>,
ratlines: &[RatlineUid],
layer_map: BTreeMap<RatlineUid, usize>,
) -> Self {
let mut plan = AnterouterPlan {
layer_map,
ratline_terminating_schemes: BTreeMap::new(), ratline_terminating_schemes: BTreeMap::new(),
}; };

View File

@ -65,6 +65,7 @@ pub trait Reconfigure<Ctx> {
) -> Self::Output; ) -> Self::Output;
} }
#[derive(Clone, Copy, Debug)]
pub enum ReconfiguratorStatus<Re, Ru> { pub enum ReconfiguratorStatus<Re, Ru> {
Running(Ru), Running(Ru),
Reconfigured(Re), Reconfigured(Re),