An elegant solution to many embedded software system problems is to take advantage of data-centric software design. Data is at the heart of any embedded system. First, data is acquired through sampling sensors, communication interfaces, and input/output devices. Then the data is converted, filtered and processed into new data assets in the system. Finally, this data is then used to create outputs. My first principle in software design is that data drives design. Here are three tips for designing data-centric software.
Tip #1 – Track Data
Every software system at its core is data-centric. The design of an effective and efficient software architecture and implementation must follow this data. Unfortunately, there are a limited number of things data can do in a software system.
First, data is produced. For example, in an embedded system, there may be an analog sensor that outputs a voltage measured by the microcontroller’s internal analog-to-digital converter. Touchscreen inputs can interrupt and provide an x/y coordinate pair. Data is often produced as input or output to the system. Data-centric software design carefully identifies inputs and outputs, because that is where we will find the data produced.
Then the data is processed. This analog-to-digital conversion just mentioned that produces ADC counts is in raw form. Sensor data can be processed to create a floating point numeric value representing the scientific units of the sensor. Usually, it is more common to leave the sensor value in raw form but filter the data. For example, the data may have a low-pass or high-pass filter to remove noise. Designers need to track data to understand how it is processed.
Finally, the data is stored. Data can be stored in memory as a single value. Data can be stored in an array of values that will be processed together. Data can be saved to non-volatile memory for later use. Again, understanding how data is stored is critical to the design process.
It doesn’t matter whether a sensor or a communication interface produces your data. It doesn’t matter if you store it on an SD card or a memory chip. From a design perspective, we care about where data enters and exits the system (produced), is processed, and then ultimately stored. To create an effective software design, you need to track data. Document the data in each state and how it is transformed. If you do this, you will find that the design naturally falls into place.
Tip #2 – Document how data changes
In each system, raw data is transferred and converted into valuable data which is then output. Tracking how data flows through a system can drive software architecture well; however, documenting how data changes and evolves in the system can help a designer assess the breadth of the software.
For example, if I have analog sensor data converted to a number and displayed in telemetry data, I know there isn’t a lot of processing power required. Data processing is simple. However, if the sensor data is retrieved, stored in a circular buffer, filtered, and then converted to a scientific unit, I know I have a larger scope for that data asset. Documenting data flow and processing events can help designers constrain and understand their design, even without the official microcontroller or hardware ever being identified.
When starting a design, follow these simple steps:
- Identify the data produced in the system.
- Check how each data asset moves through the software.
- Document how data changes throughout its lifetime.
- Record all storage media and how data is accessed.
I have found that creating a simple data flow diagram can dictate the software architecture and ensure it is scalable and not overly complicated.
Tip #3 – Act on data at the start of an event or task
A typical task in an embedded system will process information in the following steps:
- Retrieve new data.
- Filter/process data.
- Output result.
No surprise here, right? The process seems logical. Unfortunately, this does not necessarily correspond to a real-time embedded system design. One of the main goals of the real-time system is to be deterministic and to minimize jitter. The above process can maximize the jitter potential rather than minimizing it. Let’s take an example.
A motor control task reads the latest analog current and voltage measurements and then executes them through a PID controller. The PID controller will provide an output for the new motor state. Each pass through the PID controller may not run in the same number of clock cycles depending on the design of the controller. There can be a wide range of time values if one includes the possibilities of triggering interrupts in the middle of PID calculations. The result is that the engine output is never updated simultaneously. It jiggles a bit and wobbles, which can affect engine response.
A safer way to act on the new data output for the engine that minimizes jitter is to change the steps to:
- Show previous result.
- Retrieve new data.
- Filter/process data.
In this case, the new output is always sent to the engine at the start of the loop before further processing, or other non-deterministic activities occur. So, yes, we are acting on data that might be slightly older, but we can take that into account and reduce our jitter significantly.
Conclusions for Better Software Design
It all comes down to data when you break down what we do to design and implement in-vehicle software. Embedded software is nothing more than the collective processing, storing and outputting of data in a deterministic way. Following a data-centric software design approach will result in an architecture that highlights the most important data. You will find the design to be effective, efficient and not bloated with modern design patterns and burdens. If you want great software design, just follow the data.