#pragma once #include #include "frontend/decode.h" #include "infra/port.h" #include "infra/stat.h" #include "inst.h" #include "memory/dram.h" namespace backend { struct regfile : public infra::sim { infra::port *decode_restartp = nullptr; infra::port instp; infra::port *execp = nullptr; infra::port writebackp; infra::port *storep = nullptr; infra::stat ipc{"ipc"}; unsigned int generation_up = 0; unsigned int generation_down = 0; std::array regs; std::array hazards; std::uint64_t pc = 0; regfile() { regs.fill(0); hazards.fill(false); } void clock() { if (writebackp.can_read() && storep->can_write()) { auto i = writebackp.read(); if (i.generation == generation_down) { pte(i.transaction, "W", fmt::format("writeback gen={} pc={:x}", generation_down, pc)); ++ipc; assert(pc == i.init_pc); auto old_pc = pc; pc = i.linear_next_pc; switch (i.field[OPCODE]) { case OP_JUMP_ABS_IF_ZERO: case OP_JUMP_ABS_IF_NONZERO: pte(i.transaction, "", fmt::format("jump to {:x}", i.result.value())); pc = i.result.value(); break; case OP_EMIT: pte(i.transaction, "*", fmt::format("emit {}", i.result.value())); break; case OP_STORE: { memory::dram::command c; c.transaction = i.transaction; c.line_address = i.field[SRC1] >> memory::LINE_BYTES_LOG2; c.write = true; c.mask.fill(false); auto offset = i.field[SRC1] & memory::LINE_BYTE_OFFSET_MASK; pte(i.transaction, "", fmt::format("store [{:x}]={:x} offset={:x}", i.field[SRC1], i.field[SRC2], offset)); for (unsigned int j = 0; j < sizeof(i.field[SRC2]); ++j) { c.mask[offset + j] = true; c.data[offset + j] = (i.field[SRC2] >> (8 * j)) & 0xff; } storep->write(std::move(c)); } break; default: pte(i.transaction, "", fmt::format("wb r{}={:x}", i.field[FLAGS_DST] % regs.size(), i.result.value())); regs[i.field[FLAGS_DST] % regs.size()] = i.result.value(); hazards[i.field[FLAGS_DST] % regs.size()] = false; break; } if (!i.predicted_next_pc.has_value() || pc != i.predicted_next_pc.value()) { pte(i.transaction, "", "restart due to pc misprediction"); frontend::decode::restart dr; dr.new_generation = ++generation_up; dr.new_pc = pc; dr.from_pc = old_pc; decode_restartp->write(std::move(dr)); hazards.fill(false); ++generation_down; } } } if (instp.can_read() && execp->can_write() && !writebackp.can_read()) { auto i = instp.peek(); if (i.generation == generation_up) { bool hazard = false; if (!(i.field[FLAGS_DST] & FLAG_IMM1)) hazard |= hazards[i.field[SRC1] % regs.size()]; if (!(i.field[FLAGS_DST] & FLAG_IMM2)) hazard |= hazards[i.field[SRC2] % regs.size()]; switch (i.field[OPCODE]) { case OP_JUMP_ABS_IF_ZERO: case OP_JUMP_ABS_IF_NONZERO: case OP_EMIT: case OP_STORE: break; default: hazard |= hazards[i.field[FLAGS_DST] % regs.size()]; break; } if (!hazard) { auto i = instp.read(); if (!(i.field[FLAGS_DST] & FLAG_IMM1)) { auto x = regs[i.field[SRC1] % regs.size()]; pte(i.transaction, "", fmt::format("rf1[{}]={:x}", i.field[SRC1] % regs.size(), x)); i.field[SRC1] = x; } if (!(i.field[FLAGS_DST] & FLAG_IMM2)) { auto x = regs[i.field[SRC2] % regs.size()]; pte(i.transaction, "", fmt::format("rf2[{}]={:x}", i.field[SRC2] % regs.size(), x)); i.field[SRC2] = x; } pte(i.transaction, "R", fmt::format("read gen={}", generation_up)); i.generation = generation_down; switch (i.field[OPCODE]) { case OP_JUMP_ABS_IF_ZERO: case OP_JUMP_ABS_IF_NONZERO: case OP_EMIT: case OP_STORE: break; default: hazards[i.field[FLAGS_DST] % regs.size()] = true; break; } execp->write(std::move(i)); } } else { instp.discard(); } } } }; }