summaryrefslogtreecommitdiff
path: root/src/mem/bus.cc
diff options
context:
space:
mode:
authorAndreas Hansson <andreas.hansson@arm.com>2012-05-31 13:30:04 -0400
committerAndreas Hansson <andreas.hansson@arm.com>2012-05-31 13:30:04 -0400
commit0d329407116921cd9aed6f02da551cc5a8ec5131 (patch)
tree6cc534a94543ed1e40c65d502cd428338eabb94d /src/mem/bus.cc
parentfb9bfb9cfcb6ef5db6881b769c7011ff4907f219 (diff)
downloadgem5-0d329407116921cd9aed6f02da551cc5a8ec5131.tar.xz
Bus: Split the bus into a non-coherent and coherent bus
This patch introduces a class hierarchy of buses, a non-coherent one, and a coherent one, splitting the existing bus functionality. By doing so it also enables further specialisation of the two types of buses. A non-coherent bus connects a number of non-snooping masters and slaves, and routes the request and response packets based on the address. The request packets issued by the master connected to a non-coherent bus could still snoop in caches attached to a coherent bus, as is the case with the I/O bus and memory bus in most system configurations. No snoops will, however, reach any master on the non-coherent bus itself. The non-coherent bus can be used as a template for modelling PCI, PCIe, and non-coherent AMBA and OCP buses, and is typically used for the I/O buses. A coherent bus connects a number of (potentially) snooping masters and slaves, and routes the request and response packets based on the address, and also forwards all requests to the snoopers and deals with the snoop responses. The coherent bus can be used as a template for modelling QPI, HyperTransport, ACE and coherent OCP buses, and is typically used for the L1-to-L2 buses and as the main system interconnect. The configuration scripts are updated to use a NoncoherentBus for all peripheral and I/O buses. A bit of minor tidying up has also been done. --HG-- rename : src/mem/bus.cc => src/mem/coherent_bus.cc rename : src/mem/bus.hh => src/mem/coherent_bus.hh rename : src/mem/bus.cc => src/mem/noncoherent_bus.cc rename : src/mem/bus.hh => src/mem/noncoherent_bus.hh
Diffstat (limited to 'src/mem/bus.cc')
-rw-r--r--src/mem/bus.cc492
1 files changed, 34 insertions, 458 deletions
diff --git a/src/mem/bus.cc b/src/mem/bus.cc
index a22b0e7fd..8040118d2 100644
--- a/src/mem/bus.cc
+++ b/src/mem/bus.cc
@@ -53,7 +53,7 @@
#include "debug/BusAddrRanges.hh"
#include "mem/bus.hh"
-Bus::Bus(const BusParams *p)
+BaseBus::BaseBus(const BaseBusParams *p)
: MemObject(p), clock(p->clock),
headerCycles(p->header_cycles), width(p->width), tickNextIdle(0),
drainEvent(NULL), busIdleEvent(this), inRetry(false),
@@ -69,37 +69,23 @@ 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");
+}
- // create the ports based on the size of the master and slave
- // vector ports, and the presence of the default port, the ports
- // are enumerated starting from zero
- for (int i = 0; i < p->port_master_connection_count; ++i) {
- std::string portName = csprintf("%s-p%d", name(), i);
- MasterPort* bp = new BusMasterPort(portName, this, i);
- masterPorts.push_back(bp);
- }
-
- // see if we have a default slave device connected and if so add
- // our corresponding master port
- if (p->port_default_connection_count) {
- defaultPortID = masterPorts.size();
- std::string portName = csprintf("%s-default", name());
- MasterPort* bp = new BusMasterPort(portName, this, defaultPortID);
- masterPorts.push_back(bp);
+BaseBus::~BaseBus()
+{
+ for (MasterPortIter m = masterPorts.begin(); m != masterPorts.end();
+ ++m) {
+ delete *m;
}
- // create the slave ports, once again starting at zero
- for (int i = 0; i < p->port_slave_connection_count; ++i) {
- std::string portName = csprintf("%s-p%d", name(), i);
- SlavePort* bp = new BusSlavePort(portName, this, i);
- slavePorts.push_back(bp);
+ for (SlavePortIter s = slavePorts.begin(); s != slavePorts.end();
+ ++s) {
+ delete *s;
}
-
- clearPortCache();
}
MasterPort &
-Bus::getMasterPort(const std::string &if_name, int idx)
+BaseBus::getMasterPort(const std::string &if_name, int idx)
{
if (if_name == "master" && idx < masterPorts.size()) {
// the master port index translates directly to the vector position
@@ -112,7 +98,7 @@ Bus::getMasterPort(const std::string &if_name, int idx)
}
SlavePort &
-Bus::getSlavePort(const std::string &if_name, int idx)
+BaseBus::getSlavePort(const std::string &if_name, int idx)
{
if (if_name == "slave" && idx < slavePorts.size()) {
// the slave port index translates directly to the vector position
@@ -122,23 +108,8 @@ Bus::getSlavePort(const std::string &if_name, int idx)
}
}
-void
-Bus::init()
-{
- // iterate over our slave ports and determine which of our
- // neighbouring master ports are snooping and add them as snoopers
- for (SlavePortConstIter p = slavePorts.begin(); p != slavePorts.end();
- ++p) {
- if ((*p)->getMasterPort().isSnooping()) {
- DPRINTF(BusAddrRanges, "Adding snooping neighbour %s\n",
- (*p)->getMasterPort().name());
- snoopPorts.push_back(*p);
- }
- }
-}
-
Tick
-Bus::calcPacketTiming(PacketPtr pkt)
+BaseBus::calcPacketTiming(PacketPtr pkt)
{
// determine the current time rounded to the closest following
// clock edge
@@ -169,7 +140,7 @@ Bus::calcPacketTiming(PacketPtr pkt)
return headerTime;
}
-void Bus::occupyBus(Tick until)
+void BaseBus::occupyBus(Tick until)
{
if (until == 0) {
// shortcut for express snoop packets
@@ -179,12 +150,12 @@ void Bus::occupyBus(Tick until)
tickNextIdle = until;
reschedule(busIdleEvent, tickNextIdle, true);
- DPRINTF(Bus, "The bus is now occupied from tick %d to %d\n",
+ DPRINTF(BaseBus, "The bus is now occupied from tick %d to %d\n",
curTick(), tickNextIdle);
}
bool
-Bus::isOccupied(Port* port)
+BaseBus::isOccupied(Port* port)
{
// first we see if the next idle tick is in the future, next the
// bus is considered occupied if there are ports on the retry list
@@ -197,220 +168,15 @@ Bus::isOccupied(Port* port)
return false;
}
-bool
-Bus::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
-{
- // determine the source port based on the id
- SlavePort *src_port = slavePorts[slave_port_id];
-
- // test if the bus should be considered occupied for the current
- // port, and exclude express snoops from the check
- if (!pkt->isExpressSnoop() && isOccupied(src_port)) {
- DPRINTF(Bus, "recvTimingReq: src %s %s 0x%x BUSY\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
- return false;
- }
-
- DPRINTF(Bus, "recvTimingReq: src %s %s 0x%x\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
-
- // set the source port for routing of the response
- pkt->setSrc(slave_port_id);
-
- Tick headerFinishTime = pkt->isExpressSnoop() ? 0 : calcPacketTiming(pkt);
- Tick packetFinishTime = pkt->isExpressSnoop() ? 0 : pkt->finishTime;
-
- // uncacheable requests need never be snooped
- if (!pkt->req->isUncacheable()) {
- // the packet is a memory-mapped request and should be
- // broadcasted to our snoopers but the source
- forwardTiming(pkt, slave_port_id);
- }
-
- // remember if we add an outstanding req so we can undo it if
- // necessary, if the packet needs a response, we should add it
- // as outstanding and express snoops never fail so there is
- // not need to worry about them
- bool add_outstanding = !pkt->isExpressSnoop() && pkt->needsResponse();
-
- // keep track that we have an outstanding request packet
- // matching this request, this is used by the coherency
- // mechanism in determining what to do with snoop responses
- // (in recvTimingSnoop)
- if (add_outstanding) {
- // we should never have an exsiting request outstanding
- assert(outstandingReq.find(pkt->req) == outstandingReq.end());
- outstandingReq.insert(pkt->req);
- }
-
- // since it is a normal request, determine the destination
- // based on the address and attempt to send the packet
- bool success = masterPorts[findPort(pkt->getAddr())]->sendTimingReq(pkt);
-
- if (!success) {
- // inhibited packets should never be forced to retry
- assert(!pkt->memInhibitAsserted());
-
- // if it was added as outstanding and the send failed, then
- // erase it again
- if (add_outstanding)
- outstandingReq.erase(pkt->req);
-
- DPRINTF(Bus, "recvTimingReq: src %s %s 0x%x RETRY\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
-
- addToRetryList(src_port);
- occupyBus(headerFinishTime);
-
- return false;
- }
-
- succeededTiming(packetFinishTime);
-
- return true;
-}
-
-bool
-Bus::recvTimingResp(PacketPtr pkt, PortID master_port_id)
-{
- // determine the source port based on the id
- MasterPort *src_port = masterPorts[master_port_id];
-
- // test if the bus should be considered occupied for the current
- // port
- if (isOccupied(src_port)) {
- DPRINTF(Bus, "recvTimingResp: src %s %s 0x%x BUSY\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
- return false;
- }
-
- DPRINTF(Bus, "recvTimingResp: src %s %s 0x%x\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
-
- calcPacketTiming(pkt);
- Tick packetFinishTime = pkt->finishTime;
-
- // the packet is a normal response to a request that we should
- // have seen passing through the bus
- assert(outstandingReq.find(pkt->req) != outstandingReq.end());
-
- // remove it as outstanding
- outstandingReq.erase(pkt->req);
-
- // send the packet to the destination through one of our slave
- // ports, as determined by the destination field
- bool success M5_VAR_USED = slavePorts[pkt->getDest()]->sendTimingResp(pkt);
-
- // currently it is illegal to block responses... can lead to
- // deadlock
- assert(success);
-
- succeededTiming(packetFinishTime);
-
- return true;
-}
-
void
-Bus::recvTimingSnoopReq(PacketPtr pkt, PortID master_port_id)
-{
- DPRINTF(Bus, "recvTimingSnoopReq: src %s %s 0x%x\n",
- masterPorts[master_port_id]->name(), pkt->cmdString(),
- pkt->getAddr());
-
- // we should only see express snoops from caches
- assert(pkt->isExpressSnoop());
-
- // set the source port for routing of the response
- pkt->setSrc(master_port_id);
-
- // forward to all snoopers
- forwardTiming(pkt, InvalidPortID);
-
- // a snoop request came from a connected slave device (one of
- // our master ports), and if it is not coming from the slave
- // device responsible for the address range something is
- // wrong, hence there is nothing further to do as the packet
- // would be going back to where it came from
- assert(master_port_id == findPort(pkt->getAddr()));
-
- // this is an express snoop and is never forced to retry
- assert(!inRetry);
-}
-
-bool
-Bus::recvTimingSnoopResp(PacketPtr pkt, PortID slave_port_id)
-{
- // determine the source port based on the id
- SlavePort* src_port = slavePorts[slave_port_id];
-
- // test if the bus should be considered occupied for the current
- // port
- if (isOccupied(src_port)) {
- DPRINTF(Bus, "recvTimingSnoopResp: src %s %s 0x%x BUSY\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
- return false;
- }
-
- DPRINTF(Bus, "recvTimingSnoop: src %s %s 0x%x\n",
- src_port->name(), pkt->cmdString(), pkt->getAddr());
-
- // get the destination from the packet
- PortID dest = pkt->getDest();
-
- // responses are never express snoops
- assert(!pkt->isExpressSnoop());
-
- calcPacketTiming(pkt);
- Tick packetFinishTime = pkt->finishTime;
-
- // determine if the response is from a snoop request we
- // created as the result of a normal request (in which case it
- // should be in the outstandingReq), or if we merely forwarded
- // someone else's snoop request
- if (outstandingReq.find(pkt->req) == outstandingReq.end()) {
- // this is a snoop response to a snoop request we
- // forwarded, e.g. coming from the L1 and going to the L2
- // this should be forwarded as a snoop response
- bool success M5_VAR_USED = masterPorts[dest]->sendTimingSnoopResp(pkt);
- assert(success);
- } else {
- // we got a snoop response on one of our slave ports,
- // i.e. from a coherent master connected to the bus, and
- // since we created the snoop request as part of
- // recvTiming, this should now be a normal response again
- outstandingReq.erase(pkt->req);
-
- // this is a snoop response from a coherent master, with a
- // destination field set on its way through the bus as
- // request, hence it should never go back to where the
- // snoop response came from, but instead to where the
- // original request came from
- assert(slave_port_id != dest);
-
- // as a normal response, it should go back to a master
- // through one of our slave ports
- bool success M5_VAR_USED = slavePorts[dest]->sendTimingResp(pkt);
-
- // currently it is illegal to block responses... can lead
- // to deadlock
- assert(success);
- }
-
- succeededTiming(packetFinishTime);
-
- return true;
-}
-
-
-void
-Bus::succeededTiming(Tick busy_time)
+BaseBus::succeededTiming(Tick busy_time)
{
// occupy the bus accordingly
occupyBus(busy_time);
// if a retrying port succeeded, also take it off the retry list
if (inRetry) {
- DPRINTF(Bus, "Remove retry from list %s\n",
+ DPRINTF(BaseBus, "Remove retry from list %s\n",
retryList.front()->name());
retryList.pop_front();
inRetry = false;
@@ -418,24 +184,7 @@ Bus::succeededTiming(Tick busy_time)
}
void
-Bus::forwardTiming(PacketPtr pkt, PortID exclude_slave_port_id)
-{
- for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
- SlavePort *p = *s;
- // we could have gotten this request from a snooping master
- // (corresponding to our own slave port that is also in
- // snoopPorts) and should not send it back to where it came
- // from
- if (exclude_slave_port_id == InvalidPortID ||
- p->getId() != exclude_slave_port_id) {
- // cache is not allowed to refuse snoop
- p->sendTimingSnoopReq(pkt);
- }
- }
-}
-
-void
-Bus::releaseBus()
+BaseBus::releaseBus()
{
// releasing the bus means we should now be idle
assert(curTick() >= tickNextIdle);
@@ -458,7 +207,7 @@ Bus::releaseBus()
}
void
-Bus::retryWaiting()
+BaseBus::retryWaiting()
{
// this should never be called with an empty retry list
assert(!retryList.empty());
@@ -489,7 +238,7 @@ Bus::retryWaiting()
}
void
-Bus::recvRetry()
+BaseBus::recvRetry()
{
// we got a retry from a peer that we tried to send something to
// and failed, but we sent it on the account of someone else, and
@@ -509,7 +258,7 @@ Bus::recvRetry()
}
PortID
-Bus::findPort(Addr addr)
+BaseBus::findPort(Addr addr)
{
/* An interval tree would be a better way to do this. --ali. */
PortID dest_id = checkPortCache(addr);
@@ -529,12 +278,13 @@ Bus::findPort(Addr addr)
AddrRangeIter a_end = defaultRange.end();
for (AddrRangeIter i = defaultRange.begin(); i != a_end; i++) {
if (*i == addr) {
- DPRINTF(Bus, " found addr %#llx on default\n", addr);
+ DPRINTF(BusAddrRanges, " found addr %#llx on default\n",
+ addr);
return defaultPortID;
}
}
} else if (defaultPortID != InvalidPortID) {
- DPRINTF(Bus, "Unable to find destination for addr %#llx, "
+ DPRINTF(BusAddrRanges, "Unable to find destination for addr %#llx, "
"will use default port\n", addr);
return defaultPortID;
}
@@ -545,170 +295,9 @@ Bus::findPort(Addr addr)
name());
}
-Tick
-Bus::recvAtomic(PacketPtr pkt, PortID slave_port_id)
-{
- DPRINTF(Bus, "recvAtomic: packet src %s addr 0x%x cmd %s\n",
- slavePorts[slave_port_id]->name(), pkt->getAddr(),
- pkt->cmdString());
-
- MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
- Tick snoop_response_latency = 0;
-
- // uncacheable requests need never be snooped
- if (!pkt->req->isUncacheable()) {
- // forward to all snoopers but the source
- std::pair<MemCmd, Tick> snoop_result =
- forwardAtomic(pkt, slave_port_id);
- snoop_response_cmd = snoop_result.first;
- snoop_response_latency = snoop_result.second;
- }
-
- // even if we had a snoop response, we must continue and also
- // perform the actual request at the destination
- PortID dest_id = findPort(pkt->getAddr());
-
- // forward the request to the appropriate destination
- Tick response_latency = masterPorts[dest_id]->sendAtomic(pkt);
-
- // if we got a response from a snooper, restore it here
- if (snoop_response_cmd != MemCmd::InvalidCmd) {
- // no one else should have responded
- assert(!pkt->isResponse());
- pkt->cmd = snoop_response_cmd;
- response_latency = snoop_response_latency;
- }
-
- pkt->finishTime = curTick() + response_latency;
- return response_latency;
-}
-
-Tick
-Bus::recvAtomicSnoop(PacketPtr pkt, PortID master_port_id)
-{
- DPRINTF(Bus, "recvAtomicSnoop: packet src %s addr 0x%x cmd %s\n",
- masterPorts[master_port_id]->name(), pkt->getAddr(),
- pkt->cmdString());
-
- // forward to all snoopers
- std::pair<MemCmd, Tick> snoop_result =
- forwardAtomic(pkt, InvalidPortID);
- MemCmd snoop_response_cmd = snoop_result.first;
- Tick snoop_response_latency = snoop_result.second;
-
- if (snoop_response_cmd != MemCmd::InvalidCmd)
- pkt->cmd = snoop_response_cmd;
-
- pkt->finishTime = curTick() + snoop_response_latency;
- return snoop_response_latency;
-}
-
-std::pair<MemCmd, Tick>
-Bus::forwardAtomic(PacketPtr pkt, PortID exclude_slave_port_id)
-{
- // the packet may be changed on snoops, record the original
- // command to enable us to restore it between snoops so that
- // additional snoops can take place properly
- MemCmd orig_cmd = pkt->cmd;
- MemCmd snoop_response_cmd = MemCmd::InvalidCmd;
- Tick snoop_response_latency = 0;
-
- for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
- SlavePort *p = *s;
- // we could have gotten this request from a snooping master
- // (corresponding to our own slave port that is also in
- // snoopPorts) and should not send it back to where it came
- // from
- if (exclude_slave_port_id == InvalidPortID ||
- p->getId() != exclude_slave_port_id) {
- Tick latency = p->sendAtomicSnoop(pkt);
- // in contrast to a functional access, we have to keep on
- // going as all snoopers must be updated even if we get a
- // response
- if (pkt->isResponse()) {
- // response from snoop agent
- assert(pkt->cmd != orig_cmd);
- assert(pkt->memInhibitAsserted());
- // should only happen once
- assert(snoop_response_cmd == MemCmd::InvalidCmd);
- // save response state
- snoop_response_cmd = pkt->cmd;
- snoop_response_latency = latency;
- // restore original packet state for remaining snoopers
- pkt->cmd = orig_cmd;
- }
- }
- }
-
- // the packet is restored as part of the loop and any potential
- // snoop response is part of the returned pair
- return std::make_pair(snoop_response_cmd, snoop_response_latency);
-}
-
-void
-Bus::recvFunctional(PacketPtr pkt, PortID slave_port_id)
-{
- if (!pkt->isPrint()) {
- // don't do DPRINTFs on PrintReq as it clutters up the output
- DPRINTF(Bus,
- "recvFunctional: packet src %s addr 0x%x cmd %s\n",
- slavePorts[slave_port_id]->name(), pkt->getAddr(),
- pkt->cmdString());
- }
-
- // uncacheable requests need never be snooped
- if (!pkt->req->isUncacheable()) {
- // forward to all snoopers but the source
- forwardFunctional(pkt, slave_port_id);
- }
-
- // there is no need to continue if the snooping has found what we
- // were looking for and the packet is already a response
- if (!pkt->isResponse()) {
- PortID dest_id = findPort(pkt->getAddr());
-
- masterPorts[dest_id]->sendFunctional(pkt);
- }
-}
-
-void
-Bus::recvFunctionalSnoop(PacketPtr pkt, PortID master_port_id)
-{
- if (!pkt->isPrint()) {
- // don't do DPRINTFs on PrintReq as it clutters up the output
- DPRINTF(Bus,
- "recvFunctionalSnoop: packet src %s addr 0x%x cmd %s\n",
- masterPorts[master_port_id]->name(), pkt->getAddr(),
- pkt->cmdString());
- }
-
- // forward to all snoopers
- forwardFunctional(pkt, InvalidPortID);
-}
-
-void
-Bus::forwardFunctional(PacketPtr pkt, PortID exclude_slave_port_id)
-{
- for (SlavePortIter s = snoopPorts.begin(); s != snoopPorts.end(); ++s) {
- SlavePort *p = *s;
- // we could have gotten this request from a snooping master
- // (corresponding to our own slave port that is also in
- // snoopPorts) and should not send it back to where it came
- // from
- if (exclude_slave_port_id == InvalidPortID ||
- p->getId() != exclude_slave_port_id)
- p->sendFunctionalSnoop(pkt);
-
- // if we get a response we are done
- if (pkt->isResponse()) {
- break;
- }
- }
-}
-
/** Function called by the port when the bus is receiving a range change.*/
void
-Bus::recvRangeChange(PortID master_port_id)
+BaseBus::recvRangeChange(PortID master_port_id)
{
AddrRangeList ranges;
AddrRangeIter iter;
@@ -773,7 +362,7 @@ Bus::recvRangeChange(PortID master_port_id)
}
AddrRangeList
-Bus::getAddrRanges()
+BaseBus::getAddrRanges()
{
AddrRangeList ranges;
@@ -813,29 +402,22 @@ Bus::getAddrRanges()
return ranges;
}
-bool
-Bus::isSnooping() const
-{
- // in essence, answer the question if there are snooping ports
- return !snoopPorts.empty();
-}
-
unsigned
-Bus::findBlockSize()
+BaseBus::findBlockSize()
{
if (cachedBlockSizeValid)
return cachedBlockSize;
unsigned max_bs = 0;
- PortIter p_end = portMap.end();
- for (PortIter p_iter = portMap.begin(); p_iter != p_end; p_iter++) {
- unsigned tmp_bs = masterPorts[p_iter->second]->peerBlockSize();
+ for (MasterPortConstIter m = masterPorts.begin(); m != masterPorts.end();
+ ++m) {
+ unsigned tmp_bs = (*m)->peerBlockSize();
if (tmp_bs > max_bs)
max_bs = tmp_bs;
}
- for (SlavePortConstIter s = snoopPorts.begin(); s != snoopPorts.end();
+ for (SlavePortConstIter s = slavePorts.begin(); s != slavePorts.end();
++s) {
unsigned tmp_bs = (*s)->peerBlockSize();
if (tmp_bs > max_bs)
@@ -853,7 +435,7 @@ Bus::findBlockSize()
unsigned int
-Bus::drain(Event * de)
+BaseBus::drain(Event * de)
{
//We should check that we're not "doing" anything, and that noone is
//waiting. We might be idle but have someone waiting if the device we
@@ -867,14 +449,8 @@ Bus::drain(Event * de)
}
void
-Bus::startup()
+BaseBus::startup()
{
if (tickNextIdle < curTick())
tickNextIdle = (curTick() / clock) * clock + clock;
}
-
-Bus *
-BusParams::create()
-{
- return new Bus(this);
-}