1
0
mirror of https://github.com/aolofsson/oh.git synced 2025-01-17 20:02:53 +08:00
oh/elink/hdl/erx_clocks.v
Andreas Olofsson 91f8e3db5a Complete redesign of the TX
- After finding the bug in the reference model and wasting countless hours going back and forth with FPGA timing optimization and bug tweaks, I realized that the  design was fundementally broken. The decision to use two clock domains (high speed) and low speed was correct from the beginning. The FPGA is dreadfully slow, (you definitely don't want to do much logic at 300MHz...), but the handoff between tclk and tclk_div4 was too complicated. The puzzle of having to respond to wait quickly, covering the corner cases, and meeting timing was just too ugly.
- The "new" design goes back to the method of using the high speed logic only for doing a "dumb" parallel to serial converter and preparing all the necessary signals in the low speed domain.
- This feel A LOT cleaner and the it already passes basic tests with the chip reference and the loopback after less than 3 hours of redesign work!
- The TX meets timing but there is still some work to do with wait pushback testing.
2015-11-24 01:12:07 -05:00

224 lines
6.3 KiB
Verilog

`include "elink_constants.v"
module erx_clocks (/*AUTOARG*/
// Outputs
rx_lclk, rx_lclk_div4, rx_active, erx_nreset, erx_io_nreset,
// Inputs
sys_nreset, soft_reset, tx_active, sys_clk, rx_clkin
);
`ifdef TARGET_SIM
parameter RCW = 4; // reset counter width
`else
parameter RCW = 8; // reset counter width
`endif
//Frequency Settings (Mhz)
parameter FREQ_RXCLK = 300;
parameter FREQ_IDELAY = 200;
parameter RXCLK_PHASE = 0; //270; //-90 deg rxclk phase shift
parameter PLL_VCO_MULT = 4; //RX
//Don't touch these! (derived parameters)
localparam real RXCLK_PERIOD = 1000.000000 / FREQ_RXCLK; //? Why is the period needed here?
localparam integer IREF_DIVIDE = PLL_VCO_MULT * FREQ_RXCLK / FREQ_IDELAY;
localparam integer RXCLK_DIVIDE = PLL_VCO_MULT; //1:1
//Input clock, reset, config interface
input sys_nreset; // active low system reset (hw)
input soft_reset; // rx enable signal (sw)
input tx_active; // tx active input
//Main input clocks
input sys_clk; // always on input clk cclk/TX MMCM
input rx_clkin; // input clk for RX only PLL
//RX Clocks
output rx_lclk; // rx high speed clock for DDR IO
output rx_lclk_div4; // rx slow clock for logic
//Reset
output rx_active; // rx active
output erx_nreset; // reset for rx core logic
output erx_io_nreset; // io reset (synced to high speed clock)
//############
//# WIRES
//############
//Idelay controller
wire idelay_reset;
wire idelay_ready; //ignore this?
wire idelay_ref_clk;
//pll outputs
wire rx_lclk_pll;
wire rx_lclk_div4_pll;
wire idelay_ref_clk_pll;
//PLL
wire rx_lclk_fb;
wire rx_nreset_in;
//###########################
// RESET STATE MACHINE
//###########################
reg [RCW:0] reset_counter = 'b0; //works b/c of free running counter!
reg heartbeat;
wire pll_locked_sync;
reg [2:0] reset_state;
wire pll_reset;
reg rx_nreset;
wire pll_locked;
//Reset
assign rx_nreset_in = sys_nreset & tx_active;
//wrap around counter that generates a 1 cycle heartbeat
always @ (posedge sys_clk)
begin
reset_counter[RCW-1:0] <= reset_counter[RCW-1:0]+1'b1;
heartbeat <= ~(|reset_counter[RCW-1:0]);
end
`define RX_RESET_ALL 3'b000
`define RX_START_PLL 3'b001
`define RX_ACTIVE 3'b010
//Reset sequence state machine
always @ (posedge sys_clk or negedge rx_nreset_in)
if(!rx_nreset_in)
reset_state[2:0] <= `RX_RESET_ALL;
else if(heartbeat)
case(reset_state[2:0])
`RX_RESET_ALL :
if(~soft_reset)
reset_state[2:0] <= `RX_START_PLL;
`RX_START_PLL :
if(pll_locked_sync & idelay_ready)
reset_state[2:0] <= `RX_ACTIVE;
`RX_ACTIVE:
if(soft_reset)
reset_state[2:0] <= `RX_RESET_ALL; //stay there until next reset
endcase // case (reset_state[2:0])
assign pll_reset = (reset_state[2:0]==`RX_RESET_ALL);
assign idelay_reset = (reset_state[2:0]==`RX_RESET_ALL);
//Reset for RX (pipeline to improve timing)
always @ (posedge sys_clk)
rx_nreset <= ~(reset_state[2:0] != `RX_ACTIVE);
//active indicator
assign rx_active = (reset_state[2:0] == `RX_ACTIVE);
//#############################
//#RESET SYNCING
//#############################
rsync rsync_io (// Outputs
.nrst_out (erx_io_nreset),
// Inputs
.clk (rx_lclk),
.nrst_in (rx_nreset)
);
rsync rsync_core (// Outputs
.nrst_out (erx_nreset),
// Inputs
.clk (rx_lclk_div4),
.nrst_in (rx_nreset)
);
`ifdef TARGET_XILINX
//###########################
// PLL RX
//###########################
PLLE2_ADV
#(
.BANDWIDTH("OPTIMIZED"),
.CLKFBOUT_MULT(PLL_VCO_MULT),
.CLKFBOUT_PHASE(0.0),
.CLKIN1_PERIOD(RXCLK_PERIOD),
.CLKOUT0_DIVIDE(128),
.CLKOUT1_DIVIDE(128),
.CLKOUT2_DIVIDE(128),
.CLKOUT3_DIVIDE(IREF_DIVIDE), // idelay ref clk
.CLKOUT4_DIVIDE(RXCLK_DIVIDE), // rx_lclk
.CLKOUT5_DIVIDE(RXCLK_DIVIDE*4), // rx_lclk_div4
.CLKOUT0_DUTY_CYCLE(0.5),
.CLKOUT1_DUTY_CYCLE(0.5),
.CLKOUT2_DUTY_CYCLE(0.5),
.CLKOUT3_DUTY_CYCLE(0.5),
.CLKOUT4_DUTY_CYCLE(0.5),
.CLKOUT5_DUTY_CYCLE(0.5),
.CLKOUT0_PHASE(0.0),
.CLKOUT1_PHASE(0.0),
.CLKOUT2_PHASE(0.0),
.CLKOUT3_PHASE(0.0),
.CLKOUT4_PHASE(0.0),//RXCLK_PHASE
.CLKOUT5_PHASE(0.0),//RXCLK_PHASE/4
.DIVCLK_DIVIDE(1.0),
.REF_JITTER1(0.01),
.STARTUP_WAIT("FALSE")
) pll_rx
(
.CLKOUT0(),
.CLKOUT1(),
.CLKOUT2(),
.CLKOUT3(idelay_ref_clk_pll),
.CLKOUT4(rx_lclk_pll),
.CLKOUT5(rx_lclk_div4_pll),
.PWRDWN(1'b0),
.RST(pll_reset),
.CLKFBIN(rx_lclk_fb),
.CLKFBOUT(rx_lclk_fb),
.CLKIN1(rx_clkin),
.CLKIN2(1'b0),
.CLKINSEL(1'b1),
.DADDR(7'b0),
.DCLK(1'b0),
.DEN(1'b0),
.DI(16'b0),
.DWE(1'b0),
.DRDY(),//??
.DO(), //??
.LOCKED(pll_locked)
);
//Clock network
BUFG i_lclk_bufg (.I(rx_lclk_pll), .O(rx_lclk)); //300Mhz
BUFG i_lclk_div4_bufg (.I(rx_lclk_div4_pll), .O(rx_lclk_div4)); //(300Mhz/4)
BUFG i_idelay__bufg (.I(idelay_ref_clk_pll),.O(idelay_ref_clk));//idelay ctrl clock
//two clock synchronizer for lock signal
dsync dsync (.dout (pll_locked_sync),
.clk (sys_clk),
.din (pll_locked)
);
//###########################
// Idelay controller
//###########################
(* IODELAY_GROUP = "IDELAY_GROUP" *) // Group name for IDELAYCTRL
IDELAYCTRL idelayctrl_inst
(
.RDY(idelay_ready), // check ready flag in reset sequence?
.REFCLK(idelay_ref_clk),//200MHz clk (78ps tap delay)
.RST(idelay_reset));
`endif // `ifdef TARGET_XILINX
endmodule // eclocks
// Local Variables:
// verilog-library-directories:("." "../../common/hdl")
// End: