#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(); } } }