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[0] = 'b1; // drive with some real value
endmodule
module driver1(inout [1:0] sig);
assign sig[1] = '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[0] = 'b1; // drive with some real value
assign sig[1] = 'b0; // clear unused indices
endmodule
module driver1(output [1:0] sig);
assign sig[1] = 'b1; // drive with some real value
assign sig[0] = '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.
Best Answer
The simplest solution is split output and input for the driver modules.
You can allow the output to also be an input of the same module in
top
Or you can prevent the driver's output to feedback by slicking the array for the input. This may take a bit more caution. You could partition the inputs signals as two or more as well. Which ever is more intuitive easier manage for the project.
Since IEEE Std 1364-2001, Verilog allows some fancy port definitions. See IEEE Std 1364-2001 § 12.3.3 Port declarations or SystemVerilog's IEEE Std 1800-2012 § 23.2.2 Port declarations
Support for this may very across tools as this practices is not common. If your synthesizer has issues with
interface
it may have issues with port aliasing as well.If the bus is going across several lays of hierarchy then the above solution will likely be tedious to manage. An
interface
should be used. Some guidelines to reduce synthesis limitations:logic
type. Only exception are tri-states where the bits are expected to have two or more drivers.wire
ortri
logic
types within aalways_ff
,always_comb
,always_latch
(if latch is necessary block.logic
types can be assigned withassign
statements, but not all tools validate driving conflicts withassign
assign myif.io = drive_enable ? io_out : 'z;
Do not use
interface
as a port in the top most module that will be synthesized. Many current synthesizers will flatten and localize interfaces with escaped names. If needed, create wrapper module as a translation layer between interface and top most port signals. Ex: