Here’s an excellent article we found over at Robin Debreuil’s blog concerning a number of VHDL concepts and implementation. It’s a great read, and we thought it’d be cool to post here for any Papilio owners and/ or VHDL coders to take a look at! Robin totally attacks VHDL, covering everything from Mealy vs. Moore state machines to bit states to clock sync issues and debugging.
“What drew me to it [VHDL], other than how totally cool it is to be able to ‘design a chip’, is that hardware has orders of magnitude fewer bugs than software. I figured the language must have something to do with that, and indeed it does. All this makes it a great language to learn, but if you don’t have the chance, here are a few concepts I’ve found so far that could potentially ‘make us better programmers’ in the C variants world… “
Hit the jump for some knowledge, or click for Robin’s article in its natural habitat. If you have anything to add, let us know in the comments section!
You often hear how much you can learn by studying a new programming language, but it seems you hear far fewer details on what you actually might learn. One reason is people often learn a similar C based language to the one they know, so it is more domain learning than anything computer science like (which is great too). Once you start moving into less familiar territory though – for example functional languages, SQL, HDLs – you’ll find volumes of concrete and practical insights. Solutions to problems you didn’t even know you were having!
VHDL is a hardware description language used for designing microchips. It is ‘declarative’, meaning it describes hardware in a way that can be synthesized – something like the way HTML describes a web page, not ‘programming’ per se. It is based on ADA syntax, so it looks quite different than C based languages. What drew me to it, other than how totally cool it is to be able to ‘design a chip’, is that hardware has orders of magnitude fewer bugs than software. I figured the language must have something to do with that, and indeed it does. All this makes it a great language to learn, but if you don’t have the chance, here are a few concepts I’ve found so far that could potentially ‘make us better programmers’ in the C variants world…
(caveat: I’m new to VHDL, totally welcome any corrections and additions here)
Type safety in VHDL in many ways is much narrower. For integers you can say:
signal test: integer -1 to 15;
and that is different than arrays of ‘bits’, which use a vector of STD_LOGIC:
data_bus: out std_logic_vector(7 downto 0); -- 8 bits
which is a different ‘type’ than
mini_bus: out std_logic_vector(6 downto 0); -- 7 bits
If you wanted to map part of that data_bus to something that had 4 bits, you would have to use something like
data_bus(7 downto 4). Imagine the tests that don’t need to be written here, and the validation you would get for free. Much increased clarity of intent, and a bit more cumbersome.
Takeaway: Types are on a dial and always stop at some arbitrary cutoff. It is very helpful to recognize where that is, as it’s an area that tends to be full of tiny cracks. I posit that the harder something is to get to compile, the less likely it is to have bugs. Flame war ensues.
|Nine States for a Bit|
It seems a bool can only ever be zero or one, on or off, has a voltage or doesn’t… but real life (which VHDL has to describe) isn’t quite that clean. There are actually nine possible states for a std_logic ‘bit’, and for completeness here they are:
‘U’, — Uninitialized
‘X’, — Forcing Unknown
‘0′, — Forcing 0
‘1′, — Forcing 1
‘Z’, — High Impedance
‘W’, — Weak Unknown
‘L’, — Weak 0
‘H’, — Weak 1
‘-’ — Don’t care
That is a mouthful, but it does expose some of the things that are glossed over in most C like languages. Bools can be (in dynamic languages) null and undefined, uninitialized, drawn from variables that don’t exist, from variables that are later assigned null, etc. Oh the tears. Even for languages where value types are auto set to defaults, they are still possibly ‘uninitialized’ – the value isn’t necessarily the initial value you intended unless you specify it.
In VHDL if you don’t clearly specify everything, you will see a lot of “XXXX”s (aka unknowns) when you simulate, and a lot of ‘WTFWTF’s when your design becomes electronics. These X’s tend to propagate as well, so you learn early to clamp things down right off the bat – which of course pays off in the long run.
The don’t care ‘-‘ is interesting, it is essentially ‘no effect’, but allows functions that do things like:
if (std_match(address, "-01---") then ...
The high impedance ‘Z’ is quite important, as it represents a wire that is virtually ‘unplugged’. When two USB devices send data over the same cable, it is important one is ‘Z’, or they will tromp all over each other. As we’ll see next, the fact that wires are only being able to handle one ‘voltage’ at a time is a big limitation, but working with this limitation really helps keep the monsters in your code at bay.
Takeaway: Things are usually more complicated than they seem, so it is important to clearly define and limit states through the life cycle of an object. Even a simple bool can set off a chain reaction of obfuscated misery. State is the enemy, and proper initialization is the first line of defense.
In VHDL, the default is one signal can only be ‘driven’ by one thing (and must be driven by one thing, as explained above). Basically this comes down to electricity again – if you have two wires connected to one pin and the first gives it 5 volts and the second 0 volts, information will be lost. So to translate,
// and somewhere else
would be an error in VHDL. Multiple assignment locations (‘drivers’) are something I’ve never consciously thought about while programming, though they were involved in plenty of bugs I’ve had to think about! Debugging gets much more difficult once you realize a problem state could be coming from a number of sources (the fact that ‘Find All References’ is one of your first debug questions tells you something already). I think we instinctively try to limit assignments with choke points, scope and visibility. If you’ve ever written threaded code this is probably even more instinctive to you. It is very helpful to stop and recognize this as a problem, and then more deliberately limit and track ‘assigners’.
Of course sometimes you need two sources for one assignment, and VHDL is no different. A common pattern is to have a ‘variable’ that indicates which wire is ‘on’ – kind of like a group of radio buttons where only one element can be selected. The ones that aren’t ‘on’ become ‘Z’, aka high impedance, aka unplugged.
target <= sourceA when control = '1' else 'Z';
target <= sourceB when control = '0' else 'Z';
Takeaway: Many of the code breakdowns you see are from ‘assignments gone wild’. Unless there is a good reason, variables (especially those that set state) should have one boss. If that isn’t possible, then assignments should have careful and scheduling that doesn’t overlap. I think this explains more than anything why most DeclarativeUI is such a disaster. It isn’t enough to limit scope or make something private. You want to limit the number of inputs, not just their locations.
In VHDL the elements of an interface declare their direction. They can be
buffer. Combined with the previous restrictions on assignment you get fine grained control and certainty over how data moves around between modules (which are akin to class instances). A common pattern is a container hooks one module’s ‘out’ to another module’s ‘in’ – this can be thought of (almost literally) as a wire connecting one chip to another.
in – input only, can not be written to from inside the module (~class)
out – output only, can not be read from inside the module
buffer – output, but can be read by the module
inout – input or output, used for bidirectional signals
Some languages in the C world have ‘get only’ and ‘set only’ properties, but that is a little different as they generally have a private holder variable with no such restrictions. There are virtually no tools to limit access to class level variables within a class – everything is ‘inout’ by default, where in VHDL that only used for bidirectional signals.
Takeaway: Data crossing class boundaries is always a bit uncomfortable in the OOP world, too much feels like spaghetti. VHDL gets around this by making it very clear what is hooked to what, who is sending and who is receiving. It makes you realize that some things are naturally hooked to external things, there is nothing wrong with that – it’s the loose way we hook them together that gets us into trouble.
In VHDL, like in most electronics, everything runs on a clock tick. You can have different clocks, but generally they will be synchronized (eg, one is every third tick of the other). You can make asynchronous multiple clocks, but everything I’ve read says never do that… unless you are a pro and know exactly why you need it. Which takes a lot of pressure off me.
Running all code off a synchronized clock tick is a really powerful technique, which comes as no surprise to any game programmers out there. Divided and multiplied clocks (though still synced) are common in VHDL, and very interesting. I’ve never tried that in a game, and think I will next time – something like
uiClk = (clk % 32 == 0) ? 1 : 0; Hmm…
Takeaway: Ticks are your friend. If you are coding anything beyond a PHP function call and haven’t used an update loop, give it a look. It simplifies many complex problems, and gives you a lot of control over states, transistions, and hairy timing issues.
It isn’t a big secret that inheritance loses its shine as it grows, and it is inspiring to know that VHDL can layout 5 billion transistors without it. In VHDL you define modules using an interface (entity) and an implementation (architecture). You then you create instances of these in higher level modules, and instances of those in higher level modules, all the way to the top. This makes it tree based, much like an OOP object created using composition only. The top level module represents the chip itself, and its interface describes the pins.
Takeaway: I’m not an inheritance hater, but certainly prefer composition over inheritance. VHDL takes you a bit further here and shows what is possible without inheritance in the way. On the flip side, other structures it has (next) suggest what you might be missing with a strict composition only OOP design.
VHDL modules define an interface (entity) and implementation code (architectures). This is somewhat akin to a class that implements an interface. What is different is you can define multiple architectures for the same entity, and then specify which one to use when you create an instance. The problem this solves for VHDL is it gets synthesized to different types of hardware, and the efficiency of low level gate structures varies a lot based on the target (yes, VHDL gives a whole new meaning to control freak, and that is before even talking about nanosecond timing attributes). This is somewhat like if you had the option of specifying the assembly code your class would generate, and then allowing instances to choose which to use.
The interesting thing about this is it allows a sort of ‘same level inheritance’. An example of where this could be useful is a button and all its offspring. Imagine a Button, ScalableButton, ToggleButton, RadioButton, ThreeStateButton, Icon etc. The traditional thing to do is make a super duper generic button class, and then everything inherits all over the place, often ignoring things defined in the parent (eg a 9box button probably doesn’t use a single background image, another button may not have text). Composition doesn’t fare much better as your Button class gets so generic that is starts looking like Object’s twin. It might be useful to be able to say
b = new Button’toggle(); and have it grab the toggle code instead of the radio button code. The real problem here is these are really just different ways of saying the same thing – they are types of buttons at the same level in the inheritance chain, so they don’t fit into a clean hierarchy.
Maybe a better example would be an ‘OK’ button on Windows vs an ‘OK’ button on Ubuntu – same thing, but you clearly need two sets of code.
Takeaway: Next time you end up not being satisfied with your inheritance chain, and feeling that a composition solution will be awkward to define and initialize, don’t beat yourself up. Maybe the real problem is they are objects at the same logical level. There are plenty of examples of things in the real world that are the same in spirit, but treated differently in practice. Think crimes of the rich, crimes of the poor.
In VHDL, concurrency is the default. You can quite easily turn an FPGA into hundreds of independent mini CPUs, or a massively parallel DSP. With single point of assignment per clock tick it actually isn’t that hard to ensure things do not explode in the way you are used to with C style concurrency. Much to be learned here.
Takeaway: Traditional approaches to concurrency that I’ve seen focus on managing resource contention, avoiding lockups, and cleaning up the inevitable messes. I haven’t seen a lot of attempts to just ensure sure things don’t collide in the first place and skip the rest. Given state is passed around like a crack pipe in C based languages that isn’t much of a surprise. VHDL suggests that sensible management isn’t as impossible as it sounds.
|Mealy and Moore State Machines|
VHDL uses finite state machines a lot, and this is already a great lesson to take back. It also distinguishes between types of state machines. With Mealy state machines the output is based on the state as well as the input, with Moore the output is based solely on the state. Obviously the Moore ones are preferred in that they are simpler and synthesizes to something cleaner and smaller, but naturally they can’t always solve the problem at hand. That is fine, but you should at least be aware of which you are using.
Often in the C variants world we aren’t even aware of tying secondary values to outputs. Some methods will just return a value based on internal class properties, some combine that with input parameters, some with states of other external objects… and worse. While this is certainly needed sometimes, we should be very aware that we are adding ‘cost’ geometrically with each expanded step. When fusing variables isn’t avoidable, we need to make sure it happens in one place, at the logical location, and the right level of abstraction.
Takeaway: In programming, code that generates cleaner assembly is usually easier to read, debug and maintain as well. The less properties are bounced around and combined the simpler and more obvious the code will be for both you and the compiler. VHDL reminds us coding is kind of like driving, where you shouldn’t move the steering wheel more than you need to.
|Built in Testing, Timing, Simulation|
This is a huge area in VHDL, and enough for another book length post. One point you can’t overlook though, is testing is built in to the language. In the C world we tend to rely on unit tests at best, but here there is much more focus on simulation. VHDL also pays great attention to what is happening over time, which is a kind of forgotten area in most of the C world (I hit a breakpoint, and see the state of the world – how it got here, the lord only knows). Scheduling is also a huge focus in the code itself, not just the testing part.
Takeaway: Verification is super important, and while we take steps to get that right there is much more to do. VHDL is a rich area to learn from in order to extend what we know. Verification is not something you just bolt on – like security it really has to be integrated into every step of the process and thinking.
|There are Fewer Bugs Because it is Harder|
Anyone who thought writing complex code is somehow better would soon be making things as obvious as possible in VHDL – the last thing you need here is intentional complexity! Debugging is *much* harder with hardware, so it forces you to build level by level, with fully tested small and functional modules. The chances of buggy code compiling is already low, but if it does run it is much more likely to fail completely. So much time is saved in a ‘fail early and fail big’ environment, but we are often stuck with, ‘fail silently and keep going’. We can still intentionally structure our code to fail big on problems, and VHDL reinforces why you would want to do that.
Takeaway: Not mentioned, but being harder also means the language attracts (or creates?) better programmers with better attitudes. It makes you ‘think like an engineer’, which surprised me because I thought I was already doing that : ).
Ultimately there are fewer bugs when there can’t be bugs, so maybe what really need to do is reverse the question. It isn’t, “why are there so few bugs in hardware”, but more, “why is it ok to have so many bugs (to different degrees) in the C variants world”? Is it culture? Is it quality of libraries/runtimes? Does it not actually matter as much? I’d love to know.
Want to learn more VHDL? I’d recommend looking at the Papillio!