diff options
author | Gabe Black <gblack@eecs.umich.edu> | 2012-01-28 07:24:01 -0800 |
---|---|---|
committer | Gabe Black <gblack@eecs.umich.edu> | 2012-01-28 07:24:01 -0800 |
commit | c3d41a2def15cdaf2ac3984315f452dacc6a0884 (patch) | |
tree | 5324ebec3add54b934a841eee901983ac3463a7f /src/mem | |
parent | da2a4acc26ba264c3c4a12495776fd6a1c4fb133 (diff) | |
parent | 4acca8a0536d4445ed25b67edf571ae460446ab9 (diff) | |
download | gem5-c3d41a2def15cdaf2ac3984315f452dacc6a0884.tar.xz |
Merge with the main repo.
--HG--
rename : src/mem/vport.hh => src/mem/fs_translating_port_proxy.hh
rename : src/mem/translating_port.cc => src/mem/se_translating_port_proxy.cc
rename : src/mem/translating_port.hh => src/mem/se_translating_port_proxy.hh
Diffstat (limited to 'src/mem')
40 files changed, 1545 insertions, 811 deletions
diff --git a/src/mem/Bridge.py b/src/mem/Bridge.py index b48e1684d..38b344613 100644 --- a/src/mem/Bridge.py +++ b/src/mem/Bridge.py @@ -31,16 +31,12 @@ from MemObject import MemObject class Bridge(MemObject): type = 'Bridge' - side_a = Port('Side A port') - side_b = Port('Side B port') - req_size_a = Param.Int(16, "The number of requests to buffer") - req_size_b = Param.Int(16, "The number of requests to buffer") - resp_size_a = Param.Int(16, "The number of requests to buffer") - resp_size_b = Param.Int(16, "The number of requests to buffer") + slave = Port('Slave port') + master = Port('Master port') + req_size = Param.Int(16, "The number of requests to buffer") + resp_size = Param.Int(16, "The number of requests to buffer") delay = Param.Latency('0ns', "The latency of this bridge") nack_delay = Param.Latency('0ns', "The latency of this bridge") write_ack = Param.Bool(False, "Should this bridge ack writes") - filter_ranges_a = VectorParam.AddrRange([], - "What addresses shouldn't be passed through the side of the bridge") - filter_ranges_b = VectorParam.AddrRange([], - "What addresses shouldn't be passed through the side of the bridge") + ranges = VectorParam.AddrRange([AllMemory], + "Address ranges to pass through the bridge") diff --git a/src/mem/SConscript b/src/mem/SConscript index 8418a4f51..09cc93c77 100644 --- a/src/mem/SConscript +++ b/src/mem/SConscript @@ -41,14 +41,14 @@ Source('mport.cc') Source('packet.cc') Source('port.cc') Source('tport.cc') -Source('vport.cc') +Source('fs_translating_port_proxy.cc') +Source('se_translating_port_proxy.cc') if env['TARGET_ISA'] != 'no': SimObject('PhysicalMemory.py') Source('dram.cc') Source('page_table.cc') Source('physical.cc') - Source('translating_port.cc') DebugFlag('Bus') DebugFlag('BusAddrRanges') diff --git a/src/mem/bridge.cc b/src/mem/bridge.cc index 9e5e64069..b48662118 100644 --- a/src/mem/bridge.cc +++ b/src/mem/bridge.cc @@ -1,5 +1,16 @@ - /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * @@ -28,58 +39,67 @@ * * Authors: Ali Saidi * Steve Reinhardt + * Andreas Hansson */ /** * @file - * Definition of a simple bus bridge without buffering. + * Implementation of a memory-mapped bus bridge that connects a master + * and a slave through a request and response queue. */ -#include <algorithm> - -#include "base/range_ops.hh" #include "base/trace.hh" #include "debug/BusBridge.hh" #include "mem/bridge.hh" #include "params/Bridge.hh" -Bridge::BridgePort::BridgePort(const std::string &_name, - Bridge *_bridge, BridgePort *_otherPort, - int _delay, int _nack_delay, int _req_limit, - int _resp_limit, - std::vector<Range<Addr> > filter_ranges) - : Port(_name, _bridge), bridge(_bridge), otherPort(_otherPort), - delay(_delay), nackDelay(_nack_delay), filterRanges(filter_ranges), - outstandingResponses(0), queuedRequests(0), inRetry(false), - reqQueueLimit(_req_limit), respQueueLimit(_resp_limit), sendEvent(this) +Bridge::BridgeSlavePort::BridgeSlavePort(const std::string &_name, + Bridge* _bridge, + BridgeMasterPort* _masterPort, + int _delay, int _nack_delay, + int _resp_limit, + std::vector<Range<Addr> > _ranges) + : Port(_name, _bridge), bridge(_bridge), masterPort(_masterPort), + delay(_delay), nackDelay(_nack_delay), + ranges(_ranges.begin(), _ranges.end()), + outstandingResponses(0), inRetry(false), + respQueueLimit(_resp_limit), sendEvent(this) +{ +} + +Bridge::BridgeMasterPort::BridgeMasterPort(const std::string &_name, + Bridge* _bridge, + BridgeSlavePort* _slavePort, + int _delay, int _req_limit) + : Port(_name, _bridge), bridge(_bridge), slavePort(_slavePort), + delay(_delay), inRetry(false), reqQueueLimit(_req_limit), sendEvent(this) { } Bridge::Bridge(Params *p) : MemObject(p), - portA(p->name + "-portA", this, &portB, p->delay, p->nack_delay, - p->req_size_a, p->resp_size_a, p->filter_ranges_a), - portB(p->name + "-portB", this, &portA, p->delay, p->nack_delay, - p->req_size_b, p->resp_size_b, p->filter_ranges_b), + slavePort(p->name + "-slave", this, &masterPort, p->delay, + p->nack_delay, p->resp_size, p->ranges), + masterPort(p->name + "-master", this, &slavePort, p->delay, p->req_size), ackWrites(p->write_ack), _params(p) { if (ackWrites) panic("No support for acknowledging writes\n"); } -Port * +Port* Bridge::getPort(const std::string &if_name, int idx) { - BridgePort *port; + Port* port; - if (if_name == "side_a") - port = &portA; - else if (if_name == "side_b") - port = &portB; + if (if_name == "slave") + port = &slavePort; + else if (if_name == "master") + port = &masterPort; else return NULL; - if (port->getPeer() != NULL && !port->getPeer()->isDefaultPort()) + if (port->getPeer() != NULL) panic("bridge side %s already connected to %s.", if_name, port->getPeer()->name()); return port; @@ -89,106 +109,133 @@ Bridge::getPort(const std::string &if_name, int idx) void Bridge::init() { - // Make sure that both sides are connected to. - if (!portA.isConnected() || !portB.isConnected()) + // make sure both sides are connected and have the same block size + if (!slavePort.isConnected() || !masterPort.isConnected()) fatal("Both ports of bus bridge are not connected to a bus.\n"); - if (portA.peerBlockSize() != portB.peerBlockSize()) - fatal("port A size %d, port B size %d \n " \ + if (slavePort.peerBlockSize() != masterPort.peerBlockSize()) + fatal("Slave port size %d, master port size %d \n " \ "Busses don't have the same block size... Not supported.\n", - portA.peerBlockSize(), portB.peerBlockSize()); + slavePort.peerBlockSize(), masterPort.peerBlockSize()); + + // notify the master side of our address ranges + slavePort.sendRangeChange(); +} + +bool +Bridge::BridgeSlavePort::respQueueFull() +{ + return outstandingResponses == respQueueLimit; } bool -Bridge::BridgePort::respQueueFull() +Bridge::BridgeMasterPort::reqQueueFull() { - assert(outstandingResponses >= 0 && outstandingResponses <= respQueueLimit); - return outstandingResponses >= respQueueLimit; + return requestQueue.size() == reqQueueLimit; } bool -Bridge::BridgePort::reqQueueFull() +Bridge::BridgeMasterPort::recvTiming(PacketPtr pkt) { - assert(queuedRequests >= 0 && queuedRequests <= reqQueueLimit); - return queuedRequests >= reqQueueLimit; + // should only see responses on the master side + assert(pkt->isResponse()); + + // all checks are done when the request is accepted on the slave + // side, so we are guaranteed to have space for the response + + DPRINTF(BusBridge, "recvTiming: src %d dest %d addr 0x%x\n", + pkt->getSrc(), pkt->getDest(), pkt->getAddr()); + + DPRINTF(BusBridge, "Request queue size: %d\n", requestQueue.size()); + + slavePort->queueForSendTiming(pkt); + + return true; } -/** Function called by the port when the bus is receiving a Timing - * transaction.*/ bool -Bridge::BridgePort::recvTiming(PacketPtr pkt) +Bridge::BridgeSlavePort::recvTiming(PacketPtr pkt) { + // should only see requests on the slave side + assert(pkt->isRequest()); + DPRINTF(BusBridge, "recvTiming: src %d dest %d addr 0x%x\n", - pkt->getSrc(), pkt->getDest(), pkt->getAddr()); + pkt->getSrc(), pkt->getDest(), pkt->getAddr()); - DPRINTF(BusBridge, "Local queue size: %d outreq: %d outresp: %d\n", - sendQueue.size(), queuedRequests, outstandingResponses); - DPRINTF(BusBridge, "Remote queue size: %d outreq: %d outresp: %d\n", - otherPort->sendQueue.size(), otherPort->queuedRequests, - otherPort->outstandingResponses); + DPRINTF(BusBridge, "Response queue size: %d outresp: %d\n", + responseQueue.size(), outstandingResponses); - if (pkt->isRequest() && otherPort->reqQueueFull()) { - DPRINTF(BusBridge, "Remote queue full, nacking\n"); + if (masterPort->reqQueueFull()) { + DPRINTF(BusBridge, "Request queue full, nacking\n"); nackRequest(pkt); return true; } if (pkt->needsResponse()) { if (respQueueFull()) { - DPRINTF(BusBridge, "Local queue full, no space for response, nacking\n"); - DPRINTF(BusBridge, "queue size: %d outreq: %d outstanding resp: %d\n", - sendQueue.size(), queuedRequests, outstandingResponses); + DPRINTF(BusBridge, + "Response queue full, no space for response, nacking\n"); + DPRINTF(BusBridge, + "queue size: %d outstanding resp: %d\n", + responseQueue.size(), outstandingResponses); nackRequest(pkt); return true; } else { DPRINTF(BusBridge, "Request Needs response, reserving space\n"); + assert(outstandingResponses != respQueueLimit); ++outstandingResponses; } } - otherPort->queueForSendTiming(pkt); + masterPort->queueForSendTiming(pkt); return true; } void -Bridge::BridgePort::nackRequest(PacketPtr pkt) +Bridge::BridgeSlavePort::nackRequest(PacketPtr pkt) { // Nack the packet pkt->makeTimingResponse(); pkt->setNacked(); - //put it on the list to send + // The Nack packets are stored in the response queue just like any + // other response, but they do not occupy any space as this is + // tracked by the outstandingResponses, this guarantees space for + // the Nack packets, but implicitly means we have an (unrealistic) + // unbounded Nack queue. + + // put it on the list to send Tick readyTime = curTick() + nackDelay; PacketBuffer *buf = new PacketBuffer(pkt, readyTime, true); // nothing on the list, add it and we're done - if (sendQueue.empty()) { + if (responseQueue.empty()) { assert(!sendEvent.scheduled()); - schedule(sendEvent, readyTime); - sendQueue.push_back(buf); + bridge->schedule(sendEvent, readyTime); + responseQueue.push_back(buf); return; } assert(sendEvent.scheduled() || inRetry); // does it go at the end? - if (readyTime >= sendQueue.back()->ready) { - sendQueue.push_back(buf); + if (readyTime >= responseQueue.back()->ready) { + responseQueue.push_back(buf); return; } // ok, somewhere in the middle, fun - std::list<PacketBuffer*>::iterator i = sendQueue.begin(); - std::list<PacketBuffer*>::iterator end = sendQueue.end(); - std::list<PacketBuffer*>::iterator begin = sendQueue.begin(); + std::list<PacketBuffer*>::iterator i = responseQueue.begin(); + std::list<PacketBuffer*>::iterator end = responseQueue.end(); + std::list<PacketBuffer*>::iterator begin = responseQueue.begin(); bool done = false; while (i != end && !done) { if (readyTime < (*i)->ready) { if (i == begin) - reschedule(sendEvent, readyTime); - sendQueue.insert(i,buf); + bridge->reschedule(sendEvent, readyTime); + responseQueue.insert(i,buf); done = true; } i++; @@ -196,51 +243,60 @@ Bridge::BridgePort::nackRequest(PacketPtr pkt) assert(done); } - void -Bridge::BridgePort::queueForSendTiming(PacketPtr pkt) +Bridge::BridgeMasterPort::queueForSendTiming(PacketPtr pkt) { - if (pkt->isResponse()) { - // This is a response for a request we forwarded earlier. The - // corresponding PacketBuffer should be stored in the packet's - // senderState field. - - PacketBuffer *buf = dynamic_cast<PacketBuffer*>(pkt->senderState); - assert(buf != NULL); - // set up new packet dest & senderState based on values saved - // from original request - buf->fixResponse(pkt); - - DPRINTF(BusBridge, "response, new dest %d\n", pkt->getDest()); - delete buf; + Tick readyTime = curTick() + delay; + PacketBuffer *buf = new PacketBuffer(pkt, readyTime); + + // If we're about to put this packet at the head of the queue, we + // need to schedule an event to do the transmit. Otherwise there + // should already be an event scheduled for sending the head + // packet. + if (requestQueue.empty()) { + bridge->schedule(sendEvent, readyTime); } + assert(requestQueue.size() != reqQueueLimit); - if (pkt->isRequest()) { - ++queuedRequests; - } + requestQueue.push_back(buf); +} +void +Bridge::BridgeSlavePort::queueForSendTiming(PacketPtr pkt) +{ + // This is a response for a request we forwarded earlier. The + // corresponding PacketBuffer should be stored in the packet's + // senderState field. + PacketBuffer *buf = dynamic_cast<PacketBuffer*>(pkt->senderState); + assert(buf != NULL); + // set up new packet dest & senderState based on values saved + // from original request + buf->fixResponse(pkt); + + DPRINTF(BusBridge, "response, new dest %d\n", pkt->getDest()); + delete buf; Tick readyTime = curTick() + delay; - PacketBuffer *buf = new PacketBuffer(pkt, readyTime); + buf = new PacketBuffer(pkt, readyTime); // If we're about to put this packet at the head of the queue, we // need to schedule an event to do the transmit. Otherwise there // should already be an event scheduled for sending the head // packet. - if (sendQueue.empty()) { - schedule(sendEvent, readyTime); + if (responseQueue.empty()) { + bridge->schedule(sendEvent, readyTime); } - sendQueue.push_back(buf); + responseQueue.push_back(buf); } void -Bridge::BridgePort::trySend() +Bridge::BridgeMasterPort::trySend() { - assert(!sendQueue.empty()); + assert(!requestQueue.empty()); - PacketBuffer *buf = sendQueue.front(); + PacketBuffer *buf = requestQueue.front(); assert(buf->ready <= curTick()); @@ -249,110 +305,195 @@ Bridge::BridgePort::trySend() DPRINTF(BusBridge, "trySend: origSrc %d dest %d addr 0x%x\n", buf->origSrc, pkt->getDest(), pkt->getAddr()); - bool wasReq = pkt->isRequest(); - bool was_nacked_here = buf->nackedHere; - // If the send was successful, make sure sender state was set to NULL // otherwise we could get a NACK back of a packet that didn't expect a // response and we would try to use freed memory. Packet::SenderState *old_sender_state = pkt->senderState; - if (pkt->isRequest() && !buf->expectResponse) + if (!buf->expectResponse) pkt->senderState = NULL; if (sendTiming(pkt)) { // send successful - sendQueue.pop_front(); - buf->pkt = NULL; // we no longer own packet, so it's not safe to look at it + requestQueue.pop_front(); + // we no longer own packet, so it's not safe to look at it + buf->pkt = NULL; - if (buf->expectResponse) { - // Must wait for response - DPRINTF(BusBridge, " successful: awaiting response (%d)\n", - outstandingResponses); - } else { + if (!buf->expectResponse) { // no response expected... deallocate packet buffer now. DPRINTF(BusBridge, " successful: no response expected\n"); delete buf; } - if (wasReq) - --queuedRequests; - else if (!was_nacked_here) + // If there are more packets to send, schedule event to try again. + if (!requestQueue.empty()) { + buf = requestQueue.front(); + DPRINTF(BusBridge, "Scheduling next send\n"); + bridge->schedule(sendEvent, std::max(buf->ready, curTick() + 1)); + } + } else { + DPRINTF(BusBridge, " unsuccessful\n"); + pkt->senderState = old_sender_state; + inRetry = true; + } + + DPRINTF(BusBridge, "trySend: request queue size: %d\n", + requestQueue.size()); +} + +void +Bridge::BridgeSlavePort::trySend() +{ + assert(!responseQueue.empty()); + + PacketBuffer *buf = responseQueue.front(); + + assert(buf->ready <= curTick()); + + PacketPtr pkt = buf->pkt; + + DPRINTF(BusBridge, "trySend: origSrc %d dest %d addr 0x%x\n", + buf->origSrc, pkt->getDest(), pkt->getAddr()); + + bool was_nacked_here = buf->nackedHere; + + // no need to worry about the sender state since we are not + // modifying it + + if (sendTiming(pkt)) { + DPRINTF(BusBridge, " successful\n"); + // send successful + responseQueue.pop_front(); + // this is a response... deallocate packet buffer now. + delete buf; + + if (!was_nacked_here) { + assert(outstandingResponses != 0); --outstandingResponses; + } // If there are more packets to send, schedule event to try again. - if (!sendQueue.empty()) { - buf = sendQueue.front(); + if (!responseQueue.empty()) { + buf = responseQueue.front(); DPRINTF(BusBridge, "Scheduling next send\n"); - schedule(sendEvent, std::max(buf->ready, curTick() + 1)); + bridge->schedule(sendEvent, std::max(buf->ready, curTick() + 1)); } } else { DPRINTF(BusBridge, " unsuccessful\n"); - pkt->senderState = old_sender_state; inRetry = true; } - DPRINTF(BusBridge, "trySend: queue size: %d outreq: %d outstanding resp: %d\n", - sendQueue.size(), queuedRequests, outstandingResponses); + DPRINTF(BusBridge, "trySend: queue size: %d outstanding resp: %d\n", + responseQueue.size(), outstandingResponses); } +void +Bridge::BridgeMasterPort::recvRetry() +{ + inRetry = false; + Tick nextReady = requestQueue.front()->ready; + if (nextReady <= curTick()) + trySend(); + else + bridge->schedule(sendEvent, nextReady); +} void -Bridge::BridgePort::recvRetry() +Bridge::BridgeSlavePort::recvRetry() { inRetry = false; - Tick nextReady = sendQueue.front()->ready; + Tick nextReady = responseQueue.front()->ready; if (nextReady <= curTick()) trySend(); else - schedule(sendEvent, nextReady); + bridge->schedule(sendEvent, nextReady); } -/** Function called by the port when the bus is receiving a Atomic - * transaction.*/ Tick -Bridge::BridgePort::recvAtomic(PacketPtr pkt) +Bridge::BridgeMasterPort::recvAtomic(PacketPtr pkt) { - return delay + otherPort->sendAtomic(pkt); + // master port should never receive any atomic access (panic only + // works once the other side, i.e. the busses, respects this) + // + //panic("Master port on %s got a recvAtomic\n", bridge->name()); + return 0; +} + +Tick +Bridge::BridgeSlavePort::recvAtomic(PacketPtr pkt) +{ + return delay + masterPort->sendAtomic(pkt); } -/** Function called by the port when the bus is receiving a Functional - * transaction.*/ void -Bridge::BridgePort::recvFunctional(PacketPtr pkt) +Bridge::BridgeMasterPort::recvFunctional(PacketPtr pkt) +{ + // master port should never receive any functional access (panic + // only works once the other side, i.e. the busses, respect this) + + // panic("Master port on %s got a recvFunctional\n", bridge->name()); +} + +void +Bridge::BridgeSlavePort::recvFunctional(PacketPtr pkt) { std::list<PacketBuffer*>::iterator i; pkt->pushLabel(name()); - for (i = sendQueue.begin(); i != sendQueue.end(); ++i) { + // check the response queue + for (i = responseQueue.begin(); i != responseQueue.end(); ++i) { if (pkt->checkFunctional((*i)->pkt)) { pkt->makeResponse(); return; } } + // also check the master port's request queue + if (masterPort->checkFunctional(pkt)) { + return; + } + pkt->popLabel(); // fall through if pkt still not satisfied - otherPort->sendFunctional(pkt); + masterPort->sendFunctional(pkt); } -/** Function called by the port when the bus is receiving a status change.*/ +bool +Bridge::BridgeMasterPort::checkFunctional(PacketPtr pkt) +{ + bool found = false; + std::list<PacketBuffer*>::iterator i = requestQueue.begin(); + + while(i != requestQueue.end() && !found) { + if (pkt->checkFunctional((*i)->pkt)) { + pkt->makeResponse(); + found = true; + } + ++i; + } + + return found; +} + +/** Function called by the port when the bridge is receiving a range change.*/ void -Bridge::BridgePort::recvStatusChange(Port::Status status) +Bridge::BridgeMasterPort::recvRangeChange() { - otherPort->sendStatusChange(status); + // no need to forward as the bridge has a fixed set of ranges } void -Bridge::BridgePort::getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop) +Bridge::BridgeSlavePort::recvRangeChange() +{ + // is a slave port so do nothing +} + +AddrRangeList +Bridge::BridgeSlavePort::getAddrRanges() { - otherPort->getPeerAddressRanges(resp, snoop); - FilterRangeList(filterRanges, resp); - // we don't allow snooping across bridges - snoop = false; + return ranges; } Bridge * diff --git a/src/mem/bridge.hh b/src/mem/bridge.hh index 732717dd4..d389c0a5e 100644 --- a/src/mem/bridge.hh +++ b/src/mem/bridge.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * @@ -27,11 +39,13 @@ * * Authors: Ali Saidi * Steve Reinhardt + * Andreas Hansson */ /** * @file - * Declaration of a simple bus bridge object with no buffering + * Declaration of a memory-mapped bus bridge that connects a master + * and a slave through a request and response queue. */ #ifndef __MEM_BRIDGE_HH__ @@ -49,90 +63,232 @@ #include "params/Bridge.hh" #include "sim/eventq.hh" +/** + * A bridge is used to interface two different busses (or in general a + * memory-mapped master and slave), with buffering for requests and + * responses. The bridge has a fixed delay for packets passing through + * it and responds to a fixed set of address ranges. + * + * The bridge comprises a slave port and a master port, that buffer + * outgoing responses and requests respectively. Buffer space is + * reserved when a request arrives, also reserving response space + * before forwarding the request. An incoming request is always + * accepted (recvTiming returns true), but is potentially NACKed if + * there is no request space or response space. + */ class Bridge : public MemObject { protected: - /** Declaration of the buses port type, one will be instantiated for each - of the interfaces connecting to the bus. */ - class BridgePort : public Port + + /** + * A packet buffer stores packets along with their sender state + * and scheduled time for transmission. + */ + class PacketBuffer : public Packet::SenderState, public FastAlloc { + + public: + Tick ready; + PacketPtr pkt; + bool nackedHere; + Packet::SenderState *origSenderState; + short origSrc; + bool expectResponse; + + PacketBuffer(PacketPtr _pkt, Tick t, bool nack = false) + : ready(t), pkt(_pkt), nackedHere(nack), + origSenderState(_pkt->senderState), + origSrc(nack ? _pkt->getDest() : _pkt->getSrc() ), + expectResponse(_pkt->needsResponse() && !nack) + + { + if (!pkt->isResponse() && !nack) + pkt->senderState = this; + } + + void fixResponse(PacketPtr pkt) + { + assert(pkt->senderState == this); + pkt->setDest(origSrc); + pkt->senderState = origSenderState; + } + }; + + // Forward declaration to allow the slave port to have a pointer + class BridgeMasterPort; + + /** + * The port on the side that receives requests and sends + * responses. The slave port has a set of address ranges that it + * is responsible for. The slave port also has a buffer for the + * responses not yet sent. + */ + class BridgeSlavePort : public Port { + + private: + /** A pointer to the bridge to which this port belongs. */ Bridge *bridge; /** - * Pointer to the port on the other side of the bridge + * Pointer to the master port on the other side of the bridge * (connected to the other bus). */ - BridgePort *otherPort; + BridgeMasterPort* masterPort; - /** Minimum delay though this bridge. */ + /** Minimum request delay though this bridge. */ Tick delay; - /** Min delay to respond to a nack. */ + /** Min delay to respond with a nack. */ Tick nackDelay; - /** Pass ranges from one side of the bridge to the other? */ - std::vector<Range<Addr> > filterRanges; + /** Address ranges to pass through the bridge */ + AddrRangeList ranges; + + /** + * Response packet queue. Response packets are held in this + * queue for a specified delay to model the processing delay + * of the bridge. + */ + std::list<PacketBuffer*> responseQueue; - class PacketBuffer : public Packet::SenderState, public FastAlloc { + /** Counter to track the outstanding responses. */ + unsigned int outstandingResponses; + + /** If we're waiting for a retry to happen. */ + bool inRetry; + + /** Max queue size for reserved responses. */ + unsigned int respQueueLimit; + + /** + * Is this side blocked from accepting new response packets. + * + * @return true if the reserved space has reached the set limit + */ + bool respQueueFull(); + + /** + * Turn the request packet into a NACK response and put it in + * the response queue and schedule its transmission. + * + * @param pkt the request packet to NACK + */ + void nackRequest(PacketPtr pkt); + + /** + * Handle send event, scheduled when the packet at the head of + * the response queue is ready to transmit (for timing + * accesses only). + */ + void trySend(); + + /** + * Private class for scheduling sending of responses from the + * response queue. + */ + class SendEvent : public Event + { + BridgeSlavePort *port; public: - Tick ready; - PacketPtr pkt; - bool nackedHere; - Packet::SenderState *origSenderState; - short origSrc; - bool expectResponse; - - PacketBuffer(PacketPtr _pkt, Tick t, bool nack = false) - : ready(t), pkt(_pkt), nackedHere(nack), - origSenderState(_pkt->senderState), - origSrc(nack ? _pkt->getDest() : _pkt->getSrc() ), - expectResponse(_pkt->needsResponse() && !nack) - - { - if (!pkt->isResponse() && !nack) - pkt->senderState = this; - } - - void fixResponse(PacketPtr pkt) - { - assert(pkt->senderState == this); - pkt->setDest(origSrc); - pkt->senderState = origSenderState; - } + SendEvent(BridgeSlavePort *p) : port(p) {} + virtual void process() { port->trySend(); } + virtual const char *description() const { return "bridge send"; } }; + /** Send event for the response queue. */ + SendEvent sendEvent; + + public: + /** - * Outbound packet queue. Packets are held in this queue for a - * specified delay to model the processing delay of the - * bridge. + * Constructor for the BridgeSlavePort. + * + * @param _name the port name including the owner + * @param _bridge the structural owner + * @param _masterPort the master port on the other side of the bridge + * @param _delay the delay from seeing a response to sending it + * @param _nack_delay the delay from a NACK to sending the response + * @param _resp_limit the size of the response queue + * @param _ranges a number of address ranges to forward */ - std::list<PacketBuffer*> sendQueue; + BridgeSlavePort(const std::string &_name, Bridge *_bridge, + BridgeMasterPort* _masterPort, int _delay, + int _nack_delay, int _resp_limit, + std::vector<Range<Addr> > _ranges); - int outstandingResponses; - int queuedRequests; + /** + * Queue a response packet to be sent out later and also schedule + * a send if necessary. + * + * @param pkt a response to send out after a delay + */ + void queueForSendTiming(PacketPtr pkt); - /** If we're waiting for a retry to happen.*/ - bool inRetry; + protected: - /** Max queue size for outbound packets */ - int reqQueueLimit; + /** When receiving a timing request from the peer port, + pass it to the bridge. */ + virtual bool recvTiming(PacketPtr pkt); - /** Max queue size for reserved responses. */ - int respQueueLimit; + /** When receiving a retry request from the peer port, + pass it to the bridge. */ + virtual void recvRetry(); + + /** When receiving a Atomic requestfrom the peer port, + pass it to the bridge. */ + virtual Tick recvAtomic(PacketPtr pkt); + + /** When receiving a Functional request from the peer port, + pass it to the bridge. */ + virtual void recvFunctional(PacketPtr pkt); /** - * Is this side blocked from accepting outbound packets? + * When receiving a range change on the slave side do nothing. */ - bool respQueueFull(); - bool reqQueueFull(); + virtual void recvRangeChange(); - void queueForSendTiming(PacketPtr pkt); + /** When receiving a address range request the peer port, + pass it to the bridge. */ + virtual AddrRangeList getAddrRanges(); + }; - void finishSend(PacketBuffer *buf); - void nackRequest(PacketPtr pkt); + /** + * Port on the side that forwards requests and receives + * responses. The master port has a buffer for the requests not + * yet sent. + */ + class BridgeMasterPort : public Port + { + + private: + + /** A pointer to the bridge to which this port belongs. */ + Bridge* bridge; + + /** + * Pointer to the slave port on the other side of the bridge + * (connected to the other bus). + */ + BridgeSlavePort* slavePort; + + /** Minimum delay though this bridge. */ + Tick delay; + + /** + * Request packet queue. Request packets are held in this + * queue for a specified delay to model the processing delay + * of the bridge. + */ + std::list<PacketBuffer*> requestQueue; + + /** If we're waiting for a retry to happen. */ + bool inRetry; + + /** Max queue size for request packets */ + unsigned int reqQueueLimit; /** * Handle send event, scheduled when the packet at the head of @@ -141,24 +297,62 @@ class Bridge : public MemObject */ void trySend(); + /** + * Private class for scheduling sending of requests from the + * request queue. + */ class SendEvent : public Event { - BridgePort *port; + BridgeMasterPort *port; public: - SendEvent(BridgePort *p) : port(p) {} + SendEvent(BridgeMasterPort *p) : port(p) {} virtual void process() { port->trySend(); } virtual const char *description() const { return "bridge send"; } }; + /** Send event for the request queue. */ SendEvent sendEvent; public: - /** Constructor for the BusPort.*/ - BridgePort(const std::string &_name, Bridge *_bridge, - BridgePort *_otherPort, int _delay, int _nack_delay, - int _req_limit, int _resp_limit, - std::vector<Range<Addr> > filter_ranges); + + /** + * Constructor for the BridgeMasterPort. + * + * @param _name the port name including the owner + * @param _bridge the structural owner + * @param _slavePort the slave port on the other side of the bridge + * @param _delay the delay from seeing a request to sending it + * @param _req_limit the size of the request queue + */ + BridgeMasterPort(const std::string &_name, Bridge *_bridge, + BridgeSlavePort* _slavePort, int _delay, + int _req_limit); + + /** + * Is this side blocked from accepting new request packets. + * + * @return true if the occupied space has reached the set limit + */ + bool reqQueueFull(); + + /** + * Queue a request packet to be sent out later and also schedule + * a send if necessary. + * + * @param pkt a request to send out after a delay + */ + void queueForSendTiming(PacketPtr pkt); + + /** + * Check a functional request against the packets in our + * request queue. + * + * @param pkt packet to check against + * + * @return true if we find a match + */ + bool checkFunctional(PacketPtr pkt); protected: @@ -178,17 +372,17 @@ class Bridge : public MemObject pass it to the bridge. */ virtual void recvFunctional(PacketPtr pkt); - /** When receiving a status changefrom the peer port, - pass it to the bridge. */ - virtual void recvStatusChange(Status status); - - /** When receiving a address range request the peer port, - pass it to the bridge. */ - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop); + /** + * When receiving a range change, pass it through the bridge. + */ + virtual void recvRangeChange(); }; - BridgePort portA, portB; + /** Slave port of the bridge. */ + BridgeSlavePort slavePort; + + /** Master port of the bridge. */ + BridgeMasterPort masterPort; /** If this bridge should acknowledge writes. */ bool ackWrites; diff --git a/src/mem/bus.cc b/src/mem/bus.cc index db71b86b7..a20f90108 100644 --- a/src/mem/bus.cc +++ b/src/mem/bus.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * @@ -26,6 +38,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Ali Saidi + * Andreas Hansson */ /** @@ -33,9 +46,6 @@ * Definition of a bus object. */ -#include <algorithm> -#include <limits> - #include "base/misc.hh" #include "base/trace.hh" #include "debug/Bus.hh" @@ -46,8 +56,7 @@ Bus::Bus(const BusParams *p) : MemObject(p), busId(p->bus_id), clock(p->clock), headerCycles(p->header_cycles), width(p->width), tickNextIdle(0), - drainEvent(NULL), busIdle(this), inRetry(false), maxId(0), - defaultPort(NULL), funcPort(NULL), funcPortId(-4), + drainEvent(NULL), busIdle(this), inRetry(false), defaultPortId(-1), useDefaultRange(p->use_default_range), defaultBlockSize(p->block_size), cachedBlockSize(0), cachedBlockSizeValid(false) { @@ -58,65 +67,44 @@ Bus::Bus(const BusParams *p) fatal("Bus clock period must be positive\n"); if (headerCycles <= 0) fatal("Number of header cycles must be positive\n"); - clearBusCache(); clearPortCache(); } Port * Bus::getPort(const std::string &if_name, int idx) { + std::string portName; + int id = interfaces.size(); if (if_name == "default") { - if (defaultPort == NULL) { - defaultPort = new BusPort(csprintf("%s-default",name()), this, - defaultId); - cachedBlockSizeValid = false; - return defaultPort; + if (defaultPortId == -1) { + defaultPortId = id; + portName = csprintf("%s-default", name()); } else - fatal("Default port already set\n"); - } - int id; - if (if_name == "functional") { - if (!funcPort) { - id = maxId++; - funcPort = new BusPort(csprintf("%s-p%d-func", name(), id), this, id); - funcPortId = id; - interfaces[id] = funcPort; - } - return funcPort; + fatal("Default port already set on %s\n", name()); + } else { + portName = csprintf("%s-p%d", name(), id); } - - // if_name ignored? forced to be empty? - id = maxId++; - assert(maxId < std::numeric_limits<typeof(maxId)>::max()); - BusPort *bp = new BusPort(csprintf("%s-p%d", name(), id), this, id); - interfaces[id] = bp; + BusPort *bp = new BusPort(portName, this, id); + interfaces.push_back(bp); cachedBlockSizeValid = false; return bp; } void -Bus::deletePortRefs(Port *p) -{ - - BusPort *bp = dynamic_cast<BusPort*>(p); - if (bp == NULL) - panic("Couldn't convert Port* to BusPort*\n"); - // If this is our one functional port - if (funcPort == bp) - return; - interfaces.erase(bp->getId()); - clearBusCache(); - delete bp; -} - -/** Get the ranges of anyone other buses that we are connected to. */ -void Bus::init() { - m5::hash_map<short,BusPort*>::iterator intIter; - - for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++) - intIter->second->sendStatusChange(Port::RangeChange); + std::vector<BusPort*>::iterator intIter; + + // iterate over our interfaces and determine which of our neighbours + // are snooping and add them as snoopers + for (intIter = interfaces.begin(); intIter != interfaces.end(); + intIter++) { + if ((*intIter)->getPeer()->isSnooping()) { + DPRINTF(BusAddrRanges, "Adding snooping neighbour %s\n", + (*intIter)->getPeer()->name()); + snoopPorts.push_back(*intIter); + } + } } Bus::BusFreeEvent::BusFreeEvent(Bus *_bus) @@ -194,16 +182,7 @@ Bus::recvTiming(PacketPtr pkt) { short src = pkt->getSrc(); - BusPort *src_port; - if (src == defaultId) - src_port = defaultPort; - else { - src_port = checkBusCache(src); - if (src_port == NULL) { - src_port = interfaces[src]; - updateBusCache(src, src_port); - } - } + BusPort *src_port = interfaces[src]; // If the bus is busy, or other devices are in line ahead of the current // one, put this device on the retry list. @@ -229,8 +208,7 @@ Bus::recvTiming(PacketPtr pkt) if (dest == Packet::Broadcast) { dest_port_id = findPort(pkt->getAddr()); - dest_port = (dest_port_id == defaultId) ? - defaultPort : interfaces[dest_port_id]; + dest_port = interfaces[dest_port_id]; SnoopIter s_end = snoopPorts.end(); for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { BusPort *p = *s_iter; @@ -241,20 +219,10 @@ Bus::recvTiming(PacketPtr pkt) } } } else { - assert(dest < maxId); + assert(dest < interfaces.size()); assert(dest != src); // catch infinite loops dest_port_id = dest; - if (dest_port_id == defaultId) - dest_port = defaultPort; - else { - dest_port = checkBusCache(dest); - if (dest_port == NULL) { - dest_port = interfaces[dest_port_id]; - // updateBusCache(dest_port_id, dest_port); - } - } - dest_port = (dest_port_id == defaultId) ? - defaultPort : interfaces[dest_port_id]; + dest_port = interfaces[dest_port_id]; } if (dest_port_id == src) { @@ -352,7 +320,7 @@ Bus::findPort(Addr addr) for (AddrRangeIter i = defaultRange.begin(); i != a_end; i++) { if (*i == addr) { DPRINTF(Bus, " found addr %#llx on default\n", addr); - return defaultId; + return defaultPortId; } } @@ -361,7 +329,7 @@ Bus::findPort(Addr addr) DPRINTF(Bus, "Unable to find destination for addr %#llx, " "will use default port\n", addr); - return defaultId; + return defaultPortId; } @@ -385,16 +353,7 @@ Bus::recvAtomic(PacketPtr pkt) int orig_src = pkt->getSrc(); int target_port_id = findPort(pkt->getAddr()); - BusPort *target_port; - if (target_port_id == defaultId) - target_port = defaultPort; - else { - target_port = checkBusCache(target_port_id); - if (target_port == NULL) { - target_port = interfaces[target_port_id]; - updateBusCache(target_port_id, target_port); - } - } + BusPort *target_port = interfaces[target_port_id]; SnoopIter s_end = snoopPorts.end(); for (SnoopIter s_iter = snoopPorts.begin(); s_iter != s_end; s_iter++) { @@ -450,7 +409,7 @@ Bus::recvFunctional(PacketPtr pkt) assert(pkt->getDest() == Packet::Broadcast); int port_id = findPort(pkt->getAddr()); - Port *port = (port_id == defaultId) ? defaultPort : interfaces[port_id]; + Port *port = interfaces[port_id]; // The packet may be changed by another bus on snoops, restore the // id after each int src_id = pkt->getSrc(); @@ -483,30 +442,25 @@ Bus::recvFunctional(PacketPtr pkt) } } -/** Function called by the port when the bus is receiving a status change.*/ +/** Function called by the port when the bus is receiving a range change.*/ void -Bus::recvStatusChange(Port::Status status, int id) +Bus::recvRangeChange(int id) { AddrRangeList ranges; - bool snoops; AddrRangeIter iter; - if (inRecvStatusChange.count(id)) + if (inRecvRangeChange.count(id)) return; - inRecvStatusChange.insert(id); - - assert(status == Port::RangeChange && - "The other statuses need to be implemented."); + inRecvRangeChange.insert(id); DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n", id); clearPortCache(); - if (id == defaultId) { + if (id == defaultPortId) { defaultRange.clear(); // Only try to update these ranges if the user set a default responder. if (useDefaultRange) { - defaultPort->getPeerAddressRanges(ranges, snoops); - assert(snoops == false); + AddrRangeList ranges = interfaces[id]->getPeer()->getAddrRanges(); for(iter = ranges.begin(); iter != ranges.end(); iter++) { defaultRange.push_back(*iter); DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n", @@ -515,7 +469,7 @@ Bus::recvStatusChange(Port::Status status, int id) } } else { - assert((id < maxId && id >= 0) || id == defaultId); + assert(id < interfaces.size() && id >= 0); BusPort *port = interfaces[id]; // Clean out any previously existent ids @@ -527,20 +481,7 @@ Bus::recvStatusChange(Port::Status status, int id) portIter++; } - for (SnoopIter s_iter = snoopPorts.begin(); - s_iter != snoopPorts.end(); ) { - if ((*s_iter)->getId() == id) - s_iter = snoopPorts.erase(s_iter); - else - s_iter++; - } - - port->getPeerAddressRanges(ranges, snoops); - - if (snoops) { - DPRINTF(BusAddrRanges, "Adding id %d to snoop list\n", id); - snoopPorts.push_back(port); - } + ranges = port->getPeer()->getAddrRanges(); for (iter = ranges.begin(); iter != ranges.end(); iter++) { DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n", @@ -557,28 +498,25 @@ Bus::recvStatusChange(Port::Status status, int id) // tell all our peers that our address range has changed. // Don't tell the device that caused this change, it already knows - m5::hash_map<short,BusPort*>::iterator intIter; + std::vector<BusPort*>::const_iterator intIter; for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++) - if (intIter->first != id && intIter->first != funcPortId) - intIter->second->sendStatusChange(Port::RangeChange); + if ((*intIter)->getId() != id) + (*intIter)->sendRangeChange(); - if (id != defaultId && defaultPort) - defaultPort->sendStatusChange(Port::RangeChange); - inRecvStatusChange.erase(id); + inRecvRangeChange.erase(id); } -void -Bus::addressRanges(AddrRangeList &resp, bool &snoop, int id) +AddrRangeList +Bus::getAddrRanges(int id) { - resp.clear(); - snoop = false; + AddrRangeList ranges; DPRINTF(BusAddrRanges, "received address range request, returning:\n"); for (AddrRangeIter dflt_iter = defaultRange.begin(); dflt_iter != defaultRange.end(); dflt_iter++) { - resp.push_back(*dflt_iter); + ranges.push_back(*dflt_iter); DPRINTF(BusAddrRanges, " -- Dflt: %#llx : %#llx\n",dflt_iter->start, dflt_iter->end); } @@ -601,12 +539,21 @@ Bus::addressRanges(AddrRangeList &resp, bool &snoop, int id) } } if (portIter->second != id && !subset) { - resp.push_back(portIter->first); + ranges.push_back(portIter->first); DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n", portIter->first.start, portIter->first.end); } } + return ranges; +} + +bool +Bus::isSnooping(int id) +{ + // in essence, answer the question if there are other snooping + // ports rather than the port that is asking + bool snoop = false; for (SnoopIter s_iter = snoopPorts.begin(); s_iter != snoopPorts.end(); s_iter++) { if ((*s_iter)->getId() != id) { @@ -614,6 +561,7 @@ Bus::addressRanges(AddrRangeList &resp, bool &snoop, int id) break; } } + return snoop; } unsigned diff --git a/src/mem/bus.hh b/src/mem/bus.hh index 17d22ec83..5b8b373a5 100644 --- a/src/mem/bus.hh +++ b/src/mem/bus.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2002-2005 The Regents of The University of Michigan * All rights reserved. * @@ -27,6 +39,7 @@ * * Authors: Ron Dreslinski * Ali Saidi + * Andreas Hansson */ /** @@ -79,7 +92,16 @@ class Bus : public MemObject void onRetryList(bool newVal) { _onRetryList = newVal; } - int getId() { return id; } + int getId() const { return id; } + + /** + * Determine if this port should be considered a snooper. This + * is determined by the bus. + * + * @return a boolean that is true if this port is snooping + */ + virtual bool isSnooping() + { return bus->isSnooping(id); } protected: @@ -98,10 +120,10 @@ class Bus : public MemObject virtual void recvFunctional(PacketPtr pkt) { pkt->setSrc(id); bus->recvFunctional(pkt); } - /** When reciving a status changefrom the peer port (at id), + /** When reciving a range change from the peer port (at id), pass it to the bus. */ - virtual void recvStatusChange(Status status) - { bus->recvStatusChange(status, id); } + virtual void recvRangeChange() + { bus->recvRangeChange(id); } /** When reciving a retry from the peer port (at id), pass it to the bus. */ @@ -112,9 +134,8 @@ class Bus : public MemObject // downstream from this bus, yes? That is, the union of all // the 'owned' address ranges of all the other interfaces on // this bus... - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop) - { bus->addressRanges(resp, snoop, id); } + virtual AddrRangeList getAddrRanges() + { return bus->getAddrRanges(id); } // Ask the bus to ask everyone on the bus what their block size is and // take the max of it. This might need to be changed a bit if we ever @@ -147,9 +168,6 @@ class Bus : public MemObject Event * drainEvent; - - static const int defaultId = -3; //Make it unique from Broadcast - typedef range_map<Addr,int>::iterator PortIter; range_map<Addr, int> portMap; @@ -174,8 +192,8 @@ class Bus : public MemObject * requests. */ void recvRetry(int id); - /** Function called by the port when the bus is recieving a status change.*/ - void recvStatusChange(Port::Status status, int id); + /** Function called by the port when the bus is recieving a range change.*/ + void recvRangeChange(int id); /** Find which port connected to this bus (if any) should be given a packet * with this address. @@ -238,12 +256,23 @@ class Bus : public MemObject portCache[0].valid = false; } - /** Process address range request. - * @param resp addresses that we can respond to - * @param snoop addresses that we would like to snoop - * @param id ide of the busport that made the request. + /** + * Return the address ranges this port is responsible for. + * + * @param id id of the bus port that made the request + * + * @return a list of non-overlapping address ranges + */ + AddrRangeList getAddrRanges(int id); + + /** + * Determine if the bus port is snooping or not. + * + * @param id id of the bus port that made the request + * + * @return a boolean indicating if this port is snooping or not */ - void addressRanges(AddrRangeList &resp, bool &snoop, int id); + bool isSnooping(int id); /** Calculate the timing parameters for the packet. Updates the * firstWordTime and finishTime fields of the packet object. @@ -264,14 +293,11 @@ class Bus : public MemObject BusFreeEvent busIdle; bool inRetry; - std::set<int> inRecvStatusChange; + std::set<int> inRecvRangeChange; - /** max number of bus ids we've handed out so far */ - short maxId; - - /** An array of pointers to the peer port interfaces + /** An ordered vector of pointers to the peer port interfaces connected to this bus.*/ - m5::hash_map<short,BusPort*> interfaces; + std::vector<BusPort*> interfaces; /** An array of pointers to ports that retry should be called on because the * original send failed for whatever reason.*/ @@ -300,10 +326,7 @@ class Bus : public MemObject } /** Port that handles requests that don't match any of the interfaces.*/ - BusPort *defaultPort; - - BusPort *funcPort; - int funcPortId; + short defaultPortId; /** If true, use address range provided by default device. Any address not handled by another port and not in default device's @@ -315,59 +338,10 @@ class Bus : public MemObject unsigned cachedBlockSize; bool cachedBlockSizeValid; - // Cache for the peer port interfaces - struct BusCache { - bool valid; - short id; - BusPort *port; - }; - - BusCache busCache[3]; - - // Checks the peer port interfaces cache for the port id and returns - // a pointer to the matching port - inline BusPort* checkBusCache(short id) { - if (busCache[0].valid && id == busCache[0].id) { - return busCache[0].port; - } - if (busCache[1].valid && id == busCache[1].id) { - return busCache[1].port; - } - if (busCache[2].valid && id == busCache[2].id) { - return busCache[2].port; - } - - return NULL; - } - - // Replaces the earliest entry in the cache with a new entry - inline void updateBusCache(short id, BusPort *port) { - busCache[2].valid = busCache[1].valid; - busCache[2].id = busCache[1].id; - busCache[2].port = busCache[1].port; - - busCache[1].valid = busCache[0].valid; - busCache[1].id = busCache[0].id; - busCache[1].port = busCache[0].port; - - busCache[0].valid = true; - busCache[0].id = id; - busCache[0].port = port; - } - - // Invalidates the cache. Needs to be called in constructor. - inline void clearBusCache() { - busCache[2].valid = false; - busCache[1].valid = false; - busCache[0].valid = false; - } - - public: /** A function used to return the port associated with this bus object. */ virtual Port *getPort(const std::string &if_name, int idx = -1); - virtual void deletePortRefs(Port *p); virtual void init(); virtual void startup(); diff --git a/src/mem/cache/base.cc b/src/mem/cache/base.cc index 09e3d0869..2b7fa4b9f 100644 --- a/src/mem/cache/base.cc +++ b/src/mem/cache/base.cc @@ -71,11 +71,9 @@ BaseCache::BaseCache(const Params *p) } void -BaseCache::CachePort::recvStatusChange(Port::Status status) +BaseCache::CachePort::recvRangeChange() const { - if (status == Port::RangeChange) { - otherPort->sendStatusChange(Port::RangeChange); - } + otherPort->sendRangeChange(); } @@ -127,7 +125,7 @@ BaseCache::CachePort::clearBlocked() mustSendRetry = false; SendRetryEvent *ev = new SendRetryEvent(this, true); // @TODO: need to find a better time (next bus cycle?) - schedule(ev, curTick() + 1); + cache->schedule(ev, curTick() + 1); } } @@ -137,7 +135,7 @@ BaseCache::init() { if (!cpuSidePort || !memSidePort) panic("Cache not hooked up on both sides\n"); - cpuSidePort->sendStatusChange(Port::RangeChange); + cpuSidePort->sendRangeChange(); } diff --git a/src/mem/cache/base.hh b/src/mem/cache/base.hh index 8c39a2400..fded6fca6 100644 --- a/src/mem/cache/base.hh +++ b/src/mem/cache/base.hh @@ -105,7 +105,7 @@ class BaseCache : public MemObject CachePort(const std::string &_name, BaseCache *_cache, const std::string &_label); - virtual void recvStatusChange(Status status); + virtual void recvRangeChange() const; virtual unsigned deviceBlockSize() const; diff --git a/src/mem/cache/cache.hh b/src/mem/cache/cache.hh index 1ed138bb5..b5c95b301 100644 --- a/src/mem/cache/cache.hh +++ b/src/mem/cache/cache.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2012 ARM Limited + * All rights reserved. + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2002-2005 The Regents of The University of Michigan * All rights reserved. * @@ -78,8 +90,7 @@ class Cache : public BaseCache return static_cast<Cache<TagStore> *>(cache); } - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop); + virtual AddrRangeList getAddrRanges(); virtual bool recvTiming(PacketPtr pkt); @@ -106,8 +117,7 @@ class Cache : public BaseCache void processSendEvent(); - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop); + virtual bool isSnooping(); virtual bool recvTiming(PacketPtr pkt); @@ -204,7 +214,6 @@ class Cache : public BaseCache Cache(const Params *p, TagStore *tags, BasePrefetcher *prefetcher); virtual Port *getPort(const std::string &if_name, int idx = -1); - virtual void deletePortRefs(Port *p); void regStats(); @@ -225,10 +234,9 @@ class Cache : public BaseCache /** * Performs the access specified by the request. * @param pkt The request to perform. - * @return The result of the access. + * @param fromCpuSide from the CPU side port or the memory side port */ - void functionalAccess(PacketPtr pkt, CachePort *incomingPort, - CachePort *otherSidePort); + void functionalAccess(PacketPtr pkt, bool fromCpuSide); /** * Handles a response (cache line fill/write ack) from the bus. diff --git a/src/mem/cache/cache_impl.hh b/src/mem/cache/cache_impl.hh index a56495abb..13484eb79 100644 --- a/src/mem/cache/cache_impl.hh +++ b/src/mem/cache/cache_impl.hh @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 ARM Limited + * Copyright (c) 2010-2012 ARM Limited * All rights reserved. * * The license below extends only to copyright in the software and shall @@ -103,30 +103,13 @@ Cache<TagStore>::getPort(const std::string &if_name, int idx) return cpuSidePort; } else if (if_name == "mem_side") { return memSidePort; - } else if (if_name == "functional") { - CpuSidePort *funcPort = - new CpuSidePort(name() + "-cpu_side_funcport", this, - "CpuSideFuncPort"); - funcPort->setOtherPort(memSidePort); - return funcPort; - } else { + } else { panic("Port name %s unrecognized\n", if_name); } } template<class TagStore> void -Cache<TagStore>::deletePortRefs(Port *p) -{ - if (cpuSidePort == p || memSidePort == p) - panic("Can only delete functional ports\n"); - - delete p; -} - - -template<class TagStore> -void Cache<TagStore>::cmpAndSwap(BlkType *blk, PacketPtr pkt) { uint64_t overwrite_val; @@ -768,9 +751,7 @@ Cache<TagStore>::atomicAccess(PacketPtr pkt) template<class TagStore> void -Cache<TagStore>::functionalAccess(PacketPtr pkt, - CachePort *incomingPort, - CachePort *otherSidePort) +Cache<TagStore>::functionalAccess(PacketPtr pkt, bool fromCpuSide) { Addr blk_addr = blockAlign(pkt->getAddr()); BlkType *blk = tags->findBlock(pkt->getAddr()); @@ -796,10 +777,10 @@ Cache<TagStore>::functionalAccess(PacketPtr pkt, (mshr && mshr->inService && mshr->isPendingDirty())); bool done = have_dirty - || incomingPort->checkFunctional(pkt) + || cpuSidePort->checkFunctional(pkt) || mshrQueue.checkFunctional(pkt, blk_addr) || writeBuffer.checkFunctional(pkt, blk_addr) - || otherSidePort->checkFunctional(pkt); + || memSidePort->checkFunctional(pkt); DPRINTF(Cache, "functional %s %x %s%s%s\n", pkt->cmdString(), pkt->getAddr(), @@ -812,7 +793,15 @@ Cache<TagStore>::functionalAccess(PacketPtr pkt, if (done) { pkt->makeResponse(); } else { - otherSidePort->sendFunctional(pkt); + // if it came as a request from the CPU side then make sure it + // continues towards the memory side + if (fromCpuSide) { + memSidePort->sendFunctional(pkt); + } else if (forwardSnoops) { + // if it came from the memory side, it must be a snoop request + // and we should only forward it if we are forwarding snoops + cpuSidePort->sendFunctional(pkt); + } } } @@ -1559,14 +1548,15 @@ Cache<TagStore>::nextMSHRReadyTime() /////////////// template<class TagStore> -void +AddrRangeList Cache<TagStore>::CpuSidePort:: -getDeviceAddressRanges(AddrRangeList &resp, bool &snoop) +getAddrRanges() { // CPU side port doesn't snoop; it's a target only. It can // potentially respond to any address. - snoop = false; - resp.push_back(myCache()->getAddrRange()); + AddrRangeList ranges; + ranges.push_back(myCache()->getAddrRange()); + return ranges; } @@ -1598,7 +1588,7 @@ template<class TagStore> void Cache<TagStore>::CpuSidePort::recvFunctional(PacketPtr pkt) { - myCache()->functionalAccess(pkt, this, otherPort); + myCache()->functionalAccess(pkt, true); } @@ -1617,14 +1607,13 @@ CpuSidePort::CpuSidePort(const std::string &_name, Cache<TagStore> *_cache, /////////////// template<class TagStore> -void -Cache<TagStore>::MemSidePort:: -getDeviceAddressRanges(AddrRangeList &resp, bool &snoop) +bool +Cache<TagStore>::MemSidePort::isSnooping() { // Memory-side port always snoops, but never passes requests // through to targets on the cpu side (so we don't add anything to // the address range list). - snoop = true; + return true; } @@ -1670,7 +1659,7 @@ template<class TagStore> void Cache<TagStore>::MemSidePort::recvFunctional(PacketPtr pkt) { - myCache()->functionalAccess(pkt, this, otherPort); + myCache()->functionalAccess(pkt, false); } @@ -1723,7 +1712,7 @@ Cache<TagStore>::MemSidePort::sendPacket() // @TODO: need to facotr in prefetch requests here somehow if (nextReady != MaxTick) { DPRINTF(CachePort, "more packets to send @ %d\n", nextReady); - schedule(sendEvent, std::max(nextReady, curTick() + 1)); + cache->schedule(sendEvent, std::max(nextReady, curTick() + 1)); } else { // no more to send right now: if we're draining, we may be done if (drainEvent && !sendEvent->scheduled()) { diff --git a/src/mem/vport.cc b/src/mem/fs_translating_port_proxy.cc index ab061c019..c0898a003 100644 --- a/src/mem/vport.cc +++ b/src/mem/fs_translating_port_proxy.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * @@ -26,6 +38,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Ali Saidi + * Andreas Hansson */ /** @@ -34,49 +47,81 @@ */ #include "base/chunk_generator.hh" -#include "config/the_isa.hh" +#include "cpu/base.hh" #include "cpu/thread_context.hh" -#include "mem/vport.hh" +#include "mem/fs_translating_port_proxy.hh" + +using namespace TheISA; + +FSTranslatingPortProxy::FSTranslatingPortProxy(ThreadContext *tc) + : PortProxy(*(tc->getCpuPtr()->getPort("dcache_port"))), _tc(tc) +{ +} + +FSTranslatingPortProxy::FSTranslatingPortProxy(Port &port) + : PortProxy(port), _tc(NULL) +{ +} + +FSTranslatingPortProxy::~FSTranslatingPortProxy() +{ +} void -VirtualPort::readBlob(Addr addr, uint8_t *p, int size) +FSTranslatingPortProxy::readBlob(Addr addr, uint8_t *p, int size) { Addr paddr; for (ChunkGenerator gen(addr, size, TheISA::PageBytes); !gen.done(); - gen.next()) + gen.next()) { - if (tc) - paddr = TheISA::vtophys(tc,gen.addr()); + if (_tc) + paddr = TheISA::vtophys(_tc,gen.addr()); else paddr = TheISA::vtophys(gen.addr()); - FunctionalPort::readBlob(paddr, p, gen.size()); + PortProxy::readBlob(paddr, p, gen.size()); p += gen.size(); } } void -VirtualPort::writeBlob(Addr addr, uint8_t *p, int size) +FSTranslatingPortProxy::writeBlob(Addr addr, uint8_t *p, int size) { Addr paddr; for (ChunkGenerator gen(addr, size, TheISA::PageBytes); !gen.done(); - gen.next()) + gen.next()) { - if (tc) - paddr = TheISA::vtophys(tc,gen.addr()); + if (_tc) + paddr = TheISA::vtophys(_tc,gen.addr()); else paddr = TheISA::vtophys(gen.addr()); - FunctionalPort::writeBlob(paddr, p, gen.size()); + PortProxy::writeBlob(paddr, p, gen.size()); p += gen.size(); } } void +FSTranslatingPortProxy::memsetBlob(Addr address, uint8_t v, int size) +{ + Addr paddr; + for (ChunkGenerator gen(address, size, TheISA::PageBytes); !gen.done(); + gen.next()) + { + if (_tc) + paddr = TheISA::vtophys(_tc,gen.addr()); + else + paddr = TheISA::vtophys(gen.addr()); + + PortProxy::memsetBlob(paddr, v, gen.size()); + } +} + +void CopyOut(ThreadContext *tc, void *dest, Addr src, size_t cplen) { uint8_t *dst = (uint8_t *)dest; - VirtualPort *vp = tc->getVirtPort(); + FSTranslatingPortProxy* vp = tc->getVirtProxy(); vp->readBlob(src, dst, cplen); } @@ -85,7 +130,7 @@ void CopyIn(ThreadContext *tc, Addr dest, void *source, size_t cplen) { uint8_t *src = (uint8_t *)source; - VirtualPort *vp = tc->getVirtPort(); + FSTranslatingPortProxy* vp = tc->getVirtProxy(); vp->writeBlob(dest, src, cplen); } @@ -93,21 +138,25 @@ CopyIn(ThreadContext *tc, Addr dest, void *source, size_t cplen) void CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen) { - int len = 0; char *start = dst; - VirtualPort *vp = tc->getVirtPort(); + FSTranslatingPortProxy* vp = tc->getVirtProxy(); - do { - vp->readBlob(vaddr++, (uint8_t*)dst++, 1); - } while (len < maxlen && start[len++] != 0 ); + bool foundNull = false; + while ((dst - start + 1) < maxlen && !foundNull) { + vp->readBlob(vaddr++, (uint8_t*)dst, 1); + if (dst == '\0') + foundNull = true; + dst++; + } - dst[len] = 0; + if (!foundNull) + *dst = '\0'; } void CopyStringIn(ThreadContext *tc, char *src, Addr vaddr) { - VirtualPort *vp = tc->getVirtPort(); + FSTranslatingPortProxy* vp = tc->getVirtProxy(); for (ChunkGenerator gen(vaddr, strlen(src), TheISA::PageBytes); !gen.done(); gen.next()) { diff --git a/src/mem/vport.hh b/src/mem/fs_translating_port_proxy.hh index 7cf24587c..826def902 100644 --- a/src/mem/vport.hh +++ b/src/mem/fs_translating_port_proxy.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2006 The Regents of The University of Michigan * All rights reserved. * @@ -26,44 +38,49 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: Ali Saidi + * Andreas Hansson */ /** * @file - * Virtual Port Object Declaration. These ports incorporate some translation - * into their access methods. Thus you can use one to read and write data - * to/from virtual addresses. + * TranslatingPortProxy Object Declaration for FS. + * + * Port proxies are used when non structural entities need access to + * the memory system. Proxy objects replace the previous + * FunctionalPort, TranslatingPort and VirtualPort objects, which + * provided the same functionality as the proxies, but were instances + * of ports not corresponding to real structural ports of the + * simulated system. Via the port proxies all the accesses go through + * an actual port and thus are transparent to a potentially + * distributed memory and automatically adhere to the memory map of + * the system. */ -#ifndef __MEM_VPORT_HH__ -#define __MEM_VPORT_HH__ +#ifndef __MEM_FS_PORT_PROXY_HH__ +#define __MEM_FS_PORT_PROXY_HH__ #include "arch/vtophys.hh" -#include "mem/port_impl.hh" +#include "mem/port_proxy.hh" -/** A class that translates a virtual address to a physical address and then - * calls the above read/write functions. If a thread context is provided the - * address can alway be translated, If not it can only be translated if it is a - * simple address masking operation (such as alpha super page accesses). +/** + * A TranslatingPortProxy in FS mode translates a virtual address to a + * physical address and then calls the read/write functions of the + * port. If a thread context is provided the address can alway be + * translated, If not it can only be translated if it is a simple + * address masking operation (such as alpha super page accesses). */ - - -class VirtualPort : public FunctionalPort +class FSTranslatingPortProxy : public PortProxy { private: - ThreadContext *tc; + ThreadContext* _tc; public: - VirtualPort(const std::string &_name, ThreadContext *_tc = NULL) - : FunctionalPort(_name), tc(_tc) - {} - /** Return true if we have an thread context. This is used to - * prevent someone from accidently deleting the cpus statically - * allocated vport. - * @return true if a thread context isn't valid - */ - bool nullThreadContext() { return tc != NULL; } + FSTranslatingPortProxy(ThreadContext* tc); + + FSTranslatingPortProxy(Port &port); + + virtual ~FSTranslatingPortProxy(); /** Version of readblob that translates virt->phys and deals * with page boundries. */ @@ -72,13 +89,16 @@ class VirtualPort : public FunctionalPort /** Version of writeBlob that translates virt->phys and deals * with page boundries. */ virtual void writeBlob(Addr addr, uint8_t *p, int size); -}; + /** + * Fill size bytes starting at addr with byte value val. + */ + virtual void memsetBlob(Addr address, uint8_t v, int size); +}; void CopyOut(ThreadContext *tc, void *dest, Addr src, size_t cplen); void CopyIn(ThreadContext *tc, Addr dest, void *source, size_t cplen); void CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen); void CopyStringIn(ThreadContext *tc, char *src, Addr vaddr); -#endif //__MEM_VPORT_HH__ - +#endif //__MEM_FS_PORT_PROXY_HH__ diff --git a/src/mem/mem_object.cc b/src/mem/mem_object.cc index 20a1b4cd8..111d3718c 100644 --- a/src/mem/mem_object.cc +++ b/src/mem/mem_object.cc @@ -34,9 +34,3 @@ MemObject::MemObject(const Params *params) : SimObject(params) { } - -void -MemObject::deletePortRefs(Port *p) -{ - panic("This object does not support port deletion\n"); -} diff --git a/src/mem/mem_object.hh b/src/mem/mem_object.hh index b8bf4b939..5865fc935 100644 --- a/src/mem/mem_object.hh +++ b/src/mem/mem_object.hh @@ -56,10 +56,6 @@ class MemObject : public SimObject public: /** Additional function to return the Port of a memory object. */ virtual Port *getPort(const std::string &if_name, int idx = -1) = 0; - - /** Tell object that this port is about to disappear, so it should remove it - * from any structures that it's keeping it in. */ - virtual void deletePortRefs(Port *p) ; }; #endif //__MEM_MEM_OBJECT_HH__ diff --git a/src/mem/packet.cc b/src/mem/packet.cc index 64f4fcd14..4c3a785dc 100644 --- a/src/mem/packet.cc +++ b/src/mem/packet.cc @@ -168,6 +168,9 @@ MemCmd::commandInfo[] = { SET2(IsRequest, IsPrint), InvalidCmd, "PrintReq" }, /* Flush Request */ { SET3(IsRequest, IsFlush, NeedsExclusive), InvalidCmd, "FlushReq" }, + /* Invalidation Request */ + { SET3(NeedsExclusive, IsInvalidate, IsRequest), + InvalidCmd, "InvalidationReq" }, }; bool diff --git a/src/mem/packet.hh b/src/mem/packet.hh index 6347c21ea..e49ce7577 100644 --- a/src/mem/packet.hh +++ b/src/mem/packet.hh @@ -108,6 +108,7 @@ class MemCmd // Fake simulator-only commands PrintReq, // Print state matching address FlushReq, //request for a cache flush + InvalidationReq, // request for address to be invalidated from lsq NUM_MEM_CMDS }; diff --git a/src/mem/physical.cc b/src/mem/physical.cc index d5c4e892f..09ed8b292 100644 --- a/src/mem/physical.cc +++ b/src/mem/physical.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 ARM Limited + * Copyright (c) 2010-2011 ARM Limited * All rights reserved * * The license below extends only to copyright in the software and shall @@ -115,7 +115,7 @@ PhysicalMemory::init() for (PortIterator pi = ports.begin(); pi != ports.end(); ++pi) { if (*pi) - (*pi)->sendStatusChange(Port::RangeChange); + (*pi)->sendRangeChange(); } } @@ -125,6 +125,65 @@ PhysicalMemory::~PhysicalMemory() munmap((char*)pmemAddr, size()); } +void +PhysicalMemory::regStats() +{ + using namespace Stats; + + bytesRead + .name(name() + ".bytes_read") + .desc("Number of bytes read from this memory") + ; + bytesInstRead + .name(name() + ".bytes_inst_read") + .desc("Number of instructions bytes read from this memory") + ; + bytesWritten + .name(name() + ".bytes_written") + .desc("Number of bytes written to this memory") + ; + numReads + .name(name() + ".num_reads") + .desc("Number of read requests responded to by this memory") + ; + numWrites + .name(name() + ".num_writes") + .desc("Number of write requests responded to by this memory") + ; + numOther + .name(name() + ".num_other") + .desc("Number of other requests responded to by this memory") + ; + bwRead + .name(name() + ".bw_read") + .desc("Total read bandwidth from this memory (bytes/s)") + .precision(0) + .prereq(bytesRead) + ; + bwInstRead + .name(name() + ".bw_inst_read") + .desc("Instruction read bandwidth from this memory (bytes/s)") + .precision(0) + .prereq(bytesInstRead) + ; + bwWrite + .name(name() + ".bw_write") + .desc("Write bandwidth from this memory (bytes/s)") + .precision(0) + .prereq(bytesWritten) + ; + bwTotal + .name(name() + ".bw_total") + .desc("Total bandwidth to/from this memory (bytes/s)") + .precision(0) + .prereq(bwTotal) + ; + bwRead = bytesRead / simSeconds; + bwInstRead = bytesInstRead / simSeconds; + bwWrite = bytesWritten / simSeconds; + bwTotal = (bytesRead + bytesWritten) / simSeconds; +} + unsigned PhysicalMemory::deviceBlockSize() const { @@ -303,6 +362,7 @@ PhysicalMemory::doAtomicAccess(PacketPtr pkt) assert(!pkt->req->isInstFetch()); TRACE_PACKET("Read/Write"); + numOther++; } else if (pkt->isRead()) { assert(!pkt->isWrite()); if (pkt->isLLSC()) { @@ -311,12 +371,18 @@ PhysicalMemory::doAtomicAccess(PacketPtr pkt) if (pmemAddr) memcpy(pkt->getPtr<uint8_t>(), hostAddr, pkt->getSize()); TRACE_PACKET(pkt->req->isInstFetch() ? "IFetch" : "Read"); + numReads++; + bytesRead += pkt->getSize(); + if (pkt->req->isInstFetch()) + bytesInstRead += pkt->getSize(); } else if (pkt->isWrite()) { if (writeOK(pkt)) { if (pmemAddr) memcpy(hostAddr, pkt->getPtr<uint8_t>(), pkt->getSize()); assert(!pkt->req->isInstFetch()); TRACE_PACKET("Write"); + numWrites++; + bytesWritten += pkt->getSize(); } } else if (pkt->isInvalidate()) { //upgrade or invalidate @@ -371,13 +437,6 @@ PhysicalMemory::doFunctionalAccess(PacketPtr pkt) Port * PhysicalMemory::getPort(const std::string &if_name, int idx) { - // Accept request for "functional" port for backwards compatibility - // with places where this function is called from C++. I'd prefer - // to move all these into Python someday. - if (if_name == "functional") { - return new MemoryPort(csprintf("%s-functional", name()), this); - } - if (if_name != "port") { panic("PhysicalMemory::getPort: unknown port %s requested", if_name); } @@ -397,36 +456,30 @@ PhysicalMemory::getPort(const std::string &if_name, int idx) return port; } - -void -PhysicalMemory::recvStatusChange(Port::Status status) -{ -} - PhysicalMemory::MemoryPort::MemoryPort(const std::string &_name, PhysicalMemory *_memory) : SimpleTimingPort(_name, _memory), memory(_memory) { } void -PhysicalMemory::MemoryPort::recvStatusChange(Port::Status status) +PhysicalMemory::MemoryPort::recvRangeChange() { - memory->recvStatusChange(status); + // memory is a slave and thus should never have to worry about its + // neighbours address ranges } -void -PhysicalMemory::MemoryPort::getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop) +AddrRangeList +PhysicalMemory::MemoryPort::getAddrRanges() { - memory->getAddressRanges(resp, snoop); + return memory->getAddrRanges(); } -void -PhysicalMemory::getAddressRanges(AddrRangeList &resp, bool &snoop) +AddrRangeList +PhysicalMemory::getAddrRanges() { - snoop = false; - resp.clear(); - resp.push_back(RangeSize(start(), size())); + AddrRangeList ranges; + ranges.push_back(RangeSize(start(), size())); + return ranges; } unsigned diff --git a/src/mem/physical.hh b/src/mem/physical.hh index cd6d809e2..b447237c7 100644 --- a/src/mem/physical.hh +++ b/src/mem/physical.hh @@ -38,11 +38,13 @@ #include <string> #include "base/range.hh" +#include "base/statistics.hh" #include "mem/mem_object.hh" #include "mem/packet.hh" #include "mem/tport.hh" #include "params/PhysicalMemory.hh" #include "sim/eventq.hh" +#include "sim/stats.hh" // // Functional model for a contiguous block of physical memory. (i.e. RAM) @@ -65,10 +67,9 @@ class PhysicalMemory : public MemObject virtual void recvFunctional(PacketPtr pkt); - virtual void recvStatusChange(Status status); + virtual void recvRangeChange(); - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop); + virtual AddrRangeList getAddrRanges(); virtual unsigned deviceBlockSize() const; }; @@ -155,6 +156,28 @@ class PhysicalMemory : public MemObject uint64_t _size; uint64_t _start; + + /** Number of total bytes read from this memory */ + Stats::Scalar bytesRead; + /** Number of instruction bytes read from this memory */ + Stats::Scalar bytesInstRead; + /** Number of bytes written to this memory */ + Stats::Scalar bytesWritten; + /** Number of read requests */ + Stats::Scalar numReads; + /** Number of write requests */ + Stats::Scalar numWrites; + /** Number of other requests */ + Stats::Scalar numOther; + /** Read bandwidth from this memory */ + Stats::Formula bwRead; + /** Read bandwidth from this memory */ + Stats::Formula bwInstRead; + /** Write bandwidth from this memory */ + Stats::Formula bwWrite; + /** Total bandwidth from this memory */ + Stats::Formula bwTotal; + public: uint64_t size() { return _size; } uint64_t start() { return _start; } @@ -172,7 +195,7 @@ class PhysicalMemory : public MemObject public: unsigned deviceBlockSize() const; - void getAddressRanges(AddrRangeList &resp, bool &snoop); + AddrRangeList getAddrRanges(); virtual Port *getPort(const std::string &if_name, int idx = -1); void virtual init(); unsigned int drain(Event *de); @@ -181,9 +204,13 @@ class PhysicalMemory : public MemObject Tick doAtomicAccess(PacketPtr pkt); void doFunctionalAccess(PacketPtr pkt); virtual Tick calculateLatency(PacketPtr pkt); - void recvStatusChange(Port::Status status); public: + /** + * Register Statistics + */ + void regStats(); + virtual void serialize(std::ostream &os); virtual void unserialize(Checkpoint *cp, const std::string §ion); diff --git a/src/mem/port.cc b/src/mem/port.cc index c87785a49..fb1715db6 100644 --- a/src/mem/port.cc +++ b/src/mem/port.cc @@ -40,61 +40,8 @@ #include "mem/mem_object.hh" #include "mem/port.hh" -class DefaultPeerPort : public Port -{ - protected: - void blowUp() const - { - fatal("%s: Unconnected port!", peer->name()); - } - - public: - DefaultPeerPort() - : Port("default_port", NULL) - { } - - bool recvTiming(PacketPtr) - { - blowUp(); - return false; - } - - Tick recvAtomic(PacketPtr) - { - blowUp(); - return 0; - } - - void recvFunctional(PacketPtr) - { - blowUp(); - } - - void recvStatusChange(Status) - { - blowUp(); - } - - unsigned - deviceBlockSize() const - { - blowUp(); - return 0; - } - - void getDeviceAddressRanges(AddrRangeList &, bool &) - { - blowUp(); - } - - bool isDefaultPort() const { return true; } -}; - -DefaultPeerPort defaultPeerPort; - Port::Port(const std::string &_name, MemObject *_owner) - : EventManager(_owner), portName(_name), peer(&defaultPeerPort), - owner(_owner) + : portName(_name), peer(NULL), owner(_owner) { } @@ -113,19 +60,10 @@ Port::setPeer(Port *port) void Port::setOwner(MemObject *_owner) { - eventq = _owner->queue(); owner = _owner; } void -Port::removeConn() -{ - if (peer->getOwner()) - peer->getOwner()->deletePortRefs(peer); - peer = NULL; -} - -void Port::blobHelper(Addr addr, uint8_t *p, int size, MemCmd cmd) { Request req; diff --git a/src/mem/port.hh b/src/mem/port.hh index bb74bf497..98b3ad5f1 100644 --- a/src/mem/port.hh +++ b/src/mem/port.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2002-2005 The Regents of The University of Michigan * All rights reserved. * @@ -47,10 +59,8 @@ #include "base/types.hh" #include "mem/packet.hh" #include "mem/request.hh" -#include "sim/eventq.hh" -/** This typedef is used to clean up the parameter list of - * getDeviceAddressRanges() and getPeerAddressRanges(). It's declared +/** This typedef is used to clean up getAddrRanges(). It's declared * outside the Port object since it's also used by some mem objects. * Eventually we should move this typedef to wherever Addr is * defined. @@ -59,7 +69,6 @@ typedef std::list<Range<Addr> > AddrRangeList; typedef std::list<Range<Addr> >::iterator AddrRangeIter; -class EventQueue; class MemObject; /** @@ -73,7 +82,7 @@ class MemObject; * Send accessor functions are being called from the device the port is * associated with, and it will call the peer recv. accessor function. */ -class Port : public EventManager +class Port { protected: /** Descriptive name (for DPRINTF output) */ @@ -103,13 +112,6 @@ class Port : public EventManager virtual ~Port(); - // mey be better to use subclasses & RTTI? - /** Holds the ports status. Currently just that a range recomputation needs - * to be done. */ - enum Status { - RangeChange - }; - void setName(const std::string &name) { portName = name; } @@ -125,13 +127,7 @@ class Port : public EventManager /** Function to return the owner of this port. */ MemObject *getOwner() { return owner; } - /** Inform the peer port to delete itself and notify it's owner about it's - * demise. */ - void removeConn(); - - virtual bool isDefaultPort() const { return false; } - - bool isConnected() { return peer && !peer->isDefaultPort(); } + bool isConnected() { return peer != NULL; } protected: @@ -147,8 +143,8 @@ class Port : public EventManager /** Called to recive a functional call from the peer port. */ virtual void recvFunctional(PacketPtr pkt) = 0; - /** Called to recieve a status change from the peer port. */ - virtual void recvStatusChange(Status status) = 0; + /** Called to recieve an address range change from the peer port. */ + virtual void recvRangeChange() = 0; /** Called by a peer port if the send was unsuccesful, and had to wait. This shouldn't be valid for response paths (IO Devices). @@ -163,17 +159,31 @@ class Port : public EventManager */ virtual unsigned deviceBlockSize() const { return 0; } - /** The peer port is requesting us to reply with a list of the ranges we - are responsible for. - @param resp is a list of ranges responded to - @param snoop is a list of ranges snooped - */ - virtual void getDeviceAddressRanges(AddrRangeList &resp, - bool &snoop) - { panic("??"); } - public: + /** + * Get a list of the non-overlapping address ranges we are + * responsible for. The default implementation returns an empty + * list and thus no address ranges. Any slave port must override + * this function and return a populated list with at least one + * item. + * + * @return a list of ranges responded to + */ + virtual AddrRangeList getAddrRanges() + { AddrRangeList ranges; return ranges; } + + /** + * Determine if this port is snooping or not. The default + * implementation returns false and thus tells the neighbour we + * are not snooping. Any port that is to snoop (e.g. a cache + * connected to a bus) has to override this function. + * + * @return true if the port should be considered a snooper + */ + virtual bool isSnooping() + { return false; } + /** Function called by associated memory device (cache, memory, iodevice) in order to send a timing request to the port. Simply calls the peer port receive function. @@ -201,10 +211,11 @@ class Port : public EventManager void sendFunctional(PacketPtr pkt) { return peer->recvFunctional(pkt); } - /** Called by the associated device to send a status change to the device - connected to the peer interface. - */ - void sendStatusChange(Status status) {peer->recvStatusChange(status); } + /** + * Called by the associated device to send a status range to the + * peer interface. + */ + void sendRangeChange() const { peer->recvRangeChange(); } /** When a timing access doesn't return a success, some time later the Retry will be sent. @@ -216,12 +227,6 @@ class Port : public EventManager */ unsigned peerBlockSize() const { return peer->deviceBlockSize(); } - /** Called by the associated device if it wishes to find out the address - ranges connected to the peer ports devices. - */ - void getPeerAddressRanges(AddrRangeList &resp, bool &snoop) - { peer->getDeviceAddressRanges(resp, snoop); } - /** This function is a wrapper around sendFunctional() that breaks a larger, arbitrarily aligned access into appropriate chunks. The default implementation can use @@ -255,48 +260,4 @@ class Port : public EventManager void blobHelper(Addr addr, uint8_t *p, int size, MemCmd cmd); }; -/** A simple functional port that is only meant for one way communication to - * physical memory. It is only meant to be used to load data into memory before - * the simulation begins. - */ - -class FunctionalPort : public Port -{ - public: - FunctionalPort(const std::string &_name, MemObject *_owner = NULL) - : Port(_name, _owner) - {} - - protected: - virtual bool recvTiming(PacketPtr pkt) { panic("FuncPort is UniDir"); - M5_DUMMY_RETURN } - virtual Tick recvAtomic(PacketPtr pkt) { panic("FuncPort is UniDir"); - M5_DUMMY_RETURN } - virtual void recvFunctional(PacketPtr pkt) { panic("FuncPort is UniDir"); } - virtual void recvStatusChange(Status status) {} - - public: - /** a write function that also does an endian conversion. */ - template <typename T> - inline void writeHtoG(Addr addr, T d); - - /** a read function that also does an endian conversion. */ - template <typename T> - inline T readGtoH(Addr addr); - - template <typename T> - inline void write(Addr addr, T d) - { - writeBlob(addr, (uint8_t*)&d, sizeof(T)); - } - - template <typename T> - inline T read(Addr addr) - { - T d; - readBlob(addr, (uint8_t*)&d, sizeof(T)); - return d; - } -}; - #endif //__MEM_PORT_HH__ diff --git a/src/mem/port_proxy.hh b/src/mem/port_proxy.hh new file mode 100644 index 000000000..31ad4c1cd --- /dev/null +++ b/src/mem/port_proxy.hh @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andreas Hansson + */ + +/** + * @file + * PortProxy Object Declaration. + * + * Port proxies are used when non structural entities need access to + * the memory system. Proxy objects replace the previous + * FunctionalPort, TranslatingPort and VirtualPort objects, which + * provided the same functionality as the proxies, but were instances + * of ports not corresponding to real structural ports of the + * simulated system. Via the port proxies all the accesses go through + * an actual port and thus are transparent to a potentially + * distributed memory and automatically adhere to the memory map of + * the system. + */ + +#ifndef __MEM_PORT_PROXY_HH__ +#define __MEM_PORT_PROXY_HH__ + +#include "config/the_isa.hh" +#if THE_ISA != NO_ISA + #include "arch/isa_traits.hh" +#endif + +#include "base/types.hh" +#include "mem/port.hh" +#include "sim/byteswap.hh" + +/** + * This object is a proxy for a structural port, + * to be used for debug accesses. + * + * This proxy object is used when non structural entities + * (e.g. thread contexts, object file loaders) need access to the + * memory system. It calls the corresponding functions on the underlying + * structural port, and provides templatized convenience access functions. + * + * The addresses are interpreted as physical addresses. + * + * @sa SETranslatingProxy + * @sa FSTranslatingProxy + */ +class PortProxy +{ + protected: + Port &_port; + + public: + PortProxy(Port &port) : _port(port) { } + virtual ~PortProxy() { } + + public: + /** + * Read size bytes memory at address and store in p. + */ + virtual void readBlob(Addr address, uint8_t* p, int size) + { _port.readBlob(address, p, size); } + + /** + * Write size bytes from p to address. + */ + virtual void writeBlob(Addr address, uint8_t* p, int size) + { _port.writeBlob(address, p, size); } + + /** + * Fill size bytes starting at addr with byte value val. + */ + virtual void memsetBlob(Addr address, uint8_t v, int size) + { _port.memsetBlob(address, v, size); } + + /** + * Read sizeof(T) bytes from address and return as object T. + */ + template <typename T> + T read(Addr address); + + /** + * Write object T to address. Writes sizeof(T) bytes. + */ + template <typename T> + void write(Addr address, T data); + +#if THE_ISA != NO_ISA + /** + * Read sizeof(T) bytes from address and return as object T. + * Performs Guest to Host endianness transform. + */ + template <typename T> + T readGtoH(Addr address); + + /** + * Write object T to address. Writes sizeof(T) bytes. + * Performs Host to Guest endianness transform. + */ + template <typename T> + void writeHtoG(Addr address, T data); +#endif +}; + + +template <typename T> +T +PortProxy::read(Addr address) +{ + T data; + readBlob(address, (uint8_t*)&data, sizeof(T)); + return data; +} + +template <typename T> +void +PortProxy::write(Addr address, T data) +{ + writeBlob(address, (uint8_t*)&data, sizeof(T)); +} + +#if THE_ISA != NO_ISA +template <typename T> +T +PortProxy::readGtoH(Addr address) +{ + T data; + readBlob(address, (uint8_t*)&data, sizeof(T)); + return TheISA::gtoh(data); +} + +template <typename T> +void +PortProxy::writeHtoG(Addr address, T data) +{ + data = TheISA::htog(data); + writeBlob(address, (uint8_t*)&data, sizeof(T)); +} +#endif + +#endif // __MEM_PORT_PROXY_HH__ diff --git a/src/mem/protocol/MESI_CMP_directory-L1cache.sm b/src/mem/protocol/MESI_CMP_directory-L1cache.sm index f0be1fd34..934405786 100644 --- a/src/mem/protocol/MESI_CMP_directory-L1cache.sm +++ b/src/mem/protocol/MESI_CMP_directory-L1cache.sm @@ -27,14 +27,15 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -machine(L1Cache, "MSI Directory L1 Cache CMP") +machine(L1Cache, "MESI Directory L1 Cache CMP") : Sequencer * sequencer, CacheMemory * L1IcacheMemory, CacheMemory * L1DcacheMemory, int l2_select_num_bits, int l1_request_latency = 2, int l1_response_latency = 2, - int to_l2_latency = 1 + int to_l2_latency = 1, + bool send_evictions { // NODE L1 CACHE // From this node's L1 cache TO the network @@ -67,7 +68,6 @@ machine(L1Cache, "MSI Directory L1 Cache CMP") IS_I, AccessPermission:Busy, desc="L1 idle, issued GETS, saw Inv before data because directory doesn't block on GETS hit"; M_I, AccessPermission:Busy, desc="L1 replacing, waiting for ACK"; - E_I, AccessPermission:Busy, desc="L1 replacing, waiting for ACK"; SINK_WB_ACK, AccessPermission:Busy, desc="This is to sink WB_Acks from L2"; } @@ -544,6 +544,12 @@ machine(L1Cache, "MSI Directory L1 Cache CMP") } } + action(forward_eviction_to_cpu, "\cc", desc="sends eviction information to the processor") { + if (send_evictions) { + DPRINTF(RubySlicc, "Sending invalidation for %s to the CPU\n", address); + sequencer.evictionCallback(address); + } + } action(g_issuePUTX, "g", desc="send data to the L2 cache") { enqueue(requestIntraChipL1Network_out, RequestMsg, latency=l1_response_latency) { @@ -696,7 +702,7 @@ machine(L1Cache, "MSI Directory L1 Cache CMP") //***************************************************** // Transitions for Load/Store/Replacement/WriteBack from transient states - transition({IS, IM, IS_I, M_I, E_I, SM}, {Load, Ifetch, Store, L1_Replacement}) { + transition({IS, IM, IS_I, M_I, SM}, {Load, Ifetch, Store, L1_Replacement}) { z_recycleMandatoryQueue; } @@ -748,10 +754,12 @@ machine(L1Cache, "MSI Directory L1 Cache CMP") } transition(S, L1_Replacement, I) { + forward_eviction_to_cpu; ff_deallocateL1CacheBlock; } transition(S, Inv, I) { + forward_eviction_to_cpu; fi_sendInvAck; l_popRequestQueue; } @@ -770,6 +778,7 @@ machine(L1Cache, "MSI Directory L1 Cache CMP") transition(E, L1_Replacement, M_I) { // silent E replacement?? + forward_eviction_to_cpu; i_allocateTBE; g_issuePUTX; // send data, but hold in case forwarded request ff_deallocateL1CacheBlock; @@ -777,11 +786,13 @@ machine(L1Cache, "MSI Directory L1 Cache CMP") transition(E, Inv, I) { // don't send data + forward_eviction_to_cpu; fi_sendInvAck; l_popRequestQueue; } transition(E, Fwd_GETX, I) { + forward_eviction_to_cpu; d_sendDataToRequestor; l_popRequestQueue; } @@ -804,6 +815,7 @@ machine(L1Cache, "MSI Directory L1 Cache CMP") } transition(M, L1_Replacement, M_I) { + forward_eviction_to_cpu; i_allocateTBE; g_issuePUTX; // send data, but hold in case forwarded request ff_deallocateL1CacheBlock; @@ -815,6 +827,7 @@ machine(L1Cache, "MSI Directory L1 Cache CMP") } transition(M, Inv, I) { + forward_eviction_to_cpu; f_sendDataToL2; l_popRequestQueue; } @@ -825,6 +838,7 @@ machine(L1Cache, "MSI Directory L1 Cache CMP") } transition(M, Fwd_GETX, I) { + forward_eviction_to_cpu; d_sendDataToRequestor; l_popRequestQueue; } @@ -866,7 +880,6 @@ machine(L1Cache, "MSI Directory L1 Cache CMP") o_popIncomingResponseQueue; } - transition(IS, DataS_fromL1, S) { u_writeDataToL1Cache; j_sendUnblock; @@ -935,7 +948,6 @@ machine(L1Cache, "MSI Directory L1 Cache CMP") transition(SINK_WB_ACK, {Load, Store, Ifetch, L1_Replacement}){ z_recycleMandatoryQueue; - } transition(SINK_WB_ACK, Inv){ @@ -948,6 +960,3 @@ machine(L1Cache, "MSI Directory L1 Cache CMP") o_popIncomingResponseQueue; } } - - - diff --git a/src/mem/protocol/MI_example-cache.sm b/src/mem/protocol/MI_example-cache.sm index b11fddd95..2f2e4e3d7 100644 --- a/src/mem/protocol/MI_example-cache.sm +++ b/src/mem/protocol/MI_example-cache.sm @@ -3,7 +3,8 @@ machine(L1Cache, "MI Example L1 Cache") : Sequencer * sequencer, CacheMemory * cacheMemory, int cache_response_latency = 12, - int issue_latency = 2 + int issue_latency = 2, + bool send_evictions { // NETWORK BUFFERS @@ -54,7 +55,6 @@ machine(L1Cache, "MI Example L1 Cache") DataBlock DataBlk, desc="Data in the block"; } - // TBE fields structure(TBE, desc="...") { State TBEState, desc="Transient state"; @@ -70,7 +70,6 @@ machine(L1Cache, "MI Example L1 Cache") // STRUCTURES - TBETable TBEs, template_hack="<L1Cache_TBE>"; // PROTOTYPES @@ -249,7 +248,6 @@ machine(L1Cache, "MI Example L1 Cache") } } - action(e_sendData, "e", desc="Send data from cache to requestor") { peek(forwardRequestNetwork_in, RequestMsg) { enqueue(responseNetwork_out, ResponseMsg, latency=cache_response_latency) { @@ -353,13 +351,18 @@ machine(L1Cache, "MI Example L1 Cache") } } + action(forward_eviction_to_cpu, "\cc", desc="sends eviction information to the processor") { + if (send_evictions) { + DPRINTF(RubySlicc, "Sending invalidation for %s to the CPU\n", address); + sequencer.evictionCallback(address); + } + } action(v_allocateTBE, "v", desc="Allocate TBE") { TBEs.allocate(address); set_tbe(TBEs[address]); } - action(w_deallocateTBE, "w", desc="Deallocate TBE") { TBEs.deallocate(address); unset_tbe(); @@ -435,6 +438,7 @@ machine(L1Cache, "MI Example L1 Cache") transition(M, Fwd_GETX, I) { e_sendData; + forward_eviction_to_cpu; o_popForwardedRequestQueue; } @@ -446,6 +450,7 @@ machine(L1Cache, "MI Example L1 Cache") v_allocateTBE; b_issuePUT; x_copyDataFromCacheToTBE; + forward_eviction_to_cpu; h_deallocateL1CacheBlock; } @@ -474,4 +479,3 @@ machine(L1Cache, "MI Example L1 Cache") o_popForwardedRequestQueue; } } - diff --git a/src/mem/protocol/MOESI_CMP_directory-L1cache.sm b/src/mem/protocol/MOESI_CMP_directory-L1cache.sm index 2845d1ad1..7a5cc6511 100644 --- a/src/mem/protocol/MOESI_CMP_directory-L1cache.sm +++ b/src/mem/protocol/MOESI_CMP_directory-L1cache.sm @@ -37,7 +37,8 @@ machine(L1Cache, "Directory protocol") CacheMemory * L1IcacheMemory, CacheMemory * L1DcacheMemory, int l2_select_num_bits, - int request_latency = 2 + int request_latency = 2, + bool send_evictions { // NODE L1 CACHE @@ -530,7 +531,6 @@ machine(L1Cache, "Directory protocol") } } - action(ee_sendDataExclusive, "\e", desc="Send data from cache to requestor, don't keep a shared copy") { peek(requestNetwork_in, RequestMsg) { assert(is_valid(cache_entry)); @@ -689,7 +689,6 @@ machine(L1Cache, "Directory protocol") useTimerTable.set(address, 50); } - action(ub_dmaUnblockL2Cache, "ub", desc="Send dma ack to l2 cache") { peek(requestNetwork_in, RequestMsg) { enqueue(responseNetwork_out, ResponseMsg, latency=request_latency) { @@ -775,7 +774,6 @@ machine(L1Cache, "Directory protocol") } } - // L2 will usually request data for a writeback action(qq_sendWBDataFromTBEToL2, "\q", desc="Send data from TBE to L2") { enqueue(responseNetwork_out, ResponseMsg, latency=request_latency) { @@ -811,7 +809,6 @@ machine(L1Cache, "Directory protocol") //assert(in_msg.Dirty == false); } } - } action(v_writeDataToCacheVerify, "v", desc="Write data to cache, assert it was same as before") { @@ -844,7 +841,12 @@ machine(L1Cache, "Directory protocol") } } - + action(forward_eviction_to_cpu, "\cc", desc="sends eviction information to the processor") { + if (send_evictions) { + DPRINTF(RubySlicc, "Sending invalidation for %s to the CPU\n", address); + sequencer.evictionCallback(address); + } + } action(uu_profileMiss, "\u", desc="Profile the demand miss") { peek(mandatoryQueue_in, RubyRequest) { @@ -931,11 +933,13 @@ machine(L1Cache, "Directory protocol") transition(S, L1_Replacement, SI) { i_allocateTBE; dd_issuePUTS; + forward_eviction_to_cpu; kk_deallocateL1CacheBlock; } transition(S, Inv, I) { f_sendAck; + forward_eviction_to_cpu; l_popForwardQueue; } @@ -966,11 +970,13 @@ machine(L1Cache, "Directory protocol") transition(O, L1_Replacement, OI) { i_allocateTBE; dd_issuePUTO; + forward_eviction_to_cpu; kk_deallocateL1CacheBlock; } transition(O, Fwd_GETX, I) { ee_sendDataExclusive; + forward_eviction_to_cpu; l_popForwardQueue; } @@ -999,16 +1005,19 @@ machine(L1Cache, "Directory protocol") transition(MM, L1_Replacement, MI) { i_allocateTBE; d_issuePUTX; + forward_eviction_to_cpu; kk_deallocateL1CacheBlock; } transition(MM, Fwd_GETX, I) { ee_sendDataExclusive; + forward_eviction_to_cpu; l_popForwardQueue; } transition(MM, Fwd_GETS, I) { ee_sendDataExclusive; + forward_eviction_to_cpu; l_popForwardQueue; } @@ -1037,12 +1046,14 @@ machine(L1Cache, "Directory protocol") transition(M, L1_Replacement, MI) { i_allocateTBE; d_issuePUTX; + forward_eviction_to_cpu; kk_deallocateL1CacheBlock; } transition(M, Fwd_GETX, I) { // e_sendData; ee_sendDataExclusive; + forward_eviction_to_cpu; l_popForwardQueue; } @@ -1080,6 +1091,7 @@ machine(L1Cache, "Directory protocol") // Transitions from SM transition(SM, Inv, IM) { f_sendAck; + forward_eviction_to_cpu; l_popForwardQueue; } diff --git a/src/mem/protocol/MOESI_CMP_token-L1cache.sm b/src/mem/protocol/MOESI_CMP_token-L1cache.sm index 66789b594..7cc41cc20 100644 --- a/src/mem/protocol/MOESI_CMP_token-L1cache.sm +++ b/src/mem/protocol/MOESI_CMP_token-L1cache.sm @@ -43,7 +43,8 @@ machine(L1Cache, "Token protocol") int retry_threshold = 1, int fixed_timeout_latency = 100, bool dynamic_timeout_enabled = true, - bool no_mig_atomic = true + bool no_mig_atomic = true, + bool send_evictions { // From this node's L1 cache TO the network @@ -1398,7 +1399,6 @@ machine(L1Cache, "Token protocol") } } - action(q_updateTokensFromResponse, "q", desc="Update the token count based on the incoming response message") { peek(responseNetwork_in, ResponseMsg) { assert(is_valid(cache_entry)); @@ -1522,6 +1522,13 @@ machine(L1Cache, "Token protocol") } } + action(forward_eviction_to_cpu, "\cc", desc="sends eviction information to the processor") { + if (send_evictions) { + DPRINTF(RubySlicc, "Sending invalidation for %s to the CPU\n", address); + sequencer.evictionCallback(address); + } + } + action(uu_profileMiss, "\u", desc="Profile the demand miss") { peek(mandatoryQueue_in, RubyRequest) { if (L1DcacheMemory.isTagPresent(address)) { @@ -1572,7 +1579,6 @@ machine(L1Cache, "Token protocol") zz_stallAndWaitMandatoryQueue; } - // Lockdowns transition({NP, I, S, O, M, MM, M_W, MM_W, IM, SM, OM, IS}, Own_Lock_or_Unlock) { l_popPersistentQueue; @@ -1702,6 +1708,7 @@ machine(L1Cache, "Token protocol") transition(S, L1_Replacement, I) { ta_traceStalledAddress; cc_sharedReplacement; // Only needed in some cases + forward_eviction_to_cpu; gg_deallocateL1CacheBlock; ka_wakeUpAllDependents; } @@ -1709,6 +1716,7 @@ machine(L1Cache, "Token protocol") transition(S, {Transient_GETX, Transient_Local_GETX}, I) { t_sendAckWithCollectedTokens; p_informL2AboutTokenLoss; + forward_eviction_to_cpu m_popRequestQueue; } @@ -1729,6 +1737,7 @@ machine(L1Cache, "Token protocol") transition({S, S_L}, Persistent_GETX, I_L) { e_sendAckWithCollectedTokens; p_informL2AboutTokenLoss; + forward_eviction_to_cpu l_popPersistentQueue; } @@ -1780,6 +1789,7 @@ machine(L1Cache, "Token protocol") transition(O, L1_Replacement, I) { ta_traceStalledAddress; c_ownedReplacement; + forward_eviction_to_cpu gg_deallocateL1CacheBlock; ka_wakeUpAllDependents; } @@ -1787,12 +1797,14 @@ machine(L1Cache, "Token protocol") transition(O, {Transient_GETX, Transient_Local_GETX}, I) { dd_sendDataWithAllTokens; p_informL2AboutTokenLoss; + forward_eviction_to_cpu m_popRequestQueue; } transition(O, Persistent_GETX, I_L) { ee_sendDataWithAllTokens; p_informL2AboutTokenLoss; + forward_eviction_to_cpu l_popPersistentQueue; } @@ -1803,6 +1815,7 @@ machine(L1Cache, "Token protocol") transition(O, Persistent_GETS_Last_Token, I_L) { fo_sendDataWithOwnerToken; + forward_eviction_to_cpu l_popPersistentQueue; } @@ -1867,6 +1880,7 @@ machine(L1Cache, "Token protocol") transition(MM, L1_Replacement, I) { ta_traceStalledAddress; c_ownedReplacement; + forward_eviction_to_cpu gg_deallocateL1CacheBlock; ka_wakeUpAllDependents; } @@ -1874,6 +1888,7 @@ machine(L1Cache, "Token protocol") transition(MM, {Transient_GETX, Transient_Local_GETX, Transient_GETS, Transient_Local_GETS}, I) { dd_sendDataWithAllTokens; p_informL2AboutTokenLoss; + forward_eviction_to_cpu m_popRequestQueue; } @@ -1885,6 +1900,7 @@ machine(L1Cache, "Token protocol") transition(MM, {Persistent_GETX, Persistent_GETS}, I_L) { ee_sendDataWithAllTokens; p_informL2AboutTokenLoss; + forward_eviction_to_cpu l_popPersistentQueue; } @@ -1934,6 +1950,7 @@ machine(L1Cache, "Token protocol") transition(M, L1_Replacement, I) { ta_traceStalledAddress; c_ownedReplacement; + forward_eviction_to_cpu gg_deallocateL1CacheBlock; ka_wakeUpAllDependents; } @@ -1941,6 +1958,7 @@ machine(L1Cache, "Token protocol") transition(M, {Transient_GETX, Transient_Local_GETX}, I) { dd_sendDataWithAllTokens; p_informL2AboutTokenLoss; + forward_eviction_to_cpu m_popRequestQueue; } @@ -1961,6 +1979,7 @@ machine(L1Cache, "Token protocol") transition(M, Persistent_GETX, I_L) { ee_sendDataWithAllTokens; p_informL2AboutTokenLoss; + forward_eviction_to_cpu l_popPersistentQueue; } @@ -1990,22 +2009,21 @@ machine(L1Cache, "Token protocol") transition(M_W, Use_TimeoutStarverX, I_L) { s_deallocateTBE; ee_sendDataWithAllTokens; + forward_eviction_to_cpu; p_informL2AboutTokenLoss; jj_unsetUseTimer; } - - // migratory transition(MM_W, {Use_TimeoutStarverX, Use_TimeoutStarverS}, I_L) { s_deallocateTBE; ee_sendDataWithAllTokens; + forward_eviction_to_cpu; p_informL2AboutTokenLoss; jj_unsetUseTimer; } - // Transient_GETX and Transient_GETS in transient states transition(OM, {Transient_GETX, Transient_Local_GETX, Transient_GETS, Transient_GETS_Last_Token, Transient_Local_GETS_Last_Token, Transient_Local_GETS}) { m_popRequestQueue; // Even if we have the data, we can pretend we don't have it yet. @@ -2040,6 +2058,7 @@ machine(L1Cache, "Token protocol") transition({SM, SM_L}, Persistent_GETX, IM_L) { e_sendAckWithCollectedTokens; + forward_eviction_to_cpu l_popPersistentQueue; } @@ -2054,6 +2073,7 @@ machine(L1Cache, "Token protocol") transition(OM, Persistent_GETX, IM_L) { ee_sendDataWithAllTokens; + forward_eviction_to_cpu l_popPersistentQueue; } @@ -2120,6 +2140,7 @@ machine(L1Cache, "Token protocol") transition({IM, SM}, {Transient_GETX, Transient_Local_GETX}, IM) { // We don't have the data yet, but we might have collected some tokens. We give them up here to avoid livelock t_sendAckWithCollectedTokens; + forward_eviction_to_cpu; m_popRequestQueue; } @@ -2336,7 +2357,6 @@ machine(L1Cache, "Token protocol") kd_wakeUpDependents; } - // Own_Lock_or_Unlock transition(I_L, Own_Lock_or_Unlock, I) { @@ -2364,4 +2384,3 @@ machine(L1Cache, "Token protocol") kd_wakeUpDependents; } } - diff --git a/src/mem/protocol/MOESI_hammer-cache.sm b/src/mem/protocol/MOESI_hammer-cache.sm index ce16a8777..219096d26 100644 --- a/src/mem/protocol/MOESI_hammer-cache.sm +++ b/src/mem/protocol/MOESI_hammer-cache.sm @@ -41,7 +41,8 @@ machine(L1Cache, "AMD Hammer-like protocol") int cache_response_latency = 10, int issue_latency = 2, int l2_cache_hit_latency = 10, - bool no_mig_atomic = true + bool no_mig_atomic = true, + bool send_evictions { // NETWORK BUFFERS @@ -1207,6 +1208,13 @@ machine(L1Cache, "AMD Hammer-like protocol") unset_cache_entry(); } + action(forward_eviction_to_cpu, "\cc", desc="sends eviction information to the processor") { + if (send_evictions) { + DPRINTF(RubySlicc, "Sending invalidation for %s to the CPU\n", address); + sequencer.evictionCallback(address); + } + } + action(uu_profileMiss, "\u", desc="Profile the demand miss") { peek(mandatoryQueue_in, RubyRequest) { if (L1IcacheMemory.isTagPresent(address)) { @@ -1486,17 +1494,20 @@ machine(L1Cache, "AMD Hammer-like protocol") i_allocateTBE; bf_issueGETF; uu_profileMiss; + forward_eviction_to_cpu; gg_deallocateL1CacheBlock; k_popMandatoryQueue; } transition(S, L2_Replacement, I) { + forward_eviction_to_cpu; rr_deallocateL2CacheBlock; ka_wakeUpAllDependents; } transition(S, {Other_GETX, Invalidate}, I) { f_sendAck; + forward_eviction_to_cpu; l_popForwardQueue; } @@ -1528,6 +1539,7 @@ machine(L1Cache, "AMD Hammer-like protocol") bf_issueGETF; p_decrementNumberOfMessagesByOne; uu_profileMiss; + forward_eviction_to_cpu; gg_deallocateL1CacheBlock; k_popMandatoryQueue; } @@ -1535,12 +1547,14 @@ machine(L1Cache, "AMD Hammer-like protocol") transition(O, L2_Replacement, OI) { i_allocateTBE; d_issuePUT; + forward_eviction_to_cpu; rr_deallocateL2CacheBlock; ka_wakeUpAllDependents; } transition(O, {Other_GETX, Invalidate}, I) { e_sendData; + forward_eviction_to_cpu; l_popForwardQueue; } @@ -1569,6 +1583,7 @@ machine(L1Cache, "AMD Hammer-like protocol") i_allocateTBE; bf_issueGETF; p_decrementNumberOfMessagesByOne; + forward_eviction_to_cpu; gg_deallocateL1CacheBlock; k_popMandatoryQueue; } @@ -1582,17 +1597,20 @@ machine(L1Cache, "AMD Hammer-like protocol") transition(MM, L2_Replacement, MI) { i_allocateTBE; d_issuePUT; + forward_eviction_to_cpu; rr_deallocateL2CacheBlock; ka_wakeUpAllDependents; } transition(MM, {Other_GETX, Invalidate}, I) { c_sendExclusiveData; + forward_eviction_to_cpu; l_popForwardQueue; } transition(MM, Other_GETS, I) { c_sendExclusiveData; + forward_eviction_to_cpu; l_popForwardQueue; } @@ -1625,12 +1643,14 @@ machine(L1Cache, "AMD Hammer-like protocol") transition(M, L2_Replacement, MI) { i_allocateTBE; d_issuePUT; + forward_eviction_to_cpu; rr_deallocateL2CacheBlock; ka_wakeUpAllDependents; } transition(M, {Other_GETX, Invalidate}, I) { c_sendExclusiveData; + forward_eviction_to_cpu; l_popForwardQueue; } @@ -1700,11 +1720,13 @@ machine(L1Cache, "AMD Hammer-like protocol") transition(SM, {Other_GETX, Invalidate}, IM) { f_sendAck; + forward_eviction_to_cpu; l_popForwardQueue; } transition(SM_F, {Other_GETX, Invalidate}, IM_F) { f_sendAck; + forward_eviction_to_cpu; l_popForwardQueue; } @@ -1754,12 +1776,14 @@ machine(L1Cache, "AMD Hammer-like protocol") transition(OM, {Other_GETX, Invalidate}, IM) { e_sendData; pp_incrementNumberOfMessagesByOne; + forward_eviction_to_cpu; l_popForwardQueue; } transition(OM_F, {Other_GETX, Invalidate}, IM_F) { q_sendDataFromTBEToCache; pp_incrementNumberOfMessagesByOne; + forward_eviction_to_cpu; l_popForwardQueue; } diff --git a/src/mem/protocol/MOESI_hammer-dir.sm b/src/mem/protocol/MOESI_hammer-dir.sm index a20619d46..dfb62e844 100644 --- a/src/mem/protocol/MOESI_hammer-dir.sm +++ b/src/mem/protocol/MOESI_hammer-dir.sm @@ -87,7 +87,7 @@ machine(Directory, "AMD Hammer-like protocol") O_DR_B, AccessPermission:Busy, desc="Owner, Dma Read waiting for cache responses"; WB, AccessPermission:Busy, desc="Blocked on a writeback"; WB_O_W, AccessPermission:Busy, desc="Blocked on memory write, will go to O"; - WB_E_W, AccessPermission:Busy, desc="Blocked on memory write, will go to E"; + WB_E_W, AccessPermission:Read_Write, desc="Blocked on memory write, will go to E"; NO_F, AccessPermission:Busy, desc="Blocked on a flush"; NO_F_W, AccessPermission:Busy, desc="Not Owner, Blocked, waiting for Dram"; diff --git a/src/mem/protocol/RubySlicc_Types.sm b/src/mem/protocol/RubySlicc_Types.sm index c76e0fe3e..3b90dab20 100644 --- a/src/mem/protocol/RubySlicc_Types.sm +++ b/src/mem/protocol/RubySlicc_Types.sm @@ -107,6 +107,7 @@ structure (Sequencer, external = "yes") { void writeCallback(Address, GenericMachineType, DataBlock, Time, Time, Time); void checkCoherence(Address); void profileNack(Address, int, int, uint64); + void evictionCallback(Address); } structure(RubyRequest, desc="...", interface="Message", external="yes") { diff --git a/src/mem/ruby/system/RubyPort.cc b/src/mem/ruby/system/RubyPort.cc index 64faf6aed..b60ca2a07 100644 --- a/src/mem/ruby/system/RubyPort.cc +++ b/src/mem/ruby/system/RubyPort.cc @@ -1,5 +1,6 @@ /* * Copyright (c) 2009 Advanced Micro Devices, Inc. + * Copyright (c) 2011 Mark D. Hill and David A. Wood * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -93,14 +94,6 @@ RubyPort::getPort(const std::string &if_name, int idx) return physMemPort; } - if (if_name == "functional") { - // Calls for the functional port only want to access - // functional memory. Therefore, directly pass these calls - // ports to physmem. - assert(physmem != NULL); - return physmem->getPort(if_name, idx); - } - return NULL; } @@ -464,8 +457,11 @@ RubyPort::M5Port::recvFunctional(PacketPtr pkt) // turn packet around to go back to requester if response expected if (needsResponse) { pkt->setFunctionalResponseStatus(accessSucceeded); - DPRINTF(RubyPort, "Sending packet back over port\n"); - sendFunctional(pkt); + + // @todo There should not be a reverse call since the response is + // communicated through the packet pointer + // DPRINTF(RubyPort, "Sending packet back over port\n"); + // sendFunctional(pkt); } DPRINTF(RubyPort, "Functional access %s!\n", accessSucceeded ? "successful":"failed"); @@ -668,9 +664,8 @@ RubyPort::PioPort::sendTiming(PacketPtr pkt) bool RubyPort::M5Port::isPhysMemAddress(Addr addr) { - AddrRangeList physMemAddrList; - bool snoop = false; - ruby_port->physMemPort->getPeerAddressRanges(physMemAddrList, snoop); + AddrRangeList physMemAddrList = + ruby_port->physMemPort->getPeer()->getAddrRanges(); for (AddrRangeIter iter = physMemAddrList.begin(); iter != physMemAddrList.end(); iter++) { @@ -688,3 +683,14 @@ RubyPort::M5Port::deviceBlockSize() const { return (unsigned) RubySystem::getBlockSizeBytes(); } + +void +RubyPort::ruby_eviction_callback(const Address& address) +{ + DPRINTF(RubyPort, "Sending invalidations.\n"); + Request req(address.getAddress(), 0, 0); + for (CpuPortIter it = cpu_ports.begin(); it != cpu_ports.end(); it++) { + Packet *pkt = new Packet(&req, MemCmd::InvalidationReq, -1); + (*it)->sendTiming(pkt); + } +} diff --git a/src/mem/ruby/system/RubyPort.hh b/src/mem/ruby/system/RubyPort.hh index d8dbe0cda..2ffdef3d9 100644 --- a/src/mem/ruby/system/RubyPort.hh +++ b/src/mem/ruby/system/RubyPort.hh @@ -1,5 +1,6 @@ /* * Copyright (c) 2009 Advanced Micro Devices, Inc. + * Copyright (c) 2011 Mark D. Hill and David A. Wood * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -58,6 +59,7 @@ class RubyPort : public MemObject RubySystem*_system, bool _access_phys_mem); bool sendTiming(PacketPtr pkt); void hitCallback(PacketPtr pkt); + void evictionCallback(const Address& address); unsigned deviceBlockSize() const; bool onRetryList() @@ -129,8 +131,8 @@ class RubyPort : public MemObject protected: const std::string m_name; void ruby_hit_callback(PacketPtr pkt); - void hit(PacketPtr pkt); void testDrainComplete(); + void ruby_eviction_callback(const Address& address); int m_version; AbstractController* m_controller; diff --git a/src/mem/port_impl.hh b/src/mem/ruby/system/RubyPortProxy.cc index bc9592164..29a693ca6 100644 --- a/src/mem/port_impl.hh +++ b/src/mem/ruby/system/RubyPortProxy.cc @@ -1,6 +1,15 @@ /* - * Copyright (c) 2006 The Regents of The University of Michigan - * All rights reserved. + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -25,29 +34,37 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * Authors: Ali Saidi + * Authors: Andreas Hansson */ -#include "arch/isa_traits.hh" -#include "config/the_isa.hh" -#include "mem/port.hh" -#include "sim/byteswap.hh" +#include "mem/ruby/system/RubyPortProxy.hh" -template <typename T> -void -FunctionalPort::writeHtoG(Addr addr, T d) +RubyPortProxy::RubyPortProxy(const RubyPortProxyParams* p) : + RubyPort(p) { +} + +RubyPortProxy::~RubyPortProxy() { - d = TheISA::htog(d); - writeBlob(addr, (uint8_t*)&d, sizeof(T)); } +void +RubyPortProxy::init() +{ + // Merely override to not care about the m_controller being NULL +} -template <typename T> -T -FunctionalPort::readGtoH(Addr addr) +RequestStatus +RubyPortProxy::makeRequest(PacketPtr pkt) { - T d; - readBlob(addr, (uint8_t*)&d, sizeof(T)); - return TheISA::gtoh(d); + // This sequencer should only be used through the functional + // accesses made by the system port and so simply fail if this + // happens. + panic("RubyPortProxy::makeRequest should not be called"); + return RequestStatus_NULL; } +RubyPortProxy* +RubyPortProxyParams::create() +{ + return new RubyPortProxy(this); +} diff --git a/src/mem/ruby/system/RubyPortProxy.hh b/src/mem/ruby/system/RubyPortProxy.hh new file mode 100644 index 000000000..8f88541e5 --- /dev/null +++ b/src/mem/ruby/system/RubyPortProxy.hh @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Andreas Hansson + */ + +/** + * @file + * RobyPortProxy for connecting system port to Ruby + * + * A trivial wrapper that allows the system port to connect to Ruby + * and use nothing but functional accesses. + */ + +#ifndef __MEM_RUBY_SYSTEM_RUBYPORTPROXY_HH__ +#define __MEM_RUBY_SYSTEM_RUBYPORTPROXY_HH__ + +#include "mem/ruby/system/RubyPort.hh" +#include "params/RubyPortProxy.hh" + +class RubyPortProxy : public RubyPort +{ + + public: + + /** + * Create a new RubyPortProxy. + * + * @param p Parameters inherited from the RubyPort + */ + RubyPortProxy(const RubyPortProxyParams* p); + + /** + * Destruct a RubyPortProxy. + */ + virtual ~RubyPortProxy(); + + /** + * Initialise a RubyPortProxy by doing nothing and avoid + * involving the super class. + */ + void init(); + + /** + * Pure virtual member in the super class that we are forced to + * implement even if it is never used (since there are only + * functional accesses). + * + * @param pkt The packet to serve to Ruby + * @returns always a NULL status + */ + RequestStatus makeRequest(PacketPtr pkt); + + /** + * Pure virtual member in the super class that we are forced to + * implement even if it is never used (since there are only + * functional accesses). + * + * @returns always 0 + */ + int outstandingCount() const { return 0; } + + /** + * Pure virtual member in the super class that we are forced to + * implement even if it is never used (since there are only + * functional accesses). + * + * @returns always false + */ + bool isDeadlockEventScheduled() const { return false; } + + /** + * Pure virtual member in the super class that we are forced to + * implement even if it is never used (since there are only + * functional accesses). + */ + void descheduleDeadlockEvent() { } + +}; + +#endif // __MEM_RUBY_SYSTEM_RUBYPORTPROXY_HH__ diff --git a/src/mem/ruby/system/SConscript b/src/mem/ruby/system/SConscript index 66d7d95bb..cbb1da3b1 100644 --- a/src/mem/ruby/system/SConscript +++ b/src/mem/ruby/system/SConscript @@ -49,6 +49,7 @@ Source('WireBuffer.cc') Source('MemoryNode.cc') Source('PersistentTable.cc') Source('RubyPort.cc') +Source('RubyPortProxy.cc') Source('Sequencer.cc') Source('System.cc') Source('TimerTable.cc') diff --git a/src/mem/ruby/system/Sequencer.cc b/src/mem/ruby/system/Sequencer.cc index 3f9ceb34d..1cd54d45c 100644 --- a/src/mem/ruby/system/Sequencer.cc +++ b/src/mem/ruby/system/Sequencer.cc @@ -733,3 +733,9 @@ Sequencer::checkCoherence(const Address& addr) g_system_ptr->checkGlobalCoherenceInvariant(addr); #endif } + +void +Sequencer::evictionCallback(const Address& address) +{ + ruby_eviction_callback(address); +} diff --git a/src/mem/ruby/system/Sequencer.hh b/src/mem/ruby/system/Sequencer.hh index 4a6d46c01..e262e32e8 100644 --- a/src/mem/ruby/system/Sequencer.hh +++ b/src/mem/ruby/system/Sequencer.hh @@ -117,6 +117,7 @@ class Sequencer : public RubyPort, public Consumer void markRemoved(); void removeRequest(SequencerRequest* request); + void evictionCallback(const Address& address); private: void issueRequest(PacketPtr pkt, RubyRequestType type); @@ -181,4 +182,3 @@ operator<<(std::ostream& out, const Sequencer& obj) } #endif // __MEM_RUBY_SYSTEM_SEQUENCER_HH__ - diff --git a/src/mem/ruby/system/Sequencer.py b/src/mem/ruby/system/Sequencer.py index 5d56dc000..ddf760f7b 100644 --- a/src/mem/ruby/system/Sequencer.py +++ b/src/mem/ruby/system/Sequencer.py @@ -44,6 +44,9 @@ class RubyPort(MemObject): access_phys_mem = Param.Bool(True, "should the rubyport atomically update phys_mem") ruby_system = Param.RubySystem("") + +class RubyPortProxy(RubyPort): + type = 'RubyPortProxy' class RubySequencer(RubyPort): type = 'RubySequencer' diff --git a/src/mem/translating_port.cc b/src/mem/se_translating_port_proxy.cc index 7d3123012..0f7ecb491 100644 --- a/src/mem/translating_port.cc +++ b/src/mem/se_translating_port_proxy.cc @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2001-2005 The Regents of The University of Michigan * All rights reserved. * @@ -27,6 +39,7 @@ * * Authors: Ron Dreslinski * Steve Reinhardt + * Andreas Hansson */ #include <string> @@ -35,22 +48,22 @@ #include "base/chunk_generator.hh" #include "config/the_isa.hh" #include "mem/page_table.hh" -#include "mem/port.hh" -#include "mem/translating_port.hh" +#include "mem/se_translating_port_proxy.hh" #include "sim/process.hh" using namespace TheISA; -TranslatingPort::TranslatingPort(const std::string &_name, Process *p, - AllocType alloc) - : FunctionalPort(_name), pTable(p->pTable), process(p), allocating(alloc) +SETranslatingPortProxy::SETranslatingPortProxy(Port& port, Process *p, + AllocType alloc) + : PortProxy(port), pTable(p->pTable), process(p), + allocating(alloc) { } -TranslatingPort::~TranslatingPort() +SETranslatingPortProxy::~SETranslatingPortProxy() { } bool -TranslatingPort::tryReadBlob(Addr addr, uint8_t *p, int size) +SETranslatingPortProxy::tryReadBlob(Addr addr, uint8_t *p, int size) { int prevSize = 0; @@ -60,7 +73,7 @@ TranslatingPort::tryReadBlob(Addr addr, uint8_t *p, int size) if (!pTable->translate(gen.addr(),paddr)) return false; - Port::readBlob(paddr, p + prevSize, gen.size()); + PortProxy::readBlob(paddr, p + prevSize, gen.size()); prevSize += gen.size(); } @@ -68,7 +81,7 @@ TranslatingPort::tryReadBlob(Addr addr, uint8_t *p, int size) } void -TranslatingPort::readBlob(Addr addr, uint8_t *p, int size) +SETranslatingPortProxy::readBlob(Addr addr, uint8_t *p, int size) { if (!tryReadBlob(addr, p, size)) fatal("readBlob(0x%x, ...) failed", addr); @@ -76,7 +89,7 @@ TranslatingPort::readBlob(Addr addr, uint8_t *p, int size) bool -TranslatingPort::tryWriteBlob(Addr addr, uint8_t *p, int size) +SETranslatingPortProxy::tryWriteBlob(Addr addr, uint8_t *p, int size) { int prevSize = 0; @@ -98,7 +111,7 @@ TranslatingPort::tryWriteBlob(Addr addr, uint8_t *p, int size) pTable->translate(gen.addr(), paddr); } - Port::writeBlob(paddr, p + prevSize, gen.size()); + PortProxy::writeBlob(paddr, p + prevSize, gen.size()); prevSize += gen.size(); } @@ -107,14 +120,14 @@ TranslatingPort::tryWriteBlob(Addr addr, uint8_t *p, int size) void -TranslatingPort::writeBlob(Addr addr, uint8_t *p, int size) +SETranslatingPortProxy::writeBlob(Addr addr, uint8_t *p, int size) { if (!tryWriteBlob(addr, p, size)) fatal("writeBlob(0x%x, ...) failed", addr); } bool -TranslatingPort::tryMemsetBlob(Addr addr, uint8_t val, int size) +SETranslatingPortProxy::tryMemsetBlob(Addr addr, uint8_t val, int size) { for (ChunkGenerator gen(addr, size, VMPageSize); !gen.done(); gen.next()) { Addr paddr; @@ -128,14 +141,15 @@ TranslatingPort::tryMemsetBlob(Addr addr, uint8_t val, int size) return false; } } - Port::memsetBlob(paddr, val, gen.size()); + + PortProxy::memsetBlob(paddr, val, gen.size()); } return true; } void -TranslatingPort::memsetBlob(Addr addr, uint8_t val, int size) +SETranslatingPortProxy::memsetBlob(Addr addr, uint8_t val, int size) { if (!tryMemsetBlob(addr, val, size)) fatal("memsetBlob(0x%x, ...) failed", addr); @@ -143,7 +157,7 @@ TranslatingPort::memsetBlob(Addr addr, uint8_t val, int size) bool -TranslatingPort::tryWriteString(Addr addr, const char *str) +SETranslatingPortProxy::tryWriteString(Addr addr, const char *str) { uint8_t c; @@ -156,21 +170,21 @@ TranslatingPort::tryWriteString(Addr addr, const char *str) if (!pTable->translate(vaddr++, paddr)) return false; - Port::writeBlob(paddr, &c, 1); + PortProxy::writeBlob(paddr, &c, 1); } while (c); return true; } void -TranslatingPort::writeString(Addr addr, const char *str) +SETranslatingPortProxy::writeString(Addr addr, const char *str) { if (!tryWriteString(addr, str)) fatal("writeString(0x%x, ...) failed", addr); } bool -TranslatingPort::tryReadString(std::string &str, Addr addr) +SETranslatingPortProxy::tryReadString(std::string &str, Addr addr) { uint8_t c; @@ -182,7 +196,7 @@ TranslatingPort::tryReadString(std::string &str, Addr addr) if (!pTable->translate(vaddr++, paddr)) return false; - Port::readBlob(paddr, &c, 1); + PortProxy::readBlob(paddr, &c, 1); str += c; } while (c); @@ -190,7 +204,7 @@ TranslatingPort::tryReadString(std::string &str, Addr addr) } void -TranslatingPort::readString(std::string &str, Addr addr) +SETranslatingPortProxy::readString(std::string &str, Addr addr) { if (!tryReadString(str, addr)) fatal("readString(0x%x, ...) failed", addr); diff --git a/src/mem/translating_port.hh b/src/mem/se_translating_port_proxy.hh index 438d8fe61..71e8b4f50 100644 --- a/src/mem/translating_port.hh +++ b/src/mem/se_translating_port_proxy.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2011 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2001-2005 The Regents of The University of Michigan * All rights reserved. * @@ -27,18 +39,33 @@ * * Authors: Ron Dreslinski * Ali Saidi + * Andreas Hansson */ -#ifndef __MEM_TRANSLATING_PROT_HH__ -#define __MEM_TRANSLATING_PROT_HH__ - -#include "mem/port.hh" +#ifndef __MEM_SE_TRANSLATING_PORT_PROXY_HH__ +#define __MEM_SE_TRANSLATING_PORT_PROXY_HH__ -class PageTable; -class Process; +#include "mem/page_table.hh" +#include "mem/port_proxy.hh" +#include "sim/process.hh" -class TranslatingPort : public FunctionalPort +/** + * @file + * TranslatingPortProxy Object Declaration for SE. + * + * Port proxies are used when non structural entities need access to + * the memory system. Proxy objects replace the previous + * FunctionalPort, TranslatingPort and VirtualPort objects, which + * provided the same functionality as the proxies, but were instances + * of ports not corresponding to real structural ports of the + * simulated system. Via the port proxies all the accesses go through + * an actual port and thus are transparent to a potentially + * distributed memory and automatically adhere to the memory map of + * the system. + */ +class SETranslatingPortProxy : public PortProxy { + public: enum AllocType { Always, @@ -52,8 +79,8 @@ class TranslatingPort : public FunctionalPort AllocType allocating; public: - TranslatingPort(const std::string &_name, Process *p, AllocType alloc); - virtual ~TranslatingPort(); + SETranslatingPortProxy(Port& port, Process* p, AllocType alloc); + virtual ~SETranslatingPortProxy(); bool tryReadBlob(Addr addr, uint8_t *p, int size); bool tryWriteBlob(Addr addr, uint8_t *p, int size); @@ -69,4 +96,4 @@ class TranslatingPort : public FunctionalPort void readString(std::string &str, Addr addr); }; -#endif +#endif // __MEM_SE_TRANSLATING_PORT_PROXY_HH__ diff --git a/src/mem/tport.cc b/src/mem/tport.cc index 8e02215f2..7b1fdb850 100644 --- a/src/mem/tport.cc +++ b/src/mem/tport.cc @@ -29,12 +29,13 @@ */ #include "debug/Bus.hh" +#include "mem/mem_object.hh" #include "mem/tport.hh" using namespace std; SimpleTimingPort::SimpleTimingPort(string pname, MemObject *_owner) - : Port(pname, _owner), sendEvent(0), drainEvent(NULL), + : Port(pname, _owner), sendEvent(NULL), drainEvent(NULL), waitingOnRetry(false) { sendEvent = new EventWrapper<SimpleTimingPort, @@ -104,6 +105,20 @@ SimpleTimingPort::recvTiming(PacketPtr pkt) return true; } +void +SimpleTimingPort::schedSendEvent(Tick when) +{ + if (waitingOnRetry) { + assert(!sendEvent->scheduled()); + return; + } + + if (!sendEvent->scheduled()) { + owner->schedule(sendEvent, when); + } else if (sendEvent->when() > when) { + owner->reschedule(sendEvent, when); + } +} void SimpleTimingPort::schedSendTiming(PacketPtr pkt, Tick when) @@ -153,7 +168,7 @@ SimpleTimingPort::sendDeferredPacket() if (success) { if (!transmitList.empty() && !sendEvent->scheduled()) { Tick time = transmitList.front().tick; - schedule(sendEvent, time <= curTick() ? curTick()+1 : time); + owner->schedule(sendEvent, time <= curTick() ? curTick()+1 : time); } if (transmitList.empty() && drainEvent && !sendEvent->scheduled()) { diff --git a/src/mem/tport.hh b/src/mem/tport.hh index f081d8656..eaf5acd5d 100644 --- a/src/mem/tport.hh +++ b/src/mem/tport.hh @@ -106,21 +106,14 @@ class SimpleTimingPort : public Port Tick deferredPacketReadyTime() { return transmitList.empty() ? MaxTick : transmitList.front().tick; } - void - schedSendEvent(Tick when) - { - if (waitingOnRetry) { - assert(!sendEvent->scheduled()); - return; - } - - if (!sendEvent->scheduled()) { - schedule(sendEvent, when); - } else if (sendEvent->when() > when) { - reschedule(sendEvent, when); - } - } - + /** + * Schedule a send even if not already waiting for a retry. If the + * requested time is before an already scheduled send event it + * will be rescheduled. + * + * @param when + */ + void schedSendEvent(Tick when); /** Schedule a sendTiming() event to be called in the future. * @param pkt packet to send @@ -146,10 +139,13 @@ class SimpleTimingPort : public Port bool recvTiming(PacketPtr pkt); /** - * Simple ports generally don't care about any status - * changes... can always override this in cases where that's not - * true. */ - virtual void recvStatusChange(Status status) { } + * Simple ports are generally used as slave ports (i.e. the + * respond to requests) and thus do not expect to receive any + * range changes (as the neighbouring port has a master role and + * do not have any address ranges. A subclass can override the + * default behaviuor if needed. + */ + virtual void recvRangeChange() { } public: |