📄 phy_calib.v
字号:
else cal1_idel_dec_cnt <= cal1_low_freq_idel_dec; cal1_state <= CAL1_DEC_IDEL; end // decrement DQ IDELAY for final adjustment CAL1_DEC_IDEL: // once adjustment is complete, we're done with calibration for // this DQ, now return to IDLE state and repeat for next DQ // Add underflow protection for case of 2 edges found and DQS // starting in DQ window (see comments for above state) - note we // have to take into account delayed value of CAL1_IDEL_TAP_CNT - // gets updated one clock cycle after CAL1_DLYCE/INC_DQ if ((cal1_idel_dec_cnt == 7'b0000000) || (cal1_dlyce_dq && (cal1_idel_tap_cnt == 6'b000001))) begin cal1_state <= CAL1_DONE; // stop when all DQ's calibrated, or DQ[0] cal'ed (for sim) if ((count_dq == DQ_WIDTH-1) || (SIM_ONLY != 0)) calib_done_tmp[0] <= 1'b1; else // need for VHDL simulation to prevent out-of-index error next_count_dq <= count_dq + 1; end else begin // keep decrementing until final tap count reached cal1_idel_dec_cnt <= cal1_idel_dec_cnt - 1; cal1_dlyce_dq <= 1'b1; cal1_dlyinc_dq <= 1'b0; end // delay state to allow count_dq and DATA_CHK to point to the next // DQ bit (allows us to potentially begin checking for an edge on // next DQ right away). CAL1_DONE: if (!idel_set_wait) begin count_dq <= next_count_dq; if (calib_done_tmp[0]) begin calib_done[0] <= 1'b1; cal1_state <= CAL1_IDLE; end else begin // request auto-refresh after every 8-bits calibrated to // avoid tRAS violation if (cal1_refresh) begin cal1_ref_req <= 1'b1; if (calib_ref_done) cal1_state <= CAL1_INIT; end else // if no need this time for refresh, proceed to next bit cal1_state <= CAL1_INIT; end end endcase end //*************************************************************************** // Second stage calibration: DQS-FPGA Clock // Algorithm Description: // 1. Assumes a training pattern that will produce a pattern oscillating at // half the core clock frequency each on rise and fall outputs, and such // that rise and fall outputs are 180 degrees out of phase from each // other. Note that since the calibration logic runs at half the speed // of the interface, expect that data sampled with the slow clock always // to be constant (either always = 1, or = 0, and rise data != fall data) // unless we cross the edge of the data valid window // 2. Start by setting RD_DATA_SEL = 0. This selects the rising capture data // sync'ed to rising edge of core clock, and falling edge data sync'ed // to falling edge of core clock // 3. Start looking for an edge. An edge is defined as either: (1) a // change in capture value or (2) an invalid capture value (e.g. rising // data != falling data for that same clock cycle). // 4. If an edge is found, go to step (6). If edge hasn't been found, then // set RD_DATA_SEL = 1, and try again. // 5. If no edge is found, then increment IDELAY and return to step (3) // 6. If an edge if found, then invert RD_DATA_SEL - this shifts the // capture point 180 degrees from the edge of the window (minus duty // cycle distortion, delay skew between rising/falling edge capture // paths, etc.) // 7. If no edge is found by CAL2_IDEL_TAP_LIMIT (= 63 - # taps used for // stage 1 calibration), then decrement IDELAY (without reinverting // RD_DATA_SEL) by CAL2_IDEL_TAP_LIMIT/2. This guarantees we at least // have CAL2_IDEL_TAP_LIMIT/2 of slack both before and after the // capture point (not optimal, but best we can do not having found an // of the window). This happens only for very low frequencies. // 8. Repeat for each DQS group. // NOTE: Step 6 is not optimal. A better (and perhaps more complicated) // algorithm might be to find both edges of the data valid window (using // the same polarity of RD_DATA_SEL), and then decrement to the midpoint. //*************************************************************************** // RD_DATA_SEL should be tagged with FROM-TO (multi-cycle) constraint in // UCF file to relax timing. This net is "pseudo-static" (after value is // changed, FSM waits number of cycles before using the output). // Note that we are adding one clock cycle of delay (to isolate it from // the other logic CAL2_RD_DATA_SEL feeds), make sure FSM waits long // enough to compensate (by default it does, it waits a few cycles more // than minimum # of clock cycles) genvar rd_i; generate for (rd_i = 0; rd_i < DQS_WIDTH; rd_i = rd_i+1) begin: gen_rd_data_sel FD u_ff_rd_data_sel ( .Q (rd_data_sel[rd_i]), .C (clkdiv), .D (cal2_rd_data_sel[rd_i]) ) /* synthesis syn_preserve = 1 */ /* synthesis syn_replicate = 0 */; end endgenerate //***************************************************************** // Max number of taps used for stg2 cal dependent on number of taps // used for stg1 (give priority to stg1 cal - let it use as many // taps as it needs - the remainder of the IDELAY taps can be used // by stg2) //***************************************************************** always @(posedge clkdiv) cal2_idel_tap_limit <= 6'b111111 - cal1_idel_max_tap; //***************************************************************** // second stage calibration uses readback pattern of "1100" (i.e. // 1st rising = 1, 1st falling = 1, 2nd rising = 0, 2nd falling = 0) // only look at the first bit of each DQS group //***************************************************************** // deasserted when captured data has changed since IDELAY was // incremented, or when we're right on the edge (i.e. rise data = // fall data). assign cal2_detect_edge = ((((rdd_rise_q1 != cal2_rd_data_rise_last_pos) || (rdd_fall_q1 != cal2_rd_data_fall_last_pos)) && cal2_rd_data_last_valid_pos && (!cal2_curr_sel)) || (((rdd_rise_q1 != cal2_rd_data_rise_last_neg) || (rdd_fall_q1 != cal2_rd_data_fall_last_neg)) && cal2_rd_data_last_valid_neg && (cal2_curr_sel)) || (rdd_rise_q1 != rdd_fall_q1)); //***************************************************************** // keep track of edge tap counts found, and whether we've // incremented to the maximum number of taps allowed // NOTE: Assume stage 2 cal always increments the tap count (never // decrements) when searching for edge of the data valid window //***************************************************************** always @(posedge clkdiv) if (cal2_state == CAL2_INIT) begin cal2_idel_tap_limit_hit <= 1'b0; cal2_idel_tap_cnt <= 6'b000000; end else if (cal2_dlyce_dqs) begin cal2_idel_tap_cnt <= cal2_idel_tap_cnt + 1; cal2_idel_tap_limit_hit <= (cal2_idel_tap_cnt == cal2_idel_tap_limit - 1); end //***************************************************************** always @(posedge clkdiv) if (rstdiv) begin calib_done[1] <= 1'b0; calib_done_tmp[1] <= 1'bx; calib_err[1] <= 1'b0; count_dqs <= 'b0; next_count_dqs <= 'b0; cal2_dlyce_dqs <= 1'b0; cal2_dlyinc_dqs <= 1'b0; cal2_idel_dec_cnt <= 6'bxxxxxx; cal2_rd_data_last_valid_neg <= 1'bx; cal2_rd_data_last_valid_pos <= 1'bx; cal2_rd_data_sel <= 'b0; cal2_ref_req <= 1'b0; cal2_state <= CAL2_IDLE; end else begin cal2_ref_req <= 1'b0; cal2_dlyce_dqs <= 1'b0; cal2_dlyinc_dqs <= 1'b0; case (cal2_state) CAL2_IDLE: begin count_dqs <= 'b0; next_count_dqs <= 'b0; if (calib_start[1]) begin cal2_rd_data_sel <= {DQS_WIDTH{1'b0}}; calib_done[1] <= 1'b0; calib_done_tmp[1] <= 1'b0; cal2_state <= CAL2_INIT; end end // Pass through this state every time we calibrate a new DQS group CAL2_INIT: begin cal2_curr_sel <= 1'b0; cal2_rd_data_last_valid_neg <= 1'b0; cal2_rd_data_last_valid_pos <= 1'b0; cal2_state <= CAL2_INIT_IDEL_WAIT; end // Stall state only used if calibration run more than once. Can take // this state out if design never runs calibration more than once. // We need this state to give time for MUX'ed data to settle after // resetting RD_DATA_SEL CAL2_INIT_IDEL_WAIT: if (!idel_set_wait) cal2_state <= CAL2_FIND_EDGE_POS; // Look for an edge - first check "positive-edge" stage 2 capture CAL2_FIND_EDGE_POS: begin // if found an edge, then switch to the opposite edge stage 2 // capture and we're done - no need to decrement the tap count, // since switching to the opposite edge will shift the capture // point by 180 degrees if (cal2_detect_edge) begin cal2_curr_sel <= 1'b1; cal2_state <= CAL2_DONE; // set all DQS groups to be the same for simulation if (SIM_ONLY != 0) cal2_rd_data_sel <= {DQS_WIDTH{1'b1}}; else cal2_rd_data_sel[count_dqs] <= 1'b1; if ((count_dqs == DQS_WIDTH-1) || (SIM_ONLY != 0)) calib_done_tmp[1] <= 1'b1; else // MIG 2.1: Fix for simulation out-of-bounds error when // SIM_ONLY=0, and DQS_WIDTH=(power of 2) (needed for VHDL) next_count_dqs <= count_dqs + 1; end else begin // otherwise, invert polarity of stage 2 capture and look for // an edge with opposite capture clock polarity cal2_curr_sel <= 1'b1; cal2_rd_data_sel[count_dqs] <= 1'b1; cal2_state <= CAL2_FIND_EDGE_IDEL_WAIT_POS; cal2_rd_data_rise_last_pos <= rdd_rise_q1; cal2_rd_data_fall_last_pos <= rdd_fall_q1; cal2_rd_data_last_valid_pos <= 1'b1; end end // Give time to switch from positive-edge to negative-edge second // stage capture (need time for data to filter though pipe stages) CAL2_FIND_EDGE_IDEL_WAIT_POS: if (!idel_set_wait) cal2_state <= CAL2_FIND_EDGE_NEG; // Look for an edge - check "negative-edge" stage 2 capture CAL2_FIND_EDGE_NEG: if (cal2_detect_edge) begin cal2_curr_sel <= 1'b0; cal2_state <= CAL2_DONE; // set all DQS groups to be the same for simulation if (SIM_ONLY != 0) cal2_rd_data_sel <= {DQS_WIDTH{1'b0}}; else cal2_rd_data_sel[count_dqs] <= 1'b0; if ((count_dqs == DQS_WIDTH-1) || (SIM_ONLY != 0)) calib_done_tmp[1] <= 1'b1; else // MIG 2.1: Fix for simulation out-of-bounds error when // SIM_ONLY=0, and DQS_WIDTH=(power of 2) (needed for VHDL) next_count_dqs <= count_dqs + 1; end else if (cal2_idel_tap_limit_hit) begin // otherwise, if we've run out of taps, then immediately // backoff by half # of taps used - that's our best estimate // for optimal calibration point. Doesn't matter whether which // polarity we're using for capture (we don't know which one is // best to use) cal2_idel_dec_cnt <= {1'b0, cal2_idel_tap_limit[5:1]}; cal2_state <= CAL2_DEC_IDEL; if ((count_dqs == DQS_WIDTH-1) || (SIM_ONLY != 0)) calib_done_tmp[1] <= 1'b1; else // MIG 2.1: Fix for simulation out-of-bounds error when // SIM_ONLY=0, and DQS_WIDTH=(power of 2) (needed for VHDL) next_count_dqs <= count_dqs + 1; end else begin // otherwise, increment IDELAY, and start looking for edge again cal2_curr_sel <= 1'b0; cal2_rd_data_sel[count_dqs] <= 1'b0; cal2_state <= CAL2_FIND_EDGE_IDEL_WAIT_NEG; cal2_rd_data_rise_last_neg <= rdd_rise_q1; cal2_rd_data_fall_last_neg <= rdd_fall_q1; cal2_rd_data_last_valid_neg <=
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -