Skip to main content

Software architectures for embedded systems part 1 - standalone systems

An embedded system is defined as a computing system that is a part of a larger system – such as a music player, a microwave oven, a printer or an engine control unit in an automobile. Unlike a desktop computer, the above definition says that an embedded system can not be fully defined on its own; it always has to be looked at within the context of the larger system. The characteristics of an embedded system, therefore, depend on the larger system that contains the embedded system.

There are a large number of diverse embedded systems. A remote control, a cell-phone, an aircraft landing system, an industrial robotic arm, a gaming station, a mobile phone gateway and a medical ECG machine are all examples of embedded systems. They all vary in size, speed and complexity drastically. The CPU used could vary from a single tiny microcontroller to multiple powerful processors. The speed of the processor could vary from under a MHz to over a GHz depending on the type of system. The memory used in embedded systems could vary widely, ranging from a few hundred bytes to a few gigabytes. The number and complexity of I/O devices could vary largely too: a digital thermometer may have a one or two I/O devices, whereas a cell-phone could have multiple complex I/O devices such as a camera, an LCD, multiple push-buttons, microphone, speakers, Bluetooth, Wi-Fi, SD card storage and the radio interface.

So, obviously, there can not be a single architecture that would fit all the applications. If we look at the existing systems, there are a large number of system implementations, each having its own software architecture. Some of them use no operating systems at all, some of them use their own home-brew operating systems, some use freely available operating systems (and modify parts of it themselves) and some use commercial operating systems. There are hundreds of embedded operating systems existing today – ranging from a tightly coded micro-kernel with a few hundred bytes of foot print to fully featured operating systems running into hundreds of megabytes. If that is the case, how do we go about understanding software architectures?

Luckily for us, these systems fall into three broad categories. They are:
  1. Single task system, running a bare-metal code without any operating system.
  2. Multitasking system, running a real-time operating system
  3. Multi-application system, running a full-fledged operating system such as Linux.
Let us take a look at each of them.

Single task systems

Let us take an example of a TV remote control. It would contain an embedded system with a small micro-controller running at sub-MHz clock, with on-chip RAM/ROM and a few peripheral devices. The ROM and RAM required could be below a kilobyte: ROM to store the program and read-only data, and RAM for read-write data. There would be push buttons and an infra-red transmitter as peripheral devices. In such a system, the software’s role is to initialize the processor and I/O ports, and then keep checking the button status. Whenever a button is pressed, the system would have to transmit the corresponding signal using the infra-red transmitter. The system does this forever.

Examples of similar systems are microwave front panels, car door locking systems, automatic vending machines, motion sensors and washing machine controllers. All these systems keep doing the same activity as long as they are running. They receive the inputs (such as a button press), do required processing (such as identify which button was pressed and decide what action to take) and generate the outputs (such as transmitting a signal, turning on a relay or rotating a motor). We classify such systems as single task systems.

Shown below is a typical implementation of a single task system. As you can see, the system initializes the hardware devices and then gets into an infinite loop, reading the inputs, processing the inputs and then generating the required outputs.
A single task system runs one task at a time. A task is a sequence of instructions that perform a specific activity. In our example of a remote control, the task is to check the button status, find out which button was pressed and transmit the corresponding signal. In case of a digital thermometer, the task would mean reading the temperature sensor value and sending signals to LEDs to display the temperature accordingly.

A single task system could be executing different tasks at different times, but it would perform only one task at a time. For example, a microwave oven front panel may perform multiple tasks, such as (1) setting the power level of the oven, (2) turning on and turning off heating, (3) showing a clock on the display and (4) responding to buttons pressed by the user. However, the system would perform only one task at a time; the new task starts only after completion of the current task. So, in our example, while the system is updating the display, it would not respond to buttons; it would complete updating the display and then check if any button is pressed. We don’t see this situation as a problem, because updating the timer would typically take a fraction of a millisecond, and a button-press would last for a few hundred milliseconds, long enough for the system to update the display and then check if any button is pressed.

Single task systems would function well as long as the tasks complete their job fast enough. Since the tasks are performed sequentially one after the other, such systems are only as fast as their slowest task. If any one task takes a long time, the whole system’s response time would suffer. In the previous example, for some weird reason if updating the timer display takes 10 seconds, the system would not be able to respond to the user buttons for those 10 seconds.

