The Sparta Modeling Framework
Loading...
Searching...
No Matches
Best Practices, Suggestions on using Sparta

This page, coupled with the Sparta FAQ can help the modeler develop, debug, contribute and understand how to use Sparta effectively.

Using Sparta Asserts

Sparta provides a nifty error dumping/handling mechanism triggered when a model throws a C++ exception. By default, when the framework encounters a C++ exception, it will generate a file called error-dump.txt. See 3.7 Post-Run Debug dumps for more information.

The make best use of this functionality, consider using sparta_assert whenever possible to catch errors.

Using Sparta compile-time hints

Sparta defines a set of macros called SPARTA_EXPECT_TRUE and SPARTA_EXPECT_FALSE that help tell the compiler expected behavior for a condition. This enables the compiler to put the common execution path inline for best performance. A best practice is to use these macros for logging messages since they are rarely turned on during full simulation.

void MyClass::myBlock()
{
if(SPARTA_EXPECT_FALSE(my_info_logger_))
// if(SPARTA_EXPECT_FALSE(my_info_logger_.observed()) This works too!
{
my_info_logger_ << "myBlock got called";
}
// ....
}
#define SPARTA_EXPECT_FALSE(x)
A macro for hinting to the compiler a particular condition should be considered most likely false.
Message source object associated with a sparta TreeNode through which messages can be sent.

Take Advantage of C++11/17!

Since Sparta requires the use of a C++17 compiler, take advantage of the C++17 features. This can help reduce errors and clean up code immensely. As an example, try to initialized as many components directly in the header file of the class. Look at example/CoreExample/src/Dispatch.hpp in the CoreExample for examples on how to do this.

How to Use the Port/Event Mechansim Effectively

In this section, Sparta phases and general model flow will be discussed/illustrated.

Phases

Communication between resources in a user's model occurs via Ports. In SystemC, Ports are used as a mechanism to exchange interfaces between blocks, but in Sparta, Ports are used to exchange data and signals at given time intervals. This follows a cohesive pattern allowing any component to bind to another as long as it understands the shared data type.

A typical modeled block using Sparta looks like the following, with each phase following the next phase in the order listed.

Phase Intended Use
Construction phase (only once): Construction, ports and events registered
Update phase (runtime): Updating of internal resources (sparta::Pipe for example)
PortUpdate phase (runtime): Port delivery of Data (N-cycle ports)
Flush phase (runtime): Flushing
Collection phase (runtime): Collection Events
Tick phase (runtime): Operating Logic/Events
PostTick phase (runtime): Operating Logic/Events to process after main logic

where "Updating of internal resources" are Event objects scheduled/fired that were created in the sparta::SchedulingPhase::Update phase:

Event is a simple class for scheduling random events on the Scheduler.
Definition Event.hpp:42

"Port delivery of Data" are handlers called that were registered with a Port that has an N-cycle delay:

MyBlock::MyBlock(...) : // ...
{
in_port_.registerConsumerHandler(
CREATE_SPARTA_HANDLER(MyBlock, myPortCallback, ...));
}
// Called directly by the Port when data arrives on the Port in THIS
// cycle, but sent from a previous cycle. In this case, the Port is
// considered N-Cycle and this callback is being called during the
// sparta::SchedulingPhase::PortUpdate phase. If the Port is 0-cycle,
// this callback is called during the sparta::SchedulingPhase::Tick
// phase.
void MyBlock::myPortCallback(...) {
}
#define CREATE_SPARTA_HANDLER(clname, meth)

and "Operating Logic/Events" are those sparta::Event objects that were created in the sparta::SchedulingPhase::Tick phase:

// OR use the default template argument
sparta::Event<> my_main_logic_event_;

Using The Phases

Construction occurs first and only once. During this "phase" the modeler should add sparta::Event, sparta::Port, and sparta::Counter objects to the base class sparta::Unit's sets:

// Construction phase
MyBlock::MyBlock(sparta::TreeNode * node, MyParameters * params) :
sparta::Unit(node),
my_internal_tick_event_(&Unit::unit_event_set_, "my_internal_tick_event",
CREATE_SPARTA_HANDLER(MyBlock, myInternalTickCallback)),
my_other_internal_tick_event_(&Unit::unit_event_set_, "my_other_internal_tick_event",
CREATE_SPARTA_HANDLER(MyBlock, myInternalTickCallbackV2)),
my_inport_(&Unit::unit_port_set_, "my_inport", 1)
{
// Establish internal precedences here that can't be done automatically
my_internal_tick_event_ >> my_other_internal_tick_event_;
// Also, register handlers with any InPorts defined
my_inport_.registerConsumerHandler(CREATE_SPARTA_HANDLER(MyBlock, myCallback));
}
// PortUpdate Phase
void MyBlock::myCallback()
{
// my_inport_ received a message
my_internal_tick_event_.schedule(); // schedule internal tick event for this cycle
}
Node in a composite tree representing a sparta Tree item.
Definition TreeNode.hpp:205
Macros for handling exponential backoff.

The framework will automatically set precedence between Events (sparta::Event) and Ports (sparta::Port) registered in the sparta::Unit EventSet (sparta::EventSet) and PortSet (sparta::PortSet). However, the framework will not set precedence between events defined directly in the block. I.e. if there is an event called my_first_event_ created in the sparta::SchedulingPhase::Tick phase, and another event called my_second_event_ also created in the sparta::SchedulingPhase::Tick phase, the order in which these events are scheduled is random (however, deterministic between simulation runs of the same binary) unless a precedence is established:

  • my_first_event_ >> my_second_event_;

Automatic precedence establishment can be disabled by calling the method sparta::Resource::setAutoPrecedence(false) in the constructor of MyBlock.

After the block is instantiated and the simulator is now running, events will be scheduled based on established operations.

dot_inline_dotgraph_1.png
  • Scheduled UpdatePhase events are run first, allowing a user to update, clear, or simply operate on anything in their block that needs to be ready to accept data from a Port or from a Tick event
  • Scheduled PortUpdate events (for N-cycle Ports) are run next allowing Ports to deliver data to themselves as well as any registered consumer handler. Since resources were updated earlier, they can be updated
  • Scheduled Collection events are now run to collect current state on the resources going into the Tick phase. This is part of the PipelineCollector mechanism and allows a modeler to see what data was operated on THIS cycle.
  • Scheduled Tick events are finally run to operate on data sent from N-Cycle ports or data in internal resources. 0-cycle Ports are also run in this phase delivering data directly to the callback from another block. Any collection required on this type of data must be handled manually. Most of the modeler's code will be in this phase.

Simulation will terminate when there are no more continuing (see sparta::Scheduleable::setContinuing) events scheduled for execution.