Decouple initiator clock from memory responses

Previously, the initiators were implicitly coupled to the responses of
the memory, calculating each new initiator clock relatively based on the
time of the BEGIN_RESP phase. This lead to an implicit coupling as
same rounding error of the initiator clock to the memory clock was
applied each time again.

Now, initiators are in itself self-clocked and only send requests based
on backpressure.
This commit is contained in:
2025-04-29 10:46:33 +02:00
parent 9165a80ced
commit 8c861d81c9
24 changed files with 389 additions and 493 deletions

View File

@@ -598,8 +598,8 @@ void Controller::manageRequests(const sc_time& delay)
transToAcquire.payload->acquire();
// The following logic assumes that transactions are naturally aligned
const uint64_t address = transToAcquire.payload->get_address();
const uint64_t dataLength = transToAcquire.payload->get_data_length();
uint64_t address = transToAcquire.payload->get_address();
[[maybe_unused]] uint64_t dataLength = transToAcquire.payload->get_data_length();
assert((dataLength & (dataLength - 1)) == 0); // Data length must be a power of 2
assert(address % dataLength == 0); // Check if naturally aligned

View File

@@ -28,7 +28,7 @@
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Authors:
# Authors:
# Matthias Jung
# Lukas Steiner
# Derek Christ
@@ -45,8 +45,8 @@ add_library(simulator
simulator/EccModule.cpp
simulator/MemoryManager.cpp
simulator/Simulator.cpp
simulator/generator/RandomProducer.cpp
simulator/generator/SequentialProducer.cpp
simulator/generator/RandomState.cpp
simulator/generator/SequentialState.cpp
simulator/generator/TrafficGenerator.cpp
simulator/hammer/RowHammer.cpp
simulator/player/StlPlayer.cpp

View File

@@ -1,71 +0,0 @@
/*
* Copyright (c) 2023, RPTU Kaiserslautern-Landau
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of the copyright holder 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 HOLDER
* 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:
* Derek Christ
*/
#pragma once
#include "Initiator.h"
#include "request/RequestIssuer.h"
template <typename Producer> class SimpleInitiator : public Initiator
{
public:
SimpleInitiator(sc_core::sc_module_name const& name,
MemoryManager& memoryManager,
sc_core::sc_time interfaceClk,
std::optional<unsigned int> maxPendingReadRequests,
std::optional<unsigned int> maxPendingWriteRequests,
std::function<void()> transactionFinished,
std::function<void()> terminate,
Producer&& producer) :
producer(std::forward<Producer>(producer)),
issuer(
name,
memoryManager,
interfaceClk,
maxPendingReadRequests,
maxPendingWriteRequests,
[this] { return this->producer.nextRequest(); },
std::move(transactionFinished),
std::move(terminate))
{
}
void bind(tlm_utils::multi_target_base<>& target) override { issuer.iSocket.bind(target); }
uint64_t totalRequests() override { return producer.totalRequests(); };
private:
Producer producer;
RequestIssuer issuer;
};

View File

@@ -35,18 +35,19 @@
#include "Simulator.h"
#include "SimpleInitiator.h"
#include "generator/TrafficGenerator.h"
#include "hammer/RowHammer.h"
#include "player/StlPlayer.h"
#include "simulator/request/RequestIssuer.h"
#include "util.h"
Simulator::Simulator(DRAMSys::Config::Configuration configuration, std::filesystem::path baseConfig) :
Simulator::Simulator(DRAMSys::Config::Configuration configuration,
std::filesystem::path baseConfig) :
storageEnabled(configuration.simconfig.StoreMode == DRAMSys::Config::StoreModeType::Store),
memoryManager(storageEnabled),
configuration(std::move(configuration)),
dramSys(std::make_unique<DRAMSys::DRAMSys>("DRAMSys", this->configuration)),
baseConfig(baseConfig)
baseConfig(std::move(baseConfig))
{
terminateInitiator = [this]()
{
@@ -74,12 +75,12 @@ Simulator::Simulator(DRAMSys::Config::Configuration configuration, std::filesyst
{
auto initiator = instantiateInitiator(initiatorConfig);
totalTransactions += initiator->totalRequests();
initiator->bind(dramSys->tSocket);
initiator->iSocket.bind(dramSys->tSocket);
initiators.push_back(std::move(initiator));
}
}
std::unique_ptr<Initiator>
std::unique_ptr<RequestIssuer>
Simulator::instantiateInitiator(const DRAMSys::Config::Initiator& initiator)
{
uint64_t memorySize = dramSys->getMemSpec().getSimMemSizeInBytes();
@@ -87,19 +88,23 @@ Simulator::instantiateInitiator(const DRAMSys::Config::Initiator& initiator)
unsigned int defaultDataLength = dramSys->getMemSpec().defaultBytesPerBurst;
return std::visit(
[=](auto&& config) -> std::unique_ptr<Initiator>
[=](auto&& config) -> std::unique_ptr<RequestIssuer>
{
using T = std::decay_t<decltype(config)>;
if constexpr (std::is_same_v<T, DRAMSys::Config::TrafficGenerator> ||
std::is_same_v<T, DRAMSys::Config::TrafficGeneratorStateMachine>)
{
return std::make_unique<TrafficGenerator>(config,
interfaceClk,
memorySize,
defaultDataLength,
memoryManager,
finishTransaction,
terminateInitiator);
auto generator =
std::make_unique<TrafficGenerator>(config, memorySize, defaultDataLength);
return std::make_unique<RequestIssuer>(config.name.c_str(),
std::move(generator),
memoryManager,
interfaceClk,
config.maxPendingReadRequests,
config.maxPendingWriteRequests,
finishTransaction,
terminateInitiator);
}
else if constexpr (std::is_same_v<T, DRAMSys::Config::TracePlayer>)
{
@@ -119,33 +124,33 @@ Simulator::instantiateInitiator(const DRAMSys::Config::Initiator& initiator)
SC_REPORT_FATAL("Simulator", report.c_str());
}
StlPlayer player(tracePath.c_str(),
config.clkMhz,
defaultDataLength,
*traceType,
storageEnabled);
auto player = std::make_unique<StlPlayer>(tracePath.c_str(),
config.clkMhz,
defaultDataLength,
*traceType,
storageEnabled);
return std::make_unique<SimpleInitiator<StlPlayer>>(config.name.c_str(),
memoryManager,
interfaceClk,
std::nullopt,
std::nullopt,
finishTransaction,
terminateInitiator,
std::move(player));
return std::make_unique<RequestIssuer>(config.name.c_str(),
std::move(player),
memoryManager,
interfaceClk,
std::nullopt,
std::nullopt,
finishTransaction,
terminateInitiator);
}
else if constexpr (std::is_same_v<T, DRAMSys::Config::RowHammer>)
{
RowHammer hammer(config.numRequests, config.rowIncrement, defaultDataLength);
auto hammer = std::make_unique<RowHammer>(config, defaultDataLength);
return std::make_unique<SimpleInitiator<RowHammer>>(config.name.c_str(),
memoryManager,
interfaceClk,
1,
1,
finishTransaction,
terminateInitiator,
std::move(hammer));
return std::make_unique<RequestIssuer>(config.name.c_str(),
std::move(hammer),
memoryManager,
interfaceClk,
1,
1,
finishTransaction,
terminateInitiator);
}
},
initiator.getVariant());
@@ -175,5 +180,5 @@ void Simulator::run()
auto finish = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed = finish - start;
std::cout << "Simulation took " + std::to_string(elapsed.count()) + " seconds." << std::endl;
std::cout << "Simulation took " + std::to_string(elapsed.count()) + " seconds.\n";
}

View File

@@ -35,8 +35,8 @@
#pragma once
#include "Initiator.h"
#include "MemoryManager.h"
#include "simulator/request/RequestIssuer.h"
#include <DRAMSys/config/DRAMSysConfiguration.h>
#include <DRAMSys/simulation/DRAMSys.h>
@@ -49,15 +49,15 @@ public:
void run();
private:
std::unique_ptr<Initiator> instantiateInitiator(const DRAMSys::Config::Initiator& initiator);
std::unique_ptr<RequestIssuer> instantiateInitiator(const DRAMSys::Config::Initiator& initiator);
const bool storageEnabled;
bool storageEnabled;
MemoryManager memoryManager;
DRAMSys::Config::Configuration configuration;
std::unique_ptr<DRAMSys::DRAMSys> dramSys;
std::vector<std::unique_ptr<Initiator>> initiators;
std::vector<std::unique_ptr<RequestIssuer>> initiators;
std::function<void()> terminateInitiator;
std::function<void()> finishTransaction;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, RPTU Kaiserslautern-Landau
* Copyright (c) 2025, RPTU Kaiserslautern-Landau
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -35,20 +35,21 @@
#pragma once
#include <tlm_utils/multi_socket_bases.h>
#include "simulator/request/Request.h"
class Initiator
class GeneratorState
{
protected:
Initiator(const Initiator&) = default;
Initiator(Initiator&&) = default;
Initiator& operator=(const Initiator&) = default;
Initiator& operator=(Initiator&&) = default;
GeneratorState(const GeneratorState&) = default;
GeneratorState(GeneratorState&&) = default;
GeneratorState& operator=(const GeneratorState&) = default;
GeneratorState& operator=(GeneratorState&&) = default;
public:
Initiator() = default;
virtual ~Initiator() = default;
GeneratorState() = default;
virtual ~GeneratorState() = default;
virtual void bind(tlm_utils::multi_target_base<>& target) = 0;
virtual Request nextRequest() = 0;
virtual uint64_t totalRequests() = 0;
};
virtual void reset() {}
};

View File

@@ -33,11 +33,12 @@
* Derek Christ
*/
#include "RandomProducer.h"
#include "definitions.h"
#include "RandomState.h"
RandomProducer::RandomProducer(uint64_t numRequests,
std::optional<uint64_t> seed,
#include <systemc>
RandomState::RandomState(uint64_t numRequests,
uint64_t seed,
double rwRatio,
std::optional<uint64_t> minAddress,
std::optional<uint64_t> maxAddress,
@@ -45,13 +46,12 @@ RandomProducer::RandomProducer(uint64_t numRequests,
unsigned int dataLength,
unsigned int dataAlignment) :
numberOfRequests(numRequests),
seed(seed.value_or(DEFAULT_SEED)),
seed(seed),
rwRatio(rwRatio),
dataLength(dataLength),
dataAlignment(dataAlignment),
randomGenerator(this->seed),
randomAddressDistribution(minAddress.value_or(DEFAULT_MIN_ADDRESS),
maxAddress.value_or((memorySize)-dataLength))
randomAddressDistribution(minAddress.value_or(0), maxAddress.value_or((memorySize)-dataLength))
{
if (minAddress > memorySize - 1)
SC_REPORT_FATAL("TrafficGenerator", "minAddress is out of range.");
@@ -65,7 +65,7 @@ RandomProducer::RandomProducer(uint64_t numRequests,
rwRatio = std::clamp(rwRatio, 0.0, 1.0);
}
Request RandomProducer::nextRequest()
Request RandomState::nextRequest()
{
Request request;
request.address = randomAddressDistribution(randomGenerator);
@@ -76,7 +76,6 @@ Request RandomProducer::nextRequest()
request.command = readWriteDistribution(randomGenerator) < rwRatio ? Request::Command::Read
: Request::Command::Write;
request.length = dataLength;
request.delay = sc_core::SC_ZERO_TIME;
return request;
}

View File

@@ -35,16 +35,16 @@
#pragma once
#include "simulator/request/RequestProducer.h"
#include "simulator/generator/GeneratorState.h"
#include <optional>
#include <random>
class RandomProducer : public RequestProducer
class RandomState : public GeneratorState
{
public:
RandomProducer(uint64_t numRequests,
std::optional<uint64_t> seed,
RandomState(uint64_t numRequests,
uint64_t seed,
double rwRatio,
std::optional<uint64_t> minAddress,
std::optional<uint64_t> maxAddress,
@@ -53,14 +53,13 @@ public:
unsigned int dataAlignment);
Request nextRequest() override;
uint64_t totalRequests() override { return numberOfRequests; }
const uint64_t numberOfRequests;
const uint64_t seed;
const double rwRatio;
const unsigned int dataLength;
const unsigned int dataAlignment;
uint64_t numberOfRequests;
uint64_t seed;
double rwRatio;
unsigned int dataLength;
unsigned int dataAlignment;
std::default_random_engine randomGenerator;
std::uniform_real_distribution<double> readWriteDistribution{0.0, 1.0};

View File

@@ -33,11 +33,12 @@
* Derek Christ
*/
#include "SequentialProducer.h"
#include "definitions.h"
#include "SequentialState.h"
SequentialProducer::SequentialProducer(uint64_t numRequests,
std::optional<uint64_t> seed,
#include <systemc>
SequentialState::SequentialState(uint64_t numRequests,
uint64_t seed,
double rwRatio,
std::optional<uint64_t> addressIncrement,
std::optional<uint64_t> minAddress,
@@ -46,9 +47,9 @@ SequentialProducer::SequentialProducer(uint64_t numRequests,
unsigned int dataLength) :
numberOfRequests(numRequests),
addressIncrement(addressIncrement.value_or(dataLength)),
minAddress(minAddress.value_or(DEFAULT_MIN_ADDRESS)),
minAddress(minAddress.value_or(0)),
maxAddress(maxAddress.value_or(memorySize - 1)),
seed(seed.value_or(DEFAULT_SEED)),
seed(seed),
rwRatio(rwRatio),
dataLength(dataLength),
randomGenerator(this->seed)
@@ -65,14 +66,13 @@ SequentialProducer::SequentialProducer(uint64_t numRequests,
rwRatio = std::clamp(rwRatio, 0.0, 1.0);
}
Request SequentialProducer::nextRequest()
Request SequentialState::nextRequest()
{
Request request;
request.address = generatedRequests * addressIncrement % (maxAddress - minAddress) + minAddress;
request.command = readWriteDistribution(randomGenerator) < rwRatio ? Request::Command::Read
: Request::Command::Write;
request.length = dataLength;
request.delay = sc_core::SC_ZERO_TIME;
generatedRequests++;
return request;

View File

@@ -35,16 +35,16 @@
#pragma once
#include "simulator/request/RequestProducer.h"
#include "simulator/generator/GeneratorState.h"
#include <optional>
#include <random>
class SequentialProducer : public RequestProducer
class SequentialState : public GeneratorState
{
public:
SequentialProducer(uint64_t numRequests,
std::optional<uint64_t> seed,
SequentialState(uint64_t numRequests,
uint64_t seed,
double rwRatio,
std::optional<uint64_t> addressIncrement,
std::optional<uint64_t> minAddress,
@@ -53,17 +53,16 @@ public:
unsigned int dataLength);
Request nextRequest() override;
uint64_t totalRequests() override { return numberOfRequests; }
void reset() override { generatedRequests = 0; }
const uint64_t numberOfRequests;
const uint64_t addressIncrement;
const uint64_t minAddress;
const uint64_t maxAddress;
const uint64_t seed;
const double rwRatio;
const unsigned int dataLength;
uint64_t numberOfRequests;
uint64_t addressIncrement;
uint64_t minAddress;
uint64_t maxAddress;
uint64_t seed;
double rwRatio;
unsigned int dataLength;
std::default_random_engine randomGenerator;
std::uniform_real_distribution<double> readWriteDistribution{0.0, 1.0};

View File

@@ -35,27 +35,14 @@
#include "TrafficGenerator.h"
#include "RandomProducer.h"
#include "SequentialProducer.h"
#include "RandomState.h"
#include "SequentialState.h"
TrafficGenerator::TrafficGenerator(DRAMSys::Config::TrafficGeneratorStateMachine const& config,
sc_core::sc_time interfaceClk,
uint64_t memorySize,
unsigned int defaultDataLength,
MemoryManager& memoryManager,
std::function<void()> transactionFinished,
std::function<void()> terminateInitiator) :
unsigned int defaultDataLength) :
stateTransistions(config.transitions),
generatorPeriod(sc_core::sc_time(1.0 / static_cast<double>(config.clkMhz), sc_core::SC_US)),
issuer(
config.name.c_str(),
memoryManager,
interfaceClk,
config.maxPendingReadRequests,
config.maxPendingWriteRequests,
[this] { return nextRequest(); },
std::move(transactionFinished),
std::move(terminateInitiator))
generatorPeriod(sc_core::sc_time(1.0 / static_cast<double>(config.clkMhz), sc_core::SC_US))
{
unsigned int dataLength = config.dataLength.value_or(defaultDataLength);
unsigned int dataAlignment = config.dataAlignment.value_or(dataLength);
@@ -74,8 +61,8 @@ TrafficGenerator::TrafficGenerator(DRAMSys::Config::TrafficGeneratorStateMachine
if (activeState.addressDistribution ==
DRAMSys::Config::AddressDistribution::Random)
{
auto producer = std::make_unique<RandomProducer>(activeState.numRequests,
config.seed,
auto producer = std::make_unique<RandomState>(activeState.numRequests,
config.seed.value_or(0),
activeState.rwRatio,
activeState.minAddress,
activeState.maxAddress,
@@ -88,8 +75,8 @@ TrafficGenerator::TrafficGenerator(DRAMSys::Config::TrafficGeneratorStateMachine
else
{
auto producer =
std::make_unique<SequentialProducer>(activeState.numRequests,
config.seed,
std::make_unique<SequentialState>(activeState.numRequests,
config.seed.value_or(0),
activeState.rwRatio,
activeState.addressIncrement,
activeState.minAddress,
@@ -111,30 +98,17 @@ TrafficGenerator::TrafficGenerator(DRAMSys::Config::TrafficGeneratorStateMachine
}
TrafficGenerator::TrafficGenerator(DRAMSys::Config::TrafficGenerator const& config,
sc_core::sc_time interfaceClk,
uint64_t memorySize,
unsigned int defaultDataLength,
MemoryManager& memoryManager,
std::function<void()> transactionFinished,
std::function<void()> terminateInitiator) :
generatorPeriod(sc_core::sc_time(1.0 / static_cast<double>(config.clkMhz), sc_core::SC_US)),
issuer(
config.name.c_str(),
memoryManager,
interfaceClk,
config.maxPendingReadRequests,
config.maxPendingWriteRequests,
[this] { return nextRequest(); },
std::move(transactionFinished),
std::move(terminateInitiator))
unsigned int defaultDataLength) :
generatorPeriod(sc_core::sc_time(1.0 / static_cast<double>(config.clkMhz), sc_core::SC_US))
{
unsigned int dataLength = config.dataLength.value_or(defaultDataLength);
unsigned int dataAlignment = config.dataAlignment.value_or(dataLength);
if (config.addressDistribution == DRAMSys::Config::AddressDistribution::Random)
{
auto producer = std::make_unique<RandomProducer>(config.numRequests,
config.seed,
auto producer = std::make_unique<RandomState>(config.numRequests,
config.seed.value_or(0),
config.rwRatio,
config.minAddress,
config.maxAddress,
@@ -145,8 +119,8 @@ TrafficGenerator::TrafficGenerator(DRAMSys::Config::TrafficGenerator const& conf
}
else
{
auto producer = std::make_unique<SequentialProducer>(config.numRequests,
config.seed,
auto producer = std::make_unique<SequentialState>(config.numRequests,
config.seed.value_or(0),
config.rwRatio,
config.addressIncrement,
config.minAddress,
@@ -159,7 +133,14 @@ TrafficGenerator::TrafficGenerator(DRAMSys::Config::TrafficGenerator const& conf
Request TrafficGenerator::nextRequest()
{
uint64_t clksToIdle = 0;
if (currentState == STOP_STATE)
return Request{Request::Command::Stop};
Request request = producers[currentState]->nextRequest();
requestsInState++;
// Switch state if necessary
unsigned ticksToIdle = 0;
if (requestsInState >= producers[currentState]->totalRequests())
{
// Reset current producer to its initial state
@@ -167,30 +148,24 @@ Request TrafficGenerator::nextRequest()
auto newState = stateTransition(currentState);
if (!newState.has_value())
return Request{Request::Command::Stop};
auto idleStateIt = idleStateClks.find(newState.value());
auto idleStateIt = idleStateClks.find(newState);
while (idleStateIt != idleStateClks.cend())
{
clksToIdle += idleStateIt->second;
newState = stateTransition(currentState);
if (!newState.has_value())
return Request{Request::Command::Stop};
currentState = newState.value();
idleStateIt = idleStateClks.find(newState.value());
ticksToIdle += idleStateIt->second;
newState = stateTransition(newState);
idleStateIt = idleStateClks.find(newState);
}
currentState = newState.value();
currentState = newState;
requestsInState = 0;
}
requestsInState++;
if (currentState == STOP_STATE)
// Allow the issuer to finish before the response comes back
nextTriggerTime = sc_core::SC_ZERO_TIME;
else
nextTriggerTime = generatorPeriod * (1 + ticksToIdle);
Request request = producers[currentState]->nextRequest();
request.delay += generatorPeriod + generatorPeriod * static_cast<double>(clksToIdle);
return request;
}
@@ -208,9 +183,9 @@ uint64_t TrafficGenerator::totalRequests()
if (producers.find(currentState) != producers.cend())
totalRequests += producers.at(currentState)->totalRequests();
while (auto nextState = stateTransition(currentState))
while (currentState != STOP_STATE)
{
currentState = nextState.value();
currentState = stateTransition(currentState);
if (producers.find(currentState) != producers.cend())
totalRequests += producers.at(currentState)->totalRequests();
@@ -222,7 +197,7 @@ uint64_t TrafficGenerator::totalRequests()
return totalRequests;
}
std::optional<unsigned int> TrafficGenerator::stateTransition(unsigned int from)
unsigned int TrafficGenerator::stateTransition(unsigned int from)
{
using Transition = DRAMSys::Config::TrafficGeneratorStateTransition;
@@ -233,7 +208,7 @@ std::optional<unsigned int> TrafficGenerator::stateTransition(unsigned int from)
[from](Transition transition) { return transition.from == from; });
if (relevantTransitions.empty())
return std::nullopt;
return STOP_STATE;
std::vector<double> propabilities;
std::for_each(relevantTransitions.cbegin(),

View File

@@ -35,52 +35,44 @@
#pragma once
#include "simulator/Initiator.h"
#include "simulator/MemoryManager.h"
#include "simulator/request/RequestIssuer.h"
#include "GeneratorState.h"
#include "simulator/request/RequestProducer.h"
#include <DRAMSys/config/DRAMSysConfiguration.h>
#include <random>
class RequestProducer;
class TrafficGenerator : public Initiator
class TrafficGenerator : public RequestProducer
{
public:
TrafficGenerator(DRAMSys::Config::TrafficGenerator const& config,
sc_core::sc_time interfaceClk,
uint64_t memorySize,
unsigned int defaultDataLength,
MemoryManager& memoryManager,
std::function<void()> transactionFinished,
std::function<void()> terminateInitiator);
unsigned int defaultDataLength);
TrafficGenerator(DRAMSys::Config::TrafficGeneratorStateMachine const& config,
sc_core::sc_time interfaceClk,
uint64_t memorySize,
unsigned int defaultDataLength,
MemoryManager& memoryManager,
std::function<void()> transactionFinished,
std::function<void()> terminateInitiator);
void bind(tlm_utils::multi_target_base<>& target) override { issuer.iSocket.bind(target); }
unsigned int defaultDataLength);
uint64_t totalRequests() override;
Request nextRequest();
Request nextRequest() override;
sc_core::sc_time nextTrigger() override { return nextTriggerTime; }
std::optional<unsigned int> stateTransition(unsigned int from);
unsigned int stateTransition(unsigned int from);
private:
static constexpr unsigned int STOP_STATE = UINT_MAX;
uint64_t requestsInState = 0;
unsigned int currentState = 0;
const std::vector<DRAMSys::Config::TrafficGeneratorStateTransition> stateTransistions;
sc_core::sc_time nextTriggerTime = sc_core::SC_ZERO_TIME;
std::vector<DRAMSys::Config::TrafficGeneratorStateTransition> stateTransistions;
using IdleClks = uint64_t;
std::unordered_map<unsigned int, IdleClks> idleStateClks;
const sc_core::sc_time generatorPeriod;
std::unordered_map<unsigned int, unsigned int> idleStateClks;
sc_core::sc_time generatorPeriod;
std::default_random_engine randomGenerator;
std::unordered_map<unsigned int, std::unique_ptr<RequestProducer>> producers;
RequestIssuer issuer;
std::unordered_map<unsigned int, std::unique_ptr<GeneratorState>> producers;
};

View File

@@ -1,41 +0,0 @@
/*
* Copyright (c) 2023, RPTU Kaiserslautern-Landau
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. 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.
*
* 3. Neither the name of the copyright holder 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 HOLDER
* 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:
* Derek Christ
*/
#pragma once
#include <cstdint>
inline constexpr uint64_t DEFAULT_SEED = 0;
inline constexpr uint64_t DEFAULT_MIN_ADDRESS = 0;

View File

@@ -35,10 +35,11 @@
#include "RowHammer.h"
RowHammer::RowHammer(uint64_t numRequests, uint64_t rowIncrement, unsigned int dataLength) :
numberOfRequests(numRequests),
dataLength(dataLength),
rowIncrement(rowIncrement)
RowHammer::RowHammer(DRAMSys::Config::RowHammer const& config, unsigned int dataLength) :
generatorPeriod(sc_core::sc_time(1.0 / static_cast<double>(config.clkMhz), sc_core::SC_US)),
numberOfRequests(config.numRequests),
rowIncrement(config.rowIncrement),
dataLength(dataLength)
{
}
@@ -58,6 +59,5 @@ Request RowHammer::nextRequest()
request.address = currentAddress;
request.command = Request::Command::Read;
request.length = dataLength;
request.delay = sc_core::SC_ZERO_TIME;
return request;
}

View File

@@ -37,19 +37,23 @@
#include "simulator/request/RequestProducer.h"
#include <DRAMSys/config/TraceSetup.h>
#include <systemc>
class RowHammer : public RequestProducer
{
public:
RowHammer(uint64_t numRequests, uint64_t rowIncrement, unsigned int dataLength);
RowHammer(DRAMSys::Config::RowHammer const& config, unsigned int dataLength);
Request nextRequest() override;
sc_core::sc_time nextTrigger() override { return generatorPeriod; }
uint64_t totalRequests() override { return numberOfRequests; }
const uint64_t numberOfRequests;
const unsigned int dataLength;
const uint64_t rowIncrement;
sc_core::sc_time generatorPeriod;
uint64_t numberOfRequests;
uint64_t rowIncrement;
unsigned int dataLength;
uint64_t generatedRequests = 0;
uint64_t currentAddress = 0x00;

View File

@@ -42,7 +42,9 @@
#include <sstream>
StlPlayer::StlPlayer(std::string_view tracePath,
static constexpr std::size_t LINE_BUFFER_SIZE = 10000;
StlPlayer::StlPlayer(std::filesystem::path const& trace,
unsigned int clkMhz,
unsigned int defaultDataLength,
TraceType traceType,
@@ -51,18 +53,11 @@ StlPlayer::StlPlayer(std::string_view tracePath,
storageEnabled(storageEnabled),
playerPeriod(sc_core::sc_time(1.0 / static_cast<double>(clkMhz), sc_core::SC_US)),
defaultDataLength(defaultDataLength),
traceFile(tracePath.data()),
lineBuffers(
{std::make_shared<std::vector<Request>>(), std::make_shared<std::vector<Request>>()}),
parseBuffer(lineBuffers.at(1)),
readoutBuffer(lineBuffers.at(0))
traceFile(trace)
{
readoutBuffer->reserve(LINE_BUFFER_SIZE);
parseBuffer->reserve(LINE_BUFFER_SIZE);
if (!traceFile.is_open())
SC_REPORT_FATAL("StlPlayer",
(std::string("Could not open trace ") + tracePath.data()).c_str());
(std::string("Could not open trace ") + trace.string()).c_str());
{
std::string line;
@@ -72,60 +67,99 @@ StlPlayer::StlPlayer(std::string_view tracePath,
numberOfLines++;
}
if (numberOfLines == 0)
SC_REPORT_FATAL("StlPlayer", (std::string("Empty trace ") + tracePath.data()).c_str());
SC_REPORT_FATAL("StlPlayer", (std::string("Empty trace ") + trace.string()).c_str());
traceFile.clear();
traceFile.seekg(0);
}
parseTraceFile();
readoutIt = readoutBuffer->cend();
swapBuffers();
readoutIt = lineBuffers.at(consumeIndex).cbegin();
}
void StlPlayer::incrementLine()
{
readoutIt++;
if (readoutIt == lineBuffers.at(consumeIndex).cend())
{
swapBuffers();
readoutIt = lineBuffers.at(consumeIndex).cbegin();
if (lineBuffers.at(consumeIndex).empty())
{
if (parserThread.joinable())
parserThread.join();
}
}
}
std::optional<StlPlayer::LineContent> StlPlayer::currentLine() const
{
if (readoutIt == lineBuffers.at(consumeIndex).cend())
return std::nullopt;
return *readoutIt;
}
Request StlPlayer::nextRequest()
{
if (readoutIt == readoutBuffer->cend())
{
readoutIt = swapBuffers();
if (readoutIt == readoutBuffer->cend())
{
if (parserThread.joinable())
parserThread.join();
auto currentLineContent = currentLine();
// The file is read in completely. Nothing more to do.
return Request{Request::Command::Stop};
if (!currentLineContent.has_value())
{
// The file is read in completely. Nothing more to do.
return Request{Request::Command::Stop};
}
auto command = currentLineContent->command == LineContent::Command::Read
? Request::Command::Read
: Request::Command::Write;
auto dataLength = currentLineContent->dataLength.has_value()
? currentLineContent->dataLength.value()
: defaultDataLength;
Request request{command, currentLineContent->address, dataLength, currentLineContent->data};
incrementLine();
return request;
}
sc_core::sc_time StlPlayer::nextTrigger()
{
auto currentLineContent = currentLine();
sc_core::sc_time nextTrigger = sc_core::SC_ZERO_TIME;
if (currentLineContent.has_value())
{
if (traceType == TraceType::Absolute)
{
sc_core::sc_time cycleTime = currentLineContent->cycle * playerPeriod;
bool behindSchedule = sc_core::sc_time_stamp() > cycleTime;
nextTrigger =
behindSchedule ? sc_core::SC_ZERO_TIME : cycleTime - sc_core::sc_time_stamp();
}
else // if (traceType == TraceType::Relative)
{
nextTrigger = currentLineContent->cycle * playerPeriod;
}
}
sc_core::sc_time delay;
if (traceType == TraceType::Absolute)
{
bool behindSchedule = sc_core::sc_time_stamp() > readoutIt->delay;
delay =
behindSchedule ? sc_core::SC_ZERO_TIME : readoutIt->delay - sc_core::sc_time_stamp();
}
else // if (traceType == TraceType::Relative)
{
delay = readoutIt->delay;
}
Request request(*readoutIt);
request.delay = delay;
readoutIt++;
return request;
return nextTrigger;
}
void StlPlayer::parseTraceFile()
{
unsigned parsedLines = 0;
parseBuffer->clear();
auto& parseBuffer = lineBuffers.at(parseIndex);
parseBuffer.clear();
while (traceFile && !traceFile.eof() && parsedLines < LINE_BUFFER_SIZE)
{
// Get a new line from the input file.
std::string line;
std::getline(traceFile, line);
currentLine++;
currentParsedLine++;
// If the line is empty (\n or \r\n) or starts with '#' (comment) the transaction is
// ignored.
@@ -133,8 +167,8 @@ void StlPlayer::parseTraceFile()
continue;
parsedLines++;
parseBuffer->emplace_back();
Request& content = parseBuffer->back();
parseBuffer.emplace_back();
LineContent& content = parseBuffer.back();
// Trace files MUST provide timestamp, command and address for every
// transaction. The data information depends on the storage mode
@@ -148,90 +182,63 @@ void StlPlayer::parseTraceFile()
{
// Get the timestamp for the transaction.
iss >> element;
if (element.empty())
SC_REPORT_FATAL(
"StlPlayer",
("Malformed trace file line " + std::to_string(currentLine) + ".").c_str());
content.delay = playerPeriod * static_cast<double>(std::stoull(element));
content.cycle = std::stoull(element);
// Get the optional burst length and command
iss >> element;
if (element.empty())
SC_REPORT_FATAL(
"StlPlayer",
("Malformed trace file line " + std::to_string(currentLine) + ".").c_str());
if (element.at(0) == '(')
{
element.erase(0, 1);
content.length = std::stoul(element);
content.dataLength = std::stoul(element);
iss >> element;
if (element.empty())
SC_REPORT_FATAL(
"StlPlayer",
("Malformed trace file line " + std::to_string(currentLine) + ".").c_str());
}
else
content.length = defaultDataLength;
if (element == "read")
content.command = Request::Command::Read;
content.command = LineContent::Command::Read;
else if (element == "write")
content.command = Request::Command::Write;
content.command = LineContent::Command::Write;
else
SC_REPORT_FATAL(
"StlPlayer",
("Malformed trace file line " + std::to_string(currentLine) + ".").c_str());
throw std::runtime_error("Unable to parse command");
// Get the address.
iss >> element;
if (element.empty())
SC_REPORT_FATAL(
"StlPlayer",
("Malformed trace file line " + std::to_string(currentLine) + ".").c_str());
content.address = std::stoull(element, nullptr, 16);
static constexpr unsigned HEX = 16;
content.address = std::stoull(element, nullptr, HEX);
// Get the data if necessary.
if (storageEnabled && content.command == Request::Command::Write)
if (storageEnabled && content.command == LineContent::Command::Write)
{
// The input trace file must provide the data to be stored into the memory.
iss >> element;
// Check if data length in the trace file is correct.
// We need two characters to represent 1 byte in hexadecimal. Offset for 0x
// prefix.
if (element.length() != (content.length * 2 + 2))
SC_REPORT_FATAL(
"StlPlayer",
("Malformed trace file line " + std::to_string(currentLine) + ".").c_str());
// Set data
for (unsigned i = 0; i < content.length; i++)
content.data.emplace_back(static_cast<unsigned char>(
std::stoi(element.substr(i * 2 + 2, 2), nullptr, 16)));
// We need two characters to represent 1 byte in hexadecimal.
// Offset for 0x prefix.
for (std::size_t i = 2; i < element.length(); i += 2)
{
uint8_t byte = std::stoi(element.substr(i, 2), nullptr, HEX);
content.data.push_back(byte);
}
}
}
catch (...)
{
SC_REPORT_FATAL(
"StlPlayer",
("Malformed trace file line " + std::to_string(currentLine) + ".").c_str());
("Malformed trace file line " + std::to_string(currentParsedLine) + ".").c_str());
}
}
}
std::vector<Request>::const_iterator StlPlayer::swapBuffers()
void StlPlayer::swapBuffers()
{
// Wait for parser to finish
if (parserThread.joinable())
parserThread.join();
// Swap buffers
std::swap(readoutBuffer, parseBuffer);
std::swap(parseIndex, consumeIndex);
// Start new parser thread
parserThread = std::thread(&StlPlayer::parseTraceFile, this);
return readoutBuffer->cbegin();
}

View File

@@ -40,57 +40,71 @@
#pragma once
#include "simulator/request/Request.h"
#include "simulator/request/RequestProducer.h"
#include <systemc>
#include <tlm>
#include <array>
#include <filesystem>
#include <fstream>
#include <memory>
#include <optional>
#include <thread>
#include <vector>
class StlPlayer : public RequestProducer
{
public:
enum class TraceType
enum class TraceType : uint8_t
{
Absolute,
Relative,
};
StlPlayer(std::string_view tracePath,
StlPlayer(std::filesystem::path const& trace,
unsigned int clkMhz,
unsigned int defaultDataLength,
TraceType traceType,
bool storageEnabled);
Request nextRequest() override;
sc_core::sc_time nextTrigger() override;
uint64_t totalRequests() override { return numberOfLines; }
private:
struct LineContent
{
unsigned cycle{};
enum class Command : uint8_t
{
Read,
Write
} command{};
uint64_t address{};
std::optional<unsigned> dataLength;
std::vector<uint8_t> data;
};
std::optional<LineContent> currentLine() const;
void parseTraceFile();
std::vector<Request>::const_iterator swapBuffers();
void swapBuffers();
void incrementLine();
static constexpr std::size_t LINE_BUFFER_SIZE = 10000;
const TraceType traceType;
const bool storageEnabled;
const sc_core::sc_time playerPeriod;
const unsigned int defaultDataLength;
TraceType traceType;
bool storageEnabled;
sc_core::sc_time playerPeriod;
unsigned int defaultDataLength;
std::ifstream traceFile;
uint64_t currentLine = 0;
uint64_t currentParsedLine = 0;
uint64_t numberOfLines = 0;
std::array<std::shared_ptr<std::vector<Request>>, 2> lineBuffers;
std::shared_ptr<std::vector<Request>> parseBuffer;
std::shared_ptr<std::vector<Request>> readoutBuffer;
std::array<std::vector<LineContent>, 2> lineBuffers;
std::size_t parseIndex = 0;
std::size_t consumeIndex = 1;
std::vector<Request>::const_iterator readoutIt;
std::vector<LineContent>::const_iterator readoutIt;
std::thread parserThread;
};

View File

@@ -36,18 +36,17 @@
#pragma once
#include <cstdint>
#include <systemc>
#include <vector>
struct Request
{
enum class Command
enum class Command : uint8_t
{
Read,
Write,
Stop
} command;
uint64_t address{};
std::size_t length{};
sc_core::sc_time delay{};
std::vector<unsigned char> data{};
uint64_t address = 0;
std::size_t length = 0;
std::vector<unsigned char> data;
};

View File

@@ -36,22 +36,22 @@
#include "RequestIssuer.h"
RequestIssuer::RequestIssuer(sc_core::sc_module_name const& name,
std::unique_ptr<RequestProducer> producer,
MemoryManager& memoryManager,
sc_core::sc_time interfaceClk,
std::optional<unsigned int> maxPendingReadRequests,
std::optional<unsigned int> maxPendingWriteRequests,
std::function<Request()> nextRequest,
std::function<void()> transactionFinished,
std::function<void()> terminate) :
sc_module(name),
producer(std::move(producer)),
payloadEventQueue(this, &RequestIssuer::peqCallback),
memoryManager(memoryManager),
interfaceClk(interfaceClk),
maxPendingReadRequests(maxPendingReadRequests),
maxPendingWriteRequests(maxPendingWriteRequests),
transactionFinished(std::move(transactionFinished)),
terminate(std::move(terminate)),
nextRequest(std::move(nextRequest))
terminate(std::move(terminate))
{
SC_THREAD(sendNextRequest);
iSocket.register_nb_transport_bw(this, &RequestIssuer::nb_transport_bw);
@@ -59,48 +59,55 @@ RequestIssuer::RequestIssuer(sc_core::sc_module_name const& name,
void RequestIssuer::sendNextRequest()
{
Request request = nextRequest();
if (request.command == Request::Command::Stop)
while (true)
{
finished = true;
return;
Request request = producer->nextRequest();
if (request.command == Request::Command::Stop)
{
finished = true;
return;
}
if (requestInProgress)
{
wait(endReq);
}
while (!nextRequestSendable())
{
wait(beginResp);
}
tlm::tlm_generic_payload& payload = memoryManager.allocate(request.length);
payload.acquire();
payload.set_address(request.address);
payload.set_response_status(tlm::TLM_INCOMPLETE_RESPONSE);
payload.set_dmi_allowed(false);
payload.set_byte_enable_length(0);
payload.set_data_length(request.length);
payload.set_streaming_width(request.length);
payload.set_command(request.command == Request::Command::Read ? tlm::TLM_READ_COMMAND
: tlm::TLM_WRITE_COMMAND);
std::copy(request.data.cbegin(), request.data.cend(), payload.get_data_ptr());
tlm::tlm_phase phase = tlm::BEGIN_REQ;
sc_core::sc_time delay = sc_core::SC_ZERO_TIME;
iSocket->nb_transport_fw(payload, phase, delay);
requestInProgress = true;
if (request.command == Request::Command::Read)
pendingReadRequests++;
else if (request.command == Request::Command::Write)
pendingWriteRequests++;
transactionsSent++;
nextTrigger.notify(producer->nextTrigger());
wait(nextTrigger);
}
tlm::tlm_generic_payload& payload = memoryManager.allocate(request.length);
payload.acquire();
payload.set_address(request.address);
payload.set_response_status(tlm::TLM_INCOMPLETE_RESPONSE);
payload.set_dmi_allowed(false);
payload.set_byte_enable_length(0);
payload.set_data_length(request.length);
payload.set_streaming_width(request.length);
payload.set_command(request.command == Request::Command::Read ? tlm::TLM_READ_COMMAND
: tlm::TLM_WRITE_COMMAND);
std::copy(request.data.cbegin(), request.data.cend(), payload.get_data_ptr());
tlm::tlm_phase phase = tlm::BEGIN_REQ;
sc_core::sc_time delay = request.delay;
sc_core::sc_time sendingTime = sc_core::sc_time_stamp() + delay;
bool needsOffset = (sendingTime % interfaceClk) != sc_core::SC_ZERO_TIME;
if (needsOffset)
{
sendingTime += interfaceClk;
sendingTime -= sendingTime % interfaceClk;
}
delay = sendingTime - sc_core::sc_time_stamp();
iSocket->nb_transport_fw(payload, phase, delay);
if (request.command == Request::Command::Read)
pendingReadRequests++;
else if (request.command == Request::Command::Write)
pendingWriteRequests++;
transactionsSent++;
}
bool RequestIssuer::nextRequestSendable() const
@@ -121,37 +128,31 @@ void RequestIssuer::peqCallback(tlm::tlm_generic_payload& payload, const tlm::tl
{
if (phase == tlm::END_REQ)
{
requestInProgress = false;
endReq.notify(sc_core::SC_ZERO_TIME);
lastEndRequest = sc_core::sc_time_stamp();
if (nextRequestSendable())
sendNextRequest();
else
if (!nextRequestSendable())
transactionPostponed = true;
}
else if (phase == tlm::BEGIN_RESP)
{
tlm::tlm_phase nextPhase = tlm::END_RESP;
sc_core::sc_time delay = interfaceClk;
iSocket->nb_transport_fw(payload, nextPhase, delay);
payload.release();
transactionFinished();
transactionsReceived++;
transactionFinished();
if (payload.get_command() == tlm::TLM_READ_COMMAND)
pendingReadRequests--;
else if (payload.get_command() == tlm::TLM_WRITE_COMMAND)
pendingWriteRequests--;
// If the initiator wasn't able to send the next payload in the END_REQ phase, do it
// now.
if (transactionPostponed && nextRequestSendable())
{
sendNextRequest();
transactionPostponed = false;
}
beginResp.notify(sc_core::SC_ZERO_TIME);
// Send END_RESP
tlm::tlm_phase nextPhase = tlm::END_RESP;
sc_core::sc_time delay = interfaceClk;
iSocket->nb_transport_fw(payload, nextPhase, delay);
payload.release();
// If all answers were received:
if (finished && transactionsSent == transactionsReceived)

View File

@@ -36,8 +36,10 @@
#pragma once
#include "Request.h"
#include "RequestProducer.h"
#include "simulator/MemoryManager.h"
#include <memory>
#include <systemc>
#include <tlm>
#include <tlm_utils/peq_with_cb_and_phase.h>
@@ -51,21 +53,43 @@ public:
tlm_utils::simple_initiator_socket<RequestIssuer> iSocket;
RequestIssuer(sc_core::sc_module_name const& name,
std::unique_ptr<RequestProducer> producer,
MemoryManager& memoryManager,
sc_core::sc_time interfaceClk,
std::optional<unsigned int> maxPendingReadRequests,
std::optional<unsigned int> maxPendingWriteRequests,
std::function<Request()> nextRequest,
std::function<void()> transactionFinished,
std::function<void()> terminate);
SC_HAS_PROCESS(RequestIssuer);
uint64_t totalRequests() { return producer->totalRequests(); };
private:
void sendNextRequest();
bool nextRequestSendable() const;
sc_core::sc_event nextTrigger;
sc_core::sc_event endReq;
sc_core::sc_event beginResp;
tlm::tlm_sync_enum nb_transport_bw(tlm::tlm_generic_payload& payload,
tlm::tlm_phase& phase,
sc_core::sc_time& bwDelay)
{
payloadEventQueue.notify(payload, phase, bwDelay);
return tlm::TLM_ACCEPTED;
}
void peqCallback(tlm::tlm_generic_payload& payload, const tlm::tlm_phase& phase);
std::unique_ptr<RequestProducer> producer;
tlm_utils::peq_with_cb_and_phase<RequestIssuer> payloadEventQueue;
MemoryManager& memoryManager;
sc_core::sc_time interfaceClk;
bool requestInProgress = false;
bool transactionPostponed = false;
bool finished = false;
@@ -80,18 +104,4 @@ private:
std::function<void()> transactionFinished;
std::function<void()> terminate;
std::function<Request()> nextRequest;
void sendNextRequest();
bool nextRequestSendable() const;
tlm::tlm_sync_enum nb_transport_bw(tlm::tlm_generic_payload& payload,
tlm::tlm_phase& phase,
sc_core::sc_time& bwDelay)
{
payloadEventQueue.notify(payload, phase, bwDelay);
return tlm::TLM_ACCEPTED;
}
void peqCallback(tlm::tlm_generic_payload& payload, const tlm::tlm_phase& phase);
};

View File

@@ -37,6 +37,8 @@
#include "Request.h"
#include <systemc>
class RequestProducer
{
protected:
@@ -50,6 +52,7 @@ public:
virtual ~RequestProducer() = default;
virtual Request nextRequest() = 0;
virtual sc_core::sc_time nextTrigger() = 0;
virtual uint64_t totalRequests() = 0;
virtual void reset(){};
virtual void reset() {};
};