1use 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#[derive(Debug)]
28pub struct NLDMCellModel<'a, N: NetlistIds> {
29 sta_mode: StaMode,
30 delay_model_type: DelayModelType,
32 library: &'a LibertyTimingLibrary<N::CellId, N::PinId>,
33 pin_capacitances: FnvHashMap<N::PinId, NLDMOutputLoad>,
36 pin_data: FnvHashMap<N::PinId, &'a Pin<N::PinId>>,
38 ordered_pins: FnvHashMap<N::CellId, Vec<N::PinId>>,
41
42 warn_negative_slew: std::sync::Once,
44 warn_negative_delay: std::sync::Once,
45}
46
47#[derive(Copy, Debug, Clone, PartialEq)]
50pub enum DelayModelType {
51 Constant(Time),
54 NLDM,
56}
57
58#[derive(Copy, Debug, Clone, PartialEq, Eq, Hash)]
60pub enum MinMax {
61 Min = 0,
63 Max = 1,
65}
66
67#[derive(Copy, Debug, Clone, PartialEq)]
87pub struct NLDMSignal {
88 slew: [Time; 2],
90 delay: [Time; 2],
92 logic_value: Logic3,
95 edge_polarity: SignalTransitionType,
97}
98
99impl NLDMSignal {
100 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 pub fn set_polarity(&mut self, edge_polarity: SignalTransitionType) {
114 self.edge_polarity = edge_polarity;
115 }
116
117 pub fn with_polarity(mut self, edge_polarity: SignalTransitionType) -> Self {
120 self.set_polarity(edge_polarity);
121 self
122 }
123
124 pub fn slew(&self, minmax: MinMax) -> Time {
126 self.slew[minmax as usize]
127 }
128
129 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 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 pub fn set_arrival_times(&mut self, arrival_time: Time) {
149 self.delay = [arrival_time; 2];
150 }
151
152 pub fn set_slew_rate(&mut self, corner: MinMax, slew_rate: Time) {
154 *self.slew_mut(corner) = slew_rate;
155 }
156 pub fn set_arrival_time(&mut self, corner: MinMax, arrival_time: Time) {
158 *self.delay_mut(corner) = arrival_time;
159 }
160
161 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 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 pub fn set_logic_value(&mut self, value: Logic3) {
174 self.logic_value = value;
175 }
176 pub fn with_slew_rates(mut self, slew_rate: Time) -> Self {
178 self.set_slew_rates(slew_rate);
179 self
180 }
181 pub fn with_arrival_times(mut self, arrival_time: Time) -> Self {
183 self.set_arrival_times(arrival_time);
184 self
185 }
186
187 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 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 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#[derive(Copy, Debug, Clone, PartialEq)]
224pub struct NLDMDelay {
225 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#[derive(Copy, Debug, Clone, PartialEq)]
258pub struct NLDMRequiredSignal {
259 pub earliest_arrival_time: Option<Time>,
261 pub latest_arrival_time: Option<Time>,
263}
264
265impl NLDMRequiredSignal {
266 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#[derive(Copy, Debug, Clone, PartialEq, Default)]
278pub struct NLDMSlack {
279 pub hold_slack: Option<Time>,
281 pub setup_slack: Option<Time>,
283}
284
285#[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 pub fn new(library: &'a LibertyTimingLibrary<N::CellId, N::PinId>, netlist: &N) -> Self {
328 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 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 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 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 let mut slew_cache: SmallVec<[_; 1]> = Default::default();
504 let mut delay_cache: SmallVec<[_; 1]> = Default::default();
506
507 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 .map(|s| {
527 if s < Time::zero() {
528 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 .map(|d| {
559 if d < Time::zero() {
560 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 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 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 }
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 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 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 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 }
829 }
830
831 fn constraint_arcs(
832 &self,
833 _netlist: &N,
834 cell_id: &N::CellId,
835 ) -> impl Iterator<Item = CellConstraintArc<N::PinId>> + '_ {
836 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 .iter()
847 .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)) .expect("related pin not found")
898 .timing_sense
899 }
900}