1
0
mirror of https://github.com/aolofsson/oh.git synced 2025-01-30 02:32:53 +08:00
oh/xilibs/hdl/PLLE2_ADV.v
Andreas Olofsson 6e93d0399a Hold hack..
-This needs to be resolved! Currently there is a simulation problem with the PLL and IDDR circuit, likely due to the clock divider. Amazingly enough the circuit works in sim and FPGA, but there was some redundant logic hiding this.
-Need to take a closer look at this to get the non-blocking/blocking right in PLL and CLKDIV
2016-01-19 16:01:15 -05:00

204 lines
5.5 KiB
Verilog

/* verilator lint_off STMTDLY */
/* verilator lint_off WIDTH */
module PLLE2_ADV #(
parameter BANDWIDTH = "OPTIMIZED",
parameter integer CLKFBOUT_MULT = 5,
parameter real CLKFBOUT_PHASE = 0.000,
parameter real CLKIN1_PERIOD = 0.000,
parameter real CLKIN2_PERIOD = 0.000,
parameter integer CLKOUT0_DIVIDE = 1,
parameter real CLKOUT0_DUTY_CYCLE = 0.500,
parameter real CLKOUT0_PHASE = 0.000,
parameter integer CLKOUT1_DIVIDE = 1,
parameter real CLKOUT1_DUTY_CYCLE = 0.500,
parameter real CLKOUT1_PHASE = 0.000,
parameter integer CLKOUT2_DIVIDE = 1,
parameter real CLKOUT2_DUTY_CYCLE = 0.500,
parameter real CLKOUT2_PHASE = 0.000,
parameter integer CLKOUT3_DIVIDE = 1,
parameter real CLKOUT3_DUTY_CYCLE = 0.500,
parameter real CLKOUT3_PHASE = 0.000,
parameter integer CLKOUT4_DIVIDE = 1,
parameter real CLKOUT4_DUTY_CYCLE = 0.500,
parameter real CLKOUT4_PHASE = 0.000,
parameter integer CLKOUT5_DIVIDE = 1,
parameter real CLKOUT5_DUTY_CYCLE = 0.500,
parameter real CLKOUT5_PHASE = 0.000,
parameter COMPENSATION = "ZHOLD",
parameter integer DIVCLK_DIVIDE = 1,
parameter [0:0] IS_CLKINSEL_INVERTED = 1'b0,
parameter [0:0] IS_PWRDWN_INVERTED = 1'b0,
parameter [0:0] IS_RST_INVERTED = 1'b0,
parameter real REF_JITTER1 = 0.010,
parameter real REF_JITTER2 = 0.010,
parameter STARTUP_WAIT = "FALSE"
)(
output CLKOUT0,
output CLKOUT1,
output CLKOUT2,
output CLKOUT3,
output CLKOUT4,
output CLKOUT5,
output [15:0] DO,
output DRDY,
output LOCKED,
output CLKFBOUT,
input CLKFBIN,
input CLKIN1,
input CLKIN2,
input CLKINSEL,
input [6:0] DADDR,
input DCLK,
input DEN,
input [15:0] DI,
input DWE,
input PWRDWN,
input RST
);
//#LOCAL DERIVED PARAMETERS
localparam real VCO_PERIOD = (CLKIN1_PERIOD * DIVCLK_DIVIDE) / CLKFBOUT_MULT;
localparam real CLK0_DELAY = VCO_PERIOD * CLKOUT0_DIVIDE * (CLKOUT0_PHASE/360);
localparam real CLK1_DELAY = VCO_PERIOD * CLKOUT1_DIVIDE * (CLKOUT1_PHASE/360);
localparam real CLK2_DELAY = VCO_PERIOD * CLKOUT2_DIVIDE * (CLKOUT2_PHASE/360);
localparam real CLK3_DELAY = VCO_PERIOD * CLKOUT3_DIVIDE * (CLKOUT3_PHASE/360);
localparam real CLK4_DELAY = VCO_PERIOD * CLKOUT4_DIVIDE * (CLKOUT4_PHASE/360);
localparam real CLK5_DELAY = VCO_PERIOD * CLKOUT5_DIVIDE * (CLKOUT5_PHASE/360);
localparam phases = CLKFBOUT_MULT / DIVCLK_DIVIDE;
//########################################################################
//# POR
//########################################################################
//ugly POR reset
reg POR;
initial
begin
POR=1'b1;
#1
POR=1'b0;
end
//async reset
wire reset;
assign reset = POR | RST;
//########################################################################
//# CLOCK MULTIPLIER
//########################################################################
//TODO: implement DIVCLK_DIVIDE
//
integer j;
reg [2*phases-1:0] delay;
always @ (CLKIN1 or reset)
if(reset)
for(j=0; j<(2*phases); j=j+1)
delay[j] <= 1'b0;
else
for(j=0; j<(2*phases); j=j+1)
delay[j] <= #(CLKIN1_PERIOD*j/(2*phases)) CLKIN1;
reg [(phases)-1:0] clk_comb;
always @ (delay)
begin
for(j=0; j<(phases); j=j+1)
clk_comb[j] <= ~reset & delay[2*j] & ~delay[2*j+1];
end
reg vco_clk;
integer k;
always @*
begin
vco_clk = 1'b0;
for(k=0; k<(phases); k=k+1)
vco_clk = vco_clk | clk_comb[k];
end
//##############
//#DIVIDERS
//##############
wire [3:0] DIVCFG[5:0];
wire [5:0] CLKOUT_DIV;
assign DIVCFG[0] = $clog2(CLKOUT0_DIVIDE);
assign DIVCFG[1] = $clog2(CLKOUT1_DIVIDE);
assign DIVCFG[2] = $clog2(CLKOUT2_DIVIDE);
assign DIVCFG[3] = $clog2(CLKOUT3_DIVIDE);
assign DIVCFG[4] = $clog2(CLKOUT4_DIVIDE);
assign DIVCFG[5] = $clog2(CLKOUT5_DIVIDE);
genvar i;
generate for(i=0; i<6; i=i+1)
begin : gen_clkdiv
oh_clockdiv clkdiv (// Outputs
.clkout (CLKOUT_DIV[i]),
// Inputs
.clkin (vco_clk),
.divcfg (DIVCFG[i]),
.reset (reset));
end
endgenerate
reg [5:0] CLKOUT_DIV_LOCK;
`ifdef TARGET_VERILATOR
initial
begin
$display("ERROR: PLL divider not implemented");
end
`else
always @ (posedge (CLKIN1 & vco_clk) or negedge (CLKIN1&~vco_clk))
begin
CLKOUT_DIV_LOCK[5:0] = CLKOUT_DIV[5:0];
end
`endif
//##############
//#SUB PHASE DELAY
//##############
reg CLKOUT0;
reg CLKOUT1;
reg CLKOUT2;
reg CLKOUT3;
reg CLKOUT4;
reg CLKOUT5;
always @ (CLKOUT_DIV_LOCK)
begin
CLKOUT0 = #(CLK0_DELAY) ~reset & CLKOUT_DIV_LOCK[0];
CLKOUT1 = #(CLK1_DELAY) ~reset & CLKOUT_DIV_LOCK[1];
CLKOUT2 = #(CLK2_DELAY) ~reset & CLKOUT_DIV_LOCK[2];
CLKOUT3 = #(CLK3_DELAY) ~reset & CLKOUT_DIV_LOCK[3];
CLKOUT4 = #(CLK4_DELAY) ~reset & CLKOUT_DIV_LOCK[4];
CLKOUT5 = #(CLK5_DELAY) ~reset & CLKOUT_DIV_LOCK[5];
end
//##############
//#DUMMY DRIVES
//##############
assign CLKFBOUT=CLKIN1;
//###########################
//#SANITY CHECK LOCK COUNTER
//############################
localparam LCW=4;
reg [LCW-1:0] lock_counter;
always @ (posedge CLKIN1 or posedge reset)
if(reset)
lock_counter[LCW-1:0] <= {(LCW){1'b1}};
else if(~LOCKED)
lock_counter[LCW-1:0] <= lock_counter[LCW-1:0] - 1'b1;
assign LOCKED = ~(|lock_counter[LCW-1:0]);
endmodule // PLLE2_ADV
// Local Variables:
// verilog-library-directories:("." "../../common/hdl")
// End: