This question is in the context of using verilog/systemverilog for synthesizable RTL.
I have some vector signals that are going across module boundaries that are currently defined as inout ports. The reason they are inout ports is that some of the vector indices are assigned in some places within the hierarchy, while others are assigned elsewhere. However, it is hugely beneficial to be able to consider these as one large shared bus that can route to many locations.
The problem is that I am using these inout ports in synthesized RTL and some of the backend tools have issues with these inouts when it comes to low-power synthesis and logical equivalency checking.
There is really only one driver ever to these signals, so they are never really needed to be inout. The problem is just that some indices would be ideally marked as input, while others are marked as output.
In the past a solution has been to use sparse vectors, one for each hierarchical location that could be assigning one of these indices. Then, they are all combined, using generate blocks and location masks to determine how to map each of the possible drivers to the final signal.
This approach ends up being a lot of overhead and requires doing some level of masking/mapping at each hierarchical location, and all of this is only to avoid having singly-driven ports defined as inout direction.
Since my backend tool flow requires not using inout ports in this scenario, does someone have another approach that may be more coding efficient?
Here is some sample code to illustrate (untested):
My ideal code is as follows:
module driver0(inout [1:0] sig); assign sig = 'b1; // drive with some real value endmodule module driver1(inout [1:0] sig); assign sig = 'b1; // drive with some real value endmodule module top(); wire sig[1:0]; driver0 u0(.sig(sig)); driver1 u1(.sig(sig)); endmodule
No bits of the
sig vector are multi-driven, they are just driven in different modules.
My non-inout solution is follows:
module driver0(output [1:0] sig); assign sig = 'b1; // drive with some real value assign sig = 'b0; // clear unused indices endmodule module driver1(output [1:0] sig); assign sig = 'b1; // drive with some real value assign sig = 'b0; // clear unused indices endmodule module top(); wire [1:0] sig, sig0, sig1; driver0 u0(.sig(sig0)); driver1 u1(.sig(sig1)); assign sig = sig0 | sig1; endmodule
It requires having a temporary sparse vector for each possible driver and then combining them. This was just an example with a 2-bit vector and two drivers. You can imagine that it takes quite a bit more effort and wasted code when the vector is large, as it requires looping over each index and then having some mechanism for determining which bits are driven in each hierarchical level.