Introduction
Natural languages are carriers of culture. They are intrinsically related to unique ways of thinking, behaving and communicating. For example, try to imagine British humour without English. Or bossa nova without brazilian Portuguese. It would not be the same thing.
I believe that a similar observation can be made for programming languages, At some fundamental level, all programming languages may be equal, but in practice they are completely different. A particular language has a decisive influence on the way a programmer thinks about a problem and its possible solutions.
In a previous essay, I have referred to MyHDL as "A Python-based HDL". Actually, it is much stronger than that: MyHDL is Python. Technically, MyHDL is implemented as Python library, not as a separate language. Moreover, in the design of MyHDL I have tried to adhere to the Python philosophy. Therefore, in this essay I would like to talk about Python itself.
Python
Those not familiar with British humour may associate "Python" with a dangerous snake. However, the name really refers to Monty Python, the British surreal comedy group. Guido van Rossum, Python's creator, was such a big fan that he named his creation after them.
In my opinion, Guido is one of the greatest programming language designers. (He is Dutch and I am Belgian and Flemish, so there is no chauvinism involved.) Guido started to work on Python in 1989 at the CWI in the Netherlands, one of the leading European research centers in the field of mathematics and theoretical computer science. Through a history of quality development and careful language design decisions, Guido turned Python into one of the most popular programming languages. It is a great success story of grassroots open source.
Python is sometimes referred to as a "scripting language." However, that qualification does it little justice. I prefer the following definition: "Python is a general-purpose, interpreted high-level programming language whose design philosophy emphasizes code readability."
The Python design philosophy is based on minimalism and modularity. The core
language is actually very small – it doesn't even have a case
statement.
Most of the action happens in libraries that are cleanly implemented in
separate namespaces. Although Python comes "batteries included" with a very
extensive standard library, you never get lost. It is easy to find and use
exactly what you need.
Python's syntax has a conventional feel to it, but sometimes the language is delightfully unconventional. For example, many languages use delimiters to define the code structure. In addition, people use guidelines for the code layout so that it matches the structure. Python turns this upside down: the code structure is inferred from the indentation. This removes the need for most delimiters and guidelines. A brilliant idea, if you ask me.
The interpreter
One of the nice features of Python is that it is an interpreter with which you can interact. It is easy to find a Python interpreter for your system if one is not already installed. You can invoke the interpreter from the command-line as follows:
jand@gamay:~$ python Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56) [GCC 4.4.3] on linux2 >>>
The >>>
prompt means that the interpreter is waiting for something to
evaluate. I use it all the time for quick calculations, for example:
>>> bin(7540) '0b1110101110100'
Now I propose to play a little with a hardware-related topic: Gray codes. A
Gray code is a binary sequence in which adjacent states differ by a single bit.
Clive Maxfield has published an excellent series of articles about Gray codes
on the EETimes website. In Part 2 he presents an algorithm to create a
Gray code of width n
out of an existing one of width n-1
. Commencing with a
Gray code represented as a table, the steps are as follows:
- Create a mirror image of the existing Gray code and place this mirror image below the original one
- Prefix the original values with 0's and the mirrored values with 1's
Let's try this out in a Python interpreter. I will represent a Gray code as a list of bit strings and rearrange the steps a little. Let's start with a gray code of width 2:
>>> G = ['00', '01', '11', '10'] >>> G ['00', '01', '11', '10']
Prefixing the original values with 0's can be done like this:
>>> G0 = ['0' + v for v in G] >>> G0 ['000', '001', '011', '010']
Did you see what happened? You can use +
to concatenate strings. Also, you
can iterate right inside list syntax. Let's do this once again, this time
prefixing with 1's:
>>> G1 = ['1' + v for v in G] >>> G1 ['100', '101', '111', '110']
These values have to be mirrored. That's easy enough:
>>> G1.reverse() >>> G1 ['110', '111', '101', '100']
Finally, we add the two newly created lists:
>>> G0 + G1 ['000', '001', '011', '010', '110', '111', '101', '100']
Et voilĂ , we now have the Gray code of width 3.
Functions
We can make the steps above reusable by wrapping them into a recursive function that we put in a file as follows:
def gray_code(n): if n == 1: return ['0', '1'] else: G = gray_code(n-1) G0 = ['0' + v for v in G] G1 = ['1' + v for v in G] G1.reverse() return G0 + G1
Now, let's import the file back into the interpreter to try it out:
>>> gray_code(4) ['0000', '0001', '0011', '0010', '0110', '0111', '0101', '0100', '1100', '1101', '1111', '1110', '1010', '1011', '1001', '1000']
Do you agree that the gray_code
function code is beautiful? In my opinion,
it is even clearer than an explanation of the algorithm in words. This is
where Python excels.
Reference models in Python
What I have presented thus far is quite relevant for HDL design. Suppose you want to verify a Gray code counter design of a realistic width. Staring at waveforms is not going to solve it. Instead, you want a self-checking testbench that gives you a green light for functional correctness. In this way, you can easily experiment with alternative implementations.
A self-checking testbench captures the behavior of the design under test and
compares it to the behavior predicted by a reference model. In turn, a
reference model describes the essential behavior of a system at the highest
possible level, without implementation concerns. Our gray_code
function is an
excellent example. It can be used directly in a self-checking MyHDL test bench
for Gray code counters.
The concept of a reference model is a powerful technique in any HDL verification flow. Therefore, let's consider how this example would translate to Verilog or VHDL. Both HDLs have support for "programming language" features, so of course it should be possible to write a Gray code reference model in them. However, I suspect that the process would be far less straightforward and the result far less readable than in Python. By going through the experience, you may start appreciating the value of Python in a verification flow (if you are not using Python already).
Of course, you shouldn't just take my word for it, so I encourage you to try it
yourself. The spec is simple: given a width n
, write a reference model in
Verilog or VHDL that returns the corresponding Gray code in a representation of
your choice. Hardware considerations are not important – just use the
language to make it as readable as possible.
Conclusion
Experienced HDL designers know that the most difficult design task is verification. Reference models play an essential role by capturing the desired behavior of the system at the highest possible level.
I have demonstrated that Python is an ideal language for writing reference models. Therefore, it is worthwhile for HDL designers to learn and use it.
Of course, we need a way to integrate reference models in the design flow. That is where MyHDL fits in. With MyHDL, you can write the implementation at the RTL level and build the verification environment to verify if against the reference model.