In the early days of ESL, one of the rally cries was that both hardware and software engineers would finally use the same language and thus it would be possible to have a single group of engineers develop a system from concept to implementation without having to worry about what would eventually run on a processor and what would be implemented in hardware. Implementation decisions could be delayed until much later in the flow. For a while hardware engineers were worried that this would mean they would lose their jobs to the much larger numbers of software engineers available. I tried to tell people at the time that this was a fallacy and even today the high-level synthesis vendors want to try and convince us that they can synthesize nearly “the whole language”. At the same time they do not develop push button tools that would enable someone without intimate hardware knowledge to create a good implementation. So what gives?
Quite simply there are fundamental differences in the way that code is written to run on a processor and code that is intended to be implemented in hardware. The confusion point comes because they share a common syntax and semantics, but what is shared is a very specific subset of the language that conforms to particular idioms. Those differences primarily revolve around the way that they use memory.
The bad part about software is that it uses a processor that conforms to a Von Neumann architecture. This restricts the way that memory is accessed (one item at a time) and has driven into us the mentality of sequentiality. A very smart move in the early days of computing, but it creates a big problem today. Having said that, it led to the way in which software is constructed. It provided a memory access mechanism that allowed random access and over the years software has exploited that fact. If you take a look at a computer science curriculum, there are lots of courses that are basically about data structures, how to organize and manipulate data. These rely on things such as runtime storage management, linked lists, hash tables, data bases, string manipulation and so much more. In fact it is hard to think about writing any piece of software without using these kinds of constructs. Almost all of these rely on the use of pointers – things that point to where a related piece of data is. Software never needs to keep track of things such as data dependencies because they are taken care of by the language and the execution paradigm. The only time it becomes an issue is in multi-processor systems where data coherence is an issue.
Now let’s think about hardware. Pointers are one of the primary things that high-level synthesis does not allow, and as for dynamic creation of data – forget it. They will allow a pointer that is basically a handle to the beginning of an array, but don’t try and dereference it or to create data structures. Why is that? Fundamentally, hardware is throwing the notion of sequentially out of the window and so it has to be able to work out the data dependencies. If memory access happens all over the place in memory, and in parallel, then this becomes a close to impossible computational task. There is another fundamental difference. When you optimize a piece of hardware you decide the exact amount and arrangement of the physical memory which will maximize throughput, or minimize latency. This is expensive memory – memory that can be read from and written to in a single clock cycle (sometimes both in a single cycle). You do not want systems that have a more variable data size or access mechanism which would upset those optimizations. Hardware needs more structure and regularity in its data accesses. So the kinds of software that can be implemented in hardware are those that perform manipulation on a packet of data.
Recently we have heard a lot about these high-level synthesis programs being able to handle control systems, and this is true. The kinds of control we are talking about are those that primarily transfer data. Interfaces, DMA controllers and busses come to mind. These have a fairly well contained state machine that can pass data packets around a system but do not manipulate them. Most of this code is code that would never be written as software – it describes particular pieces of the hardware.
Recently, there have been several press releases that talk about different aspects of high-level synthesis. I will briefly discuss two of them. The first is from The Mathworks. They announced MATLAB coder “which enables design engineers to automatically generate readable, portable C and C++ code directly from their MATLAB algorithms”. The code that is generated is intended for embedded systems and specifically to run on a processor. In an interview, I asked them about multi-processor code and about the suitability of the code to be used for high-level synthesis. They re-iterated that the code they produce is for running on a processor. They have some support for multi-processor, but primarily targeted at single processor. They have looked at what it would take to output C suitable for high-level synthesis but have decided that such a product is not a viable product for them at this time. The restrictions they would have to put on the original MATLAB descriptions in order to be suitable for hardware would limit the power of their existing language and system. For software engineers this new coder will save them a lot of time and bridge the gap that has restricted them from doing higher-level algorithmic programming.
The second release comes from Impulse, who teamed up with Convey to create a software flow for a hybrid processor, FPGA solution. The creation of accelerators from standard C code is a very difficult task as it involves detecting which portions of the C code might be suitable for high-level synthesis and then actually managing to speed it up when they both basically share the same memory structure. I have personally worked on this problem in the past, and I know how difficult this can be. There were many programs that we could not accelerate, or required substantial rewriting to make them suitable.
C is not C is not C – they are the use of a single language to define methods that are the most suitable for a particular form of implementation. If you don’t understand the implementation method, you will never be able to write the code in the correct manner and thus software engineers are safe in their jobs and hardware engineers are safe in their jobs and high-level synthesis will continue to work or expanding the middle ground between them, but the language is not the thing that creates that middle ground!
Brian Bailey – keeping you covered