cpu: Add first-/non-faulting load support to Minor and O3
Some architectures allow masking faults of memory load instructions in some specific circumstances (e.g. first-faulting and non-faulting loads in Arm SVE). This patch adds support for such loads in the Minor and O3 CPU models. Change-Id: I264a81a078f049127779aa834e89f0e693ba0bea Signed-off-by: Gabor Dozsa <gabor.dozsa@arm.com> Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/19178 Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com> Maintainer: Andreas Sandberg <andreas.sandberg@arm.com> Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
committed by
Giacomo Gabrielli
parent
7652b2f12c
commit
46da8fb805
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2014, 2016 ARM Limited
|
* Copyright (c) 2013-2014, 2016,2018 ARM Limited
|
||||||
* All rights reserved
|
* All rights reserved
|
||||||
*
|
*
|
||||||
* The license below extends only to copyright in the software and shall
|
* The license below extends only to copyright in the software and shall
|
||||||
@@ -108,6 +108,8 @@ MinorDynInst::reportData(std::ostream &os) const
|
|||||||
os << "-";
|
os << "-";
|
||||||
else if (isFault())
|
else if (isFault())
|
||||||
os << "F;" << id;
|
os << "F;" << id;
|
||||||
|
else if (translationFault != NoFault)
|
||||||
|
os << "TF;" << id;
|
||||||
else
|
else
|
||||||
os << id;
|
os << id;
|
||||||
}
|
}
|
||||||
@@ -120,6 +122,8 @@ operator <<(std::ostream &os, const MinorDynInst &inst)
|
|||||||
|
|
||||||
if (inst.isFault())
|
if (inst.isFault())
|
||||||
os << "fault: \"" << inst.fault->name() << '"';
|
os << "fault: \"" << inst.fault->name() << '"';
|
||||||
|
else if (inst.translationFault != NoFault)
|
||||||
|
os << "translation fault: \"" << inst.translationFault->name() << '"';
|
||||||
else if (inst.staticInst)
|
else if (inst.staticInst)
|
||||||
os << inst.staticInst->getName();
|
os << inst.staticInst->getName();
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013-2014 ARM Limited
|
* Copyright (c) 2013-2014,2018 ARM Limited
|
||||||
* All rights reserved
|
* All rights reserved
|
||||||
*
|
*
|
||||||
* The license below extends only to copyright in the software and shall
|
* The license below extends only to copyright in the software and shall
|
||||||
@@ -194,6 +194,9 @@ class MinorDynInst : public RefCounted
|
|||||||
/** This instruction is in the LSQ, not a functional unit */
|
/** This instruction is in the LSQ, not a functional unit */
|
||||||
bool inLSQ;
|
bool inLSQ;
|
||||||
|
|
||||||
|
/** Translation fault in case of a mem ref */
|
||||||
|
Fault translationFault;
|
||||||
|
|
||||||
/** The instruction has been sent to the store buffer */
|
/** The instruction has been sent to the store buffer */
|
||||||
bool inStoreBuffer;
|
bool inStoreBuffer;
|
||||||
|
|
||||||
@@ -233,9 +236,9 @@ class MinorDynInst : public RefCounted
|
|||||||
staticInst(NULL), id(id_), traceData(NULL),
|
staticInst(NULL), id(id_), traceData(NULL),
|
||||||
pc(TheISA::PCState(0)), fault(fault_),
|
pc(TheISA::PCState(0)), fault(fault_),
|
||||||
triedToPredict(false), predictedTaken(false),
|
triedToPredict(false), predictedTaken(false),
|
||||||
fuIndex(0), inLSQ(false), inStoreBuffer(false),
|
fuIndex(0), inLSQ(false), translationFault(NoFault),
|
||||||
canEarlyIssue(false), predicate(true), memAccPredicate(true),
|
inStoreBuffer(false), canEarlyIssue(false), predicate(true),
|
||||||
instToWaitFor(0), extraCommitDelay(Cycles(0)),
|
memAccPredicate(true), instToWaitFor(0), extraCommitDelay(Cycles(0)),
|
||||||
extraCommitDelayExpr(NULL), minimumCommitCycle(Cycles(0))
|
extraCommitDelayExpr(NULL), minimumCommitCycle(Cycles(0))
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
|||||||
@@ -116,9 +116,8 @@ class ExecContext : public ::ExecContext
|
|||||||
const std::vector<bool>& byteEnable = std::vector<bool>())
|
const std::vector<bool>& byteEnable = std::vector<bool>())
|
||||||
override
|
override
|
||||||
{
|
{
|
||||||
execute.getLSQ().pushRequest(inst, true /* load */, nullptr,
|
return execute.getLSQ().pushRequest(inst, true /* load */, nullptr,
|
||||||
size, addr, flags, nullptr, nullptr, byteEnable);
|
size, addr, flags, nullptr, nullptr, byteEnable);
|
||||||
return NoFault;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Fault
|
Fault
|
||||||
@@ -128,9 +127,8 @@ class ExecContext : public ::ExecContext
|
|||||||
override
|
override
|
||||||
{
|
{
|
||||||
assert(byteEnable.empty() || byteEnable.size() == size);
|
assert(byteEnable.empty() || byteEnable.size() == size);
|
||||||
execute.getLSQ().pushRequest(inst, false /* store */, data,
|
return execute.getLSQ().pushRequest(inst, false /* store */, data,
|
||||||
size, addr, flags, res, nullptr, byteEnable);
|
size, addr, flags, res, nullptr, byteEnable);
|
||||||
return NoFault;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Fault
|
Fault
|
||||||
@@ -138,9 +136,8 @@ class ExecContext : public ::ExecContext
|
|||||||
AtomicOpFunctor *amo_op) override
|
AtomicOpFunctor *amo_op) override
|
||||||
{
|
{
|
||||||
// AMO requests are pushed through the store path
|
// AMO requests are pushed through the store path
|
||||||
execute.getLSQ().pushRequest(inst, false /* amo */, nullptr,
|
return execute.getLSQ().pushRequest(inst, false /* amo */, nullptr,
|
||||||
size, addr, flags, nullptr, amo_op);
|
size, addr, flags, nullptr, amo_op);
|
||||||
return NoFault;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RegVal
|
RegVal
|
||||||
|
|||||||
@@ -337,19 +337,19 @@ Execute::handleMemResponse(MinorDynInstPtr inst,
|
|||||||
* context predicate, otherwise, it will be set to false */
|
* context predicate, otherwise, it will be set to false */
|
||||||
bool use_context_predicate = true;
|
bool use_context_predicate = true;
|
||||||
|
|
||||||
if (response->fault != NoFault) {
|
if (inst->translationFault != NoFault) {
|
||||||
/* Invoke memory faults. */
|
/* Invoke memory faults. */
|
||||||
DPRINTF(MinorMem, "Completing fault from DTLB access: %s\n",
|
DPRINTF(MinorMem, "Completing fault from DTLB access: %s\n",
|
||||||
response->fault->name());
|
inst->translationFault->name());
|
||||||
|
|
||||||
if (inst->staticInst->isPrefetch()) {
|
if (inst->staticInst->isPrefetch()) {
|
||||||
DPRINTF(MinorMem, "Not taking fault on prefetch: %s\n",
|
DPRINTF(MinorMem, "Not taking fault on prefetch: %s\n",
|
||||||
response->fault->name());
|
inst->translationFault->name());
|
||||||
|
|
||||||
/* Don't assign to fault */
|
/* Don't assign to fault */
|
||||||
} else {
|
} else {
|
||||||
/* Take the fault raised during the TLB/memory access */
|
/* Take the fault raised during the TLB/memory access */
|
||||||
fault = response->fault;
|
fault = inst->translationFault;
|
||||||
|
|
||||||
fault->invoke(thread, inst->staticInst);
|
fault->invoke(thread, inst->staticInst);
|
||||||
}
|
}
|
||||||
@@ -469,6 +469,18 @@ Execute::executeMemRefInst(MinorDynInstPtr inst, BranchData &branch,
|
|||||||
Fault init_fault = inst->staticInst->initiateAcc(&context,
|
Fault init_fault = inst->staticInst->initiateAcc(&context,
|
||||||
inst->traceData);
|
inst->traceData);
|
||||||
|
|
||||||
|
if (inst->inLSQ) {
|
||||||
|
if (init_fault != NoFault) {
|
||||||
|
assert(inst->translationFault != NoFault);
|
||||||
|
// Translation faults are dealt with in handleMemResponse()
|
||||||
|
init_fault = NoFault;
|
||||||
|
} else {
|
||||||
|
// If we have a translation fault then it got suppressed by
|
||||||
|
// initateAcc()
|
||||||
|
inst->translationFault = NoFault;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (init_fault != NoFault) {
|
if (init_fault != NoFault) {
|
||||||
DPRINTF(MinorExecute, "Fault on memory inst: %s"
|
DPRINTF(MinorExecute, "Fault on memory inst: %s"
|
||||||
" initiateAcc: %s\n", *inst, init_fault->name());
|
" initiateAcc: %s\n", *inst, init_fault->name());
|
||||||
|
|||||||
@@ -65,15 +65,51 @@ LSQ::LSQRequest::LSQRequest(LSQ &port_, MinorDynInstPtr inst_, bool isLoad_,
|
|||||||
data(data_),
|
data(data_),
|
||||||
packet(NULL),
|
packet(NULL),
|
||||||
request(),
|
request(),
|
||||||
fault(NoFault),
|
|
||||||
res(res_),
|
res(res_),
|
||||||
skipped(false),
|
skipped(false),
|
||||||
issuedToMemory(false),
|
issuedToMemory(false),
|
||||||
|
isTranslationDelayed(false),
|
||||||
state(NotIssued)
|
state(NotIssued)
|
||||||
{
|
{
|
||||||
request = std::make_shared<Request>();
|
request = std::make_shared<Request>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSQ::LSQRequest::tryToSuppressFault()
|
||||||
|
{
|
||||||
|
SimpleThread &thread = *port.cpu.threads[inst->id.threadId];
|
||||||
|
TheISA::PCState old_pc = thread.pcState();
|
||||||
|
ExecContext context(port.cpu, thread, port.execute, inst);
|
||||||
|
Fault M5_VAR_USED fault = inst->translationFault;
|
||||||
|
|
||||||
|
// Give the instruction a chance to suppress a translation fault
|
||||||
|
inst->translationFault = inst->staticInst->initiateAcc(&context, nullptr);
|
||||||
|
if (inst->translationFault == NoFault) {
|
||||||
|
DPRINTFS(MinorMem, (&port),
|
||||||
|
"Translation fault suppressed for inst:%s\n", *inst);
|
||||||
|
} else {
|
||||||
|
assert(inst->translationFault == fault);
|
||||||
|
}
|
||||||
|
thread.pcState(old_pc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSQ::LSQRequest::completeDisabledMemAccess()
|
||||||
|
{
|
||||||
|
DPRINTFS(MinorMem, (&port), "Complete disabled mem access for inst:%s\n",
|
||||||
|
*inst);
|
||||||
|
|
||||||
|
SimpleThread &thread = *port.cpu.threads[inst->id.threadId];
|
||||||
|
TheISA::PCState old_pc = thread.pcState();
|
||||||
|
|
||||||
|
ExecContext context(port.cpu, thread, port.execute, inst);
|
||||||
|
|
||||||
|
context.setMemAccPredicate(false);
|
||||||
|
inst->staticInst->completeAcc(nullptr, &context, inst->traceData);
|
||||||
|
|
||||||
|
thread.pcState(old_pc);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LSQ::LSQRequest::disableMemAccess()
|
LSQ::LSQRequest::disableMemAccess()
|
||||||
{
|
{
|
||||||
@@ -227,16 +263,26 @@ void
|
|||||||
LSQ::SingleDataRequest::finish(const Fault &fault_, const RequestPtr &request_,
|
LSQ::SingleDataRequest::finish(const Fault &fault_, const RequestPtr &request_,
|
||||||
ThreadContext *tc, BaseTLB::Mode mode)
|
ThreadContext *tc, BaseTLB::Mode mode)
|
||||||
{
|
{
|
||||||
fault = fault_;
|
|
||||||
|
|
||||||
port.numAccessesInDTLB--;
|
port.numAccessesInDTLB--;
|
||||||
|
|
||||||
DPRINTFS(MinorMem, (&port), "Received translation response for"
|
DPRINTFS(MinorMem, (&port), "Received translation response for"
|
||||||
" request: %s\n", *inst);
|
" request: %s delayed:%d %s\n", *inst, isTranslationDelayed,
|
||||||
|
fault_ != NoFault ? fault_->name() : "");
|
||||||
|
|
||||||
makePacket();
|
if (fault_ != NoFault) {
|
||||||
|
inst->translationFault = fault_;
|
||||||
setState(Translated);
|
if (isTranslationDelayed) {
|
||||||
|
tryToSuppressFault();
|
||||||
|
if (inst->translationFault == NoFault) {
|
||||||
|
completeDisabledMemAccess();
|
||||||
|
setState(Complete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setState(Translated);
|
||||||
|
} else {
|
||||||
|
setState(Translated);
|
||||||
|
makePacket();
|
||||||
|
}
|
||||||
port.tryToSendToTransfers(this);
|
port.tryToSendToTransfers(this);
|
||||||
|
|
||||||
/* Let's try and wake up the processor for the next cycle */
|
/* Let's try and wake up the processor for the next cycle */
|
||||||
@@ -281,8 +327,6 @@ void
|
|||||||
LSQ::SplitDataRequest::finish(const Fault &fault_, const RequestPtr &request_,
|
LSQ::SplitDataRequest::finish(const Fault &fault_, const RequestPtr &request_,
|
||||||
ThreadContext *tc, BaseTLB::Mode mode)
|
ThreadContext *tc, BaseTLB::Mode mode)
|
||||||
{
|
{
|
||||||
fault = fault_;
|
|
||||||
|
|
||||||
port.numAccessesInDTLB--;
|
port.numAccessesInDTLB--;
|
||||||
|
|
||||||
unsigned int M5_VAR_USED expected_fragment_index =
|
unsigned int M5_VAR_USED expected_fragment_index =
|
||||||
@@ -292,7 +336,9 @@ LSQ::SplitDataRequest::finish(const Fault &fault_, const RequestPtr &request_,
|
|||||||
numTranslatedFragments++;
|
numTranslatedFragments++;
|
||||||
|
|
||||||
DPRINTFS(MinorMem, (&port), "Received translation response for fragment"
|
DPRINTFS(MinorMem, (&port), "Received translation response for fragment"
|
||||||
" %d of request: %s\n", expected_fragment_index, *inst);
|
" %d of request: %s delayed:%d %s\n", expected_fragment_index,
|
||||||
|
*inst, isTranslationDelayed,
|
||||||
|
fault_ != NoFault ? fault_->name() : "");
|
||||||
|
|
||||||
assert(request_ == fragmentRequests[expected_fragment_index]);
|
assert(request_ == fragmentRequests[expected_fragment_index]);
|
||||||
|
|
||||||
@@ -300,18 +346,33 @@ LSQ::SplitDataRequest::finish(const Fault &fault_, const RequestPtr &request_,
|
|||||||
* tryToSendToTransfers does take */
|
* tryToSendToTransfers does take */
|
||||||
port.cpu.wakeupOnEvent(Pipeline::ExecuteStageId);
|
port.cpu.wakeupOnEvent(Pipeline::ExecuteStageId);
|
||||||
|
|
||||||
if (fault != NoFault) {
|
if (fault_ != NoFault) {
|
||||||
/* tryToSendToTransfers will handle the fault */
|
/* tryToSendToTransfers will handle the fault */
|
||||||
|
inst->translationFault = fault_;
|
||||||
|
|
||||||
DPRINTFS(MinorMem, (&port), "Faulting translation for fragment:"
|
DPRINTFS(MinorMem, (&port), "Faulting translation for fragment:"
|
||||||
" %d of request: %s\n",
|
" %d of request: %s\n",
|
||||||
expected_fragment_index, *inst);
|
expected_fragment_index, *inst);
|
||||||
|
|
||||||
setState(Translated);
|
if (expected_fragment_index > 0 || isTranslationDelayed)
|
||||||
|
tryToSuppressFault();
|
||||||
|
if (expected_fragment_index == 0) {
|
||||||
|
if (isTranslationDelayed && inst->translationFault == NoFault) {
|
||||||
|
completeDisabledMemAccess();
|
||||||
|
setState(Complete);
|
||||||
|
} else {
|
||||||
|
setState(Translated);
|
||||||
|
}
|
||||||
|
} else if (inst->translationFault == NoFault) {
|
||||||
|
setState(Translated);
|
||||||
|
numTranslatedFragments--;
|
||||||
|
makeFragmentPackets();
|
||||||
|
} else {
|
||||||
|
setState(Translated);
|
||||||
|
}
|
||||||
port.tryToSendToTransfers(this);
|
port.tryToSendToTransfers(this);
|
||||||
} else if (numTranslatedFragments == numFragments) {
|
} else if (numTranslatedFragments == numFragments) {
|
||||||
makeFragmentPackets();
|
makeFragmentPackets();
|
||||||
|
|
||||||
setState(Translated);
|
setState(Translated);
|
||||||
port.tryToSendToTransfers(this);
|
port.tryToSendToTransfers(this);
|
||||||
} else {
|
} else {
|
||||||
@@ -562,6 +623,7 @@ LSQ::SplitDataRequest::stepToNextPacket()
|
|||||||
void
|
void
|
||||||
LSQ::SplitDataRequest::retireResponse(PacketPtr response)
|
LSQ::SplitDataRequest::retireResponse(PacketPtr response)
|
||||||
{
|
{
|
||||||
|
assert(inst->translationFault == NoFault);
|
||||||
assert(numRetiredFragments < numTranslatedFragments);
|
assert(numRetiredFragments < numTranslatedFragments);
|
||||||
|
|
||||||
DPRINTFS(MinorMem, (&port), "Retiring fragment addr: 0x%x size: %d"
|
DPRINTFS(MinorMem, (&port), "Retiring fragment addr: 0x%x size: %d"
|
||||||
@@ -950,7 +1012,7 @@ LSQ::tryToSendToTransfers(LSQRequestPtr request)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request->fault != NoFault) {
|
if (request->inst->translationFault != NoFault) {
|
||||||
if (request->inst->staticInst->isPrefetch()) {
|
if (request->inst->staticInst->isPrefetch()) {
|
||||||
DPRINTF(MinorMem, "Not signalling fault for faulting prefetch\n");
|
DPRINTF(MinorMem, "Not signalling fault for faulting prefetch\n");
|
||||||
}
|
}
|
||||||
@@ -1508,12 +1570,18 @@ LSQ::needsToTick()
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
Fault
|
||||||
LSQ::pushRequest(MinorDynInstPtr inst, bool isLoad, uint8_t *data,
|
LSQ::pushRequest(MinorDynInstPtr inst, bool isLoad, uint8_t *data,
|
||||||
unsigned int size, Addr addr, Request::Flags flags,
|
unsigned int size, Addr addr, Request::Flags flags,
|
||||||
uint64_t *res, AtomicOpFunctor *amo_op,
|
uint64_t *res, AtomicOpFunctor *amo_op,
|
||||||
const std::vector<bool>& byteEnable)
|
const std::vector<bool>& byteEnable)
|
||||||
{
|
{
|
||||||
|
assert(inst->translationFault == NoFault || inst->inLSQ);
|
||||||
|
|
||||||
|
if (inst->inLSQ) {
|
||||||
|
return inst->translationFault;
|
||||||
|
}
|
||||||
|
|
||||||
bool needs_burst = transferNeedsBurst(addr, size, lineWidth);
|
bool needs_burst = transferNeedsBurst(addr, size, lineWidth);
|
||||||
|
|
||||||
if (needs_burst && inst->staticInst->isAtomic()) {
|
if (needs_burst && inst->staticInst->isAtomic()) {
|
||||||
@@ -1568,12 +1636,13 @@ LSQ::pushRequest(MinorDynInstPtr inst, bool isLoad, uint8_t *data,
|
|||||||
addr, size, flags, cpu.dataMasterId(),
|
addr, size, flags, cpu.dataMasterId(),
|
||||||
/* I've no idea why we need the PC, but give it */
|
/* I've no idea why we need the PC, but give it */
|
||||||
inst->pc.instAddr(), amo_op);
|
inst->pc.instAddr(), amo_op);
|
||||||
if (!byteEnable.empty()) {
|
request->request->setByteEnable(byteEnable);
|
||||||
request->request->setByteEnable(byteEnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
requests.push(request);
|
requests.push(request);
|
||||||
|
inst->inLSQ = true;
|
||||||
request->startAddrTranslation();
|
request->startAddrTranslation();
|
||||||
|
|
||||||
|
return inst->translationFault;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1642,16 +1711,12 @@ LSQ::issuedMemBarrierInst(MinorDynInstPtr inst)
|
|||||||
void
|
void
|
||||||
LSQ::LSQRequest::makePacket()
|
LSQ::LSQRequest::makePacket()
|
||||||
{
|
{
|
||||||
|
assert(inst->translationFault == NoFault);
|
||||||
|
|
||||||
/* Make the function idempotent */
|
/* Make the function idempotent */
|
||||||
if (packet)
|
if (packet)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// if the translation faulted, do not create a packet
|
|
||||||
if (fault != NoFault) {
|
|
||||||
assert(packet == NULL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
packet = makePacketForRequest(request, isLoad, this, data);
|
packet = makePacketForRequest(request, isLoad, this, data);
|
||||||
/* Null the ret data so we know not to deallocate it when the
|
/* Null the ret data so we know not to deallocate it when the
|
||||||
* ret is destroyed. The data now belongs to the ret and
|
* ret is destroyed. The data now belongs to the ret and
|
||||||
|
|||||||
@@ -145,9 +145,6 @@ class LSQ : public Named
|
|||||||
/** The underlying request of this LSQRequest */
|
/** The underlying request of this LSQRequest */
|
||||||
RequestPtr request;
|
RequestPtr request;
|
||||||
|
|
||||||
/** Fault generated performing this request */
|
|
||||||
Fault fault;
|
|
||||||
|
|
||||||
/** Res from pushRequest */
|
/** Res from pushRequest */
|
||||||
uint64_t *res;
|
uint64_t *res;
|
||||||
|
|
||||||
@@ -160,6 +157,9 @@ class LSQ : public Named
|
|||||||
* that's visited the memory system */
|
* that's visited the memory system */
|
||||||
bool issuedToMemory;
|
bool issuedToMemory;
|
||||||
|
|
||||||
|
/** Address translation is delayed due to table walk */
|
||||||
|
bool isTranslationDelayed;
|
||||||
|
|
||||||
enum LSQRequestState
|
enum LSQRequestState
|
||||||
{
|
{
|
||||||
NotIssued, /* Newly created */
|
NotIssued, /* Newly created */
|
||||||
@@ -186,9 +186,14 @@ class LSQ : public Named
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
/** BaseTLB::Translation interface */
|
/** BaseTLB::Translation interface */
|
||||||
void markDelayed() { }
|
void markDelayed() { isTranslationDelayed = true; }
|
||||||
|
|
||||||
|
/** Instructions may want to suppress translation faults (e.g.
|
||||||
|
* non-faulting vector loads).*/
|
||||||
|
void tryToSuppressFault();
|
||||||
|
|
||||||
void disableMemAccess();
|
void disableMemAccess();
|
||||||
|
void completeDisabledMemAccess();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LSQRequest(LSQ &port_, MinorDynInstPtr inst_, bool isLoad_,
|
LSQRequest(LSQ &port_, MinorDynInstPtr inst_, bool isLoad_,
|
||||||
@@ -701,11 +706,11 @@ class LSQ : public Named
|
|||||||
|
|
||||||
/** Single interface for readMem/writeMem/amoMem to issue requests into
|
/** Single interface for readMem/writeMem/amoMem to issue requests into
|
||||||
* the LSQ */
|
* the LSQ */
|
||||||
void pushRequest(MinorDynInstPtr inst, bool isLoad, uint8_t *data,
|
Fault pushRequest(MinorDynInstPtr inst, bool isLoad, uint8_t *data,
|
||||||
unsigned int size, Addr addr, Request::Flags flags,
|
unsigned int size, Addr addr, Request::Flags flags,
|
||||||
uint64_t *res, AtomicOpFunctor *amo_op,
|
uint64_t *res, AtomicOpFunctor *amo_op,
|
||||||
const std::vector<bool>& byteEnable =
|
const std::vector<bool>& byteEnable =
|
||||||
std::vector<bool>());
|
std::vector<bool>());
|
||||||
|
|
||||||
/** Push a predicate failed-representing request into the queues just
|
/** Push a predicate failed-representing request into the queues just
|
||||||
* to maintain commit order */
|
* to maintain commit order */
|
||||||
|
|||||||
@@ -226,6 +226,7 @@ class LSQ
|
|||||||
Complete,
|
Complete,
|
||||||
Squashed,
|
Squashed,
|
||||||
Fault,
|
Fault,
|
||||||
|
PartialFault,
|
||||||
};
|
};
|
||||||
State _state;
|
State _state;
|
||||||
LSQSenderState* _senderState;
|
LSQSenderState* _senderState;
|
||||||
@@ -564,6 +565,19 @@ class LSQ
|
|||||||
return flags.isSet(Flag::Sent);
|
return flags.isSet(Flag::Sent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
isPartialFault()
|
||||||
|
{
|
||||||
|
return _state == State::PartialFault;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
isMemAccessRequired()
|
||||||
|
{
|
||||||
|
return (_state == State::Request ||
|
||||||
|
(isPartialFault() && isLoad()));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The LSQ entry is cleared
|
* The LSQ entry is cleared
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -733,7 +733,7 @@ LSQ<Impl>::pushRequest(const DynInstPtr& inst, bool isLoad, uint8_t *data,
|
|||||||
|
|
||||||
/* This is the place were instructions get the effAddr. */
|
/* This is the place were instructions get the effAddr. */
|
||||||
if (req->isTranslationComplete()) {
|
if (req->isTranslationComplete()) {
|
||||||
if (inst->getFault() == NoFault) {
|
if (req->isMemAccessRequired()) {
|
||||||
inst->effAddr = req->getVaddr();
|
inst->effAddr = req->getVaddr();
|
||||||
inst->effSize = size;
|
inst->effSize = size;
|
||||||
inst->effAddrValid(true);
|
inst->effAddrValid(true);
|
||||||
@@ -741,10 +741,17 @@ LSQ<Impl>::pushRequest(const DynInstPtr& inst, bool isLoad, uint8_t *data,
|
|||||||
if (cpu->checker) {
|
if (cpu->checker) {
|
||||||
inst->reqToVerify = std::make_shared<Request>(*req->request());
|
inst->reqToVerify = std::make_shared<Request>(*req->request());
|
||||||
}
|
}
|
||||||
|
Fault fault;
|
||||||
if (isLoad)
|
if (isLoad)
|
||||||
inst->getFault() = cpu->read(req, inst->lqIdx);
|
fault = cpu->read(req, inst->lqIdx);
|
||||||
else
|
else
|
||||||
inst->getFault() = cpu->write(req, data, inst->sqIdx);
|
fault = cpu->write(req, data, inst->sqIdx);
|
||||||
|
// inst->getFault() may have the first-fault of a
|
||||||
|
// multi-access split request at this point.
|
||||||
|
// Overwrite that only if we got another type of fault
|
||||||
|
// (e.g. re-exec).
|
||||||
|
if (fault != NoFault)
|
||||||
|
inst->getFault() = fault;
|
||||||
} else if (isLoad) {
|
} else if (isLoad) {
|
||||||
inst->setMemAccPredicate(false);
|
inst->setMemAccPredicate(false);
|
||||||
// Commit will have to clean up whatever happened. Set this
|
// Commit will have to clean up whatever happened. Set this
|
||||||
@@ -797,13 +804,16 @@ void
|
|||||||
LSQ<Impl>::SplitDataRequest::finish(const Fault &fault, const RequestPtr &req,
|
LSQ<Impl>::SplitDataRequest::finish(const Fault &fault, const RequestPtr &req,
|
||||||
ThreadContext* tc, BaseTLB::Mode mode)
|
ThreadContext* tc, BaseTLB::Mode mode)
|
||||||
{
|
{
|
||||||
_fault.push_back(fault);
|
int i;
|
||||||
assert(req == _requests[numTranslatedFragments] || this->isDelayed());
|
for (i = 0; i < _requests.size() && _requests[i] != req; i++);
|
||||||
|
assert(i < _requests.size());
|
||||||
|
_fault[i] = fault;
|
||||||
|
|
||||||
numInTranslationFragments--;
|
numInTranslationFragments--;
|
||||||
numTranslatedFragments++;
|
numTranslatedFragments++;
|
||||||
|
|
||||||
mainReq->setFlags(req->getFlags());
|
if (fault == NoFault)
|
||||||
|
mainReq->setFlags(req->getFlags());
|
||||||
|
|
||||||
if (numTranslatedFragments == _requests.size()) {
|
if (numTranslatedFragments == _requests.size()) {
|
||||||
if (_inst->isSquashed()) {
|
if (_inst->isSquashed()) {
|
||||||
@@ -811,27 +821,30 @@ LSQ<Impl>::SplitDataRequest::finish(const Fault &fault, const RequestPtr &req,
|
|||||||
} else {
|
} else {
|
||||||
_inst->strictlyOrdered(mainReq->isStrictlyOrdered());
|
_inst->strictlyOrdered(mainReq->isStrictlyOrdered());
|
||||||
flags.set(Flag::TranslationFinished);
|
flags.set(Flag::TranslationFinished);
|
||||||
auto fault_it = _fault.begin();
|
_inst->translationCompleted(true);
|
||||||
/* Ffwd to the first NoFault. */
|
|
||||||
while (fault_it != _fault.end() && *fault_it == NoFault)
|
|
||||||
fault_it++;
|
|
||||||
/* If none of the fragments faulted: */
|
|
||||||
if (fault_it == _fault.end()) {
|
|
||||||
_inst->physEffAddr = request(0)->getPaddr();
|
|
||||||
|
|
||||||
|
for (i = 0; i < _fault.size() && _fault[i] == NoFault; i++);
|
||||||
|
if (i > 0) {
|
||||||
|
_inst->physEffAddr = request(0)->getPaddr();
|
||||||
_inst->memReqFlags = mainReq->getFlags();
|
_inst->memReqFlags = mainReq->getFlags();
|
||||||
if (mainReq->isCondSwap()) {
|
if (mainReq->isCondSwap()) {
|
||||||
|
assert (i == _fault.size());
|
||||||
assert(_res);
|
assert(_res);
|
||||||
mainReq->setExtraData(*_res);
|
mainReq->setExtraData(*_res);
|
||||||
}
|
}
|
||||||
setState(State::Request);
|
if (i == _fault.size()) {
|
||||||
_inst->fault = NoFault;
|
_inst->fault = NoFault;
|
||||||
|
setState(State::Request);
|
||||||
|
} else {
|
||||||
|
_inst->fault = _fault[i];
|
||||||
|
setState(State::PartialFault);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
_inst->fault = _fault[0];
|
||||||
setState(State::Fault);
|
setState(State::Fault);
|
||||||
_inst->fault = *fault_it;
|
|
||||||
}
|
}
|
||||||
_inst->translationCompleted(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -554,6 +554,16 @@ LSQUnit<Impl>::executeLoad(const DynInstPtr &inst)
|
|||||||
if (inst->isTranslationDelayed() && load_fault == NoFault)
|
if (inst->isTranslationDelayed() && load_fault == NoFault)
|
||||||
return load_fault;
|
return load_fault;
|
||||||
|
|
||||||
|
if (load_fault != NoFault && inst->translationCompleted() &&
|
||||||
|
inst->savedReq->isPartialFault() && !inst->savedReq->isComplete()) {
|
||||||
|
assert(inst->savedReq->isSplit());
|
||||||
|
// If we have a partial fault where the mem access is not complete yet
|
||||||
|
// then the cache must have been blocked. This load will be re-executed
|
||||||
|
// when the cache gets unblocked. We will handle the fault when the
|
||||||
|
// mem access is complete.
|
||||||
|
return NoFault;
|
||||||
|
}
|
||||||
|
|
||||||
// If the instruction faulted or predicated false, then we need to send it
|
// If the instruction faulted or predicated false, then we need to send it
|
||||||
// along to commit without the instruction completing.
|
// along to commit without the instruction completing.
|
||||||
if (load_fault != NoFault || !inst->readPredicate()) {
|
if (load_fault != NoFault || !inst->readPredicate()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user