atomic.cc

来自「M5,一个功能强大的多处理器系统模拟器.很多针对处理器架构,性能的研究都使用它作」· CC 代码 · 共 837 行 · 第 1/2 页

CC
837
字号
/* * Copyright (c) 2002, 2003, 2004, 2005 * The Regents of The University of Michigan * All Rights Reserved * * This code is part of the M5 simulator. * * Permission is granted to use, copy, create derivative works and * redistribute this software and such derivative works for any * purpose, so long as the copyright notice above, this grant of * permission, and the disclaimer below appear in all copies made; and * so long as the name of The University of Michigan is not used in * any advertising or publicity pertaining to the use or distribution * of this software without specific, written prior authorization. * * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE * UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND * WITHOUT WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE. THE REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE * LIABLE FOR ANY DAMAGES, INCLUDING DIRECT, SPECIAL, INDIRECT, * INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM * ARISING OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH * DAMAGES. * * Authors: Steven K. Reinhardt */#include "arch/locked_mem.hh"#include "arch/mmaped_ipr.hh"#include "arch/utility.hh"#include "base/bigint.hh"#include "cpu/exetrace.hh"#include "cpu/simple/atomic.hh"#include "mem/packet.hh"#include "mem/packet_access.hh"#include "params/AtomicSimpleCPU.hh"#include "sim/system.hh"using namespace std;using namespace TheISA;AtomicSimpleCPU::TickEvent::TickEvent(AtomicSimpleCPU *c)    : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c){}voidAtomicSimpleCPU::TickEvent::process(){    cpu->tick();}const char *AtomicSimpleCPU::TickEvent::description() const{    return "AtomicSimpleCPU tick";}Port *AtomicSimpleCPU::getPort(const std::string &if_name, int idx){    if (if_name == "dcache_port")        return &dcachePort;    else if (if_name == "icache_port")        return &icachePort;    else if (if_name == "physmem_port") {        hasPhysMemPort = true;        return &physmemPort;    }    else        panic("No Such Port\n");}voidAtomicSimpleCPU::init(){    BaseCPU::init();    cpuId = tc->readCpuId();#if FULL_SYSTEM    for (int i = 0; i < threadContexts.size(); ++i) {        ThreadContext *tc = threadContexts[i];        // initialize CPU, including PC        TheISA::initCPU(tc, cpuId);    }#endif    if (hasPhysMemPort) {        bool snoop = false;        AddrRangeList pmAddrList;        physmemPort.getPeerAddressRanges(pmAddrList, snoop);        physMemAddr = *pmAddrList.begin();    }    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}boolAtomicSimpleCPU::CpuPort::recvTiming(PacketPtr pkt){    panic("AtomicSimpleCPU doesn't expect recvTiming callback!");    return true;}TickAtomicSimpleCPU::CpuPort::recvAtomic(PacketPtr pkt){    //Snooping a coherence request, just return    return 0;}voidAtomicSimpleCPU::CpuPort::recvFunctional(PacketPtr pkt){    //No internal storage to update, just return    return;}voidAtomicSimpleCPU::CpuPort::recvStatusChange(Status status){    if (status == RangeChange) {        if (!snoopRangeSent) {            snoopRangeSent = true;            sendStatusChange(Port::RangeChange);        }        return;    }    panic("AtomicSimpleCPU doesn't expect recvStatusChange callback!");}voidAtomicSimpleCPU::CpuPort::recvRetry(){    panic("AtomicSimpleCPU doesn't expect recvRetry callback!");}voidAtomicSimpleCPU::DcachePort::setPeer(Port *port){    Port::setPeer(port);#if FULL_SYSTEM    // Update the ThreadContext's memory ports (Functional/Virtual    // Ports)    cpu->tcBase()->connectMemPorts();#endif}AtomicSimpleCPU::AtomicSimpleCPU(Params *p)    : BaseSimpleCPU(p), tickEvent(this),      width(p->width), simulate_stalls(p->simulate_stalls),      icachePort(name() + "-iport", this), dcachePort(name() + "-iport", this),      physmemPort(name() + "-iport", this), hasPhysMemPort(false){    _status = Idle;    icachePort.snoopRangeSent = false;    dcachePort.snoopRangeSent = false;}AtomicSimpleCPU::~AtomicSimpleCPU(){}voidAtomicSimpleCPU::serialize(ostream &os){    SimObject::State so_state = SimObject::getState();    SERIALIZE_ENUM(so_state);    Status _status = status();    SERIALIZE_ENUM(_status);    BaseSimpleCPU::serialize(os);    nameOut(os, csprintf("%s.tickEvent", name()));    tickEvent.serialize(os);}voidAtomicSimpleCPU::unserialize(Checkpoint *cp, const string &section){    SimObject::State so_state;    UNSERIALIZE_ENUM(so_state);    UNSERIALIZE_ENUM(_status);    BaseSimpleCPU::unserialize(cp, section);    tickEvent.unserialize(cp, csprintf("%s.tickEvent", section));}voidAtomicSimpleCPU::resume(){    if (_status == Idle || _status == SwitchedOut)        return;    DPRINTF(SimpleCPU, "Resume\n");    assert(system->getMemoryMode() == Enums::atomic);    changeState(SimObject::Running);    if (thread->status() == ThreadContext::Active) {        if (!tickEvent.scheduled()) {            tickEvent.schedule(nextCycle());        }    }}voidAtomicSimpleCPU::switchOut(){    assert(status() == Running || status() == Idle);    _status = SwitchedOut;    tickEvent.squash();}voidAtomicSimpleCPU::takeOverFrom(BaseCPU *oldCPU){    BaseCPU::takeOverFrom(oldCPU, &icachePort, &dcachePort);    assert(!tickEvent.scheduled());    // if any of this CPU's ThreadContexts are active, mark the CPU as    // running and schedule its tick event.    for (int i = 0; i < threadContexts.size(); ++i) {        ThreadContext *tc = threadContexts[i];        if (tc->status() == ThreadContext::Active && _status != Running) {            _status = Running;            tickEvent.schedule(nextCycle());            break;        }    }    if (_status != Running) {        _status = Idle;    }    assert(threadContexts.size() == 1);    cpuId = tc->readCpuId();     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}voidAtomicSimpleCPU::activateContext(int thread_num, int delay){    DPRINTF(SimpleCPU, "ActivateContext %d (%d cycles)\n", thread_num, delay);    assert(thread_num == 0);    assert(thread);    assert(_status == Idle);    assert(!tickEvent.scheduled());    notIdleFraction++;    numCycles += tickToCycles(thread->lastActivate - thread->lastSuspend);    //Make sure ticks are still on multiples of cycles    tickEvent.schedule(nextCycle(curTick + ticks(delay)));    _status = Running;}voidAtomicSimpleCPU::suspendContext(int thread_num){    DPRINTF(SimpleCPU, "SuspendContext %d\n", thread_num);    assert(thread_num == 0);    assert(thread);    assert(_status == Running);    // tick event may not be scheduled if this gets called from inside    // an instruction's execution, e.g. "quiesce"    if (tickEvent.scheduled())        tickEvent.deschedule();    notIdleFraction--;    _status = Idle;}template <class T>FaultAtomicSimpleCPU::read(Addr addr, T &data, unsigned flags){    // use the CPU's statically allocated read request and packet objects    Request *req = &data_read_req;    if (traceData) {        traceData->setAddr(addr);    }    //The block size of our peer.    int blockSize = dcachePort.peerBlockSize();    //The size of the data we're trying to read.    int dataSize = sizeof(T);    uint8_t * dataPtr = (uint8_t *)&data;    //The address of the second part of this access if it needs to be split    //across a cache line boundary.    Addr secondAddr = roundDown(addr + dataSize - 1, blockSize);    if(secondAddr > addr)        dataSize = secondAddr - addr;    dcache_latency = 0;    while(1) {        req->setVirt(0, addr, dataSize, flags, thread->readPC());        // translate to physical address        Fault fault = thread->translateDataReadReq(req);        // Now do the access.        if (fault == NoFault) {            Packet pkt = Packet(req,                    req->isLocked() ? MemCmd::LoadLockedReq : MemCmd::ReadReq,                    Packet::Broadcast);            pkt.dataStatic(dataPtr);            if (req->isMmapedIpr())                dcache_latency += TheISA::handleIprRead(thread->getTC(), &pkt);            else {                if (hasPhysMemPort && pkt.getAddr() == physMemAddr)                    dcache_latency += physmemPort.sendAtomic(&pkt);                else                    dcache_latency += dcachePort.sendAtomic(&pkt);            }            dcache_access = true;            assert(!pkt.isError());            if (req->isLocked()) {                TheISA::handleLockedRead(thread, req);            }        }        // This will need a new way to tell if it has a dcache attached.        if (req->isUncacheable())            recordEvent("Uncached Read");        //If there's a fault, return it        if (fault != NoFault)            return fault;        //If we don't need to access a second cache line, stop now.        if (secondAddr <= addr)        {            data = gtoh(data);            return fault;        }        /*         * Set up for accessing the second cache line.         */        //Move the pointer we're reading into to the correct location.        dataPtr += dataSize;        //Adjust the size to get the remaining bytes.        dataSize = addr + sizeof(T) - secondAddr;        //And access the right address.        addr = secondAddr;    }}FaultAtomicSimpleCPU::translateDataReadAddr(Addr vaddr, Addr & paddr,        int size, unsigned flags){    // use the CPU's statically allocated read request and packet objects    Request *req = &data_read_req;    if (traceData) {        traceData->setAddr(vaddr);    }    //The block size of our peer.    int blockSize = dcachePort.peerBlockSize();    //The size of the data we're trying to read.    int dataSize = size;    bool firstTimeThrough = true;    //The address of the second part of this access if it needs to be split    //across a cache line boundary.    Addr secondAddr = roundDown(vaddr + dataSize - 1, blockSize);    if(secondAddr > vaddr)        dataSize = secondAddr - vaddr;    while(1) {        req->setVirt(0, vaddr, dataSize, flags, thread->readPC());        // translate to physical address        Fault fault = thread->translateDataReadReq(req);        //If there's a fault, return it        if (fault != NoFault)            return fault;        if (firstTimeThrough) {            paddr = req->getPaddr();            firstTimeThrough = false;        }        //If we don't need to access a second cache line, stop now.        if (secondAddr <= vaddr)            return fault;        /*         * Set up for accessing the second cache line.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?