use libreda_db::prelude::{self as db, NetlistEditUtil};
use libreda_db::prelude::{Angle, CoordinateType, Direction, Scale, TerminalId, TryCastCoord};
use libreda_db::traits::*;
use crate::def_ast;
use crate::def_ast::{NetTerminal, RoutingPoint, DEF};
use crate::lef_ast::{Layer, RectOrPolygon, Shape, SignalUse, ViaDefinition, ViaShape, LEF};
use crate::common::{Orient, PinDirection};
use num_traits::{FromPrimitive, NumCast, PrimInt, Zero};
use std::collections::HashMap;
use std::fmt::Formatter;
#[derive(Debug, Clone)]
pub enum LefDefImportError {
ComponentModelNotFound {
component_name: String,
model_name: String,
},
ComponentNotFound(String),
ViaNotFound(String),
LayerNotFound(String),
CellNameAlreadyExists(String),
Other(String),
}
impl std::fmt::Display for LefDefImportError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
LefDefImportError::ComponentModelNotFound { component_name, model_name } => {
write!(f, "Model of component '{}' not found: '{}'", component_name, model_name)
}
LefDefImportError::LayerNotFound(layer) => write!(f, "the layer '{}' could not be found in the target design and the creation of new layers is disabled", layer),
LefDefImportError::ComponentNotFound(component_name) => write!(f, "component was referenced but not found: '{}'", component_name),
LefDefImportError::ViaNotFound(via_name) => write!(f, "component is referenced but not found: '{}'", via_name),
LefDefImportError::CellNameAlreadyExists(cell_name) => write!(f, "cell '{}' already exists", cell_name),
LefDefImportError::Other(msg) => write!(f, "{}", msg)
};
Ok(())
}
}
#[derive(Clone)]
pub struct LEFImportOptions<C: L2NEdit> {
pub import_pins: bool,
pub import_power_pins: bool,
pub pin_suffix: String,
pub import_obstructions: bool,
pub obstruction_suffix: String,
pub import_via_definitions: bool,
pub import_generated_vias: bool,
pub import_fixed_vias: bool,
pub import_cell_outlines: bool,
pub cell_outline_layer: Option<String>,
pub layer_mapping: HashMap<String, C::LayerId>,
pub create_missing_layers: bool,
pub skip_existing_vias: bool,
}
impl<C: L2NEdit> Default for LEFImportOptions<C> {
fn default() -> Self {
Self {
import_pins: true,
import_power_pins: true,
pin_suffix: ".PIN".to_string(),
import_obstructions: true,
obstruction_suffix: ".OBS".to_string(),
import_via_definitions: true,
import_generated_vias: true,
import_fixed_vias: true,
import_cell_outlines: true,
cell_outline_layer: Some("OUTLINE".to_string()),
layer_mapping: Default::default(),
create_missing_layers: true,
skip_existing_vias: false,
}
}
}
impl<C: L2NEdit> LEFImportOptions<C> {
fn get_or_create_layer_by_name(
&self,
chip: &mut C,
layer_name: &String,
) -> Result<C::LayerId, LefDefImportError> {
let layer =
self.layer_mapping.get(layer_name)
.cloned()
.or_else(|| chip.layer_by_name(layer_name.as_str()))
.or_else(|| if self.create_missing_layers {
let next_idx = chip.each_layer()
.map(|id| chip.layer_info(&id).index)
.max()
.unwrap_or(0) + 1;
log::debug!("Create layer: {}", layer_name);
let layer_id = chip.create_layer(next_idx, 0);
chip.set_layer_name(&layer_id, Some(layer_name.clone().into()));
Some(layer_id)
} else {
None
});
layer.ok_or_else(|| LefDefImportError::LayerNotFound(layer_name.clone()))
}
}
fn convert_geometry<C: CoordinateType + NumCast>(
dbu_per_micron: u64,
shape: &Shape,
) -> db::Geometry<C> {
let dbu_per_micron = dbu_per_micron as f64;
let geo: db::Geometry<f64> = match shape {
Shape::Path(width, points) => db::Path::new(points, *width).scale(dbu_per_micron).into(),
Shape::Rect(p1, p2) => db::Rect::new(p1, p2).scale(dbu_per_micron).into(),
Shape::Polygon(points) => db::SimplePolygon::new(points.clone())
.scale(dbu_per_micron)
.into(),
};
let geo: db::Geometry<C> = geo.try_cast().expect("Cast from float failed."); geo
}
#[derive(Clone)]
pub struct DEFImportOptions<C: L2NEdit> {
pub lef_import_options: LEFImportOptions<C>,
pub import_blockages: bool,
pub blockages_suffix: String,
pub import_nets: bool,
pub import_wiring: bool,
pub import_fixed_vias: bool,
pub skip_existing_vias: bool,
}
impl<C: L2NEdit> Default for DEFImportOptions<C> {
fn default() -> Self {
Self {
lef_import_options: Default::default(),
import_blockages: true,
blockages_suffix: ".BLOCKAGE".to_string(),
import_nets: true,
import_wiring: true,
import_fixed_vias: true,
skip_existing_vias: false,
}
}
}
pub fn lef_to_db<C, Crd>(lef: &LEF) -> Result<C, LefDefImportError>
where
Crd: NumCast + Ord + CoordinateType,
C: L2NEdit<Coord = Crd> + Default,
{
let mut chip = C::default();
let options = Default::default();
import_lef_into_db(&options, lef, &mut chip).map(|_| chip)
}
pub fn import_lef_into_db<C, Crd>(
options: &LEFImportOptions<C>,
lef: &LEF,
chip: &mut C,
) -> Result<(), LefDefImportError>
where
Crd: NumCast + Ord + CoordinateType,
C: L2NEdit<Coord = Crd>,
{
assert!(
chip.dbu() > Crd::zero(),
"Data-base distance unit (DBU) must be a positive number"
);
let dbu_per_micron = chip.dbu().to_u64().expect("failed to convert DBU to u64");
for (i, layer) in lef.technology.layers.iter().enumerate() {
let result = options.get_or_create_layer_by_name(chip, layer.name());
if let Err(err) = result {
log::warn!("Failed to create layer: {} ({})", layer.name(), err)
}
}
if options.import_via_definitions {
import_lef_vias(options, lef, chip)?;
}
for (macro_name, lef_macro) in &lef.library.macros {
let cell = chip.create_cell(macro_name.to_string().into());
if options.import_pins {
let mut pin_layer_name_cache = HashMap::new();
for macro_pin in &lef_macro.pins {
let signal_use = macro_pin.signal_use.unwrap_or(SignalUse::Signal);
let is_power_ground_pin =
signal_use == SignalUse::Power || signal_use == SignalUse::Ground;
if !options.import_power_pins && is_power_ground_pin {
continue;
}
let direction = match ¯o_pin.direction {
None => Direction::None,
Some(d) => match d {
PinDirection::Input => Direction::Input,
PinDirection::Output(_tristate) => Direction::Output,
PinDirection::Inout => Direction::InOut,
PinDirection::Feedthru => Direction::InOut,
},
};
let pin = chip.create_pin(&cell, macro_pin.name.clone().into(), direction);
for port in ¯o_pin.ports {
for layer_geometry in &port.geometries {
let layer_name = pin_layer_name_cache
.entry(&layer_geometry.layer_name)
.or_insert_with(|| {
format!("{}{}", layer_geometry.layer_name, options.pin_suffix)
});
let layer = options.get_or_create_layer_by_name(chip, layer_name)?;
for g in &layer_geometry.geometries {
let geo = convert_geometry(dbu_per_micron, &g.shape);
let shape_id = chip.insert_shape(&cell, &layer, geo);
chip.set_pin_of_shape(&shape_id, Some(pin.clone()));
}
}
}
}
}
if options.import_cell_outlines {
if let Some((width, height)) = lef_macro.size {
if let Some(outline_layer_name) = &options.cell_outline_layer {
let shape = Shape::Rect(
lef_macro.origin,
lef_macro.origin + db::Point::new(width, height),
);
let geo = convert_geometry(dbu_per_micron, &shape);
let outline_layer =
options.get_or_create_layer_by_name(chip, outline_layer_name)?;
chip.insert_shape(&cell, &outline_layer, geo);
}
}
}
if options.import_obstructions {
let mut obs_layer_name_cache = HashMap::new();
for obs in &lef_macro.obs {
let layer_name = obs_layer_name_cache
.entry(&obs.layer_name)
.or_insert_with(|| format!("{}{}", obs.layer_name, options.obstruction_suffix));
let layer = options.get_or_create_layer_by_name(chip, layer_name)?;
for g in &obs.geometries {
let geo = convert_geometry(dbu_per_micron, &g.shape);
chip.insert_shape(&cell, &layer, geo);
}
}
}
}
Ok(())
}
pub fn import_lef_vias<C, Crd>(
options: &LEFImportOptions<C>,
lef: &LEF,
chip: &mut C,
) -> Result<Vec<C::CellId>, LefDefImportError>
where
Crd: NumCast + Ord + CoordinateType,
C: L2NEdit<Coord = Crd>,
{
let dbu_per_micron: f64 = chip.dbu().to_f64().unwrap();
let mut via_cells = vec![];
for (via_name, via) in &lef.vias {
if chip.cell_by_name(via_name).is_some() {
if options.skip_existing_vias {
continue;
} else {
return Err(LefDefImportError::CellNameAlreadyExists(via_name.clone()));
}
}
let via_cell = chip.create_cell(via_name.clone().into());
match via {
ViaDefinition::GeneratedVia(via) => {
if options.import_generated_vias {
todo!("import of generated vias")
}
}
ViaDefinition::FixedVia(via) => {
if options.import_fixed_vias {
for (layer_name, shapes) in &via.geometry {
let layer = options.get_or_create_layer_by_name(chip, layer_name)?;
for shape in shapes {
let geometry: db::Geometry<f64> = match &shape.shape {
RectOrPolygon::Rect((p1, p2)) => {
db::Rect::new(*p1, *p2).scale(dbu_per_micron).into()
}
RectOrPolygon::Polygon(points) => {
let p = db::SimplePolygon::new(points.to_vec())
.scale(dbu_per_micron);
if let Some(p) = db::SimpleRPolygon::try_new(p.points()) {
p.into()
} else {
p.into()
}
}
};
let geo: db::Geometry<C::Coord> =
geometry.try_cast().expect("cast from f64 failed");
chip.insert_shape(&via_cell, &layer, geo);
}
}
}
}
_ => {}
};
via_cells.push(via_cell);
}
Ok(via_cells)
}
pub fn lefdef_to_db<C, Crd>(
options: &DEFImportOptions<C>,
lef: &LEF,
def: &DEF,
) -> Result<C, LefDefImportError>
where
Crd: NumCast + Ord + CoordinateType + PrimInt,
C: L2NEdit<Coord = Crd> + Default,
{
let mut chip = C::default();
import_lef_into_db(&options.lef_import_options, lef, &mut chip)?;
import_def_into_db(&options, Some(lef), def, &mut chip).map(|_| chip)
}
pub fn import_def_netlist<C, Crd>(
options: &DEFImportOptions<C>,
def: &DEF,
top_cell: &C::CellId,
chip: &mut C,
) -> Result<(), LefDefImportError>
where
Crd: NumCast + Ord + CoordinateType + PrimInt,
C: L2NEdit<Coord = Crd>,
{
log::info!(
"Import netlist from DEF. Number of nets: {}",
def.nets.len()
);
let mut nets_by_name = HashMap::new();
for net in &def.nets {
if let Some(net_name) = &net.name {
let net_id = nets_by_name
.entry(net_name)
.or_insert_with(|| chip.create_net(&top_cell, Some(net_name.to_string().into())));
for term in &net.terminals {
let term_id: TerminalId<C> = match term {
NetTerminal::ComponentPin {
component_name,
pin_name,
} => {
let inst = chip
.cell_instance_by_name(&top_cell, component_name.as_str())
.ok_or_else(|| {
LefDefImportError::ComponentNotFound(component_name.to_string())
})?;
let cell = chip.template_cell(&inst);
let pin_id =
chip.pin_by_name(&cell, pin_name.as_str()).ok_or_else(|| {
LefDefImportError::Other(format!(
"Pin of component not found: {}",
pin_name
))
})?;
let pin_inst = chip.pin_instance(&inst, &pin_id);
TerminalId::PinInstId(pin_inst)
}
NetTerminal::IoPin(pin_name) => {
let pin_id =
chip.pin_by_name(&top_cell, pin_name.as_str())
.ok_or_else(|| {
LefDefImportError::Other(format!(
"Pin of top-level cell not found: {}",
pin_name
))
})?;
TerminalId::PinId(pin_id)
}
};
chip.connect_terminal(&term_id, Some(net_id.clone()));
}
} else {
log::warn!("DEF import of nets without name (MUSTJOIN nets) is not implemented yet.");
}
}
Ok(())
}
pub fn import_def_vias<C, Crd>(
options: &DEFImportOptions<C>,
def: &DEF,
chip: &mut C,
) -> Result<(), LefDefImportError>
where
Crd: NumCast + Ord + CoordinateType,
C: L2NEdit<Coord = Crd>,
{
log::debug!("import via definitions from DEF");
for (via_name, via) in &def.vias {
log::debug!("import via '{}'", via_name);
if chip.cell_by_name(via_name).is_some() {
if options.skip_existing_vias {
continue;
} else {
return Err(LefDefImportError::CellNameAlreadyExists(via_name.clone()));
}
}
let via_cell = chip.create_cell(via_name.clone().into());
match via {
def_ast::ViaDefinition::ViaGeometry(via_geometry) => {
log::debug!("import of via geometry '{}'", via_name);
for geometry in via_geometry {
if geometry.mask_num.is_some() {
log::warn!("via mask number is ignored");
}
let layer = options
.lef_import_options
.get_or_create_layer_by_name(chip, &geometry.layer)?;
let geometry: db::Geometry<db::Coord> = match &geometry.shape {
def_ast::RectOrPolygon::Rect(r) => r.clone().into(),
def_ast::RectOrPolygon::Polygon(p) => p.clone().into(),
};
let geo: db::Geometry<C::Coord> = geometry.try_cast().expect("cast failed");
chip.insert_shape(&via_cell, &layer, geo);
}
}
def_ast::ViaDefinition::ViaRule => {
log::debug!("skip import of via rule '{}'", via_name);
}
}
}
Ok(())
}
pub fn import_def_regular_wiring<C, Crd>(
options: &DEFImportOptions<C>,
lef: &LEF,
def: &DEF,
top_cell: &C::CellId,
chip: &mut C,
) -> Result<(), LefDefImportError>
where
Crd: NumCast + Ord + CoordinateType + PrimInt,
C: L2NEdit<Coord = Crd>,
{
log::info!(
"Import wiring from DEF. Number of nets with wiring: {}",
def.nets
.iter()
.filter(|net| !net.regular_wiring.is_empty())
.count()
);
let default_wiring_widths = {
let mut default_wiring_widths = HashMap::new();
let units_per_micron: f64 = chip.dbu().to_f64().unwrap();
for layer in &lef.technology.layers {
match layer {
Layer::MasterSlice(_) => {
}
Layer::Cut(_) => {
}
Layer::Routing(routing_layer) => {
if let Some(layer_id) = chip.layer_by_name(routing_layer.name.as_str()) {
let default_widths_microns = routing_layer.width;
let default_widths_dbu =
db::Coord::from_f64(default_widths_microns * units_per_micron)
.expect("Failed to cast default path width to database units.");
default_wiring_widths.insert(layer_id, default_widths_dbu);
} else {
log::warn!("Layer '{}' is not present in the current design. Default wiring width will be ignored during import.", &routing_layer.name);
}
}
}
}
default_wiring_widths
};
for net in &def.nets {
let net_name = net.name.as_ref().ok_or_else(|| {
LefDefImportError::Other("Wiring for unnamed nets is not supported.".into())
})?;
log::debug!("Import wiring for net '{}'", &net_name);
let net_id = chip
.net_by_name(top_cell, net_name.as_str())
.ok_or_else(|| LefDefImportError::Other(format!("Net not found: {}", net_name)))?;
for regular_wiring in &net.regular_wiring {
let _wiring_class = regular_wiring.class; for wiring in ®ular_wiring.wiring {
let _style = wiring.style_num; let _taper_rule = &wiring.taper_rule; let mut current_layer = options
.lef_import_options
.get_or_create_layer_by_name(chip, &wiring.start_layer_name)?;
let mut current_point = None;
let path_width = *default_wiring_widths.get(¤t_layer).ok_or_else(|| {
LefDefImportError::Other(format!(
"No default wiring width defined for layer '{}'",
&wiring.start_layer_name
))
})?;
let _2 = Crd::one() + Crd::one();
let mut begin_ext = path_width / 2;
let mut end_ext = path_width / 2;
let mut path: Vec<db::Point<_>> = Vec::new();
let mut paths: Vec<db::Path<Crd>> = Vec::new();
for routing_point in &wiring.routing_points {
match routing_point {
RoutingPoint::Point {
point,
ext_value,
mask_num,
} => {
if let Some(ext_value) = ext_value {
if current_point.is_none() {
begin_ext = *ext_value;
}
end_ext = *ext_value;
}
current_point = Some(point.clone());
path.push(point.clone());
}
RoutingPoint::Via {
via_name,
orient,
via_mask_num,
} => {
if let Some(p) = current_point {
let via_cell =
chip.cell_by_name(via_name.as_str()).ok_or_else(|| {
LefDefImportError::ViaNotFound(via_name.clone())
})?;
let transform = if let Some(orient) = orient {
def_orient_to_transform(orient)
} else {
db::SimpleTransform::identity()
}
.then(&db::SimpleTransform::translate(p.v().cast()));
let via_instance =
chip.create_cell_instance(&top_cell, &via_cell, None);
chip.set_transform(&via_instance, transform);
} else {
log::error!("current point in wiring path is not known");
}
}
RoutingPoint::Rect { rect, mask_num } => {
let rect = rect.cast();
let shape_id = chip.insert_shape(top_cell, ¤t_layer, rect.into());
chip.set_net_of_shape(&shape_id, Some(net_id.clone()));
}
RoutingPoint::Virtual(p) => {
current_point = Some(p.clone());
let finished_path = std::mem::replace(&mut path, vec![]);
let finished_path = db::Path::new_extended(
finished_path,
path_width,
begin_ext,
end_ext,
)
.cast();
paths.push(finished_path);
}
}
}
let finished_path =
db::Path::new_extended(path, path_width, begin_ext, end_ext).cast();
paths.push(finished_path);
for path in paths {
if path.len() > 1 {
let path = path.cast(); let shape_id = chip.insert_shape(top_cell, ¤t_layer, path.into());
chip.set_net_of_shape(&shape_id, Some(net_id.clone()));
}
}
}
}
}
Ok(())
}
pub fn import_def_into_db<C, Crd>(
options: &DEFImportOptions<C>,
lef: Option<&LEF>,
def: &DEF,
chip: &mut C,
) -> Result<(), LefDefImportError>
where
Crd: NumCast + Ord + CoordinateType + PrimInt,
C: L2NEdit<Coord = Crd>,
{
if options.import_fixed_vias {
import_def_vias(options, def, chip)?;
}
let top_cell = chip.create_cell(def.design_name.clone().unwrap_or("TOP".to_string()).into());
log::info!("Import '{}' from DEF.", chip.cell_name(&top_cell));
log::info!("Import top-level pins: {}", def.pins.len());
for pin in &def.pins {
let pin_dir = match &pin.direction {
None => Direction::None,
Some(d) => match d {
PinDirection::Input => Direction::Input,
PinDirection::Output(_tristate) => Direction::Output,
PinDirection::Inout => Direction::InOut,
PinDirection::Feedthru => Direction::InOut,
},
};
chip.create_pin(&top_cell, pin.net_name.to_string().into(), pin_dir);
}
if options.lef_import_options.import_cell_outlines {
if let Some(die_area) = &def.die_area {
if let Some(outline_layer_name) = &options.lef_import_options.cell_outline_layer {
let outline_layer = options
.lef_import_options
.get_or_create_layer_by_name(chip, outline_layer_name)?;
let geometry = die_area.cast().into();
chip.insert_shape(&top_cell, &outline_layer, geometry);
}
}
}
log::info!(
"Import components from DEF. Number of components: {}",
def.components.len()
);
for component in &def.components {
let module = chip.cell_by_name(component.model_name.as_str()).ok_or(
LefDefImportError::ComponentModelNotFound {
component_name: component.name.clone(),
model_name: component.model_name.clone(),
},
)?;
let inst =
chip.create_cell_instance(&top_cell, &module, Some(component.name.clone().into()));
if let Some((displacement, orientation, is_fixed)) = &component.position {
let tf = def_orient_to_transform(orientation)
.then(&db::SimpleTransform::translate(displacement.v().cast()));
chip.set_transform(&inst, tf);
}
}
if options.import_blockages {
}
if options.import_nets {
import_def_netlist(options, def, &top_cell, chip)?;
}
if options.import_wiring {
let lef = lef.ok_or_else(|| {
LefDefImportError::Other(
"No LEF data is provided but needed for import of wiring.".into(),
)
})?;
import_def_regular_wiring(options, lef, def, &top_cell, chip)?;
}
Ok(())
}
fn def_orient_to_transform<Crd>(orient: &Orient) -> db::SimpleTransform<Crd>
where
Crd: CoordinateType,
{
let (orientation, flipped) = orient.decomposed();
let angle = match orientation {
Orient::N => Angle::R0,
Orient::S => Angle::R180,
Orient::E => Angle::R270,
Orient::W => Angle::R90,
_ => unreachable!(), };
db::SimpleTransform::new(flipped, angle, Crd::one(), db::Vector::zero())
}
#[test]
fn test_lefdef_to_chip() {
use crate::def_parser::read_def_chars;
use crate::lef_parser::read_lef_chars;
let data = r#"
# Parts from gscl45nm.lef.
VERSION 5.5 ;
NAMESCASESENSITIVE ON ;
BUSBITCHARS "[]" ;
DIVIDERCHAR "/" ;
PROPERTYDEFINITIONS
LAYER contactResistance REAL ;
END PROPERTYDEFINITIONS
UNITS
DATABASE MICRONS 2000 ;
END UNITS
MANUFACTURINGGRID 0.0025 ;
LAYER poly
TYPE MASTERSLICE ;
END poly
LAYER contact
TYPE CUT ;
SPACING 0.075 ;
PROPERTY contactResistance 10.5 ;
END contact
LAYER metal1
TYPE ROUTING ;
DIRECTION HORIZONTAL ;
PITCH 0.19 ;
WIDTH 0.065 ;
SPACING 0.065 ;
RESISTANCE RPERSQ 0.38 ;
END metal1
LAYER via1
TYPE CUT ;
SPACING 0.075 ;
PROPERTY contactResistance 5.69 ;
END via1
LAYER metal2
TYPE ROUTING ;
DIRECTION VERTICAL ;
PITCH 0.19 ;
WIDTH 0.065 ;
SPACING 0.065 ;
RESISTANCE RPERSQ 0.38 ;
END metal2
LAYER OVERLAP
TYPE OVERLAP ;
END OVERLAP
VIA M2_M1_via DEFAULT
LAYER metal1 ;
RECT -0.0675 -0.0325 0.0675 0.0325 ;
LAYER via1 ;
RECT -0.0325 -0.0325 0.0325 0.0325 ;
LAYER metal2 ;
RECT -0.035 -0.0675 0.035 0.0675 ;
END M2_M1_via
VIARULE M2_M1 GENERATE
LAYER metal1 ;
ENCLOSURE 0 0.035 ;
LAYER metal2 ;
ENCLOSURE 0 0.035 ;
LAYER via1 ;
RECT -0.0325 -0.0325 0.0325 0.0325 ;
SPACING 0.14 BY 0.14 ;
END M2_M1
VIARULE M1_POLY GENERATE
LAYER poly ;
ENCLOSURE 0 0 ;
LAYER metal1 ;
ENCLOSURE 0 0.035 ;
LAYER contact ;
RECT -0.0325 -0.0325 0.0325 0.0325 ;
SPACING 0.14 BY 0.14 ;
END M1_POLY
SPACING
SAMENET metal1 metal1 0.1 ;
SAMENET metal2 metal2 0.1 ;
END SPACING
SITE CoreSite
CLASS CORE ;
SIZE 0.38 BY 2.47 ;
END CoreSite
MACRO INVX1
CLASS CORE ;
ORIGIN 0 0 ;
FOREIGN INVX1 0 0 ;
SIZE 0.57 BY 2.47 ;
SYMMETRY X Y ;
SITE CoreSite ;
PIN A
DIRECTION INPUT ;
USE SIGNAL ;
PORT
LAYER metal1 ;
RECT 0.1575 0.4875 0.2575 0.6225 ;
END
END A
PIN Y
DIRECTION OUTPUT ;
USE SIGNAL ;
PORT
LAYER metal1 ;
RECT 0.3475 0.2175 0.4125 1.815 ;
RECT 0.3125 0.2175 0.4475 0.4225 ;
END
END Y
PIN gnd
DIRECTION INOUT ;
USE GROUND ;
SHAPE ABUTMENT ;
PORT
LAYER metal1 ;
RECT 0.1625 -0.065 0.2275 0.4225 ;
RECT 0 -0.065 0.57 0.065 ;
END
END gnd
PIN vdd
DIRECTION INOUT ;
USE POWER ;
SHAPE ABUTMENT ;
PORT
LAYER metal1 ;
RECT 0.1625 1.265 0.2275 2.535 ;
RECT 0 2.405 0.57 2.535 ;
END
END vdd
END INVX1
MACRO INVX2
CLASS CORE ;
ORIGIN 0 0 ;
FOREIGN INVX1 0 0 ;
SIZE 0.57 BY 2.47 ;
SYMMETRY X Y ;
SITE CoreSite ;
PIN A
DIRECTION INPUT ;
USE SIGNAL ;
PORT
LAYER metal1 ;
RECT 0.1575 0.4875 0.2575 0.6225 ;
END
END A
PIN Y
DIRECTION OUTPUT ;
USE SIGNAL ;
PORT
LAYER metal1 ;
RECT 0.3475 0.2175 0.4125 1.815 ;
RECT 0.3125 0.2175 0.4475 0.4225 ;
END
END Y
PIN gnd
DIRECTION INOUT ;
USE GROUND ;
SHAPE ABUTMENT ;
PORT
LAYER metal1 ;
RECT 0.1625 -0.065 0.2275 0.4225 ;
RECT 0 -0.065 0.57 0.065 ;
END
END gnd
PIN vdd
DIRECTION INOUT ;
USE POWER ;
SHAPE ABUTMENT ;
PORT
LAYER metal1 ;
RECT 0.1625 1.265 0.2275 2.535 ;
RECT 0 2.405 0.57 2.535 ;
END
END vdd
END INVX2
END LIBRARY
"#;
let result = read_lef_chars(data.chars());
let lef = result.expect("LEF parsing failed.");
let def_data = r#"
VERSION 5.7 ;
DIVIDERCHAR "/" ;
BUSBITCHARS "[]" ;
DESIGN test_design ;
UNITS DISTANCE MICRONS 2000 ;
TECHNOLOGY FreePDK45 ;
DIEAREA ( 0 0 ) ( 10000 10000 ) ;
PINS 2 ;
- IN + NET IN
+ DIRECTION INPUT
- OUT + NET OUT
+ DIRECTION OUTPUT
END PINS
COMPONENTS 3 ;
- _1_ INVX1 ;
- _2_ INVX2 ;
- _3_ INVX2 ;
END COMPONENTS
NETS 6 ;
- IN ( PIN IN ) ;
- OUT ( PIN OUT ) ;
- net1 ( _1_ A ) ;
END NETS
END DESIGN
"#;
let result = read_def_chars(def_data.chars());
let def = result.expect("DEF parsing failed.");
let mut chip = db::Chip::new();
let mut options = DEFImportOptions::default();
options.lef_import_options.import_power_pins = false;
{
let result = import_lef_into_db(&options.lef_import_options, &lef, &mut chip);
if result.is_err() {
dbg!(&result);
}
}
{
let result = import_def_into_db(&options, Some(&lef), &def, &mut chip);
if result.is_err() {
dbg!(&result);
}
}
assert_eq!(chip.num_cells(), 1 + 2 + 1); let top = chip.cell_by_name("test_design").unwrap();
assert_eq!(chip.num_child_instances(&top), 3);
let invx1 = chip.cell_by_name("INVX1").expect("Cell not found.");
assert_eq!(chip.num_pins(&invx1), 2);
assert_eq!(chip.num_internal_nets(&top), 3 + 2); let pin_a = chip.pin_by_name(&invx1, "A").expect("Pin A not found.");
let inst1 = chip
.cell_instance_by_name(&top, "_1_")
.expect("Cell instance _1_ not found.");
let net = chip
.net_of_pin_instance(&chip.pin_instance(&inst1, &pin_a))
.expect("No net connected to _1_:A.");
assert_eq!(chip.net_name(&net), Some("net1".to_string()));
}