From c72951a36d6cb9775dc1ecd9bc26bc13e796f10c Mon Sep 17 00:00:00 2001 From: Julian Blake Kongslie Date: Sat, 2 Jul 2022 13:45:09 -0700 Subject: Dropping the async interface, and adding some real uarch. --- uarch/exec.cpp | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ uarch/exec.h | 29 ++++++++++++ uarch/fetch.cpp | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++ uarch/fetch.h | 28 +++++++++++ uarch/memory.cpp | 46 +++++++++++++++++++ uarch/memory.h | 21 +++++++++ uarch/types.h | 30 ++++++++++++ uarch/uarch.d | 2 + 8 files changed, 423 insertions(+) create mode 100644 uarch/exec.cpp create mode 100644 uarch/exec.h create mode 100644 uarch/fetch.cpp create mode 100644 uarch/fetch.h create mode 100644 uarch/memory.cpp create mode 100644 uarch/memory.h create mode 100644 uarch/types.h create mode 100644 uarch/uarch.d (limited to 'uarch') 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 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "uarch/exec.h" +#include "util/assert.h" + +namespace uarch { + + ExecStage::ExecStage(sim::Scheduler &scheduler, sim::Queue &execq, sim::Queue &fillreqq, sim::Queue &fillq, sim::Queue &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(); + } + } + +} 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 @@ +#pragma once + +#include + +#include "aisa/aisa.h" +#include "aisa/simple-models.h" +#include "sim/sim.h" +#include "sim/queue.h" +#include "uarch/types.h" + +namespace uarch { + + struct ExecStage : public sim::Schedulable, aisa::TaskStack, aisa::VectorRF { + sim::Queue &execq; + sim::Queue &fillreqq; + sim::Queue &fillq; + sim::Queue &storeq; + + std::unique_ptr step; + aisa::Wires wires; + bool outstanding_fill = false; + bool fill_complete = false; + + ExecStage(sim::Scheduler &scheduler, sim::Queue &execq, sim::Queue &fillreqq, sim::Queue &fillq, sim::Queue &storeq); + + void clock() override; + }; + +} 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 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "sim/sim.h" +#include "sim/queue.h" +#include "uarch/fetch.h" +#include "uarch/types.h" +#include "util/assert.h" + +namespace uarch { + + FetchStage::FetchStage(sim::Scheduler &scheduler, const aisa::ISA &isa, sim::Queue &fillreqq, sim::Queue &fillq, sim::Queue &uopq) + : sim::Schedulable(scheduler) + , fillreqq(fillreqq) + , fillq(fillq) + , uopq(uopq) + { + fillreqq.add_writer(this); + fillq.add_reader(this); + uopq.add_writer(this); + + auto [task, env] = isa.initial_task(); + store_reg(task->environment, env); + push_task(std::move(task)); + } + + void FetchStage::clock() + { + bool sent_uop = false; + while (!sent_uop) { + while (!step) { + const auto &task = *top_task().value(); + auto s = task.step(load_reg(task.environment).value()); + if (s.has_value()) { + step = std::move(s->first); + std::cout << "fetch 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 (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 << "fetch 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 << "fetch 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 << "fetch fills\n"; + wires.memory_val = std::move(fill.bytes); + outstanding_fill = false; + fill_complete = true; + } + } + if (!fill_complete) + break; + } else { + std::cout << "fetch 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"); + + ASSERT(step->mop != aisa::MOp::STORE, "No stores allowed in fetch"); + + if (wires.new_task.has_value()) { + std::cout << "fetch sends subtask " << wires.new_task->first->disasm() << " downstream\n"; + Uop uop; + static_cast(uop) = *this; + ASSERT(uop.store_reg(wires.new_task->first->environment, wires.new_task->second), "Could not write initial environment to uop"); + ASSERT(uop.push_task(std::move(wires.new_task->first)), "Could not push subtask to uop"); + uopq.write(std::move(uop)); + sent_uop = true; + } + +nextstep: + step = nullptr; + wires = {}; + outstanding_fill = false; + fill_complete = false; + } + + while (fillq.available()) { + std::cout << "fetch consumes fill\n"; + fillq.read(); + } + } + +} 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 @@ +#pragma once + +#include + +#include "aisa/aisa.h" +#include "aisa/simple-models.h" +#include "sim/sim.h" +#include "sim/queue.h" +#include "uarch/types.h" + +namespace uarch { + + struct FetchStage : public sim::Schedulable, aisa::TaskStack, aisa::VectorRF { + sim::Queue &fillreqq; + sim::Queue &fillq; + sim::Queue &uopq; + + std::unique_ptr step; + aisa::Wires wires; + bool outstanding_fill = false; + bool fill_complete = false; + + FetchStage(sim::Scheduler &scheduler, const aisa::ISA &isa, sim::Queue &fillreqq, sim::Queue &fillq, sim::Queue &uopq); + + void clock() override; + }; + +} 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 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "aisa/aisa.h" +#include "uarch/memory.h" +#include "util/assert.h" + +namespace uarch { + + MemStage::MemStage(sim::Scheduler &scheduler, sim::Queue &fillreqq, const std::initializer_list *> &fillqs, sim::Queue &storeq) + : sim::Schedulable(scheduler) + , fillreqq(fillreqq) + , storeq(storeq) + { + fillreqq.add_reader(this); + for (const auto &q : fillqs) + q->add_writer(this); + storeq.add_reader(this); + } + + void MemStage::clock() + { + if (storeq.available()) { + auto s = storeq.read(); + std::uint64_t x = 0; + for (unsigned int i = 0; i < s.bytes.size(); ++i) + x |= static_cast(s.bytes[i]) << 8 * i; + std::cout << "mem stores " << s.bytes.size() << " bytes to " << s.physical_addr << " <- " << x << "\n"; + ASSERT(store_mem(s.physical_addr, s.bytes.data(), s.bytes.size()), "Could not complete store"); + } else if (fillreqq.available()) { + auto r = fillreqq.read(); + std::cout << "mem fills " << r.size << " bytes from " << r.physical_addr << "\n"; + Fill f; + f.physical_addr = r.physical_addr; + f.bytes.resize(r.size); + ASSERT(fetch_mem(f.bytes.data(), r.physical_addr, r.size), "Could not complete fill"); + r.fillq->write(std::move(f)); + } + } + +} 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 @@ +#pragma once + +#include + +#include "aisa/simple-models.h" +#include "sim/sim.h" +#include "sim/queue.h" +#include "uarch/types.h" + +namespace uarch { + + struct MemStage : public sim::Schedulable, aisa::PagedMem<> { + sim::Queue &fillreqq; + sim::Queue &storeq; + + MemStage(sim::Scheduler &scheduler, sim::Queue &fillreqq, const std::initializer_list *> &fillqs, sim::Queue &storeq); + + void clock() override; + }; + +} 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 @@ +#pragma once + +#include + +#include "aisa/aisa.h" +#include "aisa/simple-models.h" +#include "sim/queue.h" + +namespace uarch { + + struct Fill { + aisa::addr_t physical_addr; + std::vector bytes; + }; + + struct FillReq { + sim::Queue *fillq; + aisa::addr_t physical_addr; + aisa::addr_t size; + }; + + struct Store { + aisa::addr_t physical_addr; + std::vector bytes; + }; + + struct Uop : public aisa::TaskStack, aisa::VectorRF { + }; + +} 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 @@ +uarch_SODEPS += build/libaisa.so +uarch_SODEPS += build/libsim.so -- cgit v1.2.3