I've been utilizing the system verilog interface feature to simplify and organize my code better. Unfortunately though, when packaging IP with Vivado you need to use a verilog wrapper. In order to accommodate my interfaces I've resorted to writing an interface converter that sits between my verilog wrapper and system verilog top design file...
verilog_wrapper.v (top module for the purpose of packaging IP)
system_verilog_interface_converter.sv (declares interfaces and then breaks out their signals to individual ports)
system_verilog_top.sv (actual top of my design, WISH I could just package from this)
design_modules.sv (multiple design files that all use interfaces)
...Until now I've never had to deal with an interface that includes an inout port. I've begun using the MIG IP which generates a DDR4 interface that includes inout ports for signals like the "dq" that are truly bi-directional. The question is how does one perform an interface conversion of an inout port? It can't just be assigned as a normal input or output signal would be as this would indicate a one way directionality.
Here is a sampling of what the interface looks like...
interface intf_ddr4 #( // Width of Address Bus (18 - 17) parameter ADDR_WIDTH = 17, // Width of DQ Bus (min 8, mult of 8) parameter DQ_WIDTH = 64 ); logic [ADDR_WIDTH-1:0] adr; logic [DQ_WIDTH-1:0] dq; modport std ( output adr, inout dq); endinterface
The interface converter would look something like this...
module intf_converter ( //DDR4 interface output [16:0] ddr4_adr, inout [63:0] ddr4_dq ); //interface declerations intf_ddr4 ddr4(); //interface mapping assign ddr4_adr = ddr4.adr; assign ddr4_dq??? //system verilog top module sv_top sv_top ( .ddr4 (ddr4));
...so you can see the dilemma, but I'm hoping someone has a neat solution to this issue
We found this a few years ago - in the exact same instance - for creating a ddr interface. We worked with Xilinx in coming up with an acceptable solution. There was multiple variants, all a little clunky, but the following is what we agreed on. First create a "Bidi feedthru" module:
module bidi_feedthru #( parameter WIDTH = 64 ) ( inout wire [ WIDTH - 1 : 0 ] p0, inout wire [ WIDTH - 1 : 0 ] p1 ); tran t1[ WIDTH - 1 : 0 ]( p1, p0 ); endmodule
This module creates a parameterizable width bidirectional feedthru using the switch primitives of the verilog language. There were other alternatives for how to code this module. But working with Xilinx this was the agreed upon method that IS supported by Vivado synthesis.
Next, instantiate a bidi feedthru instance wherever you need to connect up the top-level ports into the higher level SystemVerilog interface construct - on a per member basis:
module top ( inout wire [ 7 : 0 ] DRAM_DQS_P, inout wire [ 63 : 0 ] DRAM_D, ... );
bidi_feedthru #( .WIDTH( 8 ) ) dqsp_alias( DRAM_DQS_P, ddr_ctl_if.dqs_p ); bidi_feedthru #( .WIDTH( 64 ) ) ddr_d_alias( DRAM_D, ddr_ctl_if.data ); ... endmodule
Done! We've used this since Vivado 2015.4, and it works great.