【问题标题】:Parameterizing a SystemVerilog interface for optional array of a port element为端口元素的可选数组参数化 SystemVerilog 接口
【发布时间】:2020-12-31 15:44:48
【问题描述】:

我想填写正确的信号声明和 modport 声明,以便我的接口和期望代码示例用例在 Vivado 中编译。我正在使用 .sv 文件扩展名在 SystemVerilog 中工作。我想“if 语句”不同的信号声明的原因是我的接口的实例化和使用更加优雅。我不希望用户必须键入许多 [0] 来指定一维的单个元素数组中的单个元素,其中一维宽度为 1,第二维宽度为 1。

请耐心等待,我必须在工作中删除此代码应用程序的所有元素。我已将其定制为一个非常简单的示例。

如果第 1 维或数组为 1 且数组的第 2 维为 1,我不希望有人需要选择第 0 和第 0 个元素。

例如,我希望用户能够执行以下操作,但无法编译。这是我的基本问题。如何实现能够像以下 sn-p 一样优雅的代码?:

MyInterface # ( .DATA_WIDTH( 8 ), .ADDR_WIDTH( 8 ), .A1D( 1 ), .A2D( 1 ) ) my_signal () ;

assign my_signal.bus.data = 8'h20 ;

错误是:

[Synth 8-6038] cannot resolve hierarchical name for the item 'data' [".../File.sv":21]

相反,我发现用户必须为总共 1 个元素的特定信号执行此操作(以下内容没有错误):

MyInterface # ( .DATA_WIDTH( 8 ), .ADDR_WIDTH( 8 ), .A1D( 1 ), .A2D( 1 ) ) my_signal () ;

assign my_signal.bus[0][0].data = 8'h20 ;

我觉得这样的事情应该可以通过以下代码实现:

interface MyInterface #(int DATA_W, ADDR_W, A1D, A2D) () ;

  typedef struct packed
    { logic                valid
    ; logic [ADDR_W-1:0]   addr
    ; logic [DATA_W-1:0]   data
    ; } SimpleStruct;

  if ( A1D == 1 ) begin
    SimpleStruct bus;
    logic ready;
  end
  else begin
    if ( A2D == 1 ) begin
      SimpleStruct [A1D-1:0] bus;
      logic        [A1D-1:0] ready;
    end
    else begin
      SimpleStruct [A1D-1:0][A2D-1:0] bus;
      logic        [A1D-1:0][A2D-1:0] ready;
    end
  end
  
  modport SNK (input  bus, output ready);
  modport SRC (output bus, input  ready);

endinterface

这没有编译,因为 bus 和 ready 没有被“声明”......所以我改成了一个不太优雅的(我尝试在 generate 中猜测,但这没有帮助):

interface MyInterface #(int DATA_W, ADDR_W, A1D, A2D) () ;

  typedef struct packed
    { logic                valid
    ; logic [ADDR_W-1:0]   addr
    ; logic [DATA_W-1:0]   data
    ; } SimpleStruct;

  if ( A1D == 1 ) begin
    SimpleStruct bus;
    logic ready;

    modport SNK (input  bus, output ready);
    modport SRC (output bus, input  ready);
  end
  else begin
    if ( A2D == 1 ) begin
      SimpleStruct [A1D-1:0] bus;
      logic        [A1D-1:0] ready;
  
      modport SNK (input  bus, output ready);
      modport SRC (output bus, input  ready);
    end
    else begin
      SimpleStruct [A1D-1:0][A2D-1:0] bus;
      logic        [A1D-1:0][A2D-1:0] ready;
  
      modport SNK (input  bus, output ready);
      modport SRC (output bus, input  ready);
    end
  end

endinterface

同样的愿望也适用于用户想要使用长度 > 1 的单维数组的情况。例如: 恕我直言:

MyInterface # ( .DATA_WIDTH( 8 ), .ADDR_WIDTH( 8 ), .A1D( 3 ), .A2D( 1 ) ) my_signal () ;

assign my_signal.bus.data[0] = 8'h20 ;
assign my_signal.bus.data[1] = 8'h21 ;
assign my_signal.bus.data[2] = 8'h22 ;

我认为上面的代码比下面的代码看起来更好。我尝试或选择使用接口的全部意义在于代码的可重用性和优雅性。我不希望用户不断与“我想要一个元素,但会得到部分模棱两可的错误,很难找出解决方案”:
恕我直言,不可取:

MyInterface # ( .DATA_WIDTH( 8 ), .ADDR_WIDTH( 8 ), .A1D( 3 ), .A2D( 1 ) ) my_signal () ;

assign my_signal.bus.data[0][0] = 8'h20 ;
assign my_signal.bus.data[1][0] = 8'h21 ;
assign my_signal.bus.data[2][0] = 8'h22 ;

我的愿望是让用户编写他们需要的代码来完成他们想要的元素的复杂性。单个元素的用途最多,元素的一维数组有一些用途,元素的二维数组的用途很少。由于我希望定义一个接口,因此我必须允许所有用途。我不希望仅仅因为我希望界面支持 2D 而需要不优雅的代码,并且用户只需要一个元素来实现他们的特定实例化和代码。

【问题讨论】:

  • 你想做的事情在 SV 中是不可能的。另请记住,每个生成块(并且您的“if”语句是生成块)都会将分层路径添加到“总线”变量的名称中,在您的情况下,它会变成类似于my_signal.genblk1.bus.data 的名称。其中 'genblk' 是自动生成的(因为您没有为 'begin' 提供名称),并且在生成块中的每个 begin...end 对中都会有所不同。
  • 感谢您的回复,我完全同意 gen 声明行李。我不会使用生成语句。

标签: verilog system-verilog


【解决方案1】:

我想像你描述的那样做最好的方法是使用类型参数并在接口之外定义类型。这是一个例子。

interface MyInterface #(type SimpleStruct = logic) () ;  
  SimpleStruct  bus;
  logic         ready;
   
  modport SNK (input  bus, output ready);
  modport SRC (output bus, input  ready);

endinterface

module mymod #(int DATA_W = 1, int ADDR_W = 1) ();

  typedef struct packed
    { logic                valid
    ; logic [ADDR_W-1:0]   addr
    ; logic [DATA_W-1:0]   data
    ; } SimpleStruct;
  
  MyInterface #(SimpleStruct) if1();
  
  typedef SimpleStruct[2] SimpleStruct2;
  MyInterface #(SimpleStruct2) if2();

  typedef SimpleStruct[2][2] SimpleStruct3;
  MyInterface #(SimpleStruct3) if3();

endmodule
  

【讨论】:

  • 感谢您的回复,但老实说,您共享的代码不如问题中的原始解决方案优雅。我不想再将参数大小向量传递到模块的参数列表中,我希望它在编译时始终可以正确推断,因为我使用的是“接口”。话虽如此,我认为使用 typedef 基本上扩展接口多次(最多 3 次)以设置单个元素版本、1D 版本和 2D 版本的想法可能很有用。我必须在我的测试中确认。没有介绍 param'd mod