Vivado 为我的模块推断出不正确的 FREQ_HZ AXI 总线

Vivado infers incorrect FREQ_HZ for AXI busses to my module

我正在使用 Vivado 进行设计。我的顶层设计是一个框图。框图包含 IP 块和我的 Verilog RTL 模块。每当我更改我的主模块并且 Verilog 更新框图时,它总是错误地推断出我的模块的时钟频率。 我该如何解决这个问题?

这个问题真气人,问它打破了我的设计。设计中的所有其他 AXI 总线都正确使用 10MHz,但每当我更改 main 并更新框图时,Vivado 决定 main 的 AXI 总线处于 100MHz。只要时钟不匹配,我就无法构建。我可以手动更新框图中块的属性中的频率,但每次更新 main 时都会擦除这些更改(这很频繁,因为它是我的 main模块)。

我已经尝试为每个 AXI 总线添加专用时钟和重置(即使它们都连接到同一个网络)。我搞砸了 X_INTERFACE_PARAMETER(这在任何地方都有记录吗?)。都无济于事。

此外,M_AXIS_CMD 是一个主 AXI 接口,应该位于 main 模块的输出端。不知道这是怎么回事。但与时钟相比,这是一个非常小的问题。

来源:https://gitlab.com/tessera/pcd8544-tests

模块接口:

module main #(
        CLK_FREQ = 50000000// : CLK_FREQ > 0 // clock frequency
    )(
        (* X_INTERFACE_PARAMETER = "XIL_INTERFACENAME core_clk, ASSOCIATED_RESET core_rst, FREQ_HZ 10000000" *)
        (* X_INTERFACE_INFO = "xilinx.com:signal:clock:1.0 core_clk CLK" *)
        input clk,
        (* X_INTERFACE_PARAMETER = "XIL_INTERFACENAME core_rst, POLARITY ACTIVE_HIGH" *)
        (* X_INTERFACE_INFO = "xilinx.com:signal:reset:1.0 core_rst RST" *)
        input rst,
        (* X_INTERFACE_PARAMETER = "XIL_INTERFACENAME axi_rst, POLARITY ACTIVE_LOW" *)
        (* X_INTERFACE_INFO = "xilinx.com:signal:reset:1.0 axi_rst RST" *)
        input axi_rst,

        (* X_INTERFACE_INFO = "xilinx.com:interface:fifo_write:1.0 DOUT FULL" *)
        input wr_full,
        (* X_INTERFACE_INFO = "xilinx.com:interface:fifo_write:1.0 DOUT WR_DATA" *)
        output reg [8:0] wr_data,
        (* X_INTERFACE_INFO = "xilinx.com:interface:fifo_write:1.0 DOUT WR_EN" *)
        output reg wr_valid,

        // slave AXI-Lite write channel FROM PS
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG AWADDR"  *) input  wire [4:0]  s_axi_reg_awaddr,  // address
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG AWPROT"  *) input  wire [2:0]  s_axi_reg_awprot,  // channel protection type
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG AWVALID" *) input  wire        s_axi_reg_awvalid, // address valid
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG AWREADY" *) output wire        s_axi_reg_awready, // address ready
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG WDATA"   *) input  wire [31:0] s_axi_reg_wdata,   // data
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG WSTRB"   *) input  wire [3:0]  s_axi_reg_wstrb,   // strobes - one bit per byte of data
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG WVALID"  *) input  wire        s_axi_reg_wvalid,  // data/strobes valid
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG WREADY"  *) output wire        s_axi_reg_wready,  // data/strobes ready
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG BRESP"   *) output wire [1:0]  s_axi_reg_bresp,   // response
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG BVALID"  *) output wire        s_axi_reg_bvalid,  // response valid
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG BREADY"  *) input  wire        s_axi_reg_bready,  // response ready

        // slave AXI-Lite read channel FROM PS
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG ARADDR"  *) input  wire [4:0]  s_axi_reg_araddr,  // address
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG ARPROT"  *) input  wire [2:0]  s_axi_reg_arprot,  // channel protection type
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG ARVALID" *) input  wire        s_axi_reg_arvalid, // address valid
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG ARREADY" *) output wire        s_axi_reg_arready, // address ready
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG RDATA"   *) output wire [31:0] s_axi_reg_rdata,   // data
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG RRESP"   *) output wire [1:0]  s_axi_reg_rresp,   // response
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG RVALID"  *) output wire        s_axi_reg_rvalid,  // data/response valid
        (* X_INTERFACE_INFO = "xilinx.com:interface:aximm_rtl:1.0 S_AXI_REG RREADY"  *) input  wire        s_axi_reg_rready,  // data/response ready



        // master AXI-Stream command channel TO DataMover
        (* X_INTERFACE_INFO = "xilinx.com:interface:axis_rtl:1.0 M_AXIS_CMD TDATA"   *) output reg  [71:0] m_axis_cmd_data,
        (* X_INTERFACE_INFO = "xilinx.com:interface:axis_rtl:1.0 M_AXIS_CMD TREADY"  *) output reg         m_axis_cmd_valid,
        (* X_INTERFACE_INFO = "xilinx.com:interface:axis_rtl:1.0 M_AXIS_CMD TVALID"  *) input  wire        m_axis_cmd_ready,

        // slave AXI-Stream status channel FROM DataMover
        (* X_INTERFACE_INFO = "xilinx.com:interface:axis_rtl:1.0 S_AXIS_STS TDATA"   *) input  wire [7:0]  s_axis_status_data,
        (* X_INTERFACE_INFO = "xilinx.com:interface:axis_rtl:1.0 S_AXIS_STS TKEEP"   *) input  wire [0:0]  s_axis_status_keep,
        (* X_INTERFACE_INFO = "xilinx.com:interface:axis_rtl:1.0 S_AXIS_STS TLAST"   *) input  wire        s_axis_status_last,
        (* X_INTERFACE_INFO = "xilinx.com:interface:axis_rtl:1.0 S_AXIS_STS TREADY"  *) input  wire        s_axis_status_valid,
        (* X_INTERFACE_INFO = "xilinx.com:interface:axis_rtl:1.0 S_AXIS_STS TVALID"  *) output reg         s_axis_status_ready,

        // slave AXI-Stream data channel FROM DataMover
        (* X_INTERFACE_INFO = "xilinx.com:interface:axis_rtl:1.0 S_AXIS_DATA TDATA"  *) input  wire [31:0] s_axis_stream_data,
        (* X_INTERFACE_INFO = "xilinx.com:interface:axis_rtl:1.0 S_AXIS_DATA TKEEP"  *) input  wire [3:0]  s_axis_stream_keep,
        (* X_INTERFACE_INFO = "xilinx.com:interface:axis_rtl:1.0 S_AXIS_DATA TLAST"  *) input  wire        s_axis_stream_last,
        (* X_INTERFACE_INFO = "xilinx.com:interface:axis_rtl:1.0 S_AXIS_DATA TREADY" *) input  wire        s_axis_stream_valid,
        (* X_INTERFACE_INFO = "xilinx.com:interface:axis_rtl:1.0 S_AXIS_DATA TVALID" *) output reg         s_axis_stream_ready,

        // DataMover memory-mapped to stream error
        input wire datamover_mm2s_err
    );

