Waveforms and plots

Waveforms in a viewer, signals in a plot

Let’s make a capture to look at:

@encoding Phase begin IDLE; WARM; RUN end

@quartz struct Heater
  @in  go::Bool = false
  @out on::Bool
  phase::Phase = IDLE
  temp::Bits{8} = 0
end

@on Heater posedge(clk) begin
  @fsm phase begin
    @state IDLE
      go && (phase  WARM)
    @state WARM
      temp  temp + 3
      temp > 60 && (phase  RUN)
    @state RUN
      temp  temp - 1
      temp < 20 && (phase  IDLE)
  end
  on  phase == WARM
end

sim = Simulation(Heater(); clocks=(clk=1MHz,), watch="*")
out = @run sim begin
  advance_by(5µs)
  sim.go = true
  advance_by(1µs)
  sim.go = false
  advance_by(80µs)
end

Plots

With Plots loaded, a signal plots as a step plot of the wire against time in seconds, with the ticks labelled in whatever unit suits the span: a one-bit lane is labelled L and H, a pin asserted low is drawn as the voltage on it — low while asserted — and an encoded register is labelled by state name:

plot(out.temp)
plot(out.phase)

plot(out, "a", "b", ...) stacks several nets as lanes:

plot(out, "go", "phase", "temp", "on"; size=(650, 500))

VCD and Surfer

write("run.vcd", out, VCD()) writes the capture as a VCD file of the wires — real time on the axis, a pin asserted low inverted as a probe would see it, a lane per clock, and encoded registers written twice, as bits and as the state’s name, which viewers show as text. Any waveform viewer opens it.

Surfer is the one QuartzHDL talks to directly. With surfer on your path:

view(out)             # open the capture in Surfer
view(sim)             # a live view: refreshed after every @run, and during long ones

The Heater capture in Surfer: the clock, go, the phase by state name, temp, and on.

view(Surfer(), x) names the viewer explicitly. A viewer is an IO, so write(Surfer(), out, VCD()) is the same thing, and close(v) closes it. The live view keeps its zoom and the signals you have added across refreshes, so a typical debugging loop is: open the view, @run a bit, look, @run a bit more.

Signals as signals

sampled(out.temp) gives the net as one value per clock, and with SignalAnalysis.jl loaded it has a frame rate, so the rest of that package’s tools — spectra, filters, psd, specgram — take it directly:

using SignalAnalysis

x = sampled(out.temp)
framerate(x), length(x)
(1.0e6, 87)

This is the step that makes porting a signal-processing algorithm pleasant: the hardware’s output is a signal like any other, and can be compared against the floating-point reference with the same tools.

Next

Library components: the chips around the design.