77#include " gui/selection_details_widget/details_frame_widget.h"
88#include " gui/selection_details_widget/gate_details_widget/gate_info_table.h"
99#include " gui/selection_details_widget/gate_details_widget/gate_pin_tree.h"
10+ #include " gui/user_action/action_add_boolean_function.h"
1011#include " gui/user_action/action_set_object_data.h"
1112#include " gui/validator/hexadecimal_validator.h"
1213#include " hal_core/netlist/gate.h"
1314#include " hal_core/netlist/gate_library/gate_type_component/ff_component.h"
1415#include " hal_core/netlist/gate_library/gate_type_component/gate_type_component.h"
1516#include " hal_core/netlist/gate_library/gate_type_component/init_component.h"
1617#include " hal_core/netlist/gate_library/gate_type_component/latch_component.h"
18+ #include " hal_core/netlist/gate_library/gate_type_component/lut_component.h"
1719#include " hal_core/netlist/gate_library/gate_type_component/state_component.h"
1820
1921#include < QApplication>
22+ #include < QButtonGroup>
23+ #include < QHBoxLayout>
24+ #include < QRadioButton>
25+ #include < QVBoxLayout>
2026#include < QClipboard>
2127#include < QDebug>
2228#include " gui/comment_system/widgets/comment_widget.h"
@@ -50,13 +56,15 @@ namespace hal
5056 addTab (" Pins" , mPinsFrame , false );
5157
5258 // (ff / latch / lut) tab - would love to use seperate tabs, but it's a hassle to hide multiple individual tabs witouth setTabVisible() from qt 5.15
53- mFfFunctionTable = new BooleanFunctionTable (this );
59+ mFfFunctionTable = new BooleanFunctionTable (this );
5460 mLatchFunctionTable = new BooleanFunctionTable (this );
55- mLutFunctionTable = new BooleanFunctionTable (this );
56- mLutFunctionTable ->setContextMenuPlainDescr (true );
57- mLutFunctionTable ->setContextMenuPythonPlainDescr (true );
58- mLutFunctionTable ->enableChangeBooleanFunctionOption (true );
59- mLutTable = new LUTTableWidget (this );
61+ mLutPinButtonGroup = new QButtonGroup (this );
62+ mLutPinButtonGroup ->setExclusive (true );
63+ mLutRadioWidget = new QWidget (this );
64+ mLutRadioLayout = new QVBoxLayout (mLutRadioWidget );
65+ mLutRadioLayout ->setContentsMargins (4 , 4 , 4 , 4 );
66+ mLutRadioLayout ->setSpacing (4 );
67+ mLutTable = new LUTTableWidget (this );
6068 mLutConfigLabel = new QLabel (" default" , this );
6169 mLutConfigLabel ->setContextMenuPolicy (Qt::CustomContextMenu);
6270 mLutConfigLabel ->setWordWrap (true );
@@ -65,9 +73,14 @@ namespace hal
6573
6674 mFfFrame = new DetailsFrameWidget (mFfFunctionTable , " FF Information" , this );
6775 mLatchFrame = new DetailsFrameWidget (mLatchFunctionTable , " Latch Information" , this );
68- mLutFrame = new DetailsFrameWidget (mLutFunctionTable , " Boolean Function " , this );
76+ mLutFrame = new DetailsFrameWidget (mLutRadioWidget , " Boolean Functions " , this );
6977 mLutConfigurationFrame = new DetailsFrameWidget (mLutConfigLabel , " Configuration String" , this );
7078 mTruthTableFrame = new DetailsFrameWidget (mLutTable , " Truth Table" , this );
79+ // DetailsFrameWidget sets Expanding vertical policy on all content widgets, which causes the
80+ // container to distribute vertical space equally. Override for the two compact LUT frames so
81+ // they stay at their natural content height and the truth table gets any remaining space.
82+ mLutFrame ->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Fixed);
83+ mLutConfigurationFrame ->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Fixed);
7184
7285 QList<DetailsFrameWidget*> framesFfLatchLutTab ({mFfFrame , mLatchFrame , mLutFrame , mLutConfigurationFrame , mTruthTableFrame });
7386 mMultiTabIndex = addTab (" (FF / Latch / LUT)" , framesFfLatchLutTab); // save index of multi tab -> needed for show / hide
@@ -231,23 +244,81 @@ namespace hal
231244 }
232245 }
233246
247+ void GateDetailsTabWidget::handleLutPinSelectionChanged (int id)
248+ {
249+ if (mCurrentGate == nullptr || id < 0 || id >= static_cast <int >(mCurrentLutPins .size ()))
250+ return ;
251+
252+ const std::string& pinName = mCurrentLutPins [id];
253+ mSelectedLutPin = pinName;
254+
255+ BooleanFunction bf = mCurrentGate ->get_boolean_function (pinName);
256+ mLutTable ->setBooleanFunction (bf, QString::fromStdString (pinName));
257+
258+ GateType* gt = mCurrentGate ->get_type ();
259+ InitComponent* init_component = gt->get_component_as <InitComponent>([](const GateTypeComponent* c) { return InitComponent::is_class_of (c); });
260+ if (init_component == nullptr )
261+ {
262+ mLutConfigLabel ->setText (" Could not load init string." );
263+ return ;
264+ }
265+ std::string initKey = init_component->get_init_identifiers ()[0 ];
266+ u32 bit_offset = 0 ;
267+ u32 bit_count = 0 ;
268+ if (LUTComponent* lc = gt->get_component_as <LUTComponent>([](const GateTypeComponent* c) { return LUTComponent::is_class_of (c); }); lc != nullptr )
269+ {
270+ if (const LUTComponent::LUTOutputConfig* cfg = lc->get_output_pin_config (pinName); cfg != nullptr )
271+ {
272+ initKey = cfg->init_identifier ;
273+ bit_offset = cfg->bit_offset ;
274+ bit_count = cfg->bit_count ;
275+ }
276+ }
277+ const std::string raw_str = std::get<1 >(mCurrentGate ->get_data (init_component->get_init_category (), initKey));
278+ if (bit_count > 0 && !raw_str.empty ())
279+ {
280+ try
281+ {
282+ const u64 full_val = std::stoull (raw_str, nullptr , 16 );
283+ const u64 mask = (bit_count == 64 ) ? UINT64_MAX : ((1ULL << bit_count) - 1 );
284+ const u64 sliced = (full_val >> bit_offset) & mask;
285+ mLutConfigLabel ->setText (" 0x" + QString::number (static_cast <qulonglong>(sliced), 16 ).toUpper ().rightJustified (bit_count / 4 , QLatin1Char (' 0' )));
286+ }
287+ catch (...)
288+ {
289+ mLutConfigLabel ->setText (" 0x" + QString::fromStdString (raw_str));
290+ }
291+ }
292+ else
293+ {
294+ mLutConfigLabel ->setText (" 0x" + QString::fromStdString (raw_str));
295+ }
296+ }
297+
234298 void GateDetailsTabWidget::handleLutConfigContextMenuRequested (QPoint pos)
235299 {
300+ auto resolveInitKey = [this ](InitComponent* ic) -> std::string {
301+ std::string key = ic->get_init_identifiers ()[0 ];
302+ if (LUTComponent* lc = mCurrentGate ->get_type ()->get_component_as <LUTComponent>([](const GateTypeComponent* c) { return LUTComponent::is_class_of (c); }); lc != nullptr )
303+ if (const LUTComponent::LUTOutputConfig* cfg = lc->get_output_pin_config (mSelectedLutPin ); cfg != nullptr )
304+ key = cfg->init_identifier ;
305+ return key;
306+ };
307+
236308 QMenu menu;
237309
238310 menu.addAction (" Configuration string to clipboard" , [this ]() { QApplication::clipboard ()->setText (mLutConfigLabel ->text ().remove (" 0x" )); });
239- menu.addAction (" Change configuration string" , [this ]() {
311+ menu.addAction (" Change configuration string" , [this , resolveInitKey ]() {
240312 InputDialog ipd (" Change configuration string" , " New configuration string" , mLutConfigLabel ->text ().remove (" 0x" ));
241313 HexadecimalValidator hexValidator;
242314 ipd.addValidator (&hexValidator);
243315 if (ipd.exec () == QDialog::Accepted && !ipd.textValue ().isEmpty ())
244316 {
245- if (InitComponent* init_component = mCurrentGate ->get_type ()->get_component_as <InitComponent>([](const GateTypeComponent* c) { return InitComponent::is_class_of (c); });
246- init_component != nullptr )
317+ if (InitComponent* ic = mCurrentGate ->get_type ()->get_component_as <InitComponent>([](const GateTypeComponent* c) { return InitComponent::is_class_of (c); }); ic != nullptr )
247318 {
248- std::string cat = init_component ->get_init_category (), key = init_component-> get_init_identifiers ()[ 0 ] ;
249- QString data_type = " bit_vector " ;
250- ActionSetObjectData* act = new ActionSetObjectData (QString::fromStdString (cat), QString::fromStdString (key), data_type , ipd.textValue ().toUpper ());
319+ std::string cat = ic ->get_init_category ();
320+ std::string key = resolveInitKey (ic) ;
321+ ActionSetObjectData* act = new ActionSetObjectData (QString::fromStdString (cat), QString::fromStdString (key), " bit_vector " , ipd.textValue ().toUpper ());
251322 act->setObject (UserActionObject (mCurrentGate ->get_id (), UserActionObjectType::Gate));
252323 act->exec ();
253324 setGate (mCurrentGate ); // must update config string and data table, no signal for that
@@ -256,11 +327,11 @@ namespace hal
256327 log_error (" gui" , " Could not load InitComponent from gate with id {}." , mCurrentGate ->get_id ());
257328 }
258329 });
259- menu.addAction (QIcon (" :/icons/python" ), " Get configuration string" , [this ]() {
260- if (InitComponent* init_component = mCurrentGate ->get_type ()->get_component_as <InitComponent>([](const GateTypeComponent* c) { return InitComponent::is_class_of (c); });
261- init_component != nullptr )
330+ menu.addAction (QIcon (" :/icons/python" ), " Get configuration string" , [this , resolveInitKey]() {
331+ if (InitComponent* ic = mCurrentGate ->get_type ()->get_component_as <InitComponent>([](const GateTypeComponent* c) { return InitComponent::is_class_of (c); }); ic != nullptr )
262332 {
263- std::string cat = init_component->get_init_category (), key = init_component->get_init_identifiers ()[0 ];
333+ std::string cat = ic->get_init_category ();
334+ std::string key = resolveInitKey (ic);
264335 QApplication::clipboard ()->setText (PyCodeProvider::pyCodeGateData (mCurrentGate ->get_id (), QString::fromStdString (cat), QString::fromStdString (key)));
265336 }
266337 else
@@ -388,40 +459,83 @@ namespace hal
388459 {
389460 case GateDetailsTabWidget::GateTypeCategory::lut: {
390461 const std::vector<std::string> lutPins = gate->get_type ()->get_pin_names ([](const GatePin* p) { return p->get_type () == PinType::lut; });
391- // LUT Boolean Function Table only shows the LUT function
392- QVector<QSharedPointer<BooleanFunctionTableEntry>> lutEntries;
393- for (auto bfEntry : otherFunctionList)
394- {
395- if (std::find (lutPins.begin (), lutPins.end (), bfEntry->getEntryIdentifier ().toStdString ()) != lutPins.end ())
396- {
397- lutEntries.append (bfEntry);
398- }
399- }
400462
401- mLutFunctionTable ->setEntries (lutEntries);
402- mLutFunctionTable ->setGateInformation (gate);
403-
404- // Setup lut config (init) string
405- if (InitComponent* init_component = gt->get_component_as <InitComponent>([](const GateTypeComponent* c) { return InitComponent::is_class_of (c); }); init_component != nullptr )
406- {
407- auto typeAndValue = gate->get_data (init_component->get_init_category (), init_component->get_init_identifiers ()[0 ]);
408- mLutConfigLabel ->setText (" 0x" + QString::fromStdString (std::get<1 >(typeAndValue)));
409- }
410- else
463+ // Clear existing radio buttons and layout
464+ const auto existingButtons = mLutPinButtonGroup ->buttons ();
465+ for (QAbstractButton* btn : existingButtons)
466+ mLutPinButtonGroup ->removeButton (btn);
467+ while (QLayoutItem* item = mLutRadioLayout ->takeAt (0 ))
411468 {
412- mLutConfigLabel ->setText (" Could not load init string." );
469+ if (QWidget* w = item->widget ())
470+ w->deleteLater ();
471+ delete item;
413472 }
473+ mCurrentLutPins .clear ();
414474
415- // The table is only updated if the gate has a LUT pin
416- if (lutPins.size () > 0 )
475+ // Build one radio-button row per LUT output pin
476+ int idx = 0 ;
477+ for (const std::string& pin : lutPins)
417478 {
418- // All LUT pins have the same boolean function
419- std::basic_string<char > outPin = lutPins.front ();
420-
421- // Fill the LUL truth table
422- BooleanFunction lutFunction = gate->get_boolean_function (outPin);
423- mLutTable ->setBooleanFunction (lutFunction, QString::fromStdString (outPin));
479+ BooleanFunction bf = gate->get_boolean_function (pin);
480+ QString rowText = QString::fromStdString (pin) + " = " + QString::fromStdString (bf.to_string ());
481+
482+ QWidget* row = new QWidget (mLutRadioWidget );
483+ QHBoxLayout* rowLayout = new QHBoxLayout (row);
484+ rowLayout->setContentsMargins (0 , 0 , 0 , 0 );
485+ rowLayout->setSpacing (4 );
486+
487+ QRadioButton* rb = new QRadioButton (row);
488+ mLutPinButtonGroup ->addButton (rb, idx);
489+
490+ QLabel* fnLabel = new QLabel (rowText, row);
491+ fnLabel->setWordWrap (true );
492+ fnLabel->setContextMenuPolicy (Qt::CustomContextMenu);
493+ connect (fnLabel, &QLabel::customContextMenuRequested, this , [this , pin, fnLabel](QPoint pos) {
494+ if (!mCurrentGate ) return ;
495+ const BooleanFunction bf = mCurrentGate ->get_boolean_function (pin);
496+ const QString bfStr = QString::fromStdString (bf.to_string ());
497+ const u32 gateId = mCurrentGate ->get_id ();
498+ const QString pinQStr = QString::fromStdString (pin);
499+ QMenu menu;
500+ menu.addAction (" Boolean function to clipboard" , [bfStr]() {
501+ QApplication::clipboard ()->setText (bfStr);
502+ });
503+ menu.addAction (" Change Boolean function" , [this , pinQStr]() {
504+ InputDialog ipd (" Change Boolean function" , " New function" , " " );
505+ if (ipd.exec () == QDialog::Accepted && !ipd.textValue ().isEmpty ())
506+ {
507+ auto funcRes = BooleanFunction::from_string (ipd.textValue ().toStdString ());
508+ if (funcRes.is_ok ())
509+ {
510+ ActionAddBooleanFunction* act = new ActionAddBooleanFunction (pinQStr, funcRes.get (), mCurrentGate ->get_id ());
511+ act->exec ();
512+ }
513+ }
514+ });
515+ const QString pyCode = PyCodeProvider::pyCodeGateBooleanFunction (gateId, pinQStr);
516+ if (!pyCode.isEmpty ())
517+ menu.addAction (QIcon (" :/icons/python" ), " Get boolean function" , [pyCode]() {
518+ QApplication::clipboard ()->setText (pyCode);
519+ });
520+ menu.exec (fnLabel->mapToGlobal (pos));
521+ });
522+
523+ rowLayout->addWidget (rb, 0 , Qt::AlignTop);
524+ rowLayout->addWidget (fnLabel, 1 );
525+ mLutRadioLayout ->addWidget (row);
526+ mCurrentLutPins .push_back (pin);
527+
528+ connect (rb, &QRadioButton::toggled, this , [this , idx](bool checked) {
529+ if (checked) handleLutPinSelectionChanged (idx);
530+ });
531+ ++idx;
424532 }
533+ mLutRadioLayout ->addStretch ();
534+
535+ // Select the first pin by default; the toggled signal drives the config label and truth table
536+ if (!mCurrentLutPins .empty ())
537+ if (QAbstractButton* firstBtn = mLutPinButtonGroup ->button (0 ))
538+ firstBtn->setChecked (true );
425539 break ;
426540 }
427541 case GateDetailsTabWidget::GateTypeCategory::ff: {
0 commit comments