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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Copyright (c) 2020-2022 Thomas Kramer.
// SPDX-FileCopyrightText: 2022 Thomas Kramer <code@tkramer.ch>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

//! Remove buffer cells or buffer trees from a netlist while preserving the logic function.

use crate::db::*;
use std::collections::{HashMap, HashSet};

/// Buffer removal engine. Removes non-inverting buffers and inverting buffers.
/// Buffer cells are identified by name. The names must be specified manually using `add_non_inverting_buffer()` and `add_inverting_buffer()`.
pub struct BufferRemover {
    non_inverting_buffer_cells: HashSet<String>,
    inverting_buffer_cells: HashSet<String>,
}

impl BufferRemover {
    /// Register the name of a non-inverting buffer cell.
    pub fn register_non_inverting_buffer(&mut self, name: String) {
        self.non_inverting_buffer_cells.insert(name);
    }

    /// Register the name of a inverting buffer cell.
    pub fn register_inverting_buffer(&mut self, name: String) {
        self.inverting_buffer_cells.insert(name);
    }

    /// Find buffer cells by their name.
    /// Take only cells which have exactly one input and one output.
    /// Return a hashmap with cell IDs as keys and (input pin, output pin) pairs as values.
    fn get_valid_buffer_cells_by_names<N>(
        &self,
        netlist: &N,
        names: &HashSet<String>,
    ) -> HashMap<N::CellId, (N::PinId, N::PinId)>
    where
        N: NetlistBase,
    {
        names
            .iter()
            .filter_map(|name| netlist.cell_by_name(name.as_str()))
            // Test if has unique input/output pair.
            .filter_map(|cell| {
                let cell = netlist.cell_ref(&cell);

                let inputs: Vec<_> = cell.each_input_pin().collect();
                let outputs: Vec<_> = cell.each_input_pin().collect();

                if inputs.len() == 1 && outputs.len() == 1 {
                    let i = inputs.into_iter().next().unwrap().id();
                    let o = outputs.into_iter().next().unwrap().id();
                    Some((cell.id(), (i, o)))
                } else {
                    // Can't work with this cell.
                    None
                }
            })
            .collect()
    }

    /// Remove all the non-inverting buffers from the `top` circuit.
    /// They are found by the name which is specified using `.add_non_inverting_buffer()`.
    /// Return the number of removed buffer instances.
    pub fn remove_non_inverting_buffers<N>(&self, netlist: &mut N, top: &N::CellId) -> usize
    where
        N: NetlistEdit,
    {
        // Find the buffer cells to be removed.
        let buffer_cells =
            self.get_valid_buffer_cells_by_names(netlist, &self.non_inverting_buffer_cells);

        let mut num_removed_instances = 0;

        for (buffer_cell, (input_pin, output_pin)) in buffer_cells {
            // Find all instances of this buffer cell in `top`.
            let buffer_instances: Vec<_> = netlist
                .cell_ref(top)
                .each_cell_instance()
                .filter(|inst| inst.template_id() == buffer_cell)
                .map(|inst| inst.id())
                .collect();

            for inst in buffer_instances {
                self.remove_non_inverting_buffer(netlist, &inst, &input_pin, &output_pin);
                num_removed_instances += 1;
            }
        }

        num_removed_instances
    }

    /// Remove a non-inverting buffer cell and merge the nets at the input and output.
    fn remove_non_inverting_buffer<N>(
        &self,
        netlist: &mut N,
        buffer_instance: &N::CellInstId,
        input_pin: &N::PinId,
        output_pin: &N::PinId,
    ) where
        N: NetlistEdit,
    {
        let input_net =
            netlist.net_of_pin_instance(&netlist.pin_instance(buffer_instance, input_pin));
        let output_net =
            netlist.net_of_pin_instance(&netlist.pin_instance(buffer_instance, output_pin));

        netlist.remove_cell_instance(buffer_instance);

        if let (Some(i), Some(o)) = (input_net, output_net) {
            netlist.replace_net(&o, &i);
        }
    }
}