use libreda_db::prelude as db;
use libreda_db::prelude::{Scale, Direction, TryCastCoord, CoordinateType, Angle, TerminalId};
use libreda_db::traits::*;
use crate::lef_ast::{LEF, Shape, SignalUse};
use crate::def_ast::{DEF, NetTerminal};
use crate::common::{PinDirection, Orient};
use num_traits::{NumCast, PrimInt};
use std::fmt::Formatter;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub enum LefDefImportError {
ComponentModelNotFound {
component_name: String,
model_name: String,
},
ComponentNotFound(String),
LayerNotFound(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::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_cell_outlines: bool,
pub cell_outline_layer: Option<String>,
pub layer_mapping: HashMap<String, C::LayerId>,
pub create_missing_layers: 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_cell_outlines: true,
cell_outline_layer: Some("OUTLINE".to_string()),
layer_mapping: Default::default(),
create_missing_layers: true,
}
}
}
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,
}
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,
}
}
}
pub fn lef_to_db<C, Crd>(lef: &LEF) -> Result<C, LefDefImportError>
where Crd: NumCast + Ord + CoordinateType,
C: L2NEdit<Coord=Crd> {
let mut chip = C::new();
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> {
let dbu_per_micron = lef.technology.units.database_microns;
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)
}
}
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 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> {
let mut chip = C::new();
import_lef_into_db(&options.lef_import_options, lef, &mut chip)?;
import_def_into_db(&options, def, &mut chip)
.map(|_| chip)
}
pub fn import_def_into_db<C, Crd>(options: &DEFImportOptions<C>,
def: &DEF,
chip: &mut C) -> Result<(), LefDefImportError>
where Crd: NumCast + Ord + CoordinateType + PrimInt,
C: L2NEdit<Coord=Crd> {
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 (orientation, flipped) = orientation.decomposed();
let angle = match orientation {
Orient::N => Angle::R0,
Orient::S => Angle::R180,
Orient::E => Angle::R270,
Orient::W => Angle::R90,
_ => unreachable!() };
let tf = db::SimpleTransform::new(
flipped, angle, Crd::one(),
displacement.v().cast());
chip.set_transform(&inst, tf);
}
}
if options.import_blockages {
}
log::info!("Import netlist from DEF. Number of nets: {}", def.nets.len());
if options.import_nets {
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(())
}
#[test]
fn test_lefdef_to_chip() {
use crate::lef_parser::read_lef_chars;
use crate::def_parser::read_def_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 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.065 ;
SAMENET metal2 metal2 0.07 ;
SAMENET metal6 metal6 0.14 ;
SAMENET metal5 metal5 0.14 ;
SAMENET metal4 metal4 0.14 ;
SAMENET metal3 metal3 0.07 ;
SAMENET metal7 metal7 0.4 ;
SAMENET metal8 metal8 0.4 ;
SAMENET metal9 metal9 0.8 ;
SAMENET metal10 metal10 0.8 ;
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, &def, &mut chip);
if result.is_err() {
dbg!(&result);
}
}
assert_eq!(chip.num_cells(), 1 + 2);
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()));
}