From 319d1c46201f9c5d8c002672b6b643d7dc4f6407 Mon Sep 17 00:00:00 2001 From: SpaceCat-Chan <49094338+SpaceCat-Chan@users.noreply.github.com> Date: Sun, 27 Nov 2022 16:12:39 +0100 Subject: [PATCH] split partitioning code into seperate file --- common/route/awooter/rust/src/lib.rs | 529 +-------------------- common/route/awooter/rust/src/partition.rs | 529 +++++++++++++++++++++ 2 files changed, 532 insertions(+), 526 deletions(-) create mode 100644 common/route/awooter/rust/src/partition.rs diff --git a/common/route/awooter/rust/src/lib.rs b/common/route/awooter/rust/src/lib.rs index 79056d69..072909af 100644 --- a/common/route/awooter/rust/src/lib.rs +++ b/common/route/awooter/rust/src/lib.rs @@ -1,11 +1,10 @@ -use std::{collections::HashMap, ptr::NonNull, sync::atomic::AtomicUsize, time::Instant}; +use std::{ptr::NonNull, time::Instant}; use colored::Colorize; -use indicatif::{ParallelProgressIterator, ProgressBar, ProgressStyle}; -use rayon::prelude::*; #[macro_use] mod npnr; +mod partition; #[no_mangle] pub extern "C" fn npnr_router_awooter(ctx: Option>) -> bool { @@ -23,528 +22,6 @@ pub extern "C" fn npnr_router_awooter(ctx: Option>) -> bo type Arc = ((npnr::WireId, npnr::Loc), (npnr::WireId, npnr::Loc)); -fn find_partition_point( - ctx: &npnr::Context, - arcs: &[Arc], - pips: &[npnr::PipId], - x_start: i32, - x_finish: i32, - y_start: i32, - y_finish: i32, -) -> (i32, i32, Vec, Vec, Vec, Vec) { - let mut x = ((x_finish - x_start) / 2) + x_start; - let mut y = ((y_finish - y_start) / 2) + y_start; - let mut x_diff = (x_finish - x_start) / 4; - let mut y_diff = (y_finish - y_start) / 4; - - let mut ne; - let mut se; - let mut sw; - let mut nw; - - while x_diff != 0 { - (ne, se, sw, nw) = partition(ctx, arcs, pips, x, y); - let north = ne.len() + nw.len(); - let south = se.len() + sw.len(); - - let nets = (north + south) as f64; - - let ne_dist = f64::abs(((ne.len() as f64) / nets) - 0.25); - let se_dist = f64::abs(((se.len() as f64) / nets) - 0.25); - let sw_dist = f64::abs(((sw.len() as f64) / nets) - 0.25); - let nw_dist = f64::abs(((nw.len() as f64) / nets) - 0.25); - - let distortion = 100.0 * (ne_dist + se_dist + sw_dist + nw_dist); - - // Stop early if Good Enough. - if distortion <= 5.0 { - return (x, y, ne, se, sw, nw); - } - - x += match north.cmp(&south) { - std::cmp::Ordering::Less => x_diff, - std::cmp::Ordering::Equal => 0, - std::cmp::Ordering::Greater => -x_diff, - }; - - let east = ne.len() + se.len(); - let west = nw.len() + sw.len(); - y += match east.cmp(&west) { - std::cmp::Ordering::Less => y_diff, - std::cmp::Ordering::Equal => 0, - std::cmp::Ordering::Greater => -y_diff, - }; - - x_diff >>= 1; - y_diff >>= 1; - } - - (ne, se, sw, nw) = partition(ctx, arcs, pips, x, y); - - let north = ne.len() + nw.len(); - let south = se.len() + sw.len(); - let nets = (north + south) as f64; - - let ne_dist = f64::abs(((ne.len() as f64) / nets) - 0.25); - let se_dist = f64::abs(((se.len() as f64) / nets) - 0.25); - let sw_dist = f64::abs(((sw.len() as f64) / nets) - 0.25); - let nw_dist = f64::abs(((nw.len() as f64) / nets) - 0.25); - - log_info!( - "Distortion: {:.02}%\n", - 100.0 * (ne_dist + se_dist + sw_dist + nw_dist) - ); - - (x, y, ne, se, sw, nw) -} - -/// finds the y location a line would be split at if you split it at a certain x location -/// -/// the function assumes the line goes on forever in both directions, and it truncates the actual coordinate -fn split_line_over_x(line: (npnr::Loc, npnr::Loc), x_location: i32) -> i32 { - if line.0.x == line.1.x { - // the line is a straight line in the direction, there is either infinite solutions, or none - // we simply average the y coordinate to give a "best effort" guess - return (line.0.y + line.1.y) / 2; - } - - let x_diff = line.0.x - line.1.x; - let y_diff = line.0.y - line.1.y; - - // i hope for no overflows, maybe promote to i64 to be sure? - (y_diff * x_location + line.0.y * x_diff - line.0.x * y_diff) / x_diff -} - -/// finds the x location a line would be split at if you split it at a certain y location, assuming the line goes on forever in both directions -fn split_line_over_y(line: (npnr::Loc, npnr::Loc), y_location: i32) -> i32 { - // laziness supreme! - split_line_over_x( - ( - npnr::Loc { - x: line.0.y, - y: line.0.x, - z: 0, - }, - npnr::Loc { - x: line.1.y, - y: line.1.x, - z: 0, - }, - ), - y_location, - ) -} - -enum Segment { - Northeast, - Southeast, - Southwest, - Northwest, -} - -// A big thank you to @Spacecat-chan for fixing my broken and buggy partition code. -fn partition( - ctx: &npnr::Context, - arcs: &[Arc], - pips: &[npnr::PipId], - x: i32, - y: i32, -) -> (Vec, Vec, Vec, Vec) { - let mut pips_n = HashMap::new(); - let mut pips_e = HashMap::new(); - let mut pips_s = HashMap::new(); - let mut pips_w = HashMap::new(); - - let mut ne: Vec = Vec::new(); - let mut se: Vec = Vec::new(); - let mut sw: Vec = Vec::new(); - let mut nw: Vec = Vec::new(); - let mut part_horiz = AtomicUsize::new(0); - let mut part_vert = AtomicUsize::new(0); - let mut part_diag = AtomicUsize::new(0); - - let x_str = format!("X = {}", x); - let y_str = format!("Y = {}", y); - log_info!( - "Partitioning arcs along {}, {}\n", - x_str.bold(), - y_str.bold() - ); - - let mut candidates = 0; - let mut north = 0; - let mut east = 0; - let mut south = 0; - let mut west = 0; - for &pip in pips { - let loc = ctx.pip_location(pip); - if loc.x == x || loc.y == y { - let dir = ctx.pip_direction(pip); - - // This pip seems internal; skip it. - if dir.x == 0 && dir.y == 0 { - continue; - } - - candidates += 1; - - if dir.x < 0 { - north += 1; - pips_n - .entry((loc.x, loc.y)) - .and_modify(|pip_list: &mut Vec<(npnr::PipId, AtomicUsize)>| { - pip_list.push((pip, AtomicUsize::new(0))) - }) - .or_insert_with(|| vec![(pip, AtomicUsize::new(0))]); - } - - if dir.x > 0 { - south += 1; - pips_s - .entry((loc.x, loc.y)) - .and_modify(|pip_list: &mut Vec<(npnr::PipId, AtomicUsize)>| { - pip_list.push((pip, AtomicUsize::new(0))) - }) - .or_insert_with(|| vec![(pip, AtomicUsize::new(0))]); - } - - if dir.y < 0 { - east += 1; - pips_e - .entry((loc.x, loc.y)) - .and_modify(|pip_list: &mut Vec<(npnr::PipId, AtomicUsize)>| { - pip_list.push((pip, AtomicUsize::new(0))) - }) - .or_insert_with(|| vec![(pip, AtomicUsize::new(0))]); - } - - if dir.y > 0 { - west += 1; - pips_w - .entry((loc.x, loc.y)) - .and_modify(|pip_list: &mut Vec<(npnr::PipId, AtomicUsize)>| { - pip_list.push((pip, AtomicUsize::new(0))) - }) - .or_insert_with(|| vec![(pip, AtomicUsize::new(0))]); - } - } - } - - log_info!( - " Out of {} candidate pips:\n", - candidates.to_string().bold() - ); - log_info!(" {} are north-bound\n", north.to_string().bold()); - log_info!(" {} are east-bound\n", east.to_string().bold()); - log_info!(" {} are south-bound\n", south.to_string().bold()); - log_info!(" {} are west-bound\n", west.to_string().bold()); - - let progress = ProgressBar::new(arcs.len() as u64); - progress.set_style( - ProgressStyle::with_template("[{elapsed}] [{bar:40.cyan/blue}] {msg}") - .unwrap() - .progress_chars("━╸ "), - ); - - let mut explored_pips = AtomicUsize::new(0); - - let arcs = arcs - .into_par_iter() - .progress_with(progress) - .flat_map(|&((source_wire, source), (sink_wire, sink_loc))| { - let source_is_north = source.x < x; - let source_is_east = source.y < y; - let sink_is_north = sink_loc.x < x; - let sink_is_east = sink_loc.y < y; - if source_is_north == sink_is_north && source_is_east == sink_is_east { - let arc = ((source_wire, source), (sink_wire, sink_loc)); - let seg = match (source_is_north, source_is_east) { - (true, true) => Segment::Northeast, - (true, false) => Segment::Northwest, - (false, true) => Segment::Southeast, - (false, false) => Segment::Southwest, - }; - vec![(seg, arc)] - } else if source_is_north != sink_is_north && source_is_east == sink_is_east { - let middle = (x, (source.y + sink_loc.y) / 2); - let middle = ( - middle.0.clamp(1, ctx.grid_dim_x() - 1), - middle.1.clamp(1, ctx.grid_dim_y() - 1), - ); - let pips = match source_is_north { - true => pips_s.get(&middle).unwrap(), - false => pips_n.get(&middle).unwrap(), - }; - - let (selected_pip, pip_uses) = pips - .iter() - .min_by_key(|(pip, uses)| { - let src_to_pip = ctx.estimate_delay(source_wire, ctx.pip_src_wire(*pip)); - let pip_to_snk = ctx.estimate_delay(ctx.pip_dst_wire(*pip), sink_wire); - let uses = uses.load(std::sync::atomic::Ordering::Acquire); - (1000.0 * (src_to_pip + ((uses + 1) as f32) * pip_to_snk)) as u64 - }) - .unwrap(); - pip_uses.fetch_add(1, std::sync::atomic::Ordering::Release); - let selected_pip = *selected_pip; - explored_pips.fetch_add(pips.len(), std::sync::atomic::Ordering::Relaxed); - - let pip_loc = ctx.pip_location(selected_pip); - let pip_src_wire = ctx.pip_src_wire(selected_pip); - let pip_dst_wire = ctx.pip_dst_wire(selected_pip); - let src_to_pip = ((source_wire, source), (pip_src_wire, pip_loc)); - let pip_to_dst = ((pip_dst_wire, pip_loc), (sink_wire, sink_loc)); - let (seg1, seg2) = match (source_is_north, source_is_east) { - (true, true) => (Segment::Northeast, Segment::Southeast), - (true, false) => (Segment::Northwest, Segment::Southwest), - (false, true) => (Segment::Southeast, Segment::Northeast), - (false, false) => (Segment::Southwest, Segment::Northwest), - }; - part_horiz.fetch_add(1, std::sync::atomic::Ordering::Relaxed); - vec![(seg1, src_to_pip), (seg2, pip_to_dst)] - } else if source_is_north == sink_is_north && source_is_east != sink_is_east { - let middle = ((source.x + sink_loc.x) / 2, y); - let middle = ( - middle.0.clamp(1, ctx.grid_dim_x() - 1), - middle.1.clamp(1, ctx.grid_dim_y() - 1), - ); - let pips = match source_is_east { - true => pips_w.get(&middle).unwrap(), - false => pips_e.get(&middle).unwrap(), - }; - - let (selected_pip, pip_uses) = pips - .iter() - .min_by_key(|(pip, uses)| { - let src_to_pip = ctx.estimate_delay(source_wire, ctx.pip_src_wire(*pip)); - let pip_to_snk = ctx.estimate_delay(ctx.pip_dst_wire(*pip), sink_wire); - let uses = uses.load(std::sync::atomic::Ordering::Acquire); - (1000.0 * (src_to_pip + ((uses + 1) as f32) * pip_to_snk)) as u64 - }) - .unwrap(); - pip_uses.fetch_add(1, std::sync::atomic::Ordering::Release); - let selected_pip = *selected_pip; - explored_pips.fetch_add(pips.len(), std::sync::atomic::Ordering::Relaxed); - - let pip_loc = ctx.pip_location(selected_pip); - let pip_src_wire = ctx.pip_src_wire(selected_pip); - let pip_dst_wire = ctx.pip_dst_wire(selected_pip); - let src_to_pip = ((source_wire, source), (pip_src_wire, pip_loc)); - let pip_to_dst = ((pip_dst_wire, pip_loc), (sink_wire, sink_loc)); - let (seg1, seg2) = match (source_is_north, source_is_east) { - (true, true) => (Segment::Northeast, Segment::Northwest), - (true, false) => (Segment::Northwest, Segment::Northeast), - (false, true) => (Segment::Southeast, Segment::Southwest), - (false, false) => (Segment::Southwest, Segment::Southeast), - }; - part_vert.fetch_add(1, std::sync::atomic::Ordering::Relaxed); - vec![(seg1, src_to_pip), (seg2, pip_to_dst)] - } else { - let middle = (x, split_line_over_x((source, sink_loc), x)); - let middle = ( - middle.0.clamp(1, ctx.grid_dim_x() - 1), - middle.1.clamp(1, ctx.grid_dim_y() - 1), - ); - let pips = match source_is_east { - true => pips_w.get(&middle).unwrap(), - false => pips_e.get(&middle).unwrap(), - }; - - let (horiz_pip, pip_uses) = pips - .iter() - .min_by_key(|(pip, uses)| { - let src_to_pip = ctx.estimate_delay(source_wire, ctx.pip_src_wire(*pip)); - let pip_to_snk = ctx.estimate_delay(ctx.pip_dst_wire(*pip), sink_wire); - let uses = uses.load(std::sync::atomic::Ordering::Acquire); - (1000.0 * (src_to_pip + ((uses + 1) as f32) * pip_to_snk)) as u64 - }) - .unwrap(); - pip_uses.fetch_add(1, std::sync::atomic::Ordering::Release); - let horiz_pip = *horiz_pip; - explored_pips.fetch_add(pips.len(), std::sync::atomic::Ordering::Relaxed); - - let middle = (split_line_over_y((source, sink_loc), y), y); - let middle = ( - middle.0.clamp(1, ctx.grid_dim_x() - 1), - middle.1.clamp(1, ctx.grid_dim_y() - 1), - ); - let pips = match source_is_north { - true => pips_s.get(&middle).unwrap(), - false => pips_n.get(&middle).unwrap(), - }; - - let (vert_pip, pip_uses) = pips - .iter() - .min_by_key(|(pip, uses)| { - let src_to_pip = ctx.estimate_delay(source_wire, ctx.pip_src_wire(*pip)); - let pip_to_snk = ctx.estimate_delay(ctx.pip_dst_wire(*pip), sink_wire); - let uses = uses.load(std::sync::atomic::Ordering::Acquire); - (1000.0 * (src_to_pip + ((uses + 1) as f32) * pip_to_snk)) as u64 - }) - .unwrap(); - pip_uses.fetch_add(1, std::sync::atomic::Ordering::Release); - let vert_pip = *vert_pip; - explored_pips.fetch_add(pips.len(), std::sync::atomic::Ordering::Relaxed); - - let horiz_loc = ctx.pip_location(horiz_pip); - let horiz_src_wire = ctx.pip_src_wire(horiz_pip); - let horiz_dst_wire = ctx.pip_dst_wire(horiz_pip); - let horiz_is_east = horiz_loc.y < y; - let vert_loc = ctx.pip_location(vert_pip); - let vert_src_wire = ctx.pip_src_wire(vert_pip); - let vert_dst_wire = ctx.pip_dst_wire(vert_pip); - let (src_to_mid1, mid1_to_mid2, mid2_to_dst) = if horiz_is_east == source_is_east { - ( - ((source_wire, source), (horiz_src_wire, horiz_loc)), - ((horiz_dst_wire, horiz_loc), (vert_src_wire, vert_loc)), - ((vert_dst_wire, vert_loc), (sink_wire, sink_loc)), - ) - } else { - ( - ((source_wire, source), (vert_src_wire, vert_loc)), - ((vert_dst_wire, vert_loc), (horiz_src_wire, horiz_loc)), - ((horiz_dst_wire, horiz_loc), (sink_wire, sink_loc)), - ) - }; - let (seg1, seg2, seg3) = match (source_is_north, source_is_east, horiz_is_east) { - (true, true, true) => { - (Segment::Northeast, Segment::Southeast, Segment::Southwest) - } - (true, true, false) => { - (Segment::Northeast, Segment::Northwest, Segment::Southwest) - } - (true, false, true) => { - (Segment::Northwest, Segment::Northeast, Segment::Southeast) - } - (true, false, false) => { - (Segment::Northwest, Segment::Southwest, Segment::Southeast) - } - (false, true, true) => { - (Segment::Southeast, Segment::Northeast, Segment::Northwest) - } - (false, true, false) => { - (Segment::Southeast, Segment::Southwest, Segment::Northwest) - } - (false, false, true) => { - (Segment::Southwest, Segment::Southeast, Segment::Northeast) - } - (false, false, false) => { - (Segment::Southwest, Segment::Northwest, Segment::Northeast) - } - }; - part_diag.fetch_add(1, std::sync::atomic::Ordering::Relaxed); - vec![ - (seg1, src_to_mid1), - (seg2, mid1_to_mid2), - (seg3, mid2_to_dst), - ] - } - }) - .collect::>(); - - for (segment, arc) in arcs { - match segment { - Segment::Northeast => ne.push(arc), - Segment::Southeast => se.push(arc), - Segment::Southwest => sw.push(arc), - Segment::Northwest => nw.push(arc), - } - } - - log_info!( - " {} pips explored\n", - explored_pips.get_mut().to_string().bold() - ); - - let north = ne.len() + nw.len(); - let south = se.len() + sw.len(); - - let nets = (north + south) as f64; - - let ne_dist = ((ne.len() as f64) / nets) - 0.25; - let se_dist = ((se.len() as f64) / nets) - 0.25; - let sw_dist = ((sw.len() as f64) / nets) - 0.25; - let nw_dist = ((nw.len() as f64) / nets) - 0.25; - - let ne_str = ne.len().to_string(); - let se_str = se.len().to_string(); - let sw_str = sw.len().to_string(); - let nw_str = nw.len().to_string(); - - let dist_str = |dist: f64| { - if dist > 0.20 { - "(way too many nets)".red() - } else if dist > 0.05 { - "(too many nets)".yellow() - } else if dist < -0.05 { - "(too few nets)".yellow() - } else if dist < -0.20 { - "(way too few nets)".red() - } else { - "(balanced)".green() - } - }; - - log_info!( - " {} arcs partitioned horizontally\n", - part_horiz.get_mut().to_string().bold() - ); - log_info!( - " {} arcs partitioned vertically\n", - part_vert.get_mut().to_string().bold() - ); - log_info!( - " {} arcs partitioned both ways\n", - part_diag.get_mut().to_string().bold() - ); - log_info!( - " {} arcs in the northeast {}\n", - ne_str.color(if ne_dist.abs() > 0.20 { - colored::Color::Red - } else if ne_dist.abs() > 0.05 { - colored::Color::Yellow - } else { - colored::Color::Green - }), - dist_str(ne_dist) - ); - log_info!( - " {} arcs in the southeast {}\n", - se_str.color(if se_dist.abs() > 0.20 { - colored::Color::Red - } else if se_dist.abs() > 0.05 { - colored::Color::Yellow - } else { - colored::Color::Green - }), - dist_str(se_dist) - ); - log_info!( - " {} arcs in the southwest {}\n", - sw_str.color(if sw_dist.abs() > 0.20 { - colored::Color::Red - } else if sw_dist.abs() > 0.05 { - colored::Color::Yellow - } else { - colored::Color::Green - }), - dist_str(sw_dist) - ); - log_info!( - " {} arcs in the northwest {}\n", - nw_str.color(if nw_dist.abs() > 0.20 { - colored::Color::Red - } else if nw_dist.abs() > 0.05 { - colored::Color::Yellow - } else { - colored::Color::Green - }), - dist_str(nw_dist) - ); - - (ne, se, sw, nw) -} - fn extract_arcs_from_nets(ctx: &npnr::Context, nets: npnr::Nets) -> Vec { let mut arcs = vec![]; for (name, net) in nets.iter() { @@ -667,7 +144,7 @@ fn route(ctx: &mut npnr::Context) -> bool { let arcs = extract_arcs_from_nets(ctx, nets); - let (x_part, y_part, ne, se, sw, nw) = find_partition_point( + let (x_part, y_part, ne, se, sw, nw) = partition::find_partition_point( ctx, &arcs[..], pips, diff --git a/common/route/awooter/rust/src/partition.rs b/common/route/awooter/rust/src/partition.rs new file mode 100644 index 00000000..622a2038 --- /dev/null +++ b/common/route/awooter/rust/src/partition.rs @@ -0,0 +1,529 @@ +use std::{collections::HashMap, sync::atomic::AtomicUsize}; + +use colored::Colorize; +use indicatif::{ParallelProgressIterator, ProgressBar, ProgressStyle}; +use rayon::prelude::*; + +use crate::{npnr, Arc}; + +pub fn find_partition_point( + ctx: &npnr::Context, + arcs: &[Arc], + pips: &[npnr::PipId], + x_start: i32, + x_finish: i32, + y_start: i32, + y_finish: i32, +) -> (i32, i32, Vec, Vec, Vec, Vec) { + let mut x = ((x_finish - x_start) / 2) + x_start; + let mut y = ((y_finish - y_start) / 2) + y_start; + let mut x_diff = (x_finish - x_start) / 4; + let mut y_diff = (y_finish - y_start) / 4; + + let mut ne; + let mut se; + let mut sw; + let mut nw; + + while x_diff != 0 { + (ne, se, sw, nw) = partition(ctx, arcs, pips, x, y); + let north = ne.len() + nw.len(); + let south = se.len() + sw.len(); + + let nets = (north + south) as f64; + + let ne_dist = f64::abs(((ne.len() as f64) / nets) - 0.25); + let se_dist = f64::abs(((se.len() as f64) / nets) - 0.25); + let sw_dist = f64::abs(((sw.len() as f64) / nets) - 0.25); + let nw_dist = f64::abs(((nw.len() as f64) / nets) - 0.25); + + let distortion = 100.0 * (ne_dist + se_dist + sw_dist + nw_dist); + + // Stop early if Good Enough. + if distortion <= 5.0 { + return (x, y, ne, se, sw, nw); + } + + x += match north.cmp(&south) { + std::cmp::Ordering::Less => x_diff, + std::cmp::Ordering::Equal => 0, + std::cmp::Ordering::Greater => -x_diff, + }; + + let east = ne.len() + se.len(); + let west = nw.len() + sw.len(); + y += match east.cmp(&west) { + std::cmp::Ordering::Less => y_diff, + std::cmp::Ordering::Equal => 0, + std::cmp::Ordering::Greater => -y_diff, + }; + + x_diff >>= 1; + y_diff >>= 1; + } + + (ne, se, sw, nw) = partition(ctx, arcs, pips, x, y); + + let north = ne.len() + nw.len(); + let south = se.len() + sw.len(); + let nets = (north + south) as f64; + + let ne_dist = f64::abs(((ne.len() as f64) / nets) - 0.25); + let se_dist = f64::abs(((se.len() as f64) / nets) - 0.25); + let sw_dist = f64::abs(((sw.len() as f64) / nets) - 0.25); + let nw_dist = f64::abs(((nw.len() as f64) / nets) - 0.25); + + log_info!( + "Distortion: {:.02}%\n", + 100.0 * (ne_dist + se_dist + sw_dist + nw_dist) + ); + + (x, y, ne, se, sw, nw) +} + +/// finds the y location a line would be split at if you split it at a certain x location +/// +/// the function assumes the line goes on forever in both directions, and it truncates the actual coordinate +fn split_line_over_x(line: (npnr::Loc, npnr::Loc), x_location: i32) -> i32 { + if line.0.x == line.1.x { + // the line is a straight line in the direction, there is either infinite solutions, or none + // we simply average the y coordinate to give a "best effort" guess + return (line.0.y + line.1.y) / 2; + } + + let x_diff = line.0.x - line.1.x; + let y_diff = line.0.y - line.1.y; + + // i hope for no overflows, maybe promote to i64 to be sure? + (y_diff * x_location + line.0.y * x_diff - line.0.x * y_diff) / x_diff +} + +/// finds the x location a line would be split at if you split it at a certain y location, assuming the line goes on forever in both directions +fn split_line_over_y(line: (npnr::Loc, npnr::Loc), y_location: i32) -> i32 { + // laziness supreme! + split_line_over_x( + ( + npnr::Loc { + x: line.0.y, + y: line.0.x, + z: 0, + }, + npnr::Loc { + x: line.1.y, + y: line.1.x, + z: 0, + }, + ), + y_location, + ) +} + +enum Segment { + Northeast, + Southeast, + Southwest, + Northwest, +} + +// A big thank you to @Spacecat-chan for fixing my broken and buggy partition code. +fn partition( + ctx: &npnr::Context, + arcs: &[Arc], + pips: &[npnr::PipId], + x: i32, + y: i32, +) -> (Vec, Vec, Vec, Vec) { + let mut pips_n = HashMap::new(); + let mut pips_e = HashMap::new(); + let mut pips_s = HashMap::new(); + let mut pips_w = HashMap::new(); + + let mut ne: Vec = Vec::new(); + let mut se: Vec = Vec::new(); + let mut sw: Vec = Vec::new(); + let mut nw: Vec = Vec::new(); + let mut part_horiz = AtomicUsize::new(0); + let mut part_vert = AtomicUsize::new(0); + let mut part_diag = AtomicUsize::new(0); + + let x_str = format!("X = {}", x); + let y_str = format!("Y = {}", y); + log_info!( + "Partitioning arcs along {}, {}\n", + x_str.bold(), + y_str.bold() + ); + + let mut candidates = 0; + let mut north = 0; + let mut east = 0; + let mut south = 0; + let mut west = 0; + for &pip in pips { + let loc = ctx.pip_location(pip); + if loc.x == x || loc.y == y { + let dir = ctx.pip_direction(pip); + + // This pip seems internal; skip it. + if dir.x == 0 && dir.y == 0 { + continue; + } + + candidates += 1; + + if dir.x < 0 { + north += 1; + pips_n + .entry((loc.x, loc.y)) + .and_modify(|pip_list: &mut Vec<(npnr::PipId, AtomicUsize)>| { + pip_list.push((pip, AtomicUsize::new(0))) + }) + .or_insert_with(|| vec![(pip, AtomicUsize::new(0))]); + } + + if dir.x > 0 { + south += 1; + pips_s + .entry((loc.x, loc.y)) + .and_modify(|pip_list: &mut Vec<(npnr::PipId, AtomicUsize)>| { + pip_list.push((pip, AtomicUsize::new(0))) + }) + .or_insert_with(|| vec![(pip, AtomicUsize::new(0))]); + } + + if dir.y < 0 { + east += 1; + pips_e + .entry((loc.x, loc.y)) + .and_modify(|pip_list: &mut Vec<(npnr::PipId, AtomicUsize)>| { + pip_list.push((pip, AtomicUsize::new(0))) + }) + .or_insert_with(|| vec![(pip, AtomicUsize::new(0))]); + } + + if dir.y > 0 { + west += 1; + pips_w + .entry((loc.x, loc.y)) + .and_modify(|pip_list: &mut Vec<(npnr::PipId, AtomicUsize)>| { + pip_list.push((pip, AtomicUsize::new(0))) + }) + .or_insert_with(|| vec![(pip, AtomicUsize::new(0))]); + } + } + } + + log_info!( + " Out of {} candidate pips:\n", + candidates.to_string().bold() + ); + log_info!(" {} are north-bound\n", north.to_string().bold()); + log_info!(" {} are east-bound\n", east.to_string().bold()); + log_info!(" {} are south-bound\n", south.to_string().bold()); + log_info!(" {} are west-bound\n", west.to_string().bold()); + + let progress = ProgressBar::new(arcs.len() as u64); + progress.set_style( + ProgressStyle::with_template("[{elapsed}] [{bar:40.cyan/blue}] {msg}") + .unwrap() + .progress_chars("━╸ "), + ); + + let mut explored_pips = AtomicUsize::new(0); + + let arcs = arcs + .into_par_iter() + .progress_with(progress) + .flat_map(|&((source_wire, source), (sink_wire, sink_loc))| { + let source_is_north = source.x < x; + let source_is_east = source.y < y; + let sink_is_north = sink_loc.x < x; + let sink_is_east = sink_loc.y < y; + if source_is_north == sink_is_north && source_is_east == sink_is_east { + let arc = ((source_wire, source), (sink_wire, sink_loc)); + let seg = match (source_is_north, source_is_east) { + (true, true) => Segment::Northeast, + (true, false) => Segment::Northwest, + (false, true) => Segment::Southeast, + (false, false) => Segment::Southwest, + }; + vec![(seg, arc)] + } else if source_is_north != sink_is_north && source_is_east == sink_is_east { + let middle = (x, (source.y + sink_loc.y) / 2); + let middle = ( + middle.0.clamp(1, ctx.grid_dim_x() - 1), + middle.1.clamp(1, ctx.grid_dim_y() - 1), + ); + let pips = match source_is_north { + true => pips_s.get(&middle).unwrap(), + false => pips_n.get(&middle).unwrap(), + }; + + let (selected_pip, pip_uses) = pips + .iter() + .min_by_key(|(pip, uses)| { + let src_to_pip = ctx.estimate_delay(source_wire, ctx.pip_src_wire(*pip)); + let pip_to_snk = ctx.estimate_delay(ctx.pip_dst_wire(*pip), sink_wire); + let uses = uses.load(std::sync::atomic::Ordering::Acquire); + (1000.0 * (src_to_pip + ((uses + 1) as f32) * pip_to_snk)) as u64 + }) + .unwrap(); + pip_uses.fetch_add(1, std::sync::atomic::Ordering::Release); + let selected_pip = *selected_pip; + explored_pips.fetch_add(pips.len(), std::sync::atomic::Ordering::Relaxed); + + let pip_loc = ctx.pip_location(selected_pip); + let pip_src_wire = ctx.pip_src_wire(selected_pip); + let pip_dst_wire = ctx.pip_dst_wire(selected_pip); + let src_to_pip = ((source_wire, source), (pip_src_wire, pip_loc)); + let pip_to_dst = ((pip_dst_wire, pip_loc), (sink_wire, sink_loc)); + let (seg1, seg2) = match (source_is_north, source_is_east) { + (true, true) => (Segment::Northeast, Segment::Southeast), + (true, false) => (Segment::Northwest, Segment::Southwest), + (false, true) => (Segment::Southeast, Segment::Northeast), + (false, false) => (Segment::Southwest, Segment::Northwest), + }; + part_horiz.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + vec![(seg1, src_to_pip), (seg2, pip_to_dst)] + } else if source_is_north == sink_is_north && source_is_east != sink_is_east { + let middle = ((source.x + sink_loc.x) / 2, y); + let middle = ( + middle.0.clamp(1, ctx.grid_dim_x() - 1), + middle.1.clamp(1, ctx.grid_dim_y() - 1), + ); + let pips = match source_is_east { + true => pips_w.get(&middle).unwrap(), + false => pips_e.get(&middle).unwrap(), + }; + + let (selected_pip, pip_uses) = pips + .iter() + .min_by_key(|(pip, uses)| { + let src_to_pip = ctx.estimate_delay(source_wire, ctx.pip_src_wire(*pip)); + let pip_to_snk = ctx.estimate_delay(ctx.pip_dst_wire(*pip), sink_wire); + let uses = uses.load(std::sync::atomic::Ordering::Acquire); + (1000.0 * (src_to_pip + ((uses + 1) as f32) * pip_to_snk)) as u64 + }) + .unwrap(); + pip_uses.fetch_add(1, std::sync::atomic::Ordering::Release); + let selected_pip = *selected_pip; + explored_pips.fetch_add(pips.len(), std::sync::atomic::Ordering::Relaxed); + + let pip_loc = ctx.pip_location(selected_pip); + let pip_src_wire = ctx.pip_src_wire(selected_pip); + let pip_dst_wire = ctx.pip_dst_wire(selected_pip); + let src_to_pip = ((source_wire, source), (pip_src_wire, pip_loc)); + let pip_to_dst = ((pip_dst_wire, pip_loc), (sink_wire, sink_loc)); + let (seg1, seg2) = match (source_is_north, source_is_east) { + (true, true) => (Segment::Northeast, Segment::Northwest), + (true, false) => (Segment::Northwest, Segment::Northeast), + (false, true) => (Segment::Southeast, Segment::Southwest), + (false, false) => (Segment::Southwest, Segment::Southeast), + }; + part_vert.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + vec![(seg1, src_to_pip), (seg2, pip_to_dst)] + } else { + let middle = (x, split_line_over_x((source, sink_loc), x)); + let middle = ( + middle.0.clamp(1, ctx.grid_dim_x() - 1), + middle.1.clamp(1, ctx.grid_dim_y() - 1), + ); + let pips = match source_is_east { + true => pips_w.get(&middle).unwrap(), + false => pips_e.get(&middle).unwrap(), + }; + + let (horiz_pip, pip_uses) = pips + .iter() + .min_by_key(|(pip, uses)| { + let src_to_pip = ctx.estimate_delay(source_wire, ctx.pip_src_wire(*pip)); + let pip_to_snk = ctx.estimate_delay(ctx.pip_dst_wire(*pip), sink_wire); + let uses = uses.load(std::sync::atomic::Ordering::Acquire); + (1000.0 * (src_to_pip + ((uses + 1) as f32) * pip_to_snk)) as u64 + }) + .unwrap(); + pip_uses.fetch_add(1, std::sync::atomic::Ordering::Release); + let horiz_pip = *horiz_pip; + explored_pips.fetch_add(pips.len(), std::sync::atomic::Ordering::Relaxed); + + let middle = (split_line_over_y((source, sink_loc), y), y); + let middle = ( + middle.0.clamp(1, ctx.grid_dim_x() - 1), + middle.1.clamp(1, ctx.grid_dim_y() - 1), + ); + let pips = match source_is_north { + true => pips_s.get(&middle).unwrap(), + false => pips_n.get(&middle).unwrap(), + }; + + let (vert_pip, pip_uses) = pips + .iter() + .min_by_key(|(pip, uses)| { + let src_to_pip = ctx.estimate_delay(source_wire, ctx.pip_src_wire(*pip)); + let pip_to_snk = ctx.estimate_delay(ctx.pip_dst_wire(*pip), sink_wire); + let uses = uses.load(std::sync::atomic::Ordering::Acquire); + (1000.0 * (src_to_pip + ((uses + 1) as f32) * pip_to_snk)) as u64 + }) + .unwrap(); + pip_uses.fetch_add(1, std::sync::atomic::Ordering::Release); + let vert_pip = *vert_pip; + explored_pips.fetch_add(pips.len(), std::sync::atomic::Ordering::Relaxed); + + let horiz_loc = ctx.pip_location(horiz_pip); + let horiz_src_wire = ctx.pip_src_wire(horiz_pip); + let horiz_dst_wire = ctx.pip_dst_wire(horiz_pip); + let horiz_is_east = horiz_loc.y < y; + let vert_loc = ctx.pip_location(vert_pip); + let vert_src_wire = ctx.pip_src_wire(vert_pip); + let vert_dst_wire = ctx.pip_dst_wire(vert_pip); + let (src_to_mid1, mid1_to_mid2, mid2_to_dst) = if horiz_is_east == source_is_east { + ( + ((source_wire, source), (horiz_src_wire, horiz_loc)), + ((horiz_dst_wire, horiz_loc), (vert_src_wire, vert_loc)), + ((vert_dst_wire, vert_loc), (sink_wire, sink_loc)), + ) + } else { + ( + ((source_wire, source), (vert_src_wire, vert_loc)), + ((vert_dst_wire, vert_loc), (horiz_src_wire, horiz_loc)), + ((horiz_dst_wire, horiz_loc), (sink_wire, sink_loc)), + ) + }; + let (seg1, seg2, seg3) = match (source_is_north, source_is_east, horiz_is_east) { + (true, true, true) => { + (Segment::Northeast, Segment::Southeast, Segment::Southwest) + } + (true, true, false) => { + (Segment::Northeast, Segment::Northwest, Segment::Southwest) + } + (true, false, true) => { + (Segment::Northwest, Segment::Northeast, Segment::Southeast) + } + (true, false, false) => { + (Segment::Northwest, Segment::Southwest, Segment::Southeast) + } + (false, true, true) => { + (Segment::Southeast, Segment::Northeast, Segment::Northwest) + } + (false, true, false) => { + (Segment::Southeast, Segment::Southwest, Segment::Northwest) + } + (false, false, true) => { + (Segment::Southwest, Segment::Southeast, Segment::Northeast) + } + (false, false, false) => { + (Segment::Southwest, Segment::Northwest, Segment::Northeast) + } + }; + part_diag.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + vec![ + (seg1, src_to_mid1), + (seg2, mid1_to_mid2), + (seg3, mid2_to_dst), + ] + } + }) + .collect::>(); + + for (segment, arc) in arcs { + match segment { + Segment::Northeast => ne.push(arc), + Segment::Southeast => se.push(arc), + Segment::Southwest => sw.push(arc), + Segment::Northwest => nw.push(arc), + } + } + + log_info!( + " {} pips explored\n", + explored_pips.get_mut().to_string().bold() + ); + + let north = ne.len() + nw.len(); + let south = se.len() + sw.len(); + + let nets = (north + south) as f64; + + let ne_dist = ((ne.len() as f64) / nets) - 0.25; + let se_dist = ((se.len() as f64) / nets) - 0.25; + let sw_dist = ((sw.len() as f64) / nets) - 0.25; + let nw_dist = ((nw.len() as f64) / nets) - 0.25; + + let ne_str = ne.len().to_string(); + let se_str = se.len().to_string(); + let sw_str = sw.len().to_string(); + let nw_str = nw.len().to_string(); + + let dist_str = |dist: f64| { + if dist > 0.20 { + "(way too many nets)".red() + } else if dist > 0.05 { + "(too many nets)".yellow() + } else if dist < -0.05 { + "(too few nets)".yellow() + } else if dist < -0.20 { + "(way too few nets)".red() + } else { + "(balanced)".green() + } + }; + + log_info!( + " {} arcs partitioned horizontally\n", + part_horiz.get_mut().to_string().bold() + ); + log_info!( + " {} arcs partitioned vertically\n", + part_vert.get_mut().to_string().bold() + ); + log_info!( + " {} arcs partitioned both ways\n", + part_diag.get_mut().to_string().bold() + ); + log_info!( + " {} arcs in the northeast {}\n", + ne_str.color(if ne_dist.abs() > 0.20 { + colored::Color::Red + } else if ne_dist.abs() > 0.05 { + colored::Color::Yellow + } else { + colored::Color::Green + }), + dist_str(ne_dist) + ); + log_info!( + " {} arcs in the southeast {}\n", + se_str.color(if se_dist.abs() > 0.20 { + colored::Color::Red + } else if se_dist.abs() > 0.05 { + colored::Color::Yellow + } else { + colored::Color::Green + }), + dist_str(se_dist) + ); + log_info!( + " {} arcs in the southwest {}\n", + sw_str.color(if sw_dist.abs() > 0.20 { + colored::Color::Red + } else if sw_dist.abs() > 0.05 { + colored::Color::Yellow + } else { + colored::Color::Green + }), + dist_str(sw_dist) + ); + log_info!( + " {} arcs in the northwest {}\n", + nw_str.color(if nw_dist.abs() > 0.20 { + colored::Color::Red + } else if nw_dist.abs() > 0.05 { + colored::Color::Yellow + } else { + colored::Color::Green + }), + dist_str(nw_dist) + ); + + (ne, se, sw, nw) +}