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/>.
 */

/// Implement the design rule traits from `libreda-db` for LEF.
/// This allows standardized read access to design-rules and technology data.
///
/// [`LEFDesignRuleAdapter`] creates the mapping between the layers defined in a layout and the layers defined in a LEF file
/// and then provides access to the LEF data via the technology traits.

use log;

use crate::lef_ast::{LEF, TechnologyLef, Layer};
use crate::lef_ast;
use libreda_db::prelude as db;
use db::traits::*;
use libreda_db::technology::layerstack;
use libreda_db::technology::rules;
use std::marker::PhantomData;
use std::collections::HashMap;
use num_traits::{FromPrimitive, ToPrimitive};
use libreda_db::prelude::technology::layerstack::{RoutingLayerType, RoutingLayerStack};

/// Provides standardized read access to the design-rules defined in a LEF structure.
pub struct LEFDesignRuleAdapter<'a, L: db::LayoutBase> {
    /// Underlying LEF data.
    lef: &'a LEF,
    /// Mapping from layer IDs to actual LEF layer data.
    layer_mapping: HashMap<L::LayerId, &'a lef_ast::Layer>,
    /// Layers sorted by the processing order: Starts with layers close to the substrate, ends with top level metal layers.
    layer_stack: Vec<(L::LayerId, &'a lef_ast::Layer)>,
    ty: PhantomData<L>,
}


impl<'a, L> LEFDesignRuleAdapter<'a, L>
    where L: db::LayoutBase,
          L::Coord: ToPrimitive // Need to be able to cast a float into Coord.
{
    /// Convert from a distance in data-base units into a LEF distance (f64).
    fn db_distance_to_lef(&self, db_distance: L::Coord) -> f64 {
        let dbu = self.lef.technology.units.database_microns as f64;

        db_distance.to_f64()
            .expect("Conversion from LEF distance unit to database distance unit failed.")
            / dbu
    }
}

impl<'a, L> LEFDesignRuleAdapter<'a, L>
    where L: db::LayoutBase,
          L::Coord: FromPrimitive // Need to be able to cast a float into Coord.
{
    /// Convert from a LEF distance (f64) into the correct unit used by the layout.
    fn lef_distance_to_db(&self, lef_distance: f64) -> L::Coord {
        let dbu = self.lef.technology.units.database_microns as f64;

        L::Coord::from_f64(lef_distance * dbu)
            .expect("Conversion from LEF distance unit to database distance unit failed.")
    }
}

impl<'a, L> LEFDesignRuleAdapter<'a, L>
    where L: db::LayoutBase,
          L::Coord: FromPrimitive + ToPrimitive // Need to be able to cast a float into Coord.
{
    /// Create a new design rule adapter for a LEF data structure.
    /// Derives the mapping from layer IDs and LEF layers based on the layer names.
    pub fn new(lef: &'a LEF, layout: &L) -> Self {
        Self::new_from_layer_mapping(lef, &Self::extract_layer_mapping_from_layout(layout))
    }

    /// Create a new design rule adapter for a LEF data structure
    /// with a custom mapping between layer names and layer IDs.
    pub fn new_from_layer_mapping(lef: &'a LEF, layer_ids_by_name: &HashMap<String, L::LayerId>) -> Self {
        let mut new = Self {
            lef,
            layer_mapping: Default::default(),
            layer_stack: Default::default(),
            ty: Default::default(),
        };

        new.create_layer_mapping(layer_ids_by_name);

        new
    }

    fn extract_layer_mapping_from_layout(layout: &L) -> HashMap<String, L::LayerId> {
        layout.each_layer()
            .filter_map(|layer_id| {
                let layer_name = layout.layer_info(&layer_id).name.as_ref().map(|n| n.to_string());
                layer_name.map(|name| (name, layer_id))
            })
            .collect()
    }

    /// Initialize the mapping: layer ID -> LEF layer structure.
    fn create_layer_mapping(&mut self, layer_ids_by_name: &HashMap<String, L::LayerId>) {
        // Create look-up table: Layer name -> LEF layer
        let lef_layers_by_name= self.lef.technology.layers.iter()
            .map(|layer| (layer.name(), layer));

        // Create look-up table: Layer ID -> LEF layer
        let lef_layers_by_layer_id: Vec<_> = lef_layers_by_name
            .filter_map(|(layer_name, layer)| {
                let layer_id = layer_ids_by_name.get(layer_name);
                layer_id.map(|id| (id.clone(), layer))
            })
            .collect();

        self.layer_mapping = lef_layers_by_layer_id.iter()
            .cloned()
            .collect();

        self.layer_stack = lef_layers_by_layer_id;
    }
}

