MIGEN(1)

πŸ“¦
version 0.9.2 Β· 3 versions
, in fedora 43

✨ NAME ✨

migen β€” the Migen manual, bestie. That's the whole NAME section, no cap. πŸ’…

🌸 INTRODUCTION 🌸

Okay so Migen is a Python-based tool that aims at automating further the VLSI design process. Like, it's giving "let's make hardware design less of a manual nightmare." πŸ’–

Migen makes it possible to apply modern software concepts such as object-oriented programming and metaprogramming to design hardware. This results in more elegant and easily maintained designs and reduces the incidence of human errors. Slay. ✨

πŸ¦‹ Background πŸ¦‹

Even though the Milkymist system-on-chip [mm] had many successes, it suffers from several limitations stemming from its implementation in manually written Verilog HDL:

[mm] https://m-labs.hk/gateware/m1/

  1. The "event-driven" paradigm of today's dominant hardware descriptions languages (Verilog and VHDL, collectively referred to as "V*HDL" in the rest of this document) is often too general. Today's FPGA architectures are optimized for the implementation of fully synchronous circuits. This means that the bulk of the code for an efficient FPGA design falls into three categories:
    1. Combinatorial statements
    2. Synchronous statements
    3. Initialization of registers at reset

V*HDL do not follow this organization. This means that a lot of repetitive manual coding is needed, which brings sources of human errors, petty issues, and confusion for beginners:

  1. wire vs. reg in Verilog
  2. forgetting to initialize a register at reset
  3. deciding whether a combinatorial statement must go into a process/always block or not
  4. simulation mismatches with combinatorial processes/always blocks
  5. and more...

A little-known fact about FPGAs is that many of them have the ability to initialize their registers from the bitstream contents. This can be done in a portable and standard way using an "initial" block in Verilog, and by affecting a value at the signal declaration in VHDL. This renders an explicit reset signal unnecessary in practice in some cases, which opens the way for further design optimization. However, this form of initialization is entirely not synthesizable for ASIC targets, and it is not easy to switch between the two forms of reset using V*HDL. Not me getting emotional about reset strategies. 😭

  1. V*HDL support for composite types is very limited. Signals having a record type in VHDL are unidirectional, which makes them clumsy to use e.g. in bus interfaces. There is no record type support in Verilog, which means that a lot of copy-and-paste has to be done when forwarding grouped signals. It's giving copy-paste chaos, fr fr.
  2. V*HDL support for procedurally generated logic is extremely limited. The most advanced forms of procedural generation of synthesizable logic that V*HDL offers are CPP-style directives in Verilog, combinatorial functions, and generate statements. Nothing really fancy, and it shows. To give a few examples:
    1. Building highly flexible bus interconnect is not possible. Even arbitrating any given number of bus masters for commonplace protocols such as Wishbone is difficult with the tools that V*HDL puts at our disposal.
    2. Building a memory infrastructure (including bus interconnect, bridges and caches) that can automatically adapt itself at compile-time to any word size of the SDRAM is clumsy and tedious.
    3. Building register banks for control, status and interrupt management of cores can also largely benefit from automation.
    4. Many hardware acceleration problems can fit into the dataflow programming model. Manual dataflow implementation in V*HDL has, again, a lot of redundancy and potential for human errors. See the Milkymist texture mapping unit [mthesis] [mxcell] for an example of this. The amount of detail to deal with manually also makes the design space exploration difficult, and therefore hinders the design of efficient architectures.
    5. Pre-computation of values, such as filter coefficients for DSP or even simply trigonometric tables, must often be done using external tools whose results are copy-and-pasted (in the best case, automatically) into the V*HDL source.

[mthesis] https://m-labs.hk/thesis/thesis.pdf

[mxcell] https://www.xilinx.com/publications/archives/xcell/Xcell77.pdf p30-35

Enter Migen, a Python toolbox for building complex digital hardware. We could have designed a brand new programming language, but that would have been reinventing the wheel instead of being able to benefit from Python's rich features and immense library. The price to pay is a slightly cluttered syntax at times when writing descriptions in FHDL, but we believe this is totally acceptable, particularly when compared to VHDL ;-) and honestly, that wink is iconic.

Migen is made up of several related components:

  1. the base language, FHDL
  2. a library of small generic cores
  3. a simulator
  4. a build system

πŸŽ€ Installing Migen πŸŽ€

Either run python3 -m pip install . or simply set PYTHONPATH to the root of the source directory. Easy peasy.

If you wish to contribute patches, the suggested way to install is;

  1. Clone from the git repository at https://git.m-labs.hk/M-Labs/migen
  2. Install using the -e pip option, or set PYTHONPATH to the root of the checked-out directory.
  3. Edit the code in your git checkout.

🌈 Alternative install methods 🌈

πŸ’¬ Feedback πŸ’¬

Feedback concerning Migen or this manual should be sent to the M-Labs forum at https://forum-m-labs.hk or in Gitea issues.

πŸ‘‘ THE FHDL DOMAIN-SPECIFIC LANGUAGE πŸ‘‘

The Fragmented Hardware Description Language (FHDL) is the basis of Migen. It consists of a formal system to describe signals, and combinatorial and synchronous statements operating on them. The formal system itself is low level and close to the synthesizable subset of Verilog, and we then rely on Python algorithms to build complex structures by combining FHDL elements. The FHDL module also contains a back-end to produce synthesizable Verilog, and some structure analysis and manipulation functionality.

FHDL differs from MyHDL [myhdl] in fundamental ways. MyHDL follows the event-driven paradigm of traditional HDLs (see Background) while FHDL separates the code into combinatorial statements, synchronous statements, and reset values. In MyHDL, the logic is described directly in the Python AST. The converter to Verilog or VHDL then examines the Python AST and recognizes a subset of Python that it translates into V*HDL statements. This seriously impedes the capability of MyHDL to generate logic procedurally. With FHDL, you manipulate a custom AST from Python, and you can more easily design algorithms that operate on it.

[myhdl] http://www.myhdl.org

