Implement support for Branch instructions
DELETE
branchnoforwarding.bin
branchnoforwarding.bin
Binary files a/branchnoforwarding.bin and /dev/null differ

ADD
examples/binary/branchnoforwarding.bin
examples/binary/branchnoforwarding.bin
Binary files /dev/null and b/examples/binary/branchnoforwarding.bin differ

MODIFY
src/defines.h
src/defines.h
@@ -19,16 +19,20 @@ enum displayTypeN { Hex, Binary, Decimal, Unsigned, ASCII };
Q_DECLARE_METATYPE(displayTypeN)
class StagePCS {
- typedef std::pair<uint32_t, bool> PC;
-
public:
+ typedef struct {
+ uint32_t pc;
+ bool initialized; // If false, no text will be written above a pipeline stage when pipeline is reset
+ bool invalid; // If true, a stage has been flushed (the PC for the stage is set to 0) - "nop" will be written
+ // above the pipeline stage
+ } PC;
StagePCS() {}
void reset() {
- IF = PC(0, false);
- ID = PC(0, false);
- EX = PC(0, false);
- MEM = PC(0, false);
- WB = PC(0, false);
+ IF = PC{0, false, false};
+ ID = PC{0, false, false};
+ EX = PC{0, false, false};
+ MEM = PC{0, false, false};
+ WB = PC{0, false, false};
}
PC IF;
PC ID;

MODIFY
src/instructionmodel.cpp
src/instructionmodel.cpp
@@ -28,8 +28,10 @@ int InstructionModel::columnCount(const QModelIndex&) const {
namespace {
#define VALIDATE(stage) \
- if (!m_pcsptr.stage.second) { \
+ if (!m_pcsptr.stage.initialized) { \
emit textChanged(Stage::stage, ""); \
+ } else if (m_pcsptr.stage.invalid) { \
+ emit textChanged(Stage::stage, "nop (branch taken)"); \
}
}
@@ -46,28 +48,28 @@ QVariant InstructionModel::data(const QModelIndex& index, int role) const {
// size, if so, clear previous instruction texts in the pipeline widget
uint32_t byteIndex = row * 4;
uint32_t maxInstr = m_textSize - 4;
- if (byteIndex == m_pcsptr.EX.first && m_pcsptr.EX.second) {
+ if (byteIndex == m_pcsptr.EX.pc && m_pcsptr.EX.initialized && !m_pcsptr.EX.invalid) {
emit textChanged(Stage::EX, m_parserPtr->genStringRepr(memRead(row * 4)));
if (byteIndex == maxInstr) {
emit textChanged(Stage::ID, "");
}
return QString("EX");
- } else if (byteIndex == m_pcsptr.ID.first && m_pcsptr.ID.second) {
+ } else if (byteIndex == m_pcsptr.ID.pc && m_pcsptr.ID.initialized && !m_pcsptr.ID.invalid) {
emit textChanged(Stage::ID, m_parserPtr->genStringRepr(memRead(row * 4)));
if (byteIndex == maxInstr) {
emit textChanged(Stage::IF, "");
}
return QString("ID");
- } else if (byteIndex == m_pcsptr.IF.first && m_pcsptr.IF.second) {
+ } else if (byteIndex == m_pcsptr.IF.pc && m_pcsptr.IF.initialized && !m_pcsptr.IF.invalid) {
emit textChanged(Stage::IF, m_parserPtr->genStringRepr(memRead(row * 4)));
return QString("IF");
- } else if (byteIndex == m_pcsptr.MEM.first && m_pcsptr.MEM.second) {
+ } else if (byteIndex == m_pcsptr.MEM.pc && m_pcsptr.MEM.initialized && !m_pcsptr.MEM.invalid) {
emit textChanged(Stage::MEM, m_parserPtr->genStringRepr(memRead(row * 4)));
if (byteIndex == maxInstr) {
emit textChanged(Stage::EX, "");
}
return QString("MEM");
- } else if (byteIndex == m_pcsptr.WB.first && m_pcsptr.WB.second) {
+ } else if (byteIndex == m_pcsptr.WB.pc && m_pcsptr.WB.initialized && !m_pcsptr.WB.invalid) {
emit textChanged(Stage::WB, m_parserPtr->genStringRepr(memRead(row * 4)));
if (byteIndex == maxInstr) {
emit textChanged(Stage::MEM, "");

MODIFY
src/pipeline.cpp
src/pipeline.cpp
@@ -21,13 +21,13 @@ Pipeline::Pipeline() {
// ------ ID ---------
m_reg.setInputs(r_instr_IFID.getOutput(), r_writeReg_MEMWB.getOutput(), mux_memToReg.getOutput(),
r_regWrite_MEMWB.getOutput());
- r_imm_IDEX.setInput(&imm_ID);
+ r_imm_IDEX.setInput(&s_imm_ID);
r_rd1_IDEX.setInput(m_reg.getOutput(1));
r_rd2_IDEX.setInput(m_reg.getOutput(2));
r_writeReg_IDEX.setInput(&writeReg);
alu_pc_target.setControl(&s_alu_const_add);
- alu_pc_target.setInputs(r_PC_IDEX.getOutput(), &imm_ID_shifted);
+ alu_pc_target.setInputs(r_PC_IFID.getOutput(), &s_imm_ID);
// Control signals
r_regWrite_IDEX.setInput(&s_RegWrite);
@@ -67,42 +67,50 @@ Pipeline::Pipeline() {
mux_memToReg.setInput(1, r_readData_MEMWB.getOutput());
mux_memToReg.setControl(r_MemtoReg_MEMWB.getOutput());
- // Program counters
+ // Program counters & PC invalidation signals
+ // Propagate invalidPC signal through pipeline, so GUI can display nop's on invalidated stages
r_PC_IFID.setInput(r_PC_IF.getOutput());
+ r_PC_IFID.setReset(&s_PCSrc);
+ r_invalidPC_IFID.setInput(&s_PCSrc);
+
r_PC_IDEX.setInput(r_PC_IFID.getOutput());
+ r_invalidPC_IDEX.setInput(r_invalidPC_IFID.getOutput());
+
r_PC_EXMEM.setInput(r_PC_IDEX.getOutput());
+ r_invalidPC_EXMEM.setInput(r_invalidPC_IDEX.getOutput());
+
r_PC_MEMWB.setInput(r_PC_EXMEM.getOutput());
+ r_invalidPC_MEMWB.setInput(r_invalidPC_EXMEM.getOutput());
}
void Pipeline::immGen() {
if (((uint32_t)r_instr_IFID & 0b1111111) == 0b0110111) {
// LUI
- imm_ID = Signal<32>((uint32_t)r_instr_IFID & 0xfffff000);
+ s_imm_ID = Signal<32>((uint32_t)r_instr_IFID & 0xfffff000);
} else {
// Generates an immediate value on the basis of an instruction opcode
// Opcode bits 5 and 6 can define the required fields for generating the immediate
switch (((uint32_t)r_instr_IFID & 0b1100000) >> 5) {
case 0b00: {
// Load instruction, sign extend bits 31:20
- imm_ID = Signal<32>(signextend<int32_t, 12>(((uint32_t)r_instr_IFID >> 20)));
+ s_imm_ID = Signal<32>(signextend<int32_t, 12>(((uint32_t)r_instr_IFID >> 20)));
break;
}
case 0b01: {
// Store instruction
uint32_t v(r_instr_IFID);
- imm_ID = Signal<32>(signextend<int32_t, 12>(((v & 0xfe000000)) >> 20) | ((v & 0xf80) >> 7));
+ s_imm_ID = Signal<32>(signextend<int32_t, 12>(((v & 0xfe000000)) >> 20) | ((v & 0xf80) >> 7));
break;
}
default: {
// Conditional branch instructions
- uint32_t v(r_instr_IFID);
- imm_ID = Signal<32>(signextend<int32_t, 13>(((v & 0x80000000)) >> 20) | ((v & 0x7e) >> 20) |
- ((v & 0xf00) >> 7) | ((v & 0x80) << 6));
+ auto fields = Parser::getParser()->decodeBInstr((uint32_t)r_instr_IFID);
+ s_imm_ID = signextend<int32_t, 13>((fields[0] << 12) | (fields[1] << 5) | (fields[5] << 1) |
+ (fields[6] << 11));
break;
}
}
}
- imm_ID_shifted = imm_ID; //((uint32_t)imm_ID) << 1;
}
namespace {
@@ -447,9 +455,7 @@ void Pipeline::propagateCombinational() {
// ----- IF -----
alu_pc4.update();
mux_PCSrc.update();
- if ((uint32_t)s_PCSrc == 1) {
- qDebug() << "a";
- }
+
// Load nops if PC is greater than text size
s_instr_IF = Signal<32>(
(uint32_t)r_PC_IF > m_textSize ? 0 : m_memory.read((uint32_t)r_PC_IF)); // Read instruction at current PC
@@ -484,12 +490,11 @@ int Pipeline::step() {
// Propagate signals through logic
propagateCombinational();
-
// Set stage program counters
setStagePCS();
// Execution is finished if nops are in all stages except WB
- if (m_pcs.WB.first == m_textSize) {
+ if (m_pcs.WB.pc == m_textSize) {
m_finished = true;
return 1;
} else {
@@ -497,17 +502,20 @@ int Pipeline::step() {
}
}
-#define PCVAL(pc) std::pair<uint32_t, bool>((uint32_t)pc, true)
+#define PCVAL(pc, reg) \
+ StagePCS::PC { (uint32_t) pc, true, (bool)reg }
+
void Pipeline::setStagePCS() {
// To validate a PC value (whether there is actually an instruction in the stage, or if the pipeline has
// been reset), the previous stage PC is used to determine the current state of a stage To facilitate this,
// the PCS are set in reverse order
m_pcsPre = m_pcs;
- m_pcs.WB = m_pcs.MEM.second ? PCVAL(r_PC_MEMWB) : m_pcs.WB;
- m_pcs.MEM = m_pcs.EX.second ? PCVAL(r_PC_EXMEM) : m_pcs.MEM;
- m_pcs.EX = m_pcs.ID.second ? PCVAL(r_PC_IDEX) : m_pcs.EX;
- m_pcs.ID = m_pcs.IF.second ? PCVAL(r_PC_IFID) : m_pcs.ID;
- m_pcs.IF = PCVAL(r_PC_IF);
+ // Propagate "valid" in terms
+ m_pcs.WB = m_pcs.MEM.initialized ? PCVAL(r_PC_MEMWB, r_invalidPC_MEMWB) : m_pcs.WB;
+ m_pcs.MEM = m_pcs.EX.initialized ? PCVAL(r_PC_EXMEM, r_invalidPC_EXMEM) : m_pcs.MEM;
+ m_pcs.EX = m_pcs.ID.initialized ? PCVAL(r_PC_IDEX, r_invalidPC_IDEX) : m_pcs.EX;
+ m_pcs.ID = m_pcs.IF.initialized ? PCVAL(r_PC_IFID, r_invalidPC_IFID) : m_pcs.ID;
+ m_pcs.IF = PCVAL(r_PC_IF, false);
}
int Pipeline::run() {
@@ -545,5 +553,5 @@ void Pipeline::restart() {
propagateCombinational();
// set PC_IF stage to the first instruction
- m_pcs.IF = PCVAL(r_PC_IF);
+ m_pcs.IF = PCVAL(r_PC_IF, s_PCSrc);
}

MODIFY
src/pipeline.h
src/pipeline.h
@@ -61,8 +61,7 @@ private:
Mux<2, 32> mux_memToReg;
Signal<1> ctrl_memToReg;
- Signal<32> imm_ID = Signal<32>("Immediate");
- Signal<32> imm_ID_shifted = Signal<32>("Immediate SL1");
+ Signal<32> s_imm_ID = Signal<32>("Immediate");
Signal<32> s_instr_IF = Signal<32>("Instruction");
Signal<32> readData_MEM;
@@ -71,6 +70,7 @@ private:
Reg<32> r_instr_IFID, r_rd1_IDEX, r_rd2_IDEX, r_imm_IDEX, r_alures_EXMEM, r_writeData_EXMEM, r_readData_MEMWB,
r_alures_MEMWB;
Reg<32> r_PC_IF, r_PC_IFID, r_PC_IDEX, r_PC_EXMEM, r_PC_MEMWB;
+ Reg<1> r_invalidPC_IFID, r_invalidPC_IDEX, r_invalidPC_EXMEM, r_invalidPC_MEMWB;
Signal<5> writeReg;
Reg<5> r_writeReg_IDEX, r_writeReg_EXMEM, r_writeReg_MEMWB; // Write register (# of register to write to)
@@ -90,6 +90,7 @@ private:
Signal<1> s_RegWrite, s_ALUSrc, s_MemToReg, s_Branch;
Signal<ALUDefs::CTRL_SIZE> s_ALUOP;
Signal<3> s_CompOp;
+ Signal<1> s_validPC; // used for GUI to determine whether a PC = 0 is a nop (register flush) or valid
// Control signal enums
enum CompOp { BEQ = 1, BNE = 2, BLT = 3, BLTU = 4, BGE = 5, BGEU = 6 };

MODIFY
src/processortab.cpp
src/processortab.cpp
@@ -23,8 +23,13 @@ ProcessorTab::ProcessorTab(QWidget* parent) : QWidget(parent), m_ui(new Ui::Proc
// Setup execution speed slider
m_ui->execSpeed->setSliderPosition(2);
- m_timer.setInterval(1200 - 200 * m_ui->execSpeed->sliderPosition());
- connect(m_ui->execSpeed, &QSlider::valueChanged, [=](int pos) { m_timer.setInterval(1200 - 200 * pos); });
+
+ connect(m_ui->execSpeed, &QSlider::valueChanged, [=](int pos) {
+ // Reverse the slider, going from high to low
+ const static int delay = m_ui->execSpeed->maximum() + m_ui->execSpeed->minimum();
+ m_timer.setInterval(delay - pos);
+ });
+ m_ui->execSpeed->valueChanged(m_ui->execSpeed->value());
connect(m_ui->reset, &QPushButton::clicked, [=] { m_ui->start->setChecked(false); });

MODIFY
src/processortab.ui
src/processortab.ui
</sizepolicy>
</property>
<property name="minimum">
- <number>0</number>
+ <number>100</number>
</property>
<property name="maximum">
- <number>6</number>
+ <number>700</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
+ <property name="invertedControls">
+ <bool>false</bool>
+ </property>
</widget>
</item>
<item>

Implement support for Branch instructions