Interfacing with Calyx Programs
To run RTL designs created from Calyx programs, top-level reset
, go
, and done
signals must be interfaced with correctly.
Interfacing with RTL designs in this way becomes relevant when writing harnesses/testbenches
to execute programs created with Calyx.
Namely, the client for a Calyx top-level module must:
- Assert the
reset
signal and then deassert it, to initialize the state inside control registers correctly. - Assert the
go
signal, and keep it asserted as long as the module is running. - Wait for the
done
signal to be asserted while keepinggo
high. Deasserting thego
signal before a component deasserts itsdone
signal will lead to undefined behavior.
Asserting the reset
and go
signals in this order is important. Otherwise the top-level
component will begin running with garbage data inside of control registers.
Cocotb
As a concrete example, consider using cocotb to test a Calyx-generated Verilog design.
If we imagine a simple Calyx program that contains a simple toplevel module named main
:
component main()->() {
cells {
reg = std_reg(4);
}
group write_to_reg {
reg.in = 4'd3;
reg.write_en = 1'b1;
write_to_reg[done] = reg.done;
}
control{
seq{
write_to_reg;
}
}
}
In order to be able to actually write to our register, we need to drive our reset
and
go
signals in our cocotb test:
# Required for all cocotb testbenches. Included for completeness.
cocotb.start_soon(Clock(main.clk, 2, units="ns").start())
# Reset Calyx-generated control registers
main.reset.value = 1
await ClockCycles(main.clk, 5) #wait a bit
main.reset.value = 0
# Start execution of control sequence
main.go.value = 1
#At this point our Calyx program is done
await RisingEdge(main.done)