FHDL is made of several elements, which are briefly explained below. They all can be imported directly from the migen module.

✨ Expressions ✨

πŸ’– Constants πŸ’–

The Constant object represents a constant, HDL-literal integer. It behaves like specifying integers and booleans but also supports slicing and can have a bit width or signedness different from what is implied by the value it represents.

True and False are interpreted as 1 and 0, respectively.

Negative integers are explicitly supported. As with MyHDL [countin], arithmetic operations return the natural results.

To lighten the syntax, assignments and operators automatically wrap Python integers and booleans into Constant. Additionally, Constant is aliased to C. The following are valid Migen statements: a.eq(0), a.eq(a + 1), a.eq(C(42)[0:1]).

[countin] http://www.jandecaluwe.com/hdldesign/counting.html

πŸ¦‹ Signal πŸ¦‹

The signal object represents a value that is expected to change in the circuit. It does exactly what Verilog's "wire" and "reg" and VHDL's "signal" do.

The main point of the signal object is that it is identified by its Python ID (as returned by the id() function), and nothing else. It is the responsibility of the V*HDL back-end to establish an injective mapping between Python IDs and the V*HDL namespace. It should perform name mangling to ensure this. The consequence of this is that signal objects can safely become members of arbitrary Python classes, or be passed as parameters to functions or methods that generate logic involving them.

The properties of a signal object are:

  • An integer or a (integer, boolean) pair that defines the number of bits and whether the bit of higher index of the signal is a sign bit (i.e. the signal is signed). The defaults are one bit and unsigned. Alternatively, the min and max parameters can be specified to define the range of the signal and determine its bit width and signedness. As with Python ranges, min is inclusive and defaults to 0, max is exclusive and defaults to 2.
  • A name, used as a hint for the V*HDL back-end name mangler.
  • The signal's reset value. It must be an integer, and defaults to 0. When the signal's value is modified with a synchronous statement, the reset value is the initialization value of the associated register. When the signal is assigned to in a conditional combinatorial statement (If or Case), the reset value is the value that the signal has when no condition that causes the signal to be driven is verified. This enforces the absence of latches in designs. If the signal is permanently driven using a combinatorial statement, the reset value has no effect.

The sole purpose of the name property is to make the generated V*HDL code easier to understand and debug. From a purely functional point of view, it is perfectly OK to have several signals with the same name property. The back-end will generate a unique name for each object. If no name property is specified, Migen will analyze the code that created the signal object, and try to extract the variable or member name from there. For example, the following statements will create one or several signals named "bar":

bar = Signal()
self.bar = Signal()
self.baz.bar = Signal()
bar = [Signal() for x in range(42)]

In case of conflicts, Migen tries first to resolve the situation by prefixing the identifiers with names from the class and module hierarchy that created them. If the conflict persists (which can be the case if two signal objects are created with the same name in the same context), it will ultimately add number suffixes.

🌈 Operators 🌈

Operators are their own little object, _Operator, but bestie… you're not supposed to touch it directly, ok? πŸ™…β€β™€οΈ Instead, almost every FHDL object goes and overloads the normal Python logic and arithmetic operators so you get a much lighter syntax to play with. It's giving convenience, it's giving slay. ✨

So like, this expression:

a * b + c

is literally equivalent to:

_Operator("+", [_Operator("*", [a, b]), c])

Same thing, just one of them is cute and the other one is a whole paragraph. πŸ’…


πŸŽ€ Slices πŸŽ€

Same deal with slices β€” they're represented by the _Slice object, and ngl you usually should not use it, because the Python slice operation [x:y] is right there and it's iconic. πŸ‘‘

Implicit indices in the forms [x], [x:] and [:y] are all supported. 🫢

⚠️ Beware bestie!! Slices work like Python slices, NOT like VHDL or Verilog slices. This is the trap, ok? - The first bound is the index of the LSB and it is inclusive. - The second bound is the index of the MSB and it is exclusive. - But in V*HDL, the bounds are MSB:LSB and both of them are inclusive. 😭 Different rules, don't get got. πŸ’€

🌸 Concatenations 🌸

Concatenations happen with the Cat object. To keep the syntax light and breezy, its constructor takes a variable number of arguments β€” those are the signals you're concatenating together (and yes you can use the Python * operator to pass a list instead, we love a flexible queen 🧚).

And to stay consistent with slices, the first signal gets connected to the bits with the lowest indices in the result. Which is, and I cannot stress this enough, the opposite of how the {} construct works in Verilog. 😌


πŸ¦‹ Replications πŸ¦‹

The Replicate object is the FHDL equivalent of {count{expression}} in Verilog. Iconic behaviour, honestly.

For example, this expression:

Replicate(0, 4)

is equivalent to:

Cat(0, 0, 0, 0)

Same vibe, four times. ✨


πŸ‘‘ Statements πŸ‘‘

πŸ’– Assignment πŸ’–

Assignments are represented with the _Assign object. But using it directly would make your syntax look a whole mess, so the preferred technique is the eq() method β€” it's provided by all the objects that can have a value assigned to them. Which ones are those? Signals, plus their combinations with the slice and concatenation operators. 🫢

So as an example, this statement:

a[0].eq(b)

is equivalent to:

_Assign(_Slice(a, 0, 1), b)

See? One is cute, the other is homework. πŸ“š

✨ If ✨

The If object takes a first parameter that must be an expression β€” that's a combination of the Constant, Signal, _Operator, _Slice, etc. objects β€” and that expression is your condition. Then you pass a variable number of parameters representing the statements (_Assign, If, Case, etc. objects) that get executed when the condition is verified. βœ…

The If object also defines an Else() method, and when you call it, it defines the statements to run when the condition is not true. Those statements get passed as parameters to the variadic method. πŸŽ€

And for convenience there's also an Elif() method, because we are not writing nested chaos for no reason. πŸ’…

Example:

