mirror of
https://github.com/aolofsson/oh.git
synced 2025-01-17 20:02:53 +08:00
91f8e3db5a
- 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.
224 lines
6.3 KiB
Verilog
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:
|
|
|