User Tools

Site Tools


migen:migen_blinky

Hello World

The code

Before going further, let's create our very first Hello World. We'll call it blinky. Our goal here is to have a module with a led, that blinks, but at a frequency low enough to be visible from our eyes. Here is the code that we are using:

class Blinky(Module):
    def __init__(self):
        self.led = led  = Signal()
        self.counter = counter = Signal(3)
 
        self.sync += counter.eq(counter + 1)
        self.comb += led.eq(counter[2])

As we can see, we declare 2 signals:

self.led = led  = Signal()
self.counter = counter = Signal(3)

the first one represents our led. The second one is our counter that has 3 bits. which will go from 0 to 7.

To get it to increment at each clock tick, we add the fragment to the synchronous table of the module:

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

And we assign the led to the highest bit of our counter, which is the one that toggle the slowest (1/8 of the clock frequency)

self.comb += led.eq(counter[2])

Notice that the bit “2” is the 3rd register of our 3 bits counter. as “0” is the first one. The reason this statement is “combinatory” is that it does not depend on the clock. in other word “no matter what, we want our led to reflect the 3rd bit of the counter” In a real world, on a 50Mhz clock, we'd want generally to assign it to the 22nd pin or above, to see it blink. this example connects it to the third pin to make it visible on a waveform.

Testbench

With Migen, we can test a module very easily. We write for it a testbench. A module is seen as a generator. In a testbench, we issue a “yield” to make it go through one clock cycle. but the same “yield” is used to get/set values to the Signals:

As for example, let's suppose I have a touch button and I want to simulate the fact that it is pushed, here is how to write it in a testbench:

testbench(dut):
    yield dut.button.eq(1)
    yield

If I have a led, and I want to read it's value:

testbench(dut):
    led = (yield dut.led)
    yield

In our case, let say that we want to read the value of the counter as well as the state of the led for 20 cycles:

def blinky_test(dut):
    for i in range(20):
        print("{} {}".format((yield dut.counter), (yield dut.led)))
        yield

Running Hello World

Now let's put it all together:

blinky.py
from migen import *
 
class Blinky(Module):
    def __init__(self):
        self.led = led  = Signal()
        self.counter = counter = Signal(3)
 
        self.sync += counter.eq(counter + 1)
        self.comb += led.eq(counter[2])
 
 
def blinky_test(dut):
    for i in range(20):
        print("{} {}".format((yield dut.counter), (yield dut.led)))
        yield
 
dut = Blinky()
run_simulation(dut, blinky_test(dut), vcd_name="blinky.vcd")

As stated earlier, the counter would go from 0 to 7:

counter  led
dec bin
0   000   0
1   001   0
2   010   0
3   011   0
4   100   1
5   101   1
6   110   1
7   111   1

The led is always equal to the MSB of the counter, as it is assigned in a combinatory way to it.

When running the code, the testbench also generates a VCD file you can open with GTKWAVE:

As you could see, we have our counter in green, our clock in yellow, and our led signal in red. That is quite handy when debugging.

migen/migen_blinky.txt · Last modified: 2018/01/10 18:38 by po