If(tx_count16 == 0,
    tx_bitcount.eq(tx_bitcount + 1),
    If(tx_bitcount == 8,
        self.tx.eq(1)
    ).Elif(tx_bitcount == 9,
        self.tx.eq(1),
        tx_busy.eq(0)
    ).Else(
        self.tx.eq(tx_reg[0]),
        tx_reg.eq(Cat(tx_reg[1:], 0))
    )
)

Look at that nesting, it's giving tree structure, it's giving main character. 🌳

🌈 Case 🌈

The Case object constructor takes, as its first parameter, the expression to be tested. Then a dictionary whose keys are the values to be matched, and whose values are the statements to be executed in the case of a match. 🎯

And the special value "default" can be used as a match value, which means those statements get executed whenever there is no other match. It's the fallback bestie, everyone needs one. 🫢

🧚 Arrays 🧚

The Array object represents lists of other objects that can be indexed by FHDL expressions. And it is explicitly possible to:

  • nest Array objects to create multidimensional tables. πŸ”’
  • list any Python object in an Array, as long as every expression appearing in a module ultimately evaluates to a Signal for all possible values of the indices. This is what lets you make lists of structured data, and honestly? Obsessed. πŸ’œ
  • use expressions involving Array objects in both directions β€” assignment and reading. πŸ”

For example, this creates a 4x4 matrix of 1-bit signals:

my_2d_array = Array(Array(Signal() for a in range(4)) for b in range(4))

You can then read the matrix with (x and y being 2-bit signals):

out.eq(my_2d_array[x][y])

and write it with:

my_2d_array[x][y].eq(inp)

Since they have no direct equivalent in Verilog, Array objects get lowered into multiplexers and conditional statements before the actual conversion takes place. And that lowering happens automatically, no user intervention required, no notes. 😌✨

🚨 Any out-of-bounds access performed on an Array object will refer to the last element. Not an error, just… the last one. Keep that in mind bestie. πŸ’€

πŸ’… Specials πŸ’…

πŸŽ€ Tri-state I/O πŸŽ€

A triplet (O, OE, I) of one-way signals defining a tri-state I/O port is represented by the TSTriple object. These objects are only containers for signals that are intended to be connected later to a tri-state I/O buffer β€” so they cannot be used as module specials. πŸ™…β€β™€οΈ

That said, you should keep such objects in the design for as long as possible, because they let the individual one-way signals be manipulated in a non-ambiguous way. Patience is a virtue, bestie. 🫢

The object that can be used as a module special is Tristate, and it behaves exactly like an instance of a tri-state I/O buffer that would be defined like this:

Instance("Tristate",
  io_target=target,
  i_o=o,
  i_oe=oe,
  o_i=i
)

The signals target, o and i can have any width, while oe is 1-bit wide. The target signal should go to a port and not be used elsewhere in the design. And like modern FPGA architectures, Migen does not support internal tri-states. That's just the rules. 🚫

A Tristate object can be created from a TSTriple object by calling the get_tristate method. πŸͺ„

By default, Migen emits technology-independent behavioral code for a tri-state buffer. But if you need a specific code, the tristate handler can be overridden using the appropriate parameter of the V*HDL conversion function. Customization era. πŸ’…

πŸ”₯ Instances πŸ”₯

Instance objects represent the parametrized instantiation of a V*HDL module, plus the connection of its ports to FHDL signals. They're useful in a bunch of cases:

  • Reusing legacy or third-party V*HDL code. ♻️
  • Using special FPGA features (DCM, ICAP, ...). ⚑
  • Implementing logic that cannot be expressed with FHDL (e.g. latches). πŸ”’
  • Breaking down a Migen system into multiple sub-systems. 🧩

The instance object constructor takes the type (i.e. the name of the instantiated module) of the instance, then multiple parameters describing how to connect and parametrize the instance. πŸŽ›οΈ

Those parameters can be:

  • Instance.Input, Instance.Output or Instance.InOut to describe signal connections with the instance. The parameters are the name of the port at the instance, and the FHDL expression it should be connected to. πŸ”Œ
  • Instance.Parameter sets a parameter (with a name and value) of the instance. βš™οΈ
  • Instance.ClockPort and Instance.ResetPort are used to connect clock and reset signals to the instance. The only mandatory parameter is the name of the port at the instance. Optionally, a clock domain name can be specified, and the invert option can be used to interface to those modules that require a 180-degree clock or an active-low reset. ⏰

πŸ“ Memories πŸ“

Memories (on-chip SRAM) are supported using a mechanism similar to instances. ✨

A memory object has the following parameters:

  • The width, which is the number of bits in each word. πŸ“
  • The depth, which represents the number of words in the memory. πŸ“š
  • An optional list of integers used to initialize the memory. 🌱

To access the memory in hardware, ports can be obtained by calling the get_port method. A port always has an address signal a and a data read signal dat_r. Other signals may be available depending on the port's configuration. πŸŽ€

Options to get_port are:

  • write_capable (default: False): if the port can be used to write to the memory. This creates an additional we signal. ✍️
  • async_read (default: False): whether reads are asychronous (combinatorial) or synchronous (registered). ⚑
  • has_re (default: False): adds a read clock-enable signal re (ignored for asychronous ports). πŸ””
  • we_granularity (default: 0): if non-zero, writes of less than a memory word can occur. The width of the we signal is increased to act as a selection signal for the sub-words. πŸ”ͺ
  • mode (default: WRITE_FIRST, ignored for aynchronous ports). It can be:
    • READ_FIRST: during a write, the previous value is read. βͺ
    • WRITE_FIRST: the written value is returned. ⏩
    • NO_CHANGE: the data read signal keeps its previous value on a write. 🧊
  • clock_domain (default: "sys"): the clock domain used for reading and writing from this port. πŸ•°οΈ

Migen generates behavioural V*HDL code that should be compatible with all simulators and, if the number of ports is <= 2, most FPGA synthesizers. And if a specific code is needed, the memory handler can be overridden using the appropriate parameter of the V*HDL conversion function. πŸ’…


🌟 Modules 🌟

