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:
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.
- Single task system, running a bare-metal code without any operating system.
- Multitasking system, running a real-time operating system
- Multi-application system, running a full-fledged operating system such as Linux.
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 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
Post a Comment