Description
This is really just more of #738 and #740.
CIRCT needs to work harder to eliminate or copy-propagate FIRRTL Analog
types or Verilog inout
types. inout
support is extremely spotty and weird across different tooling.
Annoyingly, the typical situation where use of Analog
or inout
shows up in code is purely passing these from a module's IO to a submodule's IO. Doing the direct assignment is fine and supported by basically all tooling, but anything else is extremely spotty.
Consider something like:
circuit Foo:
module Bar:
input a: Analog<1>
module Foo:
input a: Analog<1>
inst bar of Bar
wire b: Analog<1>
attach(b, a)
attach(bar.a, b)
The wire b: Analog<1>
is really just a temporary and can be trivially copy propagated through.
The Scala FIRRTL Compiler will get rid of this and produce:
module Bar(
inout a
);
endmodule
module Foo(
inout a
);
Bar bar (
.a(a)
);
endmodule
However, CIRCT is much less aggressive here and produces:
module Bar(
inout a);
endmodule
module Foo(
inout a);
wire _a_wire; // Analog.fir:6:5
wire b; // Analog.fir:8:5
Bar bar ( // Analog.fir:6:5
.a (_a_wire)
);
`ifdef SYNTHESIS // Analog.fir:10:5
wire _T = b; // Analog.fir:10:5
assign b = a; // Analog.fir:10:5
assign a = _T; // Analog.fir:10:5
assign _a_wire = _T; // Analog.fir:11:5
assign b = _a_wire; // Analog.fir:11:5
`else
`ifdef verilator // Analog.fir:11:5
`error "Verilator does not support alias and thus cannot arbitrarily connect bidirectional wires and ports" // Analog.fir:10:5
`error "Verilator does not support alias and thus cannot arbitrarily connect bidirectional wires and ports" // Analog.fir:11:5
`else
alias b = a; // Analog.fir:10:5
alias _a_wire = b; // Analog.fir:11:5
`endif
`endif
endmodule
This has consequences because just about any tool will handle the SFC code, but the CIRCT code is extremely problematic due to spotty tooling support (see below).
Summary of Spotty Tooling
Thanks to @jackkoenig for finding and documenting all this. 👍
Concretely, any tool is happy with:
module Foo(inout bar);
SubFoo SubFoo(bar);
endmodule
While entirely equivalent, if you introduce a temporary wire for inout bar
, you're in trouble.
Synopsys Design Compiler will accept, but VCS will not accept:
module Foo(inout bar);
wire tmp;
assign tmp = bar;
assign bar = tmp;
SubFoo SubFoo(tmp);
endmodule
VCS will accept this if you express this using alias
:
module Foo(inout bar);
wire tmp;
alias tmp = bar;
SubFoo SubFoo(tmp);
endmodule
Verilator accepts neither the double assign nor alias.