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:
@@ -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_ */
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user