Giacomo Travaglini has submitted this change. (
https://gem5-review.googlesource.com/c/public/gem5/+/30327 )
Change subject: cpu: HTM Implementation for TimingCPU
......................................................................
cpu: HTM Implementation for TimingCPU
JIRA: https://gem5.atlassian.net/browse/GEM5-587
Change-Id: I3e1de639560ea5492e914470e31bacb321425f0a
Signed-off-by: Giacomo Travaglini <giacomo.travagl...@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/30327
Reviewed-by: Jason Lowe-Power <power...@gmail.com>
Maintainer: Jason Lowe-Power <power...@gmail.com>
Tested-by: kokoro <noreply+kok...@google.com>
---
M src/cpu/simple/base.cc
M src/cpu/simple/exec_context.hh
M src/cpu/simple/timing.cc
M src/cpu/simple_thread.cc
M src/cpu/simple_thread.hh
5 files changed, 272 insertions(+), 20 deletions(-)
Approvals:
Jason Lowe-Power: Looks good to me, approved; Looks good to me, approved
kokoro: Regressions pass
diff --git a/src/cpu/simple/base.cc b/src/cpu/simple/base.cc
index a597f06..bf940ba 100644
--- a/src/cpu/simple/base.cc
+++ b/src/cpu/simple/base.cc
@@ -63,6 +63,7 @@
#include "debug/Decode.hh"
#include "debug/ExecFaulting.hh"
#include "debug/Fetch.hh"
+#include "debug/HtmCpu.hh"
#include "debug/Quiesce.hh"
#include "mem/packet.hh"
#include "mem/request.hh"
@@ -453,6 +454,17 @@
Fault interrupt = interrupts[curThread]->getInterrupt();
if (interrupt != NoFault) {
+ // hardware transactional memory
+ // Postpone taking interrupts while executing transactions.
+ assert(!std::dynamic_pointer_cast<GenericHtmFailureFault>(
+ interrupt));
+ if (t_info.inHtmTransactionalState()) {
+ DPRINTF(HtmCpu, "Deferring pending interrupt - %s -"
+ "due to transactional state\n",
+ interrupt->name());
+ return;
+ }
+
t_info.fetchOffset = 0;
interrupts[curThread]->updateIntrInfo();
interrupt->invoke(tc);
diff --git a/src/cpu/simple/exec_context.hh b/src/cpu/simple/exec_context.hh
index 41e1d3d..2b2afd2 100644
--- a/src/cpu/simple/exec_context.hh
+++ b/src/cpu/simple/exec_context.hh
@@ -475,8 +475,7 @@
Fault initiateHtmCmd(Request::Flags flags) override
{
- panic("Not yet supported\n");
- return NoFault;
+ return cpu->initiateHtmCmd(flags);
}
/**
@@ -536,29 +535,26 @@
uint64_t
getHtmTransactionUid() const override
{
- panic("Not yet supported\n");
- return 0;
+ return tcBase()->getHtmCheckpointPtr()->getHtmUid();
}
uint64_t
newHtmTransactionUid() const override
{
- panic("Not yet supported\n");
- return 0;
+ return tcBase()->getHtmCheckpointPtr()->newHtmUid();
}
bool
inHtmTransactionalState() const override
{
- panic("Not yet supported\n");
- return false;
+ return (getHtmTransactionalDepth() > 0);
}
uint64_t
getHtmTransactionalDepth() const override
{
- panic("Not yet supported\n");
- return 0;
+ assert(thread->htmTransactionStarts >=
thread->htmTransactionStops);
+ return (thread->htmTransactionStarts -
thread->htmTransactionStops);
}
/**
diff --git a/src/cpu/simple/timing.cc b/src/cpu/simple/timing.cc
index d3adbcc..f22c58d 100644
--- a/src/cpu/simple/timing.cc
+++ b/src/cpu/simple/timing.cc
@@ -48,6 +48,7 @@
#include "debug/Config.hh"
#include "debug/Drain.hh"
#include "debug/ExecFaulting.hh"
+#include "debug/HtmCpu.hh"
#include "debug/Mwait.hh"
#include "debug/SimpleCPU.hh"
#include "mem/packet.hh"
@@ -173,6 +174,10 @@
SimpleExecContext& t_info = *threadInfo[curThread];
M5_VAR_USED SimpleThread* thread = t_info.thread;
+ // hardware transactional memory
+ // Cannot switch out the CPU in the middle of a transaction
+ assert(!t_info.inHtmTransactionalState());
+
BaseSimpleCPU::switchOut();
assert(!fetchEvent.scheduled());
@@ -234,6 +239,10 @@
assert(thread_num < numThreads);
activeThreads.remove(thread_num);
+ // hardware transactional memory
+ // Cannot suspend context in the middle of a transaction.
+ assert(!threadInfo[curThread]->inHtmTransactionalState());
+
if (_status == Idle)
return;
@@ -260,6 +269,12 @@
const RequestPtr &req = pkt->req;
+ // hardware transactional memory
+ // sanity check
+ if (req->isHTMCmd()) {
+ assert(!req->isLocalAccess());
+ }
+
// We're about the issues a locked load, so tell the monitor
// to start caring about this address
if (pkt->isRead() && pkt->req->isLLSC()) {
@@ -291,6 +306,17 @@
PacketPtr pkt = buildPacket(req, read);
pkt->dataDynamic<uint8_t>(data);
+ // hardware transactional memory
+ // If the core is in transactional mode or if the request is HtmCMD
+ // to abort a transaction, the packet should reflect that it is
+ // transactional and also contain a HtmUid for debugging.
+ const bool is_htm_speculative = t_info.inHtmTransactionalState();
+ if (is_htm_speculative || req->isHTMAbort()) {
+ pkt->setHtmTransactional(t_info.getHtmTransactionUid());
+ }
+ if (req->isHTMAbort())
+ DPRINTF(HtmCpu, "htmabort htmUid=%u\n",
t_info.getHtmTransactionUid());
+
if (req->getFlags().isSet(Request::NO_ACCESS)) {
assert(!dcache_pkt);
pkt->makeResponse();
@@ -322,8 +348,21 @@
TimingSimpleCPU::sendSplitData(const RequestPtr &req1, const RequestPtr
&req2,
const RequestPtr &req, uint8_t *data, bool
read)
{
+ SimpleExecContext &t_info = *threadInfo[curThread];
PacketPtr pkt1, pkt2;
buildSplitPacket(pkt1, pkt2, req1, req2, req, data, read);
+
+ // hardware transactional memory
+ // HTM commands should never use SplitData
+ assert(!req1->isHTMCmd() && !req2->isHTMCmd());
+
+ // If the thread is executing transactionally,
+ // reflect this in the packets.
+ if (t_info.inHtmTransactionalState()) {
+ pkt1->setHtmTransactional(t_info.getHtmTransactionUid());
+ pkt2->setHtmTransactional(t_info.getHtmTransactionUid());
+ }
+
if (req->getFlags().isSet(Request::NO_ACCESS)) {
assert(!dcache_pkt);
pkt1->makeResponse();
@@ -724,6 +763,25 @@
return;
if (fault != NoFault) {
+ // hardware transactional memory
+ // If a fault occurred within a transaction
+ // ensure that the transaction aborts
+ if (t_info.inHtmTransactionalState() &&
+ !std::dynamic_pointer_cast<GenericHtmFailureFault>(fault)) {
+ DPRINTF(HtmCpu, "fault (%s) occurred - "
+ "replacing with HTM abort fault htmUid=%u\n",
+ fault->name(), t_info.getHtmTransactionUid());
+
+ Fault tmfault = std::make_shared<GenericHtmFailureFault>(
+ t_info.getHtmTransactionUid(),
+ HtmFailureFaultCause::EXCEPTION);
+
+ advancePC(tmfault);
+ reschedule(fetchEvent, clockEdge(), true);
+ _status = Faulting;
+ return;
+ }
+
DPRINTF(SimpleCPU, "Fault occured. Handling the fault\n");
advancePC(fault);
@@ -783,6 +841,19 @@
preExecute();
+
+ // hardware transactional memory
+ if (curStaticInst && curStaticInst->isHtmStart()) {
+ // if this HtmStart is not within a transaction,
+ // then assign it a new htmTransactionUid
+ if (!t_info.inHtmTransactionalState())
+ t_info.newHtmTransactionUid();
+ SimpleThread* thread = t_info.thread;
+ thread->htmTransactionStarts++;
+ DPRINTF(HtmCpu, "htmTransactionStarts++=%u\n",
+ thread->htmTransactionStarts);
+ }
+
if (curStaticInst && curStaticInst->isMemRef()) {
// load or store: just send to dcache
Fault fault = curStaticInst->initiateAcc(&t_info, traceData);
@@ -838,6 +909,15 @@
TimingSimpleCPU::IcachePort::recvTimingResp(PacketPtr pkt)
{
DPRINTF(SimpleCPU, "Received fetch response %#x\n", pkt->getAddr());
+
+ // hardware transactional memory
+ // Currently, there is no support for tracking instruction fetches
+ // in an transaction's read set.
+ if (pkt->htmTransactionFailedInCache()) {
+ panic("HTM transactional support for"
+ " instruction stream not yet supported\n");
+ }
+
// we should only ever see one response per cycle since we only
// issue a new request once this response is sunk
assert(!tickEvent.scheduled());
@@ -864,6 +944,12 @@
void
TimingSimpleCPU::completeDataAccess(PacketPtr pkt)
{
+ // hardware transactional memory
+
+ SimpleExecContext *t_info = threadInfo[curThread];
+ const bool is_htm_speculative =
+ t_info->inHtmTransactionalState();
+
// received a response from the dcache: complete the load or store
// instruction
assert(!pkt->isError());
@@ -876,13 +962,35 @@
updateCycleCounters(BaseCPU::CPU_STATE_ON);
if (pkt->senderState) {
+ // hardware transactional memory
+ // There shouldn't be HtmCmds occurring in multipacket requests
+ if (pkt->req->isHTMCmd()) {
+ panic("unexpected HTM case");
+ }
+
SplitFragmentSenderState * send_state =
dynamic_cast<SplitFragmentSenderState *>(pkt->senderState);
assert(send_state);
- delete pkt;
PacketPtr big_pkt = send_state->bigPkt;
delete send_state;
+ if (pkt->isHtmTransactional()) {
+ assert(is_htm_speculative);
+
+ big_pkt->setHtmTransactional(
+ pkt->getHtmTransactionUid()
+ );
+ }
+
+ if (pkt->htmTransactionFailedInCache()) {
+ assert(is_htm_speculative);
+ big_pkt->setHtmTransactionFailedInCache(
+ pkt->getHtmTransactionFailedInCacheRC()
+ );
+ }
+
+ delete pkt;
+
SplitMainSenderState * main_send_state =
dynamic_cast<SplitMainSenderState *>(big_pkt->senderState);
assert(main_send_state);
@@ -901,8 +1009,59 @@
_status = BaseSimpleCPU::Running;
- Fault fault = curStaticInst->completeAcc(pkt, threadInfo[curThread],
- traceData);
+ Fault fault;
+
+ // hardware transactional memory
+ // sanity checks
+ // ensure htmTransactionUids are equivalent
+ if (pkt->isHtmTransactional())
+ assert (pkt->getHtmTransactionUid() ==
+ t_info->getHtmTransactionUid());
+
+ // can't have a packet that fails a transaction while not in a
transaction
+ if (pkt->htmTransactionFailedInCache())
+ assert(is_htm_speculative);
+
+ // shouldn't fail through stores because this would be inconsistent w/
O3
+ // which cannot fault after the store has been sent to memory
+ if (pkt->htmTransactionFailedInCache() && !pkt->isWrite()) {
+ const HtmCacheFailure htm_rc =
+ pkt->getHtmTransactionFailedInCacheRC();
+ DPRINTF(HtmCpu, "HTM abortion in cache (rc=%s) detected
htmUid=%u\n",
+ htmFailureToStr(htm_rc), pkt->getHtmTransactionUid());
+
+ // Currently there are only two reasons why a transaction would
+ // fail in the memory subsystem--
+ // (1) A transactional line was evicted from the cache for
+ // space (or replacement policy) reasons.
+ // (2) Another core/device requested a cache line that is in this
+ // transaction's read/write set that is incompatible with the
+ // HTM's semantics, e.g. another core requesting exclusive
access
+ // of a line in this core's read set.
+ if (htm_rc == HtmCacheFailure::FAIL_SELF) {
+ fault = std::make_shared<GenericHtmFailureFault>(
+ t_info->getHtmTransactionUid(),
+ HtmFailureFaultCause::SIZE);
+ } else if (htm_rc == HtmCacheFailure::FAIL_REMOTE) {
+ fault = std::make_shared<GenericHtmFailureFault>(
+ t_info->getHtmTransactionUid(),
+ HtmFailureFaultCause::MEMORY);
+ } else {
+ panic("HTM - unhandled rc %s", htmFailureToStr(htm_rc));
+ }
+ } else {
+ fault = curStaticInst->completeAcc(pkt, t_info,
+ traceData);
+ }
+
+ // hardware transactional memory
+ // Track HtmStop instructions,
+ // e.g. instructions which commit a transaction.
+ if (curStaticInst && curStaticInst->isHtmStop()) {
+ t_info->thread->htmTransactionStops++;
+ DPRINTF(HtmCpu, "htmTransactionStops++=%u\n",
+ t_info->thread->htmTransactionStops);
+ }
// keep an instruction count
if (fault == NoFault)
@@ -1058,14 +1217,82 @@
Fault
TimingSimpleCPU::initiateHtmCmd(Request::Flags flags)
{
- panic("not yet supported!");
+ SimpleExecContext &t_info = *threadInfo[curThread];
+ SimpleThread* thread = t_info.thread;
+
+ const Addr addr = 0x0ul;
+ const Addr pc = thread->instAddr();
+ const int size = 8;
+
+ if (traceData)
+ traceData->setMem(addr, size, flags);
+
+ RequestPtr req = std::make_shared<Request>(
+ addr, size, flags, dataMasterId());
+
+ req->setPC(pc);
+ req->setContext(thread->contextId());
+ req->taskId(taskId());
+ req->setInstCount(t_info.numInst);
+
+ assert(req->isHTMCmd());
+
+ // Use the payload as a sanity check,
+ // the memory subsystem will clear allocated data
+ uint8_t *data = new uint8_t[size];
+ assert(data);
+ uint64_t rc = 0xdeadbeeflu;
+ memcpy (data, &rc, size);
+
+ // debugging output
+ if (req->isHTMStart())
+ DPRINTF(HtmCpu, "HTMstart htmUid=%u\n",
t_info.getHtmTransactionUid());
+ else if (req->isHTMCommit())
+ DPRINTF(HtmCpu, "HTMcommit htmUid=%u\n",
t_info.getHtmTransactionUid());
+ else if (req->isHTMCancel())
+ DPRINTF(HtmCpu, "HTMcancel htmUid=%u\n",
t_info.getHtmTransactionUid());
+ else
+ panic("initiateHtmCmd: unknown CMD");
+
+ sendData(req, data, nullptr, true);
+
return NoFault;
}
void
TimingSimpleCPU::htmSendAbortSignal(HtmFailureFaultCause cause)
{
- panic("not yet supported!");
+ SimpleExecContext& t_info = *threadInfo[curThread];
+ SimpleThread* thread = t_info.thread;
+
+ const Addr addr = 0x0ul;
+ const Addr pc = thread->instAddr();
+ const int size = 8;
+ const Request::Flags flags =
+ Request::PHYSICAL|Request::STRICT_ORDER|Request::HTM_ABORT;
+
+ if (traceData)
+ traceData->setMem(addr, size, flags);
+
+ // notify l1 d-cache (ruby) that core has aborted transaction
+
+ RequestPtr req = std::make_shared<Request>(
+ addr, size, flags, dataMasterId());
+
+ req->setPC(pc);
+ req->setContext(thread->contextId());
+ req->taskId(taskId());
+ req->setInstCount(t_info.numInst);
+ req->setHtmAbortCause(cause);
+
+ assert(req->isHTMAbort());
+
+ uint8_t *data = new uint8_t[size];
+ assert(data);
+ uint64_t rc = 0lu;
+ memcpy (data, &rc, size);
+
+ sendData(req, data, nullptr, true);
}
diff --git a/src/cpu/simple_thread.cc b/src/cpu/simple_thread.cc
index b0ffc82..28a1c80 100644
--- a/src/cpu/simple_thread.cc
+++ b/src/cpu/simple_thread.cc
@@ -72,7 +72,8 @@
isa(dynamic_cast<TheISA::ISA *>(_isa)),
predicate(true), memAccPredicate(true),
comInstEventQueue("instruction-based event queue"),
- system(_sys), itb(_itb), dtb(_dtb), decoder(isa)
+ system(_sys), itb(_itb), dtb(_dtb), decoder(isa),
+ htmTransactionStarts(0), htmTransactionStops(0)
{
assert(isa);
clearArchRegs();
@@ -84,7 +85,8 @@
isa(dynamic_cast<TheISA::ISA *>(_isa)),
predicate(true), memAccPredicate(true),
comInstEventQueue("instruction-based event queue"),
- system(_sys), itb(_itb), dtb(_dtb), decoder(isa)
+ system(_sys), itb(_itb), dtb(_dtb), decoder(isa),
+ htmTransactionStarts(0), htmTransactionStops(0)
{
assert(isa);
@@ -175,17 +177,25 @@
void
SimpleThread::htmAbortTransaction(uint64_t htm_uid, HtmFailureFaultCause
cause)
{
- panic("function not implemented\n");
+ BaseSimpleCPU *baseSimpleCpu = dynamic_cast<BaseSimpleCPU*>(baseCpu);
+ assert(baseSimpleCpu);
+
+ baseSimpleCpu->htmSendAbortSignal(cause);
+
+ // these must be reset after the abort signal has been sent
+ htmTransactionStarts = 0;
+ htmTransactionStops = 0;
}
BaseHTMCheckpointPtr&
SimpleThread::getHtmCheckpointPtr()
{
- panic("function not implemented\n");
+ return _htmCheckpoint;
}
void
SimpleThread::setHtmCheckpointPtr(BaseHTMCheckpointPtr new_cpt)
{
- panic("function not implemented\n");
+ assert(!_htmCheckpoint->valid());
+ _htmCheckpoint = std::move(new_cpt);
}
diff --git a/src/cpu/simple_thread.hh b/src/cpu/simple_thread.hh
index eb88104..5fe52cb 100644
--- a/src/cpu/simple_thread.hh
+++ b/src/cpu/simple_thread.hh
@@ -106,6 +106,9 @@
TheISA::PCState _pcState;
+ // hardware transactional memory
+ std::unique_ptr<BaseHTMCheckpoint> _htmCheckpoint;
+
/** Did this instruction execute or is it predicated false */
bool predicate;
@@ -132,6 +135,10 @@
TheISA::Decoder decoder;
+ // hardware transactional memory
+ int64_t htmTransactionStarts;
+ int64_t htmTransactionStops;
+
// constructor: initialize SimpleThread from given process structure
// FS
SimpleThread(BaseCPU *_cpu, int _thread_num, System *_system,
--
To view, visit https://gem5-review.googlesource.com/c/public/gem5/+/30327
To unsubscribe, or for help writing mail filters, visit
https://gem5-review.googlesource.com/settings
Gerrit-Project: public/gem5
Gerrit-Branch: develop
Gerrit-Change-Id: I3e1de639560ea5492e914470e31bacb321425f0a
Gerrit-Change-Number: 30327
Gerrit-PatchSet: 14
Gerrit-Owner: Giacomo Travaglini <giacomo.travagl...@arm.com>
Gerrit-Reviewer: Giacomo Travaglini <giacomo.travagl...@arm.com>
Gerrit-Reviewer: Jason Lowe-Power <power...@gmail.com>
Gerrit-Reviewer: Timothy Hayes <timothy.ha...@arm.com>
Gerrit-Reviewer: kokoro <noreply+kok...@google.com>
Gerrit-MessageType: merged
_______________________________________________
gem5-dev mailing list -- gem5-dev@gem5.org
To unsubscribe send an email to gem5-dev-le...@gem5.org
%(web_page_url)slistinfo%(cgiext)s/%(_internal_name)s