📄 phy_calib.v
字号:
// make the final synthesized logic more complicated, but it does make // the HDL harder to understand b/c we have to "phrase" the logic // slightly differently than when not worrying about X's always @(*) begin // no edge found if: (1) we have recorded prev edge, and rise // data == fall data, (2) we haven't yet recorded prev edge, but // rise/fall data is equal to either [0,1] or [1,0] (i.e. rise/fall // data isn't either X's, or [0,0] or [1,1], which indicates we're // in the middle of an edge, since normally rise != fall data for stg1) if ((cal1_data_chk_last_valid && (cal1_data_chk_r == cal1_data_chk_last)) || (!cal1_data_chk_last_valid && ((cal1_data_chk_r == 2'b01) || (cal1_data_chk_r == 2'b10)))) cal1_detect_edge = 1'b0; else cal1_detect_edge = 1'b1; end always @(*) begin // assert if we've found a region where data valid window is stable // over consecutive IDELAY taps, and either rise/fall = [1,0], or [0,1] if ((cal1_data_chk_last_valid && (cal1_data_chk_r == cal1_data_chk_last)) && ((cal1_data_chk_r == 2'b01) || (cal1_data_chk_r == 2'b10))) cal1_detect_stable <= 1'b1; else cal1_detect_stable <= 1'b0; end //***************************************************************** // Find valid window: keep track of how long we've been in the same data // window. If it's been long enough, then declare that we've found a valid // window. Also returns whether we found a rising or falling window (only // valid when found_window is asserted) //***************************************************************** always @(posedge clkdiv) begin if (cal1_state == CAL1_INIT) begin cal1_window_cnt <= 4'b0000; cal1_found_window <= 1'b0; cal1_found_rising <= 1'bx; end else if (!cal1_data_chk_last_valid) begin // if we haven't stored a previous value of CAL1_DATA_CHK (or it got // invalidated because we detected an edge, and are now looking for the // second edge), then make sure FOUND_WINDOW deasserted on following // clock edge (to avoid finding a false window immediately after finding // an edge). Note that because of jitter, it's possible to not find an // edge at the end of the IDELAY increment settling time, but to find an // edge on the next clock cycle (e.g. during CAL1_FIND_FIRST_EDGE) cal1_window_cnt <= 4'b0000; cal1_found_window <= 1'b0; cal1_found_rising <= 1'bx; end else if (((cal1_state == CAL1_FIRST_EDGE_IDEL_WAIT) || (cal1_state == CAL1_SECOND_EDGE_IDEL_WAIT)) && !idel_set_wait) begin // while finding the first and second edges, see if we can detect a // stable bit window (occurs over MIN_WIN_SIZE number of taps). If // so, then we're away from an edge, and can conclusively determine the // starting DQS-DQ phase. if (cal1_detect_stable) begin cal1_window_cnt <= cal1_window_cnt + 1; if (cal1_window_cnt == MIN_WIN_SIZE-1) begin cal1_found_window <= 1'b1; if (cal1_data_chk_r == 2'b01) cal1_found_rising <= 1'b1; else cal1_found_rising <= 1'b0; end end else begin // otherwise, we're not in a data valid window, reset the window // counter, and indicate we're not currently in window. This should // happen by design at least once after finding the first edge. cal1_window_cnt <= 4'b0000; cal1_found_window <= 1'b0; cal1_found_rising <= 1'bx; end end end //***************************************************************** // keep track of edge tap counts found, and whether we've // incremented to the maximum number of taps allowed //***************************************************************** always @(posedge clkdiv) if (cal1_state == CAL1_INIT) begin cal1_idel_tap_limit_hit <= 1'b0; cal1_idel_tap_cnt <= 6'b000000; end else if (cal1_dlyce_dq) begin if (cal1_dlyinc_dq) begin cal1_idel_tap_cnt <= cal1_idel_tap_cnt + 1; cal1_idel_tap_limit_hit <= (cal1_idel_tap_cnt == 6'b111110); end else begin cal1_idel_tap_cnt <= cal1_idel_tap_cnt - 1; cal1_idel_tap_limit_hit <= 1'b0; end end //***************************************************************** // Pipeline for better timing - amount to decrement by if second // edge not found //***************************************************************** // if only one edge found (possible for low frequencies), then: // 1. Assume starting DQS-DQ phase has DQS in DQ window (aka "case 1") // 2. We have to decrement by (63 - first_edge_tap_cnt) + (BIT_TIME_TAPS/2) // (i.e. decrement by 63-first_edge_tap_cnt to get to right edge of // DQ window. Then decrement again by (BIT_TIME_TAPS/2) to get to center // of DQ window. // 3. Clamp the above value at 63 to ensure we don't underflow IDELAY // (note: clamping happens in the CAL1 state machine) always @(posedge clkdiv) cal1_low_freq_idel_dec <= (7'b0111111 - {1'b0, cal1_first_edge_tap_cnt}) + (BIT_TIME_TAPS/2); //***************************************************************** // Keep track of max taps used during stage 1, use this to limit // the number of taps that can be used in stage 2 //***************************************************************** always @(posedge clkdiv) if (rstdiv) begin cal1_idel_max_tap <= 6'b000000; cal1_idel_max_tap_we <= 1'b0; end else begin // pipeline latch enable for CAL1_IDEL_MAX_TAP - we have plenty // of time, tap count gets updated, then dead cycles waiting for // IDELAY output to settle cal1_idel_max_tap_we <= (cal1_idel_max_tap < cal1_idel_tap_cnt); // record maximum # of taps used for stg 1 cal if ((cal1_state == CAL1_DONE) && cal1_idel_max_tap_we) cal1_idel_max_tap <= cal1_idel_tap_cnt; end //***************************************************************** always @(posedge clkdiv) if (rstdiv) begin calib_done[0] <= 1'b0; calib_done_tmp[0] <= 1'bx; calib_err[0] <= 1'b0; count_dq <= {DQ_BITS{1'b0}}; next_count_dq <= {DQ_BITS{1'b0}}; cal1_bit_time_tap_cnt <= 6'bxxxxxx; cal1_data_chk_last <= 2'bxx; cal1_data_chk_last_valid <= 1'bx; cal1_dlyce_dq <= 1'b0; cal1_dlyinc_dq <= 1'b0; cal1_dqs_dq_init_phase <= 1'bx; cal1_first_edge_done <= 1'bx; cal1_found_second_edge <= 1'bx; cal1_first_edge_tap_cnt <= 6'bxxxxxx; cal1_idel_dec_cnt <= 7'bxxxxxxx; cal1_idel_inc_cnt <= 6'bxxxxxx; cal1_ref_req <= 1'b0; cal1_state <= CAL1_IDLE; end else begin // default values for all "pulse" outputs cal1_ref_req <= 1'b0; cal1_dlyce_dq <= 1'b0; cal1_dlyinc_dq <= 1'b0; case (cal1_state) CAL1_IDLE: begin count_dq <= {DQ_BITS{1'b0}}; next_count_dq <= {DQ_BITS{1'b0}}; if (calib_start[0]) begin calib_done[0] <= 1'b0; calib_done_tmp[0] <= 1'b0; cal1_state <= CAL1_INIT; end end CAL1_INIT: begin cal1_data_chk_last_valid <= 1'b0; cal1_found_second_edge <= 1'b0; cal1_dqs_dq_init_phase <= 1'b0; cal1_idel_inc_cnt <= 6'b000000; cal1_state <= CAL1_INC_IDEL; end // increment DQ IDELAY so that either: (1) DQS starts somewhere in // first rising DQ window, or (2) DQS starts in first falling DQ // window. The amount to shift is frequency dependent (and is either // precalculated by MIG or possibly adjusted by the user) CAL1_INC_IDEL: if ((cal1_idel_inc_cnt == DQ_IDEL_INIT) && !idel_set_wait) begin cal1_state <= CAL1_FIND_FIRST_EDGE; end else if (cal1_idel_inc_cnt != DQ_IDEL_INIT) begin cal1_idel_inc_cnt <= cal1_idel_inc_cnt + 1; cal1_dlyce_dq <= 1'b1; cal1_dlyinc_dq <= 1'b1; end // look for first edge CAL1_FIND_FIRST_EDGE: begin // Determine DQS-DQ phase if we can detect enough of a valid window if (cal1_found_window) cal1_dqs_dq_init_phase <= ~cal1_found_rising; // find first edge - if found then record position if (cal1_detect_edge) begin cal1_state <= CAL1_FOUND_FIRST_EDGE_WAIT; cal1_first_edge_done <= 1'b0; cal1_first_edge_tap_cnt <= cal1_idel_tap_cnt; cal1_data_chk_last_valid <= 1'b0; end else begin // otherwise, store the current value of DATA_CHK, increment // DQ IDELAY, and compare again cal1_state <= CAL1_FIRST_EDGE_IDEL_WAIT; cal1_data_chk_last <= cal1_data_chk_r; // avoid comparing against DATA_CHK_LAST for previous iteration cal1_data_chk_last_valid <= 1'b1; cal1_dlyce_dq <= 1'b1; cal1_dlyinc_dq <= 1'b1; end end // wait for DQ IDELAY to settle CAL1_FIRST_EDGE_IDEL_WAIT: if (!idel_set_wait) cal1_state <= CAL1_FIND_FIRST_EDGE; // delay state between finding first edge and looking for second // edge. Necessary in order to invalidate CAL1_FOUND_WINDOW before // starting to look for second edge CAL1_FOUND_FIRST_EDGE_WAIT: cal1_state <= CAL1_FIND_SECOND_EDGE; // Try and find second edge CAL1_FIND_SECOND_EDGE: begin // When looking for 2nd edge, first make sure data stabilized (by // detecting valid data window) - needed to avoid false edges if (cal1_found_window) begin cal1_first_edge_done <= 1'b1; cal1_dqs_dq_init_phase <= cal1_found_rising; end // exit if run out of taps to increment if (cal1_idel_tap_limit_hit) cal1_state <= CAL1_CALC_IDEL; else begin // found second edge, record the current edge count if (cal1_first_edge_done && cal1_detect_edge) begin cal1_state <= CAL1_CALC_IDEL; cal1_found_second_edge <= 1'b1; cal1_bit_time_tap_cnt <= cal1_idel_tap_cnt - cal1_first_edge_tap_cnt + 1; end else begin cal1_state <= CAL1_SECOND_EDGE_IDEL_WAIT; cal1_data_chk_last <= cal1_data_chk_r; cal1_data_chk_last_valid <= 1'b1; cal1_dlyce_dq <= 1'b1; cal1_dlyinc_dq <= 1'b1; end end end // wait for DQ IDELAY to settle, then store ISERDES output CAL1_SECOND_EDGE_IDEL_WAIT: if (!idel_set_wait) cal1_state <= CAL1_FIND_SECOND_EDGE; // pipeline delay state to calculate amount to decrement DQ IDELAY // NOTE: We're calculating the amount to decrement by, not the // absolute setting for DQ IDELAY CAL1_CALC_IDEL: begin // if two edges found if (cal1_found_second_edge) // case 1: DQS was in DQ window to start with. First edge found // corresponds to left edge of DQ rising window. Backup by 1.5*BT // NOTE: In this particular case, it is possible to decrement // "below 0" in the case where DQS delay is less than 0.5*BT, // need to limit decrement to prevent IDELAY tap underflow if (!cal1_dqs_dq_init_phase) cal1_idel_dec_cnt <= {1'b0, cal1_bit_time_tap_cnt} + {1'b0, (cal1_bit_time_tap_cnt >> 1)}; // case 2: DQS was in wrong DQ window (in DQ falling window). // First edge found is right edge of DQ rising window. Second // edge is left edge of DQ rising window. Backup by 0.5*BT else cal1_idel_dec_cnt <= {1'b0, (cal1_bit_time_tap_cnt >> 1)}; // if only one edge found - assume will always be case 1 - DQS in // DQS window. Case 2 only possible if path delay on DQS > 5ns
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -