aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/top/n3xx/n3xx_clocking.v
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/top/n3xx/n3xx_clocking.v')
-rw-r--r--fpga/usrp3/top/n3xx/n3xx_clocking.v238
1 files changed, 238 insertions, 0 deletions
diff --git a/fpga/usrp3/top/n3xx/n3xx_clocking.v b/fpga/usrp3/top/n3xx/n3xx_clocking.v
new file mode 100644
index 000000000..fc7ecbe49
--- /dev/null
+++ b/fpga/usrp3/top/n3xx/n3xx_clocking.v
@@ -0,0 +1,238 @@
+/////////////////////////////////////////////////////////////////////
+//
+// Copyright 2017 Ettus Research, A National Instruments Company
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: n3xx_clocking.v
+//
+// Purpose:
+//
+// First, instantiate clock input buffers on all clocks to provide termination
+// for the PCB traces. This file also includes the MMCM for generating meas_clk at
+// specific rates and a global buffer for the reference clock to be able to use it
+// directly within and outside of this module.
+//
+// Second, PPS inputs from the back panel (called external) and the GPSDO are captured by
+// the Reference Clock. Selection is performed amongst these and the internally-generated
+// options.
+//
+// NOTE: BUFGs are NOT instantiated on the following clocks, denoted by the _buf suffix:
+// wr_refclk_buf, netclk_buf, gige_refclk_buf, xgige_refclk_buf
+//
+//////////////////////////////////////////////////////////////////////
+
+module n3xx_clocking (
+ // Input buffers for clocks
+ input enable_ref_clk_async, // enables the ref_clk BUFG (driven async to ref_clk)
+ input FPGA_REFCLK_P, FPGA_REFCLK_N,
+ output ref_clk,
+
+ input WB_20MHz_P, WB_20MHz_N,
+ output wr_refclk_buf,
+
+ input NETCLK_REF_P, NETCLK_REF_N,
+ output netclk_buf,
+
+ input NETCLK_P, NETCLK_N,
+ output gige_refclk_buf,
+
+ input MGT156MHZ_CLK1_P, MGT156MHZ_CLK1_N,
+ output xgige_refclk_buf,
+
+ // Measurement Clock Generation
+ input misc_clks_ref,
+ output meas_clk,
+ output ddr3_dma_clk,
+ input misc_clks_reset,
+ output misc_clks_locked,
+
+ // PPS Capture & Selection
+ input ext_pps_from_pin,
+ input gps_pps_from_pin,
+ input [3:0] pps_select,
+ output reg pps_refclk
+ );
+
+ // Clock Buffering and Generation : ///////////////////////////////////////////////////
+ //
+ // Manually instantiate input buffers on all clocks, and a global buffer on the
+ // Reference Clock for use in the rest of the design. All other clocks must have
+ // global buffers other places, since the declarations here are for SI purposes.
+ //
+ ///////////////////////////////////////////////////////////////////////////////////////
+
+ wire ref_clk_buf;
+
+ // FPGA Reference Clock Buffering
+ //
+ // Only require an IBUF and BUFG here, since an MMCM is (thankfully) not needed
+ // to meet timing with the PPS signal.
+ IBUFGDS ref_clk_ibuf (
+ .O(ref_clk_buf),
+ .I(FPGA_REFCLK_P),
+ .IB(FPGA_REFCLK_N)
+ );
+
+ // BUFG ref_clk_bufg (
+ // .I(ref_clk_buf),
+ // .O(ref_clk)
+ // );
+ WrapBufg #(
+ .kEnableIsAsync(1'b1)
+ ) ref_clk_bufg (
+ .ClkIn(ref_clk_buf),
+ .aCe(enable_ref_clk_async),
+ .ClkOut(ref_clk)
+ );
+
+ // Buffers for SI Purposes
+ //
+ // Instantiate buffers on each of these differential clock inputs with DONT_TOUCH
+ // attributes in order to preserve the internal termination regardless of whether
+ // these clocks are used in the design. The lack of termination would place the
+ // voltage swings for these pins outside the acceptable range for the FPGA inputs.
+ (* dont_touch = "true" *) IBUFGDS wr_refclk_ibuf (
+ .I (WB_20MHz_P),
+ .IB(WB_20MHz_N),
+ .O (wr_refclk_buf)
+ );
+
+ (* dont_touch = "true" *) IBUFGDS netclk_ref_ibuf (
+ .I (NETCLK_REF_P),
+ .IB(NETCLK_REF_N),
+ .O (netclk_buf)
+ );
+
+ // Same deal for the MGT reference clock buffers.
+ (* dont_touch = "true" *) IBUFDS_GTE2 gige_refclk_ibuf (
+ .ODIV2(),
+ .CEB (1'b0),
+ .I (NETCLK_P),
+ .IB(NETCLK_N),
+ .O (gige_refclk_buf)
+ );
+
+ (* dont_touch = "true" *) IBUFDS_GTE2 ten_gige_refclk_ibuf (
+ .ODIV2(),
+ .CEB (1'b0),
+ .I (MGT156MHZ_CLK1_P),
+ .IB(MGT156MHZ_CLK1_N),
+ .O (xgige_refclk_buf)
+ );
+
+ // Measurement Clock MMCM Instantiation
+ //
+ // This must be an MMCM to hit the weird rates we need for meas_clk. It takes the
+ // 166.6667 MHz clock from the PS and provides the correct meas_clk rate for the TDC.
+ // BUFG is embedded in the MMCM files.
+ //----------------------------------------------------------------------------
+ // Output Output Phase Duty Cycle Pk-to-Pk Phase
+ // Clock Freq (MHz) (degrees) (%) Jitter (ps) Error (ps)
+ //----------------------------------------------------------------------------
+ // CLK_OUT1___170.543______0.000______50.0______105.052_____94.905 (meas_clk)
+ // CLK_OUT2___305.556______0.000______50.0_______93.867_____94.905 (ddr3_dma_clk)
+ //
+ //----------------------------------------------------------------------------
+ // Input Clock Freq (MHz) Input Jitter (UI)
+ //----------------------------------------------------------------------------
+ // __primary________166.666667____________0.010
+
+ misc_clock_gen misc_clock_gen_i (
+ .clk_in (misc_clks_ref),
+ .meas_clk (meas_clk),
+ .ddr3_dma_clk (ddr3_dma_clk),
+ .reset (misc_clks_reset),
+ .locked (misc_clks_locked)
+ );
+
+
+ // PPS Capture and Generation : ///////////////////////////////////////////////////////
+ //
+ // The following shows the support matrix for PPS with respect to the
+ // reference clock source and rate.
+ // _______________________________
+ // ____________| PPS |
+ // | Clocks | External | FPGA | GPSDO | WR |
+ // |--------------------------------------------|
+ // |External 10 | x | x | | |
+ // |Internal 25 | | x | | x |
+ // |GPSDO 20 | | | x | |
+ // |--------------------------------------------|
+ //
+ ///////////////////////////////////////////////////////////////////////////////////////
+
+ wire pps_ext_refclk;
+ wire pps_gps_refclk;
+ wire [3:0] pps_select_refclk;
+
+ // Generate two internal PPS signals, each with a 25% duty cycle, based on
+ // 10 MHz and 25 MHz Reference Clock rates. Only one will be used at a time.
+ wire int_pps_10mhz_refclk;
+ pps_generator #(
+ .CLK_FREQ(32'd10_000_000), .DUTY_CYCLE(25)
+ ) pps_gen_10 (
+ .clk(ref_clk), .reset(1'b0), .pps(int_pps_10mhz_refclk)
+ );
+ wire int_pps_25mhz_refclk;
+ pps_generator #(
+ .CLK_FREQ(32'd25_000_000), .DUTY_CYCLE(25)
+ ) pps_gen_25 (
+ .clk(ref_clk), .reset(1'b0), .pps(int_pps_25mhz_refclk)
+ );
+
+ // Capture the external PPSs with a FF before sending them to the mux. To be safe,
+ // we double-synchronize the external signals. If we meet timing (which we should)
+ // then this is a two-cycle delay. If we don't meet timing, then it's 1-2 cycles
+ // and our system timing is thrown off--but at least our downstream logic doesn't
+ // go metastable!
+ synchronizer #(
+ .FALSE_PATH_TO_IN(0)
+ ) ext_pps_dsync (
+ .clk(ref_clk), .rst(1'b0), .in(ext_pps_from_pin), .out(pps_ext_refclk)
+ );
+ // Same deal with the GPSDO PPS input. Double-sync, then use it.
+ synchronizer #(
+ .FALSE_PATH_TO_IN(0)
+ ) gps_pps_dsync (
+ .clk(ref_clk), .rst(1'b0), .in(gps_pps_from_pin), .out(pps_gps_refclk)
+ );
+
+ // Synchronize the select bits over to the reference clock as well. Note that this is
+ // a vector, so we could have some non-one-hot values creep through when changing.
+ // See the note below as to why this is safe.
+ synchronizer #(
+ .FALSE_PATH_TO_IN(1),
+ .WIDTH(4)
+ ) pps_select_dsync (
+ .clk(ref_clk), .rst(1'b0), .in(pps_select), .out(pps_select_refclk)
+ );
+
+ // Bit locations for the pps_select vector.
+ localparam BIT_PPS_SEL_INT_10 = 0;
+ localparam BIT_PPS_SEL_INT_25 = 1;
+ localparam BIT_PPS_SEL_EXT = 2;
+ localparam BIT_PPS_SEL_GPSDO = 3;
+
+ // PPS MUX - selects internal or external PPS.
+ always @(posedge ref_clk) begin
+
+ // Encoding is one-hot on these bits. It is possible when the vector is being double-
+ // synchronized to the reference clock domain that there could be multiple bits
+ // asserted simultaneously. This is not problematic because the order of operations
+ // in the following selection mux should take over and only one PPS should win.
+ // This could result in glitches, but that is expected during ANY PPS switchover
+ // since the switch is performed asynchronously to the PPS signal.
+ if (pps_select_refclk[BIT_PPS_SEL_INT_10])
+ pps_refclk <= int_pps_10mhz_refclk;
+ else if (pps_select_refclk[BIT_PPS_SEL_INT_25])
+ pps_refclk <= int_pps_25mhz_refclk;
+ else if (pps_select_refclk[BIT_PPS_SEL_EXT])
+ pps_refclk <= pps_ext_refclk;
+ else if (pps_select_refclk[BIT_PPS_SEL_GPSDO])
+ pps_refclk <= pps_gps_refclk;
+ else
+ pps_refclk <= pps_ext_refclk; // Compatibility with old SW stacks, pps_select_refclk = 0 = external
+
+ end
+
+endmodule