libreda_sta/
cone_propagation.rs

1// SPDX-FileCopyrightText: 2023 Thomas Kramer <code@tkramer.ch>
2//
3// SPDX-License-Identifier: AGPL-3.0-or-later
4
5//! Propagate forward and backward cones from a set of frontier nodes.
6//! The cones mark the regions where the timing must potentially be updated.
7
8use std::{borrow::Borrow, sync::atomic::Ordering};
9
10use libreda_db::traits::{NetlistBaseMT, NetlistIds};
11use petgraph::{
12    data::DataMap,
13    stable_graph::NodeIndex,
14    visit::{EdgeRef, GraphBase, IntoEdgesDirected},
15};
16
17use crate::{
18    timing_graph::{EdgeData, GraphEdgeType, NodeData, TimingGraph},
19    traits::ConstraintBase,
20};
21
22use pargraph::{
23    executors::multi_thread::MultiThreadExecutor, local_view::LocalGraphView,
24    worklists::biglock_fifo::FifoWorklist, worklists::WorklistPush, ReadonlyOperator,
25};
26
27/// Mark the fan-out cones of the supplied `frontier`-nodes with the current
28/// `generation` number. Let the set of nodes in this fan-out cones be `C_fwd`.
29/// Then mark also the nodes in the fanin-cones of the nodes in `C_fwd` with
30/// the current `generation` number.
31///
32/// This is used as a preliminary step of incremental timing propagation.
33/// Only the nodes from `C_fwd` will need to update their actual signals.
34/// And only the nodes from `C_bwd` will need to update their required signals.
35pub(crate) fn propagate_cones<Netlist, CellModel>(
36    g: &TimingGraph<Netlist, CellModel>,
37    frontier: impl Iterator<Item = NodeIndex>,
38    generation: u32,
39) where
40    Netlist: NetlistBaseMT,
41    CellModel: ConstraintBase,
42{
43    let operator = ConePropagationOp { generation };
44
45    let executor = MultiThreadExecutor::new();
46
47    let worklist =
48        FifoWorklist::new_with_local_queues(frontier.map(WorkItem::new_forward).collect());
49    executor.run_readonly(worklist, operator, &g.arc_graph);
50}
51
52#[derive(Clone, Copy, PartialEq, Eq, Debug)]
53pub(crate) enum PropagationDirection {
54    Forward,
55    Backward,
56}
57
58#[derive(Clone)]
59pub(crate) struct ConePropagationOp {
60    pub generation: u32,
61}
62
63/// Work item associated with a propagation direction.
64#[derive(Clone, Copy, Debug)]
65pub(crate) struct WorkItem<N> {
66    node: N,
67    dir: PropagationDirection,
68}
69
70impl<N> WorkItem<N> {
71    pub fn new_forward(n: N) -> Self {
72        Self {
73            node: n,
74            dir: PropagationDirection::Forward,
75        }
76    }
77    fn new_backward(n: N) -> Self {
78        Self {
79            node: n,
80            dir: PropagationDirection::Backward,
81        }
82    }
83}
84
85impl<N> Borrow<N> for WorkItem<N> {
86    fn borrow(&self) -> &N {
87        &self.node
88    }
89}
90
91impl<G, N, D> ReadonlyOperator<G> for ConePropagationOp
92where
93    N: NetlistIds,
94    D: ConstraintBase,
95    G: GraphBase
96        + IntoEdgesDirected
97        + DataMap<NodeWeight = NodeData<N, D>, EdgeWeight = EdgeData<D>>,
98{
99    type WorkItem = WorkItem<G::NodeId>;
100
101    fn op(
102        &self,
103        work_item: Self::WorkItem,
104        local_view: LocalGraphView<&G>,
105        mut worklist: impl WorklistPush<Self::WorkItem>,
106    ) {
107        let local_view = &local_view;
108        let active_node = local_view.active_node();
109
110        // Find next neighbors depending on propagation direction.
111        let next_neighbors = |direction: PropagationDirection| {
112            let dir = match direction {
113                PropagationDirection::Forward => petgraph::Direction::Outgoing,
114                PropagationDirection::Backward => petgraph::Direction::Incoming,
115            };
116
117            local_view
118                .edges_directed(active_node, dir)
119                .map(move |e| match direction {
120                    PropagationDirection::Forward => (e.target(), e.weight().edge_type),
121                    PropagationDirection::Backward => (e.source(), e.weight().edge_type),
122                })
123        };
124
125        let local_data = local_view.node_weight(active_node).unwrap();
126
127        if work_item.dir == PropagationDirection::Forward {
128            let is_forward_visited = local_data
129                .generation_forward
130                .swap(self.generation, Ordering::Relaxed)
131                == self.generation;
132
133            // Do forward propagation only, if the active node was
134            // reached by forward propagation or if the active node
135            // is an initial frontier node.
136            if !is_forward_visited {
137                // The active node must be forward-propagated before it can be backward-propagated.
138                local_data.backward_dependencies.increment_unresolved();
139
140                for (n, edge_type) in next_neighbors(PropagationDirection::Forward) {
141                    let data = local_view.node_weight(n).unwrap();
142
143                    // Keep track of the number of unresolved dependencies.
144                    match edge_type {
145                        GraphEdgeType::Delay | GraphEdgeType::Virtual => {
146                            data.forward_dependencies.increment_unresolved();
147                        }
148                        GraphEdgeType::Constraint => {
149                            data.backward_dependencies.increment_unresolved();
150                        }
151                    }
152
153                    worklist.push(WorkItem::new_forward(n));
154                }
155            }
156        }
157
158        let is_backward_visited = local_data
159            .generation_backward
160            .swap(self.generation, Ordering::Relaxed)
161            == self.generation;
162
163        if is_backward_visited {
164            // Nothing to do.
165            return;
166        }
167
168        // Always do backward propagation.
169        for (n, edge_type) in next_neighbors(PropagationDirection::Backward) {
170            let data = local_view.node_weight(n).unwrap();
171
172            // Keep track of the number of unresolved dependencies.
173            match edge_type {
174                GraphEdgeType::Delay | GraphEdgeType::Virtual => {
175                    data.backward_dependencies.increment_unresolved();
176                }
177                GraphEdgeType::Constraint => {}
178            }
179
180            worklist.push(WorkItem::new_backward(n));
181        }
182    }
183}