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

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

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

/// 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);
        }
    }
}