use libreda_db::{reference_access::NetlistReferenceAccess, traits::*};
use petgraph::visit::{EdgeRef, IntoEdgeReferences, IntoNodeReferences};
use crate::{timing_graph::*, traits::*};
use pargraph::BorrowDataCell;
#[derive(Clone)]
pub(crate) struct TimingGraphDot<'a, N, T>
where
T: ConstraintBase,
N: NetlistBase,
{
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,
{
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> {
pub fn new(timing_graph: &'a TimingGraph<N, T>, netlist: &'a N) -> Self {
Self::new_with_config(timing_graph, netlist, Default::default())
}
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)
}
}