Today all chip designs begin as lines of code. The code is written at a high level, hiding much of the complexity from the designer, and then synthesized into a low-level description for layout and analysis. I propose that the time is right for a similar evolution in software design.
Synthesis is the process of taking a high-level description and turning into a lower-level description that, in the case of software, can be compiled directly. By working at a higher level, the user is kept uninvolved with details that are not critical for his project. Synthesis involves automatic code generation (ACG).
If you use ACG tools, there are ways to perform debugging, none of which are ideal for code maintenance and increase rather than decrease the development effort. One solution involves changing the graphical input, rather than the generated code, during debugging. It is unlikely that many software engineers will want to continually go back to the original graphical description, modify it, regenerate the code, compile it, and run it each time a bug is discovered. This will also significantly slow down the development time.
The second solution is to modify the generated source code during debugging and later attempt to modify the graphical description to generate nearly identical code. This solution is very difficult, if at all possible, and not practical in most cases. The third solution is to ignore the original graphical description once the code has been generated and only work with the resulting code. This solution means that the original high-level graphical description is no longer correct or reusable. In this case the ACG tool is a great starting point for a software project but provides little utility after the development process has gotten underway.
I propose that software development should take a similar route as the one that has worked so well for hardware. Since programming languages are already at a fairly high level of abstraction, what is needed is a few higher-level constructs that tell an ACG tool what kind of code needs to be generated. The software synthesis tool would take in this source code as input, determine how to generate code to perform the function specified, and output the new source code. The programmer need not be concerned, for example, with how the inter-task communication is implemented. The programmer would not need to know the details of the processor architecture or the hardware of the system. The resulting code can be optimized for the particular processor, the particular hardware platform, and the particular requirements of the system. The input source code would be portable to other hardware configurations.
Software synthesis could be implemented in a number of different ways or, most likely, in some combination of them. The synthesis process might replace each pragma with a macro, with a subroutine, or with a system call to an underlying operating system. It might rearrange and optimize the code and generate task management code so that an operating system, in the formal sense, in no longer needed. The synthesis software would analyze the entire code and, with input from the user regarding things like timing requirements, task priorities, hardware configurations, and processor cycle times, determine the optimal way of implementing the requested function on a case by case basis.
Let us look at one specific example.
In a desktop system where applications can be loaded and unloaded at any time, it makes sense to use system calls to an operating system to implement task switching. In such a system, the synthesis tool would substitute system calls for the pragmas. These complex system calls use significant amounts of code for checking priorities and privileges and saving out registers that store the state of the system. These system calls can also take advantage of hardware designed to speed up context switches. But system calls are complex, difficult to debug, and highly dependent on the particular processor and hardware platform. The code tends to be slow because it takes into account all different combinations of software and attempts to guard against malicious, or poorly written code that may have been loaded by the user.
In an embedded system, the programmers have complete control over the code being loaded typically the code is compiled as a unit and permanently fixed in memory. In this case the synthesis tool might replace the pragmas with simple function calls. The synthesis tool might even synthesize a simple operating system in the form of a task manager that is optimized for all of the given tasks. The result is that both types of systems, desktop systems and embedded systems, can be represented with the same high-level code; the synthesis tool generates different output source code depending on the target.
For several decades the holy grail of system design has been hardware/software codesign whereby a system level description is automatically partitioned into hardware and software. Software source code is then generated in a language like C and a hardware description is generated in an HDL like Verilog. Rather than trying to start with a system level description, however, a simpler, more practical approach may be more feasible. By implementing software synthesis, combining it with hardware synthesis, and targeting the design to a flexible system like a platform FPGA or a structured ASIC, we could come much closer to this elusive goal. We can define system hardware and system software at a high level and allow the synthesis tools to generate two complex low-level descriptions. While this does not solve all problems with hardware/software codesign, it improves productivity, is practical with current technology, and brings us one step closer to system-level design.
Bob Zeidman is the founder and president of Zeidman Technologies (Cupertino, Calif.)