| Contents | Index |
| On this page… |
|---|
To generate code, in the Simulink editor window, select Tools > Code Generation > Build Model. This code can run concurrently on a multicore target with Simulink Coder or Embedded Coder software. The coder generates all target-dependent code for thread creation, thread synchronization, interrupt service routines, and signal handlers and data transfer. The source files of the mapped model (for example, model.c and model.h) do not contain target-dependent code for setting up the execution of threads. However, target-dependent code might exist for data transfer types, which generate target-specific data protection or semaphore application programming interface (API) calls.
For each periodic task, Simulink Coder combines the output and update methods of the blocks mapped to that task and binds these methods to a target-specific thread. Additionally, for each periodic task that contains continuous states, a separate solver instance is generated in the source file of the mapped model. This mapped model can concurrently run the continuous parts of the model. (The generated solver instances share the solver type specified in the Solver of the Configuration Parameters dialog box of the mapped model.)
For each aperiodic task or aperiodic trigger, Simulink Coder combines the output and update methods of the blocks mapped to that task and binds these to a target-specific Interrupt Service Routine (ISR) or to a target-specific event handler.
Following is an example of generated code in the source file of a mapped model. This code shows the functions generated for the mapped model:
Two solver instances
Three periodic tasks with two of them having continuous states
Two aperiodic tasks
Two aperiodic triggers
/*
* This function updates continuous states using the ODE3 fixed-step
* solver algorithm
*/
static void Periodic_Task1_rt_ertODEUpdateContinuousStates(RTWSolverInfo *si )
{ … }
/*
* This function updates continuous states using the ODE3 fixed-step
* solver algorithm
*/
static void Periodic_Task2_rt_ertODEUpdateContinuousStates(RTWSolverInfo *si )
{ … }
/* OutputUpdate for Task: Periodic_Task1 */
void Periodic_Task1_step(void) /* Sample time: [0.1s, 0.0s] */
{ …
Periodic_Task1_rt_ertODEUpdateContinuousStates(&task_M[0]->solverInfo);
…
}
/* OutputUpdate for Task: Periodic_Task2 */
void Periodic_Task2_step(void) /* Sample time: [0.2s, 0.0s] */
{ …
Periodic_Task2_rt_ertODEUpdateContinuousStates(&task_M[1]->solverInfo);
…
}
/* OutputUpdate for Task: Periodic_Task3 */
void Periodic_Task3_step(void) /* Sample time: [0.1s, 0.0s] */
{ … }
/* OutputUpdate for Task: Periodic_Task4 */
void Periodic_Task4_step(void) /* Sample time: [0.2s, 0.0s] */
{ … }
/* OutputUpdate for Task:Interrupt1_asyncTask1 */
void Interrupt1_asyncTask1(void)
{ … }
/* OutputUpdate for Task:Interrupt2_asyncTask2 */
void Interrupt2_asyncTask2(void)
{ … }
/* OutputUpdate for Task:Interrupt3 */
void Interrupt3(void)
{ … }
/* OutputUpdate for Task:Interrupt4 */
void Interrupt4(void)
{ … }
In addition, for continuous signals, the coder produces code for the extrapolation method that you select. There are four types of extrapolation methods: zero-order hold, linear, quadratic, and none. None is an ideal case when the two communicating tasks synchronize in minor steps.
The following code snippets contain the generated task functions in the source file of the mapped model. The sldemo_concurrent_execution model has been modified so that:
ControllerA/u1 uses Ensure data Integrity only option in the Signal Properties dialog box.
Plant is remapped to a new task that executes concurrently with Compensator. The data transfer from Plant to Compensator is Ensure deterministic transfer (minimum delay).
The coder makes sure that the data transfer between concurrently executing tasks behave as described Data Transfer Options.
For Ensure data integrity only, the generated code allows data to be shared in a concurrent environment. This is achieved with a double-buffer algorithm (for same rate data transfer) and a triple buffer algorithm (for rate-transition data transfer) and target-specific protection code for the buffer flag management. The following example shows code generated using the sldemo_concurrent_execution model:
Open the Signal Properties dialog box for ControllerA/u1 and select Data Integrity Only as the data transfer handling option from the Data Transfer pane.
To generate code, select Tools > Code Generation > Build Model.
The coder generates code like the following. This code is for ControllerA/u1 on the Linux platform.
/* Output for Task: Periodic_ControllerA */
void Periodic_ControllerA_output(void) /* Sample time: [0.1s, 0.0s] */
{
…
/* TaskTransBlk: '<Root>/TaskTransAtController AOut1' */
sldemo_concurrent_execution_DWork.
TaskTransAtControllerAOut1_buf_3[sldemo_concurrent_execution_DWork.fw_buf_3]
= sldemo_concurrent_execution_B.ControllerA;
rtw_pthread_mutex_lock(sldemo_concurrent_execution_DWork.mw_buf_3);
sldemo_concurrent_execution_DWork.fw_buf_3 = 1 -
sldemo_concurrent_execution_DWork.fw_buf_3;
rtw_pthread_mutex_unlock(sldemo_concurrent_execution_DWork.mw_buf_3);
}
/* Output for Task: Periodic_Plant */
void Periodic_Plant_output(void) /* Sample time: [0.0s, 0.0s] */
{
…
if (rtmIsMajorTimeStep(task_M[2])) {
/* TaskTransBlk: '<Root>/TaskTransAtPlantIn1' */
rtw_pthread_mutex_lock(sldemo_concurrent_execution_DWork.mw_buf_3);
wrBufIdx = 1 - sldemo_concurrent_execution_DWork.fw_buf_3;
rtw_pthread_mutex_unlock(sldemo_concurrent_execution_DWork.mw_buf_3);
sldemo_concurrent_execution_B.TaskTransAtPlantIn1 =
sldemo_concurrent_execution_DWork.TaskTransAtControllerAOut1_buf_3[wrBufIdx];
…
}
…
}
void MdlInitialize(void)
{
…
sldemo_concurrent_execution_DWork.fw_buf_3 = 0;
rtw_pthread_mutex_init(&sldemo_concurrent_execution_DWork.mw_buf_3);
…
}
For Ensure deterministic transfer (maximum delay), the generated code for the task transition is ANSI® C code with writing and reading to and from double buffers. In the Signal Properties dialog box, you provide a value for the Initial Condition value that initializes the buffer used for the first read operation.
The software generates code like the following. This example is for ControllerB/u1 on the Linux platform.
/* Output for Task: Periodic_ControllerB */
void Periodic_ControllerB_output(void) /* Sample time: [0.1s, 0.0s] */
{
…
sldemo_concurrent_execution_DWork.
TaskTransAtControllerBOut1_buf_4[sldemo_concurrent_execution_DWork.fw_buf_4]
= sldemo_concurrent_execution_B.ControllerB;
sldemo_concurrent_execution_DWork.fw_buf_4 = 1 -
sldemo_concurrent_execution_DWork.fw_buf_4;
}
/* Output for Task: Periodic_Plant */
void Periodic_Plant_output(void) /* Sample time: [0.0s, 0.0s] */
{
…
if (rtmIsMajorTimeStep(task_M[2])) {
…
/* TaskTransBlk: '<Root>/TaskTransAtPlantIn2' */
sldemo_concurrent_execution_B.TaskTransAtPlantIn2 =
sldemo_concurrent_execution_DWork.
TaskTransAtControllerBOut1_buf_4[sldemo_concurrent_execution_DWork.fr_buf_4];
sldemo_concurrent_execution_DWork.fr_buf_4 = 1 -
sldemo_concurrent_execution_DWork.fr_buf_4;
}
…
}
void MdlInitialize(void)
{
…
/* InitializeConditions for TaskTransBlk: '<Root>/TaskTransAtController BOut1' */
sldemo_concurrent_execution_DWork.fw_buf_4 = 0;
…
}
For Ensure deterministic transfer (minimum delay), the generated code for the data transfer makes sure that the task reading the data is waiting for the task writing the data to complete its computation of the data. This behavior produces target-specific code for data synchronization (for example, semaphores).
The software generates code like the following. This example code is for Plant/x on the Linux platform.
/*
* This function updates continuous states using the ODE3 fixed-step
* solver algorithm
*/
static void Periodic_Compensator_rt_ertODEUpdateContinuousStates(RTWSolverInfo
*si )
{
…
/* TaskTransBlk: '<Root>/TaskTransAtCompensatorIn1' */
if (sldemo_concurrent_execution_DWork.br_buf_5) {
sldemo_concurrent_execution_DWork.br_buf_5 = FALSE;
} else {
rtw_pthread_sem_wait(sldemo_concurrent_execution_DWork.sw_buf_5);
sldemo_concurrent_execution_B.TaskTransAtCompensatorIn1 =
sldemo_concurrent_execution_DWork.TaskTransAtPlantOut1_buf_5;
rtw_pthread_sem_post(sldemo_concurrent_execution_DWork.sr_buf_5);
if (rtmIsMajorTimeStep(task_M[0])) {
sldemo_concurrent_execution_DWork.br_buf_5 = TRUE;
}
}
/* End of TaskTransBlk: '<Root>/TaskTransAtCompensatorIn1' */
…
}
/* Output for Task: Periodic_Plant */
void Periodic_Plant_output(void) /* Sample time: [0.0s, 0.0s] */
{
…
if (rtmIsMajorTimeStep(task_M[3])) {
…
/* TaskTransBlk: '<Root>/TaskTransAtPlantOut1' */
rtw_pthread_sem_wait(sldemo_concurrent_execution_DWork.sr_buf_5);
sldemo_concurrent_execution_DWork.TaskTransAtPlantOut1_buf_5 =
sldemo_concurrent_execution_B.x;
rtw_pthread_sem_post(sldemo_concurrent_execution_DWork.sw_buf_5);
}
void MdlInitialize(void)
{
…
/* InitializeConditions for TaskTransBlk: '<Root>/TaskTransAtPlantOut1' */
sldemo_concurrent_execution_DWork.fw_buf_5 = 0;
rtw_pthread_sem_create(&sldemo_concurrent_execution_DWork.sw_buf_5, 0);
}
For Embedded Coder targets and generic real-time targets, the generated code from a mapped model creates a thread for each task and automatically leverages the threading APIs supported by the operating system running on the host machine.
If the host machine is a Windows platform, the generated code will create and run Windows threads.
If the host machine is Linux or Mac OS platform, the generated code will create and run POSIX pthreads.
| Aspect of Concurrent Execution | Linux Implementation | Windows Implementation | Mac OS Implementation |
|---|---|---|---|
Periodic triggering event | POSIX timer | Windows timer | Not applicable |
Aperiodic triggering event | POSIX real-time signal | Windows event | POSIX non-real-time signal |
Aperiodic trigger | For blocks mapped to an aperiodic task: thread waiting for a signal For blocks mapped to an aperiodic trigger: signal action | Thread waiting for an event | For blocks mapped to an aperiodic task: thread waiting for a signal For blocks mapped to an aperiodic trigger: signal action |
Threads | POSIX | Windows | POSIX |
Threads priority | Assigned based on sample time: fastest task has highest priority | Priority class inherited from the parent process. Assigned based on sample time: fastest task has highest priority for the first three fastest tasks. The rest of the tasks share the lowest priority. | Assigned based on sample time: fastest task has highest priority |
Example of overrun detection | Yes | Yes | No |
The data transfer between concurrently executing tasks behave as described in Data Transfer Options. The coders use the following APIs on supported targets for this behavior:
Data Protection and Synchronization APIs that Embedded Coder and Generic Real-Time Targets Use
| API | Linux Implementation | Windows Implementation | Mac OS Implementation |
|---|---|---|---|
| Data protection API |
|
|
|
| Synchronization API |
|
|
|
The main() code is always dynamically generated. The general structure of the generated main file is depicted in the following pseudocode:
main()
{
Model_initialize();
For_each_periodic_task
{
Create_synchronization_event(periodic_task);
Create_thread(periodic_task);
}
For_each_aperiodic_task
{
Create_external_event(aperiodic_task);
Create_thread(aperiodic_task);
}
Create_timer();
Create_thread(Periodic_task_scheduler);
For_each_aperiodic_trigger
{
Hookup_isr_to_aperiodic_event();
}
Wait_for_stopping();
Cleanup();
Model_terminate();
return 0;
}
Periodic_task_scheduler()
{
Wait_for_event_from_timer(clock);
For_each_periodic_task
{
If periodic_task has a sample time hit
Set the event to run the task
}
}
Periodic_Task()
{
Wait_for_event(Periodic_task_scheduler);
Run the appropriate periodic_task();
}
Aperiodic_Task()
{
Wait_for_aperiodic_event();
Run the appropriate aperiodic_task();
}
Isr()
{
Run the appropriate aperiodic_trigger ();
}
To generate code for an Embedded Coder target, in the Configuration Execution dialog box:
Select the Code Generation > Templates > Generate an example main program check box.
From the Code Generation > Templates > Target Operating System list, select NativeThreadsExample.

You can build and download concurrent execution models for the following targets using system target files:
Linux, Windows, and Mac OS using ert.tlc and grt.tlc.
xPC Target using xpctarget.tlc and xpctargetert.tlc.
Linux and Windows using idelink_ert.tlc and idelink_grt.tlc.
Embedded Linux and VxWorks using idelink_ert.tlc and idelink_grt.tlc.
You cannot generate code for C++ (Encapsulated) language option.
Profile the execution of your code on the multicore system using a profiling tool like that provided in the xPC Target software. Profiling lets you identify the areas in your model that are execution bottlenecks. You can analyze the execution time of each task and find the task that takes most of the execution time. For example, if you are using the xPC Target profiling tool, you can compare the average execution times of the tasks. If a task is computation-intensive, or does not satisfy real-time requirements and overruns, you can break it into multiple tasks that are less computation-intensive and that can run concurrently.
Based on this analysis, you can then explicitly change the mapping of Model blocks to efficiently use the concurrency available on your multicore system (see Mapping Blocks to Tasks).
![]() | Interpreting Simulation Results | Concurrent Execution Example | ![]() |

Learn more about Simulink through this collection of videos, articles, technical literature and the Getting Started with Simulink Guide.
| © 1984-2012- The MathWorks, Inc. - Site Help - Patents - Trademarks - Privacy Policy - Preventing Piracy - RSS |