Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ num-derive = "0.5"
num-traits = "0.2"
paste = "1"
pathfinding = "4"
indexmap = "2"
rustc-hash = "2"
proc-macro-crate = "3"
proc-macro2 = "1"
psfparser = { git = "https://github.com/ucb-substrate/substrate2", branch = "main" }
Expand Down
2 changes: 2 additions & 0 deletions substrate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ regex = { workspace = true }
grid = { workspace = true }
state = { workspace = true }
pathfinding = { workspace = true }
indexmap = { workspace = true }
rustc-hash = { workspace = true }
itertools = { workspace = true }
serde_json = { workspace = true }
anyhow = { workspace = true }
Expand Down
59 changes: 56 additions & 3 deletions substrate/src/layout/routing/auto/abs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
use std::collections::{HashMap, HashSet};

use grid::Grid;
use indexmap::IndexSet;
use itertools::Itertools;
use rustc_hash::FxBuildHasher;
use subgeom::Dir;

use super::error::*;
Expand Down Expand Up @@ -334,7 +336,11 @@ struct AbstractNetInfo {
curr_net: Net,
curr_group: ConnectionGroup,
assigned_nets: HashSet<Net>,
conn_groups: HashMap<ConnectionGroup, HashSet<Pos>>,
/// Insertion-ordered so that `pos_in_group` lists positions in the same order on
/// every run and platform: the router's search takes them as jump successors, and
/// with a randomly seeded hash set the path it picked among equal-length routes
/// varied from process to process.
conn_groups: HashMap<ConnectionGroup, IndexSet<Pos, FxBuildHasher>>,
}

impl AbstractNetInfo {
Expand Down Expand Up @@ -363,13 +369,16 @@ impl AbstractNetInfo {
self.curr_group.0 += 1;
}
}
self.conn_groups.insert(self.curr_group, HashSet::new());
self.conn_groups
.insert(self.curr_group, IndexSet::default());
self.curr_group
}

fn delete_from_group(&mut self, pos: Pos, conn_group: ConnectionGroup) {
if let Some(v) = self.conn_groups.get_mut(&conn_group) {
v.remove(&pos);
// Moves the last position into the gap: O(1), and the resulting order is
// still a function of the operation sequence alone.
v.swap_remove(&pos);
}
}

Expand Down Expand Up @@ -831,4 +840,48 @@ mod tests {
)
.expect("failed to route");
}

/// A route that joins an existing route of its own net may start from any point of
/// that route, and here five of them are equally close to the destination. The
/// choice must not depend on hash iteration order, which differs between router
/// instances and between processes.
#[test]
fn test_jump_successor_choice_is_deterministic() {
let route_once = || {
let mut router = GreedyAbstractRouter::new(
vec![
AbstractLayerConfig {
grid_space: 1,
dir: Dir::Horiz,
},
AbstractLayerConfig {
grid_space: 1,
dir: Dir::Vert,
},
],
20,
20,
);
let net = router.get_unused_net();
router
.route_with_net(
Pos::new(Layer(0), 0, 10).into(),
Pos::new(Layer(0), 19, 10).into(),
net,
)
.expect("failed to route the trunk");
// Reachable in the same number of steps from (3..=7, 10) on the trunk.
let dst = PosSpanBuilder::with_layer(Layer(0))
.with(Dir::Horiz, 3, 7)
.with(Dir::Vert, 15, 15)
.build();
router
.route_with_net(Pos::new(Layer(0), 0, 10).into(), dst, net)
.expect("failed to route the branch")
};
let first = route_once();
for _ in 0..32 {
assert_eq!(route_once(), first);
}
}
}