title: Parameterizing the Bit Widths of fields in a packed struct so that modules can infer bit width if used in port map
body: <p>Also discussed at:</p>
  <p><a href="https://verificationacademy.com/forums/systemverilog/parameterizing-bit-widths-fields-packed-struct-so-modules-can-infer-bit-width-if-used-port-map-virtual-interface-interface-compile-time-configured-struct-bit-width" rel="nofollow noreferrer">https://verificationacademy.com/forums/systemverilog/parameterizing-bit-widths-fields-packed-struct-so-modules-can-infer-bit-width-if-used-port-map-virtual-interface-interface-compile-time-configured-struct-bit-width</a></p>
  <p><a href="https://forums.xilinx.com/t5/Synthesis/Parameterizing-the-Bit-Widths-of-fields-in-a-packed-struct-so/td-p/1191678" rel="nofollow noreferrer">https://forums.xilinx.com/t5/Synthesis/Parameterizing-the-Bit-Widths-of-fields-in-a-packed-struct-so/td-p/1191678</a></p>
  <p>I am having trouble accomplishing my intent in SystemVerilog trying to use the latest language features to make my code more elegant and less verbose. For synthesis**</p>
  <p>I would like to accomplish the following:</p>
  <ul>
  <li>be able to parameterize the bit widths of fields in a packed struct that I want to define ... I have attempted to accomplish this using a parameterized interface construct</li>
  <li>I would like for modules with that parameterized interface as an INPUT to the module to be able to infer the bit width of a field inside that packed struct defined inside the interface</li>
  </ul>
  <p>I have been mostly successful in past experiments but I have run into an issue.</p>
  <p>Please see the following simple interface definition:</p>
  <pre><code>interface MyInterface #(int DATA_W, ADDR_W) () ;
  
    typedef struct packed
      { logic valid
      ; logic [ADDR_W-1:0] addr
      ; logic [DATA_W-1:0] data
      ; } SimpleStruct;
  
    SimpleStruct bus;
    logic ready;
  
    modport SNK (input bus, output ready);
    modport SRC (output bus, input ready);
  
  endinterface
  </code></pre>
  <p>It is easy enough to instantiate an interface and use it at the input of a simple module in my Top module for this example:</p>
  <pre><code>module TopTest 
    ( input wire Clock
    , input wire Reset
    , input wire [31:0] In
    , output wire dummyOut
    ) ;
  
    MyInterface # ( 32, 3 ) my_interface ();
  
    assign my_interface.bus.data = In ;
    assign my_interface.bus.addr = 3'h3 ;
  
    InnerTest inst_mod_inner_test
      ( .Clock( Clock )
      , .Reset( Reset )
      , .Sink( my_interface )
      ) ;
  
    assign dummyOut = my_interface.ready ;
  
  endmodule
  </code></pre>
  <p>The problem that I am running into is that I do not want to parameterize the actual module with field bit widths, because I believe that at compile time the bit widths of the fields should be already established and accessible. This seems to not be the case, and I am wondering if there is anything I can do to accomplish inferring the bit width of the packed struct in the interface (remember that is the case because I want it parameterized, I know it is easy to get $bits of a field of a struct that is not defined in an interface but instead defined in a package or module)</p>
  <pre><code>module InnerTest 
    ( input wire Clock
    , input wire Reset
    , MyInterface.SNK Sink
    ) ;
  
    localparam BIT_WIDTH_SINK_DATA = $bits( Sink.bus.data ) // this line errors out b/c sink is 'virtual'
  
    RAM # ( .DATA_WIDTH( BIT_WIDTH_SINK_DATA ) ) ram ( ... // etc
  
    ... other code to drive output ready of interface ...
  
  endmodule
  </code></pre>
  <p>There are many reasons why a designer would want to make a module &quot;parameterizable&quot; and I have taken that approach in the past, but I am very interested in not duplicating information. If I were to take the easy approach, I would simply parameterize my inner test module so that I provided it DATA_WIDTH, but I would then have two numbers to update and a lot of parameters that I feel I do not need. I think it would be most elegant if I could simply infer characteristics of the parameterized struct somehow. The information I am looking for is truly known at compile time in my opinion. I just can't seem to access it, or this is another shortfall of SystemVerilog.</p>
  <p><strong>Follow up Q, in Simulation</strong>
  The workaround mentioned by Dave was very useful when using QuestaSim, but now running into different issue in QuestaSim:</p>
  <p>parameter reference &quot;sink.bus.data&quot; through interface port &quot;sink&quot; is not valid when the actual interface in the instance is an arrayed instance element or below a generate construct</p>
  <p>What is the workaround for this, I don't understand why simply being in a generate statement would impact things way downstream. In this case i use a generate statement to choose between different FIFO implementations, a few layers above the line of code where the error happens.</p>
  <pre><code>typedef sink.bus.data questasim_workaround;
  localparam width = $bits(questasim_workaround);
  </code></pre>
  <p><strong>Follow Up Experiment</strong>
  I have experimented with passing in type instead of restricting myself to passing in DATA_W.</p>
  <pre><code>interface MyInterface #(int ADDR_W, type DATA_TYPE) () ;
  
    typedef struct packed
      { logic valid
      ; logic [ADDR_W-1:0] addr
      ; DATA_TYPE data
      ; } SimpleStruct;
  
    SimpleStruct bus;
    logic ready;
  
    modport SNK (input bus, output ready);
    modport SRC (output bus, input ready);
  
  endinterface
  </code></pre>
  <p>This allows for more flexibility. I have observed that Vivado Simulator and Synthesis tools can handle an example like this without issue.</p>
  <pre><code>module SomeModule
    ( MyInterface myInt
    blah...
    );
  
    localparam SOMETHING = $bits(myInt.DATA_TYPE);
    // or equivalently
    localparam SOMETHING_ELSE = $bits(myInt.data);
    // or even this, for needs of a internal signal for pipelined processing steps
    MyInterface # ($bits(myInt.addr), myInt.DATA_TYPE) internal_0 () ;
  </code></pre>
  <p>In place of this in QUestaSim we have had to implement Dave's work around:</p>
  <pre><code>module SomeModule
    ( MyInterface myInt
    blah...
    );
  
    // this gets less elegant :/
    typedef myInt.data questasim_workaround_data;
    typedef myInt.addr questasim_workaround_addr;
  
    localparam SOMETHING = $bits(questasim_workaround_data);
    // or equivalently
    localparam SOMETHING_ELSE = $bits(questasim_workaround_data);
    // or even this, for needs of a internal signal for pipelined processing steps
  
    MyInterface # ($bits(questasim_workaround_addr), questasim_workaround_data) internal_0 () ;
  </code></pre>