1
0
mirror of https://github.com/corundum/corundum.git synced 2025-01-30 08:32:52 +08:00

Rewrite PTP clock CDC module for improved performance and timing closure at 25G

This commit is contained in:
Alex Forencich 2021-03-30 15:57:46 -07:00
parent 42950abf12
commit 31c7349f90
7 changed files with 537 additions and 326 deletions

View File

@ -49,8 +49,6 @@ module eth_mac_10g_fifo #
parameter RX_FRAME_FIFO = 1,
parameter RX_DROP_BAD_FRAME = RX_FRAME_FIFO,
parameter RX_DROP_WHEN_FULL = RX_FRAME_FIFO,
parameter LOGIC_PTP_PERIOD_NS = 4'h6,
parameter LOGIC_PTP_PERIOD_FNS = 16'h6666,
parameter PTP_PERIOD_NS = 4'h6,
parameter PTP_PERIOD_FNS = 16'h6666,
parameter PTP_USE_SAMPLE_CLOCK = 0,
@ -138,6 +136,7 @@ module eth_mac_10g_fifo #
* PTP clock
*/
input wire [PTP_TS_WIDTH-1:0] ptp_ts_96,
input wire ptp_ts_step,
/*
* Configuration
@ -252,10 +251,6 @@ if (TX_PTP_TS_ENABLE) begin
.TS_WIDTH(PTP_TS_WIDTH),
.NS_WIDTH(4),
.FNS_WIDTH(16),
.INPUT_PERIOD_NS(LOGIC_PTP_PERIOD_NS),
.INPUT_PERIOD_FNS(LOGIC_PTP_PERIOD_FNS),
.OUTPUT_PERIOD_NS(PTP_PERIOD_NS),
.OUTPUT_PERIOD_FNS(PTP_PERIOD_FNS),
.USE_SAMPLE_CLOCK(PTP_USE_SAMPLE_CLOCK)
)
tx_ptp_cdc (
@ -265,9 +260,11 @@ if (TX_PTP_TS_ENABLE) begin
.output_rst(tx_rst),
.sample_clk(ptp_sample_clk),
.input_ts(ptp_ts_96),
.input_ts_step(ptp_ts_step),
.output_ts(tx_ptp_ts_96),
.output_ts_step(),
.output_pps()
.output_pps(),
.locked()
);
if (TX_PTP_TAG_ENABLE) begin
@ -432,10 +429,6 @@ if (RX_PTP_TS_ENABLE) begin
.TS_WIDTH(PTP_TS_WIDTH),
.NS_WIDTH(4),
.FNS_WIDTH(16),
.INPUT_PERIOD_NS(LOGIC_PTP_PERIOD_NS),
.INPUT_PERIOD_FNS(LOGIC_PTP_PERIOD_FNS),
.OUTPUT_PERIOD_NS(PTP_PERIOD_NS),
.OUTPUT_PERIOD_FNS(PTP_PERIOD_FNS),
.USE_SAMPLE_CLOCK(PTP_USE_SAMPLE_CLOCK)
)
rx_ptp_cdc (
@ -445,9 +438,11 @@ if (RX_PTP_TS_ENABLE) begin
.output_rst(rx_rst),
.sample_clk(ptp_sample_clk),
.input_ts(ptp_ts_96),
.input_ts_step(ptp_ts_step),
.output_ts(rx_ptp_ts_96),
.output_ts_step(),
.output_pps()
.output_pps(),
.locked()
);
axis_fifo #(

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2019 Alex Forencich
Copyright (c) 2019-2021 Alex Forencich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@ -34,12 +34,7 @@ module ptp_clock_cdc #
parameter TS_WIDTH = 96,
parameter NS_WIDTH = 4,
parameter FNS_WIDTH = 16,
parameter INPUT_PERIOD_NS = 4'h6,
parameter INPUT_PERIOD_FNS = 16'h6666,
parameter OUTPUT_PERIOD_NS = 4'h6,
parameter OUTPUT_PERIOD_FNS = 16'h6666,
parameter USE_SAMPLE_CLOCK = 1,
parameter LOG_FIFO_DEPTH = 3,
parameter LOG_RATE = 3
)
(
@ -53,6 +48,7 @@ module ptp_clock_cdc #
* Timestamp inputs from source PTP clock
*/
input wire [TS_WIDTH-1:0] input_ts,
input wire input_ts_step,
/*
* Timestamp outputs
@ -63,7 +59,12 @@ module ptp_clock_cdc #
/*
* PPS output
*/
output wire output_pps
output wire output_pps,
/*
* Status
*/
output wire locked
);
// bus width assertions
@ -76,79 +77,75 @@ end
parameter TS_NS_WIDTH = TS_WIDTH == 96 ? 30 : 48;
parameter FIFO_ADDR_WIDTH = LOG_FIFO_DEPTH+1;
parameter LOG_AVG = 6;
parameter LOG_AVG_SCALE = LOG_AVG+8;
parameter LOG_AVG_SYNC_RATE = LOG_RATE;
parameter WR_PERIOD = ((((INPUT_PERIOD_NS << 16) + INPUT_PERIOD_FNS) + 64'd0) << 16) / ((OUTPUT_PERIOD_NS << 16) + OUTPUT_PERIOD_FNS) / 2**(LOG_RATE+1);
parameter PHASE_CNT_WIDTH = LOG_RATE;
parameter PHASE_ACC_WIDTH = PHASE_CNT_WIDTH+16;
reg [NS_WIDTH-1:0] period_ns_reg = OUTPUT_PERIOD_NS;
reg [FNS_WIDTH-1:0] period_fns_reg = OUTPUT_PERIOD_FNS;
parameter LOG_SAMPLE_SYNC_RATE = LOG_RATE;
parameter SAMPLE_ACC_WIDTH = LOG_SAMPLE_SYNC_RATE+2;
reg [47:0] ts_s_reg = 0;
reg [TS_NS_WIDTH-1:0] ts_ns_reg = 0;
reg [FNS_WIDTH-1:0] ts_fns_reg = 0;
reg [TS_NS_WIDTH-1:0] ts_ns_inc_reg = 0;
reg [FNS_WIDTH-1:0] ts_fns_inc_reg = 0;
reg [TS_NS_WIDTH+1-1:0] ts_ns_ovf_reg = {TS_NS_WIDTH+1{1'b1}};
reg [FNS_WIDTH-1:0] ts_fns_ovf_reg = {FNS_WIDTH{1'b1}};
parameter DEST_SYNC_LOCK_WIDTH = 7;
parameter PTP_LOCK_WIDTH = 8;
reg ts_step_reg = 1'b0;
reg [NS_WIDTH-1:0] period_ns_reg = 0, period_ns_next;
reg [FNS_WIDTH-1:0] period_fns_reg = 0, period_fns_next;
reg [47:0] src_ts_s_capt_reg = 0;
reg [TS_NS_WIDTH-1:0] src_ts_ns_capt_reg = 0;
reg [FNS_WIDTH-1:0] src_ts_fns_capt_reg = 0;
reg src_ts_step_capt_reg = 0;
reg [47:0] dest_ts_s_capt_reg = 0;
reg [TS_NS_WIDTH-1:0] dest_ts_ns_capt_reg = 0;
reg [FNS_WIDTH-1:0] dest_ts_fns_capt_reg = 0;
reg [47:0] ts_s_sync_reg = 0;
reg [TS_NS_WIDTH-1:0] ts_ns_sync_reg = 0;
reg [FNS_WIDTH-1:0] ts_fns_sync_reg = 0;
reg ts_step_sync_reg = 0;
reg [47:0] ts_s_reg = 0, ts_s_next;
reg [TS_NS_WIDTH-1:0] ts_ns_reg = 0, ts_ns_next;
reg [FNS_WIDTH-1:0] ts_fns_reg = 0, ts_fns_next;
reg [TS_NS_WIDTH-1:0] ts_ns_inc_reg = 0, ts_ns_inc_next;
reg [FNS_WIDTH-1:0] ts_fns_inc_reg = 0, ts_fns_inc_next;
reg [TS_NS_WIDTH+1-1:0] ts_ns_ovf_reg = {TS_NS_WIDTH+1{1'b1}}, ts_ns_ovf_next;
reg [FNS_WIDTH-1:0] ts_fns_ovf_reg = {FNS_WIDTH{1'b1}}, ts_fns_ovf_next;
reg ts_step_reg = 1'b0, ts_step_next;
reg pps_reg = 0;
reg [FIFO_ADDR_WIDTH:0] wr_ptr_reg = {FIFO_ADDR_WIDTH+1{1'b0}}, wr_ptr_next;
reg [FIFO_ADDR_WIDTH:0] wr_ptr_gray_reg = {FIFO_ADDR_WIDTH+1{1'b0}}, wr_ptr_gray_next;
reg [FIFO_ADDR_WIDTH:0] rd_ptr_reg = {FIFO_ADDR_WIDTH+1{1'b0}}, rd_ptr_next;
reg [FIFO_ADDR_WIDTH:0] rd_ptr_gray_reg = {FIFO_ADDR_WIDTH+1{1'b0}}, rd_ptr_gray_next;
reg [PHASE_CNT_WIDTH-1:0] src_phase_reg = {PHASE_CNT_WIDTH{1'b0}};
reg [PHASE_ACC_WIDTH-1:0] dest_phase_reg = {PHASE_ACC_WIDTH{1'b0}}, dest_phase_next;
reg [PHASE_ACC_WIDTH-1:0] dest_phase_inc_reg = {PHASE_ACC_WIDTH{1'b0}}, dest_phase_inc_next;
reg [FIFO_ADDR_WIDTH:0] wr_ptr_gray_sync1_reg = {FIFO_ADDR_WIDTH+1{1'b0}};
reg [FIFO_ADDR_WIDTH:0] wr_ptr_gray_sync2_reg = {FIFO_ADDR_WIDTH+1{1'b0}};
wire [FIFO_ADDR_WIDTH:0] wr_ptr_sync2;
reg [FIFO_ADDR_WIDTH:0] rd_ptr_gray_sync1_reg = {FIFO_ADDR_WIDTH+1{1'b0}};
reg [FIFO_ADDR_WIDTH:0] rd_ptr_gray_sync2_reg = {FIFO_ADDR_WIDTH+1{1'b0}};
wire [FIFO_ADDR_WIDTH:0] rd_ptr_sync2;
reg src_sync_reg = 1'b0;
reg src_update_reg = 1'b0;
reg dest_sync_reg = 1'b0, dest_sync_next = 1'b0;
reg dest_update_reg = 1'b0, dest_update_next = 1'b0;
reg [FIFO_ADDR_WIDTH:0] wr_ptr_gray_sample_sync1_reg = {FIFO_ADDR_WIDTH+1{1'b0}};
reg [FIFO_ADDR_WIDTH:0] wr_ptr_gray_sample_sync2_reg = {FIFO_ADDR_WIDTH+1{1'b0}};
wire [FIFO_ADDR_WIDTH:0] wr_ptr_sample_sync2;
reg [FIFO_ADDR_WIDTH:0] rd_ptr_gray_sample_sync1_reg = {FIFO_ADDR_WIDTH+1{1'b0}};
reg [FIFO_ADDR_WIDTH:0] rd_ptr_gray_sample_sync2_reg = {FIFO_ADDR_WIDTH+1{1'b0}};
wire [FIFO_ADDR_WIDTH:0] rd_ptr_sample_sync2;
reg src_sync_sync1_reg = 1'b0;
reg src_sync_sync2_reg = 1'b0;
reg src_sync_sync3_reg = 1'b0;
reg dest_sync_sync1_reg = 1'b0;
reg dest_sync_sync2_reg = 1'b0;
reg dest_sync_sync3_reg = 1'b0;
reg [15:0] wr_acc_reg = 16'd0, wr_acc_next;
reg [15:0] wr_inc_reg = WR_PERIOD, wr_inc_next;
reg [31:0] err_int_reg = 0, err_int_next;
reg src_sync_sample_sync1_reg = 1'b0;
reg src_sync_sample_sync2_reg = 1'b0;
reg src_sync_sample_sync3_reg = 1'b0;
reg dest_sync_sample_sync1_reg = 1'b0;
reg dest_sync_sample_sync2_reg = 1'b0;
reg dest_sync_sample_sync3_reg = 1'b0;
reg [LOG_RATE-1:0] rd_cnt_reg = {LOG_RATE{1'b0}}, rd_cnt_next;
reg [LOG_FIFO_DEPTH+LOG_AVG_SCALE+2-1:0] sample_acc_reg = 0;
reg [LOG_FIFO_DEPTH+LOG_AVG_SCALE+2-1:0] sample_avg_reg = 0;
reg [LOG_AVG_SYNC_RATE-1:0] sample_cnt_reg = 0;
reg [SAMPLE_ACC_WIDTH-1:0] sample_acc_reg = 0, sample_acc_next = 0;
reg [SAMPLE_ACC_WIDTH-1:0] sample_acc_out_reg = 0;
reg [LOG_SAMPLE_SYNC_RATE-1:0] sample_cnt_reg = 0;
reg sample_update_reg = 1'b0;
reg sample_update_sync1_reg = 1'b0;
reg sample_update_sync2_reg = 1'b0;
reg sample_update_sync3_reg = 1'b0;
reg [TS_WIDTH-1:0] mem[(2**FIFO_ADDR_WIDTH)-1:0];
reg [TS_WIDTH-1:0] mem_read_data_reg = 0;
// full when first TWO MSBs do NOT match, but rest matches
// (gray code equivalent of first MSB different but rest same)
wire full = ((wr_ptr_gray_reg[FIFO_ADDR_WIDTH] != rd_ptr_gray_sync2_reg[FIFO_ADDR_WIDTH]) &&
(wr_ptr_gray_reg[FIFO_ADDR_WIDTH-1] != rd_ptr_gray_sync2_reg[FIFO_ADDR_WIDTH-1]) &&
(wr_ptr_gray_reg[FIFO_ADDR_WIDTH-2:0] == rd_ptr_gray_sync2_reg[FIFO_ADDR_WIDTH-2:0]));
// empty when pointers match exactly
wire empty = rd_ptr_gray_reg == wr_ptr_gray_sync2_reg;
wire [FIFO_ADDR_WIDTH:0] wr_depth = wr_ptr_reg - rd_ptr_sync2;
wire [FIFO_ADDR_WIDTH:0] rd_depth = wr_ptr_sync2 - rd_ptr_reg;
wire [FIFO_ADDR_WIDTH:0] sample_depth = wr_ptr_sample_sync2 - rd_ptr_sample_sync2;
// control signals
reg write;
reg read;
generate
if (TS_WIDTH == 96) begin
@ -167,279 +164,491 @@ assign output_ts_step = ts_step_reg;
assign output_pps = pps_reg;
generate
// source PTP clock capture and sync logic
reg input_ts_step_reg = 1'b0;
genvar n;
always @(posedge input_clk) begin
input_ts_step_reg <= input_ts_step || input_ts_step_reg;
for (n = 0; n < FIFO_ADDR_WIDTH+1; n = n + 1) begin
assign wr_ptr_sync2[n] = ^wr_ptr_gray_sync2_reg[FIFO_ADDR_WIDTH+1-1:n];
assign rd_ptr_sync2[n] = ^rd_ptr_gray_sync2_reg[FIFO_ADDR_WIDTH+1-1:n];
assign wr_ptr_sample_sync2[n] = ^wr_ptr_gray_sample_sync2_reg[FIFO_ADDR_WIDTH+1-1:n];
assign rd_ptr_sample_sync2[n] = ^rd_ptr_gray_sample_sync2_reg[FIFO_ADDR_WIDTH+1-1:n];
{src_update_reg, src_phase_reg} <= src_phase_reg+1;
if (src_update_reg) begin
// capture source TS
if (TS_WIDTH == 96) begin
src_ts_s_capt_reg <= input_ts[95:48];
src_ts_ns_capt_reg <= input_ts[45:16];
end else begin
src_ts_ns_capt_reg <= input_ts[63:16];
end
src_ts_fns_capt_reg <= FNS_WIDTH > 16 ? input_ts[15:0] << (FNS_WIDTH-16) : input_ts[15:0] >> (16-FNS_WIDTH);
src_ts_step_capt_reg <= input_ts_step || input_ts_step_reg;
input_ts_step_reg <= 1'b0;
src_sync_reg <= !src_sync_reg;
end
endgenerate
// pointer sync
always @(posedge input_clk) begin
if (input_rst) begin
rd_ptr_gray_sync1_reg <= {FIFO_ADDR_WIDTH+1{1'b0}};
rd_ptr_gray_sync2_reg <= {FIFO_ADDR_WIDTH+1{1'b0}};
end else begin
rd_ptr_gray_sync1_reg <= rd_ptr_gray_reg;
rd_ptr_gray_sync2_reg <= rd_ptr_gray_sync1_reg;
input_ts_step_reg <= 1'b0;
src_phase_reg <= {PHASE_CNT_WIDTH{1'b0}};
src_sync_reg <= 1'b0;
src_update_reg <= 1'b0;
end
end
// CDC logic
always @(posedge output_clk) begin
if (output_rst) begin
wr_ptr_gray_sync1_reg <= {FIFO_ADDR_WIDTH+1{1'b0}};
wr_ptr_gray_sync2_reg <= {FIFO_ADDR_WIDTH+1{1'b0}};
end else begin
wr_ptr_gray_sync1_reg <= wr_ptr_gray_reg;
wr_ptr_gray_sync2_reg <= wr_ptr_gray_sync1_reg;
end
src_sync_sync1_reg <= src_sync_reg;
src_sync_sync2_reg <= src_sync_sync1_reg;
src_sync_sync3_reg <= src_sync_sync2_reg;
dest_sync_sync1_reg <= dest_sync_reg;
dest_sync_sync2_reg <= dest_sync_sync1_reg;
dest_sync_sync3_reg <= dest_sync_sync2_reg;
end
always @(posedge sample_clk) begin
rd_ptr_gray_sample_sync1_reg <= rd_ptr_gray_reg;
rd_ptr_gray_sample_sync2_reg <= rd_ptr_gray_sample_sync1_reg;
wr_ptr_gray_sample_sync1_reg <= wr_ptr_gray_reg;
wr_ptr_gray_sample_sync2_reg <= wr_ptr_gray_sample_sync1_reg;
src_sync_sample_sync1_reg <= src_sync_reg;
src_sync_sample_sync2_reg <= src_sync_sample_sync1_reg;
src_sync_sample_sync3_reg <= src_sync_sample_sync2_reg;
dest_sync_sample_sync1_reg <= dest_sync_reg;
dest_sync_sample_sync2_reg <= dest_sync_sample_sync1_reg;
dest_sync_sample_sync3_reg <= dest_sync_sample_sync2_reg;
end
reg edge_1_reg = 1'b0;
reg edge_2_reg = 1'b0;
reg [3:0] active_reg = 0;
always @(posedge sample_clk) begin
if (USE_SAMPLE_CLOCK) begin
sample_acc_reg <= sample_acc_reg + ((sample_depth * 2**LOG_AVG_SCALE - sample_acc_reg) >> LOG_AVG);
// phase and frequency detector
if (dest_sync_sample_sync2_reg && !dest_sync_sample_sync3_reg) begin
if (src_sync_sample_sync2_reg && !src_sync_sample_sync3_reg) begin
edge_1_reg <= 1'b0;
edge_2_reg <= 1'b0;
active_reg[0] <= 1'b1;
end else begin
edge_1_reg <= !edge_2_reg;
edge_2_reg <= 1'b0;
end
end else if (src_sync_sample_sync2_reg && !src_sync_sample_sync3_reg) begin
edge_1_reg <= 1'b0;
edge_2_reg <= !edge_1_reg;
active_reg[0] <= 1'b1;
end
// accumulator
sample_acc_reg <= $signed(sample_acc_reg) + $signed({1'b0, edge_2_reg}) - $signed({1'b0, edge_1_reg});
sample_cnt_reg <= sample_cnt_reg + 1;
if (sample_cnt_reg == 0) begin
sample_update_reg <= !sample_update_reg;
sample_avg_reg <= sample_acc_reg;
active_reg <= active_reg << 1;
sample_acc_reg <= $signed({1'b0, edge_2_reg}) - $signed({1'b0, edge_1_reg});
sample_acc_out_reg <= sample_acc_reg;
if (active_reg != 0) begin
sample_update_reg <= !sample_update_reg;
end
end
end
end
always @(posedge input_clk) begin
always @(posedge output_clk) begin
sample_update_sync1_reg <= sample_update_reg;
sample_update_sync2_reg <= sample_update_sync1_reg;
sample_update_sync3_reg <= sample_update_sync2_reg;
end
reg [LOG_FIFO_DEPTH+LOG_AVG_SCALE+2-1:0] sample_avg_sync_reg = 0;
reg sample_avg_sync_valid_reg = 0;
reg [SAMPLE_ACC_WIDTH-1:0] sample_acc_sync_reg = 0;
reg sample_acc_sync_valid_reg = 0;
always @(posedge input_clk) begin
always @(posedge output_clk) begin
if (USE_SAMPLE_CLOCK) begin
sample_avg_sync_valid_reg <= 1'b0;
// latch in synchronized counts from phase detector
sample_acc_sync_valid_reg <= 1'b0;
if (sample_update_sync2_reg ^ sample_update_sync3_reg) begin
sample_avg_sync_reg <= sample_avg_reg;
sample_avg_sync_valid_reg <= 1'b1;
sample_acc_sync_reg <= sample_acc_out_reg;
sample_acc_sync_valid_reg <= 1'b1;
end
end else begin
sample_acc_reg <= sample_acc_reg + ((wr_depth * 2**LOG_AVG_SCALE - sample_acc_reg) >> LOG_AVG);
// phase and frequency detector
if (dest_sync_sync2_reg && !dest_sync_sync3_reg) begin
if (src_sync_sync2_reg && !src_sync_sync3_reg) begin
edge_1_reg <= 1'b0;
edge_2_reg <= 1'b0;
active_reg[0] <= 1'b1;
end else begin
edge_1_reg <= !edge_2_reg;
edge_2_reg <= 1'b0;
end
end else if (src_sync_sync2_reg && !src_sync_sync3_reg) begin
edge_1_reg <= 1'b0;
edge_2_reg <= !edge_1_reg;
active_reg[0] <= 1'b1;
end
// accumulator
sample_acc_reg <= $signed(sample_acc_reg) + $signed({1'b0, edge_2_reg}) - $signed({1'b0, edge_1_reg});
sample_cnt_reg <= sample_cnt_reg + 1;
sample_avg_sync_valid_reg <= 1'b0;
sample_acc_sync_valid_reg <= 1'b0;
if (sample_cnt_reg == 0) begin
sample_avg_sync_reg <= sample_acc_reg;
sample_avg_sync_valid_reg <= 1'b1;
active_reg <= active_reg << 1;
sample_acc_reg <= $signed({1'b0, edge_2_reg}) - $signed({1'b0, edge_1_reg});
sample_acc_sync_reg <= sample_acc_reg;
if (active_reg != 0) begin
sample_update_reg <= !sample_update_reg;
sample_acc_sync_valid_reg <= 1'b1;
end
end
end
end
reg [PHASE_ACC_WIDTH-1:0] dest_err_int_reg = 0, dest_err_int_next = 0;
reg [1:0] dest_ovf;
reg [DEST_SYNC_LOCK_WIDTH-1:0] dest_sync_lock_count_reg = 0, dest_sync_lock_count_next;
reg dest_sync_locked_reg = 1'b0, dest_sync_locked_next;
always @* begin
write = 1'b0;
{dest_update_next, dest_phase_next} = dest_phase_reg + dest_phase_inc_reg;
dest_phase_inc_next = dest_phase_inc_reg;
wr_ptr_next = wr_ptr_reg;
wr_ptr_gray_next = wr_ptr_gray_reg;
dest_err_int_next = dest_err_int_reg;
wr_acc_next = wr_acc_reg + wr_inc_reg;
wr_inc_next = wr_inc_reg;
dest_sync_lock_count_next = dest_sync_lock_count_reg;
dest_sync_locked_next = dest_sync_locked_reg;
err_int_next = err_int_reg;
if (sample_acc_sync_valid_reg) begin
// updated sampled dest_phase error
if (sample_avg_sync_valid_reg) begin
// updated sampled FIFO depth
err_int_next = err_int_reg + (sample_avg_sync_reg - (2**LOG_FIFO_DEPTH * 2**LOG_AVG_SCALE));
wr_inc_next = WR_PERIOD + (((2**LOG_FIFO_DEPTH * 2**LOG_AVG_SCALE) - sample_avg_sync_reg) >> 8) - ($signed(err_int_reg) >> 13);
if ($signed(wr_inc_next) > $signed(WR_PERIOD*4)) begin
wr_inc_next = WR_PERIOD*4;
end else if ($signed(wr_inc_next) < $signed(WR_PERIOD/4)) begin
wr_inc_next = WR_PERIOD/4;
// time integral of error
if (dest_sync_locked_reg) begin
{dest_ovf, dest_err_int_next} = $signed({1'b0, dest_err_int_reg}) + $signed(sample_acc_sync_reg);
end else begin
{dest_ovf, dest_err_int_next} = $signed({1'b0, dest_err_int_reg}) + ($signed(sample_acc_sync_reg) * 2**6);
end
// saturate
if (dest_ovf[1]) begin
// sign bit set indicating underflow across zero; saturate to zero
dest_err_int_next = {PHASE_ACC_WIDTH{1'b0}};
end else if (dest_ovf[0]) begin
// sign bit clear but carry bit set indicating overflow; saturate to all 1
dest_err_int_next = {PHASE_ACC_WIDTH{1'b1}};
end
// compute output
if (dest_sync_locked_reg) begin
{dest_ovf, dest_phase_inc_next} = $signed({1'b0, dest_err_int_reg}) + ($signed(sample_acc_sync_reg) * 2**4);
end else begin
{dest_ovf, dest_phase_inc_next} = $signed({1'b0, dest_err_int_reg}) + ($signed(sample_acc_sync_reg) * 2**10);
end
// saturate
if (dest_ovf[1]) begin
// sign bit set indicating underflow across zero; saturate to zero
dest_phase_inc_next = {PHASE_ACC_WIDTH{1'b0}};
end else if (dest_ovf[0]) begin
// sign bit clear but carry bit set indicating overflow; saturate to all 1
dest_phase_inc_next = {PHASE_ACC_WIDTH{1'b1}};
end
// locked status
if ($signed(sample_acc_sync_reg[SAMPLE_ACC_WIDTH-1:2]) == 0 || $signed(sample_acc_sync_reg[SAMPLE_ACC_WIDTH-1:1]) == -1) begin
if (dest_sync_lock_count_reg == {DEST_SYNC_LOCK_WIDTH{1'b1}}) begin
dest_sync_locked_next = 1'b1;
end else begin
dest_sync_lock_count_next = dest_sync_lock_count_reg + 1;
end
end else begin
dest_sync_lock_count_next = 0;
dest_sync_locked_next = 1'b0;
end
end
if (!full && wr_acc_reg[15] != wr_acc_next[15]) begin
write = 1'b1;
wr_ptr_next = wr_ptr_reg + 1;
wr_ptr_gray_next = wr_ptr_next ^ (wr_ptr_next >> 1);
end
end
always @(posedge input_clk) begin
wr_ptr_reg <= wr_ptr_next;
wr_ptr_gray_reg <= wr_ptr_gray_next;
wr_acc_reg <= wr_acc_next;
wr_inc_reg <= wr_inc_next;
err_int_reg <= err_int_next;
if (write) begin
mem[wr_ptr_reg[FIFO_ADDR_WIDTH-1:0]] <= input_ts;
end
if (input_rst) begin
wr_ptr_reg <= {FIFO_ADDR_WIDTH+1{1'b0}};
wr_ptr_gray_reg <= {FIFO_ADDR_WIDTH+1{1'b0}};
wr_acc_reg <= 16'd0;
wr_inc_reg <= WR_PERIOD;
err_int_reg <= 0;
end
end
always @* begin
read = 1'b0;
rd_ptr_next = rd_ptr_reg;
rd_ptr_gray_next = rd_ptr_gray_reg;
rd_cnt_next = rd_cnt_reg + 1;
if (!empty && rd_cnt_reg == 0) begin
read = 1'b1;
rd_ptr_next = rd_ptr_reg + 1;
rd_ptr_gray_next = rd_ptr_next ^ (rd_ptr_next >> 1);
end
end
reg ts_sync_valid_reg = 1'b0;
reg ts_capt_valid_reg = 1'b0;
always @(posedge output_clk) begin
rd_ptr_reg <= rd_ptr_next;
rd_ptr_gray_reg <= rd_ptr_gray_next;
dest_phase_reg <= dest_phase_next;
dest_phase_inc_reg <= dest_phase_inc_next;
dest_update_reg <= dest_update_next;
rd_cnt_reg <= rd_cnt_next;
if (dest_update_reg) begin
// capture local TS
dest_ts_s_capt_reg <= ts_s_reg;
dest_ts_ns_capt_reg <= ts_ns_reg;
dest_ts_fns_capt_reg <= ts_fns_reg;
if (!empty) begin
mem_read_data_reg <= mem[rd_ptr_reg[FIFO_ADDR_WIDTH-1:0]];
dest_sync_reg <= !dest_sync_reg;
ts_capt_valid_reg <= 1'b1;
end
if (read) begin
ts_sync_valid_reg <= 1'b0;
if (src_sync_sync2_reg ^ src_sync_sync3_reg) begin
// store captured source TS
if (TS_WIDTH == 96) begin
ts_s_sync_reg <= src_ts_s_capt_reg;
end
ts_ns_sync_reg <= src_ts_ns_capt_reg;
ts_fns_sync_reg <= src_ts_fns_capt_reg;
ts_step_sync_reg <= src_ts_step_capt_reg;
ts_sync_valid_reg <= ts_capt_valid_reg;
ts_capt_valid_reg <= 1'b0;
end
dest_err_int_reg <= dest_err_int_next;
dest_sync_lock_count_reg <= dest_sync_lock_count_next;
dest_sync_locked_reg <= dest_sync_locked_next;
if (output_rst) begin
rd_ptr_reg <= {FIFO_ADDR_WIDTH+1{1'b0}};
rd_ptr_gray_reg <= {FIFO_ADDR_WIDTH+1{1'b0}};
rd_cnt_reg <= {LOG_RATE{1'b0}};
dest_phase_reg <= {PHASE_ACC_WIDTH{1'b0}};
dest_phase_inc_reg <= {PHASE_ACC_WIDTH{1'b0}};
dest_sync_reg <= 1'b0;
dest_update_reg <= 1'b0;
dest_err_int_reg <= 0;
dest_sync_lock_count_reg <= 0;
dest_sync_locked_reg <= 1'b0;
ts_sync_valid_reg <= 1'b0;
ts_capt_valid_reg <= 1'b0;
end
end
reg sec_mismatch_reg = 1'b0;
reg diff_valid_reg = 1'b0;
reg diff_offset_valid_reg = 1'b0;
parameter TIME_ERR_INT_WIDTH = NS_WIDTH+FNS_WIDTH+16;
reg [TS_NS_WIDTH+1-1:0] ts_ns_diff_reg = 31'd0;
reg [FNS_WIDTH-1:0] ts_fns_diff_reg = 16'd0;
reg sec_mismatch_reg = 1'b0, sec_mismatch_next;
reg diff_valid_reg = 1'b0, diff_valid_next;
reg diff_corr_valid_reg = 1'b0, diff_corr_valid_next;
reg [48:0] time_err_int_reg = 32'd0;
reg [47:0] ts_s_diff_reg = 0, ts_s_diff_next;
reg [TS_NS_WIDTH+1-1:0] ts_ns_diff_reg = 0, ts_ns_diff_next;
reg [FNS_WIDTH-1:0] ts_fns_diff_reg = 0, ts_fns_diff_next;
always @(posedge output_clk) begin
ts_step_reg <= 0;
reg [16:0] ts_ns_diff_corr_reg = 0, ts_ns_diff_corr_next;
reg [FNS_WIDTH-1:0] ts_fns_diff_corr_reg = 0, ts_fns_diff_corr_next;
diff_valid_reg <= 1'b0;
diff_offset_valid_reg <= 1'b0;
reg [TIME_ERR_INT_WIDTH-1:0] time_err_int_reg = 0, time_err_int_next;
// 96 bit timestamp
reg [1:0] ptp_ovf;
reg [PTP_LOCK_WIDTH-1:0] ptp_lock_count_reg = 0, ptp_lock_count_next;
reg ptp_locked_reg = 1'b0, ptp_locked_next;
assign locked = ptp_locked_reg && dest_sync_locked_reg;
always @* begin
period_ns_next = period_ns_reg;
period_fns_next = period_fns_reg;
ts_s_next = ts_s_reg;
ts_ns_next = ts_ns_reg;
ts_fns_next = ts_fns_reg;
ts_ns_inc_next = ts_ns_inc_reg;
ts_fns_inc_next = ts_fns_inc_reg;
ts_ns_ovf_next = ts_ns_ovf_reg;
ts_fns_ovf_next = ts_fns_ovf_reg;
ts_step_next = 0;
diff_valid_next = 1'b0;
diff_corr_valid_next = 1'b0;
sec_mismatch_next = sec_mismatch_reg;
diff_valid_next = 1'b0;
diff_corr_valid_next = 1'b0;
ts_s_diff_next = ts_s_diff_reg;
ts_ns_diff_next = ts_ns_diff_reg;
ts_fns_diff_next = ts_fns_diff_reg;
ts_ns_diff_corr_next = ts_ns_diff_corr_reg;
ts_fns_diff_corr_next = ts_fns_diff_corr_reg;
time_err_int_next = time_err_int_reg;
// PTP clock
if (TS_WIDTH == 96) begin
// 96 bit timestamp
if (!ts_ns_ovf_reg[30]) begin
// if the overflow lookahead did not borrow, one second has elapsed
// increment seconds field, pre-compute both normal increment and overflow values
{ts_ns_inc_reg, ts_fns_inc_reg} <= {ts_ns_ovf_reg, ts_fns_ovf_reg} + {period_ns_reg, period_fns_reg};
{ts_ns_ovf_reg, ts_fns_ovf_reg} <= {ts_ns_ovf_reg, ts_fns_ovf_reg} + {period_ns_reg, period_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}};
{ts_ns_reg, ts_fns_reg} <= {ts_ns_ovf_reg, ts_fns_ovf_reg};
ts_s_reg <= ts_s_reg + 1;
{ts_ns_inc_next, ts_fns_inc_next} = {ts_ns_ovf_reg, ts_fns_ovf_reg} + {period_ns_reg, period_fns_reg};
{ts_ns_ovf_next, ts_fns_ovf_next} = {ts_ns_ovf_reg, ts_fns_ovf_reg} + {period_ns_reg, period_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}};
{ts_ns_next, ts_fns_next} = {ts_ns_ovf_reg, ts_fns_ovf_reg};
ts_s_next = ts_s_next + 1;
end else begin
// no increment seconds field, pre-compute both normal increment and overflow values
{ts_ns_inc_reg, ts_fns_inc_reg} <= {ts_ns_inc_reg, ts_fns_inc_reg} + {period_ns_reg, period_fns_reg};
{ts_ns_ovf_reg, ts_fns_ovf_reg} <= {ts_ns_inc_reg, ts_fns_inc_reg} + {period_ns_reg, period_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}};
{ts_ns_reg, ts_fns_reg} <= {ts_ns_inc_reg, ts_fns_inc_reg};
ts_s_reg <= ts_s_reg;
{ts_ns_inc_next, ts_fns_inc_next} = {ts_ns_inc_reg, ts_fns_inc_reg} + {period_ns_reg, period_fns_reg};
{ts_ns_ovf_next, ts_fns_ovf_next} = {ts_ns_inc_reg, ts_fns_inc_reg} + {period_ns_reg, period_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}};
{ts_ns_next, ts_fns_next} = {ts_ns_inc_reg, ts_fns_inc_reg};
ts_s_next = ts_s_next;
end
end else if (TS_WIDTH == 64) begin
{ts_ns_reg, ts_fns_reg} <= {ts_ns_reg, ts_fns_reg} + {period_ns_reg, period_fns_reg};
// 64 bit timestamp
{ts_ns_next, ts_fns_next} = {ts_ns_reg, ts_fns_reg} + {period_ns_reg, period_fns_reg};
end
// FIFO dequeue
if (read) begin
// dequeue from FIFO
if (ts_sync_valid_reg) begin
// Read new value
if (TS_WIDTH == 96) begin
if (mem_read_data_reg[95:48] != ts_s_reg) begin
// seconds field doesn't match
if (!sec_mismatch_reg) begin
// ignore the first time
sec_mismatch_reg <= 1'b1;
end else begin
// two seconds mismatches in a row; step the clock
sec_mismatch_reg <= 1'b0;
if (ts_step_sync_reg || sec_mismatch_reg) begin
// input stepped
sec_mismatch_next = 1'b0;
{ts_ns_inc_reg, ts_fns_inc_reg} <= (FNS_WIDTH > 16 ? mem_read_data_reg[45:0] << (FNS_WIDTH-16) : mem_read_data_reg[45:0] >> (16-FNS_WIDTH)) + {period_ns_reg, period_fns_reg};
{ts_ns_ovf_reg, ts_fns_ovf_reg} <= (FNS_WIDTH > 16 ? mem_read_data_reg[45:0] << (FNS_WIDTH-16) : mem_read_data_reg[45:0] >> (16-FNS_WIDTH)) + {period_ns_reg, period_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}};
ts_s_reg <= mem_read_data_reg[95:48];
ts_ns_reg <= mem_read_data_reg[45:16];
ts_fns_reg <= FNS_WIDTH > 16 ? mem_read_data_reg[15:0] << (FNS_WIDTH-16) : mem_read_data_reg[15:0] >> (16-FNS_WIDTH);
ts_step_reg <= 1;
end
{ts_ns_inc_next, ts_fns_inc_next} = {ts_ns_sync_reg, ts_fns_sync_reg} + {period_ns_reg, period_fns_reg};
{ts_ns_ovf_next, ts_fns_ovf_next} = {ts_ns_sync_reg, ts_fns_sync_reg} + {period_ns_reg, period_fns_reg} - {31'd1_000_000_000, {FNS_WIDTH{1'b0}}};
ts_s_next = ts_s_sync_reg;
ts_ns_next = ts_ns_sync_reg;
ts_fns_next = ts_fns_sync_reg;
ts_step_next = 1;
end else begin
// compute difference
sec_mismatch_reg <= 1'b0;
diff_valid_reg <= 1'b1;
{ts_ns_diff_reg, ts_fns_diff_reg} <= {ts_ns_reg, ts_fns_reg} - (FNS_WIDTH > 16 ? mem_read_data_reg[45:0] << (FNS_WIDTH-16) : mem_read_data_reg[45:0] >> (16-FNS_WIDTH));
sec_mismatch_next = 1'b0;
diff_valid_next = 1'b1;
ts_s_diff_next = ts_s_sync_reg - dest_ts_s_capt_reg;
{ts_ns_diff_next, ts_fns_diff_next} = {ts_ns_sync_reg, ts_fns_sync_reg} - {dest_ts_ns_capt_reg, dest_ts_fns_capt_reg};
end
end else if (TS_WIDTH == 64) begin
if (mem_read_data_reg[63:48] != ts_ns_reg[47:32]) begin
// high-order bits don't match
if (!sec_mismatch_reg) begin
// ignore the first time
sec_mismatch_reg <= 1'b1;
end else begin
// two seconds mismatches in a row; step the clock
sec_mismatch_reg <= 1'b0;
if (ts_step_sync_reg || sec_mismatch_reg) begin
// input stepped
sec_mismatch_next = 1'b0;
ts_ns_reg <= mem_read_data_reg[63:16];
ts_fns_reg <= FNS_WIDTH > 16 ? mem_read_data_reg[15:0] << (FNS_WIDTH-16) : mem_read_data_reg[15:0] >> (16-FNS_WIDTH);
ts_step_reg <= 1;
end
ts_ns_next = ts_ns_sync_reg;
ts_fns_next = ts_fns_sync_reg;
ts_step_next = 1;
end else begin
// compute difference
sec_mismatch_reg <= 1'b0;
diff_valid_reg <= 1'b1;
{ts_ns_diff_reg, ts_fns_diff_reg} <= {ts_ns_reg, ts_fns_reg} - (FNS_WIDTH > 16 ? mem_read_data_reg[63:0] << (FNS_WIDTH-16) : mem_read_data_reg[63:0] >> (16-FNS_WIDTH));
sec_mismatch_next = 1'b0;
diff_valid_next = 1'b1;
{ts_ns_diff_next, ts_fns_diff_next} = {ts_ns_sync_reg, ts_fns_sync_reg} - {dest_ts_ns_capt_reg, dest_ts_fns_capt_reg};
end
end
end else if (diff_valid_reg) begin
// offset difference by FIFO delay
diff_offset_valid_reg <= 1'b1;
diff_valid_reg <= 1'b0;
{ts_ns_diff_reg, ts_fns_diff_reg} <= {ts_ns_diff_reg, ts_fns_diff_reg} - ({period_ns_reg, period_fns_reg} * 2**(LOG_RATE + LOG_FIFO_DEPTH));
end else if (diff_offset_valid_reg) begin
// PI control
diff_offset_valid_reg <= 1'b0;
if (($signed({ts_ns_diff_reg, ts_fns_diff_reg}) / 2**10) + ($signed(time_err_int_reg) / 2**16) > 4*2**16) begin
// limit positive adjustment
time_err_int_reg <= 0;
{period_ns_reg, period_fns_reg} <= $signed(OUTPUT_PERIOD_NS*2**16 + OUTPUT_PERIOD_FNS) - ({4'd4, {FNS_WIDTH{1'b0}}});
end else if (($signed({ts_ns_diff_reg, ts_fns_diff_reg}) / 2**10) + ($signed(time_err_int_reg) / 2**16) < -8*2**16) begin
// limit negative adjustment
time_err_int_reg <= 0;
{period_ns_reg, period_fns_reg} <= $signed(OUTPUT_PERIOD_NS*2**16 + OUTPUT_PERIOD_FNS) + ({4'd8, {FNS_WIDTH{1'b0}}});
end else begin
time_err_int_reg <= $signed(time_err_int_reg) + $signed({ts_ns_diff_reg, ts_fns_diff_reg});
{period_ns_reg, period_fns_reg} <= $signed(OUTPUT_PERIOD_NS*2**16 + OUTPUT_PERIOD_FNS) - ($signed({ts_ns_diff_reg, ts_fns_diff_reg}) / 2**10) - ($signed(time_err_int_reg) / 2**16);
end
end
if (diff_valid_reg) begin
// seconds field correction
if (TS_WIDTH == 96) begin
if ($signed(ts_s_diff_reg) == 0 && ($signed(ts_ns_diff_reg[30:16]) == 0 || $signed(ts_ns_diff_reg[30:16]) == -1)) begin
// difference is small and no seconds difference; slew
ts_ns_diff_corr_next = ts_ns_diff_reg[16:0];
ts_fns_diff_corr_next = ts_fns_diff_reg;
diff_corr_valid_next = 1'b1;
end else if ($signed(ts_s_diff_reg) == 1 && ts_ns_diff_reg[30:16] == 15'h4465) begin
// difference is small with 1 second difference; adjust and slew
ts_ns_diff_corr_next = ts_ns_diff_reg[16:0] + 17'h0ca00;
ts_fns_diff_corr_next = ts_fns_diff_reg;
diff_corr_valid_next = 1'b1;
end else if ($signed(ts_s_diff_reg) == -1 && ts_ns_diff_reg[30:16] == 15'h3b9a) begin
// difference is small with 1 second difference; adjust and slew
ts_ns_diff_corr_next = ts_ns_diff_reg[16:0] - 17'h0ca00;
ts_fns_diff_corr_next = ts_fns_diff_reg;
diff_corr_valid_next = 1'b1;
end else begin
// difference is too large; step the clock
sec_mismatch_next = 1'b1;
end
end else if (TS_WIDTH == 64) begin
if ($signed(ts_ns_diff_reg[47:16]) == 0 || $signed(ts_ns_diff_reg[47:16]) == -1) begin
// difference is small enough to slew
ts_ns_diff_corr_next = ts_ns_diff_reg[16:0];
ts_fns_diff_corr_next = ts_fns_diff_reg;
diff_corr_valid_next = 1'b1;
end else begin
// difference is too large; step the clock
sec_mismatch_next = 1'b1;
end
end
end
if (diff_corr_valid_reg) begin
// PI control
// time integral of error
if (ptp_locked_reg) begin
{ptp_ovf, time_err_int_next} = $signed({1'b0, time_err_int_reg}) + $signed({ts_ns_diff_corr_reg, ts_fns_diff_corr_reg});
end else begin
{ptp_ovf, time_err_int_next} = $signed({1'b0, time_err_int_reg}) + ($signed({ts_ns_diff_corr_reg, ts_fns_diff_corr_reg}) * 2**3);
end
// saturate
if (ptp_ovf[1]) begin
// sign bit set indicating underflow across zero; saturate to zero
time_err_int_next = {TIME_ERR_INT_WIDTH{1'b0}};
end else if (ptp_ovf[0]) begin
// sign bit clear but carry bit set indicating overflow; saturate to all 1
time_err_int_next = {TIME_ERR_INT_WIDTH{1'b1}};
end
// compute output
if (ptp_locked_reg) begin
{ptp_ovf, period_ns_next, period_fns_next} = ($signed({1'b0, time_err_int_reg}) / 2**16) + ($signed({ts_ns_diff_corr_reg, ts_fns_diff_corr_reg}) / 2**10);
end else begin
{ptp_ovf, period_ns_next, period_fns_next} = ($signed({1'b0, time_err_int_reg}) / 2**16) + ($signed({ts_ns_diff_corr_reg, ts_fns_diff_corr_reg}) / 2**7);
end
// saturate
if (ptp_ovf[1]) begin
// sign bit set indicating underflow across zero; saturate to zero
{period_ns_next, period_fns_next} = {NS_WIDTH+FNS_WIDTH{1'b0}};
end else if (ptp_ovf[0]) begin
// sign bit clear but carry bit set indicating overflow; saturate to all 1
{period_ns_next, period_fns_next} = {NS_WIDTH+FNS_WIDTH{1'b1}};
end
// locked status
if ($signed(ts_ns_diff_corr_reg[17-1:4]) == 0 || $signed(ts_ns_diff_corr_reg[17-1:4]) == -1) begin
if (ptp_lock_count_reg == {PTP_LOCK_WIDTH{1'b1}}) begin
ptp_locked_next = 1'b1;
end else begin
ptp_lock_count_next = ptp_lock_count_reg + 1;
end
end else begin
ptp_lock_count_next = 0;
ptp_locked_next = 1'b0;
end
end
end
always @(posedge output_clk) begin
period_ns_reg <= period_ns_next;
period_fns_reg <= period_fns_next;
ts_s_reg <= ts_s_next;
ts_ns_reg <= ts_ns_next;
ts_fns_reg <= ts_fns_next;
ts_ns_inc_reg <= ts_ns_inc_next;
ts_fns_inc_reg <= ts_fns_inc_next;
ts_ns_ovf_reg <= ts_ns_ovf_next;
ts_fns_ovf_reg <= ts_fns_ovf_next;
ts_step_reg <= ts_step_next;
sec_mismatch_reg <= sec_mismatch_next;
diff_valid_reg <= diff_valid_next;
diff_corr_valid_reg <= diff_corr_valid_next;
ts_s_diff_reg <= ts_s_diff_next;
ts_ns_diff_reg <= ts_ns_diff_next;
ts_fns_diff_reg <= ts_fns_diff_next;
ts_ns_diff_corr_reg <= ts_ns_diff_corr_next;
ts_fns_diff_corr_reg <= ts_fns_diff_corr_next;
time_err_int_reg <= time_err_int_next;
ptp_lock_count_reg <= ptp_lock_count_next;
ptp_locked_reg <= ptp_locked_next;
// PPS output
if (TS_WIDTH == 96) begin
pps_reg <= !ts_ns_ovf_reg[30];
end else if (TS_WIDTH == 64) begin
@ -447,8 +656,8 @@ always @(posedge output_clk) begin
end
if (output_rst) begin
period_ns_reg <= OUTPUT_PERIOD_NS;
period_fns_reg <= OUTPUT_PERIOD_FNS;
period_ns_reg <= 0;
period_fns_reg <= 0;
ts_s_reg <= 0;
ts_ns_reg <= 0;
ts_fns_reg <= 0;
@ -458,6 +667,13 @@ always @(posedge output_clk) begin
ts_fns_ovf_reg <= {FNS_WIDTH{1'b1}};
ts_step_reg <= 0;
pps_reg <= 0;
diff_valid_reg <= 1'b0;
diff_corr_valid_reg <= 1'b0;
time_err_int_reg <= 0;
ptp_lock_count_reg <= 0;
ptp_locked_reg <= 1'b0;
end
end

View File

@ -1,4 +1,4 @@
# Copyright (c) 2019 Alex Forencich
# Copyright (c) 2019-2021 Alex Forencich
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@ -24,50 +24,52 @@ foreach inst [get_cells -hier -filter {(ORIG_REF_NAME == ptp_clock_cdc || REF_NA
puts "Inserting timing constraints for ptp_clock_cdc instance $inst"
# get clock periods
set input_clk [get_clocks -of_objects [get_pins $inst/wr_ptr_reg_reg[0]/C]]
set output_clk [get_clocks -of_objects [get_pins $inst/rd_ptr_reg_reg[0]/C]]
set input_clk [get_clocks -of_objects [get_pins "$inst/src_sync_reg_reg/C"]]
set output_clk [get_clocks -of_objects [get_pins "$inst/dest_sync_reg_reg/C"]]
set input_clk_period [get_property -min PERIOD $input_clk]
set output_clk_period [get_property -min PERIOD $output_clk]
# pointer synchronization
set_property ASYNC_REG TRUE [get_cells -hier -regexp ".*/(wr|rd)_ptr_gray_sync\[12\]_reg_reg\\\[\\d+\\\]" -filter "PARENT == $inst"]
# timestamp synchronization
set_property ASYNC_REG TRUE [get_cells -hier -regexp ".*/ts_(s|ns|fns|step)_sync_reg_reg(\\\[\\d+\\\])?" -filter "PARENT == $inst"]
set_max_delay -from [get_cells "$inst/rd_ptr_reg_reg[*] $inst/rd_ptr_gray_reg_reg[*]"] -to [get_cells $inst/rd_ptr_gray_sync1_reg_reg[*]] -datapath_only $output_clk_period
set_bus_skew -from [get_cells "$inst/rd_ptr_reg_reg[*] $inst/rd_ptr_gray_reg_reg[*]"] -to [get_cells $inst/rd_ptr_gray_sync1_reg_reg[*]] $input_clk_period
set_max_delay -from [get_cells "$inst/wr_ptr_reg_reg[*] $inst/wr_ptr_gray_reg_reg[*]"] -to [get_cells $inst/wr_ptr_gray_sync1_reg_reg[*]] -datapath_only $input_clk_period
set_bus_skew -from [get_cells "$inst/wr_ptr_reg_reg[*] $inst/wr_ptr_gray_reg_reg[*]"] -to [get_cells $inst/wr_ptr_gray_sync1_reg_reg[*]] $output_clk_period
# output register (needed for distributed RAM sync write/async read)
set output_reg_ffs [get_cells -quiet "$inst/mem_read_data_reg_reg[*]"]
if {[llength $output_reg_ffs]} {
set_false_path -from $input_clk -to $output_reg_ffs
if {[llength [get_cells "$inst/src_ts_s_capt_reg_reg[*]"]]} {
set_max_delay -from [get_cells "$inst/src_ts_s_capt_reg_reg[*]"] -to [get_cells "$inst/ts_s_sync_reg_reg[*]"] -datapath_only $output_clk_period
set_bus_skew -from [get_cells "$inst/src_ts_s_capt_reg_reg[*]"] -to [get_cells "$inst/ts_s_sync_reg_reg[*]"] $input_clk_period
}
# pointer synchronization (depth measurement)
set sync_ffs [get_cells -quiet -hier -regexp ".*/rd_ptr_gray_sample_sync\[12\]_reg_reg\\\[\\d+\\\]" -filter "PARENT == $inst"]
set_max_delay -from [get_cells "$inst/src_ts_ns_capt_reg_reg[*]"] -to [get_cells "$inst/ts_ns_sync_reg_reg[*]"] -datapath_only $output_clk_period
set_bus_skew -from [get_cells "$inst/src_ts_ns_capt_reg_reg[*]"] -to [get_cells "$inst/ts_ns_sync_reg_reg[*]"] $input_clk_period
set_max_delay -from [get_cells "$inst/src_ts_fns_capt_reg_reg[*]"] -to [get_cells "$inst/ts_fns_sync_reg_reg[*]"] -datapath_only $output_clk_period
set_bus_skew -from [get_cells "$inst/src_ts_fns_capt_reg_reg[*]"] -to [get_cells "$inst/ts_fns_sync_reg_reg[*]"] $input_clk_period
if {[llength [get_cells "$inst/src_ts_step_capt_reg_reg"]]} {
set_max_delay -from [get_cells "$inst/src_ts_step_capt_reg_reg"] -to [get_cells "$inst/ts_step_sync_reg_reg"] -datapath_only $output_clk_period
set_bus_skew -from [get_cells "$inst/src_ts_step_capt_reg_reg"] -to [get_cells "$inst/ts_step_sync_reg_reg"] $input_clk_period
}
# sample clock
set sync_ffs [get_cells -quiet -hier -regexp ".*/src_sample_sync\[12\]_reg_reg" -filter "PARENT == $inst"]
if {[llength $sync_ffs]} {
set_property ASYNC_REG TRUE $sync_ffs
set src_clk [get_clocks -of_objects [get_pins $inst/rd_ptr_gray_reg_reg[0]/C]]
set dest_clk [get_clocks -of_objects [get_pins $inst/rd_ptr_gray_sample_sync1_reg_reg[0]/C]]
set src_clk [get_clocks -of_objects [get_pins "$inst/src_sync_reg_reg/C"]]
set dest_clk [get_clocks -of_objects [get_pins "$inst/src_sample_sync1_reg_reg/C"]]
set_max_delay -from [get_cells "$inst/rd_ptr_reg_reg[*] $inst/rd_ptr_gray_reg_reg[*]"] -to [get_cells $inst/rd_ptr_gray_sample_sync1_reg_reg[*]] -datapath_only [get_property -min PERIOD $src_clk]
set_bus_skew -from [get_cells "$inst/rd_ptr_reg_reg[*] $inst/rd_ptr_gray_reg_reg[*]"] -to [get_cells $inst/rd_ptr_gray_sample_sync1_reg_reg[*]] [get_property -min PERIOD $dest_clk]
set_max_delay -from [get_cells "$inst/src_sync_reg_reg"] -to [get_cells "$inst/src_sample_sync1_reg_reg"] -datapath_only [get_property -min PERIOD $src_clk]
}
set sync_ffs [get_cells -quiet -hier -regexp ".*/wr_ptr_gray_sample_sync\[12\]_reg_reg\\\[\\d+\\\]" -filter "PARENT == $inst"]
set sync_ffs [get_cells -quiet -hier -regexp ".*/dest_sample_sync\[12\]_reg_reg" -filter "PARENT == $inst"]
if {[llength $sync_ffs]} {
set_property ASYNC_REG TRUE $sync_ffs
set src_clk [get_clocks -of_objects [get_pins $inst/wr_ptr_gray_reg_reg[0]/C]]
set dest_clk [get_clocks -of_objects [get_pins $inst/wr_ptr_gray_sample_sync1_reg_reg[0]/C]]
set src_clk [get_clocks -of_objects [get_pins "$inst/dest_sync_reg_reg/C"]]
set dest_clk [get_clocks -of_objects [get_pins "$inst/dest_sample_sync1_reg_reg/C"]]
set_max_delay -from [get_cells "$inst/wr_ptr_reg_reg[*] $inst/wr_ptr_gray_reg_reg[*]"] -to [get_cells $inst/wr_ptr_gray_sample_sync1_reg_reg[*]] -datapath_only [get_property -min PERIOD $src_clk]
set_bus_skew -from [get_cells "$inst/wr_ptr_reg_reg[*] $inst/wr_ptr_gray_reg_reg[*]"] -to [get_cells $inst/wr_ptr_gray_sample_sync1_reg_reg[*]] [get_property -min PERIOD $dest_clk]
set_max_delay -from [get_cells "$inst/dest_sync_reg_reg"] -to [get_cells "$inst/dest_sample_sync1_reg_reg"] -datapath_only [get_property -min PERIOD $src_clk]
}
# sample update sync
@ -76,12 +78,24 @@ foreach inst [get_cells -hier -filter {(ORIG_REF_NAME == ptp_clock_cdc || REF_NA
if {[llength $sync_ffs]} {
set_property ASYNC_REG TRUE $sync_ffs
set src_clk [get_clocks -of_objects [get_pins $inst/sample_update_reg_reg/C]]
set dest_clk [get_clocks -of_objects [get_pins $inst/sample_update_sync1_reg_reg/C]]
set src_clk [get_clocks -of_objects [get_pins "$inst/sample_update_reg_reg/C"]]
set dest_clk [get_clocks -of_objects [get_pins "$inst/sample_update_sync1_reg_reg/C"]]
set_max_delay -from [get_cells $inst/sample_update_reg_reg] -to [get_cells $inst/sample_update_sync1_reg_reg] -datapath_only [get_property -min PERIOD $src_clk]
set_max_delay -from [get_cells "$inst/sample_update_reg_reg"] -to [get_cells "$inst/sample_update_sync1_reg_reg"] -datapath_only [get_property -min PERIOD $src_clk]
set_max_delay -from [get_cells "$inst/sample_avg_reg_reg[*]"] -to [get_cells $inst/sample_avg_sync_reg_reg[*]] -datapath_only [get_property -min PERIOD $src_clk]
set_bus_skew -from [get_cells "$inst/sample_avg_reg_reg[*]"] -to [get_cells $inst/sample_avg_sync_reg_reg[*]] [get_property -min PERIOD $dest_clk]
set_max_delay -from [get_cells "$inst/sample_acc_out_reg_reg[*]"] -to [get_cells $inst/sample_acc_sync_reg_reg[*]] -datapath_only [get_property -min PERIOD $src_clk]
set_bus_skew -from [get_cells "$inst/sample_acc_out_reg_reg[*]"] -to [get_cells $inst/sample_acc_sync_reg_reg[*]] [get_property -min PERIOD $dest_clk]
}
# no sample clock
set sync_ffs [get_cells -quiet -hier -regexp ".*/src_sync_sync\[12\]_reg_reg" -filter "PARENT == $inst"]
if {[llength $sync_ffs]} {
set_property ASYNC_REG TRUE $sync_ffs
set src_clk [get_clocks -of_objects [get_pins "$inst/src_sync_reg_reg/C"]]
set dest_clk [get_clocks -of_objects [get_pins "$inst/src_sync_sync1_reg_reg/C"]]
set_max_delay -from [get_cells "$inst/src_sync_reg_reg"] -to [get_cells "$inst/src_sync_sync1_reg_reg"] -datapath_only [get_property -min PERIOD $src_clk]
}
}

View File

@ -59,8 +59,6 @@ export PARAM_RX_FIFO_PIPELINE_OUTPUT ?= 2
export PARAM_RX_FRAME_FIFO ?= 1
export PARAM_RX_DROP_BAD_FRAME ?= $(PARAM_RX_FRAME_FIFO)
export PARAM_RX_DROP_WHEN_FULL ?= $(PARAM_RX_FRAME_FIFO)
export PARAM_LOGIC_PTP_PERIOD_NS ?= 6
export PARAM_LOGIC_PTP_PERIOD_FNS ?= 26214
export PARAM_PTP_PERIOD_NS ?= 6
export PARAM_PTP_PERIOD_FNS ?= 26214
export PARAM_PTP_USE_SAMPLE_CLOCK ?= 0
@ -93,8 +91,6 @@ ifeq ($(SIM), icarus)
COMPILE_ARGS += -P $(TOPLEVEL).RX_FRAME_FIFO=$(PARAM_RX_FRAME_FIFO)
COMPILE_ARGS += -P $(TOPLEVEL).RX_DROP_BAD_FRAME=$(PARAM_RX_DROP_BAD_FRAME)
COMPILE_ARGS += -P $(TOPLEVEL).RX_DROP_WHEN_FULL=$(PARAM_RX_DROP_WHEN_FULL)
COMPILE_ARGS += -P $(TOPLEVEL).LOGIC_PTP_PERIOD_NS=$(PARAM_LOGIC_PTP_PERIOD_NS)
COMPILE_ARGS += -P $(TOPLEVEL).LOGIC_PTP_PERIOD_FNS=$(PARAM_LOGIC_PTP_PERIOD_FNS)
COMPILE_ARGS += -P $(TOPLEVEL).PTP_PERIOD_NS=$(PARAM_PTP_PERIOD_NS)
COMPILE_ARGS += -P $(TOPLEVEL).PTP_PERIOD_FNS=$(PARAM_PTP_PERIOD_FNS)
COMPILE_ARGS += -P $(TOPLEVEL).PTP_USE_SAMPLE_CLOCK=$(PARAM_PTP_USE_SAMPLE_CLOCK)
@ -131,8 +127,6 @@ else ifeq ($(SIM), verilator)
COMPILE_ARGS += -GRX_FRAME_FIFO=$(PARAM_RX_FRAME_FIFO)
COMPILE_ARGS += -GRX_DROP_BAD_FRAME=$(PARAM_RX_DROP_BAD_FRAME)
COMPILE_ARGS += -GRX_DROP_WHEN_FULL=$(PARAM_RX_DROP_WHEN_FULL)
COMPILE_ARGS += -GLOGIC_PTP_PERIOD_NS=$(PARAM_LOGIC_PTP_PERIOD_NS)
COMPILE_ARGS += -GLOGIC_PTP_PERIOD_FNS=$(PARAM_LOGIC_PTP_PERIOD_FNS)
COMPILE_ARGS += -GPTP_PERIOD_NS=$(PARAM_PTP_PERIOD_NS)
COMPILE_ARGS += -GPTP_PERIOD_FNS=$(PARAM_PTP_PERIOD_FNS)
COMPILE_ARGS += -GPTP_USE_SAMPLE_CLOCK=$(PARAM_PTP_USE_SAMPLE_CLOCK)

View File

@ -63,6 +63,7 @@ class TB:
dut.ptp_sample_clk.setimmediatevalue(0)
dut.ptp_ts_96.setimmediatevalue(0)
dut.ptp_ts_step.setimmediatevalue(0)
async def reset(self):
self.dut.logic_rst.setimmediatevalue(0)
@ -281,8 +282,6 @@ def test_eth_mac_10g_fifo(request, data_width, enable_dic):
parameters['RX_FRAME_FIFO'] = 1
parameters['RX_DROP_BAD_FRAME'] = parameters['RX_FRAME_FIFO']
parameters['RX_DROP_WHEN_FULL'] = parameters['RX_FRAME_FIFO']
parameters['LOGIC_PTP_PERIOD_NS'] = 0x6 if parameters['AXIS_DATA_WIDTH'] == 64 else 0x3
parameters['LOGIC_PTP_PERIOD_FNS'] = 0x6666 if parameters['AXIS_DATA_WIDTH'] == 64 else 0x3333
parameters['PTP_PERIOD_NS'] = 0x6 if parameters['DATA_WIDTH'] == 64 else 0x3
parameters['PTP_PERIOD_FNS'] = 0x6666 if parameters['DATA_WIDTH'] == 64 else 0x3333
parameters['PTP_USE_SAMPLE_CLOCK'] = 0

View File

@ -35,12 +35,7 @@ VERILOG_SOURCES += ../../rtl/$(DUT).v
export PARAM_TS_WIDTH ?= 96
export PARAM_NS_WIDTH ?= 4
export PARAM_FNS_WIDTH ?= 16
export PARAM_INPUT_PERIOD_NS ?= 6
export PARAM_INPUT_PERIOD_FNS ?= 26214
export PARAM_OUTPUT_PERIOD_NS ?= 6
export PARAM_OUTPUT_PERIOD_FNS ?= 26214
export PARAM_USE_SAMPLE_CLOCK ?= 1
export PARAM_LOG_FIFO_DEPTH ?= 3
export PARAM_LOG_RATE ?= 3
ifeq ($(SIM), icarus)
@ -49,12 +44,7 @@ ifeq ($(SIM), icarus)
COMPILE_ARGS += -P $(TOPLEVEL).TS_WIDTH=$(PARAM_TS_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).NS_WIDTH=$(PARAM_NS_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).FNS_WIDTH=$(PARAM_FNS_WIDTH)
COMPILE_ARGS += -P $(TOPLEVEL).INPUT_PERIOD_NS=$(PARAM_INPUT_PERIOD_NS)
COMPILE_ARGS += -P $(TOPLEVEL).INPUT_PERIOD_FNS=$(PARAM_INPUT_PERIOD_FNS)
COMPILE_ARGS += -P $(TOPLEVEL).OUTPUT_PERIOD_NS=$(PARAM_OUTPUT_PERIOD_NS)
COMPILE_ARGS += -P $(TOPLEVEL).OUTPUT_PERIOD_FNS=$(PARAM_OUTPUT_PERIOD_FNS)
COMPILE_ARGS += -P $(TOPLEVEL).USE_SAMPLE_CLOCK=$(PARAM_USE_SAMPLE_CLOCK)
COMPILE_ARGS += -P $(TOPLEVEL).LOG_FIFO_DEPTH=$(PARAM_LOG_FIFO_DEPTH)
COMPILE_ARGS += -P $(TOPLEVEL).LOG_RATE=$(PARAM_LOG_RATE)
ifeq ($(WAVES), 1)
@ -67,12 +57,7 @@ else ifeq ($(SIM), verilator)
COMPILE_ARGS += -GTS_WIDTH=$(PARAM_TS_WIDTH)
COMPILE_ARGS += -GNS_WIDTH=$(PARAM_NS_WIDTH)
COMPILE_ARGS += -GFNS_WIDTH=$(PARAM_FNS_WIDTH)
COMPILE_ARGS += -GINPUT_PERIOD_NS=$(PARAM_INPUT_PERIOD_NS)
COMPILE_ARGS += -GINPUT_PERIOD_FNS=$(PARAM_INPUT_PERIOD_FNS)
COMPILE_ARGS += -GOUTPUT_PERIOD_NS=$(PARAM_OUTPUT_PERIOD_NS)
COMPILE_ARGS += -GOUTPUT_PERIOD_FNS=$(PARAM_OUTPUT_PERIOD_FNS)
COMPILE_ARGS += -GUSE_SAMPLE_CLOCK=$(PARAM_USE_SAMPLE_CLOCK)
COMPILE_ARGS += -GLOG_FIFO_DEPTH=$(PARAM_LOG_FIFO_DEPTH)
COMPILE_ARGS += -GLOG_RATE=$(PARAM_LOG_RATE)
ifeq ($(WAVES), 1)

View File

@ -51,6 +51,7 @@ class TB:
if len(dut.input_ts) == 64:
self.ptp_clock = PtpClock(
ts_64=dut.input_ts,
ts_step=dut.input_ts_step,
clock=dut.input_clk,
reset=dut.input_rst,
period_ns=6.4
@ -58,6 +59,7 @@ class TB:
else:
self.ptp_clock = PtpClock(
ts_96=dut.input_ts,
ts_step=dut.input_ts_step,
clock=dut.input_clk,
reset=dut.input_rst,
period_ns=6.4
@ -70,6 +72,7 @@ class TB:
self.dut.input_rst.setimmediatevalue(0)
self.dut.output_rst.setimmediatevalue(0)
await RisingEdge(self.dut.input_clk)
await RisingEdge(self.dut.input_clk)
self.dut.input_rst <= 1
self.dut.output_rst <= 1
for k in range(10):
@ -131,9 +134,11 @@ async def run_test(dut):
await RisingEdge(dut.input_clk)
for i in range(20000):
for i in range(40000):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diff = await tb.measure_ts_diff()*1e9
tb.log.info(f"Difference: {diff} ns")
@ -147,9 +152,11 @@ async def run_test(dut):
await RisingEdge(dut.input_clk)
for i in range(20000):
for i in range(40000):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diff = await tb.measure_ts_diff()*1e9
tb.log.info(f"Difference: {diff} ns")
@ -163,9 +170,11 @@ async def run_test(dut):
await RisingEdge(dut.input_clk)
for i in range(20000):
for i in range(40000):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diff = await tb.measure_ts_diff()*1e9
tb.log.info(f"Difference: {diff} ns")
@ -179,9 +188,11 @@ async def run_test(dut):
await RisingEdge(dut.input_clk)
for i in range(20000):
for i in range(40000):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diff = await tb.measure_ts_diff()*1e9
tb.log.info(f"Difference: {diff} ns")
@ -198,6 +209,8 @@ async def run_test(dut):
for i in range(30000):
await RisingEdge(dut.input_clk)
assert tb.dut.locked.value.integer
diff = await tb.measure_ts_diff()*1e9
tb.log.info(f"Difference: {diff} ns")
@ -232,12 +245,7 @@ def test_ptp_clock_cdc(request, ts_width, sample_clock):
parameters['TS_WIDTH'] = ts_width
parameters['NS_WIDTH'] = 4
parameters['FNS_WIDTH'] = 16
parameters['INPUT_PERIOD_NS'] = 0x6
parameters['INPUT_PERIOD_FNS'] = 0x6666
parameters['OUTPUT_PERIOD_NS'] = 0x6
parameters['OUTPUT_PERIOD_FNS'] = 0x6666
parameters['USE_SAMPLE_CLOCK'] = sample_clock
parameters['LOG_FIFO_DEPTH'] = 3
parameters['LOG_RATE'] = 3
extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}