/*
* Copyright 2014 Google, Inc.
- * Copyright (c) 2012-2013 ARM Limited
+ * Copyright (c) 2012-2013,2015,2017 ARM Limited
* All rights reserved.
*
* The license below extends only to copyright in the software and shall
* Authors: Steve Reinhardt
*/
+#include "cpu/simple/atomic.hh"
+
#include "arch/locked_mem.hh"
#include "arch/mmapped_ipr.hh"
#include "arch/utility.hh"
#include "base/bigint.hh"
#include "base/output.hh"
#include "config/the_isa.hh"
-#include "cpu/simple/atomic.hh"
#include "cpu/exetrace.hh"
#include "debug/Drain.hh"
#include "debug/ExecFaulting.hh"
#include "mem/physical.hh"
#include "params/AtomicSimpleCPU.hh"
#include "sim/faults.hh"
-#include "sim/system.hh"
#include "sim/full_system.hh"
+#include "sim/system.hh"
using namespace std;
using namespace TheISA;
-AtomicSimpleCPU::TickEvent::TickEvent(AtomicSimpleCPU *c)
- : Event(CPU_Tick_Pri), cpu(c)
-{
-}
-
-
-void
-AtomicSimpleCPU::TickEvent::process()
-{
- cpu->tick();
-}
-
-const char *
-AtomicSimpleCPU::TickEvent::description() const
-{
- return "AtomicSimpleCPU tick";
-}
-
void
AtomicSimpleCPU::init()
{
- BaseCPU::init();
-
- // Initialise the ThreadContext's memory proxies
- tcBase()->initMemProxies(tcBase());
-
- if (FullSystem && !params()->switched_out) {
- ThreadID size = threadContexts.size();
- for (ThreadID i = 0; i < size; ++i) {
- ThreadContext *tc = threadContexts[i];
- // initialize CPU, including PC
- TheISA::initCPU(tc, tc->contextId());
- }
- }
+ BaseSimpleCPU::init();
- // Atomic doesn't do MT right now, so contextId == threadId
- ifetch_req.setThreadContext(_cpuId, 0); // Add thread ID if we add MT
- data_read_req.setThreadContext(_cpuId, 0); // Add thread ID here too
- data_write_req.setThreadContext(_cpuId, 0); // Add thread ID here too
+ int cid = threadContexts[0]->contextId();
+ ifetch_req.setContext(cid);
+ data_read_req.setContext(cid);
+ data_write_req.setContext(cid);
}
AtomicSimpleCPU::AtomicSimpleCPU(AtomicSimpleCPUParams *p)
- : BaseSimpleCPU(p), tickEvent(this), width(p->width), locked(false),
+ : BaseSimpleCPU(p),
+ tickEvent([this]{ tick(); }, "AtomicSimpleCPU tick",
+ false, Event::CPU_Tick_Pri),
+ width(p->width), locked(false),
simulate_data_stalls(p->simulate_data_stalls),
simulate_inst_stalls(p->simulate_inst_stalls),
- drain_manager(NULL),
icachePort(name() + ".icache_port", this),
dcachePort(name() + ".dcache_port", this),
fastmem(p->fastmem), dcache_access(false), dcache_latency(0),
}
}
-unsigned int
-AtomicSimpleCPU::drain(DrainManager *dm)
+DrainState
+AtomicSimpleCPU::drain()
{
- assert(!drain_manager);
+ // Deschedule any power gating event (if any)
+ deschedulePowerGatingEvent();
+
if (switchedOut())
- return 0;
+ return DrainState::Drained;
if (!isDrained()) {
- DPRINTF(Drain, "Requesting drain: %s\n", pcState());
- drain_manager = dm;
- return 1;
+ DPRINTF(Drain, "Requesting drain.\n");
+ return DrainState::Draining;
} else {
if (tickEvent.scheduled())
deschedule(tickEvent);
+ activeThreads.clear();
DPRINTF(Drain, "Not executing microcode, no need to drain.\n");
- return 0;
+ return DrainState::Drained;
+ }
+}
+
+void
+AtomicSimpleCPU::threadSnoop(PacketPtr pkt, ThreadID sender)
+{
+ DPRINTF(SimpleCPU, "received snoop pkt for addr:%#x %s\n", pkt->getAddr(),
+ pkt->cmdString());
+
+ for (ThreadID tid = 0; tid < numThreads; tid++) {
+ if (tid != sender) {
+ if (getCpuAddrMonitor(tid)->doMonitor(pkt)) {
+ wakeup(tid);
+ }
+
+ TheISA::handleLockedSnoop(threadInfo[tid]->thread,
+ pkt, dcachePort.cacheBlockMask);
+ }
}
}
AtomicSimpleCPU::drainResume()
{
assert(!tickEvent.scheduled());
- assert(!drain_manager);
if (switchedOut())
return;
verifyMemoryMode();
assert(!threadContexts.empty());
- if (threadContexts.size() > 1)
- fatal("The atomic CPU only supports one thread.\n");
- if (thread->status() == ThreadContext::Active) {
- schedule(tickEvent, nextCycle());
- _status = BaseSimpleCPU::Running;
- notIdleFraction = 1;
- } else {
- _status = BaseSimpleCPU::Idle;
- notIdleFraction = 0;
+ _status = BaseSimpleCPU::Idle;
+
+ for (ThreadID tid = 0; tid < numThreads; tid++) {
+ if (threadInfo[tid]->thread->status() == ThreadContext::Active) {
+ threadInfo[tid]->notIdleFraction = 1;
+ activeThreads.push_back(tid);
+ _status = BaseSimpleCPU::Running;
+
+ // Tick if any threads active
+ if (!tickEvent.scheduled()) {
+ schedule(tickEvent, nextCycle());
+ }
+ } else {
+ threadInfo[tid]->notIdleFraction = 0;
+ }
}
- system->totalNumInsts = 0;
+ // Reschedule any power gating event (if any)
+ schedulePowerGatingEvent();
}
bool
AtomicSimpleCPU::tryCompleteDrain()
{
- if (!drain_manager)
+ if (drainState() != DrainState::Draining)
return false;
- DPRINTF(Drain, "tryCompleteDrain: %s\n", pcState());
+ DPRINTF(Drain, "tryCompleteDrain.\n");
if (!isDrained())
return false;
DPRINTF(Drain, "CPU done draining, processing drain event\n");
- drain_manager->signalDrainDone();
- drain_manager = NULL;
+ signalDrainDone();
return true;
}
// The tick event should have been descheduled by drain()
assert(!tickEvent.scheduled());
-
- ifetch_req.setThreadContext(_cpuId, 0); // Add thread ID if we add MT
- data_read_req.setThreadContext(_cpuId, 0); // Add thread ID here too
- data_write_req.setThreadContext(_cpuId, 0); // Add thread ID here too
}
void
{
DPRINTF(SimpleCPU, "ActivateContext %d\n", thread_num);
- assert(thread_num == 0);
- assert(thread);
+ assert(thread_num < numThreads);
- assert(_status == Idle);
- assert(!tickEvent.scheduled());
-
- notIdleFraction = 1;
- Cycles delta = ticksToCycles(thread->lastActivate - thread->lastSuspend);
+ threadInfo[thread_num]->notIdleFraction = 1;
+ Cycles delta = ticksToCycles(threadInfo[thread_num]->thread->lastActivate -
+ threadInfo[thread_num]->thread->lastSuspend);
numCycles += delta;
ppCycles->notify(delta);
- //Make sure ticks are still on multiples of cycles
- schedule(tickEvent, clockEdge(Cycles(0)));
+ if (!tickEvent.scheduled()) {
+ //Make sure ticks are still on multiples of cycles
+ schedule(tickEvent, clockEdge(Cycles(0)));
+ }
_status = BaseSimpleCPU::Running;
+ if (std::find(activeThreads.begin(), activeThreads.end(), thread_num)
+ == activeThreads.end()) {
+ activeThreads.push_back(thread_num);
+ }
+
+ BaseCPU::activateContext(thread_num);
}
{
DPRINTF(SimpleCPU, "SuspendContext %d\n", thread_num);
- assert(thread_num == 0);
- assert(thread);
+ assert(thread_num < numThreads);
+ activeThreads.remove(thread_num);
if (_status == Idle)
return;
assert(_status == BaseSimpleCPU::Running);
- // tick event may not be scheduled if this gets called from inside
- // an instruction's execution, e.g. "quiesce"
- if (tickEvent.scheduled())
- deschedule(tickEvent);
+ threadInfo[thread_num]->notIdleFraction = 0;
- notIdleFraction = 0;
- _status = Idle;
+ if (activeThreads.empty()) {
+ _status = Idle;
+
+ if (tickEvent.scheduled()) {
+ deschedule(tickEvent);
+ }
+ }
+
+ BaseCPU::suspendContext(thread_num);
}
// X86 ISA: Snooping an invalidation for monitor/mwait
AtomicSimpleCPU *cpu = (AtomicSimpleCPU *)(&owner);
- if(cpu->getAddrMonitor()->doMonitor(pkt)) {
- cpu->wakeup();
+
+ for (ThreadID tid = 0; tid < cpu->numThreads; tid++) {
+ if (cpu->getCpuAddrMonitor(tid)->doMonitor(pkt)) {
+ cpu->wakeup(tid);
+ }
}
// if snoop invalidates, release any associated locks
- if (pkt->isInvalidate()) {
+ // When run without caches, Invalidation packets will not be received
+ // hence we must check if the incoming packets are writes and wakeup
+ // the processor accordingly
+ if (pkt->isInvalidate() || pkt->isWrite()) {
DPRINTF(SimpleCPU, "received invalidation for addr:%#x\n",
pkt->getAddr());
- TheISA::handleLockedSnoop(cpu->thread, pkt, cacheBlockMask);
+ for (auto &t_info : cpu->threadInfo) {
+ TheISA::handleLockedSnoop(t_info->thread, pkt, cacheBlockMask);
+ }
}
return 0;
// X86 ISA: Snooping an invalidation for monitor/mwait
AtomicSimpleCPU *cpu = (AtomicSimpleCPU *)(&owner);
- if(cpu->getAddrMonitor()->doMonitor(pkt)) {
- cpu->wakeup();
+ for (ThreadID tid = 0; tid < cpu->numThreads; tid++) {
+ if (cpu->getCpuAddrMonitor(tid)->doMonitor(pkt)) {
+ cpu->wakeup(tid);
+ }
}
// if snoop invalidates, release any associated locks
if (pkt->isInvalidate()) {
DPRINTF(SimpleCPU, "received invalidation for addr:%#x\n",
pkt->getAddr());
- TheISA::handleLockedSnoop(cpu->thread, pkt, cacheBlockMask);
+ for (auto &t_info : cpu->threadInfo) {
+ TheISA::handleLockedSnoop(t_info->thread, pkt, cacheBlockMask);
+ }
}
}
Fault
-AtomicSimpleCPU::readMem(Addr addr, uint8_t * data,
- unsigned size, unsigned flags)
+AtomicSimpleCPU::readMem(Addr addr, uint8_t * data, unsigned size,
+ Request::Flags flags)
{
+ SimpleExecContext& t_info = *threadInfo[curThread];
+ SimpleThread* thread = t_info.thread;
+
// use the CPU's statically allocated read request and packet objects
Request *req = &data_read_req;
req->setVirt(0, addr, size, flags, dataMasterId(), thread->pcState().instAddr());
// translate to physical address
- Fault fault = thread->dtb->translateAtomic(req, tc, BaseTLB::Read);
+ Fault fault = thread->dtb->translateAtomic(req, thread->getTC(),
+ BaseTLB::Read);
// Now do the access.
if (fault == NoFault && !req->getFlags().isSet(Request::NO_ACCESS)) {
//If we don't need to access a second cache line, stop now.
if (secondAddr <= addr)
{
- if (req->isLocked() && fault == NoFault) {
+ if (req->isLockedRMW() && fault == NoFault) {
assert(!locked);
locked = true;
}
+
return fault;
}
}
}
-
Fault
-AtomicSimpleCPU::writeMem(uint8_t *data, unsigned size,
- Addr addr, unsigned flags, uint64_t *res)
+AtomicSimpleCPU::initiateMemRead(Addr addr, unsigned size,
+ Request::Flags flags)
{
+ panic("initiateMemRead() is for timing accesses, and should "
+ "never be called on AtomicSimpleCPU.\n");
+}
+Fault
+AtomicSimpleCPU::writeMem(uint8_t *data, unsigned size, Addr addr,
+ Request::Flags flags, uint64_t *res)
+{
+ SimpleExecContext& t_info = *threadInfo[curThread];
+ SimpleThread* thread = t_info.thread;
static uint8_t zero_array[64] = {};
if (data == NULL) {
//across a cache line boundary.
Addr secondAddr = roundDown(addr + size - 1, cacheLineSize());
- if(secondAddr > addr)
+ if (secondAddr > addr)
size = secondAddr - addr;
dcache_latency = 0;
req->taskId(taskId());
- while(1) {
+ while (1) {
req->setVirt(0, addr, size, flags, dataMasterId(), thread->pcState().instAddr());
// translate to physical address
- Fault fault = thread->dtb->translateAtomic(req, tc, BaseTLB::Write);
+ Fault fault = thread->dtb->translateAtomic(req, thread->getTC(), BaseTLB::Write);
// Now do the access.
if (fault == NoFault) {
system->getPhysMem().access(&pkt);
else
dcache_latency += dcachePort.sendAtomic(&pkt);
+
+ // Notify other threads on this CPU of write
+ threadSnoop(&pkt, curThread);
}
dcache_access = true;
assert(!pkt.isError());
//stop now.
if (fault != NoFault || secondAddr <= addr)
{
- if (req->isLocked() && fault == NoFault) {
+ if (req->isLockedRMW() && fault == NoFault) {
assert(locked);
locked = false;
}
+
+
if (fault != NoFault && req->isPrefetch()) {
return NoFault;
} else {
{
DPRINTF(SimpleCPU, "Tick\n");
+ // Change thread if multi-threaded
+ swapActiveThread();
+
+ // Set memroy request ids to current thread
+ if (numThreads > 1) {
+ ContextID cid = threadContexts[curThread]->contextId();
+
+ ifetch_req.setContext(cid);
+ data_read_req.setContext(cid);
+ data_write_req.setContext(cid);
+ }
+
+ SimpleExecContext& t_info = *threadInfo[curThread];
+ SimpleThread* thread = t_info.thread;
+
Tick latency = 0;
for (int i = 0; i < width || locked; ++i) {
if (needToFetch) {
ifetch_req.taskId(taskId());
setupFetchRequest(&ifetch_req);
- fault = thread->itb->translateAtomic(&ifetch_req, tc,
+ fault = thread->itb->translateAtomic(&ifetch_req, thread->getTC(),
BaseTLB::Execute);
}
// like the I cache. It should be flushed, and when that works
// this code should be uncommented.
//Fetch more instruction memory if necessary
- //if(decoder.needMoreBytes())
+ //if (decoder.needMoreBytes())
//{
icache_access = true;
Packet ifetch_pkt = Packet(&ifetch_req, MemCmd::ReadReq);
preExecute();
+ Tick stall_ticks = 0;
if (curStaticInst) {
- fault = curStaticInst->execute(this, traceData);
+ fault = curStaticInst->execute(&t_info, traceData);
// keep an instruction count
if (fault == NoFault) {
traceData = NULL;
}
+ if (dynamic_pointer_cast<SyscallRetryFault>(fault)) {
+ // Retry execution of system calls after a delay.
+ // Prevents immediate re-execution since conditions which
+ // caused the retry are unlikely to change every tick.
+ stall_ticks += clockEdge(syscallRetryLatency) - curTick();
+ }
+
postExecute();
}
curStaticInst->isFirstMicroop()))
instCnt++;
- Tick stall_ticks = 0;
if (simulate_inst_stalls && icache_access)
stall_ticks += icache_latency;
}
}
- if(fault != NoFault || !stayAtPC)
+ if (fault != NoFault || !t_info.stayAtPC)
advancePC(fault);
}
latency = clockPeriod();
if (_status != Idle)
- schedule(tickEvent, curTick() + latency);
+ reschedule(tickEvent, curTick() + latency, true);
}
void
AtomicSimpleCPU *
AtomicSimpleCPUParams::create()
{
- numThreads = 1;
- if (!FullSystem && workload.size() != 1)
- panic("only one workload allowed");
return new AtomicSimpleCPU(this);
}