A module can reference other submodules to form a hierarchy.
Let's say we have the following module:
class Adder(Elaboratable): def __init__(self, width): self.a = Signal(width) self.b = Signal(width) self.o = Signal(width) def elaborate(self, platform): m = Module() self.o.eq(self.a + self.b) return m
If we want to add it as a submodule, we can do the following:
m.submodules.adder = Adder(4)
This will reference this instance of Adder
as a submodule of m
with the name “adder”.
Note that each instance of a module is independent. If we instantiated two adders, each one would consume resources and function in parallel.
If we wanted to add another Adder
as a named submodule, we would have to give it a different name, because “adder” is already used. Giving a name to a submodule helps to produce readable RTLIL/Verilog and VCD waveforms.
However, if don't have the need to name them, we can also add anonymous submodules:
m.submodules += [Adder(4) for i in range(3)]
Their name will then be determined by nMigen when elaborating the design.
Instantiating modules and forgetting to register them as submodules used to be a common pitfall in Migen (nMigen's predecessor).
Deriving from the Elaboratable
class saves us from this with a helpful UnusedElaboratable
warning:
alu_hier.py:36: UnusedElaboratable: <__main__.Adder object at 0x7fc3b136ed10> created but never used
This example is taken from nmigen/examples/basic/alu_hier.py.
from nmigen import * from nmigen.cli import main class Adder(Elaboratable): def __init__(self, width): self.a = Signal(width) self.b = Signal(width) self.o = Signal(width) def elaborate(self, platform): m = Module() m.d.comb += self.o.eq(self.a + self.b) return m class Subtractor(Elaboratable): def __init__(self, width): self.a = Signal(width) self.b = Signal(width) self.o = Signal(width) def elaborate(self, platform): m = Module() m.d.comb += self.o.eq(self.a - self.b) return m class ALU(Elaboratable): def __init__(self, width): self.op = Signal() self.a = Signal(width) self.b = Signal(width) self.o = Signal(width) self.add = Adder(width) self.sub = Subtractor(width) def elaborate(self, platform): m = Module() m.submodules.add = self.add m.submodules.sub = self.sub m.d.comb += [ self.add.a.eq(self.a), self.sub.a.eq(self.a), self.add.b.eq(self.b), self.sub.b.eq(self.b), ] with m.If(self.op): m.d.comb += self.o.eq(self.sub.o) with m.Else(): m.d.comb += self.o.eq(self.add.o) return m if __name__ == "__main__": alu = ALU(width=16) main(alu, ports=[alu.op, alu.a, alu.b, alu.o])
In the next chapter, we will see how to test our design using nMigen's built-in simulator.