What the compiler refuses, and why
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
| 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
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 Timeout needs a width |
Timeout alone has no size |
| an instance’s constructor takes no arguments |
the wiring is stated once, in @wire |
Blocks
| 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 |
| 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
| 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
| 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 |