LEF/DEF input and output module for LibrEDA.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

479 lines
15 KiB

/*
* Copyright (c) 2021-2021 Thomas Kramer.
*
* This file is part of LibrEDA
* (see https://codeberg.org/libreda/libreda-lefdef).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//! Write DEF data structure into DEF file format.
use std::io::Write;
use std::fmt;
use crate::def_ast::{DEF, NetTerminal, Blockage, SpacingOrDesignRuleWidth, BlockageShape, PlacementBlockageType};
use std::fmt::{Display, Debug};
use libreda_db::prelude::SimpleRPolygon;
use num_traits::PrimInt;
/// Error type that can happen when serializing a DEF file.
#[derive(Debug)]
pub enum DEFWriterError {
/// IO Error.
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)
}
}
}
/// Write the point of a polygon in the DEF format: ( x1 y1 ) ( x2 y2 ) ...
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(())
}
/// Write the DEF data structure into its ASCII representation.
/// Ordering of the statements follows the official recommendation.
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(())
};
// VERSION
for v in &def.version {
writeln!(w, "VERSION {} ;", v)?;
}
// NAMESCASESENSITIVE
writeln!(w, "NAMESCASESENSITIVE ON ;")?;
// DIVIDERCHAR
writeln!(w, r#"DIVIDERCHAR "{}" ;"#, def.dividerchar)?;
// BUSBITCHARS
writeln!(w, r#"BUSBITCHARS "{}{}" ;"#, def.busbitchars.0, def.busbitchars.1)?;
// DESIGN
for n in &def.design_name {
writeln!(w, "DESIGN {} ;", n)?;
}
// TECHNOLOGY
for n in &def.technology {
writeln!(w, "TECHNOLOGY {} ;", n)?;
}
// UNITS
writeln!(w, "UNITS DISTANCE MICRONS {} ;", def.units)?;
// HISTORY
for h in &def.history {
// TODO: Escape or deny special characters.
writeln!(w, "HISTORY {} ;", h)?;
}
// PROPERTYDEFINITIONS
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")?;
}
// DIEAREA
if let Some(area) = &def.die_area {
write!(w, "DIEAREA ")?;
write_polygon(w, area)?;
writeln!(w, ";")?;
}
// ROWS
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, " ;")?;
}
// TRACKS
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, " ;")?;
}
// GCELLGRID
// VIAS
// STYLES
// NONDEFAULTRULES
// REGIONS
if !def.regions.is_empty() {
writeln!(w, "REGIONS {} ;", def.regions.len())?;
for (name, region) in &def.regions {
indent(w, 1)?;
write!(w, "- {} ", name)?;
// Write rectangles that define the region.
for r in &region.regions {
write!(w, "( {} {} ) ", r.lower_left(), r.upper_right())?;
}
writeln!(w)?;
// TYPE
for region_type in &region.region_type {
indent(w, 2)?;
writeln!(w, "+ TYPE {} ", region_type)?;
}
// TODO: PROPERTY
}
writeln!(w, "END REGIONS")?;
}
// COMPONENTMASKSHIFT
// COMPONENTS
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)?; }
write!(w, "+ SOURCE {} ", comp.source)?;
// Position
// TODO: COVER
match &comp.position {
None => write!(w, "+ UNPLACED ")?,
Some((p, orient, false)) => write!(w, "+ PLACED {} {} ", p, orient)?,
Some((p, orient, true)) => write!(w, "+ FIXED {} {} ", p, orient)?,
}
// TODO: MASKSHIFT
// HALO
for (soft, left, bottom, right, top) in &comp.halo {
write!(w, "+ HALO ")?;
if *soft { write!(w, "SOFT ")?; }
write!(w, "{} {} {} {} ", left, bottom, right, top)?;
}
// ROUTEHALO
for (dist, min_layer, max_layer) in &comp.route_halo {
write!(w, "+ ROUTEHALO {} {} {} ", dist, min_layer, max_layer)?;
}
// WEIGHT
if comp.weight != 0 {
write!(w, "+ WEIGHT {} ", comp.weight)?;
}
// REGION
for r in &comp.region {
write!(w, "+ REGION {} ", r)?;
}
// PROPERTY: TODO
writeln!(w, ";")?;
}
writeln!(w, "END COMPONENTS")?;
}
// PINS
// PINPROPERTIES
// BLOCKAGES
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 {
BlockageShape::Rect(r) => {
writeln!(w, "RECT ( {} {} ) ( {} {} )",
r.lower_left().x, r.lower_left.y, r.upper_right().x, r.upper_right().y)?;
}
BlockageShape::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")?;
}
// SLOTS
// FILLS
// SPECIALNETS
// NETS
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)?
}
};
}
}
// SHIELDEDNET
if !net.shield_nets.is_empty() {
writeln!(w)?;
for s in &net.shield_nets {
indent(w, 1)?;
writeln!(w, "+ SHIELDEDNET {}", s)?;
}
}
// VPIN
// SUBNET
// XTALK
if net.xtalk_class != 0 {
indent(w, 1)?;
writeln!(w, "+ XTALK {}", net.xtalk_class)?;
}
// NONDEFAULTRULE
if let Some(r) = &net.non_default_rule {
indent(w, 1)?;
writeln!(w, "+ NONDEFAULTRULE {}", r)?;
}
// regular wiring
for wiring in &net.regular_wiring {
// TODO
indent(w, 1)?;
writeln!(w, "+ {}", wiring.class)?;
for wiring_stmt in &wiring.wiring {
// TODO
}
}
// SOURCE
if net.source != Default::default() {
indent(w, 1)?;
writeln!(w, "+ SOURCE {}", net.source)?;
}
// FIXEDBUMP
if net.fixed_bump {
indent(w, 1)?;
writeln!(w, "+ FIXEDBUMP")?;
}
// FREQUENCY
if let Some(f) = net.frequency {
indent(w, 1)?;
writeln!(w, "+ FREQUENCY {}", f)?;
}
// Original
if let Some(o) = &net.original {
indent(w, 1)?;
writeln!(w, "+ ORIGINAL {}", o)?;
}
// USE
if net.net_use != Default::default() {
indent(w, 1)?;
writeln!(w, "+ USE {}", net.net_use)?;
}
// PATTERN
if net.pattern != Default::default() {
indent(w, 1)?;
writeln!(w, "+ PATTERN {}", net.pattern)?;
}
// ESTCAP
if let Some(c) = net.est_cap {
indent(w, 1)?;
writeln!(w, "+ ESTCAP {}", c)?;
}
// WEIGHT
if net.weight != 1 {
indent(w, 1)?;
writeln!(w, "+ WEIGHT {}", net.weight)?;
}
// PROPERTY
for (name, value) in &net.properties {
indent(w, 1)?;
writeln!(w, "+ PROPERTY {} {}", name, value)?;
}
writeln!(w, " ;")?;
}
writeln!(w, "END NETS")?;
}
// SCANCHAINS
// GROUPS
// BEGINEXT
writeln!(w, "END DESIGN")?;
Ok(())
}