Software and RTOS synthesis: The next step in software development?
To investigate the use of software synthesis technology, the authors set out to use the SynthOS software synthesis tool to generate an embedded real-time operating system (RTOS).
By Arnold Berger, Mathew Hill, and Bob Zeidman
Programmable Logic DesignLine
(02/27/08, 02:06:00 PM EST)
Introduction
Stop at any booth at the Embedded Systems Conference and you'll see a demo of the latest and greatest tool for accelerating the time to market for your latest project. Just like Moore's Law has accurately predicted the growth of complexity of integrated circuits over the last 30 years or so, numerous studies have demonstrated exponential growth in embedded code size and complexity over the same period of time. Through each new project cycle, embedded systems designers are being faced with shrinking project schedules, increasing expectations of productivity improvements, pressure from offshore developers and the certain guarantee that the next project will be more complex than the previous one.

What's a poor developer to do? One solution that has been touted for several years are graphical design tools such as Statemate from Telelogic and ObjecTime from IBM. These are graphical design languages that generate embedded code from graphical software structures that generally follow the design rules of the Unified Modeling Language, or UML. However, general adoption of these tools, even though they hold the promise of freeing the designer from the low-level tasks of creating software, has been slow. While it is not the focus of this article to examine the reasons for the slow adoption of the next generation of software synthesis tools, it is instructive to consider one possible explanation.

Just as compiled high-level languages gradually took over from assembly language as the method of choice for software generation, we might expect that the introduction of another level of abstraction between the application code and the real-time execution of the code might be met with some degree of initial skepticism. In fact, at a paper presented at the 2000 San Jose Embedded Systems Conference – 'The Twenty-Five Most Common Mistakes with Real-Time Software Development' – Dave Stewart asserted that the number one mistake of embedded systems designers is their lack of understanding of the execution time parameters of the software that they are designing.

From this perspective, it is easy to guess that a software synthesis tool that abstracts the designer even further form the software that they are creating would have to deal with the designer's real need to generate efficient and high-quality code while still maintaining some level of understanding of the code created by the tool.

Even more than a software synthesis tool, a tool that synthesizes an operating system seems even more heretical. Commercial operating systems have built their reputations on the premise that creating and porting a real-time operating system to a new target platform is not for the faint of heart. They point out the engineer-years of effort that went into fine-tuning the RTOS to wring the last bit of performance out of the kernel tasks such as task switching and interrupt response. Thus, to propose that there is a tool capable of synthesizing a real-time operating system would obviously be met with a hefty dose of skepticism.

To investigate the use of software synthesis technology, Hill and Berger set out to use a software synthesis tool to generate an embedded real-time operating system (RTOS). Bob Zeidman of Zeidman Technologies and the creator of SynthOS agreed to take part in the project as both the supplier of the tool and advisor to the authors. We then worked together with the intention of creating a basic system, documenting our experiences for other designers who want to use software synthesis to get an embedded OS system up and running. Performance measurements were also taken to see how well the synthesized system worked.

Picking the ColdFire 5206eLITE board
The target system we chose for this project was Freescale's ColdFire 5206eLITE, which uses the MCF5206e processor. This board is readily available to use because it is the target system used in Professor Berger's embedded systems courses at the University of Washington Bothell campus.

With the addition of a PC and terminal emulation software, this board serves as a complete microcomputer. The board has a seven-segment LED display and two programmable timers. It also has expandable I/O capabilities and numerous other configurable options to suit our application. A block diagram of the MCF5205e is presented in Fig 1.


1. MCF5206e block diagram (courtesy of Freescale Corporation).
(Click this image to view a larger, more detailed version)

What is software synthesis?
The concept of software synthesis is similar that of hardware synthesis. With hardware synthesis you write a description in a high level language such as Verilog or VHDL and the hardware synthesis tool creates a low level description that is still in Verilog or VHDL. Similarly with software synthesis you write code in C using high-level primitives that look like function calls but really represent complex operating system concepts like thread creation and destruction, message passing mechanisms, and mutexes and semaphores, but much, much simpler. The output code, after synthesis, is still in C, but the synthesis tool handles all of the low-level implementation details.

For example, when you need to write a task that will be executed by the operating system, there's no need to worry about setting semaphores, mutexes, or priority flags. There's no need to consider deadlock conditions, infinite loops, or un-serviced tasks. When you need to have one task communicate with another task, you use one of several simple synthesis primitives. The synthesizer then takes your input code and synthesizes all of the appropriate low-level code according to the general system parameters that you give it. It also creates an operating system kernel with a scheduling algorithm customized to your needs. The synthesized code can be compiled and debugged using all of your existing C tools.

Implementing the system
It is perhaps a testimony to the value of SynthOS that the student doing the actual development, Matt Hill, was able to successfully complete the project and then make performance measurements on the completed embedded system. Generally speaking, porting an OS to a new platform would not be the first project that you give to a newly-minted engineering graduate.

