跨时钟域学习记录(二)——XPM_CDC

  本文以Xilinx提供的xpm_cdc代码为例,整理处理跨时钟域数据传输的常见方法。

宏定义

  Xilinx定义了多个宏定义代替描述触发器行为的always块,列举如下

宏名称 含义
XPM_XSRREG 带同步复位/置位的同步寄存器
XPM_XSRREGEN 带同步复位/置位和使能的寄存器
XPM_XARREG 带异步复位/置位和使能的寄存器
c 复制代码
// Define Xilinx Synchronous Register.  Only to be used by xpm_cdc_* modules.
`define XPM_XSRREG(clk, reset_p, q, d, rstval)   \
  always @(posedge clk) begin                    \
    if (reset_p == 1'b1)                         \
      q <= rstval;                               \
    else                                         \
      q <= d;                                    \
  end

// Define Xilinx Synchronous Register with Enable.  Only to be used by xpm_cdc_* modules.
`define XPM_XSRREGEN(clk, reset_p, q, d, en, rstval)   \
  always @(posedge clk) begin                          \
    if (reset_p == 1'b1)                               \
      q <= rstval;                                     \
    else                                               \
      if (en == 1'b1)                                  \
        q <= d;                                        \
  end

// Define Xilinx Asynchronous Register. Only to be used by xpm_cdc_* modules.
`define XPM_XARREG(clk, reset_p, q, d, rstval)   \
  always @(posedge clk or posedge reset_p)       \
  begin                                          \
    if (reset_p == 1'b1)                         \
      q <= rstval;                               \
    else                                         \
      q <= d;                                    \
  end

1. xpm_cdc_single

   cdc_single使用多个寄存器构成寄存器同步链的方式进行跨时钟域同步,这种方式适合于单比特数据的同步。使用该原语时需要注意:

  1. 为了保证不存在漏采脉冲信号,Xilinx规定用户逻辑需要保证每次源时钟脉冲(数据变化)至少为两个目的时钟周期。
可调参数 功能
DEST_SYNC_FF 可设置为大于2的值,用于设置目的时钟域的同步寄存器级数
SRC_INPUT_REG 可设置为0或1,在不为0时,会在源时钟域添加一级XPM_XSRREG寄存器采样src_in
c 复制代码
module xpm_cdc_single #(
  // Module parameters
  parameter integer DEST_SYNC_FF    = 4,
  parameter integer INIT_SYNC_FF    = 0,
  parameter integer SIM_ASSERT_CHK  = 0,
  parameter integer SRC_INPUT_REG   = 1,
  parameter integer VERSION         = 0
) (
  // Module ports
  input  wire         src_clk,
  input  wire         src_in,
  input  wire         dest_clk,
  output wire         dest_out
);

  // Set Asynchronous Register property on synchronizers
  (* XPM_CDC = "SINGLE", ASYNC_REG = "TRUE" *) reg [DEST_SYNC_FF-1:0] syncstages_ff;

  reg  src_ff;
  wire src_inqual;
  wire async_path_bit;

  assign dest_out       = syncstages_ff[DEST_SYNC_FF-1];
  assign async_path_bit = src_inqual;

  // Virtual mux:  Register at input optional.
  generate
  if (SRC_INPUT_REG) begin : extra_inreg
    assign src_inqual = src_ff;
  end : extra_inreg
  else begin : no_extra_inreg
    assign src_inqual = src_in;
  end : no_extra_inreg
  endgenerate

  // Instantiate Xilinx Synchronous Register
    `XPM_XSRREG(src_clk , 1'b0,  src_ff,        src_in,         1'b0)
    `XPM_XSRREG(dest_clk, 1'b0,  syncstages_ff, { syncstages_ff[DEST_SYNC_FF-2:0], async_path_bit} , {DEST_SYNC_FF{1'b0}})

endmodule : xpm_cdc_single

# 单比特伪路径
    set_false_path -to [get_cells syncstages_ff_reg[0][*]]

2. xpm_cdc_gray

   通过格雷码方式传输多位宽连续变化数据(相邻变化范围不超过1),比较适合用作计数器的同步(如异步FIFO中)。使用该原语时需要注意:

1.为了保证不存在漏采脉冲信号,Xilinx规定用户逻辑需要保证每次源时钟脉冲(数据变化)至少为两个目的时钟周期。

  1. 为了保证数据的正确性,源时钟域发出的新数据需要是由旧数据加1或减1得到的。
可调参数 功能
DEST_SYNC_FF 可设置为大于2的值,用于设置目的时钟域的同步寄存器级数
REG_OUTPUT 可设置为0或1,在不为0时,会在源时钟域添加一级XPM_XSRREG寄存器采样src_in_bin的格雷码结果
WIDTH 待同步数据位宽
c 复制代码
module xpm_cdc_gray #(
  // Module parameters
  parameter integer DEST_SYNC_FF          = 4,
  parameter integer INIT_SYNC_FF          = 0,
  parameter integer REG_OUTPUT            = 0,
  parameter integer SIM_ASSERT_CHK        = 0,
  parameter integer SIM_LOSSLESS_GRAY_CHK = 0,
  parameter integer VERSION               = 0,
  parameter integer WIDTH                 = 2
) (
  // Module ports
  input  wire             src_clk,
  input  wire [WIDTH-1:0] src_in_bin,
  input  wire             dest_clk,
  output wire [WIDTH-1:0] dest_out_bin
);

  // Set Asynchronous Register property on synchronizers
  (* XPM_CDC = "GRAY", ASYNC_REG = "TRUE" *) reg [WIDTH-1:0] dest_graysync_ff [DEST_SYNC_FF-1:0];

  `ifdef XPM_CDC_BHVSIM_ONLY
    if (INIT_SYNC_FF == 1) begin
      always @(glblGSR_xpmcdc)
        if (glblGSR_xpmcdc)
          force dest_graysync_ff = '{default:(0)};
        else
          release dest_graysync_ff;
    end
  `endif

  wire [WIDTH-1:0] gray_enc;
  reg  [WIDTH-1:0] binval;

  reg  [WIDTH-1:0] src_gray_ff;
  wire [WIDTH-1:0] synco_gray;
  wire [WIDTH-1:0] async_path;
  reg  [WIDTH-1:0] dest_out_bin_ff;

  always @(posedge dest_clk) begin
    dest_graysync_ff[0] <= async_path; // @2

    for (int syncstage = 1; syncstage < DEST_SYNC_FF ;syncstage = syncstage + 1)
      dest_graysync_ff[syncstage] <= dest_graysync_ff [syncstage-1];
  end

  assign async_path = src_gray_ff; // @1

  assign synco_gray = dest_graysync_ff[DEST_SYNC_FF-1];
  assign gray_enc = src_in_bin ^ {1'b0, src_in_bin[WIDTH-1:1]};

  // Convert gray code back to binary
  always_comb begin
    binval[WIDTH-1] = synco_gray[WIDTH-1];
    for (int j = WIDTH - 2; j >= 0; j = j - 1)
        binval[j] = binval[j+1] ^ synco_gray[j];
  end

  generate
  if(REG_OUTPUT) begin : reg_out
    assign dest_out_bin     = dest_out_bin_ff;
  end : reg_out
  else begin : comb_out
    assign dest_out_bin     = binval;
  end : comb_out
  endgenerate

  // Instantiate Xilinx Synchronous Register
    `XPM_XSRREG(src_clk, 1'b0,  src_gray_ff, gray_enc, {WIDTH{1'b0}})
    `XPM_XSRREG(dest_clk, 1'b0,  dest_out_bin_ff, binval, {WIDTH{1'b0}})


endmodule : xpm_cdc_gray

# 保证格雷码各位传输的最大路径延迟小于一个源时钟周期
    set_max_delay -from [get_cells src_gray_ff_reg*] -to [get_cells dest_graysync_ff_reg[0]*] $src_clk_period -datapath_only

# 保证格雷码各位传输的偏斜不超过一个时钟周期
    set_bus_skew  -from [get_cells src_gray_ff_reg*] -to [get_cells dest_graysync_ff_reg[0]*] [expr min ($src_clk_period, $dest_clk_period)]
    

3. xpm_cdc_handshake

  具有握手信号的跨时钟域传输,源时钟域通过src_send发起新的传输,通过src_rcv信号确认传输结束,目的时钟域通过dest_req得知出现新的传输,通过dest_ack信号确认传输。这种方式不需要使用存储资源进行同步,适合传输多位宽非连续的数据。使用该原语时需要注意:

  1. 对于源时钟域逻辑而言,在目的时钟域确认握手结束前不能发起新的传输,即每次src_send拉高都需要等到src_rcv拉低后一个周期再进行
  2. 对于源时钟域逻辑而言,在目的时钟域未确认握手时需要持续等待,即每次src_send拉低都需要等到src_rcv拉高后一个周期再进行
  3. 对于目的时钟域逻辑而言,在源时钟域未发起握手时不能发起新的确认,即每次dest_ack拉高都需要dest_req拉高后一个周期进行
  4. 对于目的时钟域逻辑而言,在源时钟域未确认握手时需要持续等待,即每次dest_ack拉低都需要等到dest_req拉低后一个周期再进行
可调参数 功能
DEST_EXT_HSK 可设置为0和1,当为1时启用dest_ack信号由用户逻辑在需要确认时确认握手,为0时由系统自行通过dest_req确认握手
DEST_SYNC_FF 可设置为大于2的值,用于设置握手信号在目的时钟域的同步寄存器级数
SRC_SYNC_FF 可设置为大于2的值,用于设置握手信号在源时钟域的同步寄存器级数
WIDTH 待同步数据位宽
c 复制代码
module xpm_cdc_handshake #(
  // Module parameters
  parameter integer DEST_EXT_HSK    = 1,
  parameter integer DEST_SYNC_FF    = 4,
  parameter integer INIT_SYNC_FF    = 0,
  parameter integer SIM_ASSERT_CHK  = 0,
  parameter integer SRC_SYNC_FF     = 4,
  parameter integer VERSION         = 0,
  parameter integer WIDTH           = 1
) (
  // Module ports
  input  wire             src_clk,
  input  wire [WIDTH-1:0] src_in,
  input  wire             src_send,
  output wire             src_rcv,

  input  wire             dest_clk,
  output wire [WIDTH-1:0] dest_out,
  output wire             dest_req,
  input  wire             dest_ack
);

  // -------------------------------------------------------------------------------------------------------------------
  // Local parameter definitions
  // -------------------------------------------------------------------------------------------------------------------

  // Set Asynchronous Register property on synchronizers
  (* XPM_CDC = "HANDSHAKE" *) reg [WIDTH-1:0] dest_hsdata_ff;

  // -------------------------------------------------------------------------------------------------------------------
  // Simulation only variable and signal assignment
  // -------------------------------------------------------------------------------------------------------------------
  
  // We can do set max delay between source and dest.
  // For option with no input register, we have to create a smart constraint
  // for set max delay.
  reg  [WIDTH-1:0] src_hsdata_ff;
  wire             dest_req_nxt;
  reg              dest_req_ff;
  (* DIRECT_ENABLE = "yes" *) wire  dest_hsdata_en;
  wire             dest_req_ext_nxt;
  reg              dest_req_ext_ff;
  wire [WIDTH-1:0] src_hsdata_nxt;
  wire [WIDTH-1:0] dest_hsdata_nxt;
  wire [WIDTH-1:0] src_data_src;
  wire             dest_req_sync;
  wire             dest_ack_sync_in;
  reg              src_sendd_ff;
  wire             src_sendd_nxt;

  // -------------------------------------------------------------------------------------------------------------------
  // xpm_cdc_single instantiation
  // -------------------------------------------------------------------------------------------------------------------
  xpm_cdc_single #  (
    .DEST_SYNC_FF   (DEST_SYNC_FF  ),
    .INIT_SYNC_FF   (INIT_SYNC_FF  ),
    .SRC_INPUT_REG  (0             ),
    .VERSION        (VERSION       )
  ) xpm_cdc_single_src2dest_inst (

    .src_clk        (src_clk       ),
    .dest_clk       (dest_clk      ),
    .src_in         (src_sendd_ff  ),
    .dest_out       (dest_req_sync )
  );

  //src_data is always registered once
  assign src_data_src = src_hsdata_ff;

  assign src_hsdata_nxt  = (src_sendd_ff == 1'b0) ? src_in : src_hsdata_ff;
  assign dest_hsdata_nxt = src_data_src;
  assign dest_out        = dest_hsdata_ff;
  assign dest_req_nxt    = dest_req_sync;
  assign dest_hsdata_en  = ~dest_req_ff && dest_req_sync;
  assign src_sendd_nxt   = src_send;

  // -------------------------------------------------------------------------------------------------------------------
  // xpm_cdc_single instantiation
  // -------------------------------------------------------------------------------------------------------------------
  xpm_cdc_single #  (
    .DEST_SYNC_FF   (SRC_SYNC_FF   ),
    .INIT_SYNC_FF   (INIT_SYNC_FF  ),
    .SRC_INPUT_REG  (0             ),
    .VERSION        (VERSION       )
  ) xpm_cdc_single_dest2src_inst (
    .src_clk        (dest_clk        ),
    .dest_clk       (src_clk         ),
    .src_in         (dest_ack_sync_in),
    .dest_out       (src_rcv         )
  );

  generate
  if(DEST_EXT_HSK) begin : ext_desthsk
    assign dest_ack_sync_in = dest_ack;
    assign dest_req_ext_nxt = dest_req_sync ;
  end : ext_desthsk
  else begin : internal_desthsk
    assign dest_ack_sync_in = dest_req_ff;
    assign dest_req_ext_nxt = dest_req_sync & ~dest_req_ff;
  end : internal_desthsk
  endgenerate

  assign dest_req = dest_req_ext_ff;

  // Instantiate Xilinx Synchronous Register
      `XPM_XSRREG(src_clk,  1'b0,  src_sendd_ff,    src_sendd_nxt,    1'b0)
      `XPM_XSRREG(src_clk,  1'b0,  src_hsdata_ff,   src_hsdata_nxt,   {WIDTH{1'b0}})
      `XPM_XSRREGEN(dest_clk, 1'b0,  dest_hsdata_ff,  dest_hsdata_nxt, dest_hsdata_en ,{WIDTH{1'b0}})
      `XPM_XSRREG(dest_clk, 1'b0,  dest_req_ff,     dest_req_nxt,     1'b0)
      `XPM_XSRREG(dest_clk, 1'b0,  dest_req_ext_ff, dest_req_ext_nxt, 1'b0)


