From eb3fd68203fee7c63245c702914c2acd3332d65a Mon Sep 17 00:00:00 2001 From: Julian Blake Kongslie Date: Thu, 22 Sep 2022 11:29:07 -0700 Subject: Initial commit. --- frontend/bundle.h | 14 ++++++ frontend/decode.h | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ frontend/fetch.h | 79 +++++++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+) create mode 100644 frontend/bundle.h create mode 100644 frontend/decode.h create mode 100644 frontend/fetch.h (limited to 'frontend') diff --git a/frontend/bundle.h b/frontend/bundle.h new file mode 100644 index 0000000..cc6f441 --- /dev/null +++ b/frontend/bundle.h @@ -0,0 +1,14 @@ +#pragma once + +#include "infra/pipetrace.h" +#include "memory/line.h" + +namespace frontend { + struct bundle { + infra::transaction transaction; + unsigned int generation; + std::uint64_t line_address; + std::uint64_t next_line_address; + memory::line data; + }; +} diff --git a/frontend/decode.h b/frontend/decode.h new file mode 100644 index 0000000..717d0f6 --- /dev/null +++ b/frontend/decode.h @@ -0,0 +1,128 @@ +#pragma once + +#include "frontend/bundle.h" +#include "frontend/fetch.h" +#include "infra/port.h" +#include "inst.h" +#include "memory/line.h" + +namespace frontend { + struct decode : public infra::sim { + struct restart { + unsigned int new_generation; + std::uint64_t new_pc; + std::uint64_t from_pc; + }; + infra::port restartp; + infra::port *fetch_restartp = nullptr; + + infra::port bundlep; + infra::port *instp = nullptr; + + inst next_inst; + unsigned int generation_up = 0; + unsigned int generation_down = 0; + std::uint64_t pc = 0; + + static constexpr unsigned int MAX_INST_SIZE = 64; + static constexpr unsigned int BYTES_PER_CYCLE = 4; + + void clock() { + if (restartp.can_read()) { + auto r = restartp.read(); + generation_down = r.new_generation; + pc = r.new_pc; + next_inst.size = 0; + for (auto &f : next_inst.field) + f = 0; + fetch::restart fr; + fr.new_generation = ++generation_up; + fr.new_next_line_address = pc >> memory::LINE_BYTES_LOG2; + fr.previous_line_address = r.from_pc >> memory::LINE_BYTES_LOG2; + fetch_restartp->write(std::move(fr)); + return; + } + if (next_inst.size >= MAX_INST_SIZE) + return; + if (bundlep.can_read()) { + const auto &b = bundlep.peek(); + for (unsigned int i = 0; i < BYTES_PER_CYCLE; ++i) { + auto line = pc >> memory::LINE_BYTES_LOG2; + auto offset = pc & memory::LINE_BYTE_OFFSET_MASK; + if (b.generation == generation_up && b.line_address == line && instp->can_write()) { + decodebyte byte; + std::memcpy(&byte, b.data.data() + offset, sizeof(byte)); + pte(b.transaction, "d", fmt::format("decode gen={} pc={:x} byte={:02x}", generation_up, pc, *reinterpret_cast(&byte))); + ++next_inst.size; + if (byte.invert) + next_inst.field[byte.field] = ~next_inst.field[byte.field]; + next_inst.field[byte.field] = next_inst.field[byte.field] << 4; + next_inst.field[byte.field] |= byte.bits; + ++pc; + if (!byte.hold) { + next_inst.transaction = infra::pt::child(b.transaction); + next_inst.generation = generation_down; + next_inst.linear_next_pc = pc; + next_inst.predicted_next_pc = pc; + pte(next_inst.transaction, "D", fmt::format("decode gen={}", generation_down)); + bool jump = false; + std::optional target; + std::optional taken; + switch (next_inst.field[OPCODE]) { + case OP_JUMP_ABS_IF_ZERO: + jump = true; + if (next_inst.field[FLAGS_DST] & FLAG_IMM1) + target = next_inst.field[SRC1]; + if (next_inst.field[FLAGS_DST] & FLAG_IMM2) + taken = next_inst.field[SRC2] == 0; + break; + case OP_JUMP_ABS_IF_NONZERO: + jump = true; + if (next_inst.field[FLAGS_DST] & FLAG_IMM1) + target = next_inst.field[SRC1]; + if (next_inst.field[FLAGS_DST] & FLAG_IMM2) + taken = next_inst.field[SRC2] != 0; + break; + } + std::optional redirect; + bool unpredictable = false; + if (jump) { + if (target.has_value()) { + if (taken.has_value()) { + if (taken.value()) + redirect = target; + } else if (target.value() < pc) { + redirect = target; + } + } else if (!taken.has_value() || taken.value()) { + unpredictable = true; + } + } + if (redirect.has_value()) { + pte(next_inst.transaction, "", fmt::format("fe predicts jump to {:x}", redirect.value())); + next_inst.predicted_next_pc = pc = redirect.value(); + } else if (unpredictable) { + pte(next_inst.transaction, "", "frontend halt due to unpredictable jump"); + next_inst.predicted_next_pc.reset(); + } + instp->write(std::move(next_inst)); + next_inst.size = unpredictable ? MAX_INST_SIZE : 0; + for (auto &f : next_inst.field) + f = 0; + } + } + } + auto line = pc >> memory::LINE_BYTES_LOG2; + if (b.generation == generation_up && b.line_address != line && b.next_line_address != line) { + fetch::restart fr; + fr.new_generation = ++generation_up; + fr.new_next_line_address = pc >> memory::LINE_BYTES_LOG2; + fr.previous_line_address = b.line_address; + fetch_restartp->write(std::move(fr)); + } + if (b.generation != generation_up || b.line_address != line) + bundlep.discard(); + } + } + }; +} diff --git a/frontend/fetch.h b/frontend/fetch.h new file mode 100644 index 0000000..0eaebd5 --- /dev/null +++ b/frontend/fetch.h @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "frontend/bundle.h" +#include "infra/pipetrace.h" +#include "infra/sim.h" +#include "memory/dram.h" + +namespace frontend { + struct fetch : public infra::sim { + struct restart { + unsigned int new_generation; + std::uint64_t previous_line_address; + std::uint64_t new_next_line_address; + }; + infra::port restartp; + + infra::port *commandp = nullptr; + infra::port responsep; + + infra::port *bundlep = nullptr; + + unsigned int generation = 0; + std::uint64_t next_line_address = 0; + + // FIXME make prediction table finite + std::map predictor; + + bool fill_request_sent = false; + + void clock() { + if (restartp.can_read()) { + auto r = restartp.read(); + generation = r.new_generation; + next_line_address = r.new_next_line_address; + fill_request_sent = false; + if (r.new_next_line_address == r.previous_line_address || r.new_next_line_address == r.previous_line_address + 1) + predictor.erase(r.previous_line_address); + else + predictor[r.previous_line_address] = r.new_next_line_address; + } + if (fill_request_sent && responsep.can_read() && bundlep->can_write()) { + auto r = responsep.read(); + if (r.line_address == next_line_address) { + bundle b; + b.transaction = r.transaction; + b.generation = generation; + b.line_address = next_line_address; + if (auto p = predictor.find(next_line_address); p != predictor.end()) + b.next_line_address = p->second; + else + b.next_line_address = next_line_address + 1; + next_line_address = b.next_line_address; + pte(b.transaction, "", fmt::format("next fetch line {:x}", next_line_address)); + b.data = std::move(r.data); + bundlep->write(std::move(b)); + fill_request_sent = false; + } + } + if (!fill_request_sent && commandp->can_write()) { + memory::dram::command c; + c.transaction = infra::pt::toplevel(); + pte(c.transaction, "F", fmt::format("fetch gen={}", generation)); + c.line_address = next_line_address; + c.responsep = &responsep; + commandp->write(std::move(c)); + fill_request_sent = true; + } + if (!fill_request_sent) + responsep.discard(); + } + }; +} -- cgit v1.2.3