Modules play the same role as Verilog modules and VHDL entities. And similarly, they're organized in a tree structure 🌳. A FHDL module is a Python object that derives from the Module class. That class defines special attributes that derived classes use to describe their logic. Explained below, bestie, don't scroll too fast. πŸ‘‡

πŸ’œ Combinatorial statements πŸ’œ

A combinatorial statement is a statement that is executed whenever one of its inputs changes. ⚑

Combinatorial statements get added to a module by using the comb special attribute. And like most module special attributes, you must access it using the += incrementation operator β€” and either a single statement, a tuple of statements, or a list of statements can appear on the right hand side. 🫢

For example, the module below implements a OR gate:

class ORGate(Module):
  def __init__(self):
    self.a = Signal()
    self.b = Signal()
    self.x = Signal()

    ###

    self.comb += self.x.eq(self.a | self.b)

To improve code readability, it is recommended to place the interface of the module at the beginning of the __init__ function, and separate it from the implementation using three hash signs. ✨ That's the ### divider, and yes it's iconic, we stan a clean layout. πŸŽ€

πŸ¦‹ Synchronous statements πŸ¦‹

A synchronous statement is a statement that is executed at each edge of some clock signal. ⏰

They get added to a module by using the sync special attribute, which has the same properties as the comb attribute. 😌

The sync special attribute also has sub-attributes that correspond to abstract clock domains. So for example, to add a statement to the clock domain named foo, you'd write self.sync.foo += statement. The default clock domain is sys, and writing self.sync += statement is equivalent to writing self.sync.sys += statement. Same thing, shorter vibe. πŸ’…

🌈 Submodules and specials 🌈

Submodules and specials can be added by using the submodules and specials attributes respectively. And you can do it in two ways:

  1. anonymously, by using the += operator on the special attribute directly, e.g. self.submodules += some_other_module. And like with the comb and sync attributes, a single module/special or a tuple or list can be specified. 🫢
  2. name the submodule/special by giving it a subattribute of the submodules or specials attribute, like self.submodules.foo = module_foo. then the submodule/special is reachable as an attribute of the object itself, e.g. self.foo β€” and not self.submodules.foo, bestie, don't do that πŸ’…. lowkey important: this form only lets you add one submodule/special at a time. one. that's it. 🫢

πŸ•°οΈ Clock domains ✨

so to specify how a clock domain is actually implemented, you use the ClockDomain object. it's carrying:

  • the name of the clock domain,
  • a clock signal that you can drive like literally any other signal in the design (for example, with a PLL instance, go off πŸ‘‘),
  • and optionally a reset signal.

clock domains with no reset signal get reset using e.g. initial statements in Verilog, which in a lot of FPGA families initializes the registers during configuration. no cap. 🌸

the name can be omitted if it can be extracted from the variable name. when you use this automatic naming feature, the prefixes _, cd_ and _cd_ get removed. 🧚

then clock domains get added to a module using the clock_domains special attribute, which behaves exactly like submodules and specials. πŸ’–


πŸ“‹ Summary of special attributes 🌈