endmodule : xpm_cdc_handshake

# 在数据位宽小于100时控制最大延时、偏斜为多周期路径
    if {$xpm_cdc_hs_width <= 100} {
        set_max_delay -from [get_cells src_hsdata_ff_reg*] -to [get_cells dest_hsdata_ff_reg*] [expr {$dest_clk_period * $xpm_cdc_hs_num_s2d_dsync_ff}] -datapath_only
        set_bus_skew  -from [get_cells src_hsdata_ff_reg*] -to [get_cells dest_hsdata_ff_reg*] [expr {$dest_clk_period * $xpm_cdc_hs_num_s2d_dsync_ff}]
    } else {
        set_max_delay -from [get_cells src_hsdata_ff_reg*] -to [get_cells dest_hsdata_ff_reg*] [expr min ($src_clk_period, $dest_clk_period)] -datapath_only
    }

4. xpm_cdc_pulse

   本原语用于脉冲信号的跨时钟域传输,使用该原语时需要注意:

  1. 复位期间不能传输脉冲信号,即src_rst或dest_rst有效期间src_pulse不能拉高
可调参数 功能
DEST_SYNC_FF 可设置为大于2的值,用于设置握手信号在目的时钟域的同步寄存器级数
REG_OUTPUT 0或1,使用额外1级触发器同步目的时钟域输出脉冲
RST_USED 0或1,1启用复位引脚
c 复制代码
module xpm_cdc_pulse #(
  parameter integer DEST_SYNC_FF    = 4,
  parameter integer INIT_SYNC_FF    = 0,
  parameter integer REG_OUTPUT      = 0,
  parameter integer RST_USED        = 1,
  parameter integer SIM_ASSERT_CHK  = 0,
  parameter integer VERSION         = 0
) (
  input  wire       src_clk,
  input  wire       src_pulse,
  input  wire       dest_clk,
  input  wire       src_rst,
  input  wire       dest_rst,
  output wire       dest_pulse
);

  
  // If toggle flop is not initialized,then it can be un-known forever.
  // It is assumed that there is no loss of coverage either way.
  // For edge detect, we would want the logic to be more controlled.
  reg  src_level_ff = 1'b0;

  reg  src_in_ff;
  wire src_level_nxt;
  wire src_edge_det;
  wire src_sync_in;

  wire dest_sync_out;
  wire dest_event_nxt;
  reg  dest_event_ff;
  wire dest_sync_qual;

  wire src_rst_qual;
  wire dest_rst_qual;

  wire dest_pulse_int;
  reg  dest_pulse_ff;

  //Assignments
  assign src_edge_det   = src_pulse & ~src_in_ff; // 上升沿
  assign src_level_nxt  = src_level_ff ^ src_edge_det; // 有上升沿且src_level_ff为低电平 或 无上升沿且src_level_ff为高电平,将脉冲信号转换为电平信号
  assign src_sync_in    = src_level_ff;     // 同步电平信号源时钟域
  assign dest_event_nxt = dest_sync_qual;
  assign dest_pulse_int = dest_sync_qual ^ dest_event_ff; // 电平信号转脉冲信号
  assign dest_sync_qual = dest_sync_out & ~dest_rst_qual; 

  generate
  if(RST_USED) begin : use_rst
    assign src_rst_qual = src_rst;
    assign dest_rst_qual = dest_rst;
  end : use_rst
  else begin : no_rst
    assign src_rst_qual = 1'b0;
    assign dest_rst_qual = 1'b0;
  end : no_rst
  endgenerate

  generate
  if(REG_OUTPUT) begin : reg_out
    assign dest_pulse     = dest_pulse_ff;
  end : reg_out
  else begin : comb_out
    assign dest_pulse     = dest_pulse_int;
  end : comb_out
  endgenerate

  xpm_cdc_single # (
    .DEST_SYNC_FF   (DEST_SYNC_FF ),
    .INIT_SYNC_FF   (INIT_SYNC_FF ),
    .SRC_INPUT_REG  (0            ),
    .VERSION        (VERSION      )
  ) xpm_cdc_single_inst (
    .src_clk       (src_clk       ),
    .dest_clk      (dest_clk      ),
    .src_in        (src_sync_in   ),
    .dest_out      (dest_sync_out )
  );

  // Instantiate Xilinx Synchronous Register
      `XPM_XSRREG(src_clk,  src_rst_qual,   src_in_ff,     src_pulse,      1'b0)
      `XPM_XSRREG(src_clk,  src_rst_qual,   src_level_ff,  src_level_nxt,  1'b0)
      `XPM_XSRREG(dest_clk, dest_rst_qual,  dest_event_ff, dest_event_nxt, 1'b0)
      `XPM_XSRREG(dest_clk, dest_rst_qual,  dest_pulse_ff, dest_pulse_int, 1'b0)


endmodule : xpm_cdc_pulse

5. xpm_cdc_array_single

  该原语对多位宽数据进行同步,用于实现多个xpm_cdc_single的功能。该原语需要注意:

  1. 输入数据宽度需要保证能在目的时钟域有效沿采样至少两次
可调参数 功能
DEST_SYNC_FF 可设置为大于2的值,用于设置握手信号在目的时钟域的同步寄存器级数
SRC_INPUT_REG 0或1,使用额外1级触发器采样源时钟域的输入数据
WIDTH 待同步数据位宽
c 复制代码
module xpm_cdc_array_single # (
  parameter integer DEST_SYNC_FF    = 4,
  parameter integer INIT_SYNC_FF    = 0,
  parameter integer SIM_ASSERT_CHK  = 0,
  parameter integer SRC_INPUT_REG   = 1,
  parameter integer VERSION         = 0,
  parameter integer WIDTH           = 2
) (
  input  wire             src_clk,
  input  wire [WIDTH-1:0] src_in,
  input  wire             dest_clk,
  output wire [WIDTH-1:0] dest_out
);


(* XPM_CDC = "ARRAY_SINGLE", ASYNC_REG = "TRUE" *) reg  [WIDTH-1:0] syncstages_ff [DEST_SYNC_FF-1:0];

  // -------------------------------------------------------------------------------------------------------------------
  // Simulation only variable and signal assignment
  // -------------------------------------------------------------------------------------------------------------------
 
  reg  [WIDTH-1:0] src_ff;
  wire [WIDTH-1:0] src_inqual;
  wire [WIDTH-1:0] async_path_bit;

  assign dest_out = syncstages_ff[DEST_SYNC_FF-1];

  always @(posedge dest_clk) begin
    syncstages_ff[0] <= async_path_bit;

    for (int syncstage = 1; syncstage < DEST_SYNC_FF ;syncstage = syncstage + 1)
      syncstages_ff[syncstage] <= syncstages_ff [syncstage-1];
  end

  assign async_path_bit = src_inqual;

  // Virtual mux:  Register at input optional.
  generate
  if (SRC_INPUT_REG) begin : extra_inreg
    assign src_inqual = src_ff;
  end : extra_inreg
  else begin : no_extra_inreg
    assign src_inqual = src_in;
  end : no_extra_inreg
  endgenerate

  genvar vara_i;
  generate
      // Instantiate Xilinx Synchronous Register 
        `XPM_XSRREG(src_clk, 1'b0, src_ff, src_in, {WIDTH{1'b0}})
  endgenerate

endmodule : xpm_cdc_array_single

# 单比特伪路径
    set_false_path -to [get_cells syncstages_ff_reg[0][*]]

6. xpm_cdc_sync_rst

  该原语用于同步复位信号的跨时钟域同步,类似xpm_cdc_single的功能。该原语需要注意:

  1. 输入复位信号宽度需要保证能在目的时钟域有效沿采样至少两次
可调参数 功能
DEST_SYNC_FF 可设置为大于2的值,用于设置握手信号在目的时钟域的同步寄存器级数
INIT 用于设置同步寄存器默认值
c 复制代码
module xpm_cdc_sync_rst # (
  parameter integer DEST_SYNC_FF    = 4,
  parameter integer INIT            = 1,
  parameter integer INIT_SYNC_FF    = 0,
  parameter integer SIM_ASSERT_CHK  = 0,
  parameter integer VERSION         = 0
  ) (
  input  wire       src_rst,
  input  wire       dest_clk,
  output wire       dest_rst
);


  // Define local parameter for settings
  localparam DEF_VAL        = (INIT == 1) ? 1'b1 : 1'b0;

  // Set asynchronous register property on synchronizers and initialize register with INIT value
  (* XPM_CDC = "SYNC_RST", ASYNC_REG = "TRUE" *) reg [DEST_SYNC_FF-1:0] syncstages_ff = {DEST_SYNC_FF{DEF_VAL}};

  // -------------------------------------------------------------------------------------------------------------------
  // Simulation only variable and signal assignment
  // -------------------------------------------------------------------------------------------------------------------
 
  wire async_path_bit;

  assign dest_rst = syncstages_ff[DEST_SYNC_FF-1];
  assign async_path_bit = src_rst;

  // Instantiate Xilinx Synchronous Register
  
      `XPM_XSRREG(dest_clk, 1'b0,  syncstages_ff, { syncstages_ff[DEST_SYNC_FF-2:0], async_path_bit}, {DEST_SYNC_FF{1'b0}})

