| Contents | Index |
By default, the Embedded Coder software generates standalone programs that do not require an external real-time executive or operating system. A standalone program requires minimal modification to be adapted to the target hardware. The standalone program architecture supports execution of models with either single or multiple sample rates.
To generate a standalone program:
In the Custom templates section of the Code Generation > Templates pane of the Configuration Parameters dialog box, select the Generate an example main program option ( is on by default). This enables the Target operating system menu.
From the Target operating system menu, select BareBoardExample (the default selection).
Generate the code.
Different code is generated for multirate models depending on the following factors:
Whether the model executes in single-tasking or multitasking mode.
Whether or not reusable code is being generated.
These factors affect the scheduling algorithms used in generated code, and in some cases affect the API for the model entry point functions. The following sections discuss these variants.
The core of a standalone program is the main loop. On each iteration, the main loop executes a background or null task and checks for a termination condition.
The main loop is periodically interrupted by a timer. The function rt_OneStep is either installed as a timer interrupt service routine (ISR), or called from a timer ISR at each clock step.
The execution driver, rt_OneStep, sequences calls to the model_step functions. The operation of rt_OneStep differs depending on whether the generating model is single-rate or multirate. In a single-rate model, rt_OneStep simply calls the model_step function. In a multirate model, rt_OneStep prioritizes and schedules execution of blocks according to the rates at which they run.
The following pseudocode shows the execution of a main program.
main()
{
Initialization (including installation of rt_OneStep as an
interrupt service routine for a real-time clock)
Initialize and start timer hardware
Enable interupts
While(not Error) and (time < final time)
Background task
EndWhile
Disable interrupts (Disable rt_OneStep from executing)
Complete any background tasks
Shutdown
}The pseudocode is a design for a harness program to drive your model. The ert_main.c or .cpp program only partially implements this design. You must modify it according to your specifications.
This section describes the minimal modifications you should make in your production version of ert_main.c or .cpp to implement your harness program.
Call model_initialize.
Initialize target-specific data structures and hardware, such as ADCs or DACs.
Install rt_OneStep as a timer ISR.
Initialize timer hardware.
Enable timer interrupts and start the timer.
Optionally, insert background task calls in the main loop.
On termination of the main loop (if applicable):
Disable timer interrupts.
Perform target-specific cleanup such as zeroing DACs.
Detect and handle errors. Note that even if your program is designed to run indefinitely, you may need to handle severe error conditions, such as timer interrupt overruns.
You can use the macros rtmGetErrorStatus and rtmSetErrorStatus to detect and signal errors.
The operation of rt_OneStep depends upon
Whether your model is single-rate or multirate. In a single-rate model, the sample times of all blocks in the model, and the model's fixed step size, are the same. Any model in which the sample times and step size do not meet these conditions is termed multirate.
Your model's solver mode (SingleTasking versus MultiTasking)
Permitted Solver Modes for Embedded Coder Targeted Models summarizes the permitted solver modes for single-rate and multirate models. Note that for a single-rate model, only SingleTasking solver mode is allowed.
Permitted Solver Modes for Embedded Coder Targeted Models
| Mode | Single-Rate | Multirate |
|---|---|---|
SingleTasking | Allowed | Allowed |
MultiTasking | Disallowed | Allowed |
Auto | Allowed (defaults to SingleTasking) | Allowed (defaults to MultiTasking) |
The generated code for rt_OneStep (and associated timing data structures and support functions) is tailored to the number of rates in the model and to the solver mode. The following sections discuss each possible case.
The only valid solver mode for a single-rate model is SingleTasking. Such models run in "single-rate" operation.
The following pseudocode shows the design of rt_OneStep in a single-rate program.
rt_OneStep()
{
Check for interrupt overflow or other error
Enable "rt_OneStep" (timer) interrupt
Model_Step() -- Time step combines output,logging,update
}For the single-rate case, the generated model_step function is
void model_step(void)
Single-rate rt_OneStep is designed to execute model_step within a single clock period. To enforce this timing constraint, rt_OneStep maintains and checks a timer overrun flag. On entry, timer interrupts are disabled until the overrun flag and other error conditions have been checked. If the overrun flag is clear, rt_OneStep sets the flag, and proceeds with timer interrupts enabled.
The overrun flag is cleared only upon successful return from model_step. Therefore, if rt_OneStep is reinterrupted before completing model_step, the reinterruption is detected through the overrun flag.
Reinterruption of rt_OneStep by the timer is an error condition. If this condition is detected rt_OneStep signals an error and returns immediately. (Note that you can change this behavior if you want to handle the condition differently.)
Note that the design of rt_OneStep assumes that interrupts are disabled before rt_OneStep is called. rt_OneStep should be noninterruptible until the interrupt overflow flag has been checked.
In a multirate multitasking system, code generation uses a prioritized, preemptive multitasking scheme to execute the different sample rates in your model.
The following pseudocode shows the design of rt_OneStep in a multirate multitasking program.
rt_OneStep()
{
Check for base-rate interrupt overrun
Enable "rt_OneStep" interrupt
Determine which rates need to run this time step
Model_Step0() -- run base-rate time step code
For N=1:NumTasks-1 -- iterate over sub-rate tasks
If (sub-rate task N is scheduled)
Check for sub-rate interrupt overrun
Model_StepN() -- run sub-rate time step code
EndIf
EndFor
}Task Identifiers. The execution of blocks having different sample rates is broken into tasks. Each block that executes at a given sample rate is assigned a task identifier (tid), which associates it with a task that executes at that rate. Where there are NumTasks tasks in the system, the range of task identifiers is 0..NumTasks-1.
Prioritization of Base-Rate and Subrate Tasks. Tasks are prioritized, in descending order, by rate. The base-rate task is the task that runs at the fastest rate in the system (the hardware clock rate). The base-rate task has highest priority (tid 0). The next fastest task (tid 1) has the next highest priority, and so on down to the slowest, lowest priority task (tid NumTasks-1).
The slower tasks, running at submultiples of the base rate, are called subrate tasks.
Rate Grouping and Rate-Specific model_step Functions. In a single-rate model, all block output computations are performed within a single function, model_step. For multirate, multitasking models, the code generator tries to use a different strategy. This strategy is called rate grouping. Rate grouping generates separate model_step functions for the base rate task and each subrate task in the model. The function naming convention for these functions is
model_stepN
where N is a task identifier. For example, for a model named my_model that has three rates, the following functions are generated:
void my_model_step0 (void); void my_model_step1 (void); void my_model_step2 (void);
Each model_stepN function executes all blocks sharing tid N; in other words, all block code that executes within task N is grouped into the associated model_stepN function.
Scheduling model_stepN Execution. On each clock tick, rt_OneStep maintains scheduling counters and event flags for each subrate task. The counters are implemented as taskCounter arrays indexed on tid. The event flags are implemented as arrays indexed on tid.
The scheduling counters and task flags for sub-rates are maintained by rt_OneStep. The scheduling counters are basically clock rate dividers that count up the sample period associated with each sub-rate task. Any pair of tasks that exchanges data maintains an interaction flag at the faster rate. Task interaction flags indicate that both fast and slow tasks are scheduled to run.
The event flags indicate whether or not a given task is scheduled for execution. rt_OneStep maintains the event flags based on a task counter that is maintained by code in the model's example main program (ert_main.c). When a counter indicates that a task's sample period has elapsed, the example main code sets the event flag for that task.
On each invocation, rt_OneStep updates its scheduling data structures and steps the base-rate task (rt_OneStep always calls model_step0 because the base-rate task must execute on every clock step). Then, rt_OneStep iterates over the scheduling flags in tid order, unconditionally calling model_stepN for any task whose flag is set. The tasks are executed in order of priority.
Preemption. Note that the design of rt_OneStep assumes that interrupts are disabled before rt_OneStep is called. rt_OneStep should be noninterruptible until the base-rate interrupt overflow flag has been checked (see pseudocode above).
The event flag array and loop variables used by rt_OneStep are stored as local (stack) variables. Therefore, rt_OneStep is reentrant. If rt_OneStep is reinterrupted, higher priority tasks preempt lower priority tasks. Upon return from interrupt, lower priority tasks resume in the previously scheduled order.
Overrun Detection. Multirate rt_OneStep also maintains an array of timer overrun flags. rt_OneStep detects timer overrun, per task, by the same logic as single-rate rt_OneStep.
Note If you have developed multirate S-functions, or if you use a customized static main program module, see Rate Grouping Compliance and Compatibility Issues for information about how to adapt your code for rate grouping compatibility. This adaptation lets your multirate, multitasking models generate more efficient code. |
In a multirate single-tasking program, by definition, all sample times in the model must be an integer multiple of the model's fixed-step size.
In a multirate single-tasking program, blocks execute at different rates, but under the same task identifier. The operation of rt_OneStep, in this case, is a simplified version of multirate multitasking operation. Rate grouping is not used. The only task is the base-rate task. Therefore, only one model_step function is generated:
For a GRT target:
void model_step(int_T tid)
For an ERT target:
void model_step(void)
On each clock tick, rt_OneStep checks the overrun flag and calls model_step, passing in tid 0. The scheduling function for a multirate single-tasking program is rate_scheduler (rather than rate_monotonic_scheduler). The scheduler maintains scheduling counters on each clock tick. There is one counter for each sample rate in the model. The counters are implemented in an array (indexed on tid) within the Timing structure within rtModel.
The counters are clock rate dividers that count up the sample period associated with each subrate task. When a counter indicates that a sample period for a given rate has elapsed, rate_scheduler clears the counter. This condition indicates that all blocks running at that rate should execute on the next call to model_step, which is responsible for checking the counters.
rt_OneStep does not require extensive modification. The only required modification is to reenable interrupts after the overrun flags and error conditions have been checked. If applicable, you should also
Save and restore your FPU context on entry and exit to rt_OneStep.
Set model inputs associated with the base rate before calling model_step0.
Get model outputs associated with the base rate after calling model_step0.
In a multirate, multitasking model, set model inputs associated with subrates before calling model_stepN in the subrate loop.
In a multirate, multitasking model, get model outputs associated with subrates after calling model_stepN in the subrate loop.
Comments in rt_OneStep indicate the place to add your code.
In multirate rt_OneStep, you can improve performance by unrolling for and while loops.
In addition, you may choose to modify the overrun behavior to continue execution after error recovery is complete.
Also observe the following cautionary guidelines:
You should not modify the way in which the counters, event flags, or other timing data structures are set in rt_OneStep, or in functions called from rt_OneStep. The rt_OneStep timing data structures (including rtModel) and logic are critical to the operation of any generated program.
If you have customized ert_main.c or .cpp to read model outputs after each base-rate model step, be aware that selecting model options Support: continuous time and Single output/update function together may cause output values read from ert_main for a continuous output port to differ slightly from the corresponding output values in the model's logged data. This is because, while logged data is a snapshot of output at major time steps, output read from ert_main after the base-rate model step potentially reflects intervening minor time steps. To eliminate the discrepancy, either separate the generated output and update functions (clear the Single output/update function option) or place a Zero-Order Hold block before the continuous output port.
In most cases, the easiest strategy for deploying generated code is to use the Generate an example main program option to generate the ert_main.c or .cpp module (see Generate a Standalone Program).
However, if you turn the Generate an example main program option off, you can use the module matlabroot/rtw/c/ert/ert_main.c as a template example for developing your embedded applications. The module is not part of the generated code; it is provided as a basis for your custom modifications, and for use in simulation. If your existing applications, developed prior to this release, depend upon a static ert_main.c, you may need to continue using this module.
When developing applications using a static ert_main.c, you should copy this module to your working directory and rename it to model_ert_main.c before making modifications. Also, you must modify the template makefile such that the build process creates model_ert_main.obj (on UNIX, model_ert_main.o) in the build directory.
The static ert_main.c contains
rt_OneStep, a timer interrupt service routine (ISR). rt_OneStep calls model_step to execute processing for one clock period of the model.
A skeletal main function. As provided, main is useful in simulation only. You must modify main for real-time interrupt-driven execution.
For single-rate models, the operation of rt_OneStep and the main function are essentially the same in the static version of ert_main.c as they are in the autogenerated version described in About Standalone Program Execution. For multirate, multitasking models, however, the static and generated code is slightly different. The next section describes this case.
Targets based on the ERT target sometimes use a static ert_main module and disallow use of the Generate an example main program option. This is done because target-specific modifications have been added to the static ert_main.c, and these modifications would not be preserved if the main program were regenerated.
Your ert_main module may or may not use rate grouping compatible model_stepN functions. If your ert_main module is based on the static ert_main.c module, it does not use rate-specific model_stepN function calls. The static ert_main.c module uses the old-style model_step function, passing in a task identifier:
void model_step(int_T tid);
By default, when the Generate an example main program option is off, the ERT target generates a model_step "wrapper" for multirate, multitasking models. The purpose of the wrapper is to interface the rate-specific model_stepN functions to the old-style call. The wrapper code dispatches to the model_stepN call with a switch statement, as in the following example:
void mymodel_step(int_T tid) /* Sample time: */
{
switch(tid) {
case 0 :
mymodel_step0();
break;
case 1 :
mymodel_step1();
break;
case 2 :
mymodel_step2();
break;
default :
break;
}
}The following pseudocode shows how rt_OneStep calls model_step from the static main program in a multirate, multitasking model.
rt_OneStep()
{
Check for base-rate interrupt overflow
Enable "rt_OneStep" interrupt
Determine which rates need to run this time step
ModelStep(tid=0) --base-rate time step
For N=1:NumTasks-1 -- iterate over sub-rate tasks
Check for sub-rate interrupt overflow
If (sub-rate task N is scheduled)
ModelStep(tid=N) --sub-rate time step
EndIf
EndFor
}You can use the TLC variable RateBasedStepFcn to specify that only the rate-based step functions are generated, without the wrapper function. If your target calls the rate grouping compatible model_stepN function directly, set RateBasedStepFcn to 1. In this case, the wrapper function is not generated.
You should set RateBasedStepFcn prior to the %include "codegenentry.tlc" statement in your system target file. Alternatively, you can set RateBasedStepFcn in your target_settings.tlc file.
As in the generated ert_main.c, a few modifications are made to the main loop and rt_OneStep. See Guidelines for Modifying the Main Program and Guidelines for Modifying rt_OneStep.
Also, you should replace the rt_OneStep call in the main loop with a background task call or null statement.
Other modifications you may need to make are
If your model has multiple rates, the generated code might not operate as expected unless:
The multirate scheduling code is removed. The relevant code is tagged with the keyword REMOVE in comments (see also the Version 3.0 comments in ert_main.c).
Use the MODEL_SETEVENTS macro (defined in ert_main.c) to set the event flags instead of accessing the flags directly. The relevant code is tagged with the keyword REPLACE in comments.
If applicable, follow comments in the code regarding where to add code for reading/writing model I/O and saving/restoring FPU context.
Note If you modify ert_main.c to read a value from a continuous output port after each base-rate model step, see the relevant cautionary guideline in Guidelines for Modifying rt_OneStep. |
When the Generate an example main program option is off autobuild.h is generated to provide an interface between the main module and generated model code. If you create your own static main program module, you would normally include autobuild.h.
Alternatively, you can suppress generation of autobuild.h, and include model.h directly in your main module. To suppress generation of autobuild.h, use the following statement in your system target file:
%assign AutoBuildProcedure = 0
If you have cleared the Terminate function required option, remove or comment out the following in your production version of ert_main.c:
The #if TERMFCN... compile-time error check
The call to MODEL_TERMINATE
If you do not want to combine output and update functions, clear the Single output/update function option and make the following changes in your production version of ert_main.c:
Replace calls to MODEL_STEP with calls to MODEL_OUTPUT and MODEL_UPDATE.
Remove the #if ONESTEPFCN... error check.
The static ert_main.c module does not support the Generate Reusable Code option. Use this option only if you are generating a main program. The following error check raises a compile-time error if Generate Reusable Code is used illegally.
#if MULTI_INSTANCE_CODE==1
The static ert_main.c module does not support the External mode option. Use this option only if you are generating a main program. The following error check raises a compile-time error if External mode is used illegally.
#ifdef EXT_MODE
When the Generate an example main program option is off, code generation produces slightly different rate grouping code, for compatibility with the older static ert_main.c module. See Rate Grouping and the Static Main Program for details.
All built-in Simulink blocks, as well as all DSP System Toolbox blocks, are compliant with the requirements for generating rate grouping code. However, user-written multirate inlined S-functions may not be rate grouping compliant. Noncompliant blocks generate less efficient code, but are otherwise compatible with rate grouping. To take full advantage of the efficiency of rate grouping, your multirate inlined S-functions must be upgraded to be fully rate grouping compliant. You should upgrade your TLC S-function implementations, as described in this section.
Use of noncompliant multirate blocks to generate rate-grouping code generates dead code. This can cause two problems:
Reduced code efficiency.
Warning messages issued at compile time. Such warnings are caused when dead code references temporary variables before initialization. Since the dead code never runs, this problem does not affect the run-time behavior of the generated code.
To make your S-functions rate grouping compliant, you can use the following TLC functions to generate ModelOutputs and ModelUpdate code, respectively:
OutputsForTID(block, system, tid) UpdateForTID(block, system, tid)
The code listings below illustrate generation of output computations without rate grouping (Listing 1) and with rate grouping (Listing 2). Note the following:
The tid argument is a task identifier (0..NumTasks-1).
Only code guarded by the tid passed in to OutputsForTID is generated. The if (%<LibIsSFcnSampleHit(portName)>) test is not used in OutputsForTID.
When generating rate grouping code, OutputsForTID and/or UpdateForTID is called during code generation. When generating non-rate-grouping code, Outputs and/or Update is called.
In rate grouping compliant code, the top-level Outputs and/or Update functions call OutputsForTID and/or UpdateForTID functions for each rate (tid) involved in the block. The code returned by OutputsForTID and/or UpdateForTID must be guarded by the corresponding tid guard:
if (%<LibIsSFcnSampleHit(portName)>)
as in Listing 2.
Listing 1: Outputs Code Generation Without Rate Grouping.
%% multirate_blk.tlc
%implements "multirate_blk" "C"
%% Function: mdlOutputs =====================================================
%% Abstract:
%%
%% Compute the two outputs (input signal decimated by the
%% specified parameter). The decimation is handled by sample times.
%% The decimation is only performed if the block is enabled.
%% Each ports has a different rate.
%%
%% Note, the usage of the enable should really be protected such that
%% Neach task has its own enable state. In this example, the enable
%% occurs immediately which may or may not be the expected behavior.
%%
%function Outputs(block, system) Output
/* %<Type> Block: %<Name> */
%assign enable = LibBlockInputSignal(0, "", "", 0)
{
int_T *enabled = &%<LibBlockIWork(0, "", "", 0)>;
%if LibGetSFcnTIDType("InputPortIdx0") == "continuous"
%% Only check the enable signal on a major time step.
if (%<LibIsMajorTimeStep()> && ...
%<LibIsSFcnSampleHit("InputPortIdx0")>) {
*enabled = (%<enable> > 0.0);
}
%else
if (%<LibIsSFcnSampleHit("InputPortIdx0")>) {
*enabled = (%<enable> > 0.0);
}
%endif
if (*enabled) {
%assign signal = LibBlockInputSignal(1, "", "", 0)
if (%<LibIsSFcnSampleHit("OutputPortIdx0")>) {
%assign y = LibBlockOutputSignal(0, "", "", 0)
%<y> = %<signal>;
}
if (%<LibIsSFcnSampleHit("OutputPortIdx1")>) {
%assign y = LibBlockOutputSignal(1, "", "", 0)
%<y> = %<signal>;
}
}
}
%endfunction
%% [EOF] sfun_multirate.tlcListing 2: Outputs Code Generation With Rate Grouping.
%% example_multirateblk.tlc
%implements "example_multirateblk" "C"
%% Function: mdlOutputs =====================================================
%% Abstract:
%%
%% Compute the two outputs (the input signal decimated by the
%% specified parameter). The decimation is handled by sample times.
%% The decimation is only performed if the block is enabled.
%% All ports have different sample rate.
%%
%% Note: the usage of the enable should really be protected such that
%% each task has its own enable state. In this example, the enable
%% occurs immediately which may or may not be the expected behavior.
%%
%function Outputs(block, system) Output
%assign portIdxName = ["InputPortIdx0","OutputPortIdx0","OutputPortIdx1"]
%assign portTID = [%<LibGetGlobalTIDFromLocalSFcnTID("InputPortIdx0")>, ...
%<LibGetGlobalTIDFromLocalSFcnTID("OutputPortIdx0")>, ...
%<LibGetGlobalTIDFromLocalSFcnTID("OutputPortIdx1")>]
%foreach i = 3
%assign portName = portIdxName[i]
%assign tid = portTID[i]
if (%<LibIsSFcnSampleHit(portName)>) {
%<OutputsForTID(block,system,tid)>
}
%endforeach
%endfunction
%function OutputsForTID(block, system, tid) Output
/* %<Type> Block: %<Name> */
%assign enable = LibBlockInputSignal(0, "", "", 0)
%assign enabled = LibBlockIWork(0, "", "", 0)
%assign signal = LibBlockInputSignal(1, "", "", 0)
%switch(tid)
%case LibGetGlobalTIDFromLocalSFcnTID("InputPortIdx0")
%if LibGetSFcnTIDType("InputPortIdx0") == "continuous"
%% Only check the enable signal on a major time step.
if (%<LibIsMajorTimeStep()>) {
%<enabled> = (%<enable> > 0.0);
}
%else
%<enabled> = (%<enable> > 0.0);
%endif
%break
%case LibGetGlobalTIDFromLocalSFcnTID("OutputPortIdx0")
if (%<enabled>) {
%assign y = LibBlockOutputSignal(0, "", "", 0)
%<y> = %<signal>;
}
%break
%case LibGetGlobalTIDFromLocalSFcnTID("OutputPortIdx1")
if (%<enabled>) {
%assign y = LibBlockOutputSignal(1, "", "", 0)
%<y> = %<signal>;
}
%break
%default
%% error it out
%endswitch
%endfunction
%% [EOF] sfun_multirate.tlc
![]() | Real-Time and Embedded Systems | Operating System Integration | ![]() |

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 |