def __call__(self, lim_register_addr, cav_num=0): registers = [] # Note that Tstep is the ADC time step, also clocks the LLRF controller. # Divide by two for the cavity simulator (rtsim) clock time step. Tstep = 10e-9 # s f0 = 1300e6 # Hz nyquist_sign = -1 # -1 represents frequency inversion, # as with high-side LO or even-numbered Nyquist zones. VPmax = 48.0 # V piezo drive max # as we scale up, the following 10 parameters replicate per cavity: PAmax = 6e3 # W RF amplifier max PAbw = 1.5e6 # Hz bandwidth of power amplifier cav_adc_max = 1.2 # sqrt(W) rfl_adc_max = 180.0 # sqrt(W) fwd_adc_max = 160.0 # sqrt(W) phase_1 = self.fwd_phase_shift # forward monitor phase shift phase_2 = self.rfl_phase_shift # reflected monitor prompt phase shift cav_adc_off = 10 rfl_adc_off = 20 fwd_adc_off = 30 mode1 = Emode(name="pi", RoverQ=1036.0, foffset=self.mode1_foffset, peakV=1.5e6, Q_0=1e10, Q_1=self.mode1_Q1, Q_2=2e9, phase_1=self.rfl_phase_shift, phase_2=self.cav_phase_shift, mech_couplings=[ self.net1_coupling, self.net2_coupling, self.net3_coupling ]) mode2 = Emode(name="8pi/9", RoverQ=10.0, foffset=-8e5, peakV=0.15e6, Q_0=1e10, Q_1=8.1e4, Q_2=2e9, phase_1=10, phase_2=-180, mech_couplings=[0, 0, 0]) mmode1 = Mmode('silly', self.mmode1_freq, self.mmode1_Q, 1.13, 40000, 1, 80000) mmode2 = Mmode('lowQ', 100000, 5.0, 1.5, 80000, 1, 20000) mmode3 = Mmode('other', 24500, 25.0, 1.5, 20000, 1, 10000) # DDS setup for simulator should be static # this construction is for 20 MHz / 94.286 MHz = 7/33 dds_num = 7 dds_den = 33 # The following three parameters are set in the Verilog at compile-time, # not run-time. Top-level setting in larger.v needs to be mirrored here. lp_shift = 9 # see lp_pair.v, a.k.a. mode_shift n_mech_modes = 7 # number of mechanical modes handled df_scale = 9 # see cav4_freq.v omega0 = f0 * 2 * pi mech_tstep = Tstep * n_mech_modes interp_gain = n_mech_modes / 2**ceil( log(n_mech_modes) / log(2)) # interp0.v amp_bw = fix(Tstep * PAbw * 32, 18, "amp_bw") dds_phstep_h = int(dds_num * 2**20 / dds_den) dds_mult = int(4096 / dds_den) dds_phstep_l = (dds_num * 2**20) % dds_den * dds_mult dds_modulo = 4096 - dds_mult * dds_den dds_phstep = dds_phstep_h << 12 | dds_phstep_l #print "# dds",dds_mult,dds_phstep_h, dds_phstep_l, dds_modulo # four registers of pair_couple.v # neglect losses between directional coupler and cavity prompt = [ fix(-sqrt(PAmax) / fwd_adc_max, 18, "out1", "cordic"), fix(-sqrt(PAmax) / rfl_adc_max, 18, "out2", "cordic"), fix(phase_1 / 180.0, 19, "out3"), fix(phase_2 / 180.0, 19, "out4") ] # Mechanical modes res_prop = [] piezo_couple = [] noise_couple = [] dot_0_k = [] outer_0_k = [] dot_1_k = [] outer_1_k = [] dot_2_k = [] outer_2_k = [] mech_mode_list = [mmode1, mmode2, mmode3] for i, m in enumerate(mech_mode_list): # print "# Cavity mechanical mode %d: %s"%(i,m.name) w1 = mech_tstep * 2 * pi * m.freq # a1 + b1 * i represents the pole in the normalized s-plane a1 = w1 * (-1 / (2.0 * m.Q)) b1 = w1 * sqrt(1 - 1 / (4.0 * m.Q**2)) z_pole = cexp(a1 + b1 * 1j) # print "# z_pole = %7f + %7fi"%(z_pole.real,z_pole.imag) a1 = z_pole.real - 1.0 b1 = z_pole.imag scale = int(-log(max(a1, b1)) / log(4)) scale = max(min(scale, 9), 2) a2 = a1 * 4**scale b2 = b1 * 4**scale # print "# debug",w1,a1,b1,scale,a2,b2 # c1 = -w1**2 / (k*b1) res_prop.append((fix(a2, 18, "a2") & (2**18 - 1)) + ((9 - scale) << 18)) res_prop.append((fix(b2, 18, "b2") & (2**18 - 1)) + ((9 - scale) << 18)) # the above is tested. Onwards to the work-in-progress dc_gain = b2 / (a2**2 + b2**2) # resonator.v m.dc_gain = dc_gain # per-electical mode coupling computation will need this piezo_couple.append(m.piezo_hack) piezo_couple.append(0) noise_couple.append(m.vibration_hack) noise_couple.append(0) # dot_2_k.append(0) # dot_2_k.append(m.piezo_hack) for i, m in enumerate([mode1, mode2]): # print "# Cavity electrical mode %d: %s"%(i,m.name) Q_L = 1 / (1 / m.Q_0 + 1 / m.Q_1 + 1 / m.Q_2) # x is defined as sqrt(U) xmax = m.peakV / sqrt(m.RoverQ * omega0) # four registers of pair_couple.v out_coupling = [ fix( sqrt(omega0 / m.Q_1) * xmax / rfl_adc_max, 18, m.name + ".out1", "cordic"), fix( sqrt(omega0 / m.Q_2) * xmax / cav_adc_max, 18, m.name + ".out2", "cordic"), fix(m.phase_1 / 180.0, 19, m.name + "out3"), fix(m.phase_2 / 180.0, 19, m.name + "out4") ] print out_coupling # see Pro tip in cav4_elec.v for better limit on foffset # XXX document using 33 for what's really a 28-bit register coarse_freq = fix(Tstep * nyquist_sign * m.foffset, 33, m.name + "coarse_freq") V_achievable = 2 * sqrt(PAmax * m.Q_1 * m.RoverQ) drive_coupling = fix(V_achievable / m.peakV, 18, m.name + "drive_coupling", "cordic") # bandwidth in Hz = f_clk/2/2^shift/(2*pi) * bw_register/2^17 = omega_0/(2*pi*2*Q_L) # XXX document origin of *2.0 better, compensates for shift right in lp_pair.v bw = fix(Tstep * omega0 / (2 * Q_L) * (2**lp_shift) * 2.0, 18, m.name + ".bw") registers.append(['coarse_freq', [cav_num, i], coarse_freq]) registers.append(['drive_coupling', [cav_num, i], drive_coupling]) registers.append(['bw', [cav_num, i], bw]) registers.append(['out_coupling', [cav_num, i], out_coupling]) for j, mech_couple in enumerate(m.mech_couplings): # print "# elec mode %d coupling index %d value %f"%(i,j,mech_couple) # Number of electrical modes implemented in the hardware is set by the # parameter mode_count in cav4_elec.v, frozen at the time of synthesis. # The address decode generator (newad.py) will embed mode index into a # text name, which is then given an address by addr_map.vh. This explains # the funny-looking exec at the end of this stanze. mech_mode = mech_mode_list[j] Amn = sqrt(mech_couple / m.RoverQ) / omega0 # sqrt(J)/V^2 Cmn = -sqrt(mech_couple * m.RoverQ) * omega0 # 1/s/sqrt(J) lorentz_en = 1 outer = lorentz_en * Amn / mech_mode.mx * m.peakV**2 / mech_mode.dc_gain # dimensionless inner = lorentz_en * Cmn * mech_mode.mx * Tstep / 2**df_scale / interp_gain # dimensionless # note that inner*outer = net_coupling * mode1.peakV**2 * Tstep #print "# outer =",outer,"inner =",inner # additional scaling below comes from the 32-bit mech_phase_fine # accumulator, but only 18-bit d_result xx = fix(outer / 128, 18, "outer") yy = fix(inner / 128, 18, "inner") if i == 0: outer_0_k.append(xx) outer_0_k.append(0) dot_0_k.append(0) dot_0_k.append(yy) elif i == 1: outer_1_k.append(xx) outer_1_k.append(0) dot_1_k.append(0) dot_1_k.append(yy) #exec '''outer_%d_k.append(xx); outer_%d_k.append(0); dot_%d_k.append(0); dot_%d_k.append(yy)''' % (i,i,i,i) # Must be after electrical mode processing, to pick up outer_0_k etc. registers.append(['cav4_elec_phase_step', [cav_num], dds_phstep]) registers.append(['cav4_elec_modulo', [cav_num], dds_modulo]) registers.append(['amp_lp_bw', [cav_num], amp_bw]) registers.append(['drive_couple_out_coupling', [cav_num], prompt]) registers.append(['a_cav_offset', [cav_num], cav_adc_off]) registers.append(['a_rfl_offset', [cav_num], rfl_adc_off]) registers.append(['a_for_offset', [cav_num], fwd_adc_off]) registers.append(['resonator_prop_const', [cav_num], res_prop]) registers.append(['dot_0_k_out', [cav_num], dot_0_k]) registers.append(['dot_1_k_out', [cav_num], dot_1_k]) registers.append(['dot_2_k_out', [cav_num], dot_2_k]) registers.append(['outer_prod_0_k_out', [cav_num], outer_0_k]) registers.append(['outer_prod_1_k_out', [cav_num], outer_1_k]) registers.append(['outer_prod_2_k_out', [cav_num], outer_2_k]) registers.append(['piezo_couple', [cav_num], piezo_couple]) registers.append(['noise_couple', [cav_num], noise_couple]) # Pseudo-random generator initialization, see tt800v.v and prng.v prng_seed = "pushmi-pullyu" if prng_seed: #print "# PRNG subsystem seed is '%s'"%prng_seed hf = sha1() hf.update(prng_seed) for x in generate_seed(hf): registers.append(['prng_iva', [cav_num], x]) for x in generate_seed(hf): registers.append(['prng_ivb', [cav_num], x]) registers.append(['prng_random_run', [cav_num], self.PRNG_en]) # Just hack in a few quick values for the controller # This should really be another construction as above, maybe in a separate Python module? # static DDS config, 7/33 as above # #print "17 %d # phase_step"%dds_phstep # #print "18 %d # modulo"%dds_modulo #print "phase_step %d # phase_step"%dds_phstep #print "modulo %d # modulo"%dds_modulo wave_samp_per = 1 wave_shift = 3 # The LO amplitude in the FPGA is scaled by (32/33)^2, so that yscale # fits nicely within the 32768 limit for small values of wave_samp_per lo_cheat = (32 / 33.0)**2 yscale = lo_cheat * (33 * wave_samp_per)**2 * 4**(8 - wave_shift) / 32 registers.append(['phase_step', [cav_num], dds_phstep]) registers.append(['modulo', [cav_num], 4]) registers.append(['wave_samp_per', [cav_num], wave_samp_per]) registers.append(['wave_shift', [cav_num], wave_shift]) registers.append(['piezo_dc', [cav_num], self.piezo_dc]) registers.append(['sel_thresh', [cav_num], 5000]) registers.append(['ph_offset', [cav_num], self.ph_offset]) registers.append(['sel_en', [cav_num], self.sel_en]) registers.append(['lp1a_kx', [cav_num], 20486]) registers.append(['lp1a_ky', [cav_num], -20486]) registers.append(['chan_keep', [cav_num], 4080]) registers.append( ['setmp', [cav_num], [int(self.set_X), int(self.set_P)]]) registers.append( ['coeff', [cav_num], [-100, -100, self.k_PA, self.k_PP]]) registers.append(['lim', [cav_num], [0, 0, 0, 0]]) self.delay_pc = 0 def delay_set(ticks, addr, data): self.delay_pc += 4 return [ ['XXX_%d' % (self.delay_pc - 4), [cav_num], ticks], ['XXX_%d' % (self.delay_pc - 3), [cav_num], addr], ['XXX_%d' % (self.delay_pc - 2), [cav_num], int(data) / 65536], ['XXX_%d' % (self.delay_pc - 1), [cav_num], int(data) % 65536] ] if 0: pass # # FGEN # regs.append(c_regs('duration',3,duration)) # regs.append(c_regs('amp_slope',4,5000)) # regs.append(c_regs('amp_max',7,amp_max)) # regs.append(c_regs('amp_dest_addr',10,36)) # regs.append(c_regs('amp_dest_addr',12,38)) # elif 0: # # TGEN # delay_pc = 4096 # # start the pulse in open loop (fixed drive amplitude) mode # regs.extend(delay_set(0, 'lim_Y_hi', 0)) # regs.extend(delay_set(0, 'lim_Y_lo', 0)) # regs.extend(delay_set(0, 'lim_X_hi', 22640)) # regs.extend(delay_set(500*8, 'lim_X_lo', 22640)) # # allow the amplitude loop to run # #regs.extend(delay_set(0, 52, 26000)) # regs.extend(delay_set(duration*8, 'lim_X_lo', 16000)) # # allow the phase loop to run # regs.extend(delay_set(0, 'lim_Y_hi', self.maxq)) # regs.extend(delay_set(5000*8, 'lim_Y_lo', -self.maxq)) # regs.extend(delay_set(0, 'lim_X_hi', 0)) # regs.extend(delay_set(0, 'lim_X_lo', 0)) else: registers.extend(delay_set(0, lim_register_addr, self.amp_max)) registers.extend( delay_set(self.duration * 8, lim_register_addr + 2, self.amp_max)) registers.extend(delay_set(0, lim_register_addr, 0)) registers.extend(delay_set(0, lim_register_addr + 2, 0)) for jx in range(6): registers.extend(delay_set(0, 0, 0)) # regs.append(c_regs('Mode Select', 65536+554, 0)) if error_cnt > 0: print "# %d scaling errors found" % error_cnt #exit(1) convert = lambda x: x + 2**32 if x < 0 else x for r in registers: if type(r[2]) is list: r[2] = map(convert, r[2]) else: r[2] = convert(r[2]) global print_once if print_once == True: for r in registers: print r[0], r[2] print_once = False return registers
def gen_reg_list(mode1_foffset=5.0,mode1_Q1=8.1e4,mmode1_freq=30e3,mmode1_Q=5.0,net1_coupling=100,net2_coupling=200,net3_coupling=150,sel_en=1,set_X=0.0,set_P=0.0,k_PA=0,k_PP=0,maxq=0,ph_offset=-35500,amp_max=22640,fwd_phase_shift=0,rfl_phase_shift=0,cav_phase_shift=0,duration=300,PRNG_en=1,piezo_dc=0): regs=[] error_cnt=0 #print 'mode1_foffset=%f,mode1_Q1=%f,mmode1_freq=%f,mmode1_Q=%f,net_coupling=%f'%(mode1_foffset,mode1_Q1,mmode1_freq,mmode1_Q,net_coupling) # Gang humbly requests that Q_1 be renamed Q_drive, and Q_2 as Q_probe. # Should apply here, physics.tex, elsewhere? # Note that Tstep is the ADC time step, also clocks the LLRF controller. # Divide by two for the cavity simulator (rtsim) clock time step. Tstep = 10e-9 # s f0 = 1300e6 # Hz nyquist_sign = -1 # -1 represents frequency inversion, # as with high-side LO or even-numbered Nyquist zones. VPmax = 48.0 # V piezo drive max # as we scale up, the following 10 parameters replicate per cavity: PAmax = 6e3 # W RF amplifier max PAbw = 1.5e6 # Hz bandwidth of power amplifier cav_adc_max = 1.2 # sqrt(W) rfl_adc_max = 180.0 # sqrt(W) fwd_adc_max = 160.0 # sqrt(W) phase_1 = fwd_phase_shift # forward monitor phase shift phase_2 = rfl_phase_shift # reflected monitor prompt phase shift cav_adc_off = 10 rfl_adc_off = 20 fwd_adc_off = 30 mode1=Emode(name="pi",RoverQ=1036.0,foffset=mode1_foffset,peakV=1.5e6,Q_0=1e10,Q_1=mode1_Q1,Q_2=2e9,phase_1=rfl_phase_shift,phase_2=cav_phase_shift,mech_couplings=[net1_coupling,net2_coupling,net3_coupling]) mode2=Emode(name="8pi/9",RoverQ=10.0,foffset=-8e5,peakV=0.15e6,Q_0=1e10,Q_1=8.1e4,Q_2=2e9,phase_1=10,phase_2=-180,mech_couplings=[0,0,0]) mmode1=Mmode('silly', mmode1_freq, mmode1_Q, 1.13, 40000, 1, 80000) mmode2=Mmode('lowQ', 100000, 5.0, 1.5, 80000, 1, 20000) mmode3=Mmode('other', 24500, 25.0, 1.5, 20000, 1, 10000) # DDS setup for simulator should be static # this construction is for 20 MHz / 94.286 MHz = 7/33 dds_num=7 dds_den=33 # The following three parameters are set in the Verilog at compile-time, # not run-time. Top-level setting in larger.v needs to be mirrored here. lp_shift = 9 # see lp_pair.v, a.k.a. mode_shift n_mech_modes = 7 # number of mechanical modes handled df_scale = 9 # see cav4_freq.v # ==== end of system configuration # ==== the following dictionaries should get pulled in from Verilog somehow sim_base=16384 # base address for vmod1, see larger.v line 33 regmap_global = { 'dds_phstep' : 1, 'dds_modulo' : 2, 'amp_bw' : 5, 'prompt' : 8, # base address of 4 registers 'cav_adc_off' : 49, 'rfl_adc_off' : 50, 'fwd_adc_off' : 51, 'sat_ctl' : 55, 'res_prop' : 1*1024, # base address of 1024 registers 'dot_0_k' : 2*1024, # base address of 1024 registers 'outer_0_k' : 3*1024, # base address of 1024 registers 'dot_1_k' : 4*1024, # base address of 1024 registers 'outer_1_k' : 5*1024, # base address of 1024 registers 'dot_2_k' : 6*1024, # base address of 1024 registers 'outer_2_k' : 7*1024, # base address of 1024 registers 'piezo_couple' : 8*1024, # base address of 1024 registers 'noise_couple' : 9*1024} # base address of 1024 registers # base address for cavity n (zero-based) is 16+8*n regmap_emode = { 'coarse_freq' : 1, 'drive_coupling' : 2, 'bw' : 3, 'out_couple' : 4} # base address of 4 registers # ==== end of hardware register dictionaries # send a register value "out" # looks address up in regmap[name] # finds value via name in python global namespace # value can be a scalar or a list # prefix and name are used to give a helpful comment # ==== now start the application-specific computations # Still may have bugs: # Mechanical mode coupling # Needs a lot of work: # LLRF controller omega0 = f0*2*pi mech_tstep = Tstep * n_mech_modes interp_gain = n_mech_modes / 2**ceil(log(n_mech_modes)/log(2)) # interp0.v #print "# Globals" amp_bw = fix(Tstep*PAbw*32, 18, "amp_bw") dds_phstep_h = int(dds_num*2**20/dds_den) dds_mult = int(4096/dds_den) dds_phstep_l = (dds_num*2**20)%dds_den * dds_mult dds_modulo = 4096 - dds_mult*dds_den dds_phstep = dds_phstep_h << 12 | dds_phstep_l sat_ctl = 65535 #print "# dds",dds_mult,dds_phstep_h, dds_phstep_l, dds_modulo # four registers of pair_couple.v # neglect losses between directional coupler and cavity prompt = [ fix(-sqrt(PAmax) / fwd_adc_max, 18, "out1", "cordic"), fix(-sqrt(PAmax) / rfl_adc_max, 18, "out2", "cordic"), fix(phase_1 / 180.0, 19, "out3"), fix(phase_2 / 180.0, 19, "out4")] # Mechanical modes res_prop=[] piezo_couple=[] noise_couple=[] dot_0_k=[]; outer_0_k=[] dot_1_k=[]; outer_1_k=[] dot_2_k=[]; outer_2_k=[] mech_mode_list = [mmode1,mmode2,mmode3] for i,m in enumerate(mech_mode_list): #print "# Cavity mechanical mode %d: %s"%(i,m.name) w1 = mech_tstep * 2*pi*m.freq # a1 + b1 * i represents the pole in the normalized s-plane a1 = w1 * (-1/(2.0*m.Q)) b1 = w1 * sqrt(1-1/(4.0*m.Q**2)) z_pole = cexp(a1+b1*1j) #print "# z_pole = %7f + %7fi"%(z_pole.real,z_pole.imag) a1 = z_pole.real-1.0 b1 = z_pole.imag scale = int(-log(max(a1,b1))/log(4)) scale = max(min(scale,9),2) a2 = a1 * 4**scale b2 = b1 * 4**scale #print "# debug",w1,a1,b1,scale,a2,b2 #c1 = -w1**2 / (k*b1) res_prop.append( (fix(a2,18,"a2")&(2**18-1)) + ((9-scale) << 18) ) res_prop.append( (fix(b2,18,"b2")&(2**18-1)) + ((9-scale) << 18) ) # the above is tested. Onwards to the work-in-progress dc_gain = b2/(a2**2+b2**2) # resonator.v m.dc_gain = dc_gain # per-electical mode coupling computation will need this piezo_couple.append(m.piezo_hack) piezo_couple.append(0) noise_couple.append(m.vibration_hack) noise_couple.append(0) #dot_2_k.append(0) #dot_2_k.append(m.piezo_hack) for i,m in enumerate([mode1,mode2]): #print "# Cavity electrical mode %d: %s"%(i,m.name) Q_L = 1 / (1/m.Q_0 + 1/m.Q_1 + 1/m.Q_2) # x is defined as sqrt(U) xmax = m.peakV / sqrt(m.RoverQ*omega0) # four registers of pair_couple.v out_couple = [ fix(sqrt(omega0/m.Q_1) * xmax / rfl_adc_max, 18, m.name+".out1", "cordic"), fix(sqrt(omega0/m.Q_2) * xmax / cav_adc_max, 18, m.name+".out2", "cordic"), fix(m.phase_1 / 180.0, 19, m.name+"out3"), fix(m.phase_2 / 180.0, 19, m.name+"out4")] # see Pro tip in eav4_elec.v for better limit on foffset # XXX document using 33 for what's really a 28-bit register coarse_freq = fix(Tstep*nyquist_sign*m.foffset, 33, m.name+"coarse_freq") V_achievable = 2*sqrt(PAmax*m.Q_1*m.RoverQ) drive_coupling = fix( V_achievable / m.peakV, 18, m.name+"drive_coupling", "cordic") # bandwidth in Hz = f_clk/2/2^shift/(2*pi) * bw_register/2^17 = omega_0/(2*pi*2*Q_L) # XXX document origin of *2.0 better, compensates for shift right in lp_pair.v bw = fix(Tstep*omega0/(2*Q_L)*(2**lp_shift)*2.0, 18, m.name+".bw") for n in regmap_emode.keys(): regs.extend(set_reg(16+8*i,m.name+".",n,regmap_emode,locals(),sim_base)) for j,mech_couple in enumerate(m.mech_couplings): #print "# elec mode %d coupling index %d value %f"%(i,j,mech_couple) # Number of electrical modes implemented in the hardware is set by the # parameter mode_count in cav4_elec.v, frozen at the time of synthesis. # The address decode generator (newad.py) will embed mode index into a # text name, which is then given an address by addr_map.vh. This explains # the funny-looking exec at the end of this stanze. mech_mode = mech_mode_list[j] Amn = sqrt(mech_couple/m.RoverQ) / omega0 # sqrt(J)/V^2 Cmn = - sqrt(mech_couple*m.RoverQ) * omega0 # 1/s/sqrt(J) lorentz_en = 1 outer = lorentz_en * Amn / mech_mode.mx * m.peakV**2 / mech_mode.dc_gain # dimensionless inner = lorentz_en * Cmn * mech_mode.mx * Tstep / 2**df_scale / interp_gain # dimensionless # note that inner*outer = net_coupling * mode1.peakV**2 * Tstep #print "# outer =",outer,"inner =",inner # additional scaling below comes from the 32-bit mech_phase_fine # accumulator, but only 18-bit d_result exec '''outer_%d_k.append(fix(outer/128,18,"outer")); outer_%d_k.append(0); dot_%d_k.append(0); dot_%d_k.append(fix(inner/128,18,"inner"))''' % (i,i,i,i) # Must be after electrical mode processing, to pick up outer_0_k etc. for n in regmap_global.keys(): r_hg_0=set_reg(0,"",n,regmap_global,locals(),sim_base) regs.extend(r_hg_0) # Pseudo-random generator initialization, see tt800v.v and prng.v prng_seed="pushmi-pullyu" if prng_seed: #print "# PRNG subsystem seed is '%s'"%prng_seed hf = sha1() hf.update(prng_seed) r53=push_seed(53+sim_base,hf) r54=push_seed(54+sim_base,hf) #print "%d 1 # turn on PRNG"%(52+sim_base) regs.extend(r53) regs.extend(r54) regs.append(c_regs('turn on PRNG',52+sim_base,PRNG_en)) # Just hack in a few quick values for the controller # This should really be another construction as above, maybe in a separate Python module? # static DDS config, 7/33 as above # #print "17 %d # phase_step"%dds_phstep # #print "18 %d # modulo"%dds_modulo #print "phase_step %d # phase_step"%dds_phstep #print "modulo %d # modulo"%dds_modulo wave_samp_per=1 wave_shift=3 # The LO amplitude in the FPGA is scaled by (32/33)^2, so that yscale # fits nicely within the 32768 limit for small values of wave_samp_per lo_cheat=(32/33.0)**2 yscale=lo_cheat*(33*wave_samp_per)**2*4**(8-wave_shift)/32 regs.append(set_ctl('phase_step',dds_phstep)) regs.append(set_ctl('modulo',4)) regs.append(set_ctl('wave_samp_per',wave_samp_per)) regs.append(set_ctl('wave_shift',wave_shift)) regs.append(set_ctl('piezo_dc',piezo_dc)) regs.append(set_ctl('sel_thresh',5000)) regs.append(set_ctl('ph_offset',ph_offset)) regs.append(set_ctl('sel_en',sel_en)) regs.append(set_ctl('lp1a_kx_re',20486)) regs.append(set_ctl('lp1a_ky_re',-20486)) #regs.append(c_regs('wait',555,150-12)) regs.append(set_ctl('chan_keep',4080)) #regs.append(set_ctl('lim',22640)) #regs.append(set_ctl('lim',22640)) regs.append(set_ctl('set_X',int(set_X))) regs.append(set_ctl('set_Y',int(set_P))) regs.append(set_ctl('coeff_X_I',-100)) regs.append(set_ctl('coeff_X_P',k_PA)) regs.append(set_ctl('coeff_Y_I',-100)) regs.append(set_ctl('coeff_Y_P',k_PP)) regs.append(set_ctl('lim_X_hi',0)) regs.append(set_ctl('lim_Y_hi',0)) regs.append(set_ctl('lim_X_lo',0)) regs.append(set_ctl('lim_Y_lo',0)) global delay_pc if 0: # FGEN regs.append(c_regs('duration',3,duration)) regs.append(c_regs('amp_slope',4,5000)) regs.append(c_regs('amp_max',7,amp_max)) regs.append(c_regs('amp_dest_addr',10,36)) regs.append(c_regs('amp_dest_addr',12,38)) elif 0: # TGEN delay_pc = 4096 # start the pulse in open loop (fixed drive amplitude) mode regs.extend(delay_set(0, 'lim_Y_hi', 0)) regs.extend(delay_set(0, 'lim_Y_lo', 0)) regs.extend(delay_set(0, 'lim_X_hi', 22640)) regs.extend(delay_set(500*8, 'lim_X_lo', 22640)) # allow the amplitude loop to run #regs.extend(delay_set(0, 52, 26000)) regs.extend(delay_set(duration*8, 'lim_X_lo', 16000)) # allow the phase loop to run regs.extend(delay_set(0, 'lim_Y_hi', maxq)) regs.extend(delay_set(5000*8, 'lim_Y_lo', -maxq)) regs.extend(delay_set(0, 'lim_X_hi', 0)) regs.extend(delay_set(0, 'lim_X_lo', 0)) else: delay_pc = 4096 regs.extend(delay_set(0, 'lim_X_hi', amp_max)) regs.extend(delay_set(duration*8, 'lim_X_lo', amp_max)) regs.extend(delay_set(0, 'lim_X_hi', 0)) regs.extend(delay_set(0, 'lim_X_lo', 0)) for jx in range(6): regs.extend(delay_set(0,'null',0)) if error_cnt > 0: print "# %d scaling errors found"%error_cnt #exit(1) return [regs,error_cnt]
# Mechanical modes res_prop = [] piezo_couple = [] dot_0_k = [] outer_0_k = [] dot_1_k = [] outer_1_k = [] dot_2_k = [] outer_2_k = [] for i, m in enumerate([mmode1, mmode2]): print "# Cavity mechanical mode %d: %s" % (i, m.name) w1 = mech_tstep * 2 * pi * m.freq # a1 + b1 * i represents the pole in the normalized s-plane a1 = w1 * (-1 / (2.0 * m.Q)) b1 = w1 * sqrt(1 - 1 / (4.0 * m.Q**2)) z_pole = cexp(a1 + b1 * 1j) print "# z_pole = %7f + %7fi" % (z_pole.real, z_pole.imag) a1 = z_pole.real - 1.0 b1 = z_pole.imag scale = int(-log(max(a1, b1)) / log(4)) scale = max(min(scale, 9), 2) a2 = a1 * 4**scale b2 = b1 * 4**scale print "# debug", w1, a1, b1, scale, a2, b2 #c1 = -w1**2 / (k*b1) res_prop.append((fix(a2, 18, "a2") & (2**18 - 1)) + ((9 - scale) << 18)) res_prop.append((fix(b2, 18, "b2") & (2**18 - 1)) + ((9 - scale) << 18)) # the above is tested. Onwards to the work-in-progress dc_gain = b2 / (a2**2 + b2**2) # resonator.v net_coupling = 130.0 # Hz / V^2, negative is implicit Amn = sqrt(net_coupling / mode1.RoverQ) / omega0 # sqrt(J)/V^2