Access parameterized Interface port's size from within module

Is there a way to access a parameterized interface port’s size from within a module that uses this interface? The code below is close to what I want to do, just a representative.

interface my_if
  #( parameter int unsigned P_WIDTH = 2 )
  (
    input wire  clk,
    input wire  srst
  );

  logic [P_WIDTH-1:0] data;

  modport master ( output data );
  modport slave  ( input  data );

endinterface : my_if

If a module uses this interface

module my_slave
  (
    input  wire                        clk,
    input  wire                        srst,
    my_if.slave                        slv,
    output logic [$bits(slv.data)-1:0] q
    // A:         ^^^^^^^^^^^^^^^
  );

  localparam C_WIDTH = $bits(slv.P_WIDTH);
  // B:                ^^^^^^^^^^^^^^^^^^

  always_ff @( posedge clk ) begin
    q <= slv.data;
  end

module : my_slave

A: $bits(slv.data) results in a value of one (1)!

B: $bits(slv.P_WIDTH) causes: “Illegal reference to a parameter below an arrayed instance: slv.P_WIDTH”.

Can this be done in a generic way instead of passing the P_WIDTH to my_slave?
– Amal

In reply to Amal:

Your code worked for me. Contact your tool vendor for support.

In reply to dave_59:

Hi Dave,

I have tried this in a number of different vendor tools (Questa, Synplify and Vivado) and I have cases open with each vendor. They seem to say that accessing interface port parameters is not valid because it is like a hierarchical access to a parameter inside an interface. And off course I want this to be synthesizable.

In the following more elaborate example, are these valid (according to LRM) accesses to the P_WIDTH parameter and data of master port mst[0]? Hierarchical access to objects within a module is not valid. If interfaces are like modules, shouldn’t this same statement be true for interfaces? But personally I would think I should be able to access an interface port’s parameters/signals this way.


  $bits(mst[0].data)
  mst[0].P_WIDTH


//--------------------------------------------------------------------------------------------------
interface my_if
  #( parameter int unsigned P_WIDTH = 2 )
  (
    input  wire  clk,
    input  wire  srst
  );

  logic [P_WIDTH-1:0] data;

  modport master ( output data );
  modport slave  ( input  data );

  initial begin
    $display("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
    $display("my_if");
    $display("  P_WIDTH : %0d - %m", P_WIDTH);
  end

endinterface : my_if

//--------------------------------------------------------------------------------------------------
module my_slave
  #(
    parameter P_COUNT = 4
  )
  (
    input  wire  clk,
    input  wire  srst,
    //
    my_if.slave  slv,
    my_if.master mst[0:P_COUNT-1]
  );

  // Uncommenting this prints:
  //#   top.slv: $bits(mst[0].data) = 1
  //#   top.slv: $bits(mst[1].data) = 5
  localparam C_WIDTH1 = $bits(mst[0].data);
  // Uncommenting this printss:
  //#   top.slv: $bits(mst[0].data) = 5
  //#   top.slv: $bits(mst[1].data) = 1
  localparam C_WIDTH2 = $bits(mst[1].data);

  // The following causes error: "Illegal reference to a parameter below an arrayed instance: mst[0].P_WIDTH"
  //localparam C_WIDTH3 = mst[0].P_WIDTH;
  //localparam C_WIDTH4 = mst[1].P_WIDTH;

  initial begin
    $display("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
    $display("my_slave");
    $display("  %m: $bits(slv.data)    = %0d", $bits(slv.data));
    $display("  %m: slv.P_WIDTH        = %0d", slv.P_WIDTH);
    $display("  %m: $bits(mst[0].data) = %0d", $bits(mst[0].data));
    $display("  %m: $bits(mst[1].data) = %0d", $bits(mst[1].data));
    $display("  %m: mst[0].P_WIDTH     = %0d", mst[0].P_WIDTH);
    $display("  %m: mst[1].P_WIDTH     = %0d", mst[1].P_WIDTH);
    //for( int unsigned i=0; i<P_COUNT; i++ ) begin
    //  $display("  %m: $bits(mst[%0d].data) = %0d", i, $bits(mst[i].data));
    //end
    //for( int unsigned i=0; i<P_COUNT; i++ ) begin
    //  $display("  %m: mst[%0d].P_WIDTH     = %0d", i, mst[i].P_WIDTH);
    //end
    //$display("  %m: C_WIDTH3           = %0d", C_WIDTH3);
    //$display("  %m: C_WIDTH4           = %0d", C_WIDTH4);
  end

  for( genvar i=0; i<P_COUNT; i++ ) begin
    always_ff @( posedge clk ) begin
      mst[i].data <= slv.data + i;
    end
  end

endmodule : my_slave

//--------------------------------------------------------------------------------------------------
module top
  #( parameter int unsigned P_WIDTH = 5 )
  (
    input  wire                clk,
    input  wire                srst,
    input  wire  [P_WIDTH-1:0] din,
    output logic [P_WIDTH-1:0] dout0,
    output logic [P_WIDTH-1:0] dout1
  );

  localparam C_COUNT = 2;

  my_if #( .P_WIDTH(P_WIDTH) ) slv_if              ( .clk(clk), .srst(srst) );
  my_if #( .P_WIDTH(P_WIDTH) ) mst_if[0:C_COUNT-1] ( .clk(clk), .srst(srst) );

  assign slv_if.data = din;

  my_slave #( .P_COUNT(C_COUNT) ) slv( .clk(clk), .srst(srst), .slv(slv_if), .mst(mst_if) );

  assign dout0 = mst_if[0].data;
  assign dout1 = mst_if[1].data;

endmodule : top

Regards,