summaryrefslogtreecommitdiff
path: root/inc/stage.h
blob: 4e0c252ca706209b24daaa84965b106835575eed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
// Simulator for the RISC-V[ECTOR] mini-ISA
// Copyright (C) 2025 Siddarth Suresh
// Copyright (C) 2025 bdunahu

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#ifndef STAGE_H
#define STAGE_H
#include "instrDTO.h"
#include "pipe_spec.h"
#include "response.h"
#include "storage.h"
#include <array>
#include <deque>
#include <memory>

enum CC {
	GT,
	EQ,
	UF,
	OF,
};

class Stage
{
  public:
	/**
	 * Constructor.
	 * @param The next stage in the pipeline.
	 * @return A newly allocated stage object.
	 */
	Stage(Stage *next);
	virtual ~Stage();
	/**
	 * Advances this stage by a single clock cycle.
	 * A boilerplate version is provided in stage.cc.
	 *
	 * @param a response, indicating whether or not the parent pipe stage is
	 * ready to accept a new instruction object next cycle.
	 * @return a DTO object containing the next instruction to be processed.
	 *
	 * Must set the status to READY when the current instruction is evicted..
	 */
	virtual InstrDTO *advance(Response p);
	/**
	 * @return the current instruction.
	 */
	InstrDTO *get_instr();
	/**
	 * Squashes the pipeline.
	 */
	void squash();

	/* The following methods are made public so that they may be tested, and are
	 * not to be called from outside classes during standard execution.
	 */

	/**
	 * Gets the bit in the condition code register correspondng to `c`.
	 * @param the condition code to retrieve,
	 */
	bool get_condition(CC c);
	/**
	 * Sets the bit in the condition code register corresponding to `c`.
	 * @param the condition code to set.
	 * @param the truthy value to set it to.
	 */
	void set_condition(CC c, bool v);

	/**
	 * The set of registers currently checked out.
	 */
	static std::deque<signed int> checked_out;

	bool is_vector_type(Mnemonic m);

	bool is_logical(Mnemonic m);

  protected:
	/**
	 * The function expected to do the majority of the work.
	 *
	 * Must set the status to OK when an operation is done.
	 * Must set the status to STALLED when an operation cannot be completed the
	 * current cycle.
	 */
	virtual void advance_helper() = 0;
	/**
	 * 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);
	/**
	 * Stores `d` into the register indexed `v`.
	 * @param the register number.
	 * @param the value to store.
	 */
	template <typename T>
	void store_register(signed int v, T d)
	{
		if constexpr (std::is_same_v<T, signed int>) {
			if (v < 0 || v >= GPR_NUM) {
				throw std::out_of_range("Invalid GPR index for storing scalar");
			}
			gprs[v] = d;
		}
		else if constexpr (std::is_same_v<T, std::array<signed int, V_R_LIMIT>>) {
			if (v < GPR_NUM || v >= GPR_NUM + V_NUM) {
				throw std::out_of_range("Invalid VR index for storing vector");
			}
			vrs[v % GPR_NUM] = d;
		}
	}
	/**
	 * Returns the value of the register corresponding to `v`.
	 * @param the register number.
	 * @return the value in the associated register.
	 */
	template <typename T>
	T dereference_register(signed int v)
	{
		if constexpr (std::is_same_v<T, signed int>) {
			if (v < 0 || v >= GPR_NUM) {
				throw std::out_of_range("Invalid GPR index");
			}
			return gprs[v];
		}
		else if constexpr (std::is_same_v<T, std::array<signed int, V_R_LIMIT>>) {
			if (v < GPR_NUM || v >= GPR_NUM + V_NUM) {
				throw std::out_of_range("Invalid vector register index");
			}
			return vrs[v % GPR_NUM];
		}
	}
	/**
	 * The shared pool of general-purpose integer registers.
	 */
	static std::array<signed int, GPR_NUM> gprs;
	/**
	 * The shared pool of general-purpose vector registers.
	 */
	static std::array<std::array<signed int, V_R_LIMIT>, V_NUM> vrs;
	/**
	 * The address of the currently executing instruction.
	 */
	static unsigned int pc;
	/**
	 * A pointer to the top-level storage device.
	 */
	static Storage *storage;
	/**
	 * A flag indicating whether pipelining should be used.
	 */
	static bool is_pipelined;
	/**
	 * A flag which tells fetch when the pipe is empty. Only used when the pipe
	 * is turned off.
	 */
	static bool is_empty;
	/**
	 * The current clock cycle.
	 */
	static int clock_cycle;
	/**
	 * A pointer to the next stage in the pipeline.
	 */
	Stage *next;
	/**
	 * A pointer to the current instruction this stage is processing.
	 */
	InstrDTO *curr_instr;
	/**
	 * The current status of this stage.
	 */
	Response status;
};

#endif /* STAGE_H_INCLUDED */