diff options
| -rw-r--r-- | gui/main.cc | 2 | ||||
| -rw-r--r-- | inc/accessor.h | 3 | ||||
| -rw-r--r-- | inc/controller.h | 2 | ||||
| -rw-r--r-- | inc/definitions.h | 32 | ||||
| -rw-r--r-- | inc/ex.h | 9 | ||||
| -rw-r--r-- | inc/id.h | 78 | ||||
| -rw-r--r-- | inc/if.h | 18 | ||||
| -rw-r--r-- | inc/instr.h | 56 | ||||
| -rw-r--r-- | inc/instrDTO.h | 69 | ||||
| -rw-r--r-- | inc/mm.h | 9 | ||||
| -rw-r--r-- | inc/response.h | 1 | ||||
| -rw-r--r-- | inc/stage.h | 58 | ||||
| -rw-r--r-- | inc/storage.h | 2 | ||||
| -rw-r--r-- | inc/utils.h | 2 | ||||
| -rw-r--r-- | inc/wb.h | 9 | ||||
| -rw-r--r-- | src/cli/cli.cc | 3 | ||||
| -rw-r--r-- | src/sim/controller.cc | 10 | ||||
| -rw-r--r-- | src/sim/ex.cc | 11 | ||||
| -rw-r--r-- | src/sim/id.cc | 139 | ||||
| -rw-r--r-- | src/sim/if.cc | 33 | ||||
| -rw-r--r-- | src/sim/instr.cc | 53 | ||||
| -rw-r--r-- | src/sim/instrDTO.cc | 26 | ||||
| -rw-r--r-- | src/sim/mm.cc | 10 | ||||
| -rw-r--r-- | src/sim/stage.cc | 36 | ||||
| -rw-r--r-- | src/sim/wb.cc | 11 | ||||
| -rw-r--r-- | src/storage/cache.cc | 4 | ||||
| -rw-r--r-- | src/utils/accessor.cc | 4 | ||||
| -rw-r--r-- | src/utils/response.cc | 2 | ||||
| -rw-r--r-- | src/utils/utils.cc | 10 | ||||
| -rw-r--r-- | tests/id.cc | 251 | ||||
| -rw-r--r-- | tests/if.cc | 46 | ||||
| -rw-r--r-- | tests/utils.cc | 8 | 
32 files changed, 913 insertions, 94 deletions
diff --git a/gui/main.cc b/gui/main.cc index e08eab7..ce47d10 100644 --- a/gui/main.cc +++ b/gui/main.cc @@ -9,6 +9,7 @@  static Logger *global_log = Logger::getInstance();  static std::string version_number = "v0.1"; +// clang-format off  static std::string banner =    "    _/_/_/    _/_/_/    _/_/_/    _/_/_/   \n"    "   _/    _/    _/    _/        _/          \n" @@ -24,6 +25,7 @@ static std::string banner =    " _/  _/    _/    _/        _/            _/      _/    _/  _/    _/    _/      \n"    "  _/      _/    _/_/_/_/    _/_/_/      _/        _/_/    _/    _/    _/       \n"    "         _/_/                                                      _/_/    "; +// clang-format on  static void print_version_number()  {  	std::cout << banner << version_number << '\n'; diff --git a/inc/accessor.h b/inc/accessor.h index fb4999d..eb56785 100644 --- a/inc/accessor.h +++ b/inc/accessor.h @@ -4,7 +4,10 @@  enum Accessor {  	IDLE, +	WRITE,  	MEM, +	EXEC, +	DCDE,  	FETCH,  	L1CACHE,  	SIDE, diff --git a/inc/controller.h b/inc/controller.h index 4b102ce..ac83a78 100644 --- a/inc/controller.h +++ b/inc/controller.h @@ -36,7 +36,7 @@ class Controller : public Stage  	 * @return the pc.  	 */  	int get_pc(); -	Response advance(InstrDTO &i) override; +	Response advance(InstrDTO &i, Response p) override;  };  #endif /* CONTROLLER_H_INCLUDED */ diff --git a/inc/definitions.h b/inc/definitions.h index ff2f7c6..3238a8b 100644 --- a/inc/definitions.h +++ b/inc/definitions.h @@ -11,6 +11,10 @@   * The total number of words in a line   */  #define LINE_SIZE static_cast<int>(pow(2, 2)) +/** + * Number of bits in a word + */ +#define WORD_SPEC 32  /**   * The number of bits to specify a memory word @@ -28,7 +32,8 @@   * The total number of lines in l1 cache   */  #define L1_CACHE_WORD_SPEC 7 -#define L1_CACHE_LINE_SPEC static_cast<unsigned int>(L1_CACHE_WORD_SPEC - LINE_SPEC) +#define L1_CACHE_LINE_SPEC                                                     \ +	static_cast<unsigned int>(L1_CACHE_WORD_SPEC - LINE_SPEC)  #define L1_CACHE_LINES static_cast<int>(pow(2, L1_CACHE_LINE_SPEC))  /** @@ -47,6 +52,31 @@  #define GPR_NUM 16  /** + * The number of vector registers + */ +#define V_NUM 8 + +/** + * The number of bits to specify an instruction type + */ +#define TYPE_SIZE 2 + +/** + * The number of bits to specify a register + */ +#define REG_SIZE 5 + +/** + * The number of bits to specify an R-Type opcode. + */ +#define R_OPCODE_SIZE 5 + +/** + * The number of bits to specify an opcode. + */ +#define OPCODE_SIZE 4 + +/**   * Return the N least-significant bits from integer K using a bit mask   * @param the integer to be parsed   * @param the number of bits to be parsed @@ -7,9 +7,14 @@  class EX : public Stage  {    public: -	using Stage::Stage; +	/** +	 * Constructor. +	 * @param The next stage in the pipeline. +	 * @return A newly allocated EX object. +	 */ +	EX(Stage *next); -	Response advance(InstrDTO &i) override; +	Response advance(InstrDTO &next_instr, Response p) override;  };  #endif /* EX_H_INCLUDED */ @@ -1,5 +1,6 @@  #ifndef ID_H  #define ID_H +#include "instr.h"  #include "instrDTO.h"  #include "response.h"  #include "stage.h" @@ -7,9 +8,80 @@  class ID : public Stage  {    public: -	using Stage::Stage; +	/** +	 * Constructor. +	 * @param The next stage in the pipeline. +	 * @return A newly allocated ID object. +	 */ +	ID(Stage *next); -	Response advance(InstrDTO &i) override; +	Response advance(InstrDTO &next_instr, Response p) override; + +	/* The following methods are made public so that they may be tested, and are +	 * not to be called from outside classes during standard execution. +	 */ + +	/** +	 * Parse an instruction into a type, opcode, and fields. If the type is +	 * invalid, only the type field will be set. +	 * +	 * This method is marked public so it may be tested, and is not used outside +	 * of this class during normal execution. +	 * +	 * @param the resulting first field, which varies per type. To call this +	 * function properly, this field must contain the full instruction bytes on +	 * function entry. +	 * @param the resulting second field, which varies per type. +	 * @param the resulting third field, which varies per type. +	 * @param the resulting mnemonic. +	 */ +	void get_instr_fields( +		signed int &s1, signed int &s2, signed int &s3, Mnemonic &m); +	/** +	 * 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: +	/** +	 * Decodes `curr_instr` and sets status to BLOCKED if a data hazard occurs. +	 */ +	void advance_helper(); +	/** +	 * Helper for `get_instr_fields` +	 * Attempts to parse and dereference instruction arguments. Uses read and +	 * write guards to prevent RAW conflicts. +	 * +	 * @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. +	 * This operation will destroy the original arguments. +	 * @param the raw bits to parse. +	 * @param the resulting mnemonic. +	 * @param the resulting type. +	 */ +	void split_instr(signed int &raw, unsigned int &type, Mnemonic &m);  }; -#endif /* ID_D_INCLUDED */ +#endif /* ID_H_INCLUDED */ @@ -1,5 +1,6 @@  #ifndef IF_H  #define IF_H +#include "accessor.h"  #include "instrDTO.h"  #include "response.h"  #include "stage.h" @@ -7,15 +8,24 @@  class IF : public Stage  {    public: -	using Stage::Stage; +	/** +	 * Constructor. +	 * @param The next stage in the pipeline. +	 * @return A newly allocated IF object. +	 */ +	IF(Stage *next); -	Response advance(InstrDTO &i) override; +	Response advance(InstrDTO &next_instr, Response p) override;    private:  	/** -	 * The name this pipeline stages uses to access storage. +	 * Performs a fetch only if a current fetch is not pending. Pending means +	 * that a fetch has completed successfully, but the caller stage in the +	 * pipeline is not ready to receive it. In this case, `curr_instr` is not +	 * the nullptr. +	 * @return STALLED if we are waiting on the storage devices, OK otherwise.  	 */ -	const Accessor id = FETCH; +	void advance_helper();  };  #endif /* IF_H_INCLUDED */ diff --git a/inc/instr.h b/inc/instr.h new file mode 100644 index 0000000..08b4fd0 --- /dev/null +++ b/inc/instr.h @@ -0,0 +1,56 @@ +#ifndef INSTR_H +#define INSTR_H +#include <functional> +#include <iostream> +#include <unordered_map> + +enum Mnemonic { +	ADD, +	SUB, +	MUL, +	QUOT, +	REM, +	SFTR, +	SFTL, +	AND, +	OR, +	NOT, +	XOR, +	ADDV, +	SUBV, +	MULV, +	DIVV, +	CMP, +	CEV, +	LOAD, +	LOADV, +	ADDI, +	SUBI, +	SFTRI, +	SFTLI, +	ANDI, +	ORI, +	XORI, +	STORE, +	STOREV, +	JMP, +	JRL, +	JAL, +	BEQ, +	BGT, +	BUF, +	BOF, +	PUSH, +	POP, +	NOP, +}; + +namespace instr +{ +// clang-format off +	extern const std::unordered_map<unsigned int, Mnemonic> mnemonic_map; +	extern const std::unordered_map<Mnemonic, std::function<void(signed int &s1, signed int &s2, signed int &s3)>> instr_map; +// clang-format on +} // namespace instr + +#endif /* INSTR_H_INCLUDED */ diff --git a/inc/instrDTO.h b/inc/instrDTO.h index 86cec05..77a223e 100644 --- a/inc/instrDTO.h +++ b/inc/instrDTO.h @@ -1,5 +1,10 @@  #ifndef INSTRDTO_H  #define INSTRDTO_H +#include "accessor.h" +#include "instr.h" +#include <functional> +#include <string> +#include <unordered_map>  class InstrDTO  { @@ -11,29 +16,81 @@ class InstrDTO  	~InstrDTO() = default;  	/** -	 * @return if_cycle +	 * @return hist entry for Accessor  	 */ -	int get_if_cycle(); +	int get_time_of(Accessor); +	/** +	 * @return id_cycle +	 */ +	int get_id_cycle();  	/**  	 * @return instr_bits  	 */  	signed int get_instr_bits(); +	/** +	 * @return s1 +	 */ +	signed int get_s1(); +	/** +	 * @return s2 +	 */ +	signed int get_s2(); +	/** +	 * @return s3 +	 */ +	signed int get_s3(); +	/** +	 * @return the mnemonic of the instruction +	 */ +	Mnemonic get_mnemonic();  	/** -	 * @param if_cycle +	 * @param set hist key  	 */ -	void set_if_cycle(int); +	void set_time_of(Accessor, int);  	/**  	 * @param instr_bits  	 */  	void set_instr_bits(signed int); +	/** +	 * @param s1 +	 */ +	void set_s1(signed int); +	/** +	 * @param s2 +	 */ +	void set_s2(signed int); +	/** +	 * @param s3 +	 */ +	void set_s3(signed int); +	/** +	 * @param the mnemonic of the instruction +	 */ +	void set_mnemonic(Mnemonic);    private:  	/** -	 * The current clock cycle. +	 * The clock cycle each stage finished an operation. +	 */ +	std::unordered_map<Accessor, int> hist; +	/** +	 * The raw bits encoding the instruction.  	 */ -	int if_cycle;  	signed int instr_bits; +	/** +	 * Slots in this instruction, for storing temporary registers, immediates, +	 * or other. +	 * Some instruction types may use these differently. +	 * The `oper` function is in charge of knowing how to parse these. +	 */ +	signed int s1; +	signed int s2; +	signed int s3; +	/** +	 * The mnemonic of the operation. +	 */ +	Mnemonic mnemonic;  };  #endif /* INSTRDTO_H_INCLUDED */ @@ -7,9 +7,14 @@  class MM : public Stage  {    public: -	using Stage::Stage; +	/** +	 * Constructor. +	 * @param The next stage in the pipeline. +	 * @return A newly allocated MM object. +	 */ +	MM(Stage *next); -	Response advance(InstrDTO &i) override; +	Response advance(InstrDTO &next_instr, Response p) override;  };  #endif /* MM_H_INCLUDED */ diff --git a/inc/response.h b/inc/response.h index 6cd6678..05e9352 100644 --- a/inc/response.h +++ b/inc/response.h @@ -6,6 +6,7 @@ enum Response {  	OK,  	WAIT,  	BLOCKED, +	STALLED,  };  std::ostream &operator<<(std::ostream &os, Response r); diff --git a/inc/stage.h b/inc/stage.h index ac810a9..19e3896 100644 --- a/inc/stage.h +++ b/inc/stage.h @@ -1,10 +1,13 @@  #ifndef STAGE_H  #define STAGE_H +#include "accessor.h"  #include "definitions.h"  #include "instrDTO.h"  #include "response.h"  #include "storage.h"  #include <array> +#include <deque> +#include <memory>  class Stage  { @@ -15,29 +18,47 @@ class Stage  	 * @return A newly allocated stage object.  	 */  	Stage(Stage *next); -	virtual ~Stage() = default; +	virtual ~Stage();  	/**  	 * Advances this stage by a single clock cycle. -	 * @param a DTO object containing various information about an instruction -	 * moving through the pipeline. -	 * @return a response, indicating whether this pipeline stage is stalled, +	 * @param a DTO object containing the next instruction to be processed. +	 * @param a response, indicating whether or not the parent pipe stage is +	 * ready to accept a new instruction object next cycle. +	 * @return a response, indicating whether this pipeline stage is stalling,  	 * busy, or done.  	 */ -	virtual Response advance(InstrDTO &i) = 0; +	virtual Response advance(InstrDTO &next_instr, Response p) = 0;    protected:  	/** +	 * 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. +	 */ +	signed int dereference_register(signed int v); +	/** +	 * The name of the pipeline stage. +	 */ +	Accessor id; +	/**  	 * The shared pool of general-purpose integer registers.  	 */ -	static std::array<int, GPR_NUM> gprs; +	static std::array<signed int, GPR_NUM> gprs;  	/** -	 * The address of the currently executing instruction. +	 * The shared pool of general-purpose vector registers.  	 */ -	static int pc; +	static std::array<signed int, V_NUM> vrs;  	/** -	 * A pointer to the next stage in the pipeline. +	 * The address of the currently executing instruction.  	 */ -	Stage *next; +	static unsigned int pc;  	/**  	 * A pointer to the top-level storage device.  	 */ @@ -50,6 +71,23 @@ 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. +	 */ +	Stage *next; +	/** +	 * A pointer to the current instruction this stage is processing. +	 */ +	std::unique_ptr<InstrDTO> curr_instr; +	/** +	 * The current status of this stage. +	 */ +	Response status;  };  #endif /* STAGE_H_INCLUDED */ diff --git a/inc/storage.h b/inc/storage.h index ff1fbcb..d6fa094 100644 --- a/inc/storage.h +++ b/inc/storage.h @@ -80,7 +80,7 @@ class Storage  	/**  	 * The accessor currently being serviced.  	 */ -	enum Accessor requester; +	Accessor requester;  	/**  	 * The number of cycles until the current request is completed.  	 */ diff --git a/inc/utils.h b/inc/utils.h index df8d374..a375b68 100644 --- a/inc/utils.h +++ b/inc/utils.h @@ -10,7 +10,7 @@   * @param the resulting index   * @param the resulting offset   */ -void get_bit_fields(int address, int *tag, int *index, int *offset); +void get_cache_fields(int address, int *tag, int *index, int *offset);  /**   * Formats a string using snprintf. @@ -7,9 +7,14 @@  class WB : public Stage  {    public: -	using Stage::Stage; +	/** +	 * Constructor. +	 * @param The next stage in the pipeline. +	 * @return A newly allocated WB object. +	 */ +	WB(Stage *next); -	Response advance(InstrDTO &i) override; +	Response advance(InstrDTO &next_instr, Response p) override;  };  #endif /* WB_H_INCLUDED */ diff --git a/src/cli/cli.cc b/src/cli/cli.cc index 022b266..58575cb 100644 --- a/src/cli/cli.cc +++ b/src/cli/cli.cc @@ -3,6 +3,7 @@  #include "definitions.h"  #include "dram.h"  #include "response.h" +#include "accessor.h"  #include "utils.h"  #include <iostream> @@ -43,6 +44,7 @@ Cli::Cli()  	};  	commands['r'] = [this](std::vector<std::string> args) { +		(void)args;  		reset();  		return;  	}; @@ -61,6 +63,7 @@ Cli::Cli()  	};  	commands['h'] = [this](std::vector<std::string> args) { +		(void)args;  		help();  		return;  	}; diff --git a/src/sim/controller.cc b/src/sim/controller.cc index 6d46dc4..17937eb 100644 --- a/src/sim/controller.cc +++ b/src/sim/controller.cc @@ -10,6 +10,8 @@ Controller::Controller(Stage *stage, Storage *storage, bool is_pipelined)  	this->is_pipelined = is_pipelined;  	this->pc = 0x0;  	this->gprs = {0}; +	// grant side-door access +	this->id = SIDE;  }  void Controller::run_for(int number) @@ -17,7 +19,7 @@ void Controller::run_for(int number)  	InstrDTO instr;  	int i;  	for (i = 0; i < number; ++i) { -		this->advance(instr); +		this->advance(instr, OK);  	}  } @@ -27,11 +29,11 @@ std::array<int, GPR_NUM> Controller::get_gprs() { return this->gprs; }  int Controller::get_pc() { return this->pc; } -Response Controller::advance(InstrDTO &i) +Response Controller::advance(InstrDTO &next_instr, Response p)  {  	Response r; -  -	r = this->next->advance(i); + +	r = this->next->advance(next_instr, p);  	++this->clock_cycle;  	return r;  } diff --git a/src/sim/ex.cc b/src/sim/ex.cc index 1de61d0..5b561f8 100644 --- a/src/sim/ex.cc +++ b/src/sim/ex.cc @@ -1,12 +1,9 @@  #include "ex.h" +#include "accessor.h"  #include "instrDTO.h" -#include "logger.h"  #include "response.h" +#include "stage.h" -static Logger *global_log = Logger::getInstance(); +EX::EX(Stage *stage) : Stage(stage) { this->id = EXEC; } -Response EX::advance(InstrDTO &i) -{ -	global_log->log(INFO, "hello from execute!"); -	return OK; -} +Response EX::advance(InstrDTO &next_instr, Response p) { return OK; } diff --git a/src/sim/id.cc b/src/sim/id.cc index df55fe2..e18ef14 100644 --- a/src/sim/id.cc +++ b/src/sim/id.cc @@ -1,12 +1,143 @@  #include "id.h" +#include "accessor.h" +#include "instr.h"  #include "instrDTO.h"  #include "logger.h"  #include "response.h" +#include "stage.h" -static Logger *global_log = Logger::getInstance(); +ID::ID(Stage *stage) : Stage(stage) { this->id = DCDE; } -Response ID::advance(InstrDTO &i) +Response ID::advance(InstrDTO &next_instr, Response p)  { -	global_log->log(INFO, "hello from decode!"); -	return OK; +	Response n; + +	this->advance_helper(); +	if (this->status == OK && p == OK) { +	  // mutual consent +		this->curr_instr->set_time_of(this->id, this->clock_cycle); +		next_instr = *this->curr_instr; +		curr_instr = nullptr; +	} + +	n = (p != OK || this->status != OK) ? BLOCKED : OK; +	// the power of consent +	n = this->next->advance(curr_instr, n); +} + +void ID::get_instr_fields( +	signed int &s1, signed int &s2, signed int &s3, Mnemonic &m) +{ +	unsigned int type; +	this->split_instr(s1, type, m); + +	switch (type) { +	case 0b00: +		this->decode_R_type(s1, s2, s3); +		break; +	case 0b01: +		this->decode_I_type(s1, s2, s3); +		break; +	case 0b10: +		this->decode_J_type(s1, s2); +		break; +	} +} + +void ID::split_instr(signed int &raw, unsigned int &type, Mnemonic &m) +{ +	unsigned int opcode, opcode_size; + +	type = GET_LS_BITS(raw, TYPE_SIZE); +	opcode_size = (type == 0b0) ? R_OPCODE_SIZE : OPCODE_SIZE; +	opcode = GET_MID_BITS(raw, TYPE_SIZE, TYPE_SIZE + opcode_size); +	try { +		m = instr::mnemonic_map.at((opcode << TYPE_SIZE) + type); +	} catch (std::out_of_range const &) { +		m = NOP; +	} + +	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); +} + +void ID::advance_helper() +{ +	signed int s1, s2, s3; +	Mnemonic m; + +	// it may be good to ensure we are not doing +	// work that has already been done +	if (this->curr_instr) { +		s1 = curr_instr->get_instr_bits(); +		get_instr_fields(s1, s2, s3, m); +		if (this->status == OK) { +			curr_instr->set_s1(s1); +			curr_instr->set_s2(s2); +			curr_instr->set_s3(s3); +			curr_instr->set_mnemonic(m); +		} +	} +} + +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 = (r1 == BLOCKED || r2 == BLOCKED) ? BLOCKED : OK; +} +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);  } diff --git a/src/sim/if.cc b/src/sim/if.cc index deed8e1..7d3291b 100644 --- a/src/sim/if.cc +++ b/src/sim/if.cc @@ -2,18 +2,35 @@  #include "accessor.h"  #include "instrDTO.h"  #include "response.h" +#include "stage.h" -Response IF::advance(InstrDTO &i) +IF::IF(Stage *stage) : Stage(stage) { this->id = FETCH; } + +Response IF::advance(InstrDTO &next_instr, Response p) +{ +	this->advance_helper(); +	if (this->status == OK && p == OK) { +		// mutual consent +		++this->pc; +		this->curr_instr->set_time_of(this->id, this->clock_cycle); +		next_instr = *this->curr_instr; +		curr_instr = nullptr; +	} +	return this->status; +} + +void IF::advance_helper()  {  	Response r;  	signed int bits; -	r = this->storage->read_word(this->id, this->pc, bits); -	if (r == OK) { -		++this->pc; -		i.set_if_cycle(this->clock_cycle); -		i.set_instr_bits(bits); +	if (this->curr_instr == nullptr) { +		r = this->storage->read_word(this->id, this->pc, bits); +		if (r == OK) { +			this->status = r; +			this->curr_instr = std::make_unique<InstrDTO>(); +			this->curr_instr->set_instr_bits(bits); +		} else +			this->status = STALLED;  	} - -	return r;  } diff --git a/src/sim/instr.cc b/src/sim/instr.cc new file mode 100644 index 0000000..08edf5e --- /dev/null +++ b/src/sim/instr.cc @@ -0,0 +1,53 @@ +#include "instr.h" +#include <functional> +#include <map> +#include <unordered_map> + +// clang-format off +#define INIT_INSTRUCTION(mnemonic, body) \ +	{mnemonic, [](signed int &s1, signed int &s2, signed int &s3) { \ +		body; \ +	}} +// clang-format on + +namespace instr +{ +// clang-format off +const std::unordered_map<Mnemonic, std::function<void(signed int &s1, signed int &s2, signed int &s3)>> +	// clang-format on +	instr_map = { + +		/* R type instructions */ +		// TODO these need to be WRAPPED with a function that sets overflow. +		// future note to self, if these are more than 2 lines each, you're +		// doing it wrong +		INIT_INSTRUCTION(ADD, s3 = s1 + s2;), +		INIT_INSTRUCTION(SUB, s3 = s1 - s2;), + +		/* I type instructions */ + +		/* J type instructions */ + +		/* NOP */ +		INIT_INSTRUCTION(NOP, (void)s3; (void)s2; (void)s1;), + +}; + +const std::unordered_map<unsigned int, Mnemonic> mnemonic_map = { +	{0b0000100, ADD},	 {0b0001000, SUB},	 {0b0001100, MUL}, +	{0b0010000, QUOT},	 {0b0010100, REM},	 {0b0011000, SFTR}, +	{0b0011100, SFTL},	 {0b0100000, AND},	 {0b0100100, OR}, +	{0b0101000, NOT},	 {0b0101100, XOR},	 {0b0110000, ADDV}, +	{0b0110100, SUBV},	 {0b0111000, MULV},	 {0b0111100, DIVV}, +	{0b1000000, CMP},	 {0b1000100, CEV},	 {0b000101, LOAD}, +	{0b001001, LOADV},	 {0b0001101, ADDI},	 {0b0010001, SUBI}, +	{0b0010101, SFTRI},	 {0b0011101, SFTLI}, {0b0100001, ANDI}, +	{0b0100101, ORI},	 {0b0101001, XORI},	 {0b0101101, STORE}, +	{0b0110001, STOREV}, {0b0000101, CEV},	 {0b0000101, LOAD}, +	{0b0001001, LOADV},	 {0b0001001, LOADV}, {0b0000110, JMP}, +	{0b0001010, JRL},	 {0b0001110, JAL},	 {0b0010010, BEQ}, +	{0b0010110, BGT},	 {0b0011010, BUF},	 {0b0011110, BOF}, +	{0b0100010, PUSH},	 {0b0100110, POP}, + +}; +} // namespace instr diff --git a/src/sim/instrDTO.cc b/src/sim/instrDTO.cc index 6427b1a..5a7fe3b 100644 --- a/src/sim/instrDTO.cc +++ b/src/sim/instrDTO.cc @@ -1,15 +1,35 @@  #include "instrDTO.h" +#include "accessor.h"  InstrDTO::InstrDTO()  { -	this->if_cycle = 0;  	this->instr_bits = 0; +	this->s1 = 0; +	this->s2 = 0; +	this->s3 = 0; +	this->mnemonic = NOP;  } -int InstrDTO::get_if_cycle() { return this->if_cycle; } +int InstrDTO::get_time_of(Accessor a) { return this->hist[a]; }  signed int InstrDTO::get_instr_bits() { return this->instr_bits; } -void InstrDTO::set_if_cycle(int cycle) { this->if_cycle = cycle; } +signed int InstrDTO::get_s1() { return this->s1; } + +signed int InstrDTO::get_s2() { return this->s2; } + +signed int InstrDTO::get_s3() { return this->s3; } + +Mnemonic InstrDTO::get_mnemonic() { return this->mnemonic; } + +void InstrDTO::set_time_of(Accessor a, int i) { this->hist[a] = i; }  void InstrDTO::set_instr_bits(signed int instr) { this->instr_bits = instr; } + +void InstrDTO::set_s1(signed int s) { this->s1 = s; } + +void InstrDTO::set_s2(signed int s) { this->s2 = s; } + +void InstrDTO::set_s3(signed int s) { this->s3 = s; } + +void InstrDTO::set_mnemonic(Mnemonic m) { this->mnemonic = m; } diff --git a/src/sim/mm.cc b/src/sim/mm.cc index 28243e7..f394420 100644 --- a/src/sim/mm.cc +++ b/src/sim/mm.cc @@ -1,12 +1,12 @@  #include "mm.h" -#include "logger.h" -#include "response.h" +#include "accessor.h"  #include "instrDTO.h" +#include "response.h" +#include "stage.h" -static Logger *global_log = Logger::getInstance(); +MM::MM(Stage *stage) : Stage(stage) { this->id = MEM; }  -Response MM::advance(InstrDTO &i) +Response MM::advance(InstrDTO &next_instr, Response p)  { -	global_log->log(INFO, "hello from memory!");  	return OK;  } diff --git a/src/sim/stage.cc b/src/sim/stage.cc index 0d48774..62a7fd6 100644 --- a/src/sim/stage.cc +++ b/src/sim/stage.cc @@ -1,11 +1,43 @@  #include "stage.h" +#include "utils.h" +#include <array> +#include <deque> -Stage::Stage(Stage *next) { +Stage::Stage(Stage *next) +{  	this->next = next; +	this->curr_instr = nullptr; +	this->status = OK; +	this->checked_out = {};  } +Stage::~Stage() { delete this->next; }; +  std::array<int, GPR_NUM> Stage::gprs; -int Stage::pc; +std::array<int, V_NUM> Stage::vrs; +std::deque<signed int> Stage::checked_out; +unsigned int Stage::pc;  Storage *Stage::storage;  bool Stage::is_pipelined;  int Stage::clock_cycle; + +signed int Stage::dereference_register(signed int v) +{ +	signed int r; + +	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/src/sim/wb.cc b/src/sim/wb.cc index 9585fd3..bdea65a 100644 --- a/src/sim/wb.cc +++ b/src/sim/wb.cc @@ -1,12 +1,9 @@  #include "wb.h" +#include "accessor.h"  #include "instrDTO.h" -#include "logger.h"  #include "response.h" +#include "stage.h" -static Logger *global_log = Logger::getInstance(); +WB::WB(Stage *stage) : Stage(stage) { this->id = WRITE; } -Response WB::advance(InstrDTO &i) -{ -	global_log->log(INFO, "hello from write back!"); -	return OK; -} +Response WB::advance(InstrDTO &next_instr, Response p) { return OK; } diff --git a/src/storage/cache.cc b/src/storage/cache.cc index dccab47..80f59ef 100644 --- a/src/storage/cache.cc +++ b/src/storage/cache.cc @@ -69,7 +69,7 @@ Response Cache::process(  	Response r = this->is_access_cleared(accessor, address);  	if (r == OK) {  		int tag, index, offset; -		get_bit_fields(address, &tag, &index, &offset); +		get_cache_fields(address, &tag, &index, &offset);  		request_handler(index, offset);  	}  	return r; @@ -104,7 +104,7 @@ void Cache::handle_miss(int expected)  	std::array<signed int, LINE_SIZE> *actual;  	std::array<int, 2> *meta; -	get_bit_fields(expected, &tag, &index, &offset); +	get_cache_fields(expected, &tag, &index, &offset);  	r = OK;  	meta = &this->meta.at(index);  	actual = &this->data->at(index); diff --git a/src/utils/accessor.cc b/src/utils/accessor.cc index 86484c5..99347ed 100644 --- a/src/utils/accessor.cc +++ b/src/utils/accessor.cc @@ -3,6 +3,8 @@  std::ostream &operator<<(std::ostream &os, Accessor a)  { -	const std::string nameA[] = {"IDLE", "MEM", "FETCH", "L1CACHE", "SIDE"}; +	const std::string nameA[] = { +		"IDLE", "WRITE", "MEM", "EXEC", "DCDE", "FETCH", "L1CACHE", "SIDE", +	};  	return os << nameA[a];  } diff --git a/src/utils/response.cc b/src/utils/response.cc index def6578..3d6e439 100644 --- a/src/utils/response.cc +++ b/src/utils/response.cc @@ -3,6 +3,6 @@  std::ostream &operator<<(std::ostream &os, Response r)  { -	const std::string nameR[] = {"OK", "WAIT", "BLOCKED"}; +	const std::string nameR[] = {"OK", "WAIT", "BLOCKED", "STALLED"};  	return os << nameR[r];  } diff --git a/src/utils/utils.cc b/src/utils/utils.cc index 3a11c2c..e12a0e0 100644 --- a/src/utils/utils.cc +++ b/src/utils/utils.cc @@ -4,10 +4,9 @@  #include <string>  #include <vector> -void get_bit_fields(int address, int *tag, int *index, int *offset) +void get_cache_fields(int address, int *tag, int *index, int *offset)  { -	*tag = GET_MID_BITS( -		address, L1_CACHE_LINE_SPEC + LINE_SPEC, MEM_WORD_SPEC); +	*tag = GET_MID_BITS(address, L1_CACHE_LINE_SPEC + LINE_SPEC, MEM_WORD_SPEC);  	*index = GET_MID_BITS(address, LINE_SPEC, L1_CACHE_LINE_SPEC + LINE_SPEC);  	*offset = GET_LS_BITS(address, LINE_SPEC);  } @@ -28,8 +27,9 @@ const std::string string_format(const char *const zcFormat, ...)  	return std::string(zc.data(), iLen);  } -int wrap_address(int address) { -	if (address < 0){ +int wrap_address(int address) +{ +	if (address < 0) {  		return ((address % MEM_WORDS) + MEM_WORDS) % MEM_WORDS;  	}  	return address % MEM_WORDS; diff --git a/tests/id.cc b/tests/id.cc new file mode 100644 index 0000000..d9c1701 --- /dev/null +++ b/tests/id.cc @@ -0,0 +1,251 @@ +#include "id.h" +#include "cache.h" +#include "controller.h" +#include "dram.h" +#include "if.h" +#include "instr.h" +#include "instrDTO.h" +#include <catch2/catch_test_macros.hpp> + +class IDFixture +{ +  public: +	IDFixture() +	{ +		Dram *dr; + +		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); +	}; +	~IDFixture() +	{ +		delete this->ct; +		delete this->c; +	}; +	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; +	} +	Cache *c; +	ID *d; +	Controller *ct; +}; + +TEST_CASE_METHOD(IDFixture, "Parse invalid type", "[id]") +{ +	signed int s1 = 0, s2 = 0, s3 = 0; +	Mnemonic m; + +	s1 = 0xFFFFFFFF; +	this->d->get_instr_fields(s1, s2, s3, m); +	CHECK(m == NOP); +} + +TEST_CASE_METHOD(IDFixture, "Parse arbitrary r-type # one", "[id]") +{ +	signed int s1 = -1, s2 = -1, s3 = -1; +	Mnemonic m; + +	s1 = this->encode_R_type(0b0, 0b1, 0b10, 0b11, 0b0); +	this->d->get_instr_fields(s1, s2, s3, m); + +	CHECK(s1 == 0x00000000); // registers are empty +	CHECK(s2 == 0x00000000); +	CHECK(s3 == 0x00000000); +	CHECK(m == MUL); +} + +TEST_CASE_METHOD(IDFixture, "Parse arbitrary r-type # two", "[id]") +{ +	signed int s1 = -1, s2 = -1, s3 = -1; +	Mnemonic m; + +	s1 = this->encode_R_type(0b10000, 0b01000, 0b00100, 0b10, 0b0); +	this->d->get_instr_fields(s1, s2, s3, m); + +	CHECK(s1 == 0x00000000); // registers are empty +	CHECK(s2 == 0b00000000); +	CHECK(s3 == 0b00000000); +	CHECK(m == SUB); +} + +TEST_CASE_METHOD(IDFixture, "Parse arbitrary i-type # one", "[id]") +{ +	signed int s1 = -1, s2 = -1, s3 = -1; +	Mnemonic m; + +	s1 = this->encode_I_type(0xF, 0b1, 0b10, 0b0111, 0b1); +	this->d->get_instr_fields(s1, s2, s3, m); + +	CHECK(s1 == 0x00000000); // registers are empty +	CHECK(s2 == 0x00000000); +	CHECK(s3 == 0xF); +	CHECK(m == SFTLI); +} + +TEST_CASE_METHOD(IDFixture, "Parse arbitrary i-type # two", "[id]") +{ +	signed int s1 = -1, s2 = -1, s3 = -1; +	Mnemonic m; + +	s1 = this->encode_I_type(0xCC, 0b010, 0b101, 0b1011, 0b1); +	this->d->get_instr_fields(s1, s2, s3, m); + +	CHECK(s1 == 0x00000000); // registers are empty +	CHECK(s2 == 0x00000000); +	CHECK(s3 == 0xCC); +	CHECK(m == STORE); +} + +TEST_CASE_METHOD(IDFixture, "Parse arbitrary j-type # one", "[id]") +{ +	signed int s1 = -1, s2 = -1, s3 = -1; +	Mnemonic m; + +	s1 = this->encode_J_type(0x3456, 0b10101, 0b0111, 0b10); +	this->d->get_instr_fields(s1, s2, s3, m); + +	CHECK(s1 == 0x00000000); // registers are empty +	CHECK(s2 == 0x3456); +	CHECK(m == BOF); +	// behavior does nothing +	CHECK(s3 == -1); +} + +TEST_CASE_METHOD(IDFixture, "Parse arbitrary j-type # two", "[id]") +{ +	signed int s1 = -1, s2 = -1, s3 = -1; +	Mnemonic m; + +	s1 = this->encode_J_type(0xBBCCF, 0b10101, 0b0011, 0b10); +	this->d->get_instr_fields(s1, s2, s3, m); + +	CHECK(s1 == 0x00000000); // registers are empty +	CHECK(s2 == 0xBBCCF); +	CHECK(m == JAL); +	// behavior does nothing +	CHECK(s3 == -1); +} + +TEST_CASE_METHOD(IDFixture, "read does not conflict with read", "[id]") +{ +	signed int v; +	Response r; + +	v = 0b1; +	r = this->d->read_guard(v); +	CHECK(v == 0b0); +	REQUIRE(r == OK); + +	v = 0b1; +	this->d->read_guard(v); +	REQUIRE(v == 0b0); +} + +TEST_CASE_METHOD(IDFixture, "write does not conflict with write", "[id]") +{ +	signed int v; + +	v = 0b1; +	this->d->write_guard(v); +	REQUIRE(v == 0b0); + +	v = 0b1; +	this->d->write_guard(v); +	REQUIRE(v == 0b0); +} + +TEST_CASE_METHOD(IDFixture, "write does not conflict with read", "[id]") +{ +	signed int v; +	Response r; + +	v = 0b1; +	r = this->d->read_guard(v); +	CHECK(v == 0b0); +	REQUIRE(r == OK); + +	v = 0b1; +	this->d->write_guard(v); +	REQUIRE(v == 0b0); +} + +TEST_CASE_METHOD(IDFixture, "read does conflict with write", "[id]") +{ +	signed int v; +	Response r; + +	v = 0b1; +	this->d->write_guard(v); +	REQUIRE(v == 0b0); + +	v = 0b1; +	r = this->d->read_guard(v); +	CHECK(v == 0b01); +	REQUIRE(r == BLOCKED); +} + +TEST_CASE_METHOD(IDFixture, "stores indefinite conflicts", "[id]") +{ +	signed int v, ov; +	Response r; + +	v = 0b0; +	ov = v; +	while (v < 0b110) { +		this->d->write_guard(v); +		REQUIRE(v == 0b0); +		v = ++ov; +	} +	this->d->write_guard(v); +	REQUIRE(v == 0b0); + +	v = 0b110; +	r = this->d->read_guard(v); +	CHECK(v == 0b110); +	REQUIRE(r == BLOCKED); + +	v = 0b0; +	r = this->d->read_guard(v); +	CHECK(v == 0b0); +	REQUIRE(r == BLOCKED); +} diff --git a/tests/if.cc b/tests/if.cc index 5c1b645..bb25afa 100644 --- a/tests/if.cc +++ b/tests/if.cc @@ -36,9 +36,9 @@ class IFPipeFixture  		Response r;  		for (i = 0; i < this->m_delay + 1; ++i) { -			r = this->ct->advance(instr); +			r = this->ct->advance(instr, OK);  			// check response -			CHECK(r == BLOCKED); +			CHECK(r == STALLED);  		}  		this->fetch_cache(instr);  	} @@ -52,11 +52,11 @@ class IFPipeFixture  		Response r;  		for (i = 0; i < this->c_delay; ++i) { -			r = this->ct->advance(instr); +			r = this->ct->advance(instr, OK);  			// check response -			CHECK(r == WAIT); +			CHECK(r == STALLED);  		} -		r = this->ct->advance(instr); +		r = this->ct->advance(instr, OK);  		// check response  		CHECK(r == OK);  	} @@ -77,7 +77,7 @@ TEST_CASE_METHOD(IFPipeFixture, "fetch returns single instuction", "[if_pipe]")  	expected_cycles = this->m_delay + this->c_delay + 2;  	this->fetch_through(instr); -	CHECK(instr.get_if_cycle() == expected_cycles); +	CHECK(instr.get_time_of(FETCH) == expected_cycles);  	REQUIRE(instr.get_instr_bits() == this->p[0]);  } @@ -89,12 +89,42 @@ TEST_CASE_METHOD(IFPipeFixture, "fetch returns two instuctions", "[if_pipe]")  	expected_cycles = this->m_delay + this->c_delay + 2;  	this->fetch_through(instr); -	CHECK(instr.get_if_cycle() == expected_cycles); +	CHECK(instr.get_time_of(FETCH) == expected_cycles);  	REQUIRE(instr.get_instr_bits() == this->p[0]);  	expected_cycles += this->c_delay + 1;  	this->fetch_cache(instr); -	CHECK(instr.get_if_cycle() == expected_cycles); +	CHECK(instr.get_time_of(FETCH) == expected_cycles);  	REQUIRE(instr.get_instr_bits() == this->p[1]);  } + +TEST_CASE_METHOD(IFPipeFixture, "fetch waits with old instruction", "[if_pipe]") +{ +	Response r; +	InstrDTO instr; +	int i, expected_cycles, fetch_cycles; + +	fetch_cycles = this->m_delay + this->c_delay + 2; +	expected_cycles = this->m_delay + (this->c_delay * 2) + 1; + +	for (i = 0; i < this->m_delay + 1; ++i) { +		r = this->ct->advance(instr, BLOCKED); +		// check response +		CHECK(r == STALLED); +	} +	for (i = 0; i < this->c_delay; ++i) { +		r = this->ct->advance(instr, BLOCKED); +		// check response +		CHECK(r == STALLED); +	} +	for (i = 0; i < expected_cycles - fetch_cycles; ++i) { +		r = this->ct->advance(instr, BLOCKED); +		// check response +		CHECK(r == OK); +	} + +	r = this->ct->advance(instr, OK); +	CHECK(instr.get_time_of(FETCH) == expected_cycles); +	REQUIRE(instr.get_instr_bits() == this->p[0]); +} diff --git a/tests/utils.cc b/tests/utils.cc index ea1a1ed..2e0e934 100644 --- a/tests/utils.cc +++ b/tests/utils.cc @@ -2,21 +2,21 @@  #include "definitions.h"  #include <catch2/catch_test_macros.hpp> -TEST_CASE("Parse arbitrary fields # one", "[cache]") +TEST_CASE("Parse arbitrary fields # one", "[utils]")  {  	int tag, index, offset;  	int address = 0b0001010101; -	get_bit_fields(address, &tag, &index, &offset); +	get_cache_fields(address, &tag, &index, &offset);  	CHECK(tag == 0b000);  	CHECK(index == 0b10101);  	CHECK(offset == 0b01);  } -TEST_CASE("Parse arbitrary fields # two", "[cache]") +TEST_CASE("Parse arbitrary fields # two", "[utils]")  {  	int tag, index, offset;  	int address = 0b0100111011; -	get_bit_fields(address, &tag, &index, &offset); +	get_cache_fields(address, &tag, &index, &offset);  	CHECK(tag == 0b010);  	CHECK(index == 0b01110);  	CHECK(offset == 0b11);  | 