Single task systems are similar to a single lane bridge, allowing only one vehicle at a time. If a bullock cart is crossing the bridge, the next vehicle can not cross the bridge faster than the bullock cart – even if it is the fastest sports car in the world!

Software architecture

A single task broadly system consists of startup code and main code.

The startup code runs when the system is powered on. It performs system initialization, such as setting up the clock speed and initializing memory. Subsequently, the startup code performs C run-time initializations, as per C programming standard and then jumps to the main code.
 

The main code performs one-time initializations, such as bringing all the hardware devices in the system to their default state and initializing all the data elements in the program to their initial values. Subsequently, the main code gets into a loop, receiving the inputs processing them and generating the required outputs.

Sometimes, such systems may use interrupts, which are signals sent by the hardware devices when they require CPU's immediate attention. In such a case, the system runs a special routine called interrupt handler, which then performs the required interaction with the interrupting device and then returns to the normal program. (More details on interrupt-driven systems here.)

Memory map

There are two possible memory configurations.

When memory requirement is not much, typically the whole system resides in the memory provided inside the microcontroller chip. Code and read-only data resides in the on-chip ROM or flash. When the program runs, it uses the on-chip RAM for read-write data. Such a configuration is shown in the diagram below.


The systems with larger memory requirements use a separate memory chip for running the program. For such a system, we either store the compressed code, or we use a secondary memory such as an SD card to store the program. Such a system runs a small piece of code called boot code that loads the main code into external RAM.


On power-on, the system starts running the boot code. The boot code performs system initialization (clock and external memory), copies the main into external memory (after decompressing it if required) and jumps to the start address of the main code. Main code then performs one-time initializations and then continues running the main loop performing its functionality.

Note: The boot code contains a vector table that contains addresses of all the ISRs in the system. One the operational code is loaded in RAM, the vector table needs to be modified to include the addresses of the ISRs in the operational code. However, the vector table, being a part of the boot code, resides in ROM and hence cannot be modified. Hence, in such systems, there is a need to relocate the vector table in RAM. Relocating the vector table can be done with either hardware support provided in the processor or using a secondary vector table in RAM - details of which are beyond this writeup.

Advantages and Limitations

Most activities in a single-task system are sequential. These systems also do not have dependency on any extern al code such as an operating system. Hence these are relatively simple to implement and verify. 

If there are real time deadlines, then the system may not be feasible with such an architecture. We would need to break down the system into multiple tasks which would have to be scheduled according to their timing requirements. That leads us to the next category of systems, the multitasking systems, also known as the real-time systems to be described in the next article.


Comments

Popular posts from this blog

polling and interrupt-driven systems

Polling and interrupt-driven systems There is a class of embedded systems that run without any operating systems. The program running on CPU receives inputs from one or more IO devices, processes them as required by the system functionality and transmits outputs to one or more IO devices. The system is classified as a polling system or an interrupt driven system based on how the program interacts with the IO devices. Polling system The program needs to know if there are any IO activities in the system. An IO activity could be data received from an input device or data transmission completed to an output device. In a polling system , the program running on the CPU actively checks for any IO activities and if so, it takes action accordingly. while(1) {      wait until input is received <-- polling      read the input data      process the input data and generate output data      wait till previous output ...

Software architectures for embedded systems part 2 - real-time systems

< Prev: Standalone systems There is a class of systems that need to carry out multiple concurrent activities in order to accomplish the application objectives. Some of these activities may have timing deadlines and hence may have to be given a priority over the others. Such systems use a real-time operating system (RTOS), which allows implementing the system as multiple tasks and execute them according to their priorities. Since meeting the deadline is critical, such systems are known as real-time systems. 2. Real-time Systems To understand multitasking, let us take an example of an internet music player. Here, we are talking about an embedded system with a microcontroller, some on-chip memory, some off-chip memory and a few peripheral devices. The microcontroller would typically run at a few tens of MHz. The RAM could range from a few tens to a few hundreds of kilobytes. The ROM / flash could be a few tens of kilobytes, either on-chip or off-chip, to store the program. The peripher...