Overview

This guide describes how to integrate the ASCET-DEVELOPER development flow with Arm Development Studio.

A brand-new automobile might contain more than 100 million lines of code in the software that controls its various systems, from engine management to atmospheric control. This amount of code will only increase as automobiles become more complex The increase in complexity of systems has led to widespread use of model-based control development tools, generating safety standard conforming, portable source code.

ETAS GmbH, a subsidiary of Robert Bosch GmbH, are a provider of development solutions for the automotive industry and related sectors. One of their product offerings is ASCET-DEVELOPER, a code generation tool which uses this type of model-based process. ASCET-DEVELOPER generates MISRA-C compatible application code, for use with the RTA-OS operating system.

Arm Development Studio is a comprehensive embedded C/C++ development solution for all Arm processors. Arm Development Studio includes the Arm Compiler, a mature toolchain tailored to bare-metal, firmware, and RTOS based applications. Arm Compiler for Functional Safety is a qualified C/C++ toolchain that has been assessed by the safety-accredited certification body TÜV SÜD. The qualified toolchain is suitable for developing embedded software for safety markets, including automotive, industrial, medical, railways and aviation.

The purpose of the integration is to provide a simple, iterative flow as control applications are developed and debugged. Optimization techniques are explored to tune the generated code to make use of the latest features of the Arm architecture.

This guide is intended for automotive or similar developers who are interested in a model-based software development flow. The tools described in this guide are Arm Development Studio and ETAS ASCET-DEVELOPER. However, content may be relevant to users of other tools, notably Keil MDK.

Before you begin

To work through this guide, you need to download and install the latest versions of Arm Development Studio and ASCET-DEVELOPER. The Arm and ASCET-DEVELOPER websites include basic installation and setup instructions. A valid software license is required for the tools to execute.

Arm Development Studio is supplied with the latest available version of the Arm Compiler at the time of release. If you want to use a different compiler version, for example Arm Compiler for Functional Safety, you can add this through the Preferences pane. For more information, see Add new compiler toolchains to Arm Development Studio.

System initialization is highly device specific. CMSIS Packs provide initialization code, often with complete example projects, for many microcontrollers. If you are using a microcontroller that allows this, you can import the necessary packs from the CMSIS Pack Manager perspective of Arm Development Studio. To import generic initialization examples for Arm processors:

  1. Navigate to the Development Studio Eclipse IDE.
  2. In the menu option, navigate to File > Import.

When you have created your Development Studio project, configure your ASCET-DEVELOPER project to generate code directly into the project folder. In the ASCET-DEVELOPER project properties:

  1. Navigate to Run > Run Configurations > Generate results into a folder.
  2. In the Folder field, specify the location of the project folder to generate to.

Code optimization

ASCET-DEVELOPER generates highly portable, MISRA-C compliant source code, which can be readily consumed by the Arm Compiler. We will introduce some useful tips to control how ASCET-DEVELOPER generates code to best suit the Arm Architecture.

Further information on global options for optimizing ASCET-DEVELOPER can be found in the User Guide. Similarly, the Arm Compiler User Guide contains general documentation on writing optimized code.

Use structures for messages and global data

The use of many global variables is generally considered bad programming practice. Data encapsulation and abstraction with structures and helper functions are encouraged to improve maintainability, readability, and correctness. Avoiding the use of many global variables is also beneficial for model generated code.

The Arm processor is a load-store architecture, and therefore the data processing instructions cannot access global data directly. This type of variable is accessed with a read-modify-write strategy, and use a register containing a base address, with an optional offset, to load and store data to the core registers.

Grouping global registers together in memory so that a common base pointer can be used is a common optimization for Arm. First, a base address is loaded, and then a read from that base address, with or without an address offset. We recommend a single base address and reference to many variables as an offset from that address. The preference for this practice leads to the preference for the use of structures for global data.

Because most embedded systems have multiple task rates, and pre-emption is used in many cases, any task container must take protected copies of messages, passed as variables between tasks. ASCET-DEVELOPER supports several types of protected message copy, for example to take copies of all used messages under suspended interrupts. You can choose task rates and define the execution of task containers within an OS configuration App file for any project.

The original code for the modules uses individual messages. This means that an individual update or read must be performed for each message for a task before any execution occurs. Because each task can manipulate many messages, this procedure can become quite time-consuming.

Because each module in ASCET-DEVELOPER has a group of output interface messages defined in an Embedded Software Description Language formatted data interface file, these can easily be replaced with a single structure of messages.

The following code shows an example output interface message:

data interface SPInterface
{
	@symbol(“SP_Dig1Speed”)
	T_Speed_SP_Dig1Speed = 0x0;
	@symbol(“SP_Dig1Pin”)
	Boolean SP_Dig1Pin = false;
	@symbol(“SP_Dig1Duty”)
	T_DutyPercent_SP_Dig1Duty = 0x0;
…

The following code shows an example of replacement with a single structure:

