summaryrefslogtreecommitdiff
path: root/src/mem/bridge.cc
diff options
context:
space:
mode:
authorAndreas Hansson <andreas.hansson@arm.com>2012-01-17 12:55:09 -0600
committerAndreas Hansson <andreas.hansson@arm.com>2012-01-17 12:55:09 -0600
commit2208ea049f60618e432c69c065926bcbc810581a (patch)
treedcc2c0afed74ec56969df9fa20b92655f767c158 /src/mem/bridge.cc
parente731cf4c1df8db0c7bcb689aba0146199a93b64e (diff)
downloadgem5-2208ea049f60618e432c69c065926bcbc810581a.tar.xz
MEM: Make the bus bridge unidirectional and fixed address range
This patch makes the bus bridge uni-directional and specialises the bus ports to be a master port and a slave port. This greatly simplifies the assumptions on both sides as either port only has to deal with requests or responses. The following patches introduce the notion of master and slave ports, and would not be possible without this split of responsibilities. In making the bridge unidirectional, the address range mechanism of the bridge is also changed. For the cases where communication is taking place both ways, an additional bridge is needed. This causes issues with the existing mechanism, as the busses cannot determine when to stop iterating the address updates from the two bridges. To avoid this issue, and also greatly simplify the specification, the bridge now has a fixed set of address ranges, specified at creation time.
Diffstat (limited to 'src/mem/bridge.cc')
-rw-r--r--src/mem/bridge.cc381
1 files changed, 262 insertions, 119 deletions
diff --git a/src/mem/bridge.cc b/src/mem/bridge.cc
index 71cbcb76c..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,54 +39,63 @@
*
* 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;
@@ -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::BridgePort::respQueueFull()
+Bridge::BridgeSlavePort::respQueueFull()
{
- assert(outstandingResponses >= 0 && outstandingResponses <= respQueueLimit);
- return outstandingResponses >= respQueueLimit;
+ return outstandingResponses == respQueueLimit;
}
bool
-Bridge::BridgePort::reqQueueFull()
+Bridge::BridgeMasterPort::reqQueueFull()
{
- assert(queuedRequests >= 0 && queuedRequests <= reqQueueLimit);
- return queuedRequests >= reqQueueLimit;
+ return requestQueue.size() == reqQueueLimit;
}
-/** Function called by the port when the bus is receiving a Timing
- * transaction.*/
bool
-Bridge::BridgePort::recvTiming(PacketPtr pkt)
+Bridge::BridgeMasterPort::recvTiming(PacketPtr pkt)
{
+ // 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());
+ pkt->getSrc(), pkt->getDest(), pkt->getAddr());
+
+ DPRINTF(BusBridge, "Request queue size: %d\n", requestQueue.size());
- 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);
+ slavePort->queueForSendTiming(pkt);
+
+ return true;
+}
- if (pkt->isRequest() && otherPort->reqQueueFull()) {
- DPRINTF(BusBridge, "Remote queue full, nacking\n");
+bool
+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());
+
+ DPRINTF(BusBridge, "Response queue size: %d outresp: %d\n",
+ responseQueue.size(), outstandingResponses);
+
+ 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());
bridge->schedule(sendEvent, readyTime);
- sendQueue.push_back(buf);
+ 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)
bridge->reschedule(sendEvent, readyTime);
- sendQueue.insert(i,buf);
+ 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()) {
+ 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,107 +305,194 @@ 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");
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
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);
+}
+
+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::recvRangeChange()
+Bridge::BridgeMasterPort::recvRangeChange()
+{
+ // no need to forward as the bridge has a fixed set of ranges
+}
+
+void
+Bridge::BridgeSlavePort::recvRangeChange()
{
- otherPort->sendRangeChange();
+ // is a slave port so do nothing
}
AddrRangeList
-Bridge::BridgePort::getAddrRanges()
+Bridge::BridgeSlavePort::getAddrRanges()
{
- AddrRangeList ranges = otherPort->getPeer()->getAddrRanges();
- FilterRangeList(filterRanges, ranges);
return ranges;
}