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