From a08c5201ade491643940466419c94457849f624b Mon Sep 17 00:00:00 2001 From: Franck Jullien Date: Tue, 28 Sep 2021 18:06:57 +0200 Subject: [PATCH] Efinix: improve ifacewriter + misc --- litex/build/efinix/efinity.py | 5 +- litex/build/efinix/ifacewriter.py | 100 +++++++++++++++++++++----- litex/soc/cores/clock/efinix_trion.py | 11 +-- 3 files changed, 93 insertions(+), 23 deletions(-) diff --git a/litex/build/efinix/efinity.py b/litex/build/efinix/efinity.py index c473f031c..ddeeec020 100644 --- a/litex/build/efinix/efinity.py +++ b/litex/build/efinix/efinity.py @@ -287,6 +287,8 @@ class EfinityToolchain(): run = True, **kwargs): + self.ifacewriter.set_build_params(platform, build_name) + # Create build directory cwd = os.getcwd() os.makedirs(build_dir, exist_ok=True) @@ -342,7 +344,8 @@ class EfinityToolchain(): # DDR doesn't have Python API so we need to configure it # directly in the peri.xml file - # self.ifacewriter.add_ddr_xml(build_name) + if self.ifacewriter.xml_blocks: + self.ifacewriter.generate_xml_blocks() # Run if run: diff --git a/litex/build/efinix/ifacewriter.py b/litex/build/efinix/ifacewriter.py index 670e4aac2..ccf8acf2d 100644 --- a/litex/build/efinix/ifacewriter.py +++ b/litex/build/efinix/ifacewriter.py @@ -16,11 +16,72 @@ class InterfaceWriter(): def __init__(self, efinity_path): self.efinity_path = efinity_path self.blocks = [] + self.xml_blocks = [] + self.filename = '' + self.platform = None - def add_ddr_xml(self, filename): + def set_build_params(self, platform, build_name): + self.filename = build_name + self.platform = platform + + def generate_xml_blocks(self): et.register_namespace('efxpt', "http://www.efinixinc.com/peri_design_db") - tree = et.parse(filename + '.peri.xml') + tree = et.parse(self.filename + '.peri.xml') root = tree.getroot() + + for block in self.xml_blocks: + if block['type'] == 'DDR': + self.add_ddr_xml(root, block) + if block['type'] == 'LVDS': + self.add_ddr_lvds(root, block) + + xml_string = et.tostring(root, 'utf-8') + reparsed = expatbuilder.parseString(xml_string, False) + print_string = reparsed.toprettyxml(indent=" ") + + # Remove lines with only whitespaces. Not sure why they are here + print_string = os.linesep.join([s for s in print_string.splitlines() if s.strip()]) + + tools.write_to_file("{}.peri.xml".format(self.filename), print_string) + + def add_ddr_lvds(self, root, params): + lvds_info = root.find('efxpt:lvds_info', namespaces) + if params['mode'] == 'OUTPUT': + dir = 'tx' + mode = 'out' + else: + dir = 'rx' + mode = 'in' + + pad = self.platform.parser.get_gpio_instance_from_pin(params['location'][0]) + pad = pad.replace('TXP', 'TX') + pad = pad.replace('TXN', 'TX') + pad = pad.replace('RXP', 'RX') + pad = pad.replace('RXN', 'RX') + # Sometimes there is an extra identifier at the end + # TODO: do a better parser + if pad.count('_') == 2: + pad = pad.rsplit('_', 1)[0] + + lvds = et.SubElement(lvds_info, 'efxpt:lvds', + name = params['name'], + lvds_def = pad, + ops_type = dir) + + et.SubElement(lvds, 'efxpt:ltx_info', pll_instance = '', + fast_clock_name = '{}'.format(params['fast_clk']), + slow_clock_name = '{}'.format(params['slow_clk']), + reset_name = '', + out_bname = '{}'.format(params['name']), + oe_name = '', + clock_div = '1', + mode = '{}'.format(mode), + serialization = '{}'.format(params['serialisation']), + reduced_swing = 'false', + load = '3') + + + def add_ddr_xml(self, root, params): ddr_info = root.find('efxpt:ddr_info', namespaces) ddr = et.SubElement(ddr_info, 'efxpt:ddr', @@ -135,15 +196,6 @@ class InterfaceWriter(): et.SubElement(cs_gate_delay, 'efxpt:param', name='GATE_C_DLY', value= '3', value_type='int') et.SubElement(cs_gate_delay, 'efxpt:param', name='GATE_F_DLY', value= '0', value_type='int') - xml_string = et.tostring(root, 'utf-8') - reparsed = expatbuilder.parseString(xml_string, False) - print_string = reparsed.toprettyxml(indent=" ") - - # Remove lines with only whitespaces. Not sure why they are here - print_string = os.linesep.join([s for s in print_string.splitlines() if s.strip()]) - - tools.write_to_file("{}.peri.xml".format(filename), print_string) - def header(self, build_name, partnumber): header = "# Autogenerated by LiteX / git: " + tools.get_litex_git_revision() header += """ @@ -185,8 +237,16 @@ design.create('{2}', '{3}', './../gateware', overwrite=True) mode = block['mode'] cmd = '' - -# TODO: {'type': 'GPIO', 'mode': 'OUTPUT', 'location': 'U16', 'size': 4, 'in_reg': 'DDIO_RESYNC', 'out_clk_pin': '', 'is_inclk_inverted': False, 'name': 'auto_ethtx_tx_data_d1', 'name_d2': 'auto_ethtx_tx_data_d2'} + if mode == 'INOUT': + if len(block['location']) == 1: + cmd += 'design.create_inout_gpio("{}")\n'.format(name) + cmd += 'design.assign_pkg_pin("{}","{}")\n'.format(name, block['location'][0]) + else: + cmd += 'design.create_inout_gpio("{}",{},0)\n'.format(name, block['size']-1) + for i, pad in enumerate(block['location']): + cmd += 'design.assign_pkg_pin("{}[{}]","{}")\n'.format(name, i, pad) + cmd += '\n' + return cmd if mode == 'INPUT': if len(block['location']) == 1: @@ -198,7 +258,7 @@ design.create('{2}', '{3}', './../gateware', overwrite=True) cmd += 'design.assign_pkg_pin("{}[{}]","{}")\n'.format(name, i, pad) if 'in_reg' in block: cmd += 'design.set_property("{}","IN_REG","{}")\n'.format(name, block['in_reg']) - cmd += 'design.set_property("{}","IN_CLK_PIN","{}")\n\n'.format(name, block['in_clk_pin']) + cmd += 'design.set_property("{}","IN_CLK_PIN","{}")\n'.format(name, block['in_clk_pin']) return cmd if mode == 'OUTPUT': @@ -262,20 +322,26 @@ design.create('{2}', '{3}', './../gateware', overwrite=True) cmd += 'design.set_property("{}", pll_config, block_type="PLL")\n\n'.format(name) + for i, clock in enumerate(block['clk_out']): + cmd += 'design.set_property("{}","CLKOUT{}_PHASE","{}","PLL")\n'.format(name, i, clock[2]) + cmd += 'target_freq = {\n' for i, clock in enumerate(block['clk_out']): cmd += ' "CLKOUT{}_FREQ": "{}",\n'.format(i, clock[1] / 1e6) - cmd += ' "CLKOUT{}_PHASE": "{}",\n'.format(i, clock[2]) cmd += '}\n' - cmd += 'calc_result = design.auto_calc_pll_clock("{}", target_freq)\n\n'.format(name) + cmd += 'calc_result = design.auto_calc_pll_clock("{}", target_freq)\n'.format(name) + if 'extra' in block: + cmd += block['extra'] + cmd += '\n' if verbose: cmd += 'print("#### {} ####")\n'.format(name) cmd += 'clksrc_info = design.trace_ref_clock("{}", block_type="PLL")\n'.format(name) cmd += 'pprint.pprint(clksrc_info)\n' - cmd += 'clock_source_prop = ["REFCLK_SOURCE", "CORE_CLK_PIN", "EXT_CLK", "CLKOUT0_EN", "CLKOUT1_EN","REFCLK_FREQ", "RESOURCE"]\n' + cmd += 'clock_source_prop = ["REFCLK_SOURCE", "CORE_CLK_PIN", "EXT_CLK", "CLKOUT1_EN", "CLKOUT2_EN","REFCLK_FREQ", "RESOURCE"]\n' cmd += 'clock_source_prop += ["CLKOUT0_FREQ", "CLKOUT1_FREQ", "CLKOUT2_FREQ"]\n' + cmd += 'clock_source_prop += ["CLKOUT0_PHASE", "CLKOUT1_PHASE", "CLKOUT2_PHASE"]\n' cmd += 'prop_map = design.get_property("{}", clock_source_prop, block_type="PLL")\n'.format(name) cmd += 'pprint.pprint(prop_map)\n' diff --git a/litex/soc/cores/clock/efinix_trion.py b/litex/soc/cores/clock/efinix_trion.py index 3811b7d63..205b23c89 100644 --- a/litex/soc/cores/clock/efinix_trion.py +++ b/litex/soc/cores/clock/efinix_trion.py @@ -11,8 +11,6 @@ from migen.genlib.resetsync import AsyncResetSynchronizer from litex.build.generic_platform import * from litex.soc.cores.clock.common import * -from litex.build.efinix import EfinixDbParser - class Open(Signal): pass #TODO: do somthing else @@ -61,16 +59,15 @@ class TRIONPLL(Module): # If clkin has a pin number, PLL clock input is EXTERNAL if self.platform.get_pin_location(clkin): - pad_name = self.platform.get_pin_location(clkin) + pad_name = self.platform.get_pin_location(clkin)[0] self.platform.delete(clkin) #tpl = "create_clock -name {clk} -period {period} [get_ports {{{clk}}}]" #sdc = self.platform.toolchain.additional_sdc_commands #sdc.append(tpl.format(clk=block['input_clock_name'], period=1/freq)) - parser = EfinixDbParser(self.platform.efinity_path, self.platform.device) try: - (pll_res, clock_no) = parser.get_pll_inst_from_pin(pad_name) + (pll_res, clock_no) = self.platform.parser.get_pll_inst_from_pin(pad_name) except: self.logger.error("Cannot find a pll with {} as input".format(pad_name)) quit() @@ -115,6 +112,10 @@ class TRIONPLL(Module): block = self.platform.toolchain.ifacewriter.get_block(self.pll_name) block['clk_out'].append([clk_out_name, freq, phase, margin]) + def extra(self, extra): + block = self.platform.toolchain.ifacewriter.get_block(self.pll_name) + block['extra'] = extra + def compute_config(self): pass