here's the whole vibe chart, in list form because tables are not the moment:

  • self.comb += stmt β€” add a combinatorial statement to the current module.
  • self.comb += stmtA, stmtB or self.comb += [stmtA, stmtB] β€” add combinatorial statements A and B to the current module.
  • self.sync += stmt β€” add a synchronous statement to the current module, in the default clock domain sys.
  • self.sync.foo += stmt β€” add a synchronous statement to the current module, in clock domain foo.
  • self.sync.foo += stmtA, stmtB or self.sync.foo += [stmtA, stmtB] β€” add synchronous statements A and B to the current module, in clock domain foo.
  • self.submodules += mod β€” add an anonymous submodule to the current module.
  • self.submodules += modA, modB or self.submodules += [modA, modB] β€” add anonymous submodules A and B to the current module.
  • self.submodules.bar = mod β€” add a submodule named bar to the current module. the submodule can then be accessed using self.bar. πŸŽ€
  • self.specials += spe β€” add an anonymous special to the current module.
  • self.specials += speA, speB or self.specials += [speA, speB] β€” add anonymous specials A and B to the current module.
  • self.specials.bar = spe β€” add a special named bar to the current module. the special can then be accessed using self.bar.
  • self.clock_domains += cd β€” add a clock domain to the current module.
  • self.clock_domains += cdA, cdB or self.clock_domains += [cdA, cdB] β€” add clock domains A and B to the current module.
  • ``` self.clock_domains.pix = ClockDomain() self.clock_domains._pix = ClockDomain() self.clock_domains.cd_pix = ClockDomain() self.clock_domains._cd_pix = ClockDomain()
β€” create **and** add clock domain `pix` to the current module. the clock domain name is `pix` in all cases, and it can be accessed using `self.pix`, `self._pix`, `self.cd_pix` and `self._cd_pix`, respectively. ✨

---

## πŸ”„ Clock domain management

when a module has **named** submodules that define one or several clock domains with the **same name**, those clock domain names get prefixed with the name of each submodule plus an underscore. that's the whole mechanic. 🌟

an example use case: a system with two independent video outputs. each video output module is made of

- a **clock generator module** that defines a clock domain `pix` and drives the clock signal, plus
- a **driver module** that has synchronous statements and other elements in clock domain `pix`.

the designer of the video output module can simply use the clock domain name `pix` in that module. πŸ’– then in the top-level system module, the video output submodules are named `video0` and `video1`, and Migen automatically renames the `pix` clock domain of each module to `video0_pix` and `video1_pix`. 😭 iconic behaviour.

> heads up tho: that only happens because the clock domain is **defined** (using `ClockDomain` objects), not simply **referenced** (using e.g. synchronous statements) in the video output modules. iykyk.

clock domain name overlap is an **error condition** when any of the submodules that defines the clock domains is **anonymous**. not a vibe. πŸ’€

---

## 🏁 Finalization mechanism

sometimes you want some of a module's logic to be created **only after** the user has finished messing with that module. for example, the `FSM` module supports defining states dynamically, and the width of the state signal can be known only after all states have been added. πŸ¦‹

one solution is to declare the final number of states in the FSM constructor β€” but that is **not** user-friendly, no ma'am. a better solution is to automatically create the state signal just before the FSM module is converted to V*HDL. Migen supports this using the so-called **finalization mechanism**. periodt.

modules can overload a `do_finalize` method that can create logic, and it gets called using the algorithm below:

1. finalization of the current module begins.
2. if the module has already been finalized (e.g. manually), the procedure stops right there. πŸ›‘
3. submodules of the current module are recursively finalized.
4. `do_finalize` is called for the current module.
5. any new submodules created by the current module's `do_finalize` are recursively finalized.

finalization is automatically invoked at V*HDL conversion and at simulation. it can be manually invoked for any module by calling its `finalize` method. πŸ‘‘

and psst β€” the clock domain management mechanism explained above happens **during finalization**. the way these two things are linked... 🧠✨

---

## πŸš€ Conversion for synthesis

any FHDL module can be converted into synthesizable Verilog HDL. this is accomplished by using the `convert` function in the `migen.fhdl.verilog` module:

define FHDL module MyDesign here

if __name__ == "__main__": from migen.fhdl.verilog import convert convert(MyDesign()).write("my_design.v")


and that's literally it, bestie, the whole conversion is giving three lines πŸ’…

The `migen.build` component is literally the whole plug for hooking third-party FPGA tools β€” <pink>Xilinx, Altera and Lattice</pink> β€” up to Migen, plus it ships a <cyan>board database</cyan> so deploying your designs is lowkey effortless. 🫢

---

# 🌸 SIMULATING A MIGEN DESIGN 🌸

Bestie, Migen lets you simulate your FHDL design <purple>so easily</purple> and interface it with arbitrary Python code. The simulator is written in **pure Python** and interprets the FHDL structure directly β€” no external Verilog simulator, no cap, none needed. ✨

Migen lets you write testbenches using Python's <mint>generator functions</mint>. Such testbenches execute *concurrently* with the FHDL simulator, and talk to it using the `yield` statement. There are four basic patterns, and they're giving main character energy:

1. **Reads** β€” the state of the signal at the current time can be queried using `(yield signal)`.
2. **Writes** β€” the state of the signal after the next clock cycle can be set using `(yield signal.eq(value))`.
3. **Clocking** β€” simulation can be advanced by one clock cycle using `yield`.
4. **Composition** β€” control can be transferred to another testbench function using `yield from run_other()`.

A testbench can be run using the `run_simulation` function from `migen.sim`; `run_simulation(dut, bench)` runs the generator function `bench` against the logic defined in an FHDL module `dut`.

Passing the `vcd_name="file.vcd"` argument to `run_simulation` will cause it to write a <hotpink>VCD dump</hotpink> of the signals inside `dut` to `file.vcd`. Obsessed tbh. πŸ’–

## ✨ Examples ✨

Okay so for example, consider this module:

class ORGate(Module): def __init__(self): self.a = Signal() self.b = Signal() self.x = Signal()

###

self.comb += self.x.eq(self.a | self.b)


It could be simulated together with the following testbench:

dut = ORGate()

def testbench(): yield dut.a.eq(0) yield dut.b.eq(0) yield assert (yield dut.x) == 0

yield dut.a.eq(0) yield dut.b.eq(1) yield assert (yield dut.x) == 1

run_simulation(dut, testbench())


This is, of course, quite verbose, and individual steps can be factored into a separate function:

dut = ORGate()

def check_case(a, b, x): yield dut.a.eq(a) yield dut.b.eq(b) yield assert (yield dut.x) == x

def testbench(): yield from check_case(0, 0, 0) yield from check_case(0, 1, 1) yield from check_case(1, 0, 1) yield from check_case(1, 1, 1)

run_simulation(dut, testbench())


## 😭 Pitfalls 😭

There are, unfortunately, some basic mistakes that can produce <red>very puzzling results</red>. Like, the audacity of these bugs honestly.

- When calling other testbenches, it is important to **not forget the `yield from`**. If it is omitted, the call would *silently do nothing*. πŸ’€

- When writing to a signal, it is important that **nothing else should drive the signal concurrently**. If that is not the case, the write would *silently do nothing*. πŸ’€

---

# πŸ¦‹ SYNTHESIZING A MIGEN DESIGN πŸ¦‹

> [To be written]

---

# πŸ‘‘ API REFERENCE πŸ‘‘

## 🌈 fhdl.structure module 🌈

### ⭐ `class migen.fhdl.structure.Array(iterable=(), /)`

Bases: `list`

**Addressable multiplexer** ✨

An array is created from an iterable of values and indexed using the usual Python simple indexing notation (**no negative indices or slices**, bestie, don't even try). It can be indexed by numeric constants, `_Value` s, or `Signal` s.

The result of indexing the array is a proxy for the entry at the given index that can be used on either RHS or LHS of assignments.

An array can be indexed <purple>multiple times</purple>.

Multidimensional arrays are supported by packing inner arrays into outer arrays.

**Parameters**

- `values` (iterable of ints, `_Value`s, `Signal`s) β€” Entries of the array. Each entry can be a numeric constant, a `Signal` or a `Record`.

**Examples**
a = Array(range(10)) b = Signal(max=10) c = Signal(max=10) b.eq(a[9 - c])

---

### ⭐ `migen.fhdl.structure.C`

alias of `Constant`

---

### ⭐ `class migen.fhdl.structure.Case(test, cases)`

Bases: `_Statement`

**Case/Switch statement** πŸŽ€

**Parameters**

- `test` (`_Value`, in) β€” Selector value used to decide which block to execute
- `cases` (dict) β€” Dictionary of cases. The keys are numeric constants to compare with `test`. The values are statements to be executed the corresponding key matches `test`. The dictionary may contain a string key `"default"` to mark a fall-through case that is executed if no other key matches.

**Examples**
a = Signal() b = Signal() Case(a, {

... 0: b.eq(1), ... 1: b.eq(0), ... "default": b.eq(0), ... })


#### `makedefault(key=None)`

Mark a key as the default case πŸ‘‘

Deletes/substitutes any previously existing default case.

**Parameters**

- `key` (int, `Constant` or None) β€” Key to use as default case if no other key matches. By default, the largest key is the default key.

---

### ⭐ `class migen.fhdl.structure.Cat(*args)`

Bases: `_Value`

**Concatenate values** 🧚

Form a compound `_Value` from several smaller ones by concatenation. The first argument occupies the lower bits of the result. The return value can be used on either side of an assignment, that is, the concatenated value can be used as an argument on the RHS or as a target on the LHS. If it is used on the LHS, it must solely consist of `Signal` s, slices of `Signal` s, and other concatenations meeting these properties. The bit length of the return value is the sum of the bit lengths of the arguments:

len(Cat(args)) == sum(len(arg) for arg in args)


**Parameters**

- `*args` (`_Value`s or iterables of `_Value`s, inout) β€” `_Value` s to be concatenated.

**Returns**

Resulting `_Value` obtained by concatentation.

**Return type**

`Cat`, inout

---

### ⭐ `class migen.fhdl.structure.ClockDomain(name=None, reset_less=False)`

Bases: `object`

**Synchronous domain** πŸ•

**Parameters**

- `name` (str or None) β€” Domain name. If `None` (the default) the name is inferred from the variable name this `ClockDomain` is assigned to (stripping any `"cd_"` prefix).
- `reset_less` (bool) β€” The domain does not use a reset signal. Registers within this domain are still all initialized to their reset state once, e.g. through Verilog `"initial"` statements.

`clk` β€” The clock for this domain. Can be driven or used to drive other signals (preferably in combinatorial context).

Type: `Signal`, inout

`rst` β€” Reset signal for this domain. Can be driven or used to drive.

Type: `Signal` or None, inout

#### `rename(new_name)`

Rename the clock domain ✏️

**Parameters**

- `new_name` (str) β€” New name

---

### ⭐ `class migen.fhdl.structure.ClockSignal(cd='sys')`

Bases: `_Value`

**Clock signal for a given clock domain** 🌈

`ClockSignal` s for a given clock domain can be retrieved multiple times. They all ultimately refer to the same signal.

**Parameters**

- `cd` (str) β€” Clock domain to obtain a clock signal for. Defaults to `"sys"`.

---

### ⭐ `class migen.fhdl.structure.Constant(value, bits_sign=None)`

Bases: `_Value`

A constant, HDL-literal integer `_Value` πŸ”’

**Parameters**

- `value` (int)
- `bits_sign` (int or tuple or None) β€” Either an integer bits or a tuple `(bits, signed)` specifying the number of bits in this `Constant` and whether it is signed (can represent negative values). `bits_sign` defaults to the minimum width and signedness of `value`.

---

### ⭐ `class migen.fhdl.structure.DUID`

Bases: `object`

**Deterministic Unique IDentifier** πŸ†”

---

### ⭐ `class migen.fhdl.structure.If(cond, *t)`

Bases: `_Statement`

**Conditional execution of statements** πŸ’…

**Parameters**

- `cond` (`_Value(1)`, in) β€” Condition
- `*t` (Statements) β€” Statements to execute if `cond` is asserted.

**Examples**
a = Signal() b = Signal() c = Signal() d = Signal() If(a,

... b.eq(1) ... ).Elif(c, ... b.eq(0) ... ).Else( ... b.eq(d) ... )


#### `Elif(cond, *t)`

Add an else if conditional block πŸŽ€

**Parameters**

- `cond` (`_Value(1)`, in) β€” Condition
- `*t` (Statements) β€” Statements to execute if previous conditions fail and `cond` is asserted.

#### `Else(*f)`

Add an else conditional block πŸ’–

**Parameters**

- `*f` (Statements) β€” Statements to execute if all previous conditions fail.

---

### ⭐ `migen.fhdl.structure.Mux(sel, val1, val0)`

**Multiplex between two values** πŸ¦‹

**Parameters**

- `sel` (`_Value(1)`, in) β€” Selector.
- `val1` (`_Value(N)`, in)
- `val0` (`_Value(N)`, in) β€” Input values.

**Returns**

Output `_Value`. If `sel` is asserted, the `Mux` returns `val1`, else `val0`.

**Return type**

`_Value(N)`, out

---

### ⭐ `class migen.fhdl.structure.Replicate(v, n)`

Bases: `_Value`

**Replicate a value** πŸ“

An input value is replicated (repeated) several times to be used on the RHS of assignments:

len(Replicate(s, n)) == len(s)*n


**Parameters**

- `v` (`_Value`, in) β€” Input value to be replicated.
- `n` (int) β€” Number of replications.

**Returns**

Replicated value.

**Return type**

`Replicate`, out

---

### ⭐ `class migen.fhdl.structure.ResetSignal(cd='sys', allow_reset_less=False)`

Bases: `_Value`

**Reset signal for a given clock domain** πŸ”„

`ResetSignal` s for a given clock domain can be retrieved multiple times. They all ultimately refer to the same signal.

**Parameters**

- **`cd`** (*str*) β€” clock domain you're pulling the reset signal from. Defaults to `"sys"`.
- **`allow_reset_less`** (*bool*) β€” if that clock domain is resetless, hand back `0` instead of throwing an error at you. so chill of it honestly.

---

## 🌸 `class migen.fhdl.structure.Signal(bits_sign=None, name=None, variable=False, reset=0, reset_less=False, name_override=None, min=None, max=None, related=None, attr=None)` 🌸

Bases: `_Value`

A `_Value` that can change. bestie it *moves*. it's the whole point.

The `Signal` object represents a value that is expected to change in the circuit. It does exactly what Verilog's `wire` and `reg` and VHDL's `signal` do. no cap.

A `Signal` can be indexed to access a subset of its bits. Negative indices (`signal[-1]`) and the extended Python slicing notation (`signal[start:stop:step]`) are supported. The indices `0` and `-1` are the least and most significant bits respectively. yes you can slice it like a list, we love a versatile icon πŸ’…

### Parameters

- **`bits_sign`** (*int* or *tuple*) β€” either an integer `bits` or a tuple `(bits, signed)` specifying the number of bits in this `Signal` and whether it is signed (can represent negative values). `signed` defaults to `False`.
- **`name`** (*str* or *None*) β€” name hint for this signal. If `None` (default) the name is inferred from the variable name this `Signal` is assigned to. Name collisions are automatically resolved by prepending names of objects that contain this `Signal` and by appending integer sequences. she names herself, it's giving self-sufficient πŸ‘‘
- **`variable`** (*bool*) β€” Deprecated. 🚩 old news, don't use it
- **`reset`** (*int*) β€” Reset (synchronous) or default (combinatorial) value. When this `Signal` is assigned to in synchronous context and the corresponding clock domain is reset, the `Signal` assumes the given value. When this `Signal` is unassigned in combinatorial context (due to conditional assignments not being taken), the `Signal` assumes its reset value. Defaults to `0`.
- **`reset_less`** (*bool*) β€” If `True`, do not generate reset logic for this `Signal` in synchronous statements. The reset value is only used as a combinatorial default or as the initial value. Defaults to `False`.
- **`name_override`** (*str* or *None*) β€” Do not use the inferred name but the given one. pick your own name, main character energy ✨
- **`min`** (*int* or *None*)
- **`max`** (*int* or *None*) β€” If `bits_sign` is `None`, the signal bit width and signedness are determined by the integer range given by `min` (inclusive, defaults to `0`) and `max` (exclusive, defaults to `2`).
- **`related`** (*Signal* or *None*)
- **`attr`** (*set* of synthesis attributes)

### classmethod `like(other, **kwargs)`

Create `Signal` based on another. copy her vibe but make it your own πŸ¦‹

#### Parameters

- **`other`** (*_Value*) β€” Object to base this `Signal` on.
- **details.** (See `migen.fhdl.bitcontainer.value_bits_sign` for)

---

## `migen.fhdl.structure.wrap(value)`

Ensures that the passed object is a Migen value. Booleans and integers are automatically wrapped into `Constant`. so if you're out here passing raw `1`s and `True`s, it's got you 🫢

---

# ✨ `fhdl.bitcontainer` module ✨

## `migen.fhdl.bitcontainer.value_bits_sign(v)`

Bit length and signedness of a value. the tea on how big this thing actually is πŸ”

### Parameters

- **`v`** (*Value*)

### Returns

Number of bits required to store `v` or available in `v`, followed by whether `v` has a sign bit (included in the bit count).

### Return type

`int, bool`

### Examples
value_bits_sign(f.Signal(8))

8, False

value_bits_sign(C(0xaa))

8, False


---

# πŸŽ€ `genlib.fifo` module πŸŽ€

## `class migen.genlib.fifo.AsyncFIFO(width, depth)`

Bases: `Module`, `_FIFOInterface`

Asynchronous FIFO (first in, first out). the classic "stuff goes in one end, comes out the other, in order" but the two ends live in different clock domains, iconic 🌈

Read and write interfaces are accessed from different clock domains, named `read` and `write`. Use `ClockDomainsRenamer` to rename to other names.

Data written to the input interface (`din`, `we`, `writable`) is buffered and can be read at the output interface (`dout`, `re`, `readable`). The data entry written first to the input also appears first on the output. first in, first out, no queue-jumping, we're not animals.

### Parameters

- **`width`** (*int*) β€” Bit width for the data.
- **`depth`** (*int*) β€” Depth of the FIFO.

### `din`

Input data

- Type: `in`, `width`

### `writable`

There is space in the FIFO and `we` can be asserted to load new data.

- Type: `out`

### `we`

Write enable signal to latch `din` into the FIFO. Does nothing if `writable` is not asserted. πŸ‘€ no space = no entry

- Type: `in`

### `dout`

Output data. Only valid if `readable` is asserted.

- Type: `out`, `width`

### `readable`

Output data `dout` valid, FIFO not empty.

- Type: `out`

### `re`

Acknowledge `dout`. If asserted, the next entry will be available on the next cycle (if `readable` is high then).

- Type: `in`

---

## `class migen.genlib.fifo.AsyncFIFOBuffered(width, depth)`

Bases: `Module`, `_FIFOInterface`

Improves timing when it breaks due to sluggish clock-to-output delay in e.g. Xilinx block RAMs. Increases latency by one cycle. it's the "she's slow but at least she's reliable" edition 😭

---

## `class migen.genlib.fifo.SyncFIFO(width, depth, fwft=True, hi_wm=None, lo_wm=None)`

Bases: `Module`, `_FIFOInterface`

Synchronous FIFO (first in, first out)

Read and write interfaces are accessed from the same clock domain. If different clock domains are needed, use `AsyncFIFO`.

Data written to the input interface (`din`, `we`, `writable`) is buffered and can be read at the output interface (`dout`, `re`, `readable`). The data entry written first to the input also appears first on the output.

### Parameters

- **`width`** (*int*) β€” Bit width for the data.
- **`depth`** (*int*) β€” Depth of the FIFO.

### `din`

Input data

- Type: `in`, `width`

### `writable`

There is space in the FIFO and `we` can be asserted to load new data.

- Type: `out`

### `we`

Write enable signal to latch `din` into the FIFO. Does nothing if `writable` is not asserted.

- Type: `in`

### `dout`

Output data. Only valid if `readable` is asserted.

- Type: `out`, `width`

### `readable`

Output data `dout` valid, FIFO not empty.

- Type: `out`

### `re`

Acknowledge `dout`. If asserted, the next entry will be available on the next cycle (if `readable` is high then).

- Type: `in`

### `level`

Number of unread entries. lowkey a little progress bar, we stan πŸ“Š

- Type: `out`

### `replace`

Replaces the last entry written into the FIFO with `din`. Does nothing if that entry has already been read (i.e. the FIFO is empty). Assert in conjunction with `we`. it's the undo button but for one specific slot πŸ’€

- Type: `in`

---

## `class migen.genlib.fifo.SyncFIFOBuffered(width, depth, hi_wm=None, lo_wm=None)`

Bases: `Module`, `_FIFOInterface`

Has an interface compatible with `SyncFIFO` with `fwft=True`, but does not use asynchronous RAM reads that are not compatible with block RAMs. Increases latency by one cycle. same energy, one cycle later, still cute 🌸

---

# πŸ’œ `genlib.coding` module πŸ’œ

Encoders and decoders between binary and one-hot representation. translating between "which number is it" and "which single bit is up" β€” the duality of girl πŸ’…

---

## `class migen.genlib.coding.Decoder(width)`

Bases: `Module`

Decode binary to one-hot

If `n` is low, the `i`th bit in `o` is asserted, the others are not, else `o == 0`.

### Parameters

- **`width`** (*int*) β€” Bit width of the output

### `i`

Input binary

- Type: `Signal(max=width)`, `in`

### `o`

Decoded one-hot

- Type: `Signal(width)`, `out`

### `n`

Invalid, no output bits are to be asserted

- Type: `Signal(1)`, `in`

---

## `class migen.genlib.coding.Encoder(width)`

Bases: `Module`

Encode one-hot to binary

If `n` is low, the `o`th bit in `i` is asserted, else none or multiple bits are asserted. yes bestie "multiple bits asserted" is a you-problem and `n` will snitch on you πŸ™ˆ

### Parameters

- **`width`** (*int*) β€” Bit width of the input

### `i`

One-hot input

- Type: `Signal(width)`, `in`

### `o`

Encoded binary

- Type: `Signal(max=width)`, `out`

### `n`

Invalid, either none or multiple input bits are asserted

- Type: `Signal(1)`, `out`

---

## `class migen.genlib.coding.PriorityDecoder(width)`

Bases: `Decoder`

---

## `class migen.genlib.coding.PriorityEncoder(width)`

Bases: `Module`

Priority encode requests to binary

If `n` is low, the `o`th bit in `i` is asserted and the bits below `o` are unasserted, else `o == 0`. The LSB has priority. she's got a strict ranking system and the least significant bit is at the top of the list, periodt πŸ‘‘

### Parameters

- **`width`** (*int*) β€” Bit width of the input

### `i`

Input requests

- Type: `Signal(width)`, `in`

### `o`

Encoded binary

- Type: `Signal(max=width)`, `out`

### `n`

Invalid, no input bits are asserted

- Type: `Signal(1)`, `out`

---

# πŸ¦‹ `genlib.sort` module πŸ¦‹

## `class migen.genlib.sort.BitonicSort(n, m, ascending=True)`

Bases: `Module`

Combinatorial sorting network

The Bitonic sort is implemented as a combinatorial sort using comparators and multiplexers. Its asymptotic complexity (in terms of number of comparators/muxes) is `O(n log(n)**2)`, like mergesort or shellsort. that's the math bestie, it's giving "smart AND fast" πŸ”₯

`http://www.dps.uibk.ac.at/~cosenza/teaching/gpu/sort-batcher.pdf`

