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/measure_length",
"autorouter/multilayer_autoroute",
"autorouter/planar_reconfigurator",
"autorouter/planar_reconfigurer",
"autorouter/multilayer_reconfigurator",
"autorouter/multilayer_reconfigurer",
"autorouter/place_via",
"autorouter/planar_autoroute",
"autorouter/planar_reconfigurator",
"autorouter/planar_reconfigurer",
"autorouter/planner",
"autorouter/pointroute",
"autorouter/presorter",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,7 +11,6 @@ use crate::{
autorouter::{
anterouter::{Anterouter, AnterouterOptions, AnterouterPlan},
invoker::GetDebugOverlayData,
planar_autoroute::PlanarAutorouteContinueStatus,
planar_reconfigurator::{PlanarAutorouteReconfigurator, PlanarReconfiguratorStatus},
ratline::RatlineUid,
Autorouter, AutorouterError, PlanarAutorouteOptions,
@ -20,7 +19,7 @@ use crate::{
drawing::graph::PrimitiveIndex,
geometry::{edit::Edit, primitive::PrimitiveShape},
router::{navcord::Navcord, navmesh::Navmesh, thetastar::ThetastarStepper},
stepper::{Abort, EstimateProgress, ReconfiguratorStatus, Step},
stepper::{Abort, EstimateProgress, ReconfiguratorStatus, Reconfigure, Step},
};
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
@ -32,8 +31,7 @@ pub struct MultilayerAutorouteOptions {
pub struct MultilayerAutorouteExecutionStepper {
planar: PlanarAutorouteReconfigurator,
anteroute_edit: BoardEdit,
// TODO: Obviously, we need something more sophisticated here.
planar_autoroute_reconfiguration_count: u64,
options: MultilayerAutorouteOptions,
}
impl MultilayerAutorouteExecutionStepper {
@ -43,14 +41,14 @@ impl MultilayerAutorouteExecutionStepper {
plan: AnterouterPlan,
options: MultilayerAutorouteOptions,
) -> Result<Self, AutorouterError> {
let mut assigner = Anterouter::new(plan);
let mut anterouter = Anterouter::new(plan);
let mut anteroute_edit = BoardEdit::new();
assigner.anteroute(autorouter, &mut anteroute_edit, &options.anterouter);
anterouter.anteroute(autorouter, &mut anteroute_edit, &options.anterouter);
Ok(Self {
planar: PlanarAutorouteReconfigurator::new(autorouter, ratlines, options.planar)?,
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.
Ok(ControlFlow::Break(Some(self.anteroute_edit.clone())))
}
Ok(ControlFlow::Continue(ReconfiguratorStatus::Reconfigured(result))) => {
self.planar_autoroute_reconfiguration_count += 1;
Ok(ControlFlow::Continue(ReconfiguratorStatus::Reconfigured(
result,
)))
}
Ok(ControlFlow::Continue(ReconfiguratorStatus::Reconfigured(result))) => Ok(
ControlFlow::Continue(ReconfiguratorStatus::Reconfigured(result)),
),
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 {
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,
};
#[derive(Clone, Debug)]
pub struct PlanarAutorouteConfiguration {
pub ratlines: Vec<RatlineUid>,
}
#[derive(Clone, Debug)]
pub struct PlanarAutorouteCosts {
pub lengths: Vec<f64>,
}
#[derive(Clone, Debug)]
pub struct PlanarAutorouteConfigurationResult {
pub configuration: PlanarAutorouteConfiguration,
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 {
/// The autoroute is currently running and in progress.
Running,

View File

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

View File

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