1
0
mirror of https://github.com/aolofsson/oh.git synced 2025-01-17 20:02:53 +08:00
oh/elink/hdl/etx_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

283 lines
8.0 KiB
Verilog

`include "elink_constants.v"
module etx_clocks (/*AUTOARG*/
// Outputs
tx_lclk_io, tx_lclk90, tx_lclk_div4, cclk_p, cclk_n, etx_nreset,
etx_io_nreset, chip_nreset, tx_active,
// Inputs
sys_nreset, soft_reset, sys_clk
);
`ifdef TARGET_SIM
parameter RCW = 4; // reset counter width
`else
parameter RCW = 8; // reset counter width
`endif
//Frequency Settings (Mhz)
parameter FREQ_SYSCLK = 100;
parameter FREQ_TXCLK = 300;
parameter FREQ_CCLK = 600;
parameter TXCLK_PHASE = 90; //txclk phase shift
//Don't touch these! (derived parameters)
parameter MMCM_VCO_MULT = 12; //TX + CCLK
localparam real SYSCLK_PERIOD = 1000.000000 / FREQ_SYSCLK;
localparam integer TXCLK_DIVIDE = MMCM_VCO_MULT * FREQ_SYSCLK / FREQ_TXCLK;
localparam integer CCLK_DIVIDE = MMCM_VCO_MULT * FREQ_SYSCLK / FREQ_CCLK;
//Input clock, reset, config interface
input sys_nreset; // por reset (hw)
input soft_reset; // rx enable signal (sw)
//Main input clocks
input sys_clk; // always on input clk cclk/TX MMCM
//TX Clocks
output tx_lclk_io; // tx clock for high speeed IO
output tx_lclk90; // tx output clock shifted by 90 degrees
output tx_lclk_div4; // tx slow clock for logic
//Epiphany "free running" clock
output cclk_p, cclk_n;
//Reset
output etx_nreset; // reset for tx core logic
output etx_io_nreset; // io reset (synced to high speed clock)
output chip_nreset; // reset fpr Epiphany chip
output tx_active; // enable for rx path (ensures active clock)
//############
//# WIRES
//############
//CCLK
wire cclk_reset;
wire cclk_mmcm;
wire cclk_bufio;
wire cclk_oddr;
//TX
wire tx_lclk_mmcm;
wire tx_lclk90_mmcm;
wire tx_lckl_div4_mmcm;
//MMCM & PLL
wire cclk_fb;
//wire cclk_fb_out;
wire lclk_fb_i;
wire pll_reset;
wire mmcm_locked;
reg mmcm_locked_reg;
reg mmcm_locked_sync;
wire lclk_locked;
wire tx_nreset;
wire mmcm_reset;
wire tx_lclk_div4_mmcm;
//###########################
// RESET STATE MACHINE
//###########################
reg [RCW:0] reset_counter = 'b0; //works b/c of free running counter!
reg heartbeat;
reg [2:0] reset_state;
reg [1:0] reset_pipe_lclkb;
reg [1:0] reset_pipe_lclk_div4b;
//wrap around counter that generates a 1 cycle heartbeat
//free running counter...
always @ (posedge sys_clk)
begin
reset_counter[RCW-1:0] <= reset_counter[RCW-1:0]+1'b1;
heartbeat <= ~(|reset_counter[RCW-1:0]);
end
//two clock synchronizer
always @ (posedge sys_clk)
begin
mmcm_locked_reg <= mmcm_locked;
mmcm_locked_sync <= mmcm_locked_reg;
end
`define TX_RESET_ALL 3'b000
`define TX_START_CCLK 3'b001
`define TX_STOP_CCLK 3'b010
`define TX_DEASSERT_RESET 3'b011
`define TX_HOLD_IT 3'b100 //???
`define TX_ACTIVE 3'b101
//Reset sequence state machine
always @ (posedge sys_clk or negedge sys_nreset)
if(!sys_nreset)
reset_state[2:0] <= `TX_RESET_ALL;
else if(heartbeat)
case(reset_state[2:0])
`TX_RESET_ALL :
if(~soft_reset)
reset_state[2:0] <= `TX_START_CCLK;
`TX_START_CCLK :
if(mmcm_locked_sync)
reset_state[2:0] <= `TX_STOP_CCLK;
`TX_STOP_CCLK :
reset_state[2:0] <= `TX_DEASSERT_RESET;
`TX_DEASSERT_RESET :
reset_state[2:0] <= `TX_HOLD_IT;
`TX_HOLD_IT :
if(mmcm_locked_sync)
reset_state[2:0] <= `TX_ACTIVE;
`TX_ACTIVE:
if(soft_reset)
reset_state[2:0] <= `TX_RESET_ALL; //stay there until nex reset
endcase // case (reset_state[2:0])
//reset mmcm (async)
assign mmcm_reset = (reset_state[2:0]==`TX_RESET_ALL) |
(reset_state[2:0]==`TX_STOP_CCLK) |
(reset_state[2:0]==`TX_DEASSERT_RESET)
;
//reset chip (active low)
assign chip_nreset = (reset_state[2:0]==`TX_DEASSERT_RESET) |
(reset_state[2:0]==`TX_HOLD_IT) |
(reset_state[2:0]==`TX_ACTIVE);
//reset the elink
assign tx_nreset = ~(reset_state[2:0] != `TX_ACTIVE);
assign tx_active = (reset_state[2:0] == `TX_ACTIVE);
//#############################
//#RESET SYNCING
//#############################
rsync rsync_io (// Outputs
.nrst_out (etx_io_nreset),
// Inputs
.clk (tx_lclk_io),
.nrst_in (tx_nreset));
rsync rsync_core (// Outputs
.nrst_out (etx_nreset),
// Inputs
.clk (tx_lclk_div4),
.nrst_in (tx_nreset));
`ifdef TARGET_XILINX
//###########################
// MMCM FOR TXCLK + CCLK
//###########################
MMCME2_ADV
#(
.BANDWIDTH("OPTIMIZED"),
.CLKFBOUT_MULT_F(MMCM_VCO_MULT),
.CLKFBOUT_PHASE(0.0),
.CLKIN1_PERIOD(SYSCLK_PERIOD),
.CLKOUT0_DIVIDE_F(CCLK_DIVIDE), //cclk_c
.CLKOUT1_DIVIDE(TXCLK_DIVIDE), //tx_lclk
.CLKOUT2_DIVIDE(TXCLK_DIVIDE), //tx_lclk90
.CLKOUT3_DIVIDE(TXCLK_DIVIDE*4), //tx_lclk_div4
.CLKOUT4_DIVIDE(128), //N/A
.CLKOUT5_DIVIDE(128), //N/A
.CLKOUT6_DIVIDE(128), //N/A
.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),
.CLKOUT6_DUTY_CYCLE(0.5),
.CLKOUT0_PHASE(0.0),
.CLKOUT1_PHASE(0.0),
.CLKOUT2_PHASE(TXCLK_PHASE),
.CLKOUT3_PHASE(0.0),
.CLKOUT4_PHASE(0.0),
.CLKOUT5_PHASE(0.0),
.CLKOUT6_PHASE(0.0),
.DIVCLK_DIVIDE(1.0),
.REF_JITTER1(0.01),
.STARTUP_WAIT("FALSE")
) mmcm_cclk
(
.CLKOUT0(cclk_mmcm),
.CLKOUT0B(),
.CLKOUT1(tx_lclk_mmcm),
.CLKOUT1B(),
.CLKOUT2(tx_lclk90_mmcm),//goes directly to IO
.CLKOUT2B(),
.CLKOUT3(tx_lclk_div4_mmcm),
.CLKOUT3B(),
.CLKOUT4(),
.CLKOUT5(),
.CLKOUT6(),
.PWRDWN(1'b0),
.RST(mmcm_reset), //reset
.CLKFBIN(cclk_fb),
.CLKFBOUT(cclk_fb), //feedback clock
.CLKFBOUTB(), //inverted output feedback clock
.CLKIN1(sys_clk), //input clock
.CLKIN2(1'b0),
.CLKINSEL(1'b1),
.DADDR(7'b0),
.DCLK(1'b0),
.DEN(1'b0),
.DI(16'b0),
.DWE(1'b0),
.DRDY(),
.DO(),
.LOCKED(mmcm_locked), //locked indicator
.PSCLK(1'b0),
.PSEN(1'b0),
.PSDONE(),
.PSINCDEC(1'b0),
.CLKFBSTOPPED(),
.CLKINSTOPPED()
);
//Tx clock buffers
BUFG i_lclk_bufg (.I(tx_lclk_mmcm), .O(tx_lclk_io)); //300MHz
BUFG i_lclk_div4_bufg (.I(tx_lclk_div4_mmcm),.O(tx_lclk_div4)); //75MHz
BUFG i_lclk90_bufg (.I(tx_lclk90_mmcm), .O(tx_lclk90)); //300MHz 90deg clock
// BUFG i_fb_buf (.I(cclk_fb_out), .O(cclk_fb_in)); //FB
//###########################
// CCLK
//###########################
//CCLK bufio
BUFIO bufio_cclk(.O(cclk_bufio), .I(cclk_mmcm));
//CCLK oddr
ODDR #(.DDR_CLK_EDGE ("SAME_EDGE"), .SRTYPE("ASYNC"))
oddr_lclk (
.Q (cclk_oddr),
.C (cclk_bufio),
.CE (1'b1),
.D1 (1'b1),
.D2 (1'b0),
.R (1'b0),
.S (1'b0));
//CCLK differential buffer
OBUFDS cclk_obuf (.O (cclk_p),
.OB (cclk_n),
.I (cclk_oddr)
);
`else // !`ifdef TARGET_XILINX
assign cclk_p = sys_clk;
assign cclk_n = sys_clk;
assign tx_lclk_io = sys_clk;
assign tx_lclk_div4 = sys_clk;
assign tx_lclk90 = sys_clk;
`endif // `ifdef TARGET_XILINX
endmodule // eclocks
// Local Variables:
// verilog-library-directories:("." "../../common/hdl")
// End: