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
// Copyright (c) 2020-2021 Thomas Kramer.
// SPDX-FileCopyrightText: 2022 Thomas Kramer <code@tkramer.ch>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

//! 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()
    }
}