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
|
#include <algorithm>
#include <deque>
#include <iostream>
#include <optional>
#include <set>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "uarch/exec.h"
#include "util/assert.h"
namespace uarch {
ExecStage::ExecStage(sim::Scheduler &scheduler, sim::Queue<Uop> &execq, sim::Queue<FillReq> &fillreqq, sim::Queue<Fill> &fillq, sim::Queue<Store> &storeq)
: sim::Schedulable(scheduler)
, execq(execq)
, fillreqq(fillreqq)
, fillq(fillq)
, storeq(storeq)
{
execq.add_reader(this);
fillreqq.add_writer(this);
fillq.add_reader(this);
storeq.add_writer(this);
}
void ExecStage::clock()
{
if (tasks.empty() && !step && execq.available()) {
auto uop = execq.read();
std::cout << "exec accepts uop " << uop.top_task().value()->disasm() << "\n";
aisa::TaskStack::operator=(std::move(uop));
aisa::VectorRF::operator=(std::move(uop));
}
bool sent_store = false;
while (!sent_store) {
while (!step) {
auto t = top_task();
if (!t)
goto no_work;
const auto &task = **t;
auto s = task.step(load_reg(task.environment).value());
if (s.has_value()) {
step = std::move(s->first);
std::cout << "exec step " << step->disasm() << "\n";
wires = {};
outstanding_fill = false;
fill_complete = false;
ASSERT(store_reg(task.environment, s->second), "Could not write next environment value");
} else {
pop_task();
if (tasks.empty())
std::cout << "exec completes uop\n";
}
}
if (step->predicate.has_value()) {
auto x = load_reg(step->predicate->first);
if (x.has_value()) {
if (*x != step->predicate->second)
goto nextstep;
} else {
std::cout << "exec stalls on predicate register";
break;
}
}
wires.source_vals.reserve(step->source_regs.size());
for (unsigned int i = wires.source_vals.size(); i < step->source_regs.size(); ++i) {
auto x = load_reg(step->source_regs[i]);
if (!x.has_value()) {
std::cout << "exec stalls on source register";
break;
}
wires.source_vals.emplace_back(*x);
}
if (step->mop == aisa::MOp::LOAD && !fill_complete) {
auto mi = step->meminfo(wires);
if (outstanding_fill) {
for (unsigned int i = 0; i < fillq.available(); ++i) {
auto &fill = fillq.peek(i);
if (fill.physical_addr == mi.physical_addr) {
std::cout << "exec fills\n";
wires.memory_val = std::move(fill.bytes);
outstanding_fill = false;
fill_complete = true;
}
}
if (!fill_complete)
break;
} else {
std::cout << "exec sends fill request\n";
FillReq req;
req.fillq = &fillq;
req.physical_addr = mi.physical_addr;
req.size = mi.size;
fillreqq.write(std::move(req));
outstanding_fill = true;
fill_complete = false;
break;
}
}
step->evaluate(wires);
for (unsigned int i = 0; i < step->destination_regs.size(); ++i)
ASSERT(store_reg(step->destination_regs[i], wires.destination_vals[i]), "Could not write destination register");
if (step->mop == aisa::MOp::STORE) {
std::cout << "exec sends store\n";
auto mi = step->meminfo(wires);
Store store;
store.physical_addr = mi.physical_addr;
store.bytes = std::move(wires.memory_val);
storeq.write(std::move(store));
//sent_store = true;
}
nextstep:
step = nullptr;
wires = {};
outstanding_fill = false;
fill_complete = false;
}
no_work:
while (fillq.available()) {
std::cout << "exec consumes fill\n";
fillq.read();
}
}
}
|