endmodule : xpm_cdc_sync_rst

# 单比特伪路径
set_false_path -to [get_cells syncstages_ff_reg[0]]

7. xpm_cdc_async_rst

  该原语用于异步复位信号的跨时钟域同步,类似xpm_cdc_sync_rst的功能。

可调参数 功能
DEST_SYNC_FF 可设置为大于2的值,用于设置握手信号在目的时钟域的同步寄存器级数
RST_ACTIVE_HIGH 0或1,分别代表低电平有效异步复位、高电平有效异步复位
c 复制代码
module xpm_cdc_async_rst # (
  parameter integer DEST_SYNC_FF    = 4,
  parameter integer INIT_SYNC_FF    = 0,
  parameter integer RST_ACTIVE_HIGH = 0,
  parameter integer VERSION         = 0
  ) (
  input  wire       src_arst,
  input  wire       dest_clk,
  output wire       dest_arst
);

  // -------------------------------------------------------------------------------------------------------------------
  // Local parameter definitions
  // -------------------------------------------------------------------------------------------------------------------

  // Define local parameter for settings
  localparam DEF_VAL        = (RST_ACTIVE_HIGH == 1) ? 1'b0 : 1'b1;
  localparam INV_DEF_VAL    = (RST_ACTIVE_HIGH == 0) ? 1'b0 : 1'b1;

  // Set asynchronous register property on synchronizers and initialize register with default value
  (* XPM_CDC = "ASYNC_RST", ASYNC_REG = "TRUE" *) reg [DEST_SYNC_FF-1:0] arststages_ff = {DEST_SYNC_FF{DEF_VAL}};

  // -------------------------------------------------------------------------------------------------------------------
  // Simulation only variable and signal assignment
  // -------------------------------------------------------------------------------------------------------------------
 
  wire                    async_path_bit;
  wire                    reset_pol;
  wire                    reset_polo;

  assign reset_polo = arststages_ff[DEST_SYNC_FF-1];
  assign async_path_bit = (RST_ACTIVE_HIGH == 1) ? 1'b0 : 1'b1;
  assign reset_pol = src_arst ^ ~RST_ACTIVE_HIGH;
  assign dest_arst = reset_polo;

  // Instantiate Xilinx Asynchronous Clear Register
      `XPM_XARREG(dest_clk, reset_pol,  arststages_ff, { arststages_ff[DEST_SYNC_FF-2:0], async_path_bit}, {DEST_SYNC_FF{INV_DEF_VAL}})

endmodule : xpm_cdc_async_rst

# 单比特伪路径
set_false_path -through [get_ports -no_traverse src_arst]

8. xpm_cdc_low_latency_handshake

  该原语功能类似与xpm_cdc_handshake,不同之处在于实现了一个深度为1的FIFO,并采用AXIS valid-ready握手的方式实现跨时钟域低延时传输。

可调参数 功能
DEST_EXT_HSK 可设置为0和1,当为1时启用dest_ack信号由用户逻辑在需要确认时确认握手,为0时由系统自行通过dest_req确认握手
DEST_SYNC_FF 可设置为大于2的值,用于设置握手信号在目的时钟域的同步寄存器级数
SRC_SYNC_FF 可设置为大于2的值,用于设置握手信号在源时钟域的同步寄存器级数
WIDTH 待同步数据位宽
c 复制代码
module xpm_cdc_low_latency_handshake #(
  // Module parameters
  parameter integer DEST_EXT_HSK    = 1,
  parameter integer DEST_SYNC_FF    = 4,
  parameter integer INIT_SYNC_FF    = 0,
  parameter integer SIM_ASSERT_CHK  = 0,
  parameter integer SRC_SYNC_FF     = 4,
  parameter integer VERSION         = 0,
  parameter integer WIDTH           = 1
) (
  // Module ports
  input  wire             src_clk,
  input  wire [WIDTH-1:0] src_in,
  input  wire             src_valid,
  output wire             src_ready,

  input  wire             dest_clk,
  output wire [WIDTH-1:0] dest_out,
  output wire             dest_valid,
  input  wire             dest_ready
);
  
  // -------------------------------------------------------------------------------------------------------------------
  // Local parameter definitions
  // -------------------------------------------------------------------------------------------------------------------

  // Set Asynchronous Register property on synchronizers
  (* XPM_CDC = "LOW_LATENCY_HANDSHAKE" *) reg [WIDTH-1:0] dest_hsdata_ff;

  // -------------------------------------------------------------------------------------------------------------------
  // Simulation only variable and signal assignment
  // -------------------------------------------------------------------------------------------------------------------


  reg  [WIDTH-1:0] src_hsdata_ff;
  wire             src_valid_nxt;
  wire              src_count_nxt;
  reg              src_count_ff = 1'b0;
  wire        src_count_sync_ff;
  (* DIRECT_ENABLE = "yes" *) wire  dest_hsdata_ff_en;
  reg        dest_valid_ext_ff;
  wire        dest_valid_nxt;
  wire              dest_ready_in;
  wire       dest_ready_nxt;
  wire             dest_count_nxt;
  wire        dest_count_eq;
  reg              dest_count_ff = 1'b0;
  wire             dest_count_sync_ff;
  wire              src_count_eq;
  wire              src_ready_nxt;
  reg        src_ready_ext_ff;

  assign src_valid_nxt = src_valid && src_ready;
  assign src_count_nxt = (src_valid_nxt == 1'b1) ? (src_count_ff+1'b1) : src_count_ff;

  assign dest_ready_nxt = dest_valid_ext_ff && dest_ready_in  ;
  assign dest_count_nxt = (dest_ready_nxt == 1'b1) ? (dest_count_ff+1'b1) : dest_count_ff;

  assign dest_count_eq  = (src_count_sync_ff == dest_count_ff) ? 1'b1 : 1'b0;
  assign dest_hsdata_ff_en = !dest_count_eq && !dest_valid_ext_ff;
  assign dest_valid_nxt = !dest_count_eq && !dest_ready_nxt;
  assign dest_valid     = dest_valid_ext_ff;


  assign src_count_eq   = (src_count_ff == dest_count_sync_ff) ? 1'b1 : 1'b0;
  assign src_ready_nxt  = src_count_eq && !src_valid_nxt;
  assign src_ready      = src_ready_ext_ff;

  assign dest_out = dest_hsdata_ff;

  generate
  if(DEST_EXT_HSK) begin : ext_desthsk
    assign dest_ready_in = dest_ready;
  end : ext_desthsk
  else begin : internal_desthsk
    assign dest_ready_in = 1'b1;
  end : internal_desthsk
  endgenerate

  //Instantiate Xilinx Synchronous Register
      `XPM_XSRREGEN(src_clk, 1'b0,  src_hsdata_ff,  src_in, src_valid_nxt ,{WIDTH{1'b0}})
      `XPM_XSRREGEN(dest_clk, 1'b0,  dest_hsdata_ff,  src_hsdata_ff, dest_hsdata_ff_en,{WIDTH{1'b0}})

      `XPM_XSRREG(src_clk, 1'b0,  src_count_ff, src_count_nxt, 1'b0)
      `XPM_XSRREG(dest_clk, 1'b0,  dest_count_ff, dest_count_nxt, 1'b0)

      `XPM_XSRREG(dest_clk, 1'b0,  dest_valid_ext_ff, dest_valid_nxt, 1'b0)
      `XPM_XSRREG(src_clk, 1'b0,  src_ready_ext_ff, src_ready_nxt, 1'b0)

  // -------------------------------------------------------------------------------------------------------------------
  // xpm_cdc_single instantiation
  // -------------------------------------------------------------------------------------------------------------------
  xpm_cdc_single #  (
    .DEST_SYNC_FF   (DEST_SYNC_FF  ),
    .INIT_SYNC_FF   (INIT_SYNC_FF  ),
    .SRC_INPUT_REG  (0             ),
    .SIM_ASSERT_CHK (SIM_ASSERT_CHK),
    .VERSION        (VERSION       )
  ) xpm_cdc_single_src2dest_inst (

    .src_clk        (src_clk       ),
    .dest_clk       (dest_clk      ),
    .src_in         (src_count_ff  ),
    .dest_out       (src_count_sync_ff )
  );

    // -------------------------------------------------------------------------------------------------------------------
  // xpm_cdc_single instantiation
  // -------------------------------------------------------------------------------------------------------------------
  xpm_cdc_single #  (
    .DEST_SYNC_FF   (SRC_SYNC_FF   ),
    .INIT_SYNC_FF   (INIT_SYNC_FF  ),
    .SRC_INPUT_REG  (0             ),
    .SIM_ASSERT_CHK (SIM_ASSERT_CHK),
    .VERSION        (VERSION       )
  ) xpm_cdc_single_dest2src_inst (
    .src_clk        (dest_clk      ),
    .dest_clk       (src_clk       ),
    .src_in         (dest_count_ff ),
    .dest_out       (dest_count_sync_ff )
  );

 endmodule : xpm_cdc_low_latency_handshake

# 在数据位宽小于100时控制最大延时、偏斜为多周期路径

    if {$xpm_cdc_hs_width <= 100} {
        set_max_delay -from [get_cells src_hsdata_ff_reg*] -to [get_cells dest_hsdata_ff_reg*] [expr {$dest_clk_period * $xpm_cdc_hs_num_s2d_dsync_ff}] -datapath_only
        set_bus_skew  -from [get_cells src_hsdata_ff_reg*] -to [get_cells dest_hsdata_ff_reg*] [expr {$dest_clk_period * $xpm_cdc_hs_num_s2d_dsync_ff}]
    } else {
        set_max_delay -from [get_cells src_hsdata_ff_reg*] -to [get_cells dest_hsdata_ff_reg*] [expr min ($src_clk_period, $dest_clk_period)] -datapath_only
    }
相关推荐
仲南音2 天前
Xilinx FPGA——时钟IP核MMCM、PLL
fpga开发
I'm a winner2 天前
基于Xilinx FPGA的LVDS高速串行通信系统(三)--数据训练及握手机制【文末源码】
fpga开发
upper20202 天前
coremark移植坑之-nostartfiles -nostdlib参数
学习·fpga开发·coremark移植·riscv设计
Eloudy3 天前
Vivado 纯命令行构建 FPGA 项目教程
fpga开发
Rambo.xia3 天前
AXI跨时钟域与数据一致性——CDC FIFO深度不够、写后读冒险、多Master仲裁、乱序返回,出了Bug根本查不出来
fpga开发·bug
upper20203 天前
FPGA部署卷积神经网络识别MINST图片
fpga开发·卷积神经网络·minst·minst识别·部署神经网络·神经网络识别图片
你是我的解忧王子3 天前
嵌入式 ADC采样 原始数据分析上层(平均值 波动率 正态分布 线性补偿 系统误差)
fpga开发·数据挖掘·数据分析
9527华安4 天前
FPGA纯verilog代码ISP图像处理培训教程,基于IMX214,提供工程源码+视频教程+FPGA开发板
图像处理·fpga开发·isp·imx214·mipi
千寻xun4 天前
二、实战篇-NVME SSD控制之ZYNQ实现(六)-读写NVME SSD硬盘数据
fpga开发·nvme·nvme ssd
千寻xun4 天前
二、实战篇-NVME SSD控制之ZYNQ实现(三)
fpga开发·nvme ssd·nvme协议