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/decode.h | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 frontend/decode.h (limited to 'frontend/decode.h') 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(); + } + } + }; +} -- cgit v1.2.3