diff options
author | bd <bdunahu@operationnull.com> | 2025-03-29 19:45:21 -0400 |
---|---|---|
committer | bd <bdunahu@operationnull.com> | 2025-03-29 19:45:21 -0400 |
commit | 6bce0485a0f9ce92bc235f063a1f9aab2d59a1c9 (patch) | |
tree | 8ac46090c2d9b8570464fcb76fd26c19a4f7f7a9 | |
parent | d21a1a9caa1f1791343a5376121936e552b1124c (diff) |
Add proper read and write guard methods, clean up id test file
-rw-r--r-- | inc/id.h | 45 | ||||
-rw-r--r-- | inc/stage.h | 53 | ||||
-rw-r--r-- | src/sim/id.cc | 95 | ||||
-rw-r--r-- | src/sim/stage.cc | 52 | ||||
-rw-r--r-- | tests/id.cc | 153 |
5 files changed, 228 insertions, 170 deletions
@@ -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 } |