From baf29d918a259d2d60c9f85b22237510f30d8ba4 Mon Sep 17 00:00:00 2001 From: "julian.speith" Date: Tue, 16 Jul 2024 14:31:48 +0200 Subject: [PATCH 1/3] started cleanup --- .../netlist_preprocessing.h | 211 ++ .../plugin_netlist_preprocessing.h | 139 +- .../python/python_bindings.cpp | 560 ++-- .../src/netlist_preprocessing.cpp | 2573 +++++++++++++++++ .../src/plugin_netlist_preprocessing.cpp | 1553 +--------- 5 files changed, 3101 insertions(+), 1935 deletions(-) create mode 100644 plugins/netlist_preprocessing/include/netlist_preprocessing/netlist_preprocessing.h create mode 100644 plugins/netlist_preprocessing/src/netlist_preprocessing.cpp diff --git a/plugins/netlist_preprocessing/include/netlist_preprocessing/netlist_preprocessing.h b/plugins/netlist_preprocessing/include/netlist_preprocessing/netlist_preprocessing.h new file mode 100644 index 00000000000..192254cf8e5 --- /dev/null +++ b/plugins/netlist_preprocessing/include/netlist_preprocessing/netlist_preprocessing.h @@ -0,0 +1,211 @@ +// MIT License +// +// Copyright (c) 2019 Ruhr University Bochum, Chair for Embedded Security. All Rights reserved. +// Copyright (c) 2019 Marc Fyrbiak, Sebastian Wallat, Max Hoffmann ("ORIGINAL AUTHORS"). All rights reserved. +// Copyright (c) 2021 Max Planck Institute for Security and Privacy. All Rights reserved. +// Copyright (c) 2021 Jörn Langheinrich, Julian Speith, Nils Albartus, René Walendy, Simon Klix ("ORIGINAL AUTHORS"). All Rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#pragma once + +#include "hal_core/defines.h" +#include "hal_core/utilities/result.h" + +#include + +namespace hal +{ + class Gate; + class Netlist; + class GateType; + class GateLibrary; + class Module; + class Net; + + namespace netlist_preprocessing + { + /** + * Removes all LUT fan-in endpoints that do not correspond to a variable within the Boolean function that determines the output of a gate. + * + * @param[in] nl - The netlist to operate on. + * @returns OK() and the number of removed LUT endpoints on success, an error otherwise. + */ + Result remove_unused_lut_inputs(Netlist* nl); + + /** + * Removes buffer gates from the netlist and connect their fan-in to their fan-out nets. + * Considers all combinational gates and takes their inputs into account. + * For example, a 2-input AND gate with one input being connected to constant `1` will also be removed. + * + * @param[in] nl - The netlist to operate on. + * @returns OK() and the number of removed buffers on success, an error otherwise. + */ + Result remove_buffers(Netlist* nl); + + /** + * Removes redundant gates from the netlist, i.e., gates that are functionally equivalent and are connected to the same input nets. + * + * @param[in] nl - The netlist to operate on. + * @param[in] filter - Optional filter to fine-tune which gates are being replaced. Default to a `nullptr`. + * @return OK() and the number of removed gates on success, an error otherwise. + */ + Result remove_redundant_gates(Netlist* nl, const std::function& filter = nullptr); + + /** + * Removes redundant sequential feedback loops. + * Sometimes flip-flops and some of their combinational fan-in form a feedback loop where the flip-flop input depends on its own output. + * For optimization, some synthesizers create multiple equivalent instances of these feedback loops. + * To simplify structural analysis, this function removes the redundant flip-flop gate of the loop from the netlist. + * Other preprocessing functions can then take care of the remaining combination gates of the loop. + * + * @param[in] nl - The netlist to operate on. + * @return OK() and the number of removed gates on success, an error otherwise. + */ + Result remove_redundant_loops(Netlist* nl); + + /** + * Removes redundant logic trees made up of combinational gates. + * If two trees compute the exact same function even if implemented with different gates we will disconnect one of the trees and afterwards clean up all dangling gates and nets. + * + * @param[in] nl - The netlist to operate on. + * @return OK() and the number of disconnected net on success, an error otherwise. + */ + Result remove_redundant_logic_trees(Netlist* nl); + + /** + * Removes gates for which all fan-out nets do not have a destination and are not global output nets. + * + * @param[in] nl - The netlist to operate on. + * @return OK() and the number of removed gates on success, an error otherwise. + */ + Result remove_unconnected_gates(Netlist* nl); + + /** + * Removes nets who have neither a source, nor a destination. + * + * @param[in] nl - The netlist to operate on. + * @return OK() and the number of removed nets on success, an error otherwise. + */ + Result remove_unconnected_nets(Netlist* nl); + + /** + * Calls remove_unconnected_gates / remove_unconnected_nets until there are no further changes. + * + * @param[in] nl - The netlist to operate on. + * @return OK() and the number of removed nets and gates on success, an error otherwise. + */ + Result remove_unconnected_looped(Netlist* nl); + + /** + * Apply manually implemented optimizations to the netlist centered around muxes. + * Currently implemented optimizations include: + * - removing inverters incase there are inverter gates in front and behind every data input and output of the mux + * - optimizing and therefore unifying possible inverters preceding the select signals by resynthesizing + * + * @param[in] nl - The netlist to operate on. + * @param[in] mux_inv_gl - A gate library only containing mux and inverter gates used for resynthesis. + * @return OK() and the difference in the total number of gates caused by these optimizations. + */ + Result manual_mux_optimizations(Netlist* nl, GateLibrary* mux_inv_gl); + + /** + * Builds for all gate output nets the Boolean function and substitutes all variables connected to vcc/gnd nets with the respective boolean value. + * If the function simplifies to a boolean constant cut the connection to the nets destinations and directly connect it to vcc/gnd. + * + * @param[in] nl - The netlist to operate on. + * @return OK() and the number rerouted destinations on success, an error otherwise. + */ + Result propagate_constants(Netlist* nl); + + /** + * Removes two consecutive inverters and reconnects the input of the first inverter to the output of the second one. + * If the first inverter has additional successors, only the second inverter is deleted. + * + * @param[in] nl - The netlist to operate on. + * @returns OK() and the number of removed inverter gates on success, an error otherwise. + */ + Result remove_consecutive_inverters(Netlist* nl); + + /** + * Replaces pins connected to GND/VCC with constants and simplifies the Boolean function of a LUT by recomputing the INIT string. + * + * @param[in] nl - The netlist to operate on. + * @return OK() and the number of simplified INIT strings on success, an error otherwise. + */ + Result simplify_lut_inits(Netlist* nl); + + /** + * Tries to reconstruct a name and index for each flip flop that was part of a multi-bit wire in the verilog code. + * This is NOT a general netlist reverse engineering algorithm and ONLY works on synthesized netlists with names annotated by the synthesizer. + * This function mainly focuses netlists synthesized with yosys since yosys names the output wires of the flip flops but not the gate it self. + * We try to reconstruct name and index for each flip flop based on the name of its output nets. + * + * The reconstructed indexed identifiers get annotated to the flip flop in the gate data container. + * + * @param[in] nl - The netlist to operate on. + * @return OK() and the number of reconstructed names on success, an error otherwise. + */ + Result reconstruct_indexed_ff_identifiers(Netlist* nl); + + /** + * Tries to reconstruct top module pin groups via indexed pin names. + * This should really be done by the verilog parser, but this is at the moment not the case. + * + * @param[in] nl - The netlist to operate on + * + * @return OK() and the number of reconstructed pin groups on success. + */ + Result reconstruct_top_module_pin_groups(Netlist* nl); + + /** + * Parses a design exchange format file and extracts the coordinates of a placed design for each component/gate. + * The extracted coordinates get annotated to the gates. + * + * @param[in] nl - The netlist to operate on. + * @param[in] def_file - Path to the def file. + * @return OK() on success, an error otherwise. + */ + Result parse_def_file(Netlist* nl, const std::filesystem::path& def_file); + + /** + * Create modules from large gates like RAMs and DSPs with the option to concat mutliple gate pingroups to larger consecutive pin groups + * + * TODO: document paramaters + */ + Result> create_multi_bit_gate_modules(Netlist* nl, const std::map>>& concatenated_pin_groups); + + /** + * TODO: document + */ + Result> create_nets_at_unconnected_pins(Netlist* nl); + + /** + * Iterates all flip-flops of the netlist or specified by the user. + * If a flip-flop has a `state` and a `neg_state` output, a new inverter gate is created and connected to the `state` output net as an additional destination. + * Finally, the `neg_state` output net is disconnected from the `neg_state` pin and re-connected to the new inverter gate's output. + * + * @param[in] nl - The netlist to operate on. + * @param[in] ffs - The flip-flops to operate on. Defaults to an empty vector, in which case all flip-flops of the netlist are considered. + * @param[in] inverter_type - The inverter gate type to use. Defaults to a `nullptr`, in which case the first inverter type found in the gate library is used. + * @returns OK() and the number of rerouted `neg_state` outputs on success, an error otherwise. + */ + Result unify_ff_outputs(Netlist* nl, const std::vector& ffs = {}, GateType* inverter_type = nullptr); + } // namespace netlist_preprocessing +} // namespace hal \ No newline at end of file diff --git a/plugins/netlist_preprocessing/include/netlist_preprocessing/plugin_netlist_preprocessing.h b/plugins/netlist_preprocessing/include/netlist_preprocessing/plugin_netlist_preprocessing.h index 564c1443a26..e6159ba9726 100644 --- a/plugins/netlist_preprocessing/include/netlist_preprocessing/plugin_netlist_preprocessing.h +++ b/plugins/netlist_preprocessing/include/netlist_preprocessing/plugin_netlist_preprocessing.h @@ -23,141 +23,62 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +/** + * @file plugin_netlist_preprocessing.h + * @brief This file contains all functions related to the HAL plugin API. + */ + #pragma once -#include "hal_core/netlist/netlist.h" #include "hal_core/plugin_system/plugin_interface_base.h" -#include "hal_core/utilities/result.h" namespace hal { + /** + * @class NetlistPreprocessingPlugin + * @brief Plugin interface for netlist preprocessing. + * + * This class provides an interface to integrate the netlist preprocessing as a plugin within the HAL framework. + */ class PLUGIN_API NetlistPreprocessingPlugin : public BasePluginInterface { public: + /** + * @brief Default constructor for `NetlistPreprocessingPlugin`. + */ + NetlistPreprocessingPlugin() = default; + + /** + * @brief Default destructor for `NetlistPreprocessingPlugin`. + */ + ~NetlistPreprocessingPlugin() = default; + /** - * Get the name of the plugin. + * @brief Get the name of the plugin. * * @returns The name of the plugin. */ std::string get_name() const override; /** - * Get the version of the plugin. + * @brief Get the version of the plugin. * * @returns The version of the plugin. */ std::string get_version() const override; /** - * Removes all LUT fan-in endpoints that do not correspond to a variable within the Boolean function that determines the output of a gate. - * - * @param[in] nl - The netlist to operate on. - * @returns The number of removed LUT endpoints on success, an error otherwise. - */ - static Result remove_unused_lut_inputs(Netlist* nl); - - /** - * Removes buffer gates from the netlist and connect their fan-in to their fan-out nets. - * Considers all combinational gates and takes their inputs into account. - * For example, a 2-input AND gate with one input being connected to constant `1` will also be removed. - * - * @param[in] nl - The netlist to operate on. - * @returns The number of removed buffers on success, an error otherwise. - */ - static Result remove_buffers(Netlist* nl); - - /** - * Removes redundant gates from the netlist, i.e., gates that are functionally equivalent and are connected to the same input nets. - * - * @param[in] nl - The netlist to operate on. - * @return The number of removed gates on success, an error otherwise. - */ - static Result remove_redundant_logic(Netlist* nl); - - /** - * Removes gates which outputs are all unconnected and not a global output net. - * - * @param[in] nl - The netlist to operate on. - * @return The number of removed gates on success, an error otherwise. - */ - static Result remove_unconnected_gates(Netlist* nl); - - /** - * Remove nets which have no source and not destination. - * - * @param[in] nl - The netlist to operate on. - * @return The number of removed nets on success, an error otherwise. - */ - static Result remove_unconnected_nets(Netlist* nl); - - /** - * Replaces pins connected to GND/VCC with constants and simplifies the boolean function of a LUT by recomputing the INIT string. - * - * @param[in] nl - The netlist to operate on. - * @return The number of simplified INIT strings on success, an error otherwise. - */ - static Result simplify_lut_inits(Netlist* nl); - - /** - * Builds the Boolean function of each output pin of the gate and constructs a gate tree implementing it. - * Afterwards the original output net is connected to the built gate tree and the gate is deleted if the 'delete_gate' flag is set. - * - * For the decomposition we currently only support the base operands AND, OR, INVERT, XOR. - * The function searches in the gate library for a fitting two input gate and uses a standard HAL gate type if none is found. - * - * @param[in] nl - The netlist to operate on. - * @param[in] gate - The gate to decompose. - * @param[in] delete_gate - Determines whether the original gate gets deleted by the function, defaults to true, - * @return Ok on success, an error otherwise. - */ - static Result decompose_gate(Netlist* nl, Gate* g, const bool delete_gate = true); - - /** - * Decomposes each gate of the specified type by building the Boolean function for each output pin of the gate and contructing a gate tree implementing it. - * Afterwards the original gate is deleted and the output net is connected to the built gate tree. - * - * For the decomposition we currently only support the base operands AND, OR, INVERT, XOR. - * The function searches in the gate library for a fitting two input gate and uses a standard HAL gate type if none is found. - * - * @param[in] nl - The netlist to operate on. - * @param[in] gate_types - The gate types that should be decomposed. - * @return Ok and the number of decomposed gates on success, an error otherwise. + * @brief Get a short description of the plugin. + * + * @returns The short description of the plugin. */ - static Result decompose_gates_of_type(Netlist* nl, const std::vector& gate_types); - - /** - * Tries to reconstruct a name and index for each flip flop that was part of a multibit wire in the verilog code. - * This is NOT a general netlist reverse engineering algorithm and ONLY works on synthesized netlists with names annotated by the synthesizer. - * This function mainly focuses netlists synthesized with yosys since yosys names the output wires of the flip flops but not the gate it self. - * We try to reconstruct name and index for each flip flop based on the name of its output nets. - * - * The reconstructed indexed identifiers get annoated to the flip flop in the gate data container. - * - * @param[in] nl - The netlist to operate on. - * return OK and the number of reconstructed names on success, an error otherwise. - */ - static Result reconstruct_indexed_ff_identifiers(Netlist* nl); - - /** - * Parses a design exchange format file and extracts the coordinated of a placed design for each component/gate. - * The extracted coordinates get annotated to the gates. - * - * @param[in] nl - The netlist to operate on. - * @param[in] def_file - Path to the def file. - * return OK on success, an error otherwise. - */ - static Result parse_def_file(Netlist* nl, const std::filesystem::path& def_file); + std::string get_description() const override; /** - * Iterates all flip-flops of the netlist or specified by the user. - * If a flip-flop has a `state` and a `neg_state` output, a new inverter gate is created and connected to the `state` output net as an additional destination. - * Finally, the `neg_state` output net is disconnected from the `neg_state` pin and re-connected to the new inverter gate's output. + * @brief Get the plugin dependencies. * - * @param[in] nl - The netlist to operate on. - * @param[in] ffs - The flip-flops to operate on. Defaults to an empty vector, in which case all flip-flops of the netlist are considered. - * @param[in] inverter_type - The inverter gate type to use. Defaults to a `nullptr`, in which case the first inverter type found in the gate library is used. - * @returns OK() and the number of rerouted `neg_state` outputs on success, an error otherwise. + * @returns A set of plugin names that this plugin depends on. */ - static Result unify_ff_outputs(Netlist* nl, const std::vector& ffs = {}, GateType* inverter_type = nullptr); + std::set get_dependencies() const override; }; } // namespace hal diff --git a/plugins/netlist_preprocessing/python/python_bindings.cpp b/plugins/netlist_preprocessing/python/python_bindings.cpp index 6a4b400c64a..ba04179fd0f 100644 --- a/plugins/netlist_preprocessing/python/python_bindings.cpp +++ b/plugins/netlist_preprocessing/python/python_bindings.cpp @@ -51,286 +51,286 @@ namespace hal :rtype: str )"); - py_netlist_preprocessing.def_static( - "remove_unused_lut_inputs", - [](Netlist* nl) -> std::optional { - auto res = NetlistPreprocessingPlugin::remove_unused_lut_inputs(nl); - if (res.is_ok()) - { - return res.get(); - } - else - { - log_error("python_context", "{}", res.get_error().get()); - return std::nullopt; - } - }, - py::arg("nl"), - R"( - Removes all LUT fan-in endpoints that do not correspond to a variable within the Boolean function that determines the output of a gate. - - :param hal_py.Netlist nl: The netlist to operate on. - :returns: The number of removed LUT endpoints on success, `None` otherwise. - :rtype: int or None - )"); - - py_netlist_preprocessing.def_static( - "remove_buffers", - [](Netlist* nl) -> std::optional { - auto res = NetlistPreprocessingPlugin::remove_buffers(nl); - if (res.is_ok()) - { - return res.get(); - } - else - { - log_error("python_context", "{}", res.get_error().get()); - return std::nullopt; - } - }, - py::arg("nl"), - R"( - Removes buffer gates from the netlist and connect their fan-in to their fan-out nets. - Considers all combinational gates and takes their inputs into account. - For example, a 2-input AND gate with one input being connected to constant '1' will also be removed. - - :param hal_py.Netlist nl: The netlist to operate on. - :returns: The number of removed buffers on success, `None` otherwise. - :rtype: int or None - )"); - - py_netlist_preprocessing.def_static( - "remove_redundant_logic", - [](Netlist* nl) -> std::optional { - auto res = NetlistPreprocessingPlugin::remove_redundant_logic(nl); - if (res.is_ok()) - { - return res.get(); - } - else - { - log_error("python_context", "{}", res.get_error().get()); - return std::nullopt; - } - }, - py::arg("nl"), - R"( - Removes redundant gates from the netlist, i.e., gates that are functionally equivalent and are connected to the same input nets. - - :param hal_py.Netlist nl: The netlist to operate on. - :returns: The number of removed gates on success, `None` otherwise. - :rtype: int or None - )"); - - py_netlist_preprocessing.def_static( - "remove_unconnected_gates", - [](Netlist* nl) -> std::optional { - auto res = NetlistPreprocessingPlugin::remove_unconnected_gates(nl); - if (res.is_ok()) - { - return res.get(); - } - else - { - log_error("python_context", "{}", res.get_error().get()); - return std::nullopt; - } - }, - py::arg("nl"), - R"( - Removes gates which outputs are all unconnected and not a global output net. - - :param hal_py.Netlist nl: The netlist to operate on. - :returns: The number of removed gates on success, `None` otherwise. - :rtype: int or None - )"); - - py_netlist_preprocessing.def_static( - "remove_unconnected_nets", - [](Netlist* nl) -> std::optional { - auto res = NetlistPreprocessingPlugin::remove_unconnected_nets(nl); - if (res.is_ok()) - { - return res.get(); - } - else - { - log_error("python_context", "{}", res.get_error().get()); - return std::nullopt; - } - }, - py::arg("nl"), - R"( - Remove nets which have no source and not destination. - - :param hal_py.Netlist nl: The netlist to operate on. - :returns: The number of removed nets on success, `None` otherwise. - :rtype: int or None - )"); - - py_netlist_preprocessing.def_static( - "simplify_lut_inits", - [](Netlist* nl) -> std::optional { - auto res = NetlistPreprocessingPlugin::simplify_lut_inits(nl); - if (res.is_ok()) - { - return res.get(); - } - else - { - log_error("python_context", "{}", res.get_error().get()); - return std::nullopt; - } - }, - py::arg("nl"), - R"( - Replaces pins connected to GND/VCC with constants and simplifies the boolean function of a LUT by recomputing the INIT string. - - :param hal_py.Netlist nl: The netlist to operate on. - :returns: The number of simplified INIT strings on success, `None` otherwise. - :rtype: int or None - )"); - - py_netlist_preprocessing.def_static( - "decompose_gate", - [](Netlist* nl, Gate* g, const bool delete_gate = true) -> bool { - auto res = NetlistPreprocessingPlugin::decompose_gate(nl, g, delete_gate); - if (res.is_ok()) - { - return true; - } - else - { - log_error("python_context", "{}", res.get_error().get()); - return false; - } - }, - py::arg("nl"), - py::arg("g"), - py::arg("delete_gate") = true, - R"( - Builds the Boolean function of each output pin of the gate and constructs a gate tree implementing it. - Afterwards the original output net is connected to the built gate tree and the gate is deleted if the 'delete_gate' flag is set. - - For the decomposition we currently only support the base operands AND, OR, INVERT. - The function searches in the gate library for a fitting two input gate and uses a standard HAL gate type if none is found. - - :param hal_py.Netlist nl: The netlist to operate on. - :param hal_py.Gate gate: The gate to decompose. - :param bool delete_gate: Determines whether the original gate gets deleted by the function, defaults to `True`. - :returns: `True` on success, `False` otherwise. - :rtype: bool - )"); - - py_netlist_preprocessing.def_static( - "decompose_gates_of_type", - [](Netlist* nl, const std::vector& gate_types) -> std::optional { - auto res = NetlistPreprocessingPlugin::decompose_gates_of_type(nl, gate_types); - if (res.is_ok()) - { - return res.get(); - } - else - { - log_error("python_context", "{}", res.get_error().get()); - return std::nullopt; - } - }, - py::arg("nl"), - py::arg("gate_types"), - R"( - Decomposes each gate of the specified type by building the Boolean function for each output pin of the gate and contructing a gate tree implementing it. - Afterwards the original gate is deleted and the output net is connected to the built gate tree. - - For the decomposition we currently only support the base operands AND, OR, INVERT. - The function searches in the gate library for a fitting two input gate and uses a standard HAL gate type if none is found. - - :param hal_py.Netlist nl: The netlist to operate on. - :param list[hal_py.GateType] gate_types: The gate types that should be decomposed. - :returns: The number of decomposed gates on success, `None` otherwise. - :rtype: int or None - )"); - - py_netlist_preprocessing.def_static( - "reconstruct_indexed_ff_identifiers", - [](Netlist* nl) -> std::optional { - auto res = NetlistPreprocessingPlugin::reconstruct_indexed_ff_identifiers(nl); - if (res.is_ok()) - { - return res.get(); - } - else - { - log_error("python_context", "{}", res.get_error().get()); - return std::nullopt; - } - }, - py::arg("nl"), - R"( - Tries to reconstruct a name and index for each flip flop that was part of a multibit wire in the verilog code. - This is NOT a general netlist reverse engineering algorithm and ONLY works on synthesized netlists with names annotated by the synthesizer. - This function mainly focuses netlists synthesized with yosys since yosys names the output wires of the flip flops but not the gate it self. - We try to reconstruct name and index for each flip flop based on the name of its output nets. - - :param hal_py.Netlist nl: The netlist to operate on. - :returns: The number of reconstructed names on success, `None` otherwise. - :rtype: int or None - )"); - - py_netlist_preprocessing.def_static( - "parse_def_file", - [](Netlist* nl, const std::filesystem::path& def_file) -> bool { - auto res = NetlistPreprocessingPlugin::parse_def_file(nl, def_file); - if (res.is_ok()) - { - return true; - } - else - { - log_error("python_context", "{}", res.get_error().get()); - return false; - } - }, - py::arg("nl"), - py::arg("def_file"), - R"( - Parses a design exchange format file and extracts the coordinated of a placed design for each component/gate. - The extracted coordinates get annotated to the gates. - - :param hal_py.Netlist nl: The netlist to operate on. - :param pathlib.Path def_file: The path to the def file - :returns: `True` on success, `False` otherwise. - :rtype: bool - )"); - - py_netlist_preprocessing.def_static( - "unify_ff_outputs", - [](Netlist* nl, const std::vector& ffs = {}, GateType* inverter_type = nullptr) -> std::optional { - auto res = NetlistPreprocessingPlugin::unify_ff_outputs(nl, ffs, inverter_type); - if (res.is_ok()) - { - return res.get(); - } - else - { - log_error("python_context", "{}", res.get_error().get()); - return std::nullopt; - } - }, - py::arg("nl"), - py::arg("ffs") = std::vector(), - py::arg("inverter_type") = nullptr, - R"( - Iterates all flip-flops of the netlist or specified by the user. - If a flip-flop has a ``state`` and a ``neg_state`` output, a new inverter gate is created and connected to the ``state`` output net as an additional destination. - Finally, the ``neg_state`` output net is disconnected from the ``neg_state`` pin and re-connected to the new inverter gate's output. - - :param hal_py.Netlist nl: The netlist to operate on. - :param list[hal_py.Gate] ffs: The flip-flops to operate on. Defaults to an empty vector, in which case all flip-flops of the netlist are considered. - :param hal_py.GateType inverter_type: The inverter gate type to use. Defaults to a ``None``, in which case the first inverter type found in the gate library is used. - :returns: The number of rerouted ``neg_state`` outputs on success, ``None`` otherwise. - :rtype: int or ``None`` - )"); + // py_netlist_preprocessing.def_static( + // "remove_unused_lut_inputs", + // [](Netlist* nl) -> std::optional { + // auto res = NetlistPreprocessingPlugin::remove_unused_lut_inputs(nl); + // if (res.is_ok()) + // { + // return res.get(); + // } + // else + // { + // log_error("python_context", "{}", res.get_error().get()); + // return std::nullopt; + // } + // }, + // py::arg("nl"), + // R"( + // Removes all LUT fan-in endpoints that do not correspond to a variable within the Boolean function that determines the output of a gate. + + // :param hal_py.Netlist nl: The netlist to operate on. + // :returns: The number of removed LUT endpoints on success, `None` otherwise. + // :rtype: int or None + // )"); + + // py_netlist_preprocessing.def_static( + // "remove_buffers", + // [](Netlist* nl) -> std::optional { + // auto res = NetlistPreprocessingPlugin::remove_buffers(nl); + // if (res.is_ok()) + // { + // return res.get(); + // } + // else + // { + // log_error("python_context", "{}", res.get_error().get()); + // return std::nullopt; + // } + // }, + // py::arg("nl"), + // R"( + // Removes buffer gates from the netlist and connect their fan-in to their fan-out nets. + // Considers all combinational gates and takes their inputs into account. + // For example, a 2-input AND gate with one input being connected to constant '1' will also be removed. + + // :param hal_py.Netlist nl: The netlist to operate on. + // :returns: The number of removed buffers on success, `None` otherwise. + // :rtype: int or None + // )"); + + // py_netlist_preprocessing.def_static( + // "remove_redundant_logic", + // [](Netlist* nl) -> std::optional { + // auto res = NetlistPreprocessingPlugin::remove_redundant_logic(nl); + // if (res.is_ok()) + // { + // return res.get(); + // } + // else + // { + // log_error("python_context", "{}", res.get_error().get()); + // return std::nullopt; + // } + // }, + // py::arg("nl"), + // R"( + // Removes redundant gates from the netlist, i.e., gates that are functionally equivalent and are connected to the same input nets. + + // :param hal_py.Netlist nl: The netlist to operate on. + // :returns: The number of removed gates on success, `None` otherwise. + // :rtype: int or None + // )"); + + // py_netlist_preprocessing.def_static( + // "remove_unconnected_gates", + // [](Netlist* nl) -> std::optional { + // auto res = NetlistPreprocessingPlugin::remove_unconnected_gates(nl); + // if (res.is_ok()) + // { + // return res.get(); + // } + // else + // { + // log_error("python_context", "{}", res.get_error().get()); + // return std::nullopt; + // } + // }, + // py::arg("nl"), + // R"( + // Removes gates which outputs are all unconnected and not a global output net. + + // :param hal_py.Netlist nl: The netlist to operate on. + // :returns: The number of removed gates on success, `None` otherwise. + // :rtype: int or None + // )"); + + // py_netlist_preprocessing.def_static( + // "remove_unconnected_nets", + // [](Netlist* nl) -> std::optional { + // auto res = NetlistPreprocessingPlugin::remove_unconnected_nets(nl); + // if (res.is_ok()) + // { + // return res.get(); + // } + // else + // { + // log_error("python_context", "{}", res.get_error().get()); + // return std::nullopt; + // } + // }, + // py::arg("nl"), + // R"( + // Remove nets which have no source and not destination. + + // :param hal_py.Netlist nl: The netlist to operate on. + // :returns: The number of removed nets on success, `None` otherwise. + // :rtype: int or None + // )"); + + // py_netlist_preprocessing.def_static( + // "simplify_lut_inits", + // [](Netlist* nl) -> std::optional { + // auto res = NetlistPreprocessingPlugin::simplify_lut_inits(nl); + // if (res.is_ok()) + // { + // return res.get(); + // } + // else + // { + // log_error("python_context", "{}", res.get_error().get()); + // return std::nullopt; + // } + // }, + // py::arg("nl"), + // R"( + // Replaces pins connected to GND/VCC with constants and simplifies the boolean function of a LUT by recomputing the INIT string. + + // :param hal_py.Netlist nl: The netlist to operate on. + // :returns: The number of simplified INIT strings on success, `None` otherwise. + // :rtype: int or None + // )"); + + // py_netlist_preprocessing.def_static( + // "decompose_gate", + // [](Netlist* nl, Gate* g, const bool delete_gate = true) -> bool { + // auto res = NetlistPreprocessingPlugin::decompose_gate(nl, g, delete_gate); + // if (res.is_ok()) + // { + // return true; + // } + // else + // { + // log_error("python_context", "{}", res.get_error().get()); + // return false; + // } + // }, + // py::arg("nl"), + // py::arg("g"), + // py::arg("delete_gate") = true, + // R"( + // Builds the Boolean function of each output pin of the gate and constructs a gate tree implementing it. + // Afterwards the original output net is connected to the built gate tree and the gate is deleted if the 'delete_gate' flag is set. + + // For the decomposition we currently only support the base operands AND, OR, INVERT. + // The function searches in the gate library for a fitting two input gate and uses a standard HAL gate type if none is found. + + // :param hal_py.Netlist nl: The netlist to operate on. + // :param hal_py.Gate gate: The gate to decompose. + // :param bool delete_gate: Determines whether the original gate gets deleted by the function, defaults to `True`. + // :returns: `True` on success, `False` otherwise. + // :rtype: bool + // )"); + + // py_netlist_preprocessing.def_static( + // "decompose_gates_of_type", + // [](Netlist* nl, const std::vector& gate_types) -> std::optional { + // auto res = NetlistPreprocessingPlugin::decompose_gates_of_type(nl, gate_types); + // if (res.is_ok()) + // { + // return res.get(); + // } + // else + // { + // log_error("python_context", "{}", res.get_error().get()); + // return std::nullopt; + // } + // }, + // py::arg("nl"), + // py::arg("gate_types"), + // R"( + // Decomposes each gate of the specified type by building the Boolean function for each output pin of the gate and contructing a gate tree implementing it. + // Afterwards the original gate is deleted and the output net is connected to the built gate tree. + + // For the decomposition we currently only support the base operands AND, OR, INVERT. + // The function searches in the gate library for a fitting two input gate and uses a standard HAL gate type if none is found. + + // :param hal_py.Netlist nl: The netlist to operate on. + // :param list[hal_py.GateType] gate_types: The gate types that should be decomposed. + // :returns: The number of decomposed gates on success, `None` otherwise. + // :rtype: int or None + // )"); + + // py_netlist_preprocessing.def_static( + // "reconstruct_indexed_ff_identifiers", + // [](Netlist* nl) -> std::optional { + // auto res = NetlistPreprocessingPlugin::reconstruct_indexed_ff_identifiers(nl); + // if (res.is_ok()) + // { + // return res.get(); + // } + // else + // { + // log_error("python_context", "{}", res.get_error().get()); + // return std::nullopt; + // } + // }, + // py::arg("nl"), + // R"( + // Tries to reconstruct a name and index for each flip flop that was part of a multibit wire in the verilog code. + // This is NOT a general netlist reverse engineering algorithm and ONLY works on synthesized netlists with names annotated by the synthesizer. + // This function mainly focuses netlists synthesized with yosys since yosys names the output wires of the flip flops but not the gate it self. + // We try to reconstruct name and index for each flip flop based on the name of its output nets. + + // :param hal_py.Netlist nl: The netlist to operate on. + // :returns: The number of reconstructed names on success, `None` otherwise. + // :rtype: int or None + // )"); + + // py_netlist_preprocessing.def_static( + // "parse_def_file", + // [](Netlist* nl, const std::filesystem::path& def_file) -> bool { + // auto res = NetlistPreprocessingPlugin::parse_def_file(nl, def_file); + // if (res.is_ok()) + // { + // return true; + // } + // else + // { + // log_error("python_context", "{}", res.get_error().get()); + // return false; + // } + // }, + // py::arg("nl"), + // py::arg("def_file"), + // R"( + // Parses a design exchange format file and extracts the coordinated of a placed design for each component/gate. + // The extracted coordinates get annotated to the gates. + + // :param hal_py.Netlist nl: The netlist to operate on. + // :param pathlib.Path def_file: The path to the def file + // :returns: `True` on success, `False` otherwise. + // :rtype: bool + // )"); + + // py_netlist_preprocessing.def_static( + // "unify_ff_outputs", + // [](Netlist* nl, const std::vector& ffs = {}, GateType* inverter_type = nullptr) -> std::optional { + // auto res = NetlistPreprocessingPlugin::unify_ff_outputs(nl, ffs, inverter_type); + // if (res.is_ok()) + // { + // return res.get(); + // } + // else + // { + // log_error("python_context", "{}", res.get_error().get()); + // return std::nullopt; + // } + // }, + // py::arg("nl"), + // py::arg("ffs") = std::vector(), + // py::arg("inverter_type") = nullptr, + // R"( + // Iterates all flip-flops of the netlist or specified by the user. + // If a flip-flop has a ``state`` and a ``neg_state`` output, a new inverter gate is created and connected to the ``state`` output net as an additional destination. + // Finally, the ``neg_state`` output net is disconnected from the ``neg_state`` pin and re-connected to the new inverter gate's output. + + // :param hal_py.Netlist nl: The netlist to operate on. + // :param list[hal_py.Gate] ffs: The flip-flops to operate on. Defaults to an empty vector, in which case all flip-flops of the netlist are considered. + // :param hal_py.GateType inverter_type: The inverter gate type to use. Defaults to a ``None``, in which case the first inverter type found in the gate library is used. + // :returns: The number of rerouted ``neg_state`` outputs on success, ``None`` otherwise. + // :rtype: int or ``None`` + // )"); #ifndef PYBIND11_MODULE return m.ptr(); diff --git a/plugins/netlist_preprocessing/src/netlist_preprocessing.cpp b/plugins/netlist_preprocessing/src/netlist_preprocessing.cpp new file mode 100644 index 00000000000..a0d4237df78 --- /dev/null +++ b/plugins/netlist_preprocessing/src/netlist_preprocessing.cpp @@ -0,0 +1,2573 @@ +#include "netlist_preprocessing/netlist_preprocessing.h" + +#include "hal_core/netlist/boolean_function/solver.h" +#include "hal_core/netlist/decorators/boolean_function_decorator.h" +#include "hal_core/netlist/decorators/boolean_function_net_decorator.h" +#include "hal_core/netlist/decorators/netlist_modification_decorator.h" +#include "hal_core/netlist/decorators/subgraph_netlist_decorator.h" +#include "hal_core/netlist/gate.h" +#include "hal_core/netlist/gate_library/gate_library_manager.h" +#include "hal_core/netlist/module.h" +#include "hal_core/netlist/net.h" +#include "hal_core/netlist/netlist.h" +#include "hal_core/utilities/token_stream.h" +#include "rapidjson/document.h" +#include "z3_utils.h" + +#include +#include +#include + +namespace hal +{ + namespace netlist_preprocessing + { + Result remove_unused_lut_inputs(Netlist* nl) + { + u32 num_eps = 0; + + // get net connected to GND + const std::vector& gnd_gates = nl->get_gnd_gates(); + if (gnd_gates.empty()) + { + return ERR("could not remove unused LUT endpoints from netlist with ID " + std::to_string(nl->get_id()) + ": no GND net available within netlist"); + } + Net* gnd_net = gnd_gates.front()->get_fan_out_nets().front(); + + // iterate all LUT gates + for (const auto& gate : nl->get_gates([](const Gate* g) { return g->get_type()->has_property(GateTypeProperty::c_lut); })) + { + std::vector fan_in = gate->get_fan_in_endpoints(); + std::unordered_map functions = gate->get_boolean_functions(); + + // skip if more than one function + if (functions.size() != 1) + { + continue; + } + + // only pins used as variables in Boolean function are considered active + auto active_pins = functions.begin()->second.get_variable_names(); + + // if there are more fan-in nets than there are active pins, remove those that are not used within the Boolean function and reconnect to GND + if (fan_in.size() > active_pins.size()) + { + for (const auto& ep : fan_in) + { + if (ep->get_net()->is_gnd_net() || ep->get_net()->is_vcc_net()) + { + continue; + } + + if (std::find(active_pins.begin(), active_pins.end(), ep->get_pin()->get_name()) == active_pins.end()) + { + GatePin* pin = ep->get_pin(); + if (!ep->get_net()->remove_destination(gate, pin)) + { + log_warning( + "netlist_preprocessing", "failed to remove unused input from LUT gate '{}' with ID {} from netlist with ID {}.", gate->get_name(), gate->get_id(), nl->get_id()); + continue; + } + if (!gnd_net->add_destination(gate, pin)) + { + log_warning("netlist_preprocessing", + "failed to reconnect unused input of LUT gate '{}' with ID {} to GND in netlist with ID {}.", + gate->get_name(), + gate->get_id(), + nl->get_id()); + continue; + } + num_eps++; + } + } + } + } + + log_info("netlist_preprocessing", "removed {} unused LUT endpoints from netlist with ID {}.", num_eps, nl->get_id()); + return OK(num_eps); + } + + // TODO make this check every pin of a gate and check whether the generated boolean function (with replaced gnd and vcc nets) is just a variable. + // Afterwards just connect input net to buffer destination. Do this for all pins and delete gate if it has no more successors and not global outputs + Result remove_buffers(Netlist* nl) + { + u32 num_gates = 0; + + std::queue gates_to_be_deleted; + + for (const auto& gate : nl->get_gates()) + { + std::vector fan_out = gate->get_fan_out_endpoints(); + + GateType* gt = gate->get_type(); + + // continue if of invalid base type + if (!gt->has_property(GateTypeProperty::combinational) || gt->has_property(GateTypeProperty::power) || gt->has_property(GateTypeProperty::ground)) + { + continue; + } + + // continue if more than one fan-out net + if (fan_out.size() != 1) + { + continue; + } + + // continue if more than one Boolean function + std::unordered_map functions = gate->get_boolean_functions(); + if (functions.size() != 1) + { + continue; + } + + // continue if Boolean function name does not match output pin + Endpoint* out_endpoint = *(fan_out.begin()); + if (out_endpoint->get_pin()->get_name() != (functions.begin())->first) + { + continue; + } + + std::vector fan_in = gate->get_fan_in_endpoints(); + BooleanFunction func = functions.begin()->second; + + // simplify Boolean function for constant 0 or 1 inputs (takes care of, e.g., an AND2 connected to an input and logic 1) + const auto substitute_res = BooleanFunctionDecorator(func).substitute_power_ground_pins(gate); + if (substitute_res.is_error()) + { + return ERR_APPEND(substitute_res.get_error(), + "Cannot replace buffers: failed to substitute pins with constants at gate " + gate->get_name() + " with ID " + std::to_string(gate->get_id())); + } + + func = substitute_res.get().simplify_local(); + + bool failed = false; + std::vector in_pins = gt->get_input_pin_names(); + if (func.is_variable() && std::find(in_pins.begin(), in_pins.end(), func.get_variable_name().get()) != in_pins.end()) + { + Net* out_net = out_endpoint->get_net(); + + // check all input endpoints and ... + for (Endpoint* in_endpoint : fan_in) + { + Net* in_net = in_endpoint->get_net(); + if (in_endpoint->get_pin()->get_name() == func.get_variable_name().get()) + { + // const auto merge_res = netlist_utils::merge_nets(nl, in_net, out_net, true); + const auto merge_res = NetlistModificationDecorator(*nl).connect_nets(out_net, in_net); + if (merge_res.is_error()) + { + log_warning("netlist_preprocessing", "{}", merge_res.get_error().get()); + failed = true; + } + } + else + { + // completely remove the input endpoint otherwise + if (!in_net->remove_destination(in_endpoint)) + { + log_warning("netlist_preprocessing", + "failed to remove destination from input net '{}' with ID {} of buffer gate '{}' with ID {} from netlist with ID {}.", + in_net->get_name(), + in_net->get_id(), + gate->get_name(), + gate->get_id(), + nl->get_id()); + failed = true; + } + } + + if (failed) + { + break; + } + } + + if (!failed) + { + gates_to_be_deleted.push(gate); + } + } + // TODO this functionality is not a buffer and is covered by propagate_constants + /* + else if (func.is_constant() && (func.has_constant_value(0) || func.has_constant_value(1))) + { + auto* out_net = out_endpoint->get_net(); + + const auto& gnd_gates = nl->get_gnd_gates(); + const auto& vcc_gates = nl->get_vcc_gates(); + if (gnd_gates.empty() || vcc_gates.empty()) + { + continue; + } + auto* gnd_net = gnd_gates.front()->get_fan_out_nets().front(); + auto* vcc_net = vcc_gates.front()->get_fan_out_nets().front(); + + for (auto* in_endpoint : fan_in) + { + auto* in_net = in_endpoint->get_net(); + + // remove the input endpoint otherwise + if (!in_net->remove_destination(gate, in_endpoint->get_pin())) + { + log_warning("netlist_preprocessing", + "failed to remove destination from input net '{}' with ID {} of buffer gate '{}' with ID {} from netlist with ID {}.", + in_net->get_name(), + in_net->get_id(), + gate->get_name(), + gate->get_id(), + nl->get_id()); + failed = true; + break; + } + } + if (!failed && func.has_constant_value(0)) + { + for (auto* dst : out_net->get_destinations()) + { + auto* dst_gate = dst->get_gate(); + auto* dst_pin = dst->get_pin(); + if (!out_net->remove_destination(dst)) + { + log_warning("netlist_preprocessing", + "failed to remove destination from output net '{}' with ID {} of buffer gate '{}' with ID {} from netlist with ID {}.", + out_net->get_name(), + out_net->get_id(), + gate->get_name(), + gate->get_id(), + nl->get_id()); + failed = true; + break; + } + if (!gnd_net->add_destination(dst_gate, dst_pin)) + { + log_warning("netlist_preprocessing", + "failed to add buffer gate '{}' with ID {} as destination to GND net '{}' with ID {} in netlist with ID {}.", + gnd_net->get_name(), + gnd_net->get_id(), + gate->get_name(), + gate->get_id(), + nl->get_id()); + failed = true; + break; + } + } + } + else if (!failed && func.has_constant_value(1)) + { + for (Endpoint* dst : out_net->get_destinations()) + { + Gate* dst_gate = dst->get_gate(); + GatePin* dst_pin = dst->get_pin(); + if (!out_net->remove_destination(dst)) + { + log_warning("netlist_preprocessing", + "failed to remove destination from output net '{}' with ID {} of buffer gate '{}' with ID {} from netlist with ID {}.", + out_net->get_name(), + out_net->get_id(), + gate->get_name(), + gate->get_id(), + nl->get_id()); + failed = true; + break; + } + if (!vcc_net->add_destination(dst_gate, dst_pin)) + { + log_warning("netlist_preprocessing", + "failed to add buffer gate '{}' with ID {} as destination to VCC net '{}' with ID {} in netlist with ID {}.", + vcc_net->get_name(), + vcc_net->get_id(), + gate->get_name(), + gate->get_id(), + nl->get_id()); + failed = true; + break; + } + } + } + + // delete output net and buffer gate + if (!failed && !nl->delete_net(out_net)) + { + log_warning("netlist_preprocessing", + "failed to remove output net '{}' with ID {} of buffer gate '{}' with ID {} from netlist with ID {}.", + out_net->get_name(), + out_net->get_id(), + gate->get_name(), + gate->get_id(), + nl->get_id()); + continue; + } + if (!failed) + { + gates_to_be_deleted.push(gate); + } + } + */ + } + + log_debug("netlist_preprocessing", "removing {} buffer gates...", gates_to_be_deleted.size()); + + while (!gates_to_be_deleted.empty()) + { + Gate* gate = gates_to_be_deleted.front(); + gates_to_be_deleted.pop(); + if (!nl->delete_gate(gate)) + { + log_warning("netlist_preprocessing", "failed to remove buffer gate '{}' with ID {} from netlist with ID {}.", gate->get_name(), gate->get_id(), nl->get_id()); + continue; + } + num_gates++; + } + + log_info("netlist_preprocessing", "removed {} buffer gates from netlist with ID {}.", num_gates, nl->get_id()); + return OK(num_gates); + } + + namespace + { + std::unordered_map> restore_ff_replacements(const Netlist* nl) + { + std::unordered_map> replacements; + + for (auto& g : nl->get_gates()) + { + if (g->has_data("preprocessing_information", "replaced_gates")) + { + const auto& [_, s] = g->get_data("preprocessing_information", "replaced_gates"); + std::vector replaced_gate_names = nlohmann::json::parse(s); + replacements.insert({g, replaced_gate_names}); + } + } + + return replacements; + } + + void update_ff_replacements(std::unordered_map>& replacements) + { + for (auto& [g, r] : replacements) + { + const nlohmann::json j = r; + const std::string s = j.dump(); + + g->set_data("preprocessing_information", "replaced_gates", "string", s); + } + + return; + } + + void annotate_ff_survivor(std::unordered_map>& replacements, Gate* survivor, Gate* to_be_replaced) + { + auto& it_s = replacements[survivor]; + + if (const auto& it = replacements.find(to_be_replaced); it != replacements.end()) + { + for (const auto& s : it->second) + { + it_s.push_back(s); + } + replacements.erase(it); + } + + it_s.push_back(to_be_replaced->get_name()); + + return; + } + } // namespace + + Result remove_redundant_gates(Netlist* nl, const std::function& filter) + { + auto config = hal::SMT::QueryConfig(); + +#ifdef BITWUZLA_LIBRARY + auto s_type = hal::SMT::SolverType::Bitwuzla; + auto s_call = hal::SMT::SolverCall::Library; + config = config.with_solver(s_type).with_call(s_call); +#endif + struct GateFingerprint + { + const GateType* type; + std::map ordered_fan_in = {}; + std::set unordered_fan_in = {}; + u8 truth_table_hw = 0; + + bool operator<(const GateFingerprint& other) const + { + return (other.type < type) || (other.type == type && other.ordered_fan_in < ordered_fan_in) + || (other.type == type && other.ordered_fan_in == ordered_fan_in && other.unordered_fan_in < unordered_fan_in) + || (other.type == type && other.ordered_fan_in == ordered_fan_in && other.unordered_fan_in == unordered_fan_in && other.truth_table_hw < truth_table_hw); + } + }; + + static std::vector hw_map = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; + + u32 num_gates = 0; + bool progress; + + std::vector target_gates; + if (filter) + { + target_gates = nl->get_gates([filter](const Gate* g) { + const auto& type = g->get_type(); + return (type->has_property(GateTypeProperty::combinational) || type->has_property(GateTypeProperty::ff)) && filter(g); + }); + } + else + { + target_gates = nl->get_gates([](const Gate* g) { + const auto& type = g->get_type(); + return type->has_property(GateTypeProperty::combinational) || type->has_property(GateTypeProperty::ff); + }); + } + + auto ff_replacements = restore_ff_replacements(nl); + + do + { + std::map> fingerprinted_gates; + + progress = false; + + for (auto* gate : target_gates) + { + GateFingerprint fingerprint; + fingerprint.type = gate->get_type(); + if (fingerprint.type->has_property(GateTypeProperty::combinational)) + { + const auto& fan_in_nets = gate->get_fan_in_nets(); + fingerprint.unordered_fan_in.insert(fan_in_nets.cbegin(), fan_in_nets.cend()); + if (fingerprint.type->has_property(GateTypeProperty::c_lut)) + { + if (const auto res = gate->get_init_data(); res.is_ok()) + { + const auto& init_str = res.get().front(); + for (const auto c : init_str) + { + u8 tmp = std::toupper(c) - 0x30; + if (tmp > 9) + { + tmp -= 0x7; + } + fingerprint.truth_table_hw += hw_map.at(tmp); + } + } + } + } + else if (fingerprint.type->has_property(GateTypeProperty::ff)) + { + for (const auto& ep : gate->get_fan_in_endpoints()) + { + fingerprint.ordered_fan_in[ep->get_pin()] = ep->get_net(); + } + } + + fingerprinted_gates[fingerprint].push_back(gate); + } + + std::vector> duplicate_gates; + for (const auto& [fingerprint, gates] : fingerprinted_gates) + { + if (gates.size() == 1) + { + continue; + } + + if (fingerprint.type->has_property(GateTypeProperty::combinational)) + { + std::set visited; + for (size_t i = 0; i < gates.size(); i++) + { + Gate* master_gate = gates.at(i); + + if (visited.find(master_gate) != visited.cend()) + { + continue; + } + + std::vector current_duplicates = {master_gate}; + + for (size_t j = i + 1; j < gates.size(); j++) + { + Gate* current_gate = gates.at(j); + bool equal = true; + for (const auto* pin : fingerprint.type->get_output_pins()) + { + const auto solver_res = + master_gate->get_resolved_boolean_function(pin) + .map([pin, current_gate](BooleanFunction&& bf_master) { + return current_gate->get_resolved_boolean_function(pin).map([bf_master = std::move(bf_master)](BooleanFunction&& bf_current) mutable { + return BooleanFunction::Eq(std::move(bf_master), std::move(bf_current), 1); + }); + }) + .map([](auto&& bf_eq) -> Result { return BooleanFunction::Not(std::move(bf_eq), 1); }) + .map([&config](auto&& bf_not) -> Result { return SMT::Solver({SMT::Constraint(std::move(bf_not))}).query(config); }); + + if (solver_res.is_error() || !solver_res.get().is_unsat()) + { + equal = false; + } + } + + if (equal) + { + current_duplicates.push_back(current_gate); + visited.insert(current_gate); + } + } + + if (current_duplicates.size() > 1) + { + duplicate_gates.push_back(current_duplicates); + } + } + } + else if (fingerprint.type->has_property(GateTypeProperty::ff)) + { + duplicate_gates.push_back(std::move(gates)); + } + } + + std::set affected_gates; + for (auto& current_duplicates : duplicate_gates) + { + std::sort(current_duplicates.begin(), current_duplicates.end(), [](const auto& g1, const auto& g2) { return g1->get_name().length() < g2->get_name().length(); }); + + auto* survivor_gate = current_duplicates.front(); + std::map out_pins_to_nets; + for (auto* ep : survivor_gate->get_fan_out_endpoints()) + { + Net* out_net = ep->get_net(); + out_pins_to_nets[ep->get_pin()] = out_net; + for (const auto* dst : out_net->get_destinations()) + { + auto* dst_gate = dst->get_gate(); + auto* dst_type = dst_gate->get_type(); + if (dst_type->has_property(GateTypeProperty::combinational) || dst_type->has_property(GateTypeProperty::ff)) + { + affected_gates.insert(dst_gate); + } + } + } + + for (u32 k = 1; k < current_duplicates.size(); k++) + { + auto* current_gate = current_duplicates.at(k); + for (auto* ep : current_gate->get_fan_out_endpoints()) + { + auto* ep_net = ep->get_net(); + auto* ep_pin = ep->get_pin(); + + if (auto it = out_pins_to_nets.find(ep_pin); it != out_pins_to_nets.cend()) + { + // survivor already has net connected to this output -> add destination to survivor's net + for (auto* dst : ep_net->get_destinations()) + { + auto* dst_gate = dst->get_gate(); + auto* dst_pin = dst->get_pin(); + dst->get_net()->remove_destination(dst); + it->second->add_destination(dst_gate, dst_pin); + + auto* dst_type = dst_gate->get_type(); + if (dst_type->has_property(GateTypeProperty::combinational) || dst_type->has_property(GateTypeProperty::ff)) + { + affected_gates.insert(dst_gate); + } + } + if (!nl->delete_net(ep_net)) + { + log_warning("netlist_preprocessing", "could not delete net '{}' with ID {} from netlist with ID {}.", ep_net->get_name(), ep_net->get_id(), nl->get_id()); + } + } + else + { + // survivor does not feature net on this output pin -> connect this net to survivor + ep_net->add_source(survivor_gate, ep_pin); + out_pins_to_nets[ep_pin] = ep_net; + for (auto* dst : ep_net->get_destinations()) + { + auto* dst_gate = dst->get_gate(); + auto* dst_type = dst_gate->get_type(); + if (dst_type->has_property(GateTypeProperty::combinational) || dst_type->has_property(GateTypeProperty::ff)) + { + affected_gates.insert(dst_gate); + } + } + } + } + + annotate_ff_survivor(ff_replacements, survivor_gate, current_gate); + + affected_gates.erase(current_gate); + if (!nl->delete_gate(current_gate)) + { + log_warning("netlist_preprocessing", "could not delete gate '{}' with ID {} from netlist with ID {}.", current_gate->get_name(), current_gate->get_id(), nl->get_id()); + } + else + { + progress = true; + num_gates++; + } + } + } + target_gates = std::vector(affected_gates.cbegin(), affected_gates.cend()); + } while (progress); + + update_ff_replacements(ff_replacements); + + log_info("netlist_preprocessing", "removed {} redundant gates from netlist with ID {}.", num_gates, nl->get_id()); + return OK(num_gates); + } + + Result remove_redundant_loops(Netlist* nl) + { + struct LoopFingerprint + { + std::map types; + std::set external_variable_names; + std::set ff_control_nets; + + bool operator<(const LoopFingerprint& other) const + { + return (other.types < types) || (other.types == types && other.external_variable_names < external_variable_names) + || (other.types == types && other.external_variable_names == external_variable_names && other.ff_control_nets < ff_control_nets); + } + }; + + auto config = hal::SMT::QueryConfig(); + +#ifdef BITWUZLA_LIBRARY + auto s_type = hal::SMT::SolverType::Bitwuzla; + auto s_call = hal::SMT::SolverCall::Library; + config = config.with_solver(s_type).with_call(s_call); +#endif + + u32 num_gates = 0; + + auto ff_replacements = restore_ff_replacements(nl); + + static const std::set ff_control_pin_types = {PinType::clock, PinType::enable, PinType::reset, PinType::set}; + + // detect combinational loops that begin and end at the same FF + // for some FFs, multiple combinational lops may exist; such loops wil be merged into a single one + std::unordered_map> loops_by_start_gate; + for (auto* start_ff : nl->get_gates([](const Gate* g) { return g->get_type()->has_property(GateTypeProperty::ff); })) + { + std::vector stack = {start_ff}; + std::vector previous_gates; + std::unordered_set visited_gates; + std::unordered_set cache; + + while (!stack.empty()) + { + auto* current_gate = stack.back(); + + if (!previous_gates.empty() && current_gate == previous_gates.back()) + { + stack.pop_back(); + previous_gates.pop_back(); + continue; + } + + visited_gates.insert(current_gate); + + bool added = false; + for (const auto* suc_ep : current_gate->get_successors()) + { + if (ff_control_pin_types.find(suc_ep->get_pin()->get_type()) != ff_control_pin_types.end()) + { + continue; + } + + auto* suc_gate = suc_ep->get_gate(); + if (suc_gate == start_ff || cache.find(suc_gate) != cache.end()) + { + loops_by_start_gate[start_ff].insert(current_gate); + cache.insert(current_gate); + for (auto it = ++(previous_gates.begin()); it != previous_gates.end(); it++) + { + cache.insert(*it); + loops_by_start_gate[start_ff].insert(*it); + } + } + else if (suc_gate->get_type()->has_property(GateTypeProperty::combinational)) + { + if (visited_gates.find(suc_gate) == visited_gates.end()) + { + stack.push_back(suc_gate); + added = true; + } + } + } + + if (added) + { + previous_gates.push_back(current_gate); + } + else + { + stack.pop_back(); + } + } + } + + std::map, BooleanFunction>>> fingerprinted_loops; + for (const auto& [start_ff, comb_gates] : loops_by_start_gate) + { + LoopFingerprint fingerprint; + + // do not consider loop of more than 30 gates + if (comb_gates.size() > 30) + { + continue; + } + + // collect FF control and data nets + std::vector data_in; + for (const auto* ep : start_ff->get_fan_in_endpoints()) + { + auto pin_type = ep->get_pin()->get_type(); + if (ff_control_pin_types.find(pin_type) != ff_control_pin_types.end()) + { + fingerprint.ff_control_nets.insert(ep->get_net()); + } + else if (pin_type == PinType::data) + { + data_in.push_back(ep); + } + } + + if (data_in.size() != 1) + { + continue; + } + + // collect gate types + fingerprint.types[start_ff->get_type()] = 1; + for (const auto* g : comb_gates) + { + const auto* gt = g->get_type(); + if (const auto type_it = fingerprint.types.find(gt); type_it == fingerprint.types.end()) + { + fingerprint.types[gt] = 0; + } + fingerprint.types[gt]++; + } + + std::vector comb_gates_vec(comb_gates.cbegin(), comb_gates.cend()); + if (auto function_res = SubgraphNetlistDecorator(*nl).get_subgraph_function(comb_gates_vec, data_in.front()->get_net()); function_res.is_ok()) + { + // get Boolean function variable names + BooleanFunction function = function_res.get(); + fingerprint.external_variable_names = function.get_variable_names(); + + // replace FF output net identifier from function variables (otherwise varies depending on FF, preventing later SMT check) + for (const auto* ep : start_ff->get_fan_out_endpoints()) + { + if (const auto it = fingerprint.external_variable_names.find(BooleanFunctionNetDecorator(*(ep->get_net())).get_boolean_variable_name()); + it != fingerprint.external_variable_names.end()) + { + function = function.substitute(*it, ep->get_pin()->get_name()); + fingerprint.external_variable_names.erase(it); + } + } + + std::vector loop_gates = {start_ff}; + loop_gates.insert(loop_gates.end(), comb_gates.begin(), comb_gates.end()); + fingerprinted_loops[fingerprint].push_back(std::make_pair(loop_gates, std::move(function))); + } + } + + std::vector>> duplicate_loops; + for (const auto& [_, loops] : fingerprinted_loops) + { + if (loops.size() == 1) + { + continue; + } + + std::set visited; + for (u32 i = 0; i < loops.size(); i++) + { + if (visited.find(i) != visited.cend()) + { + continue; + } + + const auto& master_loop = loops.at(i); + + std::vector> current_duplicates = {std::get<0>(master_loop)}; + + for (size_t j = i + 1; j < loops.size(); j++) + { + const auto& current_loop = loops.at(j); + const auto solver_res = + BooleanFunction::Eq(std::get<1>(master_loop).clone(), std::get<1>(current_loop).clone(), 1) + .map([](auto&& bf_eq) -> Result { return BooleanFunction::Not(std::move(bf_eq), 1); }) + .map([&config](auto&& bf_not) -> Result { return SMT::Solver({SMT::Constraint(std::move(bf_not))}).query(config); }); + + if (solver_res.is_ok() && solver_res.get().is_unsat()) + { + current_duplicates.push_back(std::get<0>(current_loop)); + visited.insert(j); + } + } + + if (current_duplicates.size() > 1) + { + duplicate_loops.push_back(std::move(current_duplicates)); + } + } + } + + for (const auto& current_duplicates : duplicate_loops) + { + // TODO the "replace" logic where the output of the survivor ff is connected to new sources and the old gates are deleted is a duplicate of the above functionality + const auto& survivor_loop = current_duplicates.front(); + auto* survivor_ff = survivor_loop.front(); + + std::map out_pins_to_nets; + for (auto* ep : survivor_ff->get_fan_out_endpoints()) + { + Net* out_net = ep->get_net(); + out_pins_to_nets[ep->get_pin()] = out_net; + } + + for (u32 i = 1; i < current_duplicates.size(); i++) + { + auto* current_ff = current_duplicates.at(i).front(); + for (auto* ep : current_ff->get_fan_out_endpoints()) + { + auto* ep_net = ep->get_net(); + auto* ep_pin = ep->get_pin(); + + if (auto it = out_pins_to_nets.find(ep_pin); it != out_pins_to_nets.cend()) + { + // survivor already has net connected to this output -> add destination to survivor's net + for (auto* dst : ep_net->get_destinations()) + { + auto* dst_gate = dst->get_gate(); + auto* dst_pin = dst->get_pin(); + dst->get_net()->remove_destination(dst); + it->second->add_destination(dst_gate, dst_pin); + } + if (!nl->delete_net(ep_net)) + { + log_warning("netlist_preprocessing", "could not delete net '{}' with ID {} from netlist with ID {}.", ep_net->get_name(), ep_net->get_id(), nl->get_id()); + } + } + else + { + // survivor does not feature net on this output pin -> connect this net to survivor + ep_net->add_source(survivor_ff, ep_pin); + out_pins_to_nets[ep_pin] = ep_net; + } + } + + annotate_ff_survivor(ff_replacements, survivor_ff, current_ff); + + if (!nl->delete_gate(current_ff)) + { + log_warning("netlist_preprocessing", "could not delete gate '{}' with ID {} from netlist with ID {}.", current_ff->get_name(), current_ff->get_id(), nl->get_id()); + } + else + { + num_gates++; + } + } + } + + update_ff_replacements(ff_replacements); + + log_info("netlist_preprocessing", "removed {} redundant loops from netlist with ID {}.", num_gates, nl->get_id()); + return OK(num_gates); + } + + Result remove_redundant_logic_trees(Netlist* nl) + { + struct TreeFingerprint + { + std::set external_inputs; + // std::set external_inputs; + + bool operator<(const TreeFingerprint& other) const + { + return (other.external_inputs < external_inputs); + } + }; + + const std::vector all_comb_gates_vec = nl->get_gates([](const auto& g) { return g->get_type()->has_property(GateTypeProperty::combinational); }); + // const std::unordered_set all_comb_gates_set = {all_comb_gates_vec.begin(), all_comb_gates_vec.end()}; + + std::map> fingerprint_to_nets; + for (const auto& g : all_comb_gates_vec) + { + for (const auto& out_ep : g->get_fan_out_endpoints()) + { + // const auto non_comb_destinations = out_ep->get_net()->get_destinations([](const auto& in_ep){ return !in_ep->get_gate()->get_type()->has_property(GateTypeProperty::combinational);}); + // if (!non_comb_destinations.empty()) + { + const auto& out_net = out_ep->get_net(); + auto inputs_res = SubgraphNetlistDecorator(*nl).get_subgraph_function_inputs(all_comb_gates_vec, out_net); + if (inputs_res.is_error()) + { + return ERR_APPEND(inputs_res.get_error(), + "Unable to remove redundant logic trees: failed to gather inputs for net " + out_net->get_name() + " with ID " + std::to_string(out_net->get_id())); + } + TreeFingerprint tf; + tf.external_inputs = inputs_res.get(); + // tf.external_inputs = SubgraphNetlistDecorator(*nl).get_subgraph_function(all_comb_gates_vec, out_net).get().simplify().get_variable_names(); + + fingerprint_to_nets[tf].insert(out_net); + } + } + } + + std::vector> equality_classes; + + for (const auto& [_fingerprint, nets] : fingerprint_to_nets) + { + // TODO remove + // std::cout << "Fingerprint(" << _fingerprint.external_inputs.size() << "): " << std::endl; + // for (const auto& n : _fingerprint.external_inputs) + // { + // std::cout << "\t" << n << std::endl; + // } + // std::cout << "Checking nets: " << std::endl; + // for (const auto& n : nets) + // { + // std::cout << "\t" << n->get_name() << std::endl; + // } + + std::vector current_candidate_nets = {nets.begin(), nets.end()}; + std::vector next_candidate_nets; + + while (!current_candidate_nets.empty()) + { + const auto n = current_candidate_nets.back(); + current_candidate_nets.pop_back(); + + std::vector new_equality_class = {n}; + + for (const auto& m : current_candidate_nets) + { + auto comp_res = z3_utils::compare_nets(nl, nl, n, m); + if (comp_res.is_error()) + { + return ERR_APPEND(comp_res.get_error(), + "Unable to remove redundant logic trees: failed to compare net " + n->get_name() + " with ID " + std::to_string(n->get_id()) + " with net " + + m->get_name() + " with ID " + std::to_string(m->get_id())); + } + const auto are_equal = comp_res.get(); + + if (are_equal) + { + new_equality_class.push_back(m); + } + else + { + next_candidate_nets.push_back(m); + } + } + + equality_classes.push_back(new_equality_class); + current_candidate_nets = next_candidate_nets; + next_candidate_nets.clear(); + } + } + + u32 counter = 0; + for (const auto& eq_class : equality_classes) + { + // TODO remove + // std::cout << "Equal nets: " << std::endl; + // for (const auto& n : eq_class) + // { + // std::cout << n->get_name() << std::endl; + // } + + auto survivor_net = eq_class.front(); + + for (u32 i = 1; i < eq_class.size(); i++) + { + auto victim_net = eq_class.at(i); + for (const auto& dst : victim_net->get_destinations()) + { + auto dst_gate = dst->get_gate(); + auto dst_pin = dst->get_pin(); + + if (!victim_net->remove_destination(dst)) + { + return ERR("Unable to remove redundant logic trees: failed to remove destination of net " + victim_net->get_name() + " with ID " + std::to_string(victim_net->get_id()) + + " at gate " + dst_gate->get_name() + " with ID " + std::to_string(dst_gate->get_id()) + " and pin " + dst_pin->get_name()); + } + if (!survivor_net->add_destination(dst_gate, dst_pin)) + { + return ERR("Unable to remove redundant logic trees: failed to add destination to net " + survivor_net->get_name() + " with ID " + std::to_string(survivor_net->get_id()) + + " at gate " + dst_gate->get_name() + " with ID " + std::to_string(dst_gate->get_id()) + " and pin " + dst_pin->get_name()); + } + + counter += 1; + } + } + } + + auto clean_up_res = remove_unconnected_looped(nl); + if (clean_up_res.is_error()) + { + return ERR_APPEND(clean_up_res.get_error(), "Unable to remove redundant logic trees: failed to clean up dangling trees"); + } + + return OK(clean_up_res.get() + counter); + } + + Result remove_unconnected_gates(Netlist* nl) + { + u32 num_gates = 0; + std::vector to_delete; + do + { + to_delete.clear(); + + for (const auto& g : nl->get_gates()) + { + bool is_unconnected = true; + for (const auto& on : g->get_fan_out_nets()) + { + if (!on->get_destinations().empty() || on->is_global_output_net()) + { + is_unconnected = false; + } + } + + if (is_unconnected) + { + to_delete.push_back(g); + } + } + + for (const auto& g : to_delete) + { + if (!nl->delete_gate(g)) + { + log_warning("netlist_preprocessing", "could not delete gate '{}' with ID {} from netlist with ID {}.", g->get_name(), g->get_id(), nl->get_id()); + } + else + { + num_gates++; + } + } + } while (!to_delete.empty()); + + log_info("netlist_preprocessing", "removed {} unconnected gates from netlist with ID {}.", num_gates, nl->get_id()); + return OK(num_gates); + } + + Result remove_unconnected_nets(Netlist* nl) + { + u32 num_nets = 0; + + std::vector to_delete; + + for (const auto& n : nl->get_nets()) + { + if (!n->is_global_input_net() && n->get_sources().empty() && !n->is_global_output_net() && n->get_destinations().empty()) + { + to_delete.push_back(n); + } + } + + for (const auto& n : to_delete) + { + if (!nl->delete_net(n)) + { + log_warning("netlist_preprocessing", "could not delete net '{}' with ID {} from netlist with ID {}.", n->get_name(), n->get_id(), nl->get_id()); + } + else + { + num_nets++; + } + } + + log_info("netlist_preprocessing", "removed {} unconnected nets from netlist with ID {}.", num_nets, nl->get_id()); + return OK(num_nets); + } + + Result remove_unconnected_looped(Netlist* nl) + { + u32 total_removed = 0; + + while (true) + { + auto gate_res = remove_unconnected_gates(nl); + if (gate_res.is_error()) + { + return ERR_APPEND(gate_res.get_error(), "unable to execute clean up loop: failed to remove unconnected gates"); + } + + auto net_res = remove_unconnected_nets(nl); + if (net_res.is_error()) + { + return ERR_APPEND(net_res.get_error(), "unable to execute clean up loop: failed to remove unconnected nets"); + } + + const u32 removed = gate_res.get() + net_res.get(); + total_removed += removed; + if (!removed) + { + break; + } + } + + return OK(total_removed); + } + + namespace + { + Result remove_encasing_inverters(Netlist* nl) + { + // check whether all inputs and output are inverted -> remove all inverters + + // TODO: this only considers HAL muxes, but i do not see a reason why. There is no resynthesis happening here + std::vector muxes = nl->get_gates([](const Gate* g) { return (g->get_type()->get_name().find("HAL_MUX") != std::string::npos); }); + + u32 delete_count = 0; + std::vector delete_gate_q; + + for (const auto& g : muxes) + { + if (g->get_successors().size() > 1) + { + continue; + } + + auto data_pins = g->get_type()->get_pins([](const GatePin* pin) { return (pin->get_type() == PinType::data) && (pin->get_direction() == PinDirection::input); }); + auto out_pins = g->get_type()->get_pins([](const GatePin* pin) { return (pin->get_direction() == PinDirection::output); }); + + if (data_pins.size() < 2) + { + continue; + } + + if (out_pins.size() != 1) + { + continue; + } + + bool preceded_by_inv = true; + for (const auto& pin : data_pins) + { + const auto pred = g->get_predecessor(pin); + if (pred == nullptr || pred->get_gate() == nullptr || !pred->get_gate()->get_type()->has_property(GateTypeProperty::c_inverter)) + { + preceded_by_inv = false; + break; + } + } + + if (!preceded_by_inv) + { + continue; + } + + bool succeded_by_inv = true; + for (const auto& pin : out_pins) + { + const auto suc = g->get_successor(pin); + if (suc == nullptr || suc->get_gate() == nullptr || !suc->get_gate()->get_type()->has_property(GateTypeProperty::c_inverter)) + { + succeded_by_inv = false; + break; + } + } + + if (!succeded_by_inv) + { + continue; + } + + // delete all connections from and to inverters (and inverter gates if they do not share any other connection) + for (const auto& pin : data_pins) + { + const auto pred = g->get_predecessor(pin); + + // disconnect inverter output from mux + pred->get_net()->remove_destination(g, pin); + + // connect inverter input net to mux + auto in_net = pred->get_gate()->get_fan_in_nets().front(); + in_net->add_destination(g, pin); + + // delete inverter gate if it does not have any successors + if (pred->get_gate()->get_successors().empty()) + { + delete_gate_q.push_back(pred->get_gate()); + } + } + + for (const auto& pin : out_pins) + { + const auto suc = g->get_successor(pin); + + // disconnect inverter input from mux + suc->get_net()->remove_source(g, pin); + + // connect inverter output net to mux + auto in_net = suc->get_gate()->get_fan_out_nets().front(); + in_net->add_source(g, pin); + + // delete inverter gate if it does not have any predecessors + if (suc->get_gate()->get_predecessors().empty()) + { + delete_gate_q.push_back(suc->get_gate()); + } + } + } + + for (auto g : delete_gate_q) + { + nl->delete_gate(g); + delete_count++; + } + + log_info("netlist_preprocessing", "removed {} encasing inverters", delete_count); + + return OK(delete_count); + } + + struct MuxFingerprint + { + GateType* type; + std::set inverters; + + bool operator<(const MuxFingerprint& other) const + { + return (other.type < type) || (other.type == type && other.inverters < inverters); + } + }; + + Result unify_inverted_select_signals(Netlist* nl, GateLibrary* mux_inv_gl) + { + if (nl == nullptr) + { + return ERR("netlist is a nullptr"); + } + + if (mux_inv_gl == nullptr) + { + return ERR("gate library is a nullptr"); + } + + auto base_path_res = utils::get_unique_temp_directory("resynthesis_"); + if (base_path_res.is_error()) + { + return ERR_APPEND(base_path_res.get_error(), "unable to resynthesize boolean functions with yosys: failed to get unique temp directory"); + } + const std::filesystem::path base_path = base_path_res.get(); + const std::filesystem::path genlib_path = base_path / "mux_inv.genlib"; + std::filesystem::create_directory(base_path); + + const auto gl_save_res = gate_library_manager::save(genlib_path, mux_inv_gl, true); + if (!gl_save_res) + { + return ERR("unable to unify muxe select signals: failed to save gate library " + mux_inv_gl->get_name() + " to location " + genlib_path.string()); + } + + const i64 initial_size = nl->get_gates().size(); + + // resynthesize all muxes where any select signal is preceded by an inverter hoping to unify the structure with regards to other muxes conntected to the same select signal + + // TODO: as long as resynthezising the subgraph this can only consider HAL muxes + std::vector muxes = nl->get_gates([](const Gate* g) { return (g->get_type()->get_name().find("HAL_MUX") != std::string::npos); }); + + std::map> resynth_cache; + + for (const auto& g : muxes) + { + // MUX fingerprint for caching resynthesis results + MuxFingerprint mf; + mf.type = g->get_type(); + + // mapping from MUX select pins to either the input net of the preceding inverter or the net directly connected to the select pin + std::map pin_to_input; + + auto select_pins = g->get_type()->get_pins([](const GatePin* pin) { return (pin->get_type() == PinType::select) && (pin->get_direction() == PinDirection::input); }); + + std::vector preceding_inverters; + for (const auto& pin : g->get_type()->get_input_pins()) + { + const auto pred = g->get_predecessor(pin); + const auto is_select = (std::find(select_pins.begin(), select_pins.end(), pin) != select_pins.end()); + if (!is_select || pred == nullptr || pred->get_gate() == nullptr || !pred->get_gate()->get_type()->has_property(GateTypeProperty::c_inverter) + || (pred->get_gate()->get_fan_in_endpoints().size() != 1)) + { + pin_to_input.insert({pin, g->get_fan_in_net(pin)}); + } + else + { + auto inv_gate = pred->get_gate(); + preceding_inverters.push_back(inv_gate); + pin_to_input.insert({pin, inv_gate->get_fan_in_endpoints().front()->get_net()}); + mf.inverters.insert(pin); + } + } + + // if there is at least one inverter in front of the mux gate we build a subgraph containing all inverters and the mux gate and resynthesize + if (!preceding_inverters.empty()) + { + const Netlist* resynth_nl; + + auto subgraph = preceding_inverters; + subgraph.push_back(g); + + // try to use cached resynth netlist + if (const auto it = resynth_cache.find(mf); it == resynth_cache.end()) + { + std::unordered_map bfs; + for (const auto& ep : g->get_fan_out_endpoints()) + { + const auto bf_res = SubgraphNetlistDecorator(*nl).get_subgraph_function(subgraph, ep->get_net()); + if (bf_res.is_error()) + { + return ERR_APPEND(bf_res.get_error(), + "unable to unify muxes select signals: failed to build boolean function for mux " + g->get_name() + " with ID " + std::to_string(g->get_id()) + + " at output " + ep->get_pin()->get_name()); + } + auto bf = bf_res.get(); + + // replace all net id vars with generic vaiables refering to their connectivity to the mux + for (const auto& [pin, net] : pin_to_input) + { + auto sub_res = bf.substitute(BooleanFunctionNetDecorator(*net).get_boolean_variable_name(), BooleanFunction::Var(pin->get_name(), 1)); + if (sub_res.is_error()) + { + return ERR_APPEND(sub_res.get_error(), "unable to unify muxes select signals: failed to substitute net_id variable with generic variable"); + } + bf = sub_res.get(); + } + + bfs.insert({ep->get_pin()->get_name(), std::move(bf)}); + } + + auto resynth_res = generate_resynth_netlist_for_boolean_functions(bfs, genlib_path, mux_inv_gl, true); + if (resynth_res.is_error()) + { + return ERR_APPEND(resynth_res.get_error(), "unable to unify select signals of muxes: failed to resynthesize mux subgraph to netlist"); + } + auto unique_resynth_nl = resynth_res.get(); + resynth_nl = unique_resynth_nl.get(); + resynth_cache.insert({mf, std::move(unique_resynth_nl)}); + } + else + { + resynth_nl = it->second.get(); + } + + std::unordered_map> global_io_mapping; + + // use top module pin names to find correponding nets in original netlist + for (const auto& pin : resynth_nl->get_top_module()->get_input_pins()) + { + auto net_it = pin_to_input.find(g->get_type()->get_pin_by_name(pin->get_name())); + if (net_it == pin_to_input.end()) + { + return ERR("unable to unify muxes select signals:: failed to locate net in destination netlist from global input " + pin->get_name() + " in resynthesized netlist"); + } + global_io_mapping[pin->get_net()].push_back(net_it->second); + } + for (const auto& pin : resynth_nl->get_top_module()->get_output_pins()) + { + auto net = g->get_fan_out_net(pin->get_name()); + if (net == nullptr) + { + return ERR("unable to unify muxes select signals:: failed to locate net in destination netlist from global output " + pin->get_name() + " in resynthesized netlist"); + } + global_io_mapping[pin->get_net()].push_back(net); + } + + auto replace_res = replace_subgraph_with_netlist(subgraph, global_io_mapping, resynth_nl, nl, false); + if (replace_res.is_error()) + { + return ERR("unable to unify muxes select signals: failed to replace mux subgraph with resynthesized netlist"); + } + + // delete old subgraph gates that only fed into the mux + std::vector to_delete; + for (const auto g : subgraph) + { + bool has_no_outside_destinations = true; + bool has_only_outside_destinations = true; + for (const auto& suc : g->get_successors()) + { + const auto it = std::find(subgraph.begin(), subgraph.end(), suc->get_gate()); + if (it == subgraph.end()) + { + has_no_outside_destinations = false; + } + + if (it != subgraph.end()) + { + has_only_outside_destinations = false; + } + } + + if (has_no_outside_destinations || has_only_outside_destinations) + { + to_delete.push_back(g); + } + } + + for (const auto& g : to_delete) + { + if (!nl->delete_gate(g)) + { + return ERR("unable to unify muxes select signals: failed to delete gate " + g->get_name() + " with ID " + std::to_string(g->get_id()) + " in destination netlist"); + } + } + } + } + + // delete the created directory and the contained files + std::filesystem::remove_all(base_path); + + const i64 new_size = nl->get_gates().size(); + const i64 difference = std::abs(initial_size - new_size); + + return OK(u32(difference)); + } + + Result unify_select_signals(Netlist* nl) + { + if (nl == nullptr) + { + return ERR("netlist is a nullptr"); + } + + u32 changed_connections = 0; + + // sort into groups of same type and identical select signals + std::map>, std::vector> grouped_muxes; + for (const auto& g : nl->get_gates([](const Gate* g) { return g->get_type()->has_property(GateTypeProperty::c_mux); })) + { + std::set select_signals; + const auto select_pins = g->get_type()->get_pins([](const GatePin* pin) { return (pin->get_type() == PinType::select) && (pin->get_direction() == PinDirection::input); }); + for (const auto& sp : select_pins) + { + select_signals.insert(g->get_fan_in_net(sp)); + } + + grouped_muxes[{g->get_type(), select_signals}].push_back(g); + } + + // unify select signals for each group + for (const auto& [finger_print, mux_group] : grouped_muxes) + { + const auto& [type, select_signals_set] = finger_print; + const auto select_pins = type->get_pins([](const GatePin* pin) { return (pin->get_type() == PinType::select) && (pin->get_direction() == PinDirection::input); }); + const auto output_pins = type->get_pins([](const GatePin* pin) { return pin->get_direction() == PinDirection::output; }); + + if (output_pins.size() != 1) + { + log_warning("netlist_preprocessing", + "Cannot unify select signals for muxes of type {} since the type has {} output signals and we can only handle 1.", + type->get_name(), + output_pins.size()); + continue; + } + + // check whether there is one mapping from select signals to select pins + std::map, std::vector> select_map_to_muxes; + for (const auto& g : mux_group) + { + std::map select_map; + for (const auto& sp : select_pins) + { + select_map.insert({sp, g->get_fan_in_net(sp)}); + } + + select_map_to_muxes[select_map].push_back(g); + } + + if (select_map_to_muxes.size() == 1) + { + continue; + } + + const std::vector select_signals = {select_signals_set.begin(), select_signals_set.end()}; + + // collect a new mapping from net to gate pin for each mux gate + std::map> new_net_to_pin; + + // add newly ordered select signals to pin/net mapping + for (const auto& g : mux_group) + { + for (u32 select_index = 0; select_index < select_pins.size(); select_index++) + { + auto select_pin = select_pins.at(select_index); + auto select_signal = select_signals.at(select_index); + new_net_to_pin[g][select_pin] = select_signal; + } + } + + // determine new pin/net connection for each "data" signal + auto type_bf = type->get_boolean_function(output_pins.front()); + for (u32 select_val = 0; select_val < (1 << select_signals.size()); select_val++) + { + std::map type_substitution; + for (u32 select_idx = 0; select_idx < select_pins.size(); select_idx++) + { + auto select_pin = select_pins.at(select_idx); + + auto type_substitution_val = ((select_val >> select_idx) & 0x1) ? BooleanFunction::Const(1, 1) : BooleanFunction::Const(0, 1); + type_substitution.insert({select_pin->get_name(), type_substitution_val}); + } + + auto type_substitution_res = type_bf.substitute(type_substitution); + if (type_substitution_res.is_error()) + { + return ERR_APPEND(type_substitution_res.get_error(), "cannot unify mux select signals: failed to substitute type Boolean function with select signal value mapping."); + } + auto input = type_substitution_res.get().simplify_local(); + + if (!input.is_variable()) + { + return ERR("cannot unify mux select signals: substituted and simplified type Boolean function (" + input.to_string() + ") is not a variable"); + } + + const auto pin_name = input.get_variable_name().get(); + auto pin = type->get_pins([pin_name](const auto& p) { return p->get_name() == pin_name; }).front(); + + for (const auto& g : mux_group) + { + auto gate_bf_res = g->get_resolved_boolean_function(output_pins.front(), false); + if (gate_bf_res.is_error()) + { + return ERR_APPEND(gate_bf_res.get_error(), + "cannot unify mux select signals: failed to build Boolean function for gate " + g->get_name() + " with ID " + std::to_string(g->get_id())); + } + auto gate_bf = gate_bf_res.get(); + + std::map gate_substitution; + + for (u32 select_idx = 0; select_idx < select_pins.size(); select_idx++) + { + auto gate_substitution_val = ((select_val >> select_idx) & 0x1) ? BooleanFunction::Const(1, 1) : BooleanFunction::Const(0, 1); + gate_substitution.insert({BooleanFunctionNetDecorator(*(select_signals.at(select_idx))).get_boolean_variable_name(), gate_substitution_val}); + } + + auto gate_substitution_res = gate_bf.substitute(gate_substitution); + if (gate_substitution_res.is_error()) + { + return ERR_APPEND(gate_substitution_res.get_error(), "cannot unify mux select signals: failed to substitute gate Boolean function with select signal value mapping."); + } + auto input_net_var = gate_substitution_res.get().simplify_local(); + auto net_res = BooleanFunctionNetDecorator::get_net_from(nl, input_net_var); + + if (net_res.is_error()) + { + return ERR_APPEND(net_res.get_error(), "cannot unify mux select signals: failed to extract net from substituted and simplified gate Boolean function"); + } + + auto net = net_res.get(); + new_net_to_pin[g][pin] = net; + } + } + + // apply new pin/net mapping to all gates + for (auto& [g, pin_net] : new_net_to_pin) + { + for (const auto& [pin, net] : pin_net) + { + auto connected_net = g->get_fan_in_net(pin); + if (net == connected_net) + { + continue; + } + + connected_net->remove_destination(g, pin); + net->add_destination(g, pin); + + changed_connections += 1; + } + } + } + + return OK(changed_connections); + } + } // namespace + + Result manual_mux_optimizations(Netlist* nl, GateLibrary* mux_inv_gl) + { + u32 res_count = 0; + + if (nl == nullptr) + { + return ERR("netlist is a nullptr"); + } + + if (mux_inv_gl == nullptr) + { + return ERR("gate library is a nullptr"); + } + + auto remove_res = remove_encasing_inverters(nl); + if (remove_res.is_error()) + { + return ERR_APPEND(remove_res.get_error(), "unable to apply manual mux optimizations: failed to remove encasing inverters"); + } + res_count += remove_res.get(); + + auto unify_inverted_res = unify_inverted_select_signals(nl, mux_inv_gl); + if (unify_inverted_res.is_error()) + { + return ERR_APPEND(unify_inverted_res.get_error(), "unable to apply manual mux optimizations: failed to unify inverted select signals"); + } + res_count += unify_inverted_res.get(); + + auto unify_res = unify_select_signals(nl); + if (unify_res.is_error()) + { + return ERR_APPEND(unify_res.get_error(), "unable to apply manual mux optimizations: failed to unify select signals"); + } + res_count += unify_res.get(); + + return OK(res_count); + } + + Result propagate_constants(Netlist* nl) + { + if (nl == nullptr) + { + return ERR("netlist is a nullptr"); + } + + Net* gnd_net = nl->get_gnd_gates().empty() ? nullptr : nl->get_gnd_gates().front()->get_fan_out_nets().front(); + Net* vcc_net = nl->get_vcc_gates().empty() ? nullptr : nl->get_vcc_gates().front()->get_fan_out_nets().front(); + + u32 total_replaced_dst_count = 0; + + while (true) + { + u32 replaced_dst_count = 0; + std::vector to_delete; + for (const auto g : nl->get_gates([](const auto g) { + return g->get_type()->has_property(GateTypeProperty::combinational) && !g->get_type()->has_property(GateTypeProperty::ground) + && !g->get_type()->has_property(GateTypeProperty::power); + })) + { + bool has_global_output = false; + for (const auto ep : g->get_fan_out_endpoints()) + { + if (ep->get_net()->is_global_output_net()) + { + has_global_output = true; + } + + auto bf_res = g->get_resolved_boolean_function(ep->get_pin(), false); + if (bf_res.is_error()) + { + return ERR_APPEND(bf_res.get_error(), + "unable to propagate constants: failed to generate boolean function at gate " + g->get_name() + " with ID " + std::to_string(g->get_id()) + " for pin " + + ep->get_pin()->get_name()); + } + auto bf = bf_res.get(); + auto sub_res = BooleanFunctionDecorator(bf).substitute_power_ground_nets(nl); + if (sub_res.is_error()) + { + return ERR_APPEND(bf_res.get_error(), + "unable to propagate constants: failed to substitue power and ground nets in boolean function of gate " + g->get_name() + " with ID " + + std::to_string(g->get_id()) + " for pin " + ep->get_pin()->get_name()); + } + bf = sub_res.get(); + bf = bf.simplify_local(); + + // if boolean function of output pin can be simplified to a constant connect all its successors to gnd/vcc instead + if (bf.is_constant()) + { + Net* new_source; + if (bf.has_constant_value(0)) + { + new_source = gnd_net; + } + else if (bf.has_constant_value(1)) + { + new_source = vcc_net; + } + else + { + continue; + } + + if (new_source == nullptr) + { + // log_error("netlist_preprocessing", "failed to replace bf {} with constant net because netlist is missing GND gate or VCC gate"); + return ERR("unable to propagate constants: netlist is missing gnd or vcc net!"); + } + + std::vector> to_replace; + for (auto dst : ep->get_net()->get_destinations()) + { + to_replace.push_back({dst->get_gate(), dst->get_pin()}); + } + + for (const auto& [dst_g, dst_p] : to_replace) + { + ep->get_net()->remove_destination(dst_g, dst_p); + new_source->add_destination(dst_g, dst_p); + + replaced_dst_count++; + } + + nl->delete_net(ep->get_net()); + } + } + + if (!has_global_output && g->get_successors().empty()) + { + to_delete.push_back(g); + } + } + + for (auto g : to_delete) + { + nl->delete_gate(g); + } + + if (replaced_dst_count == 0) + { + break; + } + + log_debug("netlist_preprocessing", "replaced {} destinations this with power/ground nets this iteration", replaced_dst_count); + total_replaced_dst_count += replaced_dst_count; + } + + log_info("netlist_preprocessing", "replaced {} destinations with power/ground nets in total", total_replaced_dst_count); + return OK(total_replaced_dst_count); + } + + Result remove_consecutive_inverters(Netlist* nl) + { + if (nl == nullptr) + { + return ERR("netlist is a nullptr"); + } + + std::set gates_to_delete; + for (auto* inv_gate : nl->get_gates([](const Gate* g) { return g->get_type()->has_property(GateTypeProperty::c_inverter); })) + { + if (gates_to_delete.find(inv_gate) != gates_to_delete.end()) + { + continue; + } + + const auto& connection_endpoints = inv_gate->get_fan_in_endpoints(); + if (connection_endpoints.size() != 1) + { + log_warning("netlist_preprocessing", "could not handle gate '{}' with ID {} due to a fan-in size != 1", inv_gate->get_name(), inv_gate->get_id()); + continue; + } + + auto* middle_fan_in_ep = connection_endpoints.front(); + auto* middle_net = middle_fan_in_ep->get_net(); + if (middle_net->get_sources().size() != 1) + { + log_warning("netlist_preprocessing", "could not handle gate '{}' with ID {} due to a number of predecessors != 1", inv_gate->get_name(), inv_gate->get_id()); + continue; + } + auto* pred_gate = middle_net->get_sources().front()->get_gate(); + + if (pred_gate->get_type()->has_property(GateTypeProperty::c_inverter)) + { + const auto& fan_in = pred_gate->get_fan_in_endpoints(); + if (fan_in.size() != 1) + { + log_warning("netlist_preprocessing", "could not handle gate '{}' with ID {} due to a fan-in size != 1", pred_gate->get_name(), pred_gate->get_id()); + continue; + } + if (pred_gate->get_fan_out_endpoints().size() != 1) + { + log_warning("netlist_preprocessing", "could not handle gate '{}' with ID {} due to a fan-out size != 1", pred_gate->get_name(), pred_gate->get_id()); + continue; + } + auto* in_net = fan_in.front()->get_net(); + + const auto& fan_out = inv_gate->get_fan_out_endpoints(); + if (fan_out.size() != 1) + { + log_warning("netlist_preprocessing", "could not handle gate '{}' with ID {} due to a fan-out size != 1", inv_gate->get_name(), inv_gate->get_id()); + continue; + } + auto* out_net = fan_out.front()->get_net(); + + for (auto* dst_ep : out_net->get_destinations()) + { + auto* dst_pin = dst_ep->get_pin(); + auto* dst_gate = dst_ep->get_gate(); + + out_net->remove_destination(dst_ep); + in_net->add_destination(dst_gate, dst_pin); + } + + middle_net->remove_destination(middle_fan_in_ep); + + if (middle_net->get_num_of_destinations() == 0) + { + nl->delete_net(middle_net); + gates_to_delete.insert(pred_gate); + } + + gates_to_delete.insert(inv_gate); + } + } + + u32 removed_ctr = 0; + for (auto* g : gates_to_delete) + { + nl->delete_gate(g); + removed_ctr++; + } + + return OK(removed_ctr); + } + + namespace + { + std::string generate_hex_truth_table_string(const std::vector& tt) + { + std::string tt_str = ""; + + u32 acc = 0; + for (u32 i = 0; i < tt.size(); i++) + { + const BooleanFunction::Value bit = tt.at(i); + if (bit == BooleanFunction::Value::ONE) + { + acc += (1 << (i % 4)); + } + + if ((i % 4) == 3) + { + std::stringstream stream; + stream << std::hex << acc; + + tt_str = stream.str() + tt_str; + + acc = 0; + } + } + + return tt_str; + } + } // namespace + + Result simplify_lut_inits(Netlist* nl) + { + u32 num_inits = 0; + + for (auto g : nl->get_gates([](const auto& g) { return g->get_type()->has_property(GateTypeProperty::c_lut); })) + { + auto res = g->get_init_data(); + if (res.is_error()) + { + return ERR_APPEND(res.get_error(), + "unable to simplify lut init string for gate " + g->get_name() + " with ID " + std::to_string(g->get_id()) + ": failed to get original INIT string"); + } + + const auto original_inits = res.get(); + + if (original_inits.size() != 1) + { + return ERR("unable to simplify lut init string for gate " + g->get_name() + " with ID " + std::to_string(g->get_id()) + ": found " + std::to_string(original_inits.size()) + + " init data strings but expected exactly 1."); + } + + const auto original_init = original_inits.front(); + + // skip if the gate type has more than one fan out endpoints + if (g->get_type()->get_output_pins().size() != 1) + { + continue; + } + + const auto out_ep = g->get_fan_out_endpoints().front(); + + // skip if the gate has more than one boolean function + if (g->get_boolean_functions().size() != 1) + { + continue; + } + + const auto bf_org = g->get_boolean_function(out_ep->get_pin()); + const auto org_vars = bf_org.get_variable_names(); + + const auto bf_replaced_res = BooleanFunctionDecorator(bf_org).substitute_power_ground_pins(g); + if (bf_replaced_res.is_error()) + { + return ERR_APPEND(bf_replaced_res.get_error(), + "cannot simplify LUT inits: failed to replace power and ground pins for gate " + g->get_name() + " with ID " + std::to_string(g->get_id())); + } + const auto bf_replaced = bf_replaced_res.get(); + const auto bf_simplified = bf_replaced.simplify_local(); + + const auto new_vars = bf_simplified.get_variable_names(); + + if (org_vars.size() == new_vars.size()) + { + continue; + } + + auto bf_extended = bf_simplified.clone(); + for (const auto& in_pin : g->get_type()->get_input_pin_names()) + { + if (new_vars.find(in_pin) == new_vars.end()) + { + auto bf_filler = BooleanFunction::Var(in_pin) | (~BooleanFunction::Var(in_pin)); + bf_extended = BooleanFunction::And(std::move(bf_extended), std::move(bf_filler), 1).get(); + } + } + + const auto tt = bf_extended.compute_truth_table().get(); + const auto new_init_string = generate_hex_truth_table_string(tt.front()); + + // std::cout << "Org Init: " << g->get_init_data().get().front() << std::endl; + // std::cout << "New Init: " << new_init_string << std::endl; + + g->set_init_data({new_init_string}).get(); + g->set_data("preprocessing_information", "original_init", "string", original_init); + + // const auto bf_test = g->get_boolean_function(out_ep->get_pin()); + + // std::cout << "Org: " << bf_org << std::endl; + // std::cout << "Rep: " << bf_replaced << std::endl; + // std::cout << "Simp: " << bf_simplified << std::endl; + // std::cout << "Test: " << bf_test << std::endl; + // std::cout << "Ext: " << bf_extended << std::endl; + + num_inits++; + } + + log_info("netlist_preprocessing", "simplified {} LUT INIT strings inside of netlist with ID {}.", num_inits, nl->get_id()); + return OK(num_inits); + } + + namespace + { + struct indexed_identifier + { + indexed_identifier(const std::string& p_identifier, const u32 p_index, const std::string& p_origin) : identifier{p_identifier}, index{p_index}, origin{p_origin} + { + } + + std::string identifier; + u32 index; + std::string origin; + }; + + // TODO when the verilog parser changes are merged into the master this will no longer be needed + const std::string hal_instance_index_pattern = "__\\[(\\d+)\\]__"; + const std::string hal_instance_index_pattern_reverse = "(\\d+)"; + + std::string replace_hal_instance_index(const std::string& name) + { + std::regex re(hal_instance_index_pattern); + + std::string input = name; + std::string index; + std::smatch match; + while (std::regex_search(input, match, re)) + { + index = match[1]; + input = utils::replace(input, match.str(), "" + index + ""); + } + + return input; + } + + std::string reconstruct_hal_instance_index(const std::string& name) + { + std::regex re(hal_instance_index_pattern_reverse); + + std::string input = name; + std::string index; + std::smatch match; + while (std::regex_search(input, match, re)) + { + index = match[1]; + input = utils::replace(input, match.str(), "__[" + index + "]__"); + } + + return input; + } + + const std::string net_index_pattern = "\\((\\d+)\\)"; + const std::string gate_index_pattern = "\\[(\\d+)\\]"; + + // Extracts an index from a string by taking the last integer enclosed by parentheses + std::optional extract_index(const std::string& name, const std::string& index_pattern, const std::string& origin) + { + std::regex re(index_pattern); + + std::string input = name; + std::optional last_match; + std::optional last_index; + + // Search for last match within string + std::smatch match; + while (std::regex_search(input, match, re)) + { + // Capture integer and update input string to search from after the match + last_index = std::stoi(match[1]); + last_match = match.str(); + input = match.suffix().str(); + } + + if (!last_index.has_value()) + { + return std::nullopt; + } + + const auto found_match = last_match.value(); + auto identifier_name = name; + identifier_name = identifier_name.replace(name.rfind(found_match), found_match.size(), ""); + + return std::optional{{identifier_name, last_index.value(), origin}}; + } + + // annotate all found identifiers to a gate + bool annotate_indexed_identifiers(Gate* gate, const std::vector& identifiers) + { + std::string json_identifier_str = + "[" + utils::join(", ", identifiers, [](const auto& i) { return std::string("[") + '"' + i.identifier + '"' + ", " + std::to_string(i.index) + ", " + '"' + i.origin + '"' + "]"; }) + + "]"; + + return gate->set_data("preprocessing_information", "multi_bit_indexed_identifiers", "string", json_identifier_str); + } + + // search for a net that connects to the gate at a pin of a specific type and tries to reconstruct an indexed identifier from its name or form a name of its merged wires + std::vector check_net_at_pin(const PinType pin_type, Gate* gate) + { + const auto typed_pins = gate->get_type()->get_pins([pin_type](const auto p) { return p->get_type() == pin_type; }); + + std::vector found_identfiers; + + for (const auto& pin : typed_pins) + { + const auto typed_net = (pin->get_direction() == PinDirection::output) ? gate->get_fan_out_net(pin) : gate->get_fan_in_net(pin); + + // 1) search the net name itself + const auto net_name_index = extract_index(typed_net->get_name(), net_index_pattern, "net_name"); + if (net_name_index.has_value()) + { + found_identfiers.push_back(net_name_index.value()); + } + + // 2) search all the names of the wires that where merged into this net + if (!typed_net->has_data("parser_annotation", "merged_nets")) + { + continue; + } + + const auto all_merged_nets_str = std::get<1>(typed_net->get_data("parser_annotation", "merged_nets")); + + if (all_merged_nets_str.empty()) + { + continue; + } + + // parse json list of merged net names + rapidjson::Document doc; + doc.Parse(all_merged_nets_str.c_str()); + + for (u32 i = 0; i < doc.GetArray().Size(); i++) + { + const auto list = doc[i].GetArray(); + for (u32 j = 0; j < list.Size(); j++) + { + const auto merged_wire_name = list[j].GetString(); + + const auto merged_wire_name_index = extract_index(merged_wire_name, net_index_pattern, "net_name"); + if (merged_wire_name_index.has_value()) + { + found_identfiers.push_back(merged_wire_name_index.value()); + } + } + } + } + + return found_identfiers; + } + } // namespace + + Result reconstruct_indexed_ff_identifiers(Netlist* nl) + { + u32 counter = 0; + for (auto& ff : nl->get_gates([](const auto g) { return g->get_type()->has_property(GateTypeProperty::ff); })) + { + std::vector all_identifiers; + + // 1) Check whether the ff gate already has an index annotated in its gate name + const auto cleaned_gate_name = replace_hal_instance_index(ff->get_name()); + const auto gate_name_index = extract_index(cleaned_gate_name, gate_index_pattern, "gate_name"); + + if (gate_name_index.has_value()) + { + auto found_identifier = gate_name_index.value(); + found_identifier.identifier = reconstruct_hal_instance_index(found_identifier.identifier); + all_identifiers.push_back(found_identifier); + } + + static const std::vector relevant_pin_types = {PinType::state, PinType::neg_state, PinType::data}; + + // 2) Check all relevant pin_types + for (const auto& pt : relevant_pin_types) + { + const auto found_identifiers = check_net_at_pin(pt, ff); + all_identifiers.insert(all_identifiers.end(), found_identifiers.begin(), found_identifiers.end()); + } + + if (!all_identifiers.empty()) + { + counter++; + } + + annotate_indexed_identifiers(ff, all_identifiers); + } + + return OK(counter); + } + + Result reconstruct_top_module_pin_groups(Netlist* nl) + { + std::map>> pg_name_to_indexed_pins; + + for (const auto& pin : nl->get_top_module()->get_pins()) + { + auto reconstruct = extract_index(pin->get_name(), net_index_pattern, ""); + if (!reconstruct.has_value()) + { + continue; + } + + auto [pg_name, index, _] = reconstruct.value(); + + pg_name_to_indexed_pins[pg_name][index].push_back(pin); + } + + u32 reconstructed_counter = 0; + for (const auto& [pg_name, indexed_pins] : pg_name_to_indexed_pins) + { + std::vector ordered_pins; + + bool valid_indices = true; + // NOTE: since the map already orders the indices from low to high, if we iterate over it we also get the pins in the right order + for (const auto& [_index, pins] : indexed_pins) + { + if (pins.size() > 1) + { + valid_indices = false; + break; + } + + ordered_pins.push_back(pins.front()); + } + + if (!valid_indices) + { + continue; + } + + auto res = nl->get_top_module()->create_pin_group(pg_name, ordered_pins); + if (res.is_error()) + { + return ERR_APPEND(res.get_error(), "cannot reconstruct top module pin groups: failed to create pin group " + pg_name); + } + + reconstructed_counter++; + } + + return OK(reconstructed_counter); + } + + namespace + { + struct ComponentData + { + std::string name; + std::string type; + u64 x; + u64 y; + }; + + TokenStream tokenize(std::stringstream& ss) + { + const std::string delimiters = " ;-"; + std::string current_token; + u32 line_number = 0; + + std::string line; + bool escaped = false; + + std::vector> parsed_tokens; + while (std::getline(ss, line)) + { + line_number++; + + for (char c : line) + { + // deal with escaping and strings + if (c == '\\') + { + escaped = true; + continue; + } + else if (escaped && std::isspace(c)) + { + escaped = false; + continue; + } + + if (((!std::isspace(c) && delimiters.find(c) == std::string::npos) || escaped)) + { + current_token += c; + } + else + { + if (!current_token.empty()) + { + parsed_tokens.emplace_back(line_number, current_token); + current_token.clear(); + } + + if (!std::isspace(c)) + { + parsed_tokens.emplace_back(line_number, std::string(1, c)); + } + } + } + + if (!current_token.empty()) + { + parsed_tokens.emplace_back(line_number, current_token); + current_token.clear(); + } + } + + return TokenStream(parsed_tokens, {}, {}); + } + + Result> parse_tokens(TokenStream& ts) + { + ts.consume_until("COMPONENTS"); + ts.consume("COMPONENTS"); + const auto component_count_str = ts.consume().string; + ts.consume(";"); + + u32 component_count; + if (const auto res = utils::wrapped_stoul(component_count_str); res.is_ok()) + { + component_count = res.get(); + } + else + { + return ERR_APPEND(res.get_error(), "could not parse tokens: failed to read component count from token" + component_count_str); + } + + std::cout << "Component count: " << component_count << std::endl; + + std::unordered_map component_data; + for (u32 c_idx = 0; c_idx < component_count; c_idx++) + { + // parse a line + ComponentData new_data_entry; + ts.consume("-"); + new_data_entry.name = ts.consume().string; + new_data_entry.type = ts.consume().string; + ts.consume("+"); + ts.consume("SOURCE"); + ts.consume("DIST"); + ts.consume("TIMING"); + ts.consume("+"); + ts.consume("PLACED"); + ts.consume("FIXED"); + ts.consume("("); + + if (const auto res = utils::wrapped_stoull(ts.consume().string); res.is_ok()) + { + new_data_entry.x = res.get(); + } + else + { + return ERR_APPEND(res.get_error(), "could not parse tokens: failed to read x coordinate from token"); + } + + if (const auto res = utils::wrapped_stoull(ts.consume().string); res.is_ok()) + { + new_data_entry.y = res.get(); + } + else + { + return ERR_APPEND(res.get_error(), "could not parse tokens: failed to read y coordinate from token"); + } + + ts.consume(")"); + + ts.consume_current_line(); + + component_data.insert({new_data_entry.name, new_data_entry}); + } + + return OK(component_data); + } + } // namespace + + Result parse_def_file(Netlist* nl, const std::filesystem::path& def_file) + { + std::stringstream ss; + std::ifstream ifs; + ifs.open(def_file.string(), std::ifstream::in); + if (!ifs.is_open()) + { + return ERR("could not parse DEF (Design Exchange Format) file '" + def_file.string() + "' : unable to open file"); + } + ss << ifs.rdbuf(); + ifs.close(); + + auto ts = tokenize(ss); + + std::unordered_map component_data; + // parse tokens + try + { + if (auto res = parse_tokens(ts); res.is_error()) + { + return ERR_APPEND(res.get_error(), "could not parse Design Exchange Format file '" + def_file.string() + "': unable to parse tokens"); + } + else + { + component_data = res.get(); + } + } + catch (TokenStream::TokenStreamException& e) + { + if (e.line_number != (u32)-1) + { + return ERR("could not parse Design Exchange Format file '" + def_file.string() + "': " + e.message + " (line " + std::to_string(e.line_number) + ")"); + } + else + { + return ERR("could not parse Design Exchange Format file '" + def_file.string() + "': " + e.message); + } + } + + std::unordered_map name_to_gate; + for (auto g : nl->get_gates()) + { + name_to_gate.insert({g->get_name(), g}); + } + + u32 counter = 0; + for (const auto& [gate_name, data] : component_data) + { + if (const auto& g_it = name_to_gate.find(gate_name); g_it != name_to_gate.end()) + { + // TODO figure out whereever we are saving coordinates now... + g_it->second->set_location_x(data.x); + g_it->second->set_location_y(data.y); + + counter++; + } + } + + log_info("netlist_preprocessing", "reconstructed coordinates for {} / {} ({:.2}) gates", counter, nl->get_gates().size(), (double)counter / (double)nl->get_gates().size()); + + return OK({}); + } + + Result> create_multi_bit_gate_modules(Netlist* nl, const std::map>>& concatenated_pin_groups) + { + std::vector all_modules; + for (const auto& [gt_name, pin_groups] : concatenated_pin_groups) + { + const auto& gt = nl->get_gate_library()->get_gate_type_by_name(gt_name); + if (gt == nullptr) + { + return ERR("unable to create multi bit gate module for gate type " + gt_name + ": failed to find gate type with that name in gate library " + nl->get_gate_library()->get_name()); + } + + for (const auto& g : nl->get_gates([>](const auto& g) { return g->get_type() == gt; })) + { + auto m = nl->create_module("module_" + g->get_name(), g->get_module(), {g}); + + for (const auto& [module_pg_name, gate_pg_names] : pin_groups) + { + std::vector module_pins; + for (const auto& gate_pg_name : gate_pg_names) + { + const auto& gate_pg = g->get_type()->get_pin_group_by_name(gate_pg_name); + + if (gate_pg == nullptr) + { + return ERR("unable to create multi-bit gate module for gate type " + gt_name + " and pin group " + gate_pg_name + ": failed to find pin group with that name"); + } + + std::vector pin_list = gate_pg->get_pins(); + if (!gate_pg->is_ascending()) + { + std::reverse(pin_list.begin(), pin_list.end()); + } + + for (const auto& gate_pin : pin_list) + { + const auto net = (gate_pin->get_direction() == PinDirection::output) ? g->get_fan_out_net(gate_pin) : g->get_fan_in_net(gate_pin); + if (net == nullptr) + { + continue; + } + + if (net->is_gnd_net() || net->is_vcc_net()) + { + continue; + } + + const auto module_pin = m->get_pin_by_net(net); + + module_pins.push_back(module_pin); + } + } + + m->create_pin_group(module_pg_name, module_pins); + u32 idx_counter = 0; + for (const auto& mp : module_pins) + { + m->set_pin_name(mp, module_pg_name + "_" + std::to_string(idx_counter)); + idx_counter++; + } + } + + all_modules.push_back(m); + } + } + + return OK(all_modules); + } + + Result> create_nets_at_unconnected_pins(Netlist* nl) + { + std::vector created_nets; + + for (const auto& g : nl->get_gates()) + { + for (const auto& p : g->get_type()->get_output_pins()) + { + if (g->get_fan_out_net(p) == nullptr) + { + auto new_net = nl->create_net("TEMP"); + new_net->set_name("HAL_UNCONNECTED_" + std::to_string(new_net->get_id())); + new_net->add_source(g, p); + + created_nets.push_back(new_net); + } + } + } + + return OK(created_nets); + } + + Result unify_ff_outputs(Netlist* nl, const std::vector& ffs, GateType* inverter_type) + { + if (nl == nullptr) + { + return ERR("netlist is a nullptr"); + } + + if (inverter_type == nullptr) + { + const auto* gl = nl->get_gate_library(); + const auto inv_types = + gl->get_gate_types([](const GateType* gt) { return gt->has_property(GateTypeProperty::c_inverter) && gt->get_input_pins().size() == 1 && gt->get_output_pins().size() == 1; }); + if (inv_types.empty()) + { + return ERR("gate library '" + gl->get_name() + "' of netlist does not contain an inverter gate"); + } + inverter_type = inv_types.begin()->second; + } + else + { + if (inverter_type->get_gate_library() != nl->get_gate_library()) + { + return ERR("inverter gate type '" + inverter_type->get_name() + "' of gate library '" + inverter_type->get_gate_library()->get_name() + "' does not belong to gate library '" + + nl->get_gate_library()->get_name() + "' of provided netlist"); + } + + if (!inverter_type->has_property(GateTypeProperty::c_inverter)) + { + return ERR("gate type '" + inverter_type->get_name() + "' of gate library '" + inverter_type->get_gate_library()->get_name() + "' is not an inverter gate type"); + } + + if (inverter_type->get_input_pins().size() != 1 || inverter_type->get_output_pins().size() != 1) + { + return ERR("inverter gate type '" + inverter_type->get_name() + "' of gate library '" + inverter_type->get_gate_library()->get_name() + + "' has an invalid number of input pins or output pins"); + } + } + + auto inv_in_pin = inverter_type->get_input_pins().front(); + auto inv_out_pin = inverter_type->get_output_pins().front(); + + u32 ctr = 0; + + const std::vector& gates = ffs.empty() ? nl->get_gates() : ffs; + + for (auto* ff : gates) + { + auto* ff_type = ff->get_type(); + + if (!ff_type->has_property(GateTypeProperty::ff)) + { + continue; + } + + GatePin* state_pin = nullptr; + GatePin* neg_state_pin = nullptr; + + for (auto* o_pin : ff_type->get_output_pins()) + { + if (o_pin->get_type() == PinType::state) + { + state_pin = o_pin; + } + else if (o_pin->get_type() == PinType::neg_state) + { + neg_state_pin = o_pin; + } + } + + if (neg_state_pin == nullptr) + { + continue; + } + + auto* neg_state_ep = ff->get_fan_out_endpoint(neg_state_pin); + if (neg_state_ep == nullptr) + { + continue; + } + auto* neg_state_net = neg_state_ep->get_net(); + + auto state_net = ff->get_fan_out_net(state_pin); + if (state_net == nullptr) + { + state_net = nl->create_net(ff->get_name() + "__STATE_NET__"); + state_net->add_source(ff, state_pin); + } + + auto* inv = nl->create_gate(inverter_type, ff->get_name() + "__NEG_STATE_INVERT__"); + state_net->add_destination(inv, inv_in_pin); + neg_state_net->remove_source(neg_state_ep); + neg_state_net->add_source(inv, inv_out_pin); + ctr++; + } + + return OK(ctr); + } + } // namespace netlist_preprocessing +} // namespace hal \ No newline at end of file diff --git a/plugins/netlist_preprocessing/src/plugin_netlist_preprocessing.cpp b/plugins/netlist_preprocessing/src/plugin_netlist_preprocessing.cpp index 3474f889223..5a5ca7e137a 100644 --- a/plugins/netlist_preprocessing/src/plugin_netlist_preprocessing.cpp +++ b/plugins/netlist_preprocessing/src/plugin_netlist_preprocessing.cpp @@ -1,24 +1,5 @@ #include "netlist_preprocessing/plugin_netlist_preprocessing.h" -#include "hal_core/netlist/boolean_function/solver.h" -#include "hal_core/netlist/decorators/boolean_function_decorator.h" -#include "hal_core/netlist/decorators/boolean_function_net_decorator.h" -#include "hal_core/netlist/decorators/netlist_modification_decorator.h" -#include "hal_core/netlist/decorators/subgraph_netlist_decorator.h" -#include "hal_core/netlist/endpoint.h" -#include "hal_core/netlist/gate.h" -#include "hal_core/netlist/gate_library/gate_library_manager.h" -#include "hal_core/netlist/module.h" -#include "hal_core/netlist/net.h" -#include "hal_core/netlist/netlist_utils.h" -#include "hal_core/utilities/result.h" -#include "hal_core/utilities/token_stream.h" -#include "rapidjson/document.h" - -#include -#include -#include - namespace hal { extern std::unique_ptr create_plugin_instance() @@ -33,1538 +14,18 @@ namespace hal std::string NetlistPreprocessingPlugin::get_version() const { - return std::string("0.1"); - } - - namespace - { - std::string generate_hex_truth_table_string(const std::vector& tt) - { - std::string tt_str = ""; - - u32 acc = 0; - for (u32 i = 0; i < tt.size(); i++) - { - const BooleanFunction::Value bit = tt.at(i); - if (bit == BooleanFunction::Value::ONE) - { - acc += (1 << (i % 4)); - } - - if ((i % 4) == 3) - { - std::stringstream stream; - stream << std::hex << acc; - - tt_str = stream.str() + tt_str; - - acc = 0; - } - } - - return tt_str; - } - } // namespace - - Result NetlistPreprocessingPlugin::simplify_lut_inits(Netlist* nl) - { - u32 num_inits = 0; - - for (auto g : nl->get_gates([](const auto& g) { return g->get_type()->has_property(GateTypeProperty::c_lut); })) - { - auto res = g->get_init_data(); - if (res.is_error()) - { - return ERR_APPEND(res.get_error(), "unable to simplify lut init string for gate " + g->get_name() + " with ID " + std::to_string(g->get_id()) + ": failed to get original INIT string"); - } - - const auto original_inits = res.get(); - - if (original_inits.size() != 1) - { - return ERR("unable to simplify lut init string for gate " + g->get_name() + " with ID " + std::to_string(g->get_id()) + ": found " + std::to_string(original_inits.size()) - + " init data strings but expected exactly 1."); - } - - const auto original_init = original_inits.front(); - - // skip if the gate type has more than one fan out endpoints - if (g->get_type()->get_output_pins().size() != 1) - { - continue; - } - - const auto out_ep = g->get_fan_out_endpoints().front(); - - // skip if the gate has more than one boolean function - if (g->get_boolean_functions().size() != 1) - { - continue; - } - - const auto bf_org = g->get_boolean_function(out_ep->get_pin()); - const auto org_vars = bf_org.get_variable_names(); - - const auto bf_replaced = BooleanFunctionDecorator(bf_org).substitute_power_ground_pins(g).get(); - const auto bf_simplified = bf_replaced.simplify_local(); - - const auto new_vars = bf_simplified.get_variable_names(); - - if (org_vars.size() == new_vars.size()) - { - continue; - } - - auto bf_extended = bf_simplified.clone(); - for (const auto& in_pin : g->get_type()->get_input_pin_names()) - { - if (new_vars.find(in_pin) == new_vars.end()) - { - auto bf_filler = BooleanFunction::Var(in_pin) | (~BooleanFunction::Var(in_pin)); - bf_extended = BooleanFunction::And(std::move(bf_extended), std::move(bf_filler), 1).get(); - } - } - - const auto tt = bf_extended.compute_truth_table().get(); - const auto new_init_string = generate_hex_truth_table_string(tt.front()); - - // std::cout << "Org Init: " << g->get_init_data().get().front() << std::endl; - // std::cout << "New Init: " << new_init_string << std::endl; - - g->set_init_data({new_init_string}).get(); - g->set_data("preprocessing_information", "original_init", "string", original_init); - - // const auto bf_test = g->get_boolean_function(out_ep->get_pin()); - - // std::cout << "Org: " << bf_org << std::endl; - // std::cout << "Rep: " << bf_replaced << std::endl; - // std::cout << "Simp: " << bf_simplified << std::endl; - // std::cout << "Test: " << bf_test << std::endl; - // std::cout << "Ext: " << bf_extended << std::endl; - - num_inits++; - } - - log_info("netlist_preprocessing", "simplified {} LUT INIT strings inside of netlist with ID {}.", num_inits, nl->get_id()); - return OK(num_inits); - } - - Result NetlistPreprocessingPlugin::remove_unused_lut_inputs(Netlist* nl) - { - u32 num_eps = 0; - - // get net connected to GND - const std::vector& gnd_gates = nl->get_gnd_gates(); - if (gnd_gates.empty()) - { - return ERR("could not remove unused LUT endpoints from netlist with ID " + std::to_string(nl->get_id()) + ": no GND net available within netlist"); - } - Net* gnd_net = gnd_gates.front()->get_fan_out_nets().front(); - - // iterate all LUT gates - for (const auto& gate : nl->get_gates([](const Gate* g) { return g->get_type()->has_property(GateTypeProperty::c_lut); })) - { - std::vector fan_in = gate->get_fan_in_endpoints(); - std::unordered_map functions = gate->get_boolean_functions(); - - // skip if more than one function - if (functions.size() != 1) - { - continue; - } - - // only pins used as variables in Boolean function are considered active - auto active_pins = functions.begin()->second.get_variable_names(); - - // if there are more fan-in nets than there are active pins, remove those that are not used within the Boolean function and reconnect to GND - if (fan_in.size() > active_pins.size()) - { - for (const auto& ep : fan_in) - { - if (std::find(active_pins.begin(), active_pins.end(), ep->get_pin()->get_name()) == active_pins.end()) - { - GatePin* pin = ep->get_pin(); - if (!ep->get_net()->remove_destination(gate, pin)) - { - log_warning( - "netlist_preprocessing", "failed to remove unused input from LUT gate '{}' with ID {} from netlist with ID {}.", gate->get_name(), gate->get_id(), nl->get_id()); - continue; - } - if (!gnd_net->add_destination(gate, pin)) - { - log_warning( - "netlist_preprocessing", "failed to reconnect unused input of LUT gate '{}' with ID {} to GND in netlist with ID {}.", gate->get_name(), gate->get_id(), nl->get_id()); - continue; - } - num_eps++; - } - } - } - } - - log_info("netlist_preprocessing", "removed {} unused LUT endpoints from netlist with ID {}.", num_eps, nl->get_id()); - return OK(num_eps); - } - - Result NetlistPreprocessingPlugin::remove_buffers(Netlist* nl) - { - u32 num_gates = 0; - - std::queue gates_to_be_deleted; - - for (const auto& gate : nl->get_gates()) - { - std::vector fan_out = gate->get_fan_out_endpoints(); - - GateType* gt = gate->get_type(); - - // continue if of invalid base type - if (!gt->has_property(GateTypeProperty::combinational) || gt->has_property(GateTypeProperty::power) || gt->has_property(GateTypeProperty::ground)) - { - continue; - } - - // continue if more than one fan-out net - if (fan_out.size() != 1) - { - continue; - } - - // continue if more than one Boolean function - std::unordered_map functions = gate->get_boolean_functions(); - if (functions.size() != 1) - { - continue; - } - - // continue if Boolean function name does not match output pin - Endpoint* out_endpoint = *(fan_out.begin()); - if (out_endpoint->get_pin()->get_name() != (functions.begin())->first) - { - continue; - } - - std::vector fan_in = gate->get_fan_in_endpoints(); - BooleanFunction func = functions.begin()->second; - - // simplify Boolean function for constant 0 or 1 inputs (takes care of, e.g., an AND2 connected to an input and logic 1) - const auto substitute_res = BooleanFunctionDecorator(func).substitute_power_ground_pins(gate); - if (substitute_res.is_error()) - { - return ERR_APPEND(substitute_res.get_error(), - "Cannot replace buffers: failed to substitute pins with constants at gate " + gate->get_name() + " with ID " + std::to_string(gate->get_id())); - } - - func = substitute_res.get().simplify_local(); - - bool failed = false; - std::vector in_pins = gt->get_input_pin_names(); - if (func.is_variable() && std::find(in_pins.begin(), in_pins.end(), func.get_variable_name().get()) != in_pins.end()) - { - Net* out_net = out_endpoint->get_net(); - - // check all input endpoints and ... - for (Endpoint* in_endpoint : fan_in) - { - Net* in_net = in_endpoint->get_net(); - if (in_endpoint->get_pin()->get_name() == func.get_variable_name().get()) - { - // const auto merge_res = netlist_utils::merge_nets(nl, in_net, out_net, true); - const auto merge_res = NetlistModificationDecorator(*nl).connect_nets(out_net, in_net); - if (merge_res.is_error()) - { - log_warning("netlist_preprocessing", "{}", merge_res.get_error().get()); - failed = true; - } - } - else - { - // completely remove the input endpoint otherwise - if (!in_net->remove_destination(in_endpoint)) - { - log_warning("netlist_preprocessing", - "failed to remove destination from input net '{}' with ID {} of buffer gate '{}' with ID {} from netlist with ID {}.", - in_net->get_name(), - in_net->get_id(), - gate->get_name(), - gate->get_id(), - nl->get_id()); - failed = true; - } - } - - if (failed) - { - break; - } - } - - if (!failed) - { - gates_to_be_deleted.push(gate); - } - } - else if (func.is_constant() && (func.has_constant_value(0) || func.has_constant_value(1))) - { - auto* out_net = out_endpoint->get_net(); - - const auto& gnd_gates = nl->get_gnd_gates(); - const auto& vcc_gates = nl->get_vcc_gates(); - if (gnd_gates.empty() || vcc_gates.empty()) - { - continue; - } - auto* gnd_net = gnd_gates.front()->get_fan_out_nets().front(); - auto* vcc_net = vcc_gates.front()->get_fan_out_nets().front(); - - for (auto* in_endpoint : fan_in) - { - auto* in_net = in_endpoint->get_net(); - - // remove the input endpoint otherwise - if (!in_net->remove_destination(gate, in_endpoint->get_pin())) - { - log_warning("netlist_preprocessing", - "failed to remove destination from input net '{}' with ID {} of buffer gate '{}' with ID {} from netlist with ID {}.", - in_net->get_name(), - in_net->get_id(), - gate->get_name(), - gate->get_id(), - nl->get_id()); - failed = true; - break; - } - } - if (!failed && func.has_constant_value(0)) - { - for (auto* dst : out_net->get_destinations()) - { - auto* dst_gate = dst->get_gate(); - auto* dst_pin = dst->get_pin(); - if (!out_net->remove_destination(dst)) - { - log_warning("netlist_preprocessing", - "failed to remove destination from output net '{}' with ID {} of buffer gate '{}' with ID {} from netlist with ID {}.", - out_net->get_name(), - out_net->get_id(), - gate->get_name(), - gate->get_id(), - nl->get_id()); - failed = true; - break; - } - if (!gnd_net->add_destination(dst_gate, dst_pin)) - { - log_warning("netlist_preprocessing", - "failed to add buffer gate '{}' with ID {} as destination to GND net '{}' with ID {} in netlist with ID {}.", - gnd_net->get_name(), - gnd_net->get_id(), - gate->get_name(), - gate->get_id(), - nl->get_id()); - failed = true; - break; - } - } - } - else if (!failed && func.has_constant_value(1)) - { - for (Endpoint* dst : out_net->get_destinations()) - { - Gate* dst_gate = dst->get_gate(); - GatePin* dst_pin = dst->get_pin(); - if (!out_net->remove_destination(dst)) - { - log_warning("netlist_preprocessing", - "failed to remove destination from output net '{}' with ID {} of buffer gate '{}' with ID {} from netlist with ID {}.", - out_net->get_name(), - out_net->get_id(), - gate->get_name(), - gate->get_id(), - nl->get_id()); - failed = true; - break; - } - if (!vcc_net->add_destination(dst_gate, dst_pin)) - { - log_warning("netlist_preprocessing", - "failed to add buffer gate '{}' with ID {} as destination to VCC net '{}' with ID {} in netlist with ID {}.", - vcc_net->get_name(), - vcc_net->get_id(), - gate->get_name(), - gate->get_id(), - nl->get_id()); - failed = true; - break; - } - } - } - - // delete output net and buffer gate - if (!failed && !nl->delete_net(out_net)) - { - log_warning("netlist_preprocessing", - "failed to remove output net '{}' with ID {} of buffer gate '{}' with ID {} from netlist with ID {}.", - out_net->get_name(), - out_net->get_id(), - gate->get_name(), - gate->get_id(), - nl->get_id()); - continue; - } - if (!failed) - { - gates_to_be_deleted.push(gate); - } - } - } - - log_debug("netlist_preprocessing", "removing {} buffer gates...", gates_to_be_deleted.size()); - - while (!gates_to_be_deleted.empty()) - { - Gate* gate = gates_to_be_deleted.front(); - gates_to_be_deleted.pop(); - if (!nl->delete_gate(gate)) - { - log_warning("netlist_preprocessing", "failed to remove buffer gate '{}' with ID {} from netlist with ID {}.", gate->get_name(), gate->get_id(), nl->get_id()); - continue; - } - num_gates++; - } - - log_info("netlist_preprocessing", "removed {} buffer gates from netlist with ID {}.", num_gates, nl->get_id()); - return OK(num_gates); - } - - Result NetlistPreprocessingPlugin::remove_redundant_logic(Netlist* nl) - { - const auto& nets = nl->get_nets(); - auto nets_to_check = std::set(nets.begin(), nets.end()); - - u32 num_gates = 0; - while (!nets_to_check.empty()) - { - auto* current_net = *nets_to_check.begin(); - nets_to_check.erase(current_net); - - // only continue for nets with multiple destinations - if (current_net->get_num_of_destinations() <= 1) - { - nets_to_check.erase(current_net); - continue; - } - - std::set visited_gates; - std::vector> gates_to_delete; - auto destinations = current_net->get_destinations(); - for (u32 i = 0; i < destinations.size(); i++) - { - auto* master_destination = destinations.at(i); - auto* master_gate = master_destination->get_gate(); - - // check if we have already identified the current master gate as duplicate of some other gate - if (visited_gates.find(master_gate) != visited_gates.end()) - { - continue; - } - - // skip everything that is not combinational, a FF, or a latch - auto* master_type = master_gate->get_type(); - - // cache master input nets and endpoints - std::variant, std::map> master_inputs; - if (master_type->has_property(GateTypeProperty::combinational)) - { - // for combinational gates, the order of inputs will be considered by the final SMT check only (accounts for commutative Boolean functions) - auto tmp = master_gate->get_fan_in_nets(); - std::sort(tmp.begin(), tmp.end()); - master_inputs = std::move(tmp); - } - else if (master_type->has_property(GateTypeProperty::ff) || master_type->has_property(GateTypeProperty::latch)) - { - // for FF and latch gates, pins and nets must match exactly - std::map tmp; - for (auto& ep : master_gate->get_fan_in_endpoints()) - { - tmp[ep->get_pin()] = ep->get_net(); - } - master_inputs = std::move(tmp); - } - else - { - continue; - } - - // identify duplicate gates - std::vector duplicate_gates = {master_gate}; - for (u32 j = i + 1; j < destinations.size(); j++) - { - auto* current_destination = destinations.at(j); - auto* current_gate = current_destination->get_gate(); - - if (current_gate == master_gate) - { - continue; - } - - // check if we have already identified the current gate as duplicate of some other gate - if (visited_gates.find(current_gate) != visited_gates.end()) - { - continue; - } - - // check against master gate type - auto* current_type = current_gate->get_type(); - if (current_type != master_type) - { - continue; - } - - // check current inputs against master input nets - std::variant, std::map> current_inputs; - if (master_type->has_property(GateTypeProperty::combinational)) - { - // for combinational gates, the order of inputs will be considered by the final SMT check only (accounts for commutative Boolean functions) - auto tmp = current_gate->get_fan_in_nets(); - std::sort(tmp.begin(), tmp.end()); - current_inputs = std::move(tmp); - } - else if (master_type->has_property(GateTypeProperty::ff) || master_type->has_property(GateTypeProperty::latch)) - { - // for FF and latch gates, pins and nets must match exactly - std::map tmp; - for (auto& ep : current_gate->get_fan_in_endpoints()) - { - tmp[ep->get_pin()] = ep->get_net(); - } - current_inputs = std::move(tmp); - } - - if (current_inputs != master_inputs) - { - continue; - } - - if (master_type->has_property(GateTypeProperty::combinational)) - { - // SMT Boolean function equivalence check for combinational gates - bool skip_gate = false; - for (const auto* pin : master_type->get_output_pins()) - { - const auto solver_res = - master_gate->get_resolved_boolean_function(pin) - .map([pin, current_gate](BooleanFunction&& bf_master) { - return current_gate->get_resolved_boolean_function(pin).map( - [bf_master = std::move(bf_master)](BooleanFunction&& bf_current) mutable { return BooleanFunction::Eq(std::move(bf_master), std::move(bf_current), 1); }); - }) - .map([](auto&& bf_eq) { return BooleanFunction::Not(bf_eq.clone(), 1); }) - .map([](auto&& bf_not) -> Result { return SMT::Solver({SMT::Constraint(std::move(bf_not))}).query(SMT::QueryConfig()); }); - - if (solver_res.is_error() || !solver_res.get().is_unsat()) - { - skip_gate = true; - break; - } - } - - if (skip_gate) - { - continue; - } - } - - // gate is determined to be duplicate of other gate - duplicate_gates.push_back(current_gate); - visited_gates.insert(current_gate); - } - - // remove duplicate gates - if (duplicate_gates.size() > 1) - { - gates_to_delete.push_back(duplicate_gates); - } - } - - for (auto& duplicate_gates : gates_to_delete) - { - auto* surviver_gate = duplicate_gates.front(); - std::map out_pins_to_nets; - for (auto* ep : surviver_gate->get_fan_out_endpoints()) - { - out_pins_to_nets[ep->get_pin()] = ep->get_net(); - nets_to_check.insert(ep->get_net()); - } - - for (u32 k = 1; k < duplicate_gates.size(); k++) - { - auto* current_gate = duplicate_gates.at(k); - for (auto* ep : current_gate->get_fan_out_endpoints()) - { - auto* ep_net = ep->get_net(); - auto* ep_pin = ep->get_pin(); - - if (auto it = out_pins_to_nets.find(ep_pin); it != out_pins_to_nets.end()) - { - // surviver already has net connected to this output -> add destination to surviver's net - for (auto* dst : ep_net->get_destinations()) - { - auto* dst_gate = dst->get_gate(); - auto* dst_pin = dst->get_pin(); - dst->get_net()->remove_destination(dst); - it->second->add_destination(dst_gate, dst_pin); - } - if (!nl->delete_net(ep_net)) - { - log_warning("netlist_preprocessing", "could not delete net '{}' with ID {} from netlist with ID {}.", ep_net->get_name(), ep_net->get_id(), nl->get_id()); - } - nets_to_check.erase(ep_net); - } - else - { - // surviver does not feature net on this output pin -> connect this net to surviver - ep_net->add_source(surviver_gate, ep_pin); - out_pins_to_nets[ep_pin] = ep_net; - nets_to_check.insert(ep_net); - } - } - - if (!nl->delete_gate(current_gate)) - { - log_warning("netlist_preprocessing", "could not delete gate '{}' with ID {} from netlist with ID {}.", current_gate->get_name(), current_gate->get_id(), nl->get_id()); - } - else - { - num_gates++; - } - } - } - } - - log_info("netlist_preprocessing", "removed {} redundant logic gates from netlist with ID {}.", num_gates, nl->get_id()); - return OK(num_gates); - } - - Result NetlistPreprocessingPlugin::remove_unconnected_gates(Netlist* nl) - { - u32 num_gates = 0; - std::vector to_delete; - do - { - to_delete.clear(); - - for (const auto& g : nl->get_gates()) - { - bool is_unconnected = true; - for (const auto& on : g->get_fan_out_nets()) - { - if (!on->get_destinations().empty() || on->is_global_output_net()) - { - is_unconnected = false; - } - } - - if (is_unconnected) - { - to_delete.push_back(g); - } - } - - for (const auto& g : to_delete) - { - if (!nl->delete_gate(g)) - { - log_warning("netlist_preprocessing", "could not delete gate '{}' with ID {} from netlist with ID {}.", g->get_name(), g->get_id(), nl->get_id()); - } - else - { - num_gates++; - } - } - } while (!to_delete.empty()); - - log_info("netlist_preprocessing", "removed {} unconnected gates from netlist with ID {}.", num_gates, nl->get_id()); - return OK(num_gates); + return std::string("0.2"); } - Result NetlistPreprocessingPlugin::remove_unconnected_nets(Netlist* nl) + std::string NetlistPreprocessingPlugin::get_description() const { - u32 num_nets = 0; - - std::vector to_delete; - - for (const auto& n : nl->get_nets()) - { - if (!n->is_global_input_net() && n->get_sources().empty() && !n->is_global_output_net() && n->get_destinations().empty()) - { - to_delete.push_back(n); - } - } - - for (const auto& n : to_delete) - { - if (!nl->delete_net(n)) - { - log_warning("netlist_preprocessing", "could not delete net '{}' with ID {} from netlist with ID {}.", n->get_name(), n->get_id(), nl->get_id()); - } - else - { - num_nets++; - } - } - - log_info("netlist_preprocessing", "removed {} unconnected nets from netlist with ID {}.", num_nets, nl->get_id()); - return OK(num_nets); + return "A collection of tools to preprocess a netlist and prepare it for further analysis."; } - namespace + std::set NetlistPreprocessingPlugin::get_dependencies() const { - Result, std::vector>> - find_gate_type(const GateLibrary* gl, const std::set& properties, const u32 num_inputs, const u32 num_outputs) - { - const auto get_valid_input_pins = [](const GateType* gt) -> std::vector { - return gt->get_pins([](const GatePin* gp) { return (gp->get_direction() == PinDirection::input) && (gp->get_type() != PinType::power) && (gp->get_type() != PinType::ground); }); - }; - - const auto get_valid_output_pins = [](const GateType* gt) -> std::vector { - return gt->get_pins([](const GatePin* gp) { return (gp->get_direction() == PinDirection::output) && (gp->get_type() != PinType::power) && (gp->get_type() != PinType::ground); }); - }; - - // get types that match exactly with the properties and have the exact amount of input pins (excluding power pins) - const auto candidates = gl->get_gate_types([properties, num_inputs, get_valid_input_pins, num_outputs, get_valid_output_pins](const GateType* gt) { - return (gt->get_properties() == properties) && (get_valid_input_pins(gt).size() == num_inputs) && (get_valid_output_pins(gt).size() == num_outputs); - }); - - if (candidates.empty()) - { - return ERR("Unable to find gate type matching the description"); - } - - GateType* valid_gate_type = candidates.begin()->second; - - return OK({valid_gate_type, get_valid_input_pins(valid_gate_type), get_valid_output_pins(valid_gate_type)}); - } - - // TODO change this to return a netlist. This would allow saving the decomposition of a specifc gate type - Result build_gate_tree_from_boolean_function(Netlist* nl, const BooleanFunction& bf, const std::map& var_name_to_net, const Gate* org_gate = nullptr) - { - const auto create_gate_name = [](const Gate* new_gate, const Gate* original_gate) -> std::string { - const std::string new_name = (original_gate == nullptr) ? "new_gate_" : original_gate->get_name() + "_decomposed_"; - return new_name + std::to_string(new_gate->get_id()); - }; - - const auto create_net_name = [](const Net* new_net, const Gate* original_gate) -> std::string { - const std::string new_name = (original_gate == nullptr) ? "new_net_" : original_gate->get_name() + "_decomposed_"; - return new_name + std::to_string(new_net->get_id()); - }; - - if (bf.is_empty()) - { - return ERR("cannot build gate tree for Boolean function: Boolean function is empty"); - } - - if (bf.is_index()) - { - return ERR("cannot build gate tree for Boolean function: Boolean function is of type index"); - } - - if (bf.size() != 1) - { - return ERR("cannot build gate tree for Boolean function: Boolean function if of size " + std::to_string(bf.size()) + " but we only handle size 1"); - } - - if (bf.is_constant()) - { - if (bf.has_constant_value(0)) - { - static Net* zero = nl->get_nets([](const Net* n) { return n->is_gnd_net(); }).front(); - return OK(zero); - } - - if (bf.has_constant_value(1)) - { - static Net* one = nl->get_nets([](const Net* n) { return n->is_vcc_net(); }).front(); - return OK(one); - } - } - - if (bf.is_variable()) - { - if (const auto it = var_name_to_net.find(bf.get_variable_name().get()); it == var_name_to_net.end()) - { - return ERR("Cannot build gate tree for Boolean function: Found variable " + bf.get_variable_name().get() + " with no corresponding net provided."); - } - else - { - return OK(it->second); - } - } - - if (!bf.get_top_level_node().is_operation()) - { - return ERR("Cannot build gate tree for Boolean function: cannot handle node type of top level node " + bf.get_top_level_node().to_string()); - } - - const auto operation = bf.get_top_level_node().type; - const auto parameters = bf.get_parameters(); - - // TODO put this into a function that only searches for the gate types when they are actually needed - static const auto inv_type_res = find_gate_type(nl->get_gate_library(), {GateTypeProperty::combinational, GateTypeProperty::c_inverter}, 1, 1); - static const auto and_type_res = find_gate_type(nl->get_gate_library(), {GateTypeProperty::combinational, GateTypeProperty::c_and}, 2, 1); - static const auto or_type_res = find_gate_type(nl->get_gate_library(), {GateTypeProperty::combinational, GateTypeProperty::c_or}, 2, 1); - static const auto xor_type_res = find_gate_type(nl->get_gate_library(), {GateTypeProperty::combinational, GateTypeProperty::c_xor}, 2, 1); - - if (inv_type_res.is_error()) - { - return ERR("Cannot build gate tree for Boolean function: failed to find valid inverter gate type"); - } - - if (and_type_res.is_error()) - { - return ERR("Cannot build gate tree for Boolean function: failed to find valid and gate type"); - } - - if (or_type_res.is_error()) - { - return ERR("Cannot build gate tree for Boolean function: failed to find valid or gate type"); - } - - if (xor_type_res.is_error()) - { - return ERR("Cannot build gate tree for Boolean function: failed to find valid xor gate type"); - } - - const std::map, std::vector>> node_type_to_gate_type = { - {BooleanFunction::NodeType::Not, inv_type_res.get()}, - {BooleanFunction::NodeType::And, and_type_res.get()}, - {BooleanFunction::NodeType::Or, or_type_res.get()}, - {BooleanFunction::NodeType::Xor, xor_type_res.get()}, - }; - - std::vector parameter_nets; - for (const auto& p : parameters) - { - const auto tree_res = build_gate_tree_from_boolean_function(nl, p, var_name_to_net, org_gate); - if (tree_res.is_error()) - { - return ERR_APPEND(tree_res.get_error(), "Cannot build gate tree for Boolean function: failed to do so for sub tree"); - } - parameter_nets.push_back(tree_res.get()); - } - - Gate* new_gate = nullptr; - Net* output_net = nl->create_net("__TEMP_NET_NAME__DECOMPOSED__"); - output_net->set_name(create_net_name(output_net, org_gate)); - - switch (operation) - { - case BooleanFunction::NodeType::Not: - case BooleanFunction::NodeType::And: - case BooleanFunction::NodeType::Or: - case BooleanFunction::NodeType::Xor: { - auto [gt, in_pins, out_pins] = node_type_to_gate_type.at(operation); - new_gate = nl->create_gate(gt, "__TEMP_GATE_NAME__DECOMPOSED__"); - for (u32 idx = 0; idx < parameter_nets.size(); idx++) - { - parameter_nets.at(idx)->add_destination(new_gate, in_pins.at(idx)); - } - output_net->add_source(new_gate, out_pins.front()); - break; - } - default: - break; - } - - if (new_gate == nullptr) - { - return ERR("Cannot build gate tree for Boolean function: failed to create gate for operation " + bf.get_top_level_node().to_string()); - } - - new_gate->set_name(create_gate_name(new_gate, org_gate)); - - if (org_gate != nullptr && !org_gate->get_module()->is_top_module()) - { - org_gate->get_module()->assign_gate(new_gate); - } - - return OK(output_net); - } - } // namespace - - Result NetlistPreprocessingPlugin::decompose_gate(Netlist* nl, Gate* g, const bool delete_gate) - { - // build Boolean function for each output pin of the gate - std::map output_pin_name_to_bf; - for (const auto& out_ep : g->get_fan_out_endpoints()) - { - const auto bf_res = g->get_resolved_boolean_function(out_ep->get_pin()); - if (bf_res.is_error()) - { - return ERR_APPEND(bf_res.get_error(), - "unable to decompose gate " + g->get_name() + " with ID " + std::to_string(g->get_id()) + ": failed to resolve Boolean function for pin " - + out_ep->get_pin()->get_name()); - } - output_pin_name_to_bf.insert({out_ep->get_pin()->get_name(), bf_res.get()}); - } - - // map which variables in the Boolean function belong to which net - std::map var_name_to_net; - for (const auto& in_ep : g->get_fan_in_endpoints()) - { - var_name_to_net.insert({BooleanFunctionNetDecorator(*(in_ep->get_net())).get_boolean_variable_name(), in_ep->get_net()}); - } - - // build gate tree for each output function and merge the tree output net with the origianl output net - for (const auto& [pin_name, bf] : output_pin_name_to_bf) - { - Net* output_net = g->get_fan_out_net(pin_name); - if (output_net == nullptr) - { - continue; - } - - const auto tree_res = build_gate_tree_from_boolean_function(nl, bf, var_name_to_net, g); - if (tree_res.is_error()) - { - return ERR_APPEND(tree_res.get_error(), - "unable to decompose gate " + g->get_name() + " with ID " + std::to_string(g->get_id()) + ": failed to build gate tree for output net at pin " + pin_name); - } - - auto new_output_net = tree_res.get(); - - const auto slave_net = new_output_net->is_global_input_net() ? output_net : new_output_net; - const auto master_net = new_output_net->is_global_input_net() ? new_output_net : output_net; - const auto merge_res = NetlistModificationDecorator(*nl).connect_nets(master_net, slave_net); - // const auto merge_res = netlist_utils::merge_nets(nl, new_output_net, output_net, new_output_net->is_global_input_net()); - if (merge_res.is_error()) - { - return ERR_APPEND(merge_res.get_error(), - "unable to decompose gate " + g->get_name() + " with ID " + std::to_string(g->get_id()) + ": failed to merge newly created output net with already existing one."); - } - } - - if (delete_gate) - { - if (!nl->delete_gate(g)) - { - return ERR("unable to decompose gate " + g->get_name() + " with ID " + std::to_string(g->get_id()) + ": failed to delete original gate."); - } - } - - return OK({}); - } - - Result NetlistPreprocessingPlugin::decompose_gates_of_type(Netlist* nl, const std::vector& gate_types) - { - u32 counter = 0; - for (const auto& gt : gate_types) - { - std::vector to_delete; - for (const auto& g : nl->get_gates([gt](const Gate* g) { return g->get_type() == gt; })) - { - const auto decompose_res = decompose_gate(nl, g, false); - if (decompose_res.is_error()) - { - return ERR_APPEND(decompose_res.get_error(), - "unable to decompose gates of type " + gt->get_name() + ": failed for gate " + g->get_name() + " with ID " + std::to_string(g->get_id())); - } - to_delete.push_back(g); - } - - for (const auto& g : to_delete) - { - counter += 1; - if (!nl->delete_gate(g)) - { - return ERR("unable to decompose gates of type " + gt->get_name() + ": failed to delete gate " + g->get_name() + " with ID " + std::to_string(g->get_id())); - } - } - } - - return OK(counter); - } - - namespace - { - // TODO add resynthesis with ABC (passing a gate level netlist and passing only the boolean functions) - /* - std::string build_combinational_verilog_module_from(const std::unordered_map& bfs) - { - std::unordered_set input_variable_names; - - for (const auto& [name, bf] : bfs) - { - for (const auto& var_name : bf.get_variable_names()) - { - input_variable_names.insert(var_name); - } - } - - std::string verilog_str = "module top ("; - - std::string var_str = ""; - std::string io_str = ""; - std::string function_str = ""; - - for (const auto& input_var : input_variable_names) - { - var_str += (input_var + ", "); - io_str += ("input " + input_var + ";\n"); - } - - for (const auto& [output_var, bf] : bfs) - { - var_str += (output_var + ", "); - io_str += ("output " + output_var + ";\n"); - function_str += ("assign " + output_var + " = " + bf.to_string() + ";\n"); - } - - var_str = var_str.substr(0, var_str.size() - 2); - - verilog_str += var_str; - verilog_str += ");\n"; - - verilog_str += io_str; - - verilog_str += "\n"; - - verilog_str += function_str; - - verilog_str += "\n"; - verilog_str += "endmodule\n"; - verilog_str += "\n"; - - return verilog_str; - } - - void resynthesize_boolean_functions_with_abc(const std::unordered_map& bfs, GateLibrary* gl, const bool optimize_area) - { - const auto verilog_module = build_combinational_verilog_module_from(bfs); - - const std::filesystem::path base_path = std::filesystem::temp_directory_path() / "resynthesize_boolean_functions_with_abc"; - const std::filesystem::path functional_netlist_path = base_path / "func_netlist.v"; - const std::filesystem::path resynthesized_netlist_path = base_path / "resynth_netlist.v"; - const std::filesystem::path gate_library_path = base_path / "new_gate_library.genlib"; - - log_info("netlist_preprocessing", "Writing Verilog file to {} ...", functional_netlist_path.string()); - - std::ofstream out(functional_netlist_path); - out << verilog_module; - out.close(); - - log_info("netlist_preprocessing", "Writing gatelibrary to file {} ...", gate_library_path.string()); - - gate_library_manager::save(gate_library_path, gl); - - - // const std::string command_corpus = R"#( - // read_verilog {}; - // read_library {}; - // cleanup; - // sweep; - // strash; - // dc2; - // logic; - // map -a; - // write_verilog {}; - // )#"; - // const std::string command = std::format(command_corpus, functional_netlist_path, gate_library_path, resythesized_netlist_path); - - - const std::string command = "read_verilog " + functional_netlist_path.string() + "; read_library " + gate_library_path.string() + "; cleanup; sweep; strash; dc2; logic; map" - + (optimize_area ? "" : " -a") + "; write_verilog " + resynthesized_netlist_path.string() + ";"; - - return; - } - - void resynthesize_boolean_function_with_abc(const BooleanFunction& bf, const bool optimize_area) - { - return; - } - - void resynthesize_subgraph_with_abc(const Netlist* nl, const std::vector subgraph, const bool optimize_area) - { - return; - } - */ - } // namespace - - namespace - { - struct indexed_identifier - { - indexed_identifier(const std::string& p_identifier, const u32 p_index, const std::string& p_origin) : identifier{p_identifier}, index{p_index}, origin{p_origin} - { - } - - std::string identifier; - u32 index; - std::string origin; - }; - - // TODO when the verilog parser changes are merged into the master this will no longer be needed - const std::string hal_instance_index_pattern = "__\\[(\\d+)\\]__"; - const std::string hal_instance_index_pattern_reverse = "(\\d+)"; - - std::string replace_hal_instance_index(const std::string& name) - { - std::regex re(hal_instance_index_pattern); - - std::string input = name; - std::string index; - std::smatch match; - while (std::regex_search(input, match, re)) - { - index = match[1]; - input = utils::replace(input, match.str(), "" + index + ""); - } - - return input; - } - - std::string reconstruct_hal_instance_index(const std::string& name) - { - std::regex re(hal_instance_index_pattern_reverse); - - std::string input = name; - std::string index; - std::smatch match; - while (std::regex_search(input, match, re)) - { - index = match[1]; - input = utils::replace(input, match.str(), "__[" + index + "]__"); - } - - return input; - } - - const std::string net_index_pattern = "\\((\\d+)\\)"; - const std::string gate_index_pattern = "\\[(\\d+)\\]"; - - // Extracts an index from a string by taking the last integer enclosed by parentheses - std::optional extract_index(const std::string& name, const std::string& index_pattern, const std::string& origin) - { - std::regex re(index_pattern); - - std::string input = name; - std::optional last_match; - std::optional last_index; - - // Search for last match within string - std::smatch match; - while (std::regex_search(input, match, re)) - { - // Capture integer and update input string to search from after the match - last_index = std::stoi(match[1]); - last_match = match.str(); - input = match.suffix().str(); - } - - if (!last_index.has_value()) - { - return std::nullopt; - } - - const auto found_match = last_match.value(); - auto identifier_name = name; - identifier_name = identifier_name.replace(name.rfind(found_match), found_match.size(), ""); - - return std::optional{{identifier_name, last_index.value(), origin}}; - } - - // annotate all found identifiers to a gate - bool annotate_indexed_identifiers(Gate* gate, const std::vector& identifiers) - { - std::string json_identifier_str = - "[" + utils::join(", ", identifiers, [](const auto& i) { return std::string("[") + '"' + i.identifier + '"' + ", " + std::to_string(i.index) + ", " + '"' + i.origin + '"' + "]"; }) - + "]"; - - return gate->set_data("preprocessing_information", "multi_bit_indexed_identifiers", "string", json_identifier_str); - } - - // search for a net that connects to the gate at a pin of a specific type and tries to reconstruct an indexed identifier from its name or form a name of its merged wires - std::vector check_net_at_pin(const PinType pin_type, Gate* gate) - { - const auto typed_pins = gate->get_type()->get_pins([pin_type](const auto p) { return p->get_type() == pin_type; }); - - std::vector found_identfiers; - - for (const auto& pin : typed_pins) - { - const auto typed_net = (pin->get_direction() == PinDirection::output) ? gate->get_fan_out_net(pin) : gate->get_fan_in_net(pin); - - // 1) search the net name itself - const auto net_name_index = extract_index(typed_net->get_name(), net_index_pattern, "net_name"); - if (net_name_index.has_value()) - { - found_identfiers.push_back(net_name_index.value()); - } - - // 2) search all the names of the wires that where merged into this net - if (!typed_net->has_data("parser_annotation", "merged_nets")) - { - continue; - } - - const auto all_merged_nets_str = std::get<1>(typed_net->get_data("parser_annotation", "merged_nets")); - - if (all_merged_nets_str.empty()) - { - continue; - } - - // parse json list of merged net names - rapidjson::Document doc; - doc.Parse(all_merged_nets_str.c_str()); - - for (u32 i = 0; i < doc.GetArray().Size(); i++) - { - const auto list = doc[i].GetArray(); - for (u32 j = 0; j < list.Size(); j++) - { - const auto merged_wire_name = list[j].GetString(); - - const auto merged_wire_name_index = extract_index(merged_wire_name, net_index_pattern, "net_name"); - if (merged_wire_name_index.has_value()) - { - found_identfiers.push_back(merged_wire_name_index.value()); - } - } - } - } - - return found_identfiers; - } - } // namespace - - Result NetlistPreprocessingPlugin::reconstruct_indexed_ff_identifiers(Netlist* nl) - { - u32 counter = 0; - for (auto& ff : nl->get_gates([](const auto g) { return g->get_type()->has_property(GateTypeProperty::ff); })) - { - std::vector all_identifiers; - - // 1) Check whether the ff gate already has an index annotated in its gate name - const auto cleaned_gate_name = replace_hal_instance_index(ff->get_name()); - const auto gate_name_index = extract_index(cleaned_gate_name, gate_index_pattern, "gate_name"); - - if (gate_name_index.has_value()) - { - auto found_identifier = gate_name_index.value(); - found_identifier.identifier = reconstruct_hal_instance_index(found_identifier.identifier); - all_identifiers.push_back(found_identifier); - } - - static const std::vector relevant_pin_types = {PinType::state, PinType::neg_state, PinType::data}; - - // 2) Check all relevant pin_types - for (const auto& pt : relevant_pin_types) - { - const auto found_identifiers = check_net_at_pin(pt, ff); - all_identifiers.insert(all_identifiers.end(), found_identifiers.begin(), found_identifiers.end()); - } - - if (!all_identifiers.empty()) - { - counter++; - } - - annotate_indexed_identifiers(ff, all_identifiers); - } - - return OK(counter); - } - - namespace - { - struct ComponentData - { - std::string name; - std::string type; - u64 x; - u64 y; - }; - - TokenStream tokenize(std::stringstream& ss) - { - const std::string delimiters = " ;-"; - std::string current_token; - u32 line_number = 0; - - std::string line; - bool escaped = false; - - std::vector> parsed_tokens; - while (std::getline(ss, line)) - { - line_number++; - - for (char c : line) - { - // deal with escaping and strings - if (c == '\\') - { - escaped = true; - continue; - } - else if (escaped && std::isspace(c)) - { - escaped = false; - continue; - } - - if (((!std::isspace(c) && delimiters.find(c) == std::string::npos) || escaped)) - { - current_token += c; - } - else - { - if (!current_token.empty()) - { - parsed_tokens.emplace_back(line_number, current_token); - current_token.clear(); - } - - if (!std::isspace(c)) - { - parsed_tokens.emplace_back(line_number, std::string(1, c)); - } - } - } - - if (!current_token.empty()) - { - parsed_tokens.emplace_back(line_number, current_token); - current_token.clear(); - } - } - - return TokenStream(parsed_tokens, {}, {}); - } - - Result> parse_tokens(TokenStream& ts) - { - ts.consume_until("COMPONENTS"); - ts.consume("COMPONENTS"); - const auto component_count_str = ts.consume().string; - ts.consume(";"); - - u32 component_count; - if (const auto res = utils::wrapped_stoul(component_count_str); res.is_ok()) - { - component_count = res.get(); - } - else - { - return ERR_APPEND(res.get_error(), "could not parse tokens: failed to read component count from token" + component_count_str); - } - - std::cout << "Component count: " << component_count << std::endl; - - std::unordered_map component_data; - for (u32 c_idx = 0; c_idx < component_count; c_idx++) - { - // parse a line - ComponentData new_data_entry; - ts.consume("-"); - new_data_entry.name = ts.consume().string; - new_data_entry.type = ts.consume().string; - ts.consume("+"); - ts.consume("SOURCE"); - ts.consume("DIST"); - ts.consume("TIMING"); - ts.consume("+"); - ts.consume("PLACED"); - ts.consume("FIXED"); - ts.consume("("); - - if (const auto res = utils::wrapped_stoull(ts.consume().string); res.is_ok()) - { - new_data_entry.x = res.get(); - } - else - { - return ERR_APPEND(res.get_error(), "could not parse tokens: failed to read x coordinate from token"); - } - - if (const auto res = utils::wrapped_stoull(ts.consume().string); res.is_ok()) - { - new_data_entry.y = res.get(); - } - else - { - return ERR_APPEND(res.get_error(), "could not parse tokens: failed to read y coordinate from token"); - } - - ts.consume(")"); - - ts.consume_current_line(); - - component_data.insert({new_data_entry.name, new_data_entry}); - } - - return OK(component_data); - } - } // namespace - - Result NetlistPreprocessingPlugin::parse_def_file(Netlist* nl, const std::filesystem::path& def_file) - { - std::stringstream ss; - std::ifstream ifs; - ifs.open(def_file.string(), std::ifstream::in); - if (!ifs.is_open()) - { - return ERR("could not parse DEF (Design Exchange Format) file '" + def_file.string() + "' : unable to open file"); - } - ss << ifs.rdbuf(); - ifs.close(); - - auto ts = tokenize(ss); - - std::unordered_map component_data; - // parse tokens - try - { - if (auto res = parse_tokens(ts); res.is_error()) - { - return ERR_APPEND(res.get_error(), "could not parse Design Exchange Format file '" + def_file.string() + "': unable to parse tokens"); - } - else - { - component_data = res.get(); - } - } - catch (TokenStream::TokenStreamException& e) - { - if (e.line_number != (u32)-1) - { - return ERR("could not parse Design Exchange Format file '" + def_file.string() + "': " + e.message + " (line " + std::to_string(e.line_number) + ")"); - } - else - { - return ERR("could not parse Design Exchange Format file '" + def_file.string() + "': " + e.message); - } - } - - std::unordered_map name_to_gate; - for (auto g : nl->get_gates()) - { - name_to_gate.insert({g->get_name(), g}); - } - - u32 counter = 0; - for (const auto& [gate_name, data] : component_data) - { - if (const auto& g_it = name_to_gate.find(gate_name); g_it != name_to_gate.end()) - { - // TODO figure out whereever we are saving coordinates now... - g_it->second->set_location_x(data.x); - g_it->second->set_location_y(data.y); - - counter++; - } - } - - log_info("netlist_preprocessing", "reconstructed coordinates for {} / {} ({:.2}) gates", counter, nl->get_gates().size(), (double)counter / (double)nl->get_gates().size()); - - return OK({}); - } - - Result NetlistPreprocessingPlugin::unify_ff_outputs(Netlist* nl, const std::vector& ffs, GateType* inverter_type) - { - if (nl == nullptr) - { - return ERR("netlist is a nullptr"); - } - - if (inverter_type == nullptr) - { - const auto* gl = nl->get_gate_library(); - const auto inv_types = - gl->get_gate_types([](const GateType* gt) { return gt->has_property(GateTypeProperty::c_inverter) && gt->get_input_pins().size() == 1 && gt->get_output_pins().size() == 1; }); - if (inv_types.empty()) - { - return ERR("gate library '" + gl->get_name() + "' of netlist does not contain an inverter gate"); - } - inverter_type = inv_types.begin()->second; - } - else - { - if (inverter_type->get_gate_library() != nl->get_gate_library()) - { - return ERR("inverter gate type '" + inverter_type->get_name() + "' of gate library '" + inverter_type->get_gate_library()->get_name() + "' does not belong to gate library '" - + nl->get_gate_library()->get_name() + "' of provided netlist"); - } - - if (!inverter_type->has_property(GateTypeProperty::c_inverter)) - { - return ERR("gate type '" + inverter_type->get_name() + "' of gate library '" + inverter_type->get_gate_library()->get_name() + "' is not an inverter gate type"); - } - - if (inverter_type->get_input_pins().size() != 1 || inverter_type->get_output_pins().size() != 1) - { - return ERR("inverter gate type '" + inverter_type->get_name() + "' of gate library '" + inverter_type->get_gate_library()->get_name() - + "' has an invalid number of input pins or output pins"); - } - } - - auto inv_in_pin = inverter_type->get_input_pins().front(); - auto inv_out_pin = inverter_type->get_output_pins().front(); - - u32 ctr = 0; - - const std::vector& gates = ffs.empty() ? nl->get_gates() : ffs; - - for (auto* ff : gates) - { - auto* ff_type = ff->get_type(); - - if (!ff_type->has_property(GateTypeProperty::ff)) - { - continue; - } - - GatePin* state_pin = nullptr; - GatePin* neg_state_pin = nullptr; - - for (auto* o_pin : ff_type->get_output_pins()) - { - if (o_pin->get_type() == PinType::state) - { - state_pin = o_pin; - } - else if (o_pin->get_type() == PinType::neg_state) - { - neg_state_pin = o_pin; - } - } - - if (state_pin == nullptr || neg_state_pin == nullptr) - { - continue; - } - - auto* neg_state_ep = ff->get_fan_out_endpoint(neg_state_pin); - if (neg_state_ep == nullptr) - { - continue; - } - auto* neg_state_net = neg_state_ep->get_net(); - - auto state_net = ff->get_fan_out_net(state_pin); - if (state_net == nullptr) - { - state_net = nl->create_net(ff->get_name() + "__STATE_NET__"); - state_net->add_source(ff, state_pin); - } - - auto* inv = nl->create_gate(inverter_type, ff->get_name() + "__NEG_STATE_INVERT__"); - state_net->add_destination(inv, inv_in_pin); - neg_state_net->remove_source(neg_state_ep); - neg_state_net->add_source(inv, inv_out_pin); - ctr++; - } - - return OK(ctr); + std::set retval; + // retval.insert("graph_algorithm"); + return retval; } } // namespace hal From c9ff3c46931798bf023c4e914e47415f4b4212bb Mon Sep 17 00:00:00 2001 From: "julian.speith" Date: Tue, 16 Jul 2024 15:31:21 +0200 Subject: [PATCH 2/3] I am ashamed of this, but stuff has to work soon; I really hope we clean this up later --- plugins/netlist_preprocessing/CMakeLists.txt | 2 +- .../src/netlist_preprocessing.cpp | 8 +- .../src/plugin_netlist_preprocessing.cpp | 2 +- .../test/netlist_preprocessing.cpp | 16 +- .../include/resynthesis/resynthesis.h | 14 + plugins/resynthesis/src/resynthesis.cpp | 418 +++++++++--------- 6 files changed, 238 insertions(+), 222 deletions(-) diff --git a/plugins/netlist_preprocessing/CMakeLists.txt b/plugins/netlist_preprocessing/CMakeLists.txt index 9e379c43ff5..e7ffadee809 100644 --- a/plugins/netlist_preprocessing/CMakeLists.txt +++ b/plugins/netlist_preprocessing/CMakeLists.txt @@ -9,8 +9,8 @@ if(PL_NETLIST_PREPROCESSING OR BUILD_ALL_PLUGINS) SHARED HEADER ${NETLIST_PREPROCESSING_INC} SOURCES ${NETLIST_PREPROCESSING_SRC} ${NETLIST_PREPROCESSING_PYTHON_SRC} - LINK_LIBRARIES PUBLIC ${Z3_LIBRARIES} z3_utils OpenMP::OpenMP_CXX PYDOC SPHINX_DOC_INDEX_FILE ${CMAKE_CURRENT_SOURCE_DIR}/documentation/netlist_preprocessing.rst + LINK_LIBRARIES PUBLIC ${Z3_LIBRARIES} z3_utils resynthesis OpenMP::OpenMP_CXX nlohmann_json::nlohmann_json ) add_subdirectory(test) diff --git a/plugins/netlist_preprocessing/src/netlist_preprocessing.cpp b/plugins/netlist_preprocessing/src/netlist_preprocessing.cpp index a0d4237df78..f78892c196e 100644 --- a/plugins/netlist_preprocessing/src/netlist_preprocessing.cpp +++ b/plugins/netlist_preprocessing/src/netlist_preprocessing.cpp @@ -11,8 +11,10 @@ #include "hal_core/netlist/net.h" #include "hal_core/netlist/netlist.h" #include "hal_core/utilities/token_stream.h" +#include "nlohmann_json/json.hpp" #include "rapidjson/document.h" -#include "z3_utils.h" +#include "resynthesis/resynthesis.h" +#include "z3_utils/netlist_comparison.h" #include #include @@ -1347,7 +1349,7 @@ namespace hal bfs.insert({ep->get_pin()->get_name(), std::move(bf)}); } - auto resynth_res = generate_resynth_netlist_for_boolean_functions(bfs, genlib_path, mux_inv_gl, true); + auto resynth_res = resynthesis::generate_resynth_netlist_for_boolean_functions(bfs, genlib_path, mux_inv_gl, true); if (resynth_res.is_error()) { return ERR_APPEND(resynth_res.get_error(), "unable to unify select signals of muxes: failed to resynthesize mux subgraph to netlist"); @@ -1383,7 +1385,7 @@ namespace hal global_io_mapping[pin->get_net()].push_back(net); } - auto replace_res = replace_subgraph_with_netlist(subgraph, global_io_mapping, resynth_nl, nl, false); + auto replace_res = resynthesis::replace_subgraph_with_netlist(subgraph, global_io_mapping, resynth_nl, nl, false); if (replace_res.is_error()) { return ERR("unable to unify muxes select signals: failed to replace mux subgraph with resynthesized netlist"); diff --git a/plugins/netlist_preprocessing/src/plugin_netlist_preprocessing.cpp b/plugins/netlist_preprocessing/src/plugin_netlist_preprocessing.cpp index 5a5ca7e137a..eadeaa5c035 100644 --- a/plugins/netlist_preprocessing/src/plugin_netlist_preprocessing.cpp +++ b/plugins/netlist_preprocessing/src/plugin_netlist_preprocessing.cpp @@ -25,7 +25,7 @@ namespace hal std::set NetlistPreprocessingPlugin::get_dependencies() const { std::set retval; - // retval.insert("graph_algorithm"); + retval.insert("resynthesis"); return retval; } } // namespace hal diff --git a/plugins/netlist_preprocessing/test/netlist_preprocessing.cpp b/plugins/netlist_preprocessing/test/netlist_preprocessing.cpp index 7b1fb73a191..4d55250f8ec 100644 --- a/plugins/netlist_preprocessing/test/netlist_preprocessing.cpp +++ b/plugins/netlist_preprocessing/test/netlist_preprocessing.cpp @@ -1,4 +1,4 @@ -#include "netlist_preprocessing/plugin_netlist_preprocessing.h" +#include "netlist_preprocessing/netlist_preprocessing.h" #include "netlist_test_utils.h" #include "gate_library_test_utils.h" @@ -70,7 +70,7 @@ namespace hal { EXPECT_EQ(l5->get_predecessor("I2")->get_gate(), l2); EXPECT_EQ(l5->get_predecessor("I3")->get_gate(), l3); - auto res = NetlistPreprocessingPlugin::remove_unused_lut_inputs(nl.get()); + auto res = netlist_preprocessing::remove_unused_lut_inputs(nl.get()); ASSERT_TRUE(res.is_ok()); EXPECT_EQ(res.get(), 3); @@ -129,7 +129,7 @@ namespace hal { Net* n3 = test_utils::connect(nl.get(), g0, "O", g1, "I"); Net* n4 = test_utils::connect(nl.get(), g1, "O", g2, "I0"); - auto res = NetlistPreprocessingPlugin::remove_buffers(nl.get()); + auto res = netlist_preprocessing::remove_buffers(nl.get()); ASSERT_TRUE(res.is_ok()); EXPECT_EQ(res.get(), 1); @@ -175,7 +175,7 @@ namespace hal { Net* n3 = test_utils::connect(nl.get(), g0, "O", g1, "I1"); Net* n4 = test_utils::connect(nl.get(), g1, "O", g2, "I0"); - auto res = NetlistPreprocessingPlugin::remove_buffers(nl.get()); + auto res = netlist_preprocessing::remove_buffers(nl.get()); ASSERT_TRUE(res.is_ok()); EXPECT_EQ(res.get(), 1); @@ -220,7 +220,7 @@ namespace hal { Net* n3 = test_utils::connect(nl.get(), g0, "O", g1, "I1"); Net* n4 = test_utils::connect(nl.get(), g1, "O", g2, "I0"); - auto res = NetlistPreprocessingPlugin::remove_buffers(nl.get()); + auto res = netlist_preprocessing::remove_buffers(nl.get()); ASSERT_TRUE(res.is_ok()); EXPECT_EQ(res.get(), 1); @@ -236,9 +236,9 @@ namespace hal { /** * Test the deletion of redundant logic gates. * - * Functions: remove_redundant_logic + * Functions: remove_redundant_gates */ - TEST_F(NetlistPreprocessingTest, check_remove_redundant_logic) + TEST_F(NetlistPreprocessingTest, check_remove_redundant_gates) { TEST_START { @@ -274,7 +274,7 @@ namespace hal { Net* n5 = test_utils::connect(nl.get(), g2, "Q", g4, "I0"); Net* n6 = test_utils::connect(nl.get(), g3, "QN", g4, "I1"); - auto res = NetlistPreprocessingPlugin::remove_redundant_logic(nl.get()); + auto res = netlist_preprocessing::remove_redundant_gates(nl.get()); ASSERT_TRUE(res.is_ok()); EXPECT_EQ(res.get(), 2); diff --git a/plugins/resynthesis/include/resynthesis/resynthesis.h b/plugins/resynthesis/include/resynthesis/resynthesis.h index f49597ea254..bd0f9a4d526 100644 --- a/plugins/resynthesis/include/resynthesis/resynthesis.h +++ b/plugins/resynthesis/include/resynthesis/resynthesis.h @@ -32,12 +32,14 @@ #pragma once #include "hal_core/defines.h" +#include "hal_core/netlist/boolean_function.h" #include "hal_core/utilities/result.h" namespace hal { class Netlist; class Gate; + class Net; class GateType; class GateLibrary; @@ -147,5 +149,17 @@ namespace hal * @return OK() and the number of re-synthesized gates on success, an error otherwise. */ Result resynthesize_subgraph_of_type(Netlist* nl, const std::vector& gate_types, GateLibrary* target_gl); + + // TODO functions below wil be removed/replaced soon + Result replace_subgraph_with_netlist(const std::vector& subgraph, + const std::unordered_map>& global_io_mapping, + const Netlist* src_nl, + Netlist* dst_nl, + const bool delete_subgraph_gates); + + Result> generate_resynth_netlist_for_boolean_functions(const std::unordered_map& bfs, + const std::filesystem::path& genlib_path, + GateLibrary* target_gl, + const bool optimize_area); } // namespace resynthesis } // namespace hal diff --git a/plugins/resynthesis/src/resynthesis.cpp b/plugins/resynthesis/src/resynthesis.cpp index 345bfddb358..52e76f8c7e7 100644 --- a/plugins/resynthesis/src/resynthesis.cpp +++ b/plugins/resynthesis/src/resynthesis.cpp @@ -222,157 +222,6 @@ namespace hal return OK({}); } - // NOTE there are about a hundred more checks that we could do here - Result replace_subgraph_with_netlist(const std::vector& subgraph, - const std::unordered_map>& global_io_mapping, - const Netlist* src_nl, - Netlist* dst_nl, - const bool delete_subgraph_gates) - { - std::unordered_map gate_name_to_gate; - - const auto dst_gl = dst_nl->get_gate_library(); - - // add all gates of the source netlist to the destination netlist - for (const auto src_g : src_nl->get_gates()) - { - const auto src_gt = src_g->get_type(); - const auto dst_gt = dst_gl->get_gate_type_by_name(src_gt->get_name()); - if (!dst_gt) - { - return ERR("unable to replace subgraph with netlist: gate library '" + dst_gl->get_name() + "' does not contain the required gate type " + src_gt->get_name()); - } - - auto new_gate = dst_nl->create_gate(dst_gt, "TEMP"); - const std::string new_name = new_gate_name(new_gate, src_g); - new_gate->set_name(new_name); - - gate_name_to_gate.insert({src_g->get_name(), new_gate}); - } - - // connect all nets of the source netlist to the destination netlist - for (const auto src_n : src_nl->get_nets()) - { - Net* new_net = nullptr; - - // edge case for global inputs - if (src_n->is_global_input_net()) - { - if (const auto it = global_io_mapping.find(src_n); it != global_io_mapping.end()) - { - const auto& net_connections = global_io_mapping.at(src_n); - if (net_connections.size() != 1) - { - return ERR("unable to replace subgraph with netlist: found " + std::to_string(net_connections.size()) + " net connections to the global input " + src_n->get_name() - + ", this would lead to multi-driven nets"); - } - new_net = net_connections.front(); - } - else - { - return ERR("unable to replace subgraph with netlist: failed to locate mapped net in destination netlist for global I/O net '" + src_n->get_name() + "' with ID " - + std::to_string(src_n->get_id())); - } - } - else if (src_n->is_global_output_net()) - { - if (const auto it = global_io_mapping.find(src_n); it != global_io_mapping.end()) - { - const auto& net_connections = global_io_mapping.at(src_n); - new_net = net_connections.front(); - - if (net_connections.size() != 1) - { - log_warning("resynthesis", "found multiple io connections for net '{}' with ID {}, this might lead to missing nets in the netlist", src_n->get_name(), src_n->get_id()); - // for (const auto& net : net_connections) - // { - // std::cout << net->get_id() << " - " << net->get_name() << std::endl; - // } - - // if a single global output of the src netlist leads to multiple nets in the dst netlist, that means that the nets are functionally equivalent and we can connect/merge them. - // however this can lead to nets disappearing from the dst netlist which might be unexpected behavior. - - for (u32 i = 1; i < net_connections.size(); i++) - { - const auto& res = NetlistModificationDecorator(*dst_nl).connect_nets(new_net, net_connections.at(i)); - if (res.is_error()) - { - return ERR("unable to replace subgraph with netlist: failed to connect/merge all the net connections of net '" + src_n->get_name() + "' with ID " - + std::to_string(src_n->get_id())); - } - } - } - else - { - new_net = net_connections.front(); - } - } - else - { - return ERR("unable to replace subgraph with netlist: failed to locate mapped net in destination netlist for global I/O net '" + src_n->get_name() + "' with ID " - + std::to_string(src_n->get_id())); - } - } - else if (src_n->is_gnd_net()) - { - // set new net to an existing gnd net - const auto gnd_gate = dst_nl->get_gnd_gates().front(); - const auto out_net = gnd_gate->get_fan_out_nets().front(); - new_net = out_net; - } - else if (src_n->is_vcc_net()) - { - const auto vcc_gate = dst_nl->get_vcc_gates().front(); - const auto out_net = vcc_gate->get_fan_out_nets().front(); - new_net = out_net; - } - else - { - new_net = dst_nl->create_net("TEMP"); - const std::string new_name = new_net_name(new_net, src_n); - new_net->set_name(new_name); - } - - // connect net to sources - for (const auto src_ep : src_n->get_sources()) - { - const auto org_src_name = src_ep->get_gate()->get_name(); - const auto org_src_pin_name = src_ep->get_pin()->get_name(); - auto new_src_g = gate_name_to_gate.at(org_src_name); - if (new_net->add_source(new_src_g, org_src_pin_name) == nullptr) - { - return ERR("unable to replace subgraph with netlist: failed to add gate '" + new_src_g->get_name() + "' with ID " + std::to_string(new_src_g->get_id()) + " at pin '" - + org_src_pin_name + "' as new source to net '" + new_net->get_name() + "' with ID " + std::to_string(new_net->get_id())); - } - } - - // connect net to destinations - for (const auto src_ep : src_n->get_destinations()) - { - const auto org_dst_name = src_ep->get_gate()->get_name(); - const auto org_dst_pin_name = src_ep->get_pin()->get_name(); - auto new_dst_g = gate_name_to_gate.at(org_dst_name); - if (!new_net->add_destination(new_dst_g, org_dst_pin_name)) - { - return ERR("unable to replace subgraph with netlist: failed to add gate '" + new_dst_g->get_name() + "' with ID " + std::to_string(new_dst_g->get_id()) + " at pin '" - + org_dst_pin_name + "' as new destination to net '" + new_net->get_name() + "' with ID " + std::to_string(new_net->get_id())); - } - } - } - - // delete subgraph gates if flag is set - if (delete_subgraph_gates) - { - auto delete_res = delete_subgraph(dst_nl, subgraph); - if (delete_res.is_error()) - { - return ERR_APPEND(delete_res.get_error(), "unable to replace subgraph with netlist: failed to delete subgraph"); - } - } - - return OK({}); - } - Result replace_gate_with_netlist(Gate* g, const Netlist* src_nl, Netlist* dst_nl, const bool delete_gate = true) { std::unordered_map> global_io_mapping; @@ -403,7 +252,7 @@ namespace hal global_io_mapping[g_o->get_net()].push_back(o_net); } - return replace_subgraph_with_netlist({g}, global_io_mapping, src_nl, dst_nl, delete_gate); + return resynthesis::replace_subgraph_with_netlist({g}, global_io_mapping, src_nl, dst_nl, delete_gate); } Result, std::vector>> @@ -675,62 +524,6 @@ namespace hal return verilog_str; } - Result> generate_resynth_netlist_for_boolean_functions(const std::unordered_map& bfs, - const std::filesystem::path& genlib_path, - GateLibrary* target_gl, - const bool optimize_area) - { - const auto verilog_module = build_functional_verilog_module_from(bfs); - - auto base_path_res = utils::get_unique_temp_directory("resynthesis_"); - if (base_path_res.is_error()) - { - return ERR_APPEND(base_path_res.get_error(), "unable to resynthesize Boolean functions with yosys: failed to get unique temp directory"); - } - const std::filesystem::path base_path = base_path_res.get(); - const std::filesystem::path functional_netlist_path = base_path / "func_netlist.v"; - const std::filesystem::path resynthesized_netlist_path = base_path / "resynth_netlist.v"; - - std::filesystem::create_directory(base_path); - - std::ofstream out(functional_netlist_path); - out << verilog_module; - out.close(); - - auto yosys_query_res = yosys::query_binary_path(); - if (yosys_query_res.is_error()) - { - return ERR_APPEND(yosys_query_res.get_error(), "unable to resynthesize Boolean functions with yosys: failed to find yosys path"); - } - - const auto yosys_path = yosys_query_res.get(); - const std::string command = yosys_path + " -q -p " + "\"read -sv " + functional_netlist_path.string() + "; hierarchy -top top; proc; fsm; opt; memory; opt; techmap; opt; abc -genlib " - + genlib_path.string() + "; " + "write_verilog " + resynthesized_netlist_path.string() + "; clean\""; - - // TODO check again the proper way to start a subprocess - system(command.c_str()); - - auto resynth_nl = netlist_factory::load_netlist(resynthesized_netlist_path, target_gl); - - if (resynth_nl == nullptr) - { - return ERR("unable to resynthesize Boolean functions with yosys: failed to load re-synthesized netlist at " + resynthesized_netlist_path.string()); - } - - // TODO check whether this is needed here or maybe move this somewhere else - // yosys workaround for stupid net renaming - for (const auto& pin : resynth_nl->get_top_module()->get_input_pins()) - { - auto net = pin->get_net(); - net->set_name(pin->get_name()); - } - - // delete the created directory and the contained files - std::filesystem::remove_all(base_path); - - return OK(std::move(resynth_nl)); - } - Result> generate_resynth_netlist_for_gate_level_subgraph(const Netlist* nl, const std::vector& subgraph, const std::filesystem::path& genlib_path, @@ -867,7 +660,7 @@ namespace hal output_pin_name_to_bf.insert({pin->get_name(), bf}); } - auto resynth_res = generate_resynth_netlist_for_boolean_functions(output_pin_name_to_bf, genlib_path, target_gl, true); + auto resynth_res = resynthesis::generate_resynth_netlist_for_boolean_functions(output_pin_name_to_bf, genlib_path, target_gl, true); if (resynth_res.is_error()) { return ERR_APPEND(resynth_res.get_error(), @@ -1274,6 +1067,213 @@ namespace hal return resynthesize_subgraph(nl, subgraph, target_gl); } + + // NOTE there are about a hundred more checks that we could do here + Result replace_subgraph_with_netlist(const std::vector& subgraph, + const std::unordered_map>& global_io_mapping, + const Netlist* src_nl, + Netlist* dst_nl, + const bool delete_subgraph_gates) + { + std::unordered_map gate_name_to_gate; + + const auto dst_gl = dst_nl->get_gate_library(); + + // add all gates of the source netlist to the destination netlist + for (const auto src_g : src_nl->get_gates()) + { + const auto src_gt = src_g->get_type(); + const auto dst_gt = dst_gl->get_gate_type_by_name(src_gt->get_name()); + if (!dst_gt) + { + return ERR("unable to replace subgraph with netlist: gate library '" + dst_gl->get_name() + "' does not contain the required gate type " + src_gt->get_name()); + } + + auto new_gate = dst_nl->create_gate(dst_gt, "TEMP"); + const std::string new_name = new_gate_name(new_gate, src_g); + new_gate->set_name(new_name); + + gate_name_to_gate.insert({src_g->get_name(), new_gate}); + } + + // connect all nets of the source netlist to the destination netlist + for (const auto src_n : src_nl->get_nets()) + { + Net* new_net = nullptr; + + // edge case for global inputs + if (src_n->is_global_input_net()) + { + if (const auto it = global_io_mapping.find(src_n); it != global_io_mapping.end()) + { + const auto& net_connections = global_io_mapping.at(src_n); + if (net_connections.size() != 1) + { + return ERR("unable to replace subgraph with netlist: found " + std::to_string(net_connections.size()) + " net connections to the global input " + src_n->get_name() + + ", this would lead to multi-driven nets"); + } + new_net = net_connections.front(); + } + else + { + return ERR("unable to replace subgraph with netlist: failed to locate mapped net in destination netlist for global I/O net '" + src_n->get_name() + "' with ID " + + std::to_string(src_n->get_id())); + } + } + else if (src_n->is_global_output_net()) + { + if (const auto it = global_io_mapping.find(src_n); it != global_io_mapping.end()) + { + const auto& net_connections = global_io_mapping.at(src_n); + new_net = net_connections.front(); + + if (net_connections.size() != 1) + { + log_warning("resynthesis", "found multiple io connections for net '{}' with ID {}, this might lead to missing nets in the netlist", src_n->get_name(), src_n->get_id()); + // for (const auto& net : net_connections) + // { + // std::cout << net->get_id() << " - " << net->get_name() << std::endl; + // } + + // if a single global output of the src netlist leads to multiple nets in the dst netlist, that means that the nets are functionally equivalent and we can connect/merge them. + // however this can lead to nets disappearing from the dst netlist which might be unexpected behavior. + + for (u32 i = 1; i < net_connections.size(); i++) + { + const auto& res = NetlistModificationDecorator(*dst_nl).connect_nets(new_net, net_connections.at(i)); + if (res.is_error()) + { + return ERR("unable to replace subgraph with netlist: failed to connect/merge all the net connections of net '" + src_n->get_name() + "' with ID " + + std::to_string(src_n->get_id())); + } + } + } + else + { + new_net = net_connections.front(); + } + } + else + { + return ERR("unable to replace subgraph with netlist: failed to locate mapped net in destination netlist for global I/O net '" + src_n->get_name() + "' with ID " + + std::to_string(src_n->get_id())); + } + } + else if (src_n->is_gnd_net()) + { + // set new net to an existing gnd net + const auto gnd_gate = dst_nl->get_gnd_gates().front(); + const auto out_net = gnd_gate->get_fan_out_nets().front(); + new_net = out_net; + } + else if (src_n->is_vcc_net()) + { + const auto vcc_gate = dst_nl->get_vcc_gates().front(); + const auto out_net = vcc_gate->get_fan_out_nets().front(); + new_net = out_net; + } + else + { + new_net = dst_nl->create_net("TEMP"); + const std::string new_name = new_net_name(new_net, src_n); + new_net->set_name(new_name); + } + + // connect net to sources + for (const auto src_ep : src_n->get_sources()) + { + const auto org_src_name = src_ep->get_gate()->get_name(); + const auto org_src_pin_name = src_ep->get_pin()->get_name(); + auto new_src_g = gate_name_to_gate.at(org_src_name); + if (new_net->add_source(new_src_g, org_src_pin_name) == nullptr) + { + return ERR("unable to replace subgraph with netlist: failed to add gate '" + new_src_g->get_name() + "' with ID " + std::to_string(new_src_g->get_id()) + " at pin '" + + org_src_pin_name + "' as new source to net '" + new_net->get_name() + "' with ID " + std::to_string(new_net->get_id())); + } + } + + // connect net to destinations + for (const auto src_ep : src_n->get_destinations()) + { + const auto org_dst_name = src_ep->get_gate()->get_name(); + const auto org_dst_pin_name = src_ep->get_pin()->get_name(); + auto new_dst_g = gate_name_to_gate.at(org_dst_name); + if (!new_net->add_destination(new_dst_g, org_dst_pin_name)) + { + return ERR("unable to replace subgraph with netlist: failed to add gate '" + new_dst_g->get_name() + "' with ID " + std::to_string(new_dst_g->get_id()) + " at pin '" + + org_dst_pin_name + "' as new destination to net '" + new_net->get_name() + "' with ID " + std::to_string(new_net->get_id())); + } + } + } + + // delete subgraph gates if flag is set + if (delete_subgraph_gates) + { + auto delete_res = delete_subgraph(dst_nl, subgraph); + if (delete_res.is_error()) + { + return ERR_APPEND(delete_res.get_error(), "unable to replace subgraph with netlist: failed to delete subgraph"); + } + } + + return OK({}); + } + + Result> generate_resynth_netlist_for_boolean_functions(const std::unordered_map& bfs, + const std::filesystem::path& genlib_path, + GateLibrary* target_gl, + const bool optimize_area) + { + const auto verilog_module = build_functional_verilog_module_from(bfs); + + auto base_path_res = utils::get_unique_temp_directory("resynthesis_"); + if (base_path_res.is_error()) + { + return ERR_APPEND(base_path_res.get_error(), "unable to resynthesize Boolean functions with yosys: failed to get unique temp directory"); + } + const std::filesystem::path base_path = base_path_res.get(); + const std::filesystem::path functional_netlist_path = base_path / "func_netlist.v"; + const std::filesystem::path resynthesized_netlist_path = base_path / "resynth_netlist.v"; + + std::filesystem::create_directory(base_path); + + std::ofstream out(functional_netlist_path); + out << verilog_module; + out.close(); + + auto yosys_query_res = yosys::query_binary_path(); + if (yosys_query_res.is_error()) + { + return ERR_APPEND(yosys_query_res.get_error(), "unable to resynthesize Boolean functions with yosys: failed to find yosys path"); + } + + const auto yosys_path = yosys_query_res.get(); + const std::string command = yosys_path + " -q -p " + "\"read -sv " + functional_netlist_path.string() + "; hierarchy -top top; proc; fsm; opt; memory; opt; techmap; opt; abc -genlib " + + genlib_path.string() + "; " + "write_verilog " + resynthesized_netlist_path.string() + "; clean\""; + + // TODO check again the proper way to start a subprocess + system(command.c_str()); + + auto resynth_nl = netlist_factory::load_netlist(resynthesized_netlist_path, target_gl); + + if (resynth_nl == nullptr) + { + return ERR("unable to resynthesize Boolean functions with yosys: failed to load re-synthesized netlist at " + resynthesized_netlist_path.string()); + } + + // TODO check whether this is needed here or maybe move this somewhere else + // yosys workaround for stupid net renaming + for (const auto& pin : resynth_nl->get_top_module()->get_input_pins()) + { + auto net = pin->get_net(); + net->set_name(pin->get_name()); + } + + // delete the created directory and the contained files + std::filesystem::remove_all(base_path); + + return OK(std::move(resynth_nl)); + } } // namespace resynthesis } // namespace hal \ No newline at end of file From 4dc102db607220229b956ece3fcfeb0d435894b9 Mon Sep 17 00:00:00 2001 From: "julian.speith" Date: Tue, 16 Jul 2024 15:34:22 +0200 Subject: [PATCH 3/3] this should make our scripts work; again, shame on me --- .../python/python_bindings.cpp | 717 +++++++++++------- 1 file changed, 437 insertions(+), 280 deletions(-) diff --git a/plugins/netlist_preprocessing/python/python_bindings.cpp b/plugins/netlist_preprocessing/python/python_bindings.cpp index ba04179fd0f..ca6e555087e 100644 --- a/plugins/netlist_preprocessing/python/python_bindings.cpp +++ b/plugins/netlist_preprocessing/python/python_bindings.cpp @@ -1,5 +1,6 @@ #include "hal_core/python_bindings/python_bindings.h" +#include "netlist_preprocessing/netlist_preprocessing.h" #include "netlist_preprocessing/plugin_netlist_preprocessing.h" #include "pybind11/operators.h" #include "pybind11/pybind11.h" @@ -51,286 +52,442 @@ namespace hal :rtype: str )"); - // py_netlist_preprocessing.def_static( - // "remove_unused_lut_inputs", - // [](Netlist* nl) -> std::optional { - // auto res = NetlistPreprocessingPlugin::remove_unused_lut_inputs(nl); - // if (res.is_ok()) - // { - // return res.get(); - // } - // else - // { - // log_error("python_context", "{}", res.get_error().get()); - // return std::nullopt; - // } - // }, - // py::arg("nl"), - // R"( - // Removes all LUT fan-in endpoints that do not correspond to a variable within the Boolean function that determines the output of a gate. - - // :param hal_py.Netlist nl: The netlist to operate on. - // :returns: The number of removed LUT endpoints on success, `None` otherwise. - // :rtype: int or None - // )"); - - // py_netlist_preprocessing.def_static( - // "remove_buffers", - // [](Netlist* nl) -> std::optional { - // auto res = NetlistPreprocessingPlugin::remove_buffers(nl); - // if (res.is_ok()) - // { - // return res.get(); - // } - // else - // { - // log_error("python_context", "{}", res.get_error().get()); - // return std::nullopt; - // } - // }, - // py::arg("nl"), - // R"( - // Removes buffer gates from the netlist and connect their fan-in to their fan-out nets. - // Considers all combinational gates and takes their inputs into account. - // For example, a 2-input AND gate with one input being connected to constant '1' will also be removed. - - // :param hal_py.Netlist nl: The netlist to operate on. - // :returns: The number of removed buffers on success, `None` otherwise. - // :rtype: int or None - // )"); - - // py_netlist_preprocessing.def_static( - // "remove_redundant_logic", - // [](Netlist* nl) -> std::optional { - // auto res = NetlistPreprocessingPlugin::remove_redundant_logic(nl); - // if (res.is_ok()) - // { - // return res.get(); - // } - // else - // { - // log_error("python_context", "{}", res.get_error().get()); - // return std::nullopt; - // } - // }, - // py::arg("nl"), - // R"( - // Removes redundant gates from the netlist, i.e., gates that are functionally equivalent and are connected to the same input nets. - - // :param hal_py.Netlist nl: The netlist to operate on. - // :returns: The number of removed gates on success, `None` otherwise. - // :rtype: int or None - // )"); - - // py_netlist_preprocessing.def_static( - // "remove_unconnected_gates", - // [](Netlist* nl) -> std::optional { - // auto res = NetlistPreprocessingPlugin::remove_unconnected_gates(nl); - // if (res.is_ok()) - // { - // return res.get(); - // } - // else - // { - // log_error("python_context", "{}", res.get_error().get()); - // return std::nullopt; - // } - // }, - // py::arg("nl"), - // R"( - // Removes gates which outputs are all unconnected and not a global output net. - - // :param hal_py.Netlist nl: The netlist to operate on. - // :returns: The number of removed gates on success, `None` otherwise. - // :rtype: int or None - // )"); - - // py_netlist_preprocessing.def_static( - // "remove_unconnected_nets", - // [](Netlist* nl) -> std::optional { - // auto res = NetlistPreprocessingPlugin::remove_unconnected_nets(nl); - // if (res.is_ok()) - // { - // return res.get(); - // } - // else - // { - // log_error("python_context", "{}", res.get_error().get()); - // return std::nullopt; - // } - // }, - // py::arg("nl"), - // R"( - // Remove nets which have no source and not destination. - - // :param hal_py.Netlist nl: The netlist to operate on. - // :returns: The number of removed nets on success, `None` otherwise. - // :rtype: int or None - // )"); - - // py_netlist_preprocessing.def_static( - // "simplify_lut_inits", - // [](Netlist* nl) -> std::optional { - // auto res = NetlistPreprocessingPlugin::simplify_lut_inits(nl); - // if (res.is_ok()) - // { - // return res.get(); - // } - // else - // { - // log_error("python_context", "{}", res.get_error().get()); - // return std::nullopt; - // } - // }, - // py::arg("nl"), - // R"( - // Replaces pins connected to GND/VCC with constants and simplifies the boolean function of a LUT by recomputing the INIT string. - - // :param hal_py.Netlist nl: The netlist to operate on. - // :returns: The number of simplified INIT strings on success, `None` otherwise. - // :rtype: int or None - // )"); - - // py_netlist_preprocessing.def_static( - // "decompose_gate", - // [](Netlist* nl, Gate* g, const bool delete_gate = true) -> bool { - // auto res = NetlistPreprocessingPlugin::decompose_gate(nl, g, delete_gate); - // if (res.is_ok()) - // { - // return true; - // } - // else - // { - // log_error("python_context", "{}", res.get_error().get()); - // return false; - // } - // }, - // py::arg("nl"), - // py::arg("g"), - // py::arg("delete_gate") = true, - // R"( - // Builds the Boolean function of each output pin of the gate and constructs a gate tree implementing it. - // Afterwards the original output net is connected to the built gate tree and the gate is deleted if the 'delete_gate' flag is set. - - // For the decomposition we currently only support the base operands AND, OR, INVERT. - // The function searches in the gate library for a fitting two input gate and uses a standard HAL gate type if none is found. - - // :param hal_py.Netlist nl: The netlist to operate on. - // :param hal_py.Gate gate: The gate to decompose. - // :param bool delete_gate: Determines whether the original gate gets deleted by the function, defaults to `True`. - // :returns: `True` on success, `False` otherwise. - // :rtype: bool - // )"); - - // py_netlist_preprocessing.def_static( - // "decompose_gates_of_type", - // [](Netlist* nl, const std::vector& gate_types) -> std::optional { - // auto res = NetlistPreprocessingPlugin::decompose_gates_of_type(nl, gate_types); - // if (res.is_ok()) - // { - // return res.get(); - // } - // else - // { - // log_error("python_context", "{}", res.get_error().get()); - // return std::nullopt; - // } - // }, - // py::arg("nl"), - // py::arg("gate_types"), - // R"( - // Decomposes each gate of the specified type by building the Boolean function for each output pin of the gate and contructing a gate tree implementing it. - // Afterwards the original gate is deleted and the output net is connected to the built gate tree. - - // For the decomposition we currently only support the base operands AND, OR, INVERT. - // The function searches in the gate library for a fitting two input gate and uses a standard HAL gate type if none is found. - - // :param hal_py.Netlist nl: The netlist to operate on. - // :param list[hal_py.GateType] gate_types: The gate types that should be decomposed. - // :returns: The number of decomposed gates on success, `None` otherwise. - // :rtype: int or None - // )"); - - // py_netlist_preprocessing.def_static( - // "reconstruct_indexed_ff_identifiers", - // [](Netlist* nl) -> std::optional { - // auto res = NetlistPreprocessingPlugin::reconstruct_indexed_ff_identifiers(nl); - // if (res.is_ok()) - // { - // return res.get(); - // } - // else - // { - // log_error("python_context", "{}", res.get_error().get()); - // return std::nullopt; - // } - // }, - // py::arg("nl"), - // R"( - // Tries to reconstruct a name and index for each flip flop that was part of a multibit wire in the verilog code. - // This is NOT a general netlist reverse engineering algorithm and ONLY works on synthesized netlists with names annotated by the synthesizer. - // This function mainly focuses netlists synthesized with yosys since yosys names the output wires of the flip flops but not the gate it self. - // We try to reconstruct name and index for each flip flop based on the name of its output nets. - - // :param hal_py.Netlist nl: The netlist to operate on. - // :returns: The number of reconstructed names on success, `None` otherwise. - // :rtype: int or None - // )"); - - // py_netlist_preprocessing.def_static( - // "parse_def_file", - // [](Netlist* nl, const std::filesystem::path& def_file) -> bool { - // auto res = NetlistPreprocessingPlugin::parse_def_file(nl, def_file); - // if (res.is_ok()) - // { - // return true; - // } - // else - // { - // log_error("python_context", "{}", res.get_error().get()); - // return false; - // } - // }, - // py::arg("nl"), - // py::arg("def_file"), - // R"( - // Parses a design exchange format file and extracts the coordinated of a placed design for each component/gate. - // The extracted coordinates get annotated to the gates. - - // :param hal_py.Netlist nl: The netlist to operate on. - // :param pathlib.Path def_file: The path to the def file - // :returns: `True` on success, `False` otherwise. - // :rtype: bool - // )"); - - // py_netlist_preprocessing.def_static( - // "unify_ff_outputs", - // [](Netlist* nl, const std::vector& ffs = {}, GateType* inverter_type = nullptr) -> std::optional { - // auto res = NetlistPreprocessingPlugin::unify_ff_outputs(nl, ffs, inverter_type); - // if (res.is_ok()) - // { - // return res.get(); - // } - // else - // { - // log_error("python_context", "{}", res.get_error().get()); - // return std::nullopt; - // } - // }, - // py::arg("nl"), - // py::arg("ffs") = std::vector(), - // py::arg("inverter_type") = nullptr, - // R"( - // Iterates all flip-flops of the netlist or specified by the user. - // If a flip-flop has a ``state`` and a ``neg_state`` output, a new inverter gate is created and connected to the ``state`` output net as an additional destination. - // Finally, the ``neg_state`` output net is disconnected from the ``neg_state`` pin and re-connected to the new inverter gate's output. - - // :param hal_py.Netlist nl: The netlist to operate on. - // :param list[hal_py.Gate] ffs: The flip-flops to operate on. Defaults to an empty vector, in which case all flip-flops of the netlist are considered. - // :param hal_py.GateType inverter_type: The inverter gate type to use. Defaults to a ``None``, in which case the first inverter type found in the gate library is used. - // :returns: The number of rerouted ``neg_state`` outputs on success, ``None`` otherwise. - // :rtype: int or ``None`` - // )"); + m.def( + "remove_unused_lut_inputs", + [](Netlist* nl) -> std::optional { + auto res = netlist_preprocessing::remove_unused_lut_inputs(nl); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("nl"), + R"( + Removes all LUT fan-in endpoints that do not correspond to a variable within the Boolean function that determines the output of a gate. + + :param hal_py.Netlist nl: The netlist to operate on. + :returns: The number of removed LUT endpoints on success, ``None`` otherwise. + :rtype: int or ``None`` + )"); + + m.def( + "remove_buffers", + [](Netlist* nl) -> std::optional { + auto res = netlist_preprocessing::remove_buffers(nl); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("nl"), + R"( + Removes buffer gates from the netlist and connect their fan-in to their fan-out nets. + Considers all combinational gates and takes their inputs into account. + For example, a 2-input AND gate with one input being connected to constant ``1`` will also be removed. + + :param hal_py.Netlist nl: The netlist to operate on. + :returns: The number of removed buffers on success, ``None`` otherwise. + :rtype: int or ``None`` + )"); + + m.def( + "remove_redundant_gates", + [](Netlist* nl, const std::function& filter = nullptr) -> std::optional { + auto res = netlist_preprocessing::remove_redundant_gates(nl, filter); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("nl"), + py::arg("filter") = nullptr, + R"( + Removes redundant gates from the netlist, i.e., gates that are functionally equivalent and are connected to the same input nets. + + :param hal_py.Netlist nl: The netlist to operate on. + :param lambda filter: Optional filter to fine-tune which gates are being replaced. Default to a ``None``. + :returns: The number of removed gates on success, ``None`` otherwise. + :rtype: int or ``None`` + )"); + + m.def( + "remove_redundant_loops", + [](Netlist* nl) -> std::optional { + auto res = netlist_preprocessing::remove_redundant_loops(nl); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("nl"), + R"( + Removes redundant sequential feedback loops. + Sometimes flip-flops and some of their combinational fan-in form a feedback loop where the flip-flop input depends on its own output. + For optimization, some synthesizers create multiple equivalent instances of these feedback loops. + To simplify structural analysis, this function removes the redundant flip-flop gate of the loop from the netlist. + Other preprocessing functions can then take care of the remaining combination gates of the loop. + + :param hal_py.Netlist nl: The netlist to operate on. + :returns: The number of removed gates on success, ``None`` otherwise. + :rtype: int or ``None`` + )"); + + m.def( + "remove_redundant_logic_trees", + [](Netlist* nl) -> std::optional { + auto res = netlist_preprocessing::remove_redundant_logic_trees(nl); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("nl"), + R"( + Removes redundant logic trees made up of combinational gates. + If two trees compute the exact same function even if implemented with different gates we will disconnect one of the trees and afterwards clean up all dangling gates and nets. + + :param hal_py.Netlist nl: The netlist to operate on. + :returns: The number of removed gates on success, ``None`` otherwise. + :rtype: int or ``None`` + )"); + + m.def( + "remove_unconnected_gates", + [](Netlist* nl) -> std::optional { + auto res = netlist_preprocessing::remove_unconnected_gates(nl); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("nl"), + R"( + Removes gates for which all fan-out nets do not have a destination and are not global output nets. + + :param hal_py.Netlist nl: The netlist to operate on. + :returns: The number of removed gates on success, ``None`` otherwise. + :rtype: int or ``None`` + )"); + + m.def( + "remove_unconnected_nets", + [](Netlist* nl) -> std::optional { + auto res = netlist_preprocessing::remove_unconnected_nets(nl); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("nl"), + R"( + Removes nets who have neither a source, nor a destination. + + :param hal_py.Netlist nl: The netlist to operate on. + :returns: The number of removed nets on success, ``None`` otherwise. + :rtype: int or ``None`` + )"); + + m.def( + "remove_unconnected_looped", + [](Netlist* nl) -> std::optional { + auto res = netlist_preprocessing::remove_unconnected_looped(nl); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("nl"), + R"( + Calls remove_unconnected_gates / remove_unconnected_nets until there are no further changes. + + :param hal_py.Netlist nl: The netlist to operate on. + :returns: The number of removed nets and gates on success, ``None`` otherwise. + :rtype: int or ``None`` + )"); + + m.def( + "manual_mux_optimizations", + [](Netlist* nl, GateLibrary* mux_inv_gl) -> std::optional { + auto res = netlist_preprocessing::manual_mux_optimizations(nl, mux_inv_gl); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("nl"), + py::arg("mux_inv_gl"), + R"( + Apply manually implemented optimizations to the netlist centered around muxes. + Currently implemented optimizations include: + - removing inverters incase there are inverter gates in front and behind every data input and output of the mux + - optimizing and therefore unifying possible inverters preceding the select signals by resynthesizing + + :param halp_py.Netlist nl: The netlist to operate on. + :param halp_py.GateLibrary mux_inv_gl: A gate library only containing mux and inverter gates used for resynthesis. + :returns: The difference in the total number of gates caused by these optimizations. + :rtype: int or ``None`` + )"); + + m.def( + "propagate_constants", + [](Netlist* nl) -> std::optional { + auto res = netlist_preprocessing::propagate_constants(nl); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("nl"), + R"( + Builds for all gate output nets the Boolean function and substitutes all variables connected to vcc/gnd nets with the respective boolean value. + If the function simplifies to a static boolean constant cut the connection to the nets destinations and directly connect it to vcc/gnd. + + :param hal_py.Netlist nl: The netlist to operate on. + :returns: The number of rerouted nets on success, ``None`` otherwise. + :rtype: int or ``None`` + )"); + + m.def( + "remove_consecutive_inverters", + [](Netlist* nl) -> std::optional { + auto res = netlist_preprocessing::remove_consecutive_inverters(nl); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("nl"), + R"( + Removes two consecutive inverters and reconnects the input of the first inverter to the output of the second one. + If the first inverter has additional successors, only the second inverter is deleted. + + :param hal_py.Netlist nl: The netlist to operate on. + :returns: The number of removed inverter gates on success, ``None`` otherwise. + :rtype: int or ``None`` + )"); + + m.def( + "simplify_lut_inits", + [](Netlist* nl) -> std::optional { + auto res = netlist_preprocessing::simplify_lut_inits(nl); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("nl"), + R"( + Replaces pins connected to GND/VCC with constants and simplifies the boolean function of a LUT by recomputing the INIT string. + + :param hal_py.Netlist nl: The netlist to operate on. + :returns: The number of simplified INIT strings on success, ``None`` otherwise. + :rtype: int or ``None`` + )"); + + m.def( + "reconstruct_indexed_ff_identifiers", + [](Netlist* nl) -> std::optional { + auto res = netlist_preprocessing::reconstruct_indexed_ff_identifiers(nl); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("nl"), + R"( + Tries to reconstruct a name and index for each flip flop that was part of a multi-bit wire in the verilog code. + This is NOT a general netlist reverse engineering algorithm and ONLY works on synthesized netlists with names annotated by the synthesizer. + This function mainly focuses netlists synthesized with yosys since yosys names the output wires of the flip flops but not the gate it self. + We try to reconstruct name and index for each flip flop based on the name of its output nets. + + :param hal_py.Netlist nl: The netlist to operate on. + :returns: The number of reconstructed names on success, ``None`` otherwise. + :rtype: int or ``None`` + )"); + + m.def( + "reconstruct_top_module_pin_groups", + [](Netlist* nl) -> std::optional { + auto res = netlist_preprocessing::reconstruct_top_module_pin_groups(nl); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("nl"), + R"( + Tries to reconstruct top module pin groups via indexed pin names. + + :param hal_py.Netlist nl: The netlist to operate on. + :returns: The number of reconstructed pin groups on success, ``None`` otherwise. + :rtype: int or ``None`` + )"); + + m.def( + "parse_def_file", + [](Netlist* nl, const std::filesystem::path& def_file) -> bool { + auto res = netlist_preprocessing::parse_def_file(nl, def_file); + if (res.is_ok()) + { + return true; + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return false; + } + }, + py::arg("nl"), + py::arg("def_file"), + R"( + Parses a design exchange format file and extracts the coordinates of a placed design for each component/gate. + The extracted coordinates get annotated to the gates. + + :param hal_py.Netlist nl: The netlist to operate on. + :param pathlib.Path def_file: The path to the def file + :returns: ``True`` on success, ``False`` otherwise. + :rtype: bool + )"); + + m.def( + "create_multi_bit_gate_modules", + [](Netlist* nl, const std::map>>& concatenated_pin_groups) -> std::vector { + auto res = netlist_preprocessing::create_multi_bit_gate_modules(nl, concatenated_pin_groups); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return {}; + } + }, + py::arg("nl"), + py::arg("concatenated_pin_groups"), + R"( + Create modules from large gates like RAMs and DSPs with the option to concat multiple gate pin groups to larger consecutive pin groups. + + :param hal_py.Netlist nl: The netlist to operate on. + :param concatenated_pin_groups: + :returns: ``True`` on success, ``False`` otherwise. + :rtype: bool + )"); + + m.def( + "create_nets_at_unconnected_pins", + [](Netlist* nl) -> std::vector { + auto res = netlist_preprocessing::create_nets_at_unconnected_pins(nl); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return {}; + } + }, + py::arg("nl")); + + m.def( + "unify_ff_outputs", + [](Netlist* nl, const std::vector& ffs = {}, GateType* inverter_type = nullptr) -> std::optional { + auto res = netlist_preprocessing::unify_ff_outputs(nl, ffs, inverter_type); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("nl"), + py::arg("ffs") = std::vector(), + py::arg("inverter_type") = nullptr, + R"( + Iterates all flip-flops of the netlist or specified by the user. + If a flip-flop has a ``state`` and a ``neg_state`` output, a new inverter gate is created and connected to the ``state`` output net as an additional destination. + Finally, the ``neg_state`` output net is disconnected from the ``neg_state`` pin and re-connected to the new inverter gate's output. + + :param hal_py.Netlist nl: The netlist to operate on. + :param list[hal_py.Gate] ffs: The flip-flops to operate on. Defaults to an empty vector, in which case all flip-flops of the netlist are considered. + :param hal_py.GateType inverter_type: The inverter gate type to use. Defaults to a ``None``, in which case the first inverter type found in the gate library is used. + :returns: The number of rerouted ``neg_state`` outputs on success, ``None`` otherwise. + :rtype: int or ``None`` + )"); #ifndef PYBIND11_MODULE return m.ptr();