20#include "sparta/utils/MathUtils.hpp"
21#include "sparta/collection/IterableCollector.hpp"
61 template <
typename DataT,
typename EventT=PhasedUniqueEvent>
65 using size_type = uint32_t;
66 using value_type = DataT;
67 using EventHandle = std::unique_ptr<EventT>;
68 using EventHandleList = std::list<EventHandle>;
69 using EventList = std::list<EventT*>;
70 using EventMatrix = std::array<EventList, NUM_SCHEDULING_PHASES>;
83 template<
typename DataT2,
typename EventT2>
100 template<
bool is_const_iterator = true>
103 using DataReferenceType =
104 typename std::conditional<is_const_iterator, const DataT &, DataT &>::type;
106 using DataPointerType =
107 typename std::conditional<is_const_iterator, const DataT *, DataT *>::type;
109 using PipelinePointerType =
110 typename std::conditional<is_const_iterator,
140 pipelinePtr_(rhs.pipelinePtr_),
152 pipelinePtr_(rhs.pipelinePtr_),
169 return pipelinePtr_->operator[](index_);
183 if (index_ > pipelinePtr_->capacity()) {
184 index_ = pipelinePtr_->capacity();
200 return ((pipelinePtr_ == rhs.pipelinePtr_) && (index_ == rhs.index_));
211 bool isValid()
const {
return pipelinePtr_->isValid(index_); }
214 PipelinePointerType pipelinePtr_;
218 using iterator = PipelineIterator<false>;
219 using const_iterator = PipelineIterator<true>;
221 iterator begin() {
return iterator(
this); }
222 const_iterator begin()
const {
return const_iterator(
this); }
223 const_iterator cbegin()
const {
return const_iterator(
this); }
225 iterator end() {
return iterator(
this,
capacity()); }
226 const_iterator end()
const {
return const_iterator(
this,
capacity()); }
227 const_iterator cend()
const {
return const_iterator(
this,
capacity()); }
238 const std::string & name,
239 const uint32_t num_stages,
243 pipe_(name, num_stages, clk),
244 event_list_at_stage_(num_stages),
245 event_matrix_at_stage_(num_stages),
246 es_((es == nullptr) ? &dummy_es_ : es),
249 num_stages_(num_stages)
257 static_assert(std::is_same_v<EventT, PhasedUniqueEvent> || std::is_same_v<EventT, PhasedPayloadEvent<DataT>>,
258 "Error: Pipeline is templated on a unsupported Event type. Supported Event types: "
259 "UniqueEvent, PayloaodEvent (where DataT == DataT of the Pipeline).");
260 events_valid_at_stage_.resize(num_stages,
false);
261 advance_into_stage_.resize(num_stages,
true);
276 const uint32_t num_stages,
278 Pipeline(nullptr, name, num_stages, clk)
292 template<SchedulingPhase sched_phase = SchedulingPhase::Tick>
295 sparta_assert(
static_cast<uint32_t
>(default_precedence_) ==
static_cast<uint32_t
>(Precedence::NONE),
296 "You have specified a default precedence (" <<
static_cast<uint32_t
>(default_precedence_)
297 <<
") between stages. No new handlers can be registered any more!");
299 "Attempt to register handler for invalid pipeline stage[" <<
id <<
"]!");
302 auto & event_list = event_list_at_stage_[id];
303 if constexpr (std::is_same_v<EventT, PhasedPayloadEvent<DataT>>) {
304 sparta_assert(handler.argCount() == 1,
"Expecting Sparta Handler with 1 data parameter!");
305 event_list.emplace_back(
new EventT(es_,
306 "pev_" + name_ +
"_stage_" + std::to_string(
id) +
"_" + std::to_string(event_list_at_stage_[
id].
size()),
310 sparta_assert(handler.argCount() == 0,
"Expecting Sparta Handler with no data parameter!");
311 event_list.emplace_back(
new EventT(es_,
312 "uev_" + name_ +
"_stage_" + std::to_string(
id) +
"_" + std::to_string(event_list_at_stage_[
id].
size()),
316 auto new_event = event_list.back().get();
319 if constexpr (std::is_same_v<EventT, PhasedPayloadEvent<DataT>>) {
320 new_event->getScheduleable().setScheduleableClock(clock_);
321 new_event->getScheduleable().setScheduler(clock_->
getScheduler());
323 new_event->setScheduleableClock(clock_);
328 auto & event_list_per_phase = event_matrix_at_stage_[id][
static_cast<uint32_t
>(sched_phase)];
329 if (event_list_per_phase.size() > 0) {
330 auto & producer_event = event_list_per_phase.back();
333 if constexpr (std::is_same_v<EventT, PhasedPayloadEvent<DataT>>) {
334 producer_event->getScheduleable().precedes(new_event->getScheduleable());
336 producer_event->precedes(*new_event);
339 events_valid_at_stage_[id] =
true;
343 event_list_per_phase.push_back( new_event );
356 sparta_assert(
static_cast<uint32_t
>(default_precedence_) ==
static_cast<uint32_t
>(Precedence::NONE),
357 "You have specified a default precedence (" <<
static_cast<uint32_t
>(default_precedence_)
358 <<
"). No more precedence between stages can be set!");
359 sparta_assert(pid != cid,
"Cannot specify precedence with yourself!");
360 sparta_assert((pid < event_list_at_stage_.size()) && (event_list_at_stage_[pid].size() > 0),
361 "Precedence setup fails: No handler for pipeline stage[" << pid <<
"]!");
362 sparta_assert((cid < event_list_at_stage_.size()) && (event_list_at_stage_[cid].size() > 0),
363 "Precedence setup fails: No handler for pipeline stage[" << cid <<
"]!");
366 auto & pstage_event_list = event_matrix_at_stage_[pid][phase_id];
367 auto & cstage_event_list = event_matrix_at_stage_[cid][phase_id];
369 if (pstage_event_list.empty() || cstage_event_list.empty()) {
376 if constexpr (std::is_same_v<EventT, PhasedPayloadEvent<DataT>>) {
377 (pstage_event_list.back())->getScheduleable().precedes((cstage_event_list.front())->getScheduleable());
379 (pstage_event_list.back())->precedes(*(cstage_event_list.front()));
391 template<
class DataT2,
class EventT2>
394 sparta_assert(
static_cast<void*
>(&c_pipeline) !=
static_cast<void*
>(
this),
395 "Cannot use this function to set precedence between stages within the same pipeline instance!");
396 sparta_assert((pid < event_list_at_stage_.size()) && (event_list_at_stage_[pid].size() > 0),
397 "Precedence setup fails: No handler for pipeline stage[" << pid <<
"]!");
398 sparta_assert((cid < c_pipeline.event_list_at_stage_.size()) && (c_pipeline.event_list_at_stage_[cid].size() > 0),
399 "Precedence setup fails: No handler for pipeline stage[" << cid <<
"]!");
402 auto & pstage_event_list = event_matrix_at_stage_[pid][phase_id];
403 auto & cstage_event_list = c_pipeline.event_matrix_at_stage_[cid][phase_id];
405 if (pstage_event_list.empty() || cstage_event_list.empty()) {
412 if constexpr (std::is_same_v<EventT, PhasedPayloadEvent<DataT>>) {
413 (pstage_event_list.back())->getScheduleable().precedes((cstage_event_list.front())->getScheduleable());
415 (pstage_event_list.back())->precedes(*(cstage_event_list.front()));
427 sparta_assert(
static_cast<uint32_t
>(default_precedence) <
static_cast<uint32_t
>(Precedence::NUM_OF_PRECEDENCE),
428 "Unknown default precedence is specified for sparta::Pipeline!");
430 if (
static_cast<uint32_t
>(default_precedence) ==
static_cast<uint32_t
>(Precedence::NONE)) {
434 bool forward = (
static_cast<uint32_t
>(default_precedence) ==
static_cast<uint32_t
>(Precedence::FORWARD));
437 uint32_t cid = pid + 1;
438 while (cid < event_list_at_stage_.size()) {
439 if (event_list_at_stage_[pid].
empty()) {
441 }
else if (event_list_at_stage_[cid].
empty()) {
460 default_precedence_ = default_precedence;
470 template<
typename EventType>
473 auto phase = ev_handler.getScheduleable().getSchedulingPhase();
475 "Cannot set producer event for pipeline update event, it's not on the Update phase!");
476 ev_handler.getScheduleable().precedes(ev_pipeline_update_.
getScheduleable());
486 template<
typename EventType>
489 auto phase = ev_handler.getScheduleable().getSchedulingPhase();
491 "Cannot set consumer event for pipeline update event, it's not on the Update phase!");
501 template<
typename EventType>
504 sparta_assert((
id < event_list_at_stage_.size()) && (event_list_at_stage_[
id].size() > 0),
505 "Precedence setup fails: No handler for pipeline stage[" <<
id <<
"]!");
507 auto phase_id =
static_cast<uint32_t
>(ev_handler.getScheduleable().getSchedulingPhase());
508 auto & event_list = event_matrix_at_stage_[id][phase_id];
511 "Cannot set producer event for pipeline stage[" <<
id <<
"]. No registered stage event on the SAME phase!");
512 if constexpr (std::is_same_v<EventT, PhasedPayloadEvent<DataT>>) {
513 ev_handler.getScheduleable().precedes((event_list.front())->getScheduleable());
515 ev_handler.getScheduleable().precedes(*(event_list.front()));
525 template<
typename EventType>
528 sparta_assert((
id < event_list_at_stage_.size()) && (event_list_at_stage_[
id].size() > 0),
529 "Precedence setup fails: No handler for pipeline stage[" <<
id <<
"]!");
531 auto phase_id =
static_cast<uint32_t
>(ev_handler.getScheduleable().getSchedulingPhase());
532 auto & event_list = event_matrix_at_stage_[id][phase_id];
535 "Cannot set consumer event for pipeline stage[" <<
id
536 <<
"]. No registered stage event on the SAME phase!");
537 if constexpr (std::is_same_v<EventT, PhasedPayloadEvent<DataT>>) {
538 (event_list.back())->getScheduleable().precedes(ev_handler.getScheduleable());
540 (event_list.back())->precedes(ev_handler.getScheduleable());
554 "Attempt to get events at an invalid pipeline stage["
556 using SchedUType = std::underlying_type<SchedulingPhase>::type;
557 auto & event_list = event_matrix_at_stage_[id][
static_cast<SchedUType
>(phase)];
559 "No registered events at stage[" <<
id <<
"]!");
575 "Attempt to check event handler for invalid pipeline stage[" <<
id <<
"]!");
577 return (event_list_at_stage_[
id].
size() > 0);
592 "Attempt to activate event handler for invalid pipeline stage[" <<
id <<
"]!");
594 "Activation fails: No registered event handler for stage[" <<
id <<
"]!");
596 events_valid_at_stage_[id] =
true;
610 "Attempt to deactivate event handler for invalid pipeline stage[" <<
id <<
"]!");
612 "Deactivation fails: No registered event handler for stage[" <<
id <<
"]!");
614 events_valid_at_stage_[id] =
false;
634 return appendImpl_(std::move(item));
642 return pipe_.isAppended();
651 return pipe_.readAppendedData();
660 void writeStage(
const uint32_t & stage_id,
const DataT & item)
662 writeStageImpl_(stage_id, item);
673 writeStageImpl_(stage_id, std::move(item));
683 pipe_.invalidatePS(stage_id);
685 if (perform_own_update_) {
699 void stall(
const uint32_t & stall_stage_id,
700 const uint32_t & stall_cycles,
701 const bool crush_bubbles=
false,
702 const bool suppress_events=
true)
704 sparta_assert(pipe_.isValid(stall_stage_id),
"Try to stall an empty pipeline stage!");
707 if (stall_cycles == 0) {
711 stall_cycles_ = stall_cycles;
712 stall_stage_id_ = stall_stage_id;
713 deactivate_(stall_stage_id_, crush_bubbles, suppress_events);
723 return (stall_cycles_ > 0) || (stall_stage_id_ < std::numeric_limits<uint32_t>::max());
744 cancelEventsAtStage_(flush_stage_id);
746 if (flush_stage_id == stall_stage_id_) {
748 restart_(stall_stage_id_);
751 stall_stage_id_ = std::numeric_limits<uint32_t>::max();
754 pipe_.flushPS(flush_stage_id);
778 for (uint32_t stage_id = 0; stage_id < num_stages_; stage_id++) {
808 const DataT &
operator[](
const uint32_t & stage_id)
const {
return pipe_.read(stage_id); }
815 DataT &
operator[](
const uint32_t & stage_id) {
return pipe_.access(stage_id); }
822 const DataT &
at(
const uint32_t & stage_id)
const {
return pipe_.read(stage_id); }
829 DataT &
at(
const uint32_t & stage_id) {
return pipe_.access(stage_id); }
836 bool isValid(
const uint32_t & stage_id)
const {
return pipe_.isValid(stage_id); }
845 uint32_t
numValid()
const {
return pipe_.numValid(); }
851 uint32_t
size()
const {
return pipe_.size(); }
867 if (!perform_own_update_) {
868 perform_own_update_ =
true;
870 if (pipe_.isAnyValid()) {
888 sparta_assert(perform_own_update_ ==
false,
"You asked me to perform my own update!");
902 template<SchedulingPhase phase = SchedulingPhase::Collection>
912 void appendImpl_(U && item)
914 pipe_.append(std::forward<U>(item));
916 if (perform_own_update_) {
922 void writeStageImpl_(
const uint32_t & stage_id, U && item)
924 pipe_.writePS(stage_id, std::forward<U>(item));
926 if (perform_own_update_) {
932 void internalUpdate_()
936 scheduleEventForEachStage_();
938 drain_(stall_stage_id_ + 1);
939 scheduleEventForEachStage_();
942 if (stall_cycles_ == 0) {
943 restart_(stall_stage_id_);
944 stall_stage_id_ = std::numeric_limits<uint32_t>::max();
948 if (pipe_.isAnyValid() && perform_own_update_) {
954 void drain_(
const uint32_t start_id)
956 uint32_t stage_id = num_stages_ - 1;
959 if (start_id <= stage_id && pipe_.isValid(stage_id)) {
960 pipe_.invalidatePS(stage_id);
964 while (stage_id > start_id) {
965 if (pipe_.isValid(stage_id - 1)) {
966 pipe_.writePS(stage_id, pipe_.access(stage_id - 1));
967 pipe_.invalidatePS(stage_id - 1);
978 while (stage_id > 0) {
979 if (advance_into_stage_[stage_id - 1] &&
980 !pipe_.isValid(stage_id) && pipe_.isValid(stage_id - 1)) {
981 pipe_.writePS(stage_id, pipe_.access(stage_id - 1));
982 pipe_.invalidatePS(stage_id - 1);
997 void cancelEventsAtStage_(
const uint32_t & stage_id)
1000 "Try to cancel events for invalid pipeline stage[" << stage_id <<
"]");
1001 if (pipe_.isValid(stage_id) && events_valid_at_stage_[stage_id]) {
1003 for (
const auto & ev_ptr : event_list_at_stage_[stage_id]) {
1004 ev_ptr->cancel(sparta::Clock::Cycle(0));
1010 void scheduleEventForEachStage_()
1014 for (uint32_t i = 0; i < num_stages_; i++) {
1015 if (pipe_.isValid(i) && events_valid_at_stage_[i]) {
1017 for (
const auto & ev_ptr : event_list_at_stage_[i]) {
1018 if constexpr (std::is_same_v<EventT, PhasedPayloadEvent<DataT>>) {
1019 ev_ptr->preparePayload(
at(i))->schedule(sparta::Clock::Cycle(0));
1021 ev_ptr->schedule(sparta::Clock::Cycle(0));
1029 void deactivate_(
const uint32_t & stall_stage_id,
1030 const bool crush_bubbles,
1031 const bool suppress_events)
1034 "Try to deactivate events for invalid pipeline stage[" << stall_stage_id <<
"]");
1036 for (int32_t stage_id = stall_stage_id; stage_id >= 0; stage_id--) {
1038 if (crush_bubbles && !pipe_.isValid(stage_id)) {
1042 if (suppress_events && event_list_at_stage_[stage_id].
size() > 0) {
1043 events_valid_at_stage_[stage_id] =
false;
1045 advance_into_stage_[stage_id] =
false;
1050 void restart_(
const uint32_t & stall_stage_id)
1052 for (uint32_t stage_id = 0; stage_id <= stall_stage_id; stage_id++) {
1054 "Try to restart invalid pipeline stage[" << stage_id <<
"]");
1055 if (event_list_at_stage_[stage_id].
size() > 0) {
1056 events_valid_at_stage_[stage_id] =
true;
1058 advance_into_stage_[stage_id] =
true;
1063 const std::string name_;
1066 const Clock * clock_;
1072 std::vector<EventHandleList> event_list_at_stage_;
1081 std::vector<bool> events_valid_at_stage_;
1082 std::vector<bool> advance_into_stage_;
1085 std::vector<EventMatrix> event_matrix_at_stage_;
1092 UniqueEvent<SchedulingPhase::Update> ev_pipeline_update_;
1095 bool perform_own_update_ =
false;
1098 const uint32_t num_stages_ = 0;
1101 Precedence default_precedence_ = Precedence::NONE;
1104 uint32_t stall_cycles_ = 0;
1107 uint32_t stall_stage_id_ = std::numeric_limits<uint32_t>::max();
File that defines the Clock class.
Defines a few handy (and now deprecated) C++ iterator traits.
File that defines the PhasedPayloadEvent class. Suggest using sparta::PayloadEvent instead.
File that defines the PhasedUniqueEvent class.
File that defines the Scheduleable class.
Set of macros for Sparta assertions. Caught by the framework.
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
File that contains the macro used to generate the class callbacks.
#define CREATE_SPARTA_HANDLER(clname, meth)
Specify the default event scheduling precedence between pipeline stages.
A representation of simulated time.
Scheduler * getScheduler() const
virtual Scheduleable & getScheduleable()=0
Get the scheduleable associated with this event node.
Set of Events that a unit (or sparta::TreeNode, sparta::Resource) contains and are visible through a ...
A very simple pipe, not part of the DES paradigm.
An iterator class for Pipeline class.
PipelineIterator(const PipelineIterator< false > &rhs)
Copy constructor.
DataPointerType operator->()
Override the dereferencing operator->
PipelineIterator operator++(int)
Override the pre-increment operator.
PipelineIterator operator++()
Override the pre-increment operator.
PipelineIterator(const PipelineIterator< true > &rhs)
Copy constructor.
PipelineIterator(PipelinePointerType ptr, uint32_t i)
Constructor.
bool operator!=(const PipelineIterator &rhs) const
Override the comparison(not equal) operator.
DataReferenceType operator*()
Override the dereferencing operator*.
PipelineIterator(PipelinePointerType ptr)
Constructor.
PipelineIterator & operator=(const PipelineIterator &rhs)=default
Copy assignment operator.
bool operator==(const PipelineIterator &rhs) const
Override the comparison(equal) operator.
bool isValid() const
Check the validity of the iterator.
bool isValid(const uint32_t &stage_id) const
Indicate the validity of a specific pipeline stage.
void registerHandlerAtStage(const uint32_t &id, const SpartaHandler &handler)
Register event handler for a designated pipeline stage.
void setPrecedenceBetweenStage(const uint32_t &pid, const uint32_t &cid)
Specify precedence between two different stages within the same pipeline instance.
bool isAppended() const
Is the pipe already appended data?
void deactivateEventAtStage(const uint32_t &id)
Deactivate event for a designated pipeline stage.
bool isLastValid() const
Indicate the validity of the last pipeline stage.
void setConsumerForStage(const uint32_t &id, EventType &ev_handler)
Specify consumer event for a designated pipeline stage.
const DataT & readAppendedData() const
Get the data just appended; it will assert if no data appended.
uint32_t capacity() const
Indicate the pipeline capacity.
const DataT & at(const uint32_t &stage_id) const
Access (read-only) a specific stage of the pipeline.
bool isCollected() const
Check if pipeline is collecting.
void activateEventAtStage(const uint32_t &id)
Activate event for a designated pipeline stage.
DataT & at(const uint32_t &stage_id)
Access a specific stage of the pipeline.
void setProducerForPipelineUpdate(EventType &ev_handler)
Specify producer event for the pipeline update event.
void writeStage(const uint32_t &stage_id, const DataT &item)
Modify a specific stage of the pipeline.
void append(const DataT &item)
Append data to the beginning of the pipeline.
void update()
Manually update pipeline (i.e. data-movement and event-scheduling)
uint32_t numValid() const
Indicate the number of valid pipeline stages.
Pipeline(const std::string &name, const uint32_t num_stages, const Clock *clk)
Construct a Pipeline object.
void flushStage(const const_iterator &const_iter)
Flush a specific stage of the pipeline using const iterator.
void flushAllStages()
Flush all stages of the pipeline.
void setContinuing(const bool value)
set whether the update event is continuing or not
void setConsumerForPipelineUpdate(EventType &ev_handler)
Specify consumer event for the pipeline update event.
void performOwnUpdates()
Ask pipeline to perform its own update.
void enableCollection(TreeNode *parent)
Enable pipeline collection.
void setDefaultStagePrecedence(const Precedence &default_precedence)
Specify precedence of pipeline stage-handling events as forward/backward stage order.
void stall(const uint32_t &stall_stage_id, const uint32_t &stall_cycles, const bool crush_bubbles=false, const bool suppress_events=true)
Stall the pipeline up to designated stage for a specified number of cycles.
Pipeline(EventSet *es, const std::string &name, const uint32_t num_stages, const Clock *clk)
Construct a Pipeline object by existed event set.
void flushStage(const uint32_t &flush_stage_id)
Flush a specific stage of the pipeline using stage id.
EventList & getEventsAtStage(const uint32_t &id, const SchedulingPhase phase=SchedulingPhase::Tick)
Get events at a stage for a particular scheduling phase.
void append(DataT &&item)
Append data to the beginning of the pipeline.
void writeStage(const uint32_t &stage_id, DataT &&item)
Modify a specific stage of the pipeline.
bool isStalledOrStallingAtStage(const uint32_t &stage_id) const
Check if the designated pipeline stage will be stalled the very next cycle.
bool isEventRegisteredAtStage(const uint32_t &id) const
Check if any event is registered at a designated pipeline stage.
void invalidateStage(const uint32_t &stage_id)
Invalidate a specific stage of the pipeline.
bool empty() const
Indicate no valid pipeline stages.
void flushAppend()
Flush the data just appended.
void setPrecedenceBetweenPipeline(const uint32_t &pid, Pipeline< DataT2, EventT2 > &c_pipeline, const uint32_t &cid)
Specify precedence between two stages from different pipeline instances.
uint32_t size() const
Indicate the pipeline size.
const DataT & operator[](const uint32_t &stage_id) const
Access (read-only) a specific stage of the pipeline.
bool isStalledOrStalling() const
Check if the pipeline will be stalled the very next cycle.
bool isAnyValid() const
Indicate the validity of the whole pipeline.
DataT & operator[](const uint32_t &stage_id)
Access a specific stage of the pipeline.
void flushStage(const iterator &iter)
Flush a specific stage of the pipeline using non-const iterator.
void setProducerForStage(const uint32_t &id, EventType &ev_handler)
Specify producer event for a designated pipeline stage.
void setContinuing(bool continuing)
This event, if continuing == true, will keep the simulation running.
void setScheduler(Scheduler *sched)
Set the Scheduler of this Scheduleable, and set the local vertex_ to a new vertex from the Vertex Fac...
void precedes(Scheduleable &consumer, const std::string &reason="")
Have this Scheduleable precede another.
void setScheduleableClock(const Clock *clk)
Set the clock and scheduler of this Scheduleable.
Node in a composite tree representing a sparta Tree item.
virtual void setClock(const Clock *clk)
Assigns a clock to this node. This clock will then be accessed by any descendant which has no assigne...
void schedule()
Schedule this event with its pre-set delay using the pre-set Clock.
Macros for handling exponential backoff.
const uint32_t NUM_SCHEDULING_PHASES
The number of phases.
SchedulingPhase
The SchedulingPhases used for events (Tick, Update, PortUpdate, etc)
@ Update
Resources are updated in this phase.
@ Tick
Most operations (combinational logic) occurs in this phase.