Get number of trace players and number of channes from config.

- Using multi_passthrough_sockets in the arbiter.
- Trace players, controllers and arbiter's queues are allocated dynamically.
This commit is contained in:
Éder Ferreira Zulian
2015-06-16 00:38:42 +02:00
parent fbde607efd
commit 9826354de1
7 changed files with 91 additions and 68 deletions

View File

@@ -3,6 +3,8 @@
<Debug value="1" />
<DatabaseRecording value="1" />
<PowerAnalysis value="1" />
<NumberOfTracePlayers value="4"/>
<NumberOfMemChannels value="1"/>
</simconfig>
<memspecs>
<memspec src="../resources/configs/memspecs/WideIO.xml"></memspec>
@@ -11,12 +13,12 @@
<addressmapping src="../resources/configs/amconfigs/am_wideio.xml"></addressmapping>
</addressmappings>
<memconfigs>
<memconfig src="../resources/configs/memconfigs/fr_fcfs.xml"/>
<memconfig src="../resources/configs/memconfigs/fifo.xml"/>
</memconfigs>
<tracesetups>
<tracesetup id="fifo">
<device clkMhz="200">chstone-adpcm_32.stl</device>
<device clkMhz="200">voco2.stl</device>
</tracesetup>
</tracesetups>
</simulation>

View File

