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 wrote | What happens |
|---|---|
| A setting name that is misspelled | Accepted 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 wire | Assigning 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 argument | It 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 writer | It captures the old value. Build first, mirror out afterwards - modules, wires and logic has the rule. |
What is not allowed yet
| Not allowed | Instead |
|---|---|
Reading a child module's wire in _logic | Connect 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 y | Assign 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 >> b | Shift 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 self | Name 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