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.
283 lines
8.0 KiB
Verilog
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:
|
|
|