Why configurability?
As the complexity of embedded systems grows, it becomes increasingly challenging to integrate source code from multiple projects with numerous dependencies and configuration options.
To address this issue, xCDL defines and implements a component framework consisting of a set of metadata and a collection of tools. It is specifically designed to support the multi-variant cross-building of embedded system images from reusable components. The framework is extensible, so new components can be added to the build system at any time.
Although designed to meet the requirements of building embedded applications, xCDL is generic enough to be used for building regular applications and libraries as well.
Configurability goals
Increase reusability
The primary goal is to enable large portions of embedded applications to be built from generic, reusable software components. These components must be configurable so that they work together and satisfy the constraints of the target system.
Meet memory constraints
Many embedded applications must operate with limited memory to reduce manufacturing costs. The component framework should allow users to configure components so that any unnecessary functionality is removed, minimising RAM (and sometimes Flash) usage.
Improve testability
Tightly coupled embedded systems are often difficult to test. A component framework promotes modularity, making it much easier to write comprehensive unit tests for decoupled components.
Assist with debugging
Embedded systems can be challenging to debug. Reusable components can provide various forms of debugging assistance. The ability to control which debugging features are enabled for a given application build is therefore highly desirable.
Approaches to configurability
The purpose of configurability is to control the behaviour of components and the relationships between them. The component author includes as many different behaviours as possible, but cannot predict exactly how a particular component will be used. Therefore, when an application uses a component, there must be a way to specify the desired behaviour. This is achieved through configuration choices that are evaluated at different stages: run time, link time, or compile time.
Run-time
One way to control behaviour is at run time. However, this approach has a significant disadvantage in terms of the size of the final application image: the code linked with the application must support all possible behaviour cases, even if the application does not require them.
Due to the characteristics of embedded platforms, shared libraries are not widely used, and supporting them is therefore a low priority.
Link-time
Another approach is to control behaviour at link time, typically by leveraging inheritance in an object-oriented language and linking only those implementation instances that are actually required.
Compile-time
The xCDL component framework allows behaviour to be controlled at an even earlier stage: when the component source code is compiled.
In theory, compile-time configurability should yield the best results in terms of code size, as it allows control at the individual statement level rather than at the function or object level. The overall result is that the final application image contains only the code and data necessary for the application to function, and nothing more.
Compile-time configurability is not intended to replace the other approaches but rather to complement them.
There will be times when run-time selection of behaviour is desirable. For example, an application may need to change the baud rate of a serial line at run-time, so a mechanism for doing so must be available. Similarly, there will be times when link-time selection is preferable.
Configuration hierarchy
TODO: explain what it is.
Until xcdl config will be ready, an example of a configuration hierarchy
is shown below as a tree output.
ilg@wksi micro-os-plus-iv-suite.git % tree xcdl-tree
xcdl-tree
├── micro-os-plus
│ ├── architecture (i)
│ ├── architectures
│ │ ├── aarch32
│ │ ├── aarch64
│ │ ├── cortexm
│ │ ├── riscv
│ │ └── synthetic-posix
│ ├── core
│ │ └── startup
│ ├── device (i)
│ ├── devices
│ │ ├── cortexm
│ │ ├── qemu-aarch32
│ │ ├── qemu-aarch64
│ │ └── qemu-riscv
│ ├── diag
│ │ └── trace
│ │ ├── arm
│ │ │ ├── itm
│ │ │ └── semihosting
│ │ │ ├── debug
│ │ │ └── stdout
│ │ ├── interface
│ │ ├── posix
│ │ │ ├── stderr
│ │ │ └── stdout
│ │ └── segger
│ │ └── rtt
│ ├── micro-test-plus
│ ├── platform (i)
│ ├── platforms
│ │ ├── native
│ │ │ ├── darwin (o)
│ │ │ ├── linux (o)
│ │ │ └── win32 (o)
│ │ ├── qemu-cortex-a15
│ │ ├── qemu-cortex-a72
│ │ ├── qemu-cortex-m0
│ │ ├── qemu-cortex-m3
│ │ ├── qemu-cortex-m4f
│ │ ├── qemu-cortex-m7f
│ │ ├── qemu-riscv-rv32imac
│ │ └── qemu-riscv-rv64imafdc
│ ├── semihosting
│ └── utils
│ └── lists
│ ├── trace
│ └── trace-constructors
└── tests
├── common
│ ├── compiler-options
│ │ ├── verbose-link (o)
│ │ └── warnings
│ ├── create-hex (o)
│ ├── create-listing (o)
│ └── report-size (o)
├── sample
│ ├── binary (a)
│ │ ├── hex (a)
│ │ ├── listing (a)
│ │ └── size (a)
│ └── runner (t)
└── unit
├── binary (a)
│ ├── hex (a)
│ ├── listing (a)
│ └── size (a)
├── runner (t)
└── runner-coverage (t)
Credits
The initial content of this page was based on Chapter 1. Overview of The eCos Component Writer's Guide, by Bart Veer and John Dallaway, published in 2001.
Also: