diff --git a/CHANGELOG.md b/CHANGELOG.md index fce09ec5b1c..d47875561ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased] * **WARNING:** this release breaks compatibility with Ubuntu 20.04 LTS -* **WARNING:** this release breaks the API of the `graph_algorithm` plugin -* **WARNING:** this release breaks the API of the `dataflow_analysis` plugin +* **WARNING:** this release breaks the API of the `graph_algorithm`, `dataflow`, and `xilinx_toolbox` plugins * GUI * refactored module widget * added option to show gate content for each module @@ -42,6 +41,9 @@ All notable changes to this project will be documented in this file. * extended maximum line with the CSV parser can handle * changed warning messages for waveform parsing and made them more specific * changed policy toward 'dangling' wires, they are no longer ignored but considered as global inputs or outputs + * changed `xilinx_toolbox` plugin + * added `split_shift_registers` function to split `SRL16E` gates into multiple flip-flops + * changed Python bindings for better usability * core * pin (groups) * added optional flag to determine whether a pin group has an inherent order (defaults to `false`) diff --git a/plugins/gate_libraries/definitions/XILINX_UNISIM.hgl b/plugins/gate_libraries/definitions/XILINX_UNISIM.hgl index 8b49f5edbcd..420db164bd2 100644 --- a/plugins/gate_libraries/definitions/XILINX_UNISIM.hgl +++ b/plugins/gate_libraries/definitions/XILINX_UNISIM.hgl @@ -375865,14 +375865,14 @@ { "name": "Q", "direction": "output", - "type": "data", + "type": "state", "ascending": false, "start_index": 0, "pins": [ { "name": "Q", "direction": "output", - "type": "data" + "type": "state" } ] } @@ -485878,28 +485878,28 @@ { "name": "Q", "direction": "output", - "type": "data", + "type": "state", "ascending": false, "start_index": 0, "pins": [ { "name": "Q", "direction": "output", - "type": "data" + "type": "state" } ] }, { "name": "Q31", "direction": "output", - "type": "data", + "type": "state", "ascending": false, "start_index": 0, "pins": [ { "name": "Q31", "direction": "output", - "type": "data" + "type": "state" } ] } @@ -495782,28 +495782,28 @@ { "name": "Q", "direction": "output", - "type": "data", + "type": "state", "ascending": false, "start_index": 0, "pins": [ { "name": "Q", "direction": "output", - "type": "data" + "type": "state" } ] }, { "name": "Q15", "direction": "output", - "type": "data", + "type": "state", "ascending": false, "start_index": 0, "pins": [ { "name": "Q15", "direction": "output", - "type": "data" + "type": "state" } ] } @@ -766832,14 +766832,14 @@ { "name": "Q", "direction": "output", - "type": "data", + "type": "state", "ascending": false, "start_index": 0, "pins": [ { "name": "Q", "direction": "output", - "type": "data" + "type": "state" } ] } diff --git a/plugins/verilog_writer/src/verilog_writer.cpp b/plugins/verilog_writer/src/verilog_writer.cpp index 94a64df53ae..66984a25ac4 100644 --- a/plugins/verilog_writer/src/verilog_writer.cpp +++ b/plugins/verilog_writer/src/verilog_writer.cpp @@ -473,14 +473,14 @@ namespace hal std::string VerilogWriter::get_unique_alias(std::unordered_map& name_occurrences, const std::string& name) const { + name_occurrences[name]++; + // if the name only appears once, we don't have to suffix it if (name_occurrences[name] < 2) { return name; } - name_occurrences[name]++; - // otherwise, add a unique string to the name return name + "__[" + std::to_string(name_occurrences[name]) + "]__"; } @@ -504,4 +504,4 @@ namespace hal return s; } -} // namespace hal \ No newline at end of file +} // namespace hal diff --git a/plugins/xilinx_toolbox/CMakeLists.txt b/plugins/xilinx_toolbox/CMakeLists.txt index 96b26fba675..c665bca0d17 100644 --- a/plugins/xilinx_toolbox/CMakeLists.txt +++ b/plugins/xilinx_toolbox/CMakeLists.txt @@ -1,5 +1,7 @@ option(PL_XILINX_TOOLBOX "PL_XILINX_TOOLBOX" OFF) + if(PL_XILINX_TOOLBOX OR BUILD_ALL_PLUGINS) + file(GLOB_RECURSE XILINX_TOOLBOX_INC ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) file(GLOB_RECURSE XILINX_TOOLBOX_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) file(GLOB_RECURSE XILINX_TOOLBOX_PYTHON_SRC ${CMAKE_CURRENT_SOURCE_DIR}/python/*.cpp) @@ -8,5 +10,6 @@ if(PL_XILINX_TOOLBOX OR BUILD_ALL_PLUGINS) SHARED HEADER ${XILINX_TOOLBOX_INC} SOURCES ${XILINX_TOOLBOX_SRC} ${XILINX_TOOLBOX_PYTHON_SRC} - ) + PYDOC SPHINX_DOC_INDEX_FILE ${CMAKE_CURRENT_SOURCE_DIR}/documentation/xilinx_toolbox.rst + ) endif() diff --git a/plugins/xilinx_toolbox/documentation/xilinx_toolbox.rst b/plugins/xilinx_toolbox/documentation/xilinx_toolbox.rst new file mode 100644 index 00000000000..1dbfe77cb20 --- /dev/null +++ b/plugins/xilinx_toolbox/documentation/xilinx_toolbox.rst @@ -0,0 +1,8 @@ +Xilinx Toolbox +========================== + +.. automodule:: xilinx_toolbox + :members: split_luts, split_shift_registers, parse_xdc_file + +.. autoclass:: xilinx_toolbox.XilinxToolboxPlugin + :members: \ No newline at end of file diff --git a/plugins/xilinx_toolbox/include/xilinx_toolbox/plugin_xilinx_toolbox.h b/plugins/xilinx_toolbox/include/xilinx_toolbox/plugin_xilinx_toolbox.h index 4c1b56a4552..76500469348 100644 --- a/plugins/xilinx_toolbox/include/xilinx_toolbox/plugin_xilinx_toolbox.h +++ b/plugins/xilinx_toolbox/include/xilinx_toolbox/plugin_xilinx_toolbox.h @@ -1,3 +1,33 @@ +// 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. + +/** + * @file plugin_xilinx_toolbox.h + * @brief This file contains all functions related to the HAL plugin API. + */ + #pragma once #include "hal_core/plugin_system/plugin_interface_base.h" @@ -7,13 +37,52 @@ namespace hal { class Netlist; + /** + * @class XilinxToolboxPlugin + * @brief Plugin interface for the Xilinx toolbox. + * + * This class provides an interface to integrate the Xilinx toolbox as a plugin within the HAL framework. + */ class PLUGIN_API XilinxToolboxPlugin : public BasePluginInterface { public: + /** + * @brief Default constructor for `XilinxToolboxPlugin`. + */ + XilinxToolboxPlugin() = default; + + /** + * @brief Default destructor for `XilinxToolboxPlugin`. + */ + ~XilinxToolboxPlugin() = default; + + /** + * @brief Get the name of the plugin. + * + * @returns The name of the plugin. + */ std::string get_name() const override; + + /** + * @brief Get the version of the plugin. + * + * @returns The version of the plugin. + */ std::string get_version() const override; - void initialize() override; + /** + * @brief Get a short description of the plugin. + * + * @returns The short description of the plugin. + */ + std::string get_description() const override; + + /** + * @brief Get the plugin dependencies. + * + * @returns A set of plugin names that this plugin depends on. + */ + std::set get_dependencies() const override; // preprocessing diff --git a/plugins/xilinx_toolbox/include/xilinx_toolbox/preprocessing.h b/plugins/xilinx_toolbox/include/xilinx_toolbox/preprocessing.h new file mode 100644 index 00000000000..d76a5ec13f9 --- /dev/null +++ b/plugins/xilinx_toolbox/include/xilinx_toolbox/preprocessing.h @@ -0,0 +1,73 @@ +// 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. + +/** + * @file preprocessing.h + * @brief This file contains functions specifically designed to preprocess Xilinx FPGA netlists. + */ + +#pragma once + +#include "hal_core/defines.h" +#include "hal_core/utilities/result.h" + +namespace hal +{ + class Netlist; + + namespace xilinx_toolbox + { + /** + * @brief Split LUTs with two outputs into two separate LUT gates. + * + * Replaces `LUT6_2` with a `LUT6` and a `LUT5` gate if the respective outputs of the `LUT6_2` are actually used, i.e., connected to other gates. + * + * @param[in] nl - The netlist to operate on. + * @returns The number of split `LUT6_2` gates on success, an error otherwise. + */ + Result split_luts(Netlist* nl); + + /** + * @brief Split shift register primitives and replaces them with equivalent flip-flops chains. + * + * Currently only implemented for gate type `SRL16E`. + * + * @param[in] nl - The netlist to operate on. + * @return The number of split shift registers on success, an error otherwise. + */ + Result split_shift_registers(Netlist* nl); + + /** + * @brief Parse an `.xdc` file and extract the position LOC and BEL data of each gate. + * + * Translates the coordinates extracted from the `.xdc` file into integer values. + * + * @param[in] nl - The netlist to operate on. + * @param[in] xdc_file - The path to the `.xdc` file. + * @return Ok() on success, an error otherwise. + */ + Result parse_xdc_file(Netlist* nl, const std::filesystem::path& xdc_file); + } // namespace xilinx_toolbox +} // namespace hal \ No newline at end of file diff --git a/plugins/xilinx_toolbox/include/xilinx_toolbox/types.h b/plugins/xilinx_toolbox/include/xilinx_toolbox/types.h index 8b8c48b60eb..a59a9065bd3 100644 --- a/plugins/xilinx_toolbox/include/xilinx_toolbox/types.h +++ b/plugins/xilinx_toolbox/include/xilinx_toolbox/types.h @@ -9,6 +9,10 @@ namespace hal { namespace xilinx_toolbox { + /** + * @enum BELType + * @brief BEL types relevant for XDC file parsing. + */ enum BELType { A6LUT, @@ -40,6 +44,10 @@ namespace hal OUTBUF, }; + /** + * @enum LOCType + * @brief LOC types relevant for XDC file parsing. + */ enum LOCType { SLICE, @@ -49,6 +57,12 @@ namespace hal PIN, }; + /** + * @enum LOC + * @brief Information on a LOC. + * + * This struct contains all relevant information on a LOC that is needed to parse and apply a XDC file to an existing netlist. + */ struct LOC { LOCType loc_type; @@ -57,6 +71,12 @@ namespace hal u64 loc_y; }; + /** + * @enum CellData + * @brief Data of a cell on the FPGA fabric. + * + * This struct contains all location information on a cell on the FPGA fabric, including its LOC and BEL data. + */ struct CellData { std::optional loc; diff --git a/plugins/xilinx_toolbox/python/python_bindings.cpp b/plugins/xilinx_toolbox/python/python_bindings.cpp index 50d43f2414e..7fb44c62ec2 100644 --- a/plugins/xilinx_toolbox/python/python_bindings.cpp +++ b/plugins/xilinx_toolbox/python/python_bindings.cpp @@ -5,6 +5,7 @@ #include "pybind11/stl.h" #include "pybind11/stl_bind.h" #include "xilinx_toolbox/plugin_xilinx_toolbox.h" +#include "xilinx_toolbox/preprocessing.h" namespace py = pybind11; @@ -17,45 +18,71 @@ namespace hal #ifdef PYBIND11_MODULE PYBIND11_MODULE(xilinx_toolbox, m) { - m.doc() = "hal XilinxToolboxPlugin python bindings"; + m.doc() = "A collection of functions specifically designed to operate on Xilinx FPGA netlists."; #else PYBIND11_PLUGIN(xilinx_toolbox) { - py::module m("xilinx_toolbox", "hal XilinxToolboxPlugin python bindings"); + py::module m("xilinx_toolbox", "A collection of functions specifically designed to operate on Xilinx FPGA netlists."); #endif // ifdef PYBIND11_MODULE - py::class_, BasePluginInterface> py_xilinx_toolbox(m, "XilinxToolboxPlugin"); + py::class_, BasePluginInterface> py_xilinx_toolbox_plugin(m, "XilinxToolboxPlugin"); - py_xilinx_toolbox.def_property_readonly("name", &XilinxToolboxPlugin::get_name, R"( + py_xilinx_toolbox_plugin.def_property_readonly("name", &XilinxToolboxPlugin::get_name, R"( The name of the plugin. :type: str )"); - py_xilinx_toolbox.def("get_name", &XilinxToolboxPlugin::get_name, R"( + py_xilinx_toolbox_plugin.def("get_name", &XilinxToolboxPlugin::get_name, R"( Get the name of the plugin. - :returns: Plugin name. + :returns: The name of the plugin. :rtype: str )"); - py_xilinx_toolbox.def_property_readonly("version", &XilinxToolboxPlugin::get_version, R"( + py_xilinx_toolbox_plugin.def_property_readonly("version", &XilinxToolboxPlugin::get_version, R"( The version of the plugin. :type: str )"); - py_xilinx_toolbox.def("get_version", &XilinxToolboxPlugin::get_version, R"( + py_xilinx_toolbox_plugin.def("get_version", &XilinxToolboxPlugin::get_version, R"( Get the version of the plugin. - :returns: Plugin version. + :returns: The version of the plugin. :rtype: str )"); - py_xilinx_toolbox.def_static( + py_xilinx_toolbox_plugin.def_property_readonly("description", &XilinxToolboxPlugin::get_description, R"( + The description of the plugin. + + :type: str + )"); + + py_xilinx_toolbox_plugin.def("get_description", &XilinxToolboxPlugin::get_description, R"( + Get the description of the plugin. + + :returns: The description of the plugin. + :rtype: str + )"); + + py_xilinx_toolbox_plugin.def_property_readonly("dependencies", &XilinxToolboxPlugin::get_dependencies, R"( + A set of plugin names that this plugin depends on. + + :type: set[str] + )"); + + py_xilinx_toolbox_plugin.def("get_dependencies", &XilinxToolboxPlugin::get_dependencies, R"( + Get a set of plugin names that this plugin depends on. + + :returns: A set of plugin names that this plugin depends on. + :rtype: set[str] + )"); + + m.def( "split_luts", [](Netlist* nl) -> std::optional { - auto res = XilinxToolboxPlugin::split_luts(nl); + auto res = xilinx_toolbox::split_luts(nl); if (res.is_ok()) { return res.get(); @@ -68,17 +95,42 @@ namespace hal }, py::arg("nl"), R"( - Removes all LUTs with multiple outputs and replaces them with equivalent smaller LUTs. + Split LUTs with two outputs into two separate LUT gates. + Replaces ``LUT6_2`` with a ``LUT6`` and a ``LUT5`` gate if the respective outputs of the ``LUT6_2`` are actually used, i.e., connected to other gates. :param hal_py.Netlist nl: The netlist to operate on. - :returns: The number of removed gates on success, None otherwise. + :returns: The number of split ``LUT6_2`` gates on success, ``None`` otherwise. :rtype: int or None )"); - py_xilinx_toolbox.def_static( + m.def( + "split_shift_registers", + [](Netlist* nl) -> std::optional { + auto res = xilinx_toolbox::split_shift_registers(nl); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("nl"), + R"( + Split shift register primitives and replaces them with equivalent flip-flops chains. + Currently only implemented for gate type ``SRL16E``. + + :param hal_py.Netlist nl: The netlist to operate on. + :returns: The number of split shift registers on success, ``None`` otherwise. + :rtype: int or None + )"); + + m.def( "parse_xdc_file", - [](Netlist* nl, const std::filesystem::path& xdc_file) -> std::optional { - auto res = XilinxToolboxPlugin::parse_xdc_file(nl, xdc_file); + [](Netlist* nl, const std::filesystem::path& xdc_file) -> bool { + auto res = xilinx_toolbox::parse_xdc_file(nl, xdc_file); if (res.is_ok()) { return true; @@ -86,19 +138,19 @@ namespace hal else { log_error("python_context", "{}", res.get_error().get()); - return std::nullopt; + return false; } }, py::arg("nl"), py::arg("xdc_file"), R"( - Parses an .xdc file and extracts the position LOC and BEL data. - Afterwards translates the found LOC and BEL data into integer coordinates. + Parse an ``.xdc`` file and extract the position LOC and BEL data of each gate. + Translates the coordinates extracted from the ``.xdc`` file into integer values. :param hal_py.Netlist nl: The netlist to operate on. - :param path xdc_file: The netlist to operate on. - :returns: True on success, None otherwise. - :rtype: bool or None + :param path xdc_file: The path to the ``.xdc`` file. + :returns: ``True`` on success, ``False`` otherwise. + :rtype: bool )"); #ifndef PYBIND11_MODULE diff --git a/plugins/xilinx_toolbox/src/plugin_xilinx_toolbox.cpp b/plugins/xilinx_toolbox/src/plugin_xilinx_toolbox.cpp index f348d753a7d..fc959c775ae 100644 --- a/plugins/xilinx_toolbox/src/plugin_xilinx_toolbox.cpp +++ b/plugins/xilinx_toolbox/src/plugin_xilinx_toolbox.cpp @@ -2,7 +2,6 @@ namespace hal { - extern std::unique_ptr create_plugin_instance() { return std::make_unique(); @@ -18,8 +17,13 @@ namespace hal return std::string("0.1"); } - void XilinxToolboxPlugin::initialize() + std::string XilinxToolboxPlugin::get_description() const { + return "A collection of functions specifically designed to operate on Xilinx FPGA netlists."; + } + std::set XilinxToolboxPlugin::get_dependencies() const + { + return {}; } -} +} // namespace hal diff --git a/plugins/xilinx_toolbox/src/preprocessing.cpp b/plugins/xilinx_toolbox/src/preprocessing.cpp index 80d456229e8..9ea5c37859a 100644 --- a/plugins/xilinx_toolbox/src/preprocessing.cpp +++ b/plugins/xilinx_toolbox/src/preprocessing.cpp @@ -6,7 +6,7 @@ namespace hal { - namespace XILINX_UNISIM + namespace xilinx_toolbox { Result split_luts(Netlist* nl) { @@ -63,7 +63,7 @@ namespace hal new_gates++; o5->add_source(lut5, "O"); } - + if (o6->get_num_of_destinations() > 0) { // create LUT6 @@ -105,21 +105,159 @@ namespace hal } } - log_info("xilinx_toolbox", "Split up {} LUT6_2 into {} smaller LUTs", deleted_gates, new_gates); + log_info("xilinx_toolbox", "split {} LUT6_2 gates into {} LUT6 and LUT5 gates", deleted_gates, new_gates); return OK(deleted_gates); } - } // namespace XILINX_UNISIM - - Result XilinxToolboxPlugin::split_luts(Netlist* nl) - { - std::map(Netlist*)>> gate_lib_to_func = {{"XILINX_UNISIM", XILINX_UNISIM::split_luts}}; - if (gate_lib_to_func.find(nl->get_gate_library()->get_name()) == gate_lib_to_func.end()) + Result split_shift_registers(Netlist* nl) { - return ERR("Cannot split LUTs for netlist with ID " + std::to_string(nl->get_id()) + ": Gate library " + nl->get_gate_library()->get_name() + " not supported"); - } + u32 deleted_gates = 0; + u32 new_gates = 0; + std::vector to_delete; + + GateType* ff_gt = nl->get_gate_library()->get_gate_type_by_name("FDE"); + if (ff_gt == nullptr) + { + return ERR("could not find gate type 'FDE' in gate library"); + } + + // iterate over all shift registers of type 'SRL16E' + for (const auto& gate : nl->get_gates([](const auto& g) { return g->get_type()->get_name() == "SRL16E"; })) + { + auto control_pins = gate->get_type()->get_pins([](const auto& pg) { return (pg->get_direction() == PinDirection::input) && (pg->get_type() == PinType::control); }); + if (control_pins.size() != 4) + { + return ERR("invalid number of control pins"); + } + + std::sort(control_pins.begin(), control_pins.end(), [](const auto& p1, const auto& p2) { + const u32 idx1 = std::stoull(p1->get_name().substr(1)); + const u32 idx2 = std::stoull(p2->get_name().substr(1)); + + return idx1 < idx2; + }); + + u32 select_value = 0; + for (u32 idx = 0; idx < control_pins.size(); idx++) + { + const Net* cn = gate->get_fan_in_net(control_pins.at(idx)); + + if (cn == nullptr) + { + log_warning("xilinx_toolbox", "control net at pin '{}' of gate '{}' with ID {} is 'nullptr'", control_pins.at(idx)->get_name(), gate->get_name(), gate->get_id()); + continue; + } + + if (!cn->is_gnd_net() && !cn->is_vcc_net()) + { + log_warning("xilinx_toolbox", "control net at pin '{}' of gate '{}' with ID {} is not constant", control_pins.at(idx)->get_name(), gate->get_name(), gate->get_id()); + continue; + } - return gate_lib_to_func.at(nl->get_gate_library()->get_name())(nl); - } + select_value += (cn->is_gnd_net() ? 0 : 1) << idx; + } + const auto clock_pins = gate->get_type()->get_pins([](const auto& p) { return (p->get_direction() == PinDirection::input) && (p->get_type() == PinType::clock); }); + if (clock_pins.size() != 1) + { + return ERR("invalid number of input clock pins at shift register gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id())); + } + + const auto enable_pins = gate->get_type()->get_pins([](const auto& p) { return (p->get_direction() == PinDirection::input) && (p->get_type() == PinType::enable); }); + if (enable_pins.size() != 1) + { + return ERR("invalid number of input enable pins at shift register gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id())); + } + + const auto data_pins = gate->get_type()->get_pins([](const auto& p) { return (p->get_direction() == PinDirection::input) && (p->get_type() == PinType::data); }); + if (data_pins.size() != 1) + { + return ERR("invalid number of input data pins at shift register gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id())); + } + + const auto state_pins = gate->get_type()->get_pins([](const auto& p) { return (p->get_direction() == PinDirection::output) && (p->get_type() == PinType::state); }); + if (state_pins.size() != 1) + { + return ERR("invalid number of output state pins at shift register gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id())); + } + + Net* clk_in = gate->get_fan_in_net(clock_pins.front()); + Net* enable_in = gate->get_fan_in_net(enable_pins.front()); + Net* data_in = gate->get_fan_in_net(data_pins.front()); + Net* state_out = gate->get_fan_out_net(state_pins.front()); + + if (clk_in == nullptr) + { + return ERR("no clock input net connected to shift register gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id())); + } + + if (enable_in == nullptr) + { + return ERR("no enable input net connected to shift register gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id())); + } + + if (data_in == nullptr) + { + return ERR("no data input net connected to shift register gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id())); + } + + if (state_out == nullptr) + { + return ERR("no state output net connected to shift register gate '" + gate->get_name() + "' with ID " + std::to_string(gate->get_id())); + } + + std::vector flip_flops; + std::vector state_nets; + + for (u32 ff_idx = 0; ff_idx <= select_value; ff_idx++) + { + const std::string ff_name = gate->get_name() + "_split_ff_" + std::to_string(ff_idx); + Gate* new_gate = nl->create_gate(ff_gt, ff_name); + new_gates++; + + clk_in->add_destination(new_gate, "C"); + enable_in->add_destination(new_gate, "CE"); + + if (ff_idx == 0) + { + data_in->add_destination(new_gate, "D"); + } + else + { + state_nets.back()->add_destination(new_gate, "D"); + } + + if (ff_idx == select_value) + { + state_out->add_source(new_gate, "Q"); + state_nets.push_back(state_out); + } + else + { + Net* new_net = nl->create_net(ff_name + "_out"); + new_net->add_source(new_gate, "Q"); + state_nets.push_back(new_net); + } + } + + to_delete.push_back(gate); + } + + for (const auto& g : to_delete) + { + if (!nl->delete_gate(g)) + { + return ERR("Cannot split shift register primitives for netlist with ID " + std::to_string(nl->get_id()) + ": Failed to delete gate " + g->get_name() + " with ID " + + std::to_string(g->get_id())); + } + else + { + deleted_gates++; + } + } + + log_info("xilinx_toolbox", "split {} SRLE16 gates into {} flip-flops", deleted_gates, new_gates); + return OK(deleted_gates); + } + } // namespace xilinx_toolbox } // namespace hal \ No newline at end of file diff --git a/plugins/xilinx_toolbox/src/xdc_parser.cpp b/plugins/xilinx_toolbox/src/xdc_parser.cpp index 6afe3315121..1853e59a0c8 100644 --- a/plugins/xilinx_toolbox/src/xdc_parser.cpp +++ b/plugins/xilinx_toolbox/src/xdc_parser.cpp @@ -12,326 +12,268 @@ namespace hal { namespace xilinx_toolbox { - // TODO ignore comments - // TODO there is some weird escaping with curly braces happening - TokenStream tokenize(std::stringstream& ss) + namespace { - 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)) + // TODO ignore comments + // TODO there is some weird escaping with curly braces happening + TokenStream tokenize(std::stringstream& ss) { - line_number++; + const std::string delimiters = " "; + std::string current_token; + u32 line_number = 0; + + std::string line; + bool escaped = false; - for (char c : line) + std::vector> parsed_tokens; + while (std::getline(ss, line)) { - // deal with escaping and strings - if (c == '\\') - { - escaped = true; - continue; - } - else if (escaped && std::isspace(c)) - { - escaped = false; - continue; - } + line_number++; - if (((!std::isspace(c) && delimiters.find(c) == std::string::npos) || escaped)) - { - current_token += c; - } - else + for (char c : line) { - if (!current_token.empty()) + // deal with escaping and strings + if (c == '\\') { - parsed_tokens.emplace_back(line_number, current_token); - current_token.clear(); + escaped = true; + continue; + } + else if (escaped && std::isspace(c)) + { + escaped = false; + continue; } - if (!std::isspace(c)) + if (((!std::isspace(c) && delimiters.find(c) == std::string::npos) || escaped)) { - parsed_tokens.emplace_back(line_number, std::string(1, c)); + 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(); + if (!current_token.empty()) + { + parsed_tokens.emplace_back(line_number, current_token); + current_token.clear(); + } } - } - return TokenStream(parsed_tokens, {}, {}); - } - - Result parse_LOC(TokenStream& ts) - { - const auto loc_token = ts.consume(); - std::string loc_str = loc_token.string; - - LOC new_loc; - new_loc.loc_name = loc_str; - - // test for pin names - const auto pin_pattern = std::regex("^[a-zA-Z]\\d+$"); - std::smatch match; - if (std::regex_match(loc_str, match, pin_pattern)) { - return OK(new_loc); + return TokenStream(parsed_tokens, {}, {}); } - // test for slice type + x and y coordinates - const std::string loc_type_str = loc_str.substr(0, loc_str.find_first_of("_")); - - if (is_valid_enum(loc_type_str)) - { - new_loc.loc_type = enum_from_string(loc_type_str); - } - else + Result parse_LOC(TokenStream& ts) { - return ERR("cannot parse LOC: encountered unknown LOC type '" + loc_type_str + "'" + " at line " + std::to_string(loc_token.number)); - } - - loc_str = loc_str.substr(loc_type_str.size()); - loc_str = loc_str.substr(std::string("_X").size()); - - const std::string x_str = loc_str.substr(0, loc_str.find_first_of("Y")); + const auto loc_token = ts.consume(); + std::string loc_str = loc_token.string; - loc_str = loc_str.substr(x_str.size()); - loc_str = loc_str.substr(std::string("Y").size()); + LOC new_loc; + new_loc.loc_name = loc_str; - const std::string y_str = loc_str; + // test for pin names + const auto pin_pattern = std::regex("^[a-zA-Z]\\d+$"); + std::smatch match; + if (std::regex_match(loc_str, match, pin_pattern)) + { + return OK(new_loc); + } - if (const auto res = utils::wrapped_stoull(x_str); res.is_ok()) - { - new_loc.loc_x = res.get(); - } - else - { - return ERR_APPEND(res.get_error(), "cannot parse LOC: failed to extract x position form " + loc_token.string + " at line " + std::to_string(loc_token.number)); - } + // test for slice type + x and y coordinates + const std::string loc_type_str = loc_str.substr(0, loc_str.find_first_of("_")); - if (const auto res = utils::wrapped_stoull(y_str); res.is_ok()) - { - new_loc.loc_y = res.get(); - } - else - { - return ERR_APPEND(res.get_error(), "cannot parse LOC: failed to extract y position form " + loc_token.string + " at line " + std::to_string(loc_token.number)); - } + if (is_valid_enum(loc_type_str)) + { + new_loc.loc_type = enum_from_string(loc_type_str); + } + else + { + return ERR("cannot parse LOC: encountered unknown LOC type '" + loc_type_str + "'" + " at line " + std::to_string(loc_token.number)); + } - return OK(new_loc); - } + loc_str = loc_str.substr(loc_type_str.size()); + loc_str = loc_str.substr(std::string("_X").size()); - Result parse_single_cell(TokenStream& ts) - { - const auto loc_token = ts.consume(); - if(loc_token.string != "[get_cells") - { - return ERR("cannot parse cell name: expected token " + loc_token.string + " at line " + std::to_string(loc_token.number) + " to be exactly '[get_cells'"); - } + const std::string x_str = loc_str.substr(0, loc_str.find_first_of("Y")); - const auto cell_token = ts.consume(); - std::string cell_str = cell_token.string; + loc_str = loc_str.substr(x_str.size()); + loc_str = loc_str.substr(std::string("Y").size()); - // TODO handle excaping with curly braces in tokenize() - // if (cell_str.substr(0, 1) != "{") - // { - // return ERR("cannot parse cell name: expected cell token '" + cell_str + " at line " + std::to_string(loc_token.number) + "' to start with '{'"); - // } + const std::string y_str = loc_str; - // if (cell_str.substr(cell_str.size() - 2, 2) != "}]") - // { - // return ERR("cannot parse cell name: expected cell token '" + cell_str + " at line " + std::to_string(loc_token.number) + "' to end with '}]'"); - // } + if (const auto res = utils::wrapped_stoull(x_str); res.is_ok()) + { + new_loc.loc_x = res.get(); + } + else + { + return ERR_APPEND(res.get_error(), "cannot parse LOC: failed to extract x position form " + loc_token.string + " at line " + std::to_string(loc_token.number)); + } - bool is_escpaed = cell_str.substr(0, 1) == "{" && cell_str.substr(cell_str.size() - 2, 1) == "}"; + if (const auto res = utils::wrapped_stoull(y_str); res.is_ok()) + { + new_loc.loc_y = res.get(); + } + else + { + return ERR_APPEND(res.get_error(), "cannot parse LOC: failed to extract y position form " + loc_token.string + " at line " + std::to_string(loc_token.number)); + } - if (cell_str.substr(cell_str.size() - 1, 1) != "]") - { - return ERR("cannot parse cell name: expected cell token '" + cell_str + " at line " + std::to_string(loc_token.number) + "' to end with ']'"); + return OK(new_loc); } - cell_str = cell_str.substr(is_escpaed ? 1 : 0, cell_str.size() - (is_escpaed ? 2 : 1)); + Result parse_single_cell(TokenStream& ts) + { + const auto loc_token = ts.consume(); + if (loc_token.string != "[get_cells") + { + return ERR("cannot parse cell name: expected token " + loc_token.string + " at line " + std::to_string(loc_token.number) + " to be exactly '[get_cells'"); + } - return OK(cell_str); - } + const auto cell_token = ts.consume(); + std::string cell_str = cell_token.string; - Result> parse_tokens(TokenStream& ts) - { - std::unordered_map cell_data; + bool is_escpaed = cell_str.substr(0, 1) == "{" && cell_str.substr(cell_str.size() - 2, 1) == "}"; - while (true) - { - ts.consume_until("set_property"); - if (ts.remaining() == 0) + if (cell_str.substr(cell_str.size() - 1, 1) != "]") { - break; + return ERR("cannot parse cell name: expected cell token '" + cell_str + " at line " + std::to_string(loc_token.number) + "' to end with ']'"); } - ts.consume("set_property"); + cell_str = cell_str.substr(is_escpaed ? 1 : 0, cell_str.size() - (is_escpaed ? 2 : 1)); - if (ts.peek().string == "BEL") - { - ts.consume("BEL"); + return OK(cell_str); + } - const auto bel_type = enum_from_string(ts.consume().string); + Result> parse_tokens(TokenStream& ts) + { + std::unordered_map cell_data; - if (const auto res = parse_single_cell(ts); res.is_ok()) - { - cell_data[res.get()].bel_type = bel_type; - } - else + while (true) + { + ts.consume_until("set_property"); + if (ts.remaining() == 0) { - return ERR_APPEND(res.get_error(), "cannot parse xdc token stream: failed to extract cell name"); + break; } - } - else if (ts.peek().string == "LOC") - { - ts.consume("LOC"); - LOC new_loc; - if (const auto res = parse_LOC(ts); res.is_ok()) + ts.consume("set_property"); + + if (ts.peek().string == "BEL") { - new_loc = res.get(); + ts.consume("BEL"); + + const auto bel_type = enum_from_string(ts.consume().string); + + if (const auto res = parse_single_cell(ts); res.is_ok()) + { + cell_data[res.get()].bel_type = bel_type; + } + else + { + return ERR_APPEND(res.get_error(), "cannot parse xdc token stream: failed to extract cell name"); + } } - else + else if (ts.peek().string == "LOC") { - return ERR_APPEND(res.get_error(), "cannot parse xdc token stream: failed to extract LOC"); - } + ts.consume("LOC"); - if (const auto res = parse_single_cell(ts); res.is_ok()) - { - cell_data[res.get()].loc = new_loc; + LOC new_loc; + if (const auto res = parse_LOC(ts); res.is_ok()) + { + new_loc = res.get(); + } + else + { + return ERR_APPEND(res.get_error(), "cannot parse xdc token stream: failed to extract LOC"); + } + + if (const auto res = parse_single_cell(ts); res.is_ok()) + { + cell_data[res.get()].loc = new_loc; + } + else + { + return ERR_APPEND(res.get_error(), "cannot parse xdc token stream: failed to extract cell name"); + } } else { - return ERR_APPEND(res.get_error(), "cannot parse xdc token stream: failed to extract cell name"); + ts.consume_current_line(); } } - else - { - ts.consume_current_line(); - } - } - - return OK(cell_data); - } - - // This might depend on the exact xilinx device (family) we are dealing with - // Result> reconstruct_coordinates(const CellData& cell_data) - // { - // // For the xilinx 7series boards we define that each Slice has 4 colums and 8 rows - // static std::map> bel_to_offset = { - // {BELType::A6LUT, {0, 0}}, - // {BELType::B6LUT, {0, 2}}, - // {BELType::C6LUT, {0, 4}}, - // {BELType::D6LUT, {0, 6}}, - // {BELType::A5LUT, {0, 1}}, - // {BELType::B5LUT, {0, 3}}, - // {BELType::C5LUT, {0, 5}}, - // {BELType::D5LUT, {0, 7}}, - - // {BELType::F7AMUX, {1, 0}}, - // {BELType::F7BMUX, {1, 2}}, - // {BELType::F8MUX, {1, 1}}, - - // {BELType::CARRY4, {2, 0}}, - - // {BELType::AFF, {3, 0}}, - // {BELType::BFF, {3, 2}}, - // {BELType::CFF, {3, 4}}, - // {BELType::DFF, {3, 6}}, - // {BELType::A5FF, {3, 1}}, - // {BELType::B5FF, {3, 3}}, - // {BELType::C5FF, {3, 5}}, - // {BELType::D5FF, {3, 7}}, - - // {BELType::BUFG, {0, 0}}, - // {BELType::INBUF_EN, {0, 0}}, - // {BELType::OUTBUF, {1, 0}}, - // }; - // } - } // namespace xilinx_toolbox - - Result XilinxToolboxPlugin::parse_xdc_file(Netlist* nl, const std::filesystem::path& xdc_file) - { - std::stringstream ss; - std::ifstream ifs; - ifs.open(xdc_file.string(), std::ifstream::in); - if (!ifs.is_open()) - { - return ERR("could not parse xdc file '" + xdc_file.string() + "' : unable to open file"); - } - ss << ifs.rdbuf(); - ifs.close(); - - auto ts = xilinx_toolbox::tokenize(ss); - std::unordered_map cell_data; - // parse tokens - try - { - if (auto res = xilinx_toolbox::parse_tokens(ts); res.is_error()) - { - return ERR_APPEND(res.get_error(), "could not parse xdc '" + xdc_file.string() + "': unable to parse tokens"); - } - else - { - cell_data = res.get(); - } - } - catch (TokenStream::TokenStreamException& e) - { - if (e.line_number != (u32)-1) - { - return ERR("could not parse xdc '" + xdc_file.string() + "': " + e.message + " (line " + std::to_string(e.line_number) + ")"); + return OK(cell_data); } - else - { - return ERR("could not parse xdc '" + xdc_file.string() + "': " + e.message); - } - } + } // namespace - std::unordered_map gate_name_to_gate; - for (const auto g : nl->get_gates()) + Result parse_xdc_file(Netlist* nl, const std::filesystem::path& xdc_file) { - gate_name_to_gate[g->get_name()] = g; - } + return ERR("not implemented"); - for (const auto& [gate_name, cd] : cell_data) - { - if (const auto it = gate_name_to_gate.find(gate_name); it == gate_name_to_gate.end()) - { - log_error("xilinx_toolbox", "Found gate name {} in xdc file but not in netlist!", gate_name); - continue; - } - else - { - /* - if (cd.loc.has_value()) - { - it->second->set_data("placement_information", "LOC", "string", "{type: " + enum_to_string(cd.loc.value().loc_type) + ", X: " + std::to_string(cd.loc.value().loc_x) + ", Y: " + std::to_string(cd.loc.value().loc_y) + "}"); - } + // std::stringstream ss; + // std::ifstream ifs; + // ifs.open(xdc_file.string(), std::ifstream::in); + // if (!ifs.is_open()) + // { + // return ERR("could not parse xdc file '" + xdc_file.string() + "' : unable to open file"); + // } + // ss << ifs.rdbuf(); + // ifs.close(); - if (cd.bel_type.has_value()) - { + // auto ts = xilinx_toolbox::tokenize(ss); - } - */ - } + // std::unordered_map cell_data; + // // parse tokens + // try + // { + // if (auto res = xilinx_toolbox::parse_tokens(ts); res.is_error()) + // { + // return ERR_APPEND(res.get_error(), "could not parse xdc '" + xdc_file.string() + "': unable to parse tokens"); + // } + // else + // { + // cell_data = res.get(); + // } + // } + // catch (TokenStream::TokenStreamException& e) + // { + // if (e.line_number != (u32)-1) + // { + // return ERR("could not parse xdc '" + xdc_file.string() + "': " + e.message + " (line " + std::to_string(e.line_number) + ")"); + // } + // else + // { + // return ERR("could not parse xdc '" + xdc_file.string() + "': " + e.message); + // } + // } - std::cout << gate_name << ": " << (cd.bel_type.has_value() ? enum_to_string(cd.bel_type.value()) : "NONE") << " - " - << (cd.loc.has_value() ? (enum_to_string(cd.loc.value().loc_type) + std::to_string(cd.loc.value().loc_x) + std::to_string(cd.loc.value().loc_y)) : "NONE") << std::endl; - } + // std::unordered_map gate_name_to_gate; + // for (const auto g : nl->get_gates()) + // { + // gate_name_to_gate[g->get_name()] = g; + // } - return OK({}); - } + // for (const auto& [gate_name, cd] : cell_data) + // { + // if (const auto it = gate_name_to_gate.find(gate_name); it == gate_name_to_gate.end()) + // { + // log_error("xilinx_toolbox", "Found gate name {} in xdc file but not in netlist!", gate_name); + // continue; + // } + // } + // return OK({}); + } + } // namespace xilinx_toolbox } // namespace hal \ No newline at end of file