@@ -32,6 +32,7 @@
* Authors:
* Janik Schlemminger
* Matthias Jung
* Eder F. Zulian
*/
#include "Configuration.h"
@@ -123,6 +124,10 @@ void Configuration::setParameter(std::string name, std::string value)
ErrorCSVFile = value;
else if(name == "ErrorStoreMode")
ErrorStoreMode = StringToEnum(value);
else if (name == "NumberOfTracePlayers")
NumberOfTracePlayers = string2int(value);
else if (name == "NumberOfMemChannels")
NumberOfMemChannels = string2int(value);
else
{
SC_REPORT_FATAL("Configuration", ("Parameter " + name + " not defined in Configuration").c_str());

View File

@@ -32,6 +32,7 @@
* Authors:
* Janik Schlemminger
* Matthias Jung
* Eder F. Zulian
*/
#ifndef CONFIGURATION_H_
@@ -72,6 +73,8 @@ struct Configuration
bool DatabaseRecording = true;
bool PowerAnalysis = false;
bool Debug = false;
unsigned int NumberOfTracePlayers = 4;
unsigned int NumberOfMemChannels = 1;
//MemSpec(from DRAM-Power XML)
MemSpec memSpec;

View File

@@ -38,70 +38,81 @@
#ifndef ARBITER_H_
#define ARBITER_H_
#include <deque>
#include <tlm.h>
#include <systemc.h>
#include <iostream>
#include <tlm_utils/simple_target_socket.h>
#include <tlm_utils/simple_initiator_socket.h>
#include <vector>
#include <queue>
#include <tlm_utils/multi_passthrough_target_socket.h>
#include <tlm_utils/multi_passthrough_initiator_socket.h>
#include <tlm_utils/peq_with_cb_and_phase.h>
#include "../common/xmlAddressdecoder.h"
#include "../common/dramExtension.h"
#include "../controller/core/TimingCalculation.h"
#include "../controller/core/configuration/ConfigurationLoader.h"
using namespace std;
using namespace tlm;
template<
unsigned int NUMBER_OF_THREADS = 1,
unsigned int BUSWIDTH = 128,
unsigned int NUMBER_OF_CHANNELS = 1
>
template<unsigned int BUSWIDTH = 128>
struct Arbiter: public sc_module {
public:
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];
tlm_utils::multi_passthrough_initiator_socket<Arbiter, BUSWIDTH> iSocket;
tlm_utils::multi_passthrough_target_socket<Arbiter, BUSWIDTH> tSocket;
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;
// The arbiter communicates with one or more memory unity through one or more sockets (one or more memory channels).
// Each of the arbiter's initiator sockets is bound to a memory controller's target socket.
// Anytime an transaction comes from a memory unity to the arbiter the "bw" callback is called.
iSocket.register_nb_transport_bw(this, &Arbiter::nb_transport_bw);
for (size_t i = 0; i < Configuration::getInstance().NumberOfMemChannels; ++i) {
channelIsFree.push_back(true);
pendingRequests.push_back(queue<tlm_generic_payload*>());
}
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);
// One or more devices can accesss all the memory units through the arbiter.
// Devices' initiator sockets are bound to arbiter's target sockets.
// As soon the arbiter receives a request in any of its target sockets it should treat and forward it to the proper memory channel.
tSocket.register_nb_transport_fw(this, &Arbiter::nb_transport_fw);
for (size_t i = 0; i < Configuration::getInstance().NumberOfTracePlayers; ++i) {
receivedResponses.push_back(queue<tlm_generic_payload*>());
}
}
private:
tlm_utils::peq_with_cb_and_phase<Arbiter> payloadEventQueue;
bool channelIsFree[NUMBER_OF_CHANNELS];
vector<bool> channelIsFree;
//used to account for the request_accept_delay in the dram controllers
deque<tlm_generic_payload*> pendingRequests[NUMBER_OF_CHANNELS];
// This is a queue of new transactions. The phase of a new request is BEGIN_REQ.
vector<queue<tlm_generic_payload*>> pendingRequests;
//used to account for the response_accept_delay in the initiators (traceplayer,core etc.)
deque<tlm_generic_payload*> receivedResponses[NUMBER_OF_THREADS];
// This is a queue of responses comming from the memory side. The phase of these transactions is BEGIN_RESP.
vector<queue<tlm_generic_payload*>> receivedResponses;
// Initiated by dram side
tlm_sync_enum nb_transport_bw(__attribute__((unused)) int socketId, tlm_generic_payload& payload, tlm_phase& phase, sc_time& bwDelay)
// This function is called when an arbiter's initiator socket receives a transaction from a memory controller
tlm_sync_enum nb_transport_bw(__attribute__((unused)) int id, tlm_generic_payload& payload, tlm_phase& phase, sc_time& bwDelay)
{
TlmRecorder::getInstance().recordPhase(payload, phase, bwDelay + sc_time_stamp());
payloadEventQueue.notify(payload, phase, bwDelay);
return TLM_ACCEPTED;
}
// TODO: check this id. How the payload extension propagates and is used...
// TODO: check the index mechanism for initiator and target sockets. Assert the index to be valid.
// Initiated by initiator side
// 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)
// This function is called when an arbiter's target socket receives a transaction from a device
tlm_sync_enum nb_transport_fw(int id, tlm_generic_payload& payload, tlm_phase& phase, sc_time& fwDelay)
{
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);
appendDramExtension(id, payload);
payload.acquire();
} else if (phase == END_RESP) {
payload.release();
@@ -116,55 +127,54 @@ private:
unsigned int initiatorSocket = DramExtension::getExtension(payload).getThread().ID()-1;
unsigned int channelId = DramExtension::getExtension(payload).getChannel().ID();
// TODO: here check if the channel and the initiatorSocket ID are valid. If not, the payload extension was corrupted.
// 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
// This channel was available. Forward the new transaction 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);
// This channel is busy. Enqueue the new transaction which phase is BEGIN_REQ.
pendingRequests[channelId].push(&payload);
}
} 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();
receivedResponses[initiatorSocket].pop();
// Check if there are queued BEGIN_RESP messages from memory to this device
// Check if there are queued transactoins with phase BEGIN_RESP from memory to this device
if (!receivedResponses[initiatorSocket].empty()) {
// The queue is not empty.
tlm_generic_payload* payloadToSend = receivedResponses[initiatorSocket].front();
tlm_generic_payload *payloadToSend = receivedResponses[initiatorSocket].front();
// Send ONE extra BEGIN_RESP to the device
sendToInitiator(initiatorSocket, *payloadToSend, BEGIN_RESP, SC_ZERO_TIME);
}
}
// Phases initiated by the target side from arbiter's point of view (memory controllers)
// Phases initiated by the target side from arbiter's point of view (memory side)
else if (phase == END_REQ) {
channelIsFree[channelId] = true;
// The arbiter receives an END_REQ from memory controller and
// forward it to the requester device.
// The arbiter receives a transaction which phase is END_REQ from memory controller and forwards it to the requester device.
sendToInitiator(initiatorSocket, payload, phase, SC_ZERO_TIME);
// This channel is now free! Dispatch a queued BEGIN_REQ, if any. Send it to the memory controller.
// This channel is now free! Dispatch a new transaction (phase is BEGIN_REQ) from the queue, 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.
tlm_generic_payload *payloadToSend = pendingRequests[channelId].front();
pendingRequests[channelId].pop();
// Send ONE of the enqueued new transactions (phase is BEGIN_REQ) through this channel.
sendToChannel(channelId, *payloadToSend, BEGIN_REQ, SC_ZERO_TIME);
// Mark the channel as busy again.
channelIsFree[channelId] = false;
}
} else if (phase == BEGIN_RESP) {
// The arbiter receives a BEGIN_RESP from memory controller and
// forwards it to the requester device
// The arbiter receives a transaction in BEGIN_RESP phase (that came from the memory side) 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);
// Enqueue the transaction in BEGIN_RESP phase until the initiator device acknowledge it (phase changes to END_RESP).
receivedResponses[initiatorSocket].push(&payload);
} else {
SC_REPORT_FATAL(0, "Payload event queue in arbiter was triggered with unknown phase");
}
@@ -181,11 +191,13 @@ private:
{
tlm_phase TPhase = phase;
sc_time TDelay = delay;
tSockets[id]->nb_transport_bw(payload, TPhase, TDelay);
tSocket[id]->nb_transport_bw(payload, TPhase, TDelay);
}
void appendDramExtension(int socketId, tlm_generic_payload& payload)
{
// TODO: check if channel valid before appending.
// TODO: check if all parts of the decodedAddress are inside the valid range (devices should not perform invalid requests to the arbiter, right?).
unsigned int burstlength = payload.get_streaming_width();
DecodedAddress decodedAddress = xmlAddressDecoder::getInstance().decodeAddress(payload.get_address());
DramExtension* extension = new DramExtension(Thread(socketId+1), Channel(decodedAddress.channel), Bank(decodedAddress.bank), BankGroup(decodedAddress.bankgroup), Row(decodedAddress.row), Column(decodedAddress.column),burstlength);

View File

@@ -90,7 +90,14 @@ void Simulation::setupTlmRecorder(const string &traceName, const string &pathToR
TlmRecorder::sqlScriptURI = pathToResources + string("scripts/createTraceDB.sql");
TlmRecorder::getInstance().recordMemconfig(Configuration::getInstance().memconfigUri);
TlmRecorder::getInstance().recordMemspec(Configuration::getInstance().memspecUri);
TlmRecorder::getInstance().recordTracenames(devices[0].trace + "," + devices[1].trace + "," + devices[2].trace + "," + devices[3].trace);
std::string traceNames;
for (size_t i = 0; i < devices.size(); i++) {
traceNames.append(devices[i].trace);
if (i == devices.size() - 1)
continue;
traceNames.append(",");
}
TlmRecorder::getInstance().recordTracenames(traceNames);
}
else
{
@@ -100,15 +107,15 @@ void Simulation::setupTlmRecorder(const string &traceName, const string &pathToR
void Simulation::instantiateModules(const string &pathToResources, const std::vector<Device>& devices)
{
for (size_t i = 0; i < NumberOfTracePlayers; i++) {
for (size_t i = 0; i < Configuration::getInstance().NumberOfTracePlayers; i++) {
std::string playerStr = "player" + std::to_string(i);
TracePlayer<> *player = new StlPlayer<>(playerStr.c_str(), pathToResources + string("traces/") + devices[i].trace, devices[i].clkMhz, this);
players.push_back(player);
}
arbiter = new Arbiter<NumberOfTracePlayers, 128, NumberOfMemChannels>("arbiter");
arbiter = new Arbiter<128>("arbiter");
for (size_t i = 0; i < NumberOfMemChannels; i++) {
for (size_t i = 0; i < Configuration::getInstance().NumberOfMemChannels; i++) {
std::string str = "controller" + std::to_string(i);
Controller<> *controller = new Controller<>(str.c_str());
controllers.push_back(controller);
@@ -123,11 +130,11 @@ void Simulation::bindSockets()
{
size_t i = 0;
for (auto player : players) {
player->iSocket.bind(arbiter->tSockets[i++]);
player->iSocket.bind(arbiter->tSocket);
}
for (i = 0; i < NumberOfMemChannels; i++) {
arbiter->iSocket[i].bind(controllers[i]->tSocket);
for (i = 0; i < Configuration::getInstance().NumberOfMemChannels; i++) {
arbiter->iSocket.bind(controllers[i]->tSocket);
controllers[i]->iSocket.bind(drams[i]->tSocket);
}
}
@@ -171,11 +178,10 @@ void inline Simulation::tracePlayerTerminates()
static unsigned int finishedTracePlayers = 0;
finishedTracePlayers++;
if (finishedTracePlayers == NumberOfTracePlayers)
{
if (finishedTracePlayers == Configuration::getInstance().NumberOfTracePlayers)
terminateSimulation.notify();
}
}
void Simulation::stop()
{
wait(terminateSimulation);
@@ -196,3 +202,4 @@ void Simulation::report(string message)
DebugManager::getInstance().printDebugMessage(this->name(), message);
cout << message << endl;
}

View File

@@ -86,10 +86,6 @@ 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;
@@ -101,7 +97,7 @@ private:
// and initiate transactions targeting the memory)
std::vector<TracePlayer<>*> players;
// All transactions pass through the same arbiter
Arbiter<NumberOfTracePlayers, 128, NumberOfMemChannels> *arbiter;
Arbiter<> *arbiter;
// Each DRAM unit has a controller
std::vector<Controller<>*> controllers;
// TODO: Each DRAM has a reorder buffer (check this!)

View File

@@ -162,12 +162,10 @@ void SimulationManager::startTraceAnalyzer()
void SimulationManager::addTraceSetup(SimulationBatch& batch, tinyxml2::XMLElement* element)
{
vector<Device> devices;
for (XMLElement* device = element->FirstChildElement("device"); device != NULL; device = device->NextSiblingElement("device"))
{
for (XMLElement* device = element->FirstChildElement("device"); device != NULL; device = device->NextSiblingElement("device")) {
devices.push_back(Device(device->GetText(), device->IntAttribute("clkMhz"), device->IntAttribute("bl")));
}
while (devices.size() < Simulation::NumberOfTracePlayers)
{
while (devices.size() < Configuration::getInstance().NumberOfTracePlayers) {
devices.push_back(Device());
}