impl<'a, L: db::LayoutBase> layerstack::RoutingLayerStack for LEFDesignRuleAdapter<'a, L> {
    type LayerId = L::LayerId;

    fn layer_stack(&self) -> Vec<RoutingLayerType<Self::LayerId>> {
        self.layer_stack.iter()
            // Take routing and cut layers only.
            .filter_map(|(id, layer)| match layer {
                Layer::MasterSlice(_) => None,
                Layer::Cut(_) => Some(RoutingLayerType::Cut(id.clone())),
                Layer::Routing(_) => Some(RoutingLayerType::Routing(id.clone())),
            })
            .collect()
    }
}

impl<'a, L: db::LayoutBase> rules::RuleBase for LEFDesignRuleAdapter<'a, L> {
    type LayerId = L::LayerId;
    type Distance = L::Coord;
    type Area = L::Coord;
}

impl<'a, L: db::LayoutBase> rules::MinimumSpacing for LEFDesignRuleAdapter<'a, L>
    where L::Coord: ToPrimitive + FromPrimitive {

    fn min_spacing_absolute(&self, layer_id: &Self::LayerId) -> Option<Self::Distance> {
        self.layer_mapping.get(layer_id)
            .and_then(|layer| match layer {
                Layer::MasterSlice(_) => unimplemented!("Min spacing for MASTERSLICE layers."),
                Layer::Cut(_) => unimplemented!("Min spacing for CUT layers."),
                Layer::Routing(routing_layer) => {
                    // Get spacing based on routing_layer.spacing_table or routing_layer.spacing.
                    get_absolute_min_spacing_of_routing_layer(routing_layer)
                }
            })
            .map(|d| self.lef_distance_to_db(d))
    }

    fn min_spacing(&self, layer_id: &Self::LayerId, run_length: Self::Distance, width: Self::Distance) -> Option<Self::Distance> {

        let run_length = self.db_distance_to_lef(run_length);
        let width = self.db_distance_to_lef(width);

        self.layer_mapping.get(layer_id)
            .and_then(|layer| match layer {
                Layer::MasterSlice(_) => unimplemented!("Min spacing for MASTERSLICE layers."),
                Layer::Cut(_) => unimplemented!("Min spacing for CUT layers."),
                Layer::Routing(routing_layer) => {
                    // Get spacing based on routing_layer.spacing_table or routing_layer.spacing.
                    get_min_spacing_of_routing_layer(routing_layer, run_length, width)
                }
            })
            .map(|d| self.lef_distance_to_db(d))
    }
}

/// Get the minimal spacing on the given layer.
/// Return `None` if there's no spacing rule defined.
/// TODO: Currently only SPACINGTABLEs are considered. Also support SPACING statements.
fn get_absolute_min_spacing_of_routing_layer(routing_layer: &lef_ast::RoutingLayer) -> Option<f64> {

    if let Some(spacing_table) = &routing_layer.spacing_table {

        spacing_table.spacings.first()
            .and_then(|spacings| spacings.first())
            .copied()
            .or(Some(0.))

    } else {

        // TODO: Derive spacing from SPACING statements.
        None

    }
}

