User Tools

Site Tools


nmigen:nmigen_hierarchy

Creating a hierarchical design

Submodules

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

A full example

This example is taken from nmigen/examples/basic/alu_hier.py.

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])

Next

In the next chapter, we will see how to test our design using nMigen's built-in simulator.

nmigen/nmigen_hierarchy.txt · Last modified: 2019/09/04 15:11 by jfng

Page Tools