1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 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
|