-
Notifications
You must be signed in to change notification settings - Fork 0
/
SimpleBranchPredictor.cpp
215 lines (189 loc) · 7.28 KB
/
SimpleBranchPredictor.cpp
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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#include "../include/units/SimpleBranchPredictor.h"
#include "../include/exceptions.h"
#include "../include/hw/Memory.h"
#include "../include/hw/RegisterFile.h"
#include "../include/instructions/BType.h"
#include "../include/instructions/IType.h"
#include "../include/instructions/JType.h"
SimpleBranchPredictor::SimpleBranchPredictor(Memory* memory, ushort XLEN, RegisterFile* registerFile, bytes initialPC) {
if (initialPC.size() != XLEN) {
throw BranchPredictorException("Initial PC invalid, not size of XLEN [%s]", getBytesForPrint(initialPC).c_str());
}
ulong pcVal = getBytesToULong(initialPC);
if (pcVal >= memory->getSize()) {
throw AddressOutOfMemoryException(pcVal, 4, memory->getSize(), true, "Initial PC invalid, points to out of memory");
}
if (initialPC[0] % 4 != 0) {
throw AddressMisalignedException(initialPC, bytes{0}, bytes{0}, "Initial PC invalid, not 4 byte aligned");
}
this->memory = memory;
this->XLEN = XLEN;
this->registerFile = registerFile;
this->PCQueue.push(initialPC);
this->workloop = thread (&SimpleBranchPredictor::predictionWorkloop, this);
// this->workloop->detach();
}
// SimpleBranchPredictor::SimpleBranchPredictor(SimpleBranchPredictor&& obj): workloop(move(obj.workloop)) {};
// SimpleBranchPredictor& SimpleBranchPredictor::operator=(SimpleBranchPredictor&& obj) {
// if (workloop->joinable()) {
// workloop->join();
// }
// workloop = move(obj.workloop);
// return *this;
// }
bytes SimpleBranchPredictor::getNextPC() {
lock_guard<mutex> lck(lock);
if (this->workloop.joinable()) this->workloop.join();
// delete(this->workloop);
// Check that no exceptions have been thrown by the thread
if (this->workloopExceptionPtr) {
rethrow_exception(this->workloopExceptionPtr);
}
// Check that the queue size isnt zero
if (this->PCQueue.size() < 2) {
// TODO: decide what to do here
}
if (this->executingQueue.size() > 2) {
this->executingQueue.pop();
}
// Get the next PC from the queue
bytes nextPC = this->PCQueue.front();
// Drop the front of the queue
this->PCQueue.pop();
// Add this to the queue of waiting for executionlock_guard
this->executingQueue.push(PCQueue.front());
// Start new thread to fetch new PC
this->workloop = thread (&SimpleBranchPredictor::predictionWorkloop, this);
// this->workloop->detach();
return nextPC;
}
bool SimpleBranchPredictor::checkPrediction(bytes pc, bytes address) {
lock_guard<mutex> lck(lock);
if (this->workloop.joinable()) this->workloop.join();
if (address.size() == 0) {
throw BranchPredictorException("Provided address to check prediction is zero bytes in length");
}
if (this->executingQueue.size() == 0) {
throw BranchPredictorException("Failed to fetch prediction for next PC, executing queue empty");
}
// Get the first most value in the queue, this should be next instruction
// to execute as predicted by the bp
bytes nextPredicted = this->executingQueue.front();
// Pop this value off now we are dealing with it
this->executingQueue.pop();
// Verify if we predicted correctly
if (nextPredicted == address) {
return true;
}
// Fix the mistake here
// Cancel any more prediction
this->failedPrediction = true;
// this->workloop.join();
// delete(this->workloop);
while (!this->PCQueue.empty()) {
this->PCQueue.pop();
}
this->PCQueue.push(address);
// Start fetching again
this->failedPrediction = false;
this->workloop = thread(&SimpleBranchPredictor::predictionWorkloop, this);
// this->workloop->detach();
return false;
}
void SimpleBranchPredictor::predictionWorkloop() {
// lock_guard<mutex> lck(lock);
bool exception = false;
try {
// Check that the initial size of the queue isnt zero
// The queue should have at least one value in it
if (this->PCQueue.size() == 0) {
throw BranchPredictorException("Failed to start prediction, queue length 0\n");
}
if (this->PCQueue.back()[0] % 4 != 0) {
throw BranchPredictorException("Failed to start prediction, inital PC not 4-bytes aligned\n");
}
} catch (...) {
// Pass the current exception to the exception ptr;
this->workloopExceptionPtr = current_exception();
exception = true;
}
while (!exception && !this->isProcessorExceptionGenerated && !this->failedPrediction && this->PCQueue.size() < this->queueSize) {
bytes nextPC;
try {
// TODO: Handle address-misaligned exceptions
bytes lastPC = this->PCQueue.back();
// cout << "lpc: " << getBytesForPrint(lastPC) << "\n";
bytes instruction = this->memory->readWord(getBytesToULong(lastPC));
// cout << "inst: " << getBytesForPrint(instruction) << "\n";
byte opcode = getContrainedBits(instruction, 0, 6)[0];
if (opcode == 99) {
// If opcode = beq | bne | blt | bge | bltu | bgeu
// then we need to check if the sign bit of the imm is negative,
// if so take PC - imm else PC + 4
// Uses B-Type
BTypeInstruction b = BTypeInstruction(XLEN);
b.decode(instruction);
bytes imm = b.AbstractInstruction::getImm();
nextPC = bytesAddSignedToPC(lastPC, imm);
if (instruction[3] >> 7 != 1 || nextPC[0] % 4 != 0) {
nextPC = addByteToBytes(lastPC, 4);
}
} /* else if (opcode == 103) {
// Opcode = JALR
// Uses I-Type
// TODO: Slight issue, we need to know if rs1 depends on another rd
ITypeInstruction i = ITypeInstruction(XLEN);
i.decode(instruction);
bytes imm = i.AbstractInstruction::getImm();
bytes rs1Val = this->registerFile->get((ushort)i.getRS1());
nextPC = bytesAddSignedToPC(rs1Val, imm);
} */ else if (opcode == 111) {
// opcode - JAL
// Uses J-Type
JTypeInstruction j = JTypeInstruction(XLEN);
j.decode(instruction);
bytes imm = j.AbstractInstruction::getImm();
nextPC = bytesAddSignedToPC(lastPC, imm);
} else {
nextPC = addByteToBytes(lastPC, 4);
}
ulong pcVal = getBytesToULong(nextPC);
if (pcVal >= this->memory->getSize()) {
throw AddressOutOfMemoryException(pcVal, 4, this->memory->getSize(), true);
}
if (nextPC[0] % 4 != 0) {
throw BranchPredictorException("Generated PC not 4 bytes aligned [%s]", getBytesForPrint(nextPC).c_str());
}
} catch (...) {
this->workloopExceptionPtr = current_exception();
exception = true;
}
// Push the calculated to the queue so long as
// No exception generated
// No public exception generated
// No failed prediction
if (!exception && !this->isProcessorExceptionGenerated && !this->failedPrediction) {
// Ensure we dont push a value to the queue while the other thread is removing a value
this->PCQueue.push(nextPC);
}
}
}
bytes SimpleBranchPredictor::peak() {
lock_guard<mutex> lck(lock);
return this->PCQueue.front();
}
void SimpleBranchPredictor::handleFlush() {
if (this->executingQueue.size() > 2) {
this->executingQueue.pop();
}
// Keep the executingQueue in line with what it should be
// by using the same PC as a nop uses
this->executingQueue.push(bytes(0));
}
SimpleBranchPredictor::~SimpleBranchPredictor() {
this->isProcessorExceptionGenerated = true;
if (workloop.joinable()) {
this->workloop.join();
}
// delete(this->workloop);
}