跨时钟域学习记录(二)——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
    }
相关推荐
深圳信迈科技DSP+ARM+FPGA4 小时前
基于X86+FPGA+AI的智能仓储AGV机器人解决方案
fpga开发
LEEE@FPGA11 小时前
FPGA DDR4读写实验(1)
fpga开发
顺子学不会FPGA11 小时前
SerDes介绍以及原语使用介绍(2)OSERDESE2原语仿真
fpga开发
小慧同学~16 小时前
FPGA/数字IC复习八股
fpga开发
qq_3923999019 小时前
FPGA的理解,个人的见解,不一定对
fpga开发
king8888666619 小时前
FPGA学习路线
fpga开发
神仙约架19 小时前
【总线】AXI4第六课时:寻址选项深入解析
fpga开发·axi·axi4·总线
零度随想19 小时前
使用fifo IP核,给fifo写数据,当检测到ALMOST_EMPTY时,为什么不能立即赋值
fpga开发
深圳信迈科技DSP+ARM+FPGA1 天前
基于x86/ARM+FPGA+AI工业相机的智能工艺缺陷检测,可以检测点状,线状,面状的缺陷
arm开发·fpga开发
攸志2 天前
Chirp信号生成(FPGA、基于cordic IP核)
fpga开发