use std::{borrow::Borrow, sync::atomic::Ordering};
use libreda_db::traits::{NetlistBaseMT, NetlistIds};
use petgraph::{
data::DataMap,
stable_graph::NodeIndex,
visit::{EdgeRef, GraphBase, IntoEdgesDirected},
};
use crate::{
timing_graph::{EdgeData, GraphEdgeType, NodeData, TimingGraph},
traits::ConstraintBase,
};
use pargraph::{
executors::multi_thread::MultiThreadExecutor, local_view::LocalGraphView,
worklists::biglock_fifo::FifoWorklist, worklists::WorklistPush, ReadonlyOperator,
};
pub(crate) fn propagate_cones<Netlist, CellModel>(
g: &TimingGraph<Netlist, CellModel>,
frontier: impl Iterator<Item = NodeIndex>,
generation: u32,
) where
Netlist: NetlistBaseMT,
CellModel: ConstraintBase,
{
let operator = ConePropagationOp { generation };
let executor = MultiThreadExecutor::new();
let worklist =
FifoWorklist::new_with_local_queues(frontier.map(WorkItem::new_forward).collect());
executor.run_readonly(worklist, operator, &g.arc_graph);
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum PropagationDirection {
Forward,
Backward,
}
#[derive(Clone)]
pub(crate) struct ConePropagationOp {
pub generation: u32,
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct WorkItem<N> {
node: N,
dir: PropagationDirection,
}
impl<N> WorkItem<N> {
pub fn new_forward(n: N) -> Self {
Self {
node: n,
dir: PropagationDirection::Forward,
}
}
fn new_backward(n: N) -> Self {
Self {
node: n,
dir: PropagationDirection::Backward,
}
}
}
impl<N> Borrow<N> for WorkItem<N> {
fn borrow(&self) -> &N {
&self.node
}
}
impl<G, N, D> ReadonlyOperator<G> for ConePropagationOp
where
N: NetlistIds,
D: ConstraintBase,
G: GraphBase
+ IntoEdgesDirected
+ DataMap<NodeWeight = NodeData<N, D>, EdgeWeight = EdgeData<D>>,
{
type WorkItem = WorkItem<G::NodeId>;
fn op(
&self,
work_item: Self::WorkItem,
local_view: LocalGraphView<&G>,
mut worklist: impl WorklistPush<Self::WorkItem>,
) {
let local_view = &local_view;
let active_node = local_view.active_node();
let next_neighbors = |direction: PropagationDirection| {
let dir = match direction {
PropagationDirection::Forward => petgraph::Direction::Outgoing,
PropagationDirection::Backward => petgraph::Direction::Incoming,
};
local_view
.edges_directed(active_node, dir)
.map(move |e| match direction {
PropagationDirection::Forward => (e.target(), e.weight().edge_type),
PropagationDirection::Backward => (e.source(), e.weight().edge_type),
})
};
let local_data = local_view.node_weight(active_node).unwrap();
if work_item.dir == PropagationDirection::Forward {
let is_forward_visited = local_data
.generation_forward
.swap(self.generation, Ordering::Relaxed)
== self.generation;
if !is_forward_visited {
local_data.backward_dependencies.increment_unresolved();
for (n, edge_type) in next_neighbors(PropagationDirection::Forward) {
let data = local_view.node_weight(n).unwrap();
match edge_type {
GraphEdgeType::Delay | GraphEdgeType::Virtual => {
data.forward_dependencies.increment_unresolved();
}
GraphEdgeType::Constraint => {
data.backward_dependencies.increment_unresolved();
}
}
worklist.push(WorkItem::new_forward(n));
}
}
}
let is_backward_visited = local_data
.generation_backward
.swap(self.generation, Ordering::Relaxed)
== self.generation;
if is_backward_visited {
return;
}
for (n, edge_type) in next_neighbors(PropagationDirection::Backward) {
let data = local_view.node_weight(n).unwrap();
match edge_type {
GraphEdgeType::Delay | GraphEdgeType::Virtual => {
data.backward_dependencies.increment_unresolved();
}
GraphEdgeType::Constraint => {}
GraphEdgeType::Virtual => {}
}
worklist.push(WorkItem::new_backward(n));
}
}
}