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