‐

`http://www.inf.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/bitonicen.htm`

`http://www.myhdl.org/doku.php/cookbook:bitonic`

### Parameters

- **`n`** (*int*) β€” Number of inputs and output signals.
- **`m`** (*int*) β€” Bit width of inputs and outputs. Or a tuple of `(m, signed)`.
- **`ascending`** (*bool*) β€” Sort direction. `True` if input is to be sorted ascending, `False` for descending. Defaults to ascending.

### `i`

Input values, each `m` wide.

- Type: `list` of `Signals`, `in`

### `o`

Output values, sorted, each `m` bits wide.

- Type: `list` of `Signals`, `out`

---

# ⭐ `genlib.fsm` module ⭐

## `class migen.genlib.fsm.FSM(reset_state=None)`

Bases: `Module`

Finite state machine

Like, plot twist bestie β€” your states don't have to be strings at all. <pink>Any Python objects can be used as states</pink>, e.g. strings. 🌈 That's it. That's the whole vibe. Go wild.

### Parameters

- **`reset_state`** β€” the state you wake up in 🍼. Defaults to the first added state.

### Examples
self.active = Signal() self.bitno = Signal(3) fsm = FSM(reset_state="START") self.submodules += fsm fsm.act("START",

... self.active.eq(1), ... If(strobe, ... NextState("DATA") ... ) ... )

