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
81 changes: 79 additions & 2 deletions src/sequential/core.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::collections::HashSet;
use std::collections::{HashSet, VecDeque};

use crate::graph::{Graph, GraphNodeId, GraphNodeKind};
use crate::graph::logic::LogicGraph;
use crate::graph::{Graph, GraphNode, GraphNodeId, GraphNodeKind};
use crate::logic::LogicType;

#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -74,6 +75,68 @@ pub fn recognize_rs_latch_core(graph: &Graph) -> Option<RsLatchCore> {
})
}

// Extract the acyclic logic that drives the set/reset inputs of an RS latch core.
// Feedback SCC nodes are excluded so the returned graph can be handled by the
// existing combinational placer.
pub fn rs_latch_prefix_graph(graph: &Graph, core: &RsLatchCore) -> Option<LogicGraph> {
let feedback_scc = core.feedback_scc.iter().copied().collect::<HashSet<_>>();
let output_roots = [("s", core.set_input), ("r", core.reset_input)];
let mut prefix_nodes = HashSet::new();
let mut queue = output_roots
.iter()
.map(|(_, node_id)| *node_id)
.collect::<VecDeque<_>>();

while let Some(node_id) = queue.pop_front() {
if feedback_scc.contains(&node_id) {
return None;
}
if !prefix_nodes.insert(node_id) {
continue;
}

let node = graph.find_node_by_id(node_id)?;
for input in &node.inputs {
queue.push_back(*input);
}
}

let mut nodes = graph
.nodes
.iter()
.filter(|node| prefix_nodes.contains(&node.id))
.cloned()
.map(|mut node| {
node.inputs.retain(|input| prefix_nodes.contains(input));
node.outputs.clear();
node
})
.collect::<Vec<_>>();

let mut next_node_id = graph.nodes.iter().map(|node| node.id).max().unwrap_or(0) + 1;
for (output_name, source_node_id) in output_roots {
nodes.push(GraphNode {
id: next_node_id,
kind: GraphNodeKind::Output(output_name.to_owned()),
inputs: vec![source_node_id],
..Default::default()
});
next_node_id += 1;
}
nodes.sort_by_key(|node| node.id);

let mut graph = Graph {
nodes,
..Default::default()
};
graph.build_outputs();
graph.build_producers();
graph.build_consumers();
graph.verify().ok()?;

Some(LogicGraph { graph })
}