Matt was relatively new to the embedded system world and so he had to fit a semester course in embedded systems into a few weeks. With little-to-no knowledge of embedded systems, many basic things had to be learned before work on the project could begin. For example, the project had several aspects that required the use of a timer including displaying information on the LED display and taking measurements from an A/D converter. Timers must be initialized correctly and consideration has to be given in the code for the possibility that multiple timers will trigger simultaneously.

Different processors handle interrupt service routines (ISRs) in different ways. Something we didn't initially realize is that the ColdFire processor requires that during an ISR, the interrupt must be acknowledged and other interrupts must be masked. Not properly handling interrupts in the ISRs caused errors which manifested themselves during the integration and synthesis phase of the project. This caused confusion when testing and debugging because this problem manifests itself in different ways that seem random or appear to be a hardware problem.

Once Matt mastered enough of the arcane arts that the grizzled embedded veterans know only too well, he had to turn his attention to actually using SynthOS and integrating it with the ColdFire platform to create an embedded OS. Matt discovered that an annoying shortcoming of the product is the set of vague error messages when SynthOS failed. For example, one particularly frequent error message was, 'java.lang.RuntimeException: Assertion failed.' A more meaningful error message could have said, 'Loop tasks must have a return value of void.' Matt recorded these usability issues and sent them to Bob for correcting in future revisions of the product. As an aside, this is a particularly good example of industrial/academic cooperation where both sides come out with a win.

Another issue that we believe needs to be addressed in SynthOS is that during synthesis, all comments are stripped from the generated code. Obviously, this makes debugging more difficult, making the final synthesized code harder to read.

What happened?
The specifications for this project outlined that the different types of tasks that were to be executed by the RTOS created by SynthOS had to be written, tested, and debugged individually before integration and synthesis. Once all tasks worked individually, they were to be integrated as much as possible before synthesis and without an RTOS. After as much integration testing as possible, SynthOS 'primitives' (see Table 1) must be added and all the code was to be synthesized and tested as a whole. This process of creation, testing, integration, testing, synthesis, and more testing is to find and eliminate errors as soon as they are introduced. If tasks are integrated before they are tested individually, it can be very hard to locate an error. It is also very important to test before synthesis because synthesis can introduce new errors and compound existing errors.


Table 1. SynthOS Primitives.

Once tasks were created and tested, we only had to insert these simple primitives and then create a very simple configuration file. We did not need to focus on the details of the RTOS such as the scheduling algorithms and semaphores/mutexes. The tasks we created were functions written in C (some were written in assembly language with C wrappers), designed for the operating system to call them. They generally performed a single task and then exited the function. Tasks can be anything from functions with low-level code that interact with hardware to complex functions with higher-level code. Tasks can also interact with other tasks and the operating system using the SynthOS primitives. There are several types of tasks: Initialization tasks, Loop tasks, and Call tasks. Fig 2 provides a simplified illustration as to how each type of task fits into the system.


2. Tasks in SynthOS.

Init tasks are called by the RTOS once at startup to initialize the hardware and software components of the system. Once initialization has finished, the task scheduler assumes control and continuously executes Loop tasks based on the scheduling algorithm selected in the SynthOS Project Configuration (SOP) File. Loop tasks can do many things including using SynthOS primitives to call Call tasks.

As their name might suggest, Call tasks are only executed when they are called by another task using a SynthOS primitive. Call tasks can do many things, often interacting with the hardware of the system. ISRs will generally use a non-blocking primitive to call a Call task to do the work of the ISR to minimize the time that interrupts are masked. A non-blocking call means that the Call task will be executed whenever the scheduler decides; if the Call task is given a sufficiently high priority, the task can potentially be executed right away. Inside tasks, SynthOS primitives can be used to interact with the RTOS and other tasks. When the code is synthesized, SynthOS primitives are replaced with a fair amount of code, usually related to the scheduler or TCB (task control block). Table 1 summarizes the various SynthOS primitives.

There are some limitations regarding the use of the primitives including where they can be placed and how they should be used. Blocking primitives can only be placed in the highest level of a task and cannot be placed in a function or subroutine that is called from a task. One of the repercussions of this is that recursion is not allowed. Another limitation regarding primitives is that the scheduler is not preemptive. A developer can potentially write tasks that monopolize CPU time. In order to have a responsive system, it is sometimes necessary to put in SynthOS_sleep() calls throughout long blocks of code so other tasks can be scheduled. Once SynthOS primitives are inserted into tasks, all that is left to do is create the SOP (SynthOS Project configuration) file. The SOP file is a simple text file with a .SOP extension used to allow the developer to customize the RTOS that will be synthesized. In this file, every source code file is listed, every task is listed, and some important options are included. Table 2 lists some of the options that can be specified in the SOP file.


Table 2. SOP file options.
Table 3 shows some example code generated by the synthesis process. The left half of the table has the original code used as input and the right half presents the result of the synthesis operation. As can be seen, a single primitive is synthesized into many lines of code related to the management of the RTOS, all of it thankfully without needing the user's assistance or expert knowledge.


Table 3. Original code (left) and synthesized code (right).

