330 lines
12 KiB
C++
330 lines
12 KiB
C++
/*
|
|
* Copyright (c) 2011-2015 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.
|
|
*
|
|
* 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: Ali Saidi
|
|
* Andreas Hansson
|
|
* William Wang
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* Definition of a non-coherent crossbar object.
|
|
*/
|
|
|
|
#include "mem/noncoherent_xbar.hh"
|
|
|
|
#include "base/misc.hh"
|
|
#include "base/trace.hh"
|
|
#include "debug/NoncoherentXBar.hh"
|
|
#include "debug/XBar.hh"
|
|
|
|
NoncoherentXBar::NoncoherentXBar(const NoncoherentXBarParams *p)
|
|
: BaseXBar(p)
|
|
{
|
|
// 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.master[%d]", name(), i);
|
|
MasterPort* bp = new NoncoherentXBarMasterPort(portName, *this, i);
|
|
masterPorts.push_back(bp);
|
|
reqLayers.push_back(new ReqLayer(*bp, *this,
|
|
csprintf(".reqLayer%d", i)));
|
|
}
|
|
|
|
// 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 = name() + ".default";
|
|
MasterPort* bp = new NoncoherentXBarMasterPort(portName, *this,
|
|
defaultPortID);
|
|
masterPorts.push_back(bp);
|
|
reqLayers.push_back(new ReqLayer(*bp, *this, csprintf(".reqLayer%d",
|
|
defaultPortID)));
|
|
}
|
|
|
|
// 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.slave[%d]", name(), i);
|
|
QueuedSlavePort* bp = new NoncoherentXBarSlavePort(portName, *this, i);
|
|
slavePorts.push_back(bp);
|
|
respLayers.push_back(new RespLayer(*bp, *this,
|
|
csprintf(".respLayer%d", i)));
|
|
}
|
|
|
|
clearPortCache();
|
|
}
|
|
|
|
NoncoherentXBar::~NoncoherentXBar()
|
|
{
|
|
for (auto l: reqLayers)
|
|
delete l;
|
|
for (auto l: respLayers)
|
|
delete l;
|
|
}
|
|
|
|
bool
|
|
NoncoherentXBar::recvTimingReq(PacketPtr pkt, PortID slave_port_id)
|
|
{
|
|
// determine the source port based on the id
|
|
SlavePort *src_port = slavePorts[slave_port_id];
|
|
|
|
// we should never see express snoops on a non-coherent crossbar
|
|
assert(!pkt->isExpressSnoop());
|
|
|
|
// determine the destination based on the address
|
|
PortID master_port_id = findPort(pkt->getAddr());
|
|
|
|
// test if the layer should be considered occupied for the current
|
|
// port
|
|
if (!reqLayers[master_port_id]->tryTiming(src_port)) {
|
|
DPRINTF(NoncoherentXBar, "recvTimingReq: src %s %s 0x%x BUSY\n",
|
|
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
|
return false;
|
|
}
|
|
|
|
DPRINTF(NoncoherentXBar, "recvTimingReq: src %s %s 0x%x\n",
|
|
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
|
|
|
// store size and command as they might be modified when
|
|
// forwarding the packet
|
|
unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
|
|
unsigned int pkt_cmd = pkt->cmdToIndex();
|
|
|
|
// store the old header delay so we can restore it if needed
|
|
Tick old_header_delay = pkt->headerDelay;
|
|
|
|
// a request sees the frontend and forward latency
|
|
Tick xbar_delay = (frontendLatency + forwardLatency) * clockPeriod();
|
|
|
|
// set the packet header and payload delay
|
|
calcPacketTiming(pkt, xbar_delay);
|
|
|
|
// determine how long to be crossbar layer is busy
|
|
Tick packetFinishTime = clockEdge(Cycles(1)) + pkt->payloadDelay;
|
|
|
|
// before forwarding the packet (and possibly altering it),
|
|
// remember if we are expecting a response
|
|
const bool expect_response = pkt->needsResponse() &&
|
|
!pkt->cacheResponding();
|
|
|
|
// since it is a normal request, attempt to send the packet
|
|
bool success = masterPorts[master_port_id]->sendTimingReq(pkt);
|
|
|
|
if (!success) {
|
|
DPRINTF(NoncoherentXBar, "recvTimingReq: src %s %s 0x%x RETRY\n",
|
|
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
|
|
|
// restore the header delay as it is additive
|
|
pkt->headerDelay = old_header_delay;
|
|
|
|
// occupy until the header is sent
|
|
reqLayers[master_port_id]->failedTiming(src_port,
|
|
clockEdge(Cycles(1)));
|
|
|
|
return false;
|
|
}
|
|
|
|
// remember where to route the response to
|
|
if (expect_response) {
|
|
assert(routeTo.find(pkt->req) == routeTo.end());
|
|
routeTo[pkt->req] = slave_port_id;
|
|
}
|
|
|
|
reqLayers[master_port_id]->succeededTiming(packetFinishTime);
|
|
|
|
// stats updates
|
|
pktCount[slave_port_id][master_port_id]++;
|
|
pktSize[slave_port_id][master_port_id] += pkt_size;
|
|
transDist[pkt_cmd]++;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
NoncoherentXBar::recvTimingResp(PacketPtr pkt, PortID master_port_id)
|
|
{
|
|
// determine the source port based on the id
|
|
MasterPort *src_port = masterPorts[master_port_id];
|
|
|
|
// determine the destination
|
|
const auto route_lookup = routeTo.find(pkt->req);
|
|
assert(route_lookup != routeTo.end());
|
|
const PortID slave_port_id = route_lookup->second;
|
|
assert(slave_port_id != InvalidPortID);
|
|
assert(slave_port_id < respLayers.size());
|
|
|
|
// test if the layer should be considered occupied for the current
|
|
// port
|
|
if (!respLayers[slave_port_id]->tryTiming(src_port)) {
|
|
DPRINTF(NoncoherentXBar, "recvTimingResp: src %s %s 0x%x BUSY\n",
|
|
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
|
return false;
|
|
}
|
|
|
|
DPRINTF(NoncoherentXBar, "recvTimingResp: src %s %s 0x%x\n",
|
|
src_port->name(), pkt->cmdString(), pkt->getAddr());
|
|
|
|
// store size and command as they might be modified when
|
|
// forwarding the packet
|
|
unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
|
|
unsigned int pkt_cmd = pkt->cmdToIndex();
|
|
|
|
// a response sees the response latency
|
|
Tick xbar_delay = responseLatency * clockPeriod();
|
|
|
|
// set the packet header and payload delay
|
|
calcPacketTiming(pkt, xbar_delay);
|
|
|
|
// determine how long to be crossbar layer is busy
|
|
Tick packetFinishTime = clockEdge(Cycles(1)) + pkt->payloadDelay;
|
|
|
|
// send the packet through the destination slave port, and pay for
|
|
// any outstanding latency
|
|
Tick latency = pkt->headerDelay;
|
|
pkt->headerDelay = 0;
|
|
slavePorts[slave_port_id]->schedTimingResp(pkt, curTick() + latency);
|
|
|
|
// remove the request from the routing table
|
|
routeTo.erase(route_lookup);
|
|
|
|
respLayers[slave_port_id]->succeededTiming(packetFinishTime);
|
|
|
|
// stats updates
|
|
pktCount[slave_port_id][master_port_id]++;
|
|
pktSize[slave_port_id][master_port_id] += pkt_size;
|
|
transDist[pkt_cmd]++;
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
NoncoherentXBar::recvReqRetry(PortID master_port_id)
|
|
{
|
|
// responses never block on forwarding them, so the retry will
|
|
// always be coming from a port to which we tried to forward a
|
|
// request
|
|
reqLayers[master_port_id]->recvRetry();
|
|
}
|
|
|
|
Tick
|
|
NoncoherentXBar::recvAtomic(PacketPtr pkt, PortID slave_port_id)
|
|
{
|
|
DPRINTF(NoncoherentXBar, "recvAtomic: packet src %s addr 0x%x cmd %s\n",
|
|
slavePorts[slave_port_id]->name(), pkt->getAddr(),
|
|
pkt->cmdString());
|
|
|
|
unsigned int pkt_size = pkt->hasData() ? pkt->getSize() : 0;
|
|
unsigned int pkt_cmd = pkt->cmdToIndex();
|
|
|
|
// determine the destination port
|
|
PortID master_port_id = findPort(pkt->getAddr());
|
|
|
|
// stats updates for the request
|
|
pktCount[slave_port_id][master_port_id]++;
|
|
pktSize[slave_port_id][master_port_id] += pkt_size;
|
|
transDist[pkt_cmd]++;
|
|
|
|
// forward the request to the appropriate destination
|
|
Tick response_latency = masterPorts[master_port_id]->sendAtomic(pkt);
|
|
|
|
// add the response data
|
|
if (pkt->isResponse()) {
|
|
pkt_size = pkt->hasData() ? pkt->getSize() : 0;
|
|
pkt_cmd = pkt->cmdToIndex();
|
|
|
|
// stats updates
|
|
pktCount[slave_port_id][master_port_id]++;
|
|
pktSize[slave_port_id][master_port_id] += pkt_size;
|
|
transDist[pkt_cmd]++;
|
|
}
|
|
|
|
// @todo: Not setting first-word time
|
|
pkt->payloadDelay = response_latency;
|
|
return response_latency;
|
|
}
|
|
|
|
void
|
|
NoncoherentXBar::recvFunctional(PacketPtr pkt, PortID slave_port_id)
|
|
{
|
|
if (!pkt->isPrint()) {
|
|
// don't do DPRINTFs on PrintReq as it clutters up the output
|
|
DPRINTF(NoncoherentXBar,
|
|
"recvFunctional: packet src %s addr 0x%x cmd %s\n",
|
|
slavePorts[slave_port_id]->name(), pkt->getAddr(),
|
|
pkt->cmdString());
|
|
}
|
|
|
|
// since our slave ports are queued ports we need to check them as well
|
|
for (const auto& p : slavePorts) {
|
|
// if we find a response that has the data, then the
|
|
// downstream caches/memories may be out of date, so simply stop
|
|
// here
|
|
if (p->checkFunctional(pkt)) {
|
|
if (pkt->needsResponse())
|
|
pkt->makeResponse();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// determine the destination port
|
|
PortID dest_id = findPort(pkt->getAddr());
|
|
|
|
// forward the request to the appropriate destination
|
|
masterPorts[dest_id]->sendFunctional(pkt);
|
|
}
|
|
|
|
NoncoherentXBar*
|
|
NoncoherentXBarParams::create()
|
|
{
|
|
return new NoncoherentXBar(this);
|
|
}
|
|
|
|
void
|
|
NoncoherentXBar::regStats()
|
|
{
|
|
// register the stats of the base class and our layers
|
|
BaseXBar::regStats();
|
|
for (auto l: reqLayers)
|
|
l->regStats();
|
|
for (auto l: respLayers)
|
|
l->regStats();
|
|
}
|