Skip to main content

physdes/
dme_algorithm.rs

1//! Deferred Merge Embedding (DME) algorithm for clock tree synthesis.
2//!
3//! Implements the DME algorithm for constructing zero-skew clock trees
4//! with Manhattan geometry. Supports both linear and Elmore delay models.
5//!
6//! Nodes are stored in an arena (`Tree`) and referenced by `usize` index,
7//! avoiding `Rc<RefCell<>>` overhead.
8
9use std::collections::HashMap;
10
11use crate::generic::MinDist;
12use crate::interval::Interval;
13use crate::manhattan_arc::ManhattanArc;
14use crate::point::Point;
15
16/// A clock sink with name, position, and capacitance.
17#[derive(Debug, Clone)]
18pub struct Sink {
19    /// The name of this sink (e.g. "s1", "FF_1")
20    pub name: String,
21    /// The physical position of the sink in the layout
22    pub position: Point<i32, i32>,
23    /// The load capacitance of this sink
24    pub capacitance: f64,
25}
26
27impl Sink {
28    /// Creates a new sink with the given name, position, and capacitance.
29    pub fn new(name: &str, position: Point<i32, i32>, capacitance: f64) -> Self {
30        Sink {
31            name: name.to_string(),
32            position,
33            capacitance,
34        }
35    }
36}
37
38/// Node index used throughout the DME algorithm to reference nodes in the
39/// arena-allocated `Tree`.
40pub type NodeIdx = usize;
41
42/// A node in the clock tree, stored in a `Tree` arena.
43#[derive(Debug, Clone)]
44pub struct TreeNode {
45    /// The name of this node (e.g. "s0", "n1")
46    pub name: String,
47    /// The embedded position of this node in the layout
48    pub position: Point<i32, i32>,
49    /// Index of the left child node, if any
50    pub left: Option<NodeIdx>,
51    /// Index of the right child node, if any
52    pub right: Option<NodeIdx>,
53    /// Index of the parent node, if any
54    pub parent: Option<NodeIdx>,
55    /// Wire segment length from this node to its parent
56    pub wire_length: i32,
57    /// Signal delay at this node
58    pub delay: f64,
59    /// Load capacitance at this node
60    pub capacitance: f64,
61    /// Whether this node's wire needs elongation to satisfy timing
62    pub need_elongation: bool,
63}
64
65impl TreeNode {
66    /// Creates a new tree node with the given name and position.
67    ///
68    /// All other fields are initialized to their default values
69    /// (no children, no parent, zero delay/capacitance/wire length).
70    pub fn new(name: &str, position: Point<i32, i32>) -> Self {
71        TreeNode {
72            name: name.to_string(),
73            position,
74            left: None,
75            right: None,
76            parent: None,
77            wire_length: 0,
78            delay: 0.0,
79            capacitance: 0.0,
80            need_elongation: false,
81        }
82    }
83
84    /// Returns `true` if this node is a leaf (has no children).
85    pub fn is_leaf(&self) -> bool {
86        self.left.is_none() && self.right.is_none()
87    }
88}
89
90/// Arena-allocated tree of `TreeNode`s.
91///
92/// Nodes are stored in a `Vec` and referenced by their index.
93/// This avoids `Rc<RefCell<>>` while still allowing safe mutation
94/// during bottom-up merging and top-down embedding phases.
95#[derive(Debug, Clone, Default)]
96pub struct Tree {
97    nodes: Vec<TreeNode>,
98    /// Index of the root node, if the tree has been built.
99    pub root: Option<NodeIdx>,
100}
101
102impl Tree {
103    /// Creates an empty tree with no nodes.
104    pub fn new() -> Self {
105        Self::default()
106    }
107
108    /// Adds a node to the tree, returning its index.
109    pub fn add(&mut self, node: TreeNode) -> NodeIdx {
110        let idx = self.nodes.len();
111        self.nodes.push(node);
112        idx
113    }
114
115    /// Returns a shared reference to the node at the given index.
116    pub fn get(&self, idx: NodeIdx) -> &TreeNode {
117        &self.nodes[idx]
118    }
119
120    /// Returns a mutable reference to the node at the given index.
121    pub fn get_mut(&mut self, idx: NodeIdx) -> &mut TreeNode {
122        &mut self.nodes[idx]
123    }
124
125    /// Returns the number of nodes in the tree.
126    pub fn len(&self) -> usize {
127        self.nodes.len()
128    }
129
130    /// Returns `true` if the tree contains no nodes.
131    pub fn is_empty(&self) -> bool {
132        self.nodes.is_empty()
133    }
134
135    /// Returns an iterator over all nodes in the tree.
136    pub fn iter(&self) -> impl Iterator<Item = &TreeNode> {
137        self.nodes.iter()
138    }
139
140    /// Simultaneously access two distinct nodes by index (safe, checked).
141    pub fn get_pair_mut(&mut self, a: NodeIdx, b: NodeIdx) -> (&mut TreeNode, &mut TreeNode) {
142        assert_ne!(a, b, "get_pair_mut called with identical indices");
143        if a < b {
144            let (left, right) = self.nodes.split_at_mut(b);
145            (&mut left[a], &mut right[0])
146        } else {
147            let (left, right) = self.nodes.split_at_mut(a);
148            (&mut right[0], &mut left[b])
149        }
150    }
151}
152
153/// Result of a tapping-point calculation.
154///
155/// Carries both the clamped `extend_left` (guaranteed to lie in [0, distance])
156/// and the **raw** (pre-clamp) value so the caller can compute the exact
157/// wire lengths for the two children and the `need_elongation` flags.
158#[derive(Debug, Clone, Copy, PartialEq)]
159pub struct TappingResult {
160    /// Tapping-point offset from the left child, clamped to [0, distance].
161    pub extend_left: i32,
162    /// Raw (pre-clamp) tapping-point offset.
163    pub raw_extend_left: i32,
164    /// Signal delay at the tapping point.
165    pub delay_left: f64,
166}
167
168/// Abstract delay model for wire delay calculation.
169pub trait DelayCalculator {
170    /// Calculates the total wire delay for a given length and load capacitance.
171    ///
172    /// The specific formula depends on the delay model (linear or Elmore).
173    fn calculate_wire_delay(&self, length: i32, load_capacitance: f64) -> f64;
174    /// Calculates the wire delay per unit length for a given load capacitance.
175    fn calculate_wire_delay_per_unit(&self, load_capacitance: f64) -> f64;
176    /// Calculates the total wire capacitance for a given length.
177    fn calculate_wire_capacitance(&self, length: i32) -> f64;
178    /// Computes the tapping point (split location) between two subtrees to
179    /// achieve prescribed skew, given their delays and capacitances.
180    ///
181    /// Returns a `TappingResult` containing both the clamped `extend_left`
182    /// (in [0, distance]) and the raw pre-clamp value, enabling the caller
183    /// to implement the full elongation logic.
184    fn calculate_tapping_point(
185        &self,
186        distance: i32,
187        left_delay: f64,
188        right_delay: f64,
189        left_capacitance: f64,
190        right_capacitance: f64,
191    ) -> TappingResult;
192}
193
194/// Linear delay model where wire delay is proportional to wire length.
195///
196/// `delay = delay_per_unit * length`
197pub struct LinearDelayCalculator {
198    /// Delay per unit length of wire
199    pub delay_per_unit: f64,
200    /// Capacitance per unit length of wire
201    pub capacitance_per_unit: f64,
202}
203
204impl LinearDelayCalculator {
205    /// Creates a linear delay calculator with the given parameters.
206    pub fn new(delay_per_unit: f64, capacitance_per_unit: f64) -> Self {
207        LinearDelayCalculator {
208            delay_per_unit,
209            capacitance_per_unit,
210        }
211    }
212}
213
214impl DelayCalculator for LinearDelayCalculator {
215    /// Linear wire delay: $\text{delay} = \text{delay\_per\_unit} \times \text{length}$
216    fn calculate_wire_delay(&self, length: i32, _load_capacitance: f64) -> f64 {
217        self.delay_per_unit * length as f64
218    }
219    fn calculate_wire_delay_per_unit(&self, _load_capacitance: f64) -> f64 {
220        self.delay_per_unit
221    }
222    fn calculate_wire_capacitance(&self, length: i32) -> f64 {
223        self.capacitance_per_unit * length as f64
224    }
225    /// Tapping point for linear delay model:
226    ///
227    /// $$\text{skew} = \text{right\_delay} - \text{left\_delay}$$
228    ///
229    /// $$\text{raw} = \left\lfloor\frac{\text{skew} / \text{delay\_per\_unit} + \text{distance}}{2}\right\rceil$$
230    fn calculate_tapping_point(
231        &self,
232        distance: i32,
233        left_delay: f64,
234        right_delay: f64,
235        _left_capacitance: f64,
236        _right_capacitance: f64,
237    ) -> TappingResult {
238        if distance == 0 {
239            return TappingResult {
240                extend_left: 0,
241                raw_extend_left: 0,
242                delay_left: left_delay.max(right_delay),
243            };
244        }
245        let skew = right_delay - left_delay;
246        let raw = ((skew / self.delay_per_unit + distance as f64) / 2.0).round() as i32;
247        let delay_left = left_delay + raw as f64 * self.delay_per_unit;
248        let (extend_left, delay_left) = if raw < 0 {
249            (0, left_delay)
250        } else if raw > distance {
251            (distance, right_delay)
252        } else {
253            (raw, delay_left)
254        };
255        TappingResult {
256            extend_left,
257            raw_extend_left: raw,
258            delay_left,
259        }
260    }
261}
262
263/// Elmore delay model: considers distributed wire resistance and capacitance.
264///
265/// `delay = R * (C / 2 + load_capacitance)` where `R` and `C` are
266/// the total resistance and capacitance of the wire segment.
267pub struct ElmoreDelayCalculator {
268    /// Resistance per unit length of wire
269    pub unit_resistance: f64,
270    /// Capacitance per unit length of wire
271    pub unit_capacitance: f64,
272}
273
274impl ElmoreDelayCalculator {
275    /// Creates an Elmore delay calculator with the given resistance and
276    /// capacitance per unit length.
277    pub fn new(unit_resistance: f64, unit_capacitance: f64) -> Self {
278        ElmoreDelayCalculator {
279            unit_resistance,
280            unit_capacitance,
281        }
282    }
283}
284
285impl DelayCalculator for ElmoreDelayCalculator {
286    /// Elmore wire delay: $\text{delay} = R \times \left(\frac{C}{2} + C_{\text{load}}\right)$
287    ///
288    /// where $R = r_{\text{unit}} \times \text{length}$ and $C = c_{\text{unit}} \times \text{length}$.
289    fn calculate_wire_delay(&self, length: i32, load_capacitance: f64) -> f64 {
290        let r = self.unit_resistance * length as f64;
291        let c = self.unit_capacitance * length as f64;
292        r * (c / 2.0 + load_capacitance)
293    }
294    fn calculate_wire_delay_per_unit(&self, load_capacitance: f64) -> f64 {
295        self.unit_resistance * (self.unit_capacitance / 2.0 + load_capacitance)
296    }
297    fn calculate_wire_capacitance(&self, length: i32) -> f64 {
298        self.unit_capacitance * length as f64
299    }
300    /// Tapping point for Elmore delay model. Solves for split fraction $z$:
301    ///
302    /// $$z = \frac{\text{skew} + R(C_W/2 + C_{\text{right}})}{R(C_W + C_{\text{right}} + C_{\text{left}})}$$
303    ///
304    /// where $R = r \cdot \text{distance}$ and $C_W = c \cdot \text{distance}$.
305    fn calculate_tapping_point(
306        &self,
307        distance: i32,
308        left_delay: f64,
309        right_delay: f64,
310        left_capacitance: f64,
311        right_capacitance: f64,
312    ) -> TappingResult {
313        if distance == 0 {
314            return TappingResult {
315                extend_left: 0,
316                raw_extend_left: 0,
317                delay_left: left_delay.max(right_delay),
318            };
319        }
320        let skew = right_delay - left_delay;
321        let r = distance as f64 * self.unit_resistance;
322        let c_w = distance as f64 * self.unit_capacitance;
323        let z = (skew + r * (right_capacitance + c_w / 2.0))
324            / (r * (c_w + right_capacitance + left_capacitance));
325        let raw = (z * distance as f64).round() as i32;
326        let r_left = raw as f64 * self.unit_resistance;
327        let c_left = raw as f64 * self.unit_capacitance;
328        let delay_left = left_delay + r_left * (c_left / 2.0 + left_capacitance);
329        let (extend_left, delay_left) = if raw < 0 {
330            (0, left_delay)
331        } else if raw > distance {
332            (distance, right_delay)
333        } else {
334            (raw, delay_left)
335        };
336        TappingResult {
337            extend_left,
338            raw_extend_left: raw,
339            delay_left,
340        }
341    }
342}
343
344/// Results of clock skew analysis.
345#[derive(Debug, Clone)]
346pub struct SkewAnalysis {
347    /// Maximum signal delay among all sinks
348    pub max_delay: f64,
349    /// Minimum signal delay among all sinks
350    pub min_delay: f64,
351    /// Clock skew = max_delay - min_delay
352    pub skew: f64,
353    /// Individual delays for each sink
354    pub sink_delays: Vec<f64>,
355    /// Total wire length of the clock tree
356    pub total_wirelength: i32,
357    /// Name of the delay model used (e.g. "LinearDelayCalculator")
358    pub delay_model: String,
359}
360
361/// Detailed tree statistics collected from a clock tree.
362#[derive(Debug, Clone)]
363pub struct TreeStatistics {
364    /// List of all nodes with their info
365    pub nodes: Vec<NodeInfo>,
366    /// List of all wires with their info
367    pub wires: Vec<WireInfo>,
368    /// Names of all sink nodes
369    pub sinks: Vec<String>,
370    /// Total number of nodes in the tree
371    pub total_nodes: i32,
372    /// Total number of sink (leaf) nodes
373    pub total_sinks: i32,
374    /// Total number of wire segments
375    pub total_wires: i32,
376}
377
378/// Information about a single node in the clock tree.
379#[derive(Debug, Clone)]
380pub struct NodeInfo {
381    /// Node name
382    pub name: String,
383    /// Node position as `(x, y)` coordinates
384    pub position: (i32, i32),
385    /// Node type: "sink" or "internal"
386    pub node_type: String,
387    /// Signal delay at this node
388    pub delay: f64,
389    /// Load capacitance at this node
390    pub capacitance: f64,
391}
392
393/// Information about a wire segment in the clock tree.
394#[derive(Debug, Clone)]
395pub struct WireInfo {
396    /// Name of the source (parent) node
397    pub from_node: String,
398    /// Name of the destination (child) node
399    pub to_node: String,
400    /// Wire length
401    pub length: i32,
402    /// Source node position as `(x, y)` coordinates
403    pub from_pos: (i32, i32),
404    /// Destination node position as `(x, y)` coordinates
405    pub to_pos: (i32, i32),
406}
407
408// ---------------------------------------------------------------------------
409// DME algorithm
410// ---------------------------------------------------------------------------
411
412/// The DME (Deferred Merge Embedding) algorithm for clock tree synthesis.
413///
414/// Builds a prescribed-skew clock tree using a bottom-up merging phase and
415/// a top-down embedding phase. Uses an arena-allocated node representation
416/// (`Tree`) for cache efficiency. The constructed tree can be queried
417/// via `get_tree()`.
418///
419/// Supports both linear and Elmore delay models via the `DelayCalculator`
420/// trait.
421pub struct DMEAlgorithm {
422    sinks: Vec<Sink>,
423    delay_calculator: Box<dyn DelayCalculator>,
424    node_id: i32,
425    source: Option<Point<i32, i32>>,
426    tree: Tree,
427}
428
429impl DMEAlgorithm {
430    /// Creates a new DME algorithm instance with the given sinks and delay model.
431    ///
432    /// # Panics
433    ///
434    /// Panics if `sinks` is empty.
435    pub fn new(sinks: Vec<Sink>, calculator: Box<dyn DelayCalculator>) -> Self {
436        assert!(!sinks.is_empty(), "No sinks provided");
437        DMEAlgorithm {
438            sinks,
439            delay_calculator: calculator,
440            node_id: 0,
441            source: None,
442            tree: Tree::new(),
443        }
444    }
445
446    /// Creates a new DME algorithm with a specified clock source position.
447    ///
448    /// # Panics
449    ///
450    /// Panics if `sinks` is empty.
451    pub fn with_source(
452        sinks: Vec<Sink>,
453        calculator: Box<dyn DelayCalculator>,
454        source: Point<i32, i32>,
455    ) -> Self {
456        assert!(!sinks.is_empty(), "No sinks provided");
457        DMEAlgorithm {
458            sinks,
459            delay_calculator: calculator,
460            node_id: 0,
461            source: Some(source),
462            tree: Tree::new(),
463        }
464    }
465
466    /// Returns a reference to the constructed tree.
467    pub fn get_tree(&self) -> &Tree {
468        &self.tree
469    }
470
471    /// Returns a mutable reference to the constructed tree.
472    pub fn get_tree_mut(&mut self) -> &mut Tree {
473        &mut self.tree
474    }
475
476    /// Builds the clock tree and returns the root index.
477    pub fn build_clock_tree(&mut self) -> NodeIdx {
478        self.node_id = 0;
479        self.tree = Tree::new();
480
481        for s in &self.sinks {
482            let mut node = TreeNode::new(&s.name, s.position);
483            node.capacitance = s.capacitance;
484            self.tree.add(node);
485        }
486
487        let leaf_indices: Vec<NodeIdx> = (0..self.tree.len()).collect();
488        let root = self.build_merging_tree(&leaf_indices, false);
489
490        let mut merging_segments: HashMap<NodeIdx, ManhattanArc<Interval<i32>>> = HashMap::new();
491        self.compute_merging_segment(root, &mut merging_segments);
492        self.embed_node(root, None, &merging_segments);
493        self.compute_delays(root, 0.0);
494
495        self.tree.root = Some(root);
496        root
497    }
498
499    /// Build a balanced merging tree by recursive bipartition.
500    fn build_merging_tree(&mut self, node_ids: &[NodeIdx], vertical: bool) -> NodeIdx {
501        if node_ids.len() == 1 {
502            return node_ids[0];
503        }
504
505        let mut sorted: Vec<NodeIdx> = node_ids.to_vec();
506        if vertical {
507            sorted.sort_by(|&a, &b| {
508                self.tree
509                    .get(a)
510                    .position
511                    .xcoord
512                    .cmp(&self.tree.get(b).position.xcoord)
513            });
514        } else {
515            sorted.sort_by(|&a, &b| {
516                self.tree
517                    .get(a)
518                    .position
519                    .ycoord
520                    .cmp(&self.tree.get(b).position.ycoord)
521            });
522        }
523
524        let mid = sorted.len() / 2;
525        let left_child = self.build_merging_tree(&sorted[..mid], !vertical);
526        let right_child = self.build_merging_tree(&sorted[mid..], !vertical);
527
528        let id = format!("n{}", self.node_id);
529        self.node_id += 1;
530        let pos = self.tree.get(left_child).position;
531        let parent_idx = self.tree.add(TreeNode::new(&id, pos));
532
533        self.tree.get_mut(parent_idx).left = Some(left_child);
534        self.tree.get_mut(parent_idx).right = Some(right_child);
535        self.tree.get_mut(left_child).parent = Some(parent_idx);
536        self.tree.get_mut(right_child).parent = Some(parent_idx);
537
538        parent_idx
539    }
540
541    fn compute_merging_segment(
542        &mut self,
543        node: NodeIdx,
544        segments: &mut HashMap<NodeIdx, ManhattanArc<Interval<i32>>>,
545    ) -> ManhattanArc<Interval<i32>> {
546        if self.tree.get(node).is_leaf() {
547            let pos = self.tree.get(node).position;
548            let ms1 = ManhattanArc::from_point(pos);
549            let ms = ManhattanArc::new(
550                Interval::new(ms1.xcoord(), ms1.xcoord()),
551                Interval::new(ms1.ycoord(), ms1.ycoord()),
552            );
553            segments.insert(node, ms);
554            return ms;
555        }
556
557        let left = self
558            .tree
559            .get(node)
560            .left
561            .expect("Internal node missing left child");
562        let right = self
563            .tree
564            .get(node)
565            .right
566            .expect("Internal node missing right child");
567
568        let left_ms = self.compute_merging_segment(left, segments);
569        let right_ms = self.compute_merging_segment(right, segments);
570
571        let distance = left_ms.min_dist_with(&right_ms) as i32;
572
573        let (left_delay, right_delay) = {
574            let ln = self.tree.get(left);
575            let rn = self.tree.get(right);
576            (ln.delay, rn.delay)
577        };
578        let (left_cap, right_cap) = {
579            let ln = self.tree.get(left);
580            let rn = self.tree.get(right);
581            (ln.capacitance, rn.capacitance)
582        };
583
584        let tp = self.delay_calculator.calculate_tapping_point(
585            distance,
586            left_delay,
587            right_delay,
588            left_cap,
589            right_cap,
590        );
591
592        // Apply node side-effects: wire_length and need_elongation.
593        // When the raw extend_left falls outside [0, distance], the elongated
594        // branch gets the raw (pre-clamp) value so its wire exceeds the
595        // segment distance.
596        {
597            let (l_node, r_node) = self.tree.get_pair_mut(left, right);
598            l_node.wire_length = tp.extend_left;
599            r_node.wire_length = distance - tp.raw_extend_left;
600
601            if tp.raw_extend_left < 0 {
602                l_node.wire_length = 0;
603                r_node.wire_length = distance - tp.raw_extend_left;
604                r_node.need_elongation = true;
605                #[cfg(feature = "std")]
606                log::warn!(
607                    "Warning: Right node needs elongation: extend_left < 0  => extend_left \
608                     set to 0"
609                );
610            } else if tp.raw_extend_left > distance {
611                r_node.wire_length = 0;
612                l_node.wire_length = tp.raw_extend_left;
613                l_node.need_elongation = true;
614                #[cfg(feature = "std")]
615                log::warn!(
616                    "Warning: Left node needs elongation: extend_left > distance => \
617                     extend_left set to distance"
618                );
619            }
620        }
621
622        self.tree.get_mut(node).delay = tp.delay_left;
623
624        let merged_segment = left_ms.merge_with(&right_ms, tp.extend_left);
625        segments.insert(node, merged_segment);
626
627        let wire_cap = self.delay_calculator.calculate_wire_capacitance(distance);
628        self.tree.get_mut(node).capacitance = {
629            let lc = self.tree.get(left).capacitance;
630            let rc = self.tree.get(right).capacitance;
631            lc + rc + wire_cap
632        };
633
634        merged_segment
635    }
636
637    fn embed_node(
638        &mut self,
639        node: NodeIdx,
640        parent_segment: Option<&ManhattanArc<Interval<i32>>>,
641        segments: &HashMap<NodeIdx, ManhattanArc<Interval<i32>>>,
642    ) {
643        let node_segment = segments
644            .get(&node)
645            .expect("Merging segment not found for node");
646
647        if parent_segment.is_none() {
648            if let Some(src) = self.source {
649                let nearest = node_segment.nearest_point_to(&src);
650                self.tree.get_mut(node).position = nearest;
651            } else {
652                let upper = node_segment.get_upper_corner();
653                self.tree.get_mut(node).position = upper;
654            }
655        } else {
656            let parent_pos = self
657                .tree
658                .get(node)
659                .parent
660                .map(|p| self.tree.get(p).position);
661            if let Some(pp) = parent_pos {
662                let nearest = node_segment.nearest_point_to(&pp);
663                self.tree.get_mut(node).position = nearest;
664                let dist = self.tree.get(node).position.min_dist_with(&pp) as i32;
665                self.tree.get_mut(node).wire_length = dist;
666            }
667        }
668
669        let left = self.tree.get(node).left;
670        let right = self.tree.get(node).right;
671
672        if let Some(l) = left {
673            self.embed_node(l, Some(node_segment), segments);
674        }
675        if let Some(r) = right {
676            self.embed_node(r, Some(node_segment), segments);
677        }
678    }
679
680    fn compute_delays(&mut self, node: NodeIdx, parent_delay: f64) {
681        let has_parent = self.tree.get(node).parent.is_some();
682        if has_parent {
683            let wl = self.tree.get(node).wire_length;
684            let cap = self.tree.get(node).capacitance;
685            let wire_delay = self.delay_calculator.calculate_wire_delay(wl, cap);
686            self.tree.get_mut(node).delay = parent_delay + wire_delay;
687        } else {
688            self.tree.get_mut(node).delay = 0.0;
689        }
690
691        let current_delay = self.tree.get(node).delay;
692        let left = self.tree.get(node).left;
693        let right = self.tree.get(node).right;
694
695        if let Some(l) = left {
696            self.compute_delays(l, current_delay);
697        }
698        if let Some(r) = right {
699            self.compute_delays(r, current_delay);
700        }
701    }
702
703    /// Analyze clock skew from the constructed tree.
704    pub fn analyze_skew(&self, root: NodeIdx) -> SkewAnalysis {
705        let mut sink_delays = Vec::new();
706        collect_sink_delays(&self.tree, root, &mut sink_delays);
707
708        if sink_delays.is_empty() {
709            panic!("No sink delays collected");
710        }
711
712        let max_delay = sink_delays
713            .iter()
714            .cloned()
715            .fold(f64::NEG_INFINITY, f64::max);
716        let min_delay = sink_delays.iter().cloned().fold(f64::INFINITY, f64::min);
717        let skew = max_delay - min_delay;
718        let total_wl = total_wirelength(&self.tree, root);
719        #[allow(clippy::incompatible_msrv)]
720        let delay_model = std::any::type_name_of_val(&*self.delay_calculator).to_string();
721
722        SkewAnalysis {
723            max_delay,
724            min_delay,
725            skew,
726            sink_delays,
727            total_wirelength: total_wl,
728            delay_model,
729        }
730    }
731}
732
733// ---------------------------------------------------------------------------
734// Free helper functions (work with &Tree + NodeIdx)
735// ---------------------------------------------------------------------------
736
737fn collect_sink_delays(tree: &Tree, node: NodeIdx, sink_delays: &mut Vec<f64>) {
738    if tree.get(node).is_leaf() {
739        sink_delays.push(tree.get(node).delay);
740    }
741    if let Some(l) = tree.get(node).left {
742        collect_sink_delays(tree, l, sink_delays);
743    }
744    if let Some(r) = tree.get(node).right {
745        collect_sink_delays(tree, r, sink_delays);
746    }
747}
748
749fn total_wirelength(tree: &Tree, node: NodeIdx) -> i32 {
750    let mut total = tree.get(node).wire_length;
751    if let Some(l) = tree.get(node).left {
752        total += total_wirelength(tree, l);
753    }
754    if let Some(r) = tree.get(node).right {
755        total += total_wirelength(tree, r);
756    }
757    total
758}
759
760/// Extracts detailed statistics from a clock tree.
761pub fn get_tree_statistics(tree: &Tree, root: NodeIdx) -> TreeStatistics {
762    let mut stats = TreeStatistics {
763        nodes: Vec::new(),
764        wires: Vec::new(),
765        sinks: Vec::new(),
766        total_nodes: 0,
767        total_sinks: 0,
768        total_wires: 0,
769    };
770    traverse_tree(tree, root, None, &mut stats);
771    stats.total_nodes = stats.nodes.len() as i32;
772    stats.total_sinks = stats.sinks.len() as i32;
773    stats.total_wires = stats.wires.len() as i32;
774    stats
775}
776
777fn traverse_tree(tree: &Tree, node: NodeIdx, parent: Option<NodeIdx>, stats: &mut TreeStatistics) {
778    let n = tree.get(node);
779    stats.nodes.push(NodeInfo {
780        name: n.name.clone(),
781        position: (n.position.xcoord, n.position.ycoord),
782        node_type: if n.is_leaf() {
783            "sink".to_string()
784        } else {
785            "internal".to_string()
786        },
787        delay: n.delay,
788        capacitance: n.capacitance,
789    });
790
791    if n.is_leaf() {
792        stats.sinks.push(n.name.clone());
793    }
794
795    if let Some(p) = parent {
796        let pb = tree.get(p);
797        stats.wires.push(WireInfo {
798            from_node: pb.name.clone(),
799            to_node: n.name.clone(),
800            length: n.wire_length,
801            from_pos: (pb.position.xcoord, pb.position.ycoord),
802            to_pos: (n.position.xcoord, n.position.ycoord),
803        });
804    }
805
806    let left = n.left;
807    let right = n.right;
808    let _ = n;
809
810    if let Some(l) = left {
811        traverse_tree(tree, l, Some(node), stats);
812    }
813    if let Some(r) = right {
814        traverse_tree(tree, r, Some(node), stats);
815    }
816}
817
818// ---------------------------------------------------------------------------
819// Tests
820// ---------------------------------------------------------------------------
821
822#[cfg(test)]
823mod tests {
824    use super::*;
825
826    fn make_sinks(count: i32) -> Vec<Sink> {
827        (0..count)
828            .map(|i| {
829                let x = (i * 37) % 100;
830                let y = (i * 53) % 100;
831                Sink::new(
832                    &format!("s{}", i),
833                    Point::new(x, y),
834                    1.0 + (i % 5) as f64 * 0.2,
835                )
836            })
837            .collect()
838    }
839
840    fn run_tree(sinks: Vec<Sink>, calc: Box<dyn DelayCalculator>) -> (DMEAlgorithm, SkewAnalysis) {
841        let mut dme = DMEAlgorithm::new(sinks, calc);
842        let root = dme.build_clock_tree();
843        let analysis = dme.analyze_skew(root);
844        (dme, analysis)
845    }
846
847    #[test]
848    fn test_dme_skew_within_two_percent_linear() {
849        let sinks = make_sinks(8);
850        let calc = Box::new(LinearDelayCalculator::new(0.5, 0.1));
851        let (_, analysis) = run_tree(sinks, calc);
852        assert!(analysis.max_delay > 0.0);
853        let pct = analysis.skew / analysis.max_delay * 100.0;
854        assert!(
855            pct < 2.0,
856            "Skew {:.4} ({:.2}%) exceeds 2%",
857            analysis.skew,
858            pct
859        );
860    }
861
862    #[test]
863    fn test_dme_skew_within_two_percent_elmore() {
864        let sinks = make_sinks(8);
865        let calc = Box::new(ElmoreDelayCalculator::new(0.1, 0.1));
866        let (_, analysis) = run_tree(sinks, calc);
867        assert!(analysis.max_delay > 0.0);
868        let pct = analysis.skew / analysis.max_delay * 100.0;
869        assert!(
870            pct < 2.0,
871            "Skew {:.4} ({:.2}%) exceeds 2%",
872            analysis.skew,
873            pct
874        );
875    }
876
877    #[test]
878    fn test_dme_two_sinks_zero_skew() {
879        let sinks = vec![
880            Sink::new("s1", Point::new(0, 0), 1.0),
881            Sink::new("s2", Point::new(10, 0), 1.0),
882        ];
883        let (_, analysis) = run_tree(sinks, Box::new(LinearDelayCalculator::new(1.0, 0.1)));
884        assert_eq!(
885            analysis.skew, 0.0,
886            "Two symmetric sinks should have zero skew"
887        );
888        assert_eq!(analysis.total_wirelength, 10);
889    }
890
891    #[test]
892    fn test_dme_single_sink() {
893        let sinks = vec![Sink::new("s1", Point::new(5, 5), 1.0)];
894        let (dme, analysis) = run_tree(sinks, Box::new(LinearDelayCalculator::new(0.5, 0.1)));
895        assert!(dme.get_tree().get(dme.get_tree().root.unwrap()).is_leaf());
896        assert_eq!(analysis.skew, 0.0);
897        assert_eq!(analysis.sink_delays.len(), 1);
898    }
899
900    #[test]
901    fn test_dme_with_source() {
902        let sinks = vec![
903            Sink::new("s1", Point::new(-10, -10), 1.0),
904            Sink::new("s2", Point::new(10, -10), 1.0),
905            Sink::new("s3", Point::new(-10, 10), 1.0),
906            Sink::new("s4", Point::new(10, 10), 1.0),
907        ];
908        let calc = Box::new(LinearDelayCalculator::new(0.5, 0.1));
909        let mut dme = DMEAlgorithm::with_source(sinks, calc, Point::new(0, 0));
910        let root = dme.build_clock_tree();
911        let analysis = dme.analyze_skew(root);
912        assert!(analysis.max_delay > 0.0);
913        let pct = analysis.skew / analysis.max_delay * 100.0;
914        assert!(
915            pct < 2.0,
916            "Skew {:.4} ({:.2}%) exceeds 2%",
917            analysis.skew,
918            pct
919        );
920    }
921
922    #[test]
923    fn test_get_tree_statistics() {
924        let sinks = vec![
925            Sink::new("s1", Point::new(0, 0), 1.0),
926            Sink::new("s2", Point::new(10, 0), 1.0),
927        ];
928        let mut dme = DMEAlgorithm::new(sinks, Box::new(LinearDelayCalculator::new(1.0, 0.1)));
929        let root = dme.build_clock_tree();
930        let stats = get_tree_statistics(dme.get_tree(), root);
931        assert_eq!(stats.total_nodes, 3);
932        assert_eq!(stats.total_sinks, 2);
933        assert_eq!(stats.total_wires, 2);
934    }
935
936    // -----------------------------------------------------------------------
937    // Elongation unit tests (caller-side logic matching C++ TappingResult)
938    // -----------------------------------------------------------------------
939
940    /// Simulate the caller-side elongation logic from compute_merging_segment.
941    fn apply_elongation(distance: i32, tp: &TappingResult) -> (i32, i32, bool, bool) {
942        let mut left_wl = tp.extend_left;
943        let mut right_wl = distance - tp.raw_extend_left;
944        let mut left_el = false;
945        let mut right_el = false;
946        if tp.raw_extend_left < 0 {
947            left_wl = 0;
948            right_wl = distance - tp.raw_extend_left;
949            right_el = true;
950        } else if tp.raw_extend_left > distance {
951            right_wl = 0;
952            left_wl = tp.raw_extend_left;
953            left_el = true;
954        }
955        (left_wl, right_wl, left_el, right_el)
956    }
957
958    #[test]
959    fn test_linear_tapping_point_balanced() {
960        let calc = LinearDelayCalculator::new(0.5, 0.2);
961        let tp = calc.calculate_tapping_point(10, 1.0, 1.0, 0.0, 0.0);
962        assert_eq!(tp.extend_left, 5);
963        assert_eq!(tp.raw_extend_left, 5);
964        approx_eq(tp.delay_left, 1.0 + 5.0 * 0.5);
965    }
966
967    #[test]
968    fn test_linear_tapping_point_right_slower() {
969        let calc = LinearDelayCalculator::new(0.5, 0.2);
970        let tp = calc.calculate_tapping_point(10, 1.0, 3.0, 0.0, 0.0);
971        assert_eq!(tp.extend_left, 7);
972        approx_eq(tp.delay_left, 1.0 + 7.0 * 0.5);
973    }
974
975    #[test]
976    fn test_linear_tapping_point_left_slower() {
977        let calc = LinearDelayCalculator::new(0.5, 0.2);
978        let tp = calc.calculate_tapping_point(10, 3.0, 1.0, 0.0, 0.0);
979        assert_eq!(tp.extend_left, 3);
980        approx_eq(tp.delay_left, 3.0 + 3.0 * 0.5);
981    }
982
983    #[test]
984    fn test_linear_elongation_right_branch() {
985        // left.delay=10, right.delay=1, distance=10
986        // raw = -4, clamped to 0
987        let calc = LinearDelayCalculator::new(0.5, 0.2);
988        let tp = calc.calculate_tapping_point(10, 10.0, 1.0, 0.0, 0.0);
989        assert_eq!(tp.extend_left, 0);
990        assert_eq!(tp.raw_extend_left, -4);
991        approx_eq(tp.delay_left, 10.0);
992
993        let (l_wl, r_wl, l_el, r_el) = apply_elongation(10, &tp);
994        assert_eq!(l_wl, 0);
995        assert_eq!(r_wl, 14); // distance - raw = 10 - (-4) = 14
996        assert!(!l_el);
997        assert!(r_el);
998    }
999
1000    #[test]
1001    fn test_linear_elongation_left_branch() {
1002        // left.delay=1, right.delay=10, distance=10
1003        // raw = 14, clamped to 10
1004        let calc = LinearDelayCalculator::new(0.5, 0.2);
1005        let tp = calc.calculate_tapping_point(10, 1.0, 10.0, 0.0, 0.0);
1006        assert_eq!(tp.extend_left, 10);
1007        assert_eq!(tp.raw_extend_left, 14);
1008        approx_eq(tp.delay_left, 10.0);
1009
1010        let (l_wl, r_wl, l_el, r_el) = apply_elongation(10, &tp);
1011        assert_eq!(l_wl, 14); // raw = 14
1012        assert_eq!(r_wl, 0);
1013        assert!(l_el);
1014        assert!(!r_el);
1015    }
1016
1017    #[test]
1018    fn test_elmore_elongation_right_branch() {
1019        let calc = ElmoreDelayCalculator::new(0.1, 0.2);
1020        let tp = calc.calculate_tapping_point(10, 10.0, 1.0, 1.0, 1.0);
1021        assert_eq!(tp.extend_left, 0);
1022        assert_eq!(tp.raw_extend_left, -18);
1023        approx_eq(tp.delay_left, 10.0);
1024
1025        let (l_wl, r_wl, l_el, r_el) = apply_elongation(10, &tp);
1026        assert_eq!(l_wl, 0);
1027        assert_eq!(r_wl, 28); // distance - raw = 10 - (-18) = 28
1028        assert!(!l_el);
1029        assert!(r_el);
1030    }
1031
1032    #[test]
1033    fn test_elmore_elongation_left_branch() {
1034        let calc = ElmoreDelayCalculator::new(0.1, 0.2);
1035        let tp = calc.calculate_tapping_point(10, 1.0, 10.0, 1.0, 1.0);
1036        assert_eq!(tp.extend_left, 10);
1037        assert_eq!(tp.raw_extend_left, 28);
1038        approx_eq(tp.delay_left, 10.0);
1039
1040        let (l_wl, r_wl, l_el, r_el) = apply_elongation(10, &tp);
1041        assert_eq!(l_wl, 28); // raw = 28
1042        assert_eq!(r_wl, 0);
1043        assert!(l_el);
1044        assert!(!r_el);
1045    }
1046
1047    /// Helper: approximate float equality within 1e-9.
1048    fn approx_eq(a: f64, b: f64) {
1049        assert!((a - b).abs() < 1e-9, "left={}, right={}", a, b);
1050    }
1051}