Real-Time Workshop | ![]() ![]() |
Model Execution
Before looking at the two styles of generated code, you need to have a high-level understanding of how the generated model code is executed. Real-Time Workshop generates algorithmic code as defined by your model. You may include your own code into your model via S-functions. S-functions can range from high-level signal manipulation algorithms to low-level device drivers.
Real-Time Workshop also provides a run-time interface that executes the generated model code. The run-time interface and model code are compiled together to create the model executable. The diagram below shows a high-level object-oriented view of the executable.
Figure 7-1: The Object-Oriented View of a Real-Time Program
In general, the conceptual design of the model execution driver does not change between the rapid prototyping and embedded style of generated code. The following sections describe model execution for singletasking and multitasking environments both for simulation (nonreal-time) and for real-time. For most models, the multitasking environment will provide the most efficient model execution (i.e., fastest sample rate).
The following concepts are useful in describing how models execute:
Initialization
-- Initializing the run-time interface code and the model code.
ModelOutputs
-- Calling all blocks in your model that have a time hit at the current point in time and having them produce their output. ModelOutputs
can be done in major or minor time steps. In major time steps, the output is a given simulation time step. In minor time steps, the run-time interface integrates the derivatives to update the continuous states.
ModelUpdate
-- Calling all blocks in your model that have a sample hit at the current point in time and having them update their discrete states or similar type objects.
ModelDerivatives
-- Calling all blocks in your model that have continuous states and having them update their derivatives. ModelDerivatives
is only called in minor time steps.
The pseudocode below shows the execution of a model for a singletasking simulation (nonreal-time).
main() { Initialization While (time < final time) ModelOutputs -- Major time step. LogTXY -- Log time, states and root outports. ModelUpdate -- Major time step. Integrate: -- Integration in minor time step for -- models with continuous states. ModelDerivatives Do 0 or more: ModelOutputs ModelDerivatives EndDo (Number of iterations depends upon the solver.) Integrate derivatives to update continuous states. EndIntegrate EndWhile Shutdown }
The initialization phase begins first. This consists of initializing model states and setting up the execution engine. The model then executes, one step at a time. First ModelOutputs
executes at time t, then the workspace I/O data is logged, and then ModelUpdate
updates the discrete states. Next, if your model has any continuous states, ModelDerivatives
integrates the continuous states' derivatives to generate the states for time , where h is the step size. Time then moves forward to
and the process repeats.
During the ModelOutputs
and ModelUpdate
phases of model execution, only blocks that have hit the current point in time execute. They determine if they have hit by using a macro (ssIsSampleHit
, or ssIsSpecialSampleHit
) that checks for a sample hit.
The pseudocode below shows the execution of a model for a multitasking simulation (nonreal-time).
main() { Initialization While (time < final time) ModelOutputs(tid=0) -- Major time step. LogTXY -- Log time, states, and root -- outports. ModelUpdate(tid=1) -- Major time step. Integrate -- Integration in minor time step for -- models with continuous states. ModelDerivatives Do 0 or more: ModelOutputs(tid=0) ModelDerivatives EndDo (Number of iterations depends upon the solver.) Integrate derivatives to update continuous states. EndIntegrate For i=1:NumTids ModelOutputs(tid=i) -- Major time step. ModelUpdate(tid=i) -- Major time step. EndFor EndWhile Shutdown }
The multitasking operation is more complex when compared with the singletasking execution because the output and update functions are subdivided by the task identifier (tid
) that is passed into these functions. This allows for multiple invocations of these functions with different task identifiers using overlapped interrupts, or for multiple tasks when using a real-time operating system. In simulation, multiple tasks are emulated by executing the code in the order that would occur if there were no preemption in a real-time system.
Note that the multitasking execution assumes that all tasks are multiples of the base rate. Simulink enforces this when you have created a fixed-step multitasking model.
The multitasking execution loop is very similar to that of singletasking, except for the use of the task identifier (tid
) argument to ModelOutputs
and ModelUpdate
. The ssIsSampleHit
or ssIsSpecialSampleHit
macros use the tid
to determine when blocks have a hit. For example, ModelOutputs (tid=5)
will execute only the blocks that have a sample time corresponding to task identifier 5.
The pseudocode below shows the execution of a model in a real-time singletasking system where the model is run at interrupt level.
rtOneStep() { Check for interrupt overflow Enable "rtOneStep" interrupt ModelOutputs -- Major time step. LogTXY -- Log time, states and root outports. ModelUpdate -- Major time step. Integrate -- Integration in minor time step for models -- with continuous states. ModelDerivatives Do 0 or more ModelOutputs ModelDerivatives EndDo (Number of iterations depends upon the solver.) Integrate derivatives to update continuous states. EndIntegrate } main() { Initialization (including installation of rtOneStep as an interrupt service routine, ISR, for a real-time clock). While(time < final time) Background task. EndWhile Mask interrupts (Disable rtOneStep from executing.) Complete any background tasks. Shutdown }
Real-time singletasking execution is very similar to the nonreal-time single tasking execution, except that the execution of the model code is done at interrupt level.
At the interval specified by the program's base sample rate, the interrupt service routine (ISR) preempts the background task to execute the model code. The base sample rate is the fastest rate in the model. If the model has continuous blocks, then the integration step size determines the base sample rate.
For example, if the model code is a controller operating at 100 Hz, then every 0.01 seconds the background task is interrupted. During this interrupt, the controller reads its inputs from the analog-to-digital converter (ADC), calculates its outputs, writes these outputs to the digital-to-analog converter (DAC), and updates its states. Program control then returns to the background task. All of these steps must occur before the next interrupt.
The following pseudocode shows how a model executes in a real-time multitasking system (where the model is run at interrupt level).
rtOneStep() { Check for interrupt overflow Enable "rtOneStep" interrupt ModelOutputs(tid=0) -- Major time step. LogTXY -- Log time, states and root outports. ModelUpdate(tid=0) -- Major time step. Integrate -- Integration in minor time step for -- models with continuous states. ModelDerivatives Do 0 or more: ModelOutputs(tid=0) ModelDerivatives EndDo (Number of iterations depends upon the solver.) Integrate derivatives and update continuous states. EndIntegrate For i=1:NumTasks If (hit in task i) ModelOutputs(tid=i) ModelUpdate(tid=i) EndIf EndFor } main() { Initialization (including installation of rtOneStep as an interrupt service routine, ISR, for a real-time clock). While(time < final time) Background task. EndWhile Mask interrupts (Disable rtOneStep from executing.) Complete any background tasks. Shutdown }
Running models at interrupt level in real-time multitasking environment is very similar to the previous singletasking environment, except that overlapped interrupts are employed for concurrent execution of the tasks.
The execution of a model in a singletasking or multitasking environment when using real-time operating system tasking primitives is very similar to the interrupt-level examples discussed above. The pseudocode below is for a singletasking model using real-time tasking primitives.
tSingleRate() { MainLoop: If clockSem already "given", then error out due to overflow. Wait on clockSem ModelOutputs -- Major time step. LogTXY -- Log time, states and root --outports ModelUpdate -- Major time step Integrate -- Integration in minor time step -- for models with continuous -- states. ModelDeriviatives Do 0 or more: ModelOutputs ModelDerivatives EndDo (Number of iterations depends upon the solver.) Integrate derivatives to update continuous states. EndIntegrate EndMainLoop } main() { Initialization Start/spawn task "tSingleRate". Start clock that does a "semGive" on a clockSem semaphore. Wait on "model-running" semaphore. Shutdown }
In this singletasking environment, the model is executed using real-time operating system tasking primitives. In this environment, we create a single task (tSingleRate
) to run the model code. This task is invoked when a clock tick occurs. The clock tick gives a clockSem
(clock semaphore) to the model task (tSingleRate
). The model task will wait for the semaphore before executing. The clock ticks are configured to occur at the fundamental step size (base rate) for your model.
The pseudocode below is for a multitasking model using real-time tasking primitives.
tSubRate(subTaskSem,i) { Loop: Wait on semaphore subTaskSem. ModelOutputs(tid=i) ModelUpdate(tid=i) EndLoop } tBaseRate() { MainLoop: If clockSem already "given", then error out due to overflow. Wait on clockSem For i=1:NumTasks If (hit in task i) If task i is currently executing, then error out due to overflow. Do a "semGive" on subTaskSem for task i. EndIf EndFor ModelOutputs(tid=0) -- major time step. LogTXY -- Log time, states and root outports. ModelUpdate(tid=0) -- major time step. Loop: -- Integration in minor time step for -- models with continuous states. ModelDeriviatives Do 0 or more: ModelOutputs(tid=0) ModelDerivatives EndDo (number of iterations depends upon the solver). Integrate derivatives to update continuous states. EndLoop EndMainLoop } main() { Initialization Start/spawn task "tSubRate". Start/spawn task "tBaseRate". Start clock that does a "semGive" on a clockSem semaphore. Wait on "model-running" semaphore. Shutdown }
In this multitasking environment, the model is executed using real-time operating system tasking primitives. In this environment, it is necessary to create several model tasks (tBaseRate
and several tSubRate
tasks) to run the model code. The base rate task (tBaseRate
) has a higher priority than the subrate tasks. The subrate task for tid=1
has a higher priority than the subrate task for tid=2
, and so on. The base rate task is invoked when a clock tick occurs. The clock tick gives a clockSem
to tBaseRate
. The first thing tBaseRate
does is give semaphores to the subtasks that have a hit at the current point in time. Since the base rate task has a higher priority, it continues to execute. Next it executes the fastest task (tid=0
) consisting of blocks in your model that have the fastest sample time. After this execution, it resumes waiting for the clock semaphore. The clock ticks are configured to occur at executing at the fundamental step size for your model.
![]() | Introduction | Program Timing | ![]() |