1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// SPDX-FileCopyrightText: 2023 Thomas Kramer <code@tkramer.ch>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

//! Dump the timing graph in the graphviz 'dot' format.

use libreda_db::{reference_access::NetlistReferenceAccess, traits::*};
use petgraph::visit::{EdgeRef, IntoEdgeReferences, IntoNodeReferences};

use crate::{timing_graph::*, traits::*};
use pargraph::BorrowDataCell;

/// Wrapper struct for printing a timing graph in the Graphviz Dot-format.
/// This struct's implementation of `Debug` prints the timing graph in the Dot-format.
#[derive(Clone)]
pub(crate) struct TimingGraphDot<'a, N, T>
where
    T: ConstraintBase,
    N: NetlistBase,
{
    /// Netlist is needed to resolve the names of pins and nets.
    config: TimingGraphDotConfig,
    netlist: &'a N,
    timing_graph: &'a TimingGraph<N, T>,
}

impl<'a, N, T> TimingGraphDot<'a, N, T>
where
    T: ConstraintBase,
    T::Signal: std::fmt::Debug,
    N: NetlistBase,
{
    /// Format the timing graph as a DOT digraph.
    /// This is intended for debugging.
    fn dot_format(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let g = &self.timing_graph.arc_graph;

        let node_to_string = |id: &GraphNodeType<_>| -> String {
            match id {
                GraphNodeType::ForwardPropagationSource => "source".into(),
                GraphNodeType::BackwardPropagationSource => "sink".into(),
                GraphNodeType::Terminal(t) => {
                    self.netlist.terminal_ref(t).qname(crate::PATH_SEPARATOR)
                }
            }
        };

        let quote = |s: &String| -> String { s.replace('"', r#"\""#) };

        writeln!(f, "digraph {{")?;

        for (idx, weight) in g.node_references() {
            let label = node_to_string(&weight.node_type);
            let node_data = weight.borrow_data_cell().try_read().unwrap();
            let actual_signal = &node_data.signal;
            let required_signal = &node_data.required_signal;
            writeln!(
                f,
                r#"  {} [ label = "{} {} unres_fwd={} unres_bwd={} aat={:?} rat={:?}" ]"#,
                idx.index(),
                idx.index(),
                quote(&label),
                weight.forward_dependencies.num_unresolved(),
                weight.backward_dependencies.num_unresolved(),
                actual_signal.is_some(),
                required_signal.is_some(),
            )?;
        }

        writeln!(f)?;

        let edges = g.edge_references().filter(|e| match e.weight().edge_type {
            GraphEdgeType::Delay => self.config.include_delay_arcs,
            GraphEdgeType::Constraint => self.config.include_constraint_arcs,
            GraphEdgeType::Virtual => true,
        });

        for e in edges {
            let label = format!(
                "{:?} {:?}",
                e.weight().edge_type,
                e.weight().borrow_data_cell().try_read().unwrap().delay
            );

            let color = match e.weight().edge_type {
                GraphEdgeType::Delay => self.config.delay_arc_color.as_str(),
                GraphEdgeType::Constraint => self.config.constraint_arc_color.as_str(),
                _ => "black",
            };

            writeln!(
                f,
                r#"  {} -> {} [ label = "{}", color = "{}" ]"#,
                e.source().index(),
                e.target().index(),
                quote(&label),
                color
            )?;
        }

        writeln!(f, "}}")
    }
}

#[derive(Clone, Debug)]
pub(crate) struct TimingGraphDotConfig {
    pub include_delay_arcs: bool,
    pub include_constraint_arcs: bool,
    pub constraint_arc_color: String,
    pub delay_arc_color: String,
}

impl Default for TimingGraphDotConfig {
    fn default() -> Self {
        Self {
            include_delay_arcs: true,
            include_constraint_arcs: true,
            constraint_arc_color: "green".into(),
            delay_arc_color: "black".into(),
        }
    }
}

impl<'a, N: NetlistBase, T: ConstraintBase> TimingGraphDot<'a, N, T> {
    /// Create a Graphviz formatter for the timing graph.
    ///
    /// The `netlist` is needed to resolve pin/net names. Passing another netlist
    /// than used for building the timing graph might lead to a panic during formatting.
    pub fn new(timing_graph: &'a TimingGraph<N, T>, netlist: &'a N) -> Self {
        Self::new_with_config(timing_graph, netlist, Default::default())
    }

    /// Create a Graphviz formatter for the timing graph.
    ///
    /// The `netlist` is needed to resolve pin/net names. Passing another netlist
    /// than used for building the timing graph might lead to a panic during formatting.
    pub fn new_with_config(
        timing_graph: &'a TimingGraph<N, T>,
        netlist: &'a N,
        config: TimingGraphDotConfig,
    ) -> Self {
        Self {
            netlist,
            timing_graph,
            config,
        }
    }
}
impl<'a, N: NetlistBase, T: ConstraintBase> std::fmt::Debug for TimingGraphDot<'a, N, T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.dot_format(f)
    }
}