Rules and their reasons
The constructs the compiler does not allow, and the reason for each
Everything QuartzHDL refuses to compile, it refuses with a message naming the line. The rules exist for one reason: a construct that the Julia model and the Verilog could interpret differently is not allowed, so that the two can never disagree. This page collects them.
Values
| rule | why |
|---|---|
| widths are at most 128 bits | UInt128 is the widest thing that is fast in Julia |
narrowing needs trunc |
dropping bits should be deliberate; the rule is on the width so it fires the same way every run |
| signed and unsigned do not mix without a conversion | which one wins would be a guess |
| a plain integer default must fit the width | a value that wraps at the declaration is a mistake, not a value |
| a bit index or slice must lie inside the word | out of range reads as zero in Julia and as x in Verilog |
| a computed-base slice must fit at every base | same |
| a shift by the width or more is an error | the result is zero whatever the input: a statement about a width that is wrong |
Declarations
| rule | why |
|---|---|
an input cannot be a Pulse, Timeout or Edge |
an input has no storage |
| a field or port name may not be a Verilog keyword | it is a net inside the emitted module |
a port attribute must be one of verilog, active |
a misspelt attribute that was ignored would leave a pin the wrong way round |
| a pad’s Verilog name is the same in every module that declares it | it is one net on both sides |
| an input may not share a field’s name | it would be two things — except a MetaGuard, which is fed by the input of its name by design |
a MetaGuard shadows a one-bit input only |
a word crossing a domain goes through a flag and held data; its bits would arrive on different cycles |
a Timeout needs a width |
Timeout alone has no size |
| an instance’s constructor takes no arguments | the wiring is stated once, in @wire |
Blocks
| rule | why |
|---|---|
| one writer per register | two writers in Verilog is a multiply-driven net |
a @wire drives its outputs on every path |
a combinational output that latches is not a wire |
a @wire writes only outputs, pads and instance inputs |
anything else is a register, and registers are clocked |
a field written by @on is not driven by @wire |
one net, one kind of driver |
no while in a block |
it would not have a fixed depth |
no local assignment inside if |
a local is a wire, so compute it above the if; a value that differs by condition is an ifelse |
an if used as a value has an else |
it is a mux, and a mux has two arms; this holds in a clause such as @reset too |
a bound of clamp, min or max fits the value’s width |
a hardware-typed bound sets the width and the value is widened to it; a plain integer takes that width and has to fit; the signedness rule of * applies |
| a condition may not make Julia branch on a hardware value | Julia would branch; the hardware cannot |
| a bare name must be a field, an input, a local, or defined in the enclosing Julia module | a typo would otherwise become a port nothing drives |
| a local may not shadow a field or an input | a silent shadow |
← is a statement, never an expression |
x ← c ? a : b would parse as (x ← c) ? a : b and lose the write |
only a statement — a write, a log macro, a @check — may sit to the right of cond && (...) |
a computed-and-discarded value is a mistake |
| a state name may not also be a field, an input or a local | a bare name would mean two things |
every state has a @state branch, or there is an @otherwise |
an unhandled state is a stuck machine |
| a sequence’s register is wide enough for its steps | it cannot hold a step it does not have room for |
@then, @when, @delay and @repeat are at the top level of a sequence or a @repeat |
a step boundary under an if would have no fixed meaning |
| a clock wire takes a bare net name | a gated or divided clock is a part’s job, not logic’s |
a wire to an instance or a part cannot sit under an if |
the part sees its pin every cycle; put the condition in the value |
| a black box’s inputs are all wired | an unwired pin is a floating input |
a submodule’s @wire output is read only if it does not depend on the submodule’s inputs |
the Julia model would be a cycle behind the Verilog wire |
a Multicycle wire reads only registers of its own module |
the sources must be nets the constraint can name |
a Multicycle is not read before its K-th edge |
the constraint assumes it |
Boards
| rule | why |
|---|---|
| every port has a pin, and every pin names a port | a port with no site is only found on the board |
| one port per site | two would short |
| a port’s width matches its pin count | same |
a pad that relies on a pull gets one from the board, pull or ext_pull, the same way round |
an open-drain bus with no pull never reads high |
| setting and attribute names are a closed set | an attribute nothing reads is a buffer configured by a tool default |
@primary names clocks the design has |
a constraint on a net that does not exist |
Simulation
| rule | why |
|---|---|
| combinational logic must settle within a bound | past it, the logic has a loop; the error names the fields in it |
a clock plan’s rates share a grid, or the clock is dithered |
an exact grid for two unrelated oscillators is usually an accident |
| only an input or an unwired pad can be driven from a stimulus | everything else is the design’s |
advance_until with a timeout that passes is an error |
a condition that never comes is a failed test |