From 6f2d581d62fa66c7c19ec8cbaf32c0432dbca049 Mon Sep 17 00:00:00 2001
From: Alex Forencich <alex@alexforencich.com>
Date: Sun, 27 Mar 2022 23:47:14 -0700
Subject: [PATCH] Add output pipeline to PTP clock CDC module

---
 rtl/ptp_clock_cdc.v                    | 137 ++++++++++++++++++++++---
 tb/ptp_clock_cdc/Makefile              |   3 +
 tb/ptp_clock_cdc/test_ptp_clock_cdc.py |   1 +
 3 files changed, 126 insertions(+), 15 deletions(-)

diff --git a/rtl/ptp_clock_cdc.v b/rtl/ptp_clock_cdc.v
index d0b86c98..a284c43a 100644
--- a/rtl/ptp_clock_cdc.v
+++ b/rtl/ptp_clock_cdc.v
@@ -37,7 +37,8 @@ module ptp_clock_cdc #
     parameter NS_WIDTH = 4,
     parameter FNS_WIDTH = 16,
     parameter USE_SAMPLE_CLOCK = 1,
-    parameter LOG_RATE = 3
+    parameter LOG_RATE = 3,
+    parameter PIPELINE_OUTPUT = 0
 )
 (
     input  wire                 input_clk,
@@ -115,7 +116,13 @@ 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 pps_reg = 1'b0;
+
+reg [47:0] ts_s_pipe_reg[0:PIPELINE_OUTPUT-1];
+reg [TS_NS_WIDTH-1:0] ts_ns_pipe_reg[0:PIPELINE_OUTPUT-1];
+reg [FNS_WIDTH-1:0] ts_fns_pipe_reg[0:PIPELINE_OUTPUT-1];
+reg ts_step_pipe_reg[0:PIPELINE_OUTPUT-1];
+reg pps_pipe_reg[0:PIPELINE_OUTPUT-1];
 
 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;
@@ -150,21 +157,90 @@ reg sample_update_sync3_reg = 1'b0;
 
 generate
 
-if (TS_WIDTH == 96) begin
-    assign output_ts[95:48] = ts_s_reg;
-    assign output_ts[47:46] = 2'b00;
-    assign output_ts[45:16] = ts_ns_reg;
-    assign output_ts[15:0]  = FNS_WIDTH > 16 ? ts_fns_reg >> (FNS_WIDTH-16) : ts_fns_reg << (16-FNS_WIDTH);
-end else if (TS_WIDTH == 64) begin
-    assign output_ts[63:16] = ts_ns_reg;
-    assign output_ts[15:0]  = FNS_WIDTH > 16 ? ts_fns_reg >> (FNS_WIDTH-16) : ts_fns_reg << (16-FNS_WIDTH);
+if (PIPELINE_OUTPUT > 0) begin
+
+    // pipeline
+    (* shreg_extract = "no" *)
+    reg [TS_WIDTH-1:0]  output_ts_reg[0:PIPELINE_OUTPUT-1];
+    (* shreg_extract = "no" *)
+    reg                 output_ts_step_reg[0:PIPELINE_OUTPUT-1];
+    (* shreg_extract = "no" *)
+    reg                 output_pps_reg[0:PIPELINE_OUTPUT-1];
+
+    assign output_ts = output_ts_reg[PIPELINE_OUTPUT-1];
+    assign output_ts_step = output_ts_step_reg[PIPELINE_OUTPUT-1];
+    assign output_pps = output_pps_reg[PIPELINE_OUTPUT-1];
+
+    integer i;
+
+    initial begin
+        for (i = 0; i < PIPELINE_OUTPUT; i = i + 1) begin
+            output_ts_reg[i] = 0;
+            output_ts_step_reg[i] = 1'b0;
+            output_pps_reg[i] = 1'b0;
+        end
+    end
+
+    always @(posedge output_clk) begin
+        if (TS_WIDTH == 96) begin
+            output_ts_reg[0][95:48] <= ts_s_reg;
+            output_ts_reg[0][47:46] <= 2'b00;
+            output_ts_reg[0][45:16] <= ts_ns_reg;
+            output_ts_reg[0][15:0]  <= {ts_fns_reg, 16'd0} >> FNS_WIDTH;
+        end else if (TS_WIDTH == 64) begin
+            output_ts_reg[0][63:16] <= ts_ns_reg;
+            output_ts_reg[0][15:0]  <= {ts_fns_reg, 16'd0} >> FNS_WIDTH;
+        end
+
+        output_ts_step_reg[0] <= ts_step_reg;
+        output_pps_reg[0] <= pps_reg;
+
+        for (i = 0; i < PIPELINE_OUTPUT-1; i = i + 1) begin
+            output_ts_reg[i+1] <= output_ts_reg[i];
+            output_ts_step_reg[i+1] <= output_ts_step_reg[i];
+            output_pps_reg[i+1] <= output_pps_reg[i];
+        end
+
+        if (output_rst) begin
+            for (i = 0; i < PIPELINE_OUTPUT; i = i + 1) begin
+                output_ts_reg[i] = 0;
+                output_ts_step_reg[i] = 1'b0;
+                output_pps_reg[i] = 1'b0;
+            end
+        end
+    end
+
+end else begin
+
+    if (TS_WIDTH == 96) begin
+        assign output_ts[95:48] = ts_s_reg;
+        assign output_ts[47:46] = 2'b00;
+        assign output_ts[45:16] = ts_ns_reg;
+        assign output_ts[15:0]  = {ts_fns_reg, 16'd0} >> FNS_WIDTH;
+    end else if (TS_WIDTH == 64) begin
+        assign output_ts[63:16] = ts_ns_reg;
+        assign output_ts[15:0]  = {ts_fns_reg, 16'd0} >> FNS_WIDTH;
+    end
+
+    assign output_ts_step = ts_step_reg;
+
+    assign output_pps = pps_reg;
+
 end
 
 endgenerate
 
-assign output_ts_step = ts_step_reg;
+integer i;
 
-assign output_pps = pps_reg;
+initial begin
+    for (i = 0; i < PIPELINE_OUTPUT; i = i + 1) begin
+        ts_s_pipe_reg[i] = 0;
+        ts_ns_pipe_reg[i] = 0;
+        ts_fns_pipe_reg[i] = 0;
+        ts_step_pipe_reg[i] = 1'b0;
+        pps_pipe_reg[i] = 1'b0;
+    end
+end
 
 // source PTP clock capture and sync logic
 reg input_ts_step_reg = 1'b0;
@@ -381,9 +457,15 @@ always @(posedge output_clk) begin
 
     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 (PIPELINE_OUTPUT > 0) begin
+            dest_ts_s_capt_reg <= ts_s_pipe_reg[PIPELINE_OUTPUT-1];
+            dest_ts_ns_capt_reg <= ts_ns_pipe_reg[PIPELINE_OUTPUT-1];
+            dest_ts_fns_capt_reg <= ts_fns_pipe_reg[PIPELINE_OUTPUT-1];
+        end else begin
+            dest_ts_s_capt_reg <= ts_s_reg;
+            dest_ts_ns_capt_reg <= ts_ns_reg;
+            dest_ts_fns_capt_reg <= ts_fns_reg;
+        end
 
         dest_sync_reg <= !dest_sync_reg;
         ts_capt_valid_reg <= 1'b1;
@@ -668,6 +750,23 @@ always @(posedge output_clk) begin
         pps_reg <= 1'b0; // not currently implemented for 64 bit timestamp format
     end
 
+    // pipeline
+    if (PIPELINE_OUTPUT > 0) begin
+        ts_s_pipe_reg[0] <= ts_s_reg;
+        ts_ns_pipe_reg[0] <= ts_ns_reg;
+        ts_fns_pipe_reg[0] <= ts_fns_reg;
+        ts_step_pipe_reg[0] <= ts_step_reg;
+        pps_pipe_reg[0] <= pps_reg;
+
+        for (i = 0; i < PIPELINE_OUTPUT-1; i = i + 1) begin
+            ts_s_pipe_reg[i+1] <= ts_s_pipe_reg[i];
+            ts_ns_pipe_reg[i+1] <= ts_ns_pipe_reg[i];
+            ts_fns_pipe_reg[i+1] <= ts_fns_pipe_reg[i];
+            ts_step_pipe_reg[i+1] <= ts_step_pipe_reg[i];
+            pps_pipe_reg[i+1] <= pps_pipe_reg[i];
+        end
+    end
+
     if (output_rst) begin
         period_ns_reg <= 0;
         period_fns_reg <= 0;
@@ -689,6 +788,14 @@ always @(posedge output_clk) begin
 
         ptp_lock_count_reg <= 0;
         ptp_locked_reg <= 1'b0;
+
+        for (i = 0; i < PIPELINE_OUTPUT; i = i + 1) begin
+            ts_s_pipe_reg[i] = 0;
+            ts_ns_pipe_reg[i] = 0;
+            ts_fns_pipe_reg[i] = 0;
+            ts_step_pipe_reg[i] = 1'b0;
+            pps_pipe_reg[i] = 1'b0;
+        end
     end
 end
 
diff --git a/tb/ptp_clock_cdc/Makefile b/tb/ptp_clock_cdc/Makefile
index ff4c9f9c..eac841e1 100644
--- a/tb/ptp_clock_cdc/Makefile
+++ b/tb/ptp_clock_cdc/Makefile
@@ -37,6 +37,7 @@ export PARAM_NS_WIDTH ?= 4
 export PARAM_FNS_WIDTH ?= 16
 export PARAM_USE_SAMPLE_CLOCK ?= 1
 export PARAM_LOG_RATE ?= 3
+export PARAM_PIPELINE_OUTPUT ?= 0
 
 ifeq ($(SIM), icarus)
 	PLUSARGS += -fst
@@ -46,6 +47,7 @@ ifeq ($(SIM), icarus)
 	COMPILE_ARGS += -P $(TOPLEVEL).FNS_WIDTH=$(PARAM_FNS_WIDTH)
 	COMPILE_ARGS += -P $(TOPLEVEL).USE_SAMPLE_CLOCK=$(PARAM_USE_SAMPLE_CLOCK)
 	COMPILE_ARGS += -P $(TOPLEVEL).LOG_RATE=$(PARAM_LOG_RATE)
+	COMPILE_ARGS += -P $(TOPLEVEL).PIPELINE_OUTPUT=$(PARAM_PIPELINE_OUTPUT)
 
 	ifeq ($(WAVES), 1)
 		VERILOG_SOURCES += iverilog_dump.v
@@ -59,6 +61,7 @@ else ifeq ($(SIM), verilator)
 	COMPILE_ARGS += -GFNS_WIDTH=$(PARAM_FNS_WIDTH)
 	COMPILE_ARGS += -GUSE_SAMPLE_CLOCK=$(PARAM_USE_SAMPLE_CLOCK)
 	COMPILE_ARGS += -GLOG_RATE=$(PARAM_LOG_RATE)
+	COMPILE_ARGS += -GPIPELINE_OUTPUT=$(PARAM_PIPELINE_OUTPUT)
 
 	ifeq ($(WAVES), 1)
 		COMPILE_ARGS += --trace-fst
diff --git a/tb/ptp_clock_cdc/test_ptp_clock_cdc.py b/tb/ptp_clock_cdc/test_ptp_clock_cdc.py
index f3a26f95..66cfd37a 100644
--- a/tb/ptp_clock_cdc/test_ptp_clock_cdc.py
+++ b/tb/ptp_clock_cdc/test_ptp_clock_cdc.py
@@ -247,6 +247,7 @@ def test_ptp_clock_cdc(request, ts_width, sample_clock):
     parameters['FNS_WIDTH'] = 16
     parameters['USE_SAMPLE_CLOCK'] = sample_clock
     parameters['LOG_RATE'] = 3
+    parameters['PIPELINE_OUTPUT'] = 0
 
     extra_env = {f'PARAM_{k}': str(v) for k, v in parameters.items()}
 
-- 
GitLab