libreda_sta/models/
nldm_cell_delay.rs

1// Copyright (c) 2021-2021 Thomas Kramer.
2// SPDX-FileCopyrightText: 2022 Thomas Kramer <code@tkramer.ch>
3//
4// SPDX-License-Identifier: AGPL-3.0-or-later
5
6//! Implementation of the [`CellDelayModel`] using the non-linear delay model (NLDM)
7//! with data from a liberty library.
8
9use std::collections::HashSet;
10
11use fnv::FnvHashMap;
12use libreda_logic::logic_value::Logic3;
13use num_traits::Zero;
14use smallvec::SmallVec;
15use uom::si::f64::{Capacitance, Time};
16
17use crate::liberty_library::{LibertyTimingLibrary, Pin};
18use crate::traits::cell_logic_model::{LogicModel, Unateness};
19use crate::traits::timing_library::TimingLibrary;
20use crate::{traits::*, DelayArcArg, RiseFall};
21use crate::{ConstraintArcArg, StaMode};
22use libreda_db::prelude::NetlistBase;
23use libreda_db::traits::NetlistIds;
24
25/// # Parameters
26/// `'a`: Lifetime of the liberty library.
27#[derive(Debug)]
28pub struct NLDMCellModel<'a, N: NetlistIds> {
29    sta_mode: StaMode,
30    /// Choose between using NLDM or a constant delay.
31    delay_model_type: DelayModelType,
32    library: &'a LibertyTimingLibrary<N::CellId, N::PinId>,
33    /// Table for quickly finding the input capacitance of a pin.
34    /// Capacitance is available also in `pin_data` but access might be faster using this field.
35    pin_capacitances: FnvHashMap<N::PinId, NLDMOutputLoad>,
36    /// Lookup-table for pin data (capacitance, delay arcs, constraint arcs, ...).
37    pin_data: FnvHashMap<N::PinId, &'a Pin<N::PinId>>,
38    /// Cache the pin ordering for all the cells.
39    // TODO: remove
40    ordered_pins: FnvHashMap<N::CellId, Vec<N::PinId>>,
41
42    /// Warn once about negative slew.
43    warn_negative_slew: std::sync::Once,
44    warn_negative_delay: std::sync::Once,
45}
46
47/// Specify what delay model to use.
48/// This implementation of the NLDM delay model can also fall back to a constant delay model.
49#[derive(Copy, Debug, Clone, PartialEq)]
50pub enum DelayModelType {
51    /// Use the same constant delay for all delay arcs.
52    /// Used for example for levelization.
53    Constant(Time),
54    /// Use the non-linear delay model.
55    NLDM,
56}
57
58/// Index type for selecting minimal or maximal values.
59#[derive(Copy, Debug, Clone, PartialEq, Eq, Hash)]
60pub enum MinMax {
61    /// Earliest.
62    Min = 0,
63    /// Latest.
64    Max = 1,
65}
66
67// /// Index type for selecting edge types.
68// #[derive(Copy, Debug, Clone, PartialEq, Eq, Hash)]
69// pub enum RiseFall {
70//     /// Rising edge.
71//     Rise = 0,
72//     /// Falling edge.
73//     Fall = 2,
74// }
75
76// impl From<RiseFall> for timing_library::RiseFall {
77//     fn from(value: RiseFall) -> Self {
78//         match value {
79//             RiseFall::Rise => timing_library::RiseFall::Rise,
80//             RiseFall::Fall => timing_library::RiseFall::Fall,
81//         }
82//     }
83// }
84
85/// Slew rate and edge polarity of a signal.
86#[derive(Copy, Debug, Clone, PartialEq)]
87pub struct NLDMSignal {
88    /// Min and max transition times.
89    slew: [Time; 2],
90    /// Min and max arrival times.
91    delay: [Time; 2],
92    /// Logic value on which the signal stabilizes.
93    // TODO: This is redundant and already stored in `edge_polarity`.
94    logic_value: Logic3,
95    /// Type of the transition.
96    edge_polarity: SignalTransitionType,
97}
98
99impl NLDMSignal {
100    /// Create a new signal with a given arrival time.
101    /// Other values are set to defaults.
102    pub fn new(slew_rate: Time, arrival_time: Time) -> Self {
103        Self {
104            edge_polarity: SignalTransitionType::Any,
105            logic_value: Logic3::X,
106            slew: [slew_rate; 2],
107            delay: [arrival_time; 2],
108        }
109    }
110
111    /// Set the polarity of the signal transition.
112    /// Default is [`EdgePolarity::Unknown`].
113    pub fn set_polarity(&mut self, edge_polarity: SignalTransitionType) {
114        self.edge_polarity = edge_polarity;
115    }
116
117    /// Set the polarity of the signal transition.
118    /// Default is [`EdgePolarity::Unknown`].
119    pub fn with_polarity(mut self, edge_polarity: SignalTransitionType) -> Self {
120        self.set_polarity(edge_polarity);
121        self
122    }
123
124    /// Get the minimum/maximum slew for the rise or fall edge.
125    pub fn slew(&self, minmax: MinMax) -> Time {
126        self.slew[minmax as usize]
127    }
128
129    /// Get the minimum/maximum delay.
130    pub fn delay(&self, minmax: MinMax) -> Time {
131        self.delay[minmax as usize]
132    }
133
134    fn slew_mut(&mut self, minmax: MinMax) -> &mut Time {
135        &mut self.slew[minmax as usize]
136    }
137
138    fn delay_mut(&mut self, minmax: MinMax) -> &mut Time {
139        &mut self.delay[minmax as usize]
140    }
141
142    /// Set the slew rate for all corners.
143    pub fn set_slew_rates(&mut self, slew_rate: Time) {
144        assert!(slew_rate >= Time::zero(), "slew rate must be positive");
145        self.slew = [slew_rate; 2];
146    }
147    /// Set the arrival times for all corners.
148    pub fn set_arrival_times(&mut self, arrival_time: Time) {
149        self.delay = [arrival_time; 2];
150    }
151
152    /// Set the slew rate for the specified corner.
153    pub fn set_slew_rate(&mut self, corner: MinMax, slew_rate: Time) {
154        *self.slew_mut(corner) = slew_rate;
155    }
156    /// Set the slew rate for the specified corner.
157    pub fn set_arrival_time(&mut self, corner: MinMax, arrival_time: Time) {
158        *self.delay_mut(corner) = arrival_time;
159    }
160
161    /// Take this new arrival time into acount and keep track of min/max arrival times.
162    fn summarize_arrival_time(&mut self, arrival_time: Time) {
163        self.set_arrival_time(MinMax::Min, arrival_time.min(self.delay(MinMax::Min)));
164        self.set_arrival_time(MinMax::Max, arrival_time.max(self.delay(MinMax::Max)));
165    }
166    /// Accumulate transition time.
167    fn acc_slew(&mut self, slew: Time) {
168        self.set_slew_rate(MinMax::Min, slew.min(self.slew(MinMax::Min)));
169        self.set_slew_rate(MinMax::Max, slew.max(self.slew(MinMax::Max)));
170    }
171
172    /// Set the logic value of the signal (after transition).
173    pub fn set_logic_value(&mut self, value: Logic3) {
174        self.logic_value = value;
175    }
176    /// Set the slew rate for all corners.
177    pub fn with_slew_rates(mut self, slew_rate: Time) -> Self {
178        self.set_slew_rates(slew_rate);
179        self
180    }
181    /// Set the arrival times for all corners.
182    pub fn with_arrival_times(mut self, arrival_time: Time) -> Self {
183        self.set_arrival_times(arrival_time);
184        self
185    }
186
187    /// Set the slew rate for the specified corner and transition type.
188    pub fn with_slew_rate(mut self, corner: MinMax, slew_rate: Time) -> Self {
189        self.set_slew_rate(corner, slew_rate);
190        self
191    }
192    /// Set the slew rate for the specified corner and transition type.
193    pub fn with_arrival_time(mut self, corner: MinMax, arrival_time: Time) -> Self {
194        self.set_arrival_time(corner, arrival_time);
195        self
196    }
197
198    /// Set the logic value of the signal (after transition).
199    pub fn with_logic_value(mut self, value: Logic3) -> Self {
200        self.set_logic_value(value);
201        self
202    }
203}
204
205impl Signal for NLDMSignal {
206    type LogicValue = Logic3;
207
208    fn logic_value(&self) -> Self::LogicValue {
209        self.logic_value
210    }
211
212    fn transition_type(&self) -> SignalTransitionType {
213        self.edge_polarity
214    }
215
216    fn with_transition_type(mut self, trans: SignalTransitionType) -> Self {
217        self.edge_polarity = trans;
218        self
219    }
220}
221
222/// Difference of two arrival times.
223#[derive(Copy, Debug, Clone, PartialEq)]
224pub struct NLDMDelay {
225    /// min-rise, min-fall, max-rise, max-fall
226    delay: [Time; 2],
227}
228
229impl NLDMDelay {
230    fn get(&self, minmax: MinMax) -> Time {
231        self.delay[minmax as usize]
232    }
233}
234
235impl Zero for NLDMDelay {
236    fn zero() -> Self {
237        Self {
238            delay: [Zero::zero(); 2],
239        }
240    }
241
242    fn is_zero(&self) -> bool {
243        self.delay.iter().all(|d| d.is_zero())
244    }
245}
246impl std::ops::Add for NLDMDelay {
247    type Output = Self;
248
249    fn add(self, rhs: Self) -> Self::Output {
250        Self {
251            delay: std::array::from_fn(|i| self.delay[i] + rhs.delay[i]),
252        }
253    }
254}
255
256/// Slew rate and edge polarity of a signal.
257#[derive(Copy, Debug, Clone, PartialEq)]
258pub struct NLDMRequiredSignal {
259    /// Earliest arrival time such that constraints are met.
260    pub earliest_arrival_time: Option<Time>,
261    /// Latest arrival time such that constraints are met.
262    pub latest_arrival_time: Option<Time>,
263}
264
265impl NLDMRequiredSignal {
266    /// Create a required signal.
267    pub fn new(earliest: Option<Time>, latest: Option<Time>) -> Self {
268        Self {
269            earliest_arrival_time: earliest,
270            latest_arrival_time: latest,
271        }
272    }
273}
274impl RequiredSignal for NLDMRequiredSignal {}
275
276/// Slew rate and edge polarity of a signal.
277#[derive(Copy, Debug, Clone, PartialEq, Default)]
278pub struct NLDMSlack {
279    /// actual arrival time - earliest arrival time
280    pub hold_slack: Option<Time>,
281    /// latest arrival time - actual arrival time
282    pub setup_slack: Option<Time>,
283}
284
285/// Load capacitance.
286#[derive(Copy, Clone, Debug, Default)]
287pub struct NLDMOutputLoad {
288    load_capacitance_rise: Capacitance,
289    load_capacitance_fall: Capacitance,
290}
291
292impl NLDMOutputLoad {
293    fn get(&self, edge_type: RiseFall) -> Capacitance {
294        match edge_type {
295            RiseFall::Rise => self.load_capacitance_rise,
296            RiseFall::Fall => self.load_capacitance_fall,
297        }
298    }
299}
300
301impl Zero for NLDMOutputLoad {
302    fn zero() -> Self {
303        Self {
304            load_capacitance_rise: Zero::zero(),
305            load_capacitance_fall: Zero::zero(),
306        }
307    }
308
309    fn is_zero(&self) -> bool {
310        self.load_capacitance_rise.is_zero() && self.load_capacitance_fall.is_zero()
311    }
312}
313
314impl std::ops::Add for NLDMOutputLoad {
315    type Output = Self;
316
317    fn add(self, rhs: Self) -> Self::Output {
318        Self {
319            load_capacitance_rise: self.load_capacitance_rise + rhs.load_capacitance_rise,
320            load_capacitance_fall: self.load_capacitance_fall + rhs.load_capacitance_fall,
321        }
322    }
323}
324
325impl<'a, N: NetlistBase> NLDMCellModel<'a, N> {
326    /// Create a new NLDM cell model based on a liberty library.
327    pub fn new(library: &'a LibertyTimingLibrary<N::CellId, N::PinId>, netlist: &N) -> Self {
328        // TODO: remove
329        let ordered_pins = netlist
330            .each_cell()
331            .map(|c| {
332                let pins = netlist.each_pin_vec(&c);
333                (c, pins)
334            })
335            .collect();
336
337        let mut model = Self {
338            sta_mode: StaMode::Early,
339            library,
340            pin_capacitances: Default::default(),
341            pin_data: Default::default(),
342            delay_model_type: DelayModelType::NLDM,
343            ordered_pins,
344            warn_negative_slew: std::sync::Once::new(),
345            warn_negative_delay: std::sync::Once::new(),
346        };
347
348        model.init(netlist);
349
350        model
351    }
352
353    /// Choose between NLDM and constant delays.
354    pub fn set_delay_model_type(&mut self, model_type: DelayModelType) {
355        self.delay_model_type = model_type
356    }
357
358    fn init(&mut self, netlist: &N) {
359        self.init_pin_table(netlist)
360    }
361
362    /// Create a lookup-table for the default pin capacitances.
363    fn init_pin_table(&mut self, netlist: &N) {
364        for cell_id in netlist.each_cell() {
365            for pin_id in netlist.each_pin(&cell_id) {
366                let pin_data = self.library.get_pin(&cell_id, &pin_id);
367
368                if let Some(pin_data) = pin_data {
369                    self.pin_capacitances.insert(
370                        pin_id.clone(),
371                        NLDMOutputLoad {
372                            load_capacitance_rise: pin_data.capacitance_rise,
373                            load_capacitance_fall: pin_data.capacitance_fall,
374                        },
375                    );
376                    self.pin_data.insert(pin_id, pin_data);
377                }
378            }
379        }
380    }
381}
382
383impl<'a, N: NetlistBase> LoadBase for NLDMCellModel<'a, N> {
384    type Load = NLDMOutputLoad;
385
386    fn sum_loads(&self, load1: &Self::Load, load2: &Self::Load) -> Self::Load {
387        *load1 + *load2
388    }
389}
390
391impl<'a, N: NetlistBase> TimingBase for NLDMCellModel<'a, N> {
392    type Signal = NLDMSignal;
393    type LogicValue = Logic3;
394}
395
396impl<'a, N: NetlistBase> DelayBase for NLDMCellModel<'a, N> {
397    type Delay = NLDMDelay;
398
399    fn summarize_delays(&self, a: &Self::Signal, b: &Self::Signal) -> Self::Signal {
400        let logic_value = if a.logic_value == b.logic_value {
401            a.logic_value
402        } else {
403            Default::default()
404        };
405
406        let reduction_fns = [Time::min, Time::max];
407
408        let delay = std::array::from_fn(|i| (reduction_fns[i])(a.delay[i], b.delay[i]));
409        let slew = std::array::from_fn(|i| (reduction_fns[i])(a.slew[i], b.slew[i]));
410
411        use SignalTransitionType::*;
412        let edge_polarity = match (a.edge_polarity, b.edge_polarity) {
413            (Any, _) | (_, Any) => Any,
414            (Rise, Fall) | (Fall, Rise) => Any,
415            (Rise, _) | (_, Rise) => Rise,
416            (Fall, _) | (_, Fall) => Fall,
417            (Constant(c1), Constant(c2)) if c1 == c2 => Constant(c1),
418            (Constant(_), Constant(_)) => Constant(Logic3::X),
419        };
420
421        NLDMSignal {
422            edge_polarity,
423            delay,
424            slew,
425            logic_value,
426        }
427    }
428
429    fn get_delay(&self, from: &Self::Signal, to: &Self::Signal) -> Self::Delay {
430        NLDMDelay {
431            delay: std::array::from_fn(|i| to.delay[i] - from.delay[i]),
432        }
433    }
434}
435
436impl<'a, N: NetlistBase> CellModel<N> for NLDMCellModel<'a, N> {
437    fn ordered_pins(&self, cell: &N::CellId) -> Vec<N::PinId> {
438        self.ordered_pins
439            .get(cell)
440            .expect("pin ordering not cached for this cell")
441            .clone()
442    }
443}
444
445impl<'a, N: NetlistBase> CellLoadModel<N> for NLDMCellModel<'a, N> {
446    fn input_pin_load(
447        &self,
448        input_pin: &N::PinId,
449        _other_inputs: &impl Fn(&N::PinId) -> Option<Self::LogicValue>,
450    ) -> Self::Load {
451        self.pin_capacitances
452            .get(input_pin)
453            .cloned()
454            .unwrap_or(self.zero())
455    }
456
457    fn zero(&self) -> Self::Load {
458        Default::default()
459    }
460}
461
462impl<'a, N: NetlistBase> CellDelayModel<N> for NLDMCellModel<'a, N> {
463    fn cell_output(
464        &self,
465        netlist: &N,
466        arc: &CellDelayArc<N::PinId>,
467        input_signal: &Self::Signal,
468        output_load: &Self::Load,
469        _other_inputs: &impl Fn(&N::PinId) -> Option<Self::LogicValue>,
470    ) -> Option<Self::Signal> {
471        debug_assert!(
472            netlist.parent_cell_of_pin(&arc.input_pin.0)
473                == netlist.parent_cell_of_pin(&arc.output_pin.0),
474            "start and end of the delay arc must belong to the same cell."
475        );
476
477        let cell = netlist.parent_cell_of_pin(&arc.input_pin.0);
478
479        debug_assert_eq!(
480            SignalTransitionType::from(arc.input_pin.1),
481            input_signal.transition_type(),
482            "edge type of input signal is not consistent with the timing arc"
483        );
484
485        if let SignalTransitionType::Constant(c) = input_signal.transition_type() {
486            // Propagate constant.
487            todo!("handle constant input");
488        }
489
490        let input_edge_polarity = match input_signal.transition_type() {
491            SignalTransitionType::Constant(_) => unreachable!("should be handled above"),
492            SignalTransitionType::Rise => RiseFall::Rise,
493            SignalTransitionType::Fall => RiseFall::Fall,
494            SignalTransitionType::Any => {
495                panic!("edge polarity of signal should match the edge polarity of the graph node")
496            }
497        };
498
499        assert_eq!(input_edge_polarity, arc.input_pin.1,
500            "Actual edge polarity of the input signal does not match the edge polarity of the graph node.");
501
502        // Store `(minmax, input slew, output slew)` tuples.
503        let mut slew_cache: SmallVec<[_; 1]> = Default::default();
504        // Store `(minmax, input slew, delay)` tuples.
505        let mut delay_cache: SmallVec<[_; 1]> = Default::default();
506
507        // Compute slew and delay for a certain edge type.
508        let slew_fn = |minmax: MinMax| -> Option<Time> {
509            let input_slew = input_signal.slew(minmax);
510            let cached = slew_cache
511                .iter()
512                .find(|(s, _)| s == &input_slew)
513                .map(|(_, s)| *s);
514            if let Some(slew) = cached {
515                return slew;
516            }
517
518            let slew = self
519                .library
520                .get_slew(
521                    DelayArcArg { cell: &cell, arc },
522                    input_slew,
523                    output_load.get(arc.output_pin.1),
524                )
525                // Set negative slew rates to zero.
526                .map(|s| {
527                    if s < Time::zero() {
528                        // Warn once about negative slew.
529                        self.warn_negative_slew.call_once(|| {
530                            log::warn!("Negative slewrate. Check the cell model.");
531                        });
532                    }
533                    s.max(Zero::zero())
534                });
535
536            slew_cache.push((input_slew, slew));
537            slew
538        };
539
540        let delay_fn = |minmax: MinMax| -> Option<Time> {
541            let input_slew = input_signal.slew(minmax);
542            let cached = delay_cache
543                .iter()
544                .find(|(s, _)| s == &input_slew)
545                .map(|(_, d)| *d);
546            if let Some(delay) = cached {
547                return delay;
548            }
549
550            let delay = self
551                .library
552                .get_cell_delay(
553                    DelayArcArg { cell: &cell, arc },
554                    input_signal.slew(minmax),
555                    output_load.get(arc.output_pin.1),
556                )
557                // Set negative delays to zero.
558                .map(|d| {
559                    if d < Time::zero() {
560                        // Warn once about negative delay.
561                        self.warn_negative_delay.call_once(|| {
562                            log::warn!("Negative delay. Check the cell model.");
563                        });
564                    }
565                    d.max(Zero::zero())
566                });
567            delay_cache.push((input_slew, delay));
568            delay
569        };
570
571        let args = [MinMax::Min, MinMax::Max];
572
573        let delays = args.map(delay_fn);
574        let slews = args.map(slew_fn);
575
576        for i in 0..2 {
577            assert_eq!(
578                delays[i].is_some(),
579                slews[i].is_some(),
580                "delay and slew must be defined both or none of them"
581            );
582        }
583
584        let is_all_none = delays.iter().all(Option::is_none);
585
586        if is_all_none {
587            return Default::default();
588        }
589
590        let mut s = NLDMSignal::new(Time::zero(), Time::zero());
591        s.logic_value = match arc.output_pin.1 {
592            RiseFall::Rise => Logic3::H,
593            RiseFall::Fall => Logic3::L,
594        };
595        s.edge_polarity = arc.output_pin.1.into();
596
597        let mut slew_defined = false;
598        let mut delay_defined = false;
599
600        // Add delay to arrival time of input signal.
601
602        // TODO: This is suspicious.
603        for i in 0..delays.len() {
604            if let Some(delay) = delays[i] {
605                s.delay[i] = input_signal.delay[i] + delay;
606                delay_defined = true;
607            }
608            if let Some(slew) = slews[i] {
609                s.slew[i] = slew;
610                slew_defined = true;
611            }
612        }
613
614        if !delay_defined && !slew_defined {
615            return Default::default();
616        }
617
618        debug_assert!(s.delay.iter().all(|d| d >= &Time::zero()));
619        debug_assert!(s.slew.iter().all(|s| s >= &Time::zero()));
620        debug_assert!(s.delay(MinMax::Max) >= s.delay(MinMax::Min));
621        debug_assert!(s.slew(MinMax::Max) >= s.slew(MinMax::Min));
622
623        Some(s)
624    }
625
626    fn delay_arcs(
627        &self,
628        _netlist: &N,
629        cell: &N::CellId,
630    ) -> impl Iterator<Item = CellDelayArc<N::PinId>> + '_ {
631        // Fetch cell from library.
632        let delay_arcs = &self
633            .library
634            .cells
635            .get(cell)
636            .expect("Cell not in library.")
637            .delay_arcs;
638        delay_arcs.into_iter().cloned()
639    }
640
641    // fn delay_arcs_from_pin(
642    //     &self,
643    //     netlist: &N,
644    //     input_pin: &N::PinId,
645    // ) -> Box<dyn Iterator<Item = N::PinId> + '_> {
646    //     let cell = netlist.parent_cell_of_pin(input_pin);
647
648    //     // Find the representation of the `input_pin` in the liberty library.
649    //     let pin = self
650    //         .library
651    //         .cells
652    //         .get(&cell)
653    //         .and_then(|cell| cell.pins.get(input_pin));
654
655    //     // Find the output pins of all delay arcs which start at the `input_pin`.
656    //     // Usually, an input pin has only one delay arc. So use a small SmallVec.
657    //     let output_pins: SmallVec<[_; 2]> = pin
658    //         .into_iter()
659    //         .flat_map(move |pin| pin.delay_arcs.keys())
660    //         .cloned()
661    //         .collect();
662
663    //     Box::new(output_pins.into_iter())
664    // }
665}
666
667impl<'a, N: NetlistBase> ConstraintBase for NLDMCellModel<'a, N> {
668    type Constraint = ();
669
670    type RequiredSignal = NLDMRequiredSignal;
671
672    type Slack = NLDMSlack;
673
674    fn summarize_constraints(
675        &self,
676        s1: &Self::RequiredSignal,
677        s2: &Self::RequiredSignal,
678    ) -> Self::RequiredSignal {
679        let rs = [s1, s2];
680
681        NLDMRequiredSignal {
682            earliest_arrival_time: rs
683                .iter()
684                .flat_map(|s| s.earliest_arrival_time)
685                .reduce(Time::min),
686            latest_arrival_time: rs
687                .iter()
688                .flat_map(|s| s.latest_arrival_time)
689                .reduce(Time::max),
690        }
691    }
692
693    fn solve_delay_constraint(
694        &self,
695        actual_delay: &Self::Delay,
696        required_output: &Self::RequiredSignal,
697        _actual_input: &Self::Signal,
698    ) -> Self::RequiredSignal {
699        use MinMax::*;
700        let d_max = actual_delay.get(Max);
701        let d_min = actual_delay.get(Min);
702
703        NLDMRequiredSignal {
704            earliest_arrival_time: required_output.earliest_arrival_time.map(|t| t - d_min),
705            latest_arrival_time: required_output.latest_arrival_time.map(|t| t - d_max),
706        }
707    }
708
709    fn get_slack(
710        &self,
711        actual_signal: &Self::Signal,
712        required_signal: &Self::RequiredSignal,
713    ) -> Self::Slack {
714        use MinMax::*;
715
716        let aat_earliest = actual_signal.delay(Min);
717        let aat_latest = actual_signal.delay(Max);
718
719        let hold_slack = required_signal
720            .earliest_arrival_time
721            .map(|r| aat_earliest - r);
722
723        let setup_slack = required_signal.latest_arrival_time.map(|r| r - aat_latest);
724
725        NLDMSlack {
726            hold_slack,
727            setup_slack,
728        }
729    }
730
731    fn add_clock_period(
732        &self,
733        mut required: Self::RequiredSignal,
734        clock_period: Time,
735    ) -> Self::RequiredSignal {
736        if let Some(latest) = required.latest_arrival_time.as_mut() {
737            *latest += clock_period;
738        }
739
740        required
741    }
742}
743
744impl<'a, N: NetlistBase> CellConstraintModel<N> for NLDMCellModel<'a, N> {
745    fn get_required_input(
746        &self,
747        netlist: &N,
748        arc: &CellConstraintArc<N::PinId>,
749        constrained_pin_signal: &Self::Signal,
750        related_pin_signal: &Self::Signal,
751        _other_inputs: &impl Fn(&N::PinId) -> Option<Self::Signal>,
752        output_loads: &impl Fn(&N::PinId) -> Option<Self::Load>,
753    ) -> Option<Self::RequiredSignal> {
754        let cell = netlist.parent_cell_of_pin(&arc.constrained_pin.0);
755        // Consistency check:
756        debug_assert!(
757            {
758                let cell2 = netlist.parent_cell_of_pin(&arc.related_pin.0);
759                cell == cell2
760            },
761            "Both pins of the constraint arc must belong to the same cell."
762        );
763
764        let output_load = output_loads(&arc.constrained_pin.0).unwrap_or(Zero::zero());
765
766        let hold_time = |(constrained_slew, related_slew): (MinMax, MinMax)| -> Option<Time> {
767            let constrained_pin_transition = constrained_pin_signal.slew(constrained_slew);
768            let related_pin_transition = related_pin_signal.slew(related_slew);
769
770            self.library.get_hold_constraint(
771                ConstraintArcArg { cell: &cell, arc },
772                related_pin_transition,
773                constrained_pin_transition,
774                output_load.get(arc.constrained_pin.1),
775            )
776        };
777
778        let setup_time = |(constrained_slew, related_slew): (MinMax, MinMax)| -> Option<Time> {
779            let constrained_pin_transition = constrained_pin_signal.slew(constrained_slew);
780            let related_pin_transition = related_pin_signal.slew(related_slew);
781
782            self.library.get_setup_constraint(
783                ConstraintArcArg { cell: &cell, arc },
784                related_pin_transition,
785                constrained_pin_transition,
786                output_load.get(arc.constrained_pin.1),
787            )
788        };
789
790        // TODO: Maybe only (Max, Max) is necessary. Usually setup/hold gets shorter with steeper slopes - but is that guaranteed?
791        // TODO: precompute a 'monotonic_in_slew' flag for the look-up tables. If the `monotonic_in_slew` flag is set, then is good enough to compute only for max slews.
792        let slew_combinations = [
793            (MinMax::Max, MinMax::Max),
794            (MinMax::Min, MinMax::Min),
795            (MinMax::Max, MinMax::Min),
796            (MinMax::Min, MinMax::Max),
797        ];
798
799        let max_setup_time = slew_combinations
800            .into_iter()
801            .filter_map(setup_time)
802            .reduce(Time::max);
803        let max_hold_time = slew_combinations
804            .into_iter()
805            .filter_map(hold_time)
806            .reduce(Time::max);
807
808        dbg!(max_setup_time);
809        dbg!(max_hold_time);
810
811        if max_setup_time.is_some() || max_hold_time.is_some() {
812            // Compute the arrival times which satisfy the constraint.
813            use MinMax::*;
814
815            let t_early = related_pin_signal.delay(Min);
816            let t_late = related_pin_signal.delay(Max);
817
818            let earliest_arrival_time = max_hold_time.map(|t_ho| t_late + t_ho);
819
820            let latest_arrival_time = max_setup_time.map(|t_su| t_early - t_su);
821
822            Some(NLDMRequiredSignal {
823                earliest_arrival_time,
824                latest_arrival_time,
825            })
826        } else {
827            None // Tell the timing engine that there's no constraint.
828        }
829    }
830
831    fn constraint_arcs(
832        &self,
833        _netlist: &N,
834        cell_id: &N::CellId,
835    ) -> impl Iterator<Item = CellConstraintArc<N::PinId>> + '_ {
836        // Fetch cell from library.
837        let cell = self
838            .library
839            .cells
840            .get(cell_id)
841            .expect("Cell not in library.");
842
843        let constraint_arcs: HashSet<_> = cell
844            .pins
845            // Of all pins...
846            .iter()
847            // ... get all constraint arcs
848            .flat_map(move |(constrained_pin, pin)| {
849                let constraint_arcs = pin
850                    .timing
851                    .setup_arcs
852                    .iter()
853                    .chain(pin.timing.hold_arcs.iter());
854
855                constraint_arcs.flat_map(|((related_pin, related_edge), arc)| {
856                    [
857                        (&arc.rise_constraint, RiseFall::Rise),
858                        (&arc.fall_constraint, RiseFall::Fall),
859                    ]
860                    .into_iter()
861                    .filter(|(lut, _)| lut.is_some())
862                    .map(|(_, constrained_edge)| CellConstraintArc {
863                        related_pin: (related_pin.clone(), *related_edge),
864                        constrained_pin: (constrained_pin.clone(), constrained_edge),
865                    })
866                })
867            })
868            .collect();
869
870        constraint_arcs.into_iter()
871    }
872}
873
874impl<'a, N: NetlistBase> LogicModel<N> for NLDMCellModel<'a, N> {
875    fn pin_function(
876        &self,
877        output_pin: &<N as NetlistIds>::PinId,
878    ) -> &cell_logic_model::OutputFunction<N::PinId> {
879        &self
880            .pin_data
881            .get(output_pin)
882            .expect("pin not found")
883            .output_function
884    }
885
886    fn timing_sense(
887        &self,
888        output_pin: &N::PinId,
889        related_pin: &N::PinId,
890        _other_inputs: &impl Fn(&N::PinId) -> Option<bool>,
891    ) -> Unateness {
892        self.pin_data
893            .get(output_pin)
894            .expect("pin not found")
895            .timing
896            .get_delay_lut(&(related_pin.clone(), RiseFall::Rise)) // TODO: avoid clone. Store timing sense unrelated of transition type.
897            .expect("related pin not found")
898            .timing_sense
899    }
900}