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
  phase::Phase = IDLE
  temp::Bits{8} = 0
  @out on::Bool
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(5us)
  sim.go = true
  advance_by(1us)
  sim.go = false
  advance_by(80us)
end
Capture(5 signals, 86.0us)

Plots

With Plots loaded, a signal plots as a step plot against time in seconds, 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 — real time on the axis, 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

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.