The Sparta Modeling Framework
Loading...
Searching...
No Matches
Port.hpp
Go to the documentation of this file.
1// <Port.hpp> -*- C++ -*-
2
3
10#pragma once
11#include <set>
12#include <list>
13#include <unordered_map>
14#include <algorithm>
15#include <ostream>
16#include <string>
17#include <vector>
18
28
29namespace sparta
30{
31 // Used for DAG predence rules
32 class GlobalOrderingPoint;
33
58 class Port : public TreeNode
59 {
60 public:
62 typedef std::vector<Scheduleable *> ScheduleableList;
63
68 enum Direction {
72 N_DIRECTIONS
73 };
89 Port(TreeNode* portset, Direction dir, const std::string & name) :
90 TreeNode(nullptr, name, TreeNode::GROUP_NAME_NONE,
91 TreeNode::GROUP_IDX_NONE, "Ports"),
92 dir_(dir),
93 name_(name)
94 {
95 sparta_assert(name.length() != 0, "You cannot have an unnamed port.");
96 sparta_assert(portset != nullptr,
97 "Ports must created with a PortSet: " << name);
98 setExpectedParent_(portset);
99 ensureParentIsPortSet_(portset);
100 portset->addChild(this);
101 }
102
104 virtual ~Port() {}
105
111 virtual void bind(Port * port) = 0;
112
118 void bind(Port & port) {
119 bind(&port);
120 }
121
126 virtual bool isBound() const {
127 return (bound_ports_.empty() == false);
128 }
129
134 size_t getNumBoundPorts() const {
135 return bound_ports_.size();
136 }
137
142 virtual Direction getDirection() const {
143 return dir_;
144 }
145
161 virtual void participateInAutoPrecedence(bool participate) {
162 participate_in_auto_precedence_ = participate;
163 }
164
168 virtual bool doesParticipateInAutoPrecedence() const {
169 return participate_in_auto_precedence_;
170 }
171
177 std::string stringize(bool x) const override {
178 (void)x;
179 std::stringstream ss;
180 ss << "[bound to] {";
181 for(Port * p : bound_ports_) {
182 if(p != bound_ports_[0]){
183 ss << ", ";
184 }
185 ss << p->getName() << " (" << p->getLocation() << ")";
186 }
187 ss << "}";
188 return ss.str();
189 }
190
198 virtual void setPortDelay(sparta::Clock::Cycle ) {
199 sparta_assert ("ERROR:Parent port's don't have delays" == 0);
200 }
201
203 virtual void setPortDelay(double ) {
204 sparta_assert ("ERROR:Parent port's don't have delays" == 0);
205 }
206
208 virtual Clock::Cycle getPortDelay() const {
209 sparta_assert ("ERROR:Parent port's don't have delays" == 0);
210 return 0;
211 }
212
214 virtual void enableCollection(TreeNode*) { }
215
218 virtual void setContinuing(bool continuing) {
219 continuing_ = continuing;
220 };
221
222#ifndef DO_NOT_DOCUMENT
223 template<class PObjT, void * precedes_obj = nullptr>
224 void precedes(const PObjT &) {
225 static_assert(precedes_obj != nullptr,
226 "You cannot set a precedence on a Port anymore -- use "
227 "registerConsumerEvent and registerProducingEvent instead");
228 }
229#endif
230
233 bool isAlreadyBound(const Port * pt) {
234 return (std::find(bound_ports_.begin(),
235 bound_ports_.end(), pt) != bound_ports_.end());
236 }
237
243 virtual bool isDriven(Clock::Cycle rel_cycle) const {
244 (void) rel_cycle;
245 bool NOT_DEFINED = true;
246 sparta_assert(NOT_DEFINED == false,
247 "Function not defined for this Port: " << getName());
248 return false;
249 }
250
253 virtual bool isDriven() const {
254 bool NOT_DEFINED = true;
255 sparta_assert(NOT_DEFINED == false,
256 "Function not defined for this Port: " << getName());
257 return false;
258 }
259
260 private:
262 const Direction dir_;
263
265 void validateNode_() const override {
267 }
268
270 void ensureParentIsPortSet_(TreeNode * parent);
271
273 bool participate_in_auto_precedence_ = true;
274
275 protected:
276
279 bool continuing_ = true;
280
282 const std::string name_;
283
285 std::vector<Port *> bound_ports_;
286
288 SpartaHandler explicit_consumer_handler_{"base_port_null_consumer_handler"};
289 };
290
291 // Forward declaration
292 class OutPort;
293
296 class InPort : public Port
297 {
298 public:
306 InPort(TreeNode* portset, const std::string & name,
307 sparta::SchedulingPhase delivery_phase) :
308 Port(portset, Port::Direction::IN, name),
309 delivery_phase_(delivery_phase)
310 { }
311
337 void registerConsumerHandler(const SpartaHandler & handler)
338 {
340 "Only one handler/callback is supported on this port: " << getName() <<
341 " \n\tCurrent registered handler: " << explicit_consumer_handler_.getName() <<
342 " \n\tTrying to register: " << handler.getName());
343
345
346 // Let subclasses check out the handler...
348 }
349
367 {
369 "You cannot register a consumer on an OUT port -- that doesn't make sense: "
370 << getName() << " consumer being registered: " << consumer.Scheduleable::getLabel());
371 // sparta_assert(getPhase() < TREE_FINALIZED);
372 sparta_assert(isBound() == false,
373 "You cannot register a consuming event after the port is bound. \n\tPort: '"
374 << getName() << "' Event: '" << consumer.Scheduleable::getLabel() << "'"
375 << "\n\tIf this is happening from sparta::Unit auto-precedence, set this Port's "
376 << "\n\tauto-precedence rule to false by calling the Port's method participateInAutoPrecedence(false)");
377
378 port_consumers_.push_back(&consumer);
379 }
380
386 void bind(Port * out) override
387 {
388 if(out->getDirection() != Direction::OUT) {
389 throw SpartaException("ERROR: Attempt to bind an inny: '" + out->getName() +
390 "' to an inny: '" + getName() + "'");
391 }
392
393 // Make the OutPort do all the work including calling
394 // bind_() further down...
395 out->bind(this);
396 }
397
406 return port_consumers_;
407 }
408
414
421 void precedes(InPort & consumer)
422 {
423 sparta_assert(getScheduleable_().getSchedulingPhase() == consumer.getScheduleable_().getSchedulingPhase(),
424 "ERROR: You cannot set precedence between two Ports on different phases: "
425 "producer: " << getLocation() << " consumer: " << consumer.getLocation());
427 }
428
429 protected:
430
433
435 friend OutPort;
436
440
442 virtual void registerConsumerHandler_(const sparta::SpartaHandler &) {}
443
446 virtual void setProducerPrecedence_(Scheduleable * producer) {
447 (void) producer;
448 };
449
451 virtual void bind_(Port * outp) {
452 bound_ports_.push_back(outp);
453 }
454
457 {
458 (void) user_callback_phase;
459 sparta_assert(user_callback_phase >= scheduler_->getCurrentSchedulingPhase(),
460 "\n\n\tThe currently firing event: '" <<
462 "' is in SchedulingPhase::" << scheduler_->getCurrentSchedulingPhase() <<
463 "\n\tand is driving an OutPort that's connected to a zero-cycle Inport: " << getLocation() <<
464 "\n\tUnfortunately, this InPort's registered handler '" <<
465 explicit_consumer_handler_.getName() << "' is in phase SchedulingPhase::" << user_callback_phase <<
466 ".\n\n\tThis won't work for a for a zero-cycle out_port->in_port send (where send delay == 0) "
467 "since an event on a higher phase cannot schedule an event on a lower phase within the same cycle."
468 "\n\n\tTo fix this, in the constructor of InPort '" << getName() <<
469 "' move the registered handler to at least 'sparta::SchedulingPhase::" <<
470 scheduler_->getCurrentSchedulingPhase() << "' or a later phase.\n\t\tExample: " <<
471 getName() << "(..., sparta::SchedulingPhase::" <<
473 "\tOR you add a cycle delay to the InPort '" << getName() << "' via its last construction argument.\n\n");
474 }
475
479
482
484 const Clock * receiver_clock_ = nullptr;
485
488 };
489
492 class OutPort : public Port
493 {
494 public:
507 OutPort(TreeNode* portset, const std::string & name,
508 bool presume_zero_delay) :
509 Port(portset, Port::Direction::OUT, name),
510 presume_zero_delay_(presume_zero_delay)
511 { }
512
529 {
530 sparta_assert(isBound() == false,
531 "You cannot register a producing event after the port is bound. \n\tPort: '"
532 << getName() << "' Event: '" << producer.Scheduleable::getLabel() << "'"
533 << "\n\tIf this is happening from sparta::Unit auto-precedence, set this Port's "
534 << "\n\tauto-precedence rule to false by calling the Port's method participateInAutoPrecedence(false)");
535
536 port_producers_.push_back(&producer);
537
538 // Let derived classes know about it for precedence
539 this->registerProducingEvent_(producer);
540 }
541
557 {
558 sparta_assert(isBound() == false,
559 "You cannot register a producing port after the port is bound. \n\tOutPort: '"
560 << getName() << "' InPort: '" << producer.getLocation() << "'"
561 << "\n\tIf this is happening from sparta::Unit auto-precedence, set this Port's "
562 << "\n\tauto-precedence rule to false by calling the Port's method participateInAutoPrecedence(false)");
563
564 port_producers_.push_back(&producer.getScheduleable_());
565
566 // Let derived classes know about it for precedence
568 }
569
575 void bind(Port * in) override
576 {
577 if(in->getDirection() != Port::Direction::IN) {
578 throw SpartaException("ERROR: Attempt to bind an outty: '" + in->getName() +
579 "' to an outty: '" + getName() + "'");
580 }
581
582 if(!sync_port_) {
583 sparta_assert(in->getClock()->getFrequencyMhz() == getClock()->getFrequencyMhz(),
584 "Trying to bind two ports that are on different clocks with different freq. "
585 "Recommend using SyncPorts");
586 }
587
589 "Port: '" << getLocation()
590 << "' is already bound to '" << in->getLocation() << "' ");
591
592 InPort * inp = nullptr;
593 if((inp = dynamic_cast<InPort *>(in)) == 0) {
594 throw SpartaException("ERROR: Could not cast '" +
595 in->getName() + "' to an InPort for some reason...");
596 }
597
598 // This outport precedes the newly binded in port if the
599 // delay of the inport is zero and the modeler intends to
600 // use the OutPort as zero delay.
601 //
602 // The precedence that's established is the following, but
603 // only if the producer, the inport's internal events, and
604 // the consumer event are all in the same phase.
605 //
606 // ,-----------------------------------------------.
607 // | V
608 // producer -> [ inport internal delivery ]* -> consumer
609 // [ inport handler delivery (optional) ]
610 //
611 //
612 // * The inport -> consumer precedence is established
613 // during the consumer registration from a call to
614 // InPort::registerConsumerEvent()
615 //
616 if((inp->getPortDelay() == 0) && presume_zero_delay_) {
617 for(auto & pd : port_producers_)
618 {
619 for(auto & cons : inp->port_consumers_) {
620 sparta_assert(pd != cons,
621 "Somehow, someway, '" << pd->getLabel() << "' is registered "
622 << "as a producer of Port: '" << getLocation()
623 << "' and, at the same time, a consumer of Port: '"
624 << inp->getLocation() << "'");
625 if(pd->getSchedulingPhase() == cons->getSchedulingPhase()) {
626 std::string reason = "Port::bind(" + getLocation() + "->" + inp->getLocation() + ")";
627 pd->precedes(cons, reason);
628 }
629 }
630 inp->setProducerPrecedence_(pd);
631 }
632 }
633 bound_ports_.push_back(inp);
635 inp->bind_(this);
636 }
637
638 protected:
639
643
648
651
653 bool sync_port_ = false;
654 };
655
657 // Binding methods
659
667 inline void bind(Port * p1, Port * p2) {
668 sparta_assert(p1 != nullptr);
669 sparta_assert(p2 != nullptr);
670 p1->bind(p2);
671 }
672
680 inline void bind(Port & p1, Port & p2) {
681 bind(&p1, &p2);
682 }
683
691 inline void bind(Port * p1, Port & p2) {
692 sparta_assert(p1 != nullptr);
693 bind(p1, &p2);
694 }
695
703 inline void bind(Port & p1, Port * p2) {
704 sparta_assert(p2 != nullptr);
705 bind(&p1, p2);
706 }
707}
File that defines the Clock class.
File that defines the Event class.
File that defines the Scheduleable class.
A simple time-based, event precedence based scheduler.
File that defines the phases used in simulation.
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.
Exception class for all of Sparta.
File that contains the macro used to generate the class callbacks.
Basic Node framework in sparta device tree composite pattern.
A representation of simulated time.
Definition Clock.hpp:51
double getFrequencyMhz() const
Get the clock frequency.
Definition Clock.hpp:115
Used to set precedence between Scheduleable types across simulation.
Base class for all InPort types.
Definition Port.hpp:297
Scheduler * scheduler_
The scheduler used.
Definition Port.hpp:481
friend InPort & operator>>(const GlobalOrderingPoint &, InPort &)
Methods used for precedence have access to the internal scheduleable.
void registerConsumerHandler(const SpartaHandler &handler)
Register a handler to this Port (must be Direction::IN) to handle data arrival.
Definition Port.hpp:337
void bind(Port *out) override
Bind to an OutPort.
Definition Port.hpp:386
void registerConsumerEvent(Scheduleable &consumer)
Add an event "listener" to this port.
Definition Port.hpp:366
void checkSchedulerPhaseForZeroCycleDelivery_(const sparta::SchedulingPhase &user_callback_phase)
Common method for checking phasing.
Definition Port.hpp:456
virtual void setProducerPrecedence_(Scheduleable *producer)
Definition Port.hpp:446
void precedes(InPort &consumer)
Ensure data entering this Port is handled before data on another.
Definition Port.hpp:421
sparta::SchedulingPhase getDeliverySchedulingPhase() const
Definition Port.hpp:411
virtual void bind_(Port *outp)
Called by the OutPort, remember the binding.
Definition Port.hpp:451
virtual void registerConsumerHandler_(const sparta::SpartaHandler &)
Let derived classes look over the registered consumer handler.
Definition Port.hpp:442
ScheduleableList port_consumers_
Definition Port.hpp:478
const Clock * receiver_clock_
The receiving clock.
Definition Port.hpp:484
friend OutPort
The OutPort will call bind_ and setProducerPrecedence_.
Definition Port.hpp:435
virtual Scheduleable & getScheduleable_()=0
Return the internally used Scheduleable for precedence.
const ScheduleableList & getPortTickConsumers() const
Get the list of port tick consumers.
Definition Port.hpp:405
InPort(TreeNode *portset, const std::string &name, sparta::SchedulingPhase delivery_phase)
Base class for all InPort types (sparta::DataInPort, sparta::SignalInPort, sparta::SyncInPort)
Definition Port.hpp:306
sparta::SchedulingPhase delivery_phase_
The delivery phase of this InPort.
Definition Port.hpp:487
Base class for all OutPort types.
Definition Port.hpp:493
virtual void registerProducingEvent_(Scheduleable &)
Definition Port.hpp:642
void bind(Port *in) override
Bind to an InPort.
Definition Port.hpp:575
bool presume_zero_delay_
Presume that data sent on this OutPort to be zero-delay.
Definition Port.hpp:650
OutPort(TreeNode *portset, const std::string &name, bool presume_zero_delay)
Base class for all OutPort types (sparta::DataOutPort, sparta::SignalOutPort, sparta::SyncOutPort)
Definition Port.hpp:507
void registerProducingEvent(Scheduleable &producer)
Add an event "producer" to this port.
Definition Port.hpp:528
ScheduleableList port_producers_
Definition Port.hpp:647
void registerProducingPort(InPort &producer)
Add an InPort "producer" to this OutPort.
Definition Port.hpp:556
bool sync_port_
Is this port a syncport?
Definition Port.hpp:653
The port interface used to bind port types together and defines a port behavior.
Definition Port.hpp:59
Port(TreeNode *portset, Direction dir, const std::string &name)
Construct a Port.
Definition Port.hpp:89
void bind(Port &port)
Method to bind this Port to another, reference style.
Definition Port.hpp:118
virtual void participateInAutoPrecedence(bool participate)
Turn on/off auto precedence for this port.
Definition Port.hpp:161
size_t getNumBoundPorts() const
How many ports are bound to this port?
Definition Port.hpp:134
bool isAlreadyBound(const Port *pt)
See if the given port is already bound.
Definition Port.hpp:233
std::string stringize(bool x) const override
Stringize the Port.
Definition Port.hpp:177
const std::string name_
The name of this port.
Definition Port.hpp:282
bool continuing_
Definition Port.hpp:279
virtual void setPortDelay(double)
Add a double version for SyncPort.h.
Definition Port.hpp:203
std::vector< Port * > bound_ports_
List of bound ports.
Definition Port.hpp:285
virtual Clock::Cycle getPortDelay() const
Get this Port's static delay.
Definition Port.hpp:208
std::vector< Scheduleable * > ScheduleableList
A convenience typedef.
Definition Port.hpp:62
virtual ~Port()
Destructor.
Definition Port.hpp:104
virtual bool isBound() const
Is this port bound to another port?
Definition Port.hpp:126
virtual void bind(Port *port)=0
Method to bind this Port to another, pointer style.
Direction
The direction of this port.
Definition Port.hpp:68
SpartaHandler explicit_consumer_handler_
Explicit consumer handler registered via registerConsumerHandler.
Definition Port.hpp:288
virtual void enableCollection(TreeNode *)
Enable collection on the port.
Definition Port.hpp:214
virtual bool isDriven(Clock::Cycle rel_cycle) const
Determine if this Port (out or in) is driven on the given cycle.
Definition Port.hpp:243
virtual void setPortDelay(sparta::Clock::Cycle)
Set the delay for a port.
Definition Port.hpp:198
virtual bool isDriven() const
Is this Port driven at all?
Definition Port.hpp:253
virtual bool doesParticipateInAutoPrecedence() const
Does this Port participate in auto-precedence establishment by sparta::Unit?
Definition Port.hpp:168
virtual Direction getDirection() const
The direction of the port.
Definition Port.hpp:142
virtual void setContinuing(bool continuing)
Definition Port.hpp:218
A class that defines the basic scheduling interface to the Scheduler. Not intended to be used by mode...
const char * getLabel() const
Get the internal label.
void precedes(Scheduleable &consumer, const std::string &reason="")
Have this Scheduleable precede another.
SchedulingPhase getSchedulingPhase() const
Get the internal phase number.
A class that lets you schedule events now and in the future.
SchedulingPhase getCurrentSchedulingPhase() const
const Scheduleable * getCurrentFiringEvent() const
Used to construct and throw a standard C++ exception. Inherits from std::exception.
Node in a composite tree representing a sparta Tree item.
Definition TreeNode.hpp:205
static const group_idx_type GROUP_IDX_NONE
GroupIndex indicating that a node has no group index because it belongs to no group.
Definition TreeNode.hpp:303
std::string getLocation() const override final
static constexpr char GROUP_NAME_NONE[]
Group name indicating that a node belongs to no group.
Definition TreeNode.hpp:314
TreeNode()=delete
Not default-constructable.
void addChild(TreeNode *child, bool inherit_phase=true)
Adds a TreeNode to this node as a child.
const Clock * getClock() override
Walks up parents (starting with self) until a parent with an associated local clock is found,...
const std::string & getName() const override
Gets the name of this node.
void setExpectedParent_(const TreeNode *parent)
Tracks a node as an expected parent without actually adding this node as a child. This is used almost...
Macros for handling exponential backoff.
void bind(Bus *p1, Bus *p2)
Bind two buses together.
Definition Bus.hpp:333
SchedulingPhase
The SchedulingPhases used for events (Tick, Update, PortUpdate, etc)