Wires 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:

@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 != 0

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
end

The rules are the ones a wire needs:

  • A @wire block may write only @out ports, pads, and the inputs of instances (see submodules). A field written by an @on block 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, ifelse and the rest work as they do in @on, subject to that rule.
TipIf you know Verilog

@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
end
  • drive(v) holds the pin at v; drive(v, en) holds only the bits en selects; release() lets go. sda ← v is shorthand for drive(v).
  • Read bare, sda is the resolved net — what the pin actually sees, given the module’s own drive, whatever the outside is driving, and the pull. bus[3] and bus[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, and Pad{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.

TipIf you know Verilog

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.