@quartz struct Link
@in go::Bool
@out busy::Bool
state::Bits{2} = 0
end
@on Link posedge(clk) begin
if state == 0
go && (state ← 1)
else
state ← state + 1
end
end
@wire Link busy ← state != 0Wires and pads
Combinational outputs, and pins that go both ways
@wire: an output that follows the state
An @on block writes registers. Sometimes an output should not be a register at all but a function of the current state — busy is true whenever the state machine is not idle, with no cycle of delay. That is a @wire block:
A @wire block drives its outputs continuously from the state the last clock edge produced. Several outputs go in a begin ... end:
@wire Link begin
busy ← state != 0
done ← state == 3
endThe rules are the ones a wire needs:
- A
@wireblock may write only@outports, pads, and the inputs of instances (see submodules). A field written by an@onblock may not also be driven by a@wire. - It must drive each output on every path. A combinational output that is left undriven on some branch is a latch in hardware and an error here.
if,ifelseand the rest work as they do in@on, subject to that rule.
@wire is assign. An output that is written by @on is a reg driven from an always block; one written by @wire is a wire. A given output is one or the other, never both.
Combinational logic across modules settles to a fixed point in the simulator, with a bound: past it, the logic has a loop, and the error says which fields are in it.
Pads
A Pad{N} is a pin the module both drives and reads — an open-drain bus line, a bidirectional data bus, a chip select you also sense.
@quartz struct I2CPin
@in pulling::Bool
@io sda::Pad{1} = Pad{1}(:pullup)
seen::Bool = false
end
@wire I2CPin sda ← ifelse(pulling, drive(false), release())
@on I2CPin posedge(clk) begin
seen ← sda
enddrive(v)holds the pin atv;drive(v, en)holds only the bitsenselects;release()lets go.sda ← vis shorthand fordrive(v).- Read bare,
sdais the resolved net — what the pin actually sees, given the module’s own drive, whatever the outside is driving, and the pull.bus[3]andbus[0:3]index a wider pad. Pad{1}(:pullup)says the net is pulled up, which is what makes an open-drain bus read high when nobody is holding it low;Pad{1}(:pulldown)is the mirror, andPad{1}()is push-pull, where every bit must have exactly one driver. The pull is part of the design because the design relies on it: simulation and co-simulation resolve the net with it. Whether the FPGA’s own pull or a resistor on the board provides it is the board’s business.
A pad keeps what it drives through a reset — it is a pin, not a register.
Polarity on a pad
A pad may be active=:low, and may be renamed for Verilog:
@quartz struct Clearer
@io clr::Pad{1} = Pad{1}(:pulldown) active=:low verilog="CLR_N"
@in go::Bool
end
@wire Clearer clr ← drive(go)drive(true) then puts a zero on the pin, and clr reads true when the pin is low: a pad’s value always means asserted, as a port’s does. What does not invert is the output enable — it says whether the module drives at all, not to what level — and the pull, which is a resistor on the physical net.
Everything that models the wire itself — a bench, padnet, netlevel, a co-simulation — sees the wire, because the wire is what the board has. A capture reads a pad as the design does, as its value; a plot or a VCD of it shows the wire, as for a port. A pad reaches the modules above it by name, so every module that declares the same pad must give it the same Verilog name.
A pad is emitted as an inout with a tristate assign pin = oe ? val : 1'bz; and a read of the pin. You write neither: drive/release produce the value and the enable, and the resolved net is what you read. The pull is not in the Verilog at all; it goes into the constraint file, from the board description.
Reading a clock as data
Occasionally a design samples a slow clock as if it were a signal — a reference tick, say. clocklevel(:clk_1MHz) reads a clock net’s current level from inside a block. It is rare, and it is only meaningful for a clock that is much slower than the block’s own.
Next
State machines and sequences: named states, @fsm, and multi-step transactions with @sequence.