It may be difficult for a software developer to pick up FPGA hardware programming. Here I explain a few important concepts that I wish someone had explained to me.
This post began my FPGA journey as an absolute beginner that I then was.
Several months later, a dozen books read, couple of very simple toy projects worked upon, a bunch of devices experimented with, I have better understanding of why it was so difficult, and would like to share the experience with you.
All of it may be in the books, but the books tend to either assume the electrical engineering background or stay clear off the hardware territory. Either way, it does not help a software developer. It was an occasional diagram here or a stray sentence there that lit the bulb for me. The few concepts I found most important and try to explain as clear as I can from a software developer's perspective.
There are three parts in this article. This first part is about the concept of time. The second part is about registered logic, and the third - code patterns and inferred behavior. A minimal VHDL knowledge is expected from the reader.
VHDL is not a regular (imperative) programming language. A program in VHDL is not a sequence of instructions to be executed by some underlying machine. VHDL program is a schematic of a circuit. Nothing moves there, nothing is "executed". Once VHDL program is compiled, the circuit is quite literally cast in copper, and then only electric current flows through the wires.
Let me repeat. There is nothing there but electric wires. The entire purpose of the circuit that you build is to transform input signals into output signals in a specific manner. So that the following VHDL fragment
c <= b; d <= a;
For this simple example it may look obvious, but here is another one, what do you think happens when you compile
c <= a * b;
Number theory is well explored and the multiplication algorithms may be known, but the resulting circuit is still a twisted mile of wires. It is so complicated because from the assignment that we wrote it appears that we wanted the product output to appear on the wire at the same time as the inputs are set.
The biggest hurdle of hardware programming is time.
Time means very different thing in hardware, than it does in software. For a software developer writing a program, time flows from one line of code to the next, each line happening conceptually in a separate instant. We read it as "and then ...".
For example, as a software developer, what could you say about time in the following C-like code fragment ?
Most likely your intuition will tell you that instant 1 < instant 2 < instant 3 (execution order) and duration 1 ~ duration 2 < duration 3 (estimated complexity) and that's about as much as we typically care about time in software world.
In a regular program time flows down and the lines are executed in order. In VHDL the flow of time is orthogonal, it flows along the lines, while the lines themselves happen at the same time.
Let's look at the simplest VHDL fragment again:
When you realize that each assignment is a transmission of a signal over a wire, it becomes even more interesting.
First, it's not even an "assignment" in a variable sense, the outputs are not "assigned". They just repeat the input after a short delay. It makes no difference if it is a one, zero, or a sine wave:
Then there is an issue of "same time". It takes time for the signals to propagate through the wires and the propagation delays vary. Remember the multiplication mess ? If you look closely, it appears that the integers being multiplied are actually represented as one wire per bit, and different bits take different paths through the circuit:
Therefore, even if all the input bits were asserted at the same time, the output bits may get out at different times. The nanosecond difference may sound small, but it is real, it accumulates, and becomes crucial, when you realize that one circuit's output is another's input:
Then here is the critical question:
How does a circuit know when its inputs are ready ?
It doesn't. Depending on when and in which order the input signals arrive, the circuit's output will go through transitions, some of which are useless, and some could even be semantically impossible. The output of the second circuit will therefore be garbage, before the inputs have settled. It is only after the inputs are stable plus some internal delta, when a circuit's output makes sense.
You can combine any number of circuits by wiring them together (this is called combinatorial in VHDL), but you have to pay attention to the timings and signal propagation, to know which signal arrives to its destination when, and when the output actually becomes usable.
Time flow is therefore the most important part of VHDL program. It is not mentioned in the actual code for the most part, but you just have to see it there to be able to reason about the circuit's behavior.
Thank you for reading, and the next parts of this article discuss: