The semantics of Calyx doesn't allow external memory outside top-level component. However, the SCF To calyx pass doesn't seem to abide with this.
func.func @test(%arg0 : memref<3xf32>){
%alloc = memref.alloc() : memref<3xf32>
%c0 = arith.constant 0: index
%c3 = arith.constant 3: index
%step = arith.constant 1: index
%cst_54 = arith.constant 54.5675 : f32
scf.for %i = %c0 to %c3 step %step {
memref.store %cst_54, %alloc[%i] : memref<3xf32>
}
return
}
func.func @main() {
%alloc = memref.alloc() : memref<3xf32>
%c0 = arith.constant 0: index
%c3 = arith.constant 3: index
%step = arith.constant 1: index
%cst_45 = arith.constant 45.5675 : f32
scf.for %i = %c0 to %c3 step %step {
memref.store %cst_45, %alloc[%i] : memref<3xf32>
}
func.call @test(%alloc): (memref<3xf32>) -> ()
return
}
If we use --pass-pipeline="builtin.module(lower-scf-to-calyx{top-level-function=main})" with circt-opt. It generates a test component corresponding to the test function with external attribute set to true for the memory:
module attributes {calyx.entrypoint = "main"} {
calyx.component @test(%clk: i1 {clk}, %reset: i1 {reset}, %go: i1 {go}) -> (%done: i1 {done}) {
%c1_i32 = hw.constant 1 : i32
%true = hw.constant true
%c0_i32 = hw.constant 0 : i32
%cst = calyx.constant @cst_0 <5.456750e+01 : f32> : i32
%std_slice_0.in, %std_slice_0.out = calyx.std_slice @std_slice_0 : i32, i2
%std_add_0.left, %std_add_0.right, %std_add_0.out = calyx.std_add @std_add_0 : i32, i32, i32
%mem_0.addr0, %mem_0.clk, %mem_0.reset, %mem_0.content_en, %mem_0.write_en, %mem_0.write_data, %mem_0.read_data, %mem_0.done = calyx.seq_mem @mem_0 <[3] x 32> [2] {external = true} : i2, i1, i1, i1, i1, i32, i32, i1
...
See Complete Output at Compiler Explorer
I checked the code at Conversion/SCFToCalyx/SCFToCalyx.cpp which adds external attribute to every memref.AllocaOp without checking whether it is top level component or not:
memoryOp->setAttr("external", IntegerAttr::get(rewriter.getI1Type(), llvm::APInt(1, 1)));
I went on to change the code to add an check for top level component:
if (componentOp.hasAttr("toplevel")){
memoryOp->setAttr("external", IntegerAttr::get(rewriter.getI1Type(), llvm::APInt(1, 1)));
}
But I ran into another issue now. It's in the emitter.
There seems to be an assumption that memories in calyx are either of the two - external, ref.
The CalyxEmitter while emitting native calyx checks for the existence of the external attribute in the memory. If it is not external it assumes that it is a ref which it isn't (in this case) because it is just a memory local to the component.
Should we just set external = false (for non top-level component and non ref memory) and in the emitter check if external is set to false ?
The semantics of Calyx doesn't allow external memory outside top-level component. However, the SCF To calyx pass doesn't seem to abide with this.
If we use
--pass-pipeline="builtin.module(lower-scf-to-calyx{top-level-function=main})"with circt-opt. It generates atestcomponent corresponding to thetestfunction withexternalattribute set totruefor the memory:See Complete Output at Compiler Explorer
I checked the code at Conversion/SCFToCalyx/SCFToCalyx.cpp which adds external attribute to every
memref.AllocaOpwithout checking whether it is top level component or not:I went on to change the code to add an check for top level component:
But I ran into another issue now. It's in the emitter.
There seems to be an assumption that
memoriesin calyx are either of the two -external,ref.The CalyxEmitter while emitting native calyx checks for the existence of the
externalattribute in the memory. If it is not external it assumes that it is arefwhich it isn't (in this case) because it is just a memory local to the component.Should we just set
external = false(for non top-level component and non ref memory) and in the emitter check if external is set tofalse?