本文以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使用多个寄存器构成寄存器同步链的方式进行跨时钟域同步,这种方式适合于单比特数据的同步。使用该原语时需要注意:
- 为了保证不存在漏采脉冲信号,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得到的。
可调参数 | 功能 |
---|---|
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信号确认传输。这种方式不需要使用存储资源进行同步,适合传输多位宽非连续的数据。使用该原语时需要注意:
- 对于源时钟域逻辑而言,在目的时钟域确认握手结束前不能发起新的传输,即每次src_send拉高都需要等到src_rcv拉低后一个周期再进行
- 对于源时钟域逻辑而言,在目的时钟域未确认握手时需要持续等待,即每次src_send拉低都需要等到src_rcv拉高后一个周期再进行
- 对于目的时钟域逻辑而言,在源时钟域未发起握手时不能发起新的确认,即每次dest_ack拉高都需要dest_req拉高后一个周期进行
- 对于目的时钟域逻辑而言,在源时钟域未确认握手时需要持续等待,即每次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
本原语用于脉冲信号的跨时钟域传输,使用该原语时需要注意:
- 复位期间不能传输脉冲信号,即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的功能。该原语需要注意:
- 输入数据宽度需要保证能在目的时钟域有效沿采样至少两次
可调参数 | 功能 |
---|---|
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的功能。该原语需要注意:
- 输入复位信号宽度需要保证能在目的时钟域有效沿采样至少两次
可调参数 | 功能 |
---|---|
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
}