use libreda_db::prelude as db;
use libreda_db::prelude::reference_access::*;
use libreda_db::prelude::{
Angle, CoordinateType, Direction, MapPointwise, Scale, TerminalId, ToPolygon, TryBoundingBox,
TryCastCoord,
};
use libreda_db::traits::*;
use crate::common::{Orient, PinDirection};
use crate::def_ast::{Component, Net, NetTerminal, Pin, DEF};
use crate::def_writer::write_def;
use crate::lef_ast::{Shape, SignalUse, LEF};
use num_traits::{NumCast, PrimInt};
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt::Formatter;
#[derive(Debug, Clone)]
pub enum LefDefExportError {
NameAlreadyExists(String),
UnknownLayerName,
Other(String),
}
impl std::fmt::Display for LefDefExportError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
LefDefExportError::NameAlreadyExists(name) => {
write!(f, "Name already exists in LEF/DEF structure: {}", name)
}
LefDefExportError::UnknownLayerName => {
write!(f, "No LEF/DEF layer name is known for a data-base layer.")
}
LefDefExportError::Other(msg) => write!(f, "{}", msg),
};
Ok(())
}
}
#[derive(Clone)]
pub struct DEFExportOptions<C: L2NBase> {
pub export_nets: bool,
pub keep_unconnected_nets: bool,
pub export_components: bool,
pub layer_mapping: HashMap<C::LayerId, String>,
pub outline_layer: Option<C::LayerId>,
}
impl<C: L2NBase> Default for DEFExportOptions<C> {
fn default() -> Self {
Self {
export_nets: true,
keep_unconnected_nets: false,
export_components: true,
layer_mapping: Default::default(),
outline_layer: Default::default(),
}
}
}
fn get_cell_outline_bbox<C, Crd>(
cell: &CellRef<C>,
outline_layer: &C::LayerId,
) -> Option<db::Rect<Crd>>
where
Crd: NumCast + Ord + CoordinateType + PrimInt + std::fmt::Display,
C: L2NBase<Coord = Crd>,
{
let shapes: Vec<_> = cell.each_shape_per_layer(outline_layer).collect();
if shapes.len() == 1 {
let shape = shapes.first().unwrap();
let geometry = shape.geometry_cloned();
let bbox = geometry.try_bounding_box();
if bbox.is_none() {
log::error!(
"Outline shape of cell '{}' has no defined bounding box.",
cell.name()
);
}
bbox
} else {
log::error!(
"Cell '{}' should have exactly one outline shape but has {}.",
cell.name(),
shapes.len()
);
None
}
}
pub fn export_db_to_def<C, Crd>(
options: &DEFExportOptions<C>,
chip: &C,
top_cell: &C::CellId,
def: &mut DEF,
) -> Result<(), LefDefExportError>
where
Crd: NumCast + Ord + CoordinateType + PrimInt + std::fmt::Display,
C: L2NBase<Coord = Crd>,
{
log::info!("Export cell to DEF structure: {}", chip.cell_name(top_cell));
def.design_name = Some(chip.cell_name(top_cell).into());
def.units = chip.dbu().to_u32().ok_or_else(|| {
LefDefExportError::Other(format!(
"Cannot convert DBU ({}) into a unsigned 32-bit integer.",
chip.dbu()
))
})?;
let top = chip.cell_ref(top_cell);
if options.outline_layer.is_none() {
return Err(LefDefExportError::Other(
"Outline layer must be specified in export options.".into(),
));
}
let outline_layer = options
.outline_layer
.as_ref()
.expect("outline_layer is not specified.");
{
let top_cell_outline_shapes: Vec<_> = chip.each_shape_id(top_cell, outline_layer).collect();
if top_cell_outline_shapes.len() == 1 {
let shape_id = top_cell_outline_shapes.first().unwrap();
let geometry = chip.with_shape(shape_id, |_layer, s| s.clone());
let polygon = geometry.to_polygon().exterior;
if let Some(rpolygon) = db::SimpleRPolygon::try_new(polygon.points()) {
let rpolygon: db::SimpleRPolygon<i32> = rpolygon.try_cast().ok_or_else(|| {
LefDefExportError::Other(
"Failed to convert DIEAREA into i32 coordinates.".into(),
)
})?; def.die_area = Some(rpolygon);
} else {
log::error!("Failed to convert die area into a rectilinear polygon.");
}
} else {
log::warn!(
"Top cell should have exactly one outline shape but has {}. DIEAREA is not set.",
top_cell_outline_shapes.len()
);
}
}
let macro_outlines = {
let mut macro_outlines = HashMap::new();
let mut cells_without_outline: Vec<CellRef<C>> = Vec::new();
for subcell in top.each_cell_dependency() {
if let Some(outline) = get_cell_outline_bbox(&subcell, outline_layer) {
macro_outlines.insert(subcell.id(), outline);
} else {
cells_without_outline.push(subcell);
}
}
if !cells_without_outline.is_empty() {
log::error!(
"Number of cells without outline: {}",
cells_without_outline.len()
);
for cell in &cells_without_outline {
log::error!("Cell '{}' has no outline.", cell.name());
}
return Err(LefDefExportError::Other(format!(
"{} cells have no outline.",
cells_without_outline.len()
)));
}
macro_outlines
};
let instance_names = export_instances_to_def(options, top.clone(), ¯o_outlines, def)?;
let net_names = export_nets_and_pins_to_def(options, top.clone(), &instance_names, def)?;
Ok(())
}
fn compare_name<S: Ord>(a: &Option<S>, b: &Option<S>) -> Ordering {
match (a, b) {
(None, None) => Ordering::Equal,
(_, None) => Ordering::Less,
(None, _) => Ordering::Greater,
(Some(a), Some(b)) => a.cmp(&b),
}
}
fn export_instances_to_def<C, Crd>(
options: &DEFExportOptions<C>,
top: CellRef<C>,
macro_outlines: &HashMap<C::CellId, db::Rect<Crd>>,
def: &mut DEF,
) -> Result<HashMap<C::CellInstId, String>, LefDefExportError>
where
Crd: NumCast + Ord + CoordinateType + PrimInt,
C: L2NBase<Coord = Crd>,
{
log::info!("Export instances to DEF: {}", top.num_child_instances());
let mut instances: Vec<_> = top.each_cell_instance().collect();
instances.sort_by(|a, b| compare_name(&a.name(), &b.name()));
let mut instance_names = HashMap::new();
let mut instance_name_counter = 1;
let mut get_instance_name = |inst: &CellInstRef<C>| -> String {
let name = instance_names.entry(inst.id()).or_insert_with(|| {
if let Some(name) = inst.name() {
name.into()
} else {
loop {
let new_name = format!("__{}__", instance_name_counter);
instance_name_counter += 1;
if top.cell_instance_by_name(new_name.as_str()).is_none() {
break new_name;
}
}
}
});
name.clone()
};
for inst in instances {
let mut component = Component::default();
component.name = get_instance_name(&inst);
component.model_name = inst.template().name().into();
let is_fixed = false; let tf = inst.get_transform();
let displacement: db::Point<_> = {
let outline = macro_outlines
.get(&inst.template_id())
.expect("Macro outline not found.");
let outline_transformed = outline.transform(|p| tf.transform_point(p));
outline_transformed.lower_left().cast()
};
let orient = match tf.rotation {
Angle::R0 => Orient::N,
Angle::R90 => Orient::W,
Angle::R180 => Orient::S,
Angle::R270 => Orient::E,
};
let orient = if tf.mirror { orient.flipped() } else { orient };
component.position = Some((displacement, orient, is_fixed));
def.components.push(component)
}
Ok(instance_names)
}
fn export_nets_and_pins_to_def<C, Crd>(
options: &DEFExportOptions<C>,
top: CellRef<C>,
instance_names: &HashMap<C::CellInstId, String>,
def: &mut DEF,
) -> Result<HashMap<C::NetId, String>, LefDefExportError>
where
C: L2NBase<Coord = Crd>,
{
log::info!("Export nets to DEF: {}", top.num_internal_nets());
let mut net_names = HashMap::new();
let mut net_name_generator = 1;
let mut create_new_net_name = |prefix: &str| -> String {
loop {
let new_name = format!("__{}{}__", prefix, net_name_generator);
net_name_generator += 1;
if top.net_by_name(new_name.as_str()).is_none() {
break new_name;
}
}
};
let mut get_net_name = |net: &NetRef<C>| -> String {
let name = net_names.entry(net.id()).or_insert_with(|| {
if let Some(name) = net.name() {
name.into()
} else {
create_new_net_name("")
}
});
name.clone()
};
let mut nets: Vec<_> = top.each_net().collect();
nets.sort_by(|a, b| compare_name(&a.name(), &b.name()));
for net_ref in nets {
let mut net = Net::default();
net.name = Some(get_net_name(&net_ref).to_string());
for pin in net_ref.each_pin() {
net.terminals.push(NetTerminal::IoPin(pin.name().into()));
}
for pin in net_ref.each_pin_instance() {
net.terminals.push(NetTerminal::ComponentPin {
component_name: instance_names
.get(&pin.cell_instance().id())
.expect("Cell instance not found.")
.to_string(),
pin_name: pin.pin().name().into(),
});
}
if !net.terminals.is_empty() || options.keep_unconnected_nets {
def.nets.push(net);
}
}
let mut pins: Vec<_> = top.each_pin().collect();
pins.sort_by_key(|pin| pin.name());
for pin_ref in pins {
let mut pin = Pin::default();
pin.pin_name = pin_ref.name().into();
pin.net_name = pin_ref
.net()
.map(|net| {
net_names
.get(&net.id())
.expect("Net name not found.")
.clone()
})
.unwrap_or_else(|| {
let new_net_name = create_new_net_name("pin_unconnected");
let mut net = Net::default();
net.name = Some(new_net_name.clone());
def.nets.push(net);
new_net_name
});
def.pins.push(pin);
}
Ok(net_names)
}
#[test]
fn test_export_to_def() {
use db::traits::*;
use libreda_db::prelude as db;
let mut chip = db::Chip::new();
let outline_layer = chip.create_layer(1, 0);
let top_cell = chip.create_cell("TOP".to_string());
let pin_a = chip.create_pin(&top_cell, "Input_A".to_string(), db::Direction::Input);
let pin_b = chip.create_pin(&top_cell, "Output_Y".to_string(), db::Direction::Output);
let net_a = chip.create_net(&top_cell, Some("net_a".to_string()));
let net_b = chip.create_net(&top_cell, Some("net_b".to_string()));
chip.connect_pin(&pin_a, Some(net_a.clone()));
chip.connect_pin(&pin_b, Some(net_b.clone()));
let sub_cell = chip.create_cell("SUB".to_string());
chip.insert_shape(
&sub_cell,
&outline_layer,
db::Rect::new((0, 0), (10, 10)).into(),
);
chip.create_cell_instance(&top_cell, &sub_cell, Some("inst1".to_string()));
chip.create_cell_instance(&top_cell, &sub_cell, Some("inst2".to_string()));
chip.create_cell_instance(&top_cell, &sub_cell, None);
chip.create_cell_instance(&top_cell, &sub_cell, None);
let mut def = DEF::default();
let mut export_options = DEFExportOptions::default();
export_options.outline_layer = Some(outline_layer);
let result = export_db_to_def(&export_options, &chip, &top_cell, &mut def);
if result.is_err() {
dbg!(&result);
}
result.expect("DEF export failed.");
let mut buffer: Vec<u8> = Vec::new();
write_def(&mut buffer, &def).expect("Writing DEF failed.");
let def_string = String::from_utf8(buffer).expect("DEF writer output is not valid UTF-8.");
println!("{}", &def_string);
}