Tuesday 16 August 2011

Programming Hardware vs Programming Software

I think after all these years of being a hardware engineer looking at the world of software engineering with a mixture of scorn, pity, bafflement and incomprehension I've put my finger on a fundamental truth.
First some background:
Hardware engineering has no problem with parallelism and concurrency. In fact all the languages used rely on this so that it can be synthesised into hardware with relative ease. However The development pace is regarded as relatively slow.
Software engineering is (mostly) built around the model of a von Neumann model of a processor randomly accessing memory. For better or for worse most languages are one way or another linked to C's assumptions and models. Now maybe it would be more correct to describe it as Turing architecture, but let's move on. These systems and ways of thought have huge problems with parallelism, but exhibit relatively rapid code development. The model of one thing must happen before another is one that is easy to deal with and therefore debug.
What strikes me is this falls down in complex systems where you actually are quite happy with things happening at the same time, but I haven't been able to put this into words. It became clear to me today trying to reverse engineer some C code that I didn't understand that I am going about this the wrong way.
I like a system to have clearly defined boundaries. A chip has input and output busses. You cannot from outside a chip package get inside and modify the internal registers without going through the interface, it just physically isn't possible. Well defined interfaces are not only desirable, they are all that is physically possible.
Now if you talk to a software engineer they will say they like well defined interfaces too. They like structured code and hate things that cause spaghetti. I realised today that if that were true then the world of software would be very different.
At the most simple level it is considered a good thing that I can in some code initialise a bunch of data structures and then call a do_frame() function that accesses unknown elements of a variety of objects and produces a result. Worse than that there are many situations where hidden away in hundreds of lines of code is a little reference to an object that pulls in the thing that does all the work. Throw in globals, pointers and complex objects and the aim seems to be not only to abstract out the complexity (which is good) but abstract out the functionality (which is bad). This is I think the important point that in most hardware I have seen because there is a data flow and interfaces are isolated even if you have abstracted the complexity you have to show the functionality. A hardware block cannot sneakily change the contents of your SDRAM unless you explicitly connect it up.

Now don't think that I'm trying to say hardware methodology is superior, also don't try and say that this is the difference between good code and bad code. Perhaps it is good language vs bad language, but whenever I complain about C's memory model and the dangers of how it handles structures and pointers I am told that that sort of functionality is possible in any useful language. Granted verilog will allow you to do cross hierarchy references of the type that will allow you to break hierarchy, but the syntax to do that is so obvious and rarely used that it stands out like a sore thumb when it is used and therefore isn't used except in very rare circumstances. Languages like C seem to hide this sort of trickery and therefore programmers embrace it, or at least excuse it's existance.


So I guess that has been my epiphany, that software is written to hide complexity in all its forms. Hardware has its complexity constrained by the physical need to have defined interfaces and so has complexity hidden by use of hierarchy.
As a side note I've long thought that each individual engineer will make things just as complicated as he is able to understand it. Even when they try and make things simpler they'll often do that to reflect what they think is complex potentially introducing what another person would see as extra complexity to achieve this. Further given those two forces a system will converge to be slightly more complex than the team of people working on it can comprehend. I take great pride in trying to make things simple, but realise that often I must spend so much time trying to break things down that I can't see the wood for the trees. Sometimes you read others code and it is complex, but succinct.
Well I guess if this was easy then anyone could do it.

No comments: