Work on IF stage
MODIFY
RISC-V-sim.pro
RISC-V-sim.pro
@@ -38,7 +38,8 @@ SOURCES += src/main.cpp \
src/aboutwidget.cpp \
src/graphics/backgrounditems.cpp \
src/instructionmodel.cpp \
- src/pipeline.cpp
+ src/pipeline.cpp \
+ src/mainmemory.cpp
HEADERS += \
src/parser.h \
@@ -74,7 +75,8 @@ HEADERS += \
src/instructionmodel.h \
src/binutils.h \
src/pipelineobjects.h \
- src/pipeline.h
+ src/pipeline.h \
+ src/mainmemory.h
FORMS += \
src/mainwindow.ui \

ADD
src/mainmemory.cpp
+#include "mainmemory.h"
+
+MainMemory::MainMemory() {}
+
+void MainMemory::write(uint32_t address, uint32_t value, int size) {
+ // writes value to from the given address start, and up to $size bytes of
+ // $value
+ // Using the hashtable, new allocations will automatically be handled
+ for (int i = 0; i < size; i++) {
+ m_memory[address + i] = value & 0xff;
+ value >>= 8;
+ }
+}
+
+uint32_t MainMemory::read(uint32_t address) {
+ // Note: If address is not found in memory map, a default constructed object
+ // will be created, and read. in our case uint8_t() = 0
+ uint32_t read = (m_memory[address] | (m_memory[address + 1] << 8) | (m_memory[address + 2] << 16) |
+ (m_memory[address + 3] << 24));
+ return read;
+}

ADD
src/mainmemory.h
+#ifndef MAINMEMORY_H
+#define MAINMEMORY_H
+
+#include "defines.h"
+
+class MainMemory {
+public:
+ explicit MainMemory();
+
+ void write(uint32_t address, uint32_t value, int size);
+ uint32_t read(uint32_t addess);
+ void clear() { m_memory.clear(); }
+
+private:
+ memory m_memory;
+};
+
+#endif // MAiNMEMORY_H

MODIFY
src/pipeline.cpp
src/pipeline.cpp
Pipeline::Pipeline() {
registerRegs();
+
+ // Build pipeline
+
+ // ------ IF ---------
+ // Connect alu to PC and a constant "4" signal, and set operator to "Add"
+ c_4 = Signal<32>(4);
+ alu_pc4.setInputs(std::vector<Signal<32>*>({r_PC.getOutput(), &c_4}));
}
void Pipeline::registerRegs() {
@@ -12,31 +19,51 @@ void Pipeline::registerRegs() {
void Pipeline::immGen() {
// 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 ((r_IFID.value() & 0b1100000) >> 5) {
+ switch (((uint32_t)r_IFID & 0b1100000) >> 5) {
case 0b00: {
// Load instruction, sign extend bits 31:20
- imm = Signal<64>(signextend<int32_t, 12>((r_IFID.value() >> 20)));
+ imm_ID = Signal<32>(signextend<int32_t, 12>(((uint32_t)r_IFID >> 20)));
break;
}
case 0b01: {
// Store instruction
- uint32_t v = r_IFID.value();
- imm = Signal<64>(signextend<int32_t, 12>(((v & 0xfe000000)) >> 20) | ((v & 0xf80) >> 7));
+ uint32_t v(r_IFID);
+ imm_ID = Signal<32>(signextend<int32_t, 12>(((v & 0xfe000000)) >> 20) | ((v & 0xf80) >> 7));
break;
}
default: {
// Conditional branch instructions
- uint32_t v = r_IFID.value();
- imm = Signal<64>(signextend<int32_t, 13>(((v & 0x80000000)) >> 20) | ((v & 0x7e) >> 20) |
+ uint32_t v(r_IFID);
+ imm_ID = Signal<32>(signextend<int32_t, 13>(((v & 0x80000000)) >> 20) | ((v & 0x7e) >> 20) |
((v & 0xf00) >> 7) | ((v & 0x80) << 6));
break;
}
}
}
+void Pipeline::step() {
+ // Main processing loop for the pipeline
+ // propagates the signals throgh the combinational logic and clocks the
+ // sequential logic afterwards
+
+ // The order of component updating must be done in the correct sequential order!
+
+ // IF
+ alu_pc4.update();
+ instr_IF = Signal<32>(m_memory.read((uint32_t)r_PC));
+
+ // Clock all registers
+ clock();
+}
+
void Pipeline::clock() {
// Clocks all registered registers
for (const auto& reg : m_regs) {
reg->clock();
}
}
+void Pipeline::reset() {
+ m_memory.clear();
+}
+
+void Pipeline::restart() {}

MODIFY
src/pipeline.h
src/pipeline.h
#ifndef PIPELINE_H
#define PIPELINE_H
+#include "mainmemory.h"
#include "pipelineobjects.h"
class Pipeline {
@@ -8,16 +9,29 @@ public:
Pipeline();
private:
+ // Utility functions
+ void restart();
+ void reset();
+
+ // Simulator functions
void registerRegs();
void immGen();
void clock();
+ void step();
+
+ // Objects
+ MainMemory m_memory;
- // Signals
- Signal<64> imm;
+ ALU<32> alu_pc4;
+ // Signals - Stage name is appended to name
+ Signal<32> c_4;
+ Signal<32> imm_ID;
+ Signal<32> instr_IF;
// Registers
std::vector<RegBase*> m_regs;
Reg<32> r_IFID;
+ Reg<32> r_PC;
};
#endif // PIPELINE_H

MODIFY
src/pipelineobjects.h
src/pipelineobjects.h
@@ -72,11 +72,15 @@ class Reg : public RegBase {
public:
Reg() { ASSERT_SIZE }
void clock() override { m_current = *m_next; }
- uint32_t value() const { return (uint32_t)m_current; }
- // Signal assignment operator
+ // Signal connection
void connect(Reg<n>& r) { setInput(&r.m_current); }
void connect(const Signal<n>* s) { setInput(s); }
+ Signal<n>* getOutput() { return &m_current; }
+
+ explicit operator int() const { return (int)m_current; }
+ explicit operator uint32_t() const { return (uint32_t)m_current; }
+ explicit operator bool() const { return (bool)m_current; }
private:
void setInput(const Signal<n>* in) { m_next = in; }
@@ -125,6 +129,7 @@ private:
}
};
+template <int n>
class ALU {
public:
// When calculating ALU control signals, the OPCODE is implicitely converted to an int in the Signal, which is later
@@ -134,7 +139,7 @@ public:
void update();
- void setInputs(std::vector<const Signal<WORDSIZE>*> sigs) {
+ void setInputs(std::vector<Signal<n>*> sigs) {
if (sigs.size() != 2)
throw std::invalid_argument("Input vector size must be 2 (2 ALU operands)");
m_op1 = sigs[0];
@@ -152,13 +157,14 @@ private:
return b;
}
- Signal<WORDSIZE> m_output;
- const Signal<WORDSIZE>* m_op1;
- const Signal<WORDSIZE>* m_op2;
+ Signal<n> m_output;
+ const Signal<n>* m_op1;
+ const Signal<n>* m_op2;
const Signal<CTRL_SIZE>* m_control;
};
-void ALU::update() {
+template <int n>
+void ALU<n>::update() {
if (!initialized())
throw std::runtime_error("Mux not initialized");
switch ((OPCODE)(int)*m_control) {
@@ -198,4 +204,5 @@ void ALU::update() {
break;
}
}
+
#endif // PIPELINEOBJECTS_H

Work on IF stage