Writing Verilog code using Python with Migen.

Many people who never have used a Hardware Description Language (HDL), either Verilog or VHDL, thinks that these languages are weird. Actually, being able to generate code that is executed in parallel make the head of software developers explode, yes devs, we can execute all we want at the same time. Few years ago, FPGA designer are viewed like if they write its code using only type D registers and logic gates, which is essentially true, but we have some help, the hardware description languages, that translate into type D registers and logic gates, operations that are familiar for all of us like if of switch..case instructions. Still we have some limitations, for example with the for instruction, which is an special case, and it has a different meaning than in software development. Fortunately, now exists many tools that democratize the FPGA development, creating an additional extraction layer between the user and the HDL. In this blog we already talk about MATLAB HDL coder, that allow the user implement its Simulink diagrams into an HDL code, or Litex, that allows us to generate designs using different softcores using a simple Python description, but Litex does not work alone, at the same time, it is based on a tool that generates HDL code from a Python code, its name is Migen. Lets take a look how it works.

According its official webpage,

Migen is a Python-based tool that automates further the VLSI design process.

The word “automates” it’s for me the key of Migen but, how it works? Migen are a set of Python libraries those translates a Python code into a Verilog code. All the module code is defined within a Python class. The code, like Verilog, can be structured in a first block where all the signals are generated, then other block where all the combinational logic is defined, and finally a third block with all the combinational logic. In addition, since we are writing a Python code, we will need to import the corresponding libraries. To write Verilog code, we will need to add these lines to our Python code.

from migen import *
from migen.fhdl import verilog

The first one will import all the Migen classes, and the second one is specificil to generate the Verilog code.

Regarding the signals definition, it is as simple as declare a signal as Signal(), for example, if we want to declare three one bit signals, we can use the next code.

a = Signal()
b = Signal()
c = Signal()

In order to declare more complex signals, we will add arguments to the Signal class, for example to create a counter with a 16 bits width, and a reset value of 1000, we will use the next instruction

counter = Signal(25, reset=1000)

Now that we have a signal declaration, we can add some combinational logic. All the combinational logic of the design is added to the structure comb. For example, if we want to assign to a the negate of itself, we have to add this

self.comb += a.eq(~a)

Also we can operate with those signals, for example we can and the signals and assign the result to c

self.comb += c.eq(a & b)

For the sequential blocks, we have to use the same method than the combinational blocks, but using the structure sync instead.

self.sync += eq.counter(counter + c)

If we want to use other kind of instructions like if or case structures, we have to add all the code of the instruction in the same line.

With these basic structures, we can start to test Migen. In order to have a clean installation, I have used an Ubuntu docker container, but it will work also if you are using your own PC with Linux. First of all, we need to create the docker container, and add a shared folder with the host.

docker run -it -v ~/temp_dock:/shared --name migen ubuntu

Then, inside the docker, since my Ubuntu installation is completely clean, I have to install some tools like git and python

apt update
apt install git
apt install python3
apt install python3-pip

Now, we have to clone the Migen repository from Github, and also I have made a checkout to the last release, that now is the 0.9.2.

git clone https://github.com/m-labs/migen.git
cd migen
git checkout 0.9.2

Finally, we can install Migen.

python3 ./setup.py install

Once we have Migen installed, we can generate our first Verilog module.The next code will generate a blink module with configurable blinking frequency. The ports of the module will be the led output and the prescaler input used to set the blinking frequency. The Python code is the next:

from migen import *
from migen.fhdl import verilog

# Module declaration
class blink(Module):
	def __init__(self, led, prescaler):
		
		# Signal declaration
		period = Signal(25)
		counter = Signal(25, reset = 0)
		
		# Combinational
		self.comb += period.eq(prescaler)
		
		# Sequential
		self.sync += If(counter == 0, led.eq(~led), counter.eq(prescaler)) \
								.Else(counter.eq(counter - 1))
					
# Inputs and output declaration			
led = Signal(reset = 0)
prescaler = Signal(25)

# Module creation
my_blink = blink(led, prescaler)
print(verilog.convert(my_blink, ios={led, prescaler}))

When we execute this Python file, the result is the next.

root@a3289ebdfd96:/shared# python3 example1/example1.py 
/* Machine-generated using Migen */
module top(
	output reg led,
	input [24:0] prescaler,
	input sys_clk,
	input sys_rst
);

wire [24:0] period;
reg [24:0] counter = 25'd0;

// synthesis translate_off
reg dummy_s;
initial dummy_s <= 1'd0;
// synthesis translate_on

assign period = prescaler;

always @(posedge sys_clk) begin
	if ((counter == 1'd0)) begin
		led <= (~led);
		counter <= prescaler;
	end else begin
		counter <= (counter - 1'd1);
	end
	if (sys_rst) begin
		led <= 1'd0;
		counter <= 25'd0;
	end
end

endmodule

The code generated works without any issue, that is the goal of Migen, but in my opinion, there are several things that I miss, for example, there are no comments in the code generated, what for a little code like this is not a big deal, but when we have a large code like the code generated by Litex, it is difficult to follow the match between the Python and the Verilog code. Also, the fact that the sys_rst in the always block is at the end of the block instead of the beginning is weird.

Despite of that, can we use Migen to generate DSP code?, The short answer is no. I have not found examples of signed, or fixed point arithmetic, so you will have to face to write in Python code that can be solved in a few lines in Verilog. Also, Migen works well to describe all digital code like soft-cores, but define an IIR filter with different widths for ports and coefficients will be so tedious and very inefficient.

In the project’s Github, we can read

Despite being faster than schematics entry, hardware design with Verilog and VHDL remains tedious and inefficient for several reasons. The event-driven model introduces issues and manual coding that are unnecessary for synchronous circuits, …

Say that HDL are tedious and inefficient to write hardware is simply false, actually is the most efficient way to describe hardware, as long as you don’t want to place the registers and LUT manually. Maybe what they want to say is that write code in Python is faster… for Python developers, which make me to think the that Migen is write by and for software developers who want or need to design some glue logic using an FPGA. Regarding the state of the project, although in the master branch there is recent activity, the last release was released in 2019, so I am not sure if the project it is still alive.

To conclude, if you are a software developer who want to play with FPGA, you can use Migen, but if you want to learn to program FPGA, and understand really how they work, don’t use Migen, and write your code using Verilog or VHDL.

Leave a Reply