def getFPGADict(simulation): """ getFPGADict: Illustration of conversion of user-defined physics values (contained in the simulation object, which is obtained from the JSON parser) and the FPGA register values, contained in the output dictionary linac_reg_dict. error_cnt is an error count reported from the floating-point to fix-point arithmetic conversions and scaling. """ from read_regmap import get_map regmap = get_map(base_dir + "/../_autogen/regmap_cryomodule.json") c_reg_base = get_reg_info(regmap, [], 'llrf_' + str(0) + '_xxxx')['base_addr'] # Initialize empty FPGA register objects simulation.Init_FPGA_Registers(regmap, []) # Compute FPGA register values based on configuration parameters flat_reg_dict, error_cnt = simulation.Compute_FPGA_Registers() # Replace computed values directly in register map for key, value in flat_reg_dict.iteritems(): regmap[key] = value # Make sure all values are Register instances for key, value in regmap.iteritems(): if not isinstance(value, Register): regmap[key] = Register(**value) return regmap, error_cnt, c_reg_base
def init_llrf_settings(self): temp = get_map(os.path.join(base_dir, 'parameters/llrf_core_expand.json')) temp = temp['cryomodule']['regmap'] self.app_reg_prefix = 'cryomodule_' self.wave_samp_per_name = get_reg_info(temp, [0], 'wave_samp_per')['name'].replace(self.app_reg_prefix, '') self.wave_shift_name = get_reg_info(temp, [0], 'wave_shift')['name'].replace(self.app_reg_prefix, '') self.chan_keep_name = get_reg_info(temp, [0], 'chan_keep')['name'].replace(self.app_reg_prefix, '') json_path = os.path.join(base_dir, 'parameters/llrf_core_expand.json') self.core_llrf = RegMapExpand(base=0, add_prefix=False, path=json_path) self.regmap.update(self.core_llrf.regmap) self.regmap_short.update(self.core_llrf.regmap_short) self.reg_addr_dict.update(self.core_llrf.reg_addr_dict) # see llrf_core.json self._cbuf_flip_name = 'DSP_CBUF_FLIP' self._slow_name = 'DSP_SLOW' self._cbuf_name = 'DSP_CBUF' self._cbuf_mode_channel_name = 'DSP_CBUF_MODE' self.config_rom_name = 'DSP_CONFIG_ROM' self.buf_dw = 16 self.cbuf_width = 13 self.adc_bits = 16 self.shift_base = 1 # ccfilt.v self.wave_samp_per = 1 self.wave_shift = 3 self.num_dds = 7 self.den_dds = 33 self.clk_freq = 75e6 self.chn_names = ['Field', 'Forward', 'Reflected', 'PhRef'] self.chn_names.extend(['Null'] * 2) self.slow_buf_len = 2*len(self.chn_names) + 2 self.cbuf_mode_list = ['default'] + ['Counter'] + self.chn_names[:3] + ['Drive'] self.chn_sel = self.chn_names[:4] self.prng_data = {} # self.init_llrf_settings() self.to_fpga = PhysicsToFPGA(**{x['name']:x['value'] for x in physics_regs}) self.cic_period = self.den_dds # channel FIR configurations may vary self.FIR_GAIN = 1. self.mon_gains, self.fir_gains = {}, {} for ch in self.chn_names: self.mon_gains[ch] = self.FIR_GAIN self.fir_gains[ch] = self.FIR_GAIN self.lo_gain = int((1 << 15) / self.CORDIC_GAIN) self.lo_dds_gain = self.lo_gain * 4 * self.CORDIC_GAIN / (1 << 17) self.adc_freq = 150e6 self.time_step_adc = 1./self.adc_freq self.time_step_mon = self.wave_samp_per / self.clk_freq self.trace_len = (1 << 11)/len(self.chn_sel)
def getFPGADict(simulation): """getFPGADict: Illustration of conversion of user-defined physics values (contained in the simulation object, which is obtained from the JSON parser) and the FPGA register values, contained in the output dictionary linac_reg_dict. error_cnt is an error count reported from the floating-point to fix-point arithmetic conversions and scaling. """ from read_regmap import get_map regmap = get_map("./_autogen/regmap_cryomodule.json") hierarchy = ["station", "cav4_elec", ["mode_", 3]] # Initialize empty FPGA register objects simulation.Init_FPGA_Registers(regmap, []) # (This is where register map should be parsed and register lengths should be filled in) # Compute FPGA register values based on configuration parameters error_cnt = simulation.Compute_FPGA_Registers() return error_cnt
def registers(self, regmap_json, cavity): # Read register map from JSON file from read_regmap import get_map, get_reg_info regmap = get_map(regmap_json) # Extract the registers of interest setmp = get_reg_info(regmap, [self.cavity], 'setmp') # Set-points coeff = get_reg_info(regmap, [self.cavity], 'coeff') # Feedback loop gains lim = get_reg_info(regmap, [self.cavity], 'lim') # Controller upper and lower limits setmp_val, coeff_val, lim_val = self.integers() return { setmp['name']: setmp_val, coeff['name']: coeff_val, lim['name']: lim_val }
def json_map_handle(self, regmappath, refresh=False): if refresh or not isfile(regmappath): a = read_live_array(self) r = decode_array(a) print(("Bitfile lists git commit " + r[1])) print(("Attempting write of live json to " + regmappath)) with open(regmappath, 'wb') as f: f.write(r[3]) self.regmap = get_map(regmappath) # Look for addresses in hex (encoded in JSON as string values) and convert to int for key in self.regmap: try: if isinstance(self.regmap[key]['base_addr'], str): self.regmap[key]['base_addr'] = int( self.regmap[key]['base_addr'], 0) except Exception: pass # Setting the old regmaps to null; Forcing an error on access # TODO: Clean up the deprecated codepath that reads from prc_regmap.json self.write_regmap = {} self.read_regmap = {}
from pspeps_io.utilities import decode_2s_comp from read_regmap import get_map, get_reg_info visual = 0 if visual: import matplotlib.pyplot as plt plt.ion() fig = plt.figure() cic_base_period = 33 # default parameter in llrf_dsp.v wave_samp_per = 1 # set by paramhg.py Tstep = 10e-9 # as set in paramhg.py dt = cic_base_period * 2 * wave_samp_per * Tstep # seconds base_dir = os.path.dirname(os.path.abspath(__file__)) or '.' reg_map = get_map(os.path.join(base_dir, "../_autogen/regmap_cryomodule.json")) def get_write_address(name): if type(name) is int: return name else: offset = 0 if name.endswith(']'): x = re.search('^(\w+)\s*\[(\d+)\]', name) if x: name, offset = x.group(1), int(x.group(2)) r = get_reg_info(reg_map, [0], name) return r['base_addr'] + offset
import os, sys from math import sin, cos, pi, sqrt, log, ceil from cmath import exp from hashlib import sha1 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "/submodules/build") from read_regmap import get_map, get_reg_info regmap_cryomodule = get_map("./_autogen/regmap_cryomodule.json") ctl_regmap = get_map("./_autogen/regmap_llrf_shell.json") # 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. # DDS setup for simulator should be static # this construction is for 20 MHz / 94.286 MHz = 7/33 dds_num=7 dds_den=33 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
def printFPGADict(): """printFPGADict: Replication of the old param.py program for illustration purposes. Run this program in order to call JSON parser, user-defined physics settings to FPGA register space conversion, and prints used by Verilog test-bench for configuration parsing. """ from readjson.readjson_accelerator import push_seed # Get the simulation and accelerator objects from the JSON parser file_list = [ "readjson/configfiles/LCLS-II/default_accelerator.json", "readjson/configfiles/LCLS-II/LCLS-II_accelerator.json", "readjson/configfiles/LCLS-II/LCLS-II_append.json" ] simulation = parseSim.ParseSimulation(file_list) # ==== the following dictionaries should get pulled in from Verilog somehow sim_base = 16384 # base address for vmod1, see larger.v line 33 # regmap_global and regmap_emode dictionaries will be automatically created from a JSON file # regmap_mode: base address for cavity n (zero-based) is 16+8*n regmap_file = "readjson/configfiles/LCLS-II/register_map.json" regmap_global, regmap_emode = parseRegMap.ParseRegisterMap(regmap_file) from read_regmap import get_map, get_reg_info regmap = get_map("./_autogen/regmap_cryomodule.json") print "# Globals" # Convert user input into FPGA registers error_cnt = getFPGADict(simulation) # Grab the variables of interest from the dictionary ## First go down the hierarchy and get handles to dictionaries linac_obj = simulation.linac_list[0] cryomodule_obj = linac_obj.cryomodule_list[0] station_obj = cryomodule_obj.station_list[0] linac_reg_dict = linac_obj.reg_dict station0_reg_dict = station_obj.reg_dict cm_reg_dict = cryomodule_obj.reg_dict mechMode0_reg_dict = cryomodule_obj.mechanical_mode_list[0].reg_dict mechMode1_reg_dict = cryomodule_obj.mechanical_mode_list[1].reg_dict mechMode2_reg_dict = cryomodule_obj.mechanical_mode_list[2].reg_dict elecMode0_reg_dict = station_obj.cavity.elec_modes[0].reg_dict elecMode1_reg_dict = station_obj.cavity.elec_modes[1].reg_dict # Quick hack to add name field elecMode0_reg_dict['name'] = station_obj.cavity.elec_modes[0].name elecMode1_reg_dict['name'] = station_obj.cavity.elec_modes[1].name piezo0_reg_dict = station_obj.piezo_list[0].reg_dict reg_dict = {} ## Linac variables reg_dict['dds_phstep'] = linac_reg_dict['dds_phstep'] reg_dict['dds_modulo'] = linac_reg_dict['dds_modulo'] ## Station variables reg_dict['amp_bw'] = station0_reg_dict['amp_bw'] # Manually construct prompt for now reg_dict['prompt'] = station0_reg_dict['prompt'] for e in [elecMode0_reg_dict, elecMode1_reg_dict]: reg_dict['prompt'].value.append(e["prompt_rfl"].value) reg_dict['prompt'].value.append(e["prompt_fwd"].value) reg_dict['cav_adc_off'] = station0_reg_dict['cav_adc_off'] reg_dict['rev_adc_off'] = station0_reg_dict['rev_adc_off'] reg_dict['fwd_adc_off'] = station0_reg_dict['fwd_adc_off'] reg_dict['res_prop'] = cm_reg_dict['res_prop'] reg_dict['res_prop'].set_value([ mechMode0_reg_dict['a2'].value, mechMode0_reg_dict['b2'].value, mechMode1_reg_dict['a2'].value, mechMode1_reg_dict['b2'].value, mechMode2_reg_dict['a2'].value, mechMode2_reg_dict['b2'].value ]) reg_dict['dot_0_k'] = elecMode0_reg_dict['dot_list'] reg_dict['outer_0_k'] = elecMode0_reg_dict['outer_list'] reg_dict['dot_1_k'] = elecMode1_reg_dict['dot_list'] reg_dict['outer_1_k'] = elecMode1_reg_dict['outer_list'] reg_dict['dot_2_k'] = [ piezo0_reg_dict['piezo_couple'].value[0], piezo0_reg_dict['piezo_couple'].value[1], piezo0_reg_dict['piezo_couple'].value[3], piezo0_reg_dict['piezo_couple'].value[2] ] reg_dict['outer_2_k'] = [] reg_dict['piezo_couple'] = piezo0_reg_dict['piezo_couple'] # Added to generate the vibration_hack values reg_dict['noise_couple'] = cm_reg_dict['noise_couple'] reg_dict['noise_couple'].set_value([ mechMode0_reg_dict['vibration_hack'].value[0], mechMode0_reg_dict['vibration_hack'].value[1], mechMode1_reg_dict['vibration_hack'].value[0], mechMode1_reg_dict['vibration_hack'].value[1], mechMode2_reg_dict['vibration_hack'].value[0], mechMode2_reg_dict['vibration_hack'].value[1] ]) # Pop some elements to match paramhg.py del reg_dict['prompt'].value[4:] del reg_dict['dot_2_k'][0:] for n in regmap_global.keys(): set_reg(0, "", n, regmap_global, reg_dict, sim_base) for i, emode in enumerate([elecMode0_reg_dict, elecMode1_reg_dict]): reg_dict['coarse_freq'] = emode['coarse_freq'] reg_dict['drive_coupling'] = emode['drive_coupling'] reg_dict['bw'] = emode['bw'] reg_dict['out_coupling'] = emode['out_coupling'] reg_dict['out_phase_offset'] = emode['out_phase_offset'] regmap_emode.pop('out_couple', None) regmap_emode['out_coupling'] = None regmap_emode['out_phase_offset'] = None print "# Cavity electrical mode %d: %s" % (i, emode['name']) for n in regmap_emode: set_reg(16 + 8 * i, emode['name'] + ".", n, regmap_emode, reg_dict, sim_base) # Pseudo-random generator initialization, see tt800v.v and prng.v prng_seed = "pushmi-pullyu" prng_iva = get_reg_info(regmap, [], "prng_iva") prng_ivb = get_reg_info(regmap, [], "prng_ivb") prng_random_run = get_reg_info(regmap, [], "prng_random_run") if (prng_seed is not None): from hashlib import sha1 print "# PRNG subsystem seed is '%s'" % prng_seed hf = sha1() hf.update(prng_seed) push_seed(prng_iva['base_addr'] + sim_base, hf) push_seed(prng_ivb['base_addr'] + sim_base, hf) print "%d 1 # turn on PRNG" % (prng_random_run['base_addr'] + sim_base) # 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 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 reg_list = [] # Variables defined to print out the registers value piezo_dc = 0 ph_offset = -35800 sel_en = 0 set_X = 0.0 set_P = 0.0 k_PA = 0 k_PP = 0 from read_regmap import get_map, get_reg_info ctl_regmap = get_map("./_autogen/regmap_llrf_shell.json") # Maybe the following lines should be removed in the future #reg_list.append(c_regs('phase_step', 17+18, reg_dict['dds_phstep'].value)) #reg_list.append(c_regs('modulo', 18+18, reg_dict['dds_modulo'].value)) addr = get_reg_info(ctl_regmap, [], 'wave_samp_per')['base_addr'] reg_list.append(c_regs('wave_samp_per', addr, wave_samp_per)) addr = get_reg_info(ctl_regmap, [], 'wave_shift')['base_addr'] reg_list.append(c_regs('wave_shift', addr, wave_shift)) addr = get_reg_info(ctl_regmap, [], 'piezo_dc')['base_addr'] reg_list.append(c_regs('piezo_dc', addr, piezo_dc)) addr = get_reg_info(ctl_regmap, [], 'sel_thresh')['base_addr'] reg_list.append(c_regs('sel_thresh', addr, 5000)) addr = get_reg_info(ctl_regmap, [], 'ph_offset')['base_addr'] reg_list.append(c_regs('ph_offset', addr, ph_offset)) addr = get_reg_info(ctl_regmap, [], 'sel_en')['base_addr'] reg_list.append(c_regs('sel_en', addr, sel_en)) addr = get_reg_info(ctl_regmap, [], 'lp1a_kx')['base_addr'] reg_list.append(c_regs('lp1a', addr, 20486)) addr = get_reg_info(ctl_regmap, [], 'lp1a_ky')['base_addr'] reg_list.append(c_regs('lp1a', addr, -20486)) reg_list.append(c_regs('wait', 555, 150 - 12)) addr = get_reg_info(ctl_regmap, [], 'chan_keep')['base_addr'] reg_list.append(c_regs('ch_keep', addr, 4080)) addr = get_reg_info(ctl_regmap, [], 'setmp')['base_addr'] reg_list.append(c_regs('set_X', addr, int(set_X))) reg_list.append(c_regs('set_P', addr + 1, int(set_P))) addr = get_reg_info(ctl_regmap, [], 'coeff')['base_addr'] reg_list.append(c_regs('coeff_X_I', addr + 0, -100)) reg_list.append(c_regs('coeff_X_P', addr + 2, k_PA)) reg_list.append(c_regs('coeff_Y_I', addr + 1, -100)) reg_list.append(c_regs('coeff_Y_P', addr + 3, k_PP)) addr = get_reg_info(ctl_regmap, [], 'lim')['base_addr'] reg_list.append(c_regs('lim_X_hi', addr + 0, 0)) reg_list.append(c_regs('lim_Y_hi', addr + 1, 0)) reg_list.append(c_regs('lim_X_lo', addr + 2, 0)) reg_list.append(c_regs('lim_Y_lo', addr + 3, 0)) # Variables needed amp_max = 22640 reg_list.extend(delay_set(0, addr, amp_max)) # 52 is the address of lim_X_hi reg_list.extend(delay_set(duration * 8, addr + 2, amp_max)) # 54 is the address of lim_X_lo reg_list.extend(delay_set(0, addr, 0)) # 52 is the address of lim_X_hi reg_list.extend(delay_set(0, addr + 2, 0)) # 54 is the address of lim_X_lo for jx in range(6): reg_list.extend(delay_set(0, 0, 0)) for reg in reg_list: print str(reg.addr) + " " + str(reg.value) if (error_cnt > 0): print "# %d scaling errors found" % error_cnt exit(1)
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 from read_regmap import get_map regmap = get_map("../sel4v/_autogen/regmap_cryomodule.json") # ==== 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]
def run_test_bench(setmp_val, coeff_val, lim_val, test_type, in_file, out_file, setmp_step_time=0, setmp_step_val=0, lim_step_time=0, lim_step_val=0, in_i=0, in_q=0): """ Generates an input file for fdbk_core_tb with the appropriate register settings, and runs test-bench using Icarus Verilog. """ # Get register map from JSON from read_regmap import get_map, get_reg_info regmap_fdbk_core = get_map("./_autogen/regmap_fdbk_core_tb.json") # Extract the registers of interest setmp = get_reg_info(regmap_fdbk_core, [], 'setmp') # Set-points coeff = get_reg_info(regmap_fdbk_core, [], 'coeff') # Feedback loop gains lim = get_reg_info(regmap_fdbk_core, [], 'lim') # Controller upper and lower limits setmp['value'] = setmp_val # set X, set Y coeff['value'] = coeff_val # coeff X I, coeff Y I, coeff X P, coeff Y P lim['value'] = lim_val # lim X hi, lim Y hi, lim X lo, lim Y lo # Write the configuration file (register writes) for the test-bench write_reg_file([setmp, coeff, lim], regmap_fdbk_core, in_file) # Start with empty command to handle exception command = '' if ((test_type == 2) or (test_type == 3) or (test_type == 4)): # Write configuration file for set-point step setmp_file = 'setmp_step_file_in.dat' # index selects either amplitude or phase step on the set-point if (test_type == 2 or test_type == 4): # Amplitude modulation index = 0 elif (test_type == 3): # Phase modulation index = 1 # Apply step to set-point to amplitude or phase depending on the test setmp['value'][ index] = setmp['value'][index] + setmp_step_val # set X, set Y write_reg_file([setmp], regmap_fdbk_core, setmp_file) # Write configuration file for limits step lim_file = 'lim_step_file_in.dat' lim['value'] = lim_step_val write_reg_file([lim], regmap_fdbk_core, lim_file) # Command line to run Verilog simulation # Arguments depend on the type of test-bench being run (test_type) command = 'vvp -n fdbk_core_tb +vcd +test=' + \ str(test_type) + ' +in_file=' + in_file + ' +out_file=' + out_file + \ ' +sp_step_time=' + str(setmp_step_time) + ' +sp_step_file=' + setmp_file +\ ' +lim_step_time=' + str(lim_step_time) + ' +lim_step_file=' + lim_file if test_type == 3: command = command + ' +in_i=' + str(in_i) + ' +in_q=' + str(in_q) elif (test_type == 0 or test_type == 4): command = 'vvp -n fdbk_core_tb +vcd +test=' + str( test_type) + ' +in_file=' + in_file + ' +out_file=' + out_file elif (test_type == 1): command = 'vvp -n fdbk_core_tb +vcd +test=' + str( test_type ) + ' +in_file=' + in_file + ' +out_file=' + out_file + ' +in_i=' + str( in_i) + ' +in_q=' + str(in_q) # Run the Verilog test-bench print 'Running Verilog test-bench from Python...\n\n' + command + '\n' if command == '': print 'test_type not defined with a valid code' print 'test_type value supplied is ' + str(test_type) else: return_code = call(command, shell=True)
def gen_reg_list(verbose=False): """ gen_reg_list: Replication of the old param.py program for illustration purposes. Run this program in order to call JSON parser, user-defined physics settings to FPGA register space conversion, and prints used by Verilog test-bench for configuration parsing. """ # Get the simulation and accelerator objects from the JSON parser file_list = [ base_dir + "/configfiles/LCLS-II/default_accelerator.json", base_dir + "/configfiles/LCLS-II/LCLS-II_accelerator.json", base_dir + "/configfiles/LCLS-II/LCLS-II_append.json" ] simulation = parseSim.ParseSimulation(file_list, verbose) sim_base = 0 # base address for vmod1, see larger.v line 33 # Convert user input into FPGA registers regmap, error_cnt, c_reg_base = getFPGADict(simulation) # Pseudo-random generator initialization, see tt800v.v and prng.v prng_seed = "pushmi-pullyu" prng_iva = regmap["cavity_0_prng_iva"] prng_ivb = regmap["cavity_0_prng_ivb"] prng_random_run = regmap["cavity_0_prng_random_run"] if (prng_seed is not None): from readjson_accelerator import push_seed from hashlib import sha1 # print "# PRNG subsystem seed is '%s'"%prng_seed hf = sha1() hf.update(prng_seed) push_seed(prng_iva.address + sim_base, hf) push_seed(prng_ivb.address + sim_base, hf) # print "%d 1 # turn on PRNG"%(prng_random_run.address+sim_base) # 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 wave_samp_per = 1 wave_shift = 3 reg_list = [] # Variables defined by hand, should really come from JSON controller configuration piezo_dc = 0 ph_offset = -150800 sel_en = 1 sel_thresh = 5000 lp1a_kx = 20486 lp1a_ky = -20486 chan_keep = 4080 ctl_regmap = get_map(base_dir + "/../_autogen/regmap_llrf_shell.json") reg = Register(**get_reg_info(ctl_regmap, [], 'wave_samp_per')) reg.value = wave_samp_per reg_list.append(reg) reg = Register(**get_reg_info(ctl_regmap, [], 'wave_shift')) reg.value = wave_shift reg_list.append(reg) reg = Register(**get_reg_info(ctl_regmap, [], 'piezo_dc')) reg.value = piezo_dc reg_list.append(reg) reg = Register(**get_reg_info(ctl_regmap, [], 'sel_thresh')) reg.value = sel_thresh reg_list.append(reg) reg = Register(**get_reg_info(ctl_regmap, [], 'ph_offset')) reg.value = ph_offset reg_list.append(reg) reg = Register(**get_reg_info(ctl_regmap, [], 'sel_en')) reg.value = sel_en reg_list.append(reg) reg = Register(**get_reg_info(ctl_regmap, [], 'lp1a_kx')) reg.value = lp1a_kx reg_list.append(reg) reg = Register(**get_reg_info(ctl_regmap, [], 'lp1a_ky')) reg.value = lp1a_ky reg_list.append(reg) reg = Register(**get_reg_info(ctl_regmap, [], 'chan_keep')) reg.value = chan_keep reg_list.append(reg) # reg = Register(**get_reg_info(ctl_regmap,[],'setmp')) # reg.name = 'set_X' # reg.address = reg.address + 0 # reg.value = int(set_X) # reg_list.append(reg) # reg = Register(**get_reg_info(ctl_regmap,[],'setmp')) # reg.name = 'set_P' # reg.address = reg.address + 1 # reg.value = int(set_P) # reg_list.append(reg) # reg = Register(**get_reg_info(ctl_regmap,[],'coeff')) # reg.name = 'coeff_X_I' # reg.address = reg.address + 0 # reg.value = coeff_X_I # reg_list.append(reg) # reg = Register(**get_reg_info(ctl_regmap,[],'coeff')) # reg.name = 'coeff_X_P' # reg.address = reg.address + 1 # reg.value = coeff_X_P # reg_list.append(reg) # reg = Register(**get_reg_info(ctl_regmap,[],'coeff')) # reg.name = 'coeff_Y_I' # reg.address = reg.address + 2 # reg.value = coeff_Y_I # reg_list.append(reg) # reg = Register(**get_reg_info(ctl_regmap,[],'coeff')) # reg.name = 'coeff_Y_P' # reg.address = reg.address + 3 # reg.value = coeff_Y_P # reg_list.append(reg) # reg = Register(**get_reg_info(ctl_regmap,[],'lim')) # reg.name = 'lim_X_hi' # reg.address = reg.address + 0 # reg.value = lim_X_hi # reg_list.append(reg) # reg = Register(**get_reg_info(ctl_regmap,[],'lim')) # reg.name = 'lim_Y_hi' # reg.address = reg.address + 1 # reg.value = lim_Y_hi # reg_list.append(reg) # reg = Register(**get_reg_info(ctl_regmap,[],'lim')) # reg.name = 'lim_X_lo' # reg.address = reg.address + 2 # reg.value = lim_X_lo # reg_list.append(reg) # reg = Register(**get_reg_info(ctl_regmap,[],'lim')) # reg.name = 'lim_Y_lo' # reg.address = reg.address + 3 # reg.value = lim_Y_lo # reg_list.append(reg) addr = get_reg_info(ctl_regmap, [], 'lim')['base_addr'] # Variables needed # reg_list.extend(delay_set(0, addr, amp_max)) # 52 is the address of lim_X_hi # reg_list.extend(delay_set(duration*8, addr+2, amp_max)) # 54 is the address of lim_X_lo # reg_list.extend(delay_set(0, addr, 0)) # 52 is the address of lim_X_hi # reg_list.extend(delay_set(0, addr+2, 0)) # 54 is the address of lim_X_lo # for jx in range(6): # reg_list.extend(delay_set(0,0,0)) for item in reg_list: if item.name != '': regmap[item.name] = item if (error_cnt > 0): print "# %d scaling errors found" % error_cnt exit(1) return regmap
# 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 vmod1_tb.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 # Read registers from regmap_gen_vmod1 sim_base = 0 # base address for vmod1 from read_regmap import get_map regmap = get_map(regmap_file) # ==== end of hardware register dictionaries # scale a floating point number in range [-1,1) to fit in b-bit register error_cnt = 0 def fix(x, b, msg, opt=None): global error_cnt ss = 2**(b - 1) # cordic_g = 1.646760258 if (opt is "cordic"): ss = int(ss / 1.646760258) xx = int(x * ss + 0.5) #print x,b,ss,xx if (xx > ss - 1): xx = ss - 1
from math import sin, cos, pi, sqrt, log from numpy import log as clog from numpy import exp as cexp from numpy import ceil # http://stackoverflow.com/questions/14132789/python-relative-imports-for-the-billionth-time # Leaves me with only one choice ... :( # Since I don't want to modify shell variables import os, sys sys.path.append( os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "/submodules/build") from read_regmap import get_map, get_reg_info cav_num = 0 regmap_cryomodule = get_map("./_autogen/regmap_cryomodule.json") #ctl_regmap = get_map("./_autogen/regmap_llrf_shell.json") #c_reg_base = get_reg_info(regmap_cryomodule,[],'llrf_'+str(cav_num)+'_xxxx')['base_addr'] # 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