diff --git a/CHANGELOG.md b/CHANGELOG.md index 10819f7f397..6aa21373c37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [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 + ## [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 * **WARNING:** this release contains many new unstable plugin APIs that will likely change in the future @@ -33,6 +37,7 @@ All notable changes to this project will be documented in this file. * added `json.hpp` from nlohmann to deps to offer a light weight json api * adapted cmake to consider the correct flags when finding and linking against the new version of Bitwuzla * miscellaneous + * added logic evaluator plugin * added backward compatibility for view management * slightly improved symbolic execution engine * added a version of `netlist_factory::load_netlist` that takes a path to a netlist file as well as a pointer to a gate library diff --git a/CURRENT_VERSION b/CURRENT_VERSION index fdc6698807a..cca25a93cd0 100644 --- a/CURRENT_VERSION +++ b/CURRENT_VERSION @@ -1 +1 @@ -4.4.0 +4.4.1 diff --git a/include/hal_core/plugin_system/plugin_parameter.h b/include/hal_core/plugin_system/plugin_parameter.h index ce964955d9a..cde2be570b5 100644 --- a/include/hal_core/plugin_system/plugin_parameter.h +++ b/include/hal_core/plugin_system/plugin_parameter.h @@ -32,7 +32,7 @@ namespace hal class PluginParameter { public: - enum ParameterType { Absent, Boolean, Color, ComboBox, Dictionary, ExistingDir, ExistingFile, Float, Gate, Integer, Module, NewFile, PushButton, String, TabName }; + enum ParameterType { Absent, Boolean, Color, ComboBox, Dictionary, ExistingDir, ExistingFile, Float, Gate, Integer, Label, Module, NewFile, PushButton, String, TabName }; private: ParameterType m_type; std::string m_tagname; diff --git a/plugins/.gitignore b/plugins/.gitignore index d85ee585e67..6e8f61e8cbf 100644 --- a/plugins/.gitignore +++ b/plugins/.gitignore @@ -25,6 +25,8 @@ !hgl_writer/**/* !liberty_parser* !liberty_parser/**/* +!logic_evaluator* +!logic_evaluator/**/* !module_identification* !module_identification/**/* !netlist_preprocessing* diff --git a/plugins/gui/include/gui/module_dialog/module_dialog.h b/plugins/gui/include/gui/module_dialog/module_dialog.h index 994d29807d1..dba29e66fca 100644 --- a/plugins/gui/include/gui/module_dialog/module_dialog.h +++ b/plugins/gui/include/gui/module_dialog/module_dialog.h @@ -133,9 +133,6 @@ namespace hal { QTabWidget* mTabWidget; ModuleProxyModel* mModuleTreeProxyModel; - ModuleSelectProxy* mModuleTableProxyModel; - ModuleSelectModel* mModuleSelectModel; - Searchbar* mSearchbar; QAction* mToggleSearchbar; ModuleSelectExclude mSelectExclude; diff --git a/plugins/gui/include/gui/module_model/module_model.h b/plugins/gui/include/gui/module_model/module_model.h index 5e22b8a66b0..b74c5c3fa69 100644 --- a/plugins/gui/include/gui/module_model/module_model.h +++ b/plugins/gui/include/gui/module_model/module_model.h @@ -36,6 +36,7 @@ #include #include #include +#include #include namespace hal @@ -153,6 +154,15 @@ namespace hal */ void populateTree(const QVector& modIds = {}, const QVector& gatIds = {}, const QVector& netIds = {}); + /** + * Clears current tree item model and repopulates it by a list of gates. This function will + * automatically load all parent modules to gates listed. Thus gates will be shown at their + * place within the module hierarchy. + * + * @param gates std::vector of gates to be added to the item model. + */ + void populateFromGatelist(const std::vector& gates); + /** * Add a module to the item model. For the specified module new ModuleItems are created and stored. * 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 bb5277a4eb0..6a08bc7aa19 100644 --- a/plugins/gui/include/gui/plugin_relay/gui_plugin_manager.h +++ b/plugins/gui/include/gui/plugin_relay/gui_plugin_manager.h @@ -62,6 +62,7 @@ namespace hal { bool mUserInterface; bool mGuiExtensions; QString mCliOptions; + bool mFileFound; public: GuiPluginEntry(const QFileInfo& info); @@ -72,6 +73,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/resources/stylesheet/dark.qss b/plugins/gui/resources/stylesheet/dark.qss index 2a1c41c4427..f96443036d0 100755 --- a/plugins/gui/resources/stylesheet/dark.qss +++ b/plugins/gui/resources/stylesheet/dark.qss @@ -1511,3 +1511,140 @@ hal--Overlay background : rgba(0, 0, 0, 150); } +hal--LogicEvaluatorDialog +{ + font-size : 14px; + color : #A9B7C6; + background : #515253; +} + +hal--LogicEvaluatorPingroup +{ + background : #717273; + border : 2px solid gray; + padding-top : 4px; + padding-bottom : 4px; + padding-left : 0px; + padding-right : 0px; +} + + +hal--LogicEvaluatorPingroup[output="true"] +{ + border-color : #A0A5A8 #414243 #414243 black; + border-width : 2 2 2 0; +} + +hal--LogicEvaluatorPingroup[output="false"] +{ + border-color : #A0A5A8 black #414243 #A0A5A8; + border-width : 2 0 2 2; +} + +hal--LogicEvaluatorValue +{ + min-width : 48; + max-width : 48; + min-height : 28; + max-height : 28; +} + +hal--LogicEvaluatorValue QSpinBox +{ + color : #CBD2D0; + font-family : "Iosevka"; + font-style : normal; + background-color : rgb(28, 28, 29); + border : none; +} + +hal--LogicEvaluatorValue QSpinBox::up-button +{ + color : #CBD2D0; + background-color : #505152; + border-style : solid; + border-color : #808182 #313233 #313233 #808182; + border-width : 1; +} + +hal--LogicEvaluatorValue QSpinBox::down-button +{ + color : #CBD2D0; + background-color : #505152; + border-style : solid; + border-color : #808182 #313233 #313233 #808182; + border-width : 1; +} + +/* +hal--LogicEvaluatorValue QSpinBox::up-arrow +{ + color : #CBD2D0; + background-color : rgb(128, 28, 29); +} + +hal--LogicEvaluatorValue QSpinBox::down-arrow +{ + color : #CBD2D0; + background-color : rgb(28, 28, 128); +} +*/ + +hal--LogicEvaluatorValue QLabel +{ + color : #CBD2D0; + font-family : "Iosevka"; + font-style : normal; + background-color : rgb(28, 28, 29); + border : none; +} + +hal--LogicEvaluatorPingroup > QLabel +{ + color : white; + font-family : "Iosevka"; + font-style : bold; + border : none; + background-color : none; + max-height : 28; +} + +hal--LogicEvaluatorPingroup QCheckBox +{ + font : bold "Iosevka"; + color : white; + background : black; + padding-top : 4px; + padding-bottom : 4px; + padding-left : 8px; + padding-right : 8px; + min-width : 250px; +} + +hal--LogicEvaluatorPingroup QCheckBox::indicator:checked +{ + background : rgb(255,0,0); + border : 1px solid black; +} + +hal--LogicEvaluatorPingroup QCheckBox::indicator:unchecked +{ + background : rgb(0,170,255); + border : 1px solid black; +} + +hal--LogicEvaluatorDialog > QTreeView +{ + border-style : solid; + border-color : #A0A5A8 black #414243 black; + border-width : 2 2 2 2; + background : black; + min-width : 400px; +} + +hal--LogicEvaluatorSelectGates +{ + qproperty-selForeground : #FFE8A0; + qproperty-selBackground : #102030; + min-width : 400px; +} diff --git a/plugins/gui/src/graph_widget/layouters/graph_layouter.cpp b/plugins/gui/src/graph_widget/layouters/graph_layouter.cpp index 0d56740554c..9a465eaaae1 100644 --- a/plugins/gui/src/graph_widget/layouters/graph_layouter.cpp +++ b/plugins/gui/src/graph_widget/layouters/graph_layouter.cpp @@ -778,7 +778,6 @@ namespace hal int iy0 = mNodeBoundingBox.y() * 2; float y0 = mCoordY[iy0].preLanes() * sLaneSpacing + sVRoadPadding; mCoordY[iy0].setOffset(y0); - mYValues.append(mCoordY.value(iy0).lanePosition(0)); auto ityLast = mCoordY.begin(); for (auto itNext = ityLast + 1; itNext != mCoordY.end(); ++itNext) { @@ -789,7 +788,6 @@ namespace hal { // netjunction -> endpoint itNext->setOffsetYje(ityLast.value(), mJunctionMinDistanceY.value(iy1)); - mYValues.append(itNext.value().lanePosition(0)); } else { @@ -802,6 +800,15 @@ namespace hal } ityLast = itNext; } + + iy0 = mNodeBoundingBox.y() * 2; + auto ity = mCoordY.find(iy0); + while(ity != mCoordY.end()) + { + mYValues.append(ity.value().lanePosition(0)); + iy0 += 2; + ity = mCoordY.find(iy0); + } } void GraphLayouter::placeGates() diff --git a/plugins/gui/src/main_window/plugin_parameter_dialog.cpp b/plugins/gui/src/main_window/plugin_parameter_dialog.cpp index 6fc63c0f613..f7a04ba3c32 100644 --- a/plugins/gui/src/main_window/plugin_parameter_dialog.cpp +++ b/plugins/gui/src/main_window/plugin_parameter_dialog.cpp @@ -132,6 +132,13 @@ namespace hal { mWidgetMap[parTagname] = floatBox; break; } + case PluginParameter::Label: + { + QLabel* label = new QLabel(this); + label->setText(parDefault); + mWidgetMap[parTagname] = label; + break; + } case PluginParameter::String: { QLineEdit* ledit = new QLineEdit(this); @@ -247,6 +254,11 @@ namespace hal { par.set_value(QString::number(floatBox->value()).toStdString()); break; } + case PluginParameter::Label: + { + par.set_value(std::string()); + break; + } case PluginParameter::String: { const QLineEdit* ledit = static_cast(w); diff --git a/plugins/gui/src/module_dialog/module_dialog.cpp b/plugins/gui/src/module_dialog/module_dialog.cpp index 4e2a651c25f..c5b00c9f4a4 100644 --- a/plugins/gui/src/module_dialog/module_dialog.cpp +++ b/plugins/gui/src/module_dialog/module_dialog.cpp @@ -96,11 +96,6 @@ namespace hal { mTreeView->setModel(mModuleTreeProxyModel); mTreeView->expandAll(); - mModuleTableProxyModel = new ModuleSelectProxy(this), - mModuleTableProxyModel->setSourceModel(mTableView->model()); - mTableView->setModel(mModuleTableProxyModel); - - mButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel, this); layout->addWidget(mButtonBox, 3, 0, 1, 3, Qt::AlignHCenter); @@ -116,7 +111,7 @@ namespace hal { if(mTabWidget->currentWidget() == mTreeView) mSearchbar->setColumnNames(mModuleTreeProxyModel->getColumnNames()); else - mSearchbar->setColumnNames(mModuleTableProxyModel->getColumnNames()); + mSearchbar->setColumnNames(static_cast(mTableView->model())->getColumnNames()); connect(mTabWidget, &QTabWidget::currentChanged, this, &ModuleDialog::handleCurrentTabChanged); connect(mToggleSearchbar, &QAction::triggered, this, &ModuleDialog::handleToggleSearchbar); @@ -128,7 +123,7 @@ namespace hal { connect(mTreeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ModuleDialog::handleTreeSelectionChanged); connect(mSearchbar, &Searchbar::triggerNewSearch, mModuleTreeProxyModel, &ModuleProxyModel::startSearch); - connect(mSearchbar, &Searchbar::triggerNewSearch, mModuleTableProxyModel, &ModuleSelectProxy::startSearch); + connect(mSearchbar, &Searchbar::triggerNewSearch, static_cast(mTableView->model()), &ModuleSelectProxy::startSearch); } void ModuleDialog::enableButtons() @@ -247,7 +242,7 @@ namespace hal { if(mTabWidget->currentWidget() == mTreeView) mSearchbar->setColumnNames(mModuleTreeProxyModel->getColumnNames()); else - mSearchbar->setColumnNames(mModuleTableProxyModel->getColumnNames()); + mSearchbar->setColumnNames(static_cast(mTableView->model())->getColumnNames()); mTreeView->clearSelection(); mTableView->clearSelection(); mSearchbar->clear(); diff --git a/plugins/gui/src/module_model/module_model.cpp b/plugins/gui/src/module_model/module_model.cpp index e428195be7e..5516cf0b1a1 100644 --- a/plugins/gui/src/module_model/module_model.cpp +++ b/plugins/gui/src/module_model/module_model.cpp @@ -118,6 +118,47 @@ namespace hal endResetModel(); } + void ModuleModel::populateFromGatelist(const std::vector &gates) + { + setIsModifying(true); + beginResetModel(); + clear(); + + QMap parentMap; + for (const Gate* g : gates) + { + Module* parentModule = g->get_module(); + ModuleItem* parentItem; + bool insertToRoot = true; + ModuleItem* childItem = new ModuleItem(g->get_id(), ModuleItem::TreeItemType::Gate); + + while (parentModule && insertToRoot) + { + parentItem = parentMap.value(parentModule); + if (!parentItem) + { + parentItem = new ModuleItem(parentModule->get_id(), ModuleItem::TreeItemType::Module); + parentMap.insert(parentModule, parentItem); + } + else + { + insertToRoot = false; + } + parentItem->appendChild(childItem); + parentModule = parentModule->get_parent_module(); + childItem = parentItem; + } + + if (insertToRoot) + { + mRootItem->appendChild(parentItem); + } + } + + setIsModifying(false); + endResetModel(); + } + void ModuleModel::populateTree(const QVector& modIds, const QVector& gateIds, const QVector& netIds) { setIsModifying(true); diff --git a/plugins/gui/src/plugin_relay/gui_plugin_manager.cpp b/plugins/gui/src/plugin_relay/gui_plugin_manager.cpp index 96dba50feb4..261f44e4620 100644 --- a/plugins/gui/src/plugin_relay/gui_plugin_manager.cpp +++ b/plugins/gui/src/plugin_relay/gui_plugin_manager.cpp @@ -308,6 +308,7 @@ namespace hal { if (gpe) { needUpdate = gpe->mFileModified != info.lastModified(); + gpe->setFileFound(true); } else { @@ -386,18 +387,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(); } @@ -732,7 +740,8 @@ namespace hal { mFileModified(info.lastModified()), mFeature(FacExtensionInterface::FacUnknown), mUserInterface(false), - mGuiExtensions(false) + mGuiExtensions(false), + mFileFound(true) {;} QVariant GuiPluginEntry::data(int icol) const @@ -782,6 +791,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/python/python_editor.cpp b/plugins/gui/src/python/python_editor.cpp index b986c7c716f..fd18cbfd866 100644 --- a/plugins/gui/src/python/python_editor.cpp +++ b/plugins/gui/src/python/python_editor.cpp @@ -868,6 +868,7 @@ namespace hal GraphContext* ctx = gGraphContextManager->getContextById(ctxId); if (ctx) ctx->endChange(); } + mBlockedContextIds.clear(); mFileModifiedBar->setHidden(true); } diff --git a/plugins/gui_extension_demo/python/python_bindings.cpp b/plugins/gui_extension_demo/python/python_bindings.cpp index 41ab6935f64..93ed06a2c1b 100644 --- a/plugins/gui_extension_demo/python/python_bindings.cpp +++ b/plugins/gui_extension_demo/python/python_bindings.cpp @@ -54,12 +54,14 @@ namespace hal .value("ComboBox", PluginParameter::ComboBox, R"(Combo box to select string from semicolon separated input list.)") .value("Dictionary", PluginParameter::Dictionary, R"(Key value pairs (string).)") .value("ExistingDir", PluginParameter::ExistingDir, R"(Existing directory.)") + .value("ExistingFile",PluginParameter::ExistingFile,R"(Existing file.)") .value("Float", PluginParameter::Float, R"(Floating point number.)") .value("Gate", PluginParameter::Gate, R"(Gate ID.)") .value("Integer", PluginParameter::Integer, R"(Integer number.)") .value("Module", PluginParameter::Gate, R"(Module ID.)") .value("NewFile", PluginParameter::NewFile, R"(New file name.)") .value("PushButton", PluginParameter::PushButton, R"(Push Button.)") + .value("Label", PluginParameter::Label, R"(Text Label.)") .value("String", PluginParameter::String, R"(String value.)") .value("TabName", PluginParameter::TabName, R"(Tab name for structuring other elements.)") .export_values(); 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/CMakeLists.txt b/plugins/logic_evaluator/CMakeLists.txt new file mode 100644 index 00000000000..0e0db794751 --- /dev/null +++ b/plugins/logic_evaluator/CMakeLists.txt @@ -0,0 +1,24 @@ +option(PL_LOGIC_EVALUATOR "PL_LOGIC_EVALUATOR" ON) +if(PL_LOGIC_EVALUATOR OR BUILD_ALL_PLUGINS) + + find_package(Qt5 COMPONENTS Core Gui Widgets REQUIRED) + + if(Qt5Core_FOUND) + message(VERBOSE "Qt5Core_INCLUDE_DIRS: ${Qt5Core_INCLUDE_DIRS}") + elseif(NOT Qt5Core_FOUND) + message(STATUS "Qt5Core not found for logic_evaluator") + endif(Qt5Core_FOUND) + + + file(GLOB_RECURSE LOGIC_EVALUATOR_INC ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) + file(GLOB_RECURSE LOGIC_EVALUATOR_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) + file(GLOB_RECURSE LOGIC_EVALUATOR_PYTHON_SRC ${CMAKE_CURRENT_SOURCE_DIR}/python/*.cpp) + qt5_wrap_cpp(MOC_HDR ${LOGIC_EVALUATOR_INC}) + + hal_add_plugin(logic_evaluator + SHARED + HEADER ${LOGIC_EVALUATOR_INC} + SOURCES ${LOGIC_EVALUATOR_SRC} ${LOGIC_EVALUATOR_PYTHON_SRC} ${MOC_HDR} + LINK_LIBRARIES PUBLIC gui netlist_simulator_controller Qt5::Core Qt5::Gui Qt5::Widgets) + +endif() diff --git a/plugins/logic_evaluator/include/logic_evaluator/logic_evaluator_dialog.h b/plugins/logic_evaluator/include/logic_evaluator/logic_evaluator_dialog.h new file mode 100644 index 00000000000..de68f9649b1 --- /dev/null +++ b/plugins/logic_evaluator/include/logic_evaluator/logic_evaluator_dialog.h @@ -0,0 +1,95 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include "hal_core/netlist/boolean_function.h" + +namespace hal { + class SimulationInput; + + class Gate; + + class Net; + + class LogicEvaluatorPingroup; + + class LogicEvaluatorTruthtableModel; + + class LogicEvaluatorDialog : public QDialog + { + Q_OBJECT + + struct SharedLibHandle + { + QString fnSharedLib; + void* handle; + int (*get)(int); + void (*set)(int,int); + void (*calc)(void); + void close(); + SharedLibHandle() : handle(nullptr), get(nullptr), set(nullptr), calc(nullptr) {;} + ~SharedLibHandle() { close(); }; + }; + + std::vector mGates; + SimulationInput* mSimulationInput; + QList mEvaluationOrder; + QList mInputs; + QList mOutputs; + SharedLibHandle mSharedLib; + QHash mExternalArrayIndex; + QHash mSignals; + QMenuBar* mMenuBar; + QAction* mActionCompile; + QAction* mActionIndicate; + LogicEvaluatorTruthtableModel* mTruthtable; + + void calculateEvaluationOrder(); + void recalcCompiled(); + void recalcInterpreted(); + void visualizeResultsInNetlist(); + void omitNetlistVisualization(); + public Q_SLOTS: + void recalc(); + private Q_SLOTS: + void handleCompiledToggled(bool checked); + void handleIndicateToggled(bool checked); + void handleRelaunchTriggered(); + void handleTruthtableTriggered(); + public: + LogicEvaluatorDialog(const std::vector& gates, bool skipCompile, QWidget* parent = nullptr); + ~LogicEvaluatorDialog(); + bool compile(); + }; +} diff --git a/plugins/logic_evaluator/include/logic_evaluator/logic_evaluator_pingroup.h b/plugins/logic_evaluator/include/logic_evaluator/logic_evaluator_pingroup.h new file mode 100644 index 00000000000..2e95957abc1 --- /dev/null +++ b/plugins/logic_evaluator/include/logic_evaluator/logic_evaluator_pingroup.h @@ -0,0 +1,103 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include "hal_core/netlist/boolean_function.h" + +namespace hal { + + class Net; + + class LogicEvaluatorHexSpinbox : public QSpinBox + { + Q_OBJECT + protected: + QValidator::State validate(QString &input, int &pos) const override; + int valueFromText(const QString &text) const override; + virtual QString textFromValue(int val) const override; + public: + LogicEvaluatorHexSpinbox(QWidget* parent = nullptr); + }; + + class LogicEvaluatorCheckBox : public QCheckBox + { + Q_OBJECT + const Net* mNet; + public: + LogicEvaluatorCheckBox(const Net* n, QWidget* parent = nullptr); + QPair getValue() const; + void setValue(const Net* n, BooleanFunction::Value val); + }; + + class LogicEvaluatorValue : public QWidget + { + Q_OBJECT + QLabel* mLabel; + LogicEvaluatorHexSpinbox* mSpinBox; + + private Q_SLOT: + void handleSpinBoxValueChanged(int val); + Q_SIGNALS: + void valueChanged(int val); + public: + LogicEvaluatorValue(int nbits, QWidget* parent = nullptr); + int value() const; + void setValue(int val); + bool isLabel() const { return mLabel!=nullptr; } + }; + + class LogicEvaluatorPingroup : public QFrame + { + Q_OBJECT + Q_PROPERTY(bool output READ output WRITE setOutput); + + QList mPinList; + LogicEvaluatorValue* mGroupValue; + bool mOutput; + + void constructor(const std::vector& nets, const QString& grpName = QString()); + Q_SIGNALS: + void triggerRecalc(); + private Q_SLOT: + void handleGroupValueChanged(int val); + void handleCheckStateChanged(int state); + public: + LogicEvaluatorPingroup(const std::vector& nets, bool outp, const QString& grpName, QWidget* parent = nullptr); + LogicEvaluatorPingroup(const Net*, bool outp, QWidget* parent = nullptr); + bool output() const { return mOutput; } + void setOutput(bool outp) { mOutput = outp; } + int size() const { return mPinList.size(); } + QPair getValue(int index) const; + void setValue(const Net* n, BooleanFunction::Value val); + }; +} 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 new file mode 100644 index 00000000000..6810817bfa8 --- /dev/null +++ b/plugins/logic_evaluator/include/logic_evaluator/logic_evaluator_select_gates.h @@ -0,0 +1,106 @@ + // 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 + #include + #include + #include "gui/module_model/module_model.h" + + class QDialogButtonBox; + class QCheckBox; + + namespace hal { + class Gate; + + class ModuleProxyModel; + + class Searchbar; + + class SelectGateItem : public ModuleItem + { + Qt::CheckState mState; + bool mSelectable; + public: + SelectGateItem(u32 id, ModuleItem::TreeItemType type, bool isSel = true) + : ModuleItem(id, type), mState(Qt::Unchecked), mSelectable(isSel) {;} + Qt::CheckState state() const { return mState; } + bool isSelectable() const { return mSelectable; } + void setSelectable(bool isSel) { mSelectable = isSel; } + void setState(Qt::CheckState stat) { mState = stat; } + }; + + class LogicEvaluatorSelectGates; + + class SelectGateModel : public ModuleModel + { + Q_OBJECT + std::vector mSelectedGates; + LogicEvaluatorSelectGates* mParentDialog; + + int insertModuleRecursion(const Module* mod, SelectGateItem* parentItem = nullptr); + QPair setCheckedRecursion(bool applySet, BaseTreeItem* parentItem, const QSet& selectedGateIds = QSet() ); + void setModuleStateRecursion(SelectGateItem* item, Qt::CheckState stat); + void setSelectedGatesRecursion(SelectGateItem* item = nullptr); + Q_SIGNALS: + void selectionStateChanged(bool empty); + public: + SelectGateModel(QObject* parent = nullptr); + void setChecked(const std::vector &gates); + Qt::ItemFlags flags(const QModelIndex &index) const override; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; + const std::vector& selectedGates() const {return mSelectedGates; } + }; + + class LogicEvaluatorSelectGates : public QDialog + { + Q_OBJECT + + Q_PROPERTY(QColor selBackground READ selBackground WRITE setSelBackground) + Q_PROPERTY(QColor selForeground READ selForeground WRITE setSelForeground) + + QTreeView* mTreeView; + SelectGateModel* mSelectGateModel; + ModuleProxyModel* mProxyModel; + Searchbar* mSearchbar; + QCheckBox* mCompile; + QDialogButtonBox* mButtonBox; + QColor mSelBackground; + QColor mSelForeground; + public Q_SLOTS: + void accept() override; + private Q_SLOTS: + void handleSelectionStateChanged(bool empty); + public: + LogicEvaluatorSelectGates(const std::vector &gates, QWidget* parent = nullptr); + + QColor selBackground() const { return mSelBackground; } + void setSelBackground(QColor bg) { mSelBackground = bg; } + QColor selForeground() const { return mSelForeground; } + void setSelForeground(QColor fg) { mSelForeground = fg; } + }; + } diff --git a/plugins/logic_evaluator/include/logic_evaluator/logic_evaluator_truthtable.h b/plugins/logic_evaluator/include/logic_evaluator/logic_evaluator_truthtable.h new file mode 100644 index 00000000000..29c74e38c45 --- /dev/null +++ b/plugins/logic_evaluator/include/logic_evaluator/logic_evaluator_truthtable.h @@ -0,0 +1,101 @@ +// 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 +#include +#include +#include + +namespace hal { + class Net; + + class LogicEvaluatorTruthtableColumn + { + int mRows; + int* mArray; + public: + LogicEvaluatorTruthtableColumn(int nrows, QList values); + ~LogicEvaluatorTruthtableColumn(); + int data(int irow) const; + bool lessThan(const LogicEvaluatorTruthtableColumn& other, const QList& sortRows) const; + }; + + class LogicEvaluatorTruthtableModel : public QAbstractTableModel + { + Q_OBJECT + public: + enum DisplayFormat{ ZeroOne, LowHigh, BlueRed, MAXFORMAT }; + private: + DisplayFormat mDisplayFormat; + QList mInputList; + QList mOutputList; + QList mColumnList; + int mInputSize; + int mOutputSize; + public Q_SLOTS: + void sortModelRow(int irow); + void sortModelRows(const QList& sortRows); + public: + LogicEvaluatorTruthtableModel(const QList& inpList, const QList& outList, QObject* parent = nullptr); + ~LogicEvaluatorTruthtableModel(); + QList getNets() const { return mInputList + mOutputList; } + QMap selectedColumn(int icol) const; + + void setDisplayFormat(DisplayFormat df); + void addColumn(LogicEvaluatorTruthtableColumn* letc); + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + }; + + class LogicEvaluatorTruthtableSort : public QDialog + { + Q_OBJECT + QComboBox* mSortKey[5]; + QList mNets; + public: + LogicEvaluatorTruthtableSort(QList& nets, QWidget* parent = nullptr); + QList sortOrder() const; + }; + + class LogicEvaluatorTruthtable : public QDialog + { + Q_OBJECT + QAction* mActionDisplayFormat[LogicEvaluatorTruthtableModel::MAXFORMAT]; + QAction* mActionSort; + LogicEvaluatorTruthtableModel* mModel; + int mColumnDubbleClicked; + private Q_SLOT: + void handleDisplayFormatChanged(QAction* act); + void handleSortTriggered(); + void handleColumnDubbleClicked(int icol); + public: + LogicEvaluatorTruthtable(LogicEvaluatorTruthtableModel* model, QWidget* parent = nullptr); + QMap selectedColumn() const; + }; +} diff --git a/plugins/logic_evaluator/include/logic_evaluator/plugin_logic_evaluator.h b/plugins/logic_evaluator/include/logic_evaluator/plugin_logic_evaluator.h new file mode 100644 index 00000000000..deaf0dd6fe4 --- /dev/null +++ b/plugins/logic_evaluator/include/logic_evaluator/plugin_logic_evaluator.h @@ -0,0 +1,106 @@ +// 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/plugin_system/plugin_interface_base.h" +#include "hal_core/plugin_system/gui_extension_interface.h" +#include + +Q_DECLARE_METATYPE(std::string) + +namespace hal +{ + + class LogicEvaluator; + class NetlistSimulator; + + class PLUGIN_API LogicEvaluatorPlugin : public BasePluginInterface + { + public: + LogicEvaluatorPlugin(); + + std::string get_name() const override; + std::string get_version() const override; + std::string get_description() const override; + + void on_load() override; + void on_unload() override; + std::set get_dependencies() const override; + }; + + class GuiExtensionLogicEvaluator : public GuiExtensionInterface + { + + public: + static bool acceptGate(const Gate* g); + /** + * @brief Default constructor for `GuiExtensionEvaluator`. + */ + GuiExtensionLogicEvaluator() = default; + + /** + * @brief Get a vector of configurable parameters. + * + * @returns The vector of parameters. + */ + std::vector get_parameter() const override; + + /** + * @brief Set values for a vector of configurable parameters. + * + * @param[in] params - The parameters including their values. + */ + void set_parameter(const std::vector& params) override; + + + /** + * Contribution to context menu. + * This function gets called when a context menu in GUI graphical netlist view pops up. Plugins that + * want to add their own entries in context menu will return one ContextMenuContribution record per + * entry line. + * + * @param[in] nl - The current netlist in GUI + * @param[in] mods - List of selected modules + * @param[in] gats - List of selected gates + * @param[in] nets - List of selected nets + * @return + */ + virtual std::vector get_context_contribution(const Netlist* nl, const std::vector& mods, const std::vector& gats, const std::vector& nets) override; + + /** + * Call from GUI to execute function. + * This function gets called when user selected a plugin contribution (see get_context_contribution) in + * context menu of GUI graphical netlist view or when user clicked a push button in contributed plugin menu + * + * @param[in] tag - The function tagname (unique identifier) + * @param[in] nl - The current netlist in GUI + * @param[in] mods - List of selected modules + * @param[in] gats - List of selected gates + * @param[in] nets - List of selected nets + */ + virtual void execute_function(std::string tag, Netlist* nl, const std::vector& mods, const std::vector& gats, const std::vector& nets) override; + }; +} // namespace hal diff --git a/plugins/logic_evaluator/src/logic_evaluator_dialog.cpp b/plugins/logic_evaluator/src/logic_evaluator_dialog.cpp new file mode 100644 index 00000000000..bc4082d683e --- /dev/null +++ b/plugins/logic_evaluator/src/logic_evaluator_dialog.cpp @@ -0,0 +1,592 @@ +#include "logic_evaluator/logic_evaluator_dialog.h" +#include "logic_evaluator/logic_evaluator_pingroup.h" +#include "netlist_simulator_controller/simulation_input.h" +#include "hal_core/netlist/gate.h" +#include "gui/gui_globals.h" +#include "gui/grouping/grouping_manager_widget.h" +#include "gui/module_model/module_model.h" +#include "logic_evaluator/logic_evaluator_select_gates.h" +#include "logic_evaluator/logic_evaluator_truthtable.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace hal { + + const char* LOGIC_EVALUATOR_GET = "logic_evaluator_get"; + const char* LOGIC_EVALUATOR_SET = "logic_evaluator_set"; + const char* LOGIC_EVALUATOR_CALC = "logic_evaluator_calc"; + const char* COMPILER = "gcc"; + + LogicEvaluatorDialog::LogicEvaluatorDialog(const std::vector& gates, bool skipCompile, QWidget *parent) + : QDialog(parent), mGates(gates), mSimulationInput(new SimulationInput), mActionCompile(nullptr), mActionIndicate(nullptr) + { + setAttribute(Qt::WA_DeleteOnClose); + setWindowTitle(QString("Logic Evaluator %1 Gates").arg(gates.size())); + + if (gates.empty()) + { + log_warning("logic_evaluator", "No eligible gates selected for logic evaluator, window will close"); + close(); + } + + mSimulationInput->add_gates(gates); + mSimulationInput->compute_net_groups(); + + calculateEvaluationOrder(); + QHBoxLayout* topLayout = new QHBoxLayout(this); + topLayout->setSpacing(0); + QVBoxLayout* inpLayout = new QVBoxLayout; + inpLayout->setSpacing(4); + QVBoxLayout* outLayout = new QVBoxLayout; + outLayout->setSpacing(4); + + // input pingroups + std::unordered_set inputNets = mSimulationInput->get_input_nets(); + for (SimulationInput::NetGroup grp : mSimulationInput->get_net_groups()) + { + if (!grp.is_input) continue; + for (const Net* n : grp.get_nets()) + { + auto it = inputNets.find(n); + if (it != inputNets.end()) inputNets.erase(it); + } + LogicEvaluatorPingroup* lep = new LogicEvaluatorPingroup(grp.get_nets(), false, QString::fromStdString(grp.get_name()), this); + connect(lep, &LogicEvaluatorPingroup::triggerRecalc, this, &LogicEvaluatorDialog::recalc); + mInputs.append(lep); + inpLayout->addWidget(lep); + } + + // input single pins + for (const Net* n : inputNets) + { + LogicEvaluatorPingroup* lep = new LogicEvaluatorPingroup(n, false, this); + connect(lep, &LogicEvaluatorPingroup::triggerRecalc, this, &LogicEvaluatorDialog::recalc); + mInputs.append(lep); + inpLayout->addWidget(lep); + } + + // output pingroups + std::unordered_set outputNets(mSimulationInput->get_output_nets().begin(),mSimulationInput->get_output_nets().end()); + for (SimulationInput::NetGroup grp : mSimulationInput->get_net_groups()) + { + if (grp.is_input) continue; + bool isOutputGroup = true; + + std::vector temp; + for (const Net* n : grp.get_nets()) + { + auto it = outputNets.find(n); + if (it == outputNets.end()) + { + isOutputGroup = false; + break; + } + else + { + outputNets.erase(it); + temp.push_back(n); + } + } + if (!isOutputGroup) + { + for (const Net* n : temp) + outputNets.insert(n); + continue; + } + LogicEvaluatorPingroup* lep = new LogicEvaluatorPingroup(grp.get_nets(), true, QString::fromStdString(grp.get_name()), this); + connect(lep, &LogicEvaluatorPingroup::triggerRecalc, this, &LogicEvaluatorDialog::recalc); + mOutputs.append(lep); + outLayout->addWidget(lep); + } + + // output single pins + for (const Net* n : outputNets) + { + QStringList netName; + netName.append(QString::fromStdString(n->get_name())); + LogicEvaluatorPingroup* lep = new LogicEvaluatorPingroup(n, true, this); + connect(lep, &LogicEvaluatorPingroup::triggerRecalc, this, &LogicEvaluatorDialog::recalc); + mOutputs.append(lep); + outLayout->addWidget(lep); + } + + QTreeView* tview = new QTreeView(this); + ModuleModel* tmodel = new ModuleModel(this); + tmodel->populateFromGatelist(gates); + tview->setModel(tmodel); + tview->expandAll(); + tview->setColumnWidth(0,250); + tview->setColumnWidth(1,40); + tview->setColumnWidth(2,110); + + mMenuBar = new QMenuBar(this); + QMenu* options = mMenuBar->addMenu("Options"); + mActionCompile = options->addAction("Run compiled logic"); + connect(mActionCompile, &QAction::toggled, this, &LogicEvaluatorDialog::handleCompiledToggled); + mActionCompile->setCheckable(true); + mActionCompile->setChecked(false); + mActionIndicate = options->addAction("Show in graphic view"); + connect(mActionIndicate, &QAction::toggled, this, &LogicEvaluatorDialog::handleIndicateToggled); + mActionIndicate->setCheckable(true); + QMenu* launch = mMenuBar->addMenu("Launch"); + QAction* relaunch = launch->addAction("Relaunch"); + connect(relaunch, &QAction::triggered, this, &LogicEvaluatorDialog::handleRelaunchTriggered); + QAction* ttable = launch->addAction("Truth Table"); + connect(ttable, &QAction::triggered, this, &LogicEvaluatorDialog::handleTruthtableTriggered); + + inpLayout->addStretch(); + outLayout->addStretch(); + topLayout->addLayout(inpLayout); + topLayout->addWidget(tview); + topLayout->addLayout(outLayout); + topLayout->setMenuBar(mMenuBar); + + tview->setMinimumWidth(400); + + if (!skipCompile) + { + mActionCompile->setChecked(true); + } + + QStyle* s = style(); + + s->unpolish(this); + s->polish(this); + + recalc(); + } + + LogicEvaluatorDialog::~LogicEvaluatorDialog() + { + delete mSimulationInput; + } + + void LogicEvaluatorDialog::handleRelaunchTriggered() + { + LogicEvaluatorSelectGates lesg(mGates, this); + if (lesg.exec() == QDialog::Accepted) + lower(); + } + + void LogicEvaluatorDialog::handleTruthtableTriggered() + { + if (!mSharedLib.handle) return; + if (!mTruthtable) + { + QList inpList; + QList outList; + for (const LogicEvaluatorPingroup* lepg : mInputs) + for (int i=lepg->size()-1; i>=0; i--) + inpList.append(lepg->getValue(i).first); + for (const LogicEvaluatorPingroup* lepg : mOutputs) + for (int i=lepg->size()-1; i>=0; i--) + outList.append(lepg->getValue(i).first); + if (inpList.isEmpty() || outList.isEmpty()) return; + if (inpList.size() > 10) + { + log_warning("logic_evaluator", "Cannot generate truth table for {} logic inputs.", inpList.size()); + return; + } + mTruthtable = new LogicEvaluatorTruthtableModel(inpList,outList,this); + + int maxInput = 1 << inpList.size(); + + for (int inputVal = 0; inputVal < maxInput; inputVal++) + { + QList values; + int mask = 1; + for (const Net* n : inpList) + { + int bitVal = (inputVal&mask) ? 1 : 0; + values.append(bitVal); + mSharedLib.set(mExternalArrayIndex[n], bitVal); + mask <<= 1; + } + mSharedLib.calc(); + for (const Net* n : outList) + { + int bitVal = mSharedLib.get(mExternalArrayIndex[n]); + values.append(bitVal); + } + mTruthtable->addColumn(new LogicEvaluatorTruthtableColumn(inpList.size()+outList.size(),values)); + } + } + if (!mTruthtable) return; + + LogicEvaluatorTruthtable lett(mTruthtable, this); + if (lett.exec() == QDialog::Accepted) + { + QMap vals = lett.selectedColumn(); + for (auto it = vals.constBegin(); it != vals.constEnd(); ++it) + { + const Net* n = it.key(); + BooleanFunction::Value bv = it.value() ? BooleanFunction::Value::ONE : BooleanFunction::Value::ZERO; + for (LogicEvaluatorPingroup* lepg : mInputs) + lepg->setValue(n,bv); + } + } + } + + void LogicEvaluatorDialog::handleCompiledToggled(bool checked) + { + if (checked && !mSharedLib.handle) + { + compile(); + if (!mSharedLib.handle) + mActionCompile->setChecked(false); + } + } + + void LogicEvaluatorDialog::handleIndicateToggled(bool checked) + { + if (checked) + recalc(); + else + omitNetlistVisualization(); + } + + void LogicEvaluatorDialog::SharedLibHandle::close() + { + if (handle) + { + dlclose(handle); + handle = nullptr; + } + QFile::remove(fnSharedLib); + } + + bool LogicEvaluatorDialog::compile() + { + // get input signals + QString codeEvalFunction; + mExternalArrayIndex.clear(); + + for (const LogicEvaluatorPingroup* lepg : mInputs) + { + for (int i=0; isize(); i++) + { + QPair v = lepg->getValue(i); + const Net* n = v.first; + int sz = mExternalArrayIndex.size(); + codeEvalFunction += QString(" // input[%1] - Net %2 <%3>;\n").arg(sz).arg(n->get_id()).arg(n->get_name().c_str()); + mExternalArrayIndex[n] = sz; + } + } + + // propagate by gates + for (const Gate* g : mEvaluationOrder) + { + QMap referableOutputPins; + + for (const GatePin* gp : g->get_type()->get_output_pins()) + { + QString pinNameOut = QString::fromStdString(gp->get_name()); + const Net* nOut = g->get_fan_out_net(gp); + if (!mExternalArrayIndex.contains(nOut)) + { + int sz = mExternalArrayIndex.size(); + mExternalArrayIndex[nOut] = sz; + } + + QString theFunction = QString::fromStdString(g->get_boolean_function(gp).to_string()); + for (const GatePin* gp : g->get_type()->get_input_pins()) + { + QString pinNameIn = QString::fromStdString(gp->get_name()); + const Net* nIn = g->get_fan_in_net(gp); + int inxIn = mExternalArrayIndex.value(nIn,-1); + if (inxIn < 0) return false; + QString ccVar = QString("logic_evaluator_signals[%1]").arg(inxIn); + theFunction.replace(pinNameIn, ccVar); + } + for (auto it = referableOutputPins.constBegin(); it != referableOutputPins.constEnd(); ++it) + { + theFunction.replace(it.key(),it.value()); + } + + int inxOut = mExternalArrayIndex.value(nOut,-1); + if (inxOut < 0) return false; + codeEvalFunction += QString(" logic_evaluator_signals[%1] = %2;\n").arg(inxOut).arg(theFunction); + + referableOutputPins[pinNameOut] = QString("logic_evaluator_signals[%1]").arg(inxOut); + } + } + + for (LogicEvaluatorPingroup* lepg : mOutputs) + { + int k = lepg->size(); + for (int i=0; igetValue(i).first; + int inx = mExternalArrayIndex.value(n,-1); + if (inx < 0) return false; + codeEvalFunction += QString(" // output[%1] - Net %2 <%3>;\n").arg(inx).arg(n->get_id()).arg(n->get_name().c_str()); + } + } + QString ccode; + ccode += QString("int logic_evaluator_signals[%1];\n\n").arg(mExternalArrayIndex.size()); + ccode += "void " + QString(LOGIC_EVALUATOR_SET); + ccode += "(int inx, int val) {\n" + " logic_evaluator_signals[inx] = val;\n" + "}\n\n"; + ccode += "int " + QString(LOGIC_EVALUATOR_GET); + ccode += "(int inx) {\n" + " return logic_evaluator_signals[inx];\n" + "}\n\n"; + ccode += "void " + QString(LOGIC_EVALUATOR_CALC); + ccode += "() {\n"; + ccode += codeEvalFunction + "\n}\n"; + + // write code to file + QTemporaryFile ftemp(QDir().temp().absoluteFilePath("logic_evaluator_shared_lib_XXXXXX.c")); + ftemp.setAutoRemove(true); + if (!ftemp.open()) + return false; + mSharedLib.fnSharedLib = ftemp.fileName(); + mSharedLib.fnSharedLib.replace(QRegularExpression("\.c$"), QString(".%1").arg(LIBRARY_FILE_EXTENSION)); + ftemp.write(ccode.toUtf8()); + ftemp.close(); + + // compile code + QProcess proc(this); + QStringList args; + args << "-shared" << "-Wall" << "-Werror" << "-fpic" << "-o" << mSharedLib.fnSharedLib << ftemp.fileName(); + if (proc.execute(COMPILER, args) < 0) + { + log_warning("logic_evaluator", "Failed to run compiler '{}', cannot compile logic.", COMPILER); + return false; + } + proc.waitForStarted(); + proc.waitForFinished(); + + if (proc.exitCode() || proc.exitStatus() != QProcess::NormalExit) + { + log_warning("logic_evaluator", "Failed to compile '{}', stdout: '{}', stderr: '{}", ftemp.fileName().toStdString(), proc.readAllStandardOutput().constData(), proc.readAllStandardError().constData()); + return false; + } + + // load shared libraray + + mSharedLib.handle = dlopen(mSharedLib.fnSharedLib.toUtf8().constData(), RTLD_LAZY); + if (!mSharedLib.handle) + { + log_warning("logic_evaluator", "Failed to load shared library '{}' dlerror: '{}'.", mSharedLib.fnSharedLib.toStdString(), dlerror()); + return false; + } + + // reset errors + dlerror(); + + mSharedLib.get = (int(*)(int)) dlsym(mSharedLib.handle, LOGIC_EVALUATOR_GET); + const char* dlsymError = dlerror(); + if (dlsymError) { + log_warning("logic_evaluator", "Cannot resolve symbol '{}' in shared library, dlerror: '{}'.", LOGIC_EVALUATOR_GET, dlsymError); + mSharedLib.close(); + return false; + } + + mSharedLib.set = (void(*)(int,int)) dlsym(mSharedLib.handle, LOGIC_EVALUATOR_SET); + dlsymError = dlerror(); + if (dlsymError) { + log_warning("logic_evaluator", "Cannot resolve symbol '{}' in shared library, dlerror: '{}'.", LOGIC_EVALUATOR_SET, dlsymError); + mSharedLib.close(); + return false; + } + + mSharedLib.calc = (void(*)(void)) dlsym(mSharedLib.handle, LOGIC_EVALUATOR_CALC); + dlsymError = dlerror(); + if (dlsymError) { + log_warning("logic_evaluator", "Cannot resolve symbol '{}' in shared library, dlerror: '{}'.", LOGIC_EVALUATOR_CALC, dlsymError); + mSharedLib.close(); + return false; + } + + log_info("logic_evaluator", "Temporary shared library '{}' successfully build and loaded.", mSharedLib.fnSharedLib.toStdString()); + + return true; + } + + void LogicEvaluatorDialog::recalc() + { + mSignals.clear(); + if (mActionCompile->isChecked() && mSharedLib.handle) + recalcCompiled(); + else + recalcInterpreted(); + + for (LogicEvaluatorPingroup* lepg : mOutputs) + { + int k = lepg->size(); + for (int i=0; igetValue(i).first; + lepg->setValue(n, mSignals.value(n, BooleanFunction::Value::X)); + } + } + + if (mActionIndicate->isChecked()) + visualizeResultsInNetlist(); + } + + void LogicEvaluatorDialog::recalcCompiled() + { + for (const LogicEvaluatorPingroup* lepg : mInputs) + { + for (int i=0; isize(); i++) + { + QPair v = lepg->getValue(i); + const Net* n = v.first; + int inx = mExternalArrayIndex.value(n,-1); + if (inx < 0) + { + log_warning("logic_evaluator", "No shared library index for input net id={} '{}'.", n->get_id(), n->get_name()); + return; + } + mSharedLib.set(inx, (int) v.second); + } + } + + mSharedLib.calc(); + + for (auto it=mExternalArrayIndex.begin(); it!=mExternalArrayIndex.end(); ++it) + { + const Net* n = it.key(); + int val = mSharedLib.get(it.value()); + mSignals[n] = (BooleanFunction::Value) val; + } + } + + void LogicEvaluatorDialog::recalcInterpreted() + { + // get input signals + for (const LogicEvaluatorPingroup* lepg : mInputs) + { + for (int i=0; isize(); i++) + { + QPair v = lepg->getValue(i); + mSignals[v.first] = v.second; + } + } + + // propagate by gates + for (const Gate* g : mEvaluationOrder) + { + std::unordered_map gateSignals; + for (const GatePin* gp : g->get_type()->get_input_pins()) + { + const Net* n = g->get_fan_in_net(gp); + gateSignals[gp->get_name()] = mSignals.value(n, BooleanFunction::Value::X); + } + for (const GatePin* gp : g->get_type()->get_output_pins()) + { + const Net* n = g->get_fan_out_net(gp); + auto res = g->get_boolean_function(gp).evaluate(gateSignals); + if (res.is_ok()) + mSignals[n] = res.get(); + else + log_warning("logic_evaluator", "Failed to evaluate boolean function '{}'.", g->get_boolean_function(gp).to_string()); + } + } + } + + void LogicEvaluatorDialog::calculateEvaluationOrder() + { + mEvaluationOrder.clear(); + QHash > undeterminedInput; + + // setup hash, declare all inpus as undetermined; + for (const Gate* g: mSimulationInput->get_gates()) + { + undeterminedInput.insert(g, g->get_fan_in_nets()); + } + + std::unordered_set inputSignals = mSimulationInput->get_input_nets(); + + int resolved = 1; + while (!undeterminedInput.isEmpty() && resolved) + { + resolved = 0; + std::unordered_set outputSignals; + auto it = undeterminedInput.begin(); + while (it != undeterminedInput.end()) + { + auto jt = it.value().begin(); + while (jt != it.value().end()) + { + if (inputSignals.find(*jt) == inputSignals.end()) + ++jt; + else + jt = it.value().erase(jt); + } + + // has no more undetermined inputs ? + if (it.value().empty()) + { + mEvaluationOrder.append(it.key()); + for (const Net* n : it.key()->get_fan_out_nets()) + outputSignals.insert(n); + it = undeterminedInput.erase(it); + ++resolved; + } + else + { + ++it; + } + } + inputSignals = outputSignals; + } + if (!undeterminedInput.isEmpty()) + { + std::string leftover; + for (const Gate* g : undeterminedInput.keys()) + leftover += " [" + std::to_string(g->get_id()) + ',' + g->get_name() + ']'; + log_warning("logic_evaluator", "Cannot determine evaluation order, {} gate(s) left with undetermined input: {}.", undeterminedInput.size(), leftover); + } + } + + void LogicEvaluatorDialog::visualizeResultsInNetlist() + { + GroupingTableModel* gtm = gContentManager->getGroupingManagerWidget()->getModel(); + const char* color[] = {"#707071", "#102080", "#802010" }; + static const char* grpNames[3] = {"x state", "0 state", "1 state"}; + Grouping* grp[3]; + for (int i=0; i<3; i++) + { + grp[i] = gtm->groupingByName(grpNames[i]); + if (!grp[i]) + { + grp[i] = gNetlist->create_grouping(grpNames[i]); + gtm->recolorGrouping(grp[i]->get_id(),QColor(color[i])); + } + } + + for (auto it = mSignals.constBegin(); it != mSignals.constEnd(); ++it) + { + int grpIndex = 1 + (int) it.value(); + Q_ASSERT(grpIndex >= 0 && grpIndex <= 2); + grp[grpIndex]->assign_net(const_cast(it.key()),true); + } + } + + void LogicEvaluatorDialog::omitNetlistVisualization() + { + GroupingTableModel* gtm = gContentManager->getGroupingManagerWidget()->getModel(); + static const char* grpNames[3] = {"x state", "0 state", "1 state"}; + for (int i=0; i<3; i++) + { + Grouping* grp = gtm->groupingByName(grpNames[i]); + if (grp) gNetlist->delete_grouping(grp); + } + } + +} diff --git a/plugins/logic_evaluator/src/logic_evaluator_pingroup.cpp b/plugins/logic_evaluator/src/logic_evaluator_pingroup.cpp new file mode 100644 index 00000000000..618e7c039ce --- /dev/null +++ b/plugins/logic_evaluator/src/logic_evaluator_pingroup.cpp @@ -0,0 +1,196 @@ +#include "logic_evaluator/logic_evaluator_pingroup.h" +#include +#include +#include +#include "hal_core/netlist/net.h" + +namespace hal { + + LogicEvaluatorHexSpinbox::LogicEvaluatorHexSpinbox(QWidget* parent) + : QSpinBox(parent) + {;} + + int LogicEvaluatorHexSpinbox::valueFromText(const QString &text) const + { + return text.toInt(nullptr,16); + } + + QString LogicEvaluatorHexSpinbox::textFromValue(int val) const + { + return QString::number(val,16).toUpper(); + } + + QValidator::State LogicEvaluatorHexSpinbox::validate(QString &input, int &pos) const + { + Q_UNUSED(pos); + if (input.isEmpty()) + return QValidator::Intermediate; + bool ok; + int val = input.toInt(&ok,16); + if (ok && 0 <= val && val <= maximum()) + return QValidator::Acceptable; + return QValidator::Invalid; + } + +//------------------------------------------------------------------- + + LogicEvaluatorValue::LogicEvaluatorValue(int nbits, QWidget* parent) + : QWidget(parent), mLabel(nullptr), mSpinBox(nullptr) + { + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setMargin(0); + if (nbits > 1) + { + mSpinBox = new LogicEvaluatorHexSpinbox(this); + mSpinBox->setMinimum(0); + mSpinBox->setMaximum((1 << nbits)-1); + mSpinBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + connect(mSpinBox, qOverload(&QSpinBox::valueChanged), this, &LogicEvaluatorValue::handleSpinBoxValueChanged); + layout->addWidget(mSpinBox); + } + else + { + mLabel = new QLabel(this); + layout->addWidget(mLabel); + } + } + + void LogicEvaluatorValue::handleSpinBoxValueChanged(int val) + { + Q_EMIT valueChanged(val); + } + + int LogicEvaluatorValue::value() const + { + if (mSpinBox) + return mSpinBox->value(); + return mLabel->text().toInt(); + } + + void LogicEvaluatorValue::setValue(int val) + { + if (mSpinBox) + mSpinBox->setValue(val); + else + mLabel->setText(QString::number(val,16)); + } + + //------------------------------------------------------------------- + + LogicEvaluatorCheckBox::LogicEvaluatorCheckBox(const Net* n, QWidget* parent) + : QCheckBox(QString::fromStdString(n->get_name()),parent), mNet(n) + {;} + + QPair LogicEvaluatorCheckBox::getValue() const + { + return QPair(mNet, isChecked()?BooleanFunction::Value::ONE:BooleanFunction::Value::ZERO); + } + + void LogicEvaluatorCheckBox::setValue(const Net* n, BooleanFunction::Value val) + { + if (n != mNet) return; + setChecked(val==BooleanFunction::Value::ONE); + } + //------------------------------------------------------------------- + + LogicEvaluatorPingroup::LogicEvaluatorPingroup(const std::vector &nets, bool outp, const QString& grpName, QWidget* parent) + : QFrame(parent), mOutput(outp) + { + constructor(nets,grpName); + } + + LogicEvaluatorPingroup::LogicEvaluatorPingroup(const Net* net, bool outp, QWidget* parent) + : QFrame(parent), mOutput(outp) + { + std::vector nets; + nets.push_back(net); + constructor(nets); + } + + void LogicEvaluatorPingroup::constructor(const std::vector &nets, const QString &grpName) + { + int nbits = nets.size(); + QHBoxLayout* topLayout = new QHBoxLayout(this); + topLayout->setAlignment(Qt::AlignTop); + topLayout->setContentsMargins(mOutput?0:8,4,mOutput?8:0,4); + QVBoxLayout* valLayout = new QVBoxLayout; + valLayout->setSpacing(4); + valLayout->setMargin(0); + + if (!grpName.isEmpty()) + { + QLabel* lab = new QLabel(grpName, this); + valLayout->addWidget(lab,0,Qt::AlignBottom|Qt::AlignHCenter); + } + mGroupValue = new LogicEvaluatorValue(mOutput?0:nbits, this); + connect(mGroupValue, &LogicEvaluatorValue::valueChanged, this, &LogicEvaluatorPingroup::handleGroupValueChanged); + valLayout->addWidget(mGroupValue,0, Qt::AlignTop|Qt::AlignHCenter); + + QVBoxLayout* pinLayout = new QVBoxLayout; + for (const Net* net : nets) + { + LogicEvaluatorCheckBox* lecb = new LogicEvaluatorCheckBox(net, this); + connect(lecb, &QCheckBox::stateChanged, this, &LogicEvaluatorPingroup::handleCheckStateChanged); + if (mOutput) + lecb->setDisabled(true); + else + { + lecb->setLayoutDirection(Qt::RightToLeft); + } + mPinList.prepend(lecb); + pinLayout->addWidget(lecb); + } + + if (mOutput) + { + topLayout->addLayout(pinLayout); + topLayout->addSpacing(20); + topLayout->addLayout(valLayout); + } + else + { + topLayout->addLayout(valLayout); + topLayout->addSpacing(20); + topLayout->addLayout(pinLayout); + } + mGroupValue->setValue(0); + } + + void LogicEvaluatorPingroup::handleCheckStateChanged(int state) + { + Q_UNUSED(state); + + int mask = 1 << (mPinList.size()-1); + int val = 0; + for (LogicEvaluatorCheckBox* cb : mPinList) + { + if (cb->isChecked()) val |= mask; + mask >>= 1; + } + mGroupValue->setValue(val); + if (mGroupValue->isLabel()) + Q_EMIT triggerRecalc(); + } + + void LogicEvaluatorPingroup::handleGroupValueChanged(int val) + { + int mask = 1 << (mPinList.size()-1); + for (LogicEvaluatorCheckBox* cb : mPinList) + { + cb->setChecked(val&mask); + mask >>= 1; + } + Q_EMIT triggerRecalc(); + } + + QPair LogicEvaluatorPingroup::getValue(int index) const + { + return mPinList.at(index)->getValue(); + } + + void LogicEvaluatorPingroup::setValue(const Net* n, BooleanFunction::Value val) + { + for (LogicEvaluatorCheckBox* cb : mPinList) + cb->setValue(n,val); + } +} diff --git a/plugins/logic_evaluator/src/logic_evaluator_select_gates.cpp b/plugins/logic_evaluator/src/logic_evaluator_select_gates.cpp new file mode 100644 index 00000000000..53c50ce0769 --- /dev/null +++ b/plugins/logic_evaluator/src/logic_evaluator_select_gates.cpp @@ -0,0 +1,278 @@ +#include "logic_evaluator/logic_evaluator_select_gates.h" +#include "logic_evaluator/logic_evaluator_dialog.h" +#include "logic_evaluator/plugin_logic_evaluator.h" +#include "gui/module_model/module_proxy_model.h" +#include "gui/searchbar/searchbar.h" +#include "hal_core/netlist/gate.h" +#include "gui/gui_globals.h" +#include +#include +#include +#include + +namespace hal { + QVariant SelectGateModel::data(const QModelIndex& index, int role) const + { + const SelectGateItem* item = dynamic_cast(getItemFromIndex(index)); + + switch (role) + { + case Qt::ForegroundRole: + if (item && item->getType() == ModuleItem::TreeItemType::Gate && item->state() == Qt::Checked) + { + if (mParentDialog) + return mParentDialog->selForeground(); + else + return ModuleModel::data(index, role); + } + break; + case Qt::BackgroundRole: + if (item && item->getType() == ModuleItem::TreeItemType::Gate && item->state() == Qt::Checked) + { + if (mParentDialog) + return mParentDialog->selBackground(); + else + return ModuleModel::data(index, role); + } + break; + case Qt::CheckStateRole: + if (!item->isSelectable()) return QVariant(); + if (item && !index.column()) return item->state(); + break; + } + return ModuleModel::data(index, role); + } + + bool SelectGateModel::setData(const QModelIndex& index, const QVariant& value, int role) + { + if (role != Qt::CheckStateRole) return false; + SelectGateItem* item = dynamic_cast(getItemFromIndex(index)); + if (!item) return false; + switch(item->getType()) { + case ModuleItem::TreeItemType::Module: + setModuleStateRecursion(item, (Qt::CheckState) value.toInt()); + break; + case ModuleItem::TreeItemType::Gate: + item->setState((Qt::CheckState) value.toInt()); + setCheckedRecursion(false, mRootItem); + break; + case ModuleItem::TreeItemType::Net: + Q_ASSERT(1==0); + break; + } + bool stateWasEmpty = mSelectedGates.empty(); + setSelectedGatesRecursion(); + if (stateWasEmpty != mSelectedGates.empty()) + Q_EMIT selectionStateChanged(mSelectedGates.empty()); + return true; + } + + void SelectGateModel::setModuleStateRecursion(SelectGateItem* item, Qt::CheckState stat) + { + item->setState(stat); + QModelIndex inx0 = getIndexFromItem(item); + QModelIndex inx1 = createIndex(inx0.row(), 2, inx0.internalPointer()); + Q_EMIT dataChanged(inx0,inx1); + for (BaseTreeItem* bti : item->getChildren()) + { + SelectGateItem* child = dynamic_cast(bti); + if (!child->isSelectable()) continue; + setModuleStateRecursion(child,stat); + } + } + + + SelectGateModel::SelectGateModel(QObject* parent) + : mParentDialog(dynamic_cast(parent)) + { + insertModuleRecursion(gNetlist->get_top_module()); + } + + int SelectGateModel::insertModuleRecursion(const Module* mod, SelectGateItem* parentItem) + { + int countCheckable = 0; + SelectGateItem* child = new SelectGateItem(mod->get_id(), ModuleItem::TreeItemType::Module); + 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)); + if (isSel) ++countCheckable; + } + if (!countCheckable) child->setSelectable(false); + if (parentItem) + parentItem->appendChild(child); + else + mRootItem->appendChild(child); + return countCheckable; + } + + QPair SelectGateModel::setCheckedRecursion(bool applySet, BaseTreeItem *parentItem, const QSet &selectedGateIds) + { + bool hasChecked = false; + bool hasUnchecked = false; + for (BaseTreeItem* bti : parentItem->getChildren()) + { + QPair res = setCheckedRecursion(applySet, bti, selectedGateIds); + if (res.first) hasChecked = true; + if (res.second) hasUnchecked = true; + } + SelectGateItem* item = dynamic_cast(parentItem); + if (item) + { + if (!item->isSelectable()) return QPair(false,false); + Qt::CheckState lastState; + switch (item->getType()) { + case ModuleItem::TreeItemType::Module: + lastState = item->state(); + if (hasChecked && hasUnchecked) + item->setState(Qt::PartiallyChecked); + else if (hasChecked) + item->setState(Qt::Checked); + else + item->setState(Qt::Unchecked); + if (!applySet && item->state() != lastState) + { + QModelIndex inx = getIndexFromItem(item); + Q_EMIT dataChanged(inx,inx); + } + break; + case ModuleItem::TreeItemType::Gate: + if (applySet) + { + if (selectedGateIds.contains(item->id())) + { + item->setState(Qt::Checked); + hasChecked = true; + } + else + { + item->setState(Qt::Unchecked); + hasUnchecked = true; + } + } + else + { + if (item->state() == Qt::Checked) + hasChecked = true; + else + hasUnchecked = true; + } + break; + case ModuleItem::TreeItemType::Net: + Q_ASSERT(1==0); + break; + } + } + return QPair(hasChecked,hasUnchecked); + } + + void SelectGateModel::setSelectedGatesRecursion(SelectGateItem* item) + { + if (!item) + mSelectedGates.clear(); + + BaseTreeItem* parentItem = nullptr; + if (item) + { + if (!item->isSelectable()) return; + switch (item->getType()) { + case ModuleItem::TreeItemType::Module: + parentItem = item; + break; + case ModuleItem::TreeItemType::Gate: + if (item->state() == Qt::Checked) + { + Gate*g = gNetlist->get_gate_by_id(item->id()); + if (g) mSelectedGates.push_back(g); + } + break; + case ModuleItem::TreeItemType::Net: + Q_ASSERT(1==0); + break; + } + } + else + parentItem = mRootItem; + + if (parentItem) + for (BaseTreeItem* childItem : parentItem->getChildren()) + setSelectedGatesRecursion(static_cast(childItem)); + } + + void SelectGateModel::setChecked(const std::vector &gates) + { + QSet selectedGateIds; + + for (const Gate* g : gates) + selectedGateIds.insert(g->get_id()); + + setCheckedRecursion(true, mRootItem, selectedGateIds); + setSelectedGatesRecursion(); + } + + Qt::ItemFlags SelectGateModel::flags(const QModelIndex &index) const + { + Qt::ItemFlags retval = ModuleModel::flags(index); + if (index.column()) return retval; + const SelectGateItem* item = dynamic_cast(getItemFromIndex(index)); + if (!item->isSelectable()) return Qt::NoItemFlags; + return retval | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled; + } + + LogicEvaluatorSelectGates::LogicEvaluatorSelectGates(const std::vector& gates, QWidget* parent) + : QDialog(parent) + { + setWindowTitle("Select combinatorical gates for logic evaluator"); + QGridLayout* layout = new QGridLayout(this); + mTreeView = new QTreeView(this); + mSelectGateModel = new SelectGateModel(this); + mProxyModel = new ModuleProxyModel(this); + mProxyModel->setSourceModel(mSelectGateModel); + mProxyModel->toggleFilterGates(); + mSelectGateModel->setChecked(gates); + mTreeView->setModel(mProxyModel); + mTreeView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + mTreeView->expandAll(); + mTreeView->setColumnWidth(0,250); + mTreeView->setColumnWidth(1,40); + mTreeView->setColumnWidth(2,110); + + mButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + connect(mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(mButtonBox, &QDialogButtonBox::rejected, this , &QDialog::reject); + mButtonBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + connect(mSelectGateModel, &SelectGateModel::selectionStateChanged, this, &LogicEvaluatorSelectGates::handleSelectionStateChanged); + mButtonBox->button(QDialogButtonBox::Ok)->setDisabled(mSelectGateModel->selectedGates().empty()); + + mSearchbar = new Searchbar(this); + connect(mSearchbar, &Searchbar::triggerNewSearch, mProxyModel, &ModuleProxyModel::startSearch); + + mCompile = new QCheckBox("Compile selected logic", this); + mCompile->setChecked(true); + + layout->addWidget(mTreeView,0,0,1,2); + layout->addWidget(mSearchbar, 1,0,1,2); + layout->addWidget(mCompile, 2,0,1,2); + layout->addWidget(mButtonBox, 3,1); + mTreeView->setMinimumWidth(400); + + QStyle* s = style(); + s->unpolish(this); + s->polish(this); + } + + void LogicEvaluatorSelectGates::handleSelectionStateChanged(bool empty) + { + mButtonBox->button(QDialogButtonBox::Ok)->setDisabled(empty); + } + + void LogicEvaluatorSelectGates::accept() + { + LogicEvaluatorDialog* led = new LogicEvaluatorDialog(mSelectGateModel->selectedGates(),!mCompile->isChecked()); + led->show(); + led->raise(); + QDialog::accept(); + } +} diff --git a/plugins/logic_evaluator/src/logic_evaluator_truthtable.cpp b/plugins/logic_evaluator/src/logic_evaluator_truthtable.cpp new file mode 100644 index 00000000000..fe879e6ec90 --- /dev/null +++ b/plugins/logic_evaluator/src/logic_evaluator_truthtable.cpp @@ -0,0 +1,255 @@ +#include "logic_evaluator/logic_evaluator_truthtable.h" +#include "hal_core/netlist/net.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace hal { + LogicEvaluatorTruthtableColumn::LogicEvaluatorTruthtableColumn(int nrows, QList values) + : mRows(nrows) + { + mArray = new int[mRows]; + for (int i=0; i &sortRows) const + { + for (int irow : sortRows) + { + if (mArray[irow] < other.mArray[irow]) return true; + if (mArray[irow] > other.mArray[irow]) return false; + } + return false; // equal + } + + LogicEvaluatorTruthtableColumn::~LogicEvaluatorTruthtableColumn() + { + delete [] mArray; + } + + int LogicEvaluatorTruthtableColumn::data(int irow) const + { + return mArray[irow]; + } + + //----------------------------- + LogicEvaluatorTruthtableModel::LogicEvaluatorTruthtableModel(const QList &inpList, const QList &outList, QObject* parent) + : mDisplayFormat(MAXFORMAT), // will be set by setDisplayFormat + mInputList(inpList), mOutputList(outList), mInputSize(inpList.size()), mOutputSize(outList.size()) + {;} + + LogicEvaluatorTruthtableModel::~LogicEvaluatorTruthtableModel() + { + for (LogicEvaluatorTruthtableColumn* letc : mColumnList) + delete letc; + } + + void LogicEvaluatorTruthtableModel::addColumn(LogicEvaluatorTruthtableColumn* letc) + { + mColumnList.append(letc); + } + + void LogicEvaluatorTruthtableModel::sortModelRow(int irow) + { + QList sortRows; + sortRows.append(irow); + std::sort(mColumnList.begin(),mColumnList.end(),[sortRows](const LogicEvaluatorTruthtableColumn* a, const LogicEvaluatorTruthtableColumn* b){return a->lessThan(*b,sortRows); } ); + Q_EMIT dataChanged(index(0,0),index(rowCount()-1,columnCount()-1)); + } + + void LogicEvaluatorTruthtableModel::sortModelRows(const QList& sortRows) + { + if (sortRows.isEmpty()) return; + std::sort(mColumnList.begin(),mColumnList.end(),[sortRows](const LogicEvaluatorTruthtableColumn* a, const LogicEvaluatorTruthtableColumn* b){return a->lessThan(*b,sortRows); } ); + Q_EMIT dataChanged(index(0,0),index(rowCount()-1,columnCount()-1)); + } + + QVariant LogicEvaluatorTruthtableModel::data(const QModelIndex& index, int role) const + { + int val = mColumnList.at(index.column())->data(index.row()); + + switch (role) + { + case Qt::DisplayRole: + switch (mDisplayFormat) { + case ZeroOne: + return val; + case LowHigh: + return val ? "H" : "L"; + case BlueRed: + return QChar(0x2588); + } + break; + case Qt::ForegroundRole: + if (mDisplayFormat == BlueRed) + return val ? QColor("#FF0000") : QColor("#00A0FF"); + break; + case Qt::BackgroundRole: + if (index.row() >= mInputSize) + return QColor("#080930"); + return QColor("302A12"); + break; + case Qt::TextAlignmentRole: + return QVariant(Qt::AlignHCenter | Qt::AlignVCenter); + default: + break; + } + return QVariant(); + } + + QVariant LogicEvaluatorTruthtableModel::headerData(int section, Qt::Orientation orientation, int role) const + { + if (role!=Qt::DisplayRole) return QAbstractTableModel::headerData(section, orientation, role); + if (orientation == Qt::Horizontal) + return QString::number(section, 16); + if (section < mInputSize) + return QString::fromStdString(mInputList.at(section)->get_name()); + return QString::fromStdString(mOutputList.at(section-mInputSize)->get_name()); + } + + int LogicEvaluatorTruthtableModel::rowCount(const QModelIndex &parent) const + { + Q_UNUSED(parent); + return mInputSize + mOutputSize; + } + + int LogicEvaluatorTruthtableModel::columnCount(const QModelIndex &parent) const + { + Q_UNUSED(parent); + return (1 << mInputList.size()); + } + + //-------------------------------- + LogicEvaluatorTruthtableSort::LogicEvaluatorTruthtableSort(QList& nets, QWidget* parent) + : QDialog(parent), mNets(nets) + { + QVBoxLayout* layout = new QVBoxLayout(this); + const char* ordinal[] = { "st", "nd", "th"}; + for (int i=0; i<5; i++) + { + if (i) layout->addStretch(); + layout->addWidget(new QLabel(QString("%1%2 sort key").arg(i+1).arg(i<3?ordinal[i]:ordinal[2]),this)); + mSortKey[i] = new QComboBox(this); + mSortKey[i]->addItem("--not used--", (const void*) nullptr); + for (const Net* n : mNets) + mSortKey[i]->addItem(QString::fromStdString(n->get_name()),(const void*)n); + layout->addWidget(mSortKey[i]); + } + QDialogButtonBox* bbox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + connect(bbox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(bbox, &QDialogButtonBox::rejected, this , &QDialog::reject); + bbox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + layout->addWidget(bbox); + } + + QList LogicEvaluatorTruthtableSort::sortOrder() const + { + QList retval; + QSet selected; + for (int i=0; i<5; i++) + { + int inx = mSortKey[i]->currentIndex(); + if (!inx) break; + --inx; // skip not used entry + const Net* n = mNets.at(inx); + if (!selected.contains(n)) + { + retval.append(inx); + selected.insert(n); + } + } + return retval; + } + + //-------------------------------- + LogicEvaluatorTruthtable::LogicEvaluatorTruthtable(LogicEvaluatorTruthtableModel* model, QWidget* parent) + : mModel(model), mColumnDubbleClicked(-1) + { + setWindowTitle("Truth Table from Logic Evaluator"); + QGridLayout* layout = new QGridLayout(this); + QMenuBar* menuBar = new QMenuBar(this); + QMenu* displForm = menuBar->addMenu("Format"); + + const char* displayFormatLabel[] = {"0 / 1", "L / H", "blue / red"}; + QActionGroup* actGroup = new QActionGroup(this); + for (int i=0; i< LogicEvaluatorTruthtableModel::MAXFORMAT; i++) + { + mActionDisplayFormat[i] = displForm->addAction(displayFormatLabel[i]); + mActionDisplayFormat[i]->setCheckable(true); + actGroup->addAction(mActionDisplayFormat[i]); + } + connect(actGroup, &QActionGroup::triggered, this, &LogicEvaluatorTruthtable::handleDisplayFormatChanged); + mActionDisplayFormat[LogicEvaluatorTruthtableModel::ZeroOne]->setChecked(true); + handleDisplayFormatChanged(mActionDisplayFormat[LogicEvaluatorTruthtableModel::ZeroOne]); + + QMenu* sortMenu = menuBar->addMenu("Sort"); + QAction* actSort = sortMenu->addAction("Sort"); + connect(actSort, &QAction::triggered, this, &LogicEvaluatorTruthtable::handleSortTriggered); + + QTableView* view = new QTableView(this); + view->setModel(mModel); + for (int icol=0; icolcolumnCount(); icol++) + view->setColumnWidth(icol, 32); + connect(view->verticalHeader(), &QHeaderView::sectionClicked, mModel, &LogicEvaluatorTruthtableModel::sortModelRow); + connect(view->horizontalHeader(), &QHeaderView::sectionDoubleClicked, this, &LogicEvaluatorTruthtable::handleColumnDubbleClicked); + layout->addWidget(view); + layout->setMenuBar(menuBar); + } + + void LogicEvaluatorTruthtableModel::setDisplayFormat(DisplayFormat df) + { + if (df == mDisplayFormat) return; + mDisplayFormat = df; + Q_EMIT dataChanged(index(0,0),index(rowCount()-1,columnCount()-1)); + } + + void LogicEvaluatorTruthtable::handleSortTriggered() + { + QList nets = mModel->getNets(); + LogicEvaluatorTruthtableSort lets(nets,this); + if (lets.exec() == QDialog::Accepted) + { + mModel->sortModelRows(lets.sortOrder()); + } + } + + void LogicEvaluatorTruthtable::handleColumnDubbleClicked(int icol) + { + mColumnDubbleClicked = icol; + accept(); + } + + QMap LogicEvaluatorTruthtableModel::selectedColumn(int icol) const + { + QMap retval; + if (icol < 0 || icol >= mColumnList.size()) return retval; + LogicEvaluatorTruthtableColumn* letc = mColumnList.at(icol); + int irow = 0; + for (const Net* n : mInputList) + retval[n] = letc->data(irow++); + return retval; + } + + QMap LogicEvaluatorTruthtable::selectedColumn() const + { + return mModel->selectedColumn(mColumnDubbleClicked); + } + + void LogicEvaluatorTruthtable::handleDisplayFormatChanged(QAction* act) + { + for (int i=0; isetDisplayFormat((LogicEvaluatorTruthtableModel::DisplayFormat)i); + break; + } + } +} diff --git a/plugins/logic_evaluator/src/plugin_logic_evaluator.cpp b/plugins/logic_evaluator/src/plugin_logic_evaluator.cpp new file mode 100644 index 00000000000..6cbc5d1cd73 --- /dev/null +++ b/plugins/logic_evaluator/src/plugin_logic_evaluator.cpp @@ -0,0 +1,164 @@ +#include "logic_evaluator/plugin_logic_evaluator.h" + +#include "hal_core/netlist/netlist_writer/netlist_writer_manager.h" +#include "hal_core/netlist/gate.h" +#include "logic_evaluator/logic_evaluator_dialog.h" +#include "logic_evaluator/logic_evaluator_select_gates.h" +#include "gui/content_manager/content_manager.h" +#include "gui/gui_globals.h" +#include "gui/gui_api/gui_api.h" +#include + +namespace hal +{ + LogicEvaluatorPlugin::LogicEvaluatorPlugin() + { + m_extensions.push_back(new GuiExtensionLogicEvaluator); + } + + extern std::unique_ptr create_plugin_instance() + { + return std::make_unique(); + } + + std::string LogicEvaluatorPlugin::get_name() const + { + return std::string("waveform_viewer"); + } + + std::string LogicEvaluatorPlugin::get_version() const + { + return std::string("0.7"); + } + + std::string LogicEvaluatorPlugin::get_description() const + { + return std::string("GUI to control simulation and view resulting waveforms"); + } + + std::set LogicEvaluatorPlugin::get_dependencies() const + { + std::set retval; + retval.insert("netlist_simulator_controller"); + retval.insert("hal_gui"); + return retval; + } + + void LogicEvaluatorPlugin::on_load() + { + qRegisterMetaType(); + LogManager::get_instance()->add_channel("logic_evaluator", {LogManager::create_stdout_sink(), LogManager::create_file_sink(), LogManager::create_gui_sink()}, "info"); + } + + void LogicEvaluatorPlugin::on_unload() + { + } + + //--------------------------------------- + + std::vector GuiExtensionLogicEvaluator::get_parameter() const + { + std::vector retval; + retval.push_back(PluginParameter(PluginParameter::Label, "help", "", + "Press 'Launch' to launch logic evaluator\n" + "with the gates that are currently selected.\n\n" + "Per default boolean logic gets compiled and\n" + "evaluated by compiled binary code. If 'skip' is\n" + "checked the compile step gets omitted and the\n" + "build-in BooleanFunction class does the evaluation.")); + retval.push_back(PluginParameter(PluginParameter::Boolean, "skip", "Skip compilation", "false")); + retval.push_back(PluginParameter(PluginParameter::PushButton, "exec", "Launch")); + return retval; + } + + void GuiExtensionLogicEvaluator::set_parameter(const std::vector& params) + { + bool launchPressed = false; + bool skipCompile = false; + for (const PluginParameter& pp : params) + { + if (pp.get_tagname() == "exec" && pp.get_value() == "clicked") + launchPressed = true; + if (pp.get_tagname() == "skip") + skipCompile = (pp.get_value() == "true"); + } + + if (!launchPressed) return; + + std::unordered_set gates; + for (Gate* g : GuiApi().getSelectedGates()) + if (acceptGate(g)) + gates.insert(g); + for (Module* m : GuiApi().getSelectedModules()) + for (Gate* g : m->get_gates(nullptr,true)) + if (acceptGate(g)) + gates.insert(g); + + if (gates.empty()) + { + std::vector emptyList; + LogicEvaluatorSelectGates lesg(emptyList); + lesg.exec(); + return; + } + + std::vector vgates(gates.begin(), gates.end()); + LogicEvaluatorDialog* led = new LogicEvaluatorDialog(vgates, skipCompile); + led->show(); + } + + bool GuiExtensionLogicEvaluator::acceptGate(const Gate *g) + { + const GateType* gt = g->get_type(); + if (gt->has_property(GateTypeProperty::ff)) return false; + if (gt->has_property(GateTypeProperty::latch)) return false; + if (gt->has_property(GateTypeProperty::ram)) return false; + return true; + } + + std::vector GuiExtensionLogicEvaluator::get_context_contribution(const Netlist* nl, const std::vector& mods, const std::vector& gats, const std::vector& nets) + { + std::vector retval; + if (nl && (!mods.empty() || !gats.empty())) + retval.push_back({this,"context", "Launch logic evaluator"}); + return retval; + } + + void GuiExtensionLogicEvaluator::execute_function(std::string tag, Netlist* nl, const std::vector& mods, const std::vector& gats, const std::vector& nets) + { + if (nl && (!mods.empty() || !gats.empty())) + { + std::unordered_set gates; + for (u32 gatId : gats) + { + Gate* g = nl->get_gate_by_id(gatId); + if (g && acceptGate(g)) + gates.insert(g); + } + for (u32 modId : mods) + { + Module* m = nl->get_module_by_id(modId); + if (m) + { + for (Gate* g : m->get_gates(nullptr,true)) + if (acceptGate(g)) + gates.insert(g); + } + } + + if (gates.empty()) + { + std::vector emptyList; + LogicEvaluatorSelectGates lesg(emptyList); + lesg.exec(); + return; + } + + std::vector vgates(gates.begin(), gates.end()); + LogicEvaluatorDialog* led = new LogicEvaluatorDialog(vgates, false); + led->show(); + + } + } + +} // namespace hal diff --git a/plugins/simulator/netlist_simulator_controller/src/simulation_input.cpp b/plugins/simulator/netlist_simulator_controller/src/simulation_input.cpp index bcd58d5ee39..b3e48f4d31f 100644 --- a/plugins/simulator/netlist_simulator_controller/src/simulation_input.cpp +++ b/plugins/simulator/netlist_simulator_controller/src/simulation_input.cpp @@ -14,7 +14,11 @@ namespace hal { { for (GatePin* gp : gate_pin_group->get_pins()) { - Net* n = gate->get_fan_in_net(gp); + Net* n = nullptr; + if (gp->get_direction() == PinDirection::input || gp->get_direction() == PinDirection::inout) + n = gate->get_fan_in_net(gp); + else + n = gate->get_fan_out_net(gp); if (n) retval.push_back(n); } } diff --git a/plugins/simulator/netlist_simulator_controller/src/simulation_process.cpp b/plugins/simulator/netlist_simulator_controller/src/simulation_process.cpp index 715c5b7c4df..0ce8485bb6e 100644 --- a/plugins/simulator/netlist_simulator_controller/src/simulation_process.cpp +++ b/plugins/simulator/netlist_simulator_controller/src/simulation_process.cpp @@ -253,7 +253,7 @@ namespace hal else { 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(-1)) return false; handleReadyReadStandardError(); handleReadyReadStandardOutput(); } diff --git a/src/python_bindings/bindings/netlist_factory.cpp b/src/python_bindings/bindings/netlist_factory.cpp index 5cf5263cea8..6b45d1c270b 100644 --- a/src/python_bindings/bindings/netlist_factory.cpp +++ b/src/python_bindings/bindings/netlist_factory.cpp @@ -16,11 +16,11 @@ namespace hal .def( "load_netlist", - [](const std::filesystem::path& netlist_file, const std::filesystem::path& gate_library_file) { + [](const std::filesystem::path& netlist_file, const std::filesystem::path& gate_library_file = std::filesystem::path()) { return std::shared_ptr(netlist_factory::load_netlist(netlist_file, gate_library_file)); }, py::arg("netlist_file"), - py::arg("gate_library_file") = "", + py::arg("gate_library_file") = std::filesystem::path(), R"( Create a netlist from the given file using the specified gate library file. Will either deserialize ``.hal`` file or call parser plugin for other formats. @@ -42,7 +42,7 @@ namespace hal Will either deserialize ``.hal`` file or call parser plugin for other formats. :param pathlib.Path netlist_file: Path to the file. - :param pathlib.Path gate_library_file: Path to the gate library file. + :param hal_py.GateLibrary gate_library_file: Path to the gate library file. :returns: The netlist on success, ``None`` otherwise. :rtype: hal_py.Netlist or None )")