summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Blake Kongslie2022-07-02 13:45:09 -0700
committerJulian Blake Kongslie2022-07-02 13:45:09 -0700
commitc72951a36d6cb9775dc1ecd9bc26bc13e796f10c (patch)
tree5a8fe196beba5c7c674d1b3d627c9a0beac849f5
parentTrivial code reorg. (diff)
downloadissim-c72951a36d6cb9775dc1ecd9bc26bc13e796f10c.tar.xz
Dropping the async interface, and adding some real uarch.
Diffstat (limited to '')
-rw-r--r--Makefile3
-rw-r--r--aisa/aisa.cpp6
-rw-r--r--aisa/aisa.h20
-rw-r--r--aisa/async.h225
-rw-r--r--aisa/coroutine.h119
-rw-r--r--aisa/simple-models.cpp49
-rw-r--r--aisa/simple-models.h39
-rw-r--r--fib/fib.cpp51
-rw-r--r--fib/fib.h1
-rw-r--r--main.cpp116
-rw-r--r--sim/queue.h87
-rw-r--r--sim/sim.cpp83
-rw-r--r--sim/sim.d1
-rw-r--r--sim/sim.h44
-rw-r--r--uarch/exec.cpp138
-rw-r--r--uarch/exec.h29
-rw-r--r--uarch/fetch.cpp129
-rw-r--r--uarch/fetch.h28
-rw-r--r--uarch/memory.cpp46
-rw-r--r--uarch/memory.h21
-rw-r--r--uarch/types.h30
-rw-r--r--uarch/uarch.d2
-rw-r--r--util/assert.cpp12
-rw-r--r--util/assert.h3
24 files changed, 777 insertions, 505 deletions
diff --git a/Makefile b/Makefile
index 0d154a8..f8e983b 100644
--- a/Makefile
+++ b/Makefile
@@ -85,11 +85,10 @@ $(foreach part,${PARTS},$(eval $(call mklib,${part})))
85 85
86ifdef iwyu 86ifdef iwyu
87 87
88build/iwyu.cpp: 88build/iwyu.cpp: $(wildcard *.h)
89 @mkdir -p $(dir $@) 89 @mkdir -p $(dir $@)
90 @tools/iwyu-header $(wildcard *.h) > $@ 90 @tools/iwyu-header $(wildcard *.h) > $@
91 @$(call iwyu,$@) 91 @$(call iwyu,$@)
92.PHONY: build/iwyu.cpp
93 92
94IWYU_CPPS += build/iwyu.cpp 93IWYU_CPPS += build/iwyu.cpp
95 94
diff --git a/aisa/aisa.cpp b/aisa/aisa.cpp
index 811c882..8c8cd1e 100644
--- a/aisa/aisa.cpp
+++ b/aisa/aisa.cpp
@@ -6,6 +6,12 @@
6 6
7namespace aisa { 7namespace aisa {
8 8
9 ISA::~ISA() { }
10
11 Task::~Task() { }
12
13 Step::~Step() { }
14
9 std::optional<std::pair<std::unique_ptr<const Step>, regval_t>> Task::step(regval_t environment_val) const { return {}; } 15 std::optional<std::pair<std::unique_ptr<const Step>, regval_t>> Task::step(regval_t environment_val) const { return {}; }
10 16
11 MemInfo Step::meminfo(const Wires &wires) const { return {}; } 17 MemInfo Step::meminfo(const Wires &wires) const { return {}; }
diff --git a/aisa/aisa.h b/aisa/aisa.h
index e4e1074..a986cd7 100644
--- a/aisa/aisa.h
+++ b/aisa/aisa.h
@@ -17,7 +17,7 @@ namespace aisa {
17 struct Task; 17 struct Task;
18 18
19 struct ISA { 19 struct ISA {
20 virtual ~ISA() { } 20 virtual ~ISA();
21 21
22 virtual std::pair<std::unique_ptr<const Task>, regval_t> initial_task() const = 0; 22 virtual std::pair<std::unique_ptr<const Task>, regval_t> initial_task() const = 0;
23 }; 23 };
@@ -27,7 +27,7 @@ namespace aisa {
27 struct Task { 27 struct Task {
28 regnum_t environment; 28 regnum_t environment;
29 29
30 virtual ~Task() { } 30 virtual ~Task();
31 31
32 virtual std::string disasm() const = 0; 32 virtual std::string disasm() const = 0;
33 33
@@ -58,24 +58,10 @@ namespace aisa {
58 std::vector<regnum_t> source_regs; 58 std::vector<regnum_t> source_regs;
59 std::vector<regnum_t> destination_regs; 59 std::vector<regnum_t> destination_regs;
60 60
61 virtual ~Step() { } 61 virtual ~Step();
62 62
63 virtual std::string disasm(const Wires *w = nullptr) const = 0; 63 virtual std::string disasm(const Wires *w = nullptr) const = 0;
64 64
65 std::optional<regnum_t> predicate_reg() const
66 {
67 if (predicate.has_value())
68 return predicate->first;
69 return {};
70 }
71
72 std::optional<regnum_t> expected_predicate_val() const
73 {
74 if (predicate.has_value())
75 return predicate->second;
76 return {};
77 }
78
79 MOp mop = MOp::NONE; 65 MOp mop = MOp::NONE;
80 66
81 virtual MemInfo meminfo(const Wires &wires) const; 67 virtual MemInfo meminfo(const Wires &wires) const;
diff --git a/aisa/async.h b/aisa/async.h
deleted file mode 100644
index 42e99e7..0000000
--- a/aisa/async.h
+++ /dev/null
@@ -1,225 +0,0 @@
1#pragma once
2
3#include <coroutine>
4#include <optional>
5#include <utility>
6#include <memory>
7#include <vector>
8
9#include "aisa/aisa.h"
10#include "aisa/coroutine.h" // IWYU pragma: export
11
12namespace aisa {
13
14 template<typename CRTP> struct AsyncEval {
15 CRTP & crtp() noexcept { return static_cast<CRTP &>(*this); }
16
17 task<regval_t> async_load_reg(regnum_t rn)
18 {
19 while (true) {
20 if (auto rv = crtp().load_reg(rn); rv.has_value())
21 co_return *rv;
22 co_await std::suspend_always{};
23 }
24 }
25
26 task<void> async_store_reg(regnum_t rn, regval_t rv)
27 {
28 while (true) {
29 if (crtp().store_reg(rn, rv))
30 co_return;
31 co_await std::suspend_always{};
32 }
33 }
34
35 task<void> async_fetch_mem(byte_t *bytes, addr_t physical_addr, addr_t size)
36 {
37 while (true) {
38 if (crtp().fetch_mem(bytes, physical_addr, size))
39 co_return;
40 co_await std::suspend_always{};
41 }
42 }
43
44 task<void> async_store_mem(addr_t physical_addr, const byte_t *bytes, addr_t size)
45 {
46 while (true) {
47 if (crtp().store_mem(physical_addr, bytes, size))
48 co_return;
49 co_await std::suspend_always{};
50 }
51 }
52
53 task<bool> async_predicate(const Step &step)
54 {
55 if (step.predicate.has_value()) {
56 regval_t pval = co_await crtp().async_load_reg(step.predicate->first);
57 co_return pval == step.predicate->second;
58 }
59 co_return true;
60 }
61
62 task<void> async_load_source_registers(const Step &step, Wires &w)
63 {
64 w.source_vals.resize(step.source_regs.size());
65 for (unsigned int i = 0; i < step.source_regs.size(); ++i)
66 w.source_vals[i] = co_await crtp().async_load_reg(step.source_regs[i]);
67 }
68
69 task<void> async_load_source_memory(const Step &step, const MemInfo &mi, Wires &w)
70 {
71 w.memory_val.resize(mi.size);
72 co_await crtp().async_fetch_mem(w.memory_val.data(), mi.physical_addr, mi.size);
73 }
74
75 task<void> async_load_sources(const Step &step, Wires &w)
76 {
77 co_await crtp().async_load_source_registers(step, w);
78
79 if (step.mop == MOp::LOAD) {
80 auto mi = step.meminfo(w);
81 co_await crtp().async_load_source_memory(step, mi, w);
82 }
83 }
84
85 task<void> async_write_destination_registers(const Step &step, const Wires &w)
86 {
87 for (unsigned int i = 0; i < step.destination_regs.size(); ++i)
88 co_await crtp().async_store_reg(step.destination_regs[i], w.destination_vals[i]);
89 }
90
91 task<void> async_write_destination_memory(const Step &step, const MemInfo &mi, const Wires &w)
92 {
93 co_await crtp().async_store_mem(mi.physical_addr, w.memory_val.data(), mi.size);
94 }
95
96 task<void> async_write_destinations(const Step &step, const Wires &w)
97 {
98 if (w.aborted)
99 co_return;
100
101 co_await crtp().async_write_destination_registers(step, w);
102
103 if (step.mop == MOp::STORE) {
104 auto mi = step.meminfo(w);
105 co_await crtp().async_write_destination_memory(step, mi, w);
106 }
107 }
108
109 task<void> async_push_task(std::unique_ptr<const Task> &&task)
110 {
111 while (true) {
112 if (crtp().push_task(std::move(task)))
113 co_return;
114 co_await std::suspend_always{};
115 }
116 }
117
118 task<void> async_new_task(std::unique_ptr<const Task> &&task, regval_t environment_val)
119 {
120 auto rn = task->environment;
121 co_await crtp().async_push_task(std::move(task));
122 co_await crtp().async_store_reg(rn, environment_val);
123 }
124
125 task<Wires> async_run_step(const Step &step)
126 {
127 Wires w;
128
129 if (!co_await crtp().async_predicate(step))
130 co_return std::move(w);
131
132 co_await crtp().async_load_sources(step, w);
133
134 step.evaluate(w);
135
136 co_await crtp().async_write_destinations(step, w);
137
138 if (w.new_task.has_value())
139 co_await crtp().async_new_task(std::move(w.new_task->first), w.new_task->second);
140
141 co_return std::move(w);
142 }
143
144 task<const Task *> async_top_task()
145 {
146 while (true) {
147 if (auto rv = crtp().top_task(); rv.has_value())
148 co_return &**rv;
149 co_await std::suspend_always{};
150 }
151 }
152
153 task<void> async_pop_task()
154 {
155 while (true) {
156 if (crtp().pop_task())
157 co_return;
158 co_await std::suspend_always{};
159 }
160 }
161
162 task<std::optional<std::unique_ptr<const Step>>> async_fetch_step()
163 {
164 auto task = co_await crtp().async_top_task();
165
166 auto rn = task->environment;
167 auto rv = co_await crtp().async_load_reg(rn);
168
169 auto step = task->step(rv);
170 if (step.has_value()) {
171 co_await crtp().async_store_reg(rn, step->second);
172 co_return std::move(step->first);
173 } else {
174 co_await crtp().async_pop_task();
175 co_return {};
176 }
177 }
178
179 task<std::pair<std::unique_ptr<const Step>, Wires>> async_fetch_and_run_step()
180 {
181 while (true) {
182 if (auto step = co_await crtp().async_fetch_step(); step.has_value()) {
183 auto wires = co_await crtp().async_run_step(**step);
184 co_return {std::move(*step), std::move(wires)};
185 }
186 }
187 }
188
189 task<void> async_run_subtree()
190 {
191 while (true) {
192 if (auto step = co_await crtp().async_fetch_step(); step.has_value())
193 co_await crtp().async_run_step_and_subtree(**step);
194 else
195 break;
196 }
197 }
198
199 task<Wires> async_run_step_and_subtree(const Step &step)
200 {
201 auto w = co_await crtp().async_run_step(step);
202
203 if (w.new_task.has_value())
204 co_await crtp().async_run_subtree();
205
206 co_return std::move(w);
207 }
208
209 task<void> async_setup_initial_task(const ISA &isa)
210 {
211 auto task = isa.initial_task();
212
213 co_await crtp().async_new_task(std::move(task.first), task.second);
214 }
215
216 task<void> async_run(const ISA &isa)
217 {
218 co_await crtp().async_setup_initial_task(isa);
219
220 co_await crtp().async_run_subtree();
221 }
222
223 };
224
225}
diff --git a/aisa/coroutine.h b/aisa/coroutine.h
deleted file mode 100644
index 1b55362..0000000
--- a/aisa/coroutine.h
+++ /dev/null
@@ -1,119 +0,0 @@
1#pragma once
2
3#include <coroutine>
4#include <optional>
5
6namespace aisa {
7
8 template<typename result_t> struct task_promise;
9
10 template<typename result_t> struct task : public std::coroutine_handle<task_promise<result_t>> {
11 using handle = std::coroutine_handle<task_promise<result_t>>;
12 using promise_type = task_promise<result_t>;
13 bool await_ready() const noexcept { return handle::done(); }
14 result_t await_resume() const noexcept;
15 template<typename other_t> void await_suspend(std::coroutine_handle<task_promise<other_t>> h) const noexcept;
16 std::optional<result_t> operator()() noexcept;
17 };
18
19 template<> struct task<void> : public std::coroutine_handle<task_promise<void>> {
20 using handle = std::coroutine_handle<task_promise<void>>;
21 using promise_type = task_promise<void>;
22 bool await_ready() const noexcept { return handle::done(); }
23 void await_resume() const noexcept;
24 template<typename other_t> void await_suspend(std::coroutine_handle<task_promise<other_t>> h) const noexcept;
25 bool operator()() noexcept;
26 };
27
28 template<typename result_t> struct task_promise {
29 std::coroutine_handle<> precursor;
30 std::optional<result_t> result;
31 task_promise() = default;
32 task_promise(const task_promise &) = delete;
33 task<result_t> get_return_object() noexcept { return task<result_t>{std::coroutine_handle<task_promise<result_t>>::from_promise(*this)}; }
34 std::suspend_never initial_suspend() const noexcept { return {}; }
35 std::suspend_always final_suspend() const noexcept { return {}; }
36 void unhandled_exception() { }
37 void return_value(result_t x) noexcept { result = std::move(x); }
38 };
39
40 template<> struct task_promise<void> {
41 std::coroutine_handle<> precursor;
42 task_promise() = default;
43 task_promise(const task_promise &) = delete;
44 task<void> get_return_object() noexcept { return task<void>{std::coroutine_handle<task_promise<void>>::from_promise(*this)}; }
45 std::suspend_never initial_suspend() const noexcept { return {}; }
46 std::suspend_always final_suspend() const noexcept { return {}; }
47 void unhandled_exception() { }
48 void return_void() noexcept { }
49 };
50
51 template<typename result_t> result_t task<result_t>::await_resume() const noexcept
52 {
53 auto x = std::move(handle::promise().result.value());
54 handle::destroy();
55 return x;
56 }
57
58 template<typename result_t> template<typename other_t> void task<result_t>::await_suspend(std::coroutine_handle<task_promise<other_t>> h) const noexcept
59 {
60 h.promise().precursor = *this;
61 }
62
63 template<typename result_t> std::optional<result_t> task<result_t>::operator()() noexcept
64 {
65 if (!handle::operator bool())
66 return {};
67 if (!handle::done()) {
68 auto &precursor = handle::promise().precursor;
69 if (precursor) {
70 if (!precursor.done())
71 precursor.resume();
72 if (precursor.done())
73 precursor = nullptr;
74 }
75 if (!precursor)
76 handle::resume();
77 }
78 if (handle::done()) {
79 auto x = await_resume();
80 handle::operator=(nullptr);
81 return x;
82 }
83 return {};
84 }
85
86 inline void task<void>::await_resume() const noexcept
87 {
88 handle::destroy();
89 }
90
91 template<typename other_t> void task<void>::await_suspend(std::coroutine_handle<task_promise<other_t>> h) const noexcept
92 {
93 h.promise().precursor = *this;
94 }
95
96 inline bool task<void>::operator()() noexcept
97 {
98 if (!handle::operator bool())
99 return true;
100 if (!handle::done()) {
101 auto &precursor = handle::promise().precursor;
102 if (precursor) {
103 if (!precursor.done())
104 precursor.resume();
105 if (precursor.done())
106 precursor = nullptr;
107 }
108 if (!precursor)
109 handle::resume();
110 }
111 if (handle::done()) {
112 await_resume();
113 handle::operator=(nullptr);
114 return true;
115 }
116 return false;
117 }
118
119}
diff --git a/aisa/simple-models.cpp b/aisa/simple-models.cpp
new file mode 100644
index 0000000..b69dcb7
--- /dev/null
+++ b/aisa/simple-models.cpp
@@ -0,0 +1,49 @@
1#include <algorithm>
2#include <deque>
3#include <memory>
4#include <optional>
5#include <utility>
6#include <vector>
7
8#include "aisa/aisa.h"
9#include "aisa/simple-models.h"
10
11namespace aisa {
12
13 bool TaskStack::pop_task()
14 {
15 if (tasks.empty())
16 return false;
17 tasks.pop_back();
18 return true;
19 }
20
21 bool TaskStack::push_task(std::unique_ptr<const Task> &&task)
22 {
23 tasks.emplace_back(std::move(task));
24 return true;
25 }
26
27 std::optional<const Task *> TaskStack::top_task()
28 {
29 if (tasks.empty())
30 return {};
31 return tasks.back().get();
32 }
33
34 std::optional<regval_t> VectorRF::load_reg(regnum_t rn) const
35 {
36 if (rf.size() <= rn)
37 return {};
38 return rf[rn];
39 }
40
41 bool VectorRF::store_reg(regnum_t rn, regval_t rv)
42 {
43 if (rf.size() <= rn)
44 rf.resize(rn + 1);
45 rf[rn] = rv;
46 return true;
47 }
48
49}
diff --git a/aisa/simple-models.h b/aisa/simple-models.h
index 62b5388..a94f010 100644
--- a/aisa/simple-models.h
+++ b/aisa/simple-models.h
@@ -7,7 +7,6 @@
7#include <map> 7#include <map>
8#include <memory> 8#include <memory>
9#include <optional> 9#include <optional>
10#include <utility>
11#include <vector> 10#include <vector>
12 11
13#include "aisa/aisa.h" 12#include "aisa/aisa.h"
@@ -51,45 +50,17 @@ namespace aisa {
51 struct TaskStack { 50 struct TaskStack {
52 std::deque<std::unique_ptr<const Task>> tasks; 51 std::deque<std::unique_ptr<const Task>> tasks;
53 52
54 bool pop_task() 53 bool pop_task();
55 { 54 bool push_task(std::unique_ptr<const Task> &&task);
56 if (tasks.empty())
57 return false;
58 tasks.pop_back();
59 return true;
60 }
61
62 bool push_task(std::unique_ptr<const Task> &&task)
63 {
64 tasks.emplace_back(std::move(task));
65 return true;
66 }
67 55
68 std::optional<const Task *> top_task() 56 std::optional<const Task *> top_task();
69 {
70 if (tasks.empty())
71 return {};
72 return tasks.back().get();
73 }
74 }; 57 };
75 58
76 struct VectorRF { 59 struct VectorRF {
77 std::vector<std::optional<regval_t>> rf; 60 std::vector<std::optional<regval_t>> rf;
78 61
79 std::optional<regval_t> load_reg(regnum_t rn) const 62 std::optional<regval_t> load_reg(regnum_t rn) const;
80 { 63 bool store_reg(regnum_t rn, regval_t rv);
81 if (rf.size() <= rn)
82 return {};
83 return rf[rn];
84 }
85
86 bool store_reg(regnum_t rn, regval_t rv)
87 {
88 if (rf.size() <= rn)
89 rf.resize(rn + 1);
90 rf[rn] = rv;
91 return true;
92 }
93 }; 64 };
94 65
95} 66}
diff --git a/fib/fib.cpp b/fib/fib.cpp
index 8ff7718..8e0cdf2 100644
--- a/fib/fib.cpp
+++ b/fib/fib.cpp
@@ -225,12 +225,39 @@ namespace fib {
225 std::optional<std::pair<std::unique_ptr<const aisa::Step>, aisa::regval_t>> step(aisa::regval_t env) const override 225 std::optional<std::pair<std::unique_ptr<const aisa::Step>, aisa::regval_t>> step(aisa::regval_t env) const override
226 { 226 {
227 switch (env) { 227 switch (env) {
228 case 0: return {{std::make_unique<StepLoad<WORD>>(Reg::A, Reg::PC), env+1}}; 228 case 0: return {{std::make_unique<StepMove>(Reg::AREG, Reg::PC), env+1}};
229 case 1: return {{std::make_unique<StepAddImmediate>(Reg::PC, Reg::PC, 1), env+1}}; 229 case 1: return {{std::make_unique<StepLoad<WORD>>(Reg::A, Reg::AREG), env+1}};
230 case 2: return {{std::make_unique<StepLoad<WORD>>(Reg::B, Reg::PC), env+1}}; 230 case 2: return {{std::make_unique<StepAddImmediate>(Reg::AREG, Reg::AREG, 1), env+1}};
231 case 3: return {{std::make_unique<StepAddImmediate>(Reg::AREG, Reg::PC, 1), env+1}}; 231 case 3: return {{std::make_unique<StepLoad<WORD>>(Reg::B, Reg::AREG), env+1}};
232 case 4: return {{std::make_unique<StepAdd>(Reg::Q, Reg::A, Reg::B), env+1}}; 232 case 4: return {{std::make_unique<StepAddImmediate>(Reg::AREG, Reg::AREG, 1), env+1}};
233 case 5: return {{std::make_unique<StepStore<WORD>>(Reg::AREG, Reg::Q), env+1}}; 233 case 5: return {{std::make_unique<StepAdd>(Reg::Q, Reg::A, Reg::B), env+1}};
234 case 6: return {{std::make_unique<StepStore<WORD>>(Reg::AREG, Reg::Q), env+1}};
235 }
236
237 return {};
238 }
239 };
240
241 template<unsigned int WORD> struct TaskInit : public aisa::Task {
242 TaskInit()
243 {
244 environment = Reg::ENV_INIT;
245 }
246
247 std::string disasm() const override
248 {
249 return "init";
250 }
251
252 std::optional<std::pair<std::unique_ptr<const aisa::Step>, aisa::regval_t>> step(aisa::regval_t env) const override
253 {
254 switch(env) {
255 case 0: return {{std::make_unique<StepMoveImmediate>(Reg::AREG, 0), env+1}};
256 case 1: return {{std::make_unique<StepMoveImmediate>(Reg::ATMP, 0), env+1}};
257 case 2: return {{std::make_unique<StepStore<WORD>>(Reg::AREG, Reg::ATMP), env+1}};
258 case 3: return {{std::make_unique<StepMoveImmediate>(Reg::AREG, 1), env+1}};
259 case 4: return {{std::make_unique<StepMoveImmediate>(Reg::ATMP, 1), env+1}};
260 case 5: return {{std::make_unique<StepStore<WORD>>(Reg::AREG, Reg::ATMP), env+1}};
234 } 261 }
235 262
236 return {}; 263 return {};
@@ -251,14 +278,10 @@ namespace fib {
251 std::optional<std::pair<std::unique_ptr<const aisa::Step>, aisa::regval_t>> step(aisa::regval_t env) const override 278 std::optional<std::pair<std::unique_ptr<const aisa::Step>, aisa::regval_t>> step(aisa::regval_t env) const override
252 { 279 {
253 switch (env) { 280 switch (env) {
254 case 0: return {{std::make_unique<StepMoveImmediate>(Reg::PC, 0), env+1}}; 281 case 0: return {{std::make_unique<StepSpawn<TaskInit<WORD>>>(), env+1}};
255 case 1: return {{std::make_unique<StepMoveImmediate>(Reg::AREG, 0), env+1}}; 282 case 1: return {{std::make_unique<StepMoveImmediate>(Reg::PC, 0), env+1}};
256 case 2: return {{std::make_unique<StepMoveImmediate>(Reg::ATMP, 0), env+1}}; 283 case 2: return {{std::make_unique<StepSpawn<TaskFib<WORD>>>(), env+1}};
257 case 3: return {{std::make_unique<StepStore<WORD>>(Reg::AREG, Reg::ATMP), env+1}}; 284 case 3: return {{std::make_unique<StepAddImmediate>(Reg::PC, Reg::PC, 1), env-1}};
258 case 4: return {{std::make_unique<StepMoveImmediate>(Reg::AREG, 1), env+1}};
259 case 5: return {{std::make_unique<StepMoveImmediate>(Reg::ATMP, 1), env+1}};
260 case 6: return {{std::make_unique<StepStore<WORD>>(Reg::AREG, Reg::ATMP), env+1}};
261 case 7: return {{std::make_unique<StepSpawn<TaskFib<WORD>>>(), env}};
262 } 285 }
263 286
264 return {}; 287 return {};
diff --git a/fib/fib.h b/fib/fib.h
index 6719d06..25afdc4 100644
--- a/fib/fib.h
+++ b/fib/fib.h
@@ -11,6 +11,7 @@ namespace fib {
11 11
12 enum { 12 enum {
13 ENV_TOP, 13 ENV_TOP,
14 ENV_INIT,
14 ENV_FIB, 15 ENV_FIB,
15 16
16 AREG, 17 AREG,
diff --git a/main.cpp b/main.cpp
index edb0d56..358b954 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1,113 +1,41 @@
1#include <fmt/core.h>
2#include <iostream> 1#include <iostream>
3#include <memory>
4#include <optional>
5#include <utility> 2#include <utility>
6 3
7#include "aisa/aisa.h"
8#include "aisa/async.h"
9#include "aisa/simple-models.h"
10#include "fib/fib.h" 4#include "fib/fib.h"
11#include "git-tag.h" 5#include "git-tag.h"
12 6#include "sim/sim.h"
13const unsigned int max_steps = 300; 7#include "sim/queue.h"
14const bool show_mem_fetch = false; 8#include "uarch/exec.h"
15const bool show_mem_store = true; 9#include "uarch/fetch.h"
16const bool show_regs = false; 10#include "uarch/memory.h"
17const bool show_steps = false; 11#include "uarch/types.h"
18const bool show_tasks = false;
19 12
20int main(int argc, const char *argv[]) 13int main(int argc, const char *argv[])
21{ 14{
22 std::cout << "Version " << GIT_TAG << "\n"; 15 std::cout << "Version " << GIT_TAG << "\n";
23 16
24 struct Eval : public aisa::AsyncEval<Eval>, aisa::PagedMem<>, aisa::TaskStack, aisa::VectorRF { 17 fib::Fib<3> fib;
25 bool fetch_mem(aisa::byte_t *bytes, aisa::addr_t addr, aisa::addr_t size)
26 {
27 if (aisa::PagedMem<>::fetch_mem(bytes, addr, size)) {
28 if (show_mem_fetch) {
29 fmt::print("\t\t\t");
30 for (; size; --size)
31 fmt::print("{:02x} ", *bytes++);
32 fmt::print("= [{:x}]\n", addr);
33 }
34 return true;
35 }
36 return false;
37 }
38
39 bool store_mem(aisa::addr_t addr, const aisa::byte_t *bytes, aisa::addr_t size)
40 {
41 if (aisa::PagedMem<>::store_mem(addr, bytes, size)) {
42 if (show_mem_store) {
43 fmt::print("\t\t\t[{:x}] =", addr);
44 for (; size; --size)
45 fmt::print(" {:02x}", *bytes++);
46 fmt::print("\n");
47 }
48 return true;
49 }
50 return false;
51 }
52
53 bool store_reg(aisa::regnum_t rn, aisa::regval_t rv)
54 {
55 if (aisa::VectorRF::store_reg(rn, rv)) {
56 if (show_regs)
57 fmt::print(".{} = {}\n", fib::Reg::disasm(rn), rv);
58 return true;
59 }
60 return false;
61 }
62
63 bool push_task(std::unique_ptr<const aisa::Task> &&task)
64 {
65 auto d = task->disasm();
66 if (aisa::TaskStack::push_task(std::move(task))) {
67 if (show_tasks)
68 fmt::print("\t\t*** ENTER {} ***\n", d);
69 return true;
70 }
71 return false;
72 }
73 18
74 bool pop_task() 19 sim::Scheduler sched;
75 {
76 if (aisa::TaskStack::pop_task()) {
77 if (show_tasks)
78 fmt::print("\t\t *** LEAVE ***\n");
79 return true;
80 }
81 return false;
82 }
83 } eval;
84 20
85 fib::Fib<3> fib; 21 sim::Queue<uarch::FillReq> fillreqq(sched, 1);
22 sim::Queue<uarch::Fill> fillfetchq(sched, 0);
23 sim::Queue<uarch::Fill> fillexecq(sched, 0);
24 sim::Queue<uarch::Store> storeq(sched, 1);
25 sim::Queue<uarch::Uop> execq(sched, 0);
86 26
87 if (!eval.async_setup_initial_task(fib)()) { 27 auto fetch = new uarch::FetchStage(sched, fib, fillreqq, fillfetchq, execq);
88 fmt::print("Failed to complete initial setup.\n"); 28 auto exec = new uarch::ExecStage(sched, execq, fillreqq, fillexecq, storeq);
89 return 1; 29 auto mem = new uarch::MemStage(sched, fillreqq, {&fillfetchq, &fillexecq}, storeq);
90 }
91 30
92 for (unsigned int i = 0; i < max_steps; ++i) { 31 while (true) {
93 auto res = eval.async_fetch_and_run_step()(); 32 std::cout << "\n*** cycle " << sched.now << "\n\n";
94 if (res.has_value()) { 33 sched.clock();
95 auto &es = *res;
96 if (es.first) {
97 auto &step = *es.first;
98 auto &w = es.second;
99 if (show_steps)
100 fmt::print("\t{}\n", step.disasm(&w));
101 } else {
102 break;
103 }
104 } else {
105 fmt::print("Failed to complete step.\n");
106 return 2;
107 }
108 } 34 }
109 35
110 fmt::print("Functional model exited.\n"); 36 delete fetch;
37 delete exec;
38 delete mem;
111 39
112 return 0; 40 return 0;
113} 41}
diff --git a/sim/queue.h b/sim/queue.h
new file mode 100644
index 0000000..890beb6
--- /dev/null
+++ b/sim/queue.h
@@ -0,0 +1,87 @@
1#pragma once
2
3#include <cstdint>
4#include <iterator> // IWYU pragma: keep
5#include <map>
6#include <set>
7
8#include "sim/sim.h"
9#include "util/assert.h"
10
11namespace sim {
12
13 template<typename Element> struct Queue {
14 Scheduler &scheduler;
15
16 Schedulable *reader = nullptr;
17 std::set<Schedulable *> writers;
18
19 unsigned int min_latency;
20
21 std::multimap<std::uint64_t, Element> waiting;
22
23 Queue(Scheduler &scheduler, unsigned int min_latency)
24 : scheduler(scheduler)
25 , min_latency(min_latency)
26 { }
27
28 void add_reader(Schedulable *x)
29 {
30 ASSERT(&x->scheduler == &scheduler, "Reader associated with incorrect Scheduler");
31 ASSERT(!reader, "Already have a reader");
32 reader = x;
33 if (min_latency == 0) {
34 for (const auto &w : writers)
35 scheduler.constrain(w, reader);
36 }
37 }
38
39 void add_writer(Schedulable *x)
40 {
41 ASSERT(&x->scheduler == &scheduler, "Writer associated with incorrect Scheduler");
42 writers.emplace(x);
43 if (min_latency == 0 && reader)
44 scheduler.constrain(x, reader);
45 }
46
47 void write(Element &&x)
48 {
49 write(std::move(x), min_latency);
50 }
51
52 void write(Element &&x, unsigned int latency)
53 {
54 ASSERT(latency >= min_latency, "Latency too low");
55 ASSERT(!scheduler.current_schedulable || writers.count(scheduler.current_schedulable), "Write lacks permission");
56 waiting.emplace(scheduler.now + min_latency, std::move(x));
57 }
58
59 unsigned int available()
60 {
61 ASSERT(!scheduler.current_schedulable || reader == scheduler.current_schedulable, "Read lacks permission");
62 unsigned int c = 0;
63 for (auto x = waiting.begin(); x != waiting.end() && x->first <= scheduler.now; ++x)
64 ++c;
65 return c;
66 }
67
68 Element &peek(unsigned int i=0)
69 {
70 ASSERT(i < available(), "Peek past available elements");
71 auto x = waiting.begin();
72 std::advance(x, i);
73 return x->second;
74 }
75
76 Element read(unsigned int i=0)
77 {
78 ASSERT(i < available(), "Read past available elements");
79 auto x = waiting.begin();
80 std::advance(x, i);
81 Element e{std::move(x->second)};
82 waiting.erase(x);
83 return e;
84 }
85 };
86
87}
diff --git a/sim/sim.cpp b/sim/sim.cpp
new file mode 100644
index 0000000..e8f84ee
--- /dev/null
+++ b/sim/sim.cpp
@@ -0,0 +1,83 @@
1#include <utility>
2
3#include "sim/sim.h"
4#include "util/assert.h"
5
6namespace sim {
7
8 void Scheduler::add_schedulable(Schedulable *schedulable)
9 {
10 unsorted_schedulables.emplace(schedulable);
11 sort_needed = true;
12 }
13
14 void Scheduler::remove_schedulable(Schedulable *schedulable)
15 {
16 unsorted_schedulables.erase(schedulable);
17 std::erase_if(constraints, [schedulable](const auto &item) { return item.first == schedulable || item.second == schedulable; });
18 sort_needed = true;
19 }
20
21 void Scheduler::constrain(Schedulable *prior, Schedulable *later)
22 {
23 ASSERT(unsorted_schedulables.count(prior), "Constraint prior is not associated with this Scheduler");
24 ASSERT(unsorted_schedulables.count(later), "Constraint later is not associated with this Scheduler");
25 constraints.emplace(later, prior);
26 sort_needed = true;
27 }
28
29 void Scheduler::topo_sort(std::set<Schedulable *> &live, std::set<Schedulable *> &waiting, Schedulable *candidate)
30 {
31 ASSERT(!live.count(candidate), "Dependency loop");
32 for (auto prereq = constraints.find(candidate); prereq != constraints.end() && prereq->first == candidate; ++prereq) {
33 if (prereq->second != candidate && waiting.count(prereq->second)) {
34 live.emplace(candidate);
35 topo_sort(live, waiting, prereq->second);
36 }
37 }
38 sorted_schedulables.emplace_back(candidate);
39 waiting.erase(candidate);
40 }
41
42 void Scheduler::sort()
43 {
44 sorted_schedulables.clear();
45 sorted_schedulables.reserve(unsorted_schedulables.size());
46 auto waiting = unsorted_schedulables;
47 while (!waiting.empty()) {
48 std::set<Schedulable *> live;
49 topo_sort(live, waiting, *waiting.begin());
50 }
51 ASSERT(sorted_schedulables.size() == unsorted_schedulables.size(), "Did not sort every schedulable");
52 sort_needed = false;
53 }
54
55 void Scheduler::clock()
56 {
57 if (sort_needed)
58 sort();
59 for (const auto &s : sorted_schedulables) {
60 current_schedulable = s;
61 s->clock();
62 }
63 current_schedulable = nullptr;
64 ++now;
65 }
66
67 Schedulable::Schedulable(Scheduler &scheduler)
68 : scheduler(scheduler)
69 {
70 scheduler.add_schedulable(this);
71 }
72
73 Schedulable::~Schedulable()
74 {
75 scheduler.remove_schedulable(this);
76 }
77
78 std::uint64_t Schedulable::now()
79 {
80 return scheduler.now;
81 }
82
83}
diff --git a/sim/sim.d b/sim/sim.d
new file mode 100644
index 0000000..d8953fb
--- /dev/null
+++ b/sim/sim.d
@@ -0,0 +1 @@
sim_SODEPS += build/libutil.so
diff --git a/sim/sim.h b/sim/sim.h
new file mode 100644
index 0000000..399832a
--- /dev/null
+++ b/sim/sim.h
@@ -0,0 +1,44 @@
1#pragma once
2
3#include <cstdint>
4#include <map>
5#include <set>
6#include <vector>
7
8namespace sim {
9
10 struct Schedulable;
11
12 struct Scheduler {
13 std::set<Schedulable *> unsorted_schedulables;
14 std::vector<Schedulable *> sorted_schedulables;
15 bool sort_needed = false;
16
17 std::multimap<Schedulable *, Schedulable *> constraints;
18
19 Schedulable *current_schedulable = nullptr;
20 std::uint64_t now = 0;
21
22 void add_schedulable(Schedulable *schedulable);
23 void remove_schedulable(Schedulable *schedulable);
24
25 void constrain(Schedulable *prior, Schedulable *later);
26
27 void topo_sort(std::set<Schedulable *> &live, std::set<Schedulable *> &waiting, Schedulable *candidate);
28 void sort();
29
30 void clock();
31 };
32
33 struct Schedulable {
34 Scheduler &scheduler;
35
36 Schedulable(Scheduler &scheduler);
37 virtual ~Schedulable();
38
39 virtual void clock() = 0;
40
41 std::uint64_t now();
42 };
43
44}
diff --git a/uarch/exec.cpp b/uarch/exec.cpp
new file mode 100644
index 0000000..e40813b
--- /dev/null
+++ b/uarch/exec.cpp
@@ -0,0 +1,138 @@
1#include <algorithm>
2#include <deque>
3#include <iostream>
4#include <optional>
5#include <set>
6#include <string>
7#include <type_traits>
8#include <utility>
9#include <vector>
10
11#include "uarch/exec.h"
12#include "util/assert.h"
13
14namespace uarch {
15
16 ExecStage::ExecStage(sim::Scheduler &scheduler, sim::Queue<Uop> &execq, sim::Queue<FillReq> &fillreqq, sim::Queue<Fill> &fillq, sim::Queue<Store> &storeq)
17 : sim::Schedulable(scheduler)
18 , execq(execq)
19 , fillreqq(fillreqq)
20 , fillq(fillq)
21 , storeq(storeq)
22 {
23 execq.add_reader(this);
24 fillreqq.add_writer(this);
25 fillq.add_reader(this);
26 storeq.add_writer(this);
27 }
28
29 void ExecStage::clock()
30 {
31 if (tasks.empty() && !step && execq.available()) {
32 auto uop = execq.read();
33 std::cout << "exec accepts uop " << uop.top_task().value()->disasm() << "\n";
34 aisa::TaskStack::operator=(std::move(uop));
35 aisa::VectorRF::operator=(std::move(uop));
36 }
37
38 bool sent_store = false;
39 while (!sent_store) {
40 while (!step) {
41 auto t = top_task();
42 if (!t)
43 goto no_work;
44 const auto &task = **t;
45 auto s = task.step(load_reg(task.environment).value());
46 if (s.has_value()) {
47 step = std::move(s->first);
48 std::cout << "exec step " << step->disasm() << "\n";
49 wires = {};
50 outstanding_fill = false;
51 fill_complete = false;
52 ASSERT(store_reg(task.environment, s->second), "Could not write next environment value");
53 } else {
54 pop_task();
55 if (tasks.empty())
56 std::cout << "exec completes uop\n";
57 }
58 }
59
60 if (step->predicate.has_value()) {
61 auto x = load_reg(step->predicate->first);
62 if (x.has_value()) {
63 if (*x != step->predicate->second)
64 goto nextstep;
65 } else {
66 std::cout << "exec stalls on predicate register";
67 break;
68 }
69 }
70
71 wires.source_vals.reserve(step->source_regs.size());
72 for (unsigned int i = wires.source_vals.size(); i < step->source_regs.size(); ++i) {
73 auto x = load_reg(step->source_regs[i]);
74 if (!x.has_value()) {
75 std::cout << "exec stalls on source register";
76 break;
77 }
78 wires.source_vals.emplace_back(*x);
79 }
80
81 if (step->mop == aisa::MOp::LOAD && !fill_complete) {
82 auto mi = step->meminfo(wires);
83 if (outstanding_fill) {
84 for (unsigned int i = 0; i < fillq.available(); ++i) {
85 auto &fill = fillq.peek(i);
86 if (fill.physical_addr == mi.physical_addr) {
87 std::cout << "exec fills\n";
88 wires.memory_val = std::move(fill.bytes);
89 outstanding_fill = false;
90 fill_complete = true;
91 }
92 }
93 if (!fill_complete)
94 break;
95 } else {
96 std::cout << "exec sends fill request\n";
97 FillReq req;
98 req.fillq = &fillq;
99 req.physical_addr = mi.physical_addr;
100 req.size = mi.size;
101 fillreqq.write(std::move(req));
102 outstanding_fill = true;
103 fill_complete = false;
104 break;
105 }
106 }
107
108 step->evaluate(wires);
109
110 for (unsigned int i = 0; i < step->destination_regs.size(); ++i)
111 ASSERT(store_reg(step->destination_regs[i], wires.destination_vals[i]), "Could not write destination register");
112
113 if (step->mop == aisa::MOp::STORE) {
114 std::cout << "exec sends store\n";
115 auto mi = step->meminfo(wires);
116 Store store;
117 store.physical_addr = mi.physical_addr;
118 store.bytes = std::move(wires.memory_val);
119 storeq.write(std::move(store));
120 //sent_store = true;
121 }
122
123nextstep:
124 step = nullptr;
125 wires = {};
126 outstanding_fill = false;
127 fill_complete = false;
128 }
129
130no_work:
131
132 while (fillq.available()) {
133 std::cout << "exec consumes fill\n";
134 fillq.read();
135 }
136 }
137
138}
diff --git a/uarch/exec.h b/uarch/exec.h
new file mode 100644
index 0000000..1cfc525
--- /dev/null
+++ b/uarch/exec.h
@@ -0,0 +1,29 @@
1#pragma once
2
3#include <memory>
4
5#include "aisa/aisa.h"
6#include "aisa/simple-models.h"
7#include "sim/sim.h"
8#include "sim/queue.h"
9#include "uarch/types.h"
10
11namespace uarch {
12
13 struct ExecStage : public sim::Schedulable, aisa::TaskStack, aisa::VectorRF {
14 sim::Queue<Uop> &execq;
15 sim::Queue<FillReq> &fillreqq;
16 sim::Queue<Fill> &fillq;
17 sim::Queue<Store> &storeq;
18
19 std::unique_ptr<const aisa::Step> step;
20 aisa::Wires wires;
21 bool outstanding_fill = false;
22 bool fill_complete = false;
23
24 ExecStage(sim::Scheduler &scheduler, sim::Queue<Uop> &execq, sim::Queue<FillReq> &fillreqq, sim::Queue<Fill> &fillq, sim::Queue<Store> &storeq);
25
26 void clock() override;
27 };
28
29}
diff --git a/uarch/fetch.cpp b/uarch/fetch.cpp
new file mode 100644
index 0000000..3b84d82
--- /dev/null
+++ b/uarch/fetch.cpp
@@ -0,0 +1,129 @@
1#include <algorithm>
2#include <iostream>
3#include <optional>
4#include <set>
5#include <string>
6#include <utility>
7#include <vector>
8
9#include "sim/sim.h"
10#include "sim/queue.h"
11#include "uarch/fetch.h"
12#include "uarch/types.h"
13#include "util/assert.h"
14
15namespace uarch {
16
17 FetchStage::FetchStage(sim::Scheduler &scheduler, const aisa::ISA &isa, sim::Queue<FillReq> &fillreqq, sim::Queue<Fill> &fillq, sim::Queue<Uop> &uopq)
18 : sim::Schedulable(scheduler)
19 , fillreqq(fillreqq)
20 , fillq(fillq)
21 , uopq(uopq)
22 {
23 fillreqq.add_writer(this);
24 fillq.add_reader(this);
25 uopq.add_writer(this);
26
27 auto [task, env] = isa.initial_task();
28 store_reg(task->environment, env);
29 push_task(std::move(task));
30 }
31
32 void FetchStage::clock()
33 {
34 bool sent_uop = false;
35 while (!sent_uop) {
36 while (!step) {
37 const auto &task = *top_task().value();
38 auto s = task.step(load_reg(task.environment).value());
39 if (s.has_value()) {
40 step = std::move(s->first);
41 std::cout << "fetch step " << step->disasm() << "\n";
42 wires = {};
43 outstanding_fill = false;
44 fill_complete = false;
45 ASSERT(store_reg(task.environment, s->second), "Could not write next environment value");
46 } else {
47 pop_task();
48 }
49 }
50
51 if (step->predicate.has_value()) {
52 auto x = load_reg(step->predicate->first);
53 if (x.has_value()) {
54 if (*x != step->predicate->second)
55 goto nextstep;
56 } else {
57 std::cout << "fetch stalls on predicate register";
58 break;
59 }
60 }
61
62 wires.source_vals.reserve(step->source_regs.size());
63 for (unsigned int i = wires.source_vals.size(); i < step->source_regs.size(); ++i) {
64 auto x = load_reg(step->source_regs[i]);
65 if (!x.has_value()) {
66 std::cout << "fetch stalls on source register";
67 break;
68 }
69 wires.source_vals.emplace_back(*x);
70 }
71
72 if (step->mop == aisa::MOp::LOAD && !fill_complete) {
73 auto mi = step->meminfo(wires);
74 if (outstanding_fill) {
75 for (unsigned int i = 0; i < fillq.available(); ++i) {
76 auto &fill = fillq.peek(i);
77 if (fill.physical_addr == mi.physical_addr) {
78 std::cout << "fetch fills\n";
79 wires.memory_val = std::move(fill.bytes);
80 outstanding_fill = false;
81 fill_complete = true;
82 }
83 }
84 if (!fill_complete)
85 break;
86 } else {
87 std::cout << "fetch sends fill request\n";
88 FillReq req;
89 req.fillq = &fillq;
90 req.physical_addr = mi.physical_addr;
91 req.size = mi.size;
92 fillreqq.write(std::move(req));
93 outstanding_fill = true;
94 fill_complete = false;
95 break;
96 }
97 }
98
99 step->evaluate(wires);
100
101 for (unsigned int i = 0; i < step->destination_regs.size(); ++i)
102 ASSERT(store_reg(step->destination_regs[i], wires.destination_vals[i]), "Could not write destination register");
103
104 ASSERT(step->mop != aisa::MOp::STORE, "No stores allowed in fetch");
105
106 if (wires.new_task.has_value()) {
107 std::cout << "fetch sends subtask " << wires.new_task->first->disasm() << " downstream\n";
108 Uop uop;
109 static_cast<aisa::VectorRF &>(uop) = *this;
110 ASSERT(uop.store_reg(wires.new_task->first->environment, wires.new_task->second), "Could not write initial environment to uop");
111 ASSERT(uop.push_task(std::move(wires.new_task->first)), "Could not push subtask to uop");
112 uopq.write(std::move(uop));
113 sent_uop = true;
114 }
115
116nextstep:
117 step = nullptr;
118 wires = {};
119 outstanding_fill = false;
120 fill_complete = false;
121 }
122
123 while (fillq.available()) {
124 std::cout << "fetch consumes fill\n";
125 fillq.read();
126 }
127 }
128
129}
diff --git a/uarch/fetch.h b/uarch/fetch.h
new file mode 100644
index 0000000..452b207
--- /dev/null
+++ b/uarch/fetch.h
@@ -0,0 +1,28 @@
1#pragma once
2
3#include <memory>
4
5#include "aisa/aisa.h"
6#include "aisa/simple-models.h"
7#include "sim/sim.h"
8#include "sim/queue.h"
9#include "uarch/types.h"
10
11namespace uarch {
12
13 struct FetchStage : public sim::Schedulable, aisa::TaskStack, aisa::VectorRF {
14 sim::Queue<FillReq> &fillreqq;
15 sim::Queue<Fill> &fillq;
16 sim::Queue<Uop> &uopq;
17
18 std::unique_ptr<const aisa::Step> step;
19 aisa::Wires wires;
20 bool outstanding_fill = false;
21 bool fill_complete = false;
22
23 FetchStage(sim::Scheduler &scheduler, const aisa::ISA &isa, sim::Queue<FillReq> &fillreqq, sim::Queue<Fill> &fillq, sim::Queue<Uop> &uopq);
24
25 void clock() override;
26 };
27
28}
diff --git a/uarch/memory.cpp b/uarch/memory.cpp
new file mode 100644
index 0000000..5cf9920
--- /dev/null
+++ b/uarch/memory.cpp
@@ -0,0 +1,46 @@
1#include <algorithm>
2#include <cstdint>
3#include <initializer_list>
4#include <iostream>
5#include <set>
6#include <utility>
7#include <vector>
8
9#include "aisa/aisa.h"
10#include "uarch/memory.h"
11#include "util/assert.h"
12
13namespace uarch {
14
15 MemStage::MemStage(sim::Scheduler &scheduler, sim::Queue<FillReq> &fillreqq, const std::initializer_list<sim::Queue<Fill> *> &fillqs, sim::Queue<Store> &storeq)
16 : sim::Schedulable(scheduler)
17 , fillreqq(fillreqq)
18 , storeq(storeq)
19 {
20 fillreqq.add_reader(this);
21 for (const auto &q : fillqs)
22 q->add_writer(this);
23 storeq.add_reader(this);
24 }
25
26 void MemStage::clock()
27 {
28 if (storeq.available()) {
29 auto s = storeq.read();
30 std::uint64_t x = 0;
31 for (unsigned int i = 0; i < s.bytes.size(); ++i)
32 x |= static_cast<std::uint64_t>(s.bytes[i]) << 8 * i;
33 std::cout << "mem stores " << s.bytes.size() << " bytes to " << s.physical_addr << " <- " << x << "\n";
34 ASSERT(store_mem(s.physical_addr, s.bytes.data(), s.bytes.size()), "Could not complete store");
35 } else if (fillreqq.available()) {
36 auto r = fillreqq.read();
37 std::cout << "mem fills " << r.size << " bytes from " << r.physical_addr << "\n";
38 Fill f;
39 f.physical_addr = r.physical_addr;
40 f.bytes.resize(r.size);
41 ASSERT(fetch_mem(f.bytes.data(), r.physical_addr, r.size), "Could not complete fill");
42 r.fillq->write(std::move(f));
43 }
44 }
45
46}
diff --git a/uarch/memory.h b/uarch/memory.h
new file mode 100644
index 0000000..f36e375
--- /dev/null
+++ b/uarch/memory.h
@@ -0,0 +1,21 @@
1#pragma once
2
3#include <initializer_list>
4
5#include "aisa/simple-models.h"
6#include "sim/sim.h"
7#include "sim/queue.h"
8#include "uarch/types.h"
9
10namespace uarch {
11
12 struct MemStage : public sim::Schedulable, aisa::PagedMem<> {
13 sim::Queue<FillReq> &fillreqq;
14 sim::Queue<Store> &storeq;
15
16 MemStage(sim::Scheduler &scheduler, sim::Queue<FillReq> &fillreqq, const std::initializer_list<sim::Queue<Fill> *> &fillqs, sim::Queue<Store> &storeq);
17
18 void clock() override;
19 };
20
21}
diff --git a/uarch/types.h b/uarch/types.h
new file mode 100644
index 0000000..481b8fe
--- /dev/null
+++ b/uarch/types.h
@@ -0,0 +1,30 @@
1#pragma once
2
3#include <vector>
4
5#include "aisa/aisa.h"
6#include "aisa/simple-models.h"
7#include "sim/queue.h"
8
9namespace uarch {
10
11 struct Fill {
12 aisa::addr_t physical_addr;
13 std::vector<aisa::byte_t> bytes;
14 };
15
16 struct FillReq {
17 sim::Queue<Fill> *fillq;
18 aisa::addr_t physical_addr;
19 aisa::addr_t size;
20 };
21
22 struct Store {
23 aisa::addr_t physical_addr;
24 std::vector<aisa::byte_t> bytes;
25 };
26
27 struct Uop : public aisa::TaskStack, aisa::VectorRF {
28 };
29
30}
diff --git a/uarch/uarch.d b/uarch/uarch.d
new file mode 100644
index 0000000..e99bf71
--- /dev/null
+++ b/uarch/uarch.d
@@ -0,0 +1,2 @@
1uarch_SODEPS += build/libaisa.so
2uarch_SODEPS += build/libsim.so
diff --git a/util/assert.cpp b/util/assert.cpp
new file mode 100644
index 0000000..5b57d67
--- /dev/null
+++ b/util/assert.cpp
@@ -0,0 +1,12 @@
1#include <exception>
2#include <iostream>
3
4#include "util/assert.h"
5
6void ASSERT(bool cond, const char *msg)
7{
8 if (cond)
9 return;
10 std::cout << "Assertion failed: " << msg << "\n";
11 std::terminate();
12}
diff --git a/util/assert.h b/util/assert.h
new file mode 100644
index 0000000..84bd44c
--- /dev/null
+++ b/util/assert.h
@@ -0,0 +1,3 @@
1#pragma once
2
3void ASSERT(bool cond, const char *msg);