微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

凿子/ FIRRTL DefnameDifferentPortsException

如何解决凿子/ FIRRTL DefnameDifferentPortsException

我最近将我的一个大型项目的凿子版本从3.1.1更新到了3.4.0;但是,我得到了一堆firrtl.passes.CheckHighFormlike$DefnameDifferentPortsException

firrtl.passes.CheckHighFormlike$DefnameDifferentPortsException: : ports of extmodule XilinxSimpleDualPortNoChangeBRAM with defname XilinxSimpleDualPortNoChangeBRAM are different for an extmodule with the same defname
firrtl.passes.CheckHighFormlike$DefnameDifferentPortsException: : ports of extmodule XilinxSimpleDualPortNoChangeBRAM_1 with defname XilinxSimpleDualPortNoChangeBRAM are different for an extmodule with the same defname
// and so on 241 times

以下是XilinxSimpleDualPortNoChangeBRAM的定义及其依赖项:

class XilinxSimpleDualPortNoChangeBRAM(width: Int,depth: Int,performance: String="HIGH_PERFORMANCE",initFile: String="",ramStyle: String="block",val useReset: Boolean=false)
                                       extends BlackBox(Map("RAM_WIDTH" -> width,"RAM_DEPTH" -> depth,"RAM_PERFORMANCE" -> performance,"INIT_FILE" -> initFile,"RAM_STYLE" -> ramStyle))
                                       with HasBlackBoxResource with Memory {
    val io = IO(new XilinxSimpleDualPortBRAMBlackBoxIO(log2Ceil(depth),width))
    val acceptedRamStyles = Seq("block","distributed","registers","ultra")
    require(acceptedRamStyles contains ramStyle)
    def write(wrAddr: UInt,wrData: UInt,Wren: Bool): Unit = {
      io.wea := Wren
      io.addra := wrAddr
      io.dina := wrData
    }

    def read(rdAddr: UInt,rdEn: Bool): UInt = {
      io.addrb := rdAddr
      io.regceb := rdEn
      io.enb := rdEn
      io.doutb
    }

    def defaultBindings(clock: Clock,reset: core.Reset): Unit = {
      io.clock := clock
      if(useReset)
        io.reset := reset
      else
        io.reset := false.B
    }
    setResource("/XilinxSimpleDualPortNoChangeBRAM.v")
}

trait Memory extends BaseModule {
  def read(rdAddr: UInt,rdEn: Bool): UInt
  def write(wrAddr: UInt,Wren: Bool): Unit
  val latency: Int = 2
}

class XilinxSimpleDualPortBRAMIO(addrWidth: Int,dataWidth: Int) extends Bundle {
    val addra  = Input(UInt(addrWidth.W))
    val addrb  = Input(UInt(addrWidth.W))
    val dina   = Input(UInt(dataWidth.W))
    val wea    = Input(Bool())
    val enb    = Input(Bool())
    val regceb = Input(Bool())
    val doutb  = Output(UInt(dataWidth.W))

    override def cloneType = (new XilinxSimpleDualPortBRAMIO(addrWidth,dataWidth)).asInstanceOf[this.type]
}
class XilinxSimpleDualPortBRAMBlackBoxIO(addrWidth: Int,dataWidth: Int) extends XilinxSimpleDualPortBRAMIO(addrWidth,dataWidth) {
    val clock  = Input(Clock())
    val reset  = Input(Reset())

    override def cloneType = (new XilinxSimpleDualPortBRAMBlackBoxIO(addrWidth,dataWidth)).asInstanceOf[this.type]
}

Verilog资源XilinxSimpleDualPortNoChangeBRAM.v是Vivado中可用的BRAM实例化模板之一:

