2pyrtl logo2pyrtl

Writing RTL

When it goes wrong

Most of what goes wrong goes wrong loudly: the build stops and the console says what it expected. This page is how to read that, and the handful of quiet ones - where the build finishes and the hardware is not what you meant.

Reading a refusal

A message names your design, not the tool's insides: the line you wrote, the statement in your own words, and what happens to it. A warning lets the build finish; an error stops it.

WARNING: line 13: Width Mismatch: k4 = 20. Value 20 needs 5 bits and k4 has only 4, so k4 gets its low 4 bits (4).
ERROR: line 37: Shape Mismatch: y = grid. grid has 2 x 3 elements and y has 3 x 2, and an array
is assigned whole only from one of the same shape. Assign the elements in a loop, in the order you mean.

Width mismatches are the most common. An assignment fits its value to its target, and subtraction and comparisons widen the narrower operand - both with a warning that says what you get. Bitwise & | ^ across two widths stops the build instead, because there is no honest guess to make: make the widths equal first. What you can write in an expression has the whole table.

The quiet ones

What you wroteWhat happens
A setting name that is misspelledAccepted on purpose - a project may carry settings of its own - so nothing fails and nothing reads it. Your design builds with the default you believed you had changed. Check the spelling against project settings.
A name used as a moving label while it holds a declared wireAssigning to the name drives the wire it holds, rather than moving the label, so the wrong wire is driven and the build finishes. Let a for loop make the name, or write the element itself - when an assignment drives hardware.
A helper reached out of a dict, a lambda, or an argumentIt cannot be found before it runs, so it is not transformed. A write from such a helper is refused outright; one that only builds an expression is fine. Call helpers by name.
A reader placed before the writerIt captures the old value. Build first, mirror out afterwards - modules, wires and logic has the rule.

What is not allowed yet

Not allowedInstead
Reading a child module's wire in _logicConnect the child port to one of your own wires in the constructor, and use that.
Bit-select or slicing on a wire - ptr[3], ptr[2:0](ptr / 2**3) % 2 for a bit; a WireArray of one-bit wires when the individual bits carry meaning.
Assigning an array from one of another shape - self.y = self.grid with a 2 x 3 grid and a 3 x 2 or 6-long yAssign the elements in a loop, in the order you mean: self.flat[3 * r + c] = self.grid[r][c].
A wire as a shift amount - a >> bShift in stages, one per bit of the amount, each stage choosing between its input and that input shifted - the helper example on modules, wires and logic is exactly that.
//, and / or % by a wire or by a number that is not a power of two/ 2**k and % 2**k, or >> k. A divider has to be built from its steps for now, as the library's FP16_Divider does.
A _logic whose first parameter is not called selfName it self. Today the build stops with NameError: name 'self' is not defined.

These are limits of today, not of the idea; the list gets shorter with each release, and anything that changes lands here.

When it is us

Tell us

A crash, a refusal that names nothing you recognise, or hardware that is plainly not what you wrote - use the feedback button in the workspace and say what you did. It comes straight to us with the file you had open, and it is how most of the entries above were found.