/// Get the minimal spacing on the given layer.
/// Return `None` if there's no spacing rule defined.
/// TODO: Currently only SPACINGTABLEs are considered. Also support SPACING statements.
fn get_min_spacing_of_routing_layer(routing_layer: &lef_ast::RoutingLayer,
                                    parallel_runlength: f64,
                                    width: f64) -> Option<f64> {

    if let Some(spacing_table) = &routing_layer.spacing_table {

        // Find correct row for the given width.
        let row = spacing_table.spacings.iter()
            .zip(&spacing_table.widths)
            .filter(|(row, &w)| w <= width )
            .last()
            .map(|(row, _)| row);
        row.and_then(|row| {
            // Find correct value based on the runlength.
            row.iter()
                .zip(&spacing_table.parallel_run_lengths)
                .filter(|(spacing, &run_length)| run_length <= parallel_runlength)
                .last()
                .map(|(&spacing, _)| spacing)
        })

    } else {

        // TODO: Derive spacing from SPACING statements.
        None

    }
}


impl<'a, L: db::LayoutBase> rules::MinimumWidth for LEFDesignRuleAdapter<'a, L>
    where L: db::LayoutBase,
          L::Coord: FromPrimitive {

    fn min_width(&self, layer_id: &Self::LayerId, shape_length: Option<Self::Distance>) -> Option<Self::Distance> {
        self.layer_mapping.get(layer_id)
            .and_then(|layer| match layer {
                Layer::MasterSlice(_) => unimplemented!("Minimum width for LEF 'masterslice' layers is not implemented."),
                Layer::Cut(_) => unimplemented!("Minimum width for LEF 'cut' layers is not implemented."),
                Layer::Routing(routing_layer) => routing_layer.min_width
            })
            .map(|d| self.lef_distance_to_db(d))
    }
}

#[cfg(test)]
mod tests {
    use libreda_db::prelude as db;
    use db::traits::*;
    use db::technology::rules::*;
    use crate::lef_parser::read_lef_chars;
    use crate::lef_tech_adapter::LEFDesignRuleAdapter;

    const LEF_DATA: &'static str = r#"
VERSION 5.5 ;
NAMESCASESENSITIVE ON ;
BUSBITCHARS "[]" ;
DIVIDERCHAR "/" ;

PROPERTYDEFINITIONS
  LAYER contactResistance REAL ;
END PROPERTYDEFINITIONS

UNITS
  DATABASE MICRONS 1000 ;
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 ;
  MINWIDTH 0.05 ;
  SPACING 0.065 ;
  RESISTANCE RPERSQ 0.38 ;

  SPACINGTABLE
    PARALLELRUNLENGTH   0.0  1.0
    WIDTH 0.0           0.1  0.3
    WIDTH 0.5           0.4  0.5
  ;

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

END LIBRARY

    "#;

    #[test]
    fn test_lef_rule_adapter() {
        let lef = read_lef_chars(LEF_DATA.chars())
            .expect("Failed to parse LEF");

        // Create an empty layout with some layers which match the LEF layer names.
        let mut layout = db::Chip::new();
        let layer1 = layout.create_layer(1, 0);
        layout.set_layer_name(&layer1, Some("metal1".into()));

        dbg!(&lef.technology);

        let rules = LEFDesignRuleAdapter::new(&lef, &layout);

        // Try to fetch design rules.
        assert_eq!(rules.min_width(&layer1, None), Some(50));

        assert_eq!(rules.min_spacing_absolute(&layer1), Some(100));

        assert_eq!(rules.min_spacing(&layer1, 0, 0), Some(100));

        assert_eq!(rules.min_spacing(&layer1, 999, 0), Some(100));
        assert_eq!(rules.min_spacing(&layer1, 1000, 0), Some(300));

        assert_eq!(rules.min_spacing(&layer1, 0, 499), Some(100));
        assert_eq!(rules.min_spacing(&layer1, 0, 500), Some(400));
    }
}