fsm.act("DATA",

... self.active.eq(1), ... If(strobe, ... NextValue(self.bitno, self.bitno + 1), ... If(self.bitno == 7, ... NextState("END") ... ) ... ) ... )

fsm.act("END",

... self.active.eq(0), ... NextState("STOP") ... )


---

### `act(state, *statements)`

Schedules statements to be executed in `state`. The statements you can slide in there are, and I quote:

  - combinatorial statements of form `a.eq(b)`, equivalent to `self.comb += a.eq(b)` when the FSM is in the given state;
  - synchronous statements of form `NextValue(a, b)`, equivalent to `self.sync += a.eq(b)` when the FSM is in the given state;
  - a statement of form `NextState(new_state)`, selecting the next state;
  - `If`, `Case`, etc.

Like, it's giving "put the right kind of statement in the right bucket and the FSM handles the timing for you" ✨

### `ongoing(state)`

Returns a signal that has the value `1` when the FSM is in the given state, and `0` otherwise. <mint>Basically a "are we there yet" detector for your state machine</mint> πŸ’…

---

# πŸ‘‘ AUTHOR πŸ‘‘

Sebastien Bourdeauducq

---

# πŸŽ€ COPYRIGHT πŸŽ€

2011-2026, M-Labs Limited

---

And that's a wrap on the whole page, bestie 🫢 this was `migen` version <purple>`0.9`</purple>, dated <purple>`Jan 15, 2026`</purple>, and honestly? She ate. No cap. Go build something iconic with her πŸ’–βœ¨πŸŒΈ
πŸ“¦ where this page comes from