from __future__ import print_function ''' # Copyright (C) 2015, Elphel.inc. # Class to measure and adjust I/O delays # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . @author: Andrey Filippov @copyright: 2015 Elphel, Inc. @license: GPLv3.0+ @contact: andrey@elphel.coml @deffield updated: Updated ''' __author__ = "Andrey Filippov" __copyright__ = "Copyright 2015, Elphel, Inc." __license__ = "GPL" __version__ = "3.0+" __maintainer__ = "Andrey Filippov" __email__ = "andrey@elphel.com" __status__ = "Development" import sys import pickle #import x393_mem #x393_pio_sequences #from import_verilog_parameters import VerilogParameters from x393_mem import X393Mem #from x393_axi_control_status import X393AxiControlStatus import x393_axi_control_status from x393_pio_sequences import X393PIOSequences from x393_mcntrl_timing import X393McntrlTiming from x393_mcntrl_buffers import X393McntrlBuffers from verilog_utils import split_delay,combine_delay,NUM_FINE_STEPS, convert_w32_to_mem16,convert_mem16_to_w32 import get_test_dq_dqs_data # temporary to test processing import x393_lma import time import vrlg #NUM_FINE_STEPS= 5 NUM_DLY_STEPS =NUM_FINE_STEPS * 32 # =160 DQI_KEY='dqi' DQO_KEY='dqo' ODD_KEY='odd' class X393McntrlAdjust(object): DRY_MODE= True # True DEBUG_MODE=1 x393_mem=None x393_axi_tasks=None #x393X393AxiControlStatus x393_pio_sequences=None x393_mcntrl_timing=None x393_mcntrl_buffers=None verbose=1 adjustment_state={} def __init__(self, debug_mode=1,dry_mode=True): self.DEBUG_MODE= debug_mode self.DRY_MODE= dry_mode self.x393_mem= X393Mem(debug_mode,dry_mode) # self.x393_axi_tasks= X393AxiControlStatus(debug_mode,dry_mode) self.x393_axi_tasks= x393_axi_control_status.X393AxiControlStatus(debug_mode,dry_mode) self.x393_pio_sequences= X393PIOSequences(debug_mode,dry_mode) self.x393_mcntrl_timing= X393McntrlTiming(debug_mode,dry_mode) self.x393_mcntrl_buffers= X393McntrlBuffers(debug_mode,dry_mode) # self.__dict__.update(VerilogParameters.__dict__["_VerilogParameters__shared_state"]) # Add verilog parameters to the class namespace try: self.verbose=vrlg.VERBOSE except: pass #keep as command def format_dq_to_verilog(self, estr): """ Convert dq delays list to the form to paste to the Verilog parameters code quoted string, such as: "[['0xd9', '0xdb', '0xdc', '0xd4', '0xe0', '0xda', '0xd4', '0xd8'], ['0xdc', '0xe0', '0xf1', '0xdc', '0xe0', '0xdc', '0xdc', '0xdc']]" Returns a pair of strings to paste """ se=eval(estr) # now a list of list of strings for l in se: for i,v in enumerate(l): l[i]=int(v,16) for lane in range(2): print("lane%d = 64'h"%lane,end="") for i in range(len(se[lane])): print("%02x"%se[lane][-i-1],end="") print() def missing_dqs_notused(self, rd_blk, quiet=False): """ Suspect missing final DQS puls(es) during write if last written burst matches previous one - block of 32-bit data read from DDR3 device - no output Returns True if missing DQS pulse is suspected """ if (not rd_blk) or (len(rd_blk) <8 ): return False for i in range(-4,0): if rd_blk[i] != rd_blk[i-4]: break else: if not quiet: print ("End of the block repeats 2 last 8-bursts, insufficient number of trailing DQS pulses is suspected:") print("\n%03x:"%(len(rd_blk)-8),end=" ") for i in range(len(rd_blk)-8,len(rd_blk)): print("%08x"%rd_blk[i],end=" ") print("\n") return True return False def set_phase_with_refresh(self, # check result for not None phase, quiet=1): """ Set specified phase and matching cmda_odelay while temporarily turning off refresh @param phase phase to set, signed short @param quiet reduce output @return cmda_odelay linear value or None if there is no valid cmda output delay for this phase """ if not "cmda_bspe" in self.adjustment_state: raise Exception ("No cmda_odelay data is available. 'adjust_cmda_odelay 0 1 0.1 3' command should run first.") dly_steps=self.x393_mcntrl_timing.get_dly_steps() numPhaseSteps= int(dly_steps['SDCLK_PERIOD']/dly_steps['PHASE_STEP']+0.5) cmda_odly_data=self.adjustment_state['cmda_bspe'][phase % numPhaseSteps] if (not cmda_odly_data): # phase is invalid for CMDA return None cmda_odly_lin=cmda_odly_data['ldly'] self.x393_axi_tasks.enable_refresh(0) self.x393_mcntrl_timing.axi_set_phase(phase,quiet=quiet) self.x393_mcntrl_timing.axi_set_cmda_odelay(combine_delay(cmda_odly_lin),quiet=quiet) self.x393_axi_tasks.enable_refresh(1) return cmda_odly_lin def set_phase_delays(self, phase, inp_period='A', out_period='A', refresh=True, delays_phase=None, # if None - use global quiet=1): """ Set clock phase and all I/O delays optimal for this phase @param phase value to set @param inp_period - period branch for DQ inputs: E, N, L or A @param out_period - period branch for DQ outputs: E, N, L or A @param refresh - turn refresh OFF before and ON after changing the delays and phase @param quiet - reduce output @return True on success, False on invalid phase """ num_addr=vrlg.ADDRESS_NUMBER num_banks=3 rslt_names=("early","nominal","late") enl_in=None enl_out=None enl_in_used=None enl_out_used=None try: inp=str(inp_period)[0].upper() except: print ("Invalid parameter =%s"%(str(inp_period))) return False try: outp=str(out_period)[0].upper() except: print ("Invalid parameter =%s"%(str(out_period))) return False if inp == 'A': enl_in=rslt_names else: for k in rslt_names: if inp == k[0].upper(): enl_in=(k,) break else: print ("Unrecognized parameter =%s"%(str(inp_period))) return False if outp == 'A': enl_out=rslt_names else: for k in rslt_names: if outp == k[0].upper(): enl_out=(k,) break else: print ("Unrecognized parameter =%s"%(str(out_period))) return False dly_steps=self.x393_mcntrl_timing.get_dly_steps() numPhaseSteps= int(dly_steps['SDCLK_PERIOD']/dly_steps['PHASE_STEP']+0.5) phase= phase % numPhaseSteps # valid for negative also, numPhaseSteps should be <=128 (now it is 112) if delays_phase == None: try: delays_phase=self.adjustment_state['delays_phase'] except: print("Delays for phases (self.adjustment_state['delays_phase']) are not set, running 'get_delays_vs_phase' command ") try: delays_phase=self.get_delays_vs_phase(filter_dqo=2, filter_dqi=2, filter_dqso=2, filter_dqsi=2, filter_cmda=2, filter_read=0, filter_write=0, filter_rsel=None, filter_wsel=None, keep_all=False, set_table=True, quiet=quiet+2) self.adjustment_state['delays_phase']=delays_phase except: print ("Failed to execute 'get_delays_vs_phase' command") return False elif quiet < 2: print ("using provided delays_phase") try: delays=delays_phase[phase] except: print("No valid delay data for phase %d is available"%(phase)) return False if quiet<1: print ("delays=",delays) try: cmda_odly=delays['cmda'] except: print("No valid CMDA output delay data for phase %d is available, it is required"%(phase)) return False try: dqs_idelays=delays['dqsi'] except: dqs_idelays=None if quiet < 2: print ("No valid DQS input delay data for phase %d is available, it will not be set"%(phase)) try: dqs_odelays=delays['dqso'] except: dqs_odelays=None if quiet < 2: print ("No valid DQS output delay data for phase %d is available, it will not be set"%(phase)) for k in enl_in: try: dq_idelays=delays['dqi'][k] enl_in_used=k break except: pass else: dq_idelays=None if quiet < 2: print ("No valid DQ input delay data for phase %d (period(s)=%s) is available, it will not be set"%(phase,str(enl_in))) for k in enl_out: try: dq_odelays=delays['dqo'][k] enl_out_used=k break except: pass else: dq_odelays=None if quiet < 2: print ("No valid DQ output delay data for phase %d (period(s)=%s) is available, it will not be set"%(phase,str(enl_out))) if quiet < 2: print ("Going to set:") print ("phase=",phase) print ('cmda_odly=',cmda_odly) print ('dqs_idelays=',dqs_idelays) print ('dqs_odelays=',dqs_odelays) print ('dq_idelays=',dq_idelays,' (',enl_in_used,')') print ('dq_odelays=',dq_odelays,' (',enl_out_used,')') print ('Memory refresh will %sbe controlled'%(('NOT ','')[refresh])) if refresh: self.x393_axi_tasks.enable_refresh(0) self.x393_mcntrl_timing.axi_set_phase(phase,quiet=quiet) if isinstance(cmda_odly,(list,tuple)): self.x393_mcntrl_timing.axi_set_address_odelay(combine_delay(cmda_odly[:num_addr]),quiet=quiet) self.x393_mcntrl_timing.axi_set_bank_odelay (combine_delay(cmda_odly[num_addr:num_addr+num_banks]),quiet=quiet) cmd_dly_data=cmda_odly[num_addr+num_banks:] while len(cmd_dly_data) < 5: cmd_dly_data.append(cmd_dly_data[-1]) # repeat last element (average address/command delay) self.x393_mcntrl_timing.axi_set_cmd_odelay (combine_delay(cmd_dly_data),quiet=quiet) # for now - same delay TODO: upgrade! # self.x393_mcntrl_timing.axi_set_cmda_odelay(combine_delay(cmda_odly),quiet=quiet) else: self.x393_mcntrl_timing.axi_set_cmda_odelay(combine_delay(cmda_odly),quiet=quiet) if refresh: self.x393_axi_tasks.enable_refresh(1) if not dqs_idelays is None: self.x393_mcntrl_timing.axi_set_dqs_idelay(combine_delay(dqs_idelays),quiet=quiet) if not dq_idelays is None: self.x393_mcntrl_timing.axi_set_dq_idelay(combine_delay(dq_idelays),quiet=quiet) if not dqs_odelays is None: self.x393_mcntrl_timing.axi_set_dqs_odelay(combine_delay(dqs_odelays),quiet=quiet) if not dq_odelays is None: self.x393_mcntrl_timing.axi_set_dq_odelay(combine_delay(dq_odelays),quiet=quiet) # if refresh: #already set # self.x393_axi_tasks.enable_refresh(1) return True def adjust_cmda_odelay(self, start_phase=0, reinits=1, #higher the number - more re-inits are used (0 - only where absolutely necessary max_phase_err=0.1, quiet=1 ): """ Find CMDA output delay for each phase value using linear interpolation for available results Use write levelling mode (refresh off) and A7 (that makes it write levelling or not). Only A7 is subject to marginal timing, other signals are kept safe. But accidentally it still can hit wrong timing - in that case memory is reset and re-initialized Sets global parameters, including self.adjustment_state['cmda_bspe'] @param start_phase initial phase to start measuremts (non-0 only for debugging dependencies) @param reinits higher the number - more re-inits are used (0 - only where absolutely necessary) @param max_phase_err maximal phase error for command and address line as a fraction of SDCLK period to consider @param quiet reduce output """ nbursts=16 start_phase &= 0xff if start_phase >=128: start_phase -= 256 # -128..+127 recover_cmda_dly_step=0x20 # subtract/add from cmda_odelay (hardware!!!) and retry (same as 20 decimal) max_lin_dly=NUM_DLY_STEPS-1 wlev_address_bit=7 wlev_max_bad=0.01 # <= OK, > bad def phase_step(phase,cmda_dly): """ Find marginal delay for address/comand lines for particular clock pahse Raises exception if failed to get write levelling data even after changing cmda delay and restarting memory device Returns a tuple of the current cmda_odelay (hardware) and a marginal one for a7 """ cmda_dly_lin=split_delay(cmda_dly) self.x393_mcntrl_timing.axi_set_phase(phase,quiet=quiet) self.x393_mcntrl_timing.axi_set_cmda_odelay(cmda_dly,quiet=quiet) wlev_rslt=self.x393_pio_sequences.write_levelling(1, nbursts, quiet+1) if wlev_rslt[2]>wlev_max_bad: # should be 0, if not - Try to recover if quiet <4: print("*** FAILED to read data in write levelling mode, restarting memory device") print(" Retrying with the same cmda_odelay value = 0x%x"%cmda_dly) self.x393_pio_sequences.restart_ddr3() wlev_rslt=self.x393_pio_sequences.write_levelling(1,nbursts, quiet) if wlev_rslt[2]>wlev_max_bad: # should be 0, if not - change delay and restart memory cmda_dly_old=cmda_dly if cmda_dly >=recover_cmda_dly_step: cmda_dly -= recover_cmda_dly_step else: cmda_dly += recover_cmda_dly_step if quiet <4: print("*** FAILED to read data in write levelling mode, restarting memory device") print(" old cmda_odelay= 0x%x, new cmda_odelay =0x%x"%(cmda_dly_old,cmda_dly)) self.x393_mcntrl_timing.axi_set_cmda_odelay(cmda_dly,quiet=quiet) self.x393_pio_sequences.restart_ddr3() wlev_rslt=self.x393_pio_sequences.write_levelling(1, nbursts, quiet) if wlev_rslt[2]>wlev_max_bad: # should be 0, if not - change delay and restart memory raise Exception("Failed to read in write levelling mode after modifying cmda_odelay, aborting") # Try twice step before giving up (was not needed so far) d_high=max_lin_dly self.x393_mcntrl_timing.axi_set_address_odelay( combine_delay(d_high), wlev_address_bit, quiet=quiet) wlev_rslt=self.x393_pio_sequences.write_levelling(1, nbursts, quiet+1) if not wlev_rslt[2]>wlev_max_bad: return (split_delay(cmda_dly),-1) # even maximal delay is not enough to make rising sdclk separate command from A7 # find marginal value of a7 delay to spoil write levelling mode d_high=max_lin_dly d_low=cmda_dly_lin while d_high > d_low: dly= (d_high + d_low)//2 self.x393_mcntrl_timing.axi_set_address_odelay(combine_delay(dly),wlev_address_bit,quiet=quiet) wlev_rslt=self.x393_pio_sequences.write_levelling(1, nbursts, quiet+1) if wlev_rslt[2] > wlev_max_bad: d_high=dly else: if d_low == dly: break d_low=dly self.x393_mcntrl_timing.axi_set_cmda_odelay(cmda_dly,quiet=quiet) return (split_delay(cmda_dly),d_low) dly_steps=self.x393_mcntrl_timing.get_dly_steps() if quiet<1: print (dly_steps) numPhaseSteps= int(dly_steps['SDCLK_PERIOD']/dly_steps['PHASE_STEP']+0.5) if (start_phase+numPhaseSteps)>128: old_start_phase=start_phase while (start_phase+numPhaseSteps)>128: start_phase -= numPhaseSteps print("Selected scan phase range (%d..%d) does not fit into -128..+127, changing it to %d..%d)"% (old_start_phase,old_start_phase+numPhaseSteps-1,start_phase,start_phase+numPhaseSteps-1)) #start_phase cmda_marg_dly=[None]*numPhaseSteps cmda_dly=0 safe_early=split_delay(recover_cmda_dly_step)/2 # print ("safe_early=%d(0x%x), recover_cmda_dly_step=%d(0x%x)"%(safe_early,safe_early,recover_cmda_dly_step,recover_cmda_dly_step)) if reinits>0: self.x393_pio_sequences.restart_ddr3() else: self.x393_axi_tasks.enable_refresh(0) # if not init, at least turn refresh off! for phase in range(start_phase,start_phase+numPhaseSteps): if quiet <3: print ("%d:"%(phase),end=" ") sys.stdout.flush() elif quiet < 5: print (".",end="") sys.stdout.flush() phase_mod=phase % numPhaseSteps dlys= phase_step(phase,cmda_dly) cmda_marg_dly[phase_mod]=dlys # [1] # Marginal delay or -1 cmda_dly = combine_delay(dlys[0]) # update if it was modified during recover # See if cmda_odelay is dangerously close - increase it (and re-init?) if dlys[1]<0: if quiet <3: print ("X",end=" ") sys.stdout.flush() elif quiet < 5: print (".",end="") sys.stdout.flush() if reinits > 1: #re-init each time failed to find delay if quiet <3: print ("\nFailed to find marginal odelay for A7 - re-initializing DDR3 with odelay=0x%x",cmda_dly) self.x393_mcntrl_timing.axi_set_cmda_odelay(cmda_dly,quiet=quiet) self.x393_pio_sequences.restart_ddr3() else: if quiet <3: print ("%d"%dlys[1],end=" ") sys.stdout.flush() elif quiet < 5: print (".",end="") sys.stdout.flush() lin_dly=split_delay(cmda_dly) if (dlys[1]-lin_dly) < safe_early: if (lin_dly > 0): lin_dly=max(0,lin_dly-2*safe_early) if (dlys[1]-lin_dly) < safe_early: lin_dly=min(max_lin_dly,lin_dly+2*safe_early) # or just add safe_early to dlys[1]? if lin_dly != split_delay(cmda_dly): cmda_dly=combine_delay(lin_dly) self.x393_mcntrl_timing.axi_set_cmda_odelay(cmda_dly,quiet=quiet) if reinits > 0: #re-init each time failed to find delay if quiet <3: print ("\nMeasured marginal delay for A7 is too close to cmda_odelay,re-initializing DDR3 with odelay=0x%x"%cmda_dly) self.x393_pio_sequences.restart_ddr3() if quiet <2: for i,d in enumerate(cmda_marg_dly): print ("%d %d %d"%(i, d[0], d[1])) elif quiet < 5: print () #find the largest positive step of cmda_marg_dly while cyclically increasing phase numValid=0 for i,d in enumerate(cmda_marg_dly): if d[1]>0: numValid += 1 if numValid < 2: raise Exception("Too few points with measured marginal CMDA odelay: %d"%numValid) maxPosSep=0 firstIndex=None for i,d in enumerate(cmda_marg_dly): if d[1]>0: for j in range(1,numPhaseSteps): d1=cmda_marg_dly[(i + j) % numPhaseSteps][1] if d1 >= 0: # valid data if (d1 - d[1]) > maxPosSep: maxPosSep = d1 - d[1] firstIndex=(i + j) % numPhaseSteps break; #now data from firstIndex to (firstIndex+numPhaseSteps)%numPhaseSteps is ~monotonic - apply linear approximation if quiet <2: print ("firstIndex=%d"%(firstIndex)) S0=0 SX=0 SY=0 SX2=0 SXY=0 for x in range(numPhaseSteps): y=cmda_marg_dly[(x+firstIndex) % numPhaseSteps][1] if y>=0: y+=0.5 S0+=1 SX+=x SY+=y SX2+=x*x SXY+=x*y # print("x=%f, index=%d, y=%f, S0=%f, SX=%f, SY=%f, SX2=%f, SXY=%f"%(x, (x+firstIndex) % numPhaseSteps, y, S0, SX, SY, SX2, SXY)) a = (SXY*S0 - SY*SX) / (SX2*S0 - SX*SX) b = (SY*SX2 - SXY*SX) / (SX2*S0 - SX*SX) if quiet < 2: print ("a=%f, b=%f"%(a,b)) # fine delay corrections fineCorr= [0.0]*5 fineCorrN=[0]*5 for x in range(numPhaseSteps): y=cmda_marg_dly[(x+firstIndex) % numPhaseSteps][1] if (y>0): i=y % 5 y+=0.5 diff=y- (a * x + b) fineCorr[i] += diff fineCorrN[i] += 1 for i in range(5): if fineCorrN[i]>0: fineCorr[i]/=fineCorrN[i] if (quiet <2): print ("fineCorr = %s"%str(fineCorr)) variantStep=-a*numPhaseSteps #how much b changes when moving over the full SDCLK period if (quiet <2): print ("Delay matching the full SDCLK period = %f"%(variantStep)) b-=a*firstIndex # recalculate b for phase=0 b_period=0 if (quiet <2): print ("a=%f, b=%f"%(a,b)) #Make b fit into 0..max_lin_dly range while (b>max_lin_dly): b-=variantStep b_period-=1 while (b<0): b+=variantStep # can end up having b>max_lin_dly - if the phase adjust by delay is lower than full period b_period+=1 if (quiet <2): print ("a=%f, b=%f, b_period=%d"%(a,b,b_period)) # Find best minimal delay (with higher SDCLK frequency delay range can exceed the period and there could # be more than one solution bestSolPerErr=[] #list ot tuples, each containing(best cmda_odelay,number of added periods,error) max_dly_err=abs(a)*max_phase_err*numPhaseSteps # maximal allowed delay error (in 160-step scale) if (quiet <2): print("Max dly error=%f"%(max_dly_err)) for phase in range (numPhaseSteps): periods=0 # b_period y=a*phase+b y0=y #find the lowest approximate solution to consider if y0 > (-max_dly_err): while (y0 >= (variantStep-max_dly_err)): y0 -= variantStep periods -= 1 else: while (y0<(-max_dly_err)): y0 += variantStep periods += 1 dly_min= max(0,int(y0-4.5)) dly_max= min(max_lin_dly,int(y0+5.5)) dly_to_try=[] for d in range(dly_min,dly_max+1): dly_to_try.append((d,periods)) if (y0<0): # add a second range to try (higher delay values y0+=variantStep periods += 1 dly_min= max(0,int(y0-4.5)) dly_max= min(max_lin_dly,int(y0+5.5)) for d in range(dly_min,dly_max+1): dly_to_try.append((d,periods)) bestDly=None bestDiff=None bestPeriods=None for dp in dly_to_try: actualDelay=dp[0]-fineCorr[dp[0] % 5] # delay corrected for the non-uniform 160-scale diff=actualDelay-(y+variantStep*dp[1]) # dp[1] - number of added/removed full periods if (bestDiff is None) or (abs(bestDiff) > abs(diff)): bestDiff = diff bestDly = dp[0] bestPeriods= dp[1] phase_rslt=() #Default, if nothing was found if not bestDiff is None: phase_rslt=(bestDly,bestPeriods,bestDiff) if (quiet <2): print ("%d: %s %s"%(phase, str(dly_to_try), str(phase_rslt)) ) bestSolPerErr.append(phase_rslt) if (quiet <2): for i in range(numPhaseSteps): # enumerate(cmda_marg_dly): d=cmda_marg_dly[i] print ("%d %d %d"%(i, d[0], d[1]),end=" ") if (bestSolPerErr[i]): print("%d %d %f"%(bestSolPerErr[i][0],bestSolPerErr[i][1],bestSolPerErr[i][2])) else: print() #numPhaseSteps #Add 180 dwegree shift (move cmda_odelay to EARLY of the marginal period_shift=0 b_center= b- 0.5*variantStep if b_center < 0: # have to move late b_center+=variantStep period_shift+=1 cmda_dly_per_err=[] for phase in range (numPhaseSteps): marg_phase=(phase+numPhaseSteps//2) % numPhaseSteps extra_periods=(phase+numPhaseSteps//2) // numPhaseSteps bspe= bestSolPerErr[marg_phase] # err_for_zero=int(round(-(phase+(b+fineCorr[0])/a))%numPhaseSteps)/(1.0*numPhaseSteps) err_for_zero=int(round(-(marg_phase+(b+fineCorr[0])/a))%numPhaseSteps)/(1.0*numPhaseSteps) if err_for_zero >0.5: err_for_zero=1.0-err_for_zero else: err_for_zero=None if bspe: cmda_dly_per_err.append({'ldly':bspe[0], 'period':bspe[1]+period_shift+extra_periods+b_period, # b_period - shift from the branch # where phase starts from the longest cmda_odelay and goes down 'err':bspe[2], 'zerr':err_for_zero }) else: cmda_dly_per_err.append({}) # No solution for this phase value rdict={"cmda_odly_a":a, "cmda_odly_b":b_center, "cmda_odly_period":period_shift+b_period, # "cmda_fine_corr":fineCorr, "cmda_bspe":cmda_dly_per_err} if (quiet <3): print("\ncmda_odelay adjustmet results:") print('cmda_odly_a: %f'%(rdict['cmda_odly_a'])) print('cmda_odly_b: %f'%(rdict['cmda_odly_b'])) print('cmda_odly_period: %d'%(rdict['cmda_odly_period'])) print('cmda_fine_corr: %s'%(rdict['cmda_fine_corr'])) print("\nPhase DLY0 MARG_A7 CMDA PERIODS*10 ERR*10 ZERR*100") for i in range(numPhaseSteps): # enumerate(cmda_marg_dly): d=cmda_marg_dly[i] print ("%d %d %d"%(i, d[0], d[1]),end=" ") if (rdict['cmda_bspe'][i]): e1=rdict['cmda_bspe'][i]['zerr'] if not e1 is None: e1="%.3f"%(100*e1) print("%d %d %f %s"%(rdict['cmda_bspe'][i]['ldly'], 10*rdict['cmda_bspe'][i]['period'], 10*rdict['cmda_bspe'][i]['err'], e1)) else: print() #TODO: Add 180 shift to get center, not marginal cmda_odelay self.adjustment_state.update(rdict) if (quiet <3): print ("rdict={") for k,v in rdict.items(): print("'%s':%s,"%(k,str(v))) print ("}") return rdict def adjust_write_levelling(self, start_phase=0, reinits=1, #higher the number - more re-inits are used (0 - only where absolutely necessary invert=0, # anti-align DQS (should be 180 degrees off from the normal one) max_phase_err=0.1, quiet=1 ): """ Find DQS output delay for each phase value Depends on adjust_cmda_odelay results """ nbursts=16 try: self.adjustment_state['cmda_bspe'] except: raise Exception("Command/Address delay calibration data is not found - please run 'adjust_cmda_odelay' command first") start_phase &= 0xff if start_phase >=128: start_phase -= 256 # -128..+127 max_lin_dly=NUM_DLY_STEPS-1 wlev_max_bad=0.01 # <= OK, > bad numPhaseSteps=len(self.adjustment_state['cmda_bspe']) if quiet < 2: print("cmda_bspe = %s"%str(self.adjustment_state['cmda_bspe'])) print ("numPhaseSteps=%d"%(numPhaseSteps)) self.x393_pio_sequences.set_write_lev(nbursts) # write leveling, 16 times (full buffer - 128) def wlev_phase_step (phase): def norm_wlev(wlev): #change results to invert wlev data if invert: return [1.0-wlev[0],1.0-wlev[1],wlev[2]] else: return wlev dly90=int(0.25*numPhaseSteps*abs(self.adjustment_state['cmda_odly_a']) + 0.5) # linear delay step ~ SDCLK period/4 cmda_odly_data=self.adjustment_state['cmda_bspe'][phase % numPhaseSteps] if (not cmda_odly_data): # phase is invalid for CMDA return None cmda_odly_lin=cmda_odly_data['ldly'] self.x393_mcntrl_timing.axi_set_phase(phase,quiet=quiet) self.x393_mcntrl_timing.axi_set_cmda_odelay(combine_delay(cmda_odly_lin),quiet=quiet) d_low=0 while d_low <= max_lin_dly: self.x393_mcntrl_timing.axi_set_dqs_odelay(combine_delay(d_low),quiet=quiet) wlev_rslt=norm_wlev(self.x393_pio_sequences.write_levelling(1, nbursts, quiet+1)) if wlev_rslt[2]>wlev_max_bad: # should be 0 - otherwise wlev did not work (CMDA?) raise Exception("Write levelling gave unespected data, aborting (may be wrong command/address delay, incorrectly initializaed") if (wlev_rslt[0] <= wlev_max_bad) and (wlev_rslt[1] <= wlev_max_bad): break d_low+=dly90 else: if quiet < 3: print ("Failed to find d_low during initial quadrant search for phase=%d (0x%x)"%(phase,phase)) return None # Now find d_high>d_low to get both bytes result above d_high= d_low+dly90 while d_high <= max_lin_dly: self.x393_mcntrl_timing.axi_set_dqs_odelay(combine_delay(d_high),quiet=quiet) wlev_rslt=norm_wlev(self.x393_pio_sequences.write_levelling(1, nbursts, quiet+1)) if wlev_rslt[2]>wlev_max_bad: # should be 0 - otherwise wlev did not work (CMDA?) raise Exception("Write levelling gave unespected data, aborting (may be wrong command/address delay, incorrectly initializaed") if (wlev_rslt[0] >= (1.0 -wlev_max_bad)) and (wlev_rslt[1] >= (1.0-wlev_max_bad)): break d_high+=dly90 else: if quiet < 3: print ("Failed to find d_high during initial quadrant search for phase=%d (0x%x)"%(phase,phase)) return None # Narrow range while both bytes fit if quiet < 2: print ("After quadrant adjust d_low=%d, d_high=%d"%(d_low,d_high)) while d_high > d_low: dly= (d_high + d_low)//2 self.x393_mcntrl_timing.axi_set_dqs_odelay(combine_delay(dly),quiet=quiet) wlev_rslt=norm_wlev(self.x393_pio_sequences.write_levelling(1, nbursts, quiet+1)) if wlev_rslt[2]>wlev_max_bad: # should be 0 - otherwise wlev did not work (CMDA?) raise Exception("Write levelling gave unespected data, aborting (may be wrong command/address delay, incorrectly initializaed") if (wlev_rslt[0] <= wlev_max_bad) and (wlev_rslt[1] <= wlev_max_bad): if d_low == dly: break d_low=dly elif (wlev_rslt[0] >= (1.0 -wlev_max_bad)) and (wlev_rslt[1] >= (1.0-wlev_max_bad)): d_high=dly else: break #mixed results # Now process each byte separately if quiet < 2: print ("After common adjust d_low=%d, d_high=%d"%(d_low,d_high)) d_low=[d_low,d_low] d_high=[d_high,d_high] for i in range(2): while d_high[i] > d_low[i]: dly= (d_high[i] + d_low[i])//2 if quiet < 1: print ("i=%d, d_low=%d, d_high=%d, dly=%d"%(i,d_low[i],d_high[i],dly)) dly01=[d_low[0],d_low[1]] dly01[i]=dly self.x393_mcntrl_timing.axi_set_dqs_odelay(combine_delay(dly01),quiet=quiet) wlev_rslt=norm_wlev(self.x393_pio_sequences.write_levelling(1, nbursts, quiet+1)) if wlev_rslt[2]>wlev_max_bad: # should be 0 - otherwise wlev did not work (CMDA?) raise Exception("Write levelling gave unespected data, aborting (may be wrong command/address delay, incorrectly initializaed") if wlev_rslt[i] <= wlev_max_bad: if d_low[i] == dly: break d_low[i]=dly else: d_high[i]=dly return d_low if (start_phase+numPhaseSteps)>128: old_start_phase=start_phase while (start_phase+numPhaseSteps)>128: start_phase -= numPhaseSteps print("Selected scan phase range (%d..%d) does not fit into -128..+127, changing it to %d..%d)"% (old_start_phase,old_start_phase+numPhaseSteps-1,start_phase,start_phase+numPhaseSteps-1)) #start_phase if reinits > 1: # Normally not needed (When started after adjust_cmda_odelay, but refresh should be off (init will do that) self.x393_pio_sequences.restart_ddr3() wlev_dqs_delays=[None]*numPhaseSteps for phase in range(start_phase,start_phase+numPhaseSteps): phase_mod=phase % numPhaseSteps if quiet <3: print ("%d(%d):"%(phase,phase_mod),end=" ") sys.stdout.flush() elif quiet < 5: print (".",end="") sys.stdout.flush() dlys=wlev_phase_step(phase) wlev_dqs_delays[phase_mod]=dlys if quiet <3: print ("%s"%str(dlys),end=" ") sys.stdout.flush() elif quiet < 5: print (".",end="") sys.stdout.flush() if quiet< 2: print() if quiet <2: for i,d in enumerate(wlev_dqs_delays): if d: print ("%d %d %d"%(i, d[0], d[1])) else: print ("%d"%(i)) elif quiet < 5: print () #find the largest positive step of cmda_marg_dly while cyclically increasing phase numValid=0 for i,d in enumerate(wlev_dqs_delays): if d: numValid += 1 if numValid < 2: raise Exception("Too few points with DQS output delay in write levelling mode: %d"%numValid) firstIndex=[None]*2 for lane in range(2): maxPosSep=0 for i,d in enumerate(wlev_dqs_delays): if d>0: for j in range(1,numPhaseSteps): d1=wlev_dqs_delays[(i + j) % numPhaseSteps] if d1: # valid data if (d1[lane] - d[lane]) > maxPosSep: maxPosSep = d1[lane] - d[lane] firstIndex[lane]=(i + j) % numPhaseSteps break; #now data from firstIndex to (firstIndex+numPhaseSteps)%numPhaseSteps is ~monotonic - apply linear approximation if quiet <2: print ("firstIndices=[%d,%d]"%(firstIndex[0],firstIndex[1])) #Linear approximate each lane a=[None]*2 b=[None]*2 for lane in range(2): S0=0 SX=0 SY=0 SX2=0 SXY=0 for x in range(numPhaseSteps): dlys=wlev_dqs_delays[(x+firstIndex[lane]) % numPhaseSteps] if dlys: y=dlys[lane]+0.5 S0+=1 SX+=x SY+=y SX2+=x*x SXY+=x*y # print("x=%f, index=%d, y=%f, S0=%f, SX=%f, SY=%f, SX2=%f, SXY=%f"%(x, (x+firstIndex) % numPhaseSteps, y, S0, SX, SY, SX2, SXY)) a[lane] = (SXY*S0 - SY*SX) / (SX2*S0 - SX*SX) b[lane] = (SY*SX2 - SXY*SX) / (SX2*S0 - SX*SX) if quiet < 2: print ("a=[%f, %f], b=[%f, %f]"%(a[0],a[1],b[0],b[1])) # fine delay corrections fineCorr= [[0.0]*5,[0.0]*5] # not [[0.0]*5]*2 ! - they will poin to the same top element fineCorrN=[[0]*5,[0]*5] # not [[0]*5]*2 ! for lane in range(2): for x in range(numPhaseSteps): dlys=wlev_dqs_delays[(x+firstIndex[lane]) % numPhaseSteps] if dlys: y=dlys[lane] i=y % 5 y+=0.5 diff=y- (a[lane] * x + b[lane]) fineCorr[lane][i] += diff fineCorrN[lane][i] += 1 # print("lane,x,y,i,diff,fc,fcn= %d, %d, %f, %d, %f, %f, %d"%(lane,x,y,i,diff,fineCorr[lane][i],fineCorrN[lane][i])) # print ("lane=%d, fineCorr=%s, fineCorrN=%s"%(lane, fineCorr[lane], fineCorrN[lane])) for i in range(5): if fineCorrN[lane][i]>0: fineCorr[lane][i]/=fineCorrN[lane][i] # print ("lane=%d, fineCorr=%s, fineCorrN=%s"%(lane, fineCorr[lane], fineCorrN[lane])) if (quiet <2): print ("fineCorr lane0 = %s"%str(fineCorr[0])) # Why ar they both the same? print ("fineCorr lane1 = %s"%str(fineCorr[1])) variantStep=[-a[0]*numPhaseSteps,-a[1]*numPhaseSteps] #how much b changes when moving over the full SDCLK period if (quiet <2): print ("Delay matching the full SDCLK period = [%f, %f]"%(variantStep[0],variantStep[1])) b_period=[None]*2 for lane in range(2): b[lane]-=a[lane]*firstIndex[lane] # recalculate b for phase=0 b_period[lane]=0 if (quiet <2): print ("a[%d]=%f, b[%d]=%f"%(lane,a[lane],lane,b[lane])) #Make b fit into 0..max_lin_dly range while (b[lane] > max_lin_dly): b[lane]-=variantStep[lane] b_period[lane]-=1 while (b[lane] < 0): b[lane] += variantStep[lane] # can end up having b>max_lin_dly - if the phase adjust by delay is lower than full period b_period[lane] += 1 if (quiet <2): print ("a[0]=%f, b[0]=%f, b_period[0]=%d"%(a[0],b[0],b_period[0])) print ("a[1]=%f, b[1]=%f, b_period[1]=%d"%(a[1],b[1],b_period[1])) # Find best minimal delay (with higher SDCLK frequency delay range can exceed the period and there could # be more than one solution bestSolPerErr=[[],[]] # pair (for two lanes) of lists ot tuples, each containing(best cmda_odelay,number of added periods,error) max_dly_err=[abs(a[0])*max_phase_err*numPhaseSteps, # maximal allowed delay error (in 160-step scale) abs(a[1])*max_phase_err*numPhaseSteps] if (quiet <2): print("Max dly error=%s"%(str(max_dly_err))) for lane in range(2): for phase in range (numPhaseSteps): periods=0 # b_period[lane] y=a[lane]*phase+b[lane] y0=y #find the lowest approximate solution to consider if y0 > (-max_dly_err[lane]): while (y0 >= (variantStep[lane]-max_dly_err[lane])): y0 -= variantStep[lane] periods -= 1 else: while (y0<(-max_dly_err[lane])): y0 += variantStep[lane] periods += 1 dly_min= max(0,int(y0-4.5)) dly_max= min(max_lin_dly,int(y0+5.5)) dly_to_try=[] for d in range(dly_min,dly_max+1): dly_to_try.append((d,periods)) if (y0<0): # add a second range to try (higher delay values y0+=variantStep[lane] periods += 1 dly_min= max(0,int(y0-4.5)) dly_max= min(max_lin_dly,int(y0+5.5)) for d in range(dly_min,dly_max+1): dly_to_try.append((d,periods)) bestDly=None bestDiff=None bestPeriods=None for dp in dly_to_try: actualDelay=dp[0]-fineCorr[lane][dp[0] % 5] # delay corrected for the non-uniform 160-scale diff=actualDelay-(y+variantStep[lane]*dp[1]) # dp[1] - number of added/removed full periods if (bestDiff is None) or (abs(bestDiff) > abs(diff)): bestDiff = diff bestDly = dp[0] bestPeriods= dp[1] phase_rslt=() #Default, if nothing was found if not bestDiff is None: phase_rslt=(bestDly,bestPeriods,bestDiff) if (quiet <2): print ("%d:%d: %s %s"%(lane, phase, str(dly_to_try), str(phase_rslt)) ) bestSolPerErr[lane].append(phase_rslt) if (quiet <2): for i in range(numPhaseSteps): # enumerate(cmda_marg_dly): d=wlev_dqs_delays[i] if d: print ("%d %d %d"%(i, d[0], d[1]),end=" ") else: print ("%d X X"%(i),end=" ") for lane in range(2): bspe=bestSolPerErr[lane][i] if bspe: print("%d %d %f"%(bspe[0], bspe[1], bspe[2]),end=" ") else: print("X X X",end=" ") print() wlev_bspe=[[],[]] for lane in range (2): for phase in range (numPhaseSteps): bspe=bestSolPerErr[lane][phase] if bspe: wlev_bspe[lane].append({'ldly':bspe[0], 'period':bspe[1]+b_period[lane], # b_period - shift from the branch # where phase starts from the longest cmda_odelay and goes down 'err':bspe[2]}) else: wlev_bspe[lane].append({}) rdict={"wlev_dqs_odly_a": a, #[,] "wlev_dqs_odly_b": b,#[,] "wlev_dqs_period": b_period, # "wlev_dqs_fine_corr": fineCorr, "wlev_dqs_bspe": wlev_bspe} if (quiet <3): print("\nwrite levelling DQS output delay adjustmet results:") print('wlev_dqs0_odly_a: %f'%(rdict['wlev_dqs_odly_a'][0])) print('wlev_dqs1_odly_a: %f'%(rdict['wlev_dqs_odly_a'][1])) print('wlev_dqs0_odly_b: %f'%(rdict['wlev_dqs_odly_b'][0])) print('wlev_dqs1_odly_b: %f'%(rdict['wlev_dqs_odly_b'][1])) print('wlev_dqs0_period: %d'%(rdict['wlev_dqs_period'][0])) print('wlev_dqs1_period: %d'%(rdict['wlev_dqs_period'][1])) print('wlev_dqs0_fine_corr: %s'%(rdict['wlev_dqs_fine_corr'][0])) print('wlev_dqs1_fine_corr: %s'%(rdict['wlev_dqs_fine_corr'][1])) print("\nPhase Measured_DQS0 Measured_DQS1 DQS0 PERIODS0*10 ERR0*10 DQS1 PERIODS1*10 ERR1*10") for i in range(numPhaseSteps): # enumerate(cmda_marg_dly): d=wlev_dqs_delays[i] if d: print ("%d %d %d"%(i, d[0], d[1]),end=" ") else: print ("%d X X"%(i),end=" ") for lane in range(2): bspe=rdict['wlev_dqs_bspe'][lane][i] # bestSolPerErr[lane][i] if bspe: print("%d %d %f"%(bspe['ldly'], 10*bspe['period'], 10*bspe['err']),end=" ") else: print("X X X",end=" ") print() self.adjustment_state.update(rdict) if (quiet <3): print ("rdict={") for k,v in rdict.items(): print("'%s':%s,"%(k,str(v))) print ("}") return rdict def measure_pattern(self, compare_prim_steps=True, # while scanning, compare this delay with 1 less by primary(not fine) step, # save None for fraction in unknown (previous -0.5, next +0.5) limit_step=0.125, # initial delay step as a fraction of the period max_phase_err=0.1, quiet=1, start_dly=0): #just to check dependence """ for each DQS input delay find 4 DQ transitions for each DQ bit, then use them to find finedelay for each of the DQS and DQ, linear coefficients (a,b) for each DQ vs DQS and asymmetry (late 0->1, early 1->0) for each of the DQ and DQS @param quiet reduce output """ nrep=8 max_lin_dly=NUM_DLY_STEPS-1#159 timing=self.x393_mcntrl_timing.get_dly_steps() #steps={'DLY_FINE_STEP': 0.01, 'DLY_STEP': 0.078125, 'PHASE_STEP': 0.022321428571428572, 'SDCLK_PERIOD': 2.5} dly_step=int(NUM_FINE_STEPS*limit_step*timing['SDCLK_PERIOD']/timing['DLY_STEP']+0.5) step180= int(NUM_FINE_STEPS*0.5* timing['SDCLK_PERIOD'] / timing['DLY_STEP'] +0.5) if quiet<2: print ("timing)=%s, dly_step=%d step180=%d"%(str(timing),dly_step,step180)) self.x393_pio_sequences.set_read_pattern(nrep+3) # set sequence once def patt_dqs_step(dqs_lin): patt_cache=[None]*(max_lin_dly+1) # cache for holding already measured delays def measure_patt(dly,force_meas=False): if (patt_cache[dly] is None) or force_meas: self.x393_mcntrl_timing.axi_set_dq_idelay(combine_delay(dly),quiet=quiet) patt= self.x393_pio_sequences.read_levelling(nrep, -1, # sel=1, # 0 - early, 1 - late read command (shift by a SDCLK period), -1 - use current sequence quiet+1) patt_cache[dly]=patt if quiet < 1: print ('measure_patt(%d,%s) - new measurement'%(dly,str(force_meas))) else: patt=patt_cache[dly] if quiet < 1: print ('measure_patt(%d,%s) - using cache'%(dly,str(force_meas))) return patt def get_sign(data,edge=None): """ edge: 0 - first 16, 1 - second 16 return -1 if all <0.5 return +1 if all >0.5 return 0 otherwise """ if edge == 0: return get_sign(data[:16]) if edge == 1: # return -get_sign(data[16:]) return get_sign(data[16:]) m1=True p1=True for d in data: m1 &= (d < 0.5) p1 &= (d > 0.5) if not (m1 or p1): break else: if m1: return -1 elif p1: return 1 return 0 rslt=[None]*16 # each bit will have [inphase][dqs_falling] self.x393_mcntrl_timing.axi_set_dqs_idelay(combine_delay(dqs_lin),quiet=quiet) d_low=[None]*2 # first - lowest when all are -+, second - when all are +- d_high=[None]*2 # first - when all are +- after -+, second - when all are -+ after +- dly=0 notLast=True needSigns=None lowGot=None highGot=None while (dly <= max_lin_dly) and notLast: notLast= dly < max_lin_dly patt=measure_patt(dly) # ,force_meas=False) signs=(get_sign(patt,0),get_sign(patt,1)) if quiet < 1: print ('dly=%d lowGot=%s, highGot=%s, signs=%s'%(dly,str(lowGot),str(highGot),str(signs))) if lowGot is None : # looking for the first good sample if (signs==(-1,1)) or (signs==(1,-1)) : if signs[0] == -1: # == (-1,1): lowGot=0 else: lowGot=1 d_low[lowGot] = dly needSigns=((1,-1),(-1,1))[lowGot] dly += step180-dly_step # almost 180 degrees else: # at least one is 0 dly += dly_step # small step if quiet < 1: print ('lowGot was None : dly=%d, lowGot=%s, needSigns=%s'%(dly,str(lowGot),str(needSigns))) elif highGot is None : # only one good sample is available so far if signs == needSigns: highGot=lowGot d_high[highGot] = dly d_low[1-lowGot] = dly needSigns=((-1,1),(1,-1))[lowGot] dly += step180-dly_step # almost 180 degrees else: dly += dly_step # small step if quiet < 1: print ('highGot was None : dly=%d, lowGot=%s, highGot=%s, needSigns=%s'%(dly,str(lowGot),str(lowGot),str(needSigns))) else: # looking for the 3-rd sample if signs == needSigns: highGot=1-highGot d_high[highGot] = dly break else: dly += dly_step # small step dly = min (dly,max_lin_dly) if highGot is None: if quiet < 3: print ("Could not find initial bounds for DQS input delay = %d d_low=%s, d_high=%s"%(dqs_lin,str(d_low),str(d_high))) return None if quiet < 2: print ("DQS input delay = %d , preliminary bounds: d_low=%s, d_high=%s"%(dqs_lin,str(d_low),str(d_high))) for inPhase in range(2): if not d_high[inPhase] is None: # Try to squeeze d_low, d_high closer to reduce scan range while d_high[inPhase]>d_low[inPhase]: dly=(d_high[inPhase] + d_low[inPhase])//2 patt=measure_patt(dly) # ,force_meas=False) signs=(get_sign(patt,0),get_sign(patt,1)) if signs==(-1,1): if inPhase: d_high[inPhase]=dly else: if d_low[inPhase]==dly: break d_low[inPhase]=dly elif signs==(1,-1): if inPhase: if d_low[inPhase]==dly: break d_low[inPhase]=dly else: d_high[inPhase]=dly else: # uncertain result break if quiet < 2: print ("DQS input delay = %d , squeezed bounds: d_low=%s, d_high=%s"%(dqs_lin,str(d_low),str(d_high))) #Improve squeezing - each limit to the last for inPhase in range(2): if not d_high[inPhase] is None: # Try to squeeze d_low first d_uncertain=d_high[inPhase] while d_uncertain > d_low[inPhase]: dly=(d_uncertain + d_low[inPhase])//2 patt=measure_patt(dly) # ,force_meas=False) signs=(get_sign(patt,0),get_sign(patt,1)) if signs==(-1,1): if inPhase: d_uncertain=dly else: if d_low[inPhase]==dly: break d_low[inPhase]=dly elif signs==(1,-1): if inPhase: if d_low[inPhase]==dly: break d_low[inPhase]=dly else: d_uncertain=dly else: # uncertain result d_uncertain=dly #now udjust upper limit while d_high[inPhase] > d_uncertain: dly=(d_high[inPhase] + d_uncertain)//2 patt=measure_patt(dly) # ,force_meas=False) signs=(get_sign(patt,0),get_sign(patt,1)) if signs==(-1,1): if inPhase: d_high[inPhase]=dly else: if d_uncertain==dly: break d_uncertain=dly elif signs==(1,-1): if inPhase: if d_uncertain==dly: break d_uncertain=dly else: d_high[inPhase]=dly else: # uncertain result if d_uncertain==dly: break d_uncertain=dly if quiet < 2: print ("DQS input delay = %d , tight squeezed bounds: d_low=%s, d_high=%s"%(dqs_lin,str(d_low),str(d_high))) # scan ranges, find closest solutions #compare_prim_steps best_dly= [[],[]] best_diff=[[],[]] for inPhase in range(2): if not d_high[inPhase] is None: # patt=None best_dly[inPhase]=[d_low[inPhase]]*32 best_diff[inPhase]=[None]*32 # for b,p in enumerate(patt): # positiveJump=((not inPhase) and (b<16)) or (inPhase and (b >= 16)) # may be 0, False, True # if positiveJump: # best_diff[inPhase].append(p-0.5) # else: # best_diff[inPhase].append(0.5-p) for dly in range(d_low[inPhase]+1,d_high[inPhase]+1): # patt_prev=patt #as measured data is cached, there is no need to specially maintain patt_prev from earlier measurement dly_prev= max(0,dly-(1,NUM_FINE_STEPS)[compare_prim_steps]) patt_prev=measure_patt(dly_prev) # ,force_meas=False) - will be stored in cache patt= measure_patt(dly) # ,force_meas=False) - will be stored in cache for b in range(32): positiveJump=((not inPhase) and (b<16)) or (inPhase and (b >= 16)) # may be 0, False, True signs=((-1,1)[patt_prev[b]>0.5],(-1,1)[patt[b]>0.5]) if (positiveJump and (signs==(-1,1))) or (not positiveJump and (signs==(1,-1))): if positiveJump: diffs_prev_this=(patt_prev[b]-0.5,patt[b]-0.5) else: diffs_prev_this=(0.5-patt_prev[b],0.5-patt[b]) if abs(diffs_prev_this[0]) <= abs(diffs_prev_this[1]): # store previos sample if (best_diff[inPhase][b] is None) or (abs (diffs_prev_this[0]) len(dqsi_cache)) or (dqs_idly <0 ): print ("dqs_idly=%d, dqsi_cache=%s"%(dqs_idly,str(dqsi_cache))) try: dqsi_cache[dqs_idly] except: print ("dqs_idly=%d, dqsi_cache=%s"%(dqs_idly,str(dqsi_cache))) if (dqsi_cache[dqs_idly] is None) or force_meas: self.x393_mcntrl_timing.axi_set_dqs_idelay(combine_delay(dqs_idly),quiet=quiet) self.x393_mcntrl_timing.axi_set_dq_idelay(combine_delay(dqi_dqsi[branch][dqs_idly]),quiet=quiet) buf=self.x393_pio_sequences.read_block(4 * (nrep+1) +2, (0,1)[quiet<1], #show_rslt, 1) # wait_complete=1) buf= buf[4:(nrep*4)+4] # discard first 4*32-bit words and the "tail" after nrep*4 words32 patt=convert_w32_to_mem16(buf)# will be nrep*8 items dqsi_cache[dqs_idly]=patt if quiet < 1: print ('measure_phase(%d,%s) - new measurement'%(phase,str(force_meas))) else: patt=dqsi_cache[dqs_idly] if quiet < 1: print ('measure_patt(%d,%s) - using cache'%(phase,str(force_meas))) return patt def get_bit_diffs(dqs_idly0,dqs_idly1,branch): patt0=measure_dqsi(dqs_idly0,branch) patt1=measure_dqsi(dqs_idly1,branch) if (patt0 is None) or (patt1 is None): raise Exception("Tried to compare invalid(s): dqs_idly0=%d, dqs_idly1=%d, branch=%s"%(dqs_idly0, dqs_idly1, branch)) rslt=[0]*16 for i in range (nrep*8): # with 8 nursts - 64 x16-bit words diffs=patt0[i] ^ patt1[i] for b in range(len(rslt)): rslt[b]+= (diffs >> b) & 1 return rslt def get_lane_diffs(dqs_idly0,dqs_idly1,branch): diffs= get_bit_diffs(dqs_idly0,dqs_idly1,branch) # lane_diffs=[0]*(len(diffs)//8) lane_diffs=[] for lane in range(len(diffs)//8): num_diffs=0 for b in range(8): num_diffs += (0,1)[diffs[8*lane+b] != 0] lane_diffs.append(num_diffs) if quiet <3: print ("%d ? %d : %s"%(dqs_idly0,dqs_idly1,lane_diffs)) return lane_diffs def get_lane_adiffs(dqs_idly0,dqs_idly1,branch): # Assuming all 8 bits differ in the read data - check it in a single block? Write pattern? diffs=get_lane_diffs(dqs_idly0,dqs_idly1,branch) return ((diffs[0]-4)/4.0,(diffs[1]-4)/4.0) # Set phase phase_ok=self.set_phase_with_refresh( # check result for not None phase, quiet) if not phase_ok: return None # no valid CMDA ODELAY exists for this phase # try branches (will exit on first match) for branch in dqsi_lohi.keys(): low=dqsi_lohi[branch][0] high=dqsi_lohi[branch][1] # start with low dqs idelay and increase it by 1 primary step until getting 2 results with no bit differences # (using both byte lanes now) for idly1 in range(low+NUM_FINE_STEPS,high,NUM_FINE_STEPS): diffs=get_lane_diffs(idly1-NUM_FINE_STEPS,idly1,branch) if diffs == [0,0]: # no bit diffs in both byte lanes low=idly1 break else: #failed to find two delays to get the same read results (no bit differences in both lanes) continue # got both byte lanes with no difference, now try to find dqs_idelay delay where both bytes differ for idly in range(low,high,dly_step): idly1=min(idly+dly_step,high) diffs=get_lane_diffs(low,idly1,branch) if (diffs[0] != 0) and (diffs[1] != 0): high=idly1 break elif (diffs[0] == 0) and (diffs[1] == 0): low=idly1 # move low higher else: #failed to find another delay to get different read results (both myte lanes have bit differences continue if quiet <3: print ("0: low=%d, high=%d"%(low,high)) low_safe=low # safe low # now find marginal dqs idelay for each byte lane by dividing (low,high) interval #reduce low,high range for combined lanes dly = high while low < dly: # first adjust low dly_next = (low+dly) // 2 diffs=get_lane_diffs(low,dly_next,branch) if (diffs[0] != 0) and (diffs[1] != 0): dly = dly_next high= dly elif (diffs[0] == 0) and (diffs[1] == 0): if low == dly_next: break low = dly_next # move low higher else: # one byte matches, other - not (uncertain) dly = dly_next dly = low while dly < high: # now adjust high dly_next = (high+dly) // 2 diffs=get_lane_diffs(low_safe,dly_next,branch) if (diffs[0] != 0) and (diffs[1] != 0): high= dly_next else: if dly == dly_next: break dly = dly_next # move low higher #low, high are now closer, now scan and store (delay,num_bits) for each lane #May be check maximal number of bits that mismatch for each lane? Now assuming that it can be up to all 8 # low -= NUM_FINE_STEPS # low = max(dqsi_lohi[branch][0], low - NUM_FINE_STEPS ) # try to move lower by the fine steps interval, if possible # high = min(dqsi_lohi[branch][1], high+ NUM_FINE_STEPS ) # try to move higher by the fine steps interval, if possible if quiet <3: print ("1: low=%d(%d), high=%d"%(low,low_safe,high)) high = min(dqsi_lohi[branch][1], high+ NUM_FINE_STEPS ) # try to move higher by the fine steps interval, if possible if quiet <3: print ("2: low=%d(%d), high=%d"%(low,low_safe,high)) rslt=[] bestDly=[None]*2 # [low_safe]*2 # otherwise may fail - check it? bestDiffs=[None]*2 comp_step=(1,NUM_FINE_STEPS)[compare_prim_steps] for dly in range (low, high+1): ref_dly= dly-comp_step if ref_dly < low_safe: continue if quiet <2: print ("dly=%d, ref_dly=%d"%(dly, ref_dly),end=" ") adiffs= get_lane_adiffs(low_safe,dly,branch) adiffs_ref=get_lane_adiffs(low_safe,ref_dly,branch) for lane in range(len(adiffs)): diffs_prev_this=(adiffs_ref[lane],adiffs[lane]) if (diffs_prev_this[0] <= 0) and (diffs_prev_this[1] >= 0): if abs(diffs_prev_this[0]) <= abs(diffs_prev_this[1]): # store previos sample if (bestDiffs[lane] is None) or (abs (diffs_prev_this[0]) < abs(bestDiffs[lane])): bestDly[lane]=ref_dly # dly-1/dly-NUM_FINE_STEPS bestDiffs[lane]=diffs_prev_this[0] else: if (bestDiffs[lane] is None) or (abs (diffs_prev_this[1]) 0) and (adiffs[1] > 0): break # no need to continue, data got already for lane in range(len(adiffs)): if bestDiffs[lane] == -1.0: bestDiffs[lane] = None # single step jumps from none to all rslt.append((bestDly[lane],bestDiffs[lane],branch[0])) # adding first letter of branch name if quiet <3: print ("bestDly[%d]=%s, bestDiffs[%d]=%s, branch=%s"%(lane,str(bestDly[lane]),lane,str(bestDiffs[lane]),branch)) if quiet <3: print ('dly=%d rslt=%s'%(dly,str(rslt))) if quiet < 2: for i,d in enumerate(dqsi_cache): if d: print ("%d %s %d: %s"%(phase,branch,i,str(d))) return rslt return None # All Early/Nominal/Late variants were exhausted, did not find critical DQS inoput delay for this phase value # body of the measure_dqs_idly_phase() dqsi_vs_phase=[] for phase in range (numPhaseSteps): if quiet <2: print ("====== PHASE=%d ======"%(phase)) elif quiet < 3: print ("%d:"%(phase),end=" ") sys.stdout.flush() elif quiet < 5: print (".",end="") sys.stdout.flush() dqsi_vs_phase.append(dqsi_phase_step (phase)) if quiet < 3 : print ("dqsi_vs_phase=%s"%(str(dqsi_vs_phase))) print("Phase DQSI0 DQSI1 diff0 diff1 branch0 branch1") for phase,v in enumerate(dqsi_vs_phase): print("%d"%(phase), end=" ") if v: print ("%s %s %s %s %s %s"%(str(v[0][0]),str(v[1][0]),str(v[0][1]),str(v[1][1]), v[0][2], v[1][2])) else: print() elif quiet < 5: print () self.adjustment_state['dqsi_vs_phase']= dqsi_vs_phase self.adjustment_state['dqsi_vs_phase_steps']=compare_prim_steps return dqsi_vs_phase def measure_dqo_dqso(self, compare_prim_steps = True, # while scanning, compare this delay with 1 less by primary(not fine) step, # save None for fraction in unknown (previous -0.5, next +0.5) frac_step=0.125, sel=1, quiet=1, start_dly=0): #just to check dependence """ Scan dqs odelay (setting phase appropriately), write 0x0000/0xffff/0x0000/0xffff (same as fixed pattern) data and read it with known dqsi/dqi values (maybe even set different phase for read?), discarding first and last 1.5 8-bursts Measure 4 different transitions for each data bit (rising DQS/rising DQ, falling DQS/falling DQ, rising DQS/falling DQ and falling DQS/rising DQ (that allows to measure duty cycles fro both DQS and DQ lines @param quiet reduce output """ # self.load_hardcoded_data() # TODO: REMOVE LATER try: dqi_dqsi=self.adjustment_state['dqi_dqsi'] except: print ("No DQ IDELAY vs. DQS IDELAY data available, exiting") return dqsi_phase=self.adjustment_state['dqsi_phase'] num_lanes=len(dqsi_phase) cmda_bspe=self.adjustment_state['cmda_bspe'] wlev_dqs_bspe=self.adjustment_state['wlev_dqs_bspe'] brc=(5, # 3'h5, # bank 0x1234, # 15'h1234, # row address 0x100) # 10'h100 # column address nrep=8 # number of 8-bursts to compare (actual will have 3 more, first/last will be discarded extraTgl=0 # data is repetitive,so extra toggle is not needed (an there is an extra 8-burst anyway) timing=self.x393_mcntrl_timing.get_dly_steps() dly_step=int(NUM_FINE_STEPS*frac_step*timing['SDCLK_PERIOD']/timing['DLY_STEP']+0.5) numPhaseSteps= int(timing['SDCLK_PERIOD']/timing['PHASE_STEP']+0.5) step180= int(NUM_FINE_STEPS*0.5* timing['SDCLK_PERIOD'] / timing['DLY_STEP'] +0.5) #Calculate phase for the best match for the DQS output delay (for both lanes - use average). If # solution for different lanes point to the opposite ends of the phase range - keep previous # do not look outside of +/- frac_step def get_phase_for_dqso(): phase_dqso=[] last_phase=0 for dly in range(NUM_DLY_STEPS): best_phases= [] for lane in range(num_lanes): best_diff= frac_step*NUM_DLY_STEPS best_phase=None for phase in range(numPhaseSteps): try: dly_phase=wlev_dqs_bspe[lane][phase]['ldly'] except: dly_phase=None if (not dly_phase is None) and (cmda_bspe[phase % numPhaseSteps] is None): # check that CMDA odelay exists for this phase dly_phase=None """ # Make sure that dqsi and dqi exist for the phase if dqsi_dqi_for_phase[phase] is None: dly_phase=None if dly==65: print("lane=%d dly=%d, dqsi_dqi_for_phase[%d]=%s (%s)"%(lane, dly,phase,str(dqsi_dqi_for_phase[phase]),str(dly_phase))) """ if not dly_phase is None: adiff=abs(dly_phase-dly) if adiff < best_diff: best_diff = adiff best_phase = phase if best_phase is None: best_phases=None # At least one of the lanes does not have an acceptable solution (should not normally happen) break # print("lane=%d dly=%d, best_phase=%s best_diff=%s"%(lane, dly,str(best_phase),str(best_diff))) best_phases.append(best_phase) if best_phases is None: phase_dqso.append(None) continue else: diff_per= max(best_phases)-min(best_phases) > numPhaseSteps/2 # different ends #find which one is closer to last_phase, modify the other one by +/- period sp=0.0 for lane in range(num_lanes): if diff_per and (best_phases[lane] >= numPhaseSteps/2): best_phases[lane] -= numPhaseSteps sp+=best_phases[lane] sp /= num_lanes # average phase for all lanes sp=int(round(sp)) # only if results for lanes are on the different ends - if they agree - just take an average if diff_per and (abs(sp-last_phase) > abs(sp+numPhaseSteps-last_phase)): sp += numPhaseSteps sp=max(sp,0) # May be that both best phases were OK, but their average falls into the gap - find closest if dqsi_dqi_for_phase[sp] is None: best_dist=numPhaseSteps best_phase=None for phase in range(numPhaseSteps): if not dqsi_dqi_for_phase[phase] is None: dist = min(abs(phase-sp),abs(phase+numPhaseSteps-sp),abs(phase-numPhaseSteps-sp)) if dist < best_dist: best_dist=dist best_phase=phase if best_dist >= frac_step*numPhaseSteps: # print("Could not find phase substitute for %d, %s is too far "%(sp, str(best_phase))) best_phase=None # else: # print("Using substitute %d for %d"%(best_phase,sp)) sp= best_phase sp=min(sp,numPhaseSteps-1) # print("dly=%d best_phases=%s"%(dly, str(best_phases))) phase_dqso.append(sp) return phase_dqso def get_dqsi_dqi_for_phase(): # Mark DQS idelay values that have all DQ delays valid # for each phase check that DQS input delay value exists and store DQi varinat (early/nominal/late dqsi_dqi_phase=[None]*numPhaseSteps inv_vars=('early','late') for phase in range (numPhaseSteps): # print (phase, end=" ") dqsi=[] for lane_data in dqsi_phase: dqsi.append(lane_data[phase]) if None in dqsi: continue # Keep False for k, dqi_dqsi_v in dqi_dqsi.items(): # print (" k=%s"%(k), end=" ") if not dqi_dqsi_v: continue # need to continue with next phase dqi=[] for lane, dqsi_lane in enumerate(dqsi): # print (" lane=%d"%(lane), end=" ") dq_lane=dqi_dqsi_v[dqsi_lane] #list of 16 DQ values for dqsi_lane or None if (dq_lane is None) or (None in dq_lane[8*lane:8*(lane+1)]): break dqi += dq_lane[8*lane:8*(lane+1)] else: dqsi_dqi_phase[phase]={'dqsi':dqsi, 'dqi':dqi, 'invert':k in inv_vars, 'variant':k } # dqsi - a pair of dqs input delays, dqi - dq delays for the same phase break # print() return dqsi_dqi_phase def dqs_step(dqs_lin): patt_cache=[None]*NUM_DLY_STEPS # cache for holding already measured delays def measure_block(dly,invert_patt, force_meas=False): if (patt_cache[dly] is None) or force_meas: self.x393_mcntrl_timing.axi_set_dq_odelay(combine_delay(dly),quiet=quiet) self.x393_pio_sequences.write_block() #page= 0, wait_complete=1) patt= self.x393_pio_sequences.read_levelling(nrep, -2, # Actually read block, but pre-process as a pattern quiet+1) #invert pattern if using early/late (not nominal) variants # if (invert_patt): # for i in range(len(patt)): # patt[i]=1.0-patt[i] patt_cache[dly]=patt if quiet < 1: print ('measure_block(%d,%s) - new measurement'%(dly,str(force_meas))) else: patt=patt_cache[dly] if quiet < 1: print ('measure_block(%d,%s) - using cache'%(dly,str(force_meas))) return patt def get_sign(data,edge=None): """ edge: 0 - first 16, 1 - second 16 return -1 if all <0.5 return +1 if all >0.5 return 0 otherwise """ if edge == 0: return get_sign(data[:16]) if edge == 1: # return -get_sign(data[16:]) return get_sign(data[16:]) m1=True p1=True for d in data: m1 &= (d < 0.5) p1 &= (d > 0.5) if not (m1 or p1): break else: if m1: return -1 elif p1: return 1 return 0 rslt=[None]*16 # each bit will have [inphase][dqs_falling] self.x393_mcntrl_timing.axi_set_dqs_odelay(combine_delay(dqs_lin),quiet=quiet) #set phase, cmda_odelay, dqsi, dqi to match this delay phase=phase_dqso[dqs_lin] if phase is None: # no good phase exist for the specified dqs_odelay if quiet <2: print("No dood phase for DQS odelay = %d"%(dqs_lin)) return None # set phase # TODO: maybe keep last phase set and do not change it if required is not too far from the last set # Set phase (and cmda_odelay as needed) if quiet < 2: print ("set_phase_with_refresh(%d), dqs_odelay=%d"%(phase,dqs_lin)) phase_ok=self.set_phase_with_refresh( # check result for not None phase, quiet) if not phase_ok: print ("Failed to set phase=%d for dly=%d- that should not happen (phase_dqso)- "%(phase,dqs_lin)) return None # no valid CMDA ODELAY exists for this phase #set DQS IDELAY and DQ IDELAY matching phase dqs_idelay=dqsi_dqi_for_phase[phase]['dqsi'] # 2-element list dq_idelay= dqsi_dqi_for_phase[phase]['dqi'] # 16-element list invert_patt= dqsi_dqi_for_phase[phase]['invert'] # 16-element list self.x393_mcntrl_timing.axi_set_dqs_idelay(combine_delay(dqs_idelay),quiet=quiet) self.x393_mcntrl_timing.axi_set_dq_idelay(combine_delay(dq_idelay),quiet=quiet) d_low=[None]*2 # first - lowest when all are -+, second - when all are +- d_high=[None]*2 # first - when all are +- after -+, second - when all are -+ after +- dly=0 notLast=True needSigns=None lowGot=None highGot=None while (dly < NUM_DLY_STEPS) and notLast: notLast= dly < NUM_DLY_STEPS-1 patt=measure_block(dly,invert_patt) # ,force_meas=False) signs=(get_sign(patt,0),get_sign(patt,1)) if quiet < 1: print ('dly=%d lowGot=%s, highGot=%s, signs=%s'%(dly,str(lowGot),str(highGot),str(signs))) if lowGot is None : # looking for the first good sample if (signs==(-1,1)) or (signs==(1,-1)) : if signs[0] == -1: # == (-1,1): lowGot=0 else: lowGot=1 d_low[lowGot] = dly needSigns=((1,-1),(-1,1))[lowGot] dly += step180-dly_step # almost 180 degrees else: # at least one is 0 dly += dly_step # small step if quiet < 1: print ('lowGot was None : dly=%d, lowGot=%s, needSigns=%s'%(dly,str(lowGot),str(needSigns))) elif highGot is None : # only one good sample is available so far if signs == needSigns: highGot=lowGot d_high[highGot] = dly d_low[1-lowGot] = dly needSigns=((-1,1),(1,-1))[lowGot] dly += step180-dly_step # almost 180 degrees else: dly += dly_step # small step if quiet < 1: print ('highGot was None : dly=%d, lowGot=%s, highGot=%s, needSigns=%s'%(dly,str(lowGot),str(lowGot),str(needSigns))) else: # looking for the 3-rd sample if signs == needSigns: highGot=1-highGot d_high[highGot] = dly break else: dly += dly_step # small step dly = min (dly,NUM_DLY_STEPS-1) if highGot is None: if quiet < 3: print ("Could not find initial bounds for DQS output delay = %d d_low=%s, d_high=%s"%(dqs_lin,str(d_low),str(d_high))) return None if quiet < 2: print ("DQS input delay = %d , preliminary bounds: d_low=%s, d_high=%s"%(dqs_lin,str(d_low),str(d_high))) for inPhase in range(2): if not d_high[inPhase] is None: # Try to squeeze d_low, d_high closer to reduce scan range while d_high[inPhase]>d_low[inPhase]: dly=(d_high[inPhase] + d_low[inPhase])//2 patt=measure_block(dly,invert_patt) # ,force_meas=False) signs=(get_sign(patt,0),get_sign(patt,1)) if signs==(-1,1): if inPhase: d_high[inPhase]=dly else: if d_low[inPhase]==dly: break d_low[inPhase]=dly elif signs==(1,-1): if inPhase: if d_low[inPhase]==dly: break d_low[inPhase]=dly else: d_high[inPhase]=dly else: # uncertain result break if quiet < 2: print ("DQS output delay = %d , squeezed bounds: d_low=%s, d_high=%s"%(dqs_lin,str(d_low),str(d_high))) #Improve squeezing - each limit to the last for inPhase in range(2): if not d_high[inPhase] is None: # Try to squeeze d_low first d_uncertain=d_high[inPhase] while d_uncertain > d_low[inPhase]: dly=(d_uncertain + d_low[inPhase])//2 patt=measure_block(dly,invert_patt) # ,force_meas=False) signs=(get_sign(patt,0),get_sign(patt,1)) if signs==(-1,1): if inPhase: d_uncertain=dly else: if d_low[inPhase]==dly: break d_low[inPhase]=dly elif signs==(1,-1): if inPhase: if d_low[inPhase]==dly: break d_low[inPhase]=dly else: d_uncertain=dly else: # uncertain result d_uncertain=dly #now udjust upper limit while d_high[inPhase] > d_uncertain: dly=(d_high[inPhase] + d_uncertain)//2 patt=measure_block(dly,invert_patt) # ,force_meas=False) signs=(get_sign(patt,0),get_sign(patt,1)) if signs==(-1,1): if inPhase: d_high[inPhase]=dly else: if d_uncertain==dly: break d_uncertain=dly elif signs==(1,-1): if inPhase: if d_uncertain==dly: break d_uncertain=dly else: d_high[inPhase]=dly else: # uncertain result if d_uncertain==dly: break d_uncertain=dly if quiet < 2: print ("DQS output delay = %d , tight squeezed bounds: d_low=%s, d_high=%s"%(dqs_lin,str(d_low),str(d_high))) # scan ranges, find closest solutions #compare_prim_steps best_dly= [[],[]] best_diff=[[],[]] for inPhase in range(2): if not d_high[inPhase] is None: # patt=None best_dly[inPhase]=[d_low[inPhase]]*32 best_diff[inPhase]=[None]*32 # for b,p in enumerate(patt): # positiveJump=((not inPhase) and (b<16)) or (inPhase and (b >= 16)) # may be 0, False, True # if positiveJump: # best_diff[inPhase].append(p-0.5) # else: # best_diff[inPhase].append(0.5-p) for dly in range(d_low[inPhase]+1,d_high[inPhase]+1): # patt_prev=patt #as measured data is cached, there is no need to specially maintain patt_prev from earlier measurement dly_prev= max(0,dly-(1,NUM_FINE_STEPS)[compare_prim_steps]) patt_prev=measure_block(dly_prev,invert_patt) # ,force_meas=False) - will be stored in cache patt= measure_block(dly,invert_patt) # ,force_meas=False) - will be stored in cache for b in range(32): positiveJump=((not inPhase) and (b<16)) or (inPhase and (b >= 16)) # may be 0, False, True signs=((-1,1)[patt_prev[b]>0.5],(-1,1)[patt[b]>0.5]) if (positiveJump and (signs==(-1,1))) or (not positiveJump and (signs==(1,-1))): if positiveJump: diffs_prev_this=(patt_prev[b]-0.5,patt[b]-0.5) else: diffs_prev_this=(0.5-patt_prev[b],0.5-patt[b]) if abs(diffs_prev_this[0]) <= abs(diffs_prev_this[1]): # store previos sample if (best_diff[inPhase][b] is None) or (abs (diffs_prev_this[0])0 -program with this fraction of clk period from the margin ra = 0, ba = 0, quiet=1, single=False): # quiet=0): """ Will raise exception if read non good or bad - that may happen if cmda_odelay is violated too much. Need to re- init memory if that happems and possibly re-run with largersafe_phase or safe_phase==0/None to disable this feature Final measurement of output delay on address lines, performed when read/write timing is set Writes different data in the specified block and then different pattern to all blocks different by one row address or bank address bit. Refresh is programmed to drive inverted ra and ba, so the lines have to switch during activate command of read block, and the too late switch can be detected as it will cause the different block to be read. Address and bank lines are tested with different delays, one bit at a time. """ # self.load_hardcoded_data() # TODO: TEMPORARY - remove later num_ba=3 if not single: pass1=self.measure_addr_odelay(safe_phase=safe_phase, #0.25, # 0 strictly follow cmda_odelay, >0 -program with this fraction of clk period from the margin ra = ra, # 0, ba = ba, # 0, quiet=quiet+1, #1, single=True) # single=False) pass2=self.measure_addr_odelay(safe_phase=safe_phase, #0.25, # 0 strictly follow cmda_odelay, >0 -program with this fraction of clk period from the margin ra = ra ^ ((1 << vrlg.ADDRESS_NUMBER)-1), # 0, ba = ba ^ ((1 << num_ba)-1), # 0, quiet=quiet+1, #1, single=True) # single=False) self.adjustment_state['addr_meas']=[pass1,pass2] if (quiet<4): print ('addr_meas=[') for p in [pass1,pass2]: print(p,",") print(']') return [pass1,pass2] nbursts=1 # just 1 full burst for block comparison(will write nbursts+3) sel_wr=1 sel_rd=1 extraTgl=0 inv_ra=ra ^ ((1 << vrlg.ADDRESS_NUMBER)-1) ca= ra & ((1 << vrlg.COLADDR_NUMBER) -1) inv_ba=ba ^ ((1 << num_ba)-1) if not "cmda_bspe" in self.adjustment_state: raise Exception ("No cmda_odelay data is available. 'adjust_cmda_odelay 0 1 0.1 3' command should run first.") dly_steps=self.x393_mcntrl_timing.get_dly_steps() numPhaseSteps= int(dly_steps['SDCLK_PERIOD']/dly_steps['PHASE_STEP']+0.5) #create a list of None/optimal cmda determined earlier cmda_odly= [None if (self.adjustment_state['cmda_bspe'][phase] is None) else self.adjustment_state['cmda_bspe'][phase]['ldly'] for phase in range(numPhaseSteps)] if safe_phase: cmda_odly_zerr=[None if (self.adjustment_state['cmda_bspe'][phase] is None) else self.adjustment_state['cmda_bspe'][phase]['zerr'] for phase in range(numPhaseSteps)] cmda_odly_early=[] for phase,zerr in enumerate (cmda_odly_zerr): if (not zerr is None) and (zerr < 0.5-safe_phase): cmda_odly_early.append(0) else: cmda_odly_early.append(cmda_odly[phase]) else: cmda_odly_early=cmda_odly # cmda_odly=tuple(cmda_odly) if quiet <1: for phase,dly in enumerate(cmda_odly): ldly=None if not self.adjustment_state['cmda_bspe'][phase] is None: ldly=self.adjustment_state['cmda_bspe'][phase]['ldly'] print("%d %s %s"%(phase,str(dly),str(ldly))) dly_try_step=NUM_FINE_STEPS # how far to step when looking for zero crossing (from predicted) phase_try_step=numPhaseSteps//8 # when searching for marginal delay, try not optimal+perid/2 but smaller step to accommodate per-bit variations good_patt=0xaaaa bad_patt = good_patt ^ 0xffff # make sure delay data is available try: delays_phase=self.adjustment_state['delays_phase'] except: print("Delays for phases (self.adjustment_state['delays_phase']) are not set, running 'get_delays_vs_phase' command ") try: delays_phase=self.get_delays_vs_phase(filter_dqo=2, filter_dqi=2, filter_dqso=2, filter_dqsi=2, filter_cmda=2, filter_read=0, filter_write=0, filter_rsel=None, filter_wsel=None, keep_all=False, set_table=True, quiet=quiet+2) self.adjustment_state['delays_phase']=delays_phase except: raise Exception("Failed to execute get_'delays_vs_phase' command - probably incomplete calibration data") # find first suitable phase for phase,delays in enumerate(delays_phase): if not delays is None: break else: raise Exception("Could not find a valid phase to use") #phase is usable delay # reprogram refresh self.x393_axi_tasks.enable_refresh(0) # turn off refresh self.x393_pio_sequences.set_refresh(vrlg.T_RFC, # input [ 9:0] t_rfc; # =50 for tCK=2.5ns vrlg.T_REFI, #input [ 7:0] t_refi; # 48/97 for normal, 16 - for simulation 0, # en_refresh=0, inv_ra, # used only for calibration of the address line output delay inv_ba, 0) # verbose=0 # set usable timing, enable refresh OK=self.set_phase_delays(phase=phase, refresh=True, quiet=quiet) # all the rest are defaults if not OK: raise Exception("measure_addr_odelay(): failed to set phase = %d"%(phase)) #Write 0xaaaa pattern to correct block (all used words), address number - to all with a single bit different self.x393_pio_sequences.set_read_block(ba,ra,ca,nbursts+3,sel_wr) #prepare and writ 'correct' block: wdata16_good=(good_patt,)*(8*(nbursts+3)) wdata32_good=convert_mem16_to_w32(wdata16_good) wdata16_bad=(bad_patt,)*(8*(nbursts+3)) wdata32_bad=convert_mem16_to_w32(wdata16_bad) comp32_good= wdata32_good[4:(nbursts*4)+4] # data to compare with read buffer - discard first 4*32-bit words and the "tail" after nrep*4 words32 comp32_bad= wdata32_bad[4:(nbursts*4)+4] # data to compare with read buffer - discard first 4*32-bit words and the "tail" after nrep*4 words32 self.x393_mcntrl_buffers.write_block_buf_chn(0,0,wdata32_good,quiet) # fill block memory (channel, page, number) self.x393_pio_sequences.set_write_block(ba,ra,ca,nbursts+3,extraTgl,sel_wr) # set sequence to write 'correct' block self.x393_pio_sequences.set_read_block(ba,ra,ca,nbursts+3,sel_rd) # set sequence to read block (will always be the same address) self.x393_pio_sequences.write_block() #page= 0, wait_complete=1) # write 'correct' block #prepare and write all alternative blocks (different by one address/bank self.x393_mcntrl_buffers.write_block_buf_chn(0,0,wdata32_bad,quiet) # fill block memory (channel, page, number) raba_bits=[] # print('vrlg.ADDRESS_NUMBER=',vrlg.ADDRESS_NUMBER) # print('num_ba=',num_ba) for addr_bit in range(vrlg.ADDRESS_NUMBER): raba_bits.append((addr_bit,None)) ra_alt=ra ^ (1< 0: filter_cmda=1.0*safe_phase delays_phase=self.get_delays_vs_phase(filter_dqo=0, #2, filter_dqi=1, #2, filter_dqso=0, #2, filter_dqsi=1, #2, filter_cmda=filter_cmda, #2, # special mode - try to keep cmda low, with setup violation to test marginal phase filter_read=0, filter_write=0, filter_rsel=None, filter_wsel=None, keep_all=False, set_table=False, # True, quiet=quiet+2) if quiet < 2: print ("delays_phase=",delays_phase) # self.adjustment_state['delays_phase']=delays_phase # delays_phase=self.adjustment_state['delays_phase'] # self.set_phase_delays already ran, but may now need to ease requirements def addr_phase_step(phase): def measure_block(dly, addr_bit, bank_bit, force_meas=False): if (meas_cache[dly] is None) or force_meas: for _ in range(5): #set same delays for all cmda bits (should be already done with 'set_phase_with_refresh' if not addr_bit is None: self.x393_mcntrl_timing.axi_set_address_odelay(combine_delay(dly),addr_bit,quiet=quiet) elif not bank_bit is None: self.x393_mcntrl_timing.axi_set_address_odelay(combine_delay(dly),bank_bit,quiet=quiet) else: raise Exception("BUG: both addr_bit and bank_bit are None") if quiet < 1: print ('measure_block(%d,%s) - new measurement'%(dly,str(force_meas))) self.x393_pio_sequences.manual_refresh() # run refresh that sets address bit to opposite values to the required row+bank address buf=self.x393_pio_sequences.read_block(4 * (nbursts+1) +2, (0,1)[quiet<1], #show_rslt, 1) # wait_complete=1) buf= buf[4:(nbursts*4)+4] # discard first 4*32-bit words and the "tail" after nrep*4 words32 if buf==comp32_good: meas=True elif buf==comp32_bad: meas=False else: print ("Inconclusive result for comparing read data for phase=%d, addr_bit=%s, bank_bit=%s dly=%d"%(phase,str(addr_bit),str(bank_bit),dly)) print ("Data read from memory=",buf, "(",convert_w32_to_mem16(buf),")") print ("Expected 'good' data=",comp32_good, "(",convert_w32_to_mem16(comp32_good),")") print ("Expected 'bad' data=", comp32_bad, "(",convert_w32_to_mem16(comp32_bad),")") meas=None meas_cache[dly]=meas if not meas is None: break else: print("***** FAILED to get measurement ******") # raise Exception("***** FAILED to get measurement ******") meas=False else: meas=meas_cache[dly] if quiet < 1: print ('measure_block(%d,%s) - using cache'%(dly,str(force_meas))) return meas # pass # addr_phase_step body # check that the particular phase is valid for all parameters # To increase valid range it is possible to ignore write delays as they are not used here (recalculate self.adjustment_state['delays_phase'] # after write is done # delays_phase=self.adjustment_state['delays_phase'] if quiet < 1: print ("****** phase=%d ******"%(phase),end=" ") if delays_phase[phase] is None: if quiet < 1: print ("delays_phase[%d] is None"%(phase)) return None dly_optimal= cmda_odly[phase] if dly_optimal is None: if quiet < 1: print ("dly_optimal is None") return None # may increase range by using dly_optimal=0 until it is not dangerously late (say only 1/4 period off) phase_marg= (phase+ (numPhaseSteps//2)-phase_try_step) % numPhaseSteps if cmda_odly[phase_marg] is None: phase_marg_traget=phase_marg phase_marg=None for p in range(numPhaseSteps): if not cmda_odly[p is None]: if (phase_marg is None) or (min(abs(p-phase_marg_traget), abs(p-phase_marg_traget+numPhaseSteps), abs(p-phase_marg_traget-numPhaseSteps)) < min(abs(phase_marg-phase_marg_traget), abs(phase_marg-phase_marg_traget+numPhaseSteps), abs(phase_marg-phase_marg_traget-numPhaseSteps))): phase_marg=p else: print("BUG: could to find a valid marginal phase") return None # may increase range by using dly_optimal=0 until it is not dangerously late (say only 1/4 period off) dly_marg = cmda_odly[phase_marg]# - dly_try_step if dly_marg < dly_optimal: if cmda_odly_early[phase] < dly_marg: dly_optimal=cmda_odly_early[phase] else: if quiet < 1: print ("dly_marg (==%d) < dly_optimal (==%d)"%(dly_marg,dly_optimal)) return None # It is not possble to try delay lower than optimal with this method #set phase and all optimal delays for that phase self.set_phase_delays(phase=phase, refresh=True, delays_phase=delays_phase, quiet=quiet) # all the rest are defaults # Now try #raba_bits [(addr_bit,bank_bit),...] rslt=[] for addr_bit,bank_bit in raba_bits: if quiet < 1 : print("\n===== phase=%d, dly_optimal=%d, addr_bit=%s, bank_bit=%s"%(phase, dly_optimal,str(addr_bit),str(bank_bit))) self.x393_mcntrl_timing.axi_set_cmda_odelay(combine_delay(dly_optimal),quiet=quiet) # set all bits to optimal delay meas_cache=[None]*NUM_DLY_STEPS # cache for holding results of already measured delays, new cach for each address bit # ts=dly_try_step dly=dly_marg dly_low=None #dly dly_high=None# dly while ((dly_low is None) or (dly_high is None)) and (dly > dly_optimal) and (dly < NUM_DLY_STEPS): meas=measure_block(dly, addr_bit, bank_bit) if meas : if dly==(NUM_DLY_STEPS-1): dly = None break dly_low=dly dly=min(NUM_DLY_STEPS-1,dly+dly_try_step) else: dly_high=dly dly=max(dly_optimal,dly-dly_try_step) if quiet < 1 : print ("dly_low=%s, dly_high=%s, dly=%s"%(str(dly_low),str(dly_high),str(dly))) if (dly_low is None) or (dly_high is None): # dly is None: rslt.append(None) continue #find highest delay that is lower than margin (here delay monotonicity is assumed!) while dly_low < (dly_high-1): dly=(dly_low+dly_high)//2 meas=measure_block(dly, addr_bit, bank_bit) if meas: dly_low=dly else: dly_high=dly rslt.append(dly_low) return rslt #main method body - iterate over phases addr_odelay=[] for phase in range(numPhaseSteps): if quiet < 6: print (".",end="") sys.stdout.flush() addr_odelay.append(addr_phase_step(phase)) if quiet < 6: print () # self.adjustment_state['addr_odelay_meas']=addr_odelay if quiet < 3: for phase, adly in enumerate(addr_odelay): print("%d"%(phase),end=" ") print("%s"%(str(cmda_odly[phase])),end=" ") if adly: for b in adly: if not b is None: print("%d"%(b),end=" ") else: print("?",end=" ") print() return addr_odelay def measure_cmd_odelay(self, safe_phase=0.25, # 0 strictly follow cmda_odelay, >0 -program with this fraction of clk period from the margin reinits=1, tryWrongWlev=1, # try wrong write levelling mode to make sure device is not stuck in write levelling mode quiet=0): """ Measure output delay on 3 command lines - WE, RAS and CAS, only for high-low transitions as controller keeps these lines at high (inactive) level all the time but the command itself. Scanning is performed with refresh off, one bit at a time in write levelling mode and DQS output delay set 1/4 later than nominal, so 0x01010101 pattern is supposed to be read on all bits. If it is not (usually just 0xffffffff-s) the command bit is wrong. After each test one read with normal delay is done to make sure the write levelling mode is turned off - during write levelling mode it is turned on first, then off and marginal command bit delay may cause write levelling to turn on, but not off """ # self.load_hardcoded_data() # TODO: ******** TEMPORARY - remove later nrep=4 #16 # number of 8-bursts in write levelling mode margin_error=0.1 # put 0.0? - how high wlev error can be to accept cmd_bits=(0,1,2) # WE, RAS, CAS if not "cmda_bspe" in self.adjustment_state: raise Exception ("No cmda_odelay data is available. 'adjust_cmda_odelay 0 1 0.1 3' command should run first.") dly_steps=self.x393_mcntrl_timing.get_dly_steps() numPhaseSteps= int(dly_steps['SDCLK_PERIOD']/dly_steps['PHASE_STEP']+0.5) #create a list of None/optimal cmda determined earlier cmda_odly= [None if (self.adjustment_state['cmda_bspe'][phase] is None) else self.adjustment_state['cmda_bspe'][phase]['ldly'] for phase in range(numPhaseSteps)] if safe_phase: cmda_odly_zerr=[None if (self.adjustment_state['cmda_bspe'][phase] is None) else self.adjustment_state['cmda_bspe'][phase]['zerr'] for phase in range(numPhaseSteps)] cmda_odly_early=[] for phase,zerr in enumerate (cmda_odly_zerr): if (not zerr is None) and (zerr < 0.5-safe_phase): cmda_odly_early.append(0) else: cmda_odly_early.append(cmda_odly[phase]) else: cmda_odly_early=cmda_odly #get write levellimg data if not "wlev_dqs_bspe" in self.adjustment_state: raise Exception ("No wlev_dqs_bspe data is available, this method should run after write levelling") wlev_odly=[] for wlev_data in self.adjustment_state['wlev_dqs_bspe']: wlev_odly.append([None if (wlev_data[phase] is None) else wlev_data[phase]['ldly'] for phase in range(numPhaseSteps)]) if quiet <1: print("wlev_odly=",wlev_odly) #fill gaps (if any - currently none if quiet <1: #simulate wlev_odly[0][5]=None wlev_odly[1][3]=None print("wlev_odly=",wlev_odly) for wlev_lane in wlev_odly: for phase in range(numPhaseSteps): if wlev_lane[phase] is None: otherPhase=None for p in range(phase-numPhaseSteps/8,phase+numPhaseSteps/8+1): if not wlev_lane[p % numPhaseSteps] is None: if (otherPhase is None) or (abs(phase-p) < abs(phase-otherPhase)): otherPhase=p if not otherPhase is None: print ("phase=",phase,", otherPhase=",otherPhase) wlev_lane[phase]=wlev_lane[otherPhase % numPhaseSteps] if quiet <1: print("wlev_odly=",wlev_odly) #shift by 90 degrees wlev_odly_late=[] for wlev_lane in wlev_odly: wlev_odly_late.append(wlev_lane[3*numPhaseSteps//4:]+wlev_lane[:3*numPhaseSteps//4]) if quiet <1: print("wlev_odly_late=",wlev_odly_late) if quiet <1: for phase,dly in enumerate(cmda_odly): ldly=None if not self.adjustment_state['cmda_bspe'][phase] is None: ldly=self.adjustment_state['cmda_bspe'][phase]['ldly'] print("%d %s %s"%(phase,str(dly),str(ldly))) dly_try_step=NUM_FINE_STEPS # how far to step when looking for zero crossing (from predicted) phase_try_step=numPhaseSteps//8 # when searching for marginal delay, try not optimal+perid/2 but smaller step to accommodate per-bit variations #turn off refresh - it will not be needed in this test if reinits > 0: self.x393_pio_sequences.restart_ddr3() else: self.x393_axi_tasks.enable_refresh(0) # if not init, at least turn refresh off! self.x393_pio_sequences.set_write_lev(nrep,False) # write leveling - 'good' mode def set_delays_with_reinit(phase, restart=False): """ Re-initialize memory device if it stopped responding """ if restart: if quiet < 2: print ('Re-initializing memory device after failure, phase=%d'%(phase)) self.x393_pio_sequences.restart_ddr3() if cmda_odly_early[phase] is None: if quiet < 2: print ('No good cmda_odly_early delays for phase = %d'%(phase)) return None dly_wlev=(wlev_odly_late[0][phase],wlev_odly_late[1][phase]) if None in dly_wlev: if quiet < 2: print ('No good late write levellilng DQS output delays for phase = %d'%(phase)) return None # no need to set any other delays but cmda and dqs odelay? #just set phase! self.x393_mcntrl_timing.axi_set_phase(phase,quiet=quiet) self.x393_mcntrl_timing.axi_set_cmda_odelay(combine_delay(cmda_odly_early[phase]),None, quiet=quiet) # set DQS odelays to get write levelling pattern self.x393_mcntrl_timing.axi_set_dqs_odelay(combine_delay(dly_wlev), quiet=quiet) #Verify wlev is OK wl_rslt=self.x393_pio_sequences.write_levelling(1, nrep, quiet) if wl_rslt[2] > margin_error: self.x393_pio_sequences.set_write_lev(nrep,False) # write leveling - 'good' mode (if it was not set so) wl_rslt=self.x393_pio_sequences.write_levelling(1, nrep, quiet) if wl_rslt[2] > margin_error: if not restart: set_delays_with_reinit(phase=phase,restart=True) # try with reinitialization else: raise Exception ("set_delays_with_reinit failed to read with safe delays for phase=%d after re-initializing device, wl_rslt=%s"% (phase,str(wl_rslt))) return cmda_odly_early[phase] # safe command/adderss delay def cmd_phase_step(phase): def measure_block(dly, cmd_bit, force_meas=False): if (meas_cache[dly] is None) or force_meas: #set same delays for all cmda bits (should be already done with 'set_phase_with_refresh' self.x393_mcntrl_timing.axi_set_cmd_odelay(combine_delay(cmda_odly_early[phase]),None, quiet=quiet) self.x393_mcntrl_timing.axi_set_cmd_odelay(combine_delay(dly), cmd_bit,quiet=quiet) if quiet < 1: print ('measure_block(%d,%d,%d,%d,%s) - new measurement'%(dly,cmda_odly_early[phase],cmd_bit,phase,str(force_meas))) self.x393_pio_sequences.manual_refresh() # run refresh that sets address bit to opposite values to the required row+bank address wl_rslt=self.x393_pio_sequences.write_levelling(1, nrep, quiet) meas= not (wl_rslt[2] > margin_error) # not so many errors (normally should be just 0 meas_cache[dly]=meas # now reset command bit delay and make sure it worked self.x393_mcntrl_timing.axi_set_cmd_odelay(combine_delay(cmda_odly_early[phase]),None, quiet=quiet) wl_rslt=self.x393_pio_sequences.write_levelling(1, nrep, quiet) if wl_rslt[2] > margin_error: if quiet < 2: print ("measure_block failed to re-read with safe delays for phase=%d, cmd_bit=%d. Resetting memory device, wl_rslt=%s"%(phase,cmd_bit,str(wl_rslt))) set_delays_with_reinit(phase=phase, restart=True) #retry after re-initialization wl_rslt=self.x393_pio_sequences.write_levelling(1,nrep, quiet) if wl_rslt[2] > margin_error: raise Exception ("measure_block failed to re-read with safe delays for phase=%d even after re-initializing device, wl_rslt=%s"%(phase,str(wl_rslt))) # Now make sure device responds - setup read "wrong" write levelling (no actually turning on wlev mode) if tryWrongWlev: self.x393_pio_sequences.set_write_lev(nrep, True) # 'wrong' write leveling - should not work wl_rslt=self.x393_pio_sequences.write_levelling(1, nrep, quiet) #restore normal write levelling mode: self.x393_pio_sequences.set_write_lev(nrep, False) # 'wrong' write leveling - should not work if not (wl_rslt[2] > margin_error): if quiet < 2: print ("!!! Write levelling mode is stuck (not turning off) for phase=%d, wl_rslt=%s"%(phase,str(wl_rslt))) set_delays_with_reinit(phase=phase, restart=True) # just do it, no testimng here (wlev mode is already restored else: meas=meas_cache[dly] if quiet < 1: print ('measure_block(%d,%s) - using cache'%(dly,str(force_meas))) return meas #cmd_phase_step(phase) body if quiet < 1: print ("****** phase=%d ******"%(phase),end=" ") # if delays_phase[phase] is None: # if quiet < 1: # print ("delays_phase[%d] is None"%(phase)) # return None dly_optimal= cmda_odly[phase] if dly_optimal is None: if quiet < 1: print ("dly_optimal is None") return None # may increase range by using dly_optimal=0 until it is not dangerously late (say only 1/4 period off) phase_marg= (phase+ (numPhaseSteps//2)-phase_try_step) % numPhaseSteps if cmda_odly[phase_marg] is None: phase_marg_traget=phase_marg phase_marg=None for p in range(numPhaseSteps): if not cmda_odly[p is None]: if (phase_marg is None) or (min(abs(p-phase_marg_traget), abs(p-phase_marg_traget+numPhaseSteps), abs(p-phase_marg_traget-numPhaseSteps)) < min(abs(phase_marg-phase_marg_traget), abs(phase_marg-phase_marg_traget+numPhaseSteps), abs(phase_marg-phase_marg_traget-numPhaseSteps))): phase_marg=p else: print("BUG: could to find a valid marginal phase") return None # may increase range by using dly_optimal=0 until it is not dangerously late (say only 1/4 period off) dly_marg = cmda_odly[phase_marg]# - dly_try_step if dly_marg < dly_optimal: if cmda_odly_early[phase] < dly_marg: dly_optimal=cmda_odly_early[phase] else: if quiet < 1: print ("dly_marg (==%d) < dly_optimal (==%d)"%(dly_marg,dly_optimal)) return None # It is not possble to try delay lower than optimal with this method #set phase and all optimal delays for that phase # Maybe it is not needed at all? # self.set_phase_delays(phase=phase, # refresh=True, # delays_phase=delays_phase, # quiet=quiet) # all the rest are defaults dlyOK=set_delays_with_reinit(phase=phase, restart=False) # will check wlev and re-init if required if dlyOK is None: if quiet < 1: print ("set_delays_with_reinit(%d) failed"%(phase)) return None # Now try rslt=[] for cmd_bit in cmd_bits: if quiet < 1 : print("\n===== phase=%d, dly_optimal=%d, cmd_bit=%d"%(phase, dly_optimal,cmd_bit)) set_delays_with_reinit(phase=phase, restart=False) # no need to check results? Maybe remove completely? # set_delays_with_reinit(phase=phase, restart=True) # no need to check results? Maybe remove completely? meas_cache=[None]*NUM_DLY_STEPS # cache for holding results of already measured delays, new cach for each address bit dly=dly_marg dly_low=None #dly dly_high=None# dly while ((dly_low is None) or (dly_high is None)) and (dly > dly_optimal) and (dly < NUM_DLY_STEPS): meas=measure_block(dly, cmd_bit) if meas : if dly==(NUM_DLY_STEPS-1): dly = None break dly_low=dly dly=min(NUM_DLY_STEPS-1,dly+dly_try_step) else: dly_high=dly dly=max(dly_optimal,dly-dly_try_step) if quiet < 1 : print ("dly_low=%s, dly_high=%s, dly=%s"%(str(dly_low),str(dly_high),str(dly))) if (dly_low is None) or (dly_high is None): # dly is None: rslt.append(None) continue #find highest delay that is lower than margin (here delay monotonicity is assumed!) while dly_low < (dly_high-1): dly=(dly_low+dly_high)//2 meas=measure_block(dly, cmd_bit) if meas: dly_low=dly else: dly_high=dly rslt.append(dly_low) if quiet < 1 : print ("rslt=",rslt) if quiet < 1 : print ("final rslt=",rslt) return rslt cmd_odelay=[] for phase in range(numPhaseSteps): if quiet < 6: print (".",end="") sys.stdout.flush() cmd_odelay.append(cmd_phase_step(phase)) if quiet < 6: print () self.adjustment_state['cmd_meas']=cmd_odelay if quiet < 3: for phase, cdly in enumerate(cmd_odelay): print("%d"%(phase),end=" ") print("%s"%(str(cmda_odly[phase])),end=" ") if cdly: for b in cdly: if not b is None: print("%d"%(b),end=" ") else: print("?",end=" ") print() if quiet < 3: print("cmd_meas=",cmd_odelay) # Keeps refresh off? # Restore default write levelling sequence self.x393_pio_sequences.set_write_lev(16,False) # write leveling - 'good' mode (if it was not set so) return cmd_odelay def set_read_branch(self, wbuf_dly=9, quiet=1): """ Try read mode branches and find sel (early/late read command) and wbuf delay, if possible Detect shift by 1/2 clock cycle (should not happen), if it does proq_dqi_dqsi with duifferent prim_set (^2) is needed delay vs. phase should be already calibrated @param wbuf_dly - initial wbuf delay to try @quiet reduce output @return dictionary with key(s) (early,nominal,late) containing dictionary of {'wbuf_dly':xx, 'sel':Y} optionally result may contain key 'odd' with a list of varinats that resulted in odd number of wrong words if the remaining number of errors is odd """ if wbuf_dly is None: wbuf_dly=vrlg.DFLT_WBUF_DELAY try: delays_phase=self.adjustment_state['delays_phase'] except: print("Delays for phases (self.adjustment_state['delays_phase']) are not set, running 'get_delays_vs_phase' command ") try: delays_phase=self.get_delays_vs_phase(filter_dqo=2, filter_dqi=2, filter_dqso=2, filter_dqsi=2, filter_cmda=2, filter_read=0, filter_write=0, filter_rsel=None, filter_wsel=None, keep_all=False, set_table=True, quiet=quiet+2) self.adjustment_state['delays_phase']=delays_phase except: print ("Failed to execute get_'delays_vs_phase' command") return False #find one valid phase per existing branch phase_var={} for phase, dlys in enumerate(delays_phase): try: for k,v in dlys[DQI_KEY].items(): if (not k in phase_var) and (not None in v): phase_var[k]=phase except: pass if quiet <2: print ("phase_var=",phase_var) written=False #write/used block parameters startValue=0 ca=0 ra=0 ba=0 extraTgl=1 # just in case wsel=1 rslt={} odd_list=[] for var,phase in phase_var.items(): OK=self.set_phase_delays(phase=phase, refresh=True, quiet=quiet+1) # all the rest are defaults if not OK: raise Exception("set_read_branch(): failed to set phase = %d"%(phase)) if not written: #write_block_inc, it may turn out to be shifted, have problems at the beginning or end - write is not set up yet self.x393_pio_sequences.write_block_inc(num8=64, # max 512 16-bit words startValue=startValue, ca=ca, ra=ra, ba=ba, extraTgl=extraTgl, sel=wsel, quiet=quiet+1) written=True wbuf_dly_max=12 wdly=max(0,min(wbuf_dly,wbuf_dly_max)) rsel=0 #set_and_read_inc 8 16 0 0 1 1 last_wstep=0 read_problems=None for _ in range(20): self.x393_mcntrl_timing.axi_set_wbuf_delay(wdly) read_problems=self.x393_pio_sequences. set_and_read_inc(num8=8, # max 512 16-bit words ca=ca+16, ra=ra, ba=ba, sel=rsel, quiet=quiet+1) if (read_problems[0]>=4) or ((rsel==0) and (read_problems[0]>=2)): if last_wstep < 0: if quiet < 1: print ("reversed wstep to +1 at wdly = %d"%(wdly)) break last_wstep = 1 wdly += 1 if wdly >= wbuf_dly_max: print("Exceeded maximal write buffer delay = %d while setting up read for branch '%s', phase=%d"%(wdly,var,phase)) read_problems=None break # continue with next branch continue if (read_problems[1]>4) or (rsel and (read_problems[1] == 4)): if last_wstep > 0: if quiet < 1: print ("reversed wstep to -1 at wdly = %d"%(wdly)) break last_wstep =- 1 wdly -= 1 if wdly < 1: print("Exceeded minimal write buffer delay = %d while setting up read for branch '%s', phase=%d"%(wdly,var,phase)) read_problems=None break # continue with next branch continue break # close to target if read_problems is None: continue # could not get initial wbuf delay # read_problems_initial=read_problems read_problems_min=read_problems best_dw,best_sel=(0,0) if quiet < 2: print("var=",var) print("Read_problems_min=",read_problems_min) print("wdly=",wdly) print("rsel=",rsel) print("sum(read_problems_min)=",sum(read_problems_min)) if sum(read_problems_min) > 0: for dw,sel in ((-1,0),(-1,1),(0,0),(0,1),(1,0),(1,1)): self.x393_mcntrl_timing.axi_set_wbuf_delay(wdly+dw) read_problems=self.x393_pio_sequences. set_and_read_inc(num8=8, # max 512 16-bit words ca=ca+16, ra=ra, ba=ba, sel=sel ^ rsel, quiet=quiet+1) if sum(read_problems) < sum(read_problems_min): read_problems_min=read_problems best_dw,best_sel= dw,sel if sum(read_problems_min) == 0: break wdly += best_dw rsel ^= best_sel if quiet < 2: print("-Read_problems_min=",read_problems_min) print("-wdly=",wdly) print("-rsel=",rsel) print("-sum(read_problems_min)=",sum(read_problems_min)) if sum(read_problems_min) ==0: rslt[var]={'wbuf_dly':wdly, 'sel':rsel} if quiet < 2: print("-rslt=",rslt) elif (read_problems_min[0]%2) or (read_problems_min[1]%2): odd_list.append(var) if quiet < 3: print("Failed to find read settings for varinat '%s', phase=%d - best start read errors=%d, end read errors=%d"%( var,phase,read_problems_min[0],read_problems_min[1])) print("Odd number of wrong read words means that there is a half clock period shift, you may need to change") print("primary_set parameter of proc_dqi_dqsi() from 2 to 0" ) else: if quiet < 2: print("Failed to find read settings for varinat '%s', phase=%d - best start read errors=%d, end read errors=%d"%( var,phase,read_problems_min[0],read_problems_min[1])) if quiet < 2: print("odd_list=",odd_list) for var,v in rslt.items(): try: self.x393_mcntrl_timing.axi_set_wbuf_delay(v['wbuf_dly']) break except: pass if odd_list: rslt[ODD_KEY]=odd_list self.adjustment_state['read_variants']=rslt if quiet < 3: print ('read_variants=',rslt) return rslt def set_write_branch(self, dqs_pattern=None, quiet=1): """ Try write mode branches and find sel (early/late write command), even if it does not match read settings Read mode should already be set up if possible Detect shift by 1/2 clock cycle (should not happen), if it does proq_dqi_dqsi with duifferent prim_set (^2) is needed delay vs. phase should be already calibrated @param dqs_pattern - 0x55/0xaa - DQS output toggle pattern. When it is 0x55 primary_set_out is reversed ? @quiet reduce output @return dictioray with a key(s) (early,nominal,late) containing dictionary of {'wbuf_dly':xx, 'sel':Y} or value 'ODD' if the remaining number of errors is odd """ #write/used block parameters startValue=0 num8=8 # 8 bursts to read/write ca=0 ra=0 ba=0 extraTgl=1 # just in case wsel=1 rslt={} readVarKey='read_variants' if dqs_pattern is None: dqs_pattern=vrlg.DFLT_DQS_PATTERN else: # only set if not None self.x393_mcntrl_timing.axi_set_dqs_dqm_patterns(dqs_patt=dqs_pattern, # use default dqm_patt=None, # use default quiet=quiet+2) try: delays_phase=self.adjustment_state['delays_phase'] except: print("Delays for phases (self.adjustment_state['delays_phase']) are not set, running 'get_delays_vs_phase' command ") try: delays_phase=self.get_delays_vs_phase(filter_dqo=2, filter_dqi=2, filter_dqso=2, filter_dqsi=2, filter_cmda=2, filter_read=0, filter_write=0, filter_rsel=None, filter_wsel=None, keep_all=False, set_table=True, quiet=quiet+2) self.adjustment_state['delays_phase']=delays_phase except: print ("Failed to execute get_'delays_vs_phase' command") return False try: readVars=self.adjustment_state[readVarKey] except: raise Exception ("Read variants are not set up, need to run command set_read_branch90 first") #verify read varinats have valid (not "ODD") branch readBranch=None for var in readVars: if var != ODD_KEY: readBranch=var break else: raise Exception ("No valid read variant is found, can not proceed with write setup") readPhase=None #find one valid phase per existing branch phase_var={} read_phase_var={} for phase, dlys in enumerate(delays_phase): try: for k,v in dlys[DQO_KEY].items(): if not None in v: if not k in phase_var: phase_var[k]=phase if not k in read_phase_var: # read phase is not set yet for this write variant if not None in dlys[DQI_KEY][readBranch]: # usually will fail here phase_var[k]= phase read_phase_var[k] = phase readPhase= phase # will hold phase that except: pass if quiet <2: print ("phase_var=",phase_var) print ("readPhase=",readPhase) print ("read_phase_var=",read_phase_var) if not phase_var: raise Exception("Could not find any valid phase for writing to consider") if readPhase is None: #find some phase to correctly read data, even if it does not match any write phase for phase, dlys in enumerate(delays_phase): try: if not None in dlys[DQI_KEY][readBranch]: # usually will fail here readPhase= phase break except: pass else: raise Exception("Could not find any valid phase to read block correctly") #see if any of the write varinats does not have read phase - use default readPhase for var in phase_var: if not var in read_phase_var: read_phase_var[var]=readPhase if quiet <2: print ("phase_var=",phase_var) print ("readPhase=",readPhase) print ("read_phase_var=",read_phase_var) if quiet <1: print ("readVars=",readVars) self.x393_mcntrl_timing.axi_set_wbuf_delay(readVars[readBranch]['wbuf_dly']) rsel=readVars[readBranch]['sel'] odd_list=[] for var,phase in phase_var.items(): problems_min=None best_wsel=None for wsel in range (2): OK=self.set_phase_delays(phase=phase, refresh=True, quiet=quiet+2) # all the rest are defaults if not OK: raise Exception("set_read_branch(): failed to set phase = %d"%(phase)) self.x393_pio_sequences.write_block_inc(num8=num8, # max 512 16-bit words startValue=startValue, ca=ca, ra=ra, ba=ba, extraTgl=extraTgl, sel=wsel, quiet=quiet+1) startValue += 0x200 startValue &= 0xffff if phase != read_phase_var[var]: OK=self.set_phase_delays(phase=read_phase_var[var], refresh=True, quiet=quiet+2) # all the rest are defaults if not OK: raise Exception("set_read_branch(): failed to set phase = %d"%(phase)) problems=self.x393_pio_sequences. set_and_read_inc(num8=num8, # max 512 16-bit words ca=ca, ra=ra, ba=ba, sel=rsel, quiet=quiet+1) if (problems_min is None) or (sum(problems) < sum(problems_min)): problems_min=problems best_wsel=wsel if sum(problems_min) == 0: rslt[var]={'sel':best_wsel} elif (problems_min[0]%2) or (problems_min[1]%2): odd_list.append(var) if quiet < 3: print("Failed to find write settings for varinat '%s', phase=%d - best start write errors=%d, end write errors=%d, wsel=%d"%( var,phase,problems_min[0],problems_min[1],best_wsel)) print("Odd number of wrong read words means that there is a half clock period shift, you may need to change") print("primary_set parameter of proc_dqo_dqso() 2 <->0 or change DQS pattern (0x55<->0xAA)" ) print("Using of DQS PATTERN of 0xAA (output will start from 0, not 1) is not optimal, it requires extra toggling" ) print("of the DQS line after the end of block write" ) else: if quiet < 2: print("Failed to find write settings for varinat '%s', phase=%d - best start read errors=%d, end read errors=%d, wsel=%d"%( var,phase,problems_min[0],problems_min[1],best_wsel)) if odd_list: rslt[ODD_KEY]=odd_list self.adjustment_state['write_variants']=rslt if quiet < 3: print ('write_variants=',rslt) return rslt def get_phase_range(self, rsel=None, # None (any) or 0/1 wsel=None, # None (any) or 0/1 quiet=3): """ @param rsel filter by early/late read command (in two-clock command cycle - 'sel') Valid values: None, 0 or 1 @param wsel filter by early/late write command (in two-clock command cycle - 'sel') Valid values: None, 0 or 1 @param quiet reduce output @return {'optimal_phase': optimal phase, 'rsel': read_sel, 'wsel': write_sel, 'min_phase': minimal_phase, 'max_phase': maximal_phase} 'max_phase' may be lower than 'min_phase' if the range rolls over """ # self.load_mcntrl('dbg/state_0x55.pickle') try: read_variants=self.adjustment_state['read_variants'] except: read_variants=None try: write_variants=self.adjustment_state['write_variants'] except: write_variants=None try: delays_phase=self.get_delays_vs_phase(filter_dqo=2, filter_dqi=2, filter_dqso=2, filter_dqsi=2, filter_cmda=2, filter_read=True, filter_write=True, filter_rsel=rsel, filter_wsel=wsel, keep_all=False, set_table=True, # quiet=quiet+2) quiet=quiet) self.adjustment_state['delays_phase']=delays_phase except: print ("Failed to execute 'get_delays_vs_phase' command") return None phase_starts=[] numPhases=len(delays_phase) for phase,dlys in enumerate (delays_phase): if (not dlys is None) and (delays_phase[(phase-1) % numPhases] is None): phase_starts.append(phase) phase_lengths=[] if phase_starts: for phase_start in phase_starts: for phase in range(phase_start+1,phase_start+numPhases): p=phase % numPhases if delays_phase[p] is None: phase_lengths.append((p-phase_start) % numPhases) break best_len= max(phase_lengths) best_start=phase_starts[phase_lengths.index(best_len)] else: # no ends, maybe all None, or all non-None if delays_phase[0] is None: return None else: # all are valid best_start=0 best_len=numPhases #find center rslt={} optimal_phase= (best_start+best_len//2) % numPhases rslt['optimal_phase'] = optimal_phase rslt['min_phase'] = best_start rslt['max_phase'] = (best_start+best_len-1) % numPhases read_var="A" write_var="A" if quiet < 2: print ("result=",rslt) print ("phase_starts=",phase_starts) print ("phase_lengths=",phase_lengths) if not read_variants is None: # find common variants in read_variants and delays_phase[optimal_phase] - in the future there may be several of them! variants=[] for var in read_variants.keys(): if quiet < 1: print ("var=",var) print ("delays_phase[optimal_phase]=",delays_phase[optimal_phase]) print ("delays_phase[optimal_phase][DQI_KEY]=",delays_phase[optimal_phase][DQI_KEY]) if var in delays_phase[optimal_phase][DQI_KEY]: variants.append(var) if variants: rslt['rsel'] = read_variants[variants[0]]['sel'] read_var=variants[0][0].upper() if not write_variants is None: # find common variants in write_variants and delays_phase[optimal_phase] - in the future there may be several of them! variants=[] for var in write_variants.keys(): if var in delays_phase[optimal_phase][DQO_KEY]: variants.append(var) if variants: rslt['wsel'] = write_variants[variants[0]]['sel'] write_var=variants[0][0].upper() if quiet < 4: print ("result=",rslt) #set delays to set Verilog parameters. TODO: save sel-s somehow too? self.set_phase_delays(phase=optimal_phase, inp_period=read_var, out_period=write_var, refresh=False, delays_phase=delays_phase, # if None - use global quiet=quiet) return rslt def measure_all(self, tasks="ICWRPOASZ", prim_steps=1, primary_set_in=2, primary_set_out=2, dqs_pattern=0xaa, quiet=3): """ @param tasks - "C" cmda, "W' - write levelling, "R" - read levelling (DQI-DQSI), "P" - dqs input phase (DQSI-PHASE), "O" - output timing (DQ odelay vs DQS odelay), "A" - address/bank lines output delays, "Z" - print results @param prim_steps - compare measurement with current delay with one lower by 1 primary step (5 fine delay steps), 0 - compare with one fine step lower @param primary_set_in - which of the primary sets to use when processing DQi/DQSi results (2 - normal, 0 - other DQS phase) @param primary_set_out - which of the primary sets to use when processing DQo/DQSo results (2 - normal, 0 - other DQS phase) @param dqs_pattern - 0x55/0xaa - DQS output toggle pattern. When it is 0x55 primary_set_out is reversed ? @param quiet reduce output """ # dqs_pattern=0x55 # 0xaa max_phase_err=0.1 frac_step=0.125 # read_sel=1 # set DDR3 command in the second cycle of two (0 - during the first omne) read_bin_size=5 # dealy counts write_bin_size=5 # dealy counts read_scale_w=0.0 # weight of the "binary" results relative to "analog" write_scale_w=0.0 # weight of the "binary" results relative to "analog" idly_phase_sel=1 bin_size_ps=50.0 read_phase_scale_w=0.0 prim_steps_in=prim_steps prim_steps_out=prim_steps # primary_set_in=2 # primary_set_out=2 write_sel=1 # set DDR3 command in the second cycle of two (0 - during the first omne) safe_phase=0.25 # 0: strictly follow cmda_odelay, >0 -program with this fraction of clk period from the margin commonFine=True, # use same values for fine delay for address/bank lines addr_odly_max_err= 0.125 # 1/8 period task_data=[ {'key':'I', 'func':self.x393_pio_sequences.task_set_up, 'comment':'Initial setup - memory controller, sequnces', 'params':{'dqs_pattern':dqs_pattern, 'quiet':quiet+1}}, {'key':'C', 'func':self.adjust_cmda_odelay, 'comment':'Measuring CMDA output delay for each clock phase', 'params':{'start_phase':0, 'reinits':1, 'max_phase_err':max_phase_err, 'quiet':quiet+1}}, {'key':'W', 'func':self.adjust_write_levelling, 'comment':'Write levelling - measuring optimal DQS output delay for each phase', 'params':{'start_phase':0, 'reinits':1, 'invert':0, 'max_phase_err':max_phase_err, 'quiet':quiet+1}}, {'key':'A', 'func':self.measure_cmd_odelay, 'comment':'Measuring command (WE, RAS, CAS) lines output delays', 'params':{'safe_phase':safe_phase, 'reinits': 1, 'tryWrongWlev': 1, 'quiet':quiet+1}}, {'key':'R', 'func':self.measure_pattern, 'comment':'Read levelling - measuring predefined pattern to determine DQ input delays relative to DQS ones', 'params':{'compare_prim_steps':prim_steps_in, 'limit_step':frac_step, 'max_phase_err':max_phase_err, 'quiet':quiet+1}}, {'key':'R', 'func':self.proc_dqi_dqsi, 'comment':'Processing read levelling results using Levenberg-Marquardt algorithm to determine delay model parameters and optimal delay values', 'params':{'lane':'All', 'bin_size':read_bin_size, 'primary_set':primary_set_in, 'data_set_number':-1, # use measured data 'scale_w':read_scale_w, 'quiet':quiet+1}}, {'key':'P', 'func':self.measure_dqs_idly_phase, 'comment':'Measure optimal DQS input delays for clock phases (clock boundary crossing from DQS in to internal)', 'params':{'compare_prim_steps':prim_steps_in, 'frac_step':frac_step, 'sel':idly_phase_sel, 'quiet':quiet+1}}, {'key':'P', 'func':self.proc_dqsi_phase, # compare_prim_steps??? 'comment':'Calculate optimal DQS input delays vs. clock phase', 'params':{'lane':'All', 'bin_size_ps':bin_size_ps, 'data_set_number':-1, # use measured data 'scale_w':read_phase_scale_w, 'quiet':quiet+1}}, {'key':'O', 'func':self.measure_dqo_dqso, 'comment':'Measure write mode output delays on DQ lines relative to DQS output delays', 'params':{'compare_prim_steps':prim_steps_out, 'frac_step':frac_step, 'sel': write_sel, 'quiet':quiet+1}}, {'key':'O', 'func':self.proc_dqo_dqso, 'comment':'Processing DQ output delays to DQS output data results using Levenberg-Marquardt algorithm to determine optimal delays', 'params':{'lane':'All', 'bin_size':write_bin_size, 'primary_set':primary_set_out, 'data_set_number':-1, # use measured data 'scale_w':write_scale_w, 'quiet':quiet+1}}, {'key':'A', 'func':self.measure_addr_odelay, 'comment':'Measuring address and bank lines output delays', 'params':{'safe_phase':safe_phase, 'ra': 0, 'ba': 0, 'quiet':quiet+1}}, {'key':'A', 'func':self.proc_addr_odelay, 'comment':'Processing address and bank lines output delays (using average data for RAS,CAS, WE output delays)', 'params':{'commonFine':commonFine, 'max_err':addr_odly_max_err, 'quiet':quiet+1}}, {'key':'S', 'func':self.get_delays_vs_phase, 'comment':'Setting calculated delays to global parameters to be used when setting phase', 'params':{'filter_dqo': 2, 'filter_dqi': 2, 'filter_dqso':2, 'filter_dqsi':2, 'filter_cmda':2, 'filter_read':0, 'filter_write':0, 'filter_rsel':None, 'filter_wsel':None, 'keep_all':False, 'set_table':True, 'quiet':quiet+1}}, {'key':'S', 'func':self.save_mcntrl, 'comment':'Save current state as Python pickle', 'params':{'path': None, # use path defined by the parameter 'PICKLE' 'quiet':quiet+1}}, {'key':'Z', 'func':self.show_all_vs_phase, 'comment':'Printing results table (delays and errors vs. phase)- all, including invalid phases', 'params':{'keep_all':True, 'load_hardcoded':False}}, {'key':'Z', 'func':self.show_all_vs_phase, 'comment':'Printing results table (delays and errors vs. phase)- only for valid clock phase values', 'params':{'keep_all':False, 'load_hardcoded':False}}, ] start_time=time.time() last_task_start_time=start_time for task_item in task_data: # execute tasks in predefined sequence, if their keys are enabled through arguments (several tasks may be needed for 1 key) if task_item['key'] in tasks.upper(): tim=time.time() if quiet < 5: print ("[%.3f/+%.3f] %s"%(tim-start_time,tim-last_task_start_time,task_item['comment'])) print (" %s("%(task_item['func'].__name__),end="") # print ("task_item=",task_item) # print ("task_item['params']=",task_item['params']) for k,v in task_item['params'].items(): print ("%s=%s, "%(k,str(v)),end="") print(")") # TODO: print function name and used arguments task_item['func'](**task_item['params']) last_task_start_time=tim tim=time.time() if quiet < 5: print ("[%.3f/+%.3f] %s"%(tim-start_time,tim-last_task_start_time,"All Done")) def load_hardcoded_data(self): """ Debug feature - load hard-coded previously acquired/processed data to reduce debugging time for nest stages """ self.adjustment_state["dqi_dqsi"]= get_test_dq_dqs_data.get_dqi_dqsi() self.adjustment_state["maxErrDqsi"]= get_test_dq_dqs_data.get_maxErrDqsi() self.adjustment_state["dqi_dqsi_parameters"]= get_test_dq_dqs_data.get_dqi_dqsi_parameters() self.adjustment_state["dqo_dqso"]= get_test_dq_dqs_data.get_dqo_dqso() self.adjustment_state["maxErrDqso"]= get_test_dq_dqs_data.get_maxErrDqso() self.adjustment_state["dqo_dqso_parameters"]= get_test_dq_dqs_data.get_dqo_dqso_parameters() self.adjustment_state.update(get_test_dq_dqs_data.get_adjust_cmda_odelay()) self.adjustment_state.update(get_test_dq_dqs_data.get_wlev_data()) self.adjustment_state.update(get_test_dq_dqs_data.get_dqsi_phase()) # self.adjustment_state['addr_odelay_meas']= get_test_dq_dqs_data.get_addr_meas() self.adjustment_state['addr_meas']= get_test_dq_dqs_data.get_addr_meas() self.adjustment_state['addr_odelay']= get_test_dq_dqs_data.get_addr_odly() self.adjustment_state['cmd_meas']= get_test_dq_dqs_data.get_cmd_meas() def proc_dqi_dqsi(self, lane="all", bin_size=5, primary_set=2, # compare_prim_steps=True, # while scanning, compare this delay with 1 less by primary(not fine) step, # # save None for fraction in unknown (previous -0.5, next +0.5) data_set_number=2, # not number - use measured data scale_w=0.0, # weight for "uncertain" values (where samples chane from all 0 to all 1 in one step) quiet=1): """ Run DQ vs DQS fitting for one data lane (0 or 1) using earlier acquired hard-coded data @param lane byte lane to process (or non-number - process all byte lanes of the device) @param bin_size bin size for the histograms (should be 5/10/20/40) @param primary_set which of the data edge series to use as leading (other will be trailing by 180) @param data_set_number select one of the hard-coded data sets (sets 0 and 1 use comparing with the data 1 fine step below set #2 (default) used measurement with previous primary step measurement (will not suffer from fine range wider than on primary step) If not number or <0 - use measured data @param quiet reduce output @param scale_w weight for "uncertain" values (where samples change from all 0 to all 1 in one step) For sufficient data 0.0 is OK (and seems to work better)- only "analog" samples are considered @return 3-element dictionary of ('early','nominal','late'), each being None or a 160-element list, each element being either None, or a list of 3 best DQ delay values for the DQS delay (some mey be None too) """ if quiet < 3: print ("proc_dqi_dqsi(): scale_w=%f"%(scale_w)) if isinstance (data_set_number,(int,long)) and (data_set_number>=0) : if quiet < 4: print("Using hard-coded data set #%d"%data_set_number) compare_prim_steps=get_test_dq_dqs_data.get_compare_prim_steps_in(data_set_number) meas_data=get_test_dq_dqs_data.get_data_in(data_set_number) else: if quiet < 4: print("Using measured data set") try: compare_prim_steps=self.adjustment_state["patt_prim_steps"] meas_data= self.adjustment_state["patt_meas_data"] except: print ("Pattern-measured data is not available, exiting") return meas_delays=[] for data in meas_data: if data: bits=[None]*16 for b,pData in enumerate(data): if pData: bits[b]=[None]*4 for inPhase in (0,1): if pData[inPhase]: for e in (0,1): if pData[inPhase][e]: bits[b][inPhase*2+e]=pData[inPhase][e]# [0] meas_delays.append(bits) if quiet<1: x393_lma.test_data(meas_delays,compare_prim_steps,quiet) lma=x393_lma.X393LMA() rslt = lma.lma_fit_dq_dqs(lane, bin_size, 1000.0*self.x393_mcntrl_timing.get_dly_steps()['SDCLK_PERIOD'], # 2500.0, # clk_period, 78.0, # dly_step_ds, primary_set, meas_delays, compare_prim_steps, scale_w, quiet) if quiet < 4: lma.showENLresults(rslt) self.adjustment_state["dqi_dqsi_parameters"]=rslt.pop('parameters') try: self.adjustment_state["maxErrDqsi"]=rslt.pop('maxErrDqs') if quiet < 4: print("maxErrDqsi={") for k,v in self.adjustment_state["maxErrDqsi"].items(): print ("'%s':%s,"%(k,str(v))) print ("}") except: print ("maxErrDqs does not exist") if quiet < 4: print ("dqi_dqsi={") for k,v in rslt.items(): print ("'%s':%s,"%(k,str(v))) print ("}") self.adjustment_state["dqi_dqsi"]=rslt return rslt def proc_dqsi_phase(self, lane=0, # "all", bin_size_ps=50, data_set_number=0, # not number - use measured data scale_w=0.1, # weight for "uncertain" values (where samples chane from all 0 to all 1 in one step) quiet=1): """ Run DQSI vs PHASE fitting for one data lane (0 or 1) using earlier acquired hard-coded data @param lane byte lane to process (or non-number - process all byte lanes of the device) @param bin_size_ps histogram bin size (in ps) @param data_set_number select one of the hard-coded data sets (sets 0 and 1 use comparing with the data 1 fine step below set #0 (default) used measurement with previous primary step measurement (will not suffer from fine range wider than on primary step) If not number or <0 - use measured data @param scale_w weight for "uncertain" values (where samples change from all 0 to all 1 in one step) For sufficient data 0.0 is OK (and seems to work better)- only "analog" samples are considered @param quiet reduce output @return 3-element dictionary of ('early','nominal','late'), each being None or a 160-element list, each element being either None, or a list of 3 best DQ delay values for the DQS delay (some mey be None too) """ if quiet < 3: print ("proc_dqsi_phase(): scale_w=%f"%(scale_w)) if isinstance (data_set_number,(int,long)) and (data_set_number>=0) : self.load_hardcoded_data() if quiet < 4: print("Using hard-coded data set #%d"%data_set_number) compare_prim_steps= get_test_dq_dqs_data.get_dqsi_vs_phase_prim_steps(data_set_number) dqsi_phase_data= get_test_dq_dqs_data.get_dqsi_vs_phase(data_set_number) dqsi_dqi_parameters=get_test_dq_dqs_data.get_dqi_dqsi_parameters() else: if quiet < 4: print("Using measured data set") try: compare_prim_steps= self.adjustment_state["dqsi_vs_phase_steps"] dqsi_phase_data= self.adjustment_state["dqsi_vs_phase"] dqsi_dqi_parameters= self.adjustment_state["dqi_dqsi_parameters"] except: print ("DQS input delay vs. phase measured data is not available, exiting") return timing=self.x393_mcntrl_timing.get_dly_steps() numPhaseSteps= int(timing['SDCLK_PERIOD']/timing['PHASE_STEP']+0.5) lma=x393_lma.X393LMA() # use persistent one? # print("++++++proc_dqsi_phase(), quiet=",quiet) dqsi_phase=lma.lma_fit_dqsi_phase(lane, # byte lane bin_size_ps, 1000.0*self.x393_mcntrl_timing.get_dly_steps()['SDCLK_PERIOD'], # 2500.0, # clk_period, dqsi_dqi_parameters, dqsi_phase_data, # data_set, compare_prim_steps, scale_w, numPhaseSteps, quiet) self.adjustment_state.update(dqsi_phase) return dqsi_phase def proc_dqo_dqso(self, lane="all", bin_size=5, primary_set=2, # compare_prim_steps=True, # while scanning, compare this delay with 1 less by primary(not fine) step, # # save None for fraction in unknown (previous -0.5, next +0.5) data_set_number=0, # not number - use measured data scale_w=0.0, # weight for "uncertain" values (where samples chane from all 0 to all 1 in one step) quiet=1): """ Run DQ vs DQS fitting for one data lane (0 or 1) using earlier acquired hard-coded data @param lane byte lane to process (or non-number - process all byte lanes of the device) @param bin_size bin size for the histograms (should be 5/10/20/40) @param primary_set which of the data edge series to use as leading (other will be trailing by 180) @param data_set_number select one of the hard-coded data sets (sets 0 and 1 use comparing with the data 1 fine step below set #2 (default) used measurement with previous primary step measurement (will not suffer from fine range wider than on primary step) If not number or <0 - use measured data @param scale_w weight for "uncertain" values (where samples change from all 0 to all 1 in one step) For sufficient data 0.0 is OK (and seems to work better)- only "analog" samples are considered @param quiet reduce output @return 3-element dictionary of ('early','nominal','late'), each being None or a 160-element list, each element being either None, or a list of 3 best DQ delay values for the DQS delay (some mey be None too) """ if quiet < 3: print ("proc_dqi_dqsi(): scale_w=%f"%(scale_w)) if isinstance (data_set_number,(int,long)) and (data_set_number>=0) : if quiet < 4: print("Using hard-coded data set #%d"%data_set_number) compare_prim_steps=get_test_dq_dqs_data.get_compare_prim_steps_out(data_set_number) meas_data=get_test_dq_dqs_data.get_data_out(data_set_number) else: if quiet < 4: print("Using measured data set") try: compare_prim_steps=self.adjustment_state["write_prim_steps"] meas_data= self.adjustment_state["write_meas_data"] except: print ("Pattern-measured data is not available, exiting") return meas_delays=[] for data in meas_data: if data: bits=[None]*16 for b,pData in enumerate(data): if pData: bits[b]=[None]*4 for inPhase in (0,1): if pData[inPhase]: for e in (0,1): if pData[inPhase][e]: bits[b][inPhase*2+e]=pData[inPhase][e]# [0] meas_delays.append(bits) if quiet<1: x393_lma.test_data(meas_delays,compare_prim_steps,quiet) lma=x393_lma.X393LMA() rslt = lma.lma_fit_dq_dqs(lane, bin_size, 1000.0*self.x393_mcntrl_timing.get_dly_steps()['SDCLK_PERIOD'], # 2500.0, # clk_period, 78.0, # dly_step_ds, primary_set, meas_delays, compare_prim_steps, scale_w, quiet) if quiet < 4: lma.showENLresults(rslt) self.adjustment_state["dqi_dqsi_parameters"]=rslt.pop('parameters') try: self.adjustment_state["maxErrDqso"]=rslt.pop('maxErrDqs') if quiet < 4: print("maxErrDqso={") for k,v in self.adjustment_state["maxErrDqso"].items(): print ("'%s':%s,"%(k,str(v))) print ("}") except: print ("maxErrDqs does not exist") if quiet < 4: print ("dqi_dqsi={") for k,v in rslt.items(): print ("'%s':%s,"%(k,str(v))) print ("}") self.adjustment_state["dqo_dqso"]=rslt return rslt def proc_addr_odelay(self, commonFine=True, # use same values for fine delay max_err=0.125, # 1/8 period quiet=2): """ Process delay calibration data for address and bank line, calculate delay scales, shift and rise/fall differences. Calculate finedelay corrections and finally optimal delay value for each line each phase """ # self.load_hardcoded_data() # TODO: TEMPORARY - remove later try: # addr_odelay=self.adjustment_state['addr_odelay_meas'] addr_odelay=self.adjustment_state['addr_meas'] except: print("No measurement data for address and bank lines is available. Please run 'measure_addr_odelay' first or load 'load_hardcoded_data'") return None try: cmd_odelay=self.adjustment_state['cmd_meas'] except: print("No measurement data for command (WE,RAS,CAS) lines is available. Please run 'measure_cmd_odelay' first or load 'load_hardcoded_data'") return None numCmdLines=3 numABLines=vrlg.ADDRESS_NUMBER+3 numLines= numABLines+numCmdLines # print('addr_odelay=','addr_odelay') dly_steps=self.x393_mcntrl_timing.get_dly_steps() numPhaseSteps= int(dly_steps['SDCLK_PERIOD']/dly_steps['PHASE_STEP']+0.5) phase_step=1000.0*dly_steps['PHASE_STEP'] clk_period=1000.0*dly_steps['SDCLK_PERIOD'] if quiet <1: print ("phase_step=%f, clk_period=%f"%(phase_step,clk_period)) try: cmda_odly_a=self.adjustment_state["cmda_odly_a"] except: raise Exception ("No cmda_odly_a is available.") try: cmda_odly_b=self.adjustment_state["cmda_odly_b"] except: raise Exception ("No cmda_odly_b is available.") #Combine measurements for address and command lines # print ("cmd_odelay=",cmd_odelay) for phase in range (numPhaseSteps): if (not addr_odelay[0][phase] is None) or (not cmd_odelay[phase] is None): if addr_odelay[0][phase] is None: addr_odelay[0][phase]=[None]*numABLines if cmd_odelay[phase] is None: cmd_odelay[phase]=[None]*numCmdLines addr_odelay[0][phase] += cmd_odelay[phase] if not addr_odelay[1][phase] is None: addr_odelay[1][phase] += [None]*numCmdLines tSA=-clk_period/(numPhaseSteps*cmda_odly_a) # positive variantStep=-cmda_odly_a*numPhaseSteps #how much b changes when moving over the full SDCLK period tA=cmda_odly_b*tSA-clk_period/2 while tA < 0: tA+=clk_period tOpt=cmda_odly_b*tSA while tOpt < 0: tOpt+=clk_period if quiet <1: print ("cmda_odly_a=%f, cmda_odly=%f, tSA=%f ps/step, tA=%f ps (%f), variantStep=%f, tOpt=%f(%f)"%(cmda_odly_a,cmda_odly_b, tSA, tA, tA/tSA, variantStep,tOpt,tOpt/tSA)) # return parameters={# set TSA from cmda_odelay B 'tA': [tA]*numLines, # static delay in each line, ps # set TSA from cmda_odelay A 'tSA': [tSA]*numLines, # delay per one finedelay step, ps 'tAHL':[0.0]*numLines, # time high - time low, ps 'tAF' :[0.0]*((NUM_FINE_STEPS-1)*numLines), # fine delay correction (first 4, last is minus sum of the first 4) 'tAFW':[0.0]*((NUM_FINE_STEPS)*numLines) # weight of tAF averaging } if quiet <1: print("parameters=",parameters) def proc_addr_step(indx, corrFine=False, useVarStep=False): tAF5=parameters['tAF'][(NUM_FINE_STEPS-1)*indx:(NUM_FINE_STEPS-1)*(indx+1)] tAF5.append(-sum(tAF5)) tAF5.append(tAF5[0]) if (useVarStep): tAF5C=[0.5*(tAF5[i]+tAF5[i+1]+ parameters['tSA'][indx]) for i in range(5)]# includes half-step else: tAF5C=[tAF5[i]+ 0.5*parameters['tSA'][indx] for i in range(5)]# includes half-step if quiet <1: print ("tAF5=",tAF5) print ("tAF5C=",tAF5C) S0=0 SX=0 SY=0 SX2=0 SXY=0 sAF5=[0.0]*NUM_FINE_STEPS nAF5=[0]*NUM_FINE_STEPS s01= [0.0]*len(addr_odelay) n01= [0]*len(addr_odelay) for edge,pol_data in enumerate(addr_odelay): for phase, phase_data in enumerate(pol_data): # print("##### ",phase,phase_data) if (not phase_data is None) and (not phase_data[indx] is None): dly=phase_data[indx] # y=-(phase_step*phase+tAF5C[dly %NUM_FINE_STEPS]) y=-(phase_step*phase-tAF5C[dly %NUM_FINE_STEPS]) diff0= y+parameters['tA'][indx]-parameters['tSA'][indx]*dly periods=int(round(diff0/clk_period)) y-=periods*clk_period diff=y+parameters['tA'][indx]-parameters['tSA'][indx]*dly #find closest period and add/subract S0+=1 SX+=dly SY+=y SX2+=dly*dly SXY+=dly*y sAF5[dly % NUM_FINE_STEPS]+=diff nAF5[dly % NUM_FINE_STEPS]+=1 s01[edge]+=diff n01[edge]+=1 if quiet <1: print("%d %d %d %f %f %f"%(edge, phase,dly,y,diff0,diff)) avgF=0.0 for i in range (NUM_FINE_STEPS): if nAF5[i]: sAF5[i]/=nAF5[i] avgF+=sAF5[i] avgF/=NUM_FINE_STEPS for edge in range(len(addr_odelay)): if n01[edge]: s01[edge] /= n01[edge] else: s01[edge] = None # commands have onl;y one edge tested if quiet <2: print ("avgF=",avgF) print ("sAF5=",sAF5) print ("nAF5=",nAF5) print ("s01=",s01) if quiet <2: print ("parameters['tSA'][%d]="%indx,parameters['tSA'][indx], " (old)") print ("parameters['tA'][%d]="%indx,parameters['tA'][indx], " (old)") print ("parameters['tAHL'][%d]="%indx,parameters['tAHL'][indx], " (old)") print ("tAF4=",parameters['tAF'][4*indx:4*(indx+1)], "(old)") parameters['tSA'][indx] = (SXY*S0 - SY*SX) / (SX2*S0 - SX*SX) parameters['tA'][indx] = - (SY*SX2 - SXY*SX) / (SX2*S0 - SX*SX) try: parameters['tAHL'][indx] = 2*(s01[0]-s01[1]) except: parameters['tAHL'][indx] = None if corrFine: for i in range (NUM_FINE_STEPS-1): # parameters['tAF'][(NUM_FINE_STEPS-1)*indx+i] += sAF5[i] - avgF parameters['tAF'][(NUM_FINE_STEPS-1)*indx+i] -= sAF5[i] - avgF for i in range (NUM_FINE_STEPS): parameters['tAFW'][NUM_FINE_STEPS*indx+i] =nAF5[i] if quiet <2: print ("parameters['tSA'][%d]="%indx,parameters['tSA'][indx]) print ("parameters['tA'][%d]="%indx,parameters['tA'][indx]) print ("parameters['tAHL'][%d]="%indx,parameters['tAHL'][indx]) print ("tAF4=",parameters['tAF'][4*indx:4*(indx+1)]) print ("parameters=",parameters) # correct finedelay values def average_finedelays(): tAF5A=[0.0]*NUM_FINE_STEPS tAF5W=[0.0]*NUM_FINE_STEPS for line in range(numLines): tAF5Ai=parameters['tAF'][(NUM_FINE_STEPS-1)*line:(NUM_FINE_STEPS-1)*(line+1)] tAF5Ai.append(-sum(tAF5Ai)) tAF5Wi=parameters['tAFW'][NUM_FINE_STEPS*line:NUM_FINE_STEPS*(line+1)] for i in range (NUM_FINE_STEPS): tAF5A[i]+=tAF5Ai[i]*tAF5Wi[i] tAF5W[i]+=tAF5Wi[i] for i in range (NUM_FINE_STEPS): if tAF5W[i] > 0.0: tAF5A[i]/=tAF5W[i] avg=sum(tAF5A) / NUM_FINE_STEPS if quiet<2: print ("tAF5A=",tAF5A) print ("tAF5W=",tAF5W) print ("avg=",avg) for i in range (NUM_FINE_STEPS): tAF5A[i]-=avg return tAF5A def get_optimal_dlys(phase,maxErr): """ return list of delays and maximal error expects average values in the last item(s) use average to find the branch, all others - in the same branch """ avg_index=numLines # len(parameters['tA'])-1 num_items=numLines+1 s_avg=parameters['tSA'][avg_index] t_avg= parameters['tA'][avg_index]-phase_step*phase - clk_period/2 periods=0 avg_max_delay=s_avg*NUM_DLY_STEPS # maximal delay with average line while t_avg < 0: t_avg += clk_period periods += 1 if t_avg > avg_max_delay: t_avg -= clk_period periods -= 1 if t_avg < -clk_period*maxErr: if quiet < 2: print ("No solution for average signal for phase=",phase) return None delays=[] worst_err=0 # if quiet < 1: print ("Phase=%d"%(phase)) # print("num_items=",num_items) # print("len(parameters['tA'])=",len(parameters['tA'])) # print("len(parameters['tSA'])=",len(parameters['tSA'])) # print("len(parameters['tAF'])=",len(parameters['tAF'])) for line in range(num_items): #+1): tAF5=parameters['tAF'][(NUM_FINE_STEPS-1)*line:(NUM_FINE_STEPS-1)*(line+1)] tAF5.append(-sum(tAF5)) best_dly=None best_err=None if quiet < 1: dbg_dly=[] for dly in range (NUM_DLY_STEPS): #TODO: verify finedelay polarity try: t_dly=parameters['tA'][line]-parameters['tSA'][line]*dly -phase_step*phase - clk_period/2 + tAF5[dly %NUM_FINE_STEPS] + periods*clk_period except: print ("line=",line) print ("parameters['tA']=",parameters['tA']) print ("parameters['tSA']=",parameters['tSA']) print ("tAF5=",tAF5) raise Exception("That's all") # t_dly=parameters['tA'][line]-parameters['tSA'][line]*dly -phase_step*phase - clk_period/2 - tAF5[dly %NUM_FINE_STEPS] + periods*clk_period if quiet < 1: dbg_dly.append(t_dly) if (best_dly is None) or (abs(t_dly) < abs(best_err)): best_dly=dly best_err=t_dly if quiet < 1: print ("%f"%(best_err),end=" ") delays.append(best_dly) if worst_err< abs(best_err): worst_err = abs(best_err) if quiet < 1: print () print (dbg_dly) if worst_err > maxErr*clk_period: if quiet < 2: print ("Worst signal error (%f ps) is too high (>%f ps, %f clk_periods) fpr phase %d"%(worst_err, maxErr*clk_period,maxErr, phase)) return None return {'err':worst_err, 'dlys':delays} pass #main method body: for line in range(numLines): for _ in range (6): proc_addr_step(line,1,0) if quiet<3: print ("parameters=",parameters) if commonFine: tAF5A=average_finedelays() for line in range(numLines): for i in range (NUM_FINE_STEPS-1): parameters['tAF'][(NUM_FINE_STEPS-1)*line+i] = tAF5A[i] for line in range(numLines): for _ in range (2): proc_addr_step(line,0,1) # Calculate average parameters (to be used for command bits until found better measurement for them: # print ("0:len(parameters['tAFW'])=",len(parameters['tAFW'])) parameters['tAF'] += average_finedelays()[:NUM_FINE_STEPS-1] # do only once - increases length of parameters items # print ("1:len(parameters['tAFW'])=",len(parameters['tAFW'])) for k in ("tSA",'tA','tAHL'): try: parameters[k].append(sum(parameters[k])/numLines) except: s=0.0 n=0 for d in parameters[k]: if not d is None: s+=d n+=1 if n>0: parameters[k].append(s/n) else: parameters[k].append(None) tAF5A=average_finedelays() if quiet<3: print ("parameters=",parameters) #find best solutions/errors delays=[] errors=[] for phase in range(numPhaseSteps): dly_err=get_optimal_dlys(phase,max_err) if not dly_err is None: delays.append(dly_err['dlys']) errors.append(dly_err['err']) else: delays.append(None) errors.append(None) if quiet < 4: for phase in range(numPhaseSteps): print("%d"%(phase), end=" ") if not delays[phase] is None: for d in delays[phase]: print ("%s"%(str(d)),end=" ") print ("%s"%(str(errors[phase])),end=" ") print() rslt={'err':errors, 'dlys':delays} if quiet < 4: print("addr_odelay={") print("'dlys': ",rslt['dlys'],",") print("'err': ",rslt['err']) print("}") if quiet<3: print ("parameters=",parameters) self.adjustment_state['addr_odelay']= rslt return rslt def get_delays_vs_phase(self, filter_dqo=2, filter_dqi=2, filter_dqso=2, filter_dqsi=2, filter_cmda=2, filter_read=0, filter_write=0, filter_rsel=None, filter_wsel=None, keep_all=False, set_table=True, quiet=2): """ Create list of valid "phases", after applying several filters. Each filter parameter can be one of 3 values: 0 - do not apply filter, 1 - OK if each of multiple bits fit for at least one early/nominal/late branch and 2 - all of the bits should fit the same branch @param filter_dqo for DQ output delays @param filter_dqi for DQ input delays @param filter_dqso for DQS output delays @param filter_dqsi for DQS input delays @param filter_cmda for command and address output delays if non-integer - special mode - try to keep cmda low, with setup violation to test marginal phase @param filter_read remove phases that do not provide correct reads (data shifted by some periods/half periods) @param filter_write remove phases that do not provide correct writes (data shifted by some periods/half periods) @param filter_rsel filter by early/late read command (in two-clock command cycle - 'sel') Valid values: None, 0 or 1 @param filter_wsel filter by early/late write command (in two-clock command cycle - 'sel') Valid values: None, 0 or 1 @param keep_all Keep phases where some delays do not have valid values, just mark them as None remove just items that do not have any non-None elements @param set_table store results to the global table (used to simultaneously set all pahse-derived delays in hardware @param quiet reduce output @return per-phase value list, each being none or a dictionary with optional fields "in" and "out" having values early/nominal/late """ # self.load_hardcoded_data() # TODO: REMOVE LATER num_addr=vrlg.ADDRESS_NUMBER num_banks=3 # num_cmd=3 # (we,ras,cas,cke,odt) rslt_names=("early","nominal","late") timing=self.x393_mcntrl_timing.get_dly_steps() numPhaseSteps= int(timing['SDCLK_PERIOD']/timing['PHASE_STEP']+0.5) step180= int(NUM_FINE_STEPS*0.5* timing['SDCLK_PERIOD'] / timing['DLY_STEP'] +0.5) halfDlyRange=min(NUM_DLY_STEPS//2, step180) # minimum of half adjustment range of the delay and half SDCLK period # clk_period=1000.0*timing['SDCLK_PERIOD'] # in PS if quiet <1: print ("halfDlyRange=",halfDlyRange) try: read_variants=self.adjustment_state['read_variants'] except: read_variants=None try: write_variants=self.adjustment_state['write_variants'] except: write_variants=None if filter_read or (not filter_rsel is None): if not read_variants is None: filter_read=1 # if filter_rsel is not None if filter_dqsi==0: filter_dqsi=2 if filter_dqi==0: filter_dqi=2 else: print ('Data for filter_read is not available (self.adjustment_state["read_variants"]') filter_read=0 filter_rsel=None if filter_write or (not filter_wsel is None): if not write_variants is None: filter_write=1 # if filter_wsel is not None if filter_dqso==0: filter_dqso=2 if filter_dqo==0: filter_dqo=2 else: print ('Data for filter_write is not available (self.adjustment_state["write_variants"]') filter_write=0 filter_wsel=None delays_phase=[] if filter_dqo: try: DQOvDQSO=self.adjustment_state["dqo_dqso"] except: print ('Data for filter_dqo is not available (self.adjustment_state["dqo_dqso"]') filter_dqo=0 try: wlev_dqs_bspe=self.adjustment_state['wlev_dqs_bspe'] except: print ('Data for filter_dqo requires data for filter_dqso and it is not available (self.adjustment_state["wlev_dqs_bspe"]') filter_dqo=0 if filter_dqi: try: DQIvDQSI=self.adjustment_state["dqi_dqsi"] except: print ('Data for filter_dqi is not available (self.adjustment_state["dqi_dqsi"]') filter_dqi=0 try: dqsi_phase=self.adjustment_state['dqsi_phase'] except: print ('Data for filter_dqi requires data for filter_dqsi and it is not available (self.adjustment_state["dqsi_phase"]') filter_dqi=0 if filter_dqso: try: wlev_dqs_bspe=self.adjustment_state['wlev_dqs_bspe'] except: print ('Data for filter_dqso is not available (self.adjustment_state["wlev_dqs_bspe"]') filter_dqso=0 if filter_dqsi: try: dqsi_phase=self.adjustment_state['dqsi_phase'] except: print ('Data for filter_dqsi is not available (self.adjustment_state["dqsi_phase"]') filter_dqsi=0 if filter_cmda: try: addr_odelay= self.adjustment_state['addr_odelay'] except: addr_odelay=None #keep cmda_bspe even if addr_odelay is available? try: cmda_bspe=self.adjustment_state['cmda_bspe'] except: if addr_odelay is None: print ('Data for filter_cmda is not available (self.adjustment_state["cmda_bspe"]') filter_cmda=0 if isinstance(filter_cmda,float): if filter_cmda >=0.5: print ("Invalid 'safe range' for filter_cmda. It is measured in clock cycles and should be < 0.5.Using strict cmda filter") filter_cmda=2 num_cmda=0 for phase in range(numPhaseSteps): delays_phase.append({}) if filter_cmda: if (not addr_odelay is None) and (not isinstance(filter_cmda,float)): #TODO: add zerr to addr_odelay (error as a fraction of clock period if delay is set to 0) #special case to test address setup time - will never execute now if isinstance(filter_cmda,float) and (not addr_odelay['err'][phase] is None) and (addr_odelay['err'][phase] < 0.5-filter_cmda): delays_phase[phase]['cmda']=0 else: if (addr_odelay['dlys'][phase] is None) and (not keep_all): delays_phase[phase]=None continue # next phase else: if not addr_odelay['dlys'][phase] is None: delays_phase[phase]['cmda']=addr_odelay['dlys'][phase] # list, not single value! num_cmda=len(addr_odelay['dlys'][phase]) else: #special case to test address setup time if isinstance(filter_cmda,float) and (not cmda_bspe[phase]['zerr'] is None) and (cmda_bspe[phase]['zerr']< 0.5-filter_cmda): delays_phase[phase]['cmda']=0 else: if (cmda_bspe[phase]['ldly'] is None) and (not keep_all): delays_phase[phase]=None continue # next phase else: if not cmda_bspe[phase]['ldly'] is None: delays_phase[phase]['cmda']=cmda_bspe[phase]['ldly'] #all(v is None for v in l) if filter_dqsi: dqsi=[dqsi_lane[phase] for dqsi_lane in dqsi_phase] if None in dqsi: if keep_all: if not all(v is None for v in dqsi): delays_phase[phase]['dqsi']=dqsi else: delays_phase[phase]=None continue # next phase elif (filter_dqsi == 2) and ((max(dqsi) - min(dqsi)) > halfDlyRange): delays_phase[phase]=None continue # next phase else: delays_phase[phase]['dqsi']=dqsi if filter_dqso: dqso=[None if wlev_lane[phase] is None else wlev_lane[phase]['ldly'] for wlev_lane in wlev_dqs_bspe] if None in dqso: if keep_all: if not all(v is None for v in dqso): delays_phase[phase]['dqso']=dqso else: delays_phase[phase]=None continue # next phase elif (filter_dqsi == 2) and ((max(dqso) - min(dqso)) > halfDlyRange): delays_phase[phase]=None continue # next phase else: delays_phase[phase]['dqso']=dqso if filter_dqi: dqsi=[dqsi_lane[phase] for dqsi_lane in dqsi_phase] if (None in dqsi) and (not keep_all): delays_phase[phase]=None # phase for at least one of the DQSI is invalid continue # next phase else: dqi_options={} for k in rslt_names: if (k in DQIvDQSI) and (not DQIvDQSI[k] is None): dqi= sum([[None]*8 if (dqs is None) or (DQIvDQSI[k][dqs] is None) else DQIvDQSI[k][dqs][8*lane:8*(lane+1)] for lane,dqs in enumerate(dqsi)], []) if keep_all: if not all(v is None for v in dqi): dqi_options[k]=dqi else: if None in dqi: continue # not this branch elif filter_read and not k in read_variants: continue # not this branch elif (not filter_rsel is None) and (k in read_variants) and (read_variants[k]['sel'] != filter_rsel): continue # not this branch elif (filter_dqi == 2) and ((max(dqi) - min(dqi)) > halfDlyRange): continue # failed filter, continue to the next branch dqi_options[k]=dqi if dqi_options: delays_phase[phase]['dqi']=dqi_options elif not keep_all: delays_phase[phase]=None continue # next phase if filter_dqo: dqso=[None if wlev_lane[phase] is None else wlev_lane[phase]['ldly'] for wlev_lane in wlev_dqs_bspe] if (None in dqso) and (not keep_all): delays_phase[phase]=None # phase for at least one of the DQSI is invalid continue # next phase else: dqo_options={} for k in rslt_names: if (k in DQOvDQSO) and (not DQOvDQSO[k] is None): dqo= sum([[None]*8 if (dqs is None) or (DQOvDQSO[k][dqs] is None) else DQOvDQSO[k][dqs][8*lane:8*(lane+1)] for lane,dqs in enumerate(dqso)], []) if keep_all: if not all(v is None for v in dqo): dqo_options[k]=dqo else: if None in dqo: continue # not this branch elif filter_write and not k in write_variants: continue # not this branch elif (not filter_wsel is None) and (k in write_variants) and (write_variants[k]['sel'] != filter_wsel): continue # not this branch elif (filter_dqi == 2) and ((max(dqo) - min(dqo)) > halfDlyRange): continue # failed filter, continue to the next branch dqo_options[k]=dqo if dqo_options: delays_phase[phase]['dqo']=dqo_options elif not keep_all: delays_phase[phase]=None continue # next phase if quiet <1: print("delays_phase=",delays_phase) if quiet < 2: print ("delays_phase=[") prev_none=False for phase_data in delays_phase: if phase_data is None: if not prev_none: print("\n ",end="") print ("None,",end=" ") prev_none=True else: if prev_none: print() print(" {") for k,v in phase_data.items(): if isinstance(v,str): v="'"+v+"'" print (" '%s':%s"%(k, str(v))) print (" },") prev_none=False print("]") if quiet < 3: enl_list_in=[] enl_list_out=[] numBits=None for k in rslt_names: for phase_data in delays_phase: try: if k in phase_data["dqi"]: enl_list_in.append(k) break except: pass for phase_data in delays_phase: try: if k in phase_data["dqo"]: enl_list_out.append(k) break except: pass for phase_data in delays_phase: try: numBits=len(phase_data["dqi"].values()[0]) break except: pass try: numBits=len(phase_data["dqo"].values()[0]) break except: pass if numBits is None: print ("No phase has all delays valid") return delays_phase numLanes=numBits//8 try: maxErrDqsi=self.adjustment_state["maxErrDqsi"] except: maxErrDqsi=None try: maxErrDqso=self.adjustment_state["maxErrDqso"] except: maxErrDqso=None #print header print("Phase",end=" ") if num_cmda == 0: print("CMDA",end=" ") else: for i in range(num_addr): print("A%d"%(i),end=" ") for i in range(num_banks): print("BA%d"%(i),end=" ") print ("WE RAS CAS AVG", end=" ") # AVG - average for address, banks, RCW # print("RCW",end=" ") # TODO - modify when separate command line data will be available # num_addr=vrlg.ADDRESS_NUMBER # num_banks=3 for lane in range(numLanes): print("DQS%di"%(lane),end=" ") for k in enl_list_in: for b in range(numBits): print("%s-DQ%di"%(k.upper()[0], b),end=" ") for lane in range(numLanes): print("DQS%d0"%(lane),end=" ") for k in enl_list_out: for b in range(numBits): print("%s-DQ%do"%(k.upper()[0], b),end=" ") if not read_variants is None: for k in enl_list_in: print("%s-rsel"%(k.upper()[0]),end=" ") if not write_variants is None: for k in enl_list_out: print("%s-wsel"%(k.upper()[0]),end=" ") for k in enl_list_in: print("%s-in-err"%(k.upper()[0]),end=" ") for k in enl_list_out: print("%s-out-err"%(k.upper()[0]),end=" ") print() #print table rows for phase, phase_data in enumerate(delays_phase): print ("%d"%(phase),end=" ") if not phase_data is None: if num_cmda ==0: try: print ("%d"%(phase_data['cmda']),end=" ") except: print ("?",end=" ") else: for line in range (num_cmda): try: print ("%d"%(phase_data['cmda'][line]),end=" ") except: print ("?",end=" ") for lane in range(numLanes): try: print ("%d"%(phase_data['dqsi'][lane]),end=" ") except: print ("?",end=" ") for k in enl_list_in: for b in range(numBits): try: print ("%d"%(phase_data['dqi'][k][b]),end=" ") except: print ("?",end=" ") for lane in range(numLanes): try: print ("%d"%(phase_data['dqso'][lane]),end=" ") except: print ("?",end=" ") for k in enl_list_out: for b in range(numBits): try: print ("%d"%(phase_data['dqo'][k][b]),end=" ") except: print ("?",end=" ") if not read_variants is None: for k in enl_list_in: try: if not None in phase_data['dqi'][k]: print ("%d"%(10*read_variants[k]['sel']),end=" ")# 10* - for graph visibility else: print ("?",end=" ") except: print ("?",end=" ") if not write_variants is None: for k in enl_list_out: try: # print ('k=',k,end=" ") # print ("phase_data['dqo']=",phase_data['dqo']) if not None in phase_data['dqo'][k]: print ("%d"%(12*write_variants[k]['sel']),end=" ")# 12* - for graph visibility else: print ("?",end=" ") except: print ("?",end=" ") for k in enl_list_in: try: if not None in phase_data['dqsi']: max_err=max(maxErrDqsi[k][dly][lane] for lane,dly in enumerate(phase_data['dqsi'])) print("%.1f"%(max_err),end=" ") else: print ("X",end=" ") except: print ("?",end=" ") for k in enl_list_out: try: if not None in phase_data['dqso']: max_err=max(maxErrDqso[k][dly][lane] for lane,dly in enumerate(phase_data['dqso'])) print("%.1f"%(max_err),end=" ") else: print ("x",end=" ") except: print ("?",end=" ") print() if set_table: self.adjustment_state['delays_phase'] = delays_phase return delays_phase def show_all_vs_phase(self, keep_all=False, load_hardcoded=False, filter_rw=False, filter_rsel=None, filter_wsel=None): ''' Show table (to be copied to a spreadsheet) with all delay settings for each DDR3 memory clock phase value @param keep_all - show incomplete data (some of the delays may not have valid values for selected clock phase, false - show only data for valid phases @param load_hardcoded - get hard-coded data (false - use current) ''' if load_hardcoded: self.load_hardcoded_data() self.get_delays_vs_phase( filter_dqo=2, filter_dqi=2, filter_dqso=2, filter_dqsi=2, filter_cmda=2, filter_read=(0,1)[filter_rw], # filter_write=(0,1)[filter_rw], filter_rsel=filter_rsel, filter_wsel=filter_wsel, keep_all=keep_all, set_table=False, quiet=2) def save_mcntrl(self, path=None, quiet=1): """ Save memory controller delays measuremnt/adjustment state to file @param path location to save state or None to use path defined by parameter PICKLE @return None, raises exception if path is not provided and PICKLE is not defined """ if path is None: try: path=vrlg.PICKLE except: raise Exception ("path is not provided and Verilog parameter PICKLE is not defined") pickle.dump(self.adjustment_state, open(path, "wb" )) if quiet <2: print ("mcntrl state (self.adjustment_state) is saved to %s"%(path)) def load_mcntrl(self, path=None, quiet=1): """ Load memory controller delays measuremnt/adjustment state from file @param path location to load state from or None to use path defined by parameter PICKLE @return None, raises exception if path is not provided and PICKLE is not defined """ if path is None: try: path=vrlg.PICKLE except: raise Exception ("path is not provided and Verilog parameter PICKLE is not defined") self.adjustment_state=pickle.load(open(path, "rb" )) if quiet <2: print ("mcntrl state (self.adjustment_state) is loaded from %s"%(path)) if quiet<1: print ("self.adjustment_state=",self.adjustment_state)