1#![deny(missing_docs)]
16#![deny(unused_imports)]
17
18mod clock_definition;
19mod cone_propagation;
20mod critical_path;
21mod graphviz;
22mod interp;
23mod levelization;
24pub 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;
37pub 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#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
66pub enum StaMode {
67 Early,
69 Late,
71}
72
73fn 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 let net_capacitances: FnvHashMap<_, _> = top
88 .each_net()
89 .map(|net| {
91 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#[derive(Debug, Clone)]
110pub enum StaError {
111 PinNotFoundInNetlist(String, String),
113 MissingCellsInLiberty(Vec<String>),
115 BadNetlist,
118 CombinationalCycle,
120 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#[derive(Debug)]
141pub struct SimpleSTA<N, Lib>
142where
143 N: NetlistBase,
144 Lib: DelayBase + LoadBase + ConstraintBase,
145{
146 netlist: N,
148 top_cell: N::CellId,
150 cell_model: ClockAwareModel<Lib>,
152 need_check_library: bool,
155 input_signals: FnvHashMap<(N::PinId, RiseFall), SignalWithClock<Lib::Signal>>,
157 output_loads: FnvHashMap<N::PinId, Lib::Load>,
159 required_output_signals: FnvHashMap<(N::PinId, RiseFall), SignalWithClock<Lib::RequiredSignal>>,
161 clocks: FnvHashMap<TerminalId<N>, ClockDefinition<Lib::Signal>>,
162 timing_graph: timing_graph::TimingGraph<N, ClockAwareModel<Lib>>,
166 generation: u32,
172}
173
174#[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 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 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 pub fn into_inner(self) -> N {
228 self.netlist
229 }
230
231 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#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
244pub struct InputSignal<S> {
245 signal: SignalWithClock<S>,
246}
247
248impl<S> InputSignal<S> {
249 pub fn new(signal: S) -> Self {
251 Self {
252 signal: SignalWithClock::new(signal),
253 }
254 }
255
256 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#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
266pub struct RequiredSignalArg<S> {
267 signal: SignalWithClock<S>,
268}
269
270impl<S> RequiredSignalArg<S> {
271 pub fn new(signal: S) -> Self {
273 Self {
274 signal: SignalWithClock::new(signal),
275 }
276 }
277
278 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 pub fn update_timing(&mut self) -> Result<Timed<'_, N, Lib>, StaError> {
296 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 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 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 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 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 pub fn set_required_output_signal(
411 &mut self,
412 primary_output: N::PinId,
413 required_signal: RequiredSignalArg<Lib::RequiredSignal>,
414 ) -> Result<(), StaError> {
415 let edge_type = None;
417 self.timing_graph
418 .add_terminal_to_frontier(&db::TerminalId::PinId(primary_output.clone()), edge_type);
419
420 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 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(); 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 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 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 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 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 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 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 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 self.timing_graph.add_terminal_to_frontier(&terminal, None);
564
565 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 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 for (node, signal) in nodes.iter().zip(signals) {
609 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 self.timing_graph
623 .add_terminal_to_frontier(clock_terminal, None);
624 }
625 Ok(())
626 }
627
628 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 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 fn toposort_delay_graph(&self) -> Result<Vec<TerminalId<N>>, StaError> {
666 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 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 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 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 let dot = graphviz::TimingGraphDot::new(&self.timing_graph, self.netlist());
734
735 let dot_format = format!("{:?}", dot);
736 println!("{}", dot_format);
737 }
738
739 fn run_sta(&mut self) -> Result<(), StaError>
743 where
744 Lib: Sync,
745 N: db::NetlistBaseMT,
746 {
747 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 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_timing_graph();
773
774 Ok(())
775 }
776
777 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 }
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 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
830struct 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 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 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 self.need_check_library = true;
1102 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 self.need_check_library = true;
1119 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 if let Some(p) = pin.clone() {
1217 self.timing_graph
1218 .add_terminal_to_frontier(&db::TerminalId::PinId(p), None);
1219 }
1220 }
1221
1222 let old_pin = self.netlist.set_pin_of_shape(shape_id, pin);
1224
1225 if update_top_cell {
1226 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 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 let old_net = self.netlist.set_net_of_shape(shape_id, net);
1254
1255 if update_top_cell {
1256 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 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; let ratio2 = c / c;
1291
1292 let _unitless_sum = ratio1 + ratio2;
1293}