First steps on inplementing full support to multiple memory channels/controllers based on config

Changes in the Arbiter block:
- now multiple initiator sockets are possible. These intiator sockets connect to multiple memory controllers;
- added lots of comments to the code in order to make it easier to understand.

Changes in Simulaton[cpp,h]:
- added TODO messages in some points that need to be changed to provide full support to multiple memory channles/controllers based on configuration.
This commit is contained in:
Éder Ferreira Zulian
2015-06-12 10:52:06 +02:00
parent a557e789ef
commit ae09bb105c
3 changed files with 101 additions and 70 deletions

View File

@@ -53,34 +53,42 @@
using namespace std;
using namespace tlm;
template<unsigned int NUMBER_OF_THREADS = 1, unsigned int BUSWIDTH = 128>
struct Arbiter: public sc_module
{
template<
unsigned int NUMBER_OF_THREADS = 1,
unsigned int BUSWIDTH = 128,
unsigned int NUMBER_OF_CHANNELS = 1
>
struct Arbiter: public sc_module {
public:
tlm_utils::simple_initiator_socket<Arbiter,BUSWIDTH, tlm::tlm_base_protocol_types> iSocket;
tlm_utils::simple_initiator_socket_tagged<Arbiter, BUSWIDTH, tlm::tlm_base_protocol_types> iSocket[NUMBER_OF_CHANNELS];
tlm_utils::simple_target_socket_tagged<Arbiter, BUSWIDTH, tlm::tlm_base_protocol_types> tSockets[NUMBER_OF_THREADS];
SC_CTOR(Arbiter) :
payloadEventQueue(this, &Arbiter::peqCallback), channelIsFree(true)
{
iSocket.register_nb_transport_bw(this, &Arbiter::nb_transport_bw);
for (unsigned int i = 0; i < NUMBER_OF_THREADS; ++i)
{
SC_CTOR(Arbiter) : payloadEventQueue(this, &Arbiter::peqCallback) {
for (unsigned int i = 0; i < NUMBER_OF_CHANNELS; ++i) {
// The arbiter communicates with one or more memory controller through one or more sockets (one or more memory channels).
// Each of the arbiter's iniitator sockets is binded to a memory controller's target socket.
// In this case the arbiter is the initiator.
iSocket[i].register_nb_transport_bw(this, &Arbiter::nb_transport_bw, i);
channelIsFree[i] = true;
}
for (unsigned int i = 0; i < NUMBER_OF_THREADS; ++i) {
// One or more devices can accesss the memory throught the arbiter. A device can be a processor's core, the execution of a previously recorded trace file, etc.
// As soon the arbiter receives a request in any of its target sockets it should treat and forward it to the proper memory channel.
// In this case the arbiter acts as a target.
tSockets[i].register_nb_transport_fw(this, &Arbiter::nb_transport_fw, i);
}
}
private:
tlm_utils::peq_with_cb_and_phase<Arbiter> payloadEventQueue;
bool channelIsFree;
bool channelIsFree[NUMBER_OF_CHANNELS];
//used to account for the request_accept_delay in the dram controllers
deque<tlm_generic_payload* > pendingRequests;
deque<tlm_generic_payload*> pendingRequests[NUMBER_OF_CHANNELS];
//used to account for the response_accept_delay in the initiators (traceplayer,core etc.)
deque<tlm_generic_payload* > receivedResponses[NUMBER_OF_THREADS];
deque<tlm_generic_payload*> receivedResponses[NUMBER_OF_THREADS];
// Initiated by dram side
tlm_sync_enum nb_transport_bw(tlm_generic_payload& payload, tlm_phase& phase, sc_time& bwDelay)
tlm_sync_enum nb_transport_bw(__attribute__((unused)) int socketId, tlm_generic_payload& payload, tlm_phase& phase, sc_time& bwDelay)
{
TlmRecorder::getInstance().recordPhase(payload, phase, bwDelay + sc_time_stamp());
payloadEventQueue.notify(payload, phase, bwDelay);
@@ -88,16 +96,15 @@ private:
}
// Initiated by initiator side
tlm_sync_enum nb_transport_fw(int socketId, tlm_generic_payload& payload, tlm_phase& phase,
sc_time& fwDelay)
// Anytime the arbiter receives a message from a device this callback function is called
tlm_sync_enum nb_transport_fw(int socketId, tlm_generic_payload& payload, tlm_phase& phase, sc_time& fwDelay)
{
if(phase == BEGIN_REQ)
{
if (phase == BEGIN_REQ) {
// In the begin request phase the socket ID is appended to the payload.
// It will extracted from the payload and used later.
appendDramExtension(socketId, payload);
payload.acquire();
}
else if(phase == END_RESP)
{
} else if (phase == END_RESP) {
payload.release();
}
@@ -108,65 +115,67 @@ private:
void peqCallback(tlm_generic_payload& payload, const tlm_phase& phase)
{
unsigned int initiatorSocket = DramExtension::getExtension(payload).getThread().ID()-1;
unsigned int channelId = DramExtension::getExtension(payload).getChannel().ID();
//Phases initiated by intiator side
if (phase == BEGIN_REQ)
{
if(channelIsFree)
{
channelIsFree = false;
sendToChannel(payload, phase, SC_ZERO_TIME );
// Phases initiated by the intiator side from arbiter's point of view (devices performing memory requests to the arbiter)
if (phase == BEGIN_REQ) {
if (channelIsFree[channelId]) {
// If the channel is available forward the request to the memory controller
channelIsFree[channelId] = false;
sendToChannel(channelId, payload, phase, SC_ZERO_TIME);
} else {
// If the channel is not available put the request in a queue.
pendingRequests[channelId].push_back(&payload);
}
else
{
pendingRequests.push_back(&payload);
}
}
else if (phase == END_RESP)
{
sendToChannel(payload, phase, SC_ZERO_TIME);
} else if (phase == END_RESP) {
// Send the END_RESP message to the memory
sendToChannel(channelId, payload, phase, SC_ZERO_TIME);
// Drop one element of the queue of BEGIN_RESP from memory to this device
receivedResponses[initiatorSocket].pop_front();
if(!receivedResponses[initiatorSocket].empty())
{
// Check if there are queued BEGIN_RESP messages from memory to this device
if (!receivedResponses[initiatorSocket].empty()) {
// The queue is not empty.
tlm_generic_payload* payloadToSend = receivedResponses[initiatorSocket].front();
sendToInitiator(initiatorSocket,*payloadToSend,BEGIN_RESP,SC_ZERO_TIME);
// Send ONE extra BEGIN_RESP to the device
sendToInitiator(initiatorSocket, *payloadToSend, BEGIN_RESP, SC_ZERO_TIME);
}
}
//Phases initiated by dram side
else if (phase == END_REQ)
{
channelIsFree = true;
// Phases initiated by the target side from arbiter's point of view (memory controllers)
else if (phase == END_REQ) {
channelIsFree[channelId] = true;
// The arbiter receives an END_REQ from memory controller and
// forward it to the requester device.
sendToInitiator(initiatorSocket, payload, phase, SC_ZERO_TIME);
if(!pendingRequests.empty())
{
tlm_generic_payload* payloadToSend = pendingRequests.front();
pendingRequests.pop_front();
sendToChannel(*payloadToSend, BEGIN_REQ, SC_ZERO_TIME );
channelIsFree = false;
// This channel is now free! Dispatch a queued BEGIN_REQ, if any. Send it to the memory controller.
if (!pendingRequests[channelId].empty()) {
tlm_generic_payload* payloadToSend = pendingRequests[channelId].front();
pendingRequests[channelId].pop_front();
// Send ONE enqueued BEGIN_REQ to throgh this channel.
sendToChannel(channelId, *payloadToSend, BEGIN_REQ, SC_ZERO_TIME);
// Mark the channel as busy again.
channelIsFree[channelId] = false;
}
}
else if (phase == BEGIN_RESP)
{
if(receivedResponses[initiatorSocket].empty())
} else if (phase == BEGIN_RESP) {
// The arbiter receives a BEGIN_RESP from memory controller and
// forwards it to the requester device
if (receivedResponses[initiatorSocket].empty())
sendToInitiator(initiatorSocket, payload, phase, SC_ZERO_TIME);
// Enqueue the BEGIN_RESP. It will be dequeued later on when the
// initiator device sends an END_RESP
receivedResponses[initiatorSocket].push_back(&payload);
}
else
{
} else {
SC_REPORT_FATAL(0, "Payload event queue in arbiter was triggered with unknown phase");
}
}
void sendToChannel(tlm_generic_payload& payload, const tlm_phase& phase, const sc_time& delay)
void sendToChannel(unsigned int channelId, tlm_generic_payload& payload, const tlm_phase& phase, const sc_time& delay)
{
tlm_phase TPhase = phase;
sc_time TDelay = delay;
iSocket->nb_transport_fw(payload, TPhase, TDelay);
iSocket[channelId]->nb_transport_fw(payload, TPhase, TDelay);
}
void sendToInitiator(unsigned int id, tlm_generic_payload& payload, const tlm_phase& phase, const sc_time& delay)
@@ -180,13 +189,10 @@ private:
{
unsigned int burstlength = payload.get_streaming_width();
DecodedAddress decodedAddress = xmlAddressDecoder::getInstance().decodeAddress(payload.get_address());
DramExtension* extension = new DramExtension(Thread(socketId+1), Channel(0), Bank(decodedAddress.bank),
BankGroup(decodedAddress.bankgroup), Row(decodedAddress.row), Column(decodedAddress.column),burstlength);
DramExtension* extension = new DramExtension(Thread(socketId+1), Channel(decodedAddress.channel), Bank(decodedAddress.bank), BankGroup(decodedAddress.bankgroup), Row(decodedAddress.row), Column(decodedAddress.column),burstlength);
payload.set_auto_extension(extension);
}
};
#endif /* ARBITER_H_ */

View File

@@ -99,10 +99,23 @@ void Simulation::setupTlmRecorder(const string &traceName, const string &pathToR
void Simulation::instantiateModules(const string &pathToResources, const std::vector<Device>& devices)
{
// TODO: the number of Drams objects or the number of target sockets in a Dram object should be proportional to the number of controllers/channles (info from config)
dram = new Dram<>("dram");
arbiter = new Arbiter<NumberOfTracePlayers, 128>("arbiter");
arbiter = new Arbiter<NumberOfTracePlayers, 128, NumberOfMemChannels>("arbiter");
// TODO: the number of controllers should be the number of channesl, right?!
// each of the arbiter's initiators sockets should bind to a controller...
// The number of channels is equal the number of controllers and comes from config.
controller = new Controller<>("controller");
//TODO: this depends (or should depend) on config... Should be dynamic not fixed to four!
#if 0
vector<StlPlayer> players;
for (int i = 0; i < NumberOfTracePlayers; i++) {
std::string playerStr = "player" + std::to_string(i);
players.push_back(new StlPlayer<>(playerStr, pathToResources + string("traces/") + devices[0].trace, devices[0].clkMhz, this));
}
#endif
player1 = new StlPlayer<>("player1", pathToResources + string("traces/") + devices[0].trace, devices[0].clkMhz, this);
player2 = new StlPlayer<>("player2", pathToResources + string("traces/") + devices[1].trace, devices[1].clkMhz, this);
player3 = new StlPlayer<>("player3", pathToResources + string("traces/") + devices[2].trace, devices[2].clkMhz, this);
@@ -115,7 +128,11 @@ void Simulation::bindSockets()
player2->iSocket.bind(arbiter->tSockets[1]);
player3->iSocket.bind(arbiter->tSockets[2]);
player4->iSocket.bind(arbiter->tSockets[3]);
arbiter->iSocket.bind(controller->tSocket);
// TODO: the number of controllers should be the number of channesl, right?!
// each of the arbiter's initiators sockets should bind to a controller...
arbiter->iSocket[0].bind(controller->tSocket);
controller->iSocket.bind(dram->tSocket);
}
@@ -123,6 +140,7 @@ Simulation::~Simulation()
{
delete dram;
delete arbiter;
// TODO: the number of components should come from config
delete controller;
delete player1;
delete player2;
@@ -138,6 +156,7 @@ void Simulation::start()
report(" -> memspec: \t\t" + Configuration::getInstance().memSpec.MemoryId);
cout << endl;
simulationStartTime = clock();
// TODO: the number of components should come from config
player1->nextPayload();
player2->nextPayload();
player3->nextPayload();

View File

@@ -84,7 +84,10 @@ public:
void stop();
virtual void tracePlayerTerminates() override;
// TODO: this information should be get from configuration
constexpr static unsigned int NumberOfTracePlayers = 4;
// TODO: this information should be get from configuration
constexpr static unsigned int NumberOfMemChannels = 1;
private:
std::string traceName;
@@ -92,11 +95,14 @@ private:
sc_event terminateSimulation;
// TODO: here should be a vector or some other abstract data type and elements should be dynamically allocated based on configuration
Dram<> *dram;
Arbiter<NumberOfTracePlayers, 128> *arbiter;
Arbiter<NumberOfTracePlayers, 128, NumberOfMemChannels> *arbiter;
// TODO: here should be a vector or some other abstract data type and elements should be dynamically allocated based on configuration
Controller<> *controller;
ReorderBuffer<> *reorder;
// TODO: here should be a vector or some other abstract data type and elements should be dynamically allocated based on configuration
TracePlayer<> *player1;
TracePlayer<> *player2;
TracePlayer<> *player3;