Synthesize tasks
Once the tasks were tested and proved to be working independently, Matt began the infamous part of the project, known and feared by experienced embedded systems designers everywhere: integrating the operating system. With SynthOS this consisted of the following:

Insertion of the primitives was a straightforward process of changing the non-OS function calls to SynthOS-compatible calls, i.e., 'convert();' to 'SynthOS_call(convert());' (as shown in Table 3). The creation of the .SOP file was very simple and Fig 3 shows a sample .SOP file. Once those steps were completed, synthesis was completed with a simple command such as, 'SynthOS filename.SOP'.


3. Sample .SOP file.

Testing and debugging the RTOS
Integration testing!!! Often death is more pleasant. But in this case things turned out to go fairly smoothly since we had done such extensive modular testing throughout the entire process.

Performance measurements
When comparing the performance of this SynthOS RTOS to the performance of another RTOS, it is very important to remember that the numbers were likely collected from different systems. Because each system can be completely different, the numbers can not accurately determine if one RTOS performs better than the other.

Another important consideration is that the SynthOS scheduling algorithm is dependant on the number of tasks. These performance tests were done with three to five (depending on the test) tasks to keep it simple and provide some baselines for comparison.

We performed a test to measure task switch time. The time was measured using an HP Logic Analyzer to monitor the signals coming off the board by 'instrumenting' the code to place markers in areas that we wanted to do performance measurements. The logic analyzer was set to monitor certain locations in the unused memory space of the processor. Single-instruction 'writes to memory' were added to the code wherever we wanted to measure a time interval. For example, the logic analyzer would record the memory write instruction that signaled the entry point to a task switch operation and then record the next write that would signal the exiting of the task switch. Since both memory writes were time stamped by the logic analyzer, we could determine the elapsed time for the operation to an accuracy of +/– 2.5 nanoseconds.

However, it should also be pointed out that this method does place a slight perturbation in the system so the actual performance would be two instruction times better than the observed time duration.

In order to measure the task switch time, we used an Init task and two Loop tasks. The Init task initialized communication with the Logic Analyzer. Each Loop task sent a signal to the Logic Analyzer and then made a SynthOS_sleep() call to allow the RTOS to perform a task switch.

For reference, the MCF5206e features, on average, 50 MIPS performance at 54 MHz. The test was performed with the on-chip I-cache enabled and we measured an average task switch time of 15 microseconds. While commercial, hand-crafted RTOS measurements would probably yield better numbers, these results are quite adequate for many real-time embedded applications.

Conclusion
The most compelling result that came out of this project was that a software developer without prior embedded or OS design experience can implement an embedded RTOS and have it function to a high level of usability. Software synthesis is similar to having a standard library of functions that you can include and have the functions always available; software synthesis allows this convenience on a larger scale. For example, SynthOS creates an entire RTOS, using the user-provided code like library calls. The generated code treats the user code as a black box. Now with software synthesis, instead of writing code to use library calls, we instead write the 'library calls' themselves and let the synthesis tools create the underlying low level code.

Overall we found that software synthesis tools perform much of the redundant work of repeatedly creating similar code. This reduces errors and testing because the tools have already been established as working. Furthermore, developers can move on to creating new features for the RTOS, rather than re-creating a similar RTOS for every occasion.

Software synthesis can reduce the reliance on out-sourcing. There are situations where Company A out-sources the creation of an RTOS to a third-party Company B. Company A can instead use software synthesis tools to accomplish about the same thing. The difference is that using the synthesis tools, the RTOS is in a familiar, easy to debug, and easy to modify language, rather than object code from a third-party.

Of course, there are some downsides to software synthesis tools including overhead, compatibility concerns, and availability of support. The main downside is the same for almost any software tool: overhead from learning how to use the tool. Depending on the complexity of the tool, there can be a huge learning curve before the tool is useable. If the tool is only going to be used once, time spent learning the tool is going to be very wasteful. However, if it will be used frequently, that same tool might save a lot of time and money and allow the developers to work on different and more interesting features.

Library support is another critical issue. Off-the-shelf RTOSes, like embedded Linux, come with a large number of drivers and applications that run on the RTOS that users can simply compile into the system. SynthOS does not have such a library of code, though Zeidman Technologies is working on a tool to automatically convert existing drivers and applications for synthesis.

Another concern when using software synthesis tools is compatibility. The tool should have an input and output language that is compatible with existing software and hardware. Finally, another thing to consider is the availability of support for the tool. There should at least be an informative user's manual for the tool, and ideally someone to contact for troubleshooting.

Overall we found software synthesis to be an interesting and very useful technology. We believe that its use will grow, particularly in the embedded world, and may eventually become a mainstream method of software development.

Arnold Berger is a Senior Lecturer in the CSS Department of the University of Washington Bothell. He can be reached at ABerger@bothell.washington.edu.

Mathew Hill is a recent graduate of the University of Washington Bothell. He can be reached at mjmthere@hotmail.com.

Bob Zeidman is the president of Zeidman Technologies. He can be reached at bob@zeidman.biz.