diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 817e0fe1..46614171 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -49,6 +49,8 @@ set(gui_SOURCES windows/coreview/components/value_handlers.cpp windows/coreview/components/cache.cpp widgets/hidingtabwidget.cpp + windows/predictor/predictor_btb_dock.cpp + windows/predictor/predictor_bht_dock.cpp ) set(gui_HEADERS dialogs/about/aboutdialog.h @@ -92,6 +94,8 @@ set(gui_HEADERS windows/coreview/components/cache.h helper/async_modal.h widgets/hidingtabwidget.h + windows/predictor/predictor_btb_dock.h + windows/predictor/predictor_bht_dock.h ) set(gui_UI dialogs/gotosymbol/gotosymboldialog.ui diff --git a/src/gui/dialogs/new/NewDialog.ui b/src/gui/dialogs/new/NewDialog.ui index 391cce0b..08c56fd4 100644 --- a/src/gui/dialogs/new/NewDialog.ui +++ b/src/gui/dialogs/new/NewDialog.ui @@ -6,8 +6,8 @@ 0 0 - 558 - 353 + 574 + 472 @@ -139,89 +139,279 @@ QLayout::SetDefaultConstraint - - - - - Pipelined - - - - - - - XLEN 64-bit - - - - - - - Atomic (A) - - - - - - - Delay slot - - - - - - - Multiply (M) - - - - - - - - - Hazard unit - - + + true - - false - - - - - - Stall when hazard is detected - - - - - - - Stall or forward when hazard is detected - - - true - - - - + + + + 0 + 0 + 536 + 384 + + + + + + + + + Pipelined + + + + + + + XLEN 64-bit + + + + + + + Atomic (A) + + + + + + + Delay slot + + + + + + + Multiply (M) + + + + + + + + + Hazard unit + + + true + + + false + + + + + + Stall when hazard is detected + + + + + + + Stall or forward when hazard is detected + + + true + + + + + + + + + + Branch Predictor + + + true + + + true + + + + + + 0 + + + + + Predictor type: + + + + + + + + + + + + 0 + + + + + Initial state: + + + + + + + + + + + + 0 + + + + + + 20 + 0 + + + + 0 + + + + + + + + 0 + 0 + + + + Branch History Table (BHT) PC address bits: + + + + + + + Branch Target Buffer (BTB) bits: + + + + + + + 8 + + + Qt::Horizontal + + + QSlider::TicksBothSides + + + + + + + + 20 + 0 + + + + 0 + + + + + + + Branch History Register (BHR) bits: + + + + + + + + 20 + 0 + + + + 0 + + + + + + + + 20 + 0 + + + + 0 + + + + + + + Branch History Table (BHT) bits: + + + + + + + 8 + + + Qt::Horizontal + + + QSlider::TicksBothSides + + + + + + + 8 + + + Qt::Horizontal + + + false + + + QSlider::TicksBothSides + + + + + + + + + + - - - - Qt::Vertical - - - - 20 - 40 - - - - diff --git a/src/gui/dialogs/new/newdialog.cpp b/src/gui/dialogs/new/newdialog.cpp index 41e4dc9d..87d48795 100644 --- a/src/gui/dialogs/new/newdialog.cpp +++ b/src/gui/dialogs/new/newdialog.cpp @@ -129,6 +129,32 @@ NewDialog::NewDialog(QWidget *parent, QSettings *settings) : QDialog(parent) { &NewDialog::browse_osemu_fs_root); connect(ui->osemu_fs_root, &QLineEdit::textChanged, this, &NewDialog::osemu_fs_root_change); + // Branch predictor + connect(ui->group_branch_predictor, QOverload::of(&QGroupBox::toggled), this, [this] { + config->set_bp_enabled(ui->group_branch_predictor->isChecked()); + }); + connect( + ui->select_bp_type, QOverload::of(&QComboBox::activated), this, + &NewDialog::bp_type_change); + connect(ui->select_bp_init_state, QOverload::of(&QComboBox::activated), this, [this] { + config->set_bp_init_state( + ui->select_bp_init_state->currentData().value()); + }); + connect(ui->slider_bp_btb_bits, &QAbstractSlider::valueChanged, this, [this] { + config->set_bp_btb_bits((uint8_t)ui->slider_bp_btb_bits->value()); + ui->text_bp_btb_bits_number->setText(QString::number(config->get_bp_btb_bits())); + }); + connect(ui->slider_bp_bhr_bits, &QAbstractSlider::valueChanged, this, [this] { + config->set_bp_bhr_bits((uint8_t)ui->slider_bp_bhr_bits->value()); + ui->text_bp_bhr_bits_number->setText(QString::number(config->get_bp_bhr_bits())); + ui->text_bp_bht_bits_number->setText(QString::number(config->get_bp_bht_bits())); + }); + connect(ui->slider_bp_bht_addr_bits, &QAbstractSlider::valueChanged, this, [this] { + config->set_bp_bht_addr_bits((uint8_t)ui->slider_bp_bht_addr_bits->value()); + ui->text_bp_bht_addr_bits_number->setText(QString::number(config->get_bp_bht_addr_bits())); + ui->text_bp_bht_bits_number->setText(QString::number(config->get_bp_bht_bits())); + }); + cache_handler_d = new NewDialogCacheHandler(this, ui_cache_d.data()); cache_handler_p = new NewDialogCacheHandler(this, ui_cache_p.data()); cache_handler_l2 = new NewDialogCacheHandler(this, ui_cache_l2.data()); @@ -365,6 +391,84 @@ void NewDialog::reset_at_compile_change(bool v) { config->set_reset_at_compile(v); } +void NewDialog::bp_type_change() { + // Read branch predictor type from GUI and store it in the config + const machine::PredictorType predictor_type { + ui->select_bp_type->currentData().value() + }; + config->set_bp_type(predictor_type); + + // Remove all items from init state list + ui->select_bp_init_state->clear(); + + // Configure GUI based on predictor selection + switch (predictor_type) { + case machine::PredictorType::SMITH_1_BIT: { + bp_toggle_history_table_ui(true); + + // Add items to the combo box + ui->select_bp_init_state->addItem( + predictor_state_to_string(machine::PredictorState::NOT_TAKEN, false).toString(), + QVariant::fromValue(machine::PredictorState::NOT_TAKEN)); + ui->select_bp_init_state->addItem( + predictor_state_to_string(machine::PredictorState::TAKEN, false).toString(), + QVariant::fromValue(machine::PredictorState::TAKEN)); + + // Set selected value, or set default if not found + const int index { ui->select_bp_init_state->findData( + QVariant::fromValue(config->get_bp_init_state())) }; + if (index >= 0) { + ui->select_bp_init_state->setCurrentIndex(index); + } else { + ui->select_bp_init_state->setCurrentIndex(ui->select_bp_init_state->findData( + QVariant::fromValue(machine::PredictorState::NOT_TAKEN))); + config->set_bp_init_state(machine::PredictorState::NOT_TAKEN); + } + } break; + + case machine::PredictorType::SMITH_2_BIT: + case machine::PredictorType::SMITH_2_BIT_HYSTERESIS: { + bp_toggle_history_table_ui(true); + + // Add items to the combo box + ui->select_bp_init_state->addItem( + predictor_state_to_string(machine::PredictorState::STRONGLY_NOT_TAKEN, false).toString(), + QVariant::fromValue(machine::PredictorState::STRONGLY_NOT_TAKEN)); + ui->select_bp_init_state->addItem( + predictor_state_to_string(machine::PredictorState::WEAKLY_NOT_TAKEN, false).toString(), + QVariant::fromValue(machine::PredictorState::WEAKLY_NOT_TAKEN)); + ui->select_bp_init_state->addItem( + predictor_state_to_string(machine::PredictorState::WEAKLY_TAKEN, false).toString(), + QVariant::fromValue(machine::PredictorState::WEAKLY_TAKEN)); + ui->select_bp_init_state->addItem( + predictor_state_to_string(machine::PredictorState::STRONGLY_TAKEN, false).toString(), + QVariant::fromValue(machine::PredictorState::STRONGLY_TAKEN)); + + // Set selected value, or set default if not found + const int index { ui->select_bp_init_state->findData( + QVariant::fromValue(config->get_bp_init_state())) }; + if (index >= 0) { + ui->select_bp_init_state->setCurrentIndex(index); + } else { + ui->select_bp_init_state->setCurrentIndex(ui->select_bp_init_state->findData( + QVariant::fromValue(machine::PredictorState::WEAKLY_NOT_TAKEN))); + config->set_bp_init_state(machine::PredictorState::WEAKLY_NOT_TAKEN); + } + } break; + + default: bp_toggle_history_table_ui(false); break; + } +} + +void NewDialog::bp_toggle_history_table_ui(bool enabled) { + ui->select_bp_init_state->setEnabled(enabled); + ui->slider_bp_bhr_bits->setEnabled(enabled); + ui->text_bp_bhr_bits_number->setEnabled(enabled); + ui->slider_bp_bht_addr_bits->setEnabled(enabled); + ui->text_bp_bht_addr_bits_number->setEnabled(enabled); + ui->text_bp_bht_bits_number->setEnabled(enabled); +} + void NewDialog::config_gui() { // Basic ui->elf_file->setText(config->elf()); @@ -379,6 +483,48 @@ void NewDialog::config_gui() { ui->hazard_stall->setChecked(config->hazard_unit() == machine::MachineConfig::HU_STALL); ui->hazard_stall_forward->setChecked( config->hazard_unit() == machine::MachineConfig::HU_STALL_FORWARD); + + // Branch predictor + ui->group_branch_predictor->setChecked(config->get_bp_enabled()); + ui->select_bp_type->clear(); + ui->select_bp_type->addItem( + predictor_type_to_string(machine::PredictorType::ALWAYS_NOT_TAKEN).toString(), + QVariant::fromValue(machine::PredictorType::ALWAYS_NOT_TAKEN)); + ui->select_bp_type->addItem( + predictor_type_to_string(machine::PredictorType::ALWAYS_TAKEN).toString(), + QVariant::fromValue(machine::PredictorType::ALWAYS_TAKEN)); + ui->select_bp_type->addItem( + predictor_type_to_string(machine::PredictorType::BTFNT).toString(), + QVariant::fromValue(machine::PredictorType::BTFNT)); + ui->select_bp_type->addItem( + predictor_type_to_string(machine::PredictorType::SMITH_1_BIT).toString(), + QVariant::fromValue(machine::PredictorType::SMITH_1_BIT)); + ui->select_bp_type->addItem( + predictor_type_to_string(machine::PredictorType::SMITH_2_BIT).toString(), + QVariant::fromValue(machine::PredictorType::SMITH_2_BIT)); + ui->select_bp_type->addItem( + predictor_type_to_string(machine::PredictorType::SMITH_2_BIT_HYSTERESIS).toString(), + QVariant::fromValue(machine::PredictorType::SMITH_2_BIT_HYSTERESIS)); + const int index { ui->select_bp_type->findData(QVariant::fromValue(config->get_bp_type())) }; + if (index >= 0) { + ui->select_bp_type->setCurrentIndex(index); + } else { + ui->select_bp_type->setCurrentIndex( + ui->select_bp_type->findData(QVariant::fromValue(machine::PredictorType::SMITH_1_BIT))); + config->set_bp_type(machine::PredictorType::SMITH_1_BIT); + } + bp_type_change(); + ui->slider_bp_btb_bits->setMaximum(BP_MAX_BTB_BITS); + ui->slider_bp_btb_bits->setValue(config->get_bp_btb_bits()); + ui->text_bp_btb_bits_number->setText(QString::number(config->get_bp_btb_bits())); + ui->slider_bp_bhr_bits->setMaximum(BP_MAX_BHR_BITS); + ui->slider_bp_bhr_bits->setValue(config->get_bp_bhr_bits()); + ui->text_bp_bhr_bits_number->setText(QString::number(config->get_bp_bhr_bits())); + ui->slider_bp_bht_addr_bits->setMaximum(BP_MAX_BHT_ADDR_BITS); + ui->slider_bp_bht_addr_bits->setValue(config->get_bp_bht_addr_bits()); + ui->text_bp_bht_addr_bits_number->setText(QString::number(config->get_bp_bht_addr_bits())); + ui->text_bp_bht_bits_number->setText(QString::number(config->get_bp_bht_bits())); + // Memory ui->mem_protec_exec->setChecked(config->memory_execute_protection()); ui->mem_protec_write->setChecked(config->memory_write_protection()); diff --git a/src/gui/dialogs/new/newdialog.h b/src/gui/dialogs/new/newdialog.h index 9b1bbb9a..81dd3712 100644 --- a/src/gui/dialogs/new/newdialog.h +++ b/src/gui/dialogs/new/newdialog.h @@ -3,6 +3,7 @@ #include "common/memory_ownership.h" #include "machine/machineconfig.h" +#include "predictor_types.h" #include "ui_NewDialog.h" #include "ui_NewDialogCache.h" @@ -53,6 +54,10 @@ private slots: void osemu_fs_root_change(QString val); void reset_at_compile_change(bool); + // Branch Predictor + void bp_type_change(); + void bp_toggle_history_table_ui(bool enabled); + private: Box ui {}; Box ui_cache_p {}, ui_cache_d {}, ui_cache_l2 {}; @@ -71,6 +76,7 @@ class NewDialogCacheHandler : public QObject { Q_OBJECT using Super = QObject; + public: NewDialogCacheHandler(NewDialog *nd, Ui::NewDialogCache *ui); diff --git a/src/gui/mainwindow/MainWindow.ui b/src/gui/mainwindow/MainWindow.ui index 93a83c35..cbe60b72 100644 --- a/src/gui/mainwindow/MainWindow.ui +++ b/src/gui/mainwindow/MainWindow.ui @@ -88,6 +88,8 @@ + + @@ -630,6 +632,16 @@ Show line number in the code editor. + + + Branch Predictor (History table) + + + + + Branch Predictor (Target table) + + diff --git a/src/gui/mainwindow/mainwindow.cpp b/src/gui/mainwindow/mainwindow.cpp index d79b3bc0..06d1f31d 100644 --- a/src/gui/mainwindow/mainwindow.cpp +++ b/src/gui/mainwindow/mainwindow.cpp @@ -114,6 +114,10 @@ MainWindow::MainWindow(QSettings *settings, QWidget *parent) cache_data->hide(); cache_level2.reset(new CacheDock(this, "L2")); cache_level2->hide(); + bp_btb.reset(new DockPredictorBTB(this)); + bp_btb->hide(); + bp_bht.reset(new DockPredictorBHT(this)); + bp_bht->hide(); peripherals.reset(new PeripheralsDock(this, settings)); peripherals->hide(); terminal.reset(new TerminalDock(this, settings)); @@ -154,6 +158,15 @@ MainWindow::MainWindow(QSettings *settings, QWidget *parent) connect(ui->actionProgram_Cache, &QAction::triggered, this, &MainWindow::show_cache_program); connect(ui->actionData_Cache, &QAction::triggered, this, &MainWindow::show_cache_data); connect(ui->actionL2_Cache, &QAction::triggered, this, &MainWindow::show_cache_level2); + + // Branch predictor + connect( + ui->actionBranch_Predictor_History_table, &QAction::triggered, this, + &MainWindow::show_bp_bht); + connect( + ui->actionBranch_Predictor_Target_table, &QAction::triggered, this, + &MainWindow::show_bp_btb); + connect(ui->actionPeripherals, &QAction::triggered, this, &MainWindow::show_peripherals); connect(ui->actionTerminal, &QAction::triggered, this, &MainWindow::show_terminal); connect(ui->actionLcdDisplay, &QAction::triggered, this, &MainWindow::show_lcd_display); @@ -315,6 +328,11 @@ void MainWindow::create_core( cache_data->setup(machine->cache_data()); bool cache_after_cache = config.cache_data().enabled() || config.cache_program().enabled(); cache_level2->setup(machine->cache_level2(), cache_after_cache); + + // Branch predictor + bp_btb->setup(machine->core()->get_predictor(), machine->core()); + bp_bht->setup(machine->core()->get_predictor(), machine->core()); + terminal->setup(machine->serial_port()); peripherals->setup(machine->peripheral_spi_led()); lcd_display->setup(machine->peripheral_lcd_display()); @@ -420,6 +438,8 @@ SHOW_HANDLER(memory, Qt::RightDockWidgetArea, true ) SHOW_HANDLER(cache_program, Qt::RightDockWidgetArea, false) SHOW_HANDLER(cache_data, Qt::RightDockWidgetArea, false) SHOW_HANDLER(cache_level2, Qt::RightDockWidgetArea, false) +SHOW_HANDLER(bp_btb, Qt::RightDockWidgetArea, false) +SHOW_HANDLER(bp_bht, Qt::RightDockWidgetArea, false) SHOW_HANDLER(peripherals, Qt::RightDockWidgetArea, false) SHOW_HANDLER(terminal, Qt::RightDockWidgetArea, false) SHOW_HANDLER(lcd_display, Qt::RightDockWidgetArea, false) @@ -434,6 +454,8 @@ void MainWindow::reset_windows() { reset_state_cache_program(); reset_state_cache_data(); reset_state_cache_level2(); + reset_state_bp_btb(); + reset_state_bp_bht(); reset_state_peripherals(); reset_state_terminal(); reset_state_lcd_display(); diff --git a/src/gui/mainwindow/mainwindow.h b/src/gui/mainwindow/mainwindow.h index deab97b9..d3ddcbaf 100644 --- a/src/gui/mainwindow/mainwindow.h +++ b/src/gui/mainwindow/mainwindow.h @@ -22,6 +22,8 @@ #include "windows/memory/memorydock.h" #include "windows/messages/messagesdock.h" #include "windows/peripherals/peripheralsdock.h" +#include "windows/predictor/predictor_bht_dock.h" +#include "windows/predictor/predictor_btb_dock.h" #include "windows/program/programdock.h" #include "windows/registers/registersdock.h" #include "windows/terminal/terminaldock.h" @@ -94,6 +96,11 @@ public slots: void show_messages(); void reset_windows(); void show_symbol_dialog(); + // Branch predictor + void show_bp_btb(); + void show_bp_bht(); + void reset_state_bp_btb(); + void reset_state_bp_bht(); // Actions - help void about_program(); void about_qt(); @@ -129,6 +136,11 @@ public slots: Box program {}; Box memory {}; Box cache_program {}, cache_data {}, cache_level2 {}; + + // Branch predictor + Box bp_btb {}; + Box bp_bht {}; + Box peripherals {}; Box terminal {}; Box lcd_display {}; diff --git a/src/gui/windows/predictor/predictor_bht_dock.cpp b/src/gui/windows/predictor/predictor_bht_dock.cpp new file mode 100644 index 00000000..940044c2 --- /dev/null +++ b/src/gui/windows/predictor/predictor_bht_dock.cpp @@ -0,0 +1,425 @@ +#include "predictor_bht_dock.h" + +LOG_CATEGORY("gui.DockPredictorBHT"); + +DockPredictorBHT::DockPredictorBHT(QWidget *parent) : Super(parent) { + setObjectName("PredictorBHT"); + setWindowTitle("Predictor Branch History"); + + ///////////////////////// + // Create widgets + + content = new QGroupBox(); + layout_main = new QVBoxLayout(); + + // Name + layout_type = new QHBoxLayout(); + label_type = new QLabel(); + label_type_value = new QLabel(); + + // Stats + layout_stats = new QGridLayout(); + label_stats_correct_text = new QLabel(); + label_stats_correct_value = new QLabel(); + label_stats_wrong_text = new QLabel(); + label_stats_wrong_value = new QLabel(); + label_stats_accuracy_text = new QLabel(); + label_stats_accuracy_value = new QLabel(); + + // Prediction & Update + layout_event = new QHBoxLayout(); + + // Prediction + group_event_predict = new QGroupBox(); + layout_event_predict = new QVBoxLayout(); + label_event_predict_header = new QLabel(); + label_event_predict_instruction = new QLabel(); + value_event_predict_instruction = new QLineEdit(); + label_event_predict_address = new QLabel(); + value_event_predict_address = new QLineEdit(); + label_event_predict_index = new QLabel(); + value_event_predict_index = new QLineEdit(); + label_event_predict_result = new QLabel(); + value_event_predict_result = new QLineEdit(); + + // Update + group_event_update = new QGroupBox(); + layout_event_update = new QVBoxLayout(); + label_event_update_header = new QLabel(); + label_event_update_instruction = new QLabel(); + value_event_update_instruction = new QLineEdit(); + label_event_update_address = new QLabel(); + value_event_update_address = new QLineEdit(); + label_event_update_index = new QLabel(); + value_event_update_index = new QLineEdit(); + label_event_update_result = new QLabel(); + value_event_update_result = new QLineEdit(); + + // BHR + layout_bhr = new QHBoxLayout(); + label_bhr = new QLabel(); + value_bhr = new QLineEdit(); + + // BHT + bht = new QTableWidget(); + + ///////////////////////// + // Assign layout + + // Name + layout_type->addWidget(label_type); + layout_type->addWidget(label_type_value); + + // Stats + layout_stats->addWidget(label_stats_correct_text, 0, 0); + layout_stats->addWidget(label_stats_correct_value, 0, 1); + layout_stats->addWidget(label_stats_wrong_text, 1, 0); + layout_stats->addWidget(label_stats_wrong_value, 1, 1); + layout_stats->addWidget(label_stats_accuracy_text, 2, 0); + layout_stats->addWidget(label_stats_accuracy_value, 2, 1); + + // Prediction + layout_event->addWidget(group_event_predict); + group_event_predict->setLayout(layout_event_predict); + layout_event_predict->addWidget(label_event_predict_header); + layout_event_predict->addWidget(label_event_predict_instruction); + layout_event_predict->addWidget(value_event_predict_instruction); + layout_event_predict->addWidget(label_event_predict_address); + layout_event_predict->addWidget(value_event_predict_address); + layout_event_predict->addWidget(label_event_predict_index); + layout_event_predict->addWidget(value_event_predict_index); + layout_event_predict->addWidget(label_event_predict_result); + layout_event_predict->addWidget(value_event_predict_result); + + // Update + layout_event->addWidget(group_event_update); + group_event_update->setLayout(layout_event_update); + layout_event_update->addWidget(label_event_update_header); + layout_event_update->addWidget(label_event_update_instruction); + layout_event_update->addWidget(value_event_update_instruction); + layout_event_update->addWidget(label_event_update_address); + layout_event_update->addWidget(value_event_update_address); + layout_event_update->addWidget(label_event_update_index); + layout_event_update->addWidget(value_event_update_index); + layout_event_update->addWidget(label_event_update_result); + layout_event_update->addWidget(value_event_update_result); + + // BHR + layout_bhr->addWidget(label_bhr); + layout_bhr->addWidget(value_bhr); + + // Main layout + layout_main->addLayout(layout_type); + layout_main->addLayout(layout_stats); + layout_main->addLayout(layout_event); + layout_main->addLayout(layout_bhr); + layout_main->addWidget(bht); + + content->setLayout(layout_main); + setWidget(content); + + ///////////////////////// + // Init widget properties + + // Name + label_type->setText("Predictor type:"); + label_type->setStyleSheet("font-weight: bold;"); + label_type_value->setText(""); + + // Stats + label_stats_correct_text->setText("Correct predictions:"); + label_stats_correct_value->setText("0"); + label_stats_wrong_text->setText("Wrong predictions:"); + label_stats_wrong_value->setText("0"); + label_stats_accuracy_text->setText("Accuracy:"); + label_stats_accuracy_value->setText("0 %"); + + // Prediction + label_event_predict_header->setText("Last prediction"); + label_event_predict_header->setStyleSheet("font-weight: bold;"); + label_event_predict_instruction->setText("Instruction:"); + label_event_predict_address->setText("Instruction Address:"); + label_event_predict_index->setText("Computed index:"); + label_event_predict_result->setText("Prediction result:"); + value_event_predict_instruction->setReadOnly(true); + value_event_predict_address->setReadOnly(true); + value_event_predict_index->setReadOnly(true); + value_event_predict_result->setReadOnly(true); + value_event_predict_instruction->setAlignment(Qt::AlignCenter); + value_event_predict_address->setAlignment(Qt::AlignCenter); + value_event_predict_index->setAlignment(Qt::AlignCenter); + value_event_predict_result->setAlignment(Qt::AlignCenter); + + // Update + label_event_update_header->setText("Last update"); + label_event_update_header->setStyleSheet("font-weight: bold;"); + label_event_update_instruction->setText("Instruction:"); + label_event_update_address->setText("Instruction Address:"); + label_event_update_index->setText("Computed index:"); + label_event_update_result->setText("Branch result:"); + value_event_update_instruction->setReadOnly(true); + value_event_update_address->setReadOnly(true); + value_event_update_index->setReadOnly(true); + value_event_update_result->setReadOnly(true); + value_event_update_instruction->setAlignment(Qt::AlignCenter); + value_event_update_address->setAlignment(Qt::AlignCenter); + value_event_update_index->setAlignment(Qt::AlignCenter); + value_event_update_result->setAlignment(Qt::AlignCenter); + + // BHR + label_bhr->setText("Branch History Register:"); + value_bhr->setReadOnly(true); + value_bhr->setAlignment(Qt::AlignCenter); + value_bhr->setText(""); + value_bhr->setFixedWidth(120); + // TODO set tooltips + // value_bhr->setToolTip("TEST"); + + // BHT + bht->setRowCount(0); + bht->setColumnCount(5); // Index, History, Correct, Incorrect, Accuracy + bht->setHorizontalHeaderLabels({ "Index", "History", "Correct", "Incorrect", "Accuracy" }); + bht->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + bht->resizeRowsToContents(); + bht->verticalHeader()->hide(); +} + +void DockPredictorBHT::init_table(machine::PredictorState initial_state) { + for (uint16_t row_index = 0; row_index < bht->rowCount(); row_index++) { + for (uint16_t column_index = 0; column_index < bht->columnCount(); column_index++) { + // Get cell item, or create new one if needed + QTableWidgetItem *item { bht->item(row_index, column_index) }; + if (item == nullptr) { + item = new QTableWidgetItem(); + bht->setItem(row_index, column_index, item); + } + + // Init cell + item->setTextAlignment(Qt::AlignCenter); + item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + if (column_index == DOCK_BHT_COL_INDEX) { + item->setData(Qt::DisplayRole, QString::number(row_index)); + } else if (column_index == DOCK_BHT_COL_HISTORY) { + item->setData( + Qt::DisplayRole, + machine::predictor_state_to_string(initial_state, true).toString()); + } else if (column_index == DOCK_BHT_COL_CORRECT) { + item->setData(Qt::DisplayRole, QString::number(0)); + } else if (column_index == DOCK_BHT_COL_INCORRECT) { + item->setData(Qt::DisplayRole, QString::number(0)); + } else if (column_index == DOCK_BHT_COL_ACCURACY) { + item->setData(Qt::DisplayRole, QString("0 %")); + } + } + } +} + +void DockPredictorBHT::set_table_color(QColor color) { + for (uint16_t row_index = 0; row_index < bht->rowCount(); row_index++) { + for (uint16_t column_index = 0; column_index < bht->columnCount(); column_index++) { + // Get cell item, or create new one if needed + QTableWidgetItem *item { bht->item(row_index, column_index) }; + if (item == nullptr) { + item = new QTableWidgetItem(); + bht->setItem(row_index, column_index, item); + } + + // Set color + item->setBackground(QBrush(color)); + } + } +} + +void DockPredictorBHT::set_row_color(uint16_t row_index, QColor color) { + for (uint16_t column_index = 0; column_index < bht->columnCount(); column_index++) { + // Get cell item, or create new one if needed + QTableWidgetItem *item { bht->item(row_index, column_index) }; + if (item == nullptr) { + item = new QTableWidgetItem(); + bht->setItem(row_index, column_index, item); + } + + // Set color + item->setBackground(QBrush(color)); + } +} + +void DockPredictorBHT::set_predict_widget_color(QString color_stylesheet) { + value_event_predict_instruction->setStyleSheet(color_stylesheet); + value_event_predict_address->setStyleSheet(color_stylesheet); + value_event_predict_index->setStyleSheet(color_stylesheet); + value_event_predict_result->setStyleSheet(color_stylesheet); +} + +void DockPredictorBHT::set_update_widget_color(QString color_stylesheet) { + value_event_update_instruction->setStyleSheet(color_stylesheet); + value_event_update_address->setStyleSheet(color_stylesheet); + value_event_update_index->setStyleSheet(color_stylesheet); + value_event_update_result->setStyleSheet(color_stylesheet); +} + +void DockPredictorBHT::setup( + const machine::BranchPredictor *branch_predictor, + const machine::Core *core) { + connect( + branch_predictor, &machine::BranchPredictor::update_bhr_done, this, + &DockPredictorBHT::update_bhr); + connect( + branch_predictor, &machine::BranchPredictor::prediction_done, this, + &DockPredictorBHT::update_new_prediction); + connect( + branch_predictor, &machine::BranchPredictor::update_predictor_done, this, + &DockPredictorBHT::update_new_update); + connect( + branch_predictor, &machine::BranchPredictor::update_predictor_stats_done, this, + &DockPredictorBHT::update_predictor_stats); + connect( + branch_predictor, &machine::BranchPredictor::update_predictor_bht_row_done, this, + &DockPredictorBHT::update_bht_row); + connect(core, &machine::Core::step_started, this, &DockPredictorBHT::reset_colors); + + number_of_bhr_bits = branch_predictor->get_number_of_bhr_bits(); + number_of_bht_bits = branch_predictor->get_number_of_bht_bits(); + const machine::PredictorType predictor_type { branch_predictor->get_predictor_type() }; + const bool is_predictor_dynamic { predictor_type == machine::PredictorType::SMITH_1_BIT + || predictor_type == machine::PredictorType::SMITH_2_BIT + || predictor_type + == machine::PredictorType::SMITH_2_BIT_HYSTERESIS }; + const bool is_predictor_enabled { branch_predictor->get_enabled() }; + + if (is_predictor_enabled) { + content->setDisabled(false); + } else { + content->setDisabled(true); + } + + // Init BHT + if (is_predictor_dynamic) { + bht->setDisabled(false); + bht->setRowCount(qPow(2, number_of_bht_bits)); + } else { + bht->setDisabled(true); + bht->setRowCount(0); + } + init_table(branch_predictor->get_initial_state()); + bht->resizeRowsToContents(); + set_table_color(Q_COLOR_DEFAULT); + + // Init name + if (is_predictor_enabled) { + label_type_value->setText(branch_predictor->get_predictor_name().toString()); + } else { + label_type_value->setText("None"); + } + + // Init stats + label_stats_correct_value->setText("0"); + label_stats_wrong_value->setText("0"); + label_stats_accuracy_value->setText("0 %"); + + // Init last prediction + value_event_predict_instruction->setText(""); + value_event_predict_address->setText(""); + value_event_predict_index->setText(""); + value_event_predict_result->setText(""); + set_predict_widget_color(STYLESHEET_COLOR_DEFAULT); + + // Init last update + value_event_update_instruction->setText(""); + value_event_update_address->setText(""); + value_event_update_index->setText(""); + value_event_update_result->setText(""); + set_update_widget_color(STYLESHEET_COLOR_DEFAULT); + if (is_predictor_dynamic) { + group_event_update->setDisabled(false); + } else { + group_event_update->setDisabled(true); + } + + // Init BHR + if (number_of_bhr_bits > 0) { + QString bhr_initial_value; + bhr_initial_value.fill('0', number_of_bhr_bits); + value_bhr->setText("0b" + bhr_initial_value); + value_bhr->setDisabled(false); + } else { + value_bhr->setText(""); + value_bhr->setDisabled(true); + } +} + +void DockPredictorBHT::update_bhr(uint8_t number_of_bhr_bits, uint16_t register_value) { + if (number_of_bhr_bits > 0) { + QString binary_value, zero_padding; + binary_value = QString::number(register_value, 2); + zero_padding.fill('0', number_of_bhr_bits - binary_value.count()); + value_bhr->setText("0b" + zero_padding + binary_value); + } +} + +void DockPredictorBHT::update_new_prediction( + uint16_t index, + machine::PredictionInput input, + machine::BranchResult result) { + value_event_predict_instruction->setText(input.instruction.to_str()); + value_event_predict_address->setText(addr_to_hex_str(input.instruction_address)); + value_event_predict_index->setText(QString::number(index)); + value_event_predict_result->setText(machine::branch_result_to_string(result).toString()); + + set_row_color(index, Q_COLOR_PREDICT); + set_predict_widget_color(STYLESHEET_COLOR_PREDICT); +} + +void DockPredictorBHT::update_new_update(uint16_t index, machine::PredictionFeedback feedback) { + value_event_update_instruction->setText(feedback.instruction.to_str()); + value_event_update_address->setText(addr_to_hex_str(feedback.instruction_address)); + value_event_update_index->setText(QString::number(index)); + value_event_update_result->setText( + machine::branch_result_to_string(feedback.result).toString()); + + set_row_color(index, Q_COLOR_UPDATE); + set_update_widget_color(STYLESHEET_COLOR_UPDATE); +} + +void DockPredictorBHT::update_predictor_stats(machine::PredictionStatistics stats) { + label_stats_correct_value->setText(QString::number(stats.number_of_correct_predictions)); + label_stats_wrong_value->setText(QString::number(stats.number_of_wrong_predictions)); + label_stats_accuracy_value->setText(QString::number(stats.accuracy) + " %"); +} + +void DockPredictorBHT::update_bht_row(uint16_t index, machine::BranchHistoryTableEntry entry) { + if (index >= bht->rowCount()) { + WARN("BHT dock update received invalid row index: %u", index); + return; + } + + for (uint16_t column_index = 0; column_index < bht->columnCount(); column_index++) { + // Get cell item, or create new one if needed + QTableWidgetItem *item { bht->item(index, column_index) }; + if (item == nullptr) { + item = new QTableWidgetItem(); + bht->setItem(index, column_index, item); + } + + // Init cell + item->setTextAlignment(Qt::AlignCenter); + if (column_index == DOCK_BHT_COL_HISTORY) { + item->setData( + Qt::DisplayRole, machine::predictor_state_to_string(entry.state, true).toString()); + } else if (column_index == DOCK_BHT_COL_CORRECT) { + item->setData( + Qt::DisplayRole, QString::number(entry.stats.number_of_correct_predictions)); + } else if (column_index == DOCK_BHT_COL_INCORRECT) { + item->setData( + Qt::DisplayRole, QString::number(entry.stats.number_of_wrong_predictions)); + } else if (column_index == DOCK_BHT_COL_ACCURACY) { + item->setData(Qt::DisplayRole, QString::number(entry.stats.accuracy) + " %"); + } + } +}; + +void DockPredictorBHT::reset_colors() { + set_table_color(Q_COLOR_DEFAULT); + set_predict_widget_color(STYLESHEET_COLOR_DEFAULT); + set_update_widget_color(STYLESHEET_COLOR_DEFAULT); +} \ No newline at end of file diff --git a/src/gui/windows/predictor/predictor_bht_dock.h b/src/gui/windows/predictor/predictor_bht_dock.h new file mode 100644 index 00000000..0c33d89a --- /dev/null +++ b/src/gui/windows/predictor/predictor_bht_dock.h @@ -0,0 +1,129 @@ +#ifndef PREDICTOR_BHT_DOCK_H +#define PREDICTOR_BHT_DOCK_H + +#include "common/polyfills/qt5/qtableview.h" +#include "machine/machine.h" +#include "machine/memory/address.h" +#include "machine/predictor.h" +#include "machine/predictor_types.h" +#include "ui/hexlineedit.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DOCK_BHT_COL_INDEX 0 +#define DOCK_BHT_COL_HISTORY 1 +#define DOCK_BHT_COL_CORRECT 2 +#define DOCK_BHT_COL_INCORRECT 3 +#define DOCK_BHT_COL_ACCURACY 4 + +#define STYLESHEET_COLOR_DEFAULT "background: rgb(255,255,255);" +#define STYLESHEET_COLOR_PREDICT "background: rgb(255,173,173);" +#define STYLESHEET_COLOR_UPDATE "background: rgb(173,255,229);" +#define Q_COLOR_DEFAULT QColor(255, 255, 255) +#define Q_COLOR_PREDICT QColor(255, 173, 173) +#define Q_COLOR_UPDATE QColor(173, 255, 229) + +class DockPredictorBHT : public QDockWidget { + Q_OBJECT + + using Super = QDockWidget; + +public: // Constructors & Destructor + DockPredictorBHT(QWidget *parent); + +private: // Internal functions + void init_table(machine::PredictorState initial_state = machine::PredictorState::UNDEFINED); + void set_table_color(QColor color); + void set_row_color(uint16_t row_index, QColor color); + void set_predict_widget_color(QString color_stylesheet); + void set_update_widget_color(QString color_stylesheet); + +public: // General functions + void setup(const machine::BranchPredictor *branch_predictor, const machine::Core *core); + +public slots: + void update_bhr(uint8_t number_of_bhr_bits, uint16_t register_value); + void update_new_prediction( + uint16_t index, + machine::PredictionInput input, + machine::BranchResult result); + void update_new_update(uint16_t index, machine::PredictionFeedback feedback); + void update_predictor_stats(machine::PredictionStatistics stats); + void update_bht_row(uint16_t index, machine::BranchHistoryTableEntry entry); + void reset_colors(); + +private: // Internal variables + uint8_t number_of_bhr_bits; + uint8_t number_of_bht_bits; + + QT_OWNED QGroupBox *content; + + QT_OWNED QVBoxLayout *layout_main; + + // Name + QT_OWNED QHBoxLayout *layout_type; + QT_OWNED QLabel *label_type; + QT_OWNED QLabel *label_type_value; + + // Stats + QT_OWNED QGridLayout *layout_stats; + QT_OWNED QLabel *label_stats_correct_text; + QT_OWNED QLabel *label_stats_wrong_text; + QT_OWNED QLabel *label_stats_accuracy_text; + QT_OWNED QLabel *label_stats_correct_value; + QT_OWNED QLabel *label_stats_wrong_value; + QT_OWNED QLabel *label_stats_accuracy_value; + + // Prediction & Update + QT_OWNED QHBoxLayout *layout_event; + + // Prediction + QT_OWNED QGroupBox *group_event_predict; + QT_OWNED QVBoxLayout *layout_event_predict; + QT_OWNED QLabel *label_event_predict_header; + QT_OWNED QLabel *label_event_predict_instruction; + QT_OWNED QLabel *label_event_predict_address; + QT_OWNED QLabel *label_event_predict_index; + QT_OWNED QLabel *label_event_predict_result; + QT_OWNED QLineEdit *value_event_predict_instruction; + QT_OWNED QLineEdit *value_event_predict_address; + QT_OWNED QLineEdit *value_event_predict_index; + QT_OWNED QLineEdit *value_event_predict_result; + + // Update + QT_OWNED QGroupBox *group_event_update; + QT_OWNED QVBoxLayout *layout_event_update; + QT_OWNED QLabel *label_event_update_header; + QT_OWNED QLabel *label_event_update_instruction; + QT_OWNED QLabel *label_event_update_address; + QT_OWNED QLabel *label_event_update_index; + QT_OWNED QLabel *label_event_update_result; + QT_OWNED QLineEdit *value_event_update_instruction; + QT_OWNED QLineEdit *value_event_update_address; + QT_OWNED QLineEdit *value_event_update_index; + QT_OWNED QLineEdit *value_event_update_result; + + // BHR + QT_OWNED QHBoxLayout *layout_bhr; + QT_OWNED QLabel *label_bhr; + QT_OWNED QLineEdit *value_bhr; + + // BHT + QT_OWNED QTableWidget *bht; +}; + +#endif // PREDICTOR_BHT_DOCK_H \ No newline at end of file diff --git a/src/gui/windows/predictor/predictor_btb_dock.cpp b/src/gui/windows/predictor/predictor_btb_dock.cpp new file mode 100644 index 00000000..4858a46b --- /dev/null +++ b/src/gui/windows/predictor/predictor_btb_dock.cpp @@ -0,0 +1,163 @@ +#include "predictor_btb_dock.h" + +LOG_CATEGORY("gui.DockPredictorBTB"); + +DockPredictorBTB::DockPredictorBTB(QWidget *parent) : Super(parent) { + setObjectName("PredictorBTB"); + setWindowTitle("Predictor Branch Target Buffer"); + + btb = new QTableWidget(); + btb->setRowCount(0); + btb->setColumnCount(3); // Index, Instruction Address, Target Address + btb->setHorizontalHeaderLabels({ "Index", "Instruction Address", "Target Address" }); + btb->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + btb->resizeRowsToContents(); + btb->verticalHeader()->hide(); + init_table(); + + layout = new QVBoxLayout(); + layout->addWidget(btb); + + content = new QWidget(); + content->setLayout(layout); + setWidget(content); +}; + +DockPredictorBTB::~DockPredictorBTB() { + delete btb; + btb = nullptr; + delete layout; + layout = nullptr; + delete content; + content = nullptr; +}; + +uint8_t DockPredictorBTB::init_number_of_bits(const uint8_t b) const { + if (b > BP_MAX_BTB_BITS) { + WARN("Number of BTB bits (%u) was larger than %u during init", b, BP_MAX_BTB_BITS); + return BP_MAX_BTB_BITS; + } + return b; +}; + +void DockPredictorBTB::init_table() { + for (uint16_t row_index = 0; row_index < btb->rowCount(); row_index++) { + for (uint16_t column_index = 0; column_index < btb->columnCount(); column_index++) { + // Get cell item, or create new one if needed + QTableWidgetItem *item { btb->item(row_index, column_index) }; + if (item == nullptr) { + item = new QTableWidgetItem(); + btb->setItem(row_index, column_index, item); + } + + // Init cell + item->setTextAlignment(Qt::AlignCenter); + item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + if (column_index == DOCK_BTB_COL_INDEX) { + item->setData(Qt::DisplayRole, QString::number(row_index)); + } else if (column_index == DOCK_BTB_COL_INSTR_ADDR) { + item->setData(Qt::DisplayRole, QString("")); + } else if (column_index == DOCK_BTB_COL_TARGET_ADDR) { + item->setData(Qt::DisplayRole, QString("")); + } + } + } +}; + +void DockPredictorBTB::set_table_color(QColor color) { + for (uint16_t row_index = 0; row_index < btb->rowCount(); row_index++) { + for (uint16_t column_index = 0; column_index < btb->columnCount(); column_index++) { + // Get cell item, or create new one if needed + QTableWidgetItem *item { btb->item(row_index, column_index) }; + if (item == nullptr) { + item = new QTableWidgetItem(); + btb->setItem(row_index, column_index, item); + } + + // Set color + item->setBackground(QBrush(color)); + } + } +}; + +void DockPredictorBTB::set_row_color(uint16_t row_index, QColor color) { + for (uint16_t column_index = 0; column_index < btb->columnCount(); column_index++) { + // Get cell item, or create new one if needed + QTableWidgetItem *item { btb->item(row_index, column_index) }; + if (item == nullptr) { + item = new QTableWidgetItem(); + btb->setItem(row_index, column_index, item); + } + + // Set color + item->setBackground(QBrush(color)); + } +}; + +void DockPredictorBTB::setup( + const machine::BranchPredictor *branch_predictor, + const machine::Core *core) { + connect( + branch_predictor, &machine::BranchPredictor::update_btb_row_done, this, + &DockPredictorBTB::update_row); + connect( + branch_predictor, &machine::BranchPredictor::requested_bht_target_address, this, + &DockPredictorBTB::highligh_row_after_prediction); + connect(core, &machine::Core::step_started, this, &DockPredictorBTB::reset_colors); + + number_of_bits = init_number_of_bits(branch_predictor->get_number_of_btb_bits()); + const bool is_predictor_enabled { branch_predictor->get_enabled() }; + + if (is_predictor_enabled) { + btb->setRowCount(qPow(2, number_of_bits)); + btb->setDisabled(false); + } else { + btb->setRowCount(0); + btb->setDisabled(true); + } + init_table(); + btb->resizeRowsToContents(); + set_table_color(Q_COLOR_DEFAULT); +}; + +void DockPredictorBTB::update_row( + uint16_t index, + machine::Address instruction_address, + machine::Address target_address) { + if (index >= btb->rowCount()) { + WARN("BTB dock update received invalid row index: %u", index); + return; + } + + set_row_color(index, Q_COLOR_UPDATE); + + QTableWidgetItem *item_index { btb->item(index, DOCK_BTB_COL_INDEX) }; + QTableWidgetItem *item_instr_addr { btb->item(index, DOCK_BTB_COL_INSTR_ADDR) }; + QTableWidgetItem *item_target_addr { btb->item(index, DOCK_BTB_COL_TARGET_ADDR) }; + + if (item_index == nullptr) { + item_index = new QTableWidgetItem(); + btb->setItem(index, DOCK_BTB_COL_INDEX, item_index); + } + + if (item_instr_addr == nullptr) { + item_instr_addr = new QTableWidgetItem(); + btb->setItem(index, DOCK_BTB_COL_INSTR_ADDR, item_instr_addr); + } + + if (item_target_addr == nullptr) { + item_target_addr = new QTableWidgetItem(); + btb->setItem(index, DOCK_BTB_COL_TARGET_ADDR, item_target_addr); + } + + item_instr_addr->setData(Qt::DisplayRole, machine::addr_to_hex_str(instruction_address)); + item_target_addr->setData(Qt::DisplayRole, machine::addr_to_hex_str(target_address)); +}; + +void DockPredictorBTB::highligh_row_after_prediction(uint16_t index) { + set_row_color(index, Q_COLOR_PREDICT); +} + +void DockPredictorBTB::reset_colors() { + set_table_color(Q_COLOR_DEFAULT); +} \ No newline at end of file diff --git a/src/gui/windows/predictor/predictor_btb_dock.h b/src/gui/windows/predictor/predictor_btb_dock.h new file mode 100644 index 00000000..bcfcca68 --- /dev/null +++ b/src/gui/windows/predictor/predictor_btb_dock.h @@ -0,0 +1,66 @@ +#ifndef PREDICTOR_BTB_DOCK_H +#define PREDICTOR_BTB_DOCK_H + +#include "common/polyfills/qt5/qtableview.h" +#include "machine/machine.h" +#include "machine/memory/address.h" +#include "machine/predictor.h" +#include "machine/predictor_types.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DOCK_BTB_COL_INDEX 0 +#define DOCK_BTB_COL_INSTR_ADDR 1 +#define DOCK_BTB_COL_TARGET_ADDR 2 + +#define Q_COLOR_DEFAULT QColor(255, 255, 255) +#define Q_COLOR_PREDICT QColor(255, 173, 173) +#define Q_COLOR_UPDATE QColor(173, 255, 229) + +// Branch Target Buffer Dock +class DockPredictorBTB : public QDockWidget { + Q_OBJECT + + using Super = QDockWidget; + +public: // Constructors & Destructor + DockPredictorBTB(QWidget *parent); + ~DockPredictorBTB(); + +private: // Internal functions + uint8_t init_number_of_bits(const uint8_t b) const; + void init_table(); + void set_table_color(QColor color); + void set_row_color(uint16_t row_index, QColor color); + +public: // General functions + void setup(const machine::BranchPredictor *branch_predictor, const machine::Core *core); + +public slots: + void update_row( + uint16_t index, + machine::Address instruction_address, + machine::Address target_address); + void highligh_row_after_prediction(uint16_t index); + void reset_colors(); + +private: // Internal variables + uint8_t number_of_bits; + QTableWidget *btb; + QVBoxLayout *layout; + QWidget *content; +}; + +#endif // PREDICTOR_BTB_DOCK_H \ No newline at end of file diff --git a/src/machine/CMakeLists.txt b/src/machine/CMakeLists.txt index 3f226d32..f3cca553 100644 --- a/src/machine/CMakeLists.txt +++ b/src/machine/CMakeLists.txt @@ -23,6 +23,7 @@ set(machine_SOURCES memory/frontend_memory.cpp memory/memory_bus.cpp programloader.cpp + predictor.cpp registers.cpp simulator_exception.cpp symboltable.cpp @@ -57,6 +58,7 @@ set(machine_HEADERS memory/memory_bus.h memory/memory_utils.h programloader.h + predictor_types.h predictor.h pipeline.h registers.h @@ -207,6 +209,9 @@ if(NOT ${WASM}) memory/memory_bus.h registers.cpp registers.h + predictor.cpp + predictor.h + predictor_types.h simulator_exception.cpp simulator_exception.h machineconfig.cpp diff --git a/src/machine/core.cpp b/src/machine/core.cpp index 250c70b5..8b120a3d 100644 --- a/src/machine/core.cpp +++ b/src/machine/core.cpp @@ -22,12 +22,14 @@ static InstructionFlags unsupported_inst_flags_to_check(Xlen xlen, return InstructionFlags(flags_to_check); } -Core::Core(Registers *regs, - Predictor *predictor, +Core::Core( + Registers *regs, + BranchPredictor *predictor, FrontendMemory *mem_program, FrontendMemory *mem_data, CSR::ControlState *control_state, - Xlen xlen, ConfigIsaWord isa_word) + Xlen xlen, + ConfigIsaWord isa_word) : pc_if(state.pipeline.pc.final) , if_id(state.pipeline.fetch.final) , id_ex(state.pipeline.decode.final) @@ -49,6 +51,7 @@ Core::Core(Registers *regs, } void Core::step(bool skip_break) { + emit step_started(); state.cycle_count++; do_step(skip_break); emit step_done(state); @@ -84,7 +87,7 @@ FrontendMemory *Core::get_mem_program() const { return mem_program; } -Predictor *Core::get_predictor() const { +BranchPredictor *Core::get_predictor() const { return predictor; } @@ -296,7 +299,7 @@ FetchState Core::fetch(PCInterstage pc, bool skip_break) { .inst = inst, .inst_addr = inst_addr, .next_inst_addr = inst_addr + inst.size(), - .predicted_next_inst_addr = predictor->predict(inst, inst_addr), + .predicted_next_inst_addr = predictor->predict_next_pc_address(inst, inst_addr), .excause = excause, .is_valid = true, } }; @@ -495,6 +498,22 @@ MemoryState Core::memory(const ExecuteInterstage &dt) { computed_next_inst_addr = compute_next_inst_addr(dt, branch_bxx_taken); + // Predictor update + if (dt.branch_jal) { + // JAL Jump instruction (J-type (alternative to U-type with different immediate bit order)) + predictor->update(dt.inst, dt.inst_addr, dt.branch_jal_target, BranchResult::TAKEN); + } else if (dt.branch_jalr) { + // JALR Jump register instruction (I-type) + predictor->update( + dt.inst, dt.inst_addr, Address(get_xlen_from_reg(dt.alu_val)), BranchResult::TAKEN); + } else if (dt.branch_bxx) { + // BXX Conditional branch instruction (B-type (alternative to S-type with different + // immediate bit order)) + predictor->update( + dt.inst, dt.inst_addr, dt.branch_jal_target, + branch_bxx_taken ? BranchResult::TAKEN : BranchResult::NOT_TAKEN); + } + bool csr_written = false; if (control_state != nullptr && dt.is_valid && dt.excause == EXCAUSE_NONE) { control_state->increment_internal(CSR::Id::MINSTRET, 1); @@ -575,12 +594,14 @@ uint64_t Core::get_xlen_from_reg(RegisterValue reg) const { } } -CoreSingle::CoreSingle(Registers *regs, - Predictor *predictor, +CoreSingle::CoreSingle( + Registers *regs, + BranchPredictor *predictor, FrontendMemory *mem_program, FrontendMemory *mem_data, CSR::ControlState *control_state, - Xlen xlen, ConfigIsaWord isa_word) + Xlen xlen, + ConfigIsaWord isa_word) : Core(regs, predictor, mem_program, mem_data, control_state, xlen, isa_word) { reset(); } @@ -612,7 +633,7 @@ void CoreSingle::do_reset() { CorePipelined::CorePipelined( Registers *regs, - Predictor *predictor, + BranchPredictor *predictor, FrontendMemory *mem_program, FrontendMemory *mem_data, CSR::ControlState *control_state, diff --git a/src/machine/core.h b/src/machine/core.h index e326dd6a..3f2f1855 100644 --- a/src/machine/core.h +++ b/src/machine/core.h @@ -29,7 +29,7 @@ class Core : public QObject { public: Core( Registers *regs, - Predictor *predictor, + BranchPredictor *predictor, FrontendMemory *mem_program, FrontendMemory *mem_data, CSR::ControlState *control_state, @@ -44,7 +44,7 @@ class Core : public QObject { Registers *get_regs() const; CSR::ControlState *get_control_state() const; - Predictor *get_predictor() const; + BranchPredictor *get_predictor() const; FrontendMemory *get_mem_data() const; FrontendMemory *get_mem_program() const; const CoreState &get_state() const; @@ -92,6 +92,7 @@ class Core : public QObject { signals: void stop_on_exception_reached(); + void step_started(); void step_done(const CoreState &); protected: @@ -111,7 +112,7 @@ class Core : public QObject { const InstructionFlags check_inst_flags_mask; BORROWED Registers *const regs; BORROWED CSR::ControlState *const control_state; - BORROWED Predictor *const predictor; + BORROWED BranchPredictor *const predictor; BORROWED FrontendMemory *const mem_data, *const mem_program; array stop_on_exception {}; @@ -146,7 +147,7 @@ class CoreSingle : public Core { public: CoreSingle( Registers *regs, - Predictor *predictor, + BranchPredictor *predictor, FrontendMemory *mem_program, FrontendMemory *mem_data, CSR::ControlState *control_state, @@ -165,7 +166,7 @@ class CorePipelined : public Core { public: CorePipelined( Registers *regs, - Predictor *predictor, + BranchPredictor *predictor, FrontendMemory *mem_program, FrontendMemory *mem_data, CSR::ControlState *control_state, diff --git a/src/machine/core.test.cpp b/src/machine/core.test.cpp index 6c740b58..f1aed691 100644 --- a/src/machine/core.test.cpp +++ b/src/machine/core.test.cpp @@ -5,6 +5,7 @@ #include "machine/memory/backend/memory.h" #include "machine/memory/cache/cache.h" #include "machine/memory/memory_bus.h" +#include "machine/predictor.h" #include @@ -46,7 +47,7 @@ void test_program_with_single_result() { Memory memory_backend(BIG); TrivialBus memory(&memory_backend); - FalsePredictor predictor {}; + BranchPredictor predictor {}; CSR::ControlState controlst {}; Core core(®isters, &predictor, &memory, &memory, &controlst, Xlen::_32, config_isa_word_default); @@ -703,7 +704,7 @@ void TestCore::singlecore_alu_forward() { Memory mem_res(LITTLE); TrivialBus mem_res_frontend(&mem_res); - FalsePredictor predictor {}; + BranchPredictor predictor {}; CSR::ControlState controlst {}; CoreSingle core(®_init, &predictor, &mem_init_frontend, &mem_init_frontend, &controlst, Xlen::_32, config_isa_word_default); @@ -719,7 +720,7 @@ void TestCore::pipecore_alu_forward() { Memory mem_res(LITTLE); TrivialBus mem_res_frontend(&mem_res); - FalsePredictor predictor {}; + BranchPredictor predictor {}; CSR::ControlState controlst {}; CorePipelined core(®_init, &predictor, &mem_init_frontend, &mem_init_frontend, &controlst, @@ -736,7 +737,7 @@ void TestCore::pipecorestall_alu_forward() { Memory mem_res(LITTLE); TrivialBus mem_res_frontend(&mem_res); - FalsePredictor predictor {}; + BranchPredictor predictor {}; CSR::ControlState controlst {}; CorePipelined core(®_init, &predictor, &mem_init_frontend, &mem_init_frontend, &controlst, @@ -944,7 +945,7 @@ void TestCore::singlecore_memory_tests() { TrivialBus mem_init_frontend(&mem_init); TrivialBus mem_res_frontend(&mem_res); - FalsePredictor predictor {}; + BranchPredictor predictor {}; CSR::ControlState controlst {}; CoreSingle core(®_init, &predictor, &mem_init_frontend, &mem_init_frontend, &controlst, Xlen::_32, config_isa_word_default); @@ -960,7 +961,7 @@ void TestCore::pipecore_nc_memory_tests() { TrivialBus mem_init_frontend(&mem_init); TrivialBus mem_res_frontend(&mem_res); - FalsePredictor predictor {}; + BranchPredictor predictor {}; CSR::ControlState controlst {}; CorePipelined core(®_init, &predictor, &mem_init_frontend, &mem_init_frontend, &controlst, Xlen::_32, config_isa_word_default); @@ -986,7 +987,7 @@ void TestCore::pipecore_wt_na_memory_tests() { Cache i_cache(&mem_init_frontend, &cache_conf); Cache d_cache(&mem_init_frontend, &cache_conf); - FalsePredictor predictor {}; + BranchPredictor predictor {}; CSR::ControlState controlst {}; CorePipelined core(®_init, &predictor, &i_cache, &d_cache, &controlst, Xlen::_32, config_isa_word_default); @@ -1011,7 +1012,7 @@ void TestCore::pipecore_wt_a_memory_tests() { Cache i_cache(&mem_init_frontend, &cache_conf); Cache d_cache(&mem_init_frontend, &cache_conf); - FalsePredictor predictor {}; + BranchPredictor predictor {}; CSR::ControlState controlst {}; CorePipelined core(®_init, &predictor, &i_cache, &d_cache, &controlst, Xlen::_32, config_isa_word_default); @@ -1036,7 +1037,7 @@ void TestCore::pipecore_wb_memory_tests() { Cache i_cache(&mem_init_frontend, &cache_conf); Cache d_cache(&mem_init_frontend, &cache_conf); - FalsePredictor predictor {}; + BranchPredictor predictor {}; CSR::ControlState controlst {}; CorePipelined core(®_init, &predictor, &i_cache, &d_cache, &controlst, Xlen::_32, config_isa_word_default); diff --git a/src/machine/machine.cpp b/src/machine/machine.cpp index ea9a7565..50d56355 100644 --- a/src/machine/machine.cpp +++ b/src/machine/machine.cpp @@ -78,7 +78,10 @@ Machine::Machine(MachineConfig config, bool load_symtab, bool load_executable) access_enable_burst); controlst = new CSR::ControlState(machine_config.get_simulated_xlen(), machine_config.get_isa_word()); - predictor = new FalsePredictor(); + predictor = new BranchPredictor( + machine_config.get_bp_enabled(), machine_config.get_bp_type(), + machine_config.get_bp_init_state(), machine_config.get_bp_btb_bits(), + machine_config.get_bp_bhr_bits(), machine_config.get_bp_bht_addr_bits()); if (machine_config.pipelined()) { cr = new CorePipelined( diff --git a/src/machine/machine.h b/src/machine/machine.h index edec4af1..1d98b6b0 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -125,7 +125,7 @@ private slots: Cache *cch_data = nullptr; Cache *cch_level2 = nullptr; CSR::ControlState *controlst = nullptr; - Predictor *predictor = nullptr; + BranchPredictor *predictor = nullptr; Core *cr = nullptr; QTimer *run_t = nullptr; diff --git a/src/machine/machineconfig.cpp b/src/machine/machineconfig.cpp index 38fd6e4d..d5d7083a 100644 --- a/src/machine/machineconfig.cpp +++ b/src/machine/machineconfig.cpp @@ -20,6 +20,13 @@ using namespace machine; #define DF_MEM_ACC_LEVEL2 2 #define DF_MEM_ACC_BURST_ENABLE false #define DF_ELF QString("") +/// Default config of branch predictor +#define DFC_BP_ENABLED false +#define DFC_BP_TYPE PredictorType::SMITH_1_BIT +#define DFC_BP_INIT_STATE PredictorState::NOT_TAKEN +#define DFC_BP_BTB_BITS 2 +#define DFC_BP_BHR_BITS 0 +#define DFC_BP_BHT_ADDR_BITS 2 ////////////////////////////////////////////////////////////////////////////// /// Default config of CacheConfig #define DFC_EN false @@ -55,9 +62,7 @@ CacheConfig::CacheConfig(const QSettings *sts, const QString &prefix) { n_sets = sts->value(N("Sets"), DFC_SETS).toUInt(); n_blocks = sts->value(N("Blocks"), DFC_BLOCKS).toUInt(); d_associativity = sts->value(N("Associativity"), DFC_ASSOC).toUInt(); - replac_pol - = (enum ReplacementPolicy)sts->value(N("Replacement"), DFC_REPLAC) - .toUInt(); + replac_pol = (enum ReplacementPolicy)sts->value(N("Replacement"), DFC_REPLAC).toUInt(); write_pol = (enum WritePolicy)sts->value(N("Write"), DFC_WRITE).toUInt(); } @@ -138,9 +143,8 @@ enum CacheConfig::WritePolicy CacheConfig::write_policy() const { bool CacheConfig::operator==(const CacheConfig &c) const { #define CMP(GETTER) (GETTER)() == (c.GETTER)() - return CMP(enabled) && CMP(set_count) && CMP(block_size) - && CMP(associativity) && CMP(replacement_policy) - && CMP(write_policy); + return CMP(enabled) && CMP(set_count) && CMP(block_size) && CMP(associativity) + && CMP(replacement_policy) && CMP(write_policy); #undef CMP } @@ -173,6 +177,15 @@ MachineConfig::MachineConfig() { cch_program = CacheConfig(); cch_data = CacheConfig(); cch_level2 = CacheConfig(); + + // Branch predictor + bp_enabled = DFC_BP_ENABLED; + bp_type = DFC_BP_TYPE; + bp_init_state = DFC_BP_INIT_STATE; + bp_btb_bits = DFC_BP_BTB_BITS; + bp_bhr_bits = DFC_BP_BHR_BITS; + bp_bht_addr_bits = DFC_BP_BHT_ADDR_BITS; + bp_bht_bits = bp_bhr_bits + bp_bht_addr_bits; } MachineConfig::MachineConfig(const MachineConfig *config) { @@ -200,6 +213,15 @@ MachineConfig::MachineConfig(const MachineConfig *config) { cch_program = config->cache_program(); cch_data = config->cache_data(); cch_level2 = config->cache_level2(); + + // Branch predictor + bp_enabled = config->get_bp_enabled(); + bp_type = config->get_bp_type(); + bp_init_state = config->get_bp_init_state(); + bp_btb_bits = config->get_bp_btb_bits(); + bp_bhr_bits = config->get_bp_bhr_bits(); + bp_bht_addr_bits = config->get_bp_bht_addr_bits(); + bp_bht_bits = bp_bhr_bits + bp_bht_addr_bits; } #define N(STR) (prefix + QString(STR)) @@ -207,27 +229,24 @@ MachineConfig::MachineConfig(const MachineConfig *config) { MachineConfig::MachineConfig(const QSettings *sts, const QString &prefix) { simulated_endian = LITTLE; unsigned int xlen_num_bits = sts->value(N("XlenBits"), 32).toUInt(); - simulated_xlen = xlen_num_bits == 64? Xlen::_64 : Xlen::_32; - isa_word = ConfigIsaWord(sts->value(N("IsaWord"), config_isa_word_default.toUnsigned()).toUInt()); + simulated_xlen = xlen_num_bits == 64 ? Xlen::_64 : Xlen::_32; + isa_word + = ConfigIsaWord(sts->value(N("IsaWord"), config_isa_word_default.toUnsigned()).toUInt()); isa_word |= config_isa_word_default & config_isa_word_fixed; isa_word &= config_isa_word_default | ~config_isa_word_fixed; pipeline = sts->value(N("Pipelined"), DF_PIPELINE).toBool(); delayslot = sts->value(N("DelaySlot"), DF_DELAYSLOT).toBool(); hunit = (enum HazardUnit)sts->value(N("HazardUnit"), DF_HUNIT).toUInt(); - exec_protect - = sts->value(N("MemoryExecuteProtection"), DF_EXEC_PROTEC).toBool(); - write_protect - = sts->value(N("MemoryWriteProtection"), DF_WRITE_PROTEC).toBool(); + exec_protect = sts->value(N("MemoryExecuteProtection"), DF_EXEC_PROTEC).toBool(); + write_protect = sts->value(N("MemoryWriteProtection"), DF_WRITE_PROTEC).toBool(); mem_acc_read = sts->value(N("MemoryRead"), DF_MEM_ACC_READ).toUInt(); mem_acc_write = sts->value(N("MemoryWrite"), DF_MEM_ACC_WRITE).toUInt(); mem_acc_burst = sts->value(N("MemoryBurst"), DF_MEM_ACC_BURST).toUInt(); mem_acc_level2 = sts->value(N("MemoryLevel2"), DF_MEM_ACC_LEVEL2).toUInt(); mem_acc_enable_burst = sts->value(N("MemoryBurstEnable"), DF_MEM_ACC_BURST_ENABLE).toBool(); osem_enable = sts->value(N("OsemuEnable"), true).toBool(); - osem_known_syscall_stop - = sts->value(N("OsemuKnownSyscallStop"), true).toBool(); - osem_unknown_syscall_stop - = sts->value(N("OsemuUnknownSyscallStop"), true).toBool(); + osem_known_syscall_stop = sts->value(N("OsemuKnownSyscallStop"), true).toBool(); + osem_unknown_syscall_stop = sts->value(N("OsemuUnknownSyscallStop"), true).toBool(); osem_interrupt_stop = sts->value(N("OsemuInterruptStop"), true).toBool(); osem_exception_stop = sts->value(N("OsemuExceptionStop"), true).toBool(); osem_fs_root = sts->value(N("OsemuFilesystemRoot"), "").toString(); @@ -236,10 +255,21 @@ MachineConfig::MachineConfig(const QSettings *sts, const QString &prefix) { cch_program = CacheConfig(sts, N("ProgramCache_")); cch_data = CacheConfig(sts, N("DataCache_")); cch_level2 = CacheConfig(sts, N("Level2Cache_")); + + // Branch predictor + bp_enabled = sts->value(N("BranchPredictor_Enabled"), DFC_BP_ENABLED).toBool(); + bp_type = (PredictorType)sts->value(N("BranchPredictor_Type"), (uint8_t)DFC_BP_TYPE).toUInt(); + bp_init_state + = (PredictorState)sts->value(N("BranchPredictor_InitState"), (uint8_t)DFC_BP_INIT_STATE) + .toUInt(); + bp_btb_bits = sts->value(N("BranchPredictor_BitsBTB"), DFC_BP_BTB_BITS).toUInt(); + bp_bhr_bits = sts->value(N("BranchPredictor_BitsBHR"), DFC_BP_BHR_BITS).toUInt(); + bp_bht_addr_bits = sts->value(N("BranchPredictor_BitsBHTAddr"), DFC_BP_BHT_ADDR_BITS).toUInt(); + bp_bht_bits = bp_bhr_bits + bp_bht_addr_bits; } void MachineConfig::store(QSettings *sts, const QString &prefix) { - sts->setValue(N("XlenBits"), get_simulated_xlen() == Xlen::_64? 64: 32); + sts->setValue(N("XlenBits"), get_simulated_xlen() == Xlen::_64 ? 64 : 32); sts->setValue(N("IsaWord"), get_isa_word().toUnsigned()); sts->setValue(N("Pipelined"), pipelined()); sts->setValue(N("DelaySlot"), delay_slot()); @@ -260,6 +290,14 @@ void MachineConfig::store(QSettings *sts, const QString &prefix) { cch_program.store(sts, N("ProgramCache_")); cch_data.store(sts, N("DataCache_")); cch_level2.store(sts, N("Level2Cache_")); + + // Branch predictor + sts->setValue(N("BranchPredictor_Enabled"), get_bp_enabled()); + sts->setValue(N("BranchPredictor_Type"), (uint8_t)get_bp_type()); + sts->setValue(N("BranchPredictor_InitState"), (uint8_t)get_bp_init_state()); + sts->setValue(N("BranchPredictor_BitsBTB"), get_bp_btb_bits()); + sts->setValue(N("BranchPredictor_BitsBHR"), get_bp_bhr_bits()); + sts->setValue(N("BranchPredictor_BitsBHTAddr"), get_bp_bht_addr_bits()); } #undef N @@ -302,9 +340,7 @@ void MachineConfig::preset(enum ConfigPresets p) { case CP_SINGLE: case CP_SINGLE_CACHE: case CP_PIPE_NO_HAZARD: - case CP_PIPE: - access_cache_level2()->set_enabled(false); - break; + case CP_PIPE: access_cache_level2()->set_enabled(false); break; } } @@ -327,9 +363,7 @@ bool MachineConfig::set_hazard_unit(const QString &hukind) { { "forward", HU_STALL_FORWARD }, { "stall-forward", HU_STALL_FORWARD }, }; - if (!hukind_map.contains(hukind)) { - return false; - } + if (!hukind_map.contains(hukind)) { return false; } set_hazard_unit(hukind_map.value(hukind)); return true; } @@ -528,16 +562,73 @@ ConfigIsaWord MachineConfig::get_isa_word() const { return isa_word; } +void MachineConfig::set_bp_enabled(bool e) { + bp_enabled = e; +} + +void MachineConfig::set_bp_type(PredictorType t) { + bp_type = t; +} + +void MachineConfig::set_bp_init_state(PredictorState i) { + bp_init_state = i; +} + +void MachineConfig::set_bp_btb_bits(uint8_t b) { + bp_btb_bits = b > BP_MAX_BTB_BITS ? BP_MAX_BTB_BITS : b; +} + +void MachineConfig::set_bp_bhr_bits(uint8_t b) { + bp_bhr_bits = b > BP_MAX_BHR_BITS ? BP_MAX_BHR_BITS : b; + bp_bht_addr_bits + = bp_bht_addr_bits > BP_MAX_BHT_ADDR_BITS ? BP_MAX_BHT_ADDR_BITS : bp_bht_addr_bits; + bp_bht_bits = bp_bhr_bits + bp_bht_addr_bits; + bp_bht_bits = bp_bht_bits > BP_MAX_BHT_BITS ? BP_MAX_BHT_BITS : bp_bht_bits; +} + +void MachineConfig::set_bp_bht_addr_bits(uint8_t b) { + bp_bht_addr_bits = b > BP_MAX_BHT_ADDR_BITS ? BP_MAX_BHT_ADDR_BITS : b; + bp_bhr_bits = bp_bhr_bits > BP_MAX_BHR_BITS ? BP_MAX_BHR_BITS : bp_bhr_bits; + bp_bht_bits = bp_bhr_bits + bp_bht_addr_bits; + bp_bht_bits = bp_bht_bits > BP_MAX_BHT_BITS ? BP_MAX_BHT_BITS : bp_bht_bits; +} + +bool MachineConfig::get_bp_enabled() const { + return bp_enabled; +} + +PredictorType MachineConfig::get_bp_type() const { + return bp_type; +} + +PredictorState MachineConfig::get_bp_init_state() const { + return bp_init_state; +} + +uint8_t MachineConfig::get_bp_btb_bits() const { + return bp_btb_bits; +} + +uint8_t MachineConfig::get_bp_bhr_bits() const { + return bp_bhr_bits; +} + +uint8_t MachineConfig::get_bp_bht_addr_bits() const { + return bp_bht_addr_bits; +} + +uint8_t MachineConfig::get_bp_bht_bits() const { + return bp_bht_bits; +} + bool MachineConfig::operator==(const MachineConfig &c) const { #define CMP(GETTER) (GETTER)() == (c.GETTER)() - return CMP(pipelined) && CMP(delay_slot) && CMP(hazard_unit) - && CMP(get_simulated_xlen) && CMP(get_isa_word) - && CMP(memory_execute_protection) && CMP(memory_write_protection) + return CMP(pipelined) && CMP(delay_slot) && CMP(hazard_unit) && CMP(get_simulated_xlen) + && CMP(get_isa_word) && CMP(memory_execute_protection) && CMP(memory_write_protection) && CMP(memory_access_time_read) && CMP(memory_access_time_write) && CMP(memory_access_time_burst) && CMP(memory_access_time_level2) - && CMP(memory_access_enable_burst) - && CMP(elf) && CMP(cache_program) - && CMP(cache_data) && CMP(cache_level2); + && CMP(memory_access_enable_burst) && CMP(elf) && CMP(cache_program) && CMP(cache_data) + && CMP(cache_level2); #undef CMP } diff --git a/src/machine/machineconfig.h b/src/machine/machineconfig.h index 01b27485..f331d1eb 100644 --- a/src/machine/machineconfig.h +++ b/src/machine/machineconfig.h @@ -3,6 +3,7 @@ #include "common/endian.h" #include "config_isa.h" +#include "predictor_types.h" #include #include @@ -159,6 +160,22 @@ class MachineConfig { Xlen get_simulated_xlen() const; ConfigIsaWord get_isa_word() const; + // Branch predictor - Setters + void set_bp_enabled(bool e); + void set_bp_type(PredictorType t); + void set_bp_init_state(PredictorState i); + void set_bp_btb_bits(uint8_t b); + void set_bp_bhr_bits(uint8_t b); + void set_bp_bht_addr_bits(uint8_t b); + // Branch predictor - Getters + bool get_bp_enabled() const; + PredictorType get_bp_type() const; + PredictorState get_bp_init_state() const; + uint8_t get_bp_btb_bits() const; + uint8_t get_bp_bhr_bits() const; + uint8_t get_bp_bht_addr_bits() const; + uint8_t get_bp_bht_bits() const; + CacheConfig *access_cache_program(); CacheConfig *access_cache_data(); CacheConfig *access_cache_level2(); @@ -181,6 +198,15 @@ class MachineConfig { Endian simulated_endian; Xlen simulated_xlen; ConfigIsaWord isa_word; + + // Branch predictor + bool bp_enabled; + PredictorType bp_type; + PredictorState bp_init_state; + uint8_t bp_btb_bits; + uint8_t bp_bhr_bits; + uint8_t bp_bht_addr_bits; + uint8_t bp_bht_bits; // = bp_bhr_bits + bp_bht_addr_bits }; } // namespace machine diff --git a/src/machine/predictor.cpp b/src/machine/predictor.cpp new file mode 100644 index 00000000..0d4fd9e0 --- /dev/null +++ b/src/machine/predictor.cpp @@ -0,0 +1,785 @@ +#include "predictor.h" + +LOG_CATEGORY("machine.BranchPredictor"); + +using namespace machine; + +QStringView machine::branch_result_to_string(const BranchResult result, const bool abbrv) { + switch (result) { + case BranchResult::NOT_TAKEN: return abbrv ? u"NT" : u"Not taken"; + case BranchResult::TAKEN: return abbrv ? u"T" : u"Taken"; + default: return u""; + } +} + +QStringView machine::predictor_state_to_string(const PredictorState state, const bool abbrv) { + switch (state) { + case PredictorState::NOT_TAKEN: return abbrv ? u"NT" : u"Not taken"; + case PredictorState::TAKEN: return abbrv ? u"T" : u"Taken"; + case PredictorState::STRONGLY_NOT_TAKEN: return abbrv ? u"SNT" : u"Strongly not taken"; + case PredictorState::WEAKLY_NOT_TAKEN: return abbrv ? u"WNT" : u"Weakly not taken"; + case PredictorState::WEAKLY_TAKEN: return abbrv ? u"WT" : u"Weakly taken"; + case PredictorState::STRONGLY_TAKEN: return abbrv ? u"ST" : u"Strongly taken"; + default: return u""; + } +} + +QStringView machine::predictor_type_to_string(const PredictorType type) { + switch (type) { + case PredictorType::ALWAYS_NOT_TAKEN: return u"Always not taken"; + case PredictorType::ALWAYS_TAKEN: return u"Always taken"; + case PredictorType::BTFNT: return u"Backward Taken Forward Not Taken"; + case PredictorType::SMITH_1_BIT: return u"Smith 1 bit"; + case PredictorType::SMITH_2_BIT: return u"Smith 2 bit"; + case PredictorType::SMITH_2_BIT_HYSTERESIS: return u"Smith 2 bit with hysteresis"; + default: return u""; + } +} + +QString machine::addr_to_hex_str(const machine::Address address) { + QString hex_addr, zero_padding; + hex_addr = QString::number(address.get_raw(), 16); + zero_padding.fill('0', 8 - hex_addr.count()); + return "0x" + zero_padding + hex_addr; +} + +///////////////////////////////// +// BranchHistoryRegister class // +///////////////////////////////// + +// Constructor +BranchHistoryRegister::BranchHistoryRegister(const uint8_t number_of_bits) + : number_of_bits(init_number_of_bits(number_of_bits)) + , register_mask(init_register_mask(number_of_bits)) {} + +// Init helper function to check the number of bits +uint8_t BranchHistoryRegister::init_number_of_bits(const uint8_t b) const { + if (b > BP_MAX_BHR_BITS) { + WARN("Number of BHR bits (%u) was larger than %u during init", b, BP_MAX_BHR_BITS); + return BP_MAX_BHR_BITS; + } + return b; +} + +// Init helper function to create the register mask used for masking unused bits +uint16_t BranchHistoryRegister::init_register_mask(const uint8_t b) const { + if (b >= BP_MAX_BHR_BITS) { return ~0x0; } + if (b == 0) { return 0x0; } + return (1 << b) - 1; +} + +uint8_t BranchHistoryRegister::get_number_of_bits() const { + return number_of_bits; +} + +uint16_t BranchHistoryRegister::get_register_mask() const { + return register_mask; +} + +uint16_t BranchHistoryRegister::get_value() const { + return value; +} + +// Pushes new value into the register +void BranchHistoryRegister::update(const BranchResult result) { + // Shift all bits to the left + value = value << 1; + + // Add the result as the new least significant bit + // By default the new LSB is 0, only set to 1 if branch was taken + if (result == BranchResult::TAKEN) { value |= 0x1; } + + // Set all bits outside of the scope of the register to zero + value = value & register_mask; + + emit update_done(number_of_bits, value); +} + +///////////////////////////// +// BranchTargetBuffer class // +///////////////////////////// + +// Constructor +BranchTargetBuffer::BranchTargetBuffer(uint8_t number_of_bits) + : number_of_bits(init_number_of_bits(number_of_bits)) { + btb.resize(qPow(2, number_of_bits)); +} + +uint8_t BranchTargetBuffer::init_number_of_bits(const uint8_t b) const { + if (b > BP_MAX_BTB_BITS) { + WARN("Number of BTB bits (%u) was larger than %u during init", b, BP_MAX_BTB_BITS); + return BP_MAX_BTB_BITS; + } + return b; +} + +// Calculate index for addressing Branch Target Buffer from instruction address +uint16_t BranchTargetBuffer::calculate_index(const Address instruction_address) const { + return ((uint16_t)(instruction_address.get_raw() >> 2)) & ((1 << number_of_bits) - 1); +} + +uint8_t BranchTargetBuffer::get_number_of_bits() const { + return number_of_bits; +} + +// Find instruction address in the BTB using the provided instruction address, return 0 address if +// not found +Address BranchTargetBuffer::get_instruction_address(const Address instruction_address) const { + // Get index from instruction address + const uint16_t index { calculate_index(instruction_address) }; + + // Check index validity + if (index >= btb.capacity()) { + WARN("Tried to read from BTB at invalid index: %u", index); + return Address::null(); + } + + // Return target address at index + return btb.at(index).instruction_address; +} + +// Find target address in the BTB using the provided instruction address, return 0 address if not +// found +Address BranchTargetBuffer::get_target_address(const Address instruction_address) const { + // Get index from instruction address + const uint16_t index { calculate_index(instruction_address) }; + + // Check index validity + if (index >= btb.capacity()) { + WARN("Tried to read from BTB at invalid index: %u", index); + return Address::null(); + } + + // Return target address at index + emit requested_target_address(index); + return btb.at(index).target_address; +} + +// Update BTB entry with given values, at index computed from the instruction address +void BranchTargetBuffer::update(const Address instruction_address, const Address target_address) { + // Get index from instruction address + const uint16_t index { calculate_index(instruction_address) }; + + // Check index validity + if (index >= btb.capacity()) { + WARN("Tried to update BTB at invalid index: %u", index); + return; + } + + // Write new entry to the table + btb.at(index) + = { .instruction_address = instruction_address, .target_address = target_address }; + + // Send signal with the data + emit update_row_done(index, instruction_address, target_address); +} + +///////////////////// +// Predictor class // +///////////////////// + +// Predictor Generic +// ################# + +void Predictor::update_stats(PredictionFeedback feedback) { + stats.last_result = feedback.result; + if (stats.last_prediction == stats.last_result) { + stats.number_of_correct_predictions += 1; + } else { + stats.number_of_wrong_predictions += 1; + } + + if ((stats.number_of_correct_predictions + stats.number_of_wrong_predictions) > 0) { + stats.accuracy + = (100 * stats.number_of_correct_predictions) + / (stats.number_of_correct_predictions + stats.number_of_wrong_predictions); + } else { + stats.accuracy = 0; + } +} + +BranchResult Predictor::predict(PredictionInput input) { + const BranchResult result { make_prediction(input) }; + stats.last_prediction = result; + emit prediction_done(0, input, result); + return result; +} + +void Predictor::update(PredictionFeedback feedback) { + update_stats(feedback); + emit update_stats_done(stats); +} + +// Always Not Taken +// ################ + +PredictorAlwaysNotTaken::PredictorAlwaysNotTaken() { + stats.last_prediction = BranchResult::NOT_TAKEN; +} + +BranchResult PredictorAlwaysNotTaken::make_prediction(PredictionInput input) const { + UNUSED(input); + return BranchResult::NOT_TAKEN; +} + +// Always Taken +// ############ + +PredictorAlwaysTaken::PredictorAlwaysTaken() { + stats.last_prediction = BranchResult::TAKEN; +} + +BranchResult PredictorAlwaysTaken::make_prediction(PredictionInput input) const { + UNUSED(input); + return BranchResult::TAKEN; +} + +// Backward Taken Forward Not Taken +// ################################ + +PredictorBTFNT::PredictorBTFNT() { + // TODO figure out how to best update first prediction + stats.last_prediction = BranchResult::UNDEFINED; +} + +BranchResult PredictorBTFNT::make_prediction(PredictionInput input) const { + if (input.target_address > input.instruction_address) { + // If target address is larger than jump instruction address (forward jump), predict not + // taken + return BranchResult::NOT_TAKEN; + } else { + // Otherwise (backward jump) predict taken + return BranchResult::TAKEN; + } +} + +void PredictorBTFNT::update(PredictionFeedback feedback) { + stats.last_prediction = make_prediction({ .instruction = feedback.instruction, + .bhr_value = feedback.bhr_value, + .instruction_address = feedback.instruction_address, + .target_address = feedback.target_address }); + update_stats(feedback); + emit update_stats_done(stats); +} + +// Smith Generic +// ############# + +PredictorSmith::PredictorSmith( + uint8_t number_of_bht_addr_bits, + uint8_t number_of_bht_bits, + PredictorState initial_state) + : number_of_bht_addr_bits(init_number_of_bht_addr_bits(number_of_bht_addr_bits)) + , number_of_bht_bits(init_number_of_bht_bits(number_of_bht_bits)) { + bht.resize(qPow(2, number_of_bht_bits)); + for (uint16_t i = 0; i < bht.capacity(); i++) { + bht.at(i).state = initial_state; + bht.at(i).stats.last_prediction = convert_state_to_prediction(initial_state); + } + stats.last_prediction = convert_state_to_prediction(initial_state); +} + +uint8_t PredictorSmith::init_number_of_bht_addr_bits(const uint8_t b) const { + if (b > BP_MAX_BHT_ADDR_BITS) { + WARN( + "Number of BHT bits from incstruction address (%u) was larger than %d during init", b, + BP_MAX_BHT_ADDR_BITS); + return BP_MAX_BHT_ADDR_BITS; + } + return b; +} + +uint8_t PredictorSmith::init_number_of_bht_bits(const uint8_t b) const { + if (b > BP_MAX_BHT_BITS) { + WARN("Number of BHT bits (%u) was larger than %d during init", b, BP_MAX_BHT_BITS); + return BP_MAX_BHT_BITS; + } + return b; +} + +// Calculate index for addressing Branch History Table from BHR and instruction address +uint16_t PredictorSmith::calculate_bht_index( + const uint16_t bhr_value, + const Address instruction_address) const { + const uint16_t bhr_part = bhr_value << number_of_bht_addr_bits; + const uint16_t address_mask = (1 << number_of_bht_addr_bits) - 1; + const uint16_t address_part = ((uint16_t)(instruction_address.get_raw() >> 2)) & address_mask; + const uint16_t index = bhr_part | address_part; + return index; +} + +void PredictorSmith::update_stats(PredictionFeedback feedback) { + // Get and check BHT index + const uint16_t index { calculate_bht_index(feedback.bhr_value, feedback.instruction_address) }; + if (index >= bht.capacity()) { + WARN("Tried to access BHT at invalid index: %u", index); + return; + } + + // Get current statistics from BHT row + PredictionStatistics row_stats { bht.at(index).stats }; + + row_stats.last_result = feedback.result; + stats.last_result = feedback.result; + + if (row_stats.last_prediction == row_stats.last_result) { + row_stats.number_of_correct_predictions += 1; + stats.number_of_correct_predictions += 1; + } else { + row_stats.number_of_wrong_predictions += 1; + stats.number_of_wrong_predictions += 1; + } + + if ((row_stats.number_of_correct_predictions + row_stats.number_of_wrong_predictions) > 0) { + row_stats.accuracy + = (100 * row_stats.number_of_correct_predictions) + / (row_stats.number_of_correct_predictions + row_stats.number_of_wrong_predictions); + stats.accuracy + = (100 * stats.number_of_correct_predictions) + / (stats.number_of_correct_predictions + stats.number_of_wrong_predictions); + } else { + row_stats.accuracy = 100; + stats.accuracy = 100; + } + + bht.at(index).stats = row_stats; +} + +BranchResult PredictorSmith::convert_state_to_prediction(PredictorState state) const { + if (state == PredictorState::NOT_TAKEN) { + return BranchResult::NOT_TAKEN; + } else if (state == PredictorState::TAKEN) { + return BranchResult::TAKEN; + } else if (state == PredictorState::WEAKLY_NOT_TAKEN) { + return BranchResult::NOT_TAKEN; + } else if (state == PredictorState::STRONGLY_NOT_TAKEN) { + return BranchResult::NOT_TAKEN; + } else if (state == PredictorState::WEAKLY_TAKEN) { + return BranchResult::TAKEN; + } else if (state == PredictorState::STRONGLY_TAKEN) { + return BranchResult::TAKEN; + } else { + WARN("Smith predictor was provided invalid state"); + return BranchResult::NOT_TAKEN; + } +} + +BranchResult PredictorSmith::predict(PredictionInput input) { + const uint16_t index { calculate_bht_index(input.bhr_value, input.instruction_address) }; + if (index >= bht.capacity()) { + WARN("Tried to access BHT at invalid index: %u", index); + return BranchResult::NOT_TAKEN; + } + + const BranchResult result { make_prediction(input) }; + + stats.last_prediction = result; + bht.at(index).stats.last_prediction = result; + + emit prediction_done(index, input, result); + return result; +} + +void PredictorSmith::update(PredictionFeedback feedback) { + const uint16_t index { calculate_bht_index(feedback.bhr_value, feedback.instruction_address) }; + if (index >= bht.capacity()) { + WARN("Tried to access BHT at invalid index: %u", index); + return; + } + + update_bht(feedback); + update_stats(feedback); + + emit update_done(index, feedback); + emit update_stats_done(stats); + emit update_bht_row_done(index, bht.at(index)); +} + +// Smith 1 Bit +// ########### + +PredictorSmith1Bit::PredictorSmith1Bit( + uint8_t number_of_bht_addr_bits, + uint8_t number_of_bht_bits, + PredictorState initial_state) + : PredictorSmith(number_of_bht_addr_bits, number_of_bht_bits, initial_state) {}; + +BranchResult PredictorSmith1Bit::make_prediction(PredictionInput input) const { + const uint16_t index { calculate_bht_index(input.bhr_value, input.instruction_address) }; + if (index >= bht.capacity()) { + WARN("Tried to access BHT at invalid index: %u", index); + return BranchResult::NOT_TAKEN; + } + + // Decide prediction + return convert_state_to_prediction(bht.at(index).state); +} + +void PredictorSmith1Bit::update_bht(PredictionFeedback feedback) { + const uint16_t index { calculate_bht_index(feedback.bhr_value, feedback.instruction_address) }; + if (index >= bht.capacity()) { + WARN("Tried to access BHT at invalid index: %u", index); + return; + } + + // Update internal state + if (feedback.result == BranchResult::NOT_TAKEN) { + bht.at(index).state = PredictorState::NOT_TAKEN; + } else if (feedback.result == BranchResult::TAKEN) { + bht.at(index).state = PredictorState::TAKEN; + } else { + WARN("Smith 1 bit predictor has received invalid prediction result"); + } +} + +// Smith 2 Bit +// ########### + +PredictorSmith2Bit::PredictorSmith2Bit( + uint8_t number_of_bht_addr_bits, + uint8_t number_of_bht_bits, + PredictorState initial_state) + : PredictorSmith(number_of_bht_addr_bits, number_of_bht_bits, initial_state) {}; + +BranchResult PredictorSmith2Bit::make_prediction(PredictionInput input) const { + const uint16_t index { calculate_bht_index(input.bhr_value, input.instruction_address) }; + if (index >= bht.capacity()) { + WARN("Tried to access BHT at invalid index: %u", index); + return BranchResult::NOT_TAKEN; + } + + // Decide prediction + return convert_state_to_prediction(bht.at(index).state); +} + +void PredictorSmith2Bit::update_bht(PredictionFeedback feedback) { + const uint16_t index { calculate_bht_index(feedback.bhr_value, feedback.instruction_address) }; + if (index >= bht.capacity()) { + WARN("Tried to access BHT at invalid index: %u", index); + return; + } + + // Read value from BHT at correct index + const PredictorState state = bht.at(index).state; + + // Update internal state + if (feedback.result == BranchResult::NOT_TAKEN) { + if (state == PredictorState::STRONGLY_TAKEN) { + bht.at(index).state = PredictorState::WEAKLY_TAKEN; + } else if (state == PredictorState::WEAKLY_TAKEN) { + bht.at(index).state = PredictorState::WEAKLY_NOT_TAKEN; + } else if (state == PredictorState::WEAKLY_NOT_TAKEN) { + bht.at(index).state = PredictorState::STRONGLY_NOT_TAKEN; + } else if (state == PredictorState::STRONGLY_NOT_TAKEN) { + bht.at(index).state = PredictorState::STRONGLY_NOT_TAKEN; + } else { + WARN("Smith 2 bit predictor BHT has returned invalid state"); + } + } else if (feedback.result == BranchResult::TAKEN) { + if (state == PredictorState::STRONGLY_TAKEN) { + bht.at(index).state = PredictorState::STRONGLY_TAKEN; + } else if (state == PredictorState::WEAKLY_TAKEN) { + bht.at(index).state = PredictorState::STRONGLY_TAKEN; + } else if (state == PredictorState::WEAKLY_NOT_TAKEN) { + bht.at(index).state = PredictorState::WEAKLY_TAKEN; + } else if (state == PredictorState::STRONGLY_NOT_TAKEN) { + bht.at(index).state = PredictorState::WEAKLY_NOT_TAKEN; + } else { + WARN("Smith 2 bit predictor BHT has returned invalid state"); + } + } else { + WARN("Smith 2 bit predictor has received invalid prediction result"); + } +} + +// Smith 2 Bit with hysteresis +// ########################### + +PredictorSmith2BitHysteresis::PredictorSmith2BitHysteresis( + uint8_t number_of_bht_addr_bits, + uint8_t number_of_bht_bits, + PredictorState initial_state) + : PredictorSmith(number_of_bht_addr_bits, number_of_bht_bits, initial_state) {}; + +BranchResult PredictorSmith2BitHysteresis::make_prediction(PredictionInput input) const { + const uint16_t index { calculate_bht_index(input.bhr_value, input.instruction_address) }; + if (index >= bht.capacity()) { + WARN("Tried to access BHT at invalid index: %u", index); + return BranchResult::NOT_TAKEN; + } + + // Decide prediction + return convert_state_to_prediction(bht.at(index).state); +} + +void PredictorSmith2BitHysteresis::update_bht(PredictionFeedback feedback) { + const uint16_t index { calculate_bht_index(feedback.bhr_value, feedback.instruction_address) }; + if (index >= bht.capacity()) { + WARN("Tried to access BHT at invalid index: %u", index); + return; + } + + // Read value from BHT at correct index + const PredictorState state = bht.at(index).state; + + // Update internal state + if (feedback.result == BranchResult::NOT_TAKEN) { + if (state == PredictorState::STRONGLY_TAKEN) { + bht.at(index).state = PredictorState::WEAKLY_TAKEN; + } else if (state == PredictorState::WEAKLY_TAKEN) { + bht.at(index).state = PredictorState::STRONGLY_NOT_TAKEN; + } else if (state == PredictorState::WEAKLY_NOT_TAKEN) { + bht.at(index).state = PredictorState::STRONGLY_NOT_TAKEN; + } else if (state == PredictorState::STRONGLY_NOT_TAKEN) { + bht.at(index).state = PredictorState::STRONGLY_NOT_TAKEN; + } else { + WARN("Smith 2 bit hysteresis predictor BHT has returned invalid state"); + } + } else if (feedback.result == BranchResult::TAKEN) { + if (state == PredictorState::STRONGLY_TAKEN) { + bht.at(index).state = PredictorState::STRONGLY_TAKEN; + } else if (state == PredictorState::WEAKLY_TAKEN) { + bht.at(index).state = PredictorState::STRONGLY_TAKEN; + } else if (state == PredictorState::WEAKLY_NOT_TAKEN) { + bht.at(index).state = PredictorState::STRONGLY_TAKEN; + } else if (state == PredictorState::STRONGLY_NOT_TAKEN) { + bht.at(index).state = PredictorState::WEAKLY_NOT_TAKEN; + } else { + WARN("Smith 2 bit hysteresis predictor BHT has returned invalid state"); + } + } else { + WARN("Smith 2 bit hysteresis predictor has received invalid prediction result"); + } +} + +/////////////////////////// +// BranchPredictor class // +/////////////////////////// + +BranchPredictor::BranchPredictor( + bool enabled, + PredictorType predictor_type, + PredictorState initial_state, + uint8_t number_of_btb_bits, + uint8_t number_of_bhr_bits, + uint8_t number_of_bht_addr_bits) + : enabled(enabled) + , initial_state(initial_state) + , number_of_btb_bits(init_number_of_btb_bits(number_of_btb_bits)) + , number_of_bhr_bits(init_number_of_bhr_bits(number_of_bhr_bits)) + , number_of_bht_addr_bits(init_number_of_bht_addr_bits(number_of_bht_addr_bits)) + , number_of_bht_bits(init_number_of_bht_bits(number_of_bhr_bits, number_of_bht_addr_bits)) { + // Create predicotr + switch (predictor_type) { + case PredictorType::ALWAYS_NOT_TAKEN: + predictor = new PredictorAlwaysNotTaken(); + LOG("Initialized branch predictor: %s", qPrintable(get_predictor_name().toString())); + break; + + case PredictorType::ALWAYS_TAKEN: + predictor = new PredictorAlwaysTaken(); + LOG("Initialized branch predictor: %s", qPrintable(get_predictor_name().toString())); + break; + + case PredictorType::BTFNT: + predictor = new PredictorBTFNT(); + LOG("Initialized branch predictor: %s", qPrintable(get_predictor_name().toString())); + break; + + case PredictorType::SMITH_1_BIT: + predictor + = new PredictorSmith1Bit(number_of_bht_addr_bits, number_of_bht_bits, initial_state); + LOG("Initialized branch predictor: %s, with %u BHT bits, and initial state at: %s", + qPrintable(get_predictor_name().toString()), number_of_bht_bits, + qPrintable(predictor_state_to_string(initial_state).toString())); + break; + + case PredictorType::SMITH_2_BIT: + predictor + = new PredictorSmith2Bit(number_of_bht_addr_bits, number_of_bht_bits, initial_state); + LOG("Initialized branch predictor: %s, with %u BHT bits, and initial state at: %s", + qPrintable(get_predictor_name().toString()), number_of_bht_bits, + qPrintable(predictor_state_to_string(initial_state).toString())); + break; + + case PredictorType::SMITH_2_BIT_HYSTERESIS: + predictor = new PredictorSmith2BitHysteresis( + number_of_bht_addr_bits, number_of_bht_bits, initial_state); + LOG("Initialized branch predictor: %s, with %u BHT bits, and initial state at: %s", + qPrintable(get_predictor_name().toString()), number_of_bht_bits, + qPrintable(predictor_state_to_string(initial_state).toString())); + break; + + default: throw std::invalid_argument("Invalid predictor type selected"); + } + + bhr = new BranchHistoryRegister(number_of_bhr_bits); + btb = new BranchTargetBuffer(number_of_btb_bits); + + if (enabled) { + connect( + btb, &BranchTargetBuffer::requested_target_address, this, + &BranchPredictor::requested_bht_target_address); + connect( + btb, &BranchTargetBuffer::update_row_done, this, &BranchPredictor::update_btb_row_done); + connect(bhr, &BranchHistoryRegister::update_done, this, &BranchPredictor::update_bhr_done); + connect(predictor, &Predictor::prediction_done, this, &BranchPredictor::prediction_done); + connect(predictor, &Predictor::update_done, this, &BranchPredictor::update_predictor_done); + connect( + predictor, &Predictor::update_stats_done, this, + &BranchPredictor::update_predictor_stats_done); + connect( + predictor, &Predictor::update_bht_row_done, this, + &BranchPredictor::update_predictor_bht_row_done); + } +} + +BranchPredictor::~BranchPredictor() { + delete predictor; + predictor = nullptr; + delete bhr; + bhr = nullptr; + delete btb; + btb = nullptr; +} + +uint8_t BranchPredictor::init_number_of_btb_bits(const uint8_t b) const { + if (b > BP_MAX_BTB_BITS) { + WARN("Number of BTB bits (%u) was larger than %d during init", b, BP_MAX_BTB_BITS); + return BP_MAX_BTB_BITS; + } + return b; +} + +uint8_t BranchPredictor::init_number_of_bhr_bits(const uint8_t b) const { + if (b > BP_MAX_BHR_BITS) { + WARN("Number of BHR bits (%u) was larger than %d during init", b, BP_MAX_BHR_BITS); + return BP_MAX_BHR_BITS; + } + return b; +} + +uint8_t BranchPredictor::init_number_of_bht_addr_bits(const uint8_t b) const { + if (b > BP_MAX_BHT_ADDR_BITS) { + WARN( + "Number of BHT instruction address bits (%u) was larger than %d during init", b, + BP_MAX_BHT_ADDR_BITS); + return BP_MAX_BHT_ADDR_BITS; + } + return b; +} + +uint8_t BranchPredictor::init_number_of_bht_bits(const uint8_t b_bhr, const uint8_t b_addr) const { + // Clamp number of BHR bits + uint8_t checked_number_of_bits_bhr { b_bhr }; + if (checked_number_of_bits_bhr > BP_MAX_BHR_BITS) { + checked_number_of_bits_bhr = BP_MAX_BHR_BITS; + } + + // Clamp number of address index bits + uint8_t checked_number_of_bits_addr { b_addr }; + if (checked_number_of_bits_addr > BP_MAX_BHT_ADDR_BITS) { + checked_number_of_bits_addr = BP_MAX_BHT_ADDR_BITS; + } + + // Check sum + uint8_t b_sum { (uint8_t)(checked_number_of_bits_bhr + checked_number_of_bits_addr) }; + if (b_sum > BP_MAX_BHT_BITS) { b_sum = BP_MAX_BHT_BITS; } + + return b_sum; +} + +bool BranchPredictor::get_enabled() const { + return enabled; +} + +PredictorType BranchPredictor::get_predictor_type() const { + if (!enabled) { return PredictorType::UNDEFINED; } + return predictor->get_type(); +} + +QStringView BranchPredictor::get_predictor_name() const { + if (!enabled) { return u"None"; } + return predictor_type_to_string(predictor->get_type()); +} + +PredictorState BranchPredictor::get_initial_state() const { + if (!enabled) { return PredictorState::UNDEFINED; } + return initial_state; +} + +uint8_t BranchPredictor::get_number_of_btb_bits() const { + if (!enabled) { return 0; } + return number_of_btb_bits; +} + +uint8_t BranchPredictor::get_number_of_bhr_bits() const { + if (!enabled) { return 0; } + return number_of_bhr_bits; +} + +uint8_t BranchPredictor::get_number_of_bht_addr_bits() const { + if (!enabled) { return 0; } + return number_of_bht_addr_bits; +} + +uint8_t BranchPredictor::get_number_of_bht_bits() const { + if (!enabled) { return 0; } + return number_of_bht_bits; +} + +Address BranchPredictor::predict_next_pc_address( + const Instruction instruction, + const Address instruction_address) const { + // Check if predictor is enabled + if (!enabled) { return instruction_address + 4; } + + // Get instruction address from BTB + Address instruction_address_from_btb = btb->get_instruction_address(instruction_address); + if (instruction_address_from_btb.is_null() + || instruction_address != instruction_address_from_btb) { + return instruction_address + 4; + } + + // Get target address from BTB + Address target_address_from_btb = btb->get_target_address(instruction_address); + if (target_address_from_btb.is_null()) { return instruction_address + 4; } + + // Make prediction + const PredictionInput prediction_input { + .instruction = instruction, + .bhr_value = bhr->get_value(), + .instruction_address = instruction_address, + .target_address = target_address_from_btb, + }; + const BranchResult predicted_result = predictor->predict(prediction_input); + + // If the branch was predicted taken + if (predicted_result == BranchResult::TAKEN) { return target_address_from_btb; } + + // Default prediction - not taken + return instruction_address + 4; +} + +// Function for updating the predictor and the Branch History Register (BHR) +void BranchPredictor::update( + const Instruction instruction, + const Address instruction_address, + const Address target_address, + const BranchResult result) { + // Check if predictor is enabled + if (!enabled) { return; } + + // Update Branch Target Table + if (result == BranchResult::TAKEN) { btb->update(instruction_address, target_address); } + + // Update predictor + const PredictionFeedback prediction_feedback { .instruction = instruction, + .bhr_value = bhr->get_value(), + .instruction_address = instruction_address, + .result = result }; + predictor->update(prediction_feedback); + + // Update global branch history + bhr->update(result); +} diff --git a/src/machine/predictor.h b/src/machine/predictor.h index 19164837..f3d32006 100644 --- a/src/machine/predictor.h +++ b/src/machine/predictor.h @@ -1,23 +1,325 @@ #ifndef PREDICTOR_H #define PREDICTOR_H +#include "common/logging.h" #include "instruction.h" #include "memory/address.h" +#include "predictor_types.h" + +#include +#include namespace machine { -class Predictor { -public: - virtual Address predict(Instruction inst, Address addr) = 0; +QStringView branch_result_to_string(const BranchResult result, const bool abbrv = false); + +QStringView predictor_state_to_string(const PredictorState state, const bool abbrv = false); + +QStringView predictor_type_to_string(const PredictorType type); + +QString addr_to_hex_str(const machine::Address address); + +///////////////////////////////// +// BranchHistoryRegister class // +///////////////////////////////// + +class BranchHistoryRegister final : public QObject { + Q_OBJECT + +public: // Constructors & Destructor + explicit BranchHistoryRegister(const uint8_t number_of_bits); + +private: // Internal functions + uint8_t init_number_of_bits(const uint8_t b) const; + uint16_t init_register_mask(const uint8_t b) const; + +public: // General functions + uint8_t get_number_of_bits() const; + uint16_t get_register_mask() const; + uint16_t get_value() const; + void update(const BranchResult result); + +signals: + void update_done(uint8_t number_of_bhr_bits, uint16_t register_value); + +private: // Internal variables + const uint8_t number_of_bits; + const uint16_t register_mask; + uint16_t value { 0 }; +}; + +///////////////////////////// +// BranchTargetBuffer class // +///////////////////////////// + +struct BranchTargetBufferEntry { + Address instruction_address { Address::null() }; + Address target_address { Address::null() }; +}; + +class BranchTargetBuffer final : public QObject { + Q_OBJECT + +public: // Constructors & Destructor + explicit BranchTargetBuffer(const uint8_t number_of_bits); + +private: // Internal functions + uint8_t init_number_of_bits(const uint8_t b) const; + uint16_t calculate_index(const Address instruction_address) const; + +public: // General functions + uint8_t get_number_of_bits() const; + Address get_instruction_address(const Address instruction_address) const; + Address get_target_address(const Address instruction_address) const; + void update(const Address instruction_address, const Address target_address); + +signals: + void requested_target_address(uint16_t index) const; + void update_row_done(uint16_t index, Address instruction_address, Address target_address) const; + +private: // Internal variables + const uint8_t number_of_bits; + std::vector btb; +}; + +///////////////////// +// Predictor class // +///////////////////// + +struct PredictionInput { + Instruction instruction {}; + uint16_t bhr_value { 0 }; + Address instruction_address { Address::null() }; + Address target_address { Address::null() }; +}; + +struct PredictionFeedback { + Instruction instruction {}; + uint16_t bhr_value { 0 }; + Address instruction_address { Address::null() }; + Address target_address { Address::null() }; + BranchResult result { BranchResult::UNDEFINED }; +}; + +struct PredictionStatistics { + float accuracy { 0 }; // 0 - 100 % + BranchResult last_prediction { BranchResult::UNDEFINED }; + BranchResult last_result { BranchResult::UNDEFINED }; + uint32_t number_of_correct_predictions { 0 }; + uint32_t number_of_wrong_predictions { 0 }; +}; + +struct BranchHistoryTableEntry { + PredictorState state; + PredictionStatistics stats; // Per-entry statistics +}; + +class Predictor : public QObject { + Q_OBJECT + +public: // Constructors & Destructor virtual ~Predictor() = default; + +protected: // Internal functions + virtual void update_stats(PredictionFeedback feedback); + virtual BranchResult make_prediction(PredictionInput input) const = 0; // Returns + // prediction based on + // internal state + +public: // General functions + virtual PredictorType get_type() const { return PredictorType::UNDEFINED; }; + virtual BranchResult predict(PredictionInput input); // Function which handles all actions ties + // to making a branch prediction + virtual void update(PredictionFeedback feedback); // Update predictor based on jump / branch + // result + +signals: + void prediction_done(uint16_t index, PredictionInput input, BranchResult result) const; + void update_done(uint16_t index, PredictionFeedback feedback) const; + void update_stats_done(PredictionStatistics stats) const; + void update_bht_row_done(uint16_t index, BranchHistoryTableEntry entry) const; + +protected: // Internal variables + PredictionStatistics stats; // Total predictor statistics }; -// Always predicts not taking the branch, even on JAL(R) instructions -class FalsePredictor : public Predictor { - Address predict(Instruction inst, Address addr) final { - (void)inst; // explicitly unused argument - return addr + 4; - } +// Static Predictor - Always predicts not taking the branch +class PredictorAlwaysNotTaken final : public Predictor { +public: // Constructors & Destructor + PredictorAlwaysNotTaken(); + +private: // Internal functions + BranchResult make_prediction(PredictionInput input) const override; + +public: // General functions + PredictorType get_type() const override { return PredictorType::ALWAYS_NOT_TAKEN; }; +}; + +// Static Predictor - Always predicts taking the branch +class PredictorAlwaysTaken final : public Predictor { +public: // Constructors & Destructor + PredictorAlwaysTaken(); + +private: // Internal functions + BranchResult make_prediction(PredictionInput input) const override; + +public: // General functions + PredictorType get_type() const override { return PredictorType::ALWAYS_TAKEN; }; +}; + +// Static Predictor - Backward Taken Forward Not Taken +class PredictorBTFNT final : public Predictor { +public: // Constructors & Destructor + PredictorBTFNT(); + +private: // Internal functions + BranchResult make_prediction(PredictionInput input) const override; + +public: // General functions + PredictorType get_type() const override { return PredictorType::BTFNT; }; + virtual void update(PredictionFeedback feedback) override; +}; + +// Dynamic Predictor - Smith Generic +class PredictorSmith : public Predictor { +public: // Constructors & Destructor + PredictorSmith( + uint8_t number_of_bht_addr_bits, + uint8_t number_of_bht_bits, + PredictorState initial_state); + +protected: // Internal functions + uint8_t init_number_of_bht_addr_bits(uint8_t b) const; + uint8_t init_number_of_bht_bits(uint8_t b) const; + uint16_t calculate_bht_index(const uint16_t bhr_value, const Address instruction_address) const; + void update_stats(PredictionFeedback feedback) override; + BranchResult convert_state_to_prediction(PredictorState state) const; + virtual void update_bht(PredictionFeedback feedback) = 0; + +public: // General functions + BranchResult predict(PredictionInput input) override; // Function which handles all actions ties + // to making a branch prediction + void update(PredictionFeedback feedback) override; // Update predictor based on jump / branch + // result + +protected: // Internal variables + const uint8_t number_of_bht_addr_bits; // Number of Branch History Table (BHT) bits taken from + // instruction address + const uint8_t number_of_bht_bits; // Number of Branch History Table (BHT) bits + std::vector bht; // Branch History Table (BHT) +}; + +// Dynamic Predictor - Smith 1 Bit +class PredictorSmith1Bit final : public PredictorSmith { +public: // Constructors & Destructor + PredictorSmith1Bit( + uint8_t number_of_bht_addr_bits, + uint8_t number_of_bht_bits, + PredictorState initial_state); + +private: // Internal functions + BranchResult make_prediction(PredictionInput input) const override; + void update_bht(PredictionFeedback feedback); + +public: // General functions + PredictorType get_type() const override { return PredictorType::SMITH_1_BIT; }; +}; + +// Dynamic Predictor - Smith 2 Bit +class PredictorSmith2Bit final : public PredictorSmith { +public: // Constructors & Destructor + PredictorSmith2Bit( + uint8_t number_of_bht_addr_bits, + uint8_t number_of_bht_bits, + PredictorState initial_state); + +private: // Internal functions + BranchResult make_prediction(PredictionInput input) const override; + void update_bht(PredictionFeedback feedback); + +public: // General functions + PredictorType get_type() const override { return PredictorType::SMITH_2_BIT; }; +}; + +// Dynamic Predictor - Smith 2 Bit with hysteresis +class PredictorSmith2BitHysteresis final : public PredictorSmith { +public: // Constructors & Destructor + PredictorSmith2BitHysteresis( + uint8_t number_of_bht_addr_bits, + uint8_t number_of_bht_bits, + PredictorState initial_state); + +private: // Internal functions + BranchResult make_prediction(PredictionInput input) const override; + void update_bht(PredictionFeedback feedback); + +public: // General functions + PredictorType get_type() const override { return PredictorType::SMITH_2_BIT_HYSTERESIS; }; +}; + +/////////////////////////// +// BranchPredictor class // +/////////////////////////// + +class BranchPredictor : public QObject { + Q_OBJECT + +public: // Constructors & Destructor + explicit BranchPredictor( + bool enabled = false, + PredictorType predictor_type = PredictorType::SMITH_1_BIT, + PredictorState initial_state = PredictorState::NOT_TAKEN, + uint8_t number_of_btb_bits = 2, + uint8_t number_of_bhr_bits = 0, + uint8_t number_of_bht_addr_bits = 2); + ~BranchPredictor(); + +private: // Internal functions + uint8_t init_number_of_btb_bits(const uint8_t b) const; + uint8_t init_number_of_bhr_bits(const uint8_t b) const; + uint8_t init_number_of_bht_addr_bits(const uint8_t b) const; + uint8_t init_number_of_bht_bits(const uint8_t b_bhr, const uint8_t b_addr) const; + +public: // General functions + bool get_enabled() const; + PredictorType get_predictor_type() const; + QStringView get_predictor_name() const; + PredictorState get_initial_state() const; + uint8_t get_number_of_btb_bits() const; + uint8_t get_number_of_bhr_bits() const; + uint8_t get_number_of_bht_addr_bits() const; + uint8_t get_number_of_bht_bits() const; + + Address + predict_next_pc_address(const Instruction instruction, const Address instruction_address) const; + void update( + const Instruction instruction, + const Address instruction_address, + const Address target_address, + const BranchResult result); + +signals: + void requested_bht_target_address(uint16_t index) const; + void + update_btb_row_done(uint16_t index, Address instruction_address, Address target_address) const; + void update_bhr_done(uint8_t number_of_bhr_bits, uint16_t register_value) const; + void prediction_done(uint16_t index, PredictionInput input, BranchResult result) const; + void update_predictor_done(uint16_t index, PredictionFeedback feedback) const; + void update_predictor_stats_done(PredictionStatistics stats) const; + void update_predictor_bht_row_done(uint16_t index, BranchHistoryTableEntry entry) const; + +private: // Internal variables + bool enabled; + Predictor *predictor; + BranchHistoryRegister *bhr; + BranchTargetBuffer *btb; + const PredictorState initial_state; + const uint8_t number_of_btb_bits; // Number of bits for addressing Branch Target Buffer (all + // taken from instruction address) + const uint8_t number_of_bhr_bits; // Number of bits in Branch History Register + const uint8_t number_of_bht_addr_bits; // Number of bits in Branch History Table which are taken + // from instruction address + const uint8_t number_of_bht_bits; // = number_of_bhr_bits + number_of_bht_addr_bits }; } // namespace machine diff --git a/src/machine/predictor_types.h b/src/machine/predictor_types.h new file mode 100644 index 00000000..2856d9fe --- /dev/null +++ b/src/machine/predictor_types.h @@ -0,0 +1,40 @@ +#ifndef PREDICTOR_TYPES_H +#define PREDICTOR_TYPES_H + +namespace machine { +Q_NAMESPACE + +// Should not exceed 16, because uint16_t is used for addressing +#define BP_MAX_BTB_BITS 8 +#define BP_MAX_BHR_BITS 8 +#define BP_MAX_BHT_ADDR_BITS 8 +#define BP_MAX_BHT_BITS (BP_MAX_BHT_ADDR_BITS + BP_MAX_BHT_ADDR_BITS) + +enum class BranchResult { NOT_TAKEN, TAKEN, UNDEFINED }; +Q_ENUM_NS(machine::BranchResult) + +enum class PredictorType { + ALWAYS_NOT_TAKEN, + ALWAYS_TAKEN, + BTFNT, // Backward Taken, Forward Not Taken + SMITH_1_BIT, + SMITH_2_BIT, + SMITH_2_BIT_HYSTERESIS, + UNDEFINED +}; +Q_ENUM_NS(machine::PredictorType) + +enum class PredictorState { + NOT_TAKEN, // Smith 1 bit + TAKEN, // Smith 1 bit + STRONGLY_NOT_TAKEN, // Smith 2 bit + WEAKLY_NOT_TAKEN, // Smith 2 bit + WEAKLY_TAKEN, // Smith 2 bit + STRONGLY_TAKEN, // Smith 2 bit + UNDEFINED +}; +Q_ENUM_NS(machine::PredictorState) + +} // namespace machine + +#endif // PREDICTOR_TYPES_H