@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)
endWaveforms and plots
Waveforms in a viewer, signals in a plot
Let’s make a capture to look at:
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
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.