diff --git a/rtl/hip/us/taxi_mmcm_frac.sv b/rtl/hip/us/taxi_mmcm_frac.sv index 52ba1a0..234c0ae 100644 --- a/rtl/hip/us/taxi_mmcm_frac.sv +++ b/rtl/hip/us/taxi_mmcm_frac.sv @@ -21,6 +21,11 @@ module taxi_mmcm_frac #( parameter MMCM_INPUT_DIV = 1, parameter MMCM_MULT = 8, parameter MMCM_OUTPUT_DIV = MMCM_MULT, + parameter logic EXTRA_MMCM = 1'b1, + parameter MMCM2_CASC_DIV = MMCM_OUTPUT_DIV, + parameter MMCM2_INPUT_DIV = 1, + parameter MMCM2_MULT = MMCM_OUTPUT_DIV, + parameter MMCM2_OUTPUT_DIV = MMCM2_MULT, parameter OFFSET_NUM = 1, parameter OFFSET_DENOM = 65536 ) @@ -33,8 +38,13 @@ module taxi_mmcm_frac #( output wire locked ); -// 1 tap is 1/56th of the VCO period -// to shift 1 full cycle of the output clock, 56*MMCM_OUTPUT_DIV shifts are required +localparam MMCM_OUTPUT_CLK_PERIOD = MMCM_INPUT_CLK_PERIOD*MMCM_INPUT_DIV/MMCM_MULT*MMCM_OUTPUT_DIV; +localparam MMCM2_INPUT_CLK_PERIOD = MMCM_INPUT_CLK_PERIOD*MMCM_INPUT_DIV/MMCM_MULT*MMCM2_CASC_DIV; + +// 1 phase shifter tap is 1/56th of the VCO period +// To shift the frequency by OFFSET_NUM/OFFSET_DENOM, we need to shift the VCO output +// by OFFSET_NUM VCO periods (56*OFFSET_NUM taps) every OFFSET_DENOM VCO periods. +// Since the PSCLK is divided with respect to the VCO, rescale numerator by division factor localparam DIR = OFFSET_NUM >= 0; localparam NUM_1 = (DIR ? OFFSET_NUM : -OFFSET_NUM)*56*MMCM_OUTPUT_DIV; localparam DENOM_1 = OFFSET_DENOM; @@ -46,6 +56,24 @@ if (DENOM_1 / NUM_1 < MMCM_MIN_PSCLK_CYCLES) localparam CNT_W = $clog2(DENOM_1)+1; +wire mmcm_locked; +wire mmcm2_locked; + +wire mmcm_clkfb; +wire mmcm_clk_out; +wire mmcm_clk_bufg; +wire mmcm_offset_clk_out; +wire mmcm_offset_clk_bufg; + +wire mmcm2_clkfb; +wire mmcm2_offset_clk_out; +wire mmcm2_offset_clk_bufg; + +assign locked = mmcm_locked && mmcm2_locked; + +assign output_clk = mmcm_clk_bufg; +assign output_offset_clk = EXTRA_MMCM ? mmcm2_offset_clk_bufg : mmcm_offset_clk_bufg; + logic [CNT_W-1:0] cnt_reg = '0; logic ps_en_reg = 1'b0; @@ -61,11 +89,6 @@ always_ff @(posedge output_clk) begin end end -wire clkfb; - -wire output_clk_mmcm; -wire output_offset_clk_mmcm; - MMCME3_ADV #( // input clocks .CLKIN1_PERIOD(MMCM_INPUT_CLK_PERIOD), @@ -87,7 +110,7 @@ MMCME3_ADV #( .CLKOUT0_PHASE(0), .CLKOUT0_USE_FINE_PS("FALSE"), // divide and phase shift - .CLKOUT1_DIVIDE(MMCM_OUTPUT_DIV), + .CLKOUT1_DIVIDE(EXTRA_MMCM ? MMCM2_CASC_DIV : MMCM_OUTPUT_DIV), .CLKOUT1_DUTY_CYCLE(0.5), .CLKOUT1_PHASE(0), .CLKOUT1_USE_FINE_PS("TRUE"), @@ -135,14 +158,14 @@ clk_mmcm_inst ( // select CLKIN1 .CLKINSEL(1'b1), // direct clkfb feedback - .CLKFBIN(clkfb), - .CLKFBOUT(clkfb), + .CLKFBIN(mmcm_clkfb), + .CLKFBOUT(mmcm_clkfb), .CLKFBOUTB(), - // phase-shifted output - .CLKOUT0(output_clk_mmcm), + // output (no offset) + .CLKOUT0(mmcm_clk_out), .CLKOUT0B(), - // Not used - .CLKOUT1(output_offset_clk_mmcm), + // offset output + .CLKOUT1(mmcm_offset_clk_out), .CLKOUT1B(), // Not used .CLKOUT2(), @@ -173,7 +196,7 @@ clk_mmcm_inst ( .PSCLK(output_clk), .PSDONE(), // locked output - .LOCKED(locked), + .LOCKED(mmcm_locked), // input status .CLKINSTOPPED(), .CLKFBSTOPPED(), @@ -183,17 +206,113 @@ clk_mmcm_inst ( ); BUFG -output_clk_bufg_inst ( - .I(output_clk_mmcm), - .O(output_clk) +mmcm_clk_bufg_inst ( + .I(mmcm_clk_out), + .O(mmcm_clk_bufg) ); BUFG -output_offset_clk_bufg_inst ( - .I(output_offset_clk_mmcm), - .O(output_offset_clk) +mmcm_offset_clk_bufg_inst ( + .I(mmcm_offset_clk_out), + .O(mmcm_offset_clk_bufg) ); +if (EXTRA_MMCM) begin : extra_mmcm + + // MMCM instance + MMCME3_BASE #( + // 125 MHz input + .CLKIN1_PERIOD(MMCM2_INPUT_CLK_PERIOD), + .REF_JITTER1(MMCM_INPUT_REF_JITTER), + // divide for PFD input + // US/US+: range 10 MHz to 500 MHz + .DIVCLK_DIVIDE(MMCM2_INPUT_DIV), + // multiply for VCO output + // US: range 600 MHz to 1440 MHz + // US+: range 800 MHz to 1600 MHz + .CLKFBOUT_MULT_F(MMCM2_MULT), + .CLKFBOUT_PHASE(0), + // divide + .CLKOUT0_DIVIDE_F(MMCM2_OUTPUT_DIV), + .CLKOUT0_DUTY_CYCLE(0.5), + .CLKOUT0_PHASE(0), + // Not used + .CLKOUT1_DIVIDE(1), + .CLKOUT1_DUTY_CYCLE(0.5), + .CLKOUT1_PHASE(0), + // Not used + .CLKOUT2_DIVIDE(1), + .CLKOUT2_DUTY_CYCLE(0.5), + .CLKOUT2_PHASE(0), + // Not used + .CLKOUT3_DIVIDE(1), + .CLKOUT3_DUTY_CYCLE(0.5), + .CLKOUT3_PHASE(0), + // Not used + .CLKOUT4_DIVIDE(1), + .CLKOUT4_DUTY_CYCLE(0.5), + .CLKOUT4_PHASE(0), + .CLKOUT4_CASCADE("FALSE"), + // Not used + .CLKOUT5_DIVIDE(1), + .CLKOUT5_DUTY_CYCLE(0.5), + .CLKOUT5_PHASE(0), + // Not used + .CLKOUT6_DIVIDE(1), + .CLKOUT6_DUTY_CYCLE(0.5), + .CLKOUT6_PHASE(0), + + // optimized bandwidth + .BANDWIDTH("OPTIMIZED"), + // don't wait for lock during startup + .STARTUP_WAIT("FALSE") + ) + clk_mmcm2_inst ( + // 125 MHz input + .CLKIN1(mmcm_offset_clk_bufg), + // direct clkfb feeback + .CLKFBIN(mmcm2_clkfb), + .CLKFBOUT(mmcm2_clkfb), + .CLKFBOUTB(), + // 125 MHz, 0 degrees + .CLKOUT0(mmcm2_offset_clk_out), + .CLKOUT0B(), + // Not used + .CLKOUT1(), + .CLKOUT1B(), + // Not used + .CLKOUT2(), + .CLKOUT2B(), + // Not used + .CLKOUT3(), + .CLKOUT3B(), + // Not used + .CLKOUT4(), + // Not used + .CLKOUT5(), + // Not used + .CLKOUT6(), + // reset input + .RST(input_rst || !mmcm_locked), + // don't power down + .PWRDWN(1'b0), + // locked output + .LOCKED(mmcm2_locked) + ); + + BUFG + mmcm2_offset_clk_bufg_inst ( + .I(mmcm2_offset_clk_out), + .O(mmcm2_offset_clk_bufg) + ); + +end else begin + + assign mmcm2_locked = mmcm_locked; + assign mmcm2_offset_clk_bufg = mmcm_offset_clk_bufg; + +end + endmodule `resetall