#![deny(missing_docs)]
#![allow(unused)] #![deny(unused_imports)]
mod cone_propagation;
mod graphviz;
mod levelization;
pub mod liberty_library;
pub mod models;
mod signal_propagation;
pub mod traits;
use petgraph::visit::IntoNodeReferences;
pub use traits::timing_library::*;
use traits::*;
mod liberty_util;
mod timing_graph;
use crate::models::zero_interconnect_delay::ZeroInterconnectDelayModel;
use crate::signal_propagation::propagate_signals_incremental;
use pargraph::BorrowDataCell;
use interp::{interp1d, interp2d};
use libreda_db::netlist::prelude::TerminalId;
use libreda_db::prelude as db;
use libreda_db::reference_access::*;
use libreda_db::traits::*;
use num_traits::Zero;
use std::collections::HashMap;
use std::fmt::Debug;
use crate::traits::{CellConstraintModel, CellDelayModel, CellLoadModel};
use fnv::{FnvHashMap, FnvHashSet};
use std::marker::PhantomData;
pub(crate) const PATH_SEPARATOR: &str = ":";
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum StaMode {
Early,
Late,
}
fn compute_net_loads<N: NetlistBase, Lib: CellLoadModel<N>>(
top: &CellRef<N>,
library: &Lib,
) -> FnvHashMap<N::NetId, Lib::Load> {
log::debug!(
"compute load capacitances of all nets ({})",
top.num_internal_nets()
);
let static_input_signals = None;
let net_capacitances: FnvHashMap<_, _> = top
.each_net()
.map(|net| {
let total_capacitance = net
.each_pin_instance()
.map(|pin_inst| {
let pin = pin_inst.pin();
library.input_pin_load(&pin.id(), &|_| static_input_signals)
})
.fold(library.zero(), |cap, acc| library.sum_loads(&cap, &acc));
(net.id(), total_capacitance)
})
.collect();
net_capacitances
}
#[derive(Debug, Clone)]
pub enum StaError {
PinNotFoundInNetlist(String, String),
MissingCellsInLiberty(Vec<String>),
BadNetlist,
CombinationalCycle,
NoClockDefined,
Other(String),
}
impl std::fmt::Display for StaError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
StaError::PinNotFoundInNetlist(cell, pin) =>
write!(f, "Pin '{}' in cell '{}' is referenced in liberty library but not present in the netlist.",
pin, cell),
StaError::MissingCellsInLiberty(cell_names) =>
write!(f, "Cells could not be found in liberty library: {}", cell_names.join(", ")),
StaError::BadNetlist => write!(f, "Something is not good with the netlist. See log for more details."),
StaError::CombinationalCycle => write!(f, "Circuit contains a combinational cycle. See log for more details."),
StaError::NoClockDefined => write!(f, "No clock defined."),
StaError::Other(msg) => write!(f, "STA error: {}", msg),
}
}
}
#[derive(Debug, Clone)]
struct Constraint<PinId> {
related_pin: PinId,
time: f64,
}
#[derive(Debug, Clone)]
pub struct ClockDefinition<Signal> {
period: uom::si::f64::Time,
first_edge: Signal,
second_edge: Signal,
}
impl<Signal> ClockDefinition<Signal> {
pub fn new(period: uom::si::f64::Time, first_edge: Signal, second_edge: Signal) -> Self {
assert!(
period.is_normal() && period.is_sign_positive() && !period.is_zero(),
"period must be a positive number but is '{}'",
period.value
);
Self {
period,
first_edge,
second_edge,
}
}
}
pub trait SimpleSTAState {}
#[derive(Debug)]
pub struct TimingAvailable {}
#[derive(Debug)]
pub struct Modifiable {}
impl SimpleSTAState for TimingAvailable {}
impl SimpleSTAState for Modifiable {}
#[derive(Debug)]
pub struct SimpleSTA<'a, N, Lib, State>
where
N: NetlistBase,
Lib: DelayBase + LoadBase + ConstraintBase,
{
netlist: N,
top_cell: N::CellId,
library: &'a Lib,
input_signals: HashMap<N::PinId, Lib::Signal>,
output_loads: HashMap<N::PinId, Lib::Load>,
required_output_signals: HashMap<N::PinId, Lib::RequiredSignal>,
clocks: HashMap<TerminalId<N>, ClockDefinition<Lib::Signal>>,
timing_graph: timing_graph::TimingGraph<N, Lib>,
generation: u32,
_netlist_type: PhantomData<N>,
_state: PhantomData<State>,
}
impl<'a, N, Lib> SimpleSTA<'a, N, Lib, Modifiable>
where
N: NetlistBase,
Lib: DelayBase + ConstraintBase,
{
}
impl<'a, N, Lib, S> SimpleSTA<'a, N, Lib, S>
where
N: NetlistBase,
Lib: DelayBase + ConstraintBase,
{
pub fn into_inner(self) -> N {
self.netlist
}
fn netlist(&self) -> &N {
&self.netlist
}
fn top_cell_ref(&self) -> CellRef<N> {
self.netlist().cell_ref(&self.top_cell)
}
}
impl<'a, N, Lib> SimpleSTA<'a, N, Lib, TimingAvailable>
where
N: NetlistBase,
Lib: DelayBase + ConstraintBase,
{
pub fn to_modifiable(self) -> SimpleSTA<'a, N, Lib, Modifiable> {
SimpleSTA {
top_cell: self.top_cell,
netlist: self.netlist,
library: self.library,
input_signals: self.input_signals,
output_loads: self.output_loads,
required_output_signals: self.required_output_signals,
clocks: self.clocks,
timing_graph: self.timing_graph,
_netlist_type: Default::default(),
_state: Default::default(),
generation: self.generation,
}
}
}
impl<'a, N, Lib> SimpleSTA<'a, N, Lib, Modifiable>
where
N: db::NetlistBaseMT,
Lib: CellLoadModel<N> + CellDelayModel<N> + CellConstraintModel<N> + Sync,
{
pub fn update_timing(
mut self,
) -> Result<SimpleSTA<'a, N, Lib, TimingAvailable>, (StaError, Self)> {
match self.run_sta() {
Ok(_) => Ok(SimpleSTA {
top_cell: self.top_cell,
netlist: self.netlist,
library: self.library,
input_signals: self.input_signals,
output_loads: self.output_loads,
required_output_signals: self.required_output_signals,
clocks: self.clocks,
timing_graph: self.timing_graph,
_netlist_type: Default::default(),
_state: Default::default(),
generation: self.generation,
}),
Err(err) => Err((err, self)),
}
}
}
impl<'a, N, Lib> SimpleSTA<'a, N, Lib, Modifiable>
where
N: NetlistBase,
Lib: CellLoadModel<N> + CellDelayModel<N> + CellConstraintModel<N>,
{
pub fn new(
netlist: N,
top_cell: N::CellId,
cell_timing_library: &'a Lib,
) -> Result<Self, StaError> {
let mut sta = Self {
top_cell,
netlist,
library: cell_timing_library,
input_signals: Default::default(),
output_loads: Default::default(),
required_output_signals: Default::default(),
clocks: Default::default(),
_netlist_type: Default::default(),
_state: Default::default(),
timing_graph: timing_graph::TimingGraph::new(),
generation: 0,
};
sta.init()?;
Ok(sta)
}
fn init(&mut self) -> Result<(), StaError> {
self.check_library()?;
self.build_timing_graph()?;
Ok(())
}
pub fn set_input_signal(
&mut self,
primary_input: N::PinId,
signal: Lib::Signal,
) -> Result<(), StaError> {
self.timing_graph
.add_to_frontier(&db::TerminalId::PinId(primary_input.clone()));
self.input_signals.insert(primary_input, signal);
Ok(())
}
pub fn set_output_load(
&mut self,
primary_output: N::PinId,
load: Lib::Load,
) -> Result<(), StaError> {
self.timing_graph
.add_to_frontier(&db::TerminalId::PinId(primary_output.clone()));
self.output_loads.insert(primary_output, load);
Ok(())
}
pub fn set_required_output_signal(
&mut self,
primary_output: N::PinId,
required_signal: Lib::RequiredSignal,
) -> Result<(), StaError> {
self.timing_graph
.add_to_frontier(&db::TerminalId::PinId(primary_output.clone()));
self.required_output_signals
.insert(primary_output, required_signal);
Ok(())
}
pub fn create_clock(
&mut self,
clock_pin: TerminalId<N>,
clock_definition: ClockDefinition<Lib::Signal>,
) -> Result<(), StaError> {
self.timing_graph.add_to_frontier(&clock_pin);
self.clocks.insert(clock_pin, clock_definition);
Ok(())
}
fn build_timing_graph(&mut self) -> Result<(), StaError> {
log::debug!("create timing graph");
self.timing_graph = timing_graph::TimingGraph::<_, Lib>::build_from_netlist(
&self.top_cell_ref(),
self.library,
self.library,
);
Ok(())
}
fn init_input_signals(&mut self) -> Result<(), StaError> {
log::debug!("set input signals");
for (pin, input_signal) in &self.input_signals {
let terminal = db::TerminalId::PinId(pin.clone());
self.timing_graph.add_to_frontier(&terminal);
if let Some(&node) = self.timing_graph.term2node.get(&terminal) {
let terminal_ref = self.netlist().terminal_ref(&terminal);
log::debug!(
"Set input signal for {}: {:?}",
terminal_ref.qname(PATH_SEPARATOR),
input_signal
);
let node_weight = self
.timing_graph
.arc_graph
.node_weight_mut(node)
.expect("node has no weight");
let mut node_data = node_weight
.borrow_data_cell()
.try_write(0)
.expect("node data should not be locked");
node_data.signal = Some(input_signal.clone());
node_data.is_primary_input = true;
}
}
Ok(())
}
fn init_required_output_signals(&mut self) -> Result<(), StaError> {
log::debug!("init required output signals");
for (pin, required_signal) in &self.required_output_signals {
let terminal = db::TerminalId::PinId(pin.clone());
self.timing_graph.add_to_frontier(&terminal);
if let Some(&node) = self.timing_graph.term2node.get(&terminal) {
let terminal_ref = self.netlist().terminal_ref(&terminal);
log::debug!(
"Set required output signal for {}: {:?}",
terminal_ref.qname(PATH_SEPARATOR),
required_signal
);
let node_weight = self
.timing_graph
.arc_graph
.node_weight_mut(node)
.expect("node has no weight");
let mut node_data = node_weight
.borrow_data_cell()
.try_write(0)
.expect("node data should not be locked");
node_data.required_signal = Some(required_signal.clone());
}
}
Ok(())
}
fn init_output_loads(&mut self) -> Result<(), StaError> {
log::debug!("init output loads");
for (pin, load) in &self.output_loads {
let terminal = db::TerminalId::PinId(pin.clone());
self.timing_graph.add_to_frontier(&terminal);
if let Some(&node) = self.timing_graph.term2node.get(&terminal) {
let terminal_ref = self.netlist().terminal_ref(&terminal);
log::debug!(
"Set output load for {}: {:?}",
terminal_ref.qname(PATH_SEPARATOR),
load
);
todo!();
}
}
Ok(())
}
fn init_clock_signals(&mut self) -> Result<(), StaError> {
log::debug!("set clock signals");
for (clock_terminal, clock_def) in &self.clocks {
let terminal_ref = self.netlist.terminal_ref(clock_terminal);
let clock_node = *self
.timing_graph
.term2node
.get(clock_terminal)
.ok_or_else(|| {
StaError::Other(format!(
"Cannot find clock in timing graph: {}",
terminal_ref.qname(PATH_SEPARATOR)
))
})?;
self.timing_graph.add_to_frontier(clock_terminal);
log::debug!("Set clock for {}", terminal_ref.qname(PATH_SEPARATOR));
let node_weight = self
.timing_graph
.arc_graph
.node_weight_mut(clock_node)
.expect("node has no weight");
let mut node_data = node_weight
.borrow_data_cell()
.try_write(0)
.expect("node data should not be locked");
node_data.signal = Some(clock_def.first_edge.clone()); node_data.is_primary_input = true;
}
Ok(())
}
fn propagate_signals(
&mut self,
net_loads: &FnvHashMap<N::NetId, Lib::Load>,
) -> Result<(), StaError>
where
Lib: Sync,
N: db::NetlistBaseMT,
{
log::debug!("compute actual arrival times");
self.generation += 1;
let frontier = self.timing_graph.take_frontier();
log::debug!("propagate signals");
propagate_signals_incremental(
self.netlist(),
&self.timing_graph,
self.library,
&ZeroInterconnectDelayModel::new(self.library),
&PrecomputedOutputLoads { loads: net_loads },
frontier.iter().copied(),
self.generation,
);
Ok(())
}
fn toposort_delay_graph(&self) -> Result<Vec<TerminalId<N>>, StaError> {
let delay_graph =
petgraph::visit::EdgeFiltered::from_fn(&self.timing_graph.arc_graph, |edge_ref| {
edge_ref.weight().edge_type.is_delay_arc()
});
let _constraint_graph =
petgraph::visit::EdgeFiltered::from_fn(&self.timing_graph.arc_graph, |edge_ref| {
edge_ref.weight().edge_type.is_constraint_arc()
});
log::debug!("Sort graph nodes topologically.");
let topo_sorted: Vec<TerminalId<_>> = {
let topo = petgraph::algo::toposort(&delay_graph, None);
match topo {
Ok(sorted) => sorted
.iter()
.filter(|&&id| {
id != self.timing_graph.aat_source_node
&& id != self.timing_graph.rat_source_node
})
.map(|id| self.timing_graph.node2term[id].clone())
.collect(),
Err(cycle) => {
log::warn!("netlist has combinational cycle");
if let Some(cycle_node) = self.timing_graph.node2term.get(&cycle.node_id()) {
let term_id = self.netlist().terminal_ref(cycle_node);
log::warn!(
"combinational cycle through node: {}",
term_id.qname(PATH_SEPARATOR)
);
}
return Err(StaError::CombinationalCycle);
}
}
};
Ok(topo_sorted)
}
fn debug_print_actual_signals(&self) {
for (n, weight) in self.timing_graph.arc_graph.node_references() {
println!(
"{:?}: {:?}",
weight.node_type,
weight.borrow_data_cell().try_read().unwrap().signal
);
}
}
fn debug_print_timing_graph(&self) {
println!("Topological order of graph nodes:");
let topo_sorted = self.toposort_delay_graph().unwrap();
for term in &topo_sorted {
println!(
" - {}",
self.netlist().terminal_ref(term).qname(PATH_SEPARATOR)
);
}
let dot = graphviz::TimingGraphDot::new(&self.timing_graph, self.netlist());
let dot_format = format!("{:?}", dot);
let lines = dot_format.split('\n');
for l in lines {
println!("{}", l);
}
}
fn run_sta(&mut self) -> Result<(), StaError>
where
Lib: Sync,
N: db::NetlistBaseMT,
{
let clocks: Vec<_> = self.clocks.keys().cloned().collect();
if clocks.is_empty() {
return Err(StaError::NoClockDefined);
}
if clocks.len() > 1 {
unimplemented!("More than one clock is currently not supported.");
}
self.check_library()?;
self.check_clock_sources()?;
self.init_input_signals()?;
self.init_output_loads()?;
self.init_required_output_signals()?;
self.init_clock_signals()?;
let net_loads = compute_net_loads(&self.top_cell_ref(), self.library);
self.propagate_signals(&net_loads)?;
self.debug_print_timing_graph();
Ok(())
}
fn check_library(&self) -> Result<(), StaError> {
log::debug!("check timing library");
let top = self.top_cell_ref();
let netlist = self.netlist();
let mut missing_cells: FnvHashSet<N::CellId> = Default::default();
for leaf_cell in top.each_cell_dependency() {
}
if !missing_cells.is_empty() {
let cell_names: Vec<String> = missing_cells
.iter()
.map(|c| netlist.cell_name(c).into())
.collect();
log::error!(
"Cells not found in liberty library: {}",
cell_names.join(", ")
);
Err(StaError::MissingCellsInLiberty(cell_names))
} else {
Ok(())
}
}
fn check_clock_sources(&self) -> Result<(), StaError> {
log::debug!("validate clock sources");
for clock in self.clocks.keys() {
let clock_input = self.netlist().terminal_ref(clock);
if self.top_cell != clock_input.parent().id() {
log::error!(
"clock source '{}' does not live in top cell '{}' but in '{}",
clock_input.qname(PATH_SEPARATOR),
self.top_cell_ref().name(),
clock_input.parent().name()
);
return Err(StaError::Other(
"clock source does not live in top cell".into(),
));
}
}
Ok(())
}
}
struct PrecomputedOutputLoads<'a, Net, Load> {
loads: &'a FnvHashMap<Net, Load>,
}
impl<'a, Net, Load> LoadBase for PrecomputedOutputLoads<'a, Net, Load>
where
Load: Zero + Clone + Debug + Send + Sync,
{
type Load = Load;
fn sum_loads(&self, load1: &Self::Load, load2: &Self::Load) -> Self::Load {
todo!()
}
}
impl<'a, N, Load> InterconnectLoadModel<N> for PrecomputedOutputLoads<'a, N::NetId, Load>
where
Load: Zero + Clone + Debug + Send + Sync,
N: db::NetlistBase,
{
fn interconnect_load(&self, netlist: &N, driver_terminal: &TerminalId<N>) -> Self::Load {
let net = netlist.net_of_terminal(driver_terminal);
net.and_then(|net| self.loads.get(&net))
.cloned()
.unwrap_or(Self::Load::zero())
}
}
impl<'a, N, Lib> TimingQuery for SimpleSTA<'a, N, Lib, TimingAvailable>
where
N: NetlistBase + 'static,
Lib: DelayBase + ConstraintBase,
{
type NetlistIds = N;
type ArrivalTime = Lib::Signal;
type RequiredArrivalTime = Lib::RequiredSignal;
type Slack = Lib::Slack;
fn report_aat(&self, pin: db::TerminalId<N>) -> Option<Self::ArrivalTime> {
self.timing_graph
.get_node_data(&pin)
.and_then(|d| d.signal.clone())
}
fn report_rat(&self, pin: db::TerminalId<N>) -> Option<Self::RequiredArrivalTime> {
self.timing_graph
.get_node_data(&pin)
.and_then(|d| d.required_signal.clone())
}
fn report_slack(&self, pin: db::TerminalId<N>) -> Option<Self::Slack> {
self.timing_graph.get_node_data(&pin).and_then(|d| {
let aat = d.signal.as_ref()?;
let rat = d.required_signal.as_ref()?;
self.library.get_slack(aat, rat).into()
})
}
fn report_timing(&self) -> Vec<()> {
todo!()
}
}
#[libreda_db::derive::fill(libreda_db::derive::delegate(N))]
impl<'a, N, Lib, S> db::HierarchyIds for SimpleSTA<'a, N, Lib, S>
where
N: NetlistBase,
Lib: DelayBase + ConstraintBase,
{
}
#[libreda_db::derive::fill(libreda_db::derive::delegate(N))]
impl<'a, N, Lib, S> db::NetlistIds for SimpleSTA<'a, N, Lib, S>
where
N: NetlistBase,
Lib: DelayBase + ConstraintBase,
{
}
#[libreda_db::derive::fill(libreda_db::derive::delegate(N))]
impl<'a, N, Lib, S> db::LayoutIds for SimpleSTA<'a, N, Lib, S>
where
N: LayoutIds + NetlistBase,
Lib: DelayBase + ConstraintBase,
{
}
#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
impl<'b, N, Lib, S> db::HierarchyBase for SimpleSTA<'b, N, Lib, S>
where
N: NetlistBase,
Lib: DelayBase + ConstraintBase,
{
type NameType = N::NameType;
}
#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
impl<'b, N, Lib, S> db::NetlistBase for SimpleSTA<'b, N, Lib, S>
where
N: NetlistBase,
Lib: DelayBase + ConstraintBase,
{
}
#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
impl<'b, N, Lib, S> db::LayoutBase for SimpleSTA<'b, N, Lib, S>
where
N: NetlistBase + LayoutBase,
Lib: DelayBase + ConstraintBase,
{
}
#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
impl<'b, N, Lib, S> db::L2NBase for SimpleSTA<'b, N, Lib, S>
where
N: L2NBase,
Lib: DelayBase + ConstraintBase,
{
}
#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
impl<'b, N, Lib, S> db::HierarchyEdit for SimpleSTA<'b, N, Lib, S>
where
N: NetlistBase + HierarchyEdit,
Lib: DelayBase + ConstraintBase + CellDelayModel<N> + CellConstraintModel<N>,
{
fn remove_cell(&mut self, cell_id: &Self::CellId) {
assert!(
cell_id != &self.top_cell,
"cannot remove the cell which is currently selected for static timing analysis"
);
let cell = self.netlist.cell_ref(cell_id);
for cell_inst in cell.each_reference() {
self.timing_graph.remove_cell_instance(&cell_inst);
}
self.netlist.remove_cell(cell_id)
}
fn create_cell_instance(
&mut self,
parent_cell: &Self::CellId,
template_cell: &Self::CellId,
name: Option<Self::NameType>,
) -> Self::CellInstId {
let inst = self
.netlist
.create_cell_instance(parent_cell, template_cell, name);
if parent_cell == &self.top_cell {
self.timing_graph.create_cell_instance(
&self.netlist.cell_instance_ref(&inst),
self.library,
self.library,
)
}
inst
}
fn remove_cell_instance(&mut self, inst: &Self::CellInstId) {
if self.top_cell == self.netlist.parent_cell(inst) {
todo!("remove graph nodes")
}
self.netlist.remove_cell_instance(inst)
}
}
#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
impl<'b, N, Lib, S> db::NetlistEdit for SimpleSTA<'b, N, Lib, S>
where
N: NetlistEdit,
Lib: DelayBase + ConstraintBase + CellDelayModel<N> + CellConstraintModel<N>,
{
fn create_pin(
&mut self,
cell: &Self::CellId,
name: Self::NameType,
direction: Direction,
) -> Self::PinId {
let pin_id = self.netlist.create_pin(cell, name, direction);
if cell == &self.top_cell {
self.timing_graph
.create_terminal(db::TerminalId::PinId(pin_id.clone()));
} else {
for cell_inst in self.netlist.each_cell_reference(cell) {
let pin_inst = self.netlist.pin_instance(&cell_inst, &pin_id);
self.timing_graph
.create_terminal(db::TerminalId::PinInstId(pin_inst));
}
}
pin_id
}
fn remove_pin(&mut self, id: &Self::PinId) {
if self.netlist.parent_cell_of_pin(id) == self.top_cell {
self.timing_graph.remove_pin(id.clone());
} else {
let pin_ref = self.netlist.pin_ref(id);
let cell = pin_ref.cell();
for inst in cell.each_reference() {
self.timing_graph
.remove_pin_instance(inst.pin_instance(id).id());
}
}
self.netlist.remove_pin(id)
}
fn remove_net(&mut self, net: &Self::NetId) {
if self.netlist.parent_cell_of_net(net) == self.top_cell {
self.timing_graph.remove_net(&self.netlist.net_ref(net));
}
self.netlist.remove_net(net)
}
fn connect_pin(&mut self, pin: &Self::PinId, net: Option<Self::NetId>) -> Option<Self::NetId> {
let pin_ref = self.netlist.pin_ref(pin);
if pin_ref.cell().id() == self.top_cell {
self.timing_graph
.connect_terminal(pin_ref.into_terminal(), net.clone());
}
self.netlist.connect_pin(pin, net)
}
fn connect_pin_instance(
&mut self,
pin: &Self::PinInstId,
net: Option<Self::NetId>,
) -> Option<Self::NetId> {
let old_net = self.netlist.connect_pin_instance(pin, net.clone());
let pin_inst_ref = self.netlist.pin_instance_ref(pin);
if pin_inst_ref.cell_instance().parent().id() == self.top_cell {
self.timing_graph
.connect_terminal(pin_inst_ref.into_terminal(), net);
}
old_net
}
}
#[libreda_db::derive::fill(libreda_db::derive::delegate(N; self.netlist))]
impl<'b, N, Lib, S> db::LayoutEdit for SimpleSTA<'b, N, Lib, S>
where
N: NetlistBase + LayoutEdit,
Lib: DelayBase + ConstraintBase + CellDelayModel<N> + CellConstraintModel<N>,
{
fn insert_shape(
&mut self,
parent_cell: &Self::CellId,
layer: &Self::LayerId,
geometry: db::Geometry<Self::Coord>,
) -> Self::ShapeId {
todo!()
}
fn remove_shape(&mut self, shape_id: &Self::ShapeId) -> Option<db::Geometry<Self::Coord>> {
todo!()
}
fn replace_shape(
&mut self,
shape_id: &Self::ShapeId,
geometry: db::Geometry<Self::Coord>,
) -> db::Geometry<Self::Coord> {
todo!()
}
fn set_transform(
&mut self,
cell_inst: &Self::CellInstId,
tf: db::SimpleTransform<Self::Coord>,
) {
todo!()
}
}
impl<'b, N, Lib, S> db::L2NEdit for SimpleSTA<'b, N, Lib, S>
where
N: L2NEdit,
Lib: DelayBase + ConstraintBase + CellDelayModel<N> + CellConstraintModel<N>,
{
fn set_pin_of_shape(
&mut self,
shape_id: &Self::ShapeId,
pin: Option<Self::PinId>,
) -> Option<Self::PinId> {
let update_top_cell = self.netlist.parent_of_shape(shape_id).0 == self.top_cell;
if update_top_cell {
if let Some(p) = pin.clone() {
self.timing_graph.add_to_frontier(&db::TerminalId::PinId(p));
}
}
let old_pin = self.netlist.set_pin_of_shape(shape_id, pin);
if update_top_cell {
if let Some(p) = old_pin.clone() {
self.timing_graph.add_to_frontier(&db::TerminalId::PinId(p));
}
}
old_pin
}
fn set_net_of_shape(
&mut self,
shape_id: &Self::ShapeId,
net: Option<Self::NetId>,
) -> Option<Self::NetId> {
let update_top_cell = self.netlist.parent_of_shape(shape_id).0 == self.top_cell;
if update_top_cell {
if let Some(n) = &net {
for t in self.netlist.each_terminal_of_net(n) {
self.timing_graph.add_to_frontier(&t.cast());
}
}
}
let old_net = self.netlist.set_net_of_shape(shape_id, net);
if update_top_cell {
if let Some(n) = &old_net {
for t in self.netlist.each_terminal_of_net(n) {
self.timing_graph.add_to_frontier(&t.cast());
}
}
}
old_net
}
}
#[test]
fn test_uom() {
use uom::si::capacitance::femtofarad;
use uom::si::electric_current::femtoampere;
use uom::si::f64::*;
let i = ElectricCurrent::new::<femtoampere>(1.0);
let c = Capacitance::new::<femtofarad>(2.0);
let time = c / i;
}
#[test]
fn test_uom_unit_elimination() {
use uom::si::capacitance::femtofarad;
use uom::si::electric_current::femtoampere;
use uom::si::f64::*;
let i = ElectricCurrent::new::<femtoampere>(1.0);
let c = Capacitance::new::<femtofarad>(2.0);
let ratio1 = i / i; let ratio2 = c / c;
let unitless_sum = ratio1 + ratio2;
}