calyx
calyx copied to clipboard
Unexpected behavior with reading from `seq_mem` reference and `invoke`
This issue is about divergent behavior between icarus-verilog, verilator, and the Calyx interpreter.
Here's a toy eDSL program and it's associated data file:
# bug.py
import calyx.builder as cb
def component(prog):
comp = prog.component("comp")
A = comp.seq_mem_d1("A", 32, 1, 1, is_ref=True)
B = comp.seq_mem_d1("B", 32, 1, 1, is_ref=True)
a = comp.reg(32)
zero = comp.const("zero", 1, 0)
read_A = comp.mem_load_d1(A, zero.out, a, "read")
write_B = comp.mem_store_d1(B, zero.out, a.out, "write")
comp.control += [read_A, write_B]
return comp
def insert_main(prog):
main = prog.component("main")
A = main.seq_mem_d1("A", 32, 1, 1, is_external=True)
B = main.seq_mem_d1("B", 32, 1, 1, is_external=True)
comp = component(prog)
comp = main.cell("comp", comp)
main.control += [cb.invoke(comp, ref_A=A, ref_B=B)]
if __name__ == "__main__":
prog = cb.Builder()
insert_main(prog)
prog.program.emit()
# bug.data
{
"A": {
"data": [5],
"format": {
"numeric_type": "bitnum",
"is_signed": false,
"width": 32
}
},
"B": {
"data": [0],
"format": {
"numeric_type": "bitnum",
"is_signed": false,
"width": 32
}
}
}
This program should read 5 from memory A and write this value into memory B. However, depending on whether it's run with icarus-verilog, verilator, or the calyx interpreter, we find different results:
Verilator:
am3327@havarti:/scratch/anshuman/calyx$ fud e bug-after-compile-invoke.futil -s verilog.data calyx-py/test/correctness/bug.data --to dat --through verilog -v -q
{
"cycles": 5,
"memories": {
"A": [
5
],
"B": [
5
]
}
}
Icarus-verilog:
am3327@havarti:/scratch/anshuman/calyx$ fud e bug-after-compile-invoke.futil -s verilog.data calyx-py/test/correctness/bug.data --to dat --through icarus-verilog -v -q
{
"cycles": 5,
"memories": {
"A": [
5
],
"B": [
0
]
}
}
Calyx interpreter:
am3327@havarti:/scratch/anshuman/calyx$ fud e bug-after-compile-invoke.futil -s verilog.data calyx-py/test/correctness/bug.data --to interpreter-out -v -q
{
"main": {
"A": [
5
],
"B": [
5
]
}
}
(run on Havarti by @anshumanmohan)
verilator and the Calyx interpreter produce the correct result, while icarus-verilog does not.
It appears this behavior is specific to reading from sequential memories passed as references to a component called via invoke. For example, running either of these programs with bug.data causes no issues.
A changed from seq to comb memory:
import calyx.builder as cb
def component(prog):
comp = prog.component("comp")
A = comp.comb_mem_d1("A", 32, 1, 1, is_ref=True)
B = comp.seq_mem_d1("B", 32, 1, 1, is_ref=True)
a = comp.reg(32)
zero = comp.const("zero", 1, 0)
read_A = comp.mem_load_d1(A, zero.out, a, "read")
write_B = comp.mem_store_d1(B, zero.out, a.out, "write")
comp.control += [read_A, write_B]
return comp
def insert_main(prog):
main = prog.component("main")
A = main.comb_mem_d1("A", 32, 1, 1, is_external=True)
B = main.seq_mem_d1("B", 32, 1, 1, is_external=True)
comp = component(prog)
comp = main.cell("comp", comp)
main.control += [cb.invoke(comp, ref_A=A, ref_B=B)]
if __name__ == "__main__":
prog = cb.Builder()
insert_main(prog)
prog.program.emit()
Memories no longer passed via reference through invoke:
import calyx.builder as cb
def component(prog):
comp = prog.component("main")
A = comp.seq_mem_d1("A", 32, 1, 1, is_external=True)
B = comp.seq_mem_d1("B", 32, 1, 1, is_external=True)
a = comp.reg(32)
zero = comp.const("zero", 1, 0)
read_A = comp.mem_load_d1(A, zero.out, a, "read")
write_B = comp.mem_store_d1(B, zero.out, a.out, "write")
comp.control += [read_A, write_B]
return comp
if __name__ == "__main__":
prog = cb.Builder()
component(prog)
prog.program.emit()
You can find the files referenced here on this branch:
bug.pybug.databug.futil; from runningpython bug.py > bug.futilbug-after-compile-invoke.futil; from running the compile-invoke pass onbug.futil:calyx -p compile-invoke bug.futil -m file > bug-after-compile-invoke.futil