libreda_sta/
lib.rs

1// SPDX-FileCopyrightText: 2021-2024 Thomas Kramer <code@tkramer.ch>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4//! # == this crate is experimental and not stable for now ==
5//! Incremental graph-based static timing analysis (STA) for the LibrEDA framework.
6//!
7//! Timing analysis is performed on netlist data structures that implement the
8//! [`trait@NetlistBase`] trait. This makes the STA algorithm easily portable.
9//!
10//! The concept of timing, delays and constraints is abstracted by a set of [`traits`].
11//! This architecture should allow to implement simple timing models such as the non-linear delay model (NLDM)
12//! and more complicated models (such as statistical models) in a consistent way.
13//!
14
15#![deny(missing_docs)]
16#![deny(unused_imports)]
17
18mod clock_definition;
19mod cone_propagation;
20mod critical_path;
21mod graphviz;
22mod interp;
23mod levelization;
24/// Public modules
25pub mod liberty_library;
26mod lowest_common_ancestor;
27pub mod models;
28mod signal_propagation;
29pub mod traits;
30
31pub use clock_definition::ClockDefinition;
32use libreda_logic::traits::LogicOps;
33use models::clock_tag::ClockAwareModel;
34use models::clock_tag::ClockId;
35use petgraph::visit::IntoNodeReferences;
36use traits::cell_logic_model::LogicModel;
37/// Public exports.
38pub use traits::timing_library::*;
39use traits::*;
40
41mod liberty_util;
42mod timing_graph;
43
44use crate::models::clock_tag::ClockAwareInterconnectModel;
45use crate::models::clock_tag::SignalWithClock;
46use crate::models::zero_interconnect_delay::ZeroInterconnectDelayModel;
47use crate::signal_propagation::propagate_signals_incremental;
48use pargraph::BorrowDataCell;
49
50use libreda_db::netlist::prelude::TerminalId;
51use libreda_db::prelude as db;
52use libreda_db::reference_access::*;
53use libreda_db::traits::*;
54use num_traits::Zero;
55use std::fmt::Debug;
56
57use crate::traits::{CellConstraintModel, CellDelayModel, CellLoadModel};
58use fnv::{FnvHashMap, FnvHashSet};
59
60pub(crate) const PATH_SEPARATOR: &str = ":";
61
62/// Analysis mode.
63/// * `Hold`: Check for hold violations.
64/// * `Setup`: Check for setup violations.
65#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
66pub enum StaMode {
67    /// Hold analysis mode. Find shortest paths. Early mode.
68    Early,
69    /// Setup analysis mode. Find longest paths. Late mode.
70    Late,
71}
72
73/// Compute total net capacitances for each net of the top circuit.
74/// Simply sums up the input capacitances of all connected input pins for each net.
75fn compute_net_loads<N: NetlistBase, Lib: CellLoadModel<N>>(
76    top: &CellRef<N>,
77    library: Lib,
78) -> FnvHashMap<N::NetId, Lib::Load> {
79    log::debug!(
80        "compute load capacitances of all nets ({})",
81        top.num_internal_nets()
82    );
83
84    let static_input_signals = None;
85
86    // Compute all output load capacitances.
87    let net_capacitances: FnvHashMap<_, _> = top
88        .each_net()
89        // Compute the capacitance of the net.
90        .map(|net| {
91            // Sum up all gate capacitances attached to this net.
92            let total_capacitance = net
93                .each_pin_instance()
94                .map(|pin_inst| {
95                    let pin = pin_inst.pin();
96                    library.input_pin_load(&pin.id(), &|_| static_input_signals)
97                })
98                .fold(library.zero(), |cap, acc| library.sum_loads(&cap, &acc));
99
100            (net.id(), total_capacitance)
101        })
102        .collect();
103    net_capacitances
104}
105
106/// Error during static timing analysis.
107/// This includes errors in the liberty library, mismatches between netlist and library
108/// or invalid netlists (drive conflicts, etc).
109#[derive(Debug, Clone)]
110pub enum StaError {
111    /// Pin is referenced in the library but not present in the netlist.
112    PinNotFoundInNetlist(String, String),
113    /// Some cells used in the netlist cannot be found in the liberty library.
114    MissingCellsInLiberty(Vec<String>),
115    /// Something is bad with the netlist. This includes drive conflicts,
116    /// floating input pins, etc.
117    BadNetlist,
118    /// Circuit contains a combinational cycle.
119    CombinationalCycle,
120    /// Unspecified error.
121    Other(String),
122}
123
124impl std::fmt::Display for StaError {
125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126        match self {
127            StaError::PinNotFoundInNetlist(cell, pin) =>
128                write!(f, "Pin '{}' in cell '{}' is referenced in liberty library but not present in the netlist.",
129                       pin, cell),
130            StaError::MissingCellsInLiberty(cell_names) =>
131                write!(f, "Cells could not be found in liberty library: {}", cell_names.join(", ")),
132            StaError::BadNetlist => write!(f, "Something is not good with the netlist. See log for more details."),
133            StaError::CombinationalCycle => write!(f, "Circuit contains a combinational cycle. See log for more details."),
134            StaError::Other(msg) => write!(f, "STA error: {}", msg),
135        }
136    }
137}
138
139/// Simple static timing analysis engine.
140#[derive(Debug)]
141pub struct SimpleSTA<N, Lib>
142where
143    N: NetlistBase,
144    Lib: DelayBase + LoadBase + ConstraintBase,
145{
146    /// Netlist (or reference to the netlist) which contains the circuit to be analyzed.
147    netlist: N,
148    /// ID of the circuit which should be analyzed.
149    top_cell: N::CellId,
150    /// Timing-library, enhanced with clock-awareness.
151    cell_model: ClockAwareModel<Lib>,
152    /// Set to `true` iff the library has not been checked for consistency yet.
153    /// This is set to `true` again when cells are added to the circuit.
154    need_check_library: bool,
155    /// User-specified input signals.
156    input_signals: FnvHashMap<(N::PinId, RiseFall), SignalWithClock<Lib::Signal>>,
157    /// User-specified loads.
158    output_loads: FnvHashMap<N::PinId, Lib::Load>,
159    /// Constraints for output signals.
160    required_output_signals: FnvHashMap<(N::PinId, RiseFall), SignalWithClock<Lib::RequiredSignal>>,
161    clocks: FnvHashMap<TerminalId<N>, ClockDefinition<Lib::Signal>>,
162    /// Internal representation of the timing graph.
163    /// This is constructed from the netlist and kept up-to-date during incremental
164    /// changes of the netlist.
165    timing_graph: timing_graph::TimingGraph<N, ClockAwareModel<Lib>>,
166    /// Count the number of timing updates.
167    /// Used to efficiently mark nodes in the timing graph which are relevant for the current
168    /// incremental upgrade.
169    /// Used to distinguish between the first full update (needs more initialization) and
170    /// subsequent incremental updates.
171    generation: u32,
172}
173
174/// Reference to STA engine in timed state.
175#[derive(Debug)]
176pub struct Timed<'a, N, Lib>
177where
178    N: NetlistBase,
179    Lib: DelayBase + LoadBase + ConstraintBase,
180{
181    inner: &'a SimpleSTA<N, Lib>,
182}
183
184impl<'a, N, Lib> std::ops::Deref for Timed<'a, N, Lib>
185where
186    N: NetlistBase,
187    Lib: DelayBase + LoadBase + ConstraintBase,
188{
189    type Target = SimpleSTA<N, Lib>;
190
191    fn deref(&self) -> &Self::Target {
192        self.inner
193    }
194}
195
196impl<'a, N, Lib> Timed<'a, N, Lib>
197where
198    N: NetlistBase,
199    Lib: DelayBase + LoadBase + ConstraintBase,
200{
201    /// For testing only.
202    pub fn report_clock(&self, pin: TerminalId<N>, edge_polarity: RiseFall) -> Option<ClockId> {
203        self.timing_graph
204            .get_terminal_data(&pin, edge_polarity)
205            .and_then(|d| d.signal.as_ref().and_then(|s| s.clock_id()))
206    }
207
208    /// Report longest or shortest paths.
209    pub fn report_critical_paths(&self, clock_id: ClockId, setup_hold: SetupHold) -> () {
210        todo!()
211    }
212}
213
214impl<N, Lib> SimpleSTA<N, Lib>
215where
216    N: NetlistBase,
217    Lib: DelayBase + ConstraintBase,
218{
219}
220
221impl<N, Lib> SimpleSTA<N, Lib>
222where
223    N: NetlistBase,
224    Lib: DelayBase + ConstraintBase,
225{
226    /// Get ownership of the netlist data.
227    pub fn into_inner(self) -> N {
228        self.netlist
229    }
230
231    /// Shortcut to get the reference to the netlist.
232    fn netlist(&self) -> &N {
233        &self.netlist
234    }
235
236    fn top_cell_ref(&self) -> CellRef<N> {
237        self.netlist().cell_ref(&self.top_cell)
238    }
239}
240
241/// Argument type for `set_input_signal`.
242/// This way of passing arguments allows to add other optional data without breaking API changes.
243#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
244pub struct InputSignal<S> {
245    signal: SignalWithClock<S>,
246}
247
248impl<S> InputSignal<S> {
249    /// Create a new input signal argument from a signal.
250    pub fn new(signal: S) -> Self {
251        Self {
252            signal: SignalWithClock::new(signal),
253        }
254    }
255
256    /// Associate the input signal with a clock.
257    pub fn with_clock(mut self, clock_id: Option<ClockId>) -> Self {
258        self.signal = self.signal.with_clock_id(clock_id);
259        self
260    }
261}
262
263/// Argument type for `set_required_signal`.
264/// This way of passing arguments allows to add other optional data without breaking API changes.
265#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
266pub struct RequiredSignalArg<S> {
267    signal: SignalWithClock<S>,
268}
269
270impl<S> RequiredSignalArg<S> {
271    /// Create a new input signal argument from a signal.
272    pub fn new(signal: S) -> Self {
273        Self {
274            signal: SignalWithClock::new(signal),
275        }
276    }
277
278    /// Associate the input signal with a clock.
279    pub fn with_clock(mut self, clock_id: Option<ClockId>) -> Self {
280        self.signal = self.signal.with_clock_id(clock_id);
281        self
282    }
283}
284
285impl<N, Lib> SimpleSTA<N, Lib>
286where
287    N: db::NetlistBaseMT,
288    Lib: CellLoadModel<N> + CellDelayModel<N> + CellConstraintModel<N> + LogicModel<N> + Sync,
289    Lib::LogicValue: LogicOps + From<bool> + TryInto<bool>,
290{
291    /// Compute the timing information.
292    /// On success, returns the STA engine in [`TimingAvailable`] mode where
293    /// timing queries are enabled.
294    /// On error, returns a tuple with an error code and the STA engine in [`Modifiable`] mode.
295    pub fn update_timing(&mut self) -> Result<Timed<'_, N, Lib>, StaError> {
296        // Compute the timing.
297
298        match self.run_sta() {
299            Ok(_) => Ok(Timed { inner: self }),
300            Err(err) => Err(err),
301        }
302    }
303}
304
305impl<N, Lib> SimpleSTA<N, Lib>
306where
307    N: NetlistBase,
308    Lib: CellLoadModel<N> + CellDelayModel<N> + CellConstraintModel<N> + LogicModel<N>,
309    Lib::LogicValue: LogicOps + From<bool> + TryInto<bool>,
310{
311    /// Create a new static timing analysis engine which analyzes the given netlist
312    /// using timing information of the cells from the given library.
313    pub fn new(
314        netlist: N,
315        top_cell: N::CellId,
316        cell_timing_library: Lib,
317    ) -> Result<Self, StaError> {
318        let mut sta = Self {
319            top_cell,
320            netlist,
321            need_check_library: false,
322            input_signals: Default::default(),
323            output_loads: Default::default(),
324            required_output_signals: Default::default(),
325            clocks: Default::default(),
326            timing_graph: timing_graph::TimingGraph::new(),
327            generation: 0,
328            cell_model: ClockAwareModel::new(cell_timing_library),
329        };
330
331        sta.init()?;
332
333        Ok(sta)
334    }
335
336    fn init(&mut self) -> Result<(), StaError> {
337        self.check_library()?;
338        self.build_timing_graph()?;
339
340        Ok(())
341    }
342
343    /// Set the signal at a primary input.
344    ///
345    /// # Typical usages
346    /// * Set the input delay
347    /// * Set the input slew
348    pub fn set_input_signal(
349        &mut self,
350        primary_input: N::PinId,
351        signal: InputSignal<Lib::Signal>,
352    ) -> Result<(), StaError> {
353        let transition_type = signal.signal.transition_type();
354
355        let input_terminal = db::TerminalId::PinId(primary_input.clone());
356
357        match transition_type {
358            SignalTransitionType::Constant(_) => {
359                self.timing_graph
360                    .add_terminal_to_frontier(&input_terminal, Some(RiseFall::Rise));
361                self.input_signals.insert(
362                    (primary_input.clone(), RiseFall::Rise),
363                    signal.signal.clone(),
364                );
365                self.timing_graph
366                    .add_terminal_to_frontier(&input_terminal, Some(RiseFall::Fall));
367                self.input_signals
368                    .insert((primary_input, RiseFall::Fall), signal.signal);
369            }
370            SignalTransitionType::Rise => {
371                self.timing_graph
372                    .add_terminal_to_frontier(&input_terminal, Some(RiseFall::Rise));
373                self.input_signals
374                    .insert((primary_input, RiseFall::Rise), signal.signal);
375            }
376            SignalTransitionType::Fall => {
377                self.timing_graph
378                    .add_terminal_to_frontier(&input_terminal, Some(RiseFall::Fall));
379                self.input_signals
380                    .insert((primary_input, RiseFall::Fall), signal.signal);
381            }
382            SignalTransitionType::Any => {
383                // Use the same input signal (delay/slew) for rising and falling edges.
384                use SignalTransitionType::{Fall, Rise};
385                for edge in [Rise, Fall] {
386                    let mut s = signal.clone();
387                    s.signal = s.signal.with_transition_type(edge);
388                    self.set_input_signal(primary_input.clone(), s)?;
389                }
390            }
391        }
392
393        Ok(())
394    }
395
396    /// Set the load attached to a primary output.
397    pub fn set_output_load(
398        &mut self,
399        primary_output: N::PinId,
400        load: Lib::Load,
401    ) -> Result<(), StaError> {
402        self.timing_graph
403            .add_terminal_to_frontier(&db::TerminalId::PinId(primary_output.clone()), None);
404
405        self.output_loads.insert(primary_output, load);
406        Ok(())
407    }
408
409    /// Specify the timing constraint of a primary output.
410    pub fn set_required_output_signal(
411        &mut self,
412        primary_output: N::PinId,
413        required_signal: RequiredSignalArg<Lib::RequiredSignal>,
414    ) -> Result<(), StaError> {
415        // TODO: Add only the necessary nodes to the frontier. Now we add both for rise and fall.
416        let edge_type = None;
417        self.timing_graph
418            .add_terminal_to_frontier(&db::TerminalId::PinId(primary_output.clone()), edge_type);
419
420        // TODO
421        self.required_output_signals.insert(
422            (primary_output.clone(), RiseFall::Rise),
423            required_signal.signal.clone(),
424        );
425        self.required_output_signals
426            .insert((primary_output, RiseFall::Fall), required_signal.signal);
427        Ok(())
428    }
429
430    /// Specify a clock source.
431    pub fn create_clock(
432        &mut self,
433        clock_pin: TerminalId<N>,
434        mut clock_definition: ClockDefinition<Lib::Signal>,
435    ) -> Result<ClockId, StaError> {
436        assert_eq!(
437            clock_definition.rising_edge.transition_type(),
438            SignalTransitionType::Rise,
439            "rising clock edge has wrong transition type"
440        );
441        assert_eq!(
442            clock_definition.falling_edge.transition_type(),
443            SignalTransitionType::Fall,
444            "falling clock edge has wrong transition type"
445        );
446
447        let jitter = uom::si::f64::Time::zero(); // TODO
448
449        // Create an ID of the clock. This ID is a single integer used to efficiently annotate nodes
450        // in the timing graph.
451        let clock_id = self
452            .cell_model
453            .create_primary_clock(clock_definition.period, jitter);
454
455        clock_definition.clock_id = Some(clock_id);
456
457        self.timing_graph.add_terminal_to_frontier(&clock_pin, None);
458
459        // Set clock ID for an existing signal clock signals
460        for edge_polarity in [RiseFall::Fall, RiseFall::Rise] {
461            if let Some(mut node_data) = self
462                .timing_graph
463                .get_terminal_data_mut(&clock_pin, edge_polarity)
464            {
465                if let Some(s) = node_data.signal.as_mut() {
466                    s.set_clock_id(Some(clock_id));
467                }
468            }
469        }
470        self.clocks.insert(clock_pin, clock_definition);
471
472        Ok(clock_id)
473    }
474
475    /// Create graph of delay arcs.
476    fn build_timing_graph(&mut self) -> Result<(), StaError> {
477        log::debug!("create timing graph");
478        self.timing_graph = timing_graph::TimingGraph::<_, _>::build_from_netlist(
479            &self.top_cell_ref(),
480            &self.cell_model,
481            &self.cell_model,
482        )?;
483
484        Ok(())
485    }
486
487    /// Copy the defined input signals into the nodes of the timing graph.
488    fn init_input_signals(&mut self) -> Result<(), StaError> {
489        log::debug!("set input signals");
490        for ((pin, edge_polarity), input_signal) in &self.input_signals {
491            // Ignore defined input signals which are not part of the netlist.
492            let terminal = db::TerminalId::PinId(pin.clone());
493
494            self.timing_graph
495                .add_terminal_to_frontier(&terminal, Some(*edge_polarity));
496
497            if let Some(&[node_rise, node_fall]) = self.timing_graph.term2node.get(&terminal) {
498                let node = match edge_polarity {
499                    RiseFall::Rise => node_rise,
500                    RiseFall::Fall => node_fall,
501                };
502
503                let terminal_ref = self.netlist().terminal_ref(&terminal);
504                log::debug!(
505                    "Set input signal for {}: {:?}",
506                    terminal_ref.qname(PATH_SEPARATOR),
507                    input_signal
508                );
509
510                let mut node_data = self
511                    .timing_graph
512                    .get_node_data_mut(node)
513                    .expect("failed to get node data");
514
515                node_data.signal = Some(input_signal.clone());
516                node_data.is_primary_input = true;
517            }
518        }
519
520        Ok(())
521    }
522
523    /// Copy the specified output signals into the timing graph.
524    fn init_required_output_signals(&mut self) -> Result<(), StaError> {
525        log::debug!("init required output signals");
526        for ((pin, edge_polarity), required_signal) in &self.required_output_signals {
527            let terminal = db::TerminalId::PinId(pin.clone());
528            self.timing_graph
529                .add_terminal_to_frontier(&terminal, Some(*edge_polarity));
530
531            // Ignore pins which are not part of the netlist.
532            if let Some(&[node_rise, node_fall]) = self.timing_graph.term2node.get(&terminal) {
533                let node = match edge_polarity {
534                    RiseFall::Rise => node_rise,
535                    RiseFall::Fall => node_fall,
536                };
537
538                let terminal_ref = self.netlist().terminal_ref(&terminal);
539                log::debug!(
540                    "Set required output signal for {}: {:?}",
541                    terminal_ref.qname(PATH_SEPARATOR),
542                    required_signal
543                );
544
545                let mut node_data = self
546                    .timing_graph
547                    .get_node_data_mut(node)
548                    .expect("failed to get node data");
549
550                node_data.required_signal = Some(required_signal.clone());
551            }
552        }
553        Ok(())
554    }
555
556    /// Copy the specified output loads into the timing graph.
557    fn init_output_loads(&mut self) -> Result<(), StaError> {
558        log::debug!("init output loads");
559        for (pin, load) in &self.output_loads {
560            let terminal = db::TerminalId::PinId(pin.clone());
561
562            // Mark rise and fall node to be updated.
563            self.timing_graph.add_terminal_to_frontier(&terminal, None);
564
565            // Ignore pins which are not part of the netlist.
566            if let Some(&nodes) = self.timing_graph.term2node.get(&terminal) {
567                let terminal_ref = self.netlist().terminal_ref(&terminal);
568                log::debug!(
569                    "Set output load for {}: {:?}",
570                    terminal_ref.qname(PATH_SEPARATOR),
571                    load
572                );
573
574                todo!();
575            }
576        }
577        Ok(())
578    }
579
580    fn init_clock_signals(&mut self) -> Result<(), StaError> {
581        log::debug!("Initialize clock signals");
582        log::debug!("Number of defined clocks: {}", self.clocks.len());
583        for (clock_terminal, clock_def) in &self.clocks {
584            assert!(
585                clock_def.clock_id.is_some(),
586                "clock_id is not set for the clock definition"
587            );
588
589            let terminal_ref = self.netlist.terminal_ref(clock_terminal);
590
591            log::debug!("Set clock for {}", terminal_ref.qname(PATH_SEPARATOR));
592
593            // Get graph nodes which represent the rising and falling edge of the clock input.
594            let nodes = self
595                .timing_graph
596                .term2node
597                .get(clock_terminal)
598                .ok_or_else(|| {
599                    StaError::Other(format!(
600                        "Cannot find clock in timing graph: {}",
601                        terminal_ref.qname(PATH_SEPARATOR)
602                    ))
603                })?;
604
605            let signals = [&clock_def.rising_edge, &clock_def.falling_edge];
606
607            // Mark clock nodes in the timing graph.
608            for (node, signal) in nodes.iter().zip(signals) {
609                // Get node data of the node which represents the clock terminal.
610                let mut node_data = self
611                    .timing_graph
612                    .get_node_data_mut(*node)
613                    .expect("graph node has no data");
614
615                node_data.signal =
616                    Some(SignalWithClock::new(signal.clone()).with_clock_id(clock_def.clock_id));
617
618                node_data.is_primary_input = true;
619            }
620
621            // Mark change for the next update.
622            self.timing_graph
623                .add_terminal_to_frontier(clock_terminal, None);
624        }
625        Ok(())
626    }
627
628    /// Compute actual and required arival times.
629    fn propagate_signals(
630        &mut self,
631        net_loads: &FnvHashMap<N::NetId, Lib::Load>,
632    ) -> Result<(), StaError>
633    where
634        Lib: Sync,
635        N: db::NetlistBaseMT,
636    {
637        log::debug!("compute actual arrival times");
638
639        self.generation += 1;
640
641        // Take and reset the frontier.
642        // For the first propagation, the frontier should consist only of the root node.
643        let frontier = self.timing_graph.take_frontier();
644
645        let zero_delay_ic_model = ZeroInterconnectDelayModel::new(&self.cell_model.inner);
646        let clock_aware_ic_model = ClockAwareInterconnectModel::new(zero_delay_ic_model);
647
648        log::debug!("propagate signals");
649
650        propagate_signals_incremental(
651            self.netlist(),
652            &self.timing_graph,
653            &self.cell_model,
654            &clock_aware_ic_model,
655            &PrecomputedOutputLoads { loads: net_loads },
656            frontier.iter().copied(),
657            self.generation,
658        );
659
660        Ok(())
661    }
662
663    /// Get a list all graph nodes sorted topologically.
664    /// Used for debug printing only.
665    fn toposort_delay_graph(&self) -> Result<Vec<TerminalId<N>>, StaError> {
666        // Create the delay arc graph by filtering the graph edges (remove constraint arcs).
667        let delay_graph =
668            petgraph::visit::EdgeFiltered::from_fn(&self.timing_graph.arc_graph, |edge_ref| {
669                edge_ref.weight().edge_type.is_delay_arc()
670            });
671
672        // Create the constraint arc graph by filtering the graph edges (remove delay arcs).
673        let _constraint_graph =
674            petgraph::visit::EdgeFiltered::from_fn(&self.timing_graph.arc_graph, |edge_ref| {
675                edge_ref.weight().edge_type.is_constraint_arc()
676            });
677
678        // TODO: (optimization) To enable parallel computation the graph should be topo-sorted with levels.
679        // Nodes have no connections to other nodes on the same level and hence for each level
680        // the values (slew, ...) can be computed for all nodes in parallel.
681        log::debug!("Sort graph nodes topologically.");
682        let topo_sorted: Vec<TerminalId<_>> = {
683            let topo = petgraph::algo::toposort(&delay_graph, None);
684            match topo {
685                Ok(sorted) => sorted
686                    .iter()
687                    .filter(|&&id| {
688                        id != self.timing_graph.aat_source_node
689                            && id != self.timing_graph.rat_source_node
690                    })
691                    .map(|id| self.timing_graph.node2term[id].clone())
692                    .collect(),
693                Err(cycle) => {
694                    log::warn!("netlist has combinational cycle");
695                    if let Some(cycle_node) = self.timing_graph.node2term.get(&cycle.node_id()) {
696                        let term_id = self.netlist().terminal_ref(cycle_node);
697                        log::warn!(
698                            "combinational cycle through node: {}",
699                            term_id.qname(PATH_SEPARATOR)
700                        );
701                    }
702                    return Err(StaError::CombinationalCycle);
703                }
704            }
705        };
706
707        Ok(topo_sorted)
708    }
709
710    fn debug_print_actual_signals(&self) {
711        // Print actual signals.
712        for (_n, weight) in self.timing_graph.arc_graph.node_references() {
713            println!(
714                "{:?}: {:?}",
715                weight.node_type,
716                weight.borrow_data_cell().try_read().unwrap().signal
717            );
718        }
719    }
720
721    fn debug_print_timing_graph(&self) {
722        // println!("Topological order of graph nodes:");
723
724        // let topo_sorted = self.toposort_delay_graph().unwrap();
725
726        // for term in &topo_sorted {
727        //     println!(
728        //         "  - {}",
729        //         self.netlist().terminal_ref(term).qname(PATH_SEPARATOR)
730        //     );
731        // }
732
733        let dot = graphviz::TimingGraphDot::new(&self.timing_graph, self.netlist());
734
735        let dot_format = format!("{:?}", dot);
736        println!("{}", dot_format);
737    }
738
739    /// Do static timing analysis of the `top` cell.
740    /// A single clock is allowed and specified with `clock_input`.
741    /// Results are just printed on stdout.
742    fn run_sta(&mut self) -> Result<(), StaError>
743    where
744        Lib: Sync,
745        N: db::NetlistBaseMT,
746    {
747        // Do sanity check on the timing library.
748        // TODO: don't do this for incremental updates or only if new types of cells have been added.
749        if self.need_check_library {
750            self.check_library()?;
751            self.need_check_library = false;
752        }
753
754        self.check_clock_sources()?;
755
756        self.init_input_signals()?;
757        self.init_output_loads()?;
758        self.init_required_output_signals()?;
759
760        self.init_clock_signals()?;
761
762        // Compute all output load capacitances.
763        // TODO: compute incrementally
764        // TODO: add user defined output loads
765        let net_loads = compute_net_loads(&self.top_cell_ref(), &self.cell_model.inner);
766
767        #[cfg(debug_assertions)]
768        self.timing_graph.check_cycles()?;
769
770        self.propagate_signals(&net_loads)?;
771        //self.debug_print_actual_signals();
772        self.debug_print_timing_graph();
773
774        Ok(())
775    }
776
777    /// Check that all cells in the circuit are covered by the library.
778    /// TODO: Maybe move this into the library adapter.
779    fn check_library(&self) -> Result<(), StaError> {
780        log::debug!("check timing library");
781
782        let top = self.top_cell_ref();
783        let netlist = self.netlist();
784
785        let mut missing_cells: FnvHashSet<N::CellId> = Default::default();
786
787        for leaf_cell in top.each_cell_dependency() {
788            // TODO: Check if cell is contained in the library.
789        }
790
791        if !missing_cells.is_empty() {
792            let cell_names: Vec<String> = missing_cells
793                .iter()
794                .map(|c| netlist.cell_name(c).into())
795                .collect();
796            log::error!(
797                "Cells not found in liberty library: {}",
798                cell_names.join(", ")
799            );
800            Err(StaError::MissingCellsInLiberty(cell_names))
801        } else {
802            Ok(())
803        }
804    }
805
806    /// Do some sanity checks.
807    fn check_clock_sources(&self) -> Result<(), StaError> {
808        log::debug!("validate clock sources");
809
810        for clock in self.clocks.keys() {
811            let clock_input = self.netlist().terminal_ref(clock);
812            if self.top_cell != clock_input.parent().id() {
813                log::error!(
814                    "clock source '{}' does not live in top cell '{}' but in '{}",
815                    clock_input.qname(PATH_SEPARATOR),
816                    self.top_cell_ref().name(),
817                    clock_input.parent().name()
818                );
819
820                return Err(StaError::Other(
821                    "clock source does not live in top cell".into(),
822                ));
823            }
824        }
825
826        Ok(())
827    }
828}
829
830/// Simple implementation of a net load model.
831/// The net loads are precomputed and stored in a hash map.
832struct PrecomputedOutputLoads<'a, Net, Load> {
833    loads: &'a FnvHashMap<Net, Load>,
834}
835
836impl<'a, Net, Load> LoadBase for PrecomputedOutputLoads<'a, Net, Load>
837where
838    Load: Zero + Clone + Debug + Send + Sync,
839{
840    type Load = Load;
841
842    fn sum_loads(&self, load1: &Self::Load, load2: &Self::Load) -> Self::Load {
843        todo!()
844    }
845}
846
847impl<'a, N, Load> InterconnectLoadModel<N> for PrecomputedOutputLoads<'a, N::NetId, Load>
848where
849    Load: Zero + Clone + Debug + Send + Sync,
850    N: db::NetlistBase,
851{
852    fn interconnect_load(&self, netlist: &N, driver_terminal: &TerminalId<N>) -> Self::Load {
853        let net = netlist.net_of_terminal(driver_terminal);
854        net.and_then(|net| self.loads.get(&net))
855            .cloned()
856            .unwrap_or(Self::Load::zero())
857    }
858}
859
860impl<'a, N, Lib> TimingQuery for Timed<'a, N, Lib>
861where
862    N: NetlistBase,
863    Lib: DelayBase + ConstraintBase,
864{
865    type NetlistIds = N;
866
867    type ArrivalTime = Lib::Signal;
868
869    type RequiredArrivalTime = Lib::RequiredSignal;
870
871    type Slack = Lib::Slack;
872
873    fn report_aat(
874        &self,
875        pin: db::TerminalId<N>,
876        edge_polarity: RiseFall,
877    ) -> Option<Self::ArrivalTime> {
878        self.timing_graph
879            .get_terminal_data(&pin, edge_polarity)
880            .and_then(|d| d.signal.as_ref().map(|s| s.inner().clone()))
881    }
882
883    fn report_rat(
884        &self,
885        pin: db::TerminalId<N>,
886        edge_polarity: RiseFall,
887    ) -> Option<Self::RequiredArrivalTime> {
888        self.timing_graph
889            .get_terminal_data(&pin, edge_polarity)
890            .and_then(|d| d.required_signal.as_ref().map(|s| s.inner().clone()))
891    }
892
893    fn report_slack(&self, pin: db::TerminalId<N>, edge_polarity: RiseFall) -> Option<Self::Slack> {
894        self.timing_graph
895            .get_terminal_data(&pin, edge_polarity)
896            .and_then(|d| {
897                let aat = d.signal.as_ref()?;
898                let rat = d.required_signal.as_ref()?;
899
900                self.cell_model.get_slack(aat, rat).into()
901            })
902    }
903
904    fn report_timing(&self) -> Vec<()> {
905        todo!()
906    }
907}
908
909#[libreda_db::derive::fill(libreda_db::derive::delegate(N))]
910impl<'a, N, Lib> db::HierarchyIds for Timed<'a, N, Lib>
911where
912    N: NetlistBase,
913    Lib: DelayBase + ConstraintBase,
914{
915}
916
917#[libreda_db::derive::fill(libreda_db::derive::delegate(N))]
918impl<'a, N, Lib> db::NetlistIds for Timed<'a, N, Lib>
919where
920    N: NetlistBase,
921    Lib: DelayBase + ConstraintBase,
922{
923}
924
925#[libreda_db::derive::fill(libreda_db::derive::delegate(N))]
926impl<'a, N, Lib> db::LayoutIds for Timed<'a, N, Lib>
927where
928    N: LayoutIds + NetlistBase,
929    Lib: DelayBase + ConstraintBase,
930{
931}
932
933#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
934impl<'b, N, Lib> db::HierarchyBase for Timed<'b, N, Lib>
935where
936    N: NetlistBase,
937    Lib: DelayBase + ConstraintBase,
938{
939    type NameType = N::NameType;
940}
941
942#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
943impl<'b, N, Lib> db::NetlistBase for Timed<'b, N, Lib>
944where
945    N: NetlistBase,
946    Lib: DelayBase + ConstraintBase,
947{
948}
949
950#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
951impl<'b, N, Lib> db::LayoutBase for Timed<'b, N, Lib>
952where
953    N: NetlistBase + LayoutBase,
954    Lib: DelayBase + ConstraintBase,
955{
956}
957
958#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
959impl<'b, N, Lib> db::L2NBase for Timed<'b, N, Lib>
960where
961    N: L2NBase,
962    Lib: DelayBase + ConstraintBase,
963{
964}
965
966#[libreda_db::derive::fill(libreda_db::derive::delegate(N))]
967impl<N, Lib> db::HierarchyIds for SimpleSTA<N, Lib>
968where
969    N: NetlistBase,
970    Lib: DelayBase + ConstraintBase,
971{
972}
973
974#[libreda_db::derive::fill(libreda_db::derive::delegate(N))]
975impl<N, Lib> db::NetlistIds for SimpleSTA<N, Lib>
976where
977    N: NetlistBase,
978    Lib: DelayBase + ConstraintBase,
979{
980}
981
982#[libreda_db::derive::fill(libreda_db::derive::delegate(N))]
983impl<N, Lib> db::LayoutIds for SimpleSTA<N, Lib>
984where
985    N: LayoutIds + NetlistBase,
986    Lib: DelayBase + ConstraintBase,
987{
988}
989
990#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
991impl<N, Lib> db::HierarchyBase for SimpleSTA<N, Lib>
992where
993    N: NetlistBase,
994    Lib: DelayBase + ConstraintBase,
995{
996    type NameType = N::NameType;
997}
998
999#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
1000impl<N, Lib> db::NetlistBase for SimpleSTA<N, Lib>
1001where
1002    N: NetlistBase,
1003    Lib: DelayBase + ConstraintBase,
1004{
1005}
1006
1007#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
1008impl<N, Lib> db::LayoutBase for SimpleSTA<N, Lib>
1009where
1010    N: NetlistBase + LayoutBase,
1011    Lib: DelayBase + ConstraintBase,
1012{
1013}
1014
1015#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
1016impl<N, Lib> db::L2NBase for SimpleSTA<N, Lib>
1017where
1018    N: L2NBase,
1019    Lib: DelayBase + ConstraintBase,
1020{
1021}
1022
1023#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
1024impl<N, Lib> db::HierarchyEdit for SimpleSTA<N, Lib>
1025where
1026    N: NetlistBase + HierarchyEdit,
1027    Lib: DelayBase + ConstraintBase + CellDelayModel<N> + CellConstraintModel<N>,
1028{
1029    fn create_cell(&mut self, name: Self::NameType) -> Self::CellId {
1030        self.netlist.create_cell(name)
1031    }
1032
1033    fn remove_cell(&mut self, cell_id: &Self::CellId) {
1034        assert!(
1035            cell_id != &self.top_cell,
1036            "cannot remove the cell which is currently selected for static timing analysis"
1037        );
1038
1039        // Remove all instances of this cell from the timing graph.
1040        let cell = self.netlist.cell_ref(cell_id);
1041        for cell_inst in cell.each_reference() {
1042            self.timing_graph.remove_cell_instance(&cell_inst);
1043        }
1044
1045        self.netlist.remove_cell(cell_id)
1046    }
1047
1048    fn create_cell_instance(
1049        &mut self,
1050        parent_cell: &Self::CellId,
1051        template_cell: &Self::CellId,
1052        name: Option<Self::NameType>,
1053    ) -> Self::CellInstId {
1054        // This potentially adds a new type of cell to the circuit.
1055        // TODO: Most often this does *not* introduce a new cell type. Now this leads unnecessary checks.
1056        self.need_check_library = true;
1057
1058        let inst = self
1059            .netlist
1060            .create_cell_instance(parent_cell, template_cell, name);
1061
1062        if parent_cell == &self.top_cell {
1063            self.timing_graph.create_cell_instance(
1064                &self.netlist.cell_instance_ref(&inst),
1065                &self.cell_model.inner,
1066                &self.cell_model.inner,
1067            )
1068        }
1069
1070        inst
1071    }
1072
1073    fn remove_cell_instance(&mut self, inst: &Self::CellInstId) {
1074        if self.top_cell == self.netlist.parent_cell(inst) {
1075            todo!("remove graph nodes")
1076        }
1077
1078        self.netlist.remove_cell_instance(inst)
1079    }
1080}
1081
1082#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
1083impl<N, Lib> db::NetlistEdit for SimpleSTA<N, Lib>
1084where
1085    N: NetlistEdit,
1086    Lib: DelayBase + ConstraintBase + CellDelayModel<N> + CellConstraintModel<N>,
1087{
1088    fn create_pin(
1089        &mut self,
1090        cell: &Self::CellId,
1091        name: Self::NameType,
1092        direction: Direction,
1093    ) -> Self::PinId {
1094        let pin_id = self.netlist.create_pin(cell, name, direction);
1095
1096        if cell == &self.top_cell {
1097            self.timing_graph
1098                .create_terminal(db::TerminalId::PinId(pin_id.clone()));
1099        } else {
1100            // Adding a pin to a cell might bring this circuit out of sync with the library.
1101            self.need_check_library = true;
1102            // Add the pin on all existing instances of `cell`.
1103            for cell_inst in self.netlist.each_cell_reference(cell) {
1104                let pin_inst = self.netlist.pin_instance(&cell_inst, &pin_id);
1105                self.timing_graph
1106                    .create_terminal(db::TerminalId::PinInstId(pin_inst));
1107            }
1108        }
1109
1110        pin_id
1111    }
1112
1113    fn remove_pin(&mut self, id: &Self::PinId) {
1114        if self.netlist.parent_cell_of_pin(id) == self.top_cell {
1115            self.timing_graph.remove_pin(id.clone());
1116        } else {
1117            // Remobing a pin might bring this circuit out of sync with the library.
1118            self.need_check_library = true;
1119            // Remove the pin from all cell instances.
1120            let pin_ref = self.netlist.pin_ref(id);
1121            let cell = pin_ref.cell();
1122            for inst in cell.each_reference() {
1123                self.timing_graph
1124                    .remove_pin_instance(inst.pin_instance(id).id());
1125            }
1126        }
1127        self.netlist.remove_pin(id)
1128    }
1129
1130    fn remove_net(&mut self, net: &Self::NetId) {
1131        if self.netlist.parent_cell_of_net(net) == self.top_cell {
1132            let result = self.timing_graph.remove_net(&self.netlist.net_ref(net));
1133            if let Err(e) = result {
1134                panic!("failed to remove net: {}", e);
1135            }
1136        }
1137        self.netlist.remove_net(net)
1138    }
1139
1140    fn connect_pin(&mut self, pin: &Self::PinId, net: Option<Self::NetId>) -> Option<Self::NetId> {
1141        let pin_ref = self.netlist.pin_ref(pin);
1142        if pin_ref.cell().id() == self.top_cell {
1143            self.timing_graph
1144                .connect_terminal(pin_ref.into_terminal(), net.clone());
1145        }
1146        self.netlist.connect_pin(pin, net)
1147    }
1148
1149    fn connect_pin_instance(
1150        &mut self,
1151        pin: &Self::PinInstId,
1152        net: Option<Self::NetId>,
1153    ) -> Option<Self::NetId> {
1154        let old_net = self.netlist.connect_pin_instance(pin, net.clone());
1155
1156        let pin_inst_ref = self.netlist.pin_instance_ref(pin);
1157        if pin_inst_ref.cell_instance().parent().id() == self.top_cell {
1158            self.timing_graph
1159                .connect_terminal(pin_inst_ref.into_terminal(), net);
1160        }
1161
1162        old_net
1163    }
1164}
1165
1166#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
1167impl<N, Lib> db::LayoutEdit for SimpleSTA<N, Lib>
1168where
1169    N: NetlistBase + LayoutEdit,
1170    Lib: DelayBase + ConstraintBase + CellDelayModel<N> + CellConstraintModel<N>,
1171{
1172    fn insert_shape(
1173        &mut self,
1174        _parent_cell: &Self::CellId,
1175        _layer: &Self::LayerId,
1176        _geometry: db::Geometry<Self::Coord>,
1177    ) -> Self::ShapeId {
1178        todo!("editing the layout of a timed circuit is not supported yet")
1179    }
1180
1181    fn remove_shape(&mut self, shape_id: &Self::ShapeId) -> Option<db::Geometry<Self::Coord>> {
1182        todo!("editing the layout of a timed circuit is not supported yet")
1183    }
1184
1185    fn replace_shape(
1186        &mut self,
1187        _shape_id: &Self::ShapeId,
1188        _geometry: db::Geometry<Self::Coord>,
1189    ) -> db::Geometry<Self::Coord> {
1190        todo!("editing the layout of a timed circuit is not supported yet")
1191    }
1192
1193    fn set_transform(
1194        &mut self,
1195        _cell_inst: &Self::CellInstId,
1196        _tf: db::SimpleTransform<Self::Coord>,
1197    ) {
1198        todo!("editing the layout of a timed circuit is not supported yet")
1199    }
1200}
1201
1202impl<N, Lib> db::L2NEdit for SimpleSTA<N, Lib>
1203where
1204    N: L2NEdit,
1205    Lib: DelayBase + ConstraintBase + CellDelayModel<N> + CellConstraintModel<N>,
1206{
1207    fn set_pin_of_shape(
1208        &mut self,
1209        shape_id: &Self::ShapeId,
1210        pin: Option<Self::PinId>,
1211    ) -> Option<Self::PinId> {
1212        let update_top_cell = self.netlist.parent_of_shape(shape_id).0 == self.top_cell;
1213
1214        if update_top_cell {
1215            // Mark the change for incremental updates.
1216            if let Some(p) = pin.clone() {
1217                self.timing_graph
1218                    .add_terminal_to_frontier(&db::TerminalId::PinId(p), None);
1219            }
1220        }
1221
1222        // Update underlying netlist.
1223        let old_pin = self.netlist.set_pin_of_shape(shape_id, pin);
1224
1225        if update_top_cell {
1226            // Mark the change for incremental updates.
1227            if let Some(p) = old_pin.clone() {
1228                self.timing_graph
1229                    .add_terminal_to_frontier(&db::TerminalId::PinId(p), None);
1230            }
1231        }
1232
1233        old_pin
1234    }
1235
1236    fn set_net_of_shape(
1237        &mut self,
1238        shape_id: &Self::ShapeId,
1239        net: Option<Self::NetId>,
1240    ) -> Option<Self::NetId> {
1241        let update_top_cell = self.netlist.parent_of_shape(shape_id).0 == self.top_cell;
1242
1243        if update_top_cell {
1244            // Mark the change for incremental updates.
1245            if let Some(n) = &net {
1246                for t in self.netlist.each_terminal_of_net(n) {
1247                    self.timing_graph.add_terminal_to_frontier(&t.cast(), None);
1248                }
1249            }
1250        }
1251
1252        // Update underlying netlist.
1253        let old_net = self.netlist.set_net_of_shape(shape_id, net);
1254
1255        if update_top_cell {
1256            // Mark the change for incremental updates.
1257            if let Some(n) = &old_net {
1258                for t in self.netlist.each_terminal_of_net(n) {
1259                    self.timing_graph.add_terminal_to_frontier(&t.cast(), None);
1260                }
1261            }
1262        }
1263
1264        old_net
1265    }
1266}
1267
1268#[test]
1269fn test_uom() {
1270    // Experiment with uom.
1271    use uom::si::capacitance::femtofarad;
1272    use uom::si::electric_current::femtoampere;
1273    use uom::si::f64::*;
1274
1275    let i = ElectricCurrent::new::<femtoampere>(1.0);
1276    let c = Capacitance::new::<femtofarad>(2.0);
1277    let _time = c / i;
1278}
1279
1280#[test]
1281fn test_uom_unit_elimination() {
1282    use uom::si::capacitance::femtofarad;
1283    use uom::si::electric_current::femtoampere;
1284    use uom::si::f64::*;
1285
1286    let i = ElectricCurrent::new::<femtoampere>(1.0);
1287    let c = Capacitance::new::<femtofarad>(2.0);
1288
1289    let ratio1 = i / i; // Division should strip the units.
1290    let ratio2 = c / c;
1291
1292    let _unitless_sum = ratio1 + ratio2;
1293}