A possible implementation of a multiplexed bus.
--HG-- extra : convert_revision : 3c560eda12ffd8ca539c91024baf2770b963ede8
This commit is contained in:
parent
34b697cd04
commit
00481d1f19
158
src/mem/bus.cc
158
src/mem/bus.cc
|
@ -67,6 +67,44 @@ Bus::init()
|
|||
(*intIter)->sendStatusChange(Port::RangeChange);
|
||||
}
|
||||
|
||||
Bus::BusFreeEvent::BusFreeEvent(Bus *_bus) : Event(&mainEventQueue), bus(_bus)
|
||||
{}
|
||||
|
||||
void Bus::BusFreeEvent::process()
|
||||
{
|
||||
bus->recvRetry(0);
|
||||
}
|
||||
|
||||
const char * Bus::BusFreeEvent::description()
|
||||
{
|
||||
return "bus became available";
|
||||
}
|
||||
|
||||
void
|
||||
Bus::occupyBus(int numCycles)
|
||||
{
|
||||
//Move up when the bus will next be free
|
||||
//We avoid the use of divide by adding repeatedly
|
||||
//This should be faster if the value is updated frequently, but should
|
||||
//be may be slower otherwise.
|
||||
|
||||
//Bring tickNextIdle up to the present tick
|
||||
//There is some potential ambiguity where a cycle starts, which might make
|
||||
//a difference when devices are acting right around a cycle boundary. Using
|
||||
//a < allows things which happen exactly on a cycle boundary to take up only
|
||||
//the following cycle. Anthing that happens later will have to "wait" for the
|
||||
//end of that cycle, and then start using the bus after that.
|
||||
while (tickNextIdle < curTick)
|
||||
tickNextIdle += clock;
|
||||
//Advance it numCycles bus cycles.
|
||||
//XXX Should this use the repeating add trick as well?
|
||||
tickNextIdle += (numCycles * clock);
|
||||
if (!busIdle.scheduled()) {
|
||||
busIdle.schedule(tickNextIdle);
|
||||
} else {
|
||||
busIdle.reschedule(tickNextIdle);
|
||||
}
|
||||
}
|
||||
|
||||
/** Function called by the port when the bus is receiving a Timing
|
||||
* transaction.*/
|
||||
|
@ -77,83 +115,89 @@ Bus::recvTiming(Packet *pkt)
|
|||
DPRINTF(Bus, "recvTiming: packet src %d dest %d addr 0x%x cmd %s\n",
|
||||
pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString());
|
||||
|
||||
short dest = pkt->getDest();
|
||||
//if (pkt->isRequest() && curTick < tickAddrLastUsed ||
|
||||
// (pkt->isResponse() || pkt->hasData()) && curTick < tickDataLastUsed) {
|
||||
//We're going to need resources that have already been committed
|
||||
//Send this guy to the back of the line
|
||||
//We don't need to worry about scheduling an event to deal with when the
|
||||
//bus is freed because that's handled when tick*LastUsed is incremented.
|
||||
// retryList.push_back(interfaces[pkt->getSrc()]);
|
||||
// return false;
|
||||
//}
|
||||
Port *pktPort = interfaces[pkt->getSrc()];
|
||||
|
||||
if (dest == Packet::Broadcast) {
|
||||
if ( timingSnoopPhase1(pkt) )
|
||||
{
|
||||
timingSnoopPhase2(pkt);
|
||||
port = findPort(pkt->getAddr(), pkt->getSrc());
|
||||
}
|
||||
else
|
||||
{
|
||||
//Snoop didn't succeed
|
||||
retryList.push_back(interfaces[pkt->getSrc()]);
|
||||
// If the bus is busy, or other devices are in line ahead of the current one,
|
||||
// put this device on the retry list.
|
||||
if (tickNextIdle > curTick || (retryList.size() && pktPort != retryingPort)) {
|
||||
addToRetryList(pktPort);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
assert(dest >= 0 && dest < interfaces.size());
|
||||
assert(dest != pkt->getSrc()); // catch infinite loops
|
||||
port = interfaces[dest];
|
||||
|
||||
// If the bus is blocked, make the device wait.
|
||||
if (!(port = findDestPort(pkt, pkt->getSrc()))) {
|
||||
addToRetryList(pktPort);
|
||||
return false;
|
||||
}
|
||||
|
||||
// The packet will be sent. Figure out how long it occupies the bus.
|
||||
int numCycles = 0;
|
||||
// Requests need one cycle to send an address
|
||||
if (pkt->isRequest())
|
||||
numCycles++;
|
||||
else if (pkt->isResponse() || pkt->hasData()) {
|
||||
// If a packet has data, it needs ceil(size/width) cycles to send it
|
||||
// We're using the "adding instead of dividing" trick again here
|
||||
if (pkt->hasData()) {
|
||||
int dataSize = pkt->getSize();
|
||||
for (int transmitted = 0; transmitted < dataSize;
|
||||
transmitted += width) {
|
||||
numCycles++;
|
||||
}
|
||||
} else {
|
||||
// If the packet didn't have data, it must have been a response.
|
||||
// Those use the bus for one cycle to send their data.
|
||||
numCycles++;
|
||||
}
|
||||
}
|
||||
|
||||
occupyBus(numCycles);
|
||||
|
||||
if (port->sendTiming(pkt)) {
|
||||
// Packet was successfully sent.
|
||||
// Figure out what resources were used, and then return true.
|
||||
//if (pkt->isRequest()) {
|
||||
// The address bus will be used for one cycle
|
||||
// while (tickAddrLastUsed <= curTick)
|
||||
// tickAddrLastUsed += clock;
|
||||
//}
|
||||
//if (pkt->isResponse() || pkt->hasData()) {
|
||||
// Use up the data bus for at least one bus cycle
|
||||
// while (tickDataLastUsed <= curTick)
|
||||
// tickDataLastUsed += clock;
|
||||
// Use up the data bus for however many cycles remain
|
||||
// if (pkt->hasData()) {
|
||||
// int dataSize = pkt->getSize();
|
||||
// for (int transmitted = width; transmitted < dataSize;
|
||||
// transmitted += width) {
|
||||
// tickDataLastUsed += clock;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
// Packet was successfully sent. Return true.
|
||||
return true;
|
||||
}
|
||||
|
||||
// packet not successfully sent
|
||||
retryList.push_back(interfaces[pkt->getSrc()]);
|
||||
// Packet not successfully sent. Leave or put it on the retry list.
|
||||
addToRetryList(pktPort);
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
Bus::recvRetry(int id)
|
||||
{
|
||||
// Go through all the elements on the list calling sendRetry on each
|
||||
// This is not very efficient at all but it works. Ultimately we should end
|
||||
// up with something that is more intelligent.
|
||||
int initialSize = retryList.size();
|
||||
int i;
|
||||
Port *p;
|
||||
|
||||
for (i = 0; i < initialSize; i++) {
|
||||
assert(retryList.size() > 0);
|
||||
p = retryList.front();
|
||||
//If there's anything waiting...
|
||||
if (retryList.size()) {
|
||||
retryingPort = retryList.front();
|
||||
retryingPort->sendRetry();
|
||||
//If the retryingPort pointer isn't null, either sendTiming wasn't
|
||||
//called, or it was and the packet was successfully sent.
|
||||
if (retryingPort) {
|
||||
retryList.pop_front();
|
||||
p->sendRetry();
|
||||
retryingPort = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Port *
|
||||
Bus::findDestPort(PacketPtr pkt, int id)
|
||||
{
|
||||
Port * port = NULL;
|
||||
short dest = pkt->getDest();
|
||||
|
||||
if (dest == Packet::Broadcast) {
|
||||
if (timingSnoopPhase1(pkt)) {
|
||||
timingSnoopPhase2(pkt);
|
||||
port = findPort(pkt->getAddr(), pkt->getSrc());
|
||||
}
|
||||
//else, port stays NULL
|
||||
} else {
|
||||
assert(dest >= 0 && dest < interfaces.size());
|
||||
assert(dest != pkt->getSrc()); // catch infinite loops
|
||||
port = interfaces[dest];
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
Port *
|
||||
Bus::findPort(Addr addr, int id)
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "mem/packet.hh"
|
||||
#include "mem/port.hh"
|
||||
#include "mem/request.hh"
|
||||
#include "sim/eventq.hh"
|
||||
|
||||
class Bus : public MemObject
|
||||
{
|
||||
|
@ -55,10 +56,8 @@ class Bus : public MemObject
|
|||
int clock;
|
||||
/** the width of the bus in bits */
|
||||
int width;
|
||||
/** the last tick the address bus was used */
|
||||
Tick tickAddrLastUsed;
|
||||
/** the last tick the data bus was used */
|
||||
Tick tickDataLastUsed;
|
||||
/** the next tick at which the bus will be idle */
|
||||
Tick tickNextIdle;
|
||||
|
||||
static const int defaultId = -1;
|
||||
|
||||
|
@ -101,6 +100,15 @@ class Bus : public MemObject
|
|||
*/
|
||||
Port *findPort(Addr addr, int id);
|
||||
|
||||
/** Finds the port a packet should be sent to. If the bus is blocked, no port
|
||||
* is returned.
|
||||
* @param pkt Packet to find a destination port for.
|
||||
* @param id Id of the port this packet was received from
|
||||
* (to prevent loops)
|
||||
*/
|
||||
|
||||
Port *findDestPort(PacketPtr pkt, int id);
|
||||
|
||||
/** Find all ports with a matching snoop range, except src port. Keep in mind
|
||||
* that the ranges shouldn't overlap or you will get a double snoop to the same
|
||||
* interface.and the cache will assert out.
|
||||
|
@ -189,6 +197,22 @@ class Bus : public MemObject
|
|||
|
||||
};
|
||||
|
||||
class BusFreeEvent : public Event
|
||||
{
|
||||
Bus * bus;
|
||||
|
||||
public:
|
||||
BusFreeEvent(Bus * _bus);
|
||||
void process();
|
||||
const char *description();
|
||||
};
|
||||
|
||||
BusFreeEvent busIdle;
|
||||
|
||||
void occupyBus(int numCycles);
|
||||
|
||||
Port * retryingPort;
|
||||
|
||||
/** An array of pointers to the peer port interfaces
|
||||
connected to this bus.*/
|
||||
std::vector<Port*> interfaces;
|
||||
|
@ -197,6 +221,23 @@ class Bus : public MemObject
|
|||
* original send failed for whatever reason.*/
|
||||
std::list<Port*> retryList;
|
||||
|
||||
void addToRetryList(Port * port)
|
||||
{
|
||||
if (!retryingPort) {
|
||||
// The device wasn't retrying a packet, or wasn't at an appropriate
|
||||
// time.
|
||||
retryList.push_back(port);
|
||||
} else {
|
||||
// The device was retrying a packet. It didn't work, so we'll leave
|
||||
// it at the head of the retry list.
|
||||
retryingPort = 0;
|
||||
|
||||
// We shouldn't be receiving a packet from one port when a different
|
||||
// one is retrying.
|
||||
assert(port == retryingPort);
|
||||
}
|
||||
}
|
||||
|
||||
/** Port that handles requests that don't match any of the interfaces.*/
|
||||
Port *defaultPort;
|
||||
|
||||
|
@ -209,9 +250,11 @@ class Bus : public MemObject
|
|||
|
||||
Bus(const std::string &n, int bus_id, int _clock, int _width)
|
||||
: MemObject(n), busId(bus_id), clock(_clock), width(_width),
|
||||
tickAddrLastUsed(0), tickDataLastUsed(0), defaultPort(NULL)
|
||||
tickNextIdle(0), busIdle(this), retryingPort(0), defaultPort(NULL)
|
||||
{
|
||||
//Both the width and clock period must be positive
|
||||
assert(width);
|
||||
assert(clock);
|
||||
}
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue