1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
/*
 * Copyright (c) 2020-2021 Thomas Kramer.
 *
 * This file is part of LibrEDA
 * (see https://codeberg.org/libreda).
 *
 * 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/>.
 */

//! Representation of the placement problem.
//!
//! A trait based representation gives more flexibility than passing naked arguments
//! to the placement engine.

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

/// Describe the placement status of a cell instance.
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum PlacementStatus {
    /// The instance location is fixed and must remain.
    Fixed,
    /// The instance can be put into another location by the placement engine.
    Movable,
    /// The instance should be ignored by the placement engine.
    Ignore,
}

/// Representation of the placement task.
pub trait PlacementProblem<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;

    /// Get a list of polygons which describe where cells are allowed to be placed.
    fn placement_region(&self) -> Vec<db::SimpleRPolygon<C::Coord>>;

    /// Get regions which should not be used for placement but can if necessary.
    /// Overlap of cells with this regions should be minimized.
    fn soft_blockages(&self) -> Vec<db::SimpleRPolygon<C::Coord>> {
        vec![]
    }

    /// Get the position of a cell instance which should be used as an initial value
    /// for the optimization.
    /// This is most likely the output of the previous placement step.
    fn initial_position(&self, cell_instance: &C::CellInstId) -> db::SimpleTransform<C::Coord>;

    /// Tell if the cell instance can be moved by the placement engine.
    fn placement_status(&self, cell_instance: &C::CellInstId) -> PlacementStatus;

    /// Get the abutment box / outline of the cell.
    fn cell_outline(&self, cell: &C::CellId) -> Option<db::Rect<C::Coord>>;

    /// Get the abutment box / outline of the cell instance.
    fn cell_instance_outline(&self, cell_instance: &C::CellInstId) -> Option<db::Rect<C::Coord>> {
        let template = self.fused_layout_netlist().template_cell(cell_instance);
        self.cell_outline(&template)
    }

    /// 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.
    fn net_weight(&self, _net: &C::NetId) -> f64 {
        1.0
    }

    /// TODO: fn arc_weight(&self, arc: &ArcId<C>) -> f64;

    /// Get the set of fixed instances.
    fn get_fixed_instances(&self) -> HashSet<C::CellInstId> {
        self.fused_layout_netlist()
            .each_cell_instance(&self.top_cell())
            .filter(|inst| self.placement_status(inst) == PlacementStatus::Fixed)
            .collect()
    }


    /// Get the set of movable cell instances.
    fn get_movable_instances(&self) -> HashSet<C::CellInstId> {
        self.fused_layout_netlist()
            .each_cell_instance(&self.top_cell())
            .filter(|inst| self.placement_status(inst) == PlacementStatus::Movable)
            .collect()
    }
}