Model-based design (MBD) is well established in automotive to develop embedded software but with the growing size and complexity of software, handling big sized model is often a challenge. MBD enables to easily craft a software application and from one step to another refine it, test it and generate the production code. However, it’s risky to start modeling without a well-defined software architecture. Because it’s impossible to develop an automotive software as one single function, defining a software architecture helps to break down the complexity and improves the development and testing activities. ISO 26262 addresses software modularity with the principle of “Hierarchical structure of software components” and highly recommends it for all ASIL levels. In this article, we’ll see how this principle can be applied using a component-based architecture in Simulink. First of all, let’s start with some definitions.
What is a software architecture?
Defining the software architecture is an important step to ensure an efficient development of complex software.
Note: A software application can be modeled for Rapid Prototyping without a detailed software architecture definition but later in the development process, testing a piece of the software in Software-In-the-Loop (SIL) will be impossible if the corresponding function is not present in the production code.
The software architecture describes the elements that constitute the software and their interactions. This includes static aspects such as the hierarchical structure, executable functions, interface variables, datatypes, etc. as well as dynamic aspects such as execution modes/timing/order, temporal constraints.
In many projects we observe that the notion of software hierarchy is present but the definitions are not standardized. We often hear terms like “Module”, “Feature”, “Function”, “Unit”, “Component”, “Application”, etc. but from one project to another, they are used differently. In the AUTOSAR standard, there are three levels of hierarchy with standardized definitions: Composition, Atomic Software Component and Runnable. The Runnables are the executable software entities and the Atomic Software Component and Composition are encapsulation levels. In ISO 26262 Part 6 we see two terms: Software components and Software unit. The Software unit is considered to be the lower level piece to design the software and a software component gathers one or multiple software units in an encapsulation fashion. Independently from the terms being used, the concept behind a hierarchy of dependencies shall be clearly defined in the organization. Nevertheless, if a project has to comply with a standard, it’s recommended to use the same definition and wording of the standard. In this article, we’ll describe the component-based architecture in Simulink using the terms of the ISO 26262 standard: Software component and Software unit.
Ultimately, a dedicated tool shall be used to design the software architecture. For AUTOSAR architecture, it makes sense to use an AUTOSAR authoring tool but in general, professional software architecture tools offer sufficient features and abstraction to design the architecture. For more efficiency in the model-based context, it’s recommended to use a tool which easily integrates with the model-based environment.
What’s a software unit?
According to ISO 26262, a software unit is an atomic level of a software component that can be subject to standalone testing. One or more software units constitute a software component.
Characteristics of a software unit
- It can be designed, implemented and tested separately, independently of the remaining software.
- It implements a well-defined set of requirements with bi-directional traceability.
- It’s independent in a way that it can be implemented by one single developer
- It has a high self-cohesion: it’s atomic, focuses on one functionality and its dependencies to other units is reduced to well defined interface variables.
- It can be reused across multiple software applications.
Size and complexity
There are no systematic ways to define the ideal size of a unit (e.g. in terms of numbers of blocks/signals in the model-based context) but the ISO standard recommends to restrict the size of software components, the number of interfaces and describes methods to reduce the complexity. In general, we see that the size and complexity can be balanced between few aspects:
- From the perspective of the function developer/tester, it shall be possible to analyze and understand the allocated set of requirements.
- The functionality itself: an appropriate algorithm shall be chosen to implement the function considering that embedded software is a limited world. (e.g. the ideal mathematical solver cannot always be chosen).
- Smart coupling: Highly dependent functionalities shall reside inside the same unit as much as possible while decoupled features shall be split into different units.
- Guidance of technical criteria: metrics from complexity analysis tools, hardware resource consumption limits, software partitioning, criteria such as freedom-from-interference, etc.
From software architecture to component-based architecture in Simulink
In Simulink, one model usually implements a software component. The model structures the implementation using subsystem blocks. A subsystem gathers a subset of functionalities usually derived from the requirements. Subsystems can be virtual and non-virtual (aka atomic). Virtual subsystems are meant for readability/traceability and reduction of visual complexity while atomic subsystems, in addition to that, have an influence on the behavior: the content of an atomic subsystem is executed all at once. Therefore, non-virtual subsystems are suitable to model software units. Defining an atomic subsystem will:
- reveal unwanted dependencies to the remaining model like algebraic loops, external events signals crossing the unit boundaries, etc.
- guarantee the atomic execution of the subsystem operations in the generated code
- enable to generate the subsystem as an individual c-function.
Within a software unit atomic subsystem, it’s also possible to have smaller atomic subsystems for inner implementation purposes like conditional execution parts, reusable functions or to split the unit into smaller functions for resource management in the code.
Ultimately, the resulting structure of the software component model is composed of horizontal and vertical hierarchy subsystems connected via data and control signals.
Although a model can represent the hierarchical structure of a software component, it may not support further aspects like distributed development, maintainability, reusability, testability of the individual software units if it uses pure subsystems blocks. Indeed, the scope of these blocks is limited to the model itself.
Simulink component-based modeling using Library blocks or Model references
Simulink supports a “component-based” modeling style allowing to segment a model into separate and independent models which can be integrated together via reference and link mechanisms. With this approach, each model can represent a software unit and be designed, simulated and tested individually. The integrated model can thus represent the software component. Two techniques are available for this purpose and here are their main characteristics:
Each technique or both combined can be applied for component-based architecture in Simulink. It’s possible to create a folder structure to organize the different models and synchronize the files in a software configuration management tool for versioning, etc.
Note: The reference between the software unit models and the models of higher architecture levels can use either of one the two techniques (Library or Model reference). With the library option, the software unit is an atomic subsystem block stored in a library model and the frame model instantiates the Subsystem block. With the Model reference option, the software unit is the model itself and the frame model uses a Model block to reference it.
Unit and integration tests with the modular structure
ISO 26262 recommends similar test methods on unit and integration levels. For instance, Requirements-based testing can be carried on the unit and integration models however the granularity of the requirements is not the same. On the integration level, the requirements will talk about more high-level behavior of the system while on unit level they will describe the algorithm details. Therefore, the test quality criteria on both levels are different. Unit tests shall verify that there’s no unintended functionalities by looking at the metrics of requirements coverage and structural coverage “Statement”, “Branch” and “MC/DC”. Integration test mostly looks at the requirements coverage and also addresses “Function” and “Function Call” coverage.
Note: Before performing the integration tests, the software units shall be sufficiently tested to increase the confidence on each unit so the problems occurring during the integration would most likely be reduced to the scope of the interaction between the units and not individual malfunctions.
The picture below illustrates the possible relationship between the models and the test projects:
With such modular structure, it’s easier to manage the complexity. Software units can have relatively small size. They can be designed and tested individually. Code generation, test execution and debugging are faster making the development iterations more agile. Automatic test generation for test methods like Back-To-Back Test perform much better on smaller units. Moreover, when requirements change, only the corresponding model and related tests need to be updated. All these make the approach highly suitable for agile-based software development.
The component-based architecture in Simulink allows to handle large software models by breaking the model down into smaller ones. Each smaller model can represent and fulfill the characteristics of a software unit. The software development can be performed on the software unit models and these models are later assembled via sophisticated reference mechanisms to support the next activities like integration test. The reference mechanism allows to keep the referencing models up-to-date with regards to the referenced model. Hence, testing can be performed on unit and integration levels and always execute the original software unit.
It’s clear that the decomposition of the software architecture requires additional effort in early project phases but in the end, the resulting modularity enables to successfully manage software complexity and increases the efficiency of the development process throughout the software life cycle.
Note: It is technically possible to convert an existing large model into component-based model. This can be time-consuming (e.g. model restructuring, introduction of new interface variables, fixing algebraic loops, performing regression test, etc.) but as a one-time modification, it can be worth to do it to improve your process.