📄 phy_calib.v
字号:
// don't use DLYRST to reset value of IDELAY after reset. Need to change this // if we want to allow user to recalibrate after initial reset always @(posedge clkdiv) if (rstdiv) begin dlyrst_dq <= 1'b1; dlyrst_dqs <= 1'b1; end else begin dlyrst_dq <= 1'b0; dlyrst_dqs <= 1'b0; end always @(posedge clkdiv) begin if (rstdiv) begin dlyce_dq <= 'b0; dlyinc_dq <= 'b0; dlyce_dqs <= 'b0; dlyinc_dqs <= 'b0; end else begin dlyce_dq <= 'b0; dlyinc_dq <= 'b0; dlyce_dqs <= 'b0; dlyinc_dqs <= 'b0; // stage 1 cal: change only specified DQ if (cal1_dlyce_dq) begin if (SIM_ONLY == 0) begin dlyce_dq[count_dq] <= 1'b1; dlyinc_dq[count_dq] <= cal1_dlyinc_dq; end else begin // if simulation, then calibrate only first DQ, apply results // to all DQs (i.e. assume delay on all DQs is the same) for (i = 0; i < DQ_WIDTH; i = i + 1) begin: loop_sim_dq_dly dlyce_dq[i] <= 1'b1; dlyinc_dq[i] <= cal1_dlyinc_dq; end end end else if (cal2_dlyce_dqs) begin // stage 2 cal: change DQS and all corresponding DQ's if (SIM_ONLY == 0) begin dlyce_dqs[count_dqs] <= 1'b1; dlyinc_dqs[count_dqs] <= cal2_dlyinc_dqs; for (i = 0; i < DQ_PER_DQS; i = i + 1) begin: loop_dqs_dly dlyce_dq[(DQ_PER_DQS*count_dqs)+i] <= 1'b1; dlyinc_dq[(DQ_PER_DQS*count_dqs)+i] <= cal2_dlyinc_dqs; end end else begin for (i = 0; i < DQS_WIDTH; i = i + 1) begin: loop_sim_dqs_dly // if simulation, then calibrate only first DQS dlyce_dqs[i] <= 1'b1; dlyinc_dqs[i] <= cal2_dlyinc_dqs; for (j = 0; j < DQ_PER_DQS; j = j + 1) begin: loop_sim_dq_dqs_dly dlyce_dq[(DQ_PER_DQS*i)+j] <= 1'b1; dlyinc_dq[(DQ_PER_DQS*i)+j] <= cal2_dlyinc_dqs; end end end end else if (DEBUG_EN != 0) begin // DEBUG: allow user to vary IDELAY tap settings // For DQ IDELAY taps if (dbg_idel_up_all || dbg_idel_down_all || dbg_sel_all_idel_dq) begin for (x = 0; x < DQ_WIDTH; x = x + 1) begin: loop_dly_inc_dq dlyce_dq[x] <= dbg_idel_up_all | dbg_idel_down_all | dbg_idel_up_dq | dbg_idel_down_dq; dlyinc_dq[x] <= dbg_idel_up_all | dbg_idel_up_dq; end end else begin dlyce_dq <= 'b0; dlyce_dq[dbg_sel_idel_dq] <= dbg_idel_up_dq | dbg_idel_down_dq; dlyinc_dq[dbg_sel_idel_dq] <= dbg_idel_up_dq; end // For DQS IDELAY taps if (dbg_idel_up_all || dbg_idel_down_all || dbg_sel_all_idel_dqs) begin for (x = 0; x < DQS_WIDTH; x = x + 1) begin: loop_dly_inc_dqs dlyce_dqs[x] <= dbg_idel_up_all | dbg_idel_down_all | dbg_idel_up_dqs | dbg_idel_down_dqs; dlyinc_dqs[x] <= dbg_idel_up_all | dbg_idel_up_dqs; end end else begin dlyce_dqs <= 'b0; dlyce_dqs[dbg_sel_idel_dqs] <= dbg_idel_up_dqs | dbg_idel_down_dqs; dlyinc_dqs[dbg_sel_idel_dqs] <= dbg_idel_up_dqs; end end end end // GATE synchronization is handled directly by Stage 4 calibration FSM always @(posedge clkdiv) if (rstdiv) begin dlyrst_gate <= {DQS_WIDTH{1'b1}}; dlyce_gate <= {DQS_WIDTH{1'b0}}; dlyinc_gate <= {DQS_WIDTH{1'b0}}; end else begin dlyrst_gate <= {DQS_WIDTH{1'b0}}; dlyce_gate <= {DQS_WIDTH{1'b0}}; dlyinc_gate <= {DQS_WIDTH{1'b0}}; if (cal4_dlyrst_gate) begin if (SIM_ONLY == 0) dlyrst_gate[count_gate] <= 1'b1; else for (i = 0; i < DQS_WIDTH; i = i + 1) begin: loop_gate_sim_dly_rst dlyrst_gate[i] <= 1'b1; end end if (cal4_dlyce_gate) begin if (SIM_ONLY == 0) begin dlyce_gate[count_gate] <= 1'b1; dlyinc_gate[count_gate] <= cal4_dlyinc_gate; end else begin // if simulation, then calibrate only first gate for (i = 0; i < DQS_WIDTH; i = i + 1) begin: loop_gate_sim_dly dlyce_gate[i] <= 1'b1; dlyinc_gate[i] <= cal4_dlyinc_gate; end end end else if (DEBUG_EN != 0) begin // DEBUG: allow user to vary IDELAY tap settings if (dbg_idel_up_all || dbg_idel_down_all || dbg_sel_all_idel_gate) begin for (x = 0; x < DQS_WIDTH; x = x + 1) begin: loop_dly_inc_gate dlyce_gate[x] <= dbg_idel_up_all | dbg_idel_down_all | dbg_idel_up_gate | dbg_idel_down_gate; dlyinc_gate[x] <= dbg_idel_up_all | dbg_idel_up_gate; end end else begin dlyce_gate <= {DQS_WIDTH{1'b0}}; dlyce_gate[dbg_sel_idel_gate] <= dbg_idel_up_gate | dbg_idel_down_gate; dlyinc_gate[dbg_sel_idel_gate] <= dbg_idel_up_gate; end end end //*************************************************************************** // signal to tell calibration state machines to wait and give IDELAY time to // settle after it's value is changed (both time for IDELAY chain to settle, // and for settled output to propagate through ISERDES). For general use: use // for any calibration state machines that modify any IDELAY. // Should give at least enough time for IDELAY output to settle (technically // for V5, this should be "glitchless" when IDELAY taps are changed, so don't // need any time here), and also time for new data to propagate through both // ISERDES and the "RDD" MUX + associated pipelining // For now, give very "generous" delay - doesn't really matter since only // needed during calibration //*************************************************************************** // determine if calibration polarity has changed always @(posedge clkdiv) cal2_rd_data_sel_r <= cal2_rd_data_sel; assign cal2_rd_data_sel_edge = |(cal2_rd_data_sel ^ cal2_rd_data_sel_r); // combine requests to modify any of the IDELAYs into one. Also when second // stage capture "edge" polarity is changed (IDELAY isn't changed in this // case, but use the same counter to stall cal logic) assign dlyce_or = cal1_dlyce_dq | cal2_dlyce_dqs | cal2_rd_data_sel_edge | cal4_dlyce_gate | cal4_dlyrst_gate; // SYN_NOTE: Can later recode to avoid combinational path assign idel_set_wait = dlyce_or || (idel_set_cnt != IDEL_SET_VAL); always @(posedge clkdiv) if (rstdiv) idel_set_cnt <= 4'b0000; else if (dlyce_or) idel_set_cnt <= 4'b0000; else if (idel_set_cnt != IDEL_SET_VAL) idel_set_cnt <= idel_set_cnt + 1; // generate request to PHY_INIT logic to issue auto-refresh // used by certain states to force prech/auto-refresh part way through // calibration to avoid a tRAS violation (which will happen if that // stage of calibration lasts long enough). This signal must meet the // following requirements: (1) only transition from 0->1 when the refresh // request is needed, (2) stay at 1 and only transition 1->0 when // CALIB_REF_DONE is asserted always @(posedge clkdiv) if (rstdiv) calib_ref_req <= 1'b0; else calib_ref_req <= cal1_ref_req | cal2_ref_req | cal4_ref_req; // stage 1 calibration requests auto-refresh every 4 bits generate if (DQ_BITS < 2) begin: gen_cal1_refresh_dq_lte4 assign cal1_refresh = 1'b0; end else begin: gen_cal1_refresh_dq_gt4 assign cal1_refresh = (next_count_dq[1:0] == 2'b00); end endgenerate //*************************************************************************** // First stage calibration: DQ-DQS // Definitions: // edge: detected when varying IDELAY, and current capture data != prev // capture data // valid bit window: detected when current capture data == prev capture // data for more than half the bit time // starting conditions for DQS-DQ phase: // case 1: when DQS starts somewhere in rising edge bit window, or // on the right edge of the rising bit window. // case 2: when DQS starts somewhere in falling edge bit window, or // on the right edge of the falling bit window. // Algorithm Description: // 1. Increment DQ IDELAY until we find an edge. // 2. While we're finding the first edge, note whether a valid bit window // has been detected before we found an edge. If so, then figure out if // this is the rising or falling bit window. If rising, then our starting // DQS-DQ phase is case 1. If falling, then it's case 2. If don't detect // a valid bit window, then we must have started on the edge of a window. // Need to wait until later on to decide which case we are. // - Store FIRST_EDGE IDELAY value // 3. Now look for second edge. // 4. While we're finding the second edge, note whether valid bit window // is detected. If so, then use to, along with results from (2) to figure // out what the starting case is. If in rising bit window, then we're in // case 2. If falling, then case 1. // - Store SECOND_EDGE IDELAY value // NOTES: // a. Finding two edges allows us to calculate the bit time (although // not the "same" bit time polarity - need to investigate this // more). // b. If we run out of taps looking for the second edge, then the bit // time must be too long (>= 2.5ns, and DQS-DQ starting phase must be // case 1). // 5. Calculate absolute amount to delay DQ as: // If second edge found, and case 1: // - DQ_IDELAY = FIRST_EDGE - 0.5*(SECOND_EDGE - FIRST_EDGE) // If second edge found, and case 2: // - DQ_IDELAY = SECOND_EDGE - 0.5*(SECOND_EDGE - FIRST_EDGE) // If second edge not found, then need to make an approximation on // how much to shift by (should be okay, because we have more timing // margin): // - DQ_IDELAY = FIRST_EDGE - 0.5 * (bit_time) // NOTE: Does this account for either case 1 or case 2????? // NOTE: It's also possible even when we find the second edge, that // to instead just use half the bit time to subtract from either // FIRST or SECOND_EDGE. Finding the actual bit time (which is // what (SECOND_EDGE - FIRST_EDGE) is, is slightly more accurate, // since it takes into account duty cycle distortion. // 6. Repeat for each DQ in current DQS set. //*************************************************************************** //***************************************************************** // for first stage calibration - used for checking if DQS is aligned to the // particular DQ, such that we're in the data valid window. Basically, this // is one giant MUX. // = [falling data, rising data] // = [0, 1] = rising DQS aligned in proper (rising edge) bit window // = [1, 0] = rising DQS aligned in wrong (falling edge) bit window // = [0, 0], or [1,1] = in uncertain region between windows //***************************************************************** // SYN_NOTE: May have to split this up into multiple levels - MUX can get // very wide - as wide as the data bus width always @(posedge clkdiv) cal1_data_chk_r <= {rd_data_fall_1x_r[next_count_dq], rd_data_rise_1x_r[next_count_dq]}; //***************************************************************** // determine when an edge has occurred - when either the current value // is different from the previous latched value or when the DATA_CHK // outputs are the same (rare, but indicates that we're at an edge) // This is only valid when the IDELAY output and propagation of the // data through the capture flops has had a chance to settle out. //***************************************************************** // write CAL1_DETECT_EDGE and CAL1_DETECT_STABLE in such a way that // if X's are captured on the bus during functional simulation, that // the logic will register this as an edge detected. Do this to allow // use of this HDL with Denali memory models (Denali models drive DQ // to X's on both edges of the data valid window to simulate jitter) // This is only done for functional simulation purposes. **Should not**
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -