hip: Add support for optional cascaded MMCM for offset clock

Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
Alex Forencich
2025-03-17 21:04:52 -07:00
parent b468d92e39
commit d7e29a2b5c

View File

@@ -21,6 +21,11 @@ module taxi_mmcm_frac #(
parameter MMCM_INPUT_DIV = 1, parameter MMCM_INPUT_DIV = 1,
parameter MMCM_MULT = 8, parameter MMCM_MULT = 8,
parameter MMCM_OUTPUT_DIV = MMCM_MULT, 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_NUM = 1,
parameter OFFSET_DENOM = 65536 parameter OFFSET_DENOM = 65536
) )
@@ -33,8 +38,13 @@ module taxi_mmcm_frac #(
output wire locked output wire locked
); );
// 1 tap is 1/56th of the VCO period localparam MMCM_OUTPUT_CLK_PERIOD = MMCM_INPUT_CLK_PERIOD*MMCM_INPUT_DIV/MMCM_MULT*MMCM_OUTPUT_DIV;
// to shift 1 full cycle of the output clock, 56*MMCM_OUTPUT_DIV shifts are required 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 DIR = OFFSET_NUM >= 0;
localparam NUM_1 = (DIR ? OFFSET_NUM : -OFFSET_NUM)*56*MMCM_OUTPUT_DIV; localparam NUM_1 = (DIR ? OFFSET_NUM : -OFFSET_NUM)*56*MMCM_OUTPUT_DIV;
localparam DENOM_1 = OFFSET_DENOM; 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; 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 [CNT_W-1:0] cnt_reg = '0;
logic ps_en_reg = 1'b0; logic ps_en_reg = 1'b0;
@@ -61,11 +89,6 @@ always_ff @(posedge output_clk) begin
end end
end end
wire clkfb;
wire output_clk_mmcm;
wire output_offset_clk_mmcm;
MMCME3_ADV #( MMCME3_ADV #(
// input clocks // input clocks
.CLKIN1_PERIOD(MMCM_INPUT_CLK_PERIOD), .CLKIN1_PERIOD(MMCM_INPUT_CLK_PERIOD),
@@ -87,7 +110,7 @@ MMCME3_ADV #(
.CLKOUT0_PHASE(0), .CLKOUT0_PHASE(0),
.CLKOUT0_USE_FINE_PS("FALSE"), .CLKOUT0_USE_FINE_PS("FALSE"),
// divide and phase shift // 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_DUTY_CYCLE(0.5),
.CLKOUT1_PHASE(0), .CLKOUT1_PHASE(0),
.CLKOUT1_USE_FINE_PS("TRUE"), .CLKOUT1_USE_FINE_PS("TRUE"),
@@ -135,14 +158,14 @@ clk_mmcm_inst (
// select CLKIN1 // select CLKIN1
.CLKINSEL(1'b1), .CLKINSEL(1'b1),
// direct clkfb feedback // direct clkfb feedback
.CLKFBIN(clkfb), .CLKFBIN(mmcm_clkfb),
.CLKFBOUT(clkfb), .CLKFBOUT(mmcm_clkfb),
.CLKFBOUTB(), .CLKFBOUTB(),
// phase-shifted output // output (no offset)
.CLKOUT0(output_clk_mmcm), .CLKOUT0(mmcm_clk_out),
.CLKOUT0B(), .CLKOUT0B(),
// Not used // offset output
.CLKOUT1(output_offset_clk_mmcm), .CLKOUT1(mmcm_offset_clk_out),
.CLKOUT1B(), .CLKOUT1B(),
// Not used // Not used
.CLKOUT2(), .CLKOUT2(),
@@ -173,7 +196,7 @@ clk_mmcm_inst (
.PSCLK(output_clk), .PSCLK(output_clk),
.PSDONE(), .PSDONE(),
// locked output // locked output
.LOCKED(locked), .LOCKED(mmcm_locked),
// input status // input status
.CLKINSTOPPED(), .CLKINSTOPPED(),
.CLKFBSTOPPED(), .CLKFBSTOPPED(),
@@ -183,17 +206,113 @@ clk_mmcm_inst (
); );
BUFG BUFG
output_clk_bufg_inst ( mmcm_clk_bufg_inst (
.I(output_clk_mmcm), .I(mmcm_clk_out),
.O(output_clk) .O(mmcm_clk_bufg)
); );
BUFG BUFG
output_offset_clk_bufg_inst ( mmcm_offset_clk_bufg_inst (
.I(output_offset_clk_mmcm), .I(mmcm_offset_clk_out),
.O(output_offset_clk) .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 endmodule
`resetall `resetall