diff options
Diffstat (limited to 'fpga/usrp3/top/e320/e320_clocking.v')
| -rw-r--r-- | fpga/usrp3/top/e320/e320_clocking.v | 220 | 
1 files changed, 220 insertions, 0 deletions
| diff --git a/fpga/usrp3/top/e320/e320_clocking.v b/fpga/usrp3/top/e320/e320_clocking.v new file mode 100644 index 000000000..fff99180e --- /dev/null +++ b/fpga/usrp3/top/e320/e320_clocking.v @@ -0,0 +1,220 @@ +///////////////////////////////////////////////////////////////////// +// +// Copyright 2018 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: e320_clocking.v +// +// Purpose: +// +// TODO: First, instantiate clock input buffers on all clocks to provide termination +// for the PCB traces. +// +// 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. +// +////////////////////////////////////////////////////////////////////// + +module e320_clocking ( +  input global_rst, + +  // Reference Clk +  input ref_clk_from_pin, +  output ref_clk, + +  // Input clocks +  input clk156,    // 156.25 MHz + +  // Output clocks +  output ddr3_dma_clk, +  output reg clocks_locked = 1'b0, + +  // PPS Capture & Selection +  input       ext_pps_from_pin, +  input       gps_pps_from_pin, +  input [1:0] pps_select, +  output reg  pps_refclk +); + +  //TODO: Code is same as n3xx, try reusing it. + +  // 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. +  IBUFG ref_clk_ibuf ( +    .O(ref_clk_buf), +    .I(ref_clk_from_pin) +  ); + +  BUFG ref_clk_bufg ( +    .I(ref_clk_buf), +    .O(ref_clk) +  ); + +  wire pps_ext_refclk; +  wire pps_gps_refclk; +  wire [1:0] pps_select_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(2) +  ) 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 = 0; +  localparam BIT_PPS_SEL_EXT = 1; + +  // PPS MUX - selects internal/gpsdo 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]) begin +      pps_refclk <= pps_gps_refclk; +    end else if (pps_select_refclk[BIT_PPS_SEL_EXT]) begin +      pps_refclk <= pps_ext_refclk; +    end else begin +      pps_refclk <= pps_gps_refclk; +    end +  end + +  //--------------------------------------------------------------------------- +  // Clock Generation +  //--------------------------------------------------------------------------- + +  MMCME2_ADV #( +    .BANDWIDTH            ("OPTIMIZED"), +    .CLKOUT4_CASCADE      ("FALSE"), +    .COMPENSATION         ("ZHOLD"), +    .STARTUP_WAIT         ("FALSE"), +    .DIVCLK_DIVIDE        (1), +    .CLKFBOUT_MULT_F      (6.000), +    .CLKFBOUT_PHASE       (0.000), +    .CLKFBOUT_USE_FINE_PS ("FALSE"), +    .CLKOUT0_DIVIDE_F     (3.125), +    .CLKOUT0_PHASE        (0.000), +    .CLKOUT0_DUTY_CYCLE   (0.500), +    .CLKOUT0_USE_FINE_PS  ("FALSE"), +    .CLKIN1_PERIOD        (6.400)) +  mmcm_adv_inst ( +    .CLKFBOUT            (clkfbout), +    .CLKFBOUTB           (), +    .CLKOUT0             (ddr3_dma_clk_raw), +    .CLKOUT0B            (), +    .CLKOUT1             (), +    .CLKOUT1B            (), +    .CLKOUT2             (), +    .CLKOUT2B            (), +    .CLKOUT3             (), +    .CLKOUT3B            (), +    .CLKOUT4             (), +    .CLKOUT5             (), +    .CLKOUT6             (), +     // Input clock control +    .CLKFBIN             (clkfbout), +    .CLKIN1              (clk156), +    .CLKIN2              (1'b0), +     // Tied to always select the primary input clock +    .CLKINSEL            (1'b1), +    // Ports for dynamic reconfiguration +    .DADDR               (7'h0), +    .DCLK                (1'b0), +    .DEN                 (1'b0), +    .DI                  (16'h0), +    .DO                  (), +    .DRDY                (), +    .DWE                 (1'b0), +    // Ports for dynamic phase shift +    .PSCLK               (1'b0), +    .PSEN                (1'b0), +    .PSINCDEC            (1'b0), +    .PSDONE              (), +    // Other control and status signals +    .LOCKED              (locked_raw), +    .CLKINSTOPPED        (), +    .CLKFBSTOPPED        (), +    .PWRDWN              (1'b0), +    .RST                 (global_rst)); + +  BUFG clk300_bufg +     (.O   (ddr3_dma_clk), +      .I   (ddr3_dma_clk_raw)); + + +  //--------------------------------------------------------------------------- +  // Lock Signal +  //--------------------------------------------------------------------------- +  // +  // We assume that the LOCKED signal from the MMCM is not necessarily a clean +  // asynchronous signal, so we want to make sure that the MMCM is really +  // locked before we assert our clocks_locked output. +  // +  //--------------------------------------------------------------------------- + +  reg [9:0] locked_count = ~0; + +  synchronizer lock_sync_i ( +    .clk(clk156), .rst(1'b0), .in(locked_raw), .out(locked_sync) +  ); + +  // Filter the locked signal +  always @(posedge clk156 or posedge global_rst) +  begin +    if (global_rst) begin +      locked_count  <= ~0; +      clocks_locked <=  0; +    end else begin +      if (~locked_sync) begin +        locked_count  <= ~0; +        clocks_locked <= 1'b0; +      end else begin +        if (locked_count == 0) begin +          clocks_locked <= 1'b1; +        end else begin +          clocks_locked <= 1'b0; +          locked_count <= locked_count - 1; +        end +      end +    end +  end + +endmodule | 
