libreda-pnr 0.0.4

Algorithm interface definitions of the LibrEDA place-and-route framework.
Documentation
// Copyright (c) 2020-2022 Thomas Kramer.
// SPDX-FileCopyrightText: 2022 Thomas Kramer <code@tkramer.ch>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

//! Representation of routing problems

use libreda_db::prelude as db;
use std::collections::HashMap;

// pub trait RoutingDesignRules<C: db::L2NBase> {
//
//     /// Metal routing layers, starting from the one closest to silicon.
//     fn routing_layers(&self) -> Vec<C::LayerId>;
//
//     /// Via routing layers.
//     fn via_layers(&self) -> Vec<C::LayerId>;
//
// }

/// Representation of the routing problem.
///
/// This excludes information directly related to the technology such as design rules.
pub trait RoutingProblem<C: db::L2NBase> {
    /// Get the base layout/netlist structure.
    fn fused_layout_netlist(&self) -> &C;

    /// Get the top cell whose content should be placed.
    fn top_cell(&self) -> C::CellId;

    /// Return the nets which should be routed.
    fn nets(&self) -> Box<dyn Iterator<Item=C::NetId> + '_>;

    /// Get the weight of a net. Default is `1.0`.
    /// When optimizing the wire-length, the *weighted* wire-length should be used.
    /// For example a weight `0.0` means that the net should not be considered for wire-length optimization.
    /// Place and route algorithms may or may not use the net weight.
    fn net_weight(&self, _net: &C::NetId) -> f64 {
        1.0
    }

    /// Weight of an 'arc', i.e. a net segment which starts at `signal_source` and ends at `signal_destination.
    /// Default weight is `1.0`.
    /// Place and route algorithms may or may not use the arc weight.
    fn arc_weight(&self, _signal_source: &db::TerminalId<C>, _signal_destination: &db::TerminalId<C>) -> f64 {
        1.0
    }

    /// Shapes where routes should not pass.
    fn blockages(&self) -> Box<dyn Iterator<Item=(C::LayerId, db::SimpleRPolygon<C::Coord>)>> {
        Box::new(std::iter::empty())
    }

    /// Routes must remain within this boundary.
    fn boundary(&self) -> Option<db::SimpleRPolygon<C::Coord>>;
}

/// Inputs for global routing algorithms.
pub trait GlobalRoutingProblem<C: db::L2NBase>: RoutingProblem<C> {}

/// Inputs for detail routing algorithms.
pub trait DetailRoutingProblem<C: db::L2NBase>: RoutingProblem<C> {
    /// Representation of a global route which guides a single net.
    type RoutingGuide: RoutingGuide<C>;

    /// Get the routing guide for this net.
    fn routing_guide(&self, net: &C::NetId) -> Option<&Self::RoutingGuide>;
}

/// Routing guide for a single net.
pub trait RoutingGuide<C: db::L2NBase> {
    /// Test if a point on a layer is included in the routing guide.
    fn contains_point(&self, layer: &C::LayerId, p: db::Point<C::Coord>) -> bool;

    /// Represent the routing guide as a collection of rectangles.
    fn to_rectangles(&self) -> Vec<(db::Rect<C::Coord>, C::LayerId)>;
}


/// Simple representation of the global and detail routing problems.
/// Implements the `RoutingProblem` traits.
#[derive(Clone)]
pub struct SimpleRoutingProblem<'a, LN, RoutingGuide = ()>
    where LN: db::L2NBase {
    /// A fused layout-netlist structure.
    pub chip: &'a LN,
    /// The ID of the top cell to be routed.
    pub top_cell: LN::CellId,
    /// The IDs of the nets to be routed.
    pub nets: Vec<LN::NetId>,
    /// Weights of nets for optimizing the total wire-length.
    /// Default weight is `1.0`. A net with weight `0.0` will not be optimized for length
    /// while a net with a high weight will be optimized for wiring length much more aggressively.
    pub net_weights: HashMap<LN::NetId, f64>,
    /// All routes should be contained within this boundary, if it is specified.
    pub boundary: Option<db::SimpleRPolygon<LN::Coord>>,
    /// Global routes.
    pub routing_guides: HashMap<LN::NetId, RoutingGuide>,
}

impl<'a, LN: db::L2NBase, RG> SimpleRoutingProblem<'a, LN, RG> {
    /// Create a new routing problem. By default, no nets are included.
    /// Nets which should be routed need to be added to the `nets` field.
    pub fn new(chip: &'a LN, top_cell: LN::CellId) -> Self {
        Self {
            chip,
            top_cell,
            nets: Default::default(),
            net_weights: Default::default(),
            boundary: Default::default(),
            routing_guides: Default::default(),
        }
    }

    /// Set global routes.
    pub fn with_routing_guides(mut self, routing_guides: HashMap<LN::NetId, RG>) -> Self {
        self.routing_guides = routing_guides;
        self
    }
}

impl<'a, LN, RG> RoutingProblem<LN> for SimpleRoutingProblem<'a, LN, RG>
    where LN: db::L2NBase {
    fn fused_layout_netlist(&self) -> &LN {
        self.chip
    }

    fn top_cell(&self) -> LN::CellId {
        self.top_cell.clone()
    }

    fn nets(&self) -> Box<dyn Iterator<Item=LN::NetId> + '_> {
        Box::new(self.nets.iter().cloned())
    }

    fn net_weight(&self, net: &LN::NetId) -> f64 {
        self.net_weights.get(net)
            .copied()
            .unwrap_or(1.0)
    }

    fn blockages(&self) -> Box<dyn Iterator<Item=(LN::LayerId, db::SimpleRPolygon<LN::Coord>)>> {
        Box::new(std::iter::empty())
    }

    fn boundary(&self) -> Option<db::SimpleRPolygon<LN::Coord>> {
        self.boundary.clone()
    }
}

impl<'a, LN: db::L2NBase, RG> GlobalRoutingProblem<LN> for SimpleRoutingProblem<'a, LN, RG> {}


impl<'a, LN: db::L2NBase, RG> DetailRoutingProblem<LN> for SimpleRoutingProblem<'a, LN, RG>
    where RG: RoutingGuide<LN> {
    type RoutingGuide = RG;

    fn routing_guide(&self, net: &LN::NetId) -> Option<&Self::RoutingGuide> {
        self.routing_guides.get(net)
    }
}