Skip to Main Content Skip to Search
Product Documentation

Building and Downloading the Model to a Multicore Target

Generating Code

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:

/*
 * 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:

The coder makes sure that the data transfer between concurrently executing tasks behave as described Data Transfer Options.

Generate Code to Share Data in a Concurrent Environment

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:

  1. 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.

  2. 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);
 …
}

Generate Code for Maximum Capacity During Data Transfer

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;

  …
}

Generated Code for Maximum Data Integrity During Data Transfer

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);
}

Threading APIs

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.

Aspect of Concurrent ExecutionLinux ImplementationWindows ImplementationMac 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

APILinux ImplementationWindows ImplementationMac OS Implementation
Data protection API
  • pthread_mutex_init

  • pthread_mutex_destroy

  • pthread_mutex_lock

  • pthread_mutex_unlock

  • CreateMutex

  • CloseHandle

  • WaitForSingleObject

  • ReleaseMutex

  • pthread_mutex_init

  • pthread_mutex_destroy

  • pthread_mutex_lock

  • pthread_mutex_unlock

Synchronization API
  • sem_init

  • sem_destroy

  • sem_wait

  • sem_post

  • CreateSemaphore

  • CloseHandle

  • WaitForSingleObject

  • ReleaseSemaphore

  • sem_open

  • sem_unlink

  • sem_wait

  • sem_post

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 ();
}

Configuring Embedded Coder for Native Threads Example

To generate code for an Embedded Coder target, in the Configuration Execution dialog box:

Build and Download

You can build and download concurrent execution models for the following targets using system target files:

You cannot generate code for C++ (Encapsulated) language option.

Profile and Evaluate

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).

  


Related Products & Applications

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