module XilinxSimpleDualPortNoChangeBRAM #(
  parameter RAM_WIDTH = 64,// Specify RAM data width
  parameter RAM_DEPTH = 512,// Specify RAM depth (number of entries)
  parameter RAM_PERFORMANCE = "HIGH_PERFORMANCE",// Select "HIGH_PERFORMANCE" or "LOW_LATENCY"
  parameter INIT_FILE = "",// Specify name/location of RAM initialization file if using one (leave blank if not)
  parameter RAM_STYLE = "block"                   // Target memory type. Accepted values: block,distributed,registers,ultra (UltRascale+ only)
) (
  input [clogb2(RAM_DEPTH-1)-1:0] addra,// Write address bus,width determined from RAM_DEPTH
  input [clogb2(RAM_DEPTH-1)-1:0] addrb,// Read address bus,width determined from RAM_DEPTH
  input [RAM_WIDTH-1:0] dina,// RAM input data
  input wea,// Write enable
  input enb,// Read Enable,for additional power savings,disable when not in use
  input regceb,// Output register enable
  output [RAM_WIDTH-1:0] doutb,// RAM output data
  input clock,// Clock
  input reset                          // Output reset (does not affect memory contents)
);

  (* ram_style = RAM_STYLE *) reg [RAM_WIDTH-1:0] BRAM [RAM_DEPTH-1:0];
  reg [RAM_WIDTH-1:0] ram_data = {RAM_WIDTH{1'b0}};

  // The following code either initializes the memory values to a specified file or to all zeros to match hardware
  generate
    if (INIT_FILE != "") begin: use_init_file
      initial
        $readmemh(INIT_FILE,BRAM,RAM_DEPTH-1);
    end else begin: init_bram_to_zero
      integer ram_index;
      initial
        for (ram_index = 0; ram_index < RAM_DEPTH; ram_index = ram_index + 1)
          BRAM[ram_index] = {RAM_WIDTH{1'b0}};
    end
  endgenerate

  always @(posedge clock) begin
    if (wea)
      BRAM[addra] <= dina;
    if (enb)
      ram_data <= BRAM[addrb];
  end

  //  The following code generates HIGH_PERFORMANCE (use output register) or LOW_LATENCY (no output register)
  generate
    if (RAM_PERFORMANCE == "LOW_LATENCY") begin: no_output_register

      // The following is a 1 clock cycle read latency at the cost of a longer clock-to-out timing
       assign doutb = ram_data;

    end else begin: output_register

      // The following is a 2 clock cycle read latency with improve clock-to-out timing

      reg [RAM_WIDTH-1:0] doutb_reg = {RAM_WIDTH{1'b0}};

      always @(posedge clock)
        if (reset)
          doutb_reg <= {RAM_WIDTH{1'b0}};
        else if (regceb)
          doutb_reg <= ram_data;

      assign doutb = doutb_reg;

    end
  endgenerate

  //  The following function calculates the address width based on specified RAM depth
  function integer clogb2;
    input integer depth;
      for (clogb2=0; depth>0; clogb2=clogb2+1)
        depth = depth >> 1;
  endfunction

endmodule

我试图查看引发此异常的文件CheckHighForm.scala,但是由于我不知道应该寻找什么,我很快就迷路了。

我从CheckSpec.scala的测试中了解到它应该throw an exception if ExtModules have matching port names and widths,but a different order,因此我尝试使Chisel BlackBox中的输入顺序与Verilog模块中的输入顺序相同,但我仍然有例外。

测试throw an exception if parameterless ExtModules have the same ports,but different widths使我认为,具有不同端口宽度的多个实例化可能是导致异常的原因,但随后还有另一项测试表明应该NOT throw an exception if ExtModules have parameters,matching port names,but different widths,这种情况这里的端口宽度由参数控制。

此异常的原因可能是什么?

更新:根据要求,这是黑盒的两个实例的FIRRTL IR:

  extmodule XilinxSimpleDualPortNoChangeBRAM : 
    input addra : UInt<14>
    input addrb : UInt<14>
    input dina : UInt<1>
    input wea : UInt<1>
    input enb : UInt<1>
    input regceb : UInt<1>
    output doutb : UInt<1>
    input clock : Clock
    input reset : Reset
    
    defname = XilinxSimpleDualPortNoChangeBRAM
    parameter RAM_STYLE = "block"
    parameter RAM_WIDTH = 1
    parameter RAM_DEPTH = 16384
    parameter RAM_PERFORMANCE = "HIGH_PERFORMANCE"
    parameter INIT_FILE = ""

  extmodule XilinxSimpleDualPortNoChangeBRAM_1 : 
    input addra : UInt<6>
    input addrb : UInt<6>
    input dina : UInt<518>
    input wea : UInt<1>
    input enb : UInt<1>
    input regceb : UInt<1>
    output doutb : UInt<518>
    input clock : Clock
    input reset : Reset
    
    defname = XilinxSimpleDualPortNoChangeBRAM
    parameter RAM_STYLE = "distributed"
    parameter RAM_WIDTH = 518
    parameter RAM_DEPTH = 64
    parameter RAM_PERFORMANCE = "HIGH_PERFORMANCE"
    parameter INIT_FILE = ""

更新2:显然,一些XilinxSimpleDualPortNoChangeBRAM正在使用XilinxSimpleDualPortBRAMBlackBoxIO的较旧版本,其中重置仍为Bool类型而不是Reset类型。进行更改可以解决问题。

解决方法

在检查特定的BlackBox时,此检查应被禁止。即,必须满足以下条件:

  • 如果BlackBox没有参数,则所有端口必须具有相同的名称,相同的宽度和相同的顺序
  • 如果BlackBox具有参数,则所有端口都必须具有相同的名称并具有相同的顺序(但可以具有不同的宽度)

听起来您的示例正在生成违反后一种条件的BlackBox(因为您的BlackBox具有参数),或者这暴露了FIRRTL编译器检查中的错误。

从不检查实际的Verilog模块,并且在这里不会引起任何问题。

您能否更新您的问题以提供产生这些错误的FIRRTL IR?具体来说,XilinxSimpleDualPortNoChangeBRAMXilinxSimpleDualPortNoChangeBRAM_1的FIRRTL IR是什么样的?该文件应位于“ Foo.fir”之类的文件中。或者,您可以执行以下操作:

import chisel3.stage.ChiselStage

/* Note: this is emitChirrtl ("chirrtl") as you want the FIRRTL emitted from Chisel. */
println(ChiselStage.emitChirrtl(new MyTopModule))

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。