fn find_output(graph: &Graph, output_name: &str) -> Option<GraphNodeId> {
graph.nodes.iter().find_map(|node| {
matches!(&node.kind, GraphNodeKind::Output(name) if name == output_name).then_some(node.id)
Expand Down Expand Up @@ -139,4 +202,18 @@ mod tests {
assert_eq!(core.nq_output, 10);
assert_eq!(core.feedback_scc, vec![5, 6, 7, 8]);
}

#[test]
fn rs_latch_prefix_graph_extracts_d_latch_input_gating() -> eyre::Result<()> {
let primitive = SequentialPrimitive::d_latch();
let core = recognize_rs_latch_core(&primitive.inner_graph).unwrap();
let prefix = rs_latch_prefix_graph(&primitive.inner_graph, &core).unwrap();
let table = prefix.truth_table()?;

assert_eq!(table.input_names, vec!["d".to_owned(), "en".to_owned()]);
assert_eq!(table.output_tables["s"], vec![false, false, false, true]);
assert_eq!(table.output_tables["r"], vec![false, false, true, false]);
assert!(!prefix.has_cycle());
Ok(())
}
}
12 changes: 4 additions & 8 deletions src/transform/place_and_route/local_placer/component_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use crate::sequential::{SequentialPrimitive, SequentialType};
use crate::transform::place_and_route::estimate::world_compact_cost;
use crate::transform::place_and_route::local_placer::{
generate_d_latch_gate_routes, generate_rs_latch_not_pairs, route_rs_latch_branches,
select_rs_latch_not_pairs, InputPlacementStrategy, LocalPlacer, LocalPlacerConfig,
LocalPlacerDebug, NotRouteStrategy, PlacementSamplingPolicy, SamplingPolicy,
TorchPlacementStrategy, D_LATCH_INPUT_GATING_NODES,
rs_latch_input_node_ids, select_rs_latch_not_pairs, InputPlacementStrategy, LocalPlacer,
LocalPlacerConfig, LocalPlacerDebug, NotRouteStrategy, PlacementSamplingPolicy, SamplingPolicy,
TorchPlacementStrategy,
};
use crate::transform::place_and_route::utils::{
contains_truth_table_with_world3ds, equivalent_logic_with_world3d,
Expand Down Expand Up @@ -391,18 +391,14 @@ fn test_generate_component_d_latch() -> eyre::Result<()> {
eprintln!("candidate {checked} failed: {error}; q={q:?} nq={nq:?}");
let q_support = q.walk(world[q].direction).unwrap();
let nq_support = nq.walk(world[nq].direction).unwrap();
let nodes = D_LATCH_INPUT_GATING_NODES;
let nodes = rs_latch_input_node_ids(node.id);
let _ = trace_d_latch_behavior(
world,
d,
en,
&[
("d", d),
("en", en),
("not_d", state[&nodes.not_d]),
("not_en", state[&nodes.not_en]),
("set_or", state[&nodes.set_or]),
("reset_or", state[&nodes.reset_or]),
("set", state[&nodes.set]),
("reset", state[&nodes.reset]),
("q_support", q_support),
Expand Down
87 changes: 59 additions & 28 deletions src/transform/place_and_route/local_placer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use crate::transform::place_and_route::estimate::{bounding_box_of_positions, wor
use crate::transform::place_and_route::place_bound::PropagateType;
use crate::world::block::{Block, BlockKind, Direction};
use crate::world::position::{DimSize, Position};
use crate::world::simulator::Simulator;
use crate::world::World3D;

mod config;
Expand All @@ -44,7 +43,7 @@ use sequential::{
#[cfg(test)]
use sequential::{
generate_rs_latch_not_pairs, place_sequential_macro, route_rs_latch_branches,
route_sequential_inputs, select_rs_latch_not_pairs, D_LATCH_INPUT_GATING_NODES,
route_sequential_inputs, rs_latch_input_node_ids, select_rs_latch_not_pairs,
};

pub struct LocalPlacer {
Expand All @@ -55,6 +54,15 @@ pub struct LocalPlacer {
}

type PlacerQueue = Vec<(World3D, PlacementState)>;

const STEP_SAMPLE_SCOPE: u64 = 1;
const RANKED_RANDOM_TAIL_SAMPLE_SCOPE: u64 = 2;
const LEAK_SAMPLING_QUEUE_THRESHOLD: usize = 10_000;
const PLACEMENT_DIVERSITY_SPAN_BUCKET_SIZE: usize = 4;
const RANKED_DIVERSITY_SLOT_DIVISOR: usize = 4;
const LOCAL_DENSITY_COST_WEIGHT: usize = 3;
const FUTURE_JOIN_DISTANCE_COST_WEIGHT: usize = 8;

impl LocalPlacer {
pub fn new(graph: LogicGraph, config: LocalPlacerConfig) -> eyre::Result<Self> {
let visit_orders = graph.topological_order();
Expand Down Expand Up @@ -156,12 +164,20 @@ impl LocalPlacer {
&self,
dim: DimSize,
finish_step: Option<usize>,
mut debug: Option<&mut LocalPlacerDebug>,
debug: Option<&mut LocalPlacerDebug>,
) -> PlacerQueue {
tracing::info!("generate starts");

let mut queue = PlacerQueue::new();
queue.push((World3D::new(dim), Default::default()));
self.generate_queue_from(queue, finish_step, debug)
}

fn generate_queue_from(
&self,
mut queue: PlacerQueue,
finish_step: Option<usize>,
mut debug: Option<&mut LocalPlacerDebug>,
) -> PlacerQueue {
tracing::info!("generate starts");

let mut step = 0;
while step < self.visit_orders.len() && Some(step) != finish_step {
Expand Down Expand Up @@ -252,16 +268,24 @@ impl LocalPlacer {
) -> PlacementGeneration {
let mut route_debug = None;
let items = match node.kind {
GraphNodeKind::Input(_) => input_node_kind()
.into_iter()
.flat_map(|kind| generate_inputs(&self.config, &world, kind))
.map(|(world, position)| {
GraphNodeKind::Input(_) => {
if let Some(position) = state.node_position(node.id) {
let mut state = state.clone();
state.set_node_position(node.id, position);
state.set_signal_footprint(node.id, [position]);
(world, state)
})
.collect(),
vec![(world, state)]
} else {
input_node_kind()
.into_iter()
.flat_map(|kind| generate_inputs(&self.config, &world, kind))
.map(|(world, position)| {
let mut state = state.clone();
state.set_node_position(node.id, position);
state.set_signal_footprint(node.id, [position]);
(world, state)
})
.collect()
}
}
GraphNodeKind::Output(_) => vec![(world.clone(), state.clone())],
GraphNodeKind::Logic(logic) => match logic.logic_type {
LogicType::Not => not_node_kind()
Expand Down Expand Up @@ -360,16 +384,17 @@ impl LocalPlacer {
fn sample(&self, step: usize, queue: PlacerQueue) -> PlacerQueue {
match self.config.placement_sampling_policy {
PlacementSamplingPolicy::StepPolicy => {
if self.config.leak_sampling && queue.len() > 10_000 {
if self.config.leak_sampling && queue.len() > LEAK_SAMPLING_QUEUE_THRESHOLD {
// TODO: deallocate on other thread
let leak = Box::leak(Box::new(queue));
self.config
.step_sampling_policy
.sample_with_taking_seed(leak, self.config.sampling_seed(1, step))
self.config.step_sampling_policy.sample_with_taking_seed(
leak,
self.config.sampling_seed(STEP_SAMPLE_SCOPE, step),
)
} else {
self.config
.step_sampling_policy
.sample_with_seed(queue, self.config.sampling_seed(1, step))
.sample_with_seed(queue, self.config.sampling_seed(STEP_SAMPLE_SCOPE, step))
}
}
PlacementSamplingPolicy::Cost {
Expand All @@ -380,7 +405,7 @@ impl LocalPlacer {
if step < start_step {
self.config
.step_sampling_policy
.sample_with_seed(queue, self.config.sampling_seed(1, step))
.sample_with_seed(queue, self.config.sampling_seed(STEP_SAMPLE_SCOPE, step))
} else {
self.sample_by_cost(step, queue, count, random_count)
}
Expand All @@ -393,7 +418,7 @@ impl LocalPlacer {
if step < start_step {
self.config
.step_sampling_policy
.sample_with_seed(queue, self.config.sampling_seed(1, step))
.sample_with_seed(queue, self.config.sampling_seed(STEP_SAMPLE_SCOPE, step))
} else {
self.sample_by_ranked(step, queue, count, random_count)
}
Expand Down Expand Up @@ -457,8 +482,11 @@ impl LocalPlacer {
let mut best = scored.into_iter().map(|(_, item)| item).collect_vec();
let rest = rest.into_iter().map(|(_, item)| item).collect_vec();
best.extend(
SamplingPolicy::Random(random_count)
.sample_with_seed(rest, self.config.sampling_seed(2, step)),
SamplingPolicy::Random(random_count).sample_with_seed(
rest,
self.config
.sampling_seed(RANKED_RANDOM_TAIL_SAMPLE_SCOPE, step),
),
);
best
}
Expand Down Expand Up @@ -488,7 +516,7 @@ impl LocalPlacer {
// diversity 보존용 coarse geometry bucket이다. 완전한 placement
// identity가 아니므로 semantic deduplication 용도로 쓰면 안 된다.
signature: bounding_box_of_positions(item.1.node_positions())
.map(|bounds| bounds.manhattan_span() / 4)
.map(|bounds| bounds.manhattan_span() / PLACEMENT_DIVERSITY_SPAN_BUCKET_SIZE)
.unwrap_or_default(),
index,
item,
Expand All @@ -501,7 +529,7 @@ impl LocalPlacer {
// config knob으로 드러내는 것이 좋다.
let ranked_count = count.min(scored.len());
let diversity_count = if ranked_count > 1 {
(ranked_count / 4).max(1)
(ranked_count / RANKED_DIVERSITY_SLOT_DIVISOR).max(1)
} else {
0
};
Expand Down Expand Up @@ -541,8 +569,11 @@ impl LocalPlacer {
let mut best = selected.into_iter().map(|item| item.item).collect_vec();
let random_pool = overflow.map(|item| item.item).collect_vec();
best.extend(
SamplingPolicy::Random(random_count)
.sample_with_seed(random_pool, self.config.sampling_seed(2, step)),
SamplingPolicy::Random(random_count).sample_with_seed(
random_pool,
self.config
.sampling_seed(RANKED_RANDOM_TAIL_SAMPLE_SCOPE, step),
),
);
best
}
Expand All @@ -552,15 +583,15 @@ impl LocalPlacer {
let mut cost = world_compact_cost(world);

if let Some(position) = state.node_position(current_node_id) {
cost += local_density(world, position) * 3;
cost += local_density(world, position) * LOCAL_DENSITY_COST_WEIGHT;
}

for pair in &self.cost_join_pairs_by_step[step] {
let (Some(a), Some(b)) = (state.node_position(pair.a), state.node_position(pair.b))
else {
continue;
};
cost += a.manhattan_distance(&b) * pair.weight * 8;
cost += a.manhattan_distance(&b) * pair.weight * FUTURE_JOIN_DISTANCE_COST_WEIGHT;
}

cost
Expand Down
17 changes: 11 additions & 6 deletions src/transform/place_and_route/local_placer/routing.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use super::*;

const OR_ROUTE_STEP_SAMPLE_SCOPE: u64 = 3;
const NOT_ROUTE_STEP_SAMPLE_SCOPE: u64 = 5;

pub(super) fn input_node_kind() -> Vec<BlockKind> {
vec![
BlockKind::Switch { is_on: false },
Expand Down Expand Up @@ -404,9 +407,10 @@ fn generate_redstone_routes(
}
}

queue = config
.route_step_sampling_policy
.sample_with_seed(next_queue, config.sampling_seed(5, step));
queue = config.route_step_sampling_policy.sample_with_seed(
next_queue,
config.sampling_seed(NOT_ROUTE_STEP_SAMPLE_SCOPE, step),
);
step += 1;
}

Expand Down Expand Up @@ -591,9 +595,10 @@ pub(super) fn generate_or_routes(
}

depth_debug.next_frontier_before_sampling = next_queue.len();
queue = config
.route_step_sampling_policy
.sample_with_seed(next_queue, config.sampling_seed(3, step));
queue = config.route_step_sampling_policy.sample_with_seed(
next_queue,
config.sampling_seed(OR_ROUTE_STEP_SAMPLE_SCOPE, step),
);
depth_debug.next_frontier_after_sampling = queue.len();
debug.depths.push(depth_debug);
step += 1;
Expand Down
Loading
Loading