Selection of your embedded system’s software architecture

 

In an earlier blog post, we discussed the benefits and drawbacks of both RTOS and bare-metal architectures. Bare metal is simply defined as firmware development that takes place directly on your hardware, without any intermediary layers. However, does this imply that bare metal exclusively refers to firmware development in assembly? How about using memory-mapped registers directly in C development, with the aid of header files that contain macros for such addresses? Is that metal exposed? We can't always be certain how a C compiler will convert our code to machine code because C is a high-level language.

Why do vendor-provided HALs appear in C? That is an additional layer of abstraction. Why do schedulers have sequencers? Although the notion of bare metal is frequently ambiguous, many developers consider firmware development in C utilizing HAL to be bare metal. job preemptiness, or the capacity to allocate resources to a job with a higher priority, is the primary distinction between RTOS and everything that isn't RTOS.
Since it's easy to get confused about what exactly constitutes bare metal, we'll talk about a few architectures in this piece that aren't RTOS but might be bare metal. Superloop is the most basic one that all embedded developers begin with.

Top of Form

 

Bottom of Form

1. Superloop

The simplest bare-metal strategy is Superloop. Code is executed on each iteration of the endless loop. The Arduino "loop" function is the most well-known example. The most popular method of managing the execution of individual functions is through the use of flags, which are typically set by interrupts. Superloops carry out instructions in a sequential fashion. Setting a flag from a timer ISR or determining how much time has passed since the last iteration are common methods for time control.


We'll use a small piece of code to demonstrate how to take data from a sensor in the event that a GPIO interrupt is raised, process it, and send it to the server every ten seconds.

Superloop quickly ends in spaghetti code. It can be difficult to detect and fix errors, modify the logic of an existing application, and add new capabilities. Despite its simplicity, superloop is still frequently used to develop quick proof-of-concept solutions with restricted capabilities. Because it is difficult to predict how long a single task will take to complete, it is not a good option for time-sensitive systems. Furthermore, no framework exists that can ensure the timely completion of higher-priority activities.

One will begin to seek out more sophisticated solutions that are easier to manage and more adaptable for adding new features. A sequencer is the next step in the superloop's natural growth. We'll add tasks (function pointers) to a sequencer, which will then carry out those tasks, rather than designing logic that uses flags set in an ISR to control the execution of specific functions.



2. Sequencer

Sequencer is a straightforward design pattern that lets you add tasks that must be completed to the sequencer module. The sequencer will run indefinitely and complete the jobs that are accessible. The sequencer can receive tasks from internal (timer) or external interruptions. In order for the sequencer to execute the first available tasks with a greater priority, tasks can also be given a priority. To guarantee deterministic behavior, a watchdog is deployed to guarantee the task duration, and time-critical tasks must internally ensure execution time or call frequency using timers.

The code is simpler and cleaner, which is the first thing we notice from the above image. After the execution, there are no flags that need to be put in ISR and reset. A great illustration of bare-metal event-driven design is Sequencer.
A sequencer can be implemented in a variety of ways; tasks can be added and defined during compile time with predetermined priority and indices, and they can be scheduled to execute during run time as required.

3. Cooperative scheduler

A pattern that expands on the sequencer is called a cooperative scheduler. It is a hybrid of a virtual timer and a sequencer. Because it only permits one interrupt—a timer interrupt that serves as a tick for the entire system—it is more constrained in terms of timing needs. The developer also establishes an initial delay, following which tasks are scheduled to be completed at a predetermined period.




Tasks are carried out at predetermined intervals thanks to the cooperative scheduler. The firmware developer is still in charge of ensuring job duration and preventing task overlap, though. Ticks are created using a timer, and a data structure describing individual jobs is updated using the sch_update function. These structures include data members for counting the ticks and a function pointer for a task. Sch_dispatch is called from the super loop rather than the timer ISR to ensure updates of data structures describing tasks.

 

Top of Form

 

Bottom of Form

4. When to choose bare-metal architectures and when to go with RTOS?

RTOS was briefly explained in a prior blog article. A preemptive scheduler is one of RTOS's primary parts. It is a component of RTOS that controls how tasks are carried out. Based on the tasks' current status and priority, the scheduler decides which one should be completed next. It is intended to offer deterministic task execution. This indicates that the tasks are completed on schedule and with predictable results.

An RTOS is typically a suitable fit for Internet of Things applications that make use of certain networking protocols. It is feasible to run application logic and networking stacks on bare metal event-driven systems. Writing discrete actions based on a ticker event is more difficult than defining an RTOS task and assuming it would continue indefinitely.

Though it's not always the case, an RTOS appears to be the recommended design for embedded systems. RTOS gives you the infrastructure to guarantee scalability and deterministic behavior, but it comes at the expense of using more resources. It needs a flash to hold the instructions and some RAM to keep things operating.

Navigating the intricacies of embedded system software architecture can be challenging, whether you're deciding between bare metal or RTOS, or optimizing a superloop-based design. At Silicon Signals, we specialize in developing tailored solutions for embedded systems, from bare-metal firmware to RTOS integration and beyond.

Our expertise ensures your software architecture is optimized for performance, scalability, and reliability. Ready to take your embedded system design to the next level? Connect with us today and let’s build smarter systems together!"



Comments

Popular posts from this blog

How Android System Services Connect Apps and HAL: A Deep Dive

AOSP Passthrough HAL: Architecture, Use Cases & Performance Guide

Getting Started with AOSP: Build Custom Android Solutions