From d78f15d7b179a74ab98bc87c6acf979fef3dcdc3 Mon Sep 17 00:00:00 2001 From: Lofty Date: Thu, 1 Feb 2024 10:51:27 +0000 Subject: [PATCH] awooter: early wip --- rust/Cargo.toml | 2 + rust/awooter/Cargo.toml | 19 + rust/awooter/src/lib.rs | 24 ++ rust/awooter/src/route.rs | 844 ++++++++++++++++++++++++++++++++++++++ rust/nextpnr/src/lib.rs | 14 +- 5 files changed, 902 insertions(+), 1 deletion(-) create mode 100644 rust/awooter/Cargo.toml create mode 100644 rust/awooter/src/lib.rs create mode 100644 rust/awooter/src/route.rs diff --git a/rust/Cargo.toml b/rust/Cargo.toml index edf09dfe..1b920623 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -3,4 +3,6 @@ members = [ "nextpnr", "example_printnets", + "awooter", ] +resolver = "2" diff --git a/rust/awooter/Cargo.toml b/rust/awooter/Cargo.toml new file mode 100644 index 00000000..c4818988 --- /dev/null +++ b/rust/awooter/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "awooter" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +name = "awooter" +path = "src/lib.rs" +crate-type = ["staticlib"] + +[dependencies] +nextpnr = { path = "../nextpnr" } +indicatif = { version = "0.17", features = ["rayon"] } +rayon = "1.6" +colored = "2" +itertools = "0.10.5" +enumflags2 = "0.7.5" diff --git a/rust/awooter/src/lib.rs b/rust/awooter/src/lib.rs new file mode 100644 index 00000000..ee0e065a --- /dev/null +++ b/rust/awooter/src/lib.rs @@ -0,0 +1,24 @@ +use nextpnr::{Context, IdString, NetInfo, Nets, PortRef}; + +mod route; + +fn make_route_arcs(arcs: &[(IdString, &NetInfo, &PortRef)]) -> Vec { + let mut v = Vec::new(); + + for (index, &(name, info, port)) in arcs.iter().enumerate() { + v.push(route::Arc::new(info.driver())) + } + + v +} + +#[no_mangle] +pub extern "C" fn rust_awooter(ctx: &mut Context) { + let nets = Nets::new(ctx); + let arcs = nets.to_arc_vec(); + let wires = ctx.wires_leaking(); + let mut router = route::Router::new(&nets, wires, 5.0, 0.5); + let mut progress = indicatif::MultiProgress::new(); + let thread = route::RouterThread::new(arcs, "main", &progress); + router.route(ctx, &nets, &mut thread); +} \ No newline at end of file diff --git a/rust/awooter/src/route.rs b/rust/awooter/src/route.rs new file mode 100644 index 00000000..50b0a83e --- /dev/null +++ b/rust/awooter/src/route.rs @@ -0,0 +1,844 @@ +use std::{ + collections::{BinaryHeap, HashMap, HashSet}, + sync::RwLock, + time::Instant, +}; + +use colored::Colorize; +use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; +use itertools::Itertools; +use nextpnr::{self, IdString, Loc, NetIndex, PipId, WireId}; + +#[derive(Clone, Hash, PartialEq, Eq)] +pub struct Arc { + source_wire: WireId, + source_loc: Option, + sink_wire: WireId, + sink_loc: Option, + net: NetIndex, + name: IdString, +} + +impl Arc { + pub fn new( + source_wire: WireId, + source_loc: Option, + sink_wire: WireId, + sink_loc: Option, + net: NetIndex, + name: IdString, + ) -> Self { + Self { + source_wire, + source_loc, + sink_wire, + sink_loc, + net, + name, + } + } + + pub fn split(&self, ctx: &nextpnr::Context, pip: nextpnr::PipId) -> (Self, Self) { + // should this still set the sink and source using the pip? not sure + let pip_src = ctx.pip_src_wire(pip); + let pip_dst = ctx.pip_dst_wire(pip); + ( + Self { + source_wire: self.source_wire, + source_loc: self.source_loc, + sink_wire: pip_src, + sink_loc: Some(ctx.pip_location(pip)), + net: self.net, + name: self.name, + }, + Self { + source_wire: pip_dst, + source_loc: Some(ctx.pip_location(pip)), + sink_wire: self.sink_wire, + sink_loc: self.sink_loc, + net: self.net, + name: self.name, + }, + ) + } + + pub fn source_wire(&self) -> nextpnr::WireId { + self.source_wire + } + pub fn sink_wire(&self) -> nextpnr::WireId { + self.sink_wire + } + pub fn net(&self) -> nextpnr::NetIndex { + self.net + } + pub fn get_source_loc(&self) -> Option { + self.source_loc + } + pub fn get_sink_loc(&self) -> Option { + self.sink_loc + } +} + +#[derive(Copy, Clone)] +struct QueuedWire { + delay: f32, + congest: f32, + togo: f32, + criticality: f32, + wire: nextpnr::WireId, + from_pip: Option, +} + +impl QueuedWire { + pub fn new( + delay: f32, + congest: f32, + togo: f32, + criticality: f32, + wire: nextpnr::WireId, + from_pip: Option, + ) -> Self { + Self { + delay, + congest, + togo, + criticality, + wire, + from_pip, + } + } + + fn score(&self) -> f32 { + (self.criticality * self.delay) + ((1.0 - self.criticality) * self.congest) + } +} + +impl PartialEq for QueuedWire { + fn eq(&self, other: &Self) -> bool { + self.delay == other.delay + && self.congest == other.congest + && self.togo == other.togo + && self.wire == other.wire + } +} + +impl Eq for QueuedWire {} + +impl Ord for QueuedWire { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + let me = self.score() + self.togo; + let other = other.score() + other.togo; + other.total_cmp(&me) + } +} + +impl PartialOrd for QueuedWire { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +struct PerNetData { + wires: HashMap, + done_sinks: HashSet, +} + +struct PerWireData { + wire: WireId, + curr_cong: u32, + hist_cong: f32, + unavailable: bool, + reserved_net: Option, + pip_fwd: PipId, + visited_fwd: bool, + pip_bwd: PipId, + visited_bwd: bool, +} + +pub struct RouterThread<'a> { + arcs: &'a [Arc], + id: &'a str, + progress: &'a MultiProgress, + dirty_wires: Vec, +} + +impl<'a> RouterThread<'a> { + pub fn new( + arcs: &'a [Arc], + id: &'a str, + progress: &'a MultiProgress, + ) -> Self { + Self { + arcs, + id, + progress, + dirty_wires: Vec::new(), + } + } +} + +pub struct Router { + pressure: f32, + history: f32, + nets: RwLock>, + wire_to_idx: HashMap, + flat_wires: Vec>, +} + +impl Router { + pub fn new(nets: &nextpnr::Nets, wires: &[nextpnr::WireId], pressure: f32, history: f32) -> Self { + let mut net_vec = Vec::new(); + let mut flat_wires = Vec::new(); + let mut wire_to_idx = HashMap::new(); + + for _ in 0..nets.len() { + net_vec.push(PerNetData { + wires: HashMap::new(), + done_sinks: HashSet::new(), + }); + } + + for (idx, &wire) in wires.iter().enumerate() { + flat_wires.push(RwLock::new(PerWireData { + wire, + curr_cong: 0, + hist_cong: 0.0, + unavailable: false, + reserved_net: None, + pip_fwd: PipId::null(), + visited_fwd: false, + pip_bwd: PipId::null(), + visited_bwd: false, + })); + wire_to_idx.insert(wire, idx as u32); + } + + Self { + pressure, + history, + nets: RwLock::new(net_vec), + wire_to_idx, + flat_wires, + } + } + + pub fn find_general_routing(&self, ctx: &nextpnr::Context, nets: &nextpnr::Nets, this: &mut RouterThread) -> Vec { + let is_general_routing = |wire: &str| { + wire.contains("H01") + || wire.contains("V01") + || wire.contains("H02") + || wire.contains("V02") + || wire.contains("H06") + || wire.contains("V06") + }; + + let mut delay = HashMap::new(); + + for arc in this.arcs { + delay.insert(arc, 1.0_f32); + } + + let start = Instant::now(); + + let mut max_delay = 1.0; + let mut least_overuse = usize::MAX; + let mut iters_since_improvement = 0; + + let mut route_arcs = Vec::from_iter(this.arcs.iter()); + + let progress = this.progress.add(ProgressBar::new(0)); + progress.set_style( + ProgressStyle::with_template("[{elapsed}] [{bar:40.green/green}] {msg:30!}") + .unwrap() + .progress_chars("━╸ "), + ); + + let mut new_arcs = Vec::new(); + let mut iterations = 0; + + loop { + iterations += 1; + + progress.set_position(0); + progress.set_length(route_arcs.len() as u64); + + for arc in route_arcs.iter().sorted_by(|&i, &j| { + (delay.get(j).unwrap() / max_delay).total_cmp(&(delay.get(i).unwrap() / max_delay)) + }) { + let name = ctx.name_of(arc.name).to_str().unwrap(); + progress.inc(1); + let criticality = (delay.get(arc).unwrap() / max_delay).min(0.99).powf(2.5) + 0.1; + progress.set_message(format!("{} @ {}: {}", this.id, iterations, name)); + *delay.get_mut(arc).unwrap() = self.route_arc(ctx, nets, arc, criticality); + } + + let mut overused = HashSet::new(); + for wd in self.flat_wires.iter() { + let mut wd = wd.write().unwrap(); + if wd.curr_cong > 1 && !is_general_routing(ctx.name_of_wire(wd.wire)) { + overused.insert(wd.wire); + wd.hist_cong += (wd.curr_cong as f32) * self.history; + } + } + + if overused.is_empty() { + break; + } else if overused.len() < least_overuse { + least_overuse = overused.len(); + iters_since_improvement = 0; + progress.println(format!( + "{} @ {}: {} wires overused {}", + this.id, + iterations, + overused.len(), + "(new best)".bold() + )); + } else { + iters_since_improvement += 1; + progress.println(format!( + "{} @ {}: {} wires overused", + this.id, + iterations, + overused.len() + )); + } + + let mut next_arcs = HashSet::new(); + for arc in this.arcs { + let nets = &*self.nets.read().unwrap(); + for wire in nets[arc.net.into_inner() as usize] + .wires + .keys() + { + if overused.contains(wire) { + next_arcs.insert(arc); + } + } + } + + for &arc in &next_arcs { + self.ripup_arc(ctx, arc); + } + + { + let nets = &mut *self.nets.write().unwrap(); + for net in nets.iter_mut() { + net.done_sinks.clear(); + } + } + + if iters_since_improvement > 50 { + iters_since_improvement = 0; + least_overuse = usize::MAX; + progress.println(format!( + "{} @ {}: {}", + this.id, + iterations, + "bored; rerouting everything".bold() + )); + route_arcs = Vec::from_iter(this.arcs.iter()); + } else { + route_arcs = Vec::from_iter(next_arcs.into_iter()); + } + + max_delay = this + .arcs + .iter() + .map(|arc| *delay.get(arc).unwrap()) + .reduce(f32::max) + .unwrap(); + } + + let now = start.elapsed().as_secs_f32(); + progress.println(format!( + "{} @ {}: {} in {:.0}m{:.03}s", + this.id, + iterations, + "pre-routing complete".green(), + (now / 60.0).floor(), + now % 60.0 + )); + + progress.finish_and_clear(); + + for arc in this.arcs { + let (source_wire, sink_wire) = self.ripup_arc_general_routing(ctx, arc); + new_arcs.push(Arc::new(source_wire, None, sink_wire, None, arc.net, arc.name)); + } + + new_arcs + } + + pub fn route(&self, ctx: &nextpnr::Context, nets: &nextpnr::Nets, this: &mut RouterThread) { + let mut delay = HashMap::new(); + + for arc in this.arcs { + delay.insert(arc, 1.0_f32); + } + + let start = Instant::now(); + + let mut max_delay = 1.0; + let mut least_overuse = usize::MAX; + let mut iters_since_improvement = 0; + + let mut route_arcs = Vec::from_iter(this.arcs.iter()); + + let progress = this.progress.add(ProgressBar::new(0)); + progress.set_style( + ProgressStyle::with_template("[{elapsed}] [{bar:40.magenta/red}] {msg:30!}") + .unwrap() + .progress_chars("━╸ "), + ); + + let mut iterations = 0; + + loop { + iterations += 1; + + progress.set_position(0); + progress.set_length(route_arcs.len() as u64); + + for arc in route_arcs.iter().sorted_by(|&i, &j| { + (delay.get(j).unwrap() / max_delay).total_cmp(&(delay.get(i).unwrap() / max_delay)) + }) { + let name = ctx.name_of(arc.name).to_str().unwrap(); + progress.inc(1); + let criticality = (delay.get(arc).unwrap() / max_delay); + progress.set_message(format!("{} @ {}: {}", this.id, iterations, name)); + *delay.get_mut(arc).unwrap() = self.route_arc(ctx, nets, arc, criticality); + } + + let mut overused = HashSet::new(); + for wd in self.flat_wires.iter() { + let mut wd = wd.write().unwrap(); + if wd.curr_cong > 1 { + overused.insert(wd.wire); + wd.hist_cong += (wd.curr_cong as f32) * self.history; + } + } + + if overused.is_empty() { + break; + } else if overused.len() < least_overuse { + least_overuse = overused.len(); + iters_since_improvement = 0; + progress.println(format!( + "{} @ {}: {} wires overused {}", + this.id, + iterations, + overused.len(), + "(new best)".bold() + )); + } else { + iters_since_improvement += 1; + progress.println(format!( + "{} @ {}: {} wires overused", + this.id, + iterations, + overused.len() + )); + } + + let mut next_arcs = HashSet::new(); + for arc in this.arcs { + let nets = &*self.nets.read().unwrap(); + for wire in nets[arc.net.into_inner() as usize] + .wires + .keys() + { + if overused.contains(wire) { + next_arcs.insert(arc); + } + } + } + + for &arc in &next_arcs { + self.ripup_arc(ctx, arc); + } + { + let nets = &mut *self.nets.write().unwrap(); + for net in nets.iter_mut() { + net.done_sinks.clear(); + } + } + + if iters_since_improvement > 50 { + iters_since_improvement = 0; + least_overuse = usize::MAX; + progress.println(format!( + "{} @ {}: {}", + this.id, + iterations, + "bored; rerouting everything".bold() + )); + route_arcs = Vec::from_iter(this.arcs.iter()); + } else { + route_arcs = Vec::from_iter(next_arcs.into_iter()); + } + + max_delay = this + .arcs + .iter() + .map(|arc| *delay.get(arc).unwrap()) + .reduce(f32::max) + .unwrap(); + } + + let now = (Instant::now() - start).as_secs_f32(); + progress.println(format!( + "{} @ {}: {} in {:.0}m{:.03}s", + this.id, + iterations, + "routing complete".green(), + now / 60.0, + now % 60.0 + )); + + progress.finish_and_clear(); + } + + fn can_visit_pip(&self, ctx: &nextpnr::Context, nets: &nextpnr::Nets, arc: &Arc, pip: PipId) -> bool { + let wire = ctx.pip_dst_wire(pip); + let sink = *self.wire_to_idx.get(&wire).unwrap(); + let nd = &self.nets.read().unwrap()[arc.net().into_inner() as usize]; + let nwd = &self.flat_wires[sink as usize].read().unwrap(); + /*let pip_coord = partition::Coord::from(ctx.pip_location(pip)); + if pip_coord.is_north_of(&self.box_ne) || pip_coord.is_east_of(&self.box_ne) { + return false; + } + if pip_coord.is_south_of(&self.box_sw) || pip_coord.is_west_of(&self.box_sw) { + return false; + }*/ + if !ctx.pip_avail_for_net(pip, nets.net_from_index(arc.net())) { + return false; + } + if nwd.unavailable { + return false; + } + if let Some(net) = nwd.reserved_net { + if net != arc.net() { + return false; + } + } + // Don't allow the same wire to be bound to the same net with a different driving pip + if let Some((found_pip, _)) = nd.wires.get(&wire) { + if *found_pip != pip { + return false; + } + } + true + } + + #[allow(clippy::too_many_arguments)] + fn step<'b, 'a: 'b, I>( + &'a self, + ctx: &'b nextpnr::Context, + nets: &nextpnr::Nets, + arc: &Arc, + criticality: f32, + queue: &mut BinaryHeap, + midpoint: &mut Option, + target: WireId, + dirty_wires: &mut Vec, + was_visited: impl Fn(&Self, u32) -> bool, + set_visited: impl Fn(&Self, u32, PipId, &mut Vec), + is_done: impl Fn(&Self, u32) -> bool, + pip_iter: impl Fn(&'b nextpnr::Context, WireId) -> I, + pip_wire: impl Fn(&nextpnr::Context, PipId) -> WireId, + ) -> bool + where + I: Iterator, + { + if let Some(source) = queue.pop() { + let source_idx = *self.wire_to_idx.get(&source.wire).unwrap(); + if was_visited(self, source_idx) { + return true; + } + if let Some(pip) = source.from_pip { + set_visited(self, source_idx, pip, dirty_wires); + } + if is_done(self, source_idx) { + *midpoint = Some(source_idx); + return false; + } + + for pip in pip_iter(ctx, source.wire) { + if !self.can_visit_pip(ctx, nets, arc, pip) { + continue; + } + + let wire = pip_wire(ctx, pip); + let sink = *self.wire_to_idx.get(&wire).unwrap(); + if was_visited(self, sink) { + continue; + } + + let nwd = &self.flat_wires[sink as usize].read().unwrap(); + let node_delay = ctx.pip_delay(pip) + ctx.wire_delay(wire) + ctx.delay_epsilon(); + let sum_delay = source.delay + node_delay; + let congest = source.congest + + (node_delay + nwd.hist_cong) * (1.0 + (nwd.curr_cong as f32 * self.pressure)); + + let qw = QueuedWire::new( + sum_delay, + congest, + ctx.estimate_delay(wire, target), + criticality, + wire, + Some(pip), + ); + + queue.push(qw); + } + + return true; + } + false + } + + fn route_arc( + &self, + ctx: &nextpnr::Context, + nets: &nextpnr::Nets, + arc: &Arc, + criticality: f32, + ) -> f32 { + if arc.source_wire == arc.sink_wire { + return 0.0; + } + + let mut fwd_queue = BinaryHeap::new(); + fwd_queue.push(QueuedWire::new( + 0.0, + 0.0, + ctx.estimate_delay(arc.source_wire, arc.sink_wire), + criticality, + arc.source_wire, + None, + )); + let mut bwd_queue = BinaryHeap::new(); + bwd_queue.push(QueuedWire::new( + 0.0, + 0.0, + ctx.estimate_delay(arc.source_wire, arc.sink_wire), + criticality, + arc.sink_wire, + None, + )); + + let mut midpoint = None; + + let source_wire = *self.wire_to_idx.get(&arc.source_wire).unwrap(); + let sink_wire = *self.wire_to_idx.get(&arc.sink_wire).unwrap(); + + let mut dirty_wires = Vec::new(); + + dirty_wires.push(source_wire); + dirty_wires.push(sink_wire); + + while midpoint.is_none() { + // Step forward + if !self.step( + ctx, + nets, + arc, + criticality, + &mut fwd_queue, + &mut midpoint, + arc.sink_wire, + &mut dirty_wires, + Self::was_visited_fwd, + Self::set_visited_fwd, + Self::was_visited_bwd, + nextpnr::Context::get_downhill_pips, + nextpnr::Context::pip_dst_wire, + ) { + break; + } + // Step backward + if !self.step( + ctx, + nets, + arc, + criticality, + &mut bwd_queue, + &mut midpoint, + arc.source_wire, + &mut dirty_wires, + Self::was_visited_bwd, + Self::set_visited_bwd, + Self::was_visited_fwd, + nextpnr::Context::get_uphill_pips, + nextpnr::Context::pip_src_wire, + ) { + break; + } + self.flat_wires[source_wire as usize] + .write() + .unwrap() + .visited_fwd = true; + self.flat_wires[sink_wire as usize] + .write() + .unwrap() + .visited_bwd = true; + } + + assert!( + midpoint.is_some(), + "didn't find sink wire for net {} between {} and {}", + ctx.name_of(arc.name).to_str().unwrap(), + ctx.name_of_wire(arc.source_wire), + ctx.name_of_wire(arc.sink_wire), + ); + + let mut wire = midpoint.unwrap(); + + let mut calculated_delay = 0.0; + + while wire != source_wire { + let (pip, wireid) = { + let nwd = self.flat_wires[wire as usize].read().unwrap(); + (nwd.pip_fwd, nwd.wire) + }; + assert!(pip != PipId::null()); + + let node_delay = ctx.pip_delay(pip) + ctx.wire_delay(wireid) + ctx.delay_epsilon(); + calculated_delay += node_delay; + + self.bind_pip_internal(arc.net(), wire, pip); + wire = *self.wire_to_idx.get(&ctx.pip_src_wire(pip)).unwrap(); + } + let mut wire = midpoint.unwrap(); + while wire != sink_wire { + let (pip, wireid) = { + let nwd = self.flat_wires[wire as usize].read().unwrap(); + (nwd.pip_bwd, nwd.wire) + }; + assert!(pip != PipId::null()); + // do note that the order is inverted from the fwd loop + wire = *self.wire_to_idx.get(&ctx.pip_dst_wire(pip)).unwrap(); + + let node_delay = ctx.pip_delay(pip) + ctx.wire_delay(wireid) + ctx.delay_epsilon(); + calculated_delay += node_delay; + + self.bind_pip_internal(arc.net(), wire, pip); + } + self.nets.write().unwrap()[arc.net().into_inner() as usize] + .done_sinks + .insert(arc.sink_wire); + + self.reset_wires(&dirty_wires); + + calculated_delay + } + + fn was_visited_fwd(&self, wire: u32) -> bool { + self.flat_wires[wire as usize].read().unwrap().visited_fwd + } + + fn was_visited_bwd(&self, wire: u32) -> bool { + self.flat_wires[wire as usize].read().unwrap().visited_bwd + } + + fn set_visited_fwd(&self, wire: u32, pip: PipId, dirty_wires: &mut Vec) { + let mut wd = self.flat_wires[wire as usize].write().unwrap(); + if !wd.visited_fwd { + dirty_wires.push(wire); + } + wd.pip_fwd = pip; + wd.visited_fwd = true; + } + + fn set_visited_bwd(&self, wire: u32, pip: PipId, dirty_wires: &mut Vec) { + let mut wd = self.flat_wires[wire as usize].write().unwrap(); + if !wd.visited_bwd { + dirty_wires.push(wire); + } + wd.pip_bwd = pip; + wd.visited_bwd = true; + } + + fn bind_pip_internal(&self, netindex: NetIndex, wire: u32, pip: PipId) { + let wireid = self.flat_wires[wire as usize].read().unwrap().wire; + let net = &mut self.nets.write().unwrap()[netindex.into_inner() as usize]; + if let Some((bound_pip, usage)) = net.wires.get_mut(&wireid) { + assert!(*bound_pip == pip); + *usage += 1; + } else { + net.wires.insert(wireid, (pip, 1)); + self.flat_wires[wire as usize].write().unwrap().curr_cong += 1; + } + } + + fn unbind_pip_internal(&self, net: NetIndex, wire: WireId) { + let net = net.into_inner() as usize; + let wireidx = *self.wire_to_idx.get(&wire).unwrap() as usize; + let nd = &mut self.nets.write().unwrap()[net]; + let (_pip, usage) = nd.wires.get_mut(&wire).unwrap(); + *usage -= 1; + if *usage == 0 { + self.flat_wires[wireidx].write().unwrap().curr_cong -= 1; + nd.wires.remove(&wire); + } + } + + fn ripup_arc(&self, ctx: &nextpnr::Context, arc: &Arc) { + let net = arc.net().into_inner() as usize; + let source_wire = arc.source_wire; + let mut wire = arc.sink_wire; + while wire != source_wire { + let pip = self.nets.read().unwrap()[net].wires.get(&wire).unwrap().0; + assert!(pip != PipId::null()); + self.unbind_pip_internal(arc.net(), wire); + wire = ctx.pip_src_wire(pip); + } + } + + fn ripup_arc_general_routing(&self, ctx: &nextpnr::Context, arc: &Arc) -> (WireId, WireId) { + let is_general_routing = |wire: WireId| { + let wire = ctx.name_of_wire(wire); + wire.contains("H01") + || wire.contains("V01") + || wire.contains("H02") + || wire.contains("V02") + || wire.contains("H06") + || wire.contains("V06") + }; + + let net = arc.net().into_inner() as usize; + let source_wire = arc.source_wire; + let mut wire = arc.sink_wire; + let mut last_was_general = false; + let mut w1 = arc.sink_wire; + let mut w2 = arc.source_wire; + while wire != source_wire { + let pip = self.nets.read().unwrap()[net].wires.get(&wire).expect("wire should have driving pip").0; + assert!(pip != PipId::null()); + if is_general_routing(wire) { + if !last_was_general { + w1 = wire; + } + self.unbind_pip_internal(arc.net(), wire); + last_was_general = true; + } else { + if last_was_general { + w2 = wire; + } + last_was_general = false; + } + wire = ctx.pip_src_wire(pip); + } + (w2, w1) + } + + fn reset_wires(&self, dirty_wires: &Vec) { + for &wire in dirty_wires { + let mut nwd = self.flat_wires[wire as usize].write().unwrap(); + nwd.pip_fwd = PipId::null(); + nwd.visited_fwd = false; + nwd.pip_bwd = PipId::null(); + nwd.visited_bwd = false; + } + } +} diff --git a/rust/nextpnr/src/lib.rs b/rust/nextpnr/src/lib.rs index 66a96ee1..0230d24d 100644 --- a/rust/nextpnr/src/lib.rs +++ b/rust/nextpnr/src/lib.rs @@ -302,7 +302,7 @@ impl Context { Loc{x: dst.x - src.x, y: dst.y - src.y, z: 0} } - pub fn pip_avail_for_net(&self, pip: PipId, net: &mut NetInfo) -> bool { + pub fn pip_avail_for_net(&self, pip: PipId, net: &NetInfo) -> bool { unsafe { npnr_context_check_pip_avail_for_net(self, pip, net) } } @@ -509,6 +509,18 @@ impl<'a> Nets<'a> { v.sort_by_key(|(name, _net)| name.0); v } + + pub fn to_arc_vec(&self) -> Vec<(IdString, &NetInfo, &PortRef)> { + self.nets.iter() + .filter_map(|(&name, net)| { + self.users.get(&name) + .map(|&users| + users.iter().map(move |&user| (name, &**net, user)) + ) + }) + .flatten() + .collect() + } } pub struct NetSinkWireIter<'a> {