summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Blake Kongslie2022-06-26 16:24:13 -0700
committerJulian Blake Kongslie2022-06-26 16:24:13 -0700
commitbbaf58c9fd0f485266d86868dc35f1d2be3589cd (patch)
tree4ae2b78bad51e3aa10776707ed9048804f378487
parentUse a separate EvalContext structure for holding some state. (diff)
downloadissim-bbaf58c9fd0f485266d86868dc35f1d2be3589cd.tar.xz
Significant changes, and a working "ISA" that just computes fib(n).
-rw-r--r--Makefile29
-rw-r--r--aisa/aisa.h52
-rw-r--r--aisa/async.h225
-rw-r--r--aisa/eval.h65
-rw-r--r--aisa/simple-models.h95
-rw-r--r--isa/fib/fib.h288
-rw-r--r--main.cpp135
-rw-r--r--tools/iwyu.imp3
8 files changed, 759 insertions, 133 deletions
diff --git a/Makefile b/Makefile
index 9e84cc8..ed96817 100644
--- a/Makefile
+++ b/Makefile
@@ -6,7 +6,11 @@ CXX ?= g++
6IWYU ?= iwyu 6IWYU ?= iwyu
7CHRONIC ?= chronic 7CHRONIC ?= chronic
8 8
9CXXFLAGS := -Wall -Werror -std=c++20 -fPIC -iquote . ${OPTIMIZE} ${DEBUG} 9PKGS := fmt
10PKG_FLAGS := $(shell pkg-config --cflags ${PKGS})
11PKG_LIBS := -Wl,--push-state,--as-needed $(shell pkg-config --libs ${PKGS}) -Wl,--pop-state
12
13CXXFLAGS := -Wall -Werror -std=c++20 -fPIC -iquote . ${PKG_FLAGS} ${OPTIMIZE} ${DEBUG}
10 14
11.DEFAULT_GOAL := all 15.DEFAULT_GOAL := all
12 16
@@ -24,7 +28,7 @@ endef
24export GITTAGCPP 28export GITTAGCPP
25 29
26ifneq ($(shell which ${IWYU}),) 30ifneq ($(shell which ${IWYU}),)
27iwyu = ${CHRONIC} ${IWYU} -Xiwyu --error ${CXXFLAGS} $(1) 31iwyu = ${CHRONIC} ${IWYU} -Xiwyu --error -Xiwyu --mapping_file=tools/iwyu.imp ${CXXFLAGS} $(1)
28else 32else
29$(warning Not using IWYU) 33$(warning Not using IWYU)
30iwyu = 34iwyu =
@@ -42,29 +46,32 @@ $(call libname,$(1).a): $(patsubst %.cpp,build/%.o,$(wildcard $(1)/*.cpp))
42 46
43$(call libname,$(1).so): $(patsubst %.cpp,build/%.o,$(wildcard $(1)/*.cpp)) 47$(call libname,$(1).so): $(patsubst %.cpp,build/%.o,$(wildcard $(1)/*.cpp))
44 @mkdir -p $$(dir $$@) 48 @mkdir -p $$(dir $$@)
45 $${CXX} $${CXXFLAGS} -shared -o $$@ $$+ 49 $${CXX} $${CXXFLAGS} -shared -o $$@ $$+ ${PKG_LIBS}
46 50
47PARTARS += $(call libname,$(1).a) 51PARTARS += $(call libname,$(1).a)
48PARTSOS += $(call libname,$(1).so) 52PARTSOS += $(call libname,$(1).so)
49 53
50else 54endif
51 55
52$(call libname,$(1).cpp): 56ifdef iwyu
57
58build/$(1)/iwyu.cpp:
53 @mkdir -p $$(dir $$@) 59 @mkdir -p $$(dir $$@)
54 @tools/iwyu-header $$(wildcard $(1)/*.h) > $$@ 60 @tools/iwyu-header $$(wildcard $(1)/*.h) > $$@
55 @$$(call iwyu,$$@) 61 @$$(call iwyu,$$@)
56.PHONY: $(call libname,$(1).cpp) 62.PHONY: build/$(1)/iwyu.cpp
57 63
58EXTRA_TARGETS += $(call libname,$(1).cpp) 64IWYU_CPPS += build/$(1)/iwyu.cpp
59 65
60endif 66endif
61 67
62endef 68endef
63 69
64PARTS := $(patsubst ./%,%,$(shell find -mindepth 1 -type d -\( -name .\* -prune -o -name build -prune -o -name tools -prune -o -print -\))) 70PARTS := $(patsubst ./%,%,$(shell find -mindepth 1 -type d -\( -name .\* -prune -o -name build -prune -o -name tools -prune -o -print -\)))
71
65PARTARS := 72PARTARS :=
66PARTSOS := 73PARTSOS :=
67EXTRA_TARGETS := 74IWYU_CPPS :=
68 75
69$(foreach part,${PARTS},$(eval $(call mklib,${part}))) 76$(foreach part,${PARTS},$(eval $(call mklib,${part})))
70 77
@@ -73,7 +80,7 @@ MAINOBJS += build/git-tag.o
73 80
74$(info ) 81$(info )
75 82
76all: ${EXTRA_TARGETS} issim issim-static 83all: ${IWYU_CPPS} issim issim-static
77 84
78issim: build/issim-dynamic 85issim: build/issim-dynamic
79 @ln -sf $< $@ 86 @ln -sf $< $@
@@ -85,11 +92,11 @@ issim-static: build/issim-static
85 92
86build/issim-dynamic: ${MAINOBJS} ${PARTSOS} 93build/issim-dynamic: ${MAINOBJS} ${PARTSOS}
87 @mkdir -p $(dir $@) 94 @mkdir -p $(dir $@)
88 ${CXX} ${CXXFLAGS} -o $@ $+ 95 ${CXX} ${CXXFLAGS} -o $@ $+ ${PKG_LIBS}
89 96
90build/issim-static: ${MAINOBJS} ${PARTARS} 97build/issim-static: ${MAINOBJS} ${PARTARS}
91 @mkdir -p $(dir $@) 98 @mkdir -p $(dir $@)
92 ${CXX} ${CXXFLAGS} -o $@ $+ 99 ${CXX} ${CXXFLAGS} -o $@ $+ ${PKG_LIBS}
93 100
94clean: 101clean:
95 rm -rf build issim issim-static 102 rm -rf build issim issim-static
diff --git a/aisa/aisa.h b/aisa/aisa.h
index 3c260c1..4488529 100644
--- a/aisa/aisa.h
+++ b/aisa/aisa.h
@@ -1,20 +1,67 @@
1#pragma once 1#pragma once
2 2
3#include <cstdint> 3#include <cstdint>
4#include <memory>
4#include <optional> 5#include <optional>
6#include <string>
5#include <utility> 7#include <utility>
6#include <vector> 8#include <vector>
7 9
8namespace aisa { 10namespace aisa {
9 11
12 using addr_t = std::uint64_t;
13 using byte_t = std::uint8_t;
10 using regnum_t = std::uint_fast64_t; 14 using regnum_t = std::uint_fast64_t;
11 using regval_t = std::uint64_t; 15 using regval_t = std::uint64_t;
12 16
17 struct Task;
18
19 struct ISA {
20 virtual ~ISA() { }
21
22 virtual std::pair<std::unique_ptr<const Task>, regval_t> initial_task() const = 0;
23 };
24
25 struct Step;
26
27 struct Task {
28 regnum_t environment;
29
30 virtual ~Task() { }
31
32 virtual std::string disasm() const = 0;
33
34 virtual std::optional<std::pair<std::unique_ptr<const Step>, regval_t>> step(regval_t environment_val) const { return {}; }
35 };
36
37 struct MemInfo {
38 addr_t physical_addr;
39 addr_t size;
40 };
41
42 struct Wires {
43 std::vector<regval_t> source_vals;
44 std::vector<byte_t> memory_val;
45 bool aborted = false;
46 std::vector<regval_t> destination_vals;
47 std::optional<std::pair<std::unique_ptr<const Task>, regval_t>> new_task;
48 };
49
50 enum class MOp {
51 NONE,
52 LOAD,
53 STORE,
54 };
55
13 struct Step { 56 struct Step {
14 std::optional<std::pair<regnum_t, regval_t>> predicate; 57 std::optional<std::pair<regnum_t, regval_t>> predicate;
15 std::vector<regnum_t> source_regs; 58 std::vector<regnum_t> source_regs;
16 std::vector<regnum_t> destination_regs; 59 std::vector<regnum_t> destination_regs;
17 60
61 virtual ~Step() { }
62
63 virtual std::string disasm(const Wires *w = nullptr) const = 0;
64
18 std::optional<regnum_t> predicate_reg() const 65 std::optional<regnum_t> predicate_reg() const
19 { 66 {
20 if (predicate.has_value()) 67 if (predicate.has_value())
@@ -29,7 +76,10 @@ namespace aisa {
29 return {}; 76 return {};
30 } 77 }
31 78
32 virtual std::vector<regval_t> compute_destinations(const std::vector<regval_t> &source_vals) const = 0; 79 MOp mop = MOp::NONE;
80
81 virtual MemInfo meminfo(const Wires &wires) const { return {}; }
82 virtual void evaluate(Wires &wires) const { };
33 }; 83 };
34 84
35} 85}
diff --git a/aisa/async.h b/aisa/async.h
new file mode 100644
index 0000000..42e99e7
--- /dev/null
+++ b/aisa/async.h
@@ -0,0 +1,225 @@
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/eval.h b/aisa/eval.h
deleted file mode 100644
index a301e93..0000000
--- a/aisa/eval.h
+++ /dev/null
@@ -1,65 +0,0 @@
1#pragma once
2
3#include <coroutine>
4#include <memory>
5#include <optional>
6#include <utility>
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 EvalState {
15 struct EvalContext {
16 task<void> coroutine;
17
18 bool resume() { return coroutine(); }
19 };
20
21 CRTP & crtp() noexcept { return static_cast<CRTP &>(*this); }
22
23 task<regval_t> async_load_reg(regnum_t rn)
24 {
25 while (true) {
26 if (auto rv = crtp().load_reg(rn); rv.has_value())
27 co_return *rv;
28 co_await std::suspend_always{};
29 }
30 }
31
32 task<void> async_store_reg(regnum_t rn, regval_t rv)
33 {
34 while (true) {
35 if (crtp().store_reg(rn, rv))
36 co_return;
37 co_await std::suspend_always{};
38 }
39 }
40
41 task<void> async_evaluate(EvalContext &contex, const Step &step)
42 {
43 if (step.predicate.has_value()) {
44 regval_t pval = co_await async_load_reg(step.predicate->first);
45 if (pval != step.predicate->second)
46 co_return;
47 }
48 std::vector<regval_t> source_vals;
49 source_vals.reserve(step.source_regs.size());
50 for (unsigned int i = 0; i < step.source_regs.size(); ++i)
51 source_vals.emplace_back(co_await async_load_reg(step.source_regs[i]));
52 auto destination_vals = step.compute_destinations(source_vals);
53 for (unsigned int i = 0; i < step.destination_regs.size(); ++i)
54 co_await async_store_reg(step.destination_regs[i], destination_vals[i]);
55 }
56
57 std::unique_ptr<EvalContext> operator()(const Step &step)
58 {
59 auto context = std::make_unique<EvalContext>();
60 context->coroutine = async_evaluate(*context, step);
61 return context;
62 }
63 };
64
65}
diff --git a/aisa/simple-models.h b/aisa/simple-models.h
new file mode 100644
index 0000000..89e8752
--- /dev/null
+++ b/aisa/simple-models.h
@@ -0,0 +1,95 @@
1#pragma once
2
3#include <algorithm>
4#include <array>
5#include <cstring>
6#include <deque>
7#include <map>
8#include <memory>
9#include <optional>
10#include <utility>
11#include <vector>
12
13#include "aisa/aisa.h"
14
15namespace aisa {
16
17 template<unsigned int PAGE_BITS=10> struct PagedMem {
18 using page_t = std::array<byte_t, 1 << PAGE_BITS>;
19
20 static const addr_t PAGE_SIZE = 1 << PAGE_BITS;
21 static const addr_t PAGE_MASK = PAGE_SIZE - 1;
22
23 std::map<addr_t, page_t> pages;
24
25 bool fetch_mem(byte_t *bytes, addr_t addr, addr_t size)
26 {
27 if (size == 0)
28 return true;
29 auto page_base = addr >> PAGE_BITS;
30 auto page_offset = addr & PAGE_MASK;
31 auto size_here = std::min(size, PAGE_SIZE - page_offset);
32 if (auto page = pages.find(page_base); page != pages.end())
33 std::memcpy(bytes, page->second.data() + page_offset, size_here);
34 else
35 std::memset(bytes, 0, size_here);
36 return fetch_mem(bytes + size_here, addr + size_here, size - size_here);
37 }
38
39 bool store_mem(addr_t addr, const byte_t *bytes, addr_t size)
40 {
41 if (size == 0)
42 return true;
43 auto page_base = addr >> PAGE_BITS;
44 auto page_offset = addr & PAGE_MASK;
45 auto size_here = std::min(size, PAGE_SIZE - page_offset);
46 std::memcpy(pages[page_base].data() + page_offset, bytes, size_here);
47 return store_mem(addr + size_here, bytes + size_here, size - size_here);
48 }
49 };
50
51 struct TaskStack {
52 std::deque<std::unique_ptr<const Task>> tasks;
53
54 bool pop_task()
55 {
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
68 std::optional<const Task *> top_task()
69 {
70 if (tasks.empty())
71 return {};
72 return tasks.back().get();
73 }
74 };
75
76 struct VectorRF {
77 std::vector<std::optional<regval_t>> rf;
78
79 std::optional<regval_t> load_reg(regnum_t rn) const
80 {
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 };
94
95}
diff --git a/isa/fib/fib.h b/isa/fib/fib.h
new file mode 100644
index 0000000..13801c8
--- /dev/null
+++ b/isa/fib/fib.h
@@ -0,0 +1,288 @@
1#pragma once
2
3#include <fmt/core.h>
4#include <memory>
5#include <optional>
6#include <string>
7#include <utility>
8#include <vector>
9
10#include "aisa/aisa.h"
11
12namespace isa::fib {
13
14 namespace Reg {
15 enum {
16 ENV_TOP,
17 ENV_FIB,
18
19 AREG,
20 ATMP,
21
22 PC,
23
24 A,
25 B,
26 Q,
27 };
28
29 const char *disasm(aisa::regnum_t x)
30 {
31 switch (x) {
32 case ENV_TOP: return "ENV_TOP";
33 case ENV_FIB: return "ENV_FIB";
34 case AREG: return "AREG";
35 case ATMP: return "ATMP";
36 case PC: return "PC";
37 case A: return "A";
38 case B: return "B";
39 case Q: return "Q";
40 default: return "???";
41 }
42 }
43 }
44
45 struct StepAdd : public aisa::Step {
46 StepAdd(aisa::regnum_t q, aisa::regnum_t a, aisa::regnum_t b)
47 {
48 source_regs.resize(2);
49 source_regs[0] = a;
50 source_regs[1] = b;
51
52 destination_regs.resize(1);
53 destination_regs[0] = q;
54 }
55
56 std::string disasm(const aisa::Wires *w) const override
57 {
58 if (w)
59 return fmt::format("{}({}) <- {}({}) + {}({})", Reg::disasm(destination_regs[0]), w->destination_vals[0], Reg::disasm(source_regs[0]), w->source_vals[0], Reg::disasm(source_regs[1]), w->source_vals[1]);
60 return fmt::format("{} <- {} + {}", Reg::disasm(destination_regs[0]), Reg::disasm(source_regs[0]), Reg::disasm(source_regs[1]));
61 }
62
63 void evaluate(aisa::Wires &w) const override
64 {
65 w.destination_vals.resize(1);
66 w.destination_vals[0] = w.source_vals[0] + w.source_vals[1];
67 }
68 };
69
70 struct StepAddImmediate : public aisa::Step {
71 aisa::regval_t x;
72
73 StepAddImmediate(aisa::regnum_t q, aisa::regnum_t a, aisa::regval_t x)
74 :x(x)
75 {
76 source_regs.resize(1);
77 source_regs[0] = a;
78
79 destination_regs.resize(1);
80 destination_regs[0] = q;
81 }
82
83 std::string disasm(const aisa::Wires *w) const override
84 {
85 if (w)
86 return fmt::format("{}({}) <- {}({}) + {}", Reg::disasm(destination_regs[0]), w->destination_vals[0], Reg::disasm(source_regs[0]), w->source_vals[0], x);
87 return fmt::format("{} <- {} + {}", Reg::disasm(destination_regs[0]), Reg::disasm(source_regs[0]), x);
88 }
89
90 void evaluate(aisa::Wires &w) const override
91 {
92 w.destination_vals.resize(1);
93 w.destination_vals[0] = w.source_vals[0] + x;
94 }
95 };
96
97 template<unsigned int WORD> struct StepLoad : public aisa::Step {
98 StepLoad(aisa::regnum_t q, aisa::regnum_t a)
99 {
100 source_regs.resize(1);
101 source_regs[0] = a;
102
103 destination_regs.resize(1);
104 destination_regs[0] = q;
105
106 mop = aisa::MOp::LOAD;
107 }
108
109 std::string disasm(const aisa::Wires *w) const override
110 {
111 if (w)
112 return fmt::format("{}({}) <- [{}({})]", Reg::disasm(destination_regs[0]), w->destination_vals[0], Reg::disasm(source_regs[0]), w->source_vals[0]);
113 return fmt::format("{} <- [{}]", Reg::disasm(destination_regs[0]), Reg::disasm(source_regs[0]));
114 }
115
116 aisa::MemInfo meminfo(const aisa::Wires &w) const override
117 {
118 aisa::MemInfo mi;
119 mi.physical_addr = w.source_vals[0] * WORD;
120 mi.size = WORD;
121 return mi;
122 }
123
124 void evaluate(aisa::Wires &w) const override
125 {
126 w.destination_vals.resize(1);
127 auto &q = w.destination_vals[0];
128 q = 0;
129 for (unsigned int i = 0; i < WORD; ++i)
130 q |= static_cast<aisa::regval_t>(w.memory_val[i]) << (8 * i);
131 }
132 };
133
134 struct StepMove : public aisa::Step {
135 StepMove(aisa::regnum_t q, aisa::regnum_t a)
136 {
137 source_regs.resize(1);
138 source_regs[0] = a;
139
140 destination_regs.resize(1);
141 destination_regs[0] = q;
142 }
143
144 std::string disasm(const aisa::Wires *w) const override
145 {
146 if (w)
147 return fmt::format("{}({}) <- {}({})", Reg::disasm(destination_regs[0]), w->destination_vals[0], Reg::disasm(source_regs[0]), w->source_vals[0]);
148 return fmt::format("{} <- {}", Reg::disasm(destination_regs[0]), Reg::disasm(source_regs[0]));
149 }
150
151 void evaluate(aisa::Wires &w) const override
152 {
153 w.destination_vals = w.source_vals;
154 }
155 };
156
157 struct StepMoveImmediate : public aisa::Step {
158 aisa::regval_t x;
159
160 StepMoveImmediate(aisa::regnum_t q, aisa::regval_t x)
161 : x(x)
162 {
163 destination_regs.resize(1);
164 destination_regs[0] = q;
165 }
166
167 std::string disasm(const aisa::Wires *w) const override
168 {
169 if (w)
170 return fmt::format("{}({}) <- {}", Reg::disasm(destination_regs[0]), w->destination_vals[0], x);
171 return fmt::format("{} <- {}", Reg::disasm(destination_regs[0]), x);
172 }
173
174 void evaluate(aisa::Wires &w) const override
175 {
176 w.destination_vals.resize(1);
177 w.destination_vals[0] = x;
178 }
179 };
180
181 template<typename Subtask> struct StepSpawn : public aisa::Step {
182 void evaluate(aisa::Wires &w) const override
183 {
184 w.new_task = {std::make_unique<Subtask>(), 0};
185 }
186
187 std::string disasm(const aisa::Wires *w) const override
188 {
189 return fmt::format("spawn");
190 }
191 };
192
193 template<unsigned int WORD> struct StepStore : public aisa::Step {
194 StepStore(aisa::regnum_t a, aisa::regnum_t d)
195 {
196 source_regs.resize(2);
197 source_regs[0] = a;
198 source_regs[1] = d;
199
200 mop = aisa::MOp::STORE;
201 }
202
203 std::string disasm(const aisa::Wires *w) const override
204 {
205 if (w)
206 return fmt::format("[{}({})] <- {}({})", Reg::disasm(source_regs[0]), w->source_vals[0], Reg::disasm(source_regs[1]), w->source_vals[1]);
207 return fmt::format("[{}] <- {}", Reg::disasm(source_regs[0]), Reg::disasm(source_regs[1]));
208 }
209
210 aisa::MemInfo meminfo(const aisa::Wires &w) const override
211 {
212 aisa::MemInfo mi;
213 mi.physical_addr = w.source_vals[0] * WORD;
214 mi.size = WORD;
215 return mi;
216 }
217
218 void evaluate(aisa::Wires &w) const override
219 {
220 w.memory_val.resize(WORD);
221 for (unsigned int i = 0; i < WORD; ++i)
222 w.memory_val[i] = w.source_vals[1] >> (8 * i);
223 }
224 };
225
226 template<unsigned int WORD> struct TaskFib : public aisa::Task {
227 TaskFib()
228 {
229 environment = Reg::ENV_FIB;
230 }
231
232 std::string disasm() const override
233 {
234 return "fib";
235 }
236
237 std::optional<std::pair<std::unique_ptr<const aisa::Step>, aisa::regval_t>> step(aisa::regval_t env) const override
238 {
239 switch (env) {
240 case 0: return {{std::make_unique<StepLoad<WORD>>(Reg::A, Reg::PC), env+1}};
241 case 1: return {{std::make_unique<StepAddImmediate>(Reg::PC, Reg::PC, 1), env+1}};
242 case 2: return {{std::make_unique<StepLoad<WORD>>(Reg::B, Reg::PC), env+1}};
243 case 3: return {{std::make_unique<StepAddImmediate>(Reg::AREG, Reg::PC, 1), env+1}};
244 case 4: return {{std::make_unique<StepAdd>(Reg::Q, Reg::A, Reg::B), env+1}};
245 case 5: return {{std::make_unique<StepStore<WORD>>(Reg::AREG, Reg::Q), env+1}};
246 }
247
248 return {};
249 }
250 };
251
252 template<unsigned int WORD> struct TaskTop : public aisa::Task {
253 TaskTop()
254 {
255 environment = Reg::ENV_TOP;
256 }
257
258 std::string disasm() const override
259 {
260 return "top";
261 }
262
263 std::optional<std::pair<std::unique_ptr<const aisa::Step>, aisa::regval_t>> step(aisa::regval_t env) const override
264 {
265 switch (env) {
266 case 0: return {{std::make_unique<StepMoveImmediate>(Reg::PC, 0), env+1}};
267 case 1: return {{std::make_unique<StepMoveImmediate>(Reg::AREG, 0), env+1}};
268 case 2: return {{std::make_unique<StepMoveImmediate>(Reg::ATMP, 0), env+1}};
269 case 3: return {{std::make_unique<StepStore<WORD>>(Reg::AREG, Reg::ATMP), env+1}};
270 case 4: return {{std::make_unique<StepMoveImmediate>(Reg::AREG, 1), env+1}};
271 case 5: return {{std::make_unique<StepMoveImmediate>(Reg::ATMP, 1), env+1}};
272 case 6: return {{std::make_unique<StepStore<WORD>>(Reg::AREG, Reg::ATMP), env+1}};
273 case 7: return {{std::make_unique<StepSpawn<TaskFib<WORD>>>(), env}};
274 }
275
276 return {};
277 }
278 };
279
280 template<unsigned int WORD=1> struct Fib : public aisa::ISA {
281 std::pair<std::unique_ptr<const aisa::Task>, aisa::regval_t> initial_task() const override
282 {
283 auto t = std::make_unique<TaskTop<WORD>>();
284 return {std::move(t), 0};
285 }
286 };
287
288}
diff --git a/main.cpp b/main.cpp
index e88d740..f172226 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1,89 +1,112 @@
1#include <initializer_list> 1#include <fmt/core.h>
2#include <iostream> 2#include <iostream>
3#include <map>
4#include <memory> 3#include <memory>
5#include <optional> 4#include <optional>
6#include <string> 5#include <string>
7#include <type_traits>
8#include <utility> 6#include <utility>
9#include <vector>
10 7
11#include "aisa/aisa.h" 8#include "aisa/aisa.h"
12#include "aisa/eval.h" 9#include "aisa/async.h"
10#include "aisa/simple-models.h"
13#include "git-tag.h" 11#include "git-tag.h"
12#include "isa/fib/fib.h"
13
14const bool show_mem_fetch = false;
15const bool show_mem_store = true;
16const bool show_regs = false;
17const bool show_steps = true;
18const bool show_tasks = false;
14 19
15int main(int argc, const char *argv[]) 20int main(int argc, const char *argv[])
16{ 21{
17 std::cout << "Version " << GIT_TAG << "\n"; 22 std::cout << "Version " << GIT_TAG << "\n";
18 23
19 struct CopyStep : public aisa::Step { 24 isa::fib::Fib<2> fib;
20 CopyStep(std::pair<aisa::regnum_t, aisa::regval_t> pred, const std::initializer_list<std::pair<aisa::regnum_t, aisa::regnum_t>> &regs) 25
26 struct Eval : public aisa::AsyncEval<Eval>, aisa::PagedMem<>, aisa::TaskStack, aisa::VectorRF {
27 bool fetch_mem(aisa::byte_t *bytes, aisa::addr_t addr, aisa::addr_t size)
21 { 28 {
22 predicate = std::move(pred); 29 if (aisa::PagedMem<>::fetch_mem(bytes, addr, size)) {
23 source_regs.reserve(regs.size()); 30 if (show_mem_fetch) {
24 destination_regs.reserve(regs.size()); 31 fmt::print("\t\t\t");
25 for (const auto &rp : regs) { 32 for (; size; --size)
26 source_regs.emplace_back(rp.first); 33 fmt::print("{:02x} ", *bytes++);
27 destination_regs.emplace_back(rp.second); 34 fmt::print("= [{:x}]\n", addr);
35 }
36 return true;
28 } 37 }
38 return false;
29 } 39 }
30 40 bool store_mem(aisa::addr_t addr, const aisa::byte_t *bytes, aisa::addr_t size)
31 std::vector<aisa::regval_t> compute_destinations(const std::vector<aisa::regval_t> &source_vals) const override
32 { 41 {
33 return source_vals; 42 if (aisa::PagedMem<>::store_mem(addr, bytes, size)) {
43 if (show_mem_store) {
44 fmt::print("\t\t\t[{:x}] =", addr);
45 for (; size; --size)
46 fmt::print(" {:02x}", *bytes++);
47 fmt::print("\n");
48 }
49 return true;
50 }
51 return false;
34 } 52 }
35 } step{{123, 456}, {{1, 2}, {3, 4}, {5, 6}}};
36 53
37 struct State : public aisa::EvalState<State> { 54 bool store_reg(aisa::regnum_t rn, aisa::regval_t rv)
38 std::map<aisa::regnum_t, aisa::regval_t> regs; 55 {
56 if (aisa::VectorRF::store_reg(rn, rv)) {
57 if (show_regs)
58 fmt::print(".{} = {}\n", isa::fib::Reg::disasm(rn), rv);
59 return true;
60 }
61 return false;
62 }
39 63
40 std::optional<aisa::regval_t> load_reg(aisa::regnum_t rn) 64 bool push_task(std::unique_ptr<const aisa::Task> &&task)
41 { 65 {
42 std::cout << "state.load_reg(" << rn << ") = "; 66 auto d = task->disasm();
43 if (auto x = regs.find(rn); x != regs.end()) { 67 if (aisa::TaskStack::push_task(std::move(task))) {
44 std::cout << x->second << "\n"; 68 if (show_tasks)
45 return x->second; 69 fmt::print("\t\t*** ENTER {} ***\n", d);
70 return true;
46 } 71 }
47 std::cout << "(not available)\n"; 72 return false;
48 return {};
49 } 73 }
50 74
51 bool store_reg(aisa::regnum_t rn, aisa::regval_t rv) 75 bool pop_task()
52 { 76 {
53 std::cout << "state.store_reg(" << rn << " <- " << rv << ")\n"; 77 if (aisa::TaskStack::pop_task()) {
54 regs[rn] = rv; 78 if (show_tasks)
55 return true; 79 fmt::print("\t\t *** LEAVE ***\n");
80 return true;
81 }
82 return false;
56 } 83 }
57 } state; 84 } eval;
58 85
59 auto t = state.async_load_reg(999); 86 if (!eval.async_setup_initial_task(fib)()) {
60 std::cout << "run\n"; t(); 87 fmt::print("Failed to complete initial setup.\n");
61 std::cout << "run\n"; t(); 88 return 1;
62 std::cout << "run\n"; t();
63 std::cout << "set regs[999] = 54321\n"; state.store_reg(999, 54321);
64 std::optional<aisa::regval_t> result;
65 while (!result.has_value()) {
66 std::cout << "run\n";
67 result = t();
68 } 89 }
69 std::cout << "result = " << *result << "\n";
70 90
71 std::cout << "\n\n\n"; 91 while (true) {
92 auto res = eval.async_fetch_and_run_step()();
93 if (res.has_value()) {
94 auto &es = *res;
95 if (es.first) {
96 auto &step = *es.first;
97 auto &w = es.second;
98 if (show_steps)
99 fmt::print("\t{}\n", step.disasm(&w));
100 } else {
101 break;
102 }
103 } else {
104 fmt::print("Failed to complete step.\n");
105 return 2;
106 }
107 }
72 108
73 auto w = state(step); 109 fmt::print("Functional model exited.\n");
74 std::cout << "run\n"; w->resume();
75 std::cout << "run\n"; w->resume();
76 std::cout << "run\n"; w->resume();
77 std::cout << "set predicate (valid)\n"; state.store_reg(step.predicate->first, step.predicate->second);
78 std::cout << "run\n"; w->resume();
79 std::cout << "run\n"; w->resume();
80 std::cout << "run\n"; w->resume();
81 std::cout << "set regs (all)\n";
82 for (int i = 0; i < 10; ++i)
83 state.store_reg(i, 1000 + i);
84 for (bool done = false; !done; done = w->resume())
85 std::cout << "run\n";
86 std::cout << "huzzah!\n";
87 110
88 return 0; 111 return 0;
89} 112}
diff --git a/tools/iwyu.imp b/tools/iwyu.imp
new file mode 100644
index 0000000..b48e049
--- /dev/null
+++ b/tools/iwyu.imp
@@ -0,0 +1,3 @@
1[
2 { include: [ "<ext/alloc_traits.h>", private, "<vector>", public ] },
3]