it always incorrectly infers the clock frequency for my module. How can I fix this?

我从你的 git 回购中提取出来,它在 Vivado 2017.4 中运行良好。您可能将几件不同的事情混为一谈,并且可能期待一些不应该发生的事情发生。鉴于缺乏有关专有(但有用)Vivado IP 封装器和方框图工具的文档,这完全可以理解。

  • 2017.4 在功能上与 2017.3 相同,只是增加了部分,所以这不应该造成任何差异

  • 我从时钟的 (* X_INTERFACE_PARAMETER ... *) 中删除了 FREQ_HZ。这 可能 导致了你提到的时钟不匹配,但即使我把它放回去,现在对我来说也能正常工作。但是,FREQ_HZ 只能用于输出(生成的)时钟;见下文。

  • 当我第一次打开图表或更新 main.v 并单击输入引脚时,属性显示 100MHz,正如您所提到的。但在执行 F6 "Validate" 命令后,引脚会正确报告 10MHz。这都是预料之中的。

,

  • 您不应期望 CLK_FREQ Verilog 参数会根据引脚神奇地更新。每当模块是新的时,GUI 中的参数将采用默认值。它们可以被编辑并且应该在一个更新周期内保持不变,但是由于重大编辑它们可能必须被重置。 由您来设置这些参数以匹配

  • 一些 Xilinx 内核,如 AXI Uartlite,使用一些花哨的未记录的 TCL 脚本在验证时自动将引脚频率传输到 Verilog 参数。例如,您可以在 /opt/Xilinx/Vivado/2017.4/data/ip/xilinx/axi_uartlite_v2_0/bd/bd.tcl 文件中看到这是如何完成的。

is this documented anywhere?

UG994、"Inferring Control Signals in a RTL Module"中有一些关于使用HDL模块的文档,但不是很完整。 最好的 文档在 "Lite Bulb" 家伙(Clippy 的远亲)中,您可以通过按编辑器工具栏中的 lite 灯泡找到它。

  • 主动提供的建议 #1 - 使用 RTL 模块听起来是个好主意。比完成打包您自己的 IP 的工作要容易得多。直到你发现:a)它只是在后台打包模块并在每次编辑时自动 re-runs,b)它甚至比打包器更脆弱,c)你无法控制接口推断等在。学习使用打包器创建真正的 IP 包。

  • 主动提供的建议 #2 - 将框图和项目文件保存在 TCL 脚本中。不要将整个项目或框图 .xci 和 .xml 文件放在 Git 中。请参阅以下命令:write_bd_tclwrite_project_tcl。如果您(经常)需要在项目损坏时重新生成项目,这可以得到完全一致的结果。

  • 最后,这里是 header 我设置 (* X_INTERFACE... *) 东西的方式的片段。如果时钟运行 AXI 总线,也添加 ASSOCIATED_BUSIF 标志:

    (* X_INTERFACE_INFO = "xilinx.com:signal:clock:1.0 core_clk CLK" *)
    (* X_INTERFACE_PARAMETER = "ASSOCIATED_RESET reset" *)
    input clk,
    (* X_INTERFACE_INFO = "xilinx.com:signal:reset:1.0 core_rst RST" *)
    (* X_INTERFACE_PARAMETER = "POLARITY ACTIVE_HIGH" *)
    input rst,