#pragma once #include "infra/port.h" #include "inst.h" #include "memory/dram.h" namespace backend { struct exec : public infra::sim { infra::port execp; infra::port *writebackp = nullptr; infra::port *loadp = nullptr; infra::port loadresultp; infra::port stallp; void clock() { if (stallp.can_read() && writebackp->can_write()) { const auto &i = stallp.peek(); switch (i.field[OPCODE]) { case OP_LOAD: if (loadresultp.can_read()) { auto i = stallp.read(); auto addr = i.field[SRC1] + i.field[SRC2]; auto offset = addr & memory::LINE_BYTE_OFFSET_MASK; pte(i.transaction, "", fmt::format("addr={:x} offset={:x}", addr, offset)); auto f = loadresultp.read(); std::uint64_t r = 0; for (unsigned int i = 0; i < sizeof(r); ++i) r |= f.data[i + offset] << (8 * i); i.result = r; writebackp->write(std::move(i)); } break; } } else if (execp.can_read() && writebackp->can_write() && loadp->can_write()) { auto i = execp.read(); pte(i.transaction, "E", fmt::format("exec gen={} op={:x} a={:x} b={:x}", i.generation, i.field[OPCODE], i.field[SRC1], i.field[SRC2])); switch (i.field[OPCODE]) { case OP_JUMP_ABS_IF_ZERO: if (i.field[SRC2] == 0) i.result = i.field[SRC1]; else i.result = i.linear_next_pc; break; case OP_JUMP_ABS_IF_NONZERO: if (i.field[SRC2] != 0) i.result = i.field[SRC1]; else i.result = i.linear_next_pc; break; case OP_EMIT: case OP_ADD: i.result = i.field[SRC1] + i.field[SRC2]; break; case OP_LOAD: { memory::dram::command c; c.transaction = i.transaction; c.line_address = (i.field[SRC1] + i.field[SRC2]) >> memory::LINE_BYTES_LOG2; c.write = false; c.responsep = &loadresultp; loadp->write(std::move(c)); } stallp.write(std::move(i)); break; } if (stallp.can_write()) writebackp->write(std::move(i)); } } }; }