def __init__(self, target: FPGATarget): super().__init__() self.project_dir = back2fwd(target.project_root) self.dcp_path = back2fwd( os.path.join(self.project_dir, target.prj_cfg.vivado_config.project_name + r".runs", r"synth_1", target.cfg.top_module + r".dcp"))
def subst_path(self, drive): self.subst = drive if os.path.exists(self.subst): print(self.subst, " already exists, saving copy of existing subst path") self.old_subst = back2fwd(str(pathlib.Path(self.subst).resolve())) # deleting old subst self.writeln('exec subst ' + self.subst + ' /d') self.writeln('exec subst ' + self.subst + ' ' + back2fwd(os.path.dirname(self.target.project_root))) project_root = self.subst + r"\\" + self.target.prj_cfg.vivado_config.project_name return project_root
def add_vhdl_sources(self, vhdl_src): for src in vhdl_src: if src.files: self.add_files(src.files) if src.library is not None: file_list = '{ ' + ' '.join('"' + back2fwd(file) + '"' for file in src.files) + ' }' self.set_property('library', value=f'{{{src.library}}}', objects=f'[get_files {file_list}]') if src.version is not None: file_list = '{ ' + ' '.join('"' + back2fwd(file) + '"' for file in src.files) + ' }' self.set_property('file_type', value=f'{{{src.version}}}', objects=f'[get_files {file_list}]')
def add_bd_file(self, bd_files: [BDFile]): for bd_file in bd_files: if bd_file.files: cmd = ['read_bd'] cmd.append('{ ' + ' '.join('"' + back2fwd(file) + '"' for file in bd_file.files) + ' }') self.writeln(' '.join(cmd))
def add_verilog_headers(self, ver_hdr): for src in ver_hdr: if src.files: self.add_files(src.files) file_list = '{ ' + ' '.join('"' + back2fwd(file) + '"' for file in src.files) + ' }' self.set_property('file_type', '{Verilog Header}', f'[get_files {file_list}]')
def add_ip_repo(self, ip_repos: [IPRepo]): for ip_repo in ip_repos: if ip_repo.files: self.set_property(name='ip_repo_paths', value='{ ' + ' '.join('"' + back2fwd(file) + '"' for file in ip_repo.files) + ' }', objects='[current_project]') self.writeln('update_ip_catalog -rebuild')
def add_verilog_sources(self, ver_src: [VerilogSource]): for src in ver_src: if src.files: self.add_files(src.files) if src.version is not None: file_list = '{ ' + ' '.join('"' + back2fwd(file) + '"' for file in src.files) + ' }' self.set_property('file_type', value=f'{{{src.version}}}', objects=f'[get_files {file_list}]')
def add_files(self, files, norecurse=True, fileset=None): if files is None or len(files) == 0: # don't generate a command because add_files does not work with # an empty list of files return cmd = ['add_files'] if fileset is not None: cmd.extend(['-fileset', fileset]) if norecurse: cmd.append('-norecurse') cmd.append('{ ' + ' '.join('"' + back2fwd(file) + '"' for file in files) + ' }') self.writeln(' '.join(cmd))
def __init__(self, ip_name, ip_module_name=None, props=None, ip_dir=None): super().__init__() # set defaults if props is None: props = {} if ip_module_name is None: ip_module_name = ip_name + '_0' self.ip_name = ip_name self.ip_module_name = ip_module_name self.ip_xci_path = back2fwd( os.path.join(ip_dir, ip_module_name, f'{ip_module_name}.xci')) self.props = props
def create_project(self, project_name, project_directory, force=False, full_part_name=None, board_part=None): # create the project cmd = ['create_project'] if force: cmd.append('-force') cmd.append('"' + project_name + '"') cmd.append('"' + back2fwd(project_directory) + '"') if full_part_name is not None: cmd.extend(['-part', full_part_name]) self.writeln(' '.join(cmd)) # specify the board part if known if board_part is not None: self.writeln( f'set_property board_part {board_part} [current_project]')
def __init__(self, pcfg: EmuConfig, scfg: StructureConfig, ltxfile_path, server_addr: str): super().__init__(trim_blocks=False, lstrip_blocks=False) pcfg = pcfg scfg = scfg # set server address self.server_addr = server_addr # set the paths to the LTX file self.ltx_file = back2fwd(ltxfile_path) # set the JTAG frequency. sometimes it is useful to try a slower frequency than default if there # are problems with the debug hub clock self.jtag_freq = str(int(pcfg.cfg.jtag_freq)) # set the "short" device name which is used to distinguish the FPGA part from other USB devices self.device_name = pcfg.board.short_part_name # Set aliases for probes self.probe_aliases = SVAPI() for probe in scfg.digital_probes + scfg.analog_probes + [ scfg.time_probe ]: self.probe_aliases.writeln( f'set {probe.name} [get_hw_probes "trace_port_gen_i/{probe.name}" -of_objects $ila_0_i]' ) # Set radix for probes self.probe_radix = SVAPI() for digital_probe in (scfg.digital_probes + [scfg.time_probe]): signed = 'SIGNED' if digital_probe.signed else 'UNSIGNED' self.probe_radix.writeln( f'set_property DISPLAY_RADIX {signed} ${digital_probe.name}') for analog_probe in scfg.analog_probes: self.probe_radix.writeln( f'set_property DISPLAY_RADIX SIGNED ${analog_probe.name}')
def __init__(self, pcfg: EmuConfig, scfg: StructureConfig, bitfile_path, ltxfile_path, server_addr: str): super().__init__(trim_blocks=False, lstrip_blocks=False) pcfg = pcfg scfg = scfg # set server address self.server_addr = server_addr # set the paths to the BIT and LTX file self.bit_file = back2fwd(bitfile_path) self.ltx_file = back2fwd(ltxfile_path) # set the JTAG frequency. sometimes it is useful to try a slower frequency than default if there # are problems with the debug hub clock self.jtag_freq = str(int(pcfg.cfg.jtag_freq)) # save some parameters from the board configuration self.device_name = pcfg.board.short_part_name self.no_rev_check = pcfg.board.no_rev_check # Set aliases for VIOs self.ctrl_io_aliases = SVAPI() for io in scfg.digital_ctrl_inputs + scfg.digital_ctrl_outputs + \ scfg.analog_ctrl_inputs + scfg.analog_ctrl_outputs: self.ctrl_io_aliases.writeln( f'set {io.name} [get_hw_probes "sim_ctrl_gen_i/{io.name}" -of_objects $vio_0_i]' ) # Set radix for VIOs self.ctrl_io_radix = SVAPI() for digital_in in scfg.digital_ctrl_inputs: signed = 'SIGNED' if digital_in.signed else 'UNSIGNED' self.ctrl_io_radix.writeln( f'set_property OUTPUT_VALUE_RADIX {signed} ${digital_in.name}') for digital_out in scfg.digital_ctrl_outputs: signed = 'SIGNED' if digital_out.signed else 'UNSIGNED' self.ctrl_io_radix.writeln( f'set_property INPUT_VALUE_RADIX {signed} ${digital_out.name}') for analog_in in scfg.analog_ctrl_inputs: self.ctrl_io_radix.writeln( f'set_property OUTPUT_VALUE_RADIX SIGNED ${analog_in.name}') for analog_out in scfg.analog_ctrl_outputs: self.ctrl_io_radix.writeln( f'set_property INPUT_VALUE_RADIX SIGNED ${analog_out.name}') # Set aliases for probes self.probe_aliases = SVAPI() for probe in scfg.digital_probes + scfg.analog_probes + [ scfg.time_probe ]: self.probe_aliases.writeln( f'set {probe.name} [get_hw_probes "trace_port_gen_i/{probe.name}" -of_objects $ila_0_i]' ) # Set radix for probes self.probe_radix = SVAPI() for digital_probe in (scfg.digital_probes + [scfg.time_probe]): signed = 'SIGNED' if digital_probe.signed else 'UNSIGNED' self.probe_radix.writeln( f'set_property DISPLAY_RADIX {signed} ${digital_probe.name}') for analog_probe in scfg.analog_probes: self.probe_radix.writeln( f'set_property DISPLAY_RADIX SIGNED ${analog_probe.name}')
def set_header_files(self): file_list = '{ ' + ' '.join('"' + back2fwd(file) + '"' for file in self.files) + ' }' self.set_property('file_type', '{Verilog Header}', f'[get_files {file_list}]')
def build(self): #subst drive drive = 'V:' # Check if on-chip memory is sufficient on selected FPGA board if self.target.prj_cfg.board.bram is not None: bits_per_sample = 0 probes = (self.target.str_cfg.digital_probes + self.target.str_cfg.analog_probes + [self.target.str_cfg.time_probe] + [self.target.str_cfg.dec_cmp]) for probe in probes: bits_per_sample += int(probe.width) if (bits_per_sample * self.target.prj_cfg.ila_depth) > ( self.target.prj_cfg.board.bram * 8): raise ( f'ERROR: Number ob samples to be recorded does not fit on FPGA board, please either select a ' f'board with more block memory, or change the ila_depth') else: print( f'WARNING: Check for sufficient BRAM could not be conducted, ' f'not enough information given in board definition!') scfg = self.target.str_cfg """ type : StructureConfig """ project_root = self.target.project_root # under Windows there is the problem with path length more than 146 characters, that's why we have to use # subst command to substitute project directory to a drive letter if os.name == 'nt': if len(back2fwd(self.target.project_root)) > 80: project_root = self.subst_path(drive=drive) # create a new project self.create_project( project_name=self.target.prj_cfg.vivado_config.project_name, project_directory=project_root, full_part_name=self.target.prj_cfg.board.full_part_name, board_part=self.target.prj_cfg.board.board_part, force=True) # add all source files to the project (including header files) self.add_project_sources(content=self.target.content) # define the top module self.set_property('top', f"{{{self.target.cfg.top_module}}}", '[current_fileset]') # set define variables self.add_project_defines(content=self.target.content, fileset='[current_fileset]') # specify the level of flattening to use self.set_property('STEPS.SYNTH_DESIGN.ARGS.FLATTEN_HIERARCHY', self.target.prj_cfg.cfg.flatten_hierarchy, '[get_runs synth_1]') # append user constraints for xdc_file in self.target.content.xdc_files: for file in xdc_file.files: self.writeln(f'read_xdc "{back2fwd(file)}"') if not self.target.cfg.custom_top: # write constraints to file constrs = CodeGenerator() # generate constraints for external clk constrs.use_templ(TemplExtClk(target=self.target)) # generate clock wizard IP core self.use_templ(TemplClkWiz(target=self.target)) # Add IP cores necessary for control interface ip_core_templates = self.target.ctrl.add_ip_cores( scfg=scfg, ip_dir=self.target.ip_dir) for ip_core_template in ip_core_templates: self.use_templ(ip_core_template) ## Add constraints for additional generated emu_clks constrs.writeln( 'create_generated_clock -name emu_clk -source [get_pins clk_gen_i/clk_wiz_0_i/clk_out1] -divide_by 2 [get_pins gen_emu_clks_i/buf_emu_clk/I]' ) for k in range(scfg.num_gated_clks): constrs.writeln( f'create_generated_clock -name clk_other_{k} -source [get_pins clk_gen_i/clk_wiz_0_i/clk_out1] -divide_by 4 [get_pins gen_emu_clks_i/buf_{k}/I]' ) # Setup ILA for signal probing - only of at least one probe is defined if len(scfg.analog_probes + scfg.digital_probes + [scfg.time_probe]) != 0: self.use_templ( TemplILA(target=self.target, depth=self.target.prj_cfg.ila_depth)) # Setup Debug Hub constrs.use_templ(TemplDbgHub(target=self.target)) # Add false paths for Zynq control signals. This is necessary in some cases # to provide timing violations, since the ARM core is running on a different # clock that the emulator circuitry. This is a real problem, but is handled # in firmware with handshaking and very short delays. if self.target.cfg.fpga_sim_ctrl == FPGASimCtrl.UART_ZYNQ: constrs.writeln( 'set_false_path -through [get_pins sim_ctrl_gen_i/zynq_gpio_i/*]' ) # write master constraints to file and add to project master_constr_path = os.path.join(self.target.prj_cfg.build_root, 'constrs.xdc') constrs.write_to_file(master_constr_path) self.add_files([master_constr_path], fileset='constrs_1') # read user-provided IPs self.writeln('# Custom user-provided IP cores') for xci_file in self.target.content.xci_files: for file in xci_file.files: self.writeln(f'read_ip "{back2fwd(file)}"') # read user-provided TCL scripts self.writeln('# Custom user-provided TCL scripts') for tcl_file in self.target.content.tcl_files: for file in tcl_file.files: self.writeln(f'source "{back2fwd(file)}"') # upgrade IPs as necessary self.writeln('if {[get_ips] ne ""} {') self.writeln(' upgrade_ip [get_ips]') self.writeln('}') # generate all IPs self.writeln('generate_target all [get_ips]') # create additional Hardware for control interface if self.target.cfg.fpga_sim_ctrl == FPGASimCtrl.UART_ZYNQ: self.use_templ(TemplZynqGPIO(is_ultrascale=scfg.is_ultrascale)) # launch the build and wait for it to finish num_cores = min(int(self.target.prj_cfg.vivado_config.num_cores), 8) self.writeln( f'launch_runs impl_1 -to_step write_bitstream -jobs {num_cores}') self.writeln('wait_on_run impl_1') # re-generate the LTX file # without this step, the ILA probes are sometimes split into individual bits impl_dir = os.path.join( project_root, f'{self.target.prj_cfg.vivado_config.project_name}.runs', 'impl_1', ) ltx_file_path = os.path.join(impl_dir, f"{self.target.cfg.top_module}.ltx") self.writeln('open_run impl_1') self.writeln( f'write_debug_probes -force {{{back2fwd(ltx_file_path)}}}') # export the XSA if this is a more recent version of vivado if self.version_year >= 2020: xsa_file_path = os.path.join(impl_dir, f'{self.target.cfg.top_module}.xsa') xsa_file_path = back2fwd(xsa_file_path) self.writeln( f'write_hw_platform -fixed -include_bit -force -file {{{xsa_file_path}}}' ) #remove and restore drive substitutions if self.subst: self.writeln('exec subst ' + self.subst + ' /d') if self.old_subst: self.writeln('exec subst ' + self.subst + ' ' + self.old_subst) # run bitstream generation ret_error = self.run(filename=r"bitstream.tcl", stack=self.target.prj_cfg.cfg.vivado_stack, return_error=True) if os.name == 'nt': if ret_error: #remove and restore drive substitutions if self.subst: try: subprocess.call(f'subst {drive} /d', shell=True) except: print( f'WARNING: Removing mapped drive:{drive} did not work.' ) if self.old_subst: try: subprocess.call(f'subst {drive} {self.old_subst}', shell=True) except: print( f'WARNING: Mapping of drive:{drive} to network path: {self.old_subst} did not work.' )
def __init__(self, target): super().__init__(trim_blocks=True, lstrip_blocks=True) scfg = target.str_cfg """ :type: StructureConfig """ pcfg = target.prj_cfg """ :type: EmuConfig """ self.result_path_raw = back2fwd(target.result_path_raw) ##################################################### # Add plugin specific includes ##################################################### self.plugin_includes = SVAPI() for plugin in target.plugins: for include_statement in plugin.include_statements: self.plugin_includes.writeln(f'{include_statement}') ##################################################### # Create module interface ##################################################### self.module_ifc = SVAPI() module = ModuleInst(api=self.module_ifc, name='top') module.add_inputs(scfg.clk_i) if ((target.cfg.fpga_sim_ctrl is not None) and (target.cfg.fpga_sim_ctrl == FPGASimCtrl.UART_ZYNQ) and (not pcfg.board.is_ultrascale)): module.add_inouts(TemplZynqGPIO.EXT_IOS) module.generate_header() ##################################################### # Manage clks ##################################################### # Add clk in signals for simulation case self.clk_in_sim_sigs = SVAPI() for clk_i in scfg.clk_i: self.clk_in_sim_sigs.gen_signal(io_obj=clk_i) # Add dbg clk signals self.dbg_clk_sigs = SVAPI() self.dbg_clk_sigs.gen_signal(io_obj=scfg.dbg_clk) # Instantiation of clk_gen wrapper self.clk_gen_ifc = SVAPI() clk_gen = ModuleInst(api=self.clk_gen_ifc, name='clk_gen') clk_gen.add_inputs(scfg.clk_i, connections=scfg.clk_i) clk_gen.add_output(scfg.emu_clk_2x, connection=scfg.emu_clk_2x) clk_gen.add_output(scfg.dbg_clk, connection=scfg.dbg_clk) clk_gen.add_outputs(scfg.clk_independent, connections=scfg.clk_independent) clk_gen.generate_instantiation() # Absolute path assignments for derived clks self.derived_clk_assigns = SVAPI() for k, clk in enumerate(scfg.clk_derived): self.derived_clk_assigns.writeln(f'// derived clock: {clk.name}') if clk.abspath_emu_dt is not None: self.derived_clk_assigns.writeln( f'assign {clk.abspath_emu_dt} = emu_dt;') if clk.abspath_emu_clk is not None: self.derived_clk_assigns.writeln( f'assign {clk.abspath_emu_clk} = emu_clk;') if clk.abspath_emu_rst is not None: self.derived_clk_assigns.writeln( f'assign {clk.abspath_emu_rst} = emu_rst;') if clk.abspath_dt_req is not None: self.derived_clk_assigns.writeln( f'assign dt_req_{clk.name} = {clk.abspath_dt_req};') if clk.abspath_gated_clk is not None: self.derived_clk_assigns.writeln( f'assign clk_val_{clk.name} = {clk.abspath_gated_clk_req};' ) self.derived_clk_assigns.writeln( f'assign {clk.abspath_gated_clk} = clk_{clk.name};') self.num_dt_reqs = scfg.num_dt_reqs self.num_gated_clks = scfg.num_gated_clks ##################################################### # Manage Ctrl Module ##################################################### # provide information if default oscillator was used for template evaluation self.use_default_oscillator = scfg.use_default_oscillator ctrl_ios = scfg.analog_ctrl_inputs + scfg.analog_ctrl_outputs + scfg.digital_ctrl_inputs + \ scfg.digital_ctrl_outputs ## Instantiate all ctrl signals self.inst_itl_ctlsigs = SVAPI() for ctrl_io in ctrl_ios: self.inst_itl_ctlsigs.gen_signal(io_obj=ctrl_io) ## Instantiate ctrl module self.sim_ctrl_inst_ifc = SVAPI() sim_ctrl_inst = ModuleInst(api=self.sim_ctrl_inst_ifc, name='sim_ctrl_gen') sim_ctrl_inst.add_inputs( scfg.analog_ctrl_outputs + scfg.digital_ctrl_outputs, connections=scfg.analog_ctrl_outputs + scfg.digital_ctrl_outputs) sim_ctrl_inst.add_outputs( scfg.analog_ctrl_inputs + scfg.digital_ctrl_inputs, connections=scfg.analog_ctrl_inputs + scfg.digital_ctrl_inputs) ## Wire through Zynq connections if needed if ((target.cfg.fpga_sim_ctrl is not None) and (target.cfg.fpga_sim_ctrl == FPGASimCtrl.UART_ZYNQ) and (not pcfg.board.is_ultrascale)): sim_ctrl_inst.add_inouts(TemplZynqGPIO.EXT_IOS, connections=TemplZynqGPIO.EXT_IOS) # add master clk to ctrl module emu_clk_sig = DigitalSignal(name='emu_clk', width=1, abspath=None) sim_ctrl_inst.add_input(emu_clk_sig, emu_clk_sig) sim_ctrl_inst.generate_instantiation() ## Assign custom ctrl signals via abs paths into design self.assign_custom_ctlsigs = SVAPI() for ctrl_input in scfg.digital_ctrl_inputs + scfg.analog_ctrl_inputs: if ctrl_input.abs_path is not None: self.assign_custom_ctlsigs.assign_to( io_obj=ctrl_input.abs_path, exp=ctrl_input.name) for ctrl_output in scfg.digital_ctrl_outputs + scfg.analog_ctrl_outputs: self.assign_custom_ctlsigs.assign_to(io_obj=ctrl_output.name, exp=ctrl_output.abs_path) ###################################################### # Manage trace port Module ###################################################### probes = scfg.digital_probes + scfg.analog_probes + [ scfg.time_probe ] + [scfg.dec_cmp] ## Instantiate all probe signals self.inst_probesigs = SVAPI() for probe in probes: self.inst_probesigs.gen_signal(probe) ## Instantiate traceport module self.num_probes = len(probes) self.trap_inst_ifc = SVAPI() trap_inst = ModuleInst(api=self.trap_inst_ifc, name='trace_port_gen') trap_inst.add_inputs(probes, connections=probes) trap_inst.add_input(scfg.emu_clk, connection=scfg.emu_clk) trap_inst.generate_instantiation() ## Assign probe signals via abs paths into design except for time probe self.assign_probesigs = SVAPI() for probe in scfg.digital_probes + scfg.analog_probes: self.assign_probesigs.assign_to(io_obj=probe, exp=probe.abs_path) ###################################################### # Manage emulation clks Module ###################################################### ## Instantiate all emulation clk signals self.inst_emu_clk_sigs = SVAPI() clk_io_pairs = [] for derived_clk in scfg.clk_derived: if derived_clk.abspath_gated_clk is None: continue clk_val = digsig(f'clk_val_{derived_clk.name}') clk = digsig(f'clk_{derived_clk.name}') self.inst_emu_clk_sigs.gen_signal(clk_val) self.inst_emu_clk_sigs.gen_signal(clk) clk_io_pairs.append((clk_val, clk)) # add default oscillator signals, in case default oscillator was used if self.use_default_oscillator: clk_val = digsig(f'clk_val_default_osc') clk = digsig(f'clk_default_osc') self.inst_emu_clk_sigs.gen_signal(clk_val) self.inst_emu_clk_sigs.gen_signal(clk) clk_io_pairs.append((clk_val, clk)) ## Instantiate gen emulation clk module self.emu_clks_inst_ifc = SVAPI() emu_clks_inst = ModuleInst(api=self.emu_clks_inst_ifc, name="gen_emu_clks") emu_clks_inst.add_input(scfg.emu_clk_2x, connection=scfg.emu_clk_2x) emu_clks_inst.add_output(scfg.emu_clk, connection=scfg.emu_clk) for clk_val, clk in clk_io_pairs: emu_clks_inst.add_input(clk_val, connection=clk_val) emu_clks_inst.add_output(clk, connection=clk) emu_clks_inst.generate_instantiation() ###################################################### # Manage default oscillator module ###################################################### emu_dt = digsig('emu_dt', width=pcfg.cfg.dt_width) dt_req_default_osc = DigitalSignal(name=f'dt_req_default_osc', abspath='', width=pcfg.cfg.dt_width, signed=False) if scfg.use_default_oscillator: # instantiate API self.def_osc_api = SVAPI() # generate clock and reset signals # instantiate the default oscillator def_osc_inst = ModuleInst(api=self.def_osc_api, name='osc_model_anasymod', inst_name='def_osc_i') emu_dt_req = digsig('emu_dt_req', width=pcfg.cfg.dt_width) def_osc_inst.add_output(scfg.emu_clk, connection=scfg.emu_clk) def_osc_inst.add_output(scfg.reset_ctrl, connection=scfg.reset_ctrl) def_osc_inst.add_output(emu_dt, connection=emu_dt) def_osc_inst.add_output(emu_dt_req, connection=dt_req_default_osc) def_osc_inst.add_output(digsig('cke'), connection=digsig('clk_val_default_osc')) def_osc_inst.generate_instantiation() else: self.def_osc_api = None ###################################################### # Manage time manager Module ###################################################### ## Instantiate all time manager signals self.inst_timemanager_sigs = SVAPI() self.inst_timemanager_sigs.gen_signal(emu_dt) dt_reqs = [] for derived_clk in scfg.clk_derived: if derived_clk.abspath_dt_req is None: continue dt_req_sig = digsig(f'dt_req_{derived_clk.name}', width=pcfg.cfg.dt_width) self.inst_timemanager_sigs.gen_signal(dt_req_sig) dt_reqs.append(dt_req_sig) # add input for anasymod control dt request signal dt_req_stall = DigitalSignal(name=f'dt_req_stall', abspath='', width=pcfg.cfg.dt_width, signed=False) dt_reqs.append(dt_req_stall) # add input for dt request signal, in case a default oscillator is used if scfg.use_default_oscillator: dt_reqs.append(dt_req_default_osc) ## Instantiate time manager module self.time_manager_inst_ifc = SVAPI() time_manager_inst = ModuleInst(api=self.time_manager_inst_ifc, name='gen_time_manager') time_manager_inst.add_input(scfg.emu_clk, connection=scfg.emu_clk) time_manager_inst.add_input(scfg.reset_ctrl, connection=scfg.reset_ctrl) time_manager_inst.add_output(scfg.time_probe, scfg.time_probe) time_manager_inst.add_output(emu_dt, emu_dt) for dt_req_sig in dt_reqs: time_manager_inst.add_input(dt_req_sig, connection=dt_req_sig) time_manager_inst.generate_instantiation() ###################################################### # Control module ###################################################### self.ctrl_anasymod_inst_ifc = SVAPI() ctrl_anasymod_inst = ModuleInst(api=self.ctrl_anasymod_inst_ifc, name='ctrl_anasymod', inst_name='ctrl_anasymod_i') ctrl_anasymod_inst.add_input(scfg.emu_ctrl_data, connection=scfg.emu_ctrl_data) ctrl_anasymod_inst.add_input(scfg.emu_ctrl_mode, connection=scfg.emu_ctrl_mode) ctrl_anasymod_inst.add_input(scfg.time_probe, connection=scfg.time_probe) ctrl_anasymod_inst.add_input(scfg.dec_thr_ctrl, connection=scfg.dec_thr_ctrl) ctrl_anasymod_inst.add_input(scfg.emu_clk, connection=scfg.emu_clk) ctrl_anasymod_inst.add_input(scfg.reset_ctrl, connection=scfg.reset_ctrl) ctrl_anasymod_inst.add_output(scfg.dec_cmp, connection=scfg.dec_cmp) ctrl_anasymod_inst.add_output(dt_req_stall, connection=dt_req_stall) ctrl_anasymod_inst.generate_instantiation() # indicate whether TSTOP_MSDSL should be used self.use_tstop = target.cfg.tstop is not None ##################################################### # Instantiate testbench ##################################################### self.tb_inst_ifc = SVAPI() tb_inst = ModuleInst(api=self.tb_inst_ifc, name='tb') tb_inst.add_inputs(scfg.clk_independent, connections=scfg.clk_independent) tb_inst.generate_instantiation() ##################################################### # CPU debug mode ##################################################### self.dump_debug_signals = SVAPI() if pcfg.cfg.cpu_debug_mode: dbg_mods_list = pcfg.cfg.cpu_debug_hierarchies if isinstance(dbg_mods_list, list): for dbg_module in dbg_mods_list: if isinstance(dbg_module, list): self.dump_debug_signals.writeln( f'$dumpvars({dbg_module[0]}, {dbg_module[1]});') elif isinstance(dbg_module, int) and len( dbg_mods_list ) == 2: # only one single debug module was provided self.dump_debug_signals.writeln( f'$dumpvars({dbg_mods_list[0]}, {dbg_mods_list[1]});' ) break else: raise Exception( f'ERROR: unexpected format for cpu_debug_hierarchies attribute of ' f'project_config: {dbg_mods_list}, expecting a list including <depth>, ' f'<path_to_module> or a lists of such a list.') elif not dbg_mods_list: self.dump_debug_signals.writeln(f'$dumpvars(0, top.tb_i);') else: raise Exception( f'ERROR: unexpected format for cpu_debug_hierarchies attribute of ' f'project_config: {dbg_mods_list}, expecting tuple, list or None' )
def __init__(self, target: FPGATarget, start_time: float, stop_time: float, server_addr: str): ''' :param start_time: Point in time from which recording run data will start :param stop_time: Point in time where FPGA run will be stopped :param server_addr: Hardware server address for hw server launched by Vivado ''' super().__init__(trim_blocks=False, lstrip_blocks=False) pcfg = target.prj_cfg scfg = target.str_cfg # set server address self.server_addr = server_addr # set the paths to the BIT and LTX file self.bit_file = back2fwd(target.bitfile_path) self.ltx_file = back2fwd(target.ltxfile_path) # set the JTAG frequency. sometimes it is useful to try a slower frequency than default if there # are problems with the debug hub clock self.jtag_freq = str(int(pcfg.cfg.jtag_freq)) # set the "short" device name which is used to distinguish the FPGA part from other USB devices self.device_name = pcfg.board.short_part_name # set the path where the CSV file of results from the ILA should be written self.output = back2fwd(target.result_path_raw) self.ila_reset = target.prj_cfg.vivado_config.ila_reset #tbd remove vio_reset self.vio_reset = target.prj_cfg.vivado_config.vio_reset # set the window count to half the ILA depth. this is required because the window depth will be "2" # unfortunately a window depth of "1" is not allowed in "BASIC" capture mode, which needed to allow # decimation of the sampling rate. self.window_count = str(pcfg.ila_depth / 2) # calculate decimation ratio if stop_time is None: decimation_ratio_float = 2.0 else: decimation_ratio_float = ( (stop_time - start_time) / pcfg.cfg.dt) / (pcfg.ila_depth / 2) # enforce a minimum decimation ratio of 2x since the window depth is 2 decimation_ratio_float = max(decimation_ratio_float, 2.0) # we have to subtract 1 from decimation ratio due to the FPGA implementation of this feature # that is, a decimation ratio setting of "1" actually corresponds to a decimation factor of "2"; a setting # of "2" corresponds to a decimation ratio of "3", and so on. decimation_ratio_setting = int(round(decimation_ratio_float)) - 1 # determine starting time as an integer value time = target.str_cfg.time_probe start_time_int = int(round(start_time / pcfg.cfg.dt_scale)) # export the decimation ratio, starting time, and time signal name to the template self.time_name = time.name self.decimation_ratio_setting = str(decimation_ratio_setting) self.start_time_int = f"{int(time.width)}'u{start_time_int}" # set display radix for analog probes # TODO: is it necessary to wrap the commands in "catch"? self.analog_probe_radix = [] for probe in scfg.analog_probes: self.analog_probe_radix += [ f'catch {{set_property DISPLAY_RADIX SIGNED [get_hw_probes trace_port_gen_i/{probe.name}]}}' ] self.analog_probe_radix = '\n'.join(self.analog_probe_radix) # set display radix for digital probes # TODO: is it necessary to wrap the commands in "catch"? self.digital_probe_radix = [] for probe in (scfg.digital_probes + [scfg.time_probe]): signed = 'SIGNED' if probe.signed else 'UNSIGNED' self.digital_probe_radix += [ f'catch {{set_property DISPLAY_RADIX {signed} [get_hw_probes trace_port_gen_i/{probe.name}]}}' ] self.digital_probe_radix = '\n'.join(self.digital_probe_radix)