summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbd <bdunahu@operationnull.com>2025-03-29 19:45:21 -0400
committerbd <bdunahu@operationnull.com>2025-03-29 19:45:21 -0400
commit6bce0485a0f9ce92bc235f063a1f9aab2d59a1c9 (patch)
tree8ac46090c2d9b8570464fcb76fd26c19a4f7f7a9
parentd21a1a9caa1f1791343a5376121936e552b1124c (diff)
Add proper read and write guard methods, clean up id test file
-rw-r--r--inc/id.h45
-rw-r--r--inc/stage.h53
-rw-r--r--src/sim/id.cc95
-rw-r--r--src/sim/stage.cc52
-rw-r--r--tests/id.cc153
5 files changed, 228 insertions, 170 deletions
diff --git a/inc/id.h b/inc/id.h
index 34e1a47..f8c167d 100644
--- a/inc/id.h
+++ b/inc/id.h
@@ -34,7 +34,42 @@ class ID : public Stage
void get_instr_fields(
signed int &s1, signed int &s2, signed int &s3, Mnemonic &m);
+ /* The following methods are made public so that they may be tested, and are
+ * not to be called from outside classes during standard execution.
+ */
+
+ /**
+ * Facilitates register checkout and data hazard management.
+ * It does this by checking that the register passed in is not currently
+ * checked out. If true, then replaces r with the value of the register and
+ * returns OK. If false, returns STALLED.
+ *
+ * @param the registers number, to be dereferenced.
+ * @return OK if `r` is not checked out, STALLED otherwise.
+ */
+ Response read_guard(signed int &r);
+ /**
+ * Facilitates register checkout and data hazard management.
+ * Checks out a register and returns it.
+ *
+ * @param the registers number, to be dereferenced and checked out.
+ */
+ void write_guard(signed int &r);
+
private:
+ // TODO write me
+ /**
+ * Helper for ``
+ * Attempts to parse and dereference instruction arguments. If a desc
+
+ * @param the resulting first field. To call this function properly, this
+ * field must contain the section of the instruction to be parsed.
+ * @param the resulting second field.
+ * @param the resulting third field.
+ */
+ void decode_R_type(signed int &s1, signed int &s2, signed int &s3);
+ void decode_I_type(signed int &s1, signed int &s2, signed int &s3);
+ void decode_J_type(signed int &s1, signed int &s2);
/**
* Helper for `get_instr_fields`.
* Given a raw instruction, returns the mnemonic and type.
@@ -44,14 +79,6 @@ class ID : public Stage
* @param the resulting type.
*/
void split_instr(signed int &raw, unsigned int &type, Mnemonic &m);
- /**
- * Facilitates checking out a register by dereferencing the register
- specified by the first parameter. Registers that are checked out cannot be
- checked out until they are checked in.
- * @param
- * @return the contents of the register.
- */
- Response dereference_register(signed int &);
};
-#endif /* ID_D_INCLUDED */
+#endif /* ID_H_INCLUDED */
diff --git a/inc/stage.h b/inc/stage.h
index ff4455b..19e3896 100644
--- a/inc/stage.h
+++ b/inc/stage.h
@@ -6,7 +6,7 @@
#include "response.h"
#include "storage.h"
#include <array>
-#include <set>
+#include <deque>
#include <memory>
class Stage
@@ -31,15 +31,18 @@ class Stage
protected:
/**
- * Facilitates register checkout.
- * It does this by checking that the register passed in is not currently
- * checked out. If true, then replaces r with the value of the register and
- * returns OK. If false, returns STALLED.
- *
- * @param the registers number, to be dereferenced.
- * @return OK if `r` is not checked out, STALLED otherwise.
+ * Helper for `check_out`.
+ * Returns true if r are not checked out, false otherwise.
+ * @param a list of register numbers.
+ * @return true if registers are not in checked_out, false otherwise.
+ */
+ bool is_checked_out(signed int r);
+ /**
+ * Returns the value of the register corresponding to `v`.
+ * @param the register number.
+ * @return the value in the associated register.
*/
- Response check_out(unsigned int &r);
+ signed int dereference_register(signed int v);
/**
* The name of the pipeline stage.
*/
@@ -68,6 +71,11 @@ class Stage
* The current clock cycle.
*/
static int clock_cycle;
+ // TODO fix this comment after writeback stage
+ /**
+ * The set of registers currently checked out.
+ */
+ static std::deque<signed int> checked_out;
/**
* A pointer to the next stage in the pipeline.
*/
@@ -80,33 +88,6 @@ class Stage
* The current status of this stage.
*/
Response status;
-
- private:
- /**
- * Helper for `check_out`.
- * Returns true if r are not checked out, false otherwise.
- * @param a list of register numbers.
- * @return true if registers are not in checked_out, false otherwise.
- */
- bool is_checked_out(unsigned int r);
- /**
- * Helper for `check_out`.
- * Checks out a single register, and places it in checked_out.
- * @param a register number.
- * @return the value in the register.
- */
- signed int check_out_register(unsigned int r);
- // TODO fix this comment after writeback stage
- /**
- * Helper for `check_out_register`
- * @param the register number.
- * @return the value in the associated register.
- */
- signed int dereference_register(unsigned int r);
- /**
- * The set of registers currently checked out.
- */
- static std::set<unsigned int> checked_out;
};
#endif /* STAGE_H_INCLUDED */
diff --git a/src/sim/id.cc b/src/sim/id.cc
index 83a8751..c04b31f 100644
--- a/src/sim/id.cc
+++ b/src/sim/id.cc
@@ -5,52 +5,85 @@
#include "logger.h"
#include "response.h"
#include "stage.h"
+#include <algorithm>
ID::ID(Stage *stage) : Stage(stage) { this->id = DCDE; }
Response ID::advance(InstrDTO &next_instr, Response p)
{
Response r;
- r = OK;
- // signed int s1, s2, s3;
- // Mnemonic m;
+ signed int s1, s2, s3;
+ Mnemonic m;
- // s1 = next_instr.get_instr_bits();
+ s1 = next_instr.get_instr_bits();
- // get_instr_fields(s1, s2, s3, m);
+ get_instr_fields(s1, s2, s3, m);
return r;
}
+void ID::decode_R_type(signed int &s1, signed int &s2, signed int &s3)
+{
+ unsigned int s0b, s1b, s2b;
+ Response r1, r2;
+
+ s0b = REG_SIZE;
+ s1b = s0b + REG_SIZE;
+ s2b = s1b + REG_SIZE;
+ s3 = GET_MID_BITS(s1, s1b, s2b);
+ s2 = GET_MID_BITS(s1, s0b, s1b);
+ s1 = GET_LS_BITS(s1, s0b);
+
+ r1 = this->read_guard(s1);
+ r2 = this->read_guard(s2);
+ this->write_guard(s3);
+
+ this->status = std::max(r1, r2);
+}
+void ID::decode_I_type(signed int &s1, signed int &s2, signed int &s3)
+{
+ unsigned int s0b, s1b, s2b;
+
+ s0b = REG_SIZE;
+ s1b = s0b + REG_SIZE;
+ s2b = WORD_SPEC;
+ s3 = GET_MID_BITS(s1, s1b, s2b);
+ s2 = GET_MID_BITS(s1, s0b, s1b);
+ s1 = GET_LS_BITS(s1, s0b);
+
+ this->status = this->read_guard(s1);
+ this->write_guard(s2);
+}
+
+void ID::decode_J_type(signed int &s1, signed int &s2)
+{
+ unsigned int s0b, s1b;
+
+ s0b = REG_SIZE;
+ s1b = WORD_SPEC;
+ s2 = GET_MID_BITS(s1, s0b, s1b);
+ s1 = GET_LS_BITS(s1, s0b);
+
+ this->status = this->read_guard(*&s1);
+}
+
// TODO this function is ugly
void ID::get_instr_fields(
signed int &s1, signed int &s2, signed int &s3, Mnemonic &m)
{
- unsigned int type, s0b, s1b, s2b;
+ unsigned int type;
this->split_instr(s1, type, m);
- // define the parsing bounds
- s0b = REG_SIZE;
switch (type) {
case 0b00:
- // R-TYPE
- s1b = s0b + REG_SIZE;
- s2b = s1b + REG_SIZE;
+ this->decode_R_type(s1, s2, s3);
break;
case 0b01:
- // I-TYPE
- s1b = s0b + REG_SIZE;
- s2b = WORD_SPEC;
+ this->decode_I_type(s1, s2, s3);
break;
case 0b10:
- // J-TYPE
- s1b = WORD_SPEC;
+ this->decode_J_type(s1, s2);
+ break;
}
-
- if (type != 0b10)
- s3 = GET_MID_BITS(s1, s1b, s2b);
-
- s2 = GET_MID_BITS(s1, s0b, s1b);
- s1 = GET_LS_BITS(s1, s0b);
}
void ID::split_instr(signed int &raw, unsigned int &type, Mnemonic &m)
@@ -68,3 +101,21 @@ void ID::split_instr(signed int &raw, unsigned int &type, Mnemonic &m)
raw = (unsigned int)raw >> (TYPE_SIZE + opcode_size);
}
+
+Response ID::read_guard(signed int &v)
+{
+ Response r;
+ if (this->is_checked_out(v))
+ r = BLOCKED;
+ else {
+ r = OK;
+ v = this->dereference_register(v);
+ }
+ return r;
+}
+
+void ID::write_guard(signed int &v)
+{
+ this->checked_out.push_back(v);
+ v = this->dereference_register(v);
+}
diff --git a/src/sim/stage.cc b/src/sim/stage.cc
index 48ee494..a8f3f9e 100644
--- a/src/sim/stage.cc
+++ b/src/sim/stage.cc
@@ -1,9 +1,7 @@
#include "stage.h"
#include "utils.h"
#include <array>
-#include <set>
-
-static Logger *global_log = Logger::getInstance();
+#include <deque>
Stage::Stage(Stage *next)
{
@@ -16,51 +14,29 @@ Stage::~Stage() { delete this->next; };
std::array<int, GPR_NUM> Stage::gprs;
std::array<int, V_NUM> Stage::vrs;
-std::set<unsigned int> Stage::checked_out;
+std::deque<signed int> Stage::checked_out;
unsigned int Stage::pc;
Storage *Stage::storage;
bool Stage::is_pipelined;
int Stage::clock_cycle;
-Response Stage::check_out(unsigned int &v)
-{
- Response r;
- if (this->is_checked_out(v))
- r = BLOCKED;
- else {
- r = OK;
- v = this->check_out_register(v);
- }
- return r;
-}
-
-bool Stage::is_checked_out(unsigned int r)
-{
- return this->checked_out.find(r) != this->checked_out.end();
-}
-
-signed int Stage::check_out_register(unsigned int v)
+signed int Stage::dereference_register(signed int v)
{
signed int r;
- this->checked_out.insert(v);
- r = this->dereference_register(v);
- return r;
-}
-
-signed int Stage::dereference_register(unsigned int v)
-{
- signed int r;
-
- if (v >= GPR_NUM + V_NUM) {
- global_log->log(
- ERROR, string_format(
- "instruction tried to access register %d, which does "
- "not exist",
- v));
- exit(EXIT_FAILURE);
+ if (v < 0 || v >= GPR_NUM + V_NUM) {
+ throw std::out_of_range(string_format(
+ "instruction tried to access register %d, which does "
+ "not exist",
+ v));
}
r = (v >= GPR_NUM) ? this->vrs[v % GPR_NUM] : this->gprs[v];
return r;
}
+
+bool Stage::is_checked_out(signed int r)
+{
+ return std::find(this->checked_out.begin(), this->checked_out.end(), r) !=
+ this->checked_out.end();
+}
diff --git a/tests/id.cc b/tests/id.cc
index b847026..f500b07 100644
--- a/tests/id.cc
+++ b/tests/id.cc
@@ -3,145 +3,168 @@
#include "controller.h"
#include "dram.h"
#include "if.h"
-#include "instrDTO.h"
#include "instr.h"
+#include "instrDTO.h"
#include <catch2/catch_test_macros.hpp>
-class IDPipeFixture
+class IDFixture
{
public:
- IDPipeFixture()
+ IDFixture()
{
Dram *dr;
- dr = new Dram(this->m_delay);
- // 0xC00 is a nop
- p = {0xC000, 0xC001, 0xC002, 0xC003};
- dr->load(p);
-
- this->c = new Cache(dr, this->c_delay);
+ dr = new Dram(3);
+ this->c = new Cache(dr, 1);
IF *f = new IF(nullptr);
this->d = new ID(f);
this->ct = new Controller(this->d, this->c, true);
};
- ~IDPipeFixture()
+ ~IDFixture()
{
delete this->ct;
delete this->c;
};
-
- // /**
- // * Requests something to do until it is received.
- // */
- // void fetch(InstrDTO &instr)
- // {
- // Response r = WAIT;
-
- // while (r != OK) {
- // r = this->ct->advance(instr);
- // }
- // }
-
- int m_delay = 3;
- int c_delay = 1;
+ signed int encode_R_type(
+ signed int s3,
+ signed int s2,
+ signed int s1,
+ signed int opcode,
+ signed int type)
+ {
+ signed int t;
+ t = s3;
+ t = (t << REG_SIZE) + s2;
+ t = (t << REG_SIZE) + s1;
+ t = (t << R_OPCODE_SIZE) + opcode;
+ t = (t << TYPE_SIZE) + type;
+ return t;
+ }
+ signed int encode_I_type(
+ signed int s3,
+ signed int s2,
+ signed int s1,
+ signed int opcode,
+ signed int type)
+ {
+ signed int t;
+ t = s3;
+ t = (t << REG_SIZE) + s2;
+ t = (t << REG_SIZE) + s1;
+ t = (t << OPCODE_SIZE) + opcode;
+ t = (t << TYPE_SIZE) + type;
+ return t;
+ }
+ signed int encode_J_type(
+ signed int s2, signed int s1, signed int opcode, signed int type)
+ {
+ signed int t;
+ t = s2;
+ t = (t << REG_SIZE) + s1;
+ t = (t << OPCODE_SIZE) + opcode;
+ t = (t << TYPE_SIZE) + type;
+ return t;
+ }
std::vector<signed int> p;
Cache *c;
ID *d;
Controller *ct;
};
-TEST_CASE_METHOD(IDPipeFixture, "Parse invalid type", "[id]")
+TEST_CASE_METHOD(IDFixture, "Parse invalid type", "[id]")
{
signed int s1 = 0, s2 = 0, s3 = 0;
Mnemonic m;
- s1 = 0x00FF00FF;
+ s1 = 0xFFFFFFFF;
this->d->get_instr_fields(s1, s2, s3, m);
CHECK(m == NOP);
}
-TEST_CASE_METHOD(IDPipeFixture, "Parse arbitrary r-type # one", "[id]")
+TEST_CASE_METHOD(IDFixture, "Parse arbitrary r-type # one", "[id]")
{
- signed int s1 = 0, s2 = 0, s3 = 0;
+ signed int s1 = -1, s2 = -1, s3 = -1;
Mnemonic m;
- s1 = 0xCCCCCC0C;
+ s1 = this->encode_R_type(0b0, 0b1, 0b10, 0b11, 0b0);
this->d->get_instr_fields(s1, s2, s3, m);
- CHECK(s1 == 0b11000);
- CHECK(s2 == 0b01100);
- CHECK(s3 == 0b00110);
+ CHECK(s1 == 0x00000000); // registers are empty
+ CHECK(s2 == 0x00000000);
+ CHECK(s3 == 0x00000000);
CHECK(m == MUL);
}
-TEST_CASE_METHOD(IDPipeFixture, "Parse arbitrary r-type # two", "[id]")
+TEST_CASE_METHOD(IDFixture, "Parse arbitrary r-type # two", "[id]")
{
- signed int s1 = 0, s2 = 0, s3 = 0;
+ signed int s1 = -1, s2 = -1, s3 = -1;
Mnemonic m;
- s1 = 0x99AABB0C;
+ s1 = this->encode_R_type(0b10000, 0b01000, 0b00100, 0b10, 0b0);
this->d->get_instr_fields(s1, s2, s3, m);
- CHECK(s1 == 0b10110);
- CHECK(s2 == 0b01011);
- CHECK(s3 == 0b10101);
- CHECK(m == MUL);
+ CHECK(s1 == 0x00000000); // registers are empty
+ CHECK(s2 == 0b00000000);
+ CHECK(s3 == 0b00000000);
+ CHECK(m == SUB);
}
-TEST_CASE_METHOD(IDPipeFixture, "Parse arbitrary i-type # one", "[id]")
+TEST_CASE_METHOD(IDFixture, "Parse arbitrary i-type # one", "[id]")
{
- signed int s1 = 0, s2 = 0, s3 = 0;
+ signed int s1 = -1, s2 = -1, s3 = -1;
Mnemonic m;
- s1 = 0xDDDDDDDD;
+ s1 = this->encode_I_type(0xF, 0b1, 0b10, 0b0111, 0b1);
this->d->get_instr_fields(s1, s2, s3, m);
- CHECK(s1 == 0b10111);
- CHECK(s2 == 0b11011);
- CHECK(s3 == 0xDDDD);
+ CHECK(s1 == 0x00000000); // registers are empty
+ CHECK(s2 == 0x00000000);
+ CHECK(s3 == 0xF);
CHECK(m == SFTLI);
}
-TEST_CASE_METHOD(IDPipeFixture, "Parse arbitrary i-type # two", "[id]")
+TEST_CASE_METHOD(IDFixture, "Parse arbitrary i-type # two", "[id]")
{
- signed int s1 = 0, s2 = 0, s3 = 0;
+ signed int s1 = -1, s2 = -1, s3 = -1;
Mnemonic m;
- s1 = 0xAABBCCDD;
+ s1 = this->encode_I_type(0xCC, 0b010, 0b101, 0b1011, 0b1);
this->d->get_instr_fields(s1, s2, s3, m);
- CHECK(s1 == 0b10011);
- CHECK(s2 == 0b11001);
- CHECK(s3 == 0xAABB);
- CHECK(m == SFTLI);
+ CHECK(s1 == 0x00000000); // registers are empty
+ CHECK(s2 == 0x00000000);
+ CHECK(s3 == 0xCC);
+ CHECK(m == STORE);
}
-TEST_CASE_METHOD(IDPipeFixture, "Parse arbitrary j-type # one", "[id]")
+TEST_CASE_METHOD(IDFixture, "Parse arbitrary j-type # one", "[id]")
{
- signed int s1 = 0, s2 = 0, s3 = 0;
+ signed int s1 = -1, s2 = -1, s3 = -1;
Mnemonic m;
- s1 = 0xEEEEEE1E;
+ s1 = this->encode_J_type(0x3456, 0b10101, 0b0111, 0b10);
this->d->get_instr_fields(s1, s2, s3, m);
- CHECK(s1 == 0b11000);
- CHECK(s2 == 0b111011101110111011101);
+ CHECK(s1 == 0x00000000); // registers are empty
+ CHECK(s2 == 0x3456);
CHECK(m == BOF);
// behavior does nothing
- CHECK(s3 == 0b0);
+ CHECK(s3 == -1);
}
-TEST_CASE_METHOD(IDPipeFixture, "Parse arbitrary j-type # two", "[id]")
+TEST_CASE_METHOD(IDFixture, "Parse arbitrary j-type # two", "[id]")
{
- signed int s1 = 0, s2 = 0, s3 = 0;
+ signed int s1 = -1, s2 = -1, s3 = -1;
Mnemonic m;
- s1 = 0xBBCCDD0E;
+ s1 = this->encode_J_type(0xBBCCF, 0b10101, 0b0011, 0b10);
this->d->get_instr_fields(s1, s2, s3, m);
- CHECK(s1 == 0b10100);
- CHECK(s2 == 0b101110111100110011011);
+ CHECK(s1 == 0x00000000); // registers are empty
+ CHECK(s2 == 0xBBCCF);
CHECK(m == JAL);
// behavior does nothing
- CHECK(s3 == 0b0);
+ CHECK(s3 == -1);
}
+
+// TEST_CASE_METHOD(IDFixture, "No data hazards", "[id]") { signed int }