diff --git a/CHANGELOG.md b/CHANGELOG.md index 6aa21373c37..22e62d9e132 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ All notable changes to this project will be documented in this file. ## [4.4.1](v4.4.1) - 2024-07-29 14:21:42+02:00 (urgency: medium) * fixed `hal_py.GateLibrary.gate_types` pybind * fixed `hal_py.NetlistFactory.load_netlist` pybind +* plugins + * updated `bitorder_propagation_plugin` + * added functionality to export collected bitorder information as a `.json` file to use external tools for propgating and solving conflicts, like SMT solvers ## [4.4.0](v4.4.0) - 2024-07-19 15:55:24+02:00 (urgency: medium) * **WARNING:** this release breaks the API of the `boolean_influence` and `bitorder_propagation` plugin diff --git a/cmake/detect_dependencies.cmake b/cmake/detect_dependencies.cmake index 9e4535a4419..ce1dda8d011 100644 --- a/cmake/detect_dependencies.cmake +++ b/cmake/detect_dependencies.cmake @@ -142,6 +142,27 @@ if(RapidJSON_FOUND AND NOT TARGET RapidJSON::RapidJSON) endif() +# ############################### +# #### Python support +# ############################### + +# set(Python_ADDITIONAL_VERSIONS 3.5 3.6 3.8) +find_package(Python3 COMPONENTS Interpreter Development) + +if(Python3_Interpreter_FOUND) + message(STATUS "Python3_INCLUDE_DIRS: ${Python3_INCLUDE_DIRS}") + message(STATUS "Python3_LIBRARIES: ${Python3_LIBRARIES}") + message(STATUS "PYTHON_MODULE_PREFIX: ${PYTHON_MODULE_PREFIX}") + message(STATUS "PYTHON_MODULE_EXTENSION: ${PYTHON_MODULE_EXTENSION}") +elseif(NOT Python3_Interpreter_FOUND) + set(Missing_package "TRUE") + + if(APPLE AND CMAKE_HOST_APPLE) + message(STATUS "To install python3 on MacOS using homebrew run following command:") + message(STATUS " brew install python3") + endif(APPLE AND CMAKE_HOST_APPLE) +endif(Python3_Interpreter_FOUND) + # ############################### # #### pybind11 @@ -199,28 +220,6 @@ set_target_properties(nlohmann_json::nlohmann_json PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/deps/nlohmann_json" ) - -# ############################### -# #### Python support -# ############################### - -# set(Python_ADDITIONAL_VERSIONS 3.5 3.6 3.8) -find_package(Python3 COMPONENTS Interpreter Development) - -if(Python3_Interpreter_FOUND) - message(STATUS "Python3_INCLUDE_DIRS: ${Python3_INCLUDE_DIRS}") - message(STATUS "Python3_LIBRARIES: ${Python3_LIBRARIES}") - message(STATUS "PYTHON_MODULE_PREFIX: ${PYTHON_MODULE_PREFIX}") - message(STATUS "PYTHON_MODULE_EXTENSION: ${PYTHON_MODULE_EXTENSION}") -elseif(NOT Python3_Interpreter_FOUND) - set(Missing_package "TRUE") - - if(APPLE AND CMAKE_HOST_APPLE) - message(STATUS "To install python3 on MacOS using homebrew run following command:") - message(STATUS " brew install python3") - endif(APPLE AND CMAKE_HOST_APPLE) -endif(Python3_Interpreter_FOUND) - # ############################### # #### Graphviz # ############################### diff --git a/plugins/bitorder_propagation/CMakeLists.txt b/plugins/bitorder_propagation/CMakeLists.txt index f4eecca58c3..63e22c7d313 100644 --- a/plugins/bitorder_propagation/CMakeLists.txt +++ b/plugins/bitorder_propagation/CMakeLists.txt @@ -10,5 +10,6 @@ if(PL_BITORDER_PROPAGATION OR BUILD_ALL_PLUGINS) HEADER ${BITORDER_PROPAGATION_INC} SOURCES ${BITORDER_PROPAGATION_SRC} ${BITORDER_PROPAGATION_PYTHON_SRC} PYDOC SPHINX_DOC_INDEX_FILE ${CMAKE_CURRENT_SOURCE_DIR}/documentation/bitorder_propagation.rst + LINK_LIBRARIES nlohmann_json::nlohmann_json ) endif() diff --git a/plugins/bitorder_propagation/include/bitorder_propagation/bitorder_propagation.h b/plugins/bitorder_propagation/include/bitorder_propagation/bitorder_propagation.h index 024a69dbc99..56435b61ef5 100644 --- a/plugins/bitorder_propagation/include/bitorder_propagation/bitorder_propagation.h +++ b/plugins/bitorder_propagation/include/bitorder_propagation/bitorder_propagation.h @@ -125,5 +125,29 @@ namespace hal */ Result*>, std::map>> propagate_bitorder(const std::vector*>>& src, const std::vector*>>& dst); + + /** + * @brief Export word composition, known bitorder and connectivity in .json format to solve with external tools. + * + * @param[in] src - The pairs of module and pin group with known bit order. + * @param[in] dst - The pairs of module and pin group with unknown bit order. + * @param[in] export_filepath - The filepath where the .json file should be written to. + * @returns OK and a map containing all known bit orders (including new and already known ones) on success, an error otherwise. + */ + Result*>, u32>> export_bitorder_propagation_information(const std::vector*>>& src, + const std::vector*>>& dst, + const std::string& export_filepath); + /** + * @brief Export word composition, known bitorder and connectivity in .json format to solve with external tools. + * + * @param[in] src - The known indices for the nets belonging to the given module pin groups. + * @param[in] dst - The pairs of module ID and pin group name with unknown bit order. + * @param[in] export_filepath - The filepath where the .json file should be written to. + * @returns OK and the mapping from each mdoule/pingroup pair to its index on success, an error otherwise. + */ + Result*>, u32>> export_bitorder_propagation_information(const std::map*>, std::map>& src, + const std::set*>>& dst, + const std::string& export_filepath); + } // namespace bitorder_propagation } // namespace hal \ No newline at end of file diff --git a/plugins/bitorder_propagation/python/python_bindings.cpp b/plugins/bitorder_propagation/python/python_bindings.cpp index fd1036731e1..2e6eb8401d3 100644 --- a/plugins/bitorder_propagation/python/python_bindings.cpp +++ b/plugins/bitorder_propagation/python/python_bindings.cpp @@ -241,6 +241,64 @@ namespace hal :rtype: dict[tuple(hal_py.Module,hal_py.ModulePinGroup),dict[hal_py.Net,int]] or None )"); + m.def( + "export_bitorder_propagation_information", + [](const std::map*>, std::map>& src, + const std::set*>>& dst, + const std::string& export_filepath) -> std::optional*>, u32>> { + const auto res = bitorder_propagation::export_bitorder_propagation_information(src, dst, export_filepath); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("src"), + py::arg("dst"), + py::arg("export_filepath"), + R"( + Export collected bitorder information like word composition, known bitorder and connectivity in .json format to solve with external tools. + + :param dict[tuple(hal_py.Module,hal_py.ModulePinGroup),dict[hal_py.Net,int]] src: The known indices for the nets belonging to the given module pin groups. + :param set[tuple(hal_py.Module,hal_py.ModulePinGroup)] dst: The pairs of module ID and pin group name with unknown bit order. + :param str export_filepath: The filepath where the .json file should be written to. + :returns: The mapping from each mdoule/pingroup pair to its index on success, ``None`` otherwise. + :rtype: dict[tuple(hal_py.Module, hal_py.ModulePinGroup), int] or None + )"); + + m.def( + "export_bitorder_propagation_information", + [](const std::vector*>>& src, + const std::vector*>>& dst, + const std::string& export_filepath) -> std::optional*>, u32>> { + const auto res = bitorder_propagation::export_bitorder_propagation_information(src, dst, export_filepath); + if (res.is_ok()) + { + return res.get(); + } + else + { + log_error("python_context", "{}", res.get_error().get()); + return std::nullopt; + } + }, + py::arg("src"), + py::arg("dst"), + py::arg("export_filepath"), + R"( + Export collected bitorder information like word composition, known bitorder and connectivity in .json format to solve with external tools. + + :param tuple(hal_py.Module,hal_py.ModulePinGroup) src: The pair of module and pin group with known bit order. + :param tuple(hal_py.Module,hal_py.ModulePinGroup) dst: The pair of module and pin group with unknown bit order. + :param str export_filepath: The filepath where the .json file should be written to. + :returns: The mapping from each mdoule/pingroup pair to its index on success, ``None`` otherwise. + :rtype: dict[tuple(hal_py.Module, hal_py.ModulePinGroup), int] or None + )"); + #ifndef PYBIND11_MODULE return m.ptr(); #endif // PYBIND11_MODULE diff --git a/plugins/bitorder_propagation/src/bitorder_propagation.cpp b/plugins/bitorder_propagation/src/bitorder_propagation.cpp index 4cdf2b7be16..c622e2e608e 100644 --- a/plugins/bitorder_propagation/src/bitorder_propagation.cpp +++ b/plugins/bitorder_propagation/src/bitorder_propagation.cpp @@ -1,12 +1,15 @@ #include "bitorder_propagation/bitorder_propagation.h" +#include "hal_core/netlist/decorators/boolean_function_net_decorator.h" #include "hal_core/netlist/gate.h" #include "hal_core/netlist/module.h" #include "hal_core/netlist/netlist.h" #include "hal_core/netlist/pins/gate_pin.h" #include "hal_core/netlist/pins/module_pin.h" +#include "nlohmann_json/json.hpp" #include +#include // #define PRINT_CONFLICT // #define PRINT_CONNECTIVITY @@ -1366,5 +1369,228 @@ namespace hal return OK(all_wellformed_module_pin_groups); } + + Result> export_bitorder_propagation_information(const std::vector*>>& src, + const std::vector*>>& dst, + const std::string& export_filepath) + { + std::map> known_bitorders; + std::set unknown_bitorders = {dst.begin(), dst.end()}; + + // collect known bit orders + for (auto& [m, pg] : src) + { + std::map src_bitorder; + + // TODO this is gonna crash if pin does not start at index 0 + for (u32 index = 0; index < pg->get_pins().size(); index++) + { + auto pin_res = pg->get_pin_at_index(index); + if (pin_res.is_error()) + { + return ERR_APPEND(pin_res.get_error(), "cannot propagate bit order: failed to get pin at index " + std::to_string(index) + " inside of pin group '" + pg->get_name() + "'"); + } + const ModulePin* pin = pin_res.get(); + + src_bitorder.insert({pin->get_net(), index}); + } + + known_bitorders.insert({{m, pg}, src_bitorder}); + } + + return export_bitorder_propagation_information(known_bitorders, unknown_bitorders, export_filepath); + } + + Result> export_bitorder_propagation_information(const std::map*>, std::map>& known_bitorders, + const std::set*>>& unknown_bitorders, + const std::string& export_filepath) + { + std::map, std::vector>>> connectivity_inwards; + std::map, std::vector>>> connectivity_outwards; + + std::set relevant_pin_groups = unknown_bitorders; + for (const auto& [kb, _] : known_bitorders) + { + relevant_pin_groups.insert(kb); + } + + std::map, std::map>> cache_outwards; + std::map, std::map>> cache_inwards; + + // Build connectivity + for (const auto& [m, pg] : relevant_pin_groups) + { + bool successors = pg->get_direction() == PinDirection::output; + + for (const auto& p : pg->get_pins()) + { + const auto starting_net = p->get_net(); + + std::set> visited_outwards; + const auto res_outwards = gather_connected_neighbors(starting_net, successors, relevant_pin_groups, false, nullptr, visited_outwards, cache_outwards); + if (res_outwards.is_error()) + { + return ERR_APPEND(res_outwards.get_error(), + "cannot porpagate bitorder: failed to gather bit indices outwards starting from the module with ID " + std::to_string(m->get_id()) + " and pin group " + + pg->get_name()); + } + const auto connected_outwards = res_outwards.get(); + + std::set> visited_inwards; + // NOTE when propagating inwards we guarantee the first propagation since otherwise we would stop at our starting pingroup + const auto res_inwards = gather_connected_neighbors(starting_net, !successors, relevant_pin_groups, true, m, visited_inwards, cache_inwards); + if (res_inwards.is_error()) + { + return ERR_APPEND(res_inwards.get_error(), + "cannot porpagate bitorder: failed to gather bit indices inwwards starting from the module with ID " + std::to_string(m->get_id()) + " and pin group " + + pg->get_name()); + } + const auto connected_inwards = res_inwards.get(); + + for (const auto& [org_mpg, nets] : connected_outwards) + { + connectivity_outwards[{{m, pg}, starting_net}].push_back({org_mpg, nets}); + } + + for (const auto& [org_mpg, nets] : connected_inwards) + { + connectivity_inwards[{{m, pg}, starting_net}].push_back({org_mpg, nets}); + } + } + } + + nlohmann::json info; + + // export word definitions + std::vector mpgs; + std::map mpg_to_idx; + std::vector> words; + std::map> word_definitions; + + for (const auto& [m, pg] : relevant_pin_groups) + { + std::vector nets; + std::vector nets_str; + + for (const auto& p : pg->get_pins()) + { + const auto& n = p->get_net(); + nets.push_back(n); + nets_str.push_back(BooleanFunctionNetDecorator(*n).get_boolean_variable_name()); + } + + word_definitions.insert({std::to_string(words.size()), nets_str}); + mpg_to_idx.insert({{m, pg}, mpg_to_idx.size()}); + mpgs.push_back({m, pg}); + words.push_back(nets); + } + + info["word_definitions"] = word_definitions; + + // export known bitorders + std::map> known_word_orders; + + for (u32 i = 0; i < mpgs.size(); i++) + { + const auto& mpg = mpgs.at(i); + + if (const auto it = known_bitorders.find(mpg); it != known_bitorders.end()) + { + std::vector ordered_nets_str; + + std::vector> net_index_vec = {it->second.begin(), it->second.end()}; + std::sort(net_index_vec.begin(), net_index_vec.end(), [](const auto& p1, const auto& p2) { return p1.second < p2.second; }); + for (const auto& [n, _idx] : net_index_vec) + { + ordered_nets_str.push_back(BooleanFunctionNetDecorator(*n).get_boolean_variable_name()); + } + + known_word_orders.insert({std::to_string(i), ordered_nets_str}); + } + } + + info["known_bit_order"] = known_word_orders; + + // export connectivity + std::map>> connected_words; + std::map>> connected_words_forward; + std::map>> connected_words_backward; + + for (u32 i = 0; i < mpgs.size(); i++) + { + const auto& mpg = mpgs.at(i); + const auto& nets = words.at(i); + + for (const auto& src_net : nets) + { + std::vector> connections; + std::vector> connections_forward; + std::vector> connections_backward; + + const auto it_in = connectivity_inwards.find({mpg, src_net}); + if (it_in != connectivity_inwards.end()) + { + for (const auto& [dst_mpg, dst_nets] : it_in->second) + { + for (const auto& dst_net : dst_nets) + { + connections.push_back({mpg_to_idx.at(dst_mpg), BooleanFunctionNetDecorator(*dst_net).get_boolean_variable_name()}); + connections_backward.push_back({mpg_to_idx.at(dst_mpg), BooleanFunctionNetDecorator(*dst_net).get_boolean_variable_name()}); + } + } + } + + const auto it_out = connectivity_outwards.find({mpg, src_net}); + if (it_out != connectivity_outwards.end()) + { + for (const auto& [dst_mpg, dst_nets] : it_out->second) + { + for (const auto& dst_net : dst_nets) + { + connections.push_back({mpg_to_idx.at(dst_mpg), BooleanFunctionNetDecorator(*dst_net).get_boolean_variable_name()}); + connections_forward.push_back({mpg_to_idx.at(dst_mpg), BooleanFunctionNetDecorator(*dst_net).get_boolean_variable_name()}); + } + } + } + + if (!connections.empty()) + { + const std::string identifier = "(" + std::to_string(i) + ", " + BooleanFunctionNetDecorator(*src_net).get_boolean_variable_name() + ")"; + + connected_words.insert({identifier, connections}); + + if (!connections_backward.empty()) + { + connected_words_backward.insert({identifier, connections_backward}); + } + + if (!connections_forward.empty()) + { + connected_words_forward.insert({identifier, connections_forward}); + } + } + } + } + + info["connected_words"] = connected_words; + info["connected_words_backward"] = connected_words_backward; + info["connected_words_forward"] = connected_words_forward; + + // Open a file in write mode + std::ofstream json_file(export_filepath); + + // Serialize the JSON object to the json_file + if (json_file.is_open()) + { + json_file << info.dump(4); // Pretty print with an indentation of 4 spaces + } + else + { + return ERR("cannot export bitorder information: failed to open file at path " + export_filepath + " for writing"); + } + + return OK(mpg_to_idx); + } + } // namespace bitorder_propagation } // namespace hal \ No newline at end of file diff --git a/plugins/gui/include/gui/graph_widget/items/nodes/gates/standard_graphics_gate.h b/plugins/gui/include/gui/graph_widget/items/nodes/gates/standard_graphics_gate.h index a67143bce06..97e1fe2bdc8 100644 --- a/plugins/gui/include/gui/graph_widget/items/nodes/gates/standard_graphics_gate.h +++ b/plugins/gui/include/gui/graph_widget/items/nodes/gates/standard_graphics_gate.h @@ -146,7 +146,8 @@ namespace hal QPointF mTextPosition[2]; - QVector mOutputPinPositions; + QVector mInputPinTextWidth; + QVector mOutputPinTextWidth; static const int sIconPadding; static const QSize sIconSize; diff --git a/plugins/gui/include/gui/module_model/module_item.h b/plugins/gui/include/gui/module_model/module_item.h index 537ffc27a10..d958cbfb828 100644 --- a/plugins/gui/include/gui/module_model/module_item.h +++ b/plugins/gui/include/gui/module_model/module_item.h @@ -36,6 +36,8 @@ namespace hal { + class ModuleModel; + /** * @ingroup gui * @brief An item in the ModuleModel. @@ -61,7 +63,14 @@ namespace hal * @param id - The id of the netlist item this ModuleItem represents * @param type - The type of the netlist item */ - ModuleItem(const u32 id, const TreeItemType type); + ModuleItem(const u32 id, const TreeItemType type, ModuleModel* model); + + /** + * Destructor. + * + * Must remove item from ModuleModel map as well. + */ + virtual ~ModuleItem(); /** * Given a set of ModuleItems (in a map [id]->[ModuleItem]) this function adds each ModuleItem of this set as @@ -142,5 +151,6 @@ namespace hal QString mModuleType; bool mHighlighted; + ModuleModel* mModuleModel; // reference to parent model }; } diff --git a/plugins/gui/include/gui/module_model/module_model.h b/plugins/gui/include/gui/module_model/module_model.h index 97d40885076..74dde2fb73d 100644 --- a/plugins/gui/include/gui/module_model/module_model.h +++ b/plugins/gui/include/gui/module_model/module_model.h @@ -52,6 +52,7 @@ namespace hal */ class ModuleModel : public BaseTreeModel { + friend class ModuleItem; Q_OBJECT /** diff --git a/plugins/gui/include/gui/plugin_relay/gui_plugin_manager.h b/plugins/gui/include/gui/plugin_relay/gui_plugin_manager.h index 9b40f7b1297..dc661327466 100644 --- a/plugins/gui/include/gui/plugin_relay/gui_plugin_manager.h +++ b/plugins/gui/include/gui/plugin_relay/gui_plugin_manager.h @@ -65,6 +65,7 @@ namespace hal { bool mUserInterface; bool mGuiExtensions; QString mCliOptions; + bool mFileFound; public: GuiPluginEntry(const QFileInfo& info); @@ -75,6 +76,8 @@ namespace hal { bool isLoaded() const { return mState == AutoLoad || mState == UserLoad; } bool isPlugin() const { return mState != NotAPlugin; } void updateFromLoaded(const BasePluginInterface* bpif, bool isUser, const QDateTime& modified = QDateTime()); + bool isFileFound() const { return mFileFound; } + void setFileFound(bool fnd) { mFileFound = fnd; } }; class GuiPluginManager; diff --git a/plugins/gui/src/graph_widget/items/nodes/gates/standard_graphics_gate.cpp b/plugins/gui/src/graph_widget/items/nodes/gates/standard_graphics_gate.cpp index d952955a7a4..9924f0240fd 100644 --- a/plugins/gui/src/graph_widget/items/nodes/gates/standard_graphics_gate.cpp +++ b/plugins/gui/src/graph_widget/items/nodes/gates/standard_graphics_gate.cpp @@ -169,10 +169,11 @@ void StandardGraphicsGate::paint(QPainter* painter, const QStyleOptionGraphicsIt QColor pinBackground = gContentManager->getGroupingManagerWidget()->getModel()->colorForItem(ItemType::Net, inpNetId); if (pinBackground.isValid()) { + float wbox = mInputPinTextWidth.at(i) > sPinFontHeight ? mInputPinTextWidth.at(i) : sPinFontHeight; QBrush lastBrush = painter->brush(); painter->setBrush(pinBackground); painter->setPen(QPen(pinBackground,0)); - painter->drawRoundRect(sPinOuterHorizontalSpacing,yText-sPinFontAscent,sPinFontHeight,sPinFontHeight,35,35); + painter->drawRoundRect(sPinOuterHorizontalSpacing,yText-sPinFontAscent,wbox,sPinFontHeight,35,35); painter->setBrush(lastBrush); pinTextColor = legibleColor(pinBackground); } @@ -208,7 +209,14 @@ void StandardGraphicsGate::paint(QPainter* painter, const QStyleOptionGraphicsIt QBrush lastBrush = painter->brush(); painter->setBrush(pinBackground); painter->setPen(QPen(pinBackground,0)); - painter->drawRoundRect(mWidth - sPinOuterHorizontalSpacing - sPinFontHeight,yText-sPinFontAscent,sPinFontHeight,sPinFontHeight,35,35); + float wbox = sPinFontHeight; + float xbox = mWidth - sPinOuterHorizontalSpacing - sPinFontHeight; + if (mOutputPinTextWidth.at(i) > wbox) + { + xbox -= (mOutputPinTextWidth.at(i) - wbox); + wbox = mOutputPinTextWidth.at(i); + } + painter->drawRoundRect(xbox,yText-sPinFontAscent,wbox,sPinFontHeight,35,35); painter->setBrush(lastBrush); pinTextColor = legibleColor(pinBackground); } @@ -217,7 +225,7 @@ void StandardGraphicsGate::paint(QPainter* painter, const QStyleOptionGraphicsIt sPen.setColor(pinTextColor); } painter->setPen(sPen); - painter->drawText(mOutputPinPositions.at(i), mOutputPins.at(i)); + painter->drawText(QPointF(mWidth - sPinOuterHorizontalSpacing - mOutputPinTextWidth.at(i), yText), mOutputPins.at(i)); } if (sLod < graph_widget_constants::sGateMaxLod) @@ -378,11 +386,10 @@ void StandardGraphicsGate::format(const bool& adjust_size_to_grid) qreal y = sColorBarHeight + sPinUpperVerticalSpacing + sPinFontAscent + sBaseline; + for (const QString& input_pin : mInputPins) + mInputPinTextWidth.append(pin_fm.size(0, input_pin).rwidth()); + for (const QString& output_pin : mOutputPins) - { - qreal x = mWidth - (pin_fm.size(0, output_pin).rwidth() + sPinOuterHorizontalSpacing); - mOutputPinPositions.append(QPointF(x, y)); - y += (sPinFontHeight + sPinInnerVerticalSpacing); - } + mOutputPinTextWidth.append(pin_fm.size(0, output_pin).rwidth()); } } diff --git a/plugins/gui/src/module_model/module_item.cpp b/plugins/gui/src/module_model/module_item.cpp index f3a1e531f79..9fadba89b7c 100644 --- a/plugins/gui/src/module_model/module_item.cpp +++ b/plugins/gui/src/module_model/module_item.cpp @@ -1,4 +1,5 @@ #include "gui/module_model/module_item.h" +#include "gui/module_model/module_model.h" #include "hal_core/netlist/module.h" @@ -8,11 +9,12 @@ namespace hal { -ModuleItem::ModuleItem(const u32 id, const TreeItemType type) : +ModuleItem::ModuleItem(const u32 id, const TreeItemType type, ModuleModel *model) : BaseTreeItem(), mId(id), mItemType(type), - mHighlighted(false) + mHighlighted(false), + mModuleModel(model) { switch(type) { @@ -40,6 +42,19 @@ ModuleItem::ModuleItem(const u32 id, const TreeItemType type) : break; } } + mModuleModel->mModuleItemMaps[(int)mItemType]->insertMulti(id,this); + } + + ModuleItem::~ModuleItem() + { + auto it = mModuleModel->mModuleItemMaps[(int)mItemType]->lowerBound(mId); + while (it != mModuleModel->mModuleItemMaps[(int)mItemType]->upperBound(mId)) + { + if (it.value() == this) + it = mModuleModel->mModuleItemMaps[(int)mItemType]->erase(it); + else + ++it; + } } void ModuleItem::appendExistingChildIfAny(const QMap& moduleMap) diff --git a/plugins/gui/src/module_model/module_model.cpp b/plugins/gui/src/module_model/module_model.cpp index 7047092b5b6..81c2361124e 100644 --- a/plugins/gui/src/module_model/module_model.cpp +++ b/plugins/gui/src/module_model/module_model.cpp @@ -182,14 +182,14 @@ namespace hal Module* parentModule = g->get_module(); ModuleItem* parentItem; bool insertToRoot = true; - ModuleItem* childItem = new ModuleItem(g->get_id(), ModuleItem::TreeItemType::Gate); + ModuleItem* childItem = new ModuleItem(g->get_id(), ModuleItem::TreeItemType::Gate, this); while (parentModule && insertToRoot) { parentItem = parentMap.value(parentModule); if (!parentItem) { - parentItem = new ModuleItem(parentModule->get_id(), ModuleItem::TreeItemType::Module); + parentItem = new ModuleItem(parentModule->get_id(), ModuleItem::TreeItemType::Module, this); parentMap.insert(parentModule, parentItem); } else @@ -224,10 +224,10 @@ namespace hal moduleAssignNets(); for(u32 id : gateIds) - newRootList.append(new ModuleItem(id, ModuleItem::TreeItemType::Gate)); + newRootList.append(new ModuleItem(id, ModuleItem::TreeItemType::Gate, this)); for(u32 id : netIds) - newRootList.append(new ModuleItem(id, ModuleItem::TreeItemType::Net)); + newRootList.append(new ModuleItem(id, ModuleItem::TreeItemType::Net, this)); for(auto item : newRootList) mRootItem->appendChild(item); @@ -284,50 +284,49 @@ namespace hal void ModuleModel::removeModule(const u32 id) { - auto it = mModuleMap.lowerBound(id); - while (it != mModuleMap.upperBound(id)) + QList trashcan; // access items to delete without map + + for (auto it = mModuleMap.lowerBound(id); it != mModuleMap.upperBound(id); ++it) + trashcan.append(it.value()); + + for (ModuleItem* item : trashcan) { - ModuleItem* item = it.value(); BaseTreeItem* parentItem = item->getParent(); - removeChildItem(item,parentItem); - - it = mModuleMap.erase(it); } } void ModuleModel::removeGate(const u32 id) { - auto it = mGateMap.lowerBound(id); - while (it != mGateMap.upperBound(id)) - { - ModuleItem* item = it.value(); - BaseTreeItem* parentItem = item->getParent(); + QList trashcan; // access items to delete without map - removeChildItem(item, parentItem); + for (auto it = mGateMap.lowerBound(id); it != mGateMap.upperBound(id); ++it) + trashcan.append(it.value()); - it = mGateMap.erase(it); + for (ModuleItem* item : trashcan) + { + BaseTreeItem* parentItem = item->getParent(); + removeChildItem(item,parentItem); } } void ModuleModel::removeNet(const u32 id) { - auto it = mNetMap.lowerBound(id); - while (it != mNetMap.upperBound(id)) - { - ModuleItem* item = it.value(); - BaseTreeItem* parentItem = item->getParent(); + QList trashcan; // access items to delete without map - removeChildItem(item, parentItem); + for (auto it = mNetMap.lowerBound(id); it != mNetMap.upperBound(id); ++it) + trashcan.append(it.value()); - it = mNetMap.erase(it); + for (ModuleItem* item : trashcan) + { + BaseTreeItem* parentItem = item->getParent(); + removeChildItem(item,parentItem); } } ModuleItem* ModuleModel::createChildItem(u32 id, ModuleItem::TreeItemType itemType, BaseTreeItem *parentItem) { - ModuleItem* retval = new ModuleItem(id, itemType); - mModuleItemMaps[(int)itemType]->insertMulti(id,retval); + ModuleItem* retval = new ModuleItem(id, itemType, this); if (!parentItem) parentItem = mRootItem; QModelIndex index = getIndexFromItem(parentItem); @@ -349,15 +348,6 @@ namespace hal while (itemToRemove->getChildCount()) { ModuleItem* childItem = static_cast(itemToRemove->getChildren().at(0)); - int ityp = static_cast(childItem->getType()); - auto it = mModuleItemMaps[ityp]->lowerBound(childItem->id()); - while (it != mModuleItemMaps[ityp]->upperBound(childItem->id())) - { - if (it.value() == childItem) - it = mModuleItemMaps[ityp]->erase(it); - else - ++it; - } removeChildItem(childItem,itemToRemove); } @@ -403,27 +393,25 @@ namespace hal void ModuleModel::handleModuleGateRemoved(Module* mod, u32 gateId) { + QList > trashcan; // item to delete, parent + if (mTempGateAssignment.isAccumulate()) mTempGateAssignment.removeGateFromModule(gateId,mod); else { - auto it = mGateMap.lowerBound(gateId); - while (it != mGateMap.upperBound(gateId)) + for (auto it = mGateMap.lowerBound(gateId); it != mGateMap.upperBound(gateId); ++it) { ModuleItem* item = it.value(); - if (!item->isToplevelItem()) - { - ModuleItem* parentItem = static_cast(item->getParent()); - if (parentItem->id() == mod->get_id()) - { - removeChildItem(item, parentItem); - it = mGateMap.erase(it); - continue; - } - } - ++it; + if (item->isToplevelItem()) continue;; + + ModuleItem* parentItem = static_cast(item->getParent()); + if (parentItem->id() == mod->get_id()) + trashcan.append(QPair(item, parentItem)); } } + + for (const QPair& trash : trashcan) + removeChildItem(trash.first, trash.second); } void ModuleModel::handleModuleGatesAssignBegin(Module* mod, u32 numberGates) @@ -592,8 +580,8 @@ namespace hal QSet parentsHandled; Q_ASSERT(gNetlist->get_gate_by_id(gateId)); - auto itGat = mGateMap.lowerBound(gateId); - while (itGat != mGateMap.upperBound(gateId)) + QList > trashcan; // item to delete, parent + for (auto itGat = mGateMap.lowerBound(gateId); itGat != mGateMap.upperBound(gateId); ++itGat) { ModuleItem* gatItem = itGat.value(); if (gatItem->isToplevelItem()) continue; @@ -602,16 +590,15 @@ namespace hal if (oldParentItem->id() != moduleId) { - removeChildItem(gatItem,oldParentItem); - itGat = mGateMap.erase(itGat); + trashcan.append(QPair(gatItem,oldParentItem)); } else { parentsHandled.insert(oldParentItem); - ++itGat; } - } + for (const QPair& trash : trashcan) + removeChildItem(trash.first, trash.second); if (!moduleId) return; for (auto itMod = mModuleMap.lowerBound(moduleId); itMod != mModuleMap.upperBound(moduleId); ++itMod) @@ -675,28 +662,30 @@ namespace hal newParentId = newParentModule->get_id(); } - auto itNet = mNetMap.lowerBound(netId); - while (itNet != mNetMap.upperBound(netId)) + QList > trashcan; // item to delete, parent + + for (auto itNet = mNetMap.lowerBound(netId); itNet != mNetMap.upperBound(netId); ++itNet) { if (itNet.value()->isToplevelItem()) continue; - + ModuleItem* netItem = itNet.value(); ModuleItem* oldParentItem = static_cast(netItem->getParent()); Q_ASSERT(oldParentItem); - if (newParentId == 0 || newParentId != oldParentItem->id()) { - removeChildItem(netItem,oldParentItem); - itNet = mNetMap.erase(itNet); + trashcan.append(QPair(netItem,oldParentItem)); + break; } else { parentsHandled.insert(oldParentItem); - ++itNet; } } + for (const QPair& trash : trashcan) + removeChildItem(trash.first, trash.second); + if (!newParentId) return; for (auto itMod = mModuleMap.lowerBound(newParentId); itMod != mModuleMap.upperBound(newParentId); ++itMod) { @@ -719,15 +708,13 @@ namespace hal u32 parentId = module->get_parent_module()->get_id(); Q_ASSERT(parentId > 0); - auto itSubm = mModuleMap.lowerBound(id); - while (itSubm != mModuleMap.upperBound(id)) + QList > trashcan; // item to delete, parent + + for (auto itSubm = mModuleMap.lowerBound(id); itSubm != mModuleMap.upperBound(id); ++itSubm) { ModuleItem* submItem = itSubm.value(); - if (submItem->isToplevelItem()) - { - ++itSubm; - continue; - } + if (submItem->isToplevelItem()) continue; + ModuleItem* oldParentItem = static_cast(submItem->getParent()); Q_ASSERT(oldParentItem); @@ -736,8 +723,7 @@ namespace hal if (moduleItemToBeMoved) { // remove tree item recursively - removeChildItem(submItem,oldParentItem); - itSubm = mModuleMap.erase(itSubm); + trashcan.append(QPair(submItem,oldParentItem)); } else { @@ -752,15 +738,15 @@ namespace hal oldParentItem->removeChild(submItem); endRemoveRows(); mIsModifying = false; - ++itSubm; } } else { parentsHandled.insert(oldParentItem); - ++itSubm; } } + for (const QPair& trash : trashcan) + removeChildItem(trash.first, trash.second); if (!parentId) return; for (auto itMod = mModuleMap.lowerBound(parentId); itMod != mModuleMap.upperBound(parentId); ++itMod) @@ -786,16 +772,7 @@ namespace hal if (moduleItemToBeMoved && !moduleItemReassigned) { - // stored item could not be reassigned, delete it - auto it = mModuleMap.lowerBound(id); - while (it != mModuleMap.upperBound(id)) - { - if (it.value() == moduleItemToBeMoved) - it = mModuleMap.erase(it); - else - ++it; - } - delete moduleItemToBeMoved; + removeChildItem(moduleItemToBeMoved, moduleItemToBeMoved->getParent()); } } diff --git a/plugins/gui/src/plugin_relay/gui_plugin_manager.cpp b/plugins/gui/src/plugin_relay/gui_plugin_manager.cpp index 1dbe94ebae6..590dfd2fc6c 100644 --- a/plugins/gui/src/plugin_relay/gui_plugin_manager.cpp +++ b/plugins/gui/src/plugin_relay/gui_plugin_manager.cpp @@ -339,6 +339,7 @@ namespace hal { if (gpe) { needUpdate = gpe->mFileModified != info.lastModified(); + gpe->setFileFound(true); } else { @@ -417,18 +418,25 @@ namespace hal { mEntries.clear(); mAvoid.clear(); mLookup.clear(); - for (auto it = pluginEntries.constBegin(); it != pluginEntries.constEnd(); ++it) + auto it = pluginEntries.begin(); + while (it != pluginEntries.end()) { GuiPluginEntry* gpe = it.value(); - if (gpe->isPlugin()) + if (gpe->isFileFound()) { - mLookup.insert(gpe->mName,mEntries.size()); - mEntries.append(gpe); + if (gpe->isPlugin()) + { + mLookup.insert(gpe->mName,mEntries.size()); + mEntries.append(gpe); + } + else + { + mAvoid.append(gpe); + } + ++it; } else - { - mAvoid.append(gpe); - } + it = pluginEntries.erase(it); } persist(); } @@ -763,7 +771,8 @@ namespace hal { mFileModified(info.lastModified()), mFeature(FacExtensionInterface::FacUnknown), mUserInterface(false), - mGuiExtensions(false) + mGuiExtensions(false), + mFileFound(true) {;} QVariant GuiPluginEntry::data(int icol) const @@ -813,6 +822,7 @@ namespace hal { } GuiPluginEntry::GuiPluginEntry(const QSettings *settings) + : mFileFound(false) { mState = (State) settings->value("state").toInt(); mName = settings->value("name").toString(); diff --git a/plugins/gui/src/selection_details_widget/selection_details_widget.cpp b/plugins/gui/src/selection_details_widget/selection_details_widget.cpp index 2de6d09cc74..375f8dac07f 100644 --- a/plugins/gui/src/selection_details_widget/selection_details_widget.cpp +++ b/plugins/gui/src/selection_details_widget/selection_details_widget.cpp @@ -407,17 +407,17 @@ namespace hal if (gSelectionRelay->numberSelectedModules()) { - ModuleItem sti(gSelectionRelay->selectedModulesList().at(0), ModuleItem::TreeItemType::Module); + ModuleItem sti(gSelectionRelay->selectedModulesList().at(0), ModuleItem::TreeItemType::Module, mModuleModel); singleSelectionInternal(&sti); } else if (gSelectionRelay->numberSelectedGates()) { - ModuleItem sti(gSelectionRelay->selectedGatesList().at(0), ModuleItem::TreeItemType::Gate); + ModuleItem sti(gSelectionRelay->selectedGatesList().at(0), ModuleItem::TreeItemType::Gate, mModuleModel); singleSelectionInternal(&sti); } else if (gSelectionRelay->numberSelectedNets()) { - ModuleItem sti(gSelectionRelay->selectedNetsList().at(0), ModuleItem::TreeItemType::Net); + ModuleItem sti(gSelectionRelay->selectedNetsList().at(0), ModuleItem::TreeItemType::Net, mModuleModel); singleSelectionInternal(&sti); } diff --git a/plugins/hawkeye/.gitignore b/plugins/hawkeye/.gitignore index 3ab9044d6ce..f8e4f04ad70 100644 --- a/plugins/hawkeye/.gitignore +++ b/plugins/hawkeye/.gitignore @@ -9,4 +9,5 @@ !documentation/**/* !scripts* !scripts/**/* -!.gitignore \ No newline at end of file +!.gitignore +!README.md \ No newline at end of file diff --git a/plugins/hawkeye/README.md b/plugins/hawkeye/README.md new file mode 100644 index 00000000000..df5bc80a18f --- /dev/null +++ b/plugins/hawkeye/README.md @@ -0,0 +1,5 @@ +# HAWKEYE + +This plugin was developed in the context of the academic publication titled "HAWKEYE - Recovering Symmetric Cryptography From Hardware Circuits" by Gregor Leaner, Christof Paar, Julian Speith, and Lukas Stennes published at IACR Crypto'24. You can find the paper on [IACR ePrint](https://eprint.iacr.org/2024/860.pdf) (free of charge) and [SpringerLink](https://link.springer.com/chapter/10.1007/978-3-031-68385-5_11). + +For an example application of HAWKEYE and to reproduce the results from the paper, we refer the reader to the [paper artifacts](https://artifacts.iacr.org/crypto/2024/a9/) hosted by the IACR. \ No newline at end of file diff --git a/plugins/logic_evaluator/include/logic_evaluator/logic_evaluator_select_gates.h b/plugins/logic_evaluator/include/logic_evaluator/logic_evaluator_select_gates.h index 6810817bfa8..7c9b775786d 100644 --- a/plugins/logic_evaluator/include/logic_evaluator/logic_evaluator_select_gates.h +++ b/plugins/logic_evaluator/include/logic_evaluator/logic_evaluator_select_gates.h @@ -45,8 +45,8 @@ Qt::CheckState mState; bool mSelectable; public: - SelectGateItem(u32 id, ModuleItem::TreeItemType type, bool isSel = true) - : ModuleItem(id, type), mState(Qt::Unchecked), mSelectable(isSel) {;} + SelectGateItem(u32 id, ModuleItem::TreeItemType type, ModuleModel* model, bool isSel = true) + : ModuleItem(id, type, model), mState(Qt::Unchecked), mSelectable(isSel) {;} Qt::CheckState state() const { return mState; } bool isSelectable() const { return mSelectable; } void setSelectable(bool isSel) { mSelectable = isSel; } diff --git a/plugins/logic_evaluator/src/logic_evaluator_select_gates.cpp b/plugins/logic_evaluator/src/logic_evaluator_select_gates.cpp index 53c50ce0769..c8a02a30b38 100644 --- a/plugins/logic_evaluator/src/logic_evaluator_select_gates.cpp +++ b/plugins/logic_evaluator/src/logic_evaluator_select_gates.cpp @@ -91,13 +91,13 @@ namespace hal { int SelectGateModel::insertModuleRecursion(const Module* mod, SelectGateItem* parentItem) { int countCheckable = 0; - SelectGateItem* child = new SelectGateItem(mod->get_id(), ModuleItem::TreeItemType::Module); + SelectGateItem* child = new SelectGateItem(mod->get_id(), ModuleItem::TreeItemType::Module, this); for (const Module* subm : mod->get_submodules()) countCheckable += insertModuleRecursion(subm, child); for (const Gate* g : mod->get_gates(nullptr, false)) { bool isSel = GuiExtensionLogicEvaluator::acceptGate(g); - child->appendChild(new SelectGateItem(g->get_id(), ModuleItem::TreeItemType::Gate, isSel)); + child->appendChild(new SelectGateItem(g->get_id(), ModuleItem::TreeItemType::Gate, this, isSel)); if (isSel) ++countCheckable; } if (!countCheckable) child->setSelectable(false); diff --git a/plugins/simulator/netlist_simulator_controller/src/simulation_process.cpp b/plugins/simulator/netlist_simulator_controller/src/simulation_process.cpp index 715c5b7c4df..fdf3a7a1d8f 100644 --- a/plugins/simulator/netlist_simulator_controller/src/simulation_process.cpp +++ b/plugins/simulator/netlist_simulator_controller/src/simulation_process.cpp @@ -233,6 +233,7 @@ namespace hal [this](int exitCode, QProcess::ExitStatus exitStatus){ this->handleFinished(exitCode,exitStatus); }); (*mProcessLog) << logCommand; + mProcessLog->flush(); mProcess->start(prog, args); @@ -247,13 +248,27 @@ namespace hal if (qApp) { - if (exec()) // event loop ended with non-zero value - return false; + // entering event loop + if (exec()) + return false; // event loop ended with non-zero value } else { + int msecs = 30000; + QString timeoutAfterSec = QString::fromStdString(mEngine->get_engine_property("timeout_after_sec")); + if (!timeoutAfterSec.isEmpty()) + { + bool ok; + msecs = timeoutAfterSec.toInt(&ok) * 1000; + if (!ok || msecs <= 0) msecs = -1; + } log_warning("simulation_plugin", "No QApplication running, event loop not entered, will poll for process to finish."); - if (!mProcess->waitForFinished()) return false; + if (!mProcess->waitForFinished(msecs)) + { + (*mProcessLog) << "

Process timeout after 30 sec.

\n"; + mProcessLog->flush(); + return false; + } handleReadyReadStandardError(); handleReadyReadStandardOutput(); }