libreda-lefdef 0.0.2

LEF/DEF input/output for libreda-db.
Documentation
/*
 * 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/>.
 */

//! Import LEF and DEF structures by populating data base structures.

use libreda_db::prelude as db;
use libreda_db::prelude::{Scale, Direction, TryCastCoord, CoordinateType, Angle, TerminalId};
use libreda_db::traits::*;
use libreda_db::prelude::reference_access::*;

use crate::lef_ast::{LEF, Shape, SignalUse};
use crate::def_ast::{DEF, NetTerminal, Component, Net, Pin};
use crate::common::{PinDirection, Orient};
use num_traits::{NumCast, PrimInt};
use std::fmt::Formatter;
use std::collections::HashMap;
use std::cmp::Ordering;
use crate::def_writer::write_def;

/// Error type returned from LEF/DEF output functions.
#[derive(Debug, Clone)]
pub enum LefDefExportError {
    /// Unspecified error.
    Other(String),
}

impl std::fmt::Display for LefDefExportError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            LefDefExportError::Other(msg) => write!(f, "{}", msg)
        };

        Ok(())
    }
}

/// Convert a LEF shape into a database shape with the correct units.
fn convert_geometry<C: CoordinateType + NumCast>(dbu_per_micron: u64, shape: &Shape) -> db::Geometry<C> {
    let dbu_per_micron = dbu_per_micron as f64;
    // Convert the LEF geometry into a database geometry.
    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."); // This should actually not fail.
    geo
}

/// Control the export into DEF structures.
#[derive(Clone)]
pub struct DEFExportOptions<C: L2NBase> {
    /// Enable the export of nets. Default is `true`.
    pub export_nets: bool,
    /// Export components (cell instances). Default is `true`.
    pub export_components: bool,
    /// Provide alternative names for layers. For all other layers
    /// the default name defined in the `L2NBase` type will be used.
    pub layer_mapping: HashMap<C::LayerId, String>,
    /// Take the die-area from this layer.
    /// The layer must contain a single shape.
    pub outline_layer: Option<C::LayerId>,
}

impl<C: L2NBase> Default for DEFExportOptions<C> {
    fn default() -> Self {
        Self {
            export_nets: true,
            export_components: true,
            layer_mapping: Default::default(),
            outline_layer: Default::default(),
        }
    }
}


/// Populate a [`DEF`] structure from a type implementing the [`L2NBase`] trait.
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,
          C: L2NBase<Coord=Crd> {
    log::info!("Export cell to DEF structure: {}", chip.cell_name(top_cell));

    // Set the design name.
    def.design_name = Some(chip.cell_name(top_cell).into());

    let top = chip.cell_ref(top_cell);

    // Create components (child instances inside the top cell)
    // Also get a mapping of the possibly created instance names.
    let instance_names = export_instances_to_def(options, top.clone(), def)?;

    // Export nets.
    let net_names = export_nets_and_pins_to_def(options, top.clone(), &instance_names, def)?;

    Ok(())
}

// Sort by Option<String> such that `Nones` come at the end of a sorted list.
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),
    }
}


/// Populate a [`DEF`] structure with the component instances.
/// Return a name mapping from instance IDs to their DEF names.
/// The mapping contains possibly generated instance names.
fn export_instances_to_def<C, Crd>(options: &DEFExportOptions<C>,
                                   top: CellRef<C>,
                                   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());

    // Create components.
    let mut instances: Vec<_> = top.each_cell_instance().collect();
    // Sort the instances by name. Instances without name will come at the end.
    instances.sort_by(|a, b| compare_name(&a.name(), &b.name()));

    // Generate names for unnamed instances.
    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() {
                            // Name is still free.
                            break new_name;
                        }
                    }
                }
            });
        name.clone()
    };

    // Export components.
    for inst in instances {

        // Set component name.
        let mut component = Component::default();
        component.name = get_instance_name(&inst);
        // Set model/template name
        component.model_name = inst.template().name().into();

        // Set the component placement.
        let is_fixed = false; // TODO
        let tf = inst.get_transform();
        let displacement: db::Point<_> = tf.displacement.cast().into();
        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)
    }

    // Return the name mapping.
    Ok(instance_names)
}

/// Populate a [`DEF`] structure with nets and top-level pins.
/// Return a name mapping from net IDs to their DEF names.
/// The mapping contains possibly generated instance names.
///
/// # Parameters
/// * `instance_names`: Mapping from cell instance IDs to their DEF names. This is generated by `export_instances_to_def()`.
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>
{
    // Export nets.
    // Generate names for unnamed instances.
    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() {
                // Name is still free.
                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()));

    // Convert all nets.
    for net_ref in nets {
        // Create the DEF net.
        let mut net = Net::default();
        // Set the name.
        net.name = Some(get_net_name(&net_ref).to_string());

        // Set connected terminals.
        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(),
                });
        }

        def.nets.push(net);
    }

    // Create external pins.
    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();
        // Set the pin name.
        pin.pin_name = pin_ref.name().into();
        // Set the net name attached to the pin or create a dummy net if none is attached (required by DEF).
        pin.net_name = pin_ref.net()
            .map(|net|
                net_names.get(&net.id())
                    .expect("Net name not found.")
                    .clone()
            )
            .unwrap_or_else(|| {
                // There is no net connected to the external pin but DEF requires
                // that a net is specified.
                // Create an unconnected dummy net.
                let new_net_name = create_new_net_name("pin_unconnected");

                // Make sure the new net is also listed in the NETS section.
                let mut net = Net::default();
                net.name = Some(new_net_name.clone());
                def.nets.push(net);

                new_net_name
            });

        // Append the newly created pin to the list of pins.
        def.pins.push(pin);
    }

    Ok(net_names)
}


#[test]
fn test_export_to_def() {
    use libreda_db::prelude as db;
    use db::traits::*;

    let mut chip = db::Chip::new();
    let top_cell = chip.create_cell("TOP".to_string());

    // Create pins of the top cell.
    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);

    // Create nets in the top cell.
    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()));

    // Create a sub cell.
    let sub_cell = chip.create_cell("SUB".to_string());

    // Create sub cell instances in TOP.
    chip.create_cell_instance(&top_cell, &sub_cell, Some("inst1".to_string()));
    chip.create_cell_instance(&top_cell, &sub_cell, Some("inst2".to_string()));
    // Create unnamed instances.
    chip.create_cell_instance(&top_cell, &sub_cell, None);
    chip.create_cell_instance(&top_cell, &sub_cell, None);

    let mut def = DEF::default();
    let result = export_db_to_def(&DEFExportOptions::default(), &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);
}