	data interface SPInterface
	{
		SP_InterfaceData SP_If;
	}

A memcpy can take a local copy for use in a task container. Because the size of the structure is well known, the compiler can select the most efficient memcpy implementation from the compiler supplied C library.


Function arguments

To aid creating scripts for ASCET-DEVELOPER class code unit testing, it is common to let each class have a single method which reads all inputs to the class. This method is called a ReadInputs method. This method has the benefit of generating a single line of source which clearly hooks into test scripts.

A disadvantage of the ReadInputs method is that the resulting code function has a non-optimal call. It is more efficient to pass a pointer to a structure rather than multiple arguments.

Each instance of each class has an internal static variable for each argument. It is possible to remove the ReadInputs method, and instruct ASCET-DEVELOPER to make copies of required data, To do this, select the Set option on the variables that are being read, as shown in the following screenshot:

Code optimization-Function arguments screenshot

Similarly, multiple output variables can be handled by selecting the Get option on the output variables.

The resulting C code replaces the ReadInputs function with structure-to-structure element copies which should be more efficient. This is because the structure base addresses need to be loaded only once.


Make use of intrinsic functions

The Arm C Language Extensions (ACLE) are a set of features to enable C/C++ code to exploit the advanced features of the Arm Architecture. One such extension is the definition of intrinsic functions that map to specific Arm instructions that may not otherwise be generated by the compiler.

ASCET-DEVELOPER allows the creation of an Arithmetic Services file that defines a function to be called in a specific scenario. That can subsequently be mapped to an intrinsic function at compile time.

For example, consider the following variable definition, which saturates at +/-32K:

representation R_Speed{
		range = -32768 .. 32767;
		datatype = sint16;
		formula = f_0bn0;
	}
type T_Speed is real using R_Speed;

By default, this definition may result in inefficient source code being generated, with additional saturation code being applied, as seen in the code:

	_t1sint32 = a+b;
x = (sint16)(((_t1sint32 >= =32768) ? (((_t1sint32 <=32767) ?
               _t1sint32 : 32767))  : -32768));

The following code shows how you can define an arithmetic service so that a function is used to implement the code instead:

+1|s16|s16|s16=(sint16)ADDLIM_S16S16_S16(%i1%,%i2%)

The ASCET-DEVELOPER code generator will be able to make use of this function, for example:

	x = ADDLIM_S16S16_S16(a, b);

You can then map this code to an ACLE intrinsic function in a user provided header file, for example:

#define ADDLIM_S16S16_S16(a, b)	__QADD16(a, b)

This will compile into a single QADD instruction.


Optimize placement of code and data

The Arm linker uses a mechanism known as scatterloading to place code and data at appropriate addresses in your device memory map. You can use the ACLE __attribute__ ((section (“.name”))) feature to create special ELF sections which can be referenced by the scatter file. This can be used to place frequently used or high priority tasks in the fastest memory location. Many Arm devices support tightly coupled memory local to the processor for this purpose.

Within ASCET-DEVELOPER you can define MemorySections.xml to easily locate functions to particular regions. You may wish to define a section named TCMCODE for your functions to place in tightly coupled memory as you can see in the following code:

<MemClass>
	<name>TCMCODE</name>
	<prePragma/>
	<postPragma/>
	<funcSignatureDef>%return_type% __attribute__ ((section (“.ascettcm”)))
   	     %gen_name% (%argv%)</funcSignatureDef>
	<constQualifier>true</constQualifier>
	<volatileQualifier>false</volatileQualifier>
	<description>memory section for code to execute in TCM </description>
	<category>Code</category>
</MemClass>

Your scatter file can define an execution region for the tightly coupled memory and use the section name to easily place these functions within the code, as seen in the following code:

	TCM_REGION	TCM_BASE	TCM_SIZE
	{
		*(.ascettcm)
}


Next steps

In this guide we have shown how developers can use ASCET-DEVELOPER model-based design tool to generate standards compliant source code for complex control algorithms. You can tune your design to enable the Arm Compiler from Arm Development Studio to build this code in an optimal manner for your Arm-based target.  The Eclipse IDE provides a common environment, supporting third-party plugins for version control and other functionality. If you are new to either ASCET-DEVELOPER or Arm Development Studio, free evaluation licenses are available from ETAS and Arm respectively.

The process of developing code for automotive systems is at a turning point. Creating high safety integrity systems for ISO26262 or IEC61508 does not require a complex process, it requires a clean process with good traceability from end to end. Where possible as much of the process should be contained within one application.

Arm Development Studio, providing a FuSa qualified compiler, Eclipse based IDE allowing the use of the various version management and other plugins, and integrated debug support, coupled with model based graphical coding with ASCET-DEVELOPER, is on the path to such a process.