Description
The following example has bad output. A node, defined and probed under a layerblock, is lifted out of the layerblock and into the body of the module.
FIRRTL version 5.0.0
circuit Example:
layer Verification, bind:
public module Example:
output p: Probe<UInt<1>, Verification>
layerblock Verification:
node n = UInt<1>(0)
define p = probe(n)
Goes to:
// Generated by CIRCT unknown git version
module Example_Verification();
endmodule
module Example();
wire _GEN = 1'h0;
endmodule
// ----- 8< ----- FILE "ref_Example.sv" ----- 8< -----
// Generated by CIRCT unknown git version
`define ref_Example_p _GEN
// ----- 8< ----- FILE "layers-Example-Verification.sv" ----- 8< -----
// Generated by CIRCT unknown git version
`ifndef layers_Example_Verification
`define layers_Example_Verification
bind Example Example_Verification verification ();
`endif // not def layers_Example_Verificatio
If I "donttouch" the node, we get much better output:
FIRRTL version 5.0.0
circuit Example: %[[
{
"class": "firrtl.transforms.DontTouchAnnotation",
"target": "~Example|Example>n"
}
]]
layer Verification, bind:
public module Example:
output p: Probe<UInt<1>, Verification>
layerblock Verification:
node n = UInt<1>(0)
define p = probe(n)
Goes to:
// Generated by CIRCT unknown git version
module Example_Verification();
wire n = 1'h0;
wire n_probe = n;
endmodule
module Example();
endmodule
// ----- 8< ----- FILE "ref_Example.sv" ----- 8< -----
// Generated by CIRCT unknown git version
`define ref_Example_p verification.n_probe
// ----- 8< ----- FILE "layers-Example-Verification.sv" ----- 8< -----
// Generated by CIRCT unknown git version
`ifndef layers_Example_Verification
`define layers_Example_Verification
bind Example Example_Verification verification ();
`endif // not def layers_Example_Verification
The cause of this seems to be that the initial node, located under the layerblock, is canonicalized away, and references to the node are replaced with references to the constant. Due to constant-pooling, the constant is located in the front of the body module. When the probes are lowered to XMRs, a new node is created for the XMR, which is co-located with the constant in the module body.
Here is a snippet of the IR output right before and after lower-XMR, which shows how the node has been erased:
// -----// IR Dump Before LowerXMR (firrtl-lower-xmr) //----- //
firrtl.circuit "Example" {
firrtl.layer @Verification bind {
}
firrtl.module @Example(out %p: !firrtl.probe<uint<1>, @Verification>) attributes {convention = #firrtl<convention scalarized>} {
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
%0 = firrtl.ref.send %c0_ui1 : !firrtl.uint<1>
firrtl.layerblock @Verification {
%1 = firrtl.ref.cast %0 : (!firrtl.probe<uint<1>>) -> !firrtl.probe<uint<1>, @Verification>
firrtl.ref.define %p, %1 : !firrtl.probe<uint<1>, @Verification>
}
}
}
// -----// IR Dump Before LayerSink (firrtl-layer-sink) //----- //
firrtl.module @Example() attributes {convention = #firrtl<convention scalarized>} {
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
%0 = firrtl.node sym @sym %c0_ui1 : !firrtl.uint<1>
firrtl.layerblock @Verification {
}
}