use crate::common::PinDirection;
use crate::def_ast::{
Blockage, ComponentSource, DEFSignalUse, NetTerminal, PlacementBlockageType, RectOrPolygon,
SpacingOrDesignRuleWidth, DEF,
};
use libreda_db::prelude::{SimpleRPolygon, TryBoundingBox};
use num_traits::PrimInt;
use std::fmt;
use std::fmt::{Debug, Display};
use std::io::Write;
#[derive(Debug)]
pub enum DEFWriterError {
IOError(std::io::Error),
}
impl From<std::io::Error> for DEFWriterError {
fn from(err: std::io::Error) -> Self {
match err.kind() {
_ => DEFWriterError::IOError(err),
}
}
}
impl fmt::Display for DEFWriterError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use DEFWriterError::*;
match self {
IOError(e) => write!(f, "{}", e),
}
}
}
fn write_polygon<W: Write, T: PrimInt + Display>(
w: &mut W,
p: &SimpleRPolygon<T>,
) -> Result<(), DEFWriterError> {
for point in p.points() {
write!(w, "( {} {} ) ", point.x, point.y)?;
}
Ok(())
}
pub fn write_def<W: Write>(w: &mut W, def: &DEF) -> Result<(), DEFWriterError> {
let indent = |w: &mut W, level: u32| -> Result<(), std::io::Error> {
for _ in 0..level {
write!(w, " ")?;
}
Ok(())
};
for v in &def.version {
writeln!(w, "VERSION {} ;", v)?;
}
writeln!(w, r#"DIVIDERCHAR "{}" ;"#, def.dividerchar)?;
writeln!(
w,
r#"BUSBITCHARS "{}{}" ;"#,
def.busbitchars.0, def.busbitchars.1
)?;
for n in &def.design_name {
writeln!(w, "DESIGN {} ;", n)?;
}
for n in &def.technology {
writeln!(w, "TECHNOLOGY {} ;", n)?;
}
writeln!(w, "UNITS DISTANCE MICRONS {} ;", def.units)?;
for h in &def.history {
writeln!(w, "HISTORY {} ;", h)?;
}
if !def.property_definitions.is_empty() {
writeln!(w, "PROPERTYDEFINITIONS")?;
for (name, p) in &def.property_definitions {
indent(w, 1)?;
write!(w, "{} {} {} ", p.object_type, name, p.property_type)?;
if let Some((min, max)) = &p.range {
write!(w, "RANGE {} {} ", min, max)?;
}
if let Some(default) = &p.default_value {
write!(w, "{} ", default)?;
}
writeln!(w, ";")?;
}
writeln!(w, "END PROPERTYDEFINITIONS")?;
}
if let Some(area) = &def.die_area {
write!(w, "DIEAREA ")?;
if area.len() == 4 {
let rect = area.try_bounding_box().unwrap(); write!(
w,
"( {} {} ) ( {} {} ) ",
rect.lower_left().x,
rect.lower_left().y,
rect.upper_right().x,
rect.upper_right().y
)?;
} else {
write_polygon(w, area)?;
}
writeln!(w, ";")?;
}
for (row_name, row) in &def.rows {
write!(
w,
"ROW {} {} {} {} {}",
row_name, row.site_name, row.orig.0, row.orig.1, row.site_orient
)?;
write!(
w,
" DO {} BY {}",
row.step_pattern.num_x, row.step_pattern.num_y
)?;
if let Some((dx, dy)) = row.step_pattern.step {
write!(w, " STEP {} {}", dx, dy)?;
}
if !row.properties.is_empty() {
writeln!(w)?;
for (name, value) in &row.properties {
indent(w, 1)?;
writeln!(w, "+ PROPERTY {} {}", name, value)?;
}
}
writeln!(w, " ;")?;
}
for tracks in &def.tracks {
let xy = if tracks.is_horizontal { "Y" } else { "X" };
write!(
w,
"TRACKS {} {} DO {} STEP {}",
xy, tracks.start, tracks.num_tracks, tracks.step
)?;
if let Some((mask_num, same_mask)) = &tracks.mask {
write!(w, " MASK {}", mask_num)?;
if *same_mask {
write!(w, " SAMEMASK")?;
}
}
if !tracks.layers.is_empty() {
write!(w, " LAYER")?;
for layer in &tracks.layers {
write!(w, " {}", layer)?;
}
}
writeln!(w, " ;")?;
}
if !def.regions.is_empty() {
writeln!(w, "REGIONS {} ;", def.regions.len())?;
for (name, region) in &def.regions {
indent(w, 1)?;
write!(w, "- {} ", name)?;
for r in ®ion.regions {
write!(w, "( {} {} ) ", r.lower_left(), r.upper_right())?;
}
writeln!(w)?;
for region_type in ®ion.region_type {
indent(w, 2)?;
writeln!(w, "+ TYPE {} ", region_type)?;
}
}
writeln!(w, "END REGIONS")?;
}
if !def.components.is_empty() {
writeln!(w, "COMPONENTS {} ;", def.components.len())?;
for comp in &def.components {
indent(w, 1)?;
write!(w, "- {} {} ", comp.name, comp.model_name)?;
for m in &comp.eeq_master {
write!(w, "+ EEQMASTER {} ", m)?;
}
if comp.source != ComponentSource::default() {
write!(w, "+ SOURCE {} ", comp.source)?;
}
match &comp.position {
None => write!(w, "+ UNPLACED ")?,
Some((p, orient, false)) => write!(w, "+ PLACED ( {} {} ) {} ", p.x, p.y, orient)?,
Some((p, orient, true)) => write!(w, "+ FIXED ( {} {} ) {} ", p.x, p.y, orient)?,
}
for (soft, left, bottom, right, top) in &comp.halo {
write!(w, "+ HALO ")?;
if *soft {
write!(w, "SOFT ")?;
}
write!(w, "{} {} {} {} ", left, bottom, right, top)?;
}
for (dist, min_layer, max_layer) in &comp.route_halo {
write!(w, "+ ROUTEHALO {} {} {} ", dist, min_layer, max_layer)?;
}
if comp.weight != 0 {
write!(w, "+ WEIGHT {} ", comp.weight)?;
}
for r in &comp.region {
write!(w, "+ REGION {} ", r)?;
}
writeln!(w, ";")?;
}
writeln!(w, "END COMPONENTS")?;
}
if !def.pins.is_empty() {
writeln!(w, "PINS {} ;", def.pins.len())?;
for pin in &def.pins {
indent(w, 1)?;
writeln!(w, "- {} + NET {}", pin.pin_name, pin.net_name)?;
if pin.special {
indent(w, 2)?;
writeln!(w, "+ SPECIAL")?;
}
if let Some(dir) = pin.direction {
indent(w, 2)?;
writeln!(w, "+ DIRECTION {}", dir)?;
}
if let Some(expr) = &pin.net_expr {
indent(w, 2)?;
writeln!(w, "+ NETEXPR \"{}\"", expr)?;
}
if let Some(s) = &pin.supply_sensitivity {
indent(w, 2)?;
writeln!(w, "+ SUPPLYSENSITIVITY {}", s)?;
}
if let Some(s) = &pin.ground_sensitivity {
indent(w, 2)?;
writeln!(w, "+ GROUNDSENSITIVITY {}", s)?;
}
if pin.signal_use != DEFSignalUse::default() {
indent(w, 2)?;
writeln!(w, "+ USE {}", pin.signal_use)?;
}
for port in &pin.ports {
indent(w, 2)?;
writeln!(w, "+ PORT")?;
}
indent(w, 1)?;
writeln!(w, " ;")?;
}
writeln!(w, "END PINS")?;
}
if !def.blockages.is_empty() {
writeln!(w, "BLOCKAGES {} ;", def.blockages.len())?;
for blockage in &def.blockages {
match blockage {
Blockage::PlacementBlockage(block) => {
indent(w, 1)?;
writeln!(w, "- PLACEMENT")?;
if let Some(t) = &block.blockage_type {
indent(w, 2)?;
match t {
PlacementBlockageType::Soft => writeln!(w, "+ SOFT")?,
PlacementBlockageType::Partial(max_density) => {
writeln!(w, "+ PARTIAL {}", max_density)?
}
}
}
if block.pushdown {
indent(w, 2)?;
writeln!(w, "+ PUSHDOWN")?;
}
if let Some(component_name) = &block.component {
indent(w, 2)?;
writeln!(w, "+ COMPONENT {}", component_name)?;
}
for r in &block.rects {
indent(w, 2)?;
writeln!(
w,
"RECT ( {} {} ) ( {} {} )",
r.lower_left().x,
r.lower_left().y,
r.upper_right().x,
r.upper_right().y
)?;
}
indent(w, 1)?;
writeln!(w, " ;")?;
}
Blockage::LayerBlockage(block) => {
indent(w, 1)?;
writeln!(w, "- LAYER {}", block.layer)?;
if block.slots {
indent(w, 2)?;
writeln!(w, "+ SLOTS")?;
}
if block.fills {
indent(w, 2)?;
writeln!(w, "+ FILLS")?;
}
if block.pushdown {
indent(w, 2)?;
writeln!(w, "+ PUSHDOWN")?;
}
if block.except_pg_net {
indent(w, 2)?;
writeln!(w, "+ EXCEPTPGNET")?;
}
if let Some(component_name) = &block.component {
indent(w, 2)?;
writeln!(w, "+ COMPONENT {}", component_name)?;
}
if let Some(s) = &block.spacing_or_designrule_width {
indent(w, 2)?;
match s {
SpacingOrDesignRuleWidth::MinSpacing(s) => {
writeln!(w, "+ SPACING {}", s)?
}
SpacingOrDesignRuleWidth::DesignRuleWidth(width) => {
writeln!(w, "+ DESIGNRULEWIDTH {}", width)?
}
};
}
if let Some(mask_num) = block.mask_num {
indent(w, 2)?;
writeln!(w, "+ MASK {}", mask_num)?;
}
for shape in &block.blockage_shapes {
indent(w, 2)?;
match shape {
RectOrPolygon::Rect(r) => {
writeln!(
w,
"RECT ( {} {} ) ( {} {} )",
r.lower_left().x,
r.lower_left().y,
r.upper_right().x,
r.upper_right().y
)?;
}
RectOrPolygon::Polygon(p) => {
write!(w, "POLYGON ")?;
for point in p.iter() {
write!(w, "( {} {} ) ", point.x, point.y)?;
}
writeln!(w)?;
}
};
}
indent(w, 1)?;
writeln!(w, " ;")?;
}
}
}
writeln!(w, "END BLOCKAGES")?;
}
if !def.nets.is_empty() {
writeln!(w, "NETS {} ;", def.nets.len())?;
for net in &def.nets {
indent(w, 1)?;
if let Some(name) = &net.name {
write!(w, "- {}", name)?;
for term in &net.terminals {
match term {
NetTerminal::ComponentPin {
component_name,
pin_name,
} => write!(w, " ( {} {} )", component_name, pin_name)?,
NetTerminal::IoPin(pin_name) => write!(w, " ( PIN {} )", pin_name)?,
};
}
}
if !net.shield_nets.is_empty() {
writeln!(w)?;
for s in &net.shield_nets {
indent(w, 1)?;
writeln!(w, "+ SHIELDEDNET {}", s)?;
}
}
if net.xtalk_class != 0 {
indent(w, 1)?;
writeln!(w, "+ XTALK {}", net.xtalk_class)?;
}
if let Some(r) = &net.non_default_rule {
indent(w, 1)?;
writeln!(w, "+ NONDEFAULTRULE {}", r)?;
}
for wiring in &net.regular_wiring {
indent(w, 1)?;
writeln!(w, "+ {}", wiring.class)?;
for wiring_stmt in &wiring.wiring {
}
}
if net.source != Default::default() {
indent(w, 1)?;
writeln!(w, "+ SOURCE {}", net.source)?;
}
if net.fixed_bump {
indent(w, 1)?;
writeln!(w, "+ FIXEDBUMP")?;
}
if let Some(f) = net.frequency {
indent(w, 1)?;
writeln!(w, "+ FREQUENCY {}", f)?;
}
if let Some(o) = &net.original {
indent(w, 1)?;
writeln!(w, "+ ORIGINAL {}", o)?;
}
if net.net_use != Default::default() {
indent(w, 1)?;
writeln!(w, "+ USE {}", net.net_use)?;
}
if net.pattern != Default::default() {
indent(w, 1)?;
writeln!(w, "+ PATTERN {}", net.pattern)?;
}
if let Some(c) = net.est_cap {
indent(w, 1)?;
writeln!(w, "+ ESTCAP {}", c)?;
}
if net.weight != 1 {
indent(w, 1)?;
writeln!(w, "+ WEIGHT {}", net.weight)?;
}
for (name, value) in &net.properties {
indent(w, 1)?;
writeln!(w, "+ PROPERTY {} {}", name, value)?;
}
writeln!(w, " ;")?;
}
writeln!(w, "END NETS")?;
}
writeln!(w, "END DESIGN")?;
Ok(())
}