From 0e993890b4445d2021238a6c25555a1ba5c5f2f7 Mon Sep 17 00:00:00 2001 From: chungshien-chai Date: Thu, 26 Sep 2024 14:02:32 -0700 Subject: [PATCH 1/4] New way to decide the IO routing configuration bits --- .../CFGDeviceDatabase/CMakeLists.txt | 2 + .../Gemini/config_attributes.mapping.json | 446 ++--- .../CFGDeviceDatabase/Virgo/1vg28_routing.py | 110 ++ .../Virgo/config_attributes.mapping.json | 446 ++--- ...ompact_10x8_config_attributes.mapping.json | 446 ++--- ...ompact_22x4_config_attributes.mapping.json | 446 ++--- .../Virgo/gemini_compact_22x4_routing.py | 80 + .../Virgo/gemini_compact_62x44_routing.py | 110 ++ .../Virgo/routing_library/__init__.py | 0 .../Virgo/routing_library/function_library.py | 18 + .../Virgo/routing_library/gbox_fclk_mux.py | 17 + .../Virgo/routing_library/gbox_hp_40x2.py | 148 ++ .../Virgo/routing_library/gbox_hpio.py | 33 + .../Virgo/routing_library/gbox_hv_40x2.py | 74 + .../Virgo/routing_library/gbox_osc.py | 9 + .../Virgo/routing_library/gbox_pll.py | 30 + .../Virgo/routing_library/gbox_pll_refmux.py | 56 + .../routing_library/gbox_root_bank_clkmux.py | 31 + .../Virgo/routing_library/gbox_top.py | 25 + .../gemini_compact_22x4_gbox_hp_40x1.py | 90 + .../CFGDeviceDatabase/routing_configurator.py | 1692 +++++++++++++++++ 21 files changed, 2953 insertions(+), 1356 deletions(-) create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/1vg28_routing.py create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_22x4_routing.py create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_62x44_routing.py create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/__init__.py create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/function_library.py create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_fclk_mux.py create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hp_40x2.py create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hpio.py create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hv_40x2.py create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_osc.py create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_pll.py create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_pll_refmux.py create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_root_bank_clkmux.py create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_top.py create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gemini_compact_22x4_gbox_hp_40x1.py create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/routing_configurator.py diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/CMakeLists.txt b/src/ConfigurationRaptor/CFGDeviceDatabase/CMakeLists.txt index 5ef4bf63..a4cdced5 100644 --- a/src/ConfigurationRaptor/CFGDeviceDatabase/CMakeLists.txt +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/CMakeLists.txt @@ -89,12 +89,14 @@ target_include_directories(${subsystem_exe} PUBLIC add_custom_target(subsystem_file ALL DEPENDS ${CFG_PROJECT_ROOT_DIR}/etc/devices ${subsystem_exe} COMMAND python3 ${PROJECT_SOURCE_DIR}/CFGDeviceDatabase.py ${CFG_PROJECT_ROOT_DIR}/etc ${CMAKE_CURRENT_BINARY_DIR}/configuration.json + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/configuration.json ${configuration_folder}/configuration.json COMMAND ${subsystem_exe} ${CMAKE_CURRENT_BINARY_DIR}/configuration.json ${subsystem_file} ) add_custom_target(series_folder ALL COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/Virgo ${configuration_folder}/Virgo COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/Gemini ${configuration_folder}/Gemini + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/routing_configurator.py ${configuration_folder}/routing_configurator.py ) add_dependencies(${subsystem} ${subsystem_exe}) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Gemini/config_attributes.mapping.json b/src/ConfigurationRaptor/CFGDeviceDatabase/Gemini/config_attributes.mapping.json index e8e48ebc..e1d0c858 100644 --- a/src/ConfigurationRaptor/CFGDeviceDatabase/Gemini/config_attributes.mapping.json +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Gemini/config_attributes.mapping.json @@ -88,25 +88,6 @@ "I_SERDES" : "DPA_MODE==NONE" } }, - "I_SERDES.ROOT_BANK_CLKMUX" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__", - "DPA_MODE" : "__arg1__" - }, - "results" : { - "__location__" : "__ROOT_BANK_MUX_LOCATION__", - "I_SERDES" : "ROOT_BANK_SRC==__AB__&DPA_MODE==__arg1__ --#MUX=__ROOT_BANK_MUX__" - } - }, - "I_SERDES.ROOT_MUX" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__ROOT_MUX__" - } - }, "O_SERDES.BYPASS" : { "rules" : { "WIDTH" : "__arg0__" @@ -132,13 +113,6 @@ "O_SERDES" : "DDR_MODE==SDR" } }, - "O_SERDES_CLK.IO" : { - "rules" : { - }, - "results" : { - "TX_CLOCK_IO" : "TX_clock_IO" - } - }, "O_SERDES_CLK.CLK_PHASE" : { "rules" : { "CLOCK_PHASE" : "__argCLOCK_PHASE__" @@ -158,101 +132,6 @@ "neg_results" : { "O_SERDES_CLK" : "DDR_MODE==SDR" } - }, - "BOOT_CLOCK" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : [ - { - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "48" - } - ] - }, - "PLL.PLLREF_MUX" : { - "rules" : { - }, - "results" : { - "__location__" : "u_GBOX_HP_40X2.u_gbox_pll_refmux___pll_resource__", - "PLL" : "PLLREF_SRC==__SRC__ --#PIN=__PIN__ --#BANK=__BANK__ --#DIV=__DIV__" - } - }, - "PLL.PLL" : { - "rules" : { - "PLL_DIV" : "__argDIV__", - "PLL_MULT" : "__argMULT__", - "PLL_POST_DIV" : "__argPOST_DIV__" - }, - "results" : { - "__define__" : "parse_pll_parameter", - "__location__" : "u_GBOX_HP_40X2.u_gbox_PLLTS16FFCFRACF___pll_resource__", - "PLL" : "PLL_SRC==DEFAULT", - "pll_REFDIV" : "__refdiv__", - "pll_FBDIV" : "__fbdiv__", - "pll_POSTDIV1" : "__postdiv1__", - "pll_POSTDIV2" : "__postdiv2__", - "pll_PLLEN" : "__pll_enable__" - } - }, - "PLL.ROOT_MUX0" : { - "rules" : { - "__connectivity__" : "CLK_OUT", - "__index__" : "__argIndex{default:0}__", - "OUT0_ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__define__" : "parse_pll_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__pll_root_mux_sel__" - } - }, - "PLL.ROOT_MUX1" : { - "rules" : { - "__connectivity__" : "CLK_OUT_DIV2", - "__index__" : "__argIndex{default:1}__", - "OUT1_ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__define__" : "parse_pll_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__pll_root_mux_sel__" - } - }, - "PLL.ROOT_MUX2" : { - "rules" : { - "__connectivity__" : "CLK_OUT_DIV3", - "__index__" : "__argIndex{default:2}__", - "OUT2_ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__define__" : "parse_pll_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__pll_root_mux_sel__" - } - }, - "PLL.ROOT_MUX3" : { - "rules" : { - "__connectivity__" : "CLK_OUT_DIV4", - "__index__" : "__argIndex{default:3}__", - "OUT3_ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__define__" : "parse_pll_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__pll_root_mux_sel__" - } - }, - "FCLK_BUF" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__", - "ROUTE_FROM_FABRIC_CLK" : "__arg1__" - }, - "results" : { - "__define__" : "parse_fabric_clock_buffer_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__fclk_buf_root_mux_sel__" - } } }, "properties" : { @@ -328,118 +207,75 @@ "results" : { "CLK_BUF" : "GBOX_TOP_SRC==DEFAULT" } - }, - "CLK_BUF.ROOT_BANK_CLKMUX" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__location__" : "__ROOT_BANK_MUX_LOCATION__", - "CLK_BUF" : "ROOT_BANK_SRC==__AB__ --#MUX=__ROOT_BANK_MUX__" - } - }, - "CLK_BUF.ROOT_MUX" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__ROOT_MUX__" - } } }, - "__file__" : [ - "import re", - "def get_pin_info(name) :", - " bank = 0", - " is_clock = False", - " index = 0", - " pair_index = 0", - " ab_io = 0", - " ab_name = ''", - " root_bank_mux_location = ''", - " root_bank_mux_resource = ''", - " root_bank_mux_core_input_index = 0", - " root_mux_input_index = 0", - " if name.find('BOOT_CLOCK#') == 0:", - " type = 'BOOT_CLOCK'", - " index = int(name[11:])", - " else :", - " m = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', name)", - " assert m != None", - " assert len(m.groups()) == 6", - " type = 'HP' if m.group(1) == 'P' else ('HVL' if m.group(2) in ['1', '2'] else 'HVR')", - " bank = 0 if m.group(2) in ['1', '3'] else 1", - " is_clock = m.group(2) == '_CC'", - " index = int(m.group(4))", - " pair_index = int(m.group(5))", - " ab_io = 0 if (pair_index < 10) else 1", - " ab_name = '%c' % (ord('A') + ab_io)", - " root_name = 'u_GBOX_HP_40X2' if type == 'HP' else ('u_GBOX_HV_40X2_VL' if type == 'HVL' else 'u_GBOX_HV_40X2_VR')", - " root_bank_mux_location = '%s.u_gbox_root_bank_clkmux_%d' % (root_name, bank)", - " root_bank_mux_resource = '%s (Bank %s)' % (root_bank_mux_location, ab_name)", - " root_bank_mux_core_input_index = index - (20 * ab_io)", - " root_mux_input_index = 0 if type == 'HP' else (8 if type == 'HVL' else 16)", - " root_mux_input_index += ((2 * bank) + ab_io)", - " return [type, bank, is_clock, index, pair_index, ab_io, ab_name, root_bank_mux_location, root_bank_mux_resource, root_bank_mux_core_input_index, root_mux_input_index]", - "def fclk_use_pll_resource(fclk) :", - " pll_resource = 0", - " if fclk.find('hvl_fclk_') == 0 :", - " pll_resource = 0", - " elif fclk.find('hvr_fclk_') == 0 :", - " pll_resource = 1", - " elif fclk.find('hp_fclk_0') == 0 :", - " pll_resource = 0", - " elif fclk.find('hp_fclk_1') == 0 :", - " pll_resource = 1", - " else :", - " raise Exception('Unknown FCLK %s' % fclk)", - " return [pll_resource]" - ], - "__resources__" : { - "pll" : [ - { "name" : "pll_0", "ric_name" : "u_GBOX_HP_40X2.u_gbox_PLLTS16FFCFRACF_0", "type" : "HP", "subtype" : "hp", "bank" : 0 }, - { "name" : "pll_1", "ric_name" : "u_GBOX_HP_40X2.u_gbox_PLLTS16FFCFRACF_1", "type" : "HP", "subtype" : "hp", "bank" : 1 } - ], - "fclk" : [ - { "name" : "hp_fclk_0_A", "ric_name" : "u_GBOX_HP_40X2.u_gbox_fclk_mux_all", "type" : "HP", "subtype" : "hp", "bank" : 0 }, - { "name" : "hp_fclk_0_B", "ric_name" : "u_GBOX_HP_40X2.u_gbox_fclk_mux_all", "type" : "HP", "subtype" : "hp", "bank" : 0 }, - { "name" : "hp_fclk_1_A", "ric_name" : "u_GBOX_HP_40X2.u_gbox_fclk_mux_all", "type" : "HP", "subtype" : "hp", "bank" : 1 }, - { "name" : "hp_fclk_1_B", "ric_name" : "u_GBOX_HP_40X2.u_gbox_fclk_mux_all", "type" : "HP", "subtype" : "hp", "bank" : 1 }, - { "name" : "hvl_fclk_0_A", "ric_name" : "u_GBOX_HV_40X2_VL.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvl", "bank" : 0 }, - { "name" : "hvl_fclk_0_B", "ric_name" : "u_GBOX_HV_40X2_VL.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvl", "bank" : 0 }, - { "name" : "hvl_fclk_1_A", "ric_name" : "u_GBOX_HV_40X2_VL.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvl", "bank" : 1 }, - { "name" : "hvl_fclk_1_B", "ric_name" : "u_GBOX_HV_40X2_VL.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvl", "bank" : 1 }, - { "name" : "hvr_fclk_0_A", "ric_name" : "u_GBOX_HV_40X2_VR.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvr", "bank" : 0 }, - { "name" : "hvr_fclk_0_B", "ric_name" : "u_GBOX_HV_40X2_VR.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvr", "bank" : 0 }, - { "name" : "hvr_fclk_1_A", "ric_name" : "u_GBOX_HV_40X2_VR.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvr", "bank" : 1 }, - { "name" : "hvr_fclk_1_B", "ric_name" : "u_GBOX_HV_40X2_VR.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvr", "bank" : 1 } - ] - }, - "__init__" : { + "__init_file__" : { "__args__" : [], - "__equation__" : [ - "import json", - "G_RESOURCES = json.loads('__resources_string__')", - "assert 'pll' in G_RESOURCES", - "assert 'fclk' in G_RESOURCES", + "__file__" : [ + "import re", "MAX_BOOT_CLOCK_RESOURCE = 1", - "MAX_FABRIC_CLOCK_RESOURCE = 16", - "MAX_PLL_RESOURCE = len(G_RESOURCES['pll'])", "hp_banks = ['HP_%d' % i for i in [1, 2]]", "hr_banks = ['HR_%d' % i for i in [1, 2, 3, 5]]", "all_banks = hp_banks + hr_banks", - "pin_list = ['%d_%d%c' % (i, i//2, 'N' if i%2 else 'P') for i in range(40)]", - "cc_pin_list = ['%d_%d%c' % (i, i//2, 'N' if i%2 else 'P') for i in [18, 19, 38, 39]]", - "cc_p_pin_list = [pin for pin in cc_pin_list if pin[-1] == 'P']", - "g_all_pins = ['%s_%s%s' % (i, 'CC_' if j in cc_pin_list else '', j) for i in all_banks for j in pin_list]", - "g_all_clock_pins = ['%s_CC_%s' % (i, j) for i in all_banks for j in cc_p_pin_list]", + "bank_pin_count = 40", + "CC_index = [18, 19, 38, 39]", + "exclude_index = []", + "pin_list = ['%s%d_%d%c' % ('CC_' if i in CC_index else '', i, i//2, 'N' if i%2 else 'P') for i in range(bank_pin_count) if i not in exclude_index]", + "cc_p_pin_list = [pin for pin in pin_list if (pin.find('CC_') == 0 and pin[-1] == 'P')]", + "g_all_pins = ['%s_%s' % (i, j) for i in all_banks for j in pin_list]", + "g_all_clock_pins = ['%s_%s' % (i, j) for i in all_banks for j in cc_p_pin_list]", "g_all_pll_clock_pins = [pin for pin in g_all_clock_pins]", "g_boot_clock_resources = 0", "g_pin_resources = {}", - "g_fabric_clock_resources = 0", - "g_pll_resources = []", - "g_gearbox_width = {}" + "g_input_gearbox_width = {}", + "g_output_gearbox_width = {}", + "def parse_pin_location(location):", + " assert location in g_all_pins", + " m = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', location)", + " assert m != None", + " assert len(m.groups()) == 6", + " type = 'HP' if m.group(1) == 'P' else ('HVL' if m.group(2) in ['1', '2'] else 'HVR')", + " bank = 0 if m.group(2) in ['1', '3'] else 1", + " is_clock = m.group(3) == '_CC'", + " index = int(m.group(4))", + " pair_index = int(m.group(5))", + " assert pair_index == (index//2)", + " ab_io = 0 if (pair_index < 10) else 1", + " ab_name = '%c' % (ord('A') + ab_io)", + " return [m, type, bank, is_clock, index, pair_index, ab_io, ab_name]", + "def get_peer_location(location):", + " (m, type, bank, is_clock, index, pair_index, ab_io, ab_name) = parse_pin_location(location)", + " pn = 'P' if m.group(6) == 'N' else 'N'", + " index = int(m.group(4)) & ~1", + " index += (1 if pn == 'N' else 0)", + " peer_location = 'H%s_%s%s_%d_%s%s' % (m.group(1), m.group(2), m.group(3), index, m.group(5), pn)", + " return [m.group(6), peer_location]", + "def validate_data_width_parameter(location, width, gearboxes):", + " (self_pn, peer_location) = get_peer_location(location)", + " result = width >= 3 and width <= (10 if self_pn == 'P' else 5)", + " result = result and ((peer_location not in gearboxes) or (gearboxes[peer_location] <= 5 and width <=5))", + " gearboxes[location if result else ''] = width", + " gearboxes.pop('', None)", + " return result", + "def get_pin_info(name):", + " bank = 0", + " is_clock = False", + " index = 0", + " pair_index = 0", + " ab_io = 0", + " ab_name = ''", + " if name.find('BOOT_CLOCK#') == 0:", + " type = 'BOOT_CLOCK'", + " index = int(name[11:])", + " model_name = 'hp_40x2.rc_osc_50mhz'", + " elif name.find('FABRIC_CLKBUF#') == 0:", + " type = 'FABRIC_CLKBUF'", + " index = int(name[14:])", + " model_name = 'fclk_buf'", + " else :", + " (m, type, bank, is_clock, index, pair_index, ab_io, ab_name) = parse_pin_location(name)", + " model_name = '%s_40x2.bank%d_hpio.gearbox_%s[%d]' % (type.lower(), bank, m.group(6), pair_index)", + " return [type, bank, is_clock, index, pair_index, ab_io, ab_name, model_name]" ] }, "__primary_validation__" : { @@ -451,12 +287,18 @@ "__check_ds_pin_resource__", "__clock_pin_is_valid__", "__check_boot_clock_resource__", - "__pll_clock_pin_is_valid__" + "__pll_clock_pin_is_valid__", + "__check_input_data_width_parameter__", + "__check_output_data_width_parameter__", + "__check_data_rate_parameter__", + "__check_dpa_mode_parameter__", + "__check_clock_phase_parameter__", + "__check_pll_parameter__" ], "__pin_is_valid__" : { "__module__" : ["I_BUF", "O_BUF", "O_BUFT"], "__equation__" : [ - "pin_result = '__location0__' in g_all_pins" + "validation_result = '__location0__' in g_all_pins" ] }, "__check_pin_resource__" : { @@ -465,47 +307,47 @@ "temp = '__primitive_flags__'.split(',')", "bidir = 'INOUT' in temp", "value = 1 if 'I_BUF' in temp else 2", - "pin_result = '__location0__' not in g_pin_resources or ((g_pin_resources['__location0__'] & value) == 0)", + "validation_result = '__location0__' not in g_pin_resources or ((g_pin_resources['__location0__'] & value) == 0)", "exist = 0 if '__location0__' not in g_pin_resources else g_pin_resources['__location0__']", - "g_pin_resources['__location0__' if pin_result else ''] = exist | (value if bidir else 3)", + "g_pin_resources['__location0__' if validation_result else ''] = exist | (value if bidir else 3)", "g_pin_resources.pop('', None)" ] }, "__ds_pin_is_valid__" : { "__module__" : ["I_BUF_DS", "O_BUF_DS", "O_BUFT_DS"], "__equation__" : [ - "pin_result = '__location0__' in g_all_pins", - "pin_result = pin_result and '__location1__' in g_all_pins" + "validation_result = '__location0__' in g_all_pins", + "validation_result = validation_result and '__location1__' in g_all_pins" ] }, "__pin_is_differential__" : { "__module__" : ["I_BUF_DS", "O_BUF_DS", "O_BUFT_DS"], "__equation__" : [ "import re", - "pin_result = '__location0__' in g_all_pins", - "pin_result = pin_result and '__location1__' in g_all_pins", + "validation_result = '__location0__' in g_all_pins", + "validation_result = validation_result and '__location1__' in g_all_pins", "m0 = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', '__location0__')", "m1 = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', '__location1__')", - "pin_result = pin_result and m0 != None", - "pin_result = pin_result and m1 != None", - "pin_result = pin_result and len(m0.groups()) == 6", - "pin_result = pin_result and len(m1.groups()) == 6", - "pin_result = pin_result and m0.group(1) == m1.group(1)", - "pin_result = pin_result and m0.group(2) == m1.group(2)", - "pin_result = pin_result and m0.group(3) == m1.group(3)", - "pin_result = pin_result and m0.group(4) != m1.group(4)", - "pin_result = pin_result and m0.group(5) == m1.group(5)", - "pin_result = pin_result and m0.group(6) != m1.group(6)" + "validation_result = validation_result and m0 != None", + "validation_result = validation_result and m1 != None", + "validation_result = validation_result and len(m0.groups()) == 6", + "validation_result = validation_result and len(m1.groups()) == 6", + "validation_result = validation_result and m0.group(1) == m1.group(1)", + "validation_result = validation_result and m0.group(2) == m1.group(2)", + "validation_result = validation_result and m0.group(3) == m1.group(3)", + "validation_result = validation_result and m0.group(4) != m1.group(4)", + "validation_result = validation_result and m0.group(5) == m1.group(5)", + "validation_result = validation_result and m0.group(6) != m1.group(6)" ] }, "__check_ds_pin_resource__" : { "__module__" : ["I_BUF_DS", "O_BUF_DS", "O_BUFT_DS"], "__equation__" : [ - "pin_result = '__location0__' not in g_pin_resources", - "pin_result = pin_result and '__location1__' not in g_pin_resources", - "g_pin_resources['__location0__' if pin_result else ''] = 3", + "validation_result = '__location0__' not in g_pin_resources", + "validation_result = validation_result and '__location1__' not in g_pin_resources", + "g_pin_resources['__location0__' if validation_result else ''] = 3", "g_pin_resources.pop('', None)", - "g_pin_resources['__location1__' if pin_result else ''] = 3", + "g_pin_resources['__location1__' if validation_result else ''] = 3", "g_pin_resources.pop('', None)" ] }, @@ -513,79 +355,56 @@ "__module__" : ["CLK_BUF"], "__equation__" : [ "temp = '__primitive_flags__'.split(',')", - "pin_result = '__location0__' in g_all_clock_pins or 'PIN_CLOCK_CORE_ONLY' in temp" + "validation_result = '__location0__' in g_all_clock_pins or 'PIN_CLOCK_CORE_ONLY' in temp" ] }, "__check_boot_clock_resource__" : { "__module__" : ["BOOT_CLOCK"], "__equation__" : [ - "pin_result = g_boot_clock_resources < MAX_BOOT_CLOCK_RESOURCE", - "g_boot_clock_resources += (1 if pin_result else 0)" + "validation_result = g_boot_clock_resources < MAX_BOOT_CLOCK_RESOURCE", + "g_boot_clock_resources += (1 if validation_result else 0)" ] }, "__pll_clock_pin_is_valid__" : { "__module__" : ["PLL"], "pre_primitive" : "CLK_BUF", "__equation__" : [ - "pin_result = '__location0__' in g_all_pll_clock_pins" + "validation_result = '__location0__' in g_all_pll_clock_pins" ] - } - }, - "__secondary_validation__" : { - "__seqeunce__" : [ - "__check_fabric_clock_resource__", - "__check_data_width_parameter__", - "__check_data_rate_parameter__", - "__check_dpa_mode_parameter__", - "__check_clock_phase_parameter__", - "__check_pll_parameter__", - "__update_fabric_clock_resource__" - ], - "__check_fabric_clock_resource__" : { - "__module__" : ["CLK_BUF", "PLL"], - "__connectivity__" : ["O", "CLK_OUT", "CLK_OUT_DIV2", "CLK_OUT_DIV3", "CLK_OUT_DIV4"], + }, + "__check_input_data_width_parameter__" : { + "__module__" : ["I_SERDES"], + "__parameter__" : ["WIDTH"], "__equation__" : [ - "pin_result = (__connectivity_count__ + g_fabric_clock_resources) <= MAX_FABRIC_CLOCK_RESOURCE" + "validation_result = validate_data_width_parameter('__location__', int('WIDTH'), g_input_gearbox_width)" ] }, - "__check_data_width_parameter__" : { - "__module__" : ["I_SERDES", "O_SERDES"], + "__check_output_data_width_parameter__" : { + "__module__" : ["O_SERDES"], "__parameter__" : ["WIDTH"], "__equation__" : [ - "import re", - "assert '__location__' in g_all_pins", - "m = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', '__location__')", - "assert m != None", - "pn = 'P' if m.group(6) == 'N' else 'N'", - "index = int(m.group(4)) & ~1", - "index += (1 if pn == 'N' else 0)", - "peer_location = 'H%s_%s%s_%d_%s%s' % (m.group(1), m.group(2), m.group(3), index, m.group(5), pn)", - "width = int('WIDTH')", - "pin_result = width >= 3 and width <= (10 if m.group(6) == 'P' else 5)", - "pin_result = pin_result and ((peer_location not in g_gearbox_width) or (g_gearbox_width[peer_location] <= 5 and width <=5))", - "g_gearbox_width['__location__' if pin_result else ''] = width", - "g_gearbox_width.pop('', None)" + "validation_result = validate_data_width_parameter('__location__', int('WIDTH'), g_output_gearbox_width)" ] }, "__check_data_rate_parameter__" : { "__module__" : ["I_SERDES", "O_SERDES", "O_SERDES_CLK"], "__parameter__" : ["DATA_RATE"], "__equation__" : [ - "pin_result = 'DATA_RATE' in ['SDR', 'DDR']" + "validation_result = 'DATA_RATE' in ['SDR', 'DDR']" ] }, "__check_dpa_mode_parameter__" : { "__module__" : ["I_SERDES"], "__parameter__" : ["DPA_MODE"], "__equation__" : [ - "pin_result = 'DPA_MODE' in ['NONE', 'DPA', 'CDR']" + "validation_result = 'DPA_MODE' in ['NONE', 'DPA', 'CDR']" ] }, "__check_clock_phase_parameter__" : { "__module__" : ["O_SERDES_CLK"], "__parameter__" : ["CLOCK_PHASE"], "__equation__" : [ - "pin_result = 'CLOCK_PHASE' in ['0', '90', '180', '270']" + "validation_result = 'CLOCK_PHASE' in ['0', '90', '180', '270']" ] }, "__check_pll_parameter__" : { @@ -597,72 +416,21 @@ "post_div = int('PLL_POST_DIV', 0)", "post_div1 = post_div >> 4", "post_div2 = post_div & 0xF", - "pin_result = (div >= 1 and div <= 63)", - "pin_result = pin_result and (mult >= 16 and mult <= 640)", - "pin_result = pin_result and (post_div1 >= 1 and post_div1 <= 7)", - "pin_result = pin_result and (post_div2 >= 1 and post_div2 <= 7)", - "pin_result = pin_result and (post_div1 >= post_div2)" - ] - }, - "__check_pll_clock_pin_resource__" : { - "__module__" : ["PLL"], - "__equation__" : [ - "pin_result = '__location0__' not in g_pll_resources", - "pin_result = pin_result and len(g_pll_resources) < MAX_PLL_RESOURCE", - "g_pll_resources.append('__location0__' if pin_result else '')", - "g_pll_resources = [pin for pin in g_pll_resources if pin != '']" - ] - }, - "__update_fabric_clock_resource__" : { - "__module__" : ["CLK_BUF", "PLL"], - "__connectivity__" : ["O", "CLK_OUT", "CLK_OUT_DIV2", "CLK_OUT_DIV3", "CLK_OUT_DIV4"], - "__equation__" : [ - "assert ((__connectivity_count__ + g_fabric_clock_resources) <= MAX_FABRIC_CLOCK_RESOURCE)", - "g_fabric_clock_resources += __connectivity_count__" + "validation_result = (div >= 1 and div <= 63)", + "validation_result = validation_result and (mult >= 16 and mult <= 640)", + "validation_result = validation_result and (post_div1 >= 1 and post_div1 <= 7)", + "validation_result = validation_result and (post_div2 >= 1 and post_div2 <= 7)", + "validation_result = validation_result and (post_div1 >= post_div2)" ] } }, "__define__" : { - "parse_location" : { - "__args__" : ["__type__", "__bank__"], - "__equation__" : [ - "import re", - "assert '__location__' in g_all_pins", - "m = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', '__location__')", - "__type__ = 'HP' if m.group(1) == 'P' else 'HV'", - "__bank__ = '0' if m.group(2) in ['1', '3'] else '1'" - ] - }, "parse_o_serdes_clk_phase_parameter" : { "__args__" : ["__clock_phase__"], "__equation__" : [ "__clock_phase__ = 'TX_phase_%d' % __argCLOCK_PHASE__" ] }, - "parse_pll_parameter" : { - "__args__" : ["__refdiv__", "__fbdiv__", "__postdiv1__", "__postdiv2__"], - "__equation__" : [ - "__refdiv__ = str(int('__argDIV__', 0))", - "__fbdiv__ = str(int('__argMULT__', 0))", - "__postdiv1__ = str((int('__argPOST_DIV__', 0) >> 4) & 0x7)", - "__postdiv2__ = str((int('__argPOST_DIV__', 0)) & 0x7)" - ] - }, - "parse_pll_root_mux" : { - "__args__" : ["__pll_root_mux_sel__"], - "__equation__" : [ - "__pll_root_mux_sel__ = 32 + (int('__pll_resource__') * 4) + __argIndex__", - "__pll_root_mux_sel__ = '%d' % __pll_root_mux_sel__" - ] - }, - "parse_fabric_clock_buffer_root_mux" : { - "__args__" : ["__fclk_buf_root_mux_sel__"], - "__equation__" : [ - "fabric_clock_buffer_slot = int('__arg1__')", - "__fclk_buf_root_mux_sel__ = (40 + fabric_clock_buffer_slot - 4) if (fabric_clock_buffer_slot >= 4) else (44 + fabric_clock_buffer_slot)", - "__fclk_buf_root_mux_sel__ = '%d' % __fclk_buf_root_mux_sel__" - ] - }, "parse_serdes_width" : { "__args__" : ["__peer_is_on__"], "__equation__" : [ diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/1vg28_routing.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/1vg28_routing.py new file mode 100644 index 00000000..ac7a1574 --- /dev/null +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/1vg28_routing.py @@ -0,0 +1,110 @@ +from routing_library import function_library +load_model("routing_library/gbox_hp_40x2.py") +load_model("routing_library/gbox_hv_40x2.py") + +# Block +create_block(name="Virgo", top=True) + +# Ports +for type_bank in [["P", 1], ["P", 2], ["R", 1], ["R", 2], ["R", 3], ["R", 5]] : + for i in range(40) : + location = function_library.get_location(type_bank[0], type_bank[1], i) + add_port(name=location, dir=DIR_IN) +add_port(name="fabric_clk", dir=DIR_OUT, bit=16) +add_port(name="fclk_buf", dir=DIR_IN, bit=8) + +# Instances +add_instance(name="hp_40x2", block="gbox_hp_40x2") +add_instance(name="hvl_40x2", block="gbox_hv_40x2") +add_instance(name="hvr_40x2", block="gbox_hv_40x2") + +# Connections +# pin --> hp/hvl/hvr pin +for type_bank in [["P", 1, 0], ["P", 2, 1], ["R", 1, 0], ["R", 2, 1], ["R", 3, 0], ["R", 5, 1]] : + instance = "hp" if type_bank[0] == "P" else ("hvl" if type_bank[1] in [1, 2] else "hvr") + instance = "%s_40x2" % instance + bank = type_bank[2] + bank_pin_name = "bank%d_rx_in" % bank + for i in range(40) : + top_location = function_library.get_location(type_bank[0], type_bank[1], i) + add_connection(source=top_location, destinations=["%s->%s[%d]" % (instance, bank_pin_name, i)]) +# hvl/hvr clk pin --> hp clk pin +for bank in range(2) : + for pin in range(2) : + source_pin = "bank%d_rx_io_clk[%d]" % (bank, pin) + add_connection(source="hvl_40x2->%s" % (source_pin), destinations=["hp_40x2->hvl_bank%d_rx_io_clk[%d]" % (bank, pin)]) + add_connection(source="hvr_40x2->%s" % (source_pin), destinations=["hp_40x2->hvr_bank%d_rx_io_clk[%d]" % (bank, pin)]) +# hp pll --> hvl pll +add_connection(source="hp_40x2->pll_foutvco[0]", destinations=["hvl_40x2->pll_foutvco"]) +add_connection(source="hp_40x2->pll_fout[0]", destinations=["hvl_40x2->pll_fout"]) +# hp pll --> hvr pll +add_connection(source="hp_40x2->pll_foutvco[1]", destinations=["hvr_40x2->pll_foutvco"]) +add_connection(source="hp_40x2->pll_fout[1]", destinations=["hvr_40x2->pll_fout"]) +# hvl core clk + cdr clk --> HP core clk + cdr clk +add_connection(source="hvl_40x2->bank0_root_core_clk[0]", destinations=["hp_40x2->hvl_bank0_root_core_clk[0]"]) +add_connection(source="hvl_40x2->bank0_root_core_clk[1]", destinations=["hp_40x2->hvl_bank0_root_core_clk[1]"]) +add_connection(source="hvl_40x2->bank0_root_cdr_clk[0]", destinations=["hp_40x2->hvl_bank0_root_cdr_clk[0]"]) +add_connection(source="hvl_40x2->bank0_root_cdr_clk[1]", destinations=["hp_40x2->hvl_bank0_root_cdr_clk[1]"]) +add_connection(source="hvl_40x2->bank1_root_core_clk[0]", destinations=["hp_40x2->hvl_bank1_root_core_clk[0]"]) +add_connection(source="hvl_40x2->bank1_root_core_clk[1]", destinations=["hp_40x2->hvl_bank1_root_core_clk[1]"]) +add_connection(source="hvl_40x2->bank1_root_cdr_clk[0]", destinations=["hp_40x2->hvl_bank1_root_cdr_clk[0]"]) +add_connection(source="hvl_40x2->bank1_root_cdr_clk[1]", destinations=["hp_40x2->hvl_bank1_root_cdr_clk[1]"]) +# hvr core clk + cdr clk --> HP core clk + cdr clk +add_connection(source="hvr_40x2->bank0_root_core_clk[0]", destinations=["hp_40x2->hvr_bank0_root_core_clk[0]"]) +add_connection(source="hvr_40x2->bank0_root_core_clk[1]", destinations=["hp_40x2->hvr_bank0_root_core_clk[1]"]) +add_connection(source="hvr_40x2->bank0_root_cdr_clk[0]", destinations=["hp_40x2->hvr_bank0_root_cdr_clk[0]"]) +add_connection(source="hvr_40x2->bank0_root_cdr_clk[1]", destinations=["hp_40x2->hvr_bank0_root_cdr_clk[1]"]) +add_connection(source="hvr_40x2->bank1_root_core_clk[0]", destinations=["hp_40x2->hvr_bank1_root_core_clk[0]"]) +add_connection(source="hvr_40x2->bank1_root_core_clk[1]", destinations=["hp_40x2->hvr_bank1_root_core_clk[1]"]) +add_connection(source="hvr_40x2->bank1_root_cdr_clk[0]", destinations=["hp_40x2->hvr_bank1_root_cdr_clk[0]"]) +add_connection(source="hvr_40x2->bank1_root_cdr_clk[1]", destinations=["hp_40x2->hvr_bank1_root_cdr_clk[1]"]) +# hp instance fabric clk --> fabric clk +for i in range(16) : + add_connection(source="hp_40x2->fabric_clk[%d]" % i, destinations=["fabric_clk[%d]" % i]) +# fclk buf --> instance fclk buf +for i in range(8) : + add_connection(source="fclk_buf[%d]" % i, destinations=["hp_40x2->fclk_buf[%d]" % i]) + +# Mapping to TCL model +for type_bank in [["P", 1, 0], ["P", 2, 1], ["R", 1, 0], ["R", 2, 1], ["R", 3, 0], ["R", 5, 1]] : + # Pin location + instance = "hp" if type_bank[0] == "P" else ("hvl" if type_bank[1] in [1, 2] else "hvr") + instance = "%s_40x2" % instance + bank = type_bank[2] + for i in range(40) : + # Gearbox mapping - hp_40x2.bank0_hpio.gearbox_P[0] ==> u_GBOX_HP_40X2.u_HP_GBOX_BK0_A_18 or HP_1_0_0P + python_name = "%s.bank%d_hpio.%s" % (instance, bank, function_library.get_gbox_top_name(i)) + tcl_name = function_library.get_location(type_bank[0], type_bank[1], i) + add_tcl_map(python_name, tcl_name) +for type_bank in [["p", "P", ""], ["vl", "V", "_VL"], ["vr", "V", "_VR"]] : + # Top Level Type + add_tcl_map("h%s_40x2" % type_bank[0], "u_GBOX_H%s_40X2%s" % (type_bank[1], type_bank[2])) +for type_bank in [[0, "A"], [0, "B"], [1, "A"], [1, "B"]] : + # FCLK + for bit in ["vco_clk_sel", "rx_fclkio_sel", "rxclk_phase_sel"]: + python_name = "bank%d_fclk_mux_%s->%s" % (type_bank[0], type_bank[1], bit) + tcl_name = "u_gbox_fclk_mux_all->cfg_%s_%s_%d" % (bit, type_bank[1], type_bank[0]) + add_tcl_map(python_name, tcl_name) +for bank in [0, 1] : + # Root Bank + add_tcl_map("bank%d_root_bank_clkmux" % bank, "u_gbox_root_bank_clkmux_%d" % bank) +# PLL REFMUX +add_tcl_map("pll_refmux[0]", "u_gbox_pll_refmux_0") +add_tcl_map("pll_refmux[1]", "u_gbox_pll_refmux_1") +# PLL +add_tcl_map("pll[0]", "u_gbox_PLLTS16FFCFRACF_0") +add_tcl_map("pll[1]", "u_gbox_PLLTS16FFCFRACF_1") +for slot in range(16): + python_name = "->root_mux_sel[%d]" % slot + tcl_name = ".u_gbox_clkmux_52x1_left_%d->ROOT_MUX_SEL" % slot + add_tcl_map(python_name, tcl_name) + +# Diagram mapping +for type_bank in [["P", 1, 0], ["P", 2, 1], ["R", 1, 0], ["R", 2, 1], ["R", 3, 0], ["R", 5, 1]] : + instance = "hp" if type_bank[0] == "P" else ("hvl" if type_bank[1] in [1, 2] else "hvr") + # The graphviz module that we use to draw diagram have difficulty supporting ":" + # Suppose we have to display [39:0] + mapped_name = "%s_bank%s_pin[39:0]" % (instance, type_bank[2]) + for i in range(40) : + location = function_library.get_location(type_bank[0], type_bank[1], i) + add_diagram_map(name=location, mapped_name=mapped_name) \ No newline at end of file diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/config_attributes.mapping.json b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/config_attributes.mapping.json index e8e48ebc..e1d0c858 100644 --- a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/config_attributes.mapping.json +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/config_attributes.mapping.json @@ -88,25 +88,6 @@ "I_SERDES" : "DPA_MODE==NONE" } }, - "I_SERDES.ROOT_BANK_CLKMUX" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__", - "DPA_MODE" : "__arg1__" - }, - "results" : { - "__location__" : "__ROOT_BANK_MUX_LOCATION__", - "I_SERDES" : "ROOT_BANK_SRC==__AB__&DPA_MODE==__arg1__ --#MUX=__ROOT_BANK_MUX__" - } - }, - "I_SERDES.ROOT_MUX" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__ROOT_MUX__" - } - }, "O_SERDES.BYPASS" : { "rules" : { "WIDTH" : "__arg0__" @@ -132,13 +113,6 @@ "O_SERDES" : "DDR_MODE==SDR" } }, - "O_SERDES_CLK.IO" : { - "rules" : { - }, - "results" : { - "TX_CLOCK_IO" : "TX_clock_IO" - } - }, "O_SERDES_CLK.CLK_PHASE" : { "rules" : { "CLOCK_PHASE" : "__argCLOCK_PHASE__" @@ -158,101 +132,6 @@ "neg_results" : { "O_SERDES_CLK" : "DDR_MODE==SDR" } - }, - "BOOT_CLOCK" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : [ - { - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "48" - } - ] - }, - "PLL.PLLREF_MUX" : { - "rules" : { - }, - "results" : { - "__location__" : "u_GBOX_HP_40X2.u_gbox_pll_refmux___pll_resource__", - "PLL" : "PLLREF_SRC==__SRC__ --#PIN=__PIN__ --#BANK=__BANK__ --#DIV=__DIV__" - } - }, - "PLL.PLL" : { - "rules" : { - "PLL_DIV" : "__argDIV__", - "PLL_MULT" : "__argMULT__", - "PLL_POST_DIV" : "__argPOST_DIV__" - }, - "results" : { - "__define__" : "parse_pll_parameter", - "__location__" : "u_GBOX_HP_40X2.u_gbox_PLLTS16FFCFRACF___pll_resource__", - "PLL" : "PLL_SRC==DEFAULT", - "pll_REFDIV" : "__refdiv__", - "pll_FBDIV" : "__fbdiv__", - "pll_POSTDIV1" : "__postdiv1__", - "pll_POSTDIV2" : "__postdiv2__", - "pll_PLLEN" : "__pll_enable__" - } - }, - "PLL.ROOT_MUX0" : { - "rules" : { - "__connectivity__" : "CLK_OUT", - "__index__" : "__argIndex{default:0}__", - "OUT0_ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__define__" : "parse_pll_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__pll_root_mux_sel__" - } - }, - "PLL.ROOT_MUX1" : { - "rules" : { - "__connectivity__" : "CLK_OUT_DIV2", - "__index__" : "__argIndex{default:1}__", - "OUT1_ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__define__" : "parse_pll_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__pll_root_mux_sel__" - } - }, - "PLL.ROOT_MUX2" : { - "rules" : { - "__connectivity__" : "CLK_OUT_DIV3", - "__index__" : "__argIndex{default:2}__", - "OUT2_ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__define__" : "parse_pll_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__pll_root_mux_sel__" - } - }, - "PLL.ROOT_MUX3" : { - "rules" : { - "__connectivity__" : "CLK_OUT_DIV4", - "__index__" : "__argIndex{default:3}__", - "OUT3_ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__define__" : "parse_pll_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__pll_root_mux_sel__" - } - }, - "FCLK_BUF" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__", - "ROUTE_FROM_FABRIC_CLK" : "__arg1__" - }, - "results" : { - "__define__" : "parse_fabric_clock_buffer_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__fclk_buf_root_mux_sel__" - } } }, "properties" : { @@ -328,118 +207,75 @@ "results" : { "CLK_BUF" : "GBOX_TOP_SRC==DEFAULT" } - }, - "CLK_BUF.ROOT_BANK_CLKMUX" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__location__" : "__ROOT_BANK_MUX_LOCATION__", - "CLK_BUF" : "ROOT_BANK_SRC==__AB__ --#MUX=__ROOT_BANK_MUX__" - } - }, - "CLK_BUF.ROOT_MUX" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__ROOT_MUX__" - } } }, - "__file__" : [ - "import re", - "def get_pin_info(name) :", - " bank = 0", - " is_clock = False", - " index = 0", - " pair_index = 0", - " ab_io = 0", - " ab_name = ''", - " root_bank_mux_location = ''", - " root_bank_mux_resource = ''", - " root_bank_mux_core_input_index = 0", - " root_mux_input_index = 0", - " if name.find('BOOT_CLOCK#') == 0:", - " type = 'BOOT_CLOCK'", - " index = int(name[11:])", - " else :", - " m = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', name)", - " assert m != None", - " assert len(m.groups()) == 6", - " type = 'HP' if m.group(1) == 'P' else ('HVL' if m.group(2) in ['1', '2'] else 'HVR')", - " bank = 0 if m.group(2) in ['1', '3'] else 1", - " is_clock = m.group(2) == '_CC'", - " index = int(m.group(4))", - " pair_index = int(m.group(5))", - " ab_io = 0 if (pair_index < 10) else 1", - " ab_name = '%c' % (ord('A') + ab_io)", - " root_name = 'u_GBOX_HP_40X2' if type == 'HP' else ('u_GBOX_HV_40X2_VL' if type == 'HVL' else 'u_GBOX_HV_40X2_VR')", - " root_bank_mux_location = '%s.u_gbox_root_bank_clkmux_%d' % (root_name, bank)", - " root_bank_mux_resource = '%s (Bank %s)' % (root_bank_mux_location, ab_name)", - " root_bank_mux_core_input_index = index - (20 * ab_io)", - " root_mux_input_index = 0 if type == 'HP' else (8 if type == 'HVL' else 16)", - " root_mux_input_index += ((2 * bank) + ab_io)", - " return [type, bank, is_clock, index, pair_index, ab_io, ab_name, root_bank_mux_location, root_bank_mux_resource, root_bank_mux_core_input_index, root_mux_input_index]", - "def fclk_use_pll_resource(fclk) :", - " pll_resource = 0", - " if fclk.find('hvl_fclk_') == 0 :", - " pll_resource = 0", - " elif fclk.find('hvr_fclk_') == 0 :", - " pll_resource = 1", - " elif fclk.find('hp_fclk_0') == 0 :", - " pll_resource = 0", - " elif fclk.find('hp_fclk_1') == 0 :", - " pll_resource = 1", - " else :", - " raise Exception('Unknown FCLK %s' % fclk)", - " return [pll_resource]" - ], - "__resources__" : { - "pll" : [ - { "name" : "pll_0", "ric_name" : "u_GBOX_HP_40X2.u_gbox_PLLTS16FFCFRACF_0", "type" : "HP", "subtype" : "hp", "bank" : 0 }, - { "name" : "pll_1", "ric_name" : "u_GBOX_HP_40X2.u_gbox_PLLTS16FFCFRACF_1", "type" : "HP", "subtype" : "hp", "bank" : 1 } - ], - "fclk" : [ - { "name" : "hp_fclk_0_A", "ric_name" : "u_GBOX_HP_40X2.u_gbox_fclk_mux_all", "type" : "HP", "subtype" : "hp", "bank" : 0 }, - { "name" : "hp_fclk_0_B", "ric_name" : "u_GBOX_HP_40X2.u_gbox_fclk_mux_all", "type" : "HP", "subtype" : "hp", "bank" : 0 }, - { "name" : "hp_fclk_1_A", "ric_name" : "u_GBOX_HP_40X2.u_gbox_fclk_mux_all", "type" : "HP", "subtype" : "hp", "bank" : 1 }, - { "name" : "hp_fclk_1_B", "ric_name" : "u_GBOX_HP_40X2.u_gbox_fclk_mux_all", "type" : "HP", "subtype" : "hp", "bank" : 1 }, - { "name" : "hvl_fclk_0_A", "ric_name" : "u_GBOX_HV_40X2_VL.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvl", "bank" : 0 }, - { "name" : "hvl_fclk_0_B", "ric_name" : "u_GBOX_HV_40X2_VL.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvl", "bank" : 0 }, - { "name" : "hvl_fclk_1_A", "ric_name" : "u_GBOX_HV_40X2_VL.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvl", "bank" : 1 }, - { "name" : "hvl_fclk_1_B", "ric_name" : "u_GBOX_HV_40X2_VL.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvl", "bank" : 1 }, - { "name" : "hvr_fclk_0_A", "ric_name" : "u_GBOX_HV_40X2_VR.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvr", "bank" : 0 }, - { "name" : "hvr_fclk_0_B", "ric_name" : "u_GBOX_HV_40X2_VR.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvr", "bank" : 0 }, - { "name" : "hvr_fclk_1_A", "ric_name" : "u_GBOX_HV_40X2_VR.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvr", "bank" : 1 }, - { "name" : "hvr_fclk_1_B", "ric_name" : "u_GBOX_HV_40X2_VR.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvr", "bank" : 1 } - ] - }, - "__init__" : { + "__init_file__" : { "__args__" : [], - "__equation__" : [ - "import json", - "G_RESOURCES = json.loads('__resources_string__')", - "assert 'pll' in G_RESOURCES", - "assert 'fclk' in G_RESOURCES", + "__file__" : [ + "import re", "MAX_BOOT_CLOCK_RESOURCE = 1", - "MAX_FABRIC_CLOCK_RESOURCE = 16", - "MAX_PLL_RESOURCE = len(G_RESOURCES['pll'])", "hp_banks = ['HP_%d' % i for i in [1, 2]]", "hr_banks = ['HR_%d' % i for i in [1, 2, 3, 5]]", "all_banks = hp_banks + hr_banks", - "pin_list = ['%d_%d%c' % (i, i//2, 'N' if i%2 else 'P') for i in range(40)]", - "cc_pin_list = ['%d_%d%c' % (i, i//2, 'N' if i%2 else 'P') for i in [18, 19, 38, 39]]", - "cc_p_pin_list = [pin for pin in cc_pin_list if pin[-1] == 'P']", - "g_all_pins = ['%s_%s%s' % (i, 'CC_' if j in cc_pin_list else '', j) for i in all_banks for j in pin_list]", - "g_all_clock_pins = ['%s_CC_%s' % (i, j) for i in all_banks for j in cc_p_pin_list]", + "bank_pin_count = 40", + "CC_index = [18, 19, 38, 39]", + "exclude_index = []", + "pin_list = ['%s%d_%d%c' % ('CC_' if i in CC_index else '', i, i//2, 'N' if i%2 else 'P') for i in range(bank_pin_count) if i not in exclude_index]", + "cc_p_pin_list = [pin for pin in pin_list if (pin.find('CC_') == 0 and pin[-1] == 'P')]", + "g_all_pins = ['%s_%s' % (i, j) for i in all_banks for j in pin_list]", + "g_all_clock_pins = ['%s_%s' % (i, j) for i in all_banks for j in cc_p_pin_list]", "g_all_pll_clock_pins = [pin for pin in g_all_clock_pins]", "g_boot_clock_resources = 0", "g_pin_resources = {}", - "g_fabric_clock_resources = 0", - "g_pll_resources = []", - "g_gearbox_width = {}" + "g_input_gearbox_width = {}", + "g_output_gearbox_width = {}", + "def parse_pin_location(location):", + " assert location in g_all_pins", + " m = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', location)", + " assert m != None", + " assert len(m.groups()) == 6", + " type = 'HP' if m.group(1) == 'P' else ('HVL' if m.group(2) in ['1', '2'] else 'HVR')", + " bank = 0 if m.group(2) in ['1', '3'] else 1", + " is_clock = m.group(3) == '_CC'", + " index = int(m.group(4))", + " pair_index = int(m.group(5))", + " assert pair_index == (index//2)", + " ab_io = 0 if (pair_index < 10) else 1", + " ab_name = '%c' % (ord('A') + ab_io)", + " return [m, type, bank, is_clock, index, pair_index, ab_io, ab_name]", + "def get_peer_location(location):", + " (m, type, bank, is_clock, index, pair_index, ab_io, ab_name) = parse_pin_location(location)", + " pn = 'P' if m.group(6) == 'N' else 'N'", + " index = int(m.group(4)) & ~1", + " index += (1 if pn == 'N' else 0)", + " peer_location = 'H%s_%s%s_%d_%s%s' % (m.group(1), m.group(2), m.group(3), index, m.group(5), pn)", + " return [m.group(6), peer_location]", + "def validate_data_width_parameter(location, width, gearboxes):", + " (self_pn, peer_location) = get_peer_location(location)", + " result = width >= 3 and width <= (10 if self_pn == 'P' else 5)", + " result = result and ((peer_location not in gearboxes) or (gearboxes[peer_location] <= 5 and width <=5))", + " gearboxes[location if result else ''] = width", + " gearboxes.pop('', None)", + " return result", + "def get_pin_info(name):", + " bank = 0", + " is_clock = False", + " index = 0", + " pair_index = 0", + " ab_io = 0", + " ab_name = ''", + " if name.find('BOOT_CLOCK#') == 0:", + " type = 'BOOT_CLOCK'", + " index = int(name[11:])", + " model_name = 'hp_40x2.rc_osc_50mhz'", + " elif name.find('FABRIC_CLKBUF#') == 0:", + " type = 'FABRIC_CLKBUF'", + " index = int(name[14:])", + " model_name = 'fclk_buf'", + " else :", + " (m, type, bank, is_clock, index, pair_index, ab_io, ab_name) = parse_pin_location(name)", + " model_name = '%s_40x2.bank%d_hpio.gearbox_%s[%d]' % (type.lower(), bank, m.group(6), pair_index)", + " return [type, bank, is_clock, index, pair_index, ab_io, ab_name, model_name]" ] }, "__primary_validation__" : { @@ -451,12 +287,18 @@ "__check_ds_pin_resource__", "__clock_pin_is_valid__", "__check_boot_clock_resource__", - "__pll_clock_pin_is_valid__" + "__pll_clock_pin_is_valid__", + "__check_input_data_width_parameter__", + "__check_output_data_width_parameter__", + "__check_data_rate_parameter__", + "__check_dpa_mode_parameter__", + "__check_clock_phase_parameter__", + "__check_pll_parameter__" ], "__pin_is_valid__" : { "__module__" : ["I_BUF", "O_BUF", "O_BUFT"], "__equation__" : [ - "pin_result = '__location0__' in g_all_pins" + "validation_result = '__location0__' in g_all_pins" ] }, "__check_pin_resource__" : { @@ -465,47 +307,47 @@ "temp = '__primitive_flags__'.split(',')", "bidir = 'INOUT' in temp", "value = 1 if 'I_BUF' in temp else 2", - "pin_result = '__location0__' not in g_pin_resources or ((g_pin_resources['__location0__'] & value) == 0)", + "validation_result = '__location0__' not in g_pin_resources or ((g_pin_resources['__location0__'] & value) == 0)", "exist = 0 if '__location0__' not in g_pin_resources else g_pin_resources['__location0__']", - "g_pin_resources['__location0__' if pin_result else ''] = exist | (value if bidir else 3)", + "g_pin_resources['__location0__' if validation_result else ''] = exist | (value if bidir else 3)", "g_pin_resources.pop('', None)" ] }, "__ds_pin_is_valid__" : { "__module__" : ["I_BUF_DS", "O_BUF_DS", "O_BUFT_DS"], "__equation__" : [ - "pin_result = '__location0__' in g_all_pins", - "pin_result = pin_result and '__location1__' in g_all_pins" + "validation_result = '__location0__' in g_all_pins", + "validation_result = validation_result and '__location1__' in g_all_pins" ] }, "__pin_is_differential__" : { "__module__" : ["I_BUF_DS", "O_BUF_DS", "O_BUFT_DS"], "__equation__" : [ "import re", - "pin_result = '__location0__' in g_all_pins", - "pin_result = pin_result and '__location1__' in g_all_pins", + "validation_result = '__location0__' in g_all_pins", + "validation_result = validation_result and '__location1__' in g_all_pins", "m0 = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', '__location0__')", "m1 = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', '__location1__')", - "pin_result = pin_result and m0 != None", - "pin_result = pin_result and m1 != None", - "pin_result = pin_result and len(m0.groups()) == 6", - "pin_result = pin_result and len(m1.groups()) == 6", - "pin_result = pin_result and m0.group(1) == m1.group(1)", - "pin_result = pin_result and m0.group(2) == m1.group(2)", - "pin_result = pin_result and m0.group(3) == m1.group(3)", - "pin_result = pin_result and m0.group(4) != m1.group(4)", - "pin_result = pin_result and m0.group(5) == m1.group(5)", - "pin_result = pin_result and m0.group(6) != m1.group(6)" + "validation_result = validation_result and m0 != None", + "validation_result = validation_result and m1 != None", + "validation_result = validation_result and len(m0.groups()) == 6", + "validation_result = validation_result and len(m1.groups()) == 6", + "validation_result = validation_result and m0.group(1) == m1.group(1)", + "validation_result = validation_result and m0.group(2) == m1.group(2)", + "validation_result = validation_result and m0.group(3) == m1.group(3)", + "validation_result = validation_result and m0.group(4) != m1.group(4)", + "validation_result = validation_result and m0.group(5) == m1.group(5)", + "validation_result = validation_result and m0.group(6) != m1.group(6)" ] }, "__check_ds_pin_resource__" : { "__module__" : ["I_BUF_DS", "O_BUF_DS", "O_BUFT_DS"], "__equation__" : [ - "pin_result = '__location0__' not in g_pin_resources", - "pin_result = pin_result and '__location1__' not in g_pin_resources", - "g_pin_resources['__location0__' if pin_result else ''] = 3", + "validation_result = '__location0__' not in g_pin_resources", + "validation_result = validation_result and '__location1__' not in g_pin_resources", + "g_pin_resources['__location0__' if validation_result else ''] = 3", "g_pin_resources.pop('', None)", - "g_pin_resources['__location1__' if pin_result else ''] = 3", + "g_pin_resources['__location1__' if validation_result else ''] = 3", "g_pin_resources.pop('', None)" ] }, @@ -513,79 +355,56 @@ "__module__" : ["CLK_BUF"], "__equation__" : [ "temp = '__primitive_flags__'.split(',')", - "pin_result = '__location0__' in g_all_clock_pins or 'PIN_CLOCK_CORE_ONLY' in temp" + "validation_result = '__location0__' in g_all_clock_pins or 'PIN_CLOCK_CORE_ONLY' in temp" ] }, "__check_boot_clock_resource__" : { "__module__" : ["BOOT_CLOCK"], "__equation__" : [ - "pin_result = g_boot_clock_resources < MAX_BOOT_CLOCK_RESOURCE", - "g_boot_clock_resources += (1 if pin_result else 0)" + "validation_result = g_boot_clock_resources < MAX_BOOT_CLOCK_RESOURCE", + "g_boot_clock_resources += (1 if validation_result else 0)" ] }, "__pll_clock_pin_is_valid__" : { "__module__" : ["PLL"], "pre_primitive" : "CLK_BUF", "__equation__" : [ - "pin_result = '__location0__' in g_all_pll_clock_pins" + "validation_result = '__location0__' in g_all_pll_clock_pins" ] - } - }, - "__secondary_validation__" : { - "__seqeunce__" : [ - "__check_fabric_clock_resource__", - "__check_data_width_parameter__", - "__check_data_rate_parameter__", - "__check_dpa_mode_parameter__", - "__check_clock_phase_parameter__", - "__check_pll_parameter__", - "__update_fabric_clock_resource__" - ], - "__check_fabric_clock_resource__" : { - "__module__" : ["CLK_BUF", "PLL"], - "__connectivity__" : ["O", "CLK_OUT", "CLK_OUT_DIV2", "CLK_OUT_DIV3", "CLK_OUT_DIV4"], + }, + "__check_input_data_width_parameter__" : { + "__module__" : ["I_SERDES"], + "__parameter__" : ["WIDTH"], "__equation__" : [ - "pin_result = (__connectivity_count__ + g_fabric_clock_resources) <= MAX_FABRIC_CLOCK_RESOURCE" + "validation_result = validate_data_width_parameter('__location__', int('WIDTH'), g_input_gearbox_width)" ] }, - "__check_data_width_parameter__" : { - "__module__" : ["I_SERDES", "O_SERDES"], + "__check_output_data_width_parameter__" : { + "__module__" : ["O_SERDES"], "__parameter__" : ["WIDTH"], "__equation__" : [ - "import re", - "assert '__location__' in g_all_pins", - "m = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', '__location__')", - "assert m != None", - "pn = 'P' if m.group(6) == 'N' else 'N'", - "index = int(m.group(4)) & ~1", - "index += (1 if pn == 'N' else 0)", - "peer_location = 'H%s_%s%s_%d_%s%s' % (m.group(1), m.group(2), m.group(3), index, m.group(5), pn)", - "width = int('WIDTH')", - "pin_result = width >= 3 and width <= (10 if m.group(6) == 'P' else 5)", - "pin_result = pin_result and ((peer_location not in g_gearbox_width) or (g_gearbox_width[peer_location] <= 5 and width <=5))", - "g_gearbox_width['__location__' if pin_result else ''] = width", - "g_gearbox_width.pop('', None)" + "validation_result = validate_data_width_parameter('__location__', int('WIDTH'), g_output_gearbox_width)" ] }, "__check_data_rate_parameter__" : { "__module__" : ["I_SERDES", "O_SERDES", "O_SERDES_CLK"], "__parameter__" : ["DATA_RATE"], "__equation__" : [ - "pin_result = 'DATA_RATE' in ['SDR', 'DDR']" + "validation_result = 'DATA_RATE' in ['SDR', 'DDR']" ] }, "__check_dpa_mode_parameter__" : { "__module__" : ["I_SERDES"], "__parameter__" : ["DPA_MODE"], "__equation__" : [ - "pin_result = 'DPA_MODE' in ['NONE', 'DPA', 'CDR']" + "validation_result = 'DPA_MODE' in ['NONE', 'DPA', 'CDR']" ] }, "__check_clock_phase_parameter__" : { "__module__" : ["O_SERDES_CLK"], "__parameter__" : ["CLOCK_PHASE"], "__equation__" : [ - "pin_result = 'CLOCK_PHASE' in ['0', '90', '180', '270']" + "validation_result = 'CLOCK_PHASE' in ['0', '90', '180', '270']" ] }, "__check_pll_parameter__" : { @@ -597,72 +416,21 @@ "post_div = int('PLL_POST_DIV', 0)", "post_div1 = post_div >> 4", "post_div2 = post_div & 0xF", - "pin_result = (div >= 1 and div <= 63)", - "pin_result = pin_result and (mult >= 16 and mult <= 640)", - "pin_result = pin_result and (post_div1 >= 1 and post_div1 <= 7)", - "pin_result = pin_result and (post_div2 >= 1 and post_div2 <= 7)", - "pin_result = pin_result and (post_div1 >= post_div2)" - ] - }, - "__check_pll_clock_pin_resource__" : { - "__module__" : ["PLL"], - "__equation__" : [ - "pin_result = '__location0__' not in g_pll_resources", - "pin_result = pin_result and len(g_pll_resources) < MAX_PLL_RESOURCE", - "g_pll_resources.append('__location0__' if pin_result else '')", - "g_pll_resources = [pin for pin in g_pll_resources if pin != '']" - ] - }, - "__update_fabric_clock_resource__" : { - "__module__" : ["CLK_BUF", "PLL"], - "__connectivity__" : ["O", "CLK_OUT", "CLK_OUT_DIV2", "CLK_OUT_DIV3", "CLK_OUT_DIV4"], - "__equation__" : [ - "assert ((__connectivity_count__ + g_fabric_clock_resources) <= MAX_FABRIC_CLOCK_RESOURCE)", - "g_fabric_clock_resources += __connectivity_count__" + "validation_result = (div >= 1 and div <= 63)", + "validation_result = validation_result and (mult >= 16 and mult <= 640)", + "validation_result = validation_result and (post_div1 >= 1 and post_div1 <= 7)", + "validation_result = validation_result and (post_div2 >= 1 and post_div2 <= 7)", + "validation_result = validation_result and (post_div1 >= post_div2)" ] } }, "__define__" : { - "parse_location" : { - "__args__" : ["__type__", "__bank__"], - "__equation__" : [ - "import re", - "assert '__location__' in g_all_pins", - "m = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', '__location__')", - "__type__ = 'HP' if m.group(1) == 'P' else 'HV'", - "__bank__ = '0' if m.group(2) in ['1', '3'] else '1'" - ] - }, "parse_o_serdes_clk_phase_parameter" : { "__args__" : ["__clock_phase__"], "__equation__" : [ "__clock_phase__ = 'TX_phase_%d' % __argCLOCK_PHASE__" ] }, - "parse_pll_parameter" : { - "__args__" : ["__refdiv__", "__fbdiv__", "__postdiv1__", "__postdiv2__"], - "__equation__" : [ - "__refdiv__ = str(int('__argDIV__', 0))", - "__fbdiv__ = str(int('__argMULT__', 0))", - "__postdiv1__ = str((int('__argPOST_DIV__', 0) >> 4) & 0x7)", - "__postdiv2__ = str((int('__argPOST_DIV__', 0)) & 0x7)" - ] - }, - "parse_pll_root_mux" : { - "__args__" : ["__pll_root_mux_sel__"], - "__equation__" : [ - "__pll_root_mux_sel__ = 32 + (int('__pll_resource__') * 4) + __argIndex__", - "__pll_root_mux_sel__ = '%d' % __pll_root_mux_sel__" - ] - }, - "parse_fabric_clock_buffer_root_mux" : { - "__args__" : ["__fclk_buf_root_mux_sel__"], - "__equation__" : [ - "fabric_clock_buffer_slot = int('__arg1__')", - "__fclk_buf_root_mux_sel__ = (40 + fabric_clock_buffer_slot - 4) if (fabric_clock_buffer_slot >= 4) else (44 + fabric_clock_buffer_slot)", - "__fclk_buf_root_mux_sel__ = '%d' % __fclk_buf_root_mux_sel__" - ] - }, "parse_serdes_width" : { "__args__" : ["__peer_is_on__"], "__equation__" : [ diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_10x8_config_attributes.mapping.json b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_10x8_config_attributes.mapping.json index ca1d1de5..549b9da2 100644 --- a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_10x8_config_attributes.mapping.json +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_10x8_config_attributes.mapping.json @@ -88,25 +88,6 @@ "I_SERDES" : "DPA_MODE==NONE" } }, - "I_SERDES.ROOT_BANK_CLKMUX" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__", - "DPA_MODE" : "__arg1__" - }, - "results" : { - "__location__" : "__ROOT_BANK_MUX_LOCATION__", - "I_SERDES" : "ROOT_BANK_SRC==__AB__&DPA_MODE==__arg1__ --#MUX=__ROOT_BANK_MUX__" - } - }, - "I_SERDES.ROOT_MUX" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__ROOT_MUX__" - } - }, "O_SERDES.BYPASS" : { "rules" : { "WIDTH" : "__arg0__" @@ -132,13 +113,6 @@ "O_SERDES" : "DDR_MODE==SDR" } }, - "O_SERDES_CLK.IO" : { - "rules" : { - }, - "results" : { - "TX_CLOCK_IO" : "TX_clock_IO" - } - }, "O_SERDES_CLK.CLK_PHASE" : { "rules" : { "CLOCK_PHASE" : "__argCLOCK_PHASE__" @@ -158,101 +132,6 @@ "neg_results" : { "O_SERDES_CLK" : "DDR_MODE==SDR" } - }, - "BOOT_CLOCK" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : [ - { - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "48" - } - ] - }, - "PLL.PLLREF_MUX" : { - "rules" : { - }, - "results" : { - "__location__" : "u_GBOX_HP_40X2.u_gbox_pll_refmux___pll_resource__", - "PLL" : "PLLREF_SRC==__SRC__ --#PIN=__PIN__ --#BANK=__BANK__ --#DIV=__DIV__" - } - }, - "PLL.PLL" : { - "rules" : { - "PLL_DIV" : "__argDIV__", - "PLL_MULT" : "__argMULT__", - "PLL_POST_DIV" : "__argPOST_DIV__" - }, - "results" : { - "__define__" : "parse_pll_parameter", - "__location__" : "u_GBOX_HP_40X2.u_gbox_PLLTS16FFCFRACF___pll_resource__", - "PLL" : "PLL_SRC==DEFAULT", - "pll_REFDIV" : "__refdiv__", - "pll_FBDIV" : "__fbdiv__", - "pll_POSTDIV1" : "__postdiv1__", - "pll_POSTDIV2" : "__postdiv2__", - "pll_PLLEN" : "__pll_enable__" - } - }, - "PLL.ROOT_MUX0" : { - "rules" : { - "__connectivity__" : "CLK_OUT", - "__index__" : "__argIndex{default:0}__", - "OUT0_ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__define__" : "parse_pll_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__pll_root_mux_sel__" - } - }, - "PLL.ROOT_MUX1" : { - "rules" : { - "__connectivity__" : "CLK_OUT_DIV2", - "__index__" : "__argIndex{default:1}__", - "OUT1_ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__define__" : "parse_pll_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__pll_root_mux_sel__" - } - }, - "PLL.ROOT_MUX2" : { - "rules" : { - "__connectivity__" : "CLK_OUT_DIV3", - "__index__" : "__argIndex{default:2}__", - "OUT2_ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__define__" : "parse_pll_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__pll_root_mux_sel__" - } - }, - "PLL.ROOT_MUX3" : { - "rules" : { - "__connectivity__" : "CLK_OUT_DIV4", - "__index__" : "__argIndex{default:3}__", - "OUT3_ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__define__" : "parse_pll_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__pll_root_mux_sel__" - } - }, - "FCLK_BUF" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__", - "ROUTE_FROM_FABRIC_CLK" : "__arg1__" - }, - "results" : { - "__define__" : "parse_fabric_clock_buffer_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__fclk_buf_root_mux_sel__" - } } }, "properties" : { @@ -328,118 +207,75 @@ "results" : { "CLK_BUF" : "GBOX_TOP_SRC==DEFAULT" } - }, - "CLK_BUF.ROOT_BANK_CLKMUX" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__location__" : "__ROOT_BANK_MUX_LOCATION__", - "CLK_BUF" : "ROOT_BANK_SRC==__AB__ --#MUX=__ROOT_BANK_MUX__" - } - }, - "CLK_BUF.ROOT_MUX" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__ROOT_MUX__" - } } }, - "__file__" : [ - "import re", - "def get_pin_info(name) :", - " bank = 0", - " is_clock = False", - " index = 0", - " pair_index = 0", - " ab_io = 0", - " ab_name = ''", - " root_bank_mux_location = ''", - " root_bank_mux_resource = ''", - " root_bank_mux_core_input_index = 0", - " root_mux_input_index = 0", - " if name.find('BOOT_CLOCK#') == 0:", - " type = 'BOOT_CLOCK'", - " index = int(name[11:])", - " else :", - " m = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', name)", - " assert m != None", - " assert len(m.groups()) == 6", - " type = 'HP' if m.group(1) == 'P' else ('HVL' if m.group(2) in ['1', '2'] else 'HVR')", - " bank = 0 if m.group(2) in ['1', '3'] else 1", - " is_clock = m.group(2) == '_CC'", - " index = int(m.group(4))", - " pair_index = int(m.group(5))", - " ab_io = 0 if (pair_index < 10) else 1", - " ab_name = '%c' % (ord('A') + ab_io)", - " root_name = 'u_GBOX_HP_40X2' if type == 'HP' else ('u_GBOX_HV_40X2_VL' if type == 'HVL' else 'u_GBOX_HV_40X2_VR')", - " root_bank_mux_location = '%s.u_gbox_root_bank_clkmux_%d' % (root_name, bank)", - " root_bank_mux_resource = '%s (Bank %s)' % (root_bank_mux_location, ab_name)", - " root_bank_mux_core_input_index = index - (20 * ab_io)", - " root_mux_input_index = 0 if type == 'HP' else (8 if type == 'HVL' else 16)", - " root_mux_input_index += ((2 * bank) + ab_io)", - " return [type, bank, is_clock, index, pair_index, ab_io, ab_name, root_bank_mux_location, root_bank_mux_resource, root_bank_mux_core_input_index, root_mux_input_index]", - "def fclk_use_pll_resource(fclk) :", - " pll_resource = 0", - " if fclk.find('hvl_fclk_') == 0 :", - " pll_resource = 0", - " elif fclk.find('hvr_fclk_') == 0 :", - " pll_resource = 1", - " elif fclk.find('hp_fclk_0') == 0 :", - " pll_resource = 0", - " elif fclk.find('hp_fclk_1') == 0 :", - " pll_resource = 1", - " else :", - " raise Exception('Unknown FCLK %s' % fclk)", - " return [pll_resource]" - ], - "__resources__" : { - "pll" : [ - { "name" : "pll_0", "ric_name" : "u_GBOX_HP_40X2.u_gbox_PLLTS16FFCFRACF_0", "type" : "HP", "subtype" : "hp", "bank" : 0 }, - { "name" : "pll_1", "ric_name" : "u_GBOX_HP_40X2.u_gbox_PLLTS16FFCFRACF_1", "type" : "HP", "subtype" : "hp", "bank" : 1 } - ], - "fclk" : [ - { "name" : "hp_fclk_0_A", "ric_name" : "u_GBOX_HP_40X2.u_gbox_fclk_mux_all", "type" : "HP", "subtype" : "hp", "bank" : 0 }, - { "name" : "hp_fclk_0_B", "ric_name" : "u_GBOX_HP_40X2.u_gbox_fclk_mux_all", "type" : "HP", "subtype" : "hp", "bank" : 0 }, - { "name" : "hp_fclk_1_A", "ric_name" : "u_GBOX_HP_40X2.u_gbox_fclk_mux_all", "type" : "HP", "subtype" : "hp", "bank" : 1 }, - { "name" : "hp_fclk_1_B", "ric_name" : "u_GBOX_HP_40X2.u_gbox_fclk_mux_all", "type" : "HP", "subtype" : "hp", "bank" : 1 }, - { "name" : "hvl_fclk_0_A", "ric_name" : "u_GBOX_HV_40X2_VL.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvl", "bank" : 0 }, - { "name" : "hvl_fclk_0_B", "ric_name" : "u_GBOX_HV_40X2_VL.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvl", "bank" : 0 }, - { "name" : "hvl_fclk_1_A", "ric_name" : "u_GBOX_HV_40X2_VL.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvl", "bank" : 1 }, - { "name" : "hvl_fclk_1_B", "ric_name" : "u_GBOX_HV_40X2_VL.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvl", "bank" : 1 }, - { "name" : "hvr_fclk_0_A", "ric_name" : "u_GBOX_HV_40X2_VR.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvr", "bank" : 0 }, - { "name" : "hvr_fclk_0_B", "ric_name" : "u_GBOX_HV_40X2_VR.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvr", "bank" : 0 }, - { "name" : "hvr_fclk_1_A", "ric_name" : "u_GBOX_HV_40X2_VR.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvr", "bank" : 1 }, - { "name" : "hvr_fclk_1_B", "ric_name" : "u_GBOX_HV_40X2_VR.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvr", "bank" : 1 } - ] - }, - "__init__" : { + "__init_file__" : { "__args__" : [], - "__equation__" : [ - "import json", - "G_RESOURCES = json.loads('__resources_string__')", - "assert 'pll' in G_RESOURCES", - "assert 'fclk' in G_RESOURCES", + "__file__" : [ + "import re", "MAX_BOOT_CLOCK_RESOURCE = 1", - "MAX_FABRIC_CLOCK_RESOURCE = 16", - "MAX_PLL_RESOURCE = len(G_RESOURCES['pll'])", "hp_banks = ['HP_%d' % i for i in [1]]", "hr_banks = ['HR_%d' % i for i in [1, 3]]", "all_banks = hp_banks + hr_banks", - "pin_list = ['%d_%d%c' % (i, i//2, 'N' if i%2 else 'P') for i in range(12)]", - "cc_pin_list = ['%d_%d%c' % (i, i//2, 'N' if i%2 else 'P') for i in [10, 11]]", - "cc_p_pin_list = [pin for pin in cc_pin_list if pin[-1] == 'P']", - "g_all_pins = ['%s_%s%s' % (i, 'CC_' if j in cc_pin_list else '', j) for i in all_banks for j in pin_list]", - "g_all_clock_pins = ['%s_CC_%s' % (i, j) for i in all_banks for j in cc_p_pin_list]", + "bank_pin_count = 12", + "CC_index = [10, 11]", + "exclude_index = []", + "pin_list = ['%s%d_%d%c' % ('CC_' if i in CC_index else '', i, i//2, 'N' if i%2 else 'P') for i in range(bank_pin_count) if i not in exclude_index]", + "cc_p_pin_list = [pin for pin in pin_list if (pin.find('CC_') == 0 and pin[-1] == 'P')]", + "g_all_pins = ['%s_%s' % (i, j) for i in all_banks for j in pin_list]", + "g_all_clock_pins = ['%s_%s' % (i, j) for i in all_banks for j in cc_p_pin_list]", "g_all_pll_clock_pins = [pin for pin in g_all_clock_pins]", "g_boot_clock_resources = 0", "g_pin_resources = {}", - "g_fabric_clock_resources = 0", - "g_pll_resources = []", - "g_gearbox_width = {}" + "g_input_gearbox_width = {}", + "g_output_gearbox_width = {}", + "def parse_pin_location(location):", + " assert location in g_all_pins", + " m = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', location)", + " assert m != None", + " assert len(m.groups()) == 6", + " type = 'HP' if m.group(1) == 'P' else ('HVL' if m.group(2) in ['1', '2'] else 'HVR')", + " bank = 0 if m.group(2) in ['1', '3'] else 1", + " is_clock = m.group(3) == '_CC'", + " index = int(m.group(4))", + " pair_index = int(m.group(5))", + " assert pair_index == (index//2)", + " ab_io = 0 if (pair_index < 10) else 1", + " ab_name = '%c' % (ord('A') + ab_io)", + " return [m, type, bank, is_clock, index, pair_index, ab_io, ab_name]", + "def get_peer_location(location):", + " (m, type, bank, is_clock, index, pair_index, ab_io, ab_name) = parse_pin_location(location)", + " pn = 'P' if m.group(6) == 'N' else 'N'", + " index = int(m.group(4)) & ~1", + " index += (1 if pn == 'N' else 0)", + " peer_location = 'H%s_%s%s_%d_%s%s' % (m.group(1), m.group(2), m.group(3), index, m.group(5), pn)", + " return [m.group(6), peer_location]", + "def validate_data_width_parameter(location, width, gearboxes):", + " (self_pn, peer_location) = get_peer_location(location)", + " result = width >= 3 and width <= (10 if self_pn == 'P' else 5)", + " result = result and ((peer_location not in gearboxes) or (gearboxes[peer_location] <= 5 and width <=5))", + " gearboxes[location if result else ''] = width", + " gearboxes.pop('', None)", + " return result", + "def get_pin_info(name):", + " bank = 0", + " is_clock = False", + " index = 0", + " pair_index = 0", + " ab_io = 0", + " ab_name = ''", + " if name.find('BOOT_CLOCK#') == 0:", + " type = 'BOOT_CLOCK'", + " index = int(name[11:])", + " model_name = 'hp_40x2.rc_osc_50mhz'", + " elif name.find('FABRIC_CLKBUF#') == 0:", + " type = 'FABRIC_CLKBUF'", + " index = int(name[14:])", + " model_name = 'fclk_buf'", + " else :", + " (m, type, bank, is_clock, index, pair_index, ab_io, ab_name) = parse_pin_location(name)", + " model_name = '%s_40x2.bank%d_hpio.gearbox_%s[%d]' % (type.lower(), bank, m.group(6), pair_index)", + " return [type, bank, is_clock, index, pair_index, ab_io, ab_name, model_name]" ] }, "__primary_validation__" : { @@ -451,12 +287,18 @@ "__check_ds_pin_resource__", "__clock_pin_is_valid__", "__check_boot_clock_resource__", - "__pll_clock_pin_is_valid__" + "__pll_clock_pin_is_valid__", + "__check_input_data_width_parameter__", + "__check_output_data_width_parameter__", + "__check_data_rate_parameter__", + "__check_dpa_mode_parameter__", + "__check_clock_phase_parameter__", + "__check_pll_parameter__" ], "__pin_is_valid__" : { "__module__" : ["I_BUF", "O_BUF", "O_BUFT"], "__equation__" : [ - "pin_result = '__location0__' in g_all_pins" + "validation_result = '__location0__' in g_all_pins" ] }, "__check_pin_resource__" : { @@ -465,47 +307,47 @@ "temp = '__primitive_flags__'.split(',')", "bidir = 'INOUT' in temp", "value = 1 if 'I_BUF' in temp else 2", - "pin_result = '__location0__' not in g_pin_resources or ((g_pin_resources['__location0__'] & value) == 0)", + "validation_result = '__location0__' not in g_pin_resources or ((g_pin_resources['__location0__'] & value) == 0)", "exist = 0 if '__location0__' not in g_pin_resources else g_pin_resources['__location0__']", - "g_pin_resources['__location0__' if pin_result else ''] = exist | (value if bidir else 3)", + "g_pin_resources['__location0__' if validation_result else ''] = exist | (value if bidir else 3)", "g_pin_resources.pop('', None)" ] }, "__ds_pin_is_valid__" : { "__module__" : ["I_BUF_DS", "O_BUF_DS", "O_BUFT_DS"], "__equation__" : [ - "pin_result = '__location0__' in g_all_pins", - "pin_result = pin_result and '__location1__' in g_all_pins" + "validation_result = '__location0__' in g_all_pins", + "validation_result = validation_result and '__location1__' in g_all_pins" ] }, "__pin_is_differential__" : { "__module__" : ["I_BUF_DS", "O_BUF_DS", "O_BUFT_DS"], "__equation__" : [ "import re", - "pin_result = '__location0__' in g_all_pins", - "pin_result = pin_result and '__location1__' in g_all_pins", + "validation_result = '__location0__' in g_all_pins", + "validation_result = validation_result and '__location1__' in g_all_pins", "m0 = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', '__location0__')", "m1 = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', '__location1__')", - "pin_result = pin_result and m0 != None", - "pin_result = pin_result and m1 != None", - "pin_result = pin_result and len(m0.groups()) == 6", - "pin_result = pin_result and len(m1.groups()) == 6", - "pin_result = pin_result and m0.group(1) == m1.group(1)", - "pin_result = pin_result and m0.group(2) == m1.group(2)", - "pin_result = pin_result and m0.group(3) == m1.group(3)", - "pin_result = pin_result and m0.group(4) != m1.group(4)", - "pin_result = pin_result and m0.group(5) == m1.group(5)", - "pin_result = pin_result and m0.group(6) != m1.group(6)" + "validation_result = validation_result and m0 != None", + "validation_result = validation_result and m1 != None", + "validation_result = validation_result and len(m0.groups()) == 6", + "validation_result = validation_result and len(m1.groups()) == 6", + "validation_result = validation_result and m0.group(1) == m1.group(1)", + "validation_result = validation_result and m0.group(2) == m1.group(2)", + "validation_result = validation_result and m0.group(3) == m1.group(3)", + "validation_result = validation_result and m0.group(4) != m1.group(4)", + "validation_result = validation_result and m0.group(5) == m1.group(5)", + "validation_result = validation_result and m0.group(6) != m1.group(6)" ] }, "__check_ds_pin_resource__" : { "__module__" : ["I_BUF_DS", "O_BUF_DS", "O_BUFT_DS"], "__equation__" : [ - "pin_result = '__location0__' not in g_pin_resources", - "pin_result = pin_result and '__location1__' not in g_pin_resources", - "g_pin_resources['__location0__' if pin_result else ''] = 3", + "validation_result = '__location0__' not in g_pin_resources", + "validation_result = validation_result and '__location1__' not in g_pin_resources", + "g_pin_resources['__location0__' if validation_result else ''] = 3", "g_pin_resources.pop('', None)", - "g_pin_resources['__location1__' if pin_result else ''] = 3", + "g_pin_resources['__location1__' if validation_result else ''] = 3", "g_pin_resources.pop('', None)" ] }, @@ -513,79 +355,56 @@ "__module__" : ["CLK_BUF"], "__equation__" : [ "temp = '__primitive_flags__'.split(',')", - "pin_result = '__location0__' in g_all_clock_pins or 'PIN_CLOCK_CORE_ONLY' in temp" + "validation_result = '__location0__' in g_all_clock_pins or 'PIN_CLOCK_CORE_ONLY' in temp" ] }, "__check_boot_clock_resource__" : { "__module__" : ["BOOT_CLOCK"], "__equation__" : [ - "pin_result = g_boot_clock_resources < MAX_BOOT_CLOCK_RESOURCE", - "g_boot_clock_resources += (1 if pin_result else 0)" + "validation_result = g_boot_clock_resources < MAX_BOOT_CLOCK_RESOURCE", + "g_boot_clock_resources += (1 if validation_result else 0)" ] }, "__pll_clock_pin_is_valid__" : { "__module__" : ["PLL"], "pre_primitive" : "CLK_BUF", "__equation__" : [ - "pin_result = '__location0__' in g_all_pll_clock_pins" + "validation_result = '__location0__' in g_all_pll_clock_pins" ] - } - }, - "__secondary_validation__" : { - "__seqeunce__" : [ - "__check_fabric_clock_resource__", - "__check_data_width_parameter__", - "__check_data_rate_parameter__", - "__check_dpa_mode_parameter__", - "__check_clock_phase_parameter__", - "__check_pll_parameter__", - "__update_fabric_clock_resource__" - ], - "__check_fabric_clock_resource__" : { - "__module__" : ["CLK_BUF", "PLL"], - "__connectivity__" : ["O", "CLK_OUT", "CLK_OUT_DIV2", "CLK_OUT_DIV3", "CLK_OUT_DIV4"], + }, + "__check_input_data_width_parameter__" : { + "__module__" : ["I_SERDES"], + "__parameter__" : ["WIDTH"], "__equation__" : [ - "pin_result = (__connectivity_count__ + g_fabric_clock_resources) <= MAX_FABRIC_CLOCK_RESOURCE" + "validation_result = validate_data_width_parameter('__location__', int('WIDTH'), g_input_gearbox_width)" ] }, - "__check_data_width_parameter__" : { - "__module__" : ["I_SERDES", "O_SERDES"], + "__check_output_data_width_parameter__" : { + "__module__" : ["O_SERDES"], "__parameter__" : ["WIDTH"], "__equation__" : [ - "import re", - "assert '__location__' in g_all_pins", - "m = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', '__location__')", - "assert m != None", - "pn = 'P' if m.group(6) == 'N' else 'N'", - "index = int(m.group(4)) & ~1", - "index += (1 if pn == 'N' else 0)", - "peer_location = 'H%s_%s%s_%d_%s%s' % (m.group(1), m.group(2), m.group(3), index, m.group(5), pn)", - "width = int('WIDTH')", - "pin_result = width >= 3 and width <= (10 if m.group(6) == 'P' else 5)", - "pin_result = pin_result and ((peer_location not in g_gearbox_width) or (g_gearbox_width[peer_location] <= 5 and width <=5))", - "g_gearbox_width['__location__' if pin_result else ''] = width", - "g_gearbox_width.pop('', None)" + "validation_result = validate_data_width_parameter('__location__', int('WIDTH'), g_output_gearbox_width)" ] }, "__check_data_rate_parameter__" : { "__module__" : ["I_SERDES", "O_SERDES", "O_SERDES_CLK"], "__parameter__" : ["DATA_RATE"], "__equation__" : [ - "pin_result = 'DATA_RATE' in ['SDR', 'DDR']" + "validation_result = 'DATA_RATE' in ['SDR', 'DDR']" ] }, "__check_dpa_mode_parameter__" : { "__module__" : ["I_SERDES"], "__parameter__" : ["DPA_MODE"], "__equation__" : [ - "pin_result = 'DPA_MODE' in ['NONE', 'DPA', 'CDR']" + "validation_result = 'DPA_MODE' in ['NONE', 'DPA', 'CDR']" ] }, "__check_clock_phase_parameter__" : { "__module__" : ["O_SERDES_CLK"], "__parameter__" : ["CLOCK_PHASE"], "__equation__" : [ - "pin_result = 'CLOCK_PHASE' in ['0', '90', '180', '270']" + "validation_result = 'CLOCK_PHASE' in ['0', '90', '180', '270']" ] }, "__check_pll_parameter__" : { @@ -597,72 +416,21 @@ "post_div = int('PLL_POST_DIV', 0)", "post_div1 = post_div >> 4", "post_div2 = post_div & 0xF", - "pin_result = (div >= 1 and div <= 63)", - "pin_result = pin_result and (mult >= 16 and mult <= 640)", - "pin_result = pin_result and (post_div1 >= 1 and post_div1 <= 7)", - "pin_result = pin_result and (post_div2 >= 1 and post_div2 <= 7)", - "pin_result = pin_result and (post_div1 >= post_div2)" - ] - }, - "__check_pll_clock_pin_resource__" : { - "__module__" : ["PLL"], - "__equation__" : [ - "pin_result = '__location0__' not in g_pll_resources", - "pin_result = pin_result and len(g_pll_resources) < MAX_PLL_RESOURCE", - "g_pll_resources.append('__location0__' if pin_result else '')", - "g_pll_resources = [pin for pin in g_pll_resources if pin != '']" - ] - }, - "__update_fabric_clock_resource__" : { - "__module__" : ["CLK_BUF", "PLL"], - "__connectivity__" : ["O", "CLK_OUT", "CLK_OUT_DIV2", "CLK_OUT_DIV3", "CLK_OUT_DIV4"], - "__equation__" : [ - "assert ((__connectivity_count__ + g_fabric_clock_resources) <= MAX_FABRIC_CLOCK_RESOURCE)", - "g_fabric_clock_resources += __connectivity_count__" + "validation_result = (div >= 1 and div <= 63)", + "validation_result = validation_result and (mult >= 16 and mult <= 640)", + "validation_result = validation_result and (post_div1 >= 1 and post_div1 <= 7)", + "validation_result = validation_result and (post_div2 >= 1 and post_div2 <= 7)", + "validation_result = validation_result and (post_div1 >= post_div2)" ] } }, "__define__" : { - "parse_location" : { - "__args__" : ["__type__", "__bank__"], - "__equation__" : [ - "import re", - "assert '__location__' in g_all_pins", - "m = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', '__location__')", - "__type__ = 'HP' if m.group(1) == 'P' else 'HV'", - "__bank__ = '0' if m.group(2) in ['1', '3'] else '1'" - ] - }, "parse_o_serdes_clk_phase_parameter" : { "__args__" : ["__clock_phase__"], "__equation__" : [ "__clock_phase__ = 'TX_phase_%d' % __argCLOCK_PHASE__" ] }, - "parse_pll_parameter" : { - "__args__" : ["__refdiv__", "__fbdiv__", "__postdiv1__", "__postdiv2__"], - "__equation__" : [ - "__refdiv__ = str(int('__argDIV__', 0))", - "__fbdiv__ = str(int('__argMULT__', 0))", - "__postdiv1__ = str((int('__argPOST_DIV__', 0) >> 4) & 0x7)", - "__postdiv2__ = str((int('__argPOST_DIV__', 0)) & 0x7)" - ] - }, - "parse_pll_root_mux" : { - "__args__" : ["__pll_root_mux_sel__"], - "__equation__" : [ - "__pll_root_mux_sel__ = 32 + (int('__pll_resource__') * 4) + __argIndex__", - "__pll_root_mux_sel__ = '%d' % __pll_root_mux_sel__" - ] - }, - "parse_fabric_clock_buffer_root_mux" : { - "__args__" : ["__fclk_buf_root_mux_sel__"], - "__equation__" : [ - "fabric_clock_buffer_slot = int('__arg1__')", - "__fclk_buf_root_mux_sel__ = (40 + fabric_clock_buffer_slot - 4) if (fabric_clock_buffer_slot >= 4) else (44 + fabric_clock_buffer_slot)", - "__fclk_buf_root_mux_sel__ = '%d' % __fclk_buf_root_mux_sel__" - ] - }, "parse_serdes_width" : { "__args__" : ["__peer_is_on__"], "__equation__" : [ diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_22x4_config_attributes.mapping.json b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_22x4_config_attributes.mapping.json index 2d88336f..cd84f69a 100644 --- a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_22x4_config_attributes.mapping.json +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_22x4_config_attributes.mapping.json @@ -88,25 +88,6 @@ "I_SERDES" : "DPA_MODE==NONE" } }, - "I_SERDES.ROOT_BANK_CLKMUX" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__", - "DPA_MODE" : "__arg1__" - }, - "results" : { - "__location__" : "__ROOT_BANK_MUX_LOCATION__", - "I_SERDES" : "ROOT_BANK_SRC==__AB__&DPA_MODE==__arg1__ --#MUX=__ROOT_BANK_MUX__" - } - }, - "I_SERDES.ROOT_MUX" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__ROOT_MUX__" - } - }, "O_SERDES.BYPASS" : { "rules" : { "WIDTH" : "__arg0__" @@ -132,13 +113,6 @@ "O_SERDES" : "DDR_MODE==SDR" } }, - "O_SERDES_CLK.IO" : { - "rules" : { - }, - "results" : { - "TX_CLOCK_IO" : "TX_clock_IO" - } - }, "O_SERDES_CLK.CLK_PHASE" : { "rules" : { "CLOCK_PHASE" : "__argCLOCK_PHASE__" @@ -158,101 +132,6 @@ "neg_results" : { "O_SERDES_CLK" : "DDR_MODE==SDR" } - }, - "BOOT_CLOCK" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : [ - { - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "48" - } - ] - }, - "PLL.PLLREF_MUX" : { - "rules" : { - }, - "results" : { - "__location__" : "u_GBOX_HP_40X2.u_gbox_pll_refmux___pll_resource__", - "PLL" : "PLLREF_SRC==__SRC__ --#PIN=__PIN__ --#BANK=__BANK__ --#DIV=__DIV__" - } - }, - "PLL.PLL" : { - "rules" : { - "PLL_DIV" : "__argDIV__", - "PLL_MULT" : "__argMULT__", - "PLL_POST_DIV" : "__argPOST_DIV__" - }, - "results" : { - "__define__" : "parse_pll_parameter", - "__location__" : "u_GBOX_HP_40X2.u_gbox_PLLTS16FFCFRACF___pll_resource__", - "PLL" : "PLL_SRC==DEFAULT", - "pll_REFDIV" : "__refdiv__", - "pll_FBDIV" : "__fbdiv__", - "pll_POSTDIV1" : "__postdiv1__", - "pll_POSTDIV2" : "__postdiv2__", - "pll_PLLEN" : "__pll_enable__" - } - }, - "PLL.ROOT_MUX0" : { - "rules" : { - "__connectivity__" : "CLK_OUT", - "__index__" : "__argIndex{default:0}__", - "OUT0_ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__define__" : "parse_pll_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__pll_root_mux_sel__" - } - }, - "PLL.ROOT_MUX1" : { - "rules" : { - "__connectivity__" : "CLK_OUT_DIV2", - "__index__" : "__argIndex{default:1}__", - "OUT1_ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__define__" : "parse_pll_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__pll_root_mux_sel__" - } - }, - "PLL.ROOT_MUX2" : { - "rules" : { - "__connectivity__" : "CLK_OUT_DIV3", - "__index__" : "__argIndex{default:2}__", - "OUT2_ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__define__" : "parse_pll_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__pll_root_mux_sel__" - } - }, - "PLL.ROOT_MUX3" : { - "rules" : { - "__connectivity__" : "CLK_OUT_DIV4", - "__index__" : "__argIndex{default:3}__", - "OUT3_ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__define__" : "parse_pll_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__pll_root_mux_sel__" - } - }, - "FCLK_BUF" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__", - "ROUTE_FROM_FABRIC_CLK" : "__arg1__" - }, - "results" : { - "__define__" : "parse_fabric_clock_buffer_root_mux", - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__fclk_buf_root_mux_sel__" - } } }, "properties" : { @@ -328,117 +207,74 @@ "results" : { "CLK_BUF" : "GBOX_TOP_SRC==DEFAULT" } - }, - "CLK_BUF.ROOT_BANK_CLKMUX" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__location__" : "__ROOT_BANK_MUX_LOCATION__", - "CLK_BUF" : "ROOT_BANK_SRC==__AB__ --#MUX=__ROOT_BANK_MUX__" - } - }, - "CLK_BUF.ROOT_MUX" : { - "rules" : { - "ROUTE_TO_FABRIC_CLK" : "__arg0__" - }, - "results" : { - "__location__" : "u_GBOX_HP_40X2.u_gbox_clkmux_52x1_left___arg0__", - "ROOT_MUX_SEL" : "__ROOT_MUX__" - } } }, - "__file__" : [ - "import re", - "def get_pin_info(name) :", - " bank = 0", - " is_clock = False", - " index = 0", - " pair_index = 0", - " ab_io = 0", - " ab_name = ''", - " root_bank_mux_location = ''", - " root_bank_mux_resource = ''", - " root_bank_mux_core_input_index = 0", - " root_mux_input_index = 0", - " if name.find('BOOT_CLOCK#') == 0:", - " type = 'BOOT_CLOCK'", - " index = int(name[11:])", - " else :", - " m = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', name)", - " assert m != None", - " assert len(m.groups()) == 6", - " type = 'HP' if m.group(1) == 'P' else ('HVL' if m.group(2) in ['1', '2'] else 'HVR')", - " bank = 0 if m.group(2) in ['1', '3'] else 1", - " is_clock = m.group(2) == '_CC'", - " index = int(m.group(4))", - " pair_index = int(m.group(5))", - " ab_io = 0 if (pair_index < 10) else 1", - " ab_name = '%c' % (ord('A') + ab_io)", - " root_name = 'u_GBOX_HP_40X2' if type == 'HP' else ('u_GBOX_HV_40X2_VL' if type == 'HVL' else 'u_GBOX_HV_40X2_VR')", - " root_bank_mux_location = '%s.u_gbox_root_bank_clkmux_%d' % (root_name, bank)", - " root_bank_mux_resource = '%s (Bank %s)' % (root_bank_mux_location, ab_name)", - " root_bank_mux_core_input_index = index - (20 * ab_io)", - " root_mux_input_index = 0 if type == 'HP' else (8 if type == 'HVL' else 16)", - " root_mux_input_index += ((2 * bank) + ab_io)", - " return [type, bank, is_clock, index, pair_index, ab_io, ab_name, root_bank_mux_location, root_bank_mux_resource, root_bank_mux_core_input_index, root_mux_input_index]", - "def fclk_use_pll_resource(fclk) :", - " pll_resource = 0", - " if fclk.find('hvl_fclk_') == 0 :", - " pll_resource = 0", - " elif fclk.find('hvr_fclk_') == 0 :", - " pll_resource = 1", - " elif fclk.find('hp_fclk_0') == 0 :", - " pll_resource = 0", - " elif fclk.find('hp_fclk_1') == 0 :", - " pll_resource = 1", - " else :", - " raise Exception('Unknown FCLK %s' % fclk)", - " return [pll_resource]" - ], - "__resources__" : { - "pll" : [ - { "name" : "pll_0", "ric_name" : "u_GBOX_HP_40X2.u_gbox_PLLTS16FFCFRACF_0", "type" : "HP", "subtype" : "hp", "bank" : 0 }, - { "name" : "pll_1", "ric_name" : "u_GBOX_HP_40X2.u_gbox_PLLTS16FFCFRACF_1", "type" : "HP", "subtype" : "hp", "bank" : 1 } - ], - "fclk" : [ - { "name" : "hp_fclk_0_A", "ric_name" : "u_GBOX_HP_40X2.u_gbox_fclk_mux_all", "type" : "HP", "subtype" : "hp", "bank" : 0 }, - { "name" : "hp_fclk_0_B", "ric_name" : "u_GBOX_HP_40X2.u_gbox_fclk_mux_all", "type" : "HP", "subtype" : "hp", "bank" : 0 }, - { "name" : "hp_fclk_1_A", "ric_name" : "u_GBOX_HP_40X2.u_gbox_fclk_mux_all", "type" : "HP", "subtype" : "hp", "bank" : 1 }, - { "name" : "hp_fclk_1_B", "ric_name" : "u_GBOX_HP_40X2.u_gbox_fclk_mux_all", "type" : "HP", "subtype" : "hp", "bank" : 1 }, - { "name" : "hvl_fclk_0_A", "ric_name" : "u_GBOX_HV_40X2_VL.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvl", "bank" : 0 }, - { "name" : "hvl_fclk_0_B", "ric_name" : "u_GBOX_HV_40X2_VL.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvl", "bank" : 0 }, - { "name" : "hvl_fclk_1_A", "ric_name" : "u_GBOX_HV_40X2_VL.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvl", "bank" : 1 }, - { "name" : "hvl_fclk_1_B", "ric_name" : "u_GBOX_HV_40X2_VL.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvl", "bank" : 1 }, - { "name" : "hvr_fclk_0_A", "ric_name" : "u_GBOX_HV_40X2_VR.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvr", "bank" : 0 }, - { "name" : "hvr_fclk_0_B", "ric_name" : "u_GBOX_HV_40X2_VR.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvr", "bank" : 0 }, - { "name" : "hvr_fclk_1_A", "ric_name" : "u_GBOX_HV_40X2_VR.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvr", "bank" : 1 }, - { "name" : "hvr_fclk_1_B", "ric_name" : "u_GBOX_HV_40X2_VR.u_gbox_fclk_mux_all", "type" : "HV", "subtype" : "hvr", "bank" : 1 } - ] - }, - "__init__" : { + "__init_file__" : { "__args__" : [], - "__equation__" : [ - "import json", - "G_RESOURCES = json.loads('__resources_string__')", - "assert 'pll' in G_RESOURCES", - "assert 'fclk' in G_RESOURCES", + "__file__" : [ + "import re", "MAX_BOOT_CLOCK_RESOURCE = 1", - "MAX_FABRIC_CLOCK_RESOURCE = 16", - "MAX_PLL_RESOURCE = len(G_RESOURCES['pll'])", "hp_banks = ['HP_%d' % i for i in [1]]", "all_banks = hp_banks", - "pin_list = ['%d_%d%c' % (i, i//2, 'N' if i%2 else 'P') for i in range(40)]", - "cc_pin_list = ['%d_%d%c' % (i, i//2, 'N' if i%2 else 'P') for i in [18, 19, 38, 39]]", - "cc_p_pin_list = [pin for pin in cc_pin_list if pin[-1] == 'P']", - "g_all_pins = ['%s_%s%s' % (i, 'CC_' if j in cc_pin_list else '', j) for i in all_banks for j in pin_list]", - "g_all_clock_pins = ['%s_CC_%s' % (i, j) for i in all_banks for j in cc_p_pin_list]", + "bank_pin_count = 40", + "CC_index = [18, 19, 38, 39]", + "exclude_index = [16, 17, 36, 37]", + "pin_list = ['%s%d_%d%c' % ('CC_' if i in CC_index else '', i, i//2, 'N' if i%2 else 'P') for i in range(bank_pin_count) if i not in exclude_index]", + "cc_p_pin_list = [pin for pin in pin_list if (pin.find('CC_') == 0 and pin[-1] == 'P')]", + "g_all_pins = ['%s_%s' % (i, j) for i in all_banks for j in pin_list]", + "g_all_clock_pins = ['%s_%s' % (i, j) for i in all_banks for j in cc_p_pin_list]", "g_all_pll_clock_pins = [pin for pin in g_all_clock_pins]", "g_boot_clock_resources = 0", "g_pin_resources = {}", - "g_fabric_clock_resources = 0", - "g_pll_resources = []", - "g_gearbox_width = {}" + "g_input_gearbox_width = {}", + "g_output_gearbox_width = {}", + "def parse_pin_location(location):", + " assert location in g_all_pins", + " m = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', location)", + " assert m != None", + " assert len(m.groups()) == 6", + " type = 'HP' if m.group(1) == 'P' else ('HVL' if m.group(2) in ['1', '2'] else 'HVR')", + " bank = 0 if m.group(2) in ['1', '3'] else 1", + " is_clock = m.group(3) == '_CC'", + " index = int(m.group(4))", + " pair_index = int(m.group(5))", + " assert pair_index == (index//2)", + " ab_io = 0 if (pair_index < 10) else 1", + " ab_name = '%c' % (ord('A') + ab_io)", + " return [m, type, bank, is_clock, index, pair_index, ab_io, ab_name]", + "def get_peer_location(location):", + " (m, type, bank, is_clock, index, pair_index, ab_io, ab_name) = parse_pin_location(location)", + " pn = 'P' if m.group(6) == 'N' else 'N'", + " index = int(m.group(4)) & ~1", + " index += (1 if pn == 'N' else 0)", + " peer_location = 'H%s_%s%s_%d_%s%s' % (m.group(1), m.group(2), m.group(3), index, m.group(5), pn)", + " return [m.group(6), peer_location]", + "def validate_data_width_parameter(location, width, gearboxes):", + " (self_pn, peer_location) = get_peer_location(location)", + " result = width >= 3 and width <= (10 if self_pn == 'P' else 5)", + " result = result and ((peer_location not in gearboxes) or (gearboxes[peer_location] <= 5 and width <=5))", + " gearboxes[location if result else ''] = width", + " gearboxes.pop('', None)", + " return result", + "def get_pin_info(name):", + " bank = 0", + " is_clock = False", + " index = 0", + " pair_index = 0", + " ab_io = 0", + " ab_name = ''", + " if name.find('BOOT_CLOCK#') == 0:", + " type = 'BOOT_CLOCK'", + " index = int(name[11:])", + " model_name = 'hp_40x1.rc_osc_50mhz'", + " elif name.find('FABRIC_CLKBUF#') == 0:", + " type = 'FABRIC_CLKBUF'", + " index = int(name[14:])", + " model_name = 'fclk_buf'", + " else :", + " (m, type, bank, is_clock, index, pair_index, ab_io, ab_name) = parse_pin_location(name)", + " model_name = '%s_40x1.bank%d_hpio.gearbox_%s[%d]' % (type.lower(), bank, m.group(6), pair_index)", + " return [type, bank, is_clock, index, pair_index, ab_io, ab_name, model_name]" ] }, "__primary_validation__" : { @@ -450,12 +286,18 @@ "__check_ds_pin_resource__", "__clock_pin_is_valid__", "__check_boot_clock_resource__", - "__pll_clock_pin_is_valid__" + "__pll_clock_pin_is_valid__", + "__check_input_data_width_parameter__", + "__check_output_data_width_parameter__", + "__check_data_rate_parameter__", + "__check_dpa_mode_parameter__", + "__check_clock_phase_parameter__", + "__check_pll_parameter__" ], "__pin_is_valid__" : { "__module__" : ["I_BUF", "O_BUF", "O_BUFT"], "__equation__" : [ - "pin_result = '__location0__' in g_all_pins" + "validation_result = '__location0__' in g_all_pins" ] }, "__check_pin_resource__" : { @@ -464,47 +306,47 @@ "temp = '__primitive_flags__'.split(',')", "bidir = 'INOUT' in temp", "value = 1 if 'I_BUF' in temp else 2", - "pin_result = '__location0__' not in g_pin_resources or ((g_pin_resources['__location0__'] & value) == 0)", + "validation_result = '__location0__' not in g_pin_resources or ((g_pin_resources['__location0__'] & value) == 0)", "exist = 0 if '__location0__' not in g_pin_resources else g_pin_resources['__location0__']", - "g_pin_resources['__location0__' if pin_result else ''] = exist | (value if bidir else 3)", + "g_pin_resources['__location0__' if validation_result else ''] = exist | (value if bidir else 3)", "g_pin_resources.pop('', None)" ] }, "__ds_pin_is_valid__" : { "__module__" : ["I_BUF_DS", "O_BUF_DS", "O_BUFT_DS"], "__equation__" : [ - "pin_result = '__location0__' in g_all_pins", - "pin_result = pin_result and '__location1__' in g_all_pins" + "validation_result = '__location0__' in g_all_pins", + "validation_result = validation_result and '__location1__' in g_all_pins" ] }, "__pin_is_differential__" : { "__module__" : ["I_BUF_DS", "O_BUF_DS", "O_BUFT_DS"], "__equation__" : [ "import re", - "pin_result = '__location0__' in g_all_pins", - "pin_result = pin_result and '__location1__' in g_all_pins", + "validation_result = '__location0__' in g_all_pins", + "validation_result = validation_result and '__location1__' in g_all_pins", "m0 = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', '__location0__')", "m1 = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', '__location1__')", - "pin_result = pin_result and m0 != None", - "pin_result = pin_result and m1 != None", - "pin_result = pin_result and len(m0.groups()) == 6", - "pin_result = pin_result and len(m1.groups()) == 6", - "pin_result = pin_result and m0.group(1) == m1.group(1)", - "pin_result = pin_result and m0.group(2) == m1.group(2)", - "pin_result = pin_result and m0.group(3) == m1.group(3)", - "pin_result = pin_result and m0.group(4) != m1.group(4)", - "pin_result = pin_result and m0.group(5) == m1.group(5)", - "pin_result = pin_result and m0.group(6) != m1.group(6)" + "validation_result = validation_result and m0 != None", + "validation_result = validation_result and m1 != None", + "validation_result = validation_result and len(m0.groups()) == 6", + "validation_result = validation_result and len(m1.groups()) == 6", + "validation_result = validation_result and m0.group(1) == m1.group(1)", + "validation_result = validation_result and m0.group(2) == m1.group(2)", + "validation_result = validation_result and m0.group(3) == m1.group(3)", + "validation_result = validation_result and m0.group(4) != m1.group(4)", + "validation_result = validation_result and m0.group(5) == m1.group(5)", + "validation_result = validation_result and m0.group(6) != m1.group(6)" ] }, "__check_ds_pin_resource__" : { "__module__" : ["I_BUF_DS", "O_BUF_DS", "O_BUFT_DS"], "__equation__" : [ - "pin_result = '__location0__' not in g_pin_resources", - "pin_result = pin_result and '__location1__' not in g_pin_resources", - "g_pin_resources['__location0__' if pin_result else ''] = 3", + "validation_result = '__location0__' not in g_pin_resources", + "validation_result = validation_result and '__location1__' not in g_pin_resources", + "g_pin_resources['__location0__' if validation_result else ''] = 3", "g_pin_resources.pop('', None)", - "g_pin_resources['__location1__' if pin_result else ''] = 3", + "g_pin_resources['__location1__' if validation_result else ''] = 3", "g_pin_resources.pop('', None)" ] }, @@ -512,79 +354,56 @@ "__module__" : ["CLK_BUF"], "__equation__" : [ "temp = '__primitive_flags__'.split(',')", - "pin_result = '__location0__' in g_all_clock_pins or 'PIN_CLOCK_CORE_ONLY' in temp" + "validation_result = '__location0__' in g_all_clock_pins or 'PIN_CLOCK_CORE_ONLY' in temp" ] }, "__check_boot_clock_resource__" : { "__module__" : ["BOOT_CLOCK"], "__equation__" : [ - "pin_result = g_boot_clock_resources < MAX_BOOT_CLOCK_RESOURCE", - "g_boot_clock_resources += (1 if pin_result else 0)" + "validation_result = g_boot_clock_resources < MAX_BOOT_CLOCK_RESOURCE", + "g_boot_clock_resources += (1 if validation_result else 0)" ] }, "__pll_clock_pin_is_valid__" : { "__module__" : ["PLL"], "pre_primitive" : "CLK_BUF", "__equation__" : [ - "pin_result = '__location0__' in g_all_pll_clock_pins" + "validation_result = '__location0__' in g_all_pll_clock_pins" ] - } - }, - "__secondary_validation__" : { - "__seqeunce__" : [ - "__check_fabric_clock_resource__", - "__check_data_width_parameter__", - "__check_data_rate_parameter__", - "__check_dpa_mode_parameter__", - "__check_clock_phase_parameter__", - "__check_pll_parameter__", - "__update_fabric_clock_resource__" - ], - "__check_fabric_clock_resource__" : { - "__module__" : ["CLK_BUF", "PLL"], - "__connectivity__" : ["O", "CLK_OUT", "CLK_OUT_DIV2", "CLK_OUT_DIV3", "CLK_OUT_DIV4"], + }, + "__check_input_data_width_parameter__" : { + "__module__" : ["I_SERDES"], + "__parameter__" : ["WIDTH"], "__equation__" : [ - "pin_result = (__connectivity_count__ + g_fabric_clock_resources) <= MAX_FABRIC_CLOCK_RESOURCE" + "validation_result = validate_data_width_parameter('__location__', int('WIDTH'), g_input_gearbox_width)" ] }, - "__check_data_width_parameter__" : { - "__module__" : ["I_SERDES", "O_SERDES"], + "__check_output_data_width_parameter__" : { + "__module__" : ["O_SERDES"], "__parameter__" : ["WIDTH"], "__equation__" : [ - "import re", - "assert '__location__' in g_all_pins", - "m = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', '__location__')", - "assert m != None", - "pn = 'P' if m.group(6) == 'N' else 'N'", - "index = int(m.group(4)) & ~1", - "index += (1 if pn == 'N' else 0)", - "peer_location = 'H%s_%s%s_%d_%s%s' % (m.group(1), m.group(2), m.group(3), index, m.group(5), pn)", - "width = int('WIDTH')", - "pin_result = width >= 3 and width <= (10 if m.group(6) == 'P' else 5)", - "pin_result = pin_result and ((peer_location not in g_gearbox_width) or (g_gearbox_width[peer_location] <= 5 and width <=5))", - "g_gearbox_width['__location__' if pin_result else ''] = width", - "g_gearbox_width.pop('', None)" + "validation_result = validate_data_width_parameter('__location__', int('WIDTH'), g_output_gearbox_width)" ] }, "__check_data_rate_parameter__" : { "__module__" : ["I_SERDES", "O_SERDES", "O_SERDES_CLK"], "__parameter__" : ["DATA_RATE"], "__equation__" : [ - "pin_result = 'DATA_RATE' in ['SDR', 'DDR']" + "validation_result = 'DATA_RATE' in ['SDR', 'DDR']" ] }, "__check_dpa_mode_parameter__" : { "__module__" : ["I_SERDES"], "__parameter__" : ["DPA_MODE"], "__equation__" : [ - "pin_result = 'DPA_MODE' in ['NONE', 'DPA', 'CDR']" + "validation_result = 'DPA_MODE' in ['NONE', 'DPA', 'CDR']" ] }, "__check_clock_phase_parameter__" : { "__module__" : ["O_SERDES_CLK"], "__parameter__" : ["CLOCK_PHASE"], "__equation__" : [ - "pin_result = 'CLOCK_PHASE' in ['0', '90', '180', '270']" + "validation_result = 'CLOCK_PHASE' in ['0', '90', '180', '270']" ] }, "__check_pll_parameter__" : { @@ -596,72 +415,21 @@ "post_div = int('PLL_POST_DIV', 0)", "post_div1 = post_div >> 4", "post_div2 = post_div & 0xF", - "pin_result = (div >= 1 and div <= 63)", - "pin_result = pin_result and (mult >= 16 and mult <= 640)", - "pin_result = pin_result and (post_div1 >= 1 and post_div1 <= 7)", - "pin_result = pin_result and (post_div2 >= 1 and post_div2 <= 7)", - "pin_result = pin_result and (post_div1 >= post_div2)" - ] - }, - "__check_pll_clock_pin_resource__" : { - "__module__" : ["PLL"], - "__equation__" : [ - "pin_result = '__location0__' not in g_pll_resources", - "pin_result = pin_result and len(g_pll_resources) < MAX_PLL_RESOURCE", - "g_pll_resources.append('__location0__' if pin_result else '')", - "g_pll_resources = [pin for pin in g_pll_resources if pin != '']" - ] - }, - "__update_fabric_clock_resource__" : { - "__module__" : ["CLK_BUF", "PLL"], - "__connectivity__" : ["O", "CLK_OUT", "CLK_OUT_DIV2", "CLK_OUT_DIV3", "CLK_OUT_DIV4"], - "__equation__" : [ - "assert ((__connectivity_count__ + g_fabric_clock_resources) <= MAX_FABRIC_CLOCK_RESOURCE)", - "g_fabric_clock_resources += __connectivity_count__" + "validation_result = (div >= 1 and div <= 63)", + "validation_result = validation_result and (mult >= 16 and mult <= 640)", + "validation_result = validation_result and (post_div1 >= 1 and post_div1 <= 7)", + "validation_result = validation_result and (post_div2 >= 1 and post_div2 <= 7)", + "validation_result = validation_result and (post_div1 >= post_div2)" ] } }, "__define__" : { - "parse_location" : { - "__args__" : ["__type__", "__bank__"], - "__equation__" : [ - "import re", - "assert '__location__' in g_all_pins", - "m = re.search(r'H(P|R?)_(\\d?)(|_CC?)_(\\d+?)_(\\d\\d?)(P|N?)', '__location__')", - "__type__ = 'HP' if m.group(1) == 'P' else 'HV'", - "__bank__ = '0' if m.group(2) in ['1', '3'] else '1'" - ] - }, "parse_o_serdes_clk_phase_parameter" : { "__args__" : ["__clock_phase__"], "__equation__" : [ "__clock_phase__ = 'TX_phase_%d' % __argCLOCK_PHASE__" ] }, - "parse_pll_parameter" : { - "__args__" : ["__refdiv__", "__fbdiv__", "__postdiv1__", "__postdiv2__"], - "__equation__" : [ - "__refdiv__ = str(int('__argDIV__', 0))", - "__fbdiv__ = str(int('__argMULT__', 0))", - "__postdiv1__ = str((int('__argPOST_DIV__', 0) >> 4) & 0x7)", - "__postdiv2__ = str((int('__argPOST_DIV__', 0)) & 0x7)" - ] - }, - "parse_pll_root_mux" : { - "__args__" : ["__pll_root_mux_sel__"], - "__equation__" : [ - "__pll_root_mux_sel__ = 32 + (int('__pll_resource__') * 4) + __argIndex__", - "__pll_root_mux_sel__ = '%d' % __pll_root_mux_sel__" - ] - }, - "parse_fabric_clock_buffer_root_mux" : { - "__args__" : ["__fclk_buf_root_mux_sel__"], - "__equation__" : [ - "fabric_clock_buffer_slot = int('__arg1__')", - "__fclk_buf_root_mux_sel__ = (40 + fabric_clock_buffer_slot - 4) if (fabric_clock_buffer_slot >= 4) else (44 + fabric_clock_buffer_slot)", - "__fclk_buf_root_mux_sel__ = '%d' % __fclk_buf_root_mux_sel__" - ] - }, "parse_serdes_width" : { "__args__" : ["__peer_is_on__"], "__equation__" : [ diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_22x4_routing.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_22x4_routing.py new file mode 100644 index 00000000..d36a6c3e --- /dev/null +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_22x4_routing.py @@ -0,0 +1,80 @@ +from routing_library import function_library +load_model("routing_library/gemini_compact_22x4_gbox_hp_40x1.py") + +# Block +create_block(name="Virgo", top=True) + +# Ports +for type_bank in [["P", 1]] : + for i in range(40) : + if i in [16, 17, 36, 37]: + continue + location = function_library.get_location(type_bank[0], type_bank[1], i) + add_port(name=location, dir=DIR_IN) +add_port(name="fabric_clk", dir=DIR_OUT, bit=16) +add_port(name="fclk_buf", dir=DIR_IN, bit=8) + +# Instances +add_instance(name="hp_40x1", block="gbox_hp_40x1") + +# Connections +# pin --> hp/hvl/hvr pin +for type_bank in [["P", 1, 0]] : + instance = "hp_40x1" + bank = type_bank[2] + bank_pin_name = "bank%d_rx_in" % bank + for i in range(40) : + if i in [16, 17, 36, 37]: + continue + top_location = function_library.get_location(type_bank[0], type_bank[1], i) + add_connection(source=top_location, destinations=["%s->%s[%d]" % (instance, bank_pin_name, i)]) +# hp instance fabric clk --> fabric clk +for i in range(16) : + add_connection(source="hp_40x1->fabric_clk[%d]" % i, destinations=["fabric_clk[%d]" % i]) +# fclk buf --> instance fclk buf +for i in range(8) : + add_connection(source="fclk_buf[%d]" % i, destinations=["hp_40x1->fclk_buf[%d]" % i]) + +# Mapping to TCL model +for type_bank in [["P", 1, 0]] : + # Pin location + instance = "hp_40x1" + bank = type_bank[2] + for i in range(40) : + if i in [16, 17, 36, 37]: + continue + # Gearbox mapping - hp_40x1.bank0_hpio.gearbox_P[0] ==> u_GBOX_HP_40X2.u_HP_GBOX_BK0_A_18 or HP_1_0_0P + python_name = "%s.bank%d_hpio.%s" % (instance, bank, function_library.get_gbox_top_name(i)) + tcl_name = function_library.get_location(type_bank[0], type_bank[1], i) + add_tcl_map(python_name, tcl_name) +# Top Level Type +add_tcl_map("hp_40x1", "u_GBOX_HP_40X2") +for type_bank in [[0, "A"], [0, "B"], [1, "A"], [1, "B"]] : + # FCLK + for bit in ["vco_clk_sel", "rx_fclkio_sel", "rxclk_phase_sel"]: + python_name = "bank%d_fclk_mux_%s->%s" % (type_bank[0], type_bank[1], bit) + tcl_name = "u_gbox_fclk_mux_all->cfg_%s_%s_%d" % (bit, type_bank[1], type_bank[0]) + add_tcl_map(python_name, tcl_name) +for bank in [0, 1] : + # Root Bank + add_tcl_map("bank%d_root_bank_clkmux" % bank, "u_gbox_root_bank_clkmux_%d" % bank) +# PLL REFMUX +add_tcl_map("pll_refmux[0]", "u_gbox_pll_refmux_0") +add_tcl_map("pll_refmux[1]", "u_gbox_pll_refmux_1") +# PLL +add_tcl_map("pll[0]", "u_gbox_PLLTS16FFCFRACF_0") +add_tcl_map("pll[1]", "u_gbox_PLLTS16FFCFRACF_1") +for slot in range(16): + python_name = "->root_mux_sel[%d]" % slot + tcl_name = ".u_gbox_clkmux_52x1_left_%d->ROOT_MUX_SEL" % slot + add_tcl_map(python_name, tcl_name) + +# Diagram mapping +# The graphviz module that we use to draw diagram have difficulty supporting ":" +# Suppose we have to display [39:0] +mapped_name = "hp_bank0_pin[39:0]" +for i in range(40) : + if i in [16, 17, 36, 37]: + continue + location = function_library.get_location("P", 1, i) + add_diagram_map(name=location, mapped_name=mapped_name) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_62x44_routing.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_62x44_routing.py new file mode 100644 index 00000000..ac7a1574 --- /dev/null +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_62x44_routing.py @@ -0,0 +1,110 @@ +from routing_library import function_library +load_model("routing_library/gbox_hp_40x2.py") +load_model("routing_library/gbox_hv_40x2.py") + +# Block +create_block(name="Virgo", top=True) + +# Ports +for type_bank in [["P", 1], ["P", 2], ["R", 1], ["R", 2], ["R", 3], ["R", 5]] : + for i in range(40) : + location = function_library.get_location(type_bank[0], type_bank[1], i) + add_port(name=location, dir=DIR_IN) +add_port(name="fabric_clk", dir=DIR_OUT, bit=16) +add_port(name="fclk_buf", dir=DIR_IN, bit=8) + +# Instances +add_instance(name="hp_40x2", block="gbox_hp_40x2") +add_instance(name="hvl_40x2", block="gbox_hv_40x2") +add_instance(name="hvr_40x2", block="gbox_hv_40x2") + +# Connections +# pin --> hp/hvl/hvr pin +for type_bank in [["P", 1, 0], ["P", 2, 1], ["R", 1, 0], ["R", 2, 1], ["R", 3, 0], ["R", 5, 1]] : + instance = "hp" if type_bank[0] == "P" else ("hvl" if type_bank[1] in [1, 2] else "hvr") + instance = "%s_40x2" % instance + bank = type_bank[2] + bank_pin_name = "bank%d_rx_in" % bank + for i in range(40) : + top_location = function_library.get_location(type_bank[0], type_bank[1], i) + add_connection(source=top_location, destinations=["%s->%s[%d]" % (instance, bank_pin_name, i)]) +# hvl/hvr clk pin --> hp clk pin +for bank in range(2) : + for pin in range(2) : + source_pin = "bank%d_rx_io_clk[%d]" % (bank, pin) + add_connection(source="hvl_40x2->%s" % (source_pin), destinations=["hp_40x2->hvl_bank%d_rx_io_clk[%d]" % (bank, pin)]) + add_connection(source="hvr_40x2->%s" % (source_pin), destinations=["hp_40x2->hvr_bank%d_rx_io_clk[%d]" % (bank, pin)]) +# hp pll --> hvl pll +add_connection(source="hp_40x2->pll_foutvco[0]", destinations=["hvl_40x2->pll_foutvco"]) +add_connection(source="hp_40x2->pll_fout[0]", destinations=["hvl_40x2->pll_fout"]) +# hp pll --> hvr pll +add_connection(source="hp_40x2->pll_foutvco[1]", destinations=["hvr_40x2->pll_foutvco"]) +add_connection(source="hp_40x2->pll_fout[1]", destinations=["hvr_40x2->pll_fout"]) +# hvl core clk + cdr clk --> HP core clk + cdr clk +add_connection(source="hvl_40x2->bank0_root_core_clk[0]", destinations=["hp_40x2->hvl_bank0_root_core_clk[0]"]) +add_connection(source="hvl_40x2->bank0_root_core_clk[1]", destinations=["hp_40x2->hvl_bank0_root_core_clk[1]"]) +add_connection(source="hvl_40x2->bank0_root_cdr_clk[0]", destinations=["hp_40x2->hvl_bank0_root_cdr_clk[0]"]) +add_connection(source="hvl_40x2->bank0_root_cdr_clk[1]", destinations=["hp_40x2->hvl_bank0_root_cdr_clk[1]"]) +add_connection(source="hvl_40x2->bank1_root_core_clk[0]", destinations=["hp_40x2->hvl_bank1_root_core_clk[0]"]) +add_connection(source="hvl_40x2->bank1_root_core_clk[1]", destinations=["hp_40x2->hvl_bank1_root_core_clk[1]"]) +add_connection(source="hvl_40x2->bank1_root_cdr_clk[0]", destinations=["hp_40x2->hvl_bank1_root_cdr_clk[0]"]) +add_connection(source="hvl_40x2->bank1_root_cdr_clk[1]", destinations=["hp_40x2->hvl_bank1_root_cdr_clk[1]"]) +# hvr core clk + cdr clk --> HP core clk + cdr clk +add_connection(source="hvr_40x2->bank0_root_core_clk[0]", destinations=["hp_40x2->hvr_bank0_root_core_clk[0]"]) +add_connection(source="hvr_40x2->bank0_root_core_clk[1]", destinations=["hp_40x2->hvr_bank0_root_core_clk[1]"]) +add_connection(source="hvr_40x2->bank0_root_cdr_clk[0]", destinations=["hp_40x2->hvr_bank0_root_cdr_clk[0]"]) +add_connection(source="hvr_40x2->bank0_root_cdr_clk[1]", destinations=["hp_40x2->hvr_bank0_root_cdr_clk[1]"]) +add_connection(source="hvr_40x2->bank1_root_core_clk[0]", destinations=["hp_40x2->hvr_bank1_root_core_clk[0]"]) +add_connection(source="hvr_40x2->bank1_root_core_clk[1]", destinations=["hp_40x2->hvr_bank1_root_core_clk[1]"]) +add_connection(source="hvr_40x2->bank1_root_cdr_clk[0]", destinations=["hp_40x2->hvr_bank1_root_cdr_clk[0]"]) +add_connection(source="hvr_40x2->bank1_root_cdr_clk[1]", destinations=["hp_40x2->hvr_bank1_root_cdr_clk[1]"]) +# hp instance fabric clk --> fabric clk +for i in range(16) : + add_connection(source="hp_40x2->fabric_clk[%d]" % i, destinations=["fabric_clk[%d]" % i]) +# fclk buf --> instance fclk buf +for i in range(8) : + add_connection(source="fclk_buf[%d]" % i, destinations=["hp_40x2->fclk_buf[%d]" % i]) + +# Mapping to TCL model +for type_bank in [["P", 1, 0], ["P", 2, 1], ["R", 1, 0], ["R", 2, 1], ["R", 3, 0], ["R", 5, 1]] : + # Pin location + instance = "hp" if type_bank[0] == "P" else ("hvl" if type_bank[1] in [1, 2] else "hvr") + instance = "%s_40x2" % instance + bank = type_bank[2] + for i in range(40) : + # Gearbox mapping - hp_40x2.bank0_hpio.gearbox_P[0] ==> u_GBOX_HP_40X2.u_HP_GBOX_BK0_A_18 or HP_1_0_0P + python_name = "%s.bank%d_hpio.%s" % (instance, bank, function_library.get_gbox_top_name(i)) + tcl_name = function_library.get_location(type_bank[0], type_bank[1], i) + add_tcl_map(python_name, tcl_name) +for type_bank in [["p", "P", ""], ["vl", "V", "_VL"], ["vr", "V", "_VR"]] : + # Top Level Type + add_tcl_map("h%s_40x2" % type_bank[0], "u_GBOX_H%s_40X2%s" % (type_bank[1], type_bank[2])) +for type_bank in [[0, "A"], [0, "B"], [1, "A"], [1, "B"]] : + # FCLK + for bit in ["vco_clk_sel", "rx_fclkio_sel", "rxclk_phase_sel"]: + python_name = "bank%d_fclk_mux_%s->%s" % (type_bank[0], type_bank[1], bit) + tcl_name = "u_gbox_fclk_mux_all->cfg_%s_%s_%d" % (bit, type_bank[1], type_bank[0]) + add_tcl_map(python_name, tcl_name) +for bank in [0, 1] : + # Root Bank + add_tcl_map("bank%d_root_bank_clkmux" % bank, "u_gbox_root_bank_clkmux_%d" % bank) +# PLL REFMUX +add_tcl_map("pll_refmux[0]", "u_gbox_pll_refmux_0") +add_tcl_map("pll_refmux[1]", "u_gbox_pll_refmux_1") +# PLL +add_tcl_map("pll[0]", "u_gbox_PLLTS16FFCFRACF_0") +add_tcl_map("pll[1]", "u_gbox_PLLTS16FFCFRACF_1") +for slot in range(16): + python_name = "->root_mux_sel[%d]" % slot + tcl_name = ".u_gbox_clkmux_52x1_left_%d->ROOT_MUX_SEL" % slot + add_tcl_map(python_name, tcl_name) + +# Diagram mapping +for type_bank in [["P", 1, 0], ["P", 2, 1], ["R", 1, 0], ["R", 2, 1], ["R", 3, 0], ["R", 5, 1]] : + instance = "hp" if type_bank[0] == "P" else ("hvl" if type_bank[1] in [1, 2] else "hvr") + # The graphviz module that we use to draw diagram have difficulty supporting ":" + # Suppose we have to display [39:0] + mapped_name = "%s_bank%s_pin[39:0]" % (instance, type_bank[2]) + for i in range(40) : + location = function_library.get_location(type_bank[0], type_bank[1], i) + add_diagram_map(name=location, mapped_name=mapped_name) \ No newline at end of file diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/__init__.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/function_library.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/function_library.py new file mode 100644 index 00000000..2a4e7ad0 --- /dev/null +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/function_library.py @@ -0,0 +1,18 @@ +import os +import sys +current_dir = os.path.dirname(os.path.abspath(__file__)) +if current_dir not in sys.path: + sys.path.insert(0, current_dir) + +def get_gbox_top_name(index) : + + pn = "P" if (index % 2) == 0 else "N" + return "gearbox_%s[%d]" % (pn, index//2) + +def get_location(type, bank, index) : + + pn = "P" if (index % 2) == 0 else "N" + if index in [18, 19, 38, 39] : + return "H%s_%d_CC_%d_%d%s" % (type, bank, index, index//2, pn) + else : + return "H%s_%d_%d_%d%s" % (type, bank, index, index//2, pn) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_fclk_mux.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_fclk_mux.py new file mode 100644 index 00000000..9cdd1b32 --- /dev/null +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_fclk_mux.py @@ -0,0 +1,17 @@ +# Block +create_block(name="gbox_fclk_mux") + +# Ports +add_port(name="vco_clk", dir=DIR_IN, bit=2) +add_port(name="rx_io_clk", dir=DIR_IN, bit=2) +add_port(name="fast_clk", dir=DIR_OUT, bit=1) + +# Config +add_config_mux(out="fast_clk", + selection={0b000 : "vco_clk[0]", + 0b001 : "vco_clk[1]", + 0b100 : "rx_io_clk[0]", + 0b110 : "rx_io_clk[1]"}, + bits=[{"vco_clk_sel" : 1}, + {"rx_fclkio_sel" : 1}, + {"rxclk_phase_sel":1}]) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hp_40x2.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hp_40x2.py new file mode 100644 index 00000000..d298f1c4 --- /dev/null +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hp_40x2.py @@ -0,0 +1,148 @@ +import function_library +load_model("gbox_osc.py") +load_model("gbox_pll_refmux.py") +load_model("gbox_pll.py") +load_model("gbox_fclk_mux.py") +load_model("gbox_hpio.py") +load_model("gbox_top.py") +load_model("gbox_root_bank_clkmux.py") + +# Block +create_block(name="gbox_hp_40x2") + +# Ports +add_port(name="bank0_rx_in", dir=DIR_IN, bit=40) +add_port(name="bank1_rx_in", dir=DIR_IN, bit=40) +add_port(name="hvl_bank0_rx_io_clk", dir=DIR_IN, bit=2) +add_port(name="hvl_bank1_rx_io_clk", dir=DIR_IN, bit=2) +add_port(name="hvr_bank0_rx_io_clk", dir=DIR_IN, bit=2) +add_port(name="hvr_bank1_rx_io_clk", dir=DIR_IN, bit=2) +add_port(name="hvl_bank0_root_core_clk", dir=DIR_IN, bit=2) +add_port(name="hvl_bank0_root_cdr_clk", dir=DIR_IN, bit=2) +add_port(name="hvl_bank1_root_core_clk", dir=DIR_IN, bit=2) +add_port(name="hvl_bank1_root_cdr_clk", dir=DIR_IN, bit=2) +add_port(name="hvr_bank0_root_core_clk", dir=DIR_IN, bit=2) +add_port(name="hvr_bank0_root_cdr_clk", dir=DIR_IN, bit=2) +add_port(name="hvr_bank1_root_core_clk", dir=DIR_IN, bit=2) +add_port(name="hvr_bank1_root_cdr_clk", dir=DIR_IN, bit=2) +add_port(name="fclk_buf", dir=DIR_IN, bit=8) +add_port(name="pll_foutvco", dir=DIR_OUT, bit=2) +add_port(name="pll_fout", dir=DIR_OUT, bit=2) +add_port(name="fabric_clk", dir=DIR_OUT, bit=16) + +# Instances +add_instance(name="rc_osc_50mhz", block="rc_osc_50mhz") +add_instance(name="pll_refmux[0]", block="gbox_pll_refmux") +add_instance(name="pll_refmux[1]", block="gbox_pll_refmux") +add_instance(name="pll[0]", block="PLL") +add_instance(name="pll[1]", block="PLL") +add_instance(name="bank0_fclk_mux_A", block="gbox_fclk_mux") +add_instance(name="bank1_fclk_mux_A", block="gbox_fclk_mux") +add_instance(name="bank0_fclk_mux_B", block="gbox_fclk_mux") +add_instance(name="bank1_fclk_mux_B", block="gbox_fclk_mux") +add_instance(name="bank0_hpio", block="gbox_hpio") +add_instance(name="bank1_hpio", block="gbox_hpio") +add_instance(name="bank0_root_bank_clkmux", block="gbox_root_bank_clkmux") +add_instance(name="bank1_root_bank_clkmux", block="gbox_root_bank_clkmux") + +# Connections +# osc + pin --> refmux +add_connection(source="rc_osc_50mhz->o_osc", destinations=["pll_refmux[0]->rosc_clk", "pll_refmux[1]->rosc_clk"]) +add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["pll_refmux[0]->bank0_hp_rx_io_clk[0]", "pll_refmux[1]->bank0_hp_rx_io_clk[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["pll_refmux[0]->bank0_hp_rx_io_clk[1]", "pll_refmux[1]->bank0_hp_rx_io_clk[1]"]) +add_connection(source="bank1_hpio->rx_io_clk[0]", destinations=["pll_refmux[0]->bank1_hp_rx_io_clk[0]", "pll_refmux[1]->bank1_hp_rx_io_clk[0]"]) +add_connection(source="bank1_hpio->rx_io_clk[1]", destinations=["pll_refmux[0]->bank1_hp_rx_io_clk[1]", "pll_refmux[1]->bank1_hp_rx_io_clk[1]"]) +add_connection(source="hvl_bank0_rx_io_clk[0]", destinations=["pll_refmux[0]->bank0_hv_rx_io_clk[0]"]) +add_connection(source="hvl_bank0_rx_io_clk[1]", destinations=["pll_refmux[0]->bank0_hv_rx_io_clk[1]"]) +add_connection(source="hvl_bank1_rx_io_clk[0]", destinations=["pll_refmux[0]->bank1_hv_rx_io_clk[0]"]) +add_connection(source="hvl_bank1_rx_io_clk[1]", destinations=["pll_refmux[0]->bank1_hv_rx_io_clk[1]"]) +add_connection(source="hvr_bank0_rx_io_clk[0]", destinations=["pll_refmux[1]->bank0_hv_rx_io_clk[0]"]) +add_connection(source="hvr_bank0_rx_io_clk[1]", destinations=["pll_refmux[1]->bank0_hv_rx_io_clk[1]"]) +add_connection(source="hvr_bank1_rx_io_clk[0]", destinations=["pll_refmux[1]->bank1_hv_rx_io_clk[0]"]) +add_connection(source="hvr_bank1_rx_io_clk[1]", destinations=["pll_refmux[1]->bank1_hv_rx_io_clk[1]"]) +# refmux --> pll +add_connection(source="pll_refmux[0]->out", destinations=["pll[0]->fref"]) +add_connection(source="pll_refmux[1]->out", destinations=["pll[1]->fref"]) +# pll + pin --> fclk mux +add_connection(source="pll[0]->foutvco", destinations=["bank0_fclk_mux_A->vco_clk[0]", "pll_foutvco[0]"]) +add_connection(source="pll[0]->fout[0]", destinations=["bank0_fclk_mux_A->vco_clk[1]", "pll_fout[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_A->rx_io_clk[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_A->rx_io_clk[1]"]) +add_connection(source="pll[1]->foutvco", destinations=["bank1_fclk_mux_A->vco_clk[0]", "pll_foutvco[1]"]) +add_connection(source="pll[1]->fout[0]", destinations=["bank1_fclk_mux_A->vco_clk[1]", "pll_fout[1]"]) +add_connection(source="bank1_hpio->rx_io_clk[0]", destinations=["bank1_fclk_mux_A->rx_io_clk[0]"]) +add_connection(source="bank1_hpio->rx_io_clk[1]", destinations=["bank1_fclk_mux_A->rx_io_clk[1]"]) +add_connection(source="pll[0]->foutvco", destinations=["bank0_fclk_mux_B->vco_clk[0]"]) +add_connection(source="pll[0]->fout[0]", destinations=["bank0_fclk_mux_B->vco_clk[1]"]) +add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_B->rx_io_clk[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_B->rx_io_clk[1]"]) +add_connection(source="pll[1]->foutvco", destinations=["bank1_fclk_mux_B->vco_clk[0]"]) +add_connection(source="pll[1]->fout[0]", destinations=["bank1_fclk_mux_B->vco_clk[1]"]) +add_connection(source="bank1_hpio->rx_io_clk[0]", destinations=["bank1_fclk_mux_B->rx_io_clk[0]"]) +add_connection(source="bank1_hpio->rx_io_clk[1]", destinations=["bank1_fclk_mux_B->rx_io_clk[1]"]) +# fclk mux --> gearbox fast clk +add_connection(source="bank0_fclk_mux_A->fast_clk", destinations=["bank0_hpio->fast_clk_A"]) +add_connection(source="bank0_fclk_mux_B->fast_clk", destinations=["bank0_hpio->fast_clk_B"]) +add_connection(source="bank1_fclk_mux_A->fast_clk", destinations=["bank1_hpio->fast_clk_A"]) +add_connection(source="bank1_fclk_mux_B->fast_clk", destinations=["bank1_hpio->fast_clk_B"]) +# pin --> gearbox pin +# gearbox core clk + cdr clk --> root bank core clk + cdr clk +for bank in range(2) : + gbox_hpio_name = "bank%d_hpio" % bank + gbox_root_bank_clkmux_name = "bank%d_root_bank_clkmux" % bank + for i in range(40) : + gbox_pin = "bank%d_rx_in[%d]" % (bank, i) + add_connection(source=gbox_pin, destinations=["%s->rx_in[%d]" % (gbox_hpio_name, i)]) + add_connection(source="%s->core_clk[%d]" % (gbox_hpio_name, i), destinations=["%s->core_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) + add_connection(source="%s->cdr_clk[%d]" % (gbox_hpio_name, i), destinations=["%s->cdr_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) + +# Config +# Root selection for fabric_clk +root_mux_selection = { + 0 : "bank0_root_bank_clkmux->core_clk[0]", + 1 : "bank0_root_bank_clkmux->core_clk[1]", + 2 : "bank1_root_bank_clkmux->core_clk[0]", + 3 : "bank1_root_bank_clkmux->core_clk[1]", + 4 : "bank0_root_bank_clkmux->cdr_clk[0]", + 5 : "bank0_root_bank_clkmux->cdr_clk[1]", + 6 : "bank1_root_bank_clkmux->cdr_clk[0]", + 7 : "bank1_root_bank_clkmux->cdr_clk[1]", + 8 : "hvl_bank0_root_core_clk[0]", + 9 : "hvl_bank0_root_core_clk[1]", + 10 : "hvl_bank1_root_core_clk[0]", + 11 : "hvl_bank1_root_core_clk[1]", + 12 : "hvl_bank0_root_cdr_clk[0]", + 13 : "hvl_bank0_root_cdr_clk[1]", + 14 : "hvl_bank1_root_cdr_clk[0]", + 15 : "hvl_bank1_root_cdr_clk[1]", + 16 : "hvr_bank0_root_core_clk[0]", + 17 : "hvr_bank0_root_core_clk[1]", + 18 : "hvr_bank1_root_core_clk[0]", + 19 : "hvr_bank1_root_core_clk[1]", + 20 : "hvr_bank0_root_cdr_clk[0]", + 21 : "hvr_bank0_root_cdr_clk[1]", + 22 : "hvr_bank1_root_cdr_clk[0]", + 24 : "hvr_bank1_root_cdr_clk[1]", + 32 : "pll[0]->fout[0]", + 33 : "pll[0]->fout[1]", + 34 : "pll[0]->fout[2]", + 35 : "pll[0]->fout[3]", + 36 : "pll[1]->fout[0]", + 37 : "pll[1]->fout[1]", + 38 : "pll[1]->fout[2]", + 39 : "pll[1]->fout[3]", + 40 : "fclk_buf[4]", + 41 : "fclk_buf[5]", + 42 : "fclk_buf[6]", + 43 : "fclk_buf[7]", + 44 : "fclk_buf[0]", + 45 : "fclk_buf[1]", + 46 : "fclk_buf[2]", + 47 : "fclk_buf[3]", + 48 : "rc_osc_50mhz->o_osc" +} +for i in range(16) : + add_config_mux(out="fabric_clk[%d]" % i, + selection=root_mux_selection, + bits=[{"root_mux_sel[%d]" % i : 6}]) + \ No newline at end of file diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hpio.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hpio.py new file mode 100644 index 00000000..e2719415 --- /dev/null +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hpio.py @@ -0,0 +1,33 @@ +import function_library +load_model("gbox_top.py") + +# Block +create_block(name="gbox_hpio") + +# Ports +add_port(name="fast_clk_A", dir=DIR_IN) +add_port(name="fast_clk_B", dir=DIR_IN) +add_port(name="rx_in", dir=DIR_IN, bit=40) +add_port(name="rx_io_clk", dir=DIR_OUT, bit=2) +add_port(name="core_clk", dir=DIR_OUT, bit=40) +add_port(name="cdr_clk", dir=DIR_OUT, bit=40) +add_port(name="tx_clk", dir=DIR_OUT, bit=40) + +# Instances +for i in range(40) : + instance_name = function_library.get_gbox_top_name(i) + add_instance(name=instance_name, block="gbox_top") + +# Connections +for i in range(40) : + instance_name = function_library.get_gbox_top_name(i) + if i < 20 : + add_connection(source="fast_clk_A", destinations=["%s->fast_clk" % instance_name]) + else : + add_connection(source="fast_clk_B", destinations=["%s->fast_clk" % instance_name]) + add_connection(source="rx_in[%d]" % i, destinations=["%s->rx_in" % instance_name]) + add_connection(source="%s->core_clk" % instance_name, destinations=["core_clk[%d]" % i]) + add_connection(source="%s->cdr_clk" % instance_name, destinations=["cdr_clk[%d]" % i]) + add_connection(source="%s->tx_clk" % instance_name, destinations=["tx_clk[%d]" % i]) +add_connection(source="rx_in[18]", destinations=["rx_io_clk[0]"]) +add_connection(source="rx_in[38]", destinations=["rx_io_clk[1]"]) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hv_40x2.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hv_40x2.py new file mode 100644 index 00000000..256484b6 --- /dev/null +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hv_40x2.py @@ -0,0 +1,74 @@ +import function_library +load_model("gbox_hpio.py") + +# Block +create_block(name="gbox_hv_40x2") + +# Ports +add_port(name="bank0_rx_in", dir=DIR_IN, bit=40) +add_port(name="bank1_rx_in", dir=DIR_IN, bit=40) +add_port(name="pll_fout", dir=DIR_IN, bit=1) +add_port(name="pll_foutvco", dir=DIR_IN, bit=1) +add_port(name="bank0_rx_io_clk", dir=DIR_OUT, bit=2) +add_port(name="bank1_rx_io_clk", dir=DIR_OUT, bit=2) +add_port(name="bank0_root_core_clk", dir=DIR_OUT, bit=2) +add_port(name="bank1_root_core_clk", dir=DIR_OUT, bit=2) +add_port(name="bank0_root_cdr_clk", dir=DIR_OUT, bit=2) +add_port(name="bank1_root_cdr_clk", dir=DIR_OUT, bit=2) + +# Instances +add_instance(name="bank0_fclk_mux_A", block="gbox_fclk_mux") +add_instance(name="bank1_fclk_mux_A", block="gbox_fclk_mux") +add_instance(name="bank0_fclk_mux_B", block="gbox_fclk_mux") +add_instance(name="bank1_fclk_mux_B", block="gbox_fclk_mux") +add_instance(name="bank0_hpio", block="gbox_hpio") +add_instance(name="bank1_hpio", block="gbox_hpio") +add_instance(name="bank0_root_bank_clkmux", block="gbox_root_bank_clkmux") +add_instance(name="bank1_root_bank_clkmux", block="gbox_root_bank_clkmux") + +# Connections +# pll + pin --> fclk mux +add_connection(source="pll_foutvco", destinations=["bank0_fclk_mux_A->vco_clk[0]"]) +add_connection(source="pll_fout", destinations=["bank0_fclk_mux_A->vco_clk[1]"]) +add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_A->rx_io_clk[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_A->rx_io_clk[1]"]) +add_connection(source="pll_foutvco", destinations=["bank1_fclk_mux_A->vco_clk[0]"]) +add_connection(source="pll_fout", destinations=["bank1_fclk_mux_A->vco_clk[1]"]) +add_connection(source="bank1_hpio->rx_io_clk[0]", destinations=["bank1_fclk_mux_A->rx_io_clk[0]"]) +add_connection(source="bank1_hpio->rx_io_clk[1]", destinations=["bank1_fclk_mux_A->rx_io_clk[1]"]) +add_connection(source="pll_foutvco", destinations=["bank0_fclk_mux_B->vco_clk[0]"]) +add_connection(source="pll_fout", destinations=["bank0_fclk_mux_B->vco_clk[1]"]) +add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_B->rx_io_clk[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_B->rx_io_clk[1]"]) +add_connection(source="pll_foutvco", destinations=["bank1_fclk_mux_B->vco_clk[0]"]) +add_connection(source="pll_fout", destinations=["bank1_fclk_mux_B->vco_clk[1]"]) +add_connection(source="bank1_hpio->rx_io_clk[0]", destinations=["bank1_fclk_mux_B->rx_io_clk[0]"]) +add_connection(source="bank1_hpio->rx_io_clk[1]", destinations=["bank1_fclk_mux_B->rx_io_clk[1]"]) +# fclk mux --> gearbox fast clk +add_connection(source="bank0_fclk_mux_A->fast_clk", destinations=["bank0_hpio->fast_clk_A"]) +add_connection(source="bank0_fclk_mux_B->fast_clk", destinations=["bank0_hpio->fast_clk_B"]) +add_connection(source="bank1_fclk_mux_A->fast_clk", destinations=["bank1_hpio->fast_clk_A"]) +add_connection(source="bank1_fclk_mux_B->fast_clk", destinations=["bank1_hpio->fast_clk_B"]) +# pin --> gearbox pin +# gearbox core clk + cdr clk --> root bank core clk + cdr clk +for bank in range(2) : + gbox_hpio_name = "bank%d_hpio" % bank + gbox_root_bank_clkmux_name = "bank%d_root_bank_clkmux" % bank + for i in range(40) : + source = "bank%d_rx_in[%d]" % (bank, i) + add_connection(source=source, destinations=["%s->rx_in[%d]" % (gbox_hpio_name, i)]) + source = "%s->core_clk[%d]" % (gbox_hpio_name, i) + add_connection(source=source, destinations=["%s->core_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) + source = "%s->cdr_clk[%d]" % (gbox_hpio_name, i) + add_connection(source=source, destinations=["%s->cdr_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) + for i in range(2) : + add_connection(source="bank%s_hpio->rx_io_clk[%d]" % (bank, i), destinations=["bank%d_rx_io_clk[%d]" % (bank, i)]) +# root bank core clk + cdr clk --> pin core clk + cdr clk +add_connection(source="bank0_root_bank_clkmux->core_clk[0]", destinations=["bank0_root_core_clk[0]"]) +add_connection(source="bank0_root_bank_clkmux->core_clk[1]", destinations=["bank0_root_core_clk[1]"]) +add_connection(source="bank1_root_bank_clkmux->core_clk[0]", destinations=["bank1_root_core_clk[0]"]) +add_connection(source="bank1_root_bank_clkmux->core_clk[1]", destinations=["bank1_root_core_clk[1]"]) +add_connection(source="bank0_root_bank_clkmux->cdr_clk[0]", destinations=["bank0_root_cdr_clk[0]"]) +add_connection(source="bank0_root_bank_clkmux->cdr_clk[1]", destinations=["bank0_root_cdr_clk[1]"]) +add_connection(source="bank1_root_bank_clkmux->cdr_clk[0]", destinations=["bank1_root_cdr_clk[0]"]) +add_connection(source="bank1_root_bank_clkmux->cdr_clk[1]", destinations=["bank1_root_cdr_clk[1]"]) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_osc.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_osc.py new file mode 100644 index 00000000..b5ca877e --- /dev/null +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_osc.py @@ -0,0 +1,9 @@ +# Block +create_block(name="rc_osc_50mhz") + +# Ports +add_port(name="osc", dir=DIR_IN) +add_port(name="o_osc", dir=DIR_OUT) + +# Connections +add_connection(source="osc", destinations=["o_osc"]) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_pll.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_pll.py new file mode 100644 index 00000000..78be46e3 --- /dev/null +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_pll.py @@ -0,0 +1,30 @@ +# PLL +create_block(name="PLL") + +# Ports +add_port(name="fref", dir=DIR_IN) +add_port(name="fout", dir=DIR_OUT, bit=4) +add_port(name="foutvco", dir=DIR_OUT, bit=1) + +# Connections +add_connection(source="fref", destinations=["fout[0]", "fout[1]", "fout[2]", "fout[3]", "foutvco"]) + +# Add parameters +parameter_script = """ +if 'PLL_DIV' in self.parameters and 'PLL_MULT' in self.parameters and 'PLL_POST_DIV' in self.parameters and '__pll_enable__' in self.parameters: + post_div = int(self.parameters['PLL_POST_DIV'], 0) + self.defined_parameters['pll_DSKEWCALBYP'] = 'DSKEWCALBYP_0' + self.defined_parameters['pll_DSKEWCALIN'] = 0 + self.defined_parameters['pll_DSKEWCALCNT'] = 2 + self.defined_parameters['pll_DSKEWFASTCAL'] = 'DSKEWFASTCAL_0' + self.defined_parameters['pll_DSKEWCALEN'] = 'DSKEWCALEN_0' + self.defined_parameters['pll_FRAC'] = 0 + self.defined_parameters['pll_FBDIV'] = int(self.parameters['PLL_MULT'], 0) + self.defined_parameters['pll_REFDIV'] = int(self.parameters['PLL_DIV'], 0) + self.defined_parameters['pll_PLLEN'] = int(self.parameters['__pll_enable__'], 0) + self.defined_parameters['pll_POSTDIV1'] = (post_div >> 4) & 7 + self.defined_parameters['pll_POSTDIV2'] = post_div & 7 + self.defined_parameters['pll_DSMEN'] = 'DSMEN_0' + self.defined_parameters['pll_DACEN'] = 'DACEN_0' +""" +add_parameter("PLL", parameter_script) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_pll_refmux.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_pll_refmux.py new file mode 100644 index 00000000..07630b19 --- /dev/null +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_pll_refmux.py @@ -0,0 +1,56 @@ +# Block +create_block(name="gbox_pll_refmux") + +# Ports +add_port(name="rosc_clk", dir=DIR_IN) +add_port(name="bank0_hp_rx_io_clk", dir=DIR_IN, bit=2) +add_port(name="bank1_hp_rx_io_clk", dir=DIR_IN, bit=2) +add_port(name="bank0_hv_rx_io_clk", dir=DIR_IN, bit=2) +add_port(name="bank1_hv_rx_io_clk", dir=DIR_IN, bit=2) +add_port(name="out", dir=DIR_OUT) + +# Config +''' + selection | use_rosc use_hv hp_bank_rx_io_sel hp_rx_io_sel hv_bank_rx_io_sel hv_rx_io_sel + --------- | -------- ------ ----------------- ------------ ----------------- ------------ + rosc_clk | 1 x x xx xx x + bank0_hp_rx_io_clk[0] | 0 0 0 x0 xx x + bank0_hp_rx_io_clk[1] | 0 0 0 x1 xx x + bank1_hp_rx_io_clk[0] | 0 0 1 0x xx x + bank1_hp_rx_io_clk[1] | 0 0 1 1x xx x + bank0_hv_rx_io_clk[0] | 0 1 x xx 00 0 + bank0_hv_rx_io_clk[1] | 0 1 x xx 00 1 + bank1_hv_rx_io_clk[0] | 0 1 x xx 01 0 + bank1_hv_rx_io_clk[1] | 0 1 x xx 01 1 +''' +selection = { + 0b10000000 : "rosc_clk", + 0b00000000 : "bank0_hp_rx_io_clk[0]", + 0b00001000 : "bank0_hp_rx_io_clk[1]", + 0b00100000 : "bank1_hp_rx_io_clk[0]", + 0b00110000 : "bank1_hp_rx_io_clk[1]", + 0b01000000 : "bank0_hv_rx_io_clk[0]", + 0b01000001 : "bank0_hv_rx_io_clk[1]", + 0b01000010 : "bank1_hv_rx_io_clk[0]", + 0b01000011 : "bank1_hv_rx_io_clk[1]" +} +bits = [ + {"cfg_pllref_hv_rx_io_sel" : 1}, + {"cfg_pllref_hv_bank_rx_io_sel" : 2}, + {"cfg_pllref_hp_rx_io_sel" : 2}, + {"cfg_pllref_hp_bank_rx_io_sel" : 1}, + {"cfg_pllref_use_hv" : 1}, + {"cfg_pllref_use_rosc" : 1} +] +add_config_mux(out="out", + selection=selection, + bits=bits) + +# Add parameters +parameter_script = """ +if 'DIVIDE_CLK_IN_BY_2' in self.parameters and self.parameters['DIVIDE_CLK_IN_BY_2'] in ['TRUE', 'ON', '1']: + self.defined_parameters['cfg_pllref_use_div'] = 1 +else: + self.defined_parameters['cfg_pllref_use_div'] = 0 +""" +add_parameter("PLL", parameter_script) \ No newline at end of file diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_root_bank_clkmux.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_root_bank_clkmux.py new file mode 100644 index 00000000..e5795189 --- /dev/null +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_root_bank_clkmux.py @@ -0,0 +1,31 @@ +# Block +create_block(name="gbox_root_bank_clkmux") + +# Ports +add_port(name="core_clk_in", dir=DIR_IN, bit=40) +add_port(name="cdr_clk_in", dir=DIR_IN, bit=40) +add_port(name="core_clk", dir=DIR_OUT, bit=2) +add_port(name="cdr_clk", dir=DIR_OUT, bit=2) + +# Config +core_clk_selection_A = {} +core_clk_selection_B = {} +cdr_clk_selection_A = {} +cdr_clk_selection_B = {} +for i in range(20) : + core_clk_selection_A[i] = "core_clk_in[%d]" % i + core_clk_selection_B[i] = "core_clk_in[%d]" % (20 + i) + cdr_clk_selection_A[i] = "cdr_clk_in[%d]" % i + cdr_clk_selection_B[i] = "cdr_clk_in[%d]" % (20 + i) +add_config_mux(out="core_clk[0]", + selection=core_clk_selection_A, + bits=[{"CORE_CLK_ROOT_SEL_A" : 5}]) +add_config_mux(out="core_clk[1]", + selection=core_clk_selection_B, + bits=[{"CORE_CLK_ROOT_SEL_B" : 5}]) +add_config_mux(out="cdr_clk[0]", + selection=cdr_clk_selection_A, + bits=[{"CDR_CLK_ROOT_SEL_A" : 5}]) +add_config_mux(out="cdr_clk[1]", + selection=cdr_clk_selection_B, + bits=[{"CDR_CLK_ROOT_SEL_B" : 5}]) \ No newline at end of file diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_top.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_top.py new file mode 100644 index 00000000..43944cc4 --- /dev/null +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_top.py @@ -0,0 +1,25 @@ +# Block +create_block(name="gbox_top") + +# Ports +add_port(name="fast_clk", dir=DIR_IN) +add_port(name="rx_in", dir=DIR_IN) +add_port(name="core_clk", dir=DIR_OUT) +add_port(name="cdr_clk", dir=DIR_OUT) +add_port(name="tx_clk", dir=DIR_OUT) + +# Connections +add_connection(source="fast_clk", destinations=["cdr_clk"]) + +# Config +add_config_mux(out="core_clk", + selection={ + 0 : "fast_clk", + 1 : "rx_in" + }, + bits=[{"RX_CLOCK_IO" : 1}]) +add_config_mux(out="tx_clk", + selection={ + 1 : "fast_clk" + }, + bits=[{"TX_CLOCK_IO" : 1}]) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gemini_compact_22x4_gbox_hp_40x1.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gemini_compact_22x4_gbox_hp_40x1.py new file mode 100644 index 00000000..ef7e4dbd --- /dev/null +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gemini_compact_22x4_gbox_hp_40x1.py @@ -0,0 +1,90 @@ +import function_library +load_model("gbox_osc.py") +load_model("gbox_pll_refmux.py") +load_model("gbox_pll.py") +load_model("gbox_fclk_mux.py") +load_model("gbox_hpio.py") +load_model("gbox_top.py") +load_model("gbox_root_bank_clkmux.py") + +# Block +create_block(name="gbox_hp_40x1") + +# Ports +add_port(name="bank0_rx_in", dir=DIR_IN, bit=40) +add_port(name="fclk_buf", dir=DIR_IN, bit=8) +add_port(name="fabric_clk", dir=DIR_OUT, bit=16) + +# Instances +add_instance(name="rc_osc_50mhz", block="rc_osc_50mhz") +add_instance(name="pll_refmux[0]", block="gbox_pll_refmux") +add_instance(name="pll_refmux[1]", block="gbox_pll_refmux") +add_instance(name="pll[0]", block="PLL") +add_instance(name="pll[1]", block="PLL") +add_instance(name="bank0_fclk_mux_A", block="gbox_fclk_mux") +add_instance(name="bank0_fclk_mux_B", block="gbox_fclk_mux") +add_instance(name="bank0_hpio", block="gbox_hpio") +add_instance(name="bank0_root_bank_clkmux", block="gbox_root_bank_clkmux") + +# Connections +# osc + pin --> refmux +add_connection(source="rc_osc_50mhz->o_osc", destinations=["pll_refmux[0]->rosc_clk", "pll_refmux[1]->rosc_clk"]) +add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["pll_refmux[0]->bank0_hp_rx_io_clk[0]", "pll_refmux[1]->bank0_hp_rx_io_clk[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["pll_refmux[0]->bank0_hp_rx_io_clk[1]", "pll_refmux[1]->bank0_hp_rx_io_clk[1]"]) +# refmux --> pll +add_connection(source="pll_refmux[0]->out", destinations=["pll[0]->fref"]) +add_connection(source="pll_refmux[1]->out", destinations=["pll[1]->fref"]) +# pll + pin --> fclk mux +add_connection(source="pll[0]->foutvco", destinations=["bank0_fclk_mux_A->vco_clk[0]"]) +add_connection(source="pll[0]->fout[0]", destinations=["bank0_fclk_mux_A->vco_clk[1]"]) +add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_A->rx_io_clk[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_A->rx_io_clk[1]"]) +add_connection(source="pll[0]->foutvco", destinations=["bank0_fclk_mux_B->vco_clk[0]"]) +add_connection(source="pll[0]->fout[0]", destinations=["bank0_fclk_mux_B->vco_clk[1]"]) +add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_B->rx_io_clk[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_B->rx_io_clk[1]"]) +# fclk mux --> gearbox fast clk +add_connection(source="bank0_fclk_mux_A->fast_clk", destinations=["bank0_hpio->fast_clk_A"]) +add_connection(source="bank0_fclk_mux_B->fast_clk", destinations=["bank0_hpio->fast_clk_B"]) +# pin --> gearbox pin +# gearbox core clk + cdr clk --> root bank core clk + cdr clk +gbox_hpio_name = "bank0_hpio" +gbox_root_bank_clkmux_name = "bank0_root_bank_clkmux" +for i in range(40) : + if i in [16, 17, 36, 37]: + continue + gbox_pin = "bank0_rx_in[%d]" % (i) + add_connection(source=gbox_pin, destinations=["%s->rx_in[%d]" % (gbox_hpio_name, i)]) + add_connection(source="%s->core_clk[%d]" % (gbox_hpio_name, i), destinations=["%s->core_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) + add_connection(source="%s->cdr_clk[%d]" % (gbox_hpio_name, i), destinations=["%s->cdr_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) + +# Config +# Root selection for fabric_clk +root_mux_selection = { + 0 : "bank0_root_bank_clkmux->core_clk[0]", + 1 : "bank0_root_bank_clkmux->core_clk[1]", + 4 : "bank0_root_bank_clkmux->cdr_clk[0]", + 5 : "bank0_root_bank_clkmux->cdr_clk[1]", + 32 : "pll[0]->fout[0]", + 33 : "pll[0]->fout[1]", + 34 : "pll[0]->fout[2]", + 35 : "pll[0]->fout[3]", + 36 : "pll[1]->fout[0]", + 37 : "pll[1]->fout[1]", + 38 : "pll[1]->fout[2]", + 39 : "pll[1]->fout[3]", + 40 : "fclk_buf[4]", + 41 : "fclk_buf[5]", + 42 : "fclk_buf[6]", + 43 : "fclk_buf[7]", + 44 : "fclk_buf[0]", + 45 : "fclk_buf[1]", + 46 : "fclk_buf[2]", + 47 : "fclk_buf[3]", + 48 : "rc_osc_50mhz->o_osc" +} +for i in range(16) : + add_config_mux(out="fabric_clk[%d]" % i, + selection=root_mux_selection, + bits=[{"root_mux_sel[%d]" % i : 6}]) + \ No newline at end of file diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/routing_configurator.py b/src/ConfigurationRaptor/CFGDeviceDatabase/routing_configurator.py new file mode 100644 index 00000000..135f24af --- /dev/null +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/routing_configurator.py @@ -0,0 +1,1692 @@ +import argparse +import os +import shlex +import sys +import copy +import re +import json +import threading +import ast +import time + +DIR_IN = 0 +DIR_OUT = 1 +MAX_THREAD = 4 + +def match_string(keyword, strings): + + match = -1 + if keyword.find("partial:") == 0: + keyword = keyword[8:] + assert len(keyword) + for i, s in enumerate(strings): + if s.find(keyword) != -1: + match = i + break + elif keyword.find("RE:") == 0: + keyword = keyword[3:] + keyword = keyword.replace("[", "\[") + keyword = keyword.replace("]", "\]") + expected_match_count = 0 + for replacement in ["(*s*)", "(*d*)"]: + index = keyword.find(replacement) + while index != -1: + expected_match_count += 1 + index = keyword.find(replacement, index + len(replacement)) + if replacement == "(*s*)": + keyword = keyword.replace(replacement, "([-+]?[a-zA-Z0-9_.\[\]]+)") + else: + keyword = keyword.replace(replacement, "([-+]?[0-9]+)") + assert len(keyword) + for i, s in enumerate(strings): + m = re.search(r"%s\n" % keyword, "%s\n" % s) + if m != None and len(m.groups()) == expected_match_count: + match = i + break + else: + if keyword in strings: + match = strings.index(keyword) + else: + match = -1 + return match + +class PORT: + + def __init__(self, bit): + + self.bit = bit + +class CONFIG_BIT: + + def __init__(self, bit): + + self.bit = bit + +class CONFIG_RESULT: + + def __init__(self, feature, instance, bit_name, bit_value): + + self.feature = feature + self.instance = instance + self.bit_name = bit_name + self.bit_value = bit_value + +class DIAGRAM: + + def __init__(self, block_name, instance_name, type, instance): + + self.block_name = block_name + self.instance_name = instance_name + self.type = type + self.instance = instance + self.mapped_edge = {} + assert self.type in ["IN_PORTS", "OUT_PORTS", "BLOCK", "MUX", "NODE"] + self.iport_names = "" + self.oport_names = "" + self.mux_names = "" + if type == "NODE": + self.mapped_edge[instance_name] = instance_name + + def add_ports(self, dir, ports, format): + + assert dir in [DIR_IN, DIR_OUT] + if dir == DIR_IN: + assert self.iport_names == "" + else: + assert self.oport_names == "" + + def update_mapped_edge(edge, bit, name): + + for i in range(bit): + if self.type == "BLOCK": + original_name = "%s->%s%s" % ( + self.instance_name, + name, + "" if bit == 1 else ("[%d]" % i), + ) + else: + original_name = "%s%s" % (name, "" if bit == 1 else ("[%d]" % i)) + assert original_name not in self.mapped_edge + self.mapped_edge[original_name] = edge + + # Create port name + names = ["**INPUT Ports**" if dir == DIR_IN else "**OUTPUT Ports**"] + mapped_names = [] + for name, p in ports.items(): + edge = "edge%d" % len(self.mapped_edge) + if (format & 2) == 0 and name in self.instance.diagram_map: + if self.instance.diagram_map[name] not in mapped_names: + mapped_names.append(self.instance.diagram_map[name]) + names.append("<%s>%s" % (edge, self.instance.diagram_map[name])) + update_mapped_edge(edge, p.bit, name) + else: + if p.bit == 1: + names.append("<%s>%s" % (edge, name)) + else: + names.append("<%s>%s[%d:%d]" % (edge, name, p.bit - 1, 0)) + update_mapped_edge(edge, p.bit, name) + + # Serialize + if dir == DIR_IN: + self.iport_names = "|".join(names) + else: + self.oport_names = "|".join(names) + + def add_mux_input(self, inputs, values, bits): + + assert len(inputs) + assert len(inputs) == len(values) + assert self.iport_names == "" + assert self.mux_names == "" + total_size = 0 + bit_infos = [self.block_name] + msize = 0 + for (b, size) in bits: + total_size += size + bit_infos.append("%s - %dbit(s)" % (b, size)) + assert total_size + dsize = len(str((1 << total_size) - 1)) + names = ["**INPUT Ports**"] + for i, v in zip(inputs, values): + edge = "edge%d" % len(self.mapped_edge) + names.append( + "<%s>%s\\n0b%0*d (%*d)" + % (edge, i, total_size, int(bin(v)[2:]), dsize, v) + ) + names[-1] = names[-1].replace("->", "\\n") + assert i not in self.mapped_edge + self.mapped_edge[i] = edge + self.iport_names = "|".join(names) + self.mux_names = "|".join(bit_infos) + + def add_mux_output(self, output): + + assert len(output) + assert self.oport_names == "" + names = ["**OUTPUT Ports**"] + edge = "edge%d" % len(self.mapped_edge) + names.append("<%s>%s" % (edge, output)) + names[-1] = names[-1].replace("->", "\\n") + assert output not in self.mapped_edge + self.mapped_edge[output] = edge + self.oport_names = "|".join(names) + + def get_node_label(self): + + if self.type == "IN_PORTS": + return "{%s}" % (self.iport_names) + elif self.type == "OUT_PORTS": + return "{%s}" % (self.oport_names) + elif self.type == "BLOCK": + return "{%s} | {%s|%s} | {%s}" % ( + self.iport_names, + self.block_name, + self.instance_name, + self.oport_names, + ) + elif self.type == "MUX": + return "{%s} | {%s} | {%s}" % ( + self.iport_names, + self.mux_names, + self.oport_names, + ) + else: + assert self.type == "NODE" + return self.instance_name + +class BLOCK: + + def __init__(self, name, top): + + self.name = name + self.top = top + self.instantiated = False + self.instance_name = None + self.parent = None + self.id = None + self.in_ports = {} + self.out_ports = {} + self.nodes = [] + self.instances = {} + self.drives = {} + self.sinks = {} + self.mux_drives = {} + self.mux_sinks = {} + self.mux_values = {} + self.mux_bits = {} + self.config_bits = {} + self.mux_drive_selection = {} + self.parameter_primitive_name = None + self.parameter_script = None + self.diagram_map = {} + + def fullname(self, do_not_include_if_not_exist=False): + + if self.parent == None: + assert self.top + if do_not_include_if_not_exist: + return "" + else: + return self.instance_name + else: + assert not self.top + parent_name = self.parent.fullname(do_not_include_if_not_exist) + if len(parent_name): + return "%s.%s" % (parent_name, self.instance_name) + else: + return self.instance_name + + def update_child(self, instances, ignore=False): + + for name, instance in self.instances.items(): + assert instance.instance_name == None or ignore + assert instance.parent == None or ignore + assert instance.id == None or ignore + instance.instance_name = name + instance.parent = self + instance.id = len(instances) + instances.append(instance) + instance.update_child(instances, ignore) + + def check_naming(self, name, support_indexing, feature): + + m = re.search(r"([-+]?[a-zA-Z0-9_]+)", name) + if m != None and len(m.groups()) == 1 and m.group(1) == name: + pass + elif support_indexing: + # Retry + valid = False + index = name.find("[") + if index != -1 and name[-1] == "]": + regular_name = name[:index] + if len(regular_name): + m = re.search(r"([-+]?[a-zA-Z0-9_]+)", regular_name) + if ( + m != None + and len(m.groups()) == 1 + and m.group(1) == regular_name + ): + sub_name = name[index + 1 : -1] + if len(sub_name): + m = re.search(r"([-+]?[0-9]+)", sub_name) + if ( + m != None + and len(m.groups()) == 1 + and m.group(1) == sub_name + ): + valid = True + name = regular_name + if not valid: + raise Exception( + "Invalid %s name %s which should consist character in re format {a-zA-Z0-9_} or {a-zA-Z0-9_}[{0-9}]" + % (feature, name) + ) + else: + raise Exception( + "Invalid %s name %s which should consist character in re format {a-zA-Z0-9_}" + % (feature, name) + ) + return name + + def add_port(self, name, dir, bit): + + assert dir in [DIR_IN, DIR_OUT] + assert bit > 0 + self.check_naming(name, False, "port") + assert name not in self.in_ports, ( + "Port %s had been defined as input port" % name + ) + assert name not in self.out_ports, ( + "Port %s had been defined as ouput port" % name + ) + for n in self.nodes: + n = self.check_naming(n, True, "node") + assert name != n, "Port %s had been defined as node" % name + if dir == DIR_IN: + self.in_ports[name] = PORT(bit) + else: + self.out_ports[name] = PORT(bit) + + def add_node(self, name): + + no_index_name = self.check_naming(name, True, "node") + assert name not in self.in_ports, ( + "Node %s had been defined as input port" % name + ) + assert name not in self.out_ports, ( + "Node %s had been defined as output port" % name + ) + assert name not in self.nodes, "Node %s had been defined as node" % name + assert no_index_name not in self.in_ports, ( + "Node %s had been defined as input port" % no_index_name + ) + assert no_index_name not in self.out_ports, ( + "Node %s had been defined as output port" % no_index_name + ) + assert no_index_name not in self.nodes, ( + "Node %s had been defined as node" % no_index_name + ) + self.nodes.append(name) + + def add_instance(self, name, block): + + self.check_naming(name, True, "instance") + assert name not in self.instances, "Block %s already has instance named %s" % ( + self.name, + name, + ) + assert isinstance(block, BLOCK) + self.instances[name] = block + + def check_connection_bit(self, is_input, connection): + + if connection in self.nodes: + return connection + else: + assert is_input in [None, False, True] + m = re.search(r"([-+]?[a-zA-Z0-9_]+)\[([-+]?[0-9]+)\]", connection) + if m != None: + assert len(m.groups()) == 2 + name = m.group(1) + bit = int(m.group(2)) + else: + name = connection + bit = None + if is_input == None: + assert ( + name in self.in_ports or name in self.out_ports + ), "Block %s does not have input/output port named %s" % ( + self.name, + name, + ) + if name in self.in_ports: + bits = self.in_ports[name] + else: + bits = self.out_ports[name] + elif is_input == True: + assert ( + name in self.in_ports + ), "Block %s does not have input port named %s" % (self.name, name) + bits = self.in_ports[name] + else: + assert is_input == False + assert ( + name in self.out_ports + ), "Block %s does not have output port named %s" % (self.name, name) + bits = self.out_ports[name] + assert (bit == None and bits.bit == 1) or ( + bit != None and bit < bits.bit + ), ( + "Block %s port %s is %d bit(s), connection should be made bit by bit or within valid bit range" + % (self.name, name, bits.bit) + ) + if bits.bit == 1: + # Always return no [] + return name + else: + return connection + + def get_connection_info(self, connection): + + instance = None + port = None + assert isinstance(connection, str) + cons = connection.split("->") + assert len(cons) in [1, 2] + if len(cons) == 1: + # This is a pin + port = cons[0] + instance = self + else: + # This is instance + if cons[0] == self.fullname(): + instance = self + elif cons[0] in self.instances: + instance = self.instances[cons[0]] + elif self.parent != None and cons[0] == self.parent.fullname(): + instance = self.parent + else: + for inst in self.instances: + if self.instances[inst].fullname() == cons[0]: + instance = self.instances[inst] + break + if instance == None and self.parent != None: + for inst in self.parent.instances: + if self.parent.instances[inst].fullname() == cons[0]: + instance = self.parent.instances[inst] + break + assert ( + instance != None + ), "Block %s (%s) does not have instance named %s" % ( + self.name, + self.fullname(), + cons[0], + ) + port = cons[1] + assert isinstance(instance, BLOCK) + port = instance.check_connection_bit(None, port) + return [instance, port] + + def check_connection(self, connection, is_drive): + + assert isinstance(connection, str) + cons = connection.split("->") + assert len(cons) in [1, 2] + if len(cons) == 1: + # This is a pin + return self.check_connection_bit(is_drive, cons[0]) + else: + # This is instance + # Node is not accessible by instance + assert cons[0] in self.instances, ( + "Instance %s does not exist to make connection" % cons[0] + ) + instance = self.instances[cons[0]] + assert ( + cons[1] not in instance.nodes + ), "Block %s (any block) should not access instance %s node %s" % ( + self.name, + cons[0], + cons[1], + ) + return "%s->%s" % ( + cons[0], + instance.check_connection_bit(not is_drive, cons[1]), + ) + + def add_connection(self, source, destinations): + + # Pin [input] -> Instance [input] + # Instance [output] -> Instance [input] + # Instance [output] -> Pin [output] + # Pin [input] -> Pin [output] + for i in range(len(destinations)): + assert ( + destinations[i] not in self.nodes + ), "Block %s (any block) does not support add connection to a node %s" % ( + self.name, + destinations[i], + ) + destinations[i] = self.check_connection(destinations[i], False) + assert ( + source not in self.nodes + ), "Block %s (any block) does not support add connection from a node %s" % ( + self.name, + source, + ) + source = self.check_connection(source, True) + # assert source not in self.drives, "Block %s driving source %s connection had been made" % (self.name, source) + if source in self.drives: + for dest in destinations: + assert ( + dest not in self.drives[source] + ), "Block %s driving source %s already drive %s" % ( + self.name, + source, + dest, + ) + self.drives[source].append(dest) + else: + self.drives[source] = destinations + for dest in destinations: + dest = self.check_connection(dest, False) + assert ( + dest not in self.sinks + ), "Block %s sink destination %s connection had been made" % ( + self.name, + dest, + ) + self.sinks[dest] = source + + def add_config_mux(self, out, selection, bits): + + out = self.check_connection(out, False) + assert ( + out not in self.mux_sinks + ), "Block %s mux sink destination %s connection had been made" % ( + self.name, + out, + ) + assert out not in self.mux_values, "Block %s mux value %s had been defined" % ( + self.name, + out, + ) + assert out not in self.mux_bits, "Block %s mux bits %s had been defined" % ( + self.name, + out, + ) + self.mux_sinks[out] = [] + self.mux_values[out] = [] + self.mux_bits[out] = [] + total_bits = 0 + bit_names = [] + for bit in bits: + for b, size in bit.items(): + self.check_naming(b, True, "mux-bit") + assert ( + b not in bit_names + ), "Block %s configuration bits %s had been defined" % (self.name, b) + if b in self.config_bits: + assert self.config_bits[b].bit == size, ( + "Block %s configuration bits %s has conflict bit size definition (%d defined vs %d defining)" + % (self.name, b, self.config_bits[b].bit, size) + ) + else: + self.config_bits[b] = CONFIG_BIT(size) + bit_names.append(b) + total_bits += size + for value, drive in selection.items(): + drive = self.check_connection(drive, True) + assert value < (1 << total_bits), ( + "Block %s configuration mux %s selection value 0x%X (%d) (drive: %s) is out of range (bit-size=%d)" + % (self.name, out, value, value, drive, total_bits) + ) + if drive not in self.mux_drives: + self.mux_drives[drive] = [] + assert out not in self.mux_drives[drive] + self.mux_drives[drive].append(out) + self.mux_sinks[out].append(drive) + self.mux_values[out].append(value) + # bits + values = {} + index = 0 + for bit in bits: + for b, size in bit.items(): + v = (value >> index) & ((1 << size) - 1) + index += size + values[b] = str(v) + drive_path = "%s | %s" % (drive, out) + assert drive_path not in self.mux_drive_selection + self.mux_drive_selection[drive_path] = values + for bit in bits: + for b, size in bit.items(): + self.mux_bits[out].append([b, size]) + + def add_parameter(self, primitive_name, script): + + assert self.parameter_primitive_name == None + assert self.parameter_script == None + self.parameter_primitive_name = primitive_name + self.parameter_script = script + + def add_diagram_map(self, name, mapped_name): + + assert name not in self.diagram_map + assert name in self.in_ports or name in self.out_ports + self.diagram_map[name] = mapped_name + + def recursive_query( + self, + start_node, + paths, + configs, + end_node=None, + filters=None, + path=[], + config=[None], + ): + + instance_node = "%s->%s" % (self.instance_name, start_node) + full_instance_node = "%s->%s" % (self.fullname(), start_node) + path = list(path) + path.append(full_instance_node) + if filters != None: + assert isinstance(filters, list) + for f in filters: + assert isinstance(f, str) and len(f) > 0 + if match_string(f, [full_instance_node]) != -1: + return + if end_node != None and match_string(end_node, [full_instance_node]) != -1: + paths.append(path) + configs.append(config) + return + found = False + if start_node in self.drives: + for dest in self.drives[start_node]: + (instance, port) = self.get_connection_info(dest) + instance.recursive_query( + port, paths, configs, end_node, filters, path, config + [None] + ) + found = True + if self.parent != None and instance_node in self.parent.drives: + for dest in self.parent.drives[instance_node]: + (instance, port) = self.parent.get_connection_info(dest) + instance.recursive_query( + port, paths, configs, end_node, filters, path, config + [None] + ) + found = True + if start_node in self.mux_drives: + for dest in self.mux_drives[start_node]: + (instance, port) = self.get_connection_info(dest) + c = "%s | %s" % (start_node, dest) + assert c in self.mux_drive_selection + c = self.mux_drive_selection[c] + instance.recursive_query( + port, + paths, + configs, + end_node, + filters, + path, + config + [{self.fullname(True): c}], + ) + found = True + if self.parent != None and instance_node in self.parent.mux_drives: + for dest in self.parent.mux_drives[instance_node]: + (instance, port) = self.parent.get_connection_info(dest) + c = "%s | %s" % (instance_node, dest) + assert c in self.parent.mux_drive_selection + c = self.parent.mux_drive_selection[c] + instance.recursive_query( + port, + paths, + configs, + end_node, + filters, + path, + config + [{self.parent.fullname(True): c}], + ) + found = True + if not found: + paths.append(path) + configs.append(config) + + def query(self, start_node, end_node=None, filters=None): + + assert end_node == None or (isinstance(end_node, str) and len(end_node) > 0) + assert filters == None or (isinstance(filters, list)) + potential_paths = [] + potential_configs = [] + paths = [] + configs = [] + start_node = self.check_connection(start_node, True) + self.recursive_query( + start_node, potential_paths, potential_configs, end_node, filters + ) + assert isinstance(potential_paths, list) + assert isinstance(potential_configs, list) + assert len(potential_paths) == len(potential_configs) + for p, c in zip(potential_paths, potential_configs): + assert isinstance(p, list) + assert isinstance(c, list) + assert len(p) + assert len(p) == len(c) + if end_node != None: + if match_string(end_node, [p[-1]]) != -1: + paths.append(p) + configs.append(c) + else: + paths.append(p) + configs.append(c) + return [paths, configs] + + def graph_diagram(self, graph, format): + def get_edge(diagrams, is_self_connection, instance_name, connection, is_mux): + + if is_self_connection: + name = connection + else: + name = "%s->%s" % (instance_name, connection) + edge = "" + for diagram in diagrams: + if ( + is_mux + and diagram.type == "MUX" + and diagram.instance_name == instance_name + ) or (not is_mux and diagram.type != "MUX"): + if name in diagram.mapped_edge: + if diagram.type == "NODE": + edge = "%s" % (diagram.instance_name) + else: + edge = "%s:%s" % ( + diagram.instance_name, + diagram.mapped_edge[name], + ) + break + return edge + + def make_mux_connection( + connections, connection_pairs, diagrams, mux_name, connection, m2b + ): + + mux_edge = get_edge(diagrams, True, mux_name, connection, True) + assert len(mux_edge) + inst, connection = self.get_connection_info(connection) + edge = get_edge( + diagrams, inst == self, inst.instance_name, connection, False + ) + if len(edge): + if m2b: + connection_name = "%s + %s" % (mux_edge, edge) + else: + connection_name = "%s + %s" % (edge, mux_edge) + if connection_name not in connections: + connection_pairs.append( + [mux_edge, edge] if m2b else [edge, mux_edge] + ) + connections.append(connection_name) + + graph.rankdir = "LR" + diagrams = [] + # We must have input ports + assert len(self.in_ports) + diagrams.append(DIAGRAM(self.name, "@in", "IN_PORTS", self)) + diagrams[-1].add_ports(DIR_IN, self.in_ports, format) + + # Add instance as "BLOCK" + for inst_name, instance in self.instances.items(): + diagrams.append(DIAGRAM(instance.name, inst_name, "BLOCK", instance)) + diagrams[-1].add_ports(DIR_IN, instance.in_ports, format) + diagrams[-1].add_ports(DIR_OUT, instance.out_ports, format) + + mux_names = {} + for out, selections in self.mux_sinks.items(): + assert len(selections) == len(self.mux_values[out]) + name = "@mux%d" % len(mux_names) + mux_names[out] = name + diagrams.append(DIAGRAM("MUX", name, "MUX", self)) + diagrams[-1].add_mux_input( + selections, self.mux_values[out], self.mux_bits[out] + ) + diagrams[-1].add_mux_output(out) + + for node in self.nodes: + diagrams.append(DIAGRAM("NODE", node, "NODE", self)) + + # Not neccessary for output ports + diagrams.append(DIAGRAM(self.name, "@out", "OUT_PORTS", self)) + diagrams[-1].add_ports(DIR_OUT, self.out_ports, format) + + for diagram in diagrams: + graph.node( + diagram.instance_name, + label=diagram.get_node_label(), + shape=("circle" if diagram.type == "NODE" else "record"), + ) + + connections = [] + connection_pairs = [] + for src, destinations in self.drives.items(): + inst, connection = self.get_connection_info(src) + src_edge = get_edge( + diagrams, inst == self, inst.instance_name, connection, False + ) + if len(src_edge): + for destination in destinations: + inst, connection = self.get_connection_info(destination) + dest_edge = get_edge( + diagrams, inst == self, inst.instance_name, connection, False + ) + if len(dest_edge): + connection_name = "%s + %s" % (src_edge, dest_edge) + if connection_name not in connections: + connection_pairs.append([src_edge, dest_edge]) + connections.append(connection_name) + + for out, selections in self.mux_sinks.items(): + make_mux_connection( + connections, connection_pairs, diagrams, mux_names[out], out, True + ) + if len(selections) <= 10 or (format & 4) != 0: + for s in selections: + make_mux_connection( + connections, + connection_pairs, + diagrams, + mux_names[out], + s, + False, + ) + + # RGB + if len(connection_pairs): + color_groups = (len(connection_pairs) + 7) // 8 + color_step = int(((255 + color_groups - 1) / color_groups) * 0.8) + lcolor = 0 + ccolor = color_step + for i, pair in enumerate(connection_pairs): + rcolor = ccolor if (i & 1) else lcolor + gcolor = ccolor if (i & 2) else lcolor + bcolor = ccolor if (i & 4) else lcolor + color = "#%02X%02X%02X" % (rcolor, gcolor, bcolor) + graph.edge(pair[0], pair[1], color=color) + if (i % 8) == 7: + lcolor += color_step // 2 + ccolor += color_step + +class PathFinderThread(threading.Thread): + + def __init__(self, configurator, routing): + + threading.Thread.__init__(self) + self.configurator = configurator + self.routing = routing + self.paths = None + self.configs = None + self.status = False + self.error_msg = "" + self.parameters = {} + self.defined_parameters = {} + + def run(self): + + assert "feature" in self.routing + assert "source" in self.routing + assert "destinations" in self.routing + assert "flags" in self.routing + assert "filters" in self.routing + assert "parameters" in self.routing + feature = self.routing["feature"] + src = self.routing["source"] + dests = self.routing["destinations"] + flags = self.routing["flags"] + filters = self.routing["filters"] + parameters = self.routing["parameters"] + assert self.configurator.top_block != None + assert isinstance(src, str) and len(src) + assert isinstance(dests, list) + assert isinstance(flags, list) + assert isinstance(filters, list) + (src, self.error_msg) = self.configurator.validate_node(src, True) + if len(self.error_msg) == 0: + self.routing["source"] = src + for i, dest in enumerate(dests): + assert isinstance(dest, str) and len(dest) + (dests[i], self.error_msg) = self.configurator.validate_node( + dests[i], False + ) + if len(self.error_msg): + break + if len(self.error_msg) == 0: + self.routing["destinations"] = dests + for flag in flags: + assert isinstance(flag, str) and len(flag) + for filter in filters: + assert isinstance(filter, str) and len(filter) + assert src not in dests + (self.paths, self.configs) = self.configurator.query( + src, None if len(dests) == 0 else dests[-1], filters + ) + self.apply_parameters(parameters) + self.configurator.validate_paths_and_configs(self.paths, self.configs) + ( + self.paths, + self.configs, + ) = self.configurator.confirm_intermediate_path( + self.paths, self.configs, dests[:-1] + ) + (self.paths, self.configs) = self.configurator.apply_flag( + self.paths, self.configs, flags + ) + self.status = True + + def filter(self, update_routing): + + assert self.paths != None and self.configs != None + (self.paths, self.configs) = self.configurator.filter_used_path( + self.routing["feature"], self.paths, self.configs, self.routing["msgs"] + ) + if update_routing: + self.routing["potential paths"] = self.paths + + def apply_parameters(self, parameters): + + assert isinstance(parameters, dict) + if len(parameters): + for path, config in zip(self.paths, self.configs): + applied_block = [] + for i in range(len(path)): + ps = path[i].split("->") + assert len(ps) == 2 + for inst in self.configurator.instances: + if ( + inst.fullname() == ps[0] + and inst.parameter_primitive_name != None + and inst.parameter_script != None + and inst.parameter_primitive_name in parameters + and inst.name not in applied_block + ): + self.parameters = parameters[inst.parameter_primitive_name] + self.defined_parameters = {} + exec(inst.parameter_script) + if config[i] == None: + config[i] = {} + inst_name = inst.fullname(True) + if inst_name not in config[i]: + config[i][inst_name] = {} + for p, v in self.defined_parameters.items(): + assert isinstance(p, (str, int)) and len(p) + assert isinstance(v, (str, int)) + if isinstance(v, int): + v = str(v) + assert len(v) + if p not in config[i][inst_name]: + config[i][inst_name][p] = v + else: + assert config[i][inst_name][p] == v + applied_block.append(inst.name) + break + +class CONFIGURATOR: + + def __init__(self, top_model_file, enable_tcl_map, debug_level, is_external): + + self.top_model_file = top_model_file + assert os.path.exists(self.top_model_file), ( + "Model file %s must exist" % self.top_model_file + ) + self.enable_tcl_map = enable_tcl_map + self.debug_level = debug_level + self.is_external = is_external + self.top_directory = os.path.dirname(os.path.abspath(self.top_model_file)) + if self.top_directory not in sys.path: + sys.path.insert(0, self.top_directory) + self.sub_directories = [] + self.top_model_file_md5 = None + self.models_md5 = [] + self.current_model_md5 = None + self.current_block = None + self.top_block = None + self.blocks = [] + self.instances = [] + self.config_results = {} + self.tcl_map = {} + self.print_info(1, 0, "Top model file: %s" % self.top_model_file) + self.load_model_file(self.top_model_file) + + def reset_config_results(self): + + self.config_results = {} + + def print_info(self, level, space, msg): + + if level <= self.debug_level: + + assert space >= 0 + spacing = "" + while space != 0: + spacing += " " + space -= 1 + print("INFO : %s%s" % (spacing, msg)) + + def print_warning(self, msg): + + if self.is_external: + print("WARNING: %s" % msg) + + def load_model_file(self, file): + + def load_model(file): + self.load_model_file(file) + + def create_block(name, top=False): + self.create_block(name, top) + + def add_port(name, dir, bit=1, parent=None): + self.add_port(name, dir, bit, parent) + + def add_node(name, parent=None): + self.add_node(name, parent) + + def add_instance(name, block, parent=None): + self.add_instance(name, block, parent) + + def add_connection(source, destinations, parent=None): + self.add_connection(source, destinations, parent) + + def add_config_mux(out, selection, bits, parent=None): + self.add_config_mux(out, selection, bits, parent) + + def add_parameter(primitive_name, script, parent=None): + self.add_parameter(primitive_name, script, parent) + + def add_tcl_map(name, tcl_name): + self.add_tcl_map(name, tcl_name) + + def add_diagram_map(name, mapped_name, parent=None): + self.add_diagram_map(name, mapped_name, parent) + + assert isinstance(file, str) and len(file) + if os.path.exists(file): + pass + else: + found = False + tested_file = [file] + search_directories = [self.top_directory] + self.sub_directories + for directory in search_directories: + relative_file = "%s/%s" % (directory, file) + relative_file = relative_file.replace("\\", "/") + while relative_file.find("//") != -1: + relative_file = relative_file.replace("//", "/") + if os.path.exists(relative_file): + file = relative_file + found = True + break + elif relative_file not in tested_file: + tested_file.append(relative_file) + if not found: + raise Exception("Model file(s) (%s) does not exist" % (tested_file)) + current_dir = os.path.dirname(os.path.abspath(file)) + if current_dir != self.top_directory and current_dir not in self.sub_directories: + self.sub_directories.append(current_dir) + self.print_info(1, 1, "Load model file: %s" % file) + # Original intention is to use md5 but it does not work for msys2 + # Fullpath will work too + # hashlib.sha256(open(file, "rb").read()).hexdigest() + md5 = os.path.abspath(file) + if self.top_model_file_md5 == None: + assert len(self.models_md5) == 0 + assert self.current_model_md5 == None + self.top_model_file_md5 = md5 + self.current_model_md5 = md5 + exec(open(file).read()) + self.set_top() + elif self.current_model_md5 == md5: + raise Exception("Illegal to self-load model file %s" % file) + elif md5 in self.models_md5: + self.print_warning("Model file %s had been loaded. Skip" % file) + else: + assert md5 != self.top_model_file_md5, ( + "Illegal to load top model file %s more than once" % file + ) + self.models_md5.append(md5) + backup_md5 = self.current_model_md5 + self.current_model_md5 = md5 + exec(open(file).read()) + self.current_model_md5 = backup_md5 + + def create_block(self, name, top): + + assert isinstance(name, str) and len(name) + assert isinstance(top, bool) + self.print_info(1, 2, "Create block %s (Top:%r)" % (name, top)) + assert len(name) + self.curret_block = None + for block in self.blocks: + if block.name == name: + raise Exception("Block %s had already been created" % name) + else: + assert not top or not block.top, ( + "Block %s already has been set as top module, cannot set block %s as top module" + % (block.name, name) + ) + if self.curret_block == None: + self.current_block = BLOCK(name, top) + self.blocks.append(self.current_block) + + def get_current_block(self, parent): + + assert self.current_block != None, "No block is created" + if parent != None: + self.current_block = None + for block in self.blocks: + if block.name == parent: + self.current_block = block + break + assert self.current_block == None, ( + "Specified parent %s does not exist" % parent + ) + self.print_info( + 2, 1, "Switch the current block to %s" % self.current_block.name + ) + + def set_top(self): + + assert self.top_block == None + assert len(self.instances) == 0 + for b in self.blocks: + if b.top: + assert self.top_block == None + assert not b.instantiated + self.top_block = b + assert self.top_block != None, "Not top block is set" + assert self.top_block.instance_name == None + assert self.top_block.id == None + self.top_block.instance_name = self.top_block.name + self.top_block.id = len(self.instances) + self.instances.append(self.top_block) + self.top_block.update_child(self.instances) + assert self.top_block.parent == None + assert self.top_block.fullname(True) == "" + assert self.top_block.fullname() == self.top_block.instance_name + + def copy_block(self, name): + + block = None + for b in self.blocks: + if b.name == name: + b.instantiated = True + block = copy.deepcopy(b) + break + assert block != None, "Could not find block %s" % (name) + return block + + def add_port(self, name, dir, bit, parent): + + assert isinstance(name, str) and len(name) + assert isinstance(bit, int) and bit > 0 + assert dir in [DIR_IN, DIR_OUT] + assert parent == None or (isinstance(parent, str) and len(parent)) + self.get_current_block(parent) + self.print_info(2, 3, "Add port %s" % name) + assert not self.current_block.instantiated, ( + "Block %s had instantiated, it is illegal to add more port" + % self.current_block.name + ) + self.current_block.add_port(name, dir, bit) + + def add_node(self, name, parent): + + assert isinstance(name, str) and len(name) + assert parent == None or (isinstance(parent, str) and len(parent)) + self.get_current_block(parent) + self.print_info(2, 3, "Add node %s" % name) + assert not self.current_block.instantiated, ( + "Block %s had instantiated, it is illegal to add more node" + % self.current_block.name + ) + self.current_block.add_node(name) + + def add_instance(self, name, block, parent): + + assert isinstance(name, str) and len(name) + assert isinstance(block, str) and len(block) + assert parent == None or (isinstance(parent, str) and len(parent)) + self.get_current_block(parent) + self.print_info(2, 3, "Add block %s as instance %s" % (block, name)) + assert not self.current_block.instantiated, ( + "Block %s had instantiated, it is illegal to add more instance" + % self.current_block.name + ) + block = self.copy_block(block) + assert isinstance(block, BLOCK) + self.current_block.add_instance(name, block) + + def add_connection(self, source, destinations, parent): + + assert isinstance(source, str) + assert isinstance(destinations, list) and len(destinations) > 0 + for dest in destinations: + assert isinstance(dest, str) + assert parent == None or (isinstance(parent, str) and len(parent)) + self.get_current_block(parent) + self.print_info( + 3, 2, "Add connection: source=%s, destinations=%s" % (source, destinations) + ) + self.current_block.add_connection(source, destinations) + + def add_config_mux(self, out, selection, bits, parent): + + assert isinstance(out, str) and len(out) + assert isinstance(selection, dict) and len(selection) + for value, drive in selection.items(): + assert isinstance(value, int) + assert isinstance(drive, str) + assert isinstance(bits, list) and len(bits) + for bit in bits: + assert isinstance(bit, dict) and len(bit) == 1 + for b, size in bit.items(): + assert isinstance(b, str) + assert isinstance(size, int) and size > 0 + assert parent == None or (isinstance(parent, str) and len(parent)) + self.get_current_block(parent) + self.print_info(2, 3, "Add configuration mux for %s" % (out)) + self.current_block.add_config_mux(out, selection, bits) + + def add_parameter(self, primitive_name, script, parent): + + assert isinstance(primitive_name, str) and len(primitive_name) + assert isinstance(script, str) and len(script) + assert parent == None or (isinstance(parent, str) and len(parent)) + self.get_current_block(parent) + self.print_info(2, 3, "Add parameter") + self.current_block.add_parameter(primitive_name, script) + + def add_tcl_map(self, name, tcl_name): + + assert isinstance(name, str) and len(name) + assert isinstance(tcl_name, str) and len(tcl_name) + assert name not in self.tcl_map, "Duplicated tcl map: %s" % name + self.tcl_map[name] = tcl_name + + def add_diagram_map(self, name, mapped_name, parent): + + assert isinstance(name, str) and len(name) + assert isinstance(mapped_name, str) and len(mapped_name) + assert parent == None or (isinstance(parent, str) and len(parent)) + self.get_current_block(parent) + self.current_block.add_diagram_map(name, mapped_name) + + def get_instance_and_node(self, node): + + block = None + cons = node.split("->") + if len(cons) == 1: + # No instance name, so it is top + block = self.top_block + else: + assert len(cons) == 2 + # There is instance + for instance in self.instances: + if cons[0] == instance.fullname() or cons[0] == instance.fullname(True): + block = instance + break + assert block != None, "Instance name %s does not exist" % cons[0] + node = cons[1] + return [block, node] + + def validate_node(self, node, is_source): + + assert not is_source or (node.find("partial:") != 0 and node.find("RE:") != 0) + if node.find("partial:") == 0 or node.find("RE:") == 0: + pass + else: + try: + cons = node.split("->") + assert len(cons) in [1, 2] + if len(cons) == 1: + node = "%s->%s" % ( + self.top_block.name, + self.top_block.check_connection_bit( + True if is_source else None, cons[0] + ), + ) + elif len(cons) == 2: + status = False + for instance in self.instances: + if cons[0] == instance.fullname() or cons[ + 0 + ] == instance.fullname(True): + status = True + node = "%s->%s" % (instance.fullname(), cons[1]) + break + if not status: + count = 0 + fullname = "" + for instance in self.instances: + if cons[0] == instance.instance_name: + count += 1 + fullname = instance.fullname() + if count == 1: + node = "%s->%s" % (fullname, cons[1]) + elif count == 0: + raise Exception("Instance name %s is invalid" % cons[0]) + else: + raise Exception( + "There are more than one instance with name %s, please use more detail name" + % cons[0] + ) + except AssertionError as msg: + msg = str(msg) + assert len(msg) + return [node, msg] + except Exception as error: + return [node, "Exception: %s: %s" % (type(error).__name__, str(error))] + return [node, ""] + + def query(self, node, dest, filters): + + assert self.top_block != None + assert isinstance(node, str) + assert dest == None or isinstance(dest, str) + (block, node) = self.get_instance_and_node(node) + (paths, configs) = block.query(node, dest, filters) + return [paths, configs] + + def validate_paths_and_configs(self, paths, configs): + + assert isinstance(paths, list) + assert isinstance(configs, list) + for p, c in zip(paths, configs): + assert isinstance(p, list) + assert isinstance(c, list) + assert len(p) == len(c) + + def finalize_path(self, paths, configs, routing): + + assert isinstance(paths, list) + assert isinstance(configs, list) + assert len(paths) + assert len(paths) == len(configs) + for config in configs: + if config != None: + assert isinstance(config, dict), config + assert len(config) == 1 + for instance, muxes in config.items(): + for mux, value in muxes.items(): + imux = "%s->%s" % (instance, mux) + if imux in self.config_results: + assert self.config_results[imux].bit_value == value + else: + self.config_results[imux] = CONFIG_RESULT( + routing["feature"], instance, mux, value + ) + + def confirm_intermediate_path( + self, potential_paths, potential_configs, intermediate_paths + ): + + self.validate_paths_and_configs(potential_paths, potential_configs) + assert isinstance(intermediate_paths, list) + paths = [] + configs = [] + for path, config in zip(potential_paths, potential_configs): + count = 0 + for ip in intermediate_paths: + if match_string(ip, path) != -1: + count += 1 + if count == len(intermediate_paths): + paths.append(path) + configs.append(config) + return [paths, configs] + + def apply_flag(self, potential_paths, potential_configs, flags): + + self.validate_paths_and_configs(potential_paths, potential_configs) + if len(potential_paths) > 0 and "shortest path" in flags: + for flag in flags: + if flag == "shortest path": + shortest_path = len(potential_paths[0]) + paths = [] + configs = [] + for path, config in zip(potential_paths, potential_configs): + if len(path) < shortest_path: + shortest_path = len(path) + for path, config in zip(potential_paths, potential_configs): + if len(path) == shortest_path: + paths.append(path) + configs.append(config) + # Update for next flag if we need to support more + potential_paths = paths + patontial_configs = configs + else: + paths = potential_paths + configs = potential_configs + return [paths, configs] + + def filter_used_path(self, feature, potential_paths, potential_configs, msgs): + + self.validate_paths_and_configs(potential_paths, potential_configs) + paths = [] + configs = [] + for path, config in zip(potential_paths, potential_configs): + conflict = False + for c in config: + if c != None: + assert isinstance(c, dict) + assert len(c) == 1 + for instance, muxes in c.items(): + for mux, value in muxes.items(): + imux = "%s->%s" % (instance, mux) + if imux in self.config_results: + if self.config_results[imux].bit_value != value: + msgs.append( + "'%s' had conflict to set config mux %s to value %s, had been set with value %s by '%s'" + % ( + feature, + imux, + value, + self.config_results[imux].bit_value, + self.config_results[imux].feature, + ) + ) + conflict = True + break + if conflict: + break + if conflict: + break + if not conflict: + paths.append(path) + configs.append(config) + return [paths, configs] + + def find_path(self, routing): + + thread = PathFinderThread(self, routing) + thread.start() + thread.join() + # thread.filter(True) + return [thread.status, thread.error_msg, thread.paths, thread.configs] + + def finalize_config_mux(self, config_mux): + + assert isinstance(config_mux, list) + assert len(config_mux) + if self.enable_tcl_map: + tcl_config_mux = [] + muxed = False + for config in config_mux: + if config != None: + tcl_config = {} + assert isinstance(config, dict) + assert len(config) + for module, bits in config.items(): + assert isinstance(bits, dict) + for bit, value in bits.items(): + assert module.find("->") == -1 + assert bit.find("->") == -1 + name = "%s->%s" % (module, bit) + for p, t in self.tcl_map.items(): + name = name.replace(p, t) + names = name.split("->") + assert len(names) == 2 + if names[0] not in tcl_config: + tcl_config[names[0]] = {} + tcl_config[names[0]][names[1]] = value + muxed = True + tcl_config_mux.append(tcl_config) + else: + tcl_config_mux.append(None) + assert muxed + return tcl_config_mux + else: + return config_mux + + def solve_routings(self, path_file, clean): + + assert os.path.exists(path_file), "Routing JSON %s does not exist" % path_file + file = open(path_file) + routings = json.load(file) + file.close() + assert isinstance(routings, list) and len(routings) + # Reset + restart_routings = [] + for routing in routings: + assert isinstance(routing, dict) + temp = {} + for key in [ + "feature", + "comments", + "source", + "destinations", + "filters", + "flags", + "parameters", + ]: + if key in routing: + temp[key] = routing[key] + elif key in ["feature"]: + temp[key] = "NOT-provided" + elif key in ["comments", "filters", "flags"]: + temp[key] = [] + elif key in ["parameters"]: + temp[key] = {} + restart_routings.append(temp) + routings = restart_routings + if clean == None or clean == False: + threads = [] + running_threads = [] + for routing in routings: + assert isinstance(routing, dict) + assert "feature" in routing + assert "source" in routing + assert "destinations" in routing + assert "filters" in routing + assert "flags" in routing + routing["msgs"] = [] + thread = PathFinderThread(self, routing) + thread.start() + running_threads.append(thread) + if len(running_threads) == MAX_THREAD: + running_threads[0].join() + threads.append(running_threads[0]) + running_threads.pop(0) + while len(running_threads): + running_threads[0].join() + threads.append(running_threads[0]) + running_threads.pop(0) + assert len(routings) == len(threads) + for force in [False, True]: + for thread in threads: + if "status" not in thread.routing: + if thread.status: + thread.filter(not force) + if len(thread.paths) == 0: + thread.routing["msgs"].append( + "Fail to find any paths %s round" + % ("first" if force == False else "second") + ) + thread.routing["config mux"] = [] + thread.routing["status"] = False + elif len(thread.paths) == 1: + self.validate_paths_and_configs( + thread.paths, thread.configs + ) + self.finalize_path( + thread.paths[0], thread.configs[0], thread.routing + ) + thread.routing["config mux"] = self.finalize_config_mux( + thread.configs[0] + ) + thread.routing["status"] = True + elif force: + assert "potential paths" in thread.routing + assert len(thread.routing["potential paths"]) + index = -1 + for i, paths in enumerate( + thread.routing["potential paths"] + ): + if len(paths) == len(thread.paths[0]): + found = True + for p0, p1 in zip(paths, thread.paths[0]): + if p0 != p1: + found = False + break + if found: + index = i + break + assert index != -1 + thread.routing["msgs"].append( + "Force to use first valid path at index #%d" % index + ) + self.finalize_path( + thread.paths[0], thread.configs[0], thread.routing + ) + thread.routing["config mux"] = self.finalize_config_mux( + thread.configs[0] + ) + thread.routing["status"] = True + else: + # There is an error + if len(thread.error_msg): + thread.routing["msgs"].append(thread.error_msg) + else: + thread.routing["msgs"].append("Unknown error in finding path") + thread.routing["potential paths"] = [] + thread.routing["config mux"] = [] + thread.routing["status"] = False + file = open(path_file, "w") + json.dump(routings, file, indent=2) + file.close() + + def solve_routing(self, commands): + + assert isinstance(commands, list) and len(commands) + query_parser = argparse.ArgumentParser( + prog="Configuration Modeler Query", + description="Query Configuration Modeler", + ) + query_parser.add_argument( + "--source", type=str, required=True, help="Source node" + ) + query_parser.add_argument( + "--destinations", type=str, help="Destination nodes seperated by comma" + ) + query_parser.add_argument( + "--filters", type=str, help="Filters seperated by comma" + ) + query_parser.add_argument( + "--parameters", type=str, help="Parameters dictionary text" + ) + query_parser.add_argument("--flags", type=str, help="Flags seperated by comma") + args = query_parser.parse_args(commands) + self.reset_config_results() + routing = {"feature": "Query", "source": args.source} + if args.destinations != None: + routing["destinations"] = args.destinations.split(",") + else: + routing["destinations"] = [] + if args.filters != None: + routing["filters"] = args.filters.split(",") + else: + routing["filters"] = [] + if args.flags != None: + routing["flags"] = args.flags.split(",") + else: + routing["flags"] = [] + if args.parameters != None: + routing["parameters"] = ast.literal_eval(args.parameters) + assert isinstance(routing["parameters"], dict) + else: + routing["parameters"] = {} + routing["msgs"] = [] + (status, error_msg, paths, configs) = self.find_path(routing) + if status: + self.validate_paths_and_configs(paths, configs) + self.print_info(0, 0, "Messages:") + for i, msg in enumerate(routing["msgs"]): + self.print_info(0, 1, "%d) %s" % (i, msg)) + self.print_info(0, 0, "Potential Paths:") + for i, (path, config) in enumerate(zip(paths, configs)): + max = 0 + for p in path: + if len(p) > max: + max = len(p) + for j, (p, c) in enumerate(zip(path, config)): + i_str = str(i) + c_str = str(c) + if c != None: + assert isinstance(c, dict) + assert len(c) == 1 + for k, v in c.items(): + c_str = "%s: %s" % (k, str(v)) + if j == 0: + self.print_info(0, 1, "%s) %-*s (%s)" % (i_str, max, p, c_str)) + else: + self.print_info( + 0, 1, "%s %-*s (%s)" % (" " * len(i_str), max, p, c_str) + ) + elif len(error_msg): + self.print_info(0, 0, "ErrorMessages: %s" % error_msg) + else: + self.print_info(0, 0, "ErrorMessages: Unknown error in finding path") + + def diagram(self, format): + + import graphviz + + for block in self.blocks: + block.top = False + for block in self.blocks: + graph = graphviz.Digraph("%s" % block.name) + graph.attr("node", shape="record") + graph.attr(layout="dot", splines="spline") + graph.attr(nodesep="1", ranksep="2") + block.top = True + block.update_child([], True) + block.graph_diagram(graph, format) + block.top = False + graph.render() + +def main(external): + + start = time.time() + parser = argparse.ArgumentParser( + prog="Configuration Modeler", description="Generate configuration via modeling" + ) + parser.add_argument( + "-m", "--model", type=str, required=True, help="Top model input file" + ) + parser.add_argument("-s", "--solve", type=str, help="Routing JSON to solve") + parser.add_argument( + "-q", + "--query", + type=str, + help='Query. Format "--source=??? --destinations=???,??? --filters=???,??? --flags=???,???"', + ) + parser.add_argument("-t", "--tcl_map", action="store_true", help=argparse.SUPPRESS) + parser.add_argument("-v", "--debug", type=int, default=0, help="Debug level") + parser.add_argument("-c", "--clean", action="store_true", help=argparse.SUPPRESS) + parser.add_argument("-d", "--diagram", type=int, default=0, help=argparse.SUPPRESS) + args = parser.parse_args() + configurator = CONFIGURATOR(args.model, args.tcl_map == True, args.debug, external) + if args.solve != None: + configurator.solve_routings(args.solve, args.clean) + if args.query != None: + configurator.solve_routing(shlex.split(args.query)) + if args.diagram > 0: + configurator.diagram(args.diagram) + end = time.time() + if external: print("Elapsed time: %f" % (end - start)) + +def cpp_entry(top_file, json_file): + + assert isinstance(top_file, str) + assert isinstance(json_file, str) + sys.argv = ["config_model.py", "--model", top_file, "--solve", json_file, "--tcl_map"] + main(False) + return [True] + +if __name__ == "__main__": + main(True) From df22a464bf1059ff3f3ae5923d4a128fc69b0181 Mon Sep 17 00:00:00 2001 From: chungshien-chai Date: Thu, 26 Sep 2024 14:39:20 -0700 Subject: [PATCH 2/4] Commit 1vg28 gb block diagrams as reference --- .../gearbox_block_diagrams/1vg28/PLL.gv.pdf | Bin 0 -> 7465 bytes .../gearbox_block_diagrams/1vg28/Virgo.gv.pdf | Bin 0 -> 14027 bytes .../1vg28/gbox_fclk_mux.gv.pdf | Bin 0 -> 10937 bytes .../1vg28/gbox_hp_40x2.gv.pdf | Bin 0 -> 31402 bytes .../1vg28/gbox_hpio.gv.pdf | Bin 0 -> 30388 bytes .../1vg28/gbox_hv_40x2.gv.pdf | Bin 0 -> 14172 bytes .../1vg28/gbox_pll_refmux.gv.pdf | Bin 0 -> 12930 bytes .../1vg28/gbox_root_bank_clkmux.gv.pdf | Bin 0 -> 14234 bytes .../1vg28/gbox_top.gv.pdf | Bin 0 -> 11027 bytes .../1vg28/rc_osc_50mhz.gv.pdf | Bin 0 -> 6221 bytes 10 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/PLL.gv.pdf create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/Virgo.gv.pdf create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_fclk_mux.gv.pdf create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_hp_40x2.gv.pdf create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_hpio.gv.pdf create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_hv_40x2.gv.pdf create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_pll_refmux.gv.pdf create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_root_bank_clkmux.gv.pdf create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_top.gv.pdf create mode 100644 src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/rc_osc_50mhz.gv.pdf diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/PLL.gv.pdf b/src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/PLL.gv.pdf new file mode 100644 index 0000000000000000000000000000000000000000..07e309d1257b3bd0cd2f05bef8fd32ed50ab069e GIT binary patch literal 7465 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

ptLtd63xeLRr6sVy4|l-LN{%Y zX}N!S*uAf3btXgaPyUQa9wDQwkLKDE1W|tGCuz^#6{AJ$Jxov=G8t+lYJK zM(JRQ>6_E8YK&-$Df~Sq0skOgKJLv4F$!vFsa#KoYkfhxyaAj*DV6xG42nHiljJ$B z5t%7>xMt}6wYO+kK6YF4duM|4^ID;@XJz?c;ft~?(vC_`MGf@WD}Be2Qi-*tZNan8h& z9fPGppv)_mg1xfuio#T)GpJ@8zTQU?Q$ei@z^xC4CKFcR23$B{UdDX36L$Az3VFw2 z#1K-Mr6f$e_=c=x4@)epj_ND_q*QFmyc)BPecE)6V=nWLhG<-5Stpu`|u0=~FZ=p3?l3o@0B^Zl43ckoi1p4-;Q!A7g)jpn0l^Z0bg#l6c=-5`O zRP6cp5P~#+CLjAt~d(Ssx+E#jFMh$AWQx3b-ex) z;EpdBwVkw7Q7aN+YtVYgt-BuyUXEw3Q$z=&NT%gQlyP;oGo>M$FWFP*%rbeXrRB+$ zaZOg}rp@JIEv)v`U--6o=&yQoQY)Y(Y7Gk|qSj@x-`;Z735*cERoD=YQh=tUqo@g~ zW<_91rOAE>Fmnh3anVh2E75h;k>czSbiHqPVF%Z%gWf+OCHUj zv-91Idm+XkqxdT~)OORsKR{82ISkJ(X7eZ-;?4SsE5wkdO`VM}Ya{YOT%>#Phc)U) ztPWZg5tTY5T>FBv>rT{=WRFQ71e(Ht;fp@mDIV=EaL zc4g`AmBp&5zQ2Fk(cZw*MRzP%^`;P`5ec_!(;kx^t6xZ;MJ1mkrGSZ;sVMicgW^*1 z6;8?-)abj#O8}DNOjZb&-h5Vhe)LwRa4vFX)tO!{^-99H^{l=;`RWr;lGg=#g+l(i zF#OaSTA8o&3<`z94U&CGl^LZuZlK1>jLKb%s_(@;R4RuIy!hfwi%L`FkJIFnJ9l$jFu7Clr7fH9M@fe*;-KIh{HRCuy1j?M_qOw?Cu2^ zsSG;z%UM@r$M=RKO5;0=cwMhMVrbWY@_K10l4Stb6?|Zhs5yUw{aJ=oa%wE`>(?0g zETi80wV@==CbHozR+ZN`3YUG(eRJvHLob|_Up`aq?|i?7A!dKOJu=lt#Xs?~5JNEp z$e!h%m6@B_&U%p@WAmUq_e>I;G%Xha#`;exMLSZilQqHG!#L4ZeFKGCI?s-2L}3q9 zj+ws7`u;IZKqR^IV$n~cOCQ4<233PwE@#Ak)*~ZD#b)W%PyN%E)HACN+8i1&Ddfb% z{a*HOFgN;N#O`|?lx>NuQR%p!EEuU4I4ald|AJN?)oICI8p?`av6v63t(_V2H^cEu zqJPt$z!u(KLePo)UM=DGN(7KAX;LokIINp)iZlg8v7ti`RIBZ7j!Su+1%sMKcNL;e zvQcJt>F9QtQQn5;OzeU@j&2F=kNdooWc=>bctqw^11xe8*O}FV4YJ;bqZ4vA=Ek8l zcPnDF-eg}dW%hc0I?9hl2jxOQ>w z$b^D;Q9mlb+%e(|utG7adDMl2-lGMca~~>j_s2>cIcFlljLy8Crzrrc-q=0(44zw8 zAO`~|MOA_kFOv&Km^tMIx;)Yg2Q@}TBM#j7s*-ap2?k$Xq<-9a1wE)2<6&orL#pv{ z=DMMk+`T@wwe&E`!rXE@3CD?8avgtWt6*6o-)Yj>lJJJehr}$Ui5by`GW=vYk9ZBo zOhkdASd+z!WV?3kbiNgHx?VJzesq_X0zbDk^h**&{CRErArfB42~8VK6?1QNnM#|P|LQ(YXzz2vbPb;s3F+P5 zjgsGzLuF)LI{#>_d?Pn#G~(Km6sKg0I&!PfVh!NHkhqIPh|-omRaRuEl~T9{YL`_8 z`cT=BB{T1daNFJtImJyVP{cthD|FD3<U zq-x#m(t6SMHv1h<5hsUrvT{+0#1~s7-?F0>yFed$5^HwCa*ApSu!*2p#n)nl_-uW3 z%+Lfiyw96@@4mf(+UC#l9}%wyA!S!=jWjl#;NT^Tg1|D7)49WurKPX%o709UJ&5@r zSaFleWi;-QB}tvAWXvC3K3z3bvg)z!P|3UN<%g!@MTg+Cr6q2aI`mSZ=1Q{;J_}8a zJtC*if;A+d@=~#F+sys~!9O|q=2Kd5jThgO6NYag3O9v)h#8?YB#Qqr4Jt=Gx3nF1 z&dvP_@7WOXZ7y&}Z5{m}@%>0$i4K}cjXSWsd$+jPbK?6Ig^RY?Y-bd2fF@ojFsOJr z6r_huVN6MaLX%}|Vk2-+xD`~X%E|pvYG6H0CG?>^{0mm21`WahNq%U32zz$=#|?gi z*pu0-Z!$Qcc(Wr`U4ixY+1!{#jC~{_n^h^;9&w+VV3R#EI0~}P7yV4e& zr1&ToBvK|x_9F$)I$_-eYJAjJVtsNX1t0Ru_>=e`pN@xCSJy}*rC<^(-z&|To($uq}quHp|GGFsa#r9Q&J0oz@l4==NN1u{4Lbzadv@S(+l>IzI3(xd& zEv+jth#a3@T*Or5B600Xi&_8SgX9a=Oo=7k4CY}RwS-(AqH<6 z8rp@-@111?>2{r=_Z?VPCgf!TGYmNPt*S{iweUIGCpBuRh0U84w`*Y~(!Q>5P+YRy zm|>erpArdewfmTlOfe`O_VP%|LAEO}O~r#p9dd z5hi_KLumpLXQgePS_^&K{qY?2!kboYKdDarSy-{d0zTf?M(h! zsy~LF7Wq#r`=|Hg+MXQ<1U@M|iERJ!0sLO|i@Q5ZDmy=}_yK%;Pr&2FVhDT`04$=y z01$x1*zocB=m&U=`153uu(xymhqf6LkO>F?|LsS3RC@gURT%5P3InjHcsQ5>Sl$?# zn*yi+px=eRF@%^t<^}pYEsMOViKU^4y*ohr5ds20Y#gir5C{YY=>FCheJuGgffL}# zk@3fCAqlZ}arj4V{*>|?{ku8Jk7Yv)?VKE*N;CHO8#)Bf?gfB+yMCp-JUxmUJ4 z-?`(c)*i3a9L$#&?H}zcxY6t#Imc0Fi5dmNyzSZ`v&Zxe^)W+=2^fQo>thq9rWFw) z#fpGUqkUUH^nwS|(I6WjMx7nunMPn3LlK3M-M5rf)E6eyxEmm2NLoWVYrFhKhiWQhia% zJ^KDTEF-T7T9qP>iM%1$m_$*%X_3~*xjN7&juQKTo#3hmV%&(^=i?#AK4ESqT0~jP zsYbtR&(@2jC0vykh&yXJt$Zr_rX6D(KdQC%XIw2-d2990*-~rf^^1ib*-T)c8iK36DKmhL zu#89E2B9@Z0yn`&Id&*kh1w#vfj7IEWSsYnt9f-JTS|_WQwdv%_>9cK^8+u2aMVf2 zM+3bw;sI28vL&aY;~M3HFUSQ(6|ejU1|vaX_Bw_b9k0+;3eBXk2P@JjWj5GDI}Q9$ zURJ&7!39(1LM~PM(*{D-2=ipfah+wB(@Dn9(@C&#V%BWbe>BGGj(__R*)saR4mUvcd4RRL%E16P&njZ&x`DIC%Gi%Ln7veC6Jhb_2*Sim{x=1w zDW7M`tgYLe%>_D3X3mo@FID${p1-@|xMJUj)AOh*%{h+Ocs~->_8Cq*{eQEuwX!U$?o?nww zU6V8|cV}Ie@9z_4a?%6OIH}e2Ru+ptoOa`LC677W*IdS3;wp4iz2HwX*aT$;hs+cT z_=4h5?6Ppx0r7p4RWLAs3`A)&;W8cArCGC?KSaX(G@ z_dlW84rNAmk3B!?dIy-^%}S}-BCpr8OH>?`%Q|XmAQ%3i_2cFXZ>hXi?+eWWc^;CS z#`1OY(whYgyv|D>M9#7?kSB3hI1!o}$(NOmnZ-KoFzgU@0vh-3A^5K@by27!&FM<& zBjs?PQ81D?nFQ|XN;aJ9mEWwqNgxsrd->KB?cF6i`}g_VsaXSp(HiTy99@!!ov0}5 zSstEWO-MUdgi%%x*_I2pLBrG(VgdoDFUKaWP*}KLMRXAofXWWlvLx(SQ{CxNrLlNl z*NWM2lZNP>$T|$VE<@I$%}4zRMh9*nUQs-7a(+}YVRj#)4Ye^6zS)vZ1R3bc-{3Jubkx7azXA5hWmKBZ z2DV&b)UH(;23u9)jT0=mUC9g1XL}ffSl{&%;ckI-dmHU^$PDvQ7*b{?XLv11 zr@yMiSJC>$n-F6q0fpxc4q_WTkGF^}gAQD!t!Kw!yQ=m#s9uf4`e6S|7gc=~wT`Q> zK3VT9u&8`cc=i1%35A3#M)k@+^ZQiuJNGiQYN^p%Xdu2`=XtfiXs;r%sO_fP(ZT~4 zw`LTLb?@LU%0d1wi(h5Kyk1#@O>iaxDCcp$%!N6kbShR33EgSm3*z8%hVahWtEez@ zb+0|9hNgmbM;V6oPEs7+nzh*=Q=c9&QduVS1%Es(R7ngu8ivn2=35;veAi?Xy%~C> zxIA{I`w9Kw`OoFA4%g&*jdC&Kb{3&hO6Q&i&4nwgbSo zoQPmV&H@{Bj-N!&BHMcsdd7qw+S}UK+7tYS6P%MP7UM z{xovFoF!~rTdh=wt=#VFv%LDM?vmH+1xGRJP87>G3WhWHy(9nFp>q84G;XKw_sN>` zy!4lMtbWhmrym)m(;};W!77ihGPgP5m7WYERC;}!S?ZfJ}keeyY6RVQ2;k_5y&is-+n@yKUsoRj|yf{F*`s{ zyWN;b^had(e7H+NOHDP7AUt5uXt=BM{?1Dj{&tu1qWm>Nd6aRFt`_VvN{gW|*$)a1 zzt}Xf5@-KeEsA`~S$H|Lk;9ejHe(~@B0)c{a$XjTa7t-fn_=ncPq-TD1{*HWqwnlO zR94r4+K9&s)rssRO}L%bn@BH7L%16^at&BJ9B)p23u=$&YOH%N3$*mxWrOZ*bZ;*X z4iCJ*-yK$K)W3wvE{uF|yon$6dRKC1aQgnRj-P*_{-!$xL(O5$S_?a}+O*BL%h-Y~ zAM4}&XW{n{VAHEt7)xoO9DVh+UbQ4P8kB5gE3KGVT)#M?>wxD(Wn`*fdepk?>u#I2 z`4Dm6n(#zGlBX*<+)$-9 zJxRp6>V45?(zbJ3ASX3D(!JR5CBiT%rKUsV4qeL%))K)&tWHMD@>`R_inpZC5vr6* zu*yR^%z9Ec28MteD)<7V1w1{j7P_k+Oam+hd`d_Rz^Vf!eLB2fnBvO_rh*o~fF~5{ z*bZ$PCyuP#>Zqzc7Mj#Zl+HH{;jOqM=)y%Y#WaCs)c6D8qL!DO+MM>9^+u8hu!n8S z!&~KdWe3>#pBJj{K$c$P6@H3k8+#e$WrglGGy?Ni!E<5#n~LCv=*{>dPF$9zq}bC_ z+X@6v8dw^l4$XAElpm7nlMnBlVWsa!5kH+}AeUuZ@qgZUr`@REiitkmjbx(CNsGUQN7<4DeneUeSy#`X|thgjxY&lSNJ zhlR_V((a_<^tp&ep(cRhjDHi=59@|uGQ=V_z zHO2`_X5S=2Zpz+YiBih0qVsQwYsTS1Jg`+5WspF%=byQP@# zAY-prIFrv{R>rgM13huxr;8RArR%jTF}%SYWyPWHilQZb0mr;B^Bla#0Z~dHZX5UW zE_X3(OdRGhUVVKSol5=Ho@tKrrMxmCsx*~Xugts9#x54K5;3YhDw9wRcCHvk%0N~* z+OA5ynGJR_UJkA(cOlwFD6HN@DaGYHGa4p9uoo-Zie_+6#|NEoa1_S+)eB};pLw0# zLsfk5yHmH|6s^_iGOJnj4-HKd&e!!H%8BihU-Ltt{S6HTr61*tP5Oug%X#rm^xp4X zWk@(V4#cCC^G!lZoM(?B7r}S%+q-Yaj1hO&g)P6lkr!VPLUv@VQE!wL+hD>HigYgM zkD9|YejBIJ7|u~}a6P>|Ur6(^;p8xmtU)2;{R<(l3AA%V5$kEOh9pA*r6JZyBw6Di-HuIMsWlS=Of#wdg?EdaiztT@0@=p9 zLLwOL#uNU%HYaXutQaS5WIk|Q+I`^hQ)0zqWB4S^W9uK|-Nt7V&fsX#q#1!O*^IWB z>iYwGu#9{1&)zOhtO??w5?DY+zL*vBbSx?XId zBD#)*;zZd2P1+Ee!7zp~+FleIGMSr$r?T7BP8GQJQ2@X$Uy!`!0LSF0wR44jk&)lg z>9Z?Ze@m`=TnKmKd?pWW537oq9ts=>w!IM_k@gcSqd*C_X(SDih$}x%zr%`~{-V3% zd(mHq9B+aY4^PEBR382YEB{Ek#{h?TR3h#xy4N$2?aKoCTr1emE~^{!T%wJ0^H&gA z*+J{_sAayOQ@Lcl8f$BhW5Z{boFx38*Tyf#>J70SC|L%Sn=LXLKWkiRd`>(FJk0Gg zAFOnbog6g#3F9`}>eeE-g-AQa1Bp_j+&}BV64ejWvAZ!#$u0QgtaPo&QqgJA9Fk7#CbVCTa8W6`Ox^GbGp3c=g-&+2^%y-4}s?y7n!P7VcKR) zYBV*{Qa#y-;ekWiQ={0Tl{Cmm{+{G1!yoLzLT`$J*R2&BASw?e3G;%&n&)Lv|>0q5C+C> zDDmkHaLSmcghh(7AG_@B#tNnQ=wP=H=Qv=NN1T3;Jq!lJX(^{P&1QAW`m zc_Yz2fcgwR^vY)NeVAn!|0+wK@JqLR)WW2auFw2gW`(>^Jd76U+V;XWZQhpzhN_Qr z)3sKnFBi<5Qoafyi+!ym-!yD084Yhv_~brB8a`VTLK@v$eEmG@1#uQR<#3uAfeG5; zM7J}Y?#PLWduPl0eNy_2B~j{ZX?H){Ca!i)jM2R9xO&DH79ZL!DJt4j*XU00u8p`P zy8DdEhIGv6*mlhe?U_8`1x5HpNDYfit!)^(KTgPaWVLrn8+(6T@nX1D zOz%z88nYgV?X9AjFQ6Ge_!$XWE;B!BHDcUU?gHf*>`xePp%0nw*NlIpv3yyoU7Z?b z=6XiQ)sT6$k2n(*?b zUpE#HZw}0}JEw)QK+^(`>Hld&{oswdIv6+OF1H z&nigu7$b)7Y_9Y3yU*Pj(NkP@Hp(hITvjPEdpj~S*Ci_I!}8v~h5uMok40$|obU=j z@P(Q#r@dXUS95>wjX&a<Eayyw z8Eh~o802J;x+fXqO2ydE6Lj|l1hJ(i`&^Af1NtWjGT$#MlJPK-4&xc@ag$=g&G%%7wh5{Jn(2N)mysxyB2Rh_VhR`uud#n)6WXbR5%mua+Ca!3*e)$a3miO zQ&Z|*!8QwZdEBDZTIvf}X0&IMp0j&SIOfGrKW5qH#>2_!>a2S1KIwk$KIp#V9_Sm2 zNn3ROu+Nb%XkJ+XA1pEp6@rO zt8blI>i39YJGp%s@7QsGiPJtSbVuBKwD+i+w;F%n!j#bO}h~yk#*@V%|1th55pd#;C`zw|BZVLLqKR89x}YW8Z?t?m9U_3GhNa&@91BBNdYnwxg` zev2`|?S$7M;}#>M{=yNaTtX4PZgRz+7h z2iTw=tk8h#1P&aX?%4!2ag7!!O|Jzb3RcA3a%RWL?o&KobBMGi?~*5gPA>a*DH{Sf zv!7kVX+^tc;s%9N8oo3Te=#7k?f14t9D{)3z!tO(2MjdBdB(5Uv53*N==l7zbeI&~ z=VydU*O+hWUo8X}Ib$V!ecMCuv)d)}iwn@JwPp9iJ{i3~`lcu_N}%WVpCh$&Gl9kgpb zy_!KmjIiQxN~j;fh2O-Ry%pBd>)T8E9^Rr!>0}}dTU4kaYS}L(fe={_>zU+?P^CAu zcdN&C9(K2PG-K)5w&Ar#mU+t_O0V!D{cAOy)|ASH*{WB0nHfkYr=qoc@3Pj`Z#Mc` z$pr1MhqvmJ&k;vfy{Fs?eqcQisJOfi&4GFEG+0fE)RTKGxL=we@RQehOpt(GBSe6wcI4lkfJ%|FClb73Nred-qAAs!4|9pQAgPyED&Bw#1jbWbawXKFsT&Pjei=` zjUbwSdoeZ}t+m#G93@$pvkNzs;eSBtIT=a%%^UxMEU%xsd zk9>`Zv@tg8$Jf0yFCIo=@4vQ;JCMAXfB_)P<1k&kR`e5NfGvSJ_$Jx*VoVVR6iCdq z#yu&Cl$?rHSr(Xszh=!#JVq^&3V)wh-mWmF9t6I92lH8U-N#E|w_qqJhbRl)UB9Ee zEj^JOn(iN?7`Uk&fKBw{0hhHTRT$X${ptcQ!{_uoM$SEZCACz+^&*nfmXe=Y+^sS-aB%&;# zs*%LgeGE)5lhi$>2H_!M6v4Cc`c_a=B9Fb8K#zWDbj?1NZ0Jrm)Vv=ml+bi70y@u@ z(4(auunXKv&Rk1{V5A=Fx zYf>(A?2CBC)&CjIIVY06=*OgG?)oWlzA%O2p({`^FLt@HWXKM2emP84HPemX(#Tb_ zzf3J+O8Q4U^_dBgu71VmLP23y0V^}t0h0omW1nIYx46Sa1>hn_)L-v;17OleZ8$h2}sY^DSqXzkgzuNpeE52N`}O07eb zA-*3alCLDTNQOZ25l8Z6#`?X4N)jfk%9-)>-@j$u6o^f(_`F0Dx0f>wGAT-y zW2TN*ayL_=NPb22GNen+A?DN-!eVN4bP?ldOfvKh!s;5)72y&C${8Md6&j(2U{;;{ z1@5g2?@0EsC1uVoquCTYhJhc`1`1RcqSxnk@cxrRve$s5kyY!lCU7tQvW2!as(L;y|>){jcPo`x^K|q+Tf)M&_e>C^q{>knl30qqLYK4?$iU7YTaL9;L!ZSFR9}pYw{C1Vaqg zVt(=LK_P(FIVl^5(R?h1rnVHJ5_;S55CVEL7cp*-@asuWa-5f+;|W%zxst!iB^b`W zeE!)uJ0R%OxF)=wOfu4E@fZmQ2IiIOXBfX&ZiOFO5v~hfYi#`@X&TE1{r9H((+==o zo9>)oAo%ZnaPI%!D+WDnaR2`??{$@g?KXignhtPzTrmPk2J^MrkiPEfK`h)VEMFVQ zI$Ie~5QRDQ9ej$&j*Zz$EZv^s-g+0D*nxdhiSBzIqhRy1 zVP(-PUd{eT|0sA}pJ>6-;!NJ#dgJw!suFe0@0IEe zZ=E(qm-RoX77v`it_v8p{qF(ryIcRJU4U5s9sr;xj>F$^yHl_gJ?_|PG#!r+C zZdR_};!_2mWF}TNj^BW&p@WpErMbl;YvVDS-;lDisjV7->reV8LF#YesfmwF2vb(T z6ItS)dI5~@27()IwfSlo9 zGKlq0)&AY!kM;l81QfS>Jndp>XAWRdx3m+sbF%y!_+7NRrHQkJ(<3E?4fNEE|GYVX z|3tuSV8H+NePqZy#{7A*{SN&z)xU??xSzN?e{L>L4geR38vy2f;?g|bU@#cK#s0VS zgs^e{@AFS>IJh5c!43p)fFJdr-rOJn8|R-en4SHxB~QOd;K#x6SLhJ~16hCT0s-ut zTmTLb+aqlV^oX!O=JLe1;Q+J!88we|8hcec%clcEfTwK!*{}aS%%2E9PQUFaYiaUi z#iyDp{fp4!^2Go72df736#e(ecmuIFaWQ^8ltgQ6XbG_gfS5sGW*~sp!r9ruiHGHp zoAij9Lkt}(ERCI*?IGrLe- literal 0 HcmV?d00001 diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_fclk_mux.gv.pdf b/src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_fclk_mux.gv.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f35ad6138d8c30f10d05a55f7238e54909d5c8d2 GIT binary patch literal 10937 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 literal 0 HcmV?d00001 diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_hp_40x2.gv.pdf b/src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_hp_40x2.gv.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a429b8fc6751478be23c7fc2a59bf48d7b843a25 GIT binary patch literal 31402 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 q&p&Uy@^f?LAxq-(WI`}ZADHHsbP`G&0&ntO5H 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 Kd#Naa`Tqc^j+>PL literal 0 HcmV?d00001 diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_hpio.gv.pdf b/src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_hpio.gv.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c42b6f16765bfa0e4f9a31b772c6bff8f5152387 GIT binary patch literal 30388 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 z zkn)hP2XrH_)y`;fIS-vVjJtVUf*NaQH-3`y+h+saR_YoN29`GajEXT`?}W=SHC>cM zi!kZ*E9kN1>b?0W%2NEe)lJKoF+br!% zoIv^H^l7YAf?`@xV$A&tp{BCIJB@piQf1M9gz_dGHSw5jFMZG7Fa_9jESiLMN`kx-cJK<@9zU7T8=)4xG%kfy|Vc4*}%0xsvhJeY_I!0>j;% zplk8|f>$XtXwc90B&M7QNrs?C+c9j3J(0@;^TfYIsg(U!SXTW<$YkXi=xk6Rm0{-i zK>{pus*hm`s(!HHBE(AH-Z5ZU2oSj^e{$%`jlpT};_Jbhi%*vhWZTsT&a{{GwJNQ~(=3`+^Lc}y_1ddyDeS89VIEqrCLDxJhN4(lVU3JOd=worQqZ@{<0VPcCavQw|? zpw3@zqJj;{c5s;y;p0$uvo2$7H{skvWO`BB1aMkeF3HL)(OefsD^b?DY7Llw9KS2T z`NInETdO2_E3#vTVbb8QT_Q;JHdx}Lx^)p{d_b`0IT3a%5Wba3)%Ve2|g+?-v40yV8v?x z%enx67Pra@-cgZTb(zLSy;((k0vIB$bur2AS?KlZ$Srw}^Y6Ofwd#EKBvVWrawE6o zIEIrUjpO&QFDKg+|Mx7)N?i)dI?ma9PJ&pYP8TT^K|+W43N zS^wj64P}QU1p)t!R*TXTERdt)XDLuI(1ByjNn>PXM~;6O(e@YEECID@n_>c=MX+=)BiOG%HSP^|QidFY^IUi((0&8rfuJUY>z2Pw9)wSc~ZN79HWsQ+(anpPY z=EC(Gx~f8miulZs8*P{Cnx31o(iNVOHH>)WHezkRi#(U$!Jr;`v2VW%_|7<_)cspe`}CoFJJhC+Nzu zCa3}zhZl(kTd&ui;jx0WXuZOshC1Le|9cKleRSNjz^S0SII0hNOdF3*!Hp6C=I=HT zkvZFYJL$|OL5X40dX^Rl;fVgs9EcpYlyi#j5->-$$L7Lqa2`nu7>(-}@DQyp_y6cDLd&#dDKRy?)gt@}7?p{?Q=Ar(Otem46K}*FIj}JXA}wWbXz$XAF)L zuPqe_Kpc%&&}&Mrw;z=P<^Rt5hME0<>QQA54JpbF+wx8aWu7zun#d0pdko^2Kec7G zck-MoAn&r7MZ zmL?L8Vet{|fHjn+-EfjDL#0>EHn5MzYDD#>SF*#MulH|r!g(12HbY{8wp8p(V?tCt z3qqWAjW!btJ+&U`l}GY6F=&ijZjSZu<+oL`hS)_i$EKvfbaQels-|Eh{Eje=>`88w z!dw=hgg%I-u5ho$j;V8)!1@=sEDrq&cEk=^P7%I%N3Y8{USHI1F{2aCW7;W2g1^X( zH_<&Arph~*+!fWQWv>Ny>^Gy9M+{49Q_Ff-omZ2aj5=k06Ye~maT#C$x2h33qhc8G z+)bM4IEdxD^@?$sIQNP|#m^yiP^4{H=Y=RYk?mJMv1JvO6~|?6TUNj#f}htavN}lq zA*l|mqFJS?YFG;B73kWjRxb{zz--X(B!UVJV8UzZui2sK<>y1?5G=Plj;{l6YYJpC zW?m5KwLV{30-ROYE3|ZBu5;=zdjf&WS#aY)*Qc}5h4h~7Z1KDm_|R?D3Y2YH*Bjmd zKA-w5DYgu&jcA+MxFB)*`h06)=TJjmQ01kzTRCEYan;U^!d9VeNOZK_m7&Uc*CLN) zo%1ZWrExAW9*)rSu2PfR@M?)#?WkN5`#HlwA7pM|2KJcZzDZXUt}uozazg!*A=R-x zGOon(lkPhpqc*gD1<3U%Y-8mz36yY%mg;RH2cWuknNcEpw5U=88dNC(Woy=7*FT}g zLl5*7*(9C(P<+|~a@n#nfM*#xGPzJU3YOei!Pa!hkYdF`brJE-ECY+CebBDgM-9@Q z7{L-JoCGjW#@?#cc2NKy>;RUY51Kr8)3<4> zS+<}Rgjo`812cIPZCtVlYY6~oP?z-6+w!xc9<;9lgywle;{|L@k@=1W7FLmFeZ>ijq zQcKoO)%g?Y6CP;fuT)J^XBy;$5$OD0;r2ia(q!)cSR%h$k?p_+vpFRS#cPEO-^XKCAKol12S$ehVEZu{) zi*=}uL_~mdfNh$0({TBQH6f}acS{?q##2eeZ(91>)%&VBR7OLFJK6S_MD^!9>3Dfu@vqIJ9OHfBRS#}PLYv3c7zDWwN)(+mxgEGNk;QZa?GWePm@ zqB|HNIthX&a@C|`aJ%((=9DVGa3}GYn#01ge=-$~nM)Atj%{6{Cc$f?Mr5)wFZqk$WR>s$bYG0V|z!3WZsvHc%*ZSvS2N< zjXuJB%<~_1=Zsqf-2^pue`#_;Js)Djo%hAQOd-Y8A%T1GR1He0j9|(&Z0|^Eat;A_ zE)bg}MK3(xG4bGaabFYqSwJmb1%y6cnhJt=&nGaHZWI7+*2hlX`%xT5# zyVKARj@p|@ydgj?;m4b?2m5i!^%H4i37J+kUS1MlHuw}&1@~0>T zl@SD4GBbP>DYoZI3x^aQyeq!x zUsAHW)~!^j_7%F;gB&_*m=G>y^&StW8)jR{g)327{Pc1^w{~@`oQy6SSZ`R{ z)L>XZMiLPj<@xCTK{~>!t$*E^4+nJ10_&qQW+GC4FyA9NX)m!C(=>{&Xiw=x*yLbF zsIdwONaOzk5<8YzgV6f-&o?a>i>7BzW`ulEENdL=QFI6K-)oDKtKJ${W3be&>n3@` zWu#ju6qA+{5^7EeTf!K@qn0#YyqRO+X$cr*c9i^WxRYB-FO~ zV0)%DG;>f@{US;!p{_f8Po96RB^~&8xorebTDrY@n$}pvefuJc!~cl}k3=5x zpJ=XFm6U5;(U(x%2`&ClG^>#XrI!B@jcSi3=R%noEy*@uPxPO;^NVfhakiLL>ld5; z80QY@s`w;%C6u92lf=xT2mPwZILv(_cZH z{!*rK8=$9O+|(!6?=WoY@wJ#R zsu!F5km1h5_0&LPZhVfcfRiN4DZ#7SiId@Or&bd0w{zqTPZNfpW|vV2wNqO?=4SoI zHg8-=3Fk>;$5c5?0148h6;g5Yby~6cpmf{OZafPxt~!YElsSd5F*c^!|44-AhoR!Q z(wI7BP_NfM_*os!&4dmY9&2^`9``xsVAnF*9$SKVbQqUl{RG9cYe#{nDxYC>n|KMn z$=QBA6*UsNsh7fzcdxI{&H6%;l2EBzlYc?yTK!};qA4Xl=7wRl;ihd4GC=Xcki7=} z@txUfA*v-R{@5LlLA||X8_e*0ZFdqz>3*uY> z+CXf`b@&8p3Ub3v-2@A8XhRA-z!gh-bK~5&f0-t(8_b0XH&w>-Rae5fqSoUdD(xV# zw`L5^sws`rAlm%7rB$maf!za%6jf5B2#Y@zbq1_i{?S^2M`bh&^&Z-s(r7g)K0;u_ ziGpxSDcrsxAI%(+ipU7TcZ6?7Dm+DRf9DEYA?es_$B2>mRORy{fkkm4lX-Drw;YdY zcdeT>v}%ZPj|(?~4?W2Qgt+p9S2shem3TX2bA=`l3PI8gM=zwK)rQLd4aX}H%Vn(H z&ZSz1*>i>e6U+E7J${H->>*+q<>_wnOh*$c5zcEsWgXwSBmxTIDyQvn5b`3X8){zc zlcE)2YSb&T91le!pb=_*n%{<7W6B!FU7*n`wgRZGL(fN9uHBzDxOT9|cb~a~GqT~P zjYbYt<6X4N-WoTf26XsZvob*^B>E-43W=WQWF8|XI}7u2cZw7uyJ*T(Q8cop=ww^; ztfOu7J}7(URZ4NImjb$no<$*p=v%epY`mo9hU&QRkO`S0Yjx1tvRtH(Spfs_+J9qk z31G8hT`rObgZm>SyHI|=Pdtd=$>P@Uz@sg9tA>}4jQIa4_4j2w*QEEB_sqO>gAp)s!hys-v)E2xOXksfoZL7 z6+#J1!iLnFa0iSb6#LH{u%v&RIMkWod)7)!XSM9j2&?cxMixN-J#=e-K zssWW};os}g>?sH97t!xk{Q}=0U>)ZPQTex|BYKT%fb7xRAe$!(Lhvt5k(OXO)ds^( z0BmSOB6TEB7PUFd)9yYO!khWqYzHO}VAIx`Z!`wahwNfTO$5@2+4%3fcJ~qzeG8bJsnJC=pHLGC(v?6 zcy0YdeNGgnW`x>02x9K`Ap0*o8EeBS4q@NXAvbQbRXwt_L%Gh|X3>Tjmr;YFQr=@9 zXwTr2oUO1Cy_-LO(!hr{$;gS>tkPv}ACv&|tlD@%=^Srb2D`ihC9l5+)5rSA&j(AZe zr_4IA=)wo>I(@VtU@NH^0LL;O_xI44O~Y>gTA_2X9aqQV^uf4=fWUq80-8fl6_AoR z?^oE`Q#BYEQzmen0>#O#t80LC_9P@N#;k``?-^P)ZZYySUklKhl z3USw=Lh7ynqKXc}yZsF~n}7uvBzwG2^kt z@D!H?AzZ+|#p0#vy^162#HWq1q!ZS;u%_{;t#+GbyhiCS)I3YJZpaP@v)*SUECM@~ z3nT!Gbt*AX+vb9;iE=%FlwI8g%W}WM2r63eqNLmuRrd4ixBiUV8Gwb~+xRL!8$6ms z#w#tqa@jpNUQ1xmcH8>ZsuZj#JY3E?HztUqc7OWh<$2b=Rt)CT?f~ zI3mSOOo%jeHXT%)o?J&yuE?WI5{VUm9j8;=6FS3WB0*hIr@=1Vf$-1qE8<)5ejyym z%nK0knfQ55l#TQv}oeBF%D9q6f z_cT|L@_0d<#~iuTNW`C~A^lcA$-E_DPz5AQx8y|K*#2N#yFY$dVU!?Yn(BSEmfZV! z29b~zD;Nfp?WT6#D9||zN7Fgbvp5M8u;e4#%u3?UF z{S@G&@oXKrj34uBw=~KqXcO{l96XVDe;nA;)Jpf!Uft8fOQ>sJ!I267l4WVgSF@D` zr_)>#oFw7*n70zX^X9BWIWo6uC6vy5x?c1>gS30c*4Zk$6nzxi@hnLo6qx;V?NNDF z4vPEK+^^)+v_g`8)v+1ydD%2StQP8AXQBqYoo3kds0Xul^QLhgLO$idW9DIwU5_zv zapU*6Mjm*~2%g=6472NA^@JnL;tDFfx4N-L0!rjSPV(wW86d`_8F7q1Z&P7ui&B&0 zP6iT>)G?o_`_fif4SqdabTwU%$9TH2;ECMI;^7IO>@LybMU1h~H_~kQr7(p8z&w1^ zZQ2TDi1?l|z=nv!l3;~U@yPRXKN%l|vVL~5F~-FuMuGV}vTyqG{-?>d2g$a3$;hA; zwQKD=s8r$8mJc7>-QYoV3nWLk;99wfr(nASmGbS#obyJ+TsxV>9|z!{54_Zuo!%~# zBesWu%KrNvPmK`wN+(`*gcLcL>`X6y%NfL| zYOtMLa3GS{50xzhhOu<*!>e0afwOMef*E$d@&9PUtnvW-<;>T8?0Z{WSKxfy1b^hm z;0Cq^?9?w^zPVczoYx@*a(QaZx_Bdt{L-ih4Nm$g`!%j(rxW8#n0*3ewG$8au%S&Tq{=vN1ByekRdEr1^t(AcXK@|f>a4DM`hY#M)o9Tn%rA~Koj_wWCST4=a%yRz@Ly7C$dON) z%6?`y3n0I09y8_IL|#F6Hf3iGb?XcL#gRO^QdJg#El~GqVui z!DF#W(>;Xo>6HF=SyJP2wsQWW!ho*BAK!Q1{M-t!>O_C@(nNOR1Z2n3>#?~~KdKQ-!)Z^k&BHG5lE@E>ngbPdEVuq8fx>%m!{Zt~|qNAtD1m~xHrzUfpJ zazCih6ET2HGkta$det2}I&q|p8t)3t+>EGttTqA7#oZ7_-mZdghmB0L*AOdc=2y$= zp$)4++<;zQ%hzCShOxB?_6l33t|>pXWKvWoTB;VV zo0G25rVhMs4(>n^OmOlA6_eM(hTmQwQ2LfLn2qX^{QcoW6FG$Mfp=>na$R6Jb)55h zty6#LH9Cx=Tl$&utwp^G*ryq@v955M)EF|PM*XZ+$e#42p*twMYUmZaT3=XFmG=*? zNNwI%blanE28ul}8B}L*$v?uH6p5_SUQw{~ zSs~=L4#Mri4rFB%rUtWpi7J~PcxdqG}u zeuFqA+)3l^(v&##v;yq4eucTQ2GEG@m8LHnu`w%hf%0E0Ij3Cf_|_lbW+u4DXC z42l|`fYG<$B6HkdRIY8P-v<9Y7z^}hCWM+79w~*Ea6R?B$WBkH<|T#%T>4EPev;qu z+qFh>EPU<0&ic7D7F3c+HouRhDSJtC*m{e=r@O~9v+nW8-uI>SdwIGkN~Go~3z2S` z&Cnq6*GOu_=c8-)VZN)$AvIyz#@I(pQyF?hni+=B>`dX$NlO7*J8hr;5F-~v_II<% zdkDtPJx*PZvanmsxEOE+HXy^bNR)AWi!zk1k)+RejSx=`7)Xn22yXe)GsfguN@+Ff7xSq9!@A6w|cWV+<*vvH`!1 zgE{Hy2#iM4YH|eJOS+udswZ7;+g@1x$ zb;EIX+f|Uo%w=pjG}}xWrhIuJXLL{Akvd$;|PS1 zsmbO1&3B=KoB~^*^p0AD16~G6jj&)6Z%JwOs*I$GaQ1jtVkyIrz9}uWy^1M^xyQvK z>pX+2`x*b_F>gA{B{T_!TW{F-T}0l1fx1!>kvhjJA_osD#cbT0-WA^VBD>k+0m!zUCpUv^nbQ4YBBmXYoloeW8Z*9Fd8n{OUd;rWi& zQ#n-3u^-8*&G&2Y_ez_)Vl&YqH;wl`IC{ijz6*ALwCRmcdrbQV2P?>6;l-YaCuyJT zT9d!O#9YzBx(##Fqh{y-^L4bJcSwvqiyyu<<)L_<3|p|pau0tqHaTOiQ7bIoY(6B4 zy~hDA+dY>4=?HOGm2u;x{MApWWbV4N|ROkKDcWw>m21lE}i{5K^1ztJty#L56DKMmJ zRs`MmOWKZT(xPS}jBr5&8SK0Yehg#tM8o~GpT%B2>Q&+?{~vg_e2}QoYH0_J(&Vu| zCl)xe8)+G3_QVQg%qS{qX@;#&8H2hPZi0^W(_<}thBT-3=f^_%OY$5OY`e&a7Ns=x?o^DoLz+4px-EqY9> zf%|E=tUpZJVbcX~U~YH~-1LO?Qf-b*v~p29t!^pd_sq+I;`&EvB+}5^)ad1IcgL&z z3l~hRwwqmeg`WF1)a9q?TwvMTRTBUw9$wVtqW|G-z*5^7i|p{RMA;FjO$WnuGMRQA zrex%cpx+)TqW3#gOmCvFO+AG?c$SJk^A~y7JhigVTb_IyWxDl39QoWH6ZkL%^ToZy zBC`cZcMK7EJ_6kG{A0UmZsIm(v_7HQvu5xHgxkOMms_)Rl@RydqRq2lshcqc8wBf9uAz8T0v0<}2 zg%Z}b4!X+?@@(6DvMd_#N?Wm-JcQKjlN6SGt%yK zZ3MCS!qhF(DLz%akB&ZyT+{P0d=X9!jA#S) zLbnox8)!sf!$ONdVZE$L@Oh#F>HpN2TDOseGwxxNqLw9o3#OK;^-_T&wK@Z0D;^pg zj^Yj9N6A`Y(7$V>4eRKH*3`e$IR*L?AF>&B=B@?msQFJ;){ zx+N7R>=5iHQgwWzjOxbB{entWfYR&oX^dVxilQk#taEi%3xqhbm#R)=hTmteHwBfp zZU(Rr{N!Tz{TMDpRZui(F8!6RNE;@=F;0rDqHG?dm{$w;_5dOP2|puN zhG6u9;Ra?=hAYu|xs~YoRg|)sHQ3k?oiksFo&fL8&(??c2HlUe#;)t+FnpRMTc70PC?bK(#FIdw?y(B z;FgXs@FEl~FNxeh^ZW%1kw6>VejDe?UU`I{!L#x6_|4w7+nS@cG=+qyYFZ(uRdu8O zfWY2!*Bq!_A~QCcPOXs7yPu}KVphfWS;VY{tzA5baQ9W{Oi(&|(K@r6LQ4N6bXWI9 zam4Z%0#6&G3F{HDc(O9@7T-vQ*OW*BZLM0K^bvuCDD{sg`6~9;x`R3VST`!#QD+GPtg7P z{qGLshNVCq*@40VN>__qfo+MyR0Nd#%wAsG#QnOf{aknR$@#D~KR;qH5w~^B78jVqDnGrZk1+5RY&@Oz*}Dl#WWWc(zxT5b=M|mr5#g==)E3kPxSSa z3766St{Y{=lZP*Frg&Ydf3VQQ!`>suU#hQoJ|EONgL^P?{k!nTRalI_)LPu+{#0n> zFOV6J4gtI%yR*bpn_%7i49z^#5Gwcjv*ppMdAm^AW&GmfjCG?oCzb*Ds}qUv?6C8W zA_9(^$*5N5N6ZnK&7_j2EN>T*iHZIilUZK5j$i7eU!m<}t^~z@3xYEgdE`gv!%P2P zV_yLl$FhVQf=hs)!QFj<#ogUK5L^RGa3?r~-~_keuEE{i-QC@Tg}3C$J@=mXzVFSz z>{NBNRo67b&Od)YnkUVCCCFQPo&rvTenPe4BA$n}6=QiwAT+XsZ}NScf^y9;#_#3U z{$fgt(jINAJ1gSU7hQF1up7S6&{cC$ooU!tgKPH@eD=r-j5v&IdOqT(dcqnB1dwRr z#}^ z+5(O0TsXHpe&B>Q!8hfxo9d)Rc;>z}K@T7ugiQ{@0JWG2dGCUT$MQ0gZ?*Y}432F{ zbLaKAguw4RAA@8zF@ng9`c~G-Z*Xbi$>ABd>Ve^G{oE<$5Qo`Ia{bWp(T*1l&iO|k zh}N5*U?Bt8Qor!Tqg!A@wv6HAgSrWK_XK`D{gkMTjBOsB9_^Em;)jI}RCAkb)^(6( zg}0_ZN>gqQ|7@VCu)N;{fq;=_x2Nzj=RMV*N{OTwv+C9L+l|Q6N7*o!i|(HlA|E+N zg2s&XL(sScX$mCv9SDn1^KZ$bwNX}*h6uKr;tgOK?pEN>SZu;%^>I$FNSCIckgg$i zDGN`6GEbWy#x>@T*Sx5;YS`X-28JN9mKtaFC2wICQ6^6BOVzFl%bYmB_CV5g8fH{0 z%u1%XH|i>Cc>2_|BT*Tm2;XqqdVwRrfo3|c8&jlI2P9>NO@Fs@!g3ok6ob4x#EP<6 z$C?_g633?7PAh3INMW5Bx^VHSO&djoPCF*%>ikP5eR#eluB5TY)Tx2y%m}neO*wFc zBROxRW+^X*tu`sUzT}c&SS3YaJvL+HZm(*mo_g@S!$#IvF~CQT+_>hgrYP++R%qB4 zQc%sx?BX7h>q&7L-3;ETN)6)&XW#*NW(LR^XXtqUta|v$F1*qttgzFh5{Dw{O0YX2 zKla|67(U5od(P0AuzmgjFrD{td0)pf>9yyiec-RT;3{&eqVGtLdoJ=Qu`zIJG)QE3 zLBh#CYBU(isD1ANB+cCx#_$nPA}iT#A>$$HCKCn7@Vl%vpOQOXyB-t`V<=lUE6j0k zn^WQ@Ros4qOQqT>T7*iIgK@R;}C z$$vJZjP>^*Tk5$JHEsPVE|GC>YPCBUVT?WwWn|lOffG_o62SkCJWRt_d8Xn^+pN>z z3E@P783LWhKra9n{##}gQm1TIwf2MF;gIgAR>bLuxwikOZ9&VB4WZ%NAA*L3vNB^B z5cOgBJ(viw4NA#Z23^NQ8n8g`s7`*9S<-2`1||7CF~CM04~f zMj)(9(%43Xd)Ry{LE)4Hzw7}HSk<1=S5kp<4Tl8X^K=O zwy+j@p4BP8te0ajA5^$7LlL^ z%{?Hs2xc}Cg!`>_qHfF2&|LeNc+D@f?kl^2CqHzETQxkv_awDS*MhNI$9!Yb{VH5H zf>!ZB1kPdV`QTKxd1~Y9Zfy3I;l=6NxoE^R;&kI!2KbT#8O=KVIZ()OgfSitd*s+X z&ZQo|rAo$0rgTb4Q2q)CZf6H~8ljnMJU=$CIA>&^RCvh88C&9A z^tqZbU>e6_P;Fm8=VHNalP1S_7$|SyE1$x6wp=sOvm_;2XQ9Z`SMb04UIxs;vB&AR zZ|#n%Tyu2QW*7N#8q$U@dh%=DWCqJ2vlLmseE)v6+d}om)9&q^c-D4tVnk3y56adg z?iOp3C_f3o}oCugmT<4hQ90n&^DQ>*bltr%qTrs-*Q8vFfj$UpS8g z7KR6HBDwc=`UEOb^>&iY0;7Fb${)r_=%KoFS z_|>NA$A&q-<_*Tbd)07HqsT6ITR}4h10X{T&A`tXH$G|{P}A9TM;409x4K&0?3Ye0 zrm}Ol68zWNIDf)<^)FU9j1c$#oF$G-8_W# z5VEGqea%=;$tL1j*6012D201BxCPSHoTk-7$Pw_Dg^{uVW(70OS$;~`u8zG9QH(o} zLN*lbM7gf$pp9?0rldEm(XT24dMLRe`MD>4a8?aCk0U-8g|haZClMveX2a}g31lgf zW^y;kWYJg)=Wn+Er0KSQh(w%cWWT#$Tih<@xdl%(J?@Yvn<3_I!0j zyROQ~LMqOB*wZThDI+B7osvU%|Vlp}BmJCT+{_*6l)H>_K|U-U?Lo>t30RzV)&C}n-a7&RT~np(iF z!}H;A)Kftl<$T3XNX`oRYT0189hN{VQ~z`lJpZ}5(NdUR3dM2m1s{bK8s*~WD2q1R z`LB(yu-1e?YJe@dmJ}Bq zw>cuLjxpY|j@DkBSZjG3V6ioUaup{M%(mni-)m5bk>2jT82&LpM zb-J|fWp+L||CGAzngEm@ZfgsetTzH6e1qgjChK*sPMsF zmSGgYexv`)Gj*F#5q8ao)z{nISRUcs{oL{JX}GM^Z2ILEB`vh1TTo!XYDX#dk?G?{ z@{cPkLBcVqVTRoJ<@5Oh7`BKETx;-W7RM)(n61;kt1}M^_1~s6xD-e4aom5ncG+qS zDj~hkV5(pJPCj?SB5}LO2s0I6TWg0<>)3iiJhGVfgK(pH|ItAbQa7ZtQsj)J^}u>S zJA+Qd6NKY($xhT`QSU{Uh?fW4vi{+L{Lsc10cylq=y|}RY%o!||AfjUHzl8g(pd@V ziPe8uo>tC`0?k>PV}T^mG{VoKmi8{+i~?3i6K|9!VK=t~nR~(?4NqZ5V2+m&*$U;F zH8H-n$zTQhPL!09%)+Y2jPa-5x6jKe$TD7w^h1@%91Q0(;@%z;iOGbQx1i^p<5o!7 z)`Bwx6C2|{Dd)gvFJyHu40SJ`V2(NfGcyO^#o|R~{hQ40H+`L$i-UxU1DLvwn3wlO z0p6Gln85-ulZX&83o(kPBWq`jc zBW6-{wKXATQZz6#A*LW^`CYl9ft?AsESA6WGRc`3TNns~T!?kRDrRC9PEH^PF_43q zSogQR2)Jf&27BTcM!rAj_ay8!~l1~&G#FSQxD{-u^8cCd3a z`AZT3TZx)DSs0lpO9;Ox|Lm-?i9N{C&d9``_{DL`|L7YBSpFYYK+eCL^?zdpd~xj` z^nonIEX-WsApzI%H&5WUo4W^wSPMu^Lu2%+slnaUEltXlvVTiVc*FmdQWsVvM{oye zM${`QIQU3oS6EW&3uOOz66)xDd?##OxZ-jl3{vq}wK!eqD9m)xsAiVgDm<=_vPlnz zX;5cr?jx0C%4f##wPq+Yd>f@EI5=2v&Uh1n!sUX!38FbBE#@{ecyiZ z0XvRKyJz|HF9iR+m|!6>O9ma$TZn3yEfxAB_h5GvJPX3k%%M0$>s#V;;l+R&`XG$x z4iXyGuFO*CuGQ-jZL9L-$KJAfRez;m9GRAE0aExd<@;dust}HHByq~nD8+umWFrnc zU}(q%j8q1c3QS0GVlD2`Mt~4Q6rJL7ee`lFqLrgyD+Ou2eN{OoUPm|-tlUT&By~Tf z!9`93QW;*+NmXfW;X}`c<3VM`Px!qxs=3hKaHf{x(jK*V?B@!Lev|h(r7cKEkk**Q zx0G&g6USe5TWkz`Wz|ycIpcgQ1rsePf>n{lJ~5434{K5*#=StQ^@V+qyFCfSP+M9# zqr;VgJrXCvHLolzFDc)aO4S&V&fEW$Q%hM{SjnQGMZCSFXvs^n(`VotlF}AZiV%jV zL5g`Bu`d8Um1Z4)w}JlVMsz&I9JQEU8Uvbg3xj7tn+$tFTZ~$;J@KY5Oz1c&I5s(% z-E;%PukTmJbiuiVFQCWuOBk+g+++EB#YFxPcxG_wWQw7pA9F@YOXihc3tPoAid9q_DJJuf)*Skq zHWQxhKHw1B%4T^^B#8HB_vSSPcu4v%wz|?Uu}sMRiouxs$etm^U^C zL?+m9Q0h!rYw4ZomCG;>V#(R*N0^O#em5#vuWS+i#Asl&r@yAtTME`!4r-)`ijtHx-R7 zGsNeK@nVpqdSp+fL1DEkRs;6ruS${#{K@l7mmw>y+r*Sdc9~i|@D)^UZ}Ir;;HvuT zOmv>AwuY5?wfWQ+QfqCF%5p(Ejd#wt!-$`E$TH$p9kg7TNz$Z{-qa*)zLx!HTRv|{ zXjTIyQjFN@k83Vhc}P*;4yzuMl8(iuBU~Zw9Ei`O$xJ!eiJpiA!50hT({e0qm27$* z&4;VN=6>)w3h!9^=WJH`F|0Z6)dvQsn{nZ_*eyXjX0BNL^c3b{wqf33z%V!Xk2NO) z8-tg0$3ok}+QQhv&uaK;p9OvEd|(UEz+2zj*4y0s@t415&gcMtP)g92NU6w)NSers z%81GZ%`D9=%_vRT8{hrUDMy@&Ii`Jybxb4svqV=1aqbu=4$!vvajE!y)WD4DP5va&AWI@Qq&M9Tbx@)~nS!3$Rr>Cyn*SpH`;UV@~@ey=%-LeqH@IHDmI) z8vh=VZDMT0xOn~G;IKGD$4T`?2va(`J>V!;n(T{#RTE}r&VdY_pcj!_Q?SX&Gi11C!T^hYA%O1bQY zHjl$_lG}i?)X;o6#`hwoOlS;s6OEmkn{d`rbNCuDs>gk`stfO!#J+!Zv1(8d>^D6K zb6wkACGFx9_ckfRBq6N`o^PLrt+s_gbpnl3^xM z+$iNSwiqSd^o9U|DF_v;z;6&V?ZfcsnfB~B-M(Kvs3p$7FQ4EVE}OW@7p@mcwXDV& zoRj25$XtrpHlM8rq@5u^s9O(nD`REts~SOV2TM*yI4qL0YnU3vw3<(47M(tH+#oqN zn^daw$)2QF!NApNLawi&?pypEz^P<|3 zp5gn`Ld;jvs}rjWt8sKYoOau=kDYf0aRxI6H9`0)cQm;)JH7t>6UGn5 z5-cB#X(~r*FcGS{aVC%+WF@{=^?f`2Fu=slz^?H{CMV~nJ6zf|#wf<#uc=*avkcR+ zd-D-5mt#n-RBG+KfUi&X%WFKsL(5JWnq-{iV)9=8|0@e(RZ-6n6FsdfJl{Dt8@_=uLqQJU$Np#9HWt8*{Y5OiFUpF z3PX?k2AFS(XoMLi-pLtQ^-bt2Gg{5pWDC-0>bYXmEC|B3zJs+8k*2fnTQk>l2yRVd zrl;d1PwfOOpKshSs1M}c9qfN*R5?mw)P0Rsu8$4{49K;mnZeL$nCcy|h+^vUBe!*m z^+qFo6_RF#HhpsvRfMw(!f}9$gS-bSI42qz?ftcfJsqTX+fui@*aP4)YLQ9cX znI7%D{hBjVEN``!h#H0(5lFjg_Qcg}6`N?EAEh0)To-V$DQ;3j4iIiuaauSi0pc+F zK_`k(}d6}#qtWF?;2vlrsl*e7vsqY0@{2HZ7Q zmP$gJj~kk~HuAufAAgG@l(R%Gcv^Dm<4Fp&R5*OvttdFx6lu`D8XvRyO6X;nah0)L zrfFQ+-v>g>Vq1h`8@PGrHpDy)6k`+?3pCWJXd0@K)$;?+r%(>D@~)SpysD3gxO^y& zIDcCE!Teth>;x1(A}_zbr+wB=>lElTf7#h z6+efCfY=={A-t>XT45FC90iKkNc5ldC0%+=;AwKVBlZ7;`ne2Byd%s5@Oie&4|V zUNQ6DcJCeOml*uo+Q8dD(;EVAa#s7jXU@5&Tk%(YKQd3t4Ln(cknXs|B+!+3yJ|WK zaS&Y{KKM=V8^Jr3u|!EX^nz-A3K9#&$d^xprFQ)tBr318yhSa{Qzn8U7?3!z77&*M zAV?fZXk}^1?foD5+d27xB-8$AoFlzjDU`-$_o7Oect=4)qo|un0?@zaS;q>>M>o^m7wVa!e*Blpdl(->$JhoUfVm(_IGlEua6!?N~M-n~G@)hIH3p#X9(`k5#%yn-U$^z77X zQCO8NL0ZJ1ZK`&$jY@4%cZ1vs^aJDMvCgIwL~es8tGKaQSZ0md?oARfPWTn4FqbTt zRyl5YgbU=Q)Dd>dXQgOAU$k}#5gYy`_Q+wbg1}HVGCOl%ZYmXdQ_n9aP;PENV9Cdm z#Yq7s-5^*7r3bE`nyTOI-J0}Za8hs@Pg}IL^?hO&_UgWC4jvjTpYxj1hl% zZp-EH1BS&K>w*=bl_R&uMQ;iUPpy>a-nb;+eHTG9suO``eeVKo&jW=Io?hF@Rm0q6 zk(X6+#H>^Z-``4eic%zI)TN4#%Hx%iW9Co9fr@J$Ja_P6ArlFYCbZ5oN3^p=P?}4U zZpH6>c|j(xMb{v>Qzr4}8F_HR7~pj3W;@vV7hX+U_^&q2zxFR`JWC;%POi0HeBUf0 z;2e-a>hWtjU4H+nFlL@|EDV|=;{(n9JE@AUfs-pOx`3Z` z0~r>1RSWS%9^!K`?<6H)i26q22+ z)0{zI-u=#X9#*lBD!bbpL!|tXImyWaJRO&6h4Z~D>MH$8u{-YSk;cW8eyhUJi`4I& z`u$22*Doy!XRcp&R&S;8D4=cRifE;!)GJhX^^wPv(ROjBY>5%j=dxe*O)UtQgyyj0 zX-Xz&)#Y&OJfDfa&hcC5^b>dR1SU@sXnn`MtxTkAXV?11R&o&OwC=iM|J>yK&ABL# zOXFgDOmqyJ72&H)q}MyfNI+TfUsLmUlVKwxc#gHEv7^Zv{8fjpkEko$M7W#hWG-xk zend(r1rpOn2j3@rake>k_LT5-`0eAkwLG^`4rWbQ&6c_Fnih;YQRt)?)uHfiPJX~R z;bu$2&Xtp_Y7fs%nnaO{&?cg9vgTNb7n_**X+Ouj^)sRjhNnZvMW0JkxoJ#Qs%24&o+K0m^D(C@%p7NA91;VUWhK}4%;{5A4khl92bY$r_l2@ zygN48s0$Gl6iMvP0jX)dZyDl+U_L&I%AT;`4u>+=o^1eI<+WofzO%y0!5{3)88gaO zF>bGA9Ujg!wD8_D91F%mUX#bC6FxyrpvRuW@YSZbyP2bonaQYLEBx80O9m^<&3$Bd z2o#-mAI49Fef&eTev9h5DELOMtYLD)MlLL;pFDU?PL_5ei$vFxLfyPeY)iaLfF-W? zx%%bsZjt&XR69tT0w^QxexFq966lzk-+K@&P6@cvA8_Y>RGNMIxxAppWK%rfsjjbb zk^z$U&P{XfY^HQvx}kc)|pnrxARg`BECTNNp^BjsQkes@)VkQ@(ntpQ$blIIyOSU zi13JSd`4p2qkb{?wVuf2%}w%{HmLSJAuhfkhlas}IvMNgUGdRt_HY|kv?&}2vI9P& zmIoCv#oC$-AkBbDv>=pv#%ro~P+4o39=;!f-x>yRhQ?jMD84ewlr1)Euyb%88(H@T zbMd^Vs(62!?UhXV4Cy`v0)3xPS64kNJDPJ}bH1JgZpr3i-xkE6p-+dOdMiGQh&bq2 zK3|ONUk)hd>P|kvL`szqmUWoNp@D?nfB%fI-L_O`0w3^;%g#%A3}crxMYqP5_uAaN zwB#5WFy7Hd#T|!uiM2v&0eh7KCgolz2r);8R*KD5l__-e(D$(Q==&9VikKZ=07Wq7 zZ$mTz7+M*2bWFCgJPb#1w?2ta$e%59E)v)v;Y8Xi?YJ0voTLhZbl`dB7v$LKWy$hs zEPCY~w@!8L=E2N(B5n|J&#{dC0di5VWcO#Qx*_~HKMLyCan3LOXq~dMuZGBG{5b<> zBc0gVkIiFjsA`l%PM!ig4{>*@Cu?|cXL=mBFBYEmoT)i(P$^N+c#6F%4K$oN_!g^V=+Es=I96ha_8#1PbTsta>3~4@3FZUyyDnT@Yo_90#>FC9{?XJ(zz!|y2>4~O%F2p#TECZ`6hFLwlF%sg zQuQ7=7Wnb&`f~Tf2XAWJJGML0He7mE!qQKY{%`TLLu?@MQ+?t$^3I&VC}oNSj3I;- zJP!s8@58toK0Nj{iBJ4XWe4@ApC9ULPoHPKwGW)31tMuo-Cf8?=y%p6^^c{|59p9T!t)3skxG|E`W(Do6A=!T5I z>I-^nya$(A4A;t7-v8>o>sWKauGFsg;PG><_nbf0=dI*1SVjK88BJAR+A;2_SI3}n zJI_b!nS9K5^|ICVL|K$62$%0l1nH+8oY7Wd-rTkX+=-2W1X-^iQnym{z%SG$P@s}f z3R?O85{0xRy{(|kEKd;m2(bVceWHCu#{U1oLKb~kD{eECU6TTVFycpu!t8`Gu;;mTkCt9!Atr${XXinoZ2mqS+ zYz7*TNV-xlaQ{)vRX<_7+zX|35C0M>W`J@NvOK(|hs)&|!a-n62P!t)f57}I<}lw! z^RqCMCteuvYhYL;tLk?tTW7BRo>BV>Y9h5Lsk6Xy<{F%^-gtdX<|!k56B4(~tBkom ze+f)B+6Y!seGUd=3e@^j$2$X4KlYnv zy*(E^%YGJ|)9qOT(ZXJzStaE8evqzszW-75{_zTXTlU25J7S#WzoM%unp*KKwi4sfrvzp05ss^6@fS za&_JJ2w*T>x@@~Sgs6E)6x-!HzwkNI2|JKL6=SS8yl zw-6XP77(fV<{}ZQ8lr<_$i9s=T)Z42VbkBak9p@p@CieB)y0cSqWVBpM zoW6u7>bb&D0gWD^}S!BJLa{MKDxU6~!H}42^PP z7p+?~r;S&jHn-bhyj!K^kSFc*(Rqx(pwct>n~ihiv7Dxoi_@Yy`m!m-ViL2mc!iFf zu@$S1in^GUd?bTH@;+9`N2NJ1_lO5m!HR>gdXe#D4{yUh)clVx%XK8O96M2A2#pYX z3#gQbvr-P8_*$zDMOyXUr7?ZDnr?CsuE5Y@rzkVa{z=oy!i>+Q%KZcNq`LbQ3TpFv zDsPod$mrq_9RmF5u_a9yv?!x;C@YOWMKbP_hhle+bqiHe2h#foVf92o)817}(+-sU z5T@8xS1v6#Q8WJ4)b8L#lgN=U#85p}Pny{Hg`ql9Ie@{3LWa9PWdq$IT>6XI?)V0J zwhJatBeksAZqz)24hCfab3u6w21cY7Ah94Chqga`C4Q&K*zLnbw)BPSMc2Cv7tF-m z=z`8ZjBmWT(fw3?WvYOR&%6iZ5u$zmLD1aazwpvWo}Ey6T$S#q@LQ9j=Ofvd4%1M;deSyn#ss>EYFvrJEu31`gYcI>gQ+R-b$ zQj@)r{B5E{RQp-w7J_ct5K?menFT1(7{v4xq2!$uS`<>Z(dE-3ov!38?egeU(NHe* z?eb_Cz!gbhA(S`UQTl?JLMZUSDhN$_;;1%S;E3o31`z1e1=p1>>9In8kSy7BrVhe7 z&+mE@+Mu?97INgETp!CxfPCP9me;|J--Ud6L=_CR@lBPqLkwRSD&s>w1+dOcB?MDX z{~EQZLtE7_FkA~qQ(Bg}S5@riYeP24sXcO-5L-6kdRpOiE?g4&rmw@qXUaXIXQIBU zzR9-jNyBxc&sE-g&p?RhW1=IcXGA?an4kX!B)mKv{}YhF!O6+>7es*T-;dE*UY`B` z|E;d4t0H8x$c)-_iOHKABv=`jsVvB>NRCak0Q2eLRml7L*M`y=+kW1wy5Ui;xaN2O zW}Ef;%TBMyXC)d zM?Mx!Jh}wi@q=n5EFO#>>q|Z+Uo>?}8iaUg1+}?KQUN`6Ppt*y+&gK!xa@D^J|R2@ ze!MqDwbYeiX@1Zv$7n|y@?C7ur}5QUQ?Gz@^2M{o-TqmiPm_C^xl+LIpgn!Q_RxXSCUg$RdoZ-;xNvA&Mt94n zwia^%uN7q~b|ym+6TFh2M8L+=ozNbS({?J&-#2Czh)k$^9_-PyBXTBu5Rf5KD6*!w zE*PX56iH$c=#L|5M(h%wIcWPl>&W2XEpWr|-abx^PDam3w$@7i@tm-DuuymVl4w_V zH09qh@Oxzc3<5S5_P=6)m-#(JFQ> zaBJ9@iP?c*`so7Y(tOQE@Qp7(;_TPd20*JEz?MWF6;}<`^v|0IYz?0((KI$K+BbI+c zsub-&#*Rkd)kmp~3@q$G#4L;~Kt^U_YI6q%TYGLMFm?;9HM29YHMcObX9U@q(fn~K zh=A87b+7>0h`!*ys71K}%m6lKE&z~)jhU03g@KuaikX?_pOyXxH1-#cOT++tae&Nz zH~e=O{zP&82m9q>XJU!~#&EGAF#o*~1KHVG*@;bw|Ih)rI5@!r{jw3;{H0?CYyYJK zBfd;R zAkUBj5&!^T7BRDQG_rrbTIx9(2^ksK7#bn*@*+7n+8gOvA-SfHYN|(XHeh<6=;RFS zy&|u5WwF5rAm{@M*=2+DZZww8DvrvIdOv*7c}pAC>>kXMr04p5GHhD4Pk#j)73ufN zn3cbb2j(G zv+K?89yof=vUd$ry4W_Nr9Eqk1J)EKW{Xil+QuKoSa(~=hdfr7zp@VuHOYm723qX9 z$6GW>4{^1u#XnMMX#_YYfb>-Ipg+cX{TwpWYuDDPvf ztsOt1HP}D+y?w8=ie=pxo>m~4xP|`l4c&U^$)yPOYMx3$0J3Km`|+w8k!#Cht5(v| z+}TyV=+brk^~uwf78A6Xgtk^a-9!PM1g?s3@_1>txiD@-I8SR(a!mq;&Fh=Ga|5M5 zw+WI(Uf1mYVP$=-j2Rvr!yD){tOofsgrg#nxZPfts*}9eOB>0oBJayV@lH`)tpW0= zeUa^*1|(ScQt`eVYS?ZGdTc+T2=X@Ex{Knv%57wp}b(}e)yFqn*MJ;Zl zn)c`*OYM}T28#qg{aL@LIW>sv-fe_NpQfco-?GUh_rgwt>02}Tvdd=KO1QMl;dJEy zVVi!-c=s7&;1kkE5%fl*w>Cdr+m1I}BkmgY1`{8GkreOfPTnEDjW+GiHncF+CluU{ zBR(T(sv)B{i_6RI5?&Jr8K8scPr#V;CvGy{v7`PVApC@Zc9LaYvk_I2&}~t&^0o8G z3adv0!rP*GO45y@i$EjXF+6#{3BnQUej)rXbBrVUMvLlO_40ZO6WAVH~M7H~C z^utu>kZso%9`j#H)f%dV|`?cSKJ7>D^N>0g<2o z*kyVUvV^6eP<%mlqU1clVXI0n6(_ozeHt-(ALPo6DWw#6*Q@8>|K6=nHhYK^@cxV;X3cn1Kyr6d`z4I0j`Qz&62~q&$M!x1)j)D!EB)>R3H1QQ{hJU-kF&x%zHZiD9*PMS_Mr53ehrf_RFZR`kPvP zf%VRxth!pFSTIU?iJEHPtMt_8f`OvcGs*P;XzKm>V?Cr%j$G?$?nIUvW!P)S!KSv+ zPc3d&E5L7GKyb12vYh9+`65LhdVMN#h5_HYB~f;^&nBnJ^}pYBPfl{TbW7rBUzD5y(G}4Z(d#<pnLtr&`3d?YMYQ)7^G~(F&COe z)+XQOQ4f7`&!#KpP%F#f>}qnNIe4>gc@-nuMtLI6sC+X~wl>i2?ZJRP7f?^8n=>9Dnf9>G@$=1EB z!K`AjaOqO9dnp2PFMShZf%msytVtQw{#Xxt*)Ib0Dqq3%;|MKHm8Y01$y-PrjGDXJ zc7>&u&Nskc-lWFeETGEu1B1*tWv7lZ7{xQ(-<0vQr83ujAFr2xND6DGCxx|+yPP>Y zQ)Z1i70-w7C{$l_GX1#Qx%gPJiQ8z@y}VcNs(QbWIcat#*_b_4O~HhA|M_F3ZB7Ph zo*o(C@UP*rH8IExm`*D!(r)q;uTCwWPw#XyA zF|=ar1o8C5>hq3Yt6OMNBf0rGRhsWN`gI|#jMf-Az$%u4*wHg5#q%}z&=Ev}fk^pv zV^nz5ZyE1S_ ze1$9;9Ucw#JdSm@uI(0Mim}+;-UN?e;=mur*nZ}tBU(Vw^PYcQ2$N6~n<%02iJ0x# zaGbtR(qY69SZjQcT?P2vt}gzm?GVi757B{8fn&IcLw$7a&se$2rmaJx{!|XVaisoi z3@mnzbjE>#RLIi$#obRo@xfDv3i^iZgU(WaP7U?0^H$o)O;^R#iA1H1v8tBK z*>txPF?P*!K4Sg$Nl?^;fxp85HKn?v%+vZzy41?T>5)5Wt4sr1B_ZqA*&Hp4oDdy- z5SK%!!>n8RoI&D8pZoBXq%acy0I^e3HMv~P^>;3b@4w=z8F9CKe}yTMQ5u0Vx*IZs zTi#1TL43VZ!xB)TH5-3MDMcSyg}z9+OJQ+dM-iFFd+jhN;@y)Z*GN^k%Xyoj z>d9ich<3dZsd^dvMl-(kP-A~4fm3)A25;z9E^;ej7tz6Jh&)S~ai#DYBGl4TT(2+5Q&Lj|!O9V4->&8-RzB@Lo+lbfP8 zI_iP_$LX;mHh$7XFL`;VE8)N-X7D(xl$SiMsK<)aHgLy?`EBaSPsFjE{6pWY8cu5E zp&~B@Ec~{5lcMW;>?~%Ez?0w(ew{8ER4ymX_k_-|#m9O+P#QQwi6ZM^o)r>LMsIiD zhRG&uZdjK{sMCfExalYt1~nIt-6^jqA+JlNWz4_9l`Ze^%z>31CU9<>SYqRAR>b61 z=H^laI%Sy3&K1E(UxYTnm-yd1+kP*(H5B+!4E1T5M-E0)gn{=v-HZmK&G(wepY2p= zl(wNfsNJ~!qd*v_wcJfDxo%8xv-1X8g0M630Z8>7M@(iY{U$ph z=?idt_HCDx#g{%s#M7@3`aw^=P(PT)GeOwb$FZLp33yQ~2%0g5Zt?*AxU-mkim`b$ zAnH5%lTAnT*VfDaCOA!5GRi;7gBhElC1m--%J@Rbm1TLfgA2EXYSVwaHC2oWAk&s8 ztFHdo+dC+uK=)DM9vUc@e#ND@(n6d2wWh6%J$lUKqL9xEN!Aqp(%`l%UnoC$eU_ z@Fr)gh4a~ismlQK4k{%V5=k4x`Z;g1^%z90Exc%dPib4b`T5g=hIopTEy>c=V z1xNGpyiF0T?)BZC&_@s0(W(#M=j0U(u)pkPj0XyDxUl5FFGNFBQihEJt)-lK1!$IJ z39GZyNA>9|KV@VMQ8}_yNyD;LBJi>;hmdt+4K4r%BBwl0*X-m6jzskvE>q@Y3F6N7Z1`DpB;SC=>Njy)l_*f0S_`kjlkUr@B! zhfP2i)}{#^rBToaCUj4<%B8JMB2W|-nGlHLoZh!lnXg>zUNt{`)1G)_ zrgAaIn9E8wjW`T|1xC<5<0w>e?#CQyRaGUe#q$_&H+-Rz1z%CYr>+#@CSL>9oirOF zFqmO%0=a0vRU_@fKRF_~#9ip}Zi`f>&b(c=V$S-!_LX-)6lEGg)sH8-F9+6Jl~-VA ztIFS%$l)`$!XIWy;-y7oJ~|Lj`KPA2z{)=`~xy-E?kd3(PpRF*r2$tEN8`F^Ljio z%J|lCv8~HZkZ0_6_q~xwqVO0NL(i+MFThWY2o=Pe>f?HREav=MVSHf@`4`UZu+rjJ z!75>!OUS*U#ywJ73tPkdA@>xlYVvMkYiL%>U~)S!ydtAlJeO5q-k~kl5V=EqEzuwa z$(fwnQW+Ur;WmSbwqSc_Tup*&IA+V0!=~ti6M)Ioe*}bCs9vDC??w~O>GC~<&jCC8 zjd-5S_P|d+(w`&eX6GY`>k3tInXkFFrI0>MiCyV$_I@VU#>1o*V5QOmi0o9NBB*Zl zNIoznd7 zp9GYVjI0g+6@+-cdkJ5>#4KK}&ru6jFo@$t;YDQmcXZ=VJVV&kQB=|KIhFz7=6(U5 z4`w~!vjAWg2rkRIpeRCS;WTL@gLg8j6g;pfbH)H$+Ob)=Ugimlvm@}u6uMfpQ%NlPi$N=AOm#Xk3YbS}7%*07{j%s_rVawq`= zVt}42yuREfrSB>DlSt^dUVKjs`D6gXL?}%j$yfw%7c)x;(`F>YA?U0JkIqEd33?HT#22un+!U=78ddKd!W*-|t8dt>3#rHlx_a|G{m#BL#B~K$1*le7|722AKbMnEa z2dZ$WSD$Xli=KZ>7FbTyC=YZrdZRhH!kcTVfn3=1gMNg{*&|M)Go`kb#IA^4kxo3MEH%NZ~dO|#ZAj%DK$o^$Opi9DGb~>v3(7l zV}zDCSP=VIKEy95Z`5lizAf}!_%FUoTC>HKaCRY!a6;X;!W_~{4lYlYbfVxgr6Ig7 zq{zyTWuBgKor1m0cB)n8;efJHP?haO7kK?)P@BWqvyXb;UGs4$Y_*~60ams2;Daxs zzojJK3F9wlaNbkclwr}@PF0Z~XdRovjv^3_alQUZ%*A69Eq)2JX%=%xvCM6DXjb2% zz$xSM)6A!Eg^Mj+{`PoHjQBXaxnjZr*$jP=tT2&0?1ZEPgZN*XY^+^)XiCL~;kmvn zwz&W#EMYW%BswX_r~rD)!Q}1*MIi?yYBlxvcSus-X|)ebqtj1JadSSwn!G3ffVLkY zl;7RmdZzfr;oZHz^N85{H=jXF3YPIbZs$Dq1+6l?fT?#%@uYDm1HEyv)$z6WR~AOj5Q8_u zY70YD@ccx}fw$BI1etP`rS$wxHa?-K=5tB=Evm@}6gRQ8C z7?aD1|3drvKFkXRfunta*U7WiU>{~`RY*I(lS6y+$cz|pNebM!ByJZWRSYoDz}C8r z@y={VLW27o@K~4vDwOrS7!94exudO@tD_aGBmKhp;)BanB;FW! zXu?9jvRtOpkX~)|=o?BeHsj<-;VjhU&`Z9sAA+DAi9UslX_|~g(-`B{MIpb%8q-eMKD%D}Xjb>Y z5svSd5LDd77Ovj0);X9=B=jP7NW+6T+`^tcb&emGyf^HSOmFGPBgCf#J5pMP~lD;R=Y9yfnt@ssg=~|^uGOFSE zU63=K%g4(QbhzyV5|Xk(Hfp?sOKNs%@b{yyrQR=;x#!7)ox0NAZn0TK!FVvmVQ4`U zFe$!SrlQa|NN0#HL6P#W<%q(fBx0My;BU%4;9va3OEZ0mI%k!;_$3D>#AK#7d@7P0 zt?!Z`_?=?ZIhgvwOp_q%p!jUs4b4?bILh#|a;<;%=5$$$Pdln{jLPYP!#ME(%gh(9 z6B3s6g>N>iZfhX>8lq^$=!8|8Mb@9Vwa(VdSA^2r1%L}owIq~ZpSU}( zDFNM(1^rCZ23Wr`Os8LZ6H!Vg(_1inVhE4&i(+l6rL) zb#X+T+9!F1muY(Ch^pazIK8GL`e6y(c4*HBf-o`(uyr3KMGElC$(K8qnv%MZ8kRbc z`aRM)vNn>xH$?bI2t(*k7{^96cYgqL*>#tHw@Kxrm`yt$KOLW*Z8&2{>`an~-h$SG zK0thx?wpw@+al8@#s0*POUpYw#HbMJR|D%e zYY)$(yxJ?9`I>E68yPPH>-d#c&xLO(Y0gJ0wp)+4Rxp*7n)0k0$7U{u!KCynGuUGf z)=xF}$Aoq^#?HqLVQtL82JLXpoeg;DpRo8@HN;V$16MS5=%kGUL8XIll#J-NEsl6A zn>xD!j6zX!S}9P2p+X?&BS)quyHR`JQK{_msqnF-I6s<;+&bYNqm8dCTG&5J{Ndg7|EeDjylk zB~=rc*0qo5S&cJC}EogyTE$j4jHVTwP2N3PX< zrN5u1sq$eoFx=;NA)!gGJlv!8PUze#FZ~NVgp04xSKD2XZ;sbcnx0F-J(rX=nO%F_U-T}Z8R5T%_=3{ZI+j;lK!&nQ?IginEMl7xZO z(u9E;$BoeUO6f@7qO|RM#o2qS9C#-_zF8@V{ZylyE}PP|4ac7HO`#$&e;(Gi4!c%) zV6$su_%)KYDnS;R;a50>zzTtDJUs%R4q#Rm^N34Alj?bz)@3({Hwt1;H73P4ZC*u| z#=k~vOubV>NK6&Hpl7Fc#5O^N%xjW<=v+WJCB3)J`Z4G;qp?KF6@!V|nC;fxo%)^n z{Q^=Sv}g|Zv`eF1zEQqYzE!+IkX^G`^R2gDD(fiksJt;leZ0ob(l+8n-@>HjJ<$NS zNpb0Xb_}GR>Z0-sB)BSmPeJ3F`nb7W>jhy;YJg#0^9Vg?WAAl5A7O>D?}LL zRkv3q$DOCxu{Wo63fdibKg@vYQBM|pHWqm=k20iFU7XX-rna{BDSPMhYE;DZsm2HUcjm03)+t#-&?7WJ~aF zo+FdxBi;c<<_<#78Vq021QhA|8Y?XM0;NZ5giVNXMopw3!-*5WhWuMmf=&RU4rPib zi8{8W3Jbh?NZ%O2(Xs2ATGAqg%LqXiUi}C5$e5?Ks9q_saiYf2$&xXJUZkB_W=VC0 z>x%M5&aavZW_81wp+OT#;gp66)3PIP#}Dj1Y+6jh1)3@4QrcD@8EyDJTFGFsfkpJ| z2w>ONwEf4!+ai)&M@Yiw=#WXG2k7r7<>Am+^kU6r>hnJeHn5UOOT~e?@qc`-{EYt% zJ2uA`k-gq5<1O?pl3P$6k@lhaoJ9Rr{J7YV1od%2Yq6zGfQXs{?ps-3; zhf`@Z8(SYVGn>$0)D5;vsT*mM(Eh1$tbVL@ycE)4TfeO_jB|jqM5Pz!kk>j-N~r=_gxyNqZBCk4BGW*R5_7{mheY zli+mY&l3_)3CV0cRGk!kcl<-uAe2ezh$}i1e*`^POn-tvmOx*}bzpIl(7tt0q^CmOEPfcQrX21xmg%0fx*hImS zV8d&L3!e%&lu}{PfUZ7@zq^@$FfL~8DUrEjZ*%HncA2fQ%5=k#eV1fkki_Yf)M?#` zUDfhF%aQfrp8kH`?k?Qc{t(`m@I^Iq;rD*^wt7rYu#4s5uYmg4*}L z&nfL}DE0-1vrE?nf5c4Vuw`~4hZ2%{YL&~>EHdKdeQ5H~JAPOEVdCT({(;|T5;y?d z0pk0$;C%DILSVf2OX+h>A-)duOvdMgtS1O(mGaDcUBcl^EAAI~XuD-k-tFc#$`%(k zoUF8X-T&|!nc5i88+J*m&bL;`QdBLzrliu&YQ!k45#RaYeXld#etVqd_|W>@0pEvT z<=|#d6&AciGmaC{XXgx4lg)9)eQIrmw5Y#%*)dv@zf{32MFiAc59sbj#Dd3`NtWZ# z)nE7|9+z$QZfbUkq&C>3vw_ZWX;MTZtl0V^O7tNEx@j?1Ub4C#Edta#wN9m?CSBzW zxSh-Kydd#5=b6`TlMpp@ERBNRTFPlY@lanG3OF3a9%vF~VG$W~Zs zPGxRuPz8;@HyXTp7QNv}h8k(ARe z-l(N>_QUH0LRB53euiDjCeh(E>lmFGRc(?DsS&R#ZIOH$XTi4}0m8~CXn7c--K^u- zFa_FT(0t%kI58?M9V!}-*UF+;H+;P>{~Ie@@zATSHc8!d*E%8b4b4I8l686Rk2~tn ze7l>3mLl|HA{dhbMWkyv3|o|Z_VXlAH8Wp06WZ;vj|LtP->ijb4a4uhvB9F^$EAXj zkV;;~$nt^%ho9*PbEE1+= z#m6Vv4NZWd)Aygk$n@t||ArT&8$o41*lmH*D@5>l)O?fER=^N@Bm$868_>_G@41mR ziJINkDxTM7fRY}LYzJkH#GdrA%Z_`T(5o!^o5%ueo!@AFO23ri7pqs_um-XxW+(W- zUzWD>z)MKZueHq7`VJ_j3DAcL4#|_M%O#f!DzqajNxIcJ4qjD!EUpNW>Ua>LyD)Qe zyB4!Ls(WC9@|+1r!e(APsUV+iOQg{VsF^MPZv`>WZ?<>NjFBGAquXHc=eC2zo295*H$lEi7P12CM_ z_l^RB4WKkh6!`T(3_7t3`$w&mg=&oao%BY)=7o(12)0#dm@|?_bfkF5Y)nrNDK=`m z4YP1n*GodGE5q4JB3op+aiCxUJ8jj=iT~i2{(b-c&Xsxg`P8M$D<#{MW*e8Is|>Ej zxnnBo7SfyT!n!)y!QNM?gK3s)g+Dr{QrXFRzAr=7CKTzozY+Rhk}X z?cfB6Az3$N2%%UJT^<<(Z--T}f-ZQPswHA@1Blu{5eUOwMcyNKtt%^r#ufA>m<1*$ z-d5uWahsWmhl|esLRv5?bcC&F9k47$C%Yre&D2mO|6L9FBvDS|_jR|odes7c+o{~SB%~oSq-mU%_AwJ#Dx~zUhe4uQOFU0fCa@Mv zBxc!S_J&n6@BK4dzwrLPAr!4EXQA@cRXmhf5(-zT;FlVQ1nd_6f>sM9ZYx_3uR|D& z*X`HMb`HfP!$ES#GK74V(T&-$)MqHiAS+KE#It-WR(Bdu4t$Opi_7A6+WBshmG+1c zvGcgdak*CDB->nQ4XW))pKMt-vCN#$Iks#MGC`FoS0O^D$xNZ{%{qQm`&L89T8B#D zoB8tr&xMLVmU)9Z1-t&DKlw7^VlJZfzF$>(hTXgr9gH}Vl7*F9*Lr}MJ!~+Y_#?Tw zZl{KffBYJnrfhyfKU~j_Xw!pV{{r9YYC3y6r*X2)pf;8+mLM*qLEOV*Jqbr*ilOBL zgY+wLnV?!d@OGt^md|5#>A`>(Pc1zzV@dkdBLzvI3qw%2_>DFLK zv^Uh#G&Xkd)c)=I_0)SwO=5S6rSH)@ku|sqw$K0CRnihGJ@KPVLWFR+Acl4DZ{h<| zVeQc}dUlc>&N)eEu73EPTX_t&No7n;WEvDY>9`%I%*fte2l%)il-!{@dp({cbK1-| zH5n%j0-n|jAv3{gP^T?t8T);ztF9N*XCKeCk5`b}M8zGdbEQ6;M9*{t$CVIC%Dq*! zEH^sTQ>P%y)_HxVt@xz@za$1egXiJMnC6pvO|oX(a{9=XcUKGXAcQB*Z|Qk~&PL^W zMc`D`)=ZMrPc%=uQ8#TUGq?~)0E}*8}B#lYbM#OSBWfg zKYC8OypBnE9S*3?@0mZFt==^OPMRK*U`aggqnq4>KlRnN&A|!T9Bc%TqoE<9(VO-} z3(%m>TfRCMhOa}^4d(xjjD83WmBt3cDDx==(0TIv_R*h|EvF#+7UvgleDs<*bQpAr zwLqlpBdSJTV@Kb)hHs*5(d;$(v>!`0Y2MrP@Va`g*C#{49;?4A{Pi|o)?+(@4hAFIW>qvyoHi4U z#qg9245z`KP4f|jGCwKi?m^!{HyHx-KJlCg9`TogBkT^=C9;Y@|M?jFUH5Y%HU%*Z zeBO|3MXrcG6e7A~W+@bh;b0Y(!aOX{+v7P0BxUJ5rc-a~{j!K#$PtuY(q zw$#V@qV(|eW@rj#)I>D$(CeURJ?1-`(R)pEh-uK3JbA4F?kFd9P zW11uj*yJ+9=kBIoYklA$Np9vj$_IU#^f906Qz_NMFFTilo3qty|0_uoeuJtYe%(dM zS27a2M0LHkNN=ai28S#I5pLm?BzOfj#C%vkU*;4{qwl!L(W2(q9a5VDobseoQL7GO zf{XpVy{A#EP;I%piTH7|pHMVi%*MMOeMh2AiMW@tR=!X5Rrp@t$kVE#Tb*2KvP^f~ zcL+%Sa>>vzdK5iK>`!><_8=&Z~9*Zn092#iHNG_x)0LnPR)ADy0e0W<~?{ zoeZLzsNRyo;7Qr$QKP`Gqc`U&A$2%0Q!|_SDe1sC_9(7BLQMN*agBGIjdYw}V)N+S zq$x#}&do1O@62({NTkt9HxtayYb3O5VsO#bcf)1u2L-&b$U2zFm?>iYG0z_yD1W&n zaH|SrmN!nAD2i{;VOdF0!uQ9S2%S5kna!t*5h|Te$G!8S^W~9+PSwQ`ABET=XtmuTSC7HZ4{e4ML}wh=Fg z#*Gx?{DPnlr(gJNPd)X+gvwsf+`I(85a&s`AFjUl?sB}VjMh}^iQB0r!)QG8S& zX4ZAEXNK+0Hb7}wBuA~4Pans{2usD{c99s<7fN8$nMV*Of}huuv*!% zHMhcbhp@CC2gaDC3ogP3Jp&n~DKE08TA36S1h&5!MoAc#=DVt(F{%ue5uJaQ2va&= zb3NySq!h>ElMgv2ELX;wQ4caJfeQkbcPOq!1~)xm-#4W$lYbWPiT`Qnml8owW)SE4 zz9BOr^z(#Jejf3rH5Yzg@-7WJK4&+-Xkr{@kMR3${vdRgsh{ugzu@fwS>z*xCVp`Q z5^Kl?RV_FU3V~fe$DxtP!lANG@XLV3^x#tmwE~^pRWWIa022aS_<)!vl}XvxNL%@@ z>6%zp`A-~UA3{R6xx_Zn$Rm@*C>nTO@Wi#PiAD*w5R*RFV}#6uP{O6z)Q@wk~-SG z5Cgq+CQfu+X+4O4h0ZQi$oYM-rc62-ywm$oMLl9P{b27aEFtJi2|CQ56lm1YgUr)d zgv+(Xy-3Ve{g|cQ8cO;>Ve0sDGE(0Ml%yjl19m9BaUoJ;!bHjmLUK-tA~A5Z`;D<& zX${`FBJtC8F@81dVN1VUy$XKvK|b|)h!3Pk>gev`h~WBk-CxFsX`9++YBXwj@Az>h*_Q+fTij@nX}V%Z?LEEE~am z`V(Wg_mNp9+%rALc1d59RV&@lJTxvj3X=C&vm5|C-}NNSr*=64FLz2Gf(<%~!s4!z zrX&Wm2ux<2n%y@>r&5{k_Ibx(wCKy7E@R2JXo-K#4D^sl-e1&sPQ*->Sk$lVi;O1FlG0r7@s*SwlppuomD2S2cx?clN?m}JjUFaN+zwaz%)NbUsQ2ABVC(g)dzZ8qCL*5QYKu+M#i?=68 zm=j$Sb&uB+9bCx_flDV1Af69}dpw&Yjdo%_Kx?m(x`=`h zy^xW)o{E#Ak-eGmzZFFEtjsLk05pH)0sdst{*`N~XYx!i0RLN6;4cmYBRd->fRTlj z4FCoKAgfH;d%V8JN~N9N#F4g z$3y9vSMlPiKd6A7gV7(x$p5En%)+A~!t%^&`8)l~ z5(wwB1*|{-gzZ`X<;n>Hu(1Di&&JC7+>4jj5Zm)ac)8vex?*XqbBxx zwx(tV4oo)oCbWM&27=Ffs2t5~tc70aNHjw4!9Xx8kQ2-XVg+(Qz>Gk4Y9NsIpN;+> zm+5bMk)Yo5gJEOx$M8R0_!qh8|0qeW_D04?Kmdpp3Ha{=zy^V^Kmf*ozc3IBq?XizV*xV7=~*ghBhYbrT7>e)N~@h*tv*%l;9N?}ySp6R^>A=^2=4Cg?(QDk-61%^JwR~x5D2cpfhNXxd)8>+v6X~?niE+2hq~0Rr z19$DalVT-a{C>2A9dVoUgQ5NWEjP&B-5|LfnC189^~BVK5qVxXF--N%fq`g6LAUnA zgsJ3w9jKJ02@%{11+V8;Ccx6^LUDcU8+wN&P-j?5)d)D{Rb)=c-yqvjHGKz=WXUW^CZBT8_ z4fn7cZDgR+y%9V3JZ%8rodyxlD%EBZGQn-KvPW>|?Q-QR+qh0MpU|S-C?N_=VEy0_^X(dLM~`$fr!t8z zu&OES>x!n-4EH20pDG;4JrJt#5q~Hh9bdUQRyp$qHn0Z(;j7brlnX((f%@_<^(;)Tg&N^m1Hirsb;<{3up`Hu0pS_1po z&K_P)JDqm2pQMeBv-oX+%2V+jUPzvEGk9@CJ_Y^okz_+rw|1kR940`dVSZ1@MwzwUHu~vGQ4ZkbSw$;5~_)erM z`PGQ6XnNAwvh9g;4H~Pn@NOTtQax>EpSV6kFGxhH^NnNg3`CYW1Q``&tB~Pr)UPaG z&pWNjb(s}ZSKk8DPc{=6_r^0u)qst(&6^dSEmB#hElcFWWbIj<%XXwoEY++h%}ad% z_fl444^9N%r7XOo(~M9q@C>|G4S^W0yb_JEm=7R!-}%;oHPOmwx6k7btec^Eb|vWh z*9Cp3fs5bHgZsrz18UZo;PQiQcYk1_R+dBt)797`bH@2+@2ZO@YbX*rbhElXpG&{qitDq2JO-zC}ZQ!-bSxOlKJ# zt&`H>S#NS|#yIN55lwt}2zK*w#E(xg1TBQS42)zj%|XL9sIKK=KK4lMv?Wi39|8*l zEqBMthQq|zb8up0X&Q*pDiw6$2PHw+iW9j95uzs?_=<4GJ`fKr7bkA z-}3A#ek?wE!xu%9#s|q4@6^eB4Y_VC#LzHdvOR;EHwxYmh*&vNk2+^nRf~3ralVmq zNPH*1wklr}O*0e~8|_`gAO^{!B;1`hM%XSXAFyhH)O$&IZT4f4BFk9z6r%eiXcoq= zw&Kg_*)Qj!1J6B-?h=v(bYTJyRUQHzQMz@?6e66o3U~=BXKaM3%cOVqIUHx|OZ&Rw zxKw3>x!Y-}Xbypb~$YtPecxNe0(|TKQsyzE6m>L0%A%{^k_;=4a2%#{3sc z*5L|D?^0w~L@5JaSMY1F+Q8=|#KL!E1P$-t4-JD6d%}h;U18S-;rY~&amsG|HK!KQ zWJB}XD1Rl9_5_7Tf{A#951?%rWUW=g^J44ElNHtmL#s%!CJr%zOM^^Bie_+>m2Kq$ zM5QFrMIv@EoC|m%=7X8v;mhlfIEdB`E7eybw<$mgmc@-6;ZaSsY=Yw2flxm-BHoF3 zAA=Kzz>5b=oLX+AVUju|Hh%J!3Wks=OX%5$@XC&-TV_}g`GHkRrmmI%AcPLsqH(b2 z@J)a^iGq;D84KhbgD7EFFi?Z?(vMc9ddGw`G@TBGJ@ds`-8Bx5VohG5cf}YElB4mY zJiJ8y&0wRwRY{k61H!tc1r?g=lg~R=`ua^xVu^+;Ko#cxR1Ut_*Z}B2_2Y@ zHA&h0<=ObttQ3f)r#cw*VhBiak$9z{^qG?YPO9ugUdPaPv-rXT(?>Aybd}&=x-$I< zf*G6Q%H{*y-fQpLY)HPOEVlHBbxLE(H*`0Tjd^q&d@2r zjI#(g?Z|vQf{)L_PH+LY=1+$Ur3)ggCiW6o6IHL}crkkU{nZ)Ee>wv&Dtp+Q02t*B%uE2}0OsGF%NsbFytKvqcU?vq z6JrYlAvz|O)6;ACL}==`=8e(Cqc+zIfyI{C8{5qGq6vHyoJfBN{X{d+J< zFFiXN*gDz2_GRSpw^|b5?C4_hw

xC1T=gVPv8xF7&GWYp9AQPIfMiMkY>x*RU!6 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 literal 0 HcmV?d00001 diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_root_bank_clkmux.gv.pdf b/src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_root_bank_clkmux.gv.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1ab413192ce35061d682b9e3af2dbd5c95393cd0 GIT binary patch literal 14234 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 C#U#1_ literal 0 HcmV?d00001 diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_top.gv.pdf b/src/ConfigurationRaptor/CFGDeviceDatabase/gearbox_block_diagrams/1vg28/gbox_top.gv.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e2ef83e672dc9fd160c76df2aa7f0ccb5effef65 GIT binary patch literal 11027 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 ^T%?T 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 Ztcy*Oi6K)rq6Hi*pv`%Yt{s)&*UWotz literal 0 HcmV?d00001 From 561bf2ef544a00798896b59bdfe39ad978117b6a Mon Sep 17 00:00:00 2001 From: chungshien-chai Date: Fri, 27 Sep 2024 00:55:03 -0700 Subject: [PATCH 3/4] Misc update on spacing --- .../CFGDeviceDatabase/Virgo/1vg28_routing.py | 54 +++---- .../Virgo/gemini_compact_22x4_routing.py | 6 +- .../Virgo/gemini_compact_62x44_routing.py | 56 +++---- .../Virgo/routing_library/gbox_hp_40x2.py | 138 +++++++++--------- .../Virgo/routing_library/gbox_hpio.py | 30 ++-- .../Virgo/routing_library/gbox_hv_40x2.py | 100 ++++++------- .../Virgo/routing_library/gbox_osc.py | 6 +- .../Virgo/routing_library/gbox_pll.py | 6 +- .../routing_library/gbox_root_bank_clkmux.py | 8 +- .../Virgo/routing_library/gbox_top.py | 10 +- .../gemini_compact_22x4_gbox_hp_40x1.py | 60 ++++---- 11 files changed, 237 insertions(+), 237 deletions(-) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/1vg28_routing.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/1vg28_routing.py index ac7a1574..5fc680fc 100644 --- a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/1vg28_routing.py +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/1vg28_routing.py @@ -14,7 +14,7 @@ add_port(name="fclk_buf", dir=DIR_IN, bit=8) # Instances -add_instance(name="hp_40x2", block="gbox_hp_40x2") +add_instance(name="hp_40x2", block="gbox_hp_40x2") add_instance(name="hvl_40x2", block="gbox_hv_40x2") add_instance(name="hvr_40x2", block="gbox_hv_40x2") @@ -27,43 +27,43 @@ bank_pin_name = "bank%d_rx_in" % bank for i in range(40) : top_location = function_library.get_location(type_bank[0], type_bank[1], i) - add_connection(source=top_location, destinations=["%s->%s[%d]" % (instance, bank_pin_name, i)]) + add_connection(source=top_location, destinations=["%s->%s[%d]" % (instance, bank_pin_name, i)]) # hvl/hvr clk pin --> hp clk pin for bank in range(2) : for pin in range(2) : source_pin = "bank%d_rx_io_clk[%d]" % (bank, pin) - add_connection(source="hvl_40x2->%s" % (source_pin), destinations=["hp_40x2->hvl_bank%d_rx_io_clk[%d]" % (bank, pin)]) - add_connection(source="hvr_40x2->%s" % (source_pin), destinations=["hp_40x2->hvr_bank%d_rx_io_clk[%d]" % (bank, pin)]) + add_connection(source="hvl_40x2->%s" % (source_pin), destinations=["hp_40x2->hvl_bank%d_rx_io_clk[%d]" % (bank, pin)]) + add_connection(source="hvr_40x2->%s" % (source_pin), destinations=["hp_40x2->hvr_bank%d_rx_io_clk[%d]" % (bank, pin)]) # hp pll --> hvl pll -add_connection(source="hp_40x2->pll_foutvco[0]", destinations=["hvl_40x2->pll_foutvco"]) -add_connection(source="hp_40x2->pll_fout[0]", destinations=["hvl_40x2->pll_fout"]) +add_connection(source="hp_40x2->pll_foutvco[0]", destinations=["hvl_40x2->pll_foutvco"]) +add_connection(source="hp_40x2->pll_fout[0]", destinations=["hvl_40x2->pll_fout"]) # hp pll --> hvr pll -add_connection(source="hp_40x2->pll_foutvco[1]", destinations=["hvr_40x2->pll_foutvco"]) -add_connection(source="hp_40x2->pll_fout[1]", destinations=["hvr_40x2->pll_fout"]) +add_connection(source="hp_40x2->pll_foutvco[1]", destinations=["hvr_40x2->pll_foutvco"]) +add_connection(source="hp_40x2->pll_fout[1]", destinations=["hvr_40x2->pll_fout"]) # hvl core clk + cdr clk --> HP core clk + cdr clk -add_connection(source="hvl_40x2->bank0_root_core_clk[0]", destinations=["hp_40x2->hvl_bank0_root_core_clk[0]"]) -add_connection(source="hvl_40x2->bank0_root_core_clk[1]", destinations=["hp_40x2->hvl_bank0_root_core_clk[1]"]) -add_connection(source="hvl_40x2->bank0_root_cdr_clk[0]", destinations=["hp_40x2->hvl_bank0_root_cdr_clk[0]"]) -add_connection(source="hvl_40x2->bank0_root_cdr_clk[1]", destinations=["hp_40x2->hvl_bank0_root_cdr_clk[1]"]) -add_connection(source="hvl_40x2->bank1_root_core_clk[0]", destinations=["hp_40x2->hvl_bank1_root_core_clk[0]"]) -add_connection(source="hvl_40x2->bank1_root_core_clk[1]", destinations=["hp_40x2->hvl_bank1_root_core_clk[1]"]) -add_connection(source="hvl_40x2->bank1_root_cdr_clk[0]", destinations=["hp_40x2->hvl_bank1_root_cdr_clk[0]"]) -add_connection(source="hvl_40x2->bank1_root_cdr_clk[1]", destinations=["hp_40x2->hvl_bank1_root_cdr_clk[1]"]) +add_connection(source="hvl_40x2->bank0_root_core_clk[0]", destinations=["hp_40x2->hvl_bank0_root_core_clk[0]"]) +add_connection(source="hvl_40x2->bank0_root_core_clk[1]", destinations=["hp_40x2->hvl_bank0_root_core_clk[1]"]) +add_connection(source="hvl_40x2->bank0_root_cdr_clk[0]", destinations=["hp_40x2->hvl_bank0_root_cdr_clk[0]"]) +add_connection(source="hvl_40x2->bank0_root_cdr_clk[1]", destinations=["hp_40x2->hvl_bank0_root_cdr_clk[1]"]) +add_connection(source="hvl_40x2->bank1_root_core_clk[0]", destinations=["hp_40x2->hvl_bank1_root_core_clk[0]"]) +add_connection(source="hvl_40x2->bank1_root_core_clk[1]", destinations=["hp_40x2->hvl_bank1_root_core_clk[1]"]) +add_connection(source="hvl_40x2->bank1_root_cdr_clk[0]", destinations=["hp_40x2->hvl_bank1_root_cdr_clk[0]"]) +add_connection(source="hvl_40x2->bank1_root_cdr_clk[1]", destinations=["hp_40x2->hvl_bank1_root_cdr_clk[1]"]) # hvr core clk + cdr clk --> HP core clk + cdr clk -add_connection(source="hvr_40x2->bank0_root_core_clk[0]", destinations=["hp_40x2->hvr_bank0_root_core_clk[0]"]) -add_connection(source="hvr_40x2->bank0_root_core_clk[1]", destinations=["hp_40x2->hvr_bank0_root_core_clk[1]"]) -add_connection(source="hvr_40x2->bank0_root_cdr_clk[0]", destinations=["hp_40x2->hvr_bank0_root_cdr_clk[0]"]) -add_connection(source="hvr_40x2->bank0_root_cdr_clk[1]", destinations=["hp_40x2->hvr_bank0_root_cdr_clk[1]"]) -add_connection(source="hvr_40x2->bank1_root_core_clk[0]", destinations=["hp_40x2->hvr_bank1_root_core_clk[0]"]) -add_connection(source="hvr_40x2->bank1_root_core_clk[1]", destinations=["hp_40x2->hvr_bank1_root_core_clk[1]"]) -add_connection(source="hvr_40x2->bank1_root_cdr_clk[0]", destinations=["hp_40x2->hvr_bank1_root_cdr_clk[0]"]) -add_connection(source="hvr_40x2->bank1_root_cdr_clk[1]", destinations=["hp_40x2->hvr_bank1_root_cdr_clk[1]"]) +add_connection(source="hvr_40x2->bank0_root_core_clk[0]", destinations=["hp_40x2->hvr_bank0_root_core_clk[0]"]) +add_connection(source="hvr_40x2->bank0_root_core_clk[1]", destinations=["hp_40x2->hvr_bank0_root_core_clk[1]"]) +add_connection(source="hvr_40x2->bank0_root_cdr_clk[0]", destinations=["hp_40x2->hvr_bank0_root_cdr_clk[0]"]) +add_connection(source="hvr_40x2->bank0_root_cdr_clk[1]", destinations=["hp_40x2->hvr_bank0_root_cdr_clk[1]"]) +add_connection(source="hvr_40x2->bank1_root_core_clk[0]", destinations=["hp_40x2->hvr_bank1_root_core_clk[0]"]) +add_connection(source="hvr_40x2->bank1_root_core_clk[1]", destinations=["hp_40x2->hvr_bank1_root_core_clk[1]"]) +add_connection(source="hvr_40x2->bank1_root_cdr_clk[0]", destinations=["hp_40x2->hvr_bank1_root_cdr_clk[0]"]) +add_connection(source="hvr_40x2->bank1_root_cdr_clk[1]", destinations=["hp_40x2->hvr_bank1_root_cdr_clk[1]"]) # hp instance fabric clk --> fabric clk for i in range(16) : - add_connection(source="hp_40x2->fabric_clk[%d]" % i, destinations=["fabric_clk[%d]" % i]) + add_connection(source="hp_40x2->fabric_clk[%d]" % i, destinations=["fabric_clk[%d]" % i]) # fclk buf --> instance fclk buf for i in range(8) : - add_connection(source="fclk_buf[%d]" % i, destinations=["hp_40x2->fclk_buf[%d]" % i]) + add_connection(source="fclk_buf[%d]" % i, destinations=["hp_40x2->fclk_buf[%d]" % i]) # Mapping to TCL model for type_bank in [["P", 1, 0], ["P", 2, 1], ["R", 1, 0], ["R", 2, 1], ["R", 3, 0], ["R", 5, 1]] : @@ -107,4 +107,4 @@ mapped_name = "%s_bank%s_pin[39:0]" % (instance, type_bank[2]) for i in range(40) : location = function_library.get_location(type_bank[0], type_bank[1], i) - add_diagram_map(name=location, mapped_name=mapped_name) \ No newline at end of file + add_diagram_map(name=location, mapped_name=mapped_name) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_22x4_routing.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_22x4_routing.py index d36a6c3e..f43778e9 100644 --- a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_22x4_routing.py +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_22x4_routing.py @@ -27,13 +27,13 @@ if i in [16, 17, 36, 37]: continue top_location = function_library.get_location(type_bank[0], type_bank[1], i) - add_connection(source=top_location, destinations=["%s->%s[%d]" % (instance, bank_pin_name, i)]) + add_connection(source=top_location, destinations=["%s->%s[%d]" % (instance, bank_pin_name, i)]) # hp instance fabric clk --> fabric clk for i in range(16) : - add_connection(source="hp_40x1->fabric_clk[%d]" % i, destinations=["fabric_clk[%d]" % i]) + add_connection(source="hp_40x1->fabric_clk[%d]" % i, destinations=["fabric_clk[%d]" % i]) # fclk buf --> instance fclk buf for i in range(8) : - add_connection(source="fclk_buf[%d]" % i, destinations=["hp_40x1->fclk_buf[%d]" % i]) + add_connection(source="fclk_buf[%d]" % i, destinations=["hp_40x1->fclk_buf[%d]" % i]) # Mapping to TCL model for type_bank in [["P", 1, 0]] : diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_62x44_routing.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_62x44_routing.py index ac7a1574..baefc61c 100644 --- a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_62x44_routing.py +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/gemini_compact_62x44_routing.py @@ -14,7 +14,7 @@ add_port(name="fclk_buf", dir=DIR_IN, bit=8) # Instances -add_instance(name="hp_40x2", block="gbox_hp_40x2") +add_instance(name="hp_40x2", block="gbox_hp_40x2") add_instance(name="hvl_40x2", block="gbox_hv_40x2") add_instance(name="hvr_40x2", block="gbox_hv_40x2") @@ -27,44 +27,44 @@ bank_pin_name = "bank%d_rx_in" % bank for i in range(40) : top_location = function_library.get_location(type_bank[0], type_bank[1], i) - add_connection(source=top_location, destinations=["%s->%s[%d]" % (instance, bank_pin_name, i)]) + add_connection(source=top_location, destinations=["%s->%s[%d]" % (instance, bank_pin_name, i)]) # hvl/hvr clk pin --> hp clk pin for bank in range(2) : for pin in range(2) : source_pin = "bank%d_rx_io_clk[%d]" % (bank, pin) - add_connection(source="hvl_40x2->%s" % (source_pin), destinations=["hp_40x2->hvl_bank%d_rx_io_clk[%d]" % (bank, pin)]) - add_connection(source="hvr_40x2->%s" % (source_pin), destinations=["hp_40x2->hvr_bank%d_rx_io_clk[%d]" % (bank, pin)]) + add_connection(source="hvl_40x2->%s" % (source_pin), destinations=["hp_40x2->hvl_bank%d_rx_io_clk[%d]" % (bank, pin)]) + add_connection(source="hvr_40x2->%s" % (source_pin), destinations=["hp_40x2->hvr_bank%d_rx_io_clk[%d]" % (bank, pin)]) # hp pll --> hvl pll -add_connection(source="hp_40x2->pll_foutvco[0]", destinations=["hvl_40x2->pll_foutvco"]) -add_connection(source="hp_40x2->pll_fout[0]", destinations=["hvl_40x2->pll_fout"]) +add_connection(source="hp_40x2->pll_foutvco[0]", destinations=["hvl_40x2->pll_foutvco"]) +add_connection(source="hp_40x2->pll_fout[0]", destinations=["hvl_40x2->pll_fout"]) # hp pll --> hvr pll -add_connection(source="hp_40x2->pll_foutvco[1]", destinations=["hvr_40x2->pll_foutvco"]) -add_connection(source="hp_40x2->pll_fout[1]", destinations=["hvr_40x2->pll_fout"]) +add_connection(source="hp_40x2->pll_foutvco[1]", destinations=["hvr_40x2->pll_foutvco"]) +add_connection(source="hp_40x2->pll_fout[1]", destinations=["hvr_40x2->pll_fout"]) # hvl core clk + cdr clk --> HP core clk + cdr clk -add_connection(source="hvl_40x2->bank0_root_core_clk[0]", destinations=["hp_40x2->hvl_bank0_root_core_clk[0]"]) -add_connection(source="hvl_40x2->bank0_root_core_clk[1]", destinations=["hp_40x2->hvl_bank0_root_core_clk[1]"]) -add_connection(source="hvl_40x2->bank0_root_cdr_clk[0]", destinations=["hp_40x2->hvl_bank0_root_cdr_clk[0]"]) -add_connection(source="hvl_40x2->bank0_root_cdr_clk[1]", destinations=["hp_40x2->hvl_bank0_root_cdr_clk[1]"]) -add_connection(source="hvl_40x2->bank1_root_core_clk[0]", destinations=["hp_40x2->hvl_bank1_root_core_clk[0]"]) -add_connection(source="hvl_40x2->bank1_root_core_clk[1]", destinations=["hp_40x2->hvl_bank1_root_core_clk[1]"]) -add_connection(source="hvl_40x2->bank1_root_cdr_clk[0]", destinations=["hp_40x2->hvl_bank1_root_cdr_clk[0]"]) -add_connection(source="hvl_40x2->bank1_root_cdr_clk[1]", destinations=["hp_40x2->hvl_bank1_root_cdr_clk[1]"]) +add_connection(source="hvl_40x2->bank0_root_core_clk[0]", destinations=["hp_40x2->hvl_bank0_root_core_clk[0]"]) +add_connection(source="hvl_40x2->bank0_root_core_clk[1]", destinations=["hp_40x2->hvl_bank0_root_core_clk[1]"]) +add_connection(source="hvl_40x2->bank0_root_cdr_clk[0]", destinations=["hp_40x2->hvl_bank0_root_cdr_clk[0]"]) +add_connection(source="hvl_40x2->bank0_root_cdr_clk[1]", destinations=["hp_40x2->hvl_bank0_root_cdr_clk[1]"]) +add_connection(source="hvl_40x2->bank1_root_core_clk[0]", destinations=["hp_40x2->hvl_bank1_root_core_clk[0]"]) +add_connection(source="hvl_40x2->bank1_root_core_clk[1]", destinations=["hp_40x2->hvl_bank1_root_core_clk[1]"]) +add_connection(source="hvl_40x2->bank1_root_cdr_clk[0]", destinations=["hp_40x2->hvl_bank1_root_cdr_clk[0]"]) +add_connection(source="hvl_40x2->bank1_root_cdr_clk[1]", destinations=["hp_40x2->hvl_bank1_root_cdr_clk[1]"]) # hvr core clk + cdr clk --> HP core clk + cdr clk -add_connection(source="hvr_40x2->bank0_root_core_clk[0]", destinations=["hp_40x2->hvr_bank0_root_core_clk[0]"]) -add_connection(source="hvr_40x2->bank0_root_core_clk[1]", destinations=["hp_40x2->hvr_bank0_root_core_clk[1]"]) -add_connection(source="hvr_40x2->bank0_root_cdr_clk[0]", destinations=["hp_40x2->hvr_bank0_root_cdr_clk[0]"]) -add_connection(source="hvr_40x2->bank0_root_cdr_clk[1]", destinations=["hp_40x2->hvr_bank0_root_cdr_clk[1]"]) -add_connection(source="hvr_40x2->bank1_root_core_clk[0]", destinations=["hp_40x2->hvr_bank1_root_core_clk[0]"]) -add_connection(source="hvr_40x2->bank1_root_core_clk[1]", destinations=["hp_40x2->hvr_bank1_root_core_clk[1]"]) -add_connection(source="hvr_40x2->bank1_root_cdr_clk[0]", destinations=["hp_40x2->hvr_bank1_root_cdr_clk[0]"]) -add_connection(source="hvr_40x2->bank1_root_cdr_clk[1]", destinations=["hp_40x2->hvr_bank1_root_cdr_clk[1]"]) +add_connection(source="hvr_40x2->bank0_root_core_clk[0]", destinations=["hp_40x2->hvr_bank0_root_core_clk[0]"]) +add_connection(source="hvr_40x2->bank0_root_core_clk[1]", destinations=["hp_40x2->hvr_bank0_root_core_clk[1]"]) +add_connection(source="hvr_40x2->bank0_root_cdr_clk[0]", destinations=["hp_40x2->hvr_bank0_root_cdr_clk[0]"]) +add_connection(source="hvr_40x2->bank0_root_cdr_clk[1]", destinations=["hp_40x2->hvr_bank0_root_cdr_clk[1]"]) +add_connection(source="hvr_40x2->bank1_root_core_clk[0]", destinations=["hp_40x2->hvr_bank1_root_core_clk[0]"]) +add_connection(source="hvr_40x2->bank1_root_core_clk[1]", destinations=["hp_40x2->hvr_bank1_root_core_clk[1]"]) +add_connection(source="hvr_40x2->bank1_root_cdr_clk[0]", destinations=["hp_40x2->hvr_bank1_root_cdr_clk[0]"]) +add_connection(source="hvr_40x2->bank1_root_cdr_clk[1]", destinations=["hp_40x2->hvr_bank1_root_cdr_clk[1]"]) # hp instance fabric clk --> fabric clk for i in range(16) : - add_connection(source="hp_40x2->fabric_clk[%d]" % i, destinations=["fabric_clk[%d]" % i]) + add_connection(source="hp_40x2->fabric_clk[%d]" % i, destinations=["fabric_clk[%d]" % i]) # fclk buf --> instance fclk buf for i in range(8) : - add_connection(source="fclk_buf[%d]" % i, destinations=["hp_40x2->fclk_buf[%d]" % i]) - + add_connection(source="fclk_buf[%d]" % i, destinations=["hp_40x2->fclk_buf[%d]" % i]) + # Mapping to TCL model for type_bank in [["P", 1, 0], ["P", 2, 1], ["R", 1, 0], ["R", 2, 1], ["R", 3, 0], ["R", 5, 1]] : # Pin location @@ -107,4 +107,4 @@ mapped_name = "%s_bank%s_pin[39:0]" % (instance, type_bank[2]) for i in range(40) : location = function_library.get_location(type_bank[0], type_bank[1], i) - add_diagram_map(name=location, mapped_name=mapped_name) \ No newline at end of file + add_diagram_map(name=location, mapped_name=mapped_name) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hp_40x2.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hp_40x2.py index d298f1c4..628af589 100644 --- a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hp_40x2.py +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hp_40x2.py @@ -11,80 +11,80 @@ create_block(name="gbox_hp_40x2") # Ports -add_port(name="bank0_rx_in", dir=DIR_IN, bit=40) -add_port(name="bank1_rx_in", dir=DIR_IN, bit=40) -add_port(name="hvl_bank0_rx_io_clk", dir=DIR_IN, bit=2) -add_port(name="hvl_bank1_rx_io_clk", dir=DIR_IN, bit=2) -add_port(name="hvr_bank0_rx_io_clk", dir=DIR_IN, bit=2) -add_port(name="hvr_bank1_rx_io_clk", dir=DIR_IN, bit=2) -add_port(name="hvl_bank0_root_core_clk", dir=DIR_IN, bit=2) -add_port(name="hvl_bank0_root_cdr_clk", dir=DIR_IN, bit=2) -add_port(name="hvl_bank1_root_core_clk", dir=DIR_IN, bit=2) -add_port(name="hvl_bank1_root_cdr_clk", dir=DIR_IN, bit=2) -add_port(name="hvr_bank0_root_core_clk", dir=DIR_IN, bit=2) -add_port(name="hvr_bank0_root_cdr_clk", dir=DIR_IN, bit=2) -add_port(name="hvr_bank1_root_core_clk", dir=DIR_IN, bit=2) -add_port(name="hvr_bank1_root_cdr_clk", dir=DIR_IN, bit=2) -add_port(name="fclk_buf", dir=DIR_IN, bit=8) -add_port(name="pll_foutvco", dir=DIR_OUT, bit=2) -add_port(name="pll_fout", dir=DIR_OUT, bit=2) -add_port(name="fabric_clk", dir=DIR_OUT, bit=16) +add_port(name="bank0_rx_in", dir=DIR_IN, bit=40) +add_port(name="bank1_rx_in", dir=DIR_IN, bit=40) +add_port(name="hvl_bank0_rx_io_clk", dir=DIR_IN, bit=2) +add_port(name="hvl_bank1_rx_io_clk", dir=DIR_IN, bit=2) +add_port(name="hvr_bank0_rx_io_clk", dir=DIR_IN, bit=2) +add_port(name="hvr_bank1_rx_io_clk", dir=DIR_IN, bit=2) +add_port(name="hvl_bank0_root_core_clk", dir=DIR_IN, bit=2) +add_port(name="hvl_bank0_root_cdr_clk", dir=DIR_IN, bit=2) +add_port(name="hvl_bank1_root_core_clk", dir=DIR_IN, bit=2) +add_port(name="hvl_bank1_root_cdr_clk", dir=DIR_IN, bit=2) +add_port(name="hvr_bank0_root_core_clk", dir=DIR_IN, bit=2) +add_port(name="hvr_bank0_root_cdr_clk", dir=DIR_IN, bit=2) +add_port(name="hvr_bank1_root_core_clk", dir=DIR_IN, bit=2) +add_port(name="hvr_bank1_root_cdr_clk", dir=DIR_IN, bit=2) +add_port(name="fclk_buf", dir=DIR_IN, bit=8) +add_port(name="pll_foutvco", dir=DIR_OUT, bit=2) +add_port(name="pll_fout", dir=DIR_OUT, bit=2) +add_port(name="fabric_clk", dir=DIR_OUT, bit=16) # Instances -add_instance(name="rc_osc_50mhz", block="rc_osc_50mhz") -add_instance(name="pll_refmux[0]", block="gbox_pll_refmux") -add_instance(name="pll_refmux[1]", block="gbox_pll_refmux") -add_instance(name="pll[0]", block="PLL") -add_instance(name="pll[1]", block="PLL") -add_instance(name="bank0_fclk_mux_A", block="gbox_fclk_mux") -add_instance(name="bank1_fclk_mux_A", block="gbox_fclk_mux") -add_instance(name="bank0_fclk_mux_B", block="gbox_fclk_mux") -add_instance(name="bank1_fclk_mux_B", block="gbox_fclk_mux") -add_instance(name="bank0_hpio", block="gbox_hpio") -add_instance(name="bank1_hpio", block="gbox_hpio") -add_instance(name="bank0_root_bank_clkmux", block="gbox_root_bank_clkmux") -add_instance(name="bank1_root_bank_clkmux", block="gbox_root_bank_clkmux") +add_instance(name="rc_osc_50mhz", block="rc_osc_50mhz") +add_instance(name="pll_refmux[0]", block="gbox_pll_refmux") +add_instance(name="pll_refmux[1]", block="gbox_pll_refmux") +add_instance(name="pll[0]", block="PLL") +add_instance(name="pll[1]", block="PLL") +add_instance(name="bank0_fclk_mux_A", block="gbox_fclk_mux") +add_instance(name="bank1_fclk_mux_A", block="gbox_fclk_mux") +add_instance(name="bank0_fclk_mux_B", block="gbox_fclk_mux") +add_instance(name="bank1_fclk_mux_B", block="gbox_fclk_mux") +add_instance(name="bank0_hpio", block="gbox_hpio") +add_instance(name="bank1_hpio", block="gbox_hpio") +add_instance(name="bank0_root_bank_clkmux", block="gbox_root_bank_clkmux") +add_instance(name="bank1_root_bank_clkmux", block="gbox_root_bank_clkmux") # Connections # osc + pin --> refmux -add_connection(source="rc_osc_50mhz->o_osc", destinations=["pll_refmux[0]->rosc_clk", "pll_refmux[1]->rosc_clk"]) -add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["pll_refmux[0]->bank0_hp_rx_io_clk[0]", "pll_refmux[1]->bank0_hp_rx_io_clk[0]"]) -add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["pll_refmux[0]->bank0_hp_rx_io_clk[1]", "pll_refmux[1]->bank0_hp_rx_io_clk[1]"]) -add_connection(source="bank1_hpio->rx_io_clk[0]", destinations=["pll_refmux[0]->bank1_hp_rx_io_clk[0]", "pll_refmux[1]->bank1_hp_rx_io_clk[0]"]) -add_connection(source="bank1_hpio->rx_io_clk[1]", destinations=["pll_refmux[0]->bank1_hp_rx_io_clk[1]", "pll_refmux[1]->bank1_hp_rx_io_clk[1]"]) -add_connection(source="hvl_bank0_rx_io_clk[0]", destinations=["pll_refmux[0]->bank0_hv_rx_io_clk[0]"]) -add_connection(source="hvl_bank0_rx_io_clk[1]", destinations=["pll_refmux[0]->bank0_hv_rx_io_clk[1]"]) -add_connection(source="hvl_bank1_rx_io_clk[0]", destinations=["pll_refmux[0]->bank1_hv_rx_io_clk[0]"]) -add_connection(source="hvl_bank1_rx_io_clk[1]", destinations=["pll_refmux[0]->bank1_hv_rx_io_clk[1]"]) -add_connection(source="hvr_bank0_rx_io_clk[0]", destinations=["pll_refmux[1]->bank0_hv_rx_io_clk[0]"]) -add_connection(source="hvr_bank0_rx_io_clk[1]", destinations=["pll_refmux[1]->bank0_hv_rx_io_clk[1]"]) -add_connection(source="hvr_bank1_rx_io_clk[0]", destinations=["pll_refmux[1]->bank1_hv_rx_io_clk[0]"]) -add_connection(source="hvr_bank1_rx_io_clk[1]", destinations=["pll_refmux[1]->bank1_hv_rx_io_clk[1]"]) +add_connection(source="rc_osc_50mhz->o_osc", destinations=["pll_refmux[0]->rosc_clk", "pll_refmux[1]->rosc_clk"]) +add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["pll_refmux[0]->bank0_hp_rx_io_clk[0]", "pll_refmux[1]->bank0_hp_rx_io_clk[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["pll_refmux[0]->bank0_hp_rx_io_clk[1]", "pll_refmux[1]->bank0_hp_rx_io_clk[1]"]) +add_connection(source="bank1_hpio->rx_io_clk[0]", destinations=["pll_refmux[0]->bank1_hp_rx_io_clk[0]", "pll_refmux[1]->bank1_hp_rx_io_clk[0]"]) +add_connection(source="bank1_hpio->rx_io_clk[1]", destinations=["pll_refmux[0]->bank1_hp_rx_io_clk[1]", "pll_refmux[1]->bank1_hp_rx_io_clk[1]"]) +add_connection(source="hvl_bank0_rx_io_clk[0]", destinations=["pll_refmux[0]->bank0_hv_rx_io_clk[0]"]) +add_connection(source="hvl_bank0_rx_io_clk[1]", destinations=["pll_refmux[0]->bank0_hv_rx_io_clk[1]"]) +add_connection(source="hvl_bank1_rx_io_clk[0]", destinations=["pll_refmux[0]->bank1_hv_rx_io_clk[0]"]) +add_connection(source="hvl_bank1_rx_io_clk[1]", destinations=["pll_refmux[0]->bank1_hv_rx_io_clk[1]"]) +add_connection(source="hvr_bank0_rx_io_clk[0]", destinations=["pll_refmux[1]->bank0_hv_rx_io_clk[0]"]) +add_connection(source="hvr_bank0_rx_io_clk[1]", destinations=["pll_refmux[1]->bank0_hv_rx_io_clk[1]"]) +add_connection(source="hvr_bank1_rx_io_clk[0]", destinations=["pll_refmux[1]->bank1_hv_rx_io_clk[0]"]) +add_connection(source="hvr_bank1_rx_io_clk[1]", destinations=["pll_refmux[1]->bank1_hv_rx_io_clk[1]"]) # refmux --> pll -add_connection(source="pll_refmux[0]->out", destinations=["pll[0]->fref"]) -add_connection(source="pll_refmux[1]->out", destinations=["pll[1]->fref"]) +add_connection(source="pll_refmux[0]->out", destinations=["pll[0]->fref"]) +add_connection(source="pll_refmux[1]->out", destinations=["pll[1]->fref"]) # pll + pin --> fclk mux -add_connection(source="pll[0]->foutvco", destinations=["bank0_fclk_mux_A->vco_clk[0]", "pll_foutvco[0]"]) -add_connection(source="pll[0]->fout[0]", destinations=["bank0_fclk_mux_A->vco_clk[1]", "pll_fout[0]"]) -add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_A->rx_io_clk[0]"]) -add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_A->rx_io_clk[1]"]) -add_connection(source="pll[1]->foutvco", destinations=["bank1_fclk_mux_A->vco_clk[0]", "pll_foutvco[1]"]) -add_connection(source="pll[1]->fout[0]", destinations=["bank1_fclk_mux_A->vco_clk[1]", "pll_fout[1]"]) -add_connection(source="bank1_hpio->rx_io_clk[0]", destinations=["bank1_fclk_mux_A->rx_io_clk[0]"]) -add_connection(source="bank1_hpio->rx_io_clk[1]", destinations=["bank1_fclk_mux_A->rx_io_clk[1]"]) -add_connection(source="pll[0]->foutvco", destinations=["bank0_fclk_mux_B->vco_clk[0]"]) -add_connection(source="pll[0]->fout[0]", destinations=["bank0_fclk_mux_B->vco_clk[1]"]) -add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_B->rx_io_clk[0]"]) -add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_B->rx_io_clk[1]"]) -add_connection(source="pll[1]->foutvco", destinations=["bank1_fclk_mux_B->vco_clk[0]"]) -add_connection(source="pll[1]->fout[0]", destinations=["bank1_fclk_mux_B->vco_clk[1]"]) -add_connection(source="bank1_hpio->rx_io_clk[0]", destinations=["bank1_fclk_mux_B->rx_io_clk[0]"]) -add_connection(source="bank1_hpio->rx_io_clk[1]", destinations=["bank1_fclk_mux_B->rx_io_clk[1]"]) +add_connection(source="pll[0]->foutvco", destinations=["bank0_fclk_mux_A->vco_clk[0]", "pll_foutvco[0]"]) +add_connection(source="pll[0]->fout[0]", destinations=["bank0_fclk_mux_A->vco_clk[1]", "pll_fout[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_A->rx_io_clk[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_A->rx_io_clk[1]"]) +add_connection(source="pll[1]->foutvco", destinations=["bank1_fclk_mux_A->vco_clk[0]", "pll_foutvco[1]"]) +add_connection(source="pll[1]->fout[0]", destinations=["bank1_fclk_mux_A->vco_clk[1]", "pll_fout[1]"]) +add_connection(source="bank1_hpio->rx_io_clk[0]", destinations=["bank1_fclk_mux_A->rx_io_clk[0]"]) +add_connection(source="bank1_hpio->rx_io_clk[1]", destinations=["bank1_fclk_mux_A->rx_io_clk[1]"]) +add_connection(source="pll[0]->foutvco", destinations=["bank0_fclk_mux_B->vco_clk[0]"]) +add_connection(source="pll[0]->fout[0]", destinations=["bank0_fclk_mux_B->vco_clk[1]"]) +add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_B->rx_io_clk[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_B->rx_io_clk[1]"]) +add_connection(source="pll[1]->foutvco", destinations=["bank1_fclk_mux_B->vco_clk[0]"]) +add_connection(source="pll[1]->fout[0]", destinations=["bank1_fclk_mux_B->vco_clk[1]"]) +add_connection(source="bank1_hpio->rx_io_clk[0]", destinations=["bank1_fclk_mux_B->rx_io_clk[0]"]) +add_connection(source="bank1_hpio->rx_io_clk[1]", destinations=["bank1_fclk_mux_B->rx_io_clk[1]"]) # fclk mux --> gearbox fast clk -add_connection(source="bank0_fclk_mux_A->fast_clk", destinations=["bank0_hpio->fast_clk_A"]) -add_connection(source="bank0_fclk_mux_B->fast_clk", destinations=["bank0_hpio->fast_clk_B"]) -add_connection(source="bank1_fclk_mux_A->fast_clk", destinations=["bank1_hpio->fast_clk_A"]) -add_connection(source="bank1_fclk_mux_B->fast_clk", destinations=["bank1_hpio->fast_clk_B"]) +add_connection(source="bank0_fclk_mux_A->fast_clk", destinations=["bank0_hpio->fast_clk_A"]) +add_connection(source="bank0_fclk_mux_B->fast_clk", destinations=["bank0_hpio->fast_clk_B"]) +add_connection(source="bank1_fclk_mux_A->fast_clk", destinations=["bank1_hpio->fast_clk_A"]) +add_connection(source="bank1_fclk_mux_B->fast_clk", destinations=["bank1_hpio->fast_clk_B"]) # pin --> gearbox pin # gearbox core clk + cdr clk --> root bank core clk + cdr clk for bank in range(2) : @@ -92,9 +92,9 @@ gbox_root_bank_clkmux_name = "bank%d_root_bank_clkmux" % bank for i in range(40) : gbox_pin = "bank%d_rx_in[%d]" % (bank, i) - add_connection(source=gbox_pin, destinations=["%s->rx_in[%d]" % (gbox_hpio_name, i)]) - add_connection(source="%s->core_clk[%d]" % (gbox_hpio_name, i), destinations=["%s->core_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) - add_connection(source="%s->cdr_clk[%d]" % (gbox_hpio_name, i), destinations=["%s->cdr_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) + add_connection(source=gbox_pin, destinations=["%s->rx_in[%d]" % (gbox_hpio_name, i)]) + add_connection(source="%s->core_clk[%d]" % (gbox_hpio_name, i), destinations=["%s->core_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) + add_connection(source="%s->cdr_clk[%d]" % (gbox_hpio_name, i), destinations=["%s->cdr_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) # Config # Root selection for fabric_clk diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hpio.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hpio.py index e2719415..de2ddce0 100644 --- a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hpio.py +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hpio.py @@ -5,13 +5,13 @@ create_block(name="gbox_hpio") # Ports -add_port(name="fast_clk_A", dir=DIR_IN) -add_port(name="fast_clk_B", dir=DIR_IN) -add_port(name="rx_in", dir=DIR_IN, bit=40) -add_port(name="rx_io_clk", dir=DIR_OUT, bit=2) -add_port(name="core_clk", dir=DIR_OUT, bit=40) -add_port(name="cdr_clk", dir=DIR_OUT, bit=40) -add_port(name="tx_clk", dir=DIR_OUT, bit=40) +add_port(name="fast_clk_A", dir=DIR_IN) +add_port(name="fast_clk_B", dir=DIR_IN) +add_port(name="rx_in", dir=DIR_IN, bit=40) +add_port(name="rx_io_clk", dir=DIR_OUT, bit=2) +add_port(name="core_clk", dir=DIR_OUT, bit=40) +add_port(name="cdr_clk", dir=DIR_OUT, bit=40) +add_port(name="tx_clk", dir=DIR_OUT, bit=40) # Instances for i in range(40) : @@ -22,12 +22,12 @@ for i in range(40) : instance_name = function_library.get_gbox_top_name(i) if i < 20 : - add_connection(source="fast_clk_A", destinations=["%s->fast_clk" % instance_name]) + add_connection(source="fast_clk_A", destinations=["%s->fast_clk" % instance_name]) else : - add_connection(source="fast_clk_B", destinations=["%s->fast_clk" % instance_name]) - add_connection(source="rx_in[%d]" % i, destinations=["%s->rx_in" % instance_name]) - add_connection(source="%s->core_clk" % instance_name, destinations=["core_clk[%d]" % i]) - add_connection(source="%s->cdr_clk" % instance_name, destinations=["cdr_clk[%d]" % i]) - add_connection(source="%s->tx_clk" % instance_name, destinations=["tx_clk[%d]" % i]) -add_connection(source="rx_in[18]", destinations=["rx_io_clk[0]"]) -add_connection(source="rx_in[38]", destinations=["rx_io_clk[1]"]) + add_connection(source="fast_clk_B", destinations=["%s->fast_clk" % instance_name]) + add_connection(source="rx_in[%d]" % i, destinations=["%s->rx_in" % instance_name]) + add_connection(source="%s->core_clk" % instance_name, destinations=["core_clk[%d]" % i]) + add_connection(source="%s->cdr_clk" % instance_name, destinations=["cdr_clk[%d]" % i]) + add_connection(source="%s->tx_clk" % instance_name, destinations=["tx_clk[%d]" % i]) +add_connection(source="rx_in[18]", destinations=["rx_io_clk[0]"]) +add_connection(source="rx_in[38]", destinations=["rx_io_clk[1]"]) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hv_40x2.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hv_40x2.py index 256484b6..f47e5968 100644 --- a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hv_40x2.py +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_hv_40x2.py @@ -5,50 +5,50 @@ create_block(name="gbox_hv_40x2") # Ports -add_port(name="bank0_rx_in", dir=DIR_IN, bit=40) -add_port(name="bank1_rx_in", dir=DIR_IN, bit=40) -add_port(name="pll_fout", dir=DIR_IN, bit=1) -add_port(name="pll_foutvco", dir=DIR_IN, bit=1) -add_port(name="bank0_rx_io_clk", dir=DIR_OUT, bit=2) -add_port(name="bank1_rx_io_clk", dir=DIR_OUT, bit=2) -add_port(name="bank0_root_core_clk", dir=DIR_OUT, bit=2) -add_port(name="bank1_root_core_clk", dir=DIR_OUT, bit=2) -add_port(name="bank0_root_cdr_clk", dir=DIR_OUT, bit=2) -add_port(name="bank1_root_cdr_clk", dir=DIR_OUT, bit=2) +add_port(name="bank0_rx_in", dir=DIR_IN, bit=40) +add_port(name="bank1_rx_in", dir=DIR_IN, bit=40) +add_port(name="pll_fout", dir=DIR_IN, bit=1) +add_port(name="pll_foutvco", dir=DIR_IN, bit=1) +add_port(name="bank0_rx_io_clk", dir=DIR_OUT, bit=2) +add_port(name="bank1_rx_io_clk", dir=DIR_OUT, bit=2) +add_port(name="bank0_root_core_clk", dir=DIR_OUT, bit=2) +add_port(name="bank1_root_core_clk", dir=DIR_OUT, bit=2) +add_port(name="bank0_root_cdr_clk", dir=DIR_OUT, bit=2) +add_port(name="bank1_root_cdr_clk", dir=DIR_OUT, bit=2) # Instances -add_instance(name="bank0_fclk_mux_A", block="gbox_fclk_mux") -add_instance(name="bank1_fclk_mux_A", block="gbox_fclk_mux") -add_instance(name="bank0_fclk_mux_B", block="gbox_fclk_mux") -add_instance(name="bank1_fclk_mux_B", block="gbox_fclk_mux") -add_instance(name="bank0_hpio", block="gbox_hpio") -add_instance(name="bank1_hpio", block="gbox_hpio") -add_instance(name="bank0_root_bank_clkmux", block="gbox_root_bank_clkmux") -add_instance(name="bank1_root_bank_clkmux", block="gbox_root_bank_clkmux") +add_instance(name="bank0_fclk_mux_A", block="gbox_fclk_mux") +add_instance(name="bank1_fclk_mux_A", block="gbox_fclk_mux") +add_instance(name="bank0_fclk_mux_B", block="gbox_fclk_mux") +add_instance(name="bank1_fclk_mux_B", block="gbox_fclk_mux") +add_instance(name="bank0_hpio", block="gbox_hpio") +add_instance(name="bank1_hpio", block="gbox_hpio") +add_instance(name="bank0_root_bank_clkmux", block="gbox_root_bank_clkmux") +add_instance(name="bank1_root_bank_clkmux", block="gbox_root_bank_clkmux") # Connections # pll + pin --> fclk mux -add_connection(source="pll_foutvco", destinations=["bank0_fclk_mux_A->vco_clk[0]"]) -add_connection(source="pll_fout", destinations=["bank0_fclk_mux_A->vco_clk[1]"]) -add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_A->rx_io_clk[0]"]) -add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_A->rx_io_clk[1]"]) -add_connection(source="pll_foutvco", destinations=["bank1_fclk_mux_A->vco_clk[0]"]) -add_connection(source="pll_fout", destinations=["bank1_fclk_mux_A->vco_clk[1]"]) -add_connection(source="bank1_hpio->rx_io_clk[0]", destinations=["bank1_fclk_mux_A->rx_io_clk[0]"]) -add_connection(source="bank1_hpio->rx_io_clk[1]", destinations=["bank1_fclk_mux_A->rx_io_clk[1]"]) -add_connection(source="pll_foutvco", destinations=["bank0_fclk_mux_B->vco_clk[0]"]) -add_connection(source="pll_fout", destinations=["bank0_fclk_mux_B->vco_clk[1]"]) -add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_B->rx_io_clk[0]"]) -add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_B->rx_io_clk[1]"]) -add_connection(source="pll_foutvco", destinations=["bank1_fclk_mux_B->vco_clk[0]"]) -add_connection(source="pll_fout", destinations=["bank1_fclk_mux_B->vco_clk[1]"]) -add_connection(source="bank1_hpio->rx_io_clk[0]", destinations=["bank1_fclk_mux_B->rx_io_clk[0]"]) -add_connection(source="bank1_hpio->rx_io_clk[1]", destinations=["bank1_fclk_mux_B->rx_io_clk[1]"]) +add_connection(source="pll_foutvco", destinations=["bank0_fclk_mux_A->vco_clk[0]"]) +add_connection(source="pll_fout", destinations=["bank0_fclk_mux_A->vco_clk[1]"]) +add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_A->rx_io_clk[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_A->rx_io_clk[1]"]) +add_connection(source="pll_foutvco", destinations=["bank1_fclk_mux_A->vco_clk[0]"]) +add_connection(source="pll_fout", destinations=["bank1_fclk_mux_A->vco_clk[1]"]) +add_connection(source="bank1_hpio->rx_io_clk[0]", destinations=["bank1_fclk_mux_A->rx_io_clk[0]"]) +add_connection(source="bank1_hpio->rx_io_clk[1]", destinations=["bank1_fclk_mux_A->rx_io_clk[1]"]) +add_connection(source="pll_foutvco", destinations=["bank0_fclk_mux_B->vco_clk[0]"]) +add_connection(source="pll_fout", destinations=["bank0_fclk_mux_B->vco_clk[1]"]) +add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_B->rx_io_clk[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_B->rx_io_clk[1]"]) +add_connection(source="pll_foutvco", destinations=["bank1_fclk_mux_B->vco_clk[0]"]) +add_connection(source="pll_fout", destinations=["bank1_fclk_mux_B->vco_clk[1]"]) +add_connection(source="bank1_hpio->rx_io_clk[0]", destinations=["bank1_fclk_mux_B->rx_io_clk[0]"]) +add_connection(source="bank1_hpio->rx_io_clk[1]", destinations=["bank1_fclk_mux_B->rx_io_clk[1]"]) # fclk mux --> gearbox fast clk -add_connection(source="bank0_fclk_mux_A->fast_clk", destinations=["bank0_hpio->fast_clk_A"]) -add_connection(source="bank0_fclk_mux_B->fast_clk", destinations=["bank0_hpio->fast_clk_B"]) -add_connection(source="bank1_fclk_mux_A->fast_clk", destinations=["bank1_hpio->fast_clk_A"]) -add_connection(source="bank1_fclk_mux_B->fast_clk", destinations=["bank1_hpio->fast_clk_B"]) +add_connection(source="bank0_fclk_mux_A->fast_clk", destinations=["bank0_hpio->fast_clk_A"]) +add_connection(source="bank0_fclk_mux_B->fast_clk", destinations=["bank0_hpio->fast_clk_B"]) +add_connection(source="bank1_fclk_mux_A->fast_clk", destinations=["bank1_hpio->fast_clk_A"]) +add_connection(source="bank1_fclk_mux_B->fast_clk", destinations=["bank1_hpio->fast_clk_B"]) # pin --> gearbox pin # gearbox core clk + cdr clk --> root bank core clk + cdr clk for bank in range(2) : @@ -56,19 +56,19 @@ gbox_root_bank_clkmux_name = "bank%d_root_bank_clkmux" % bank for i in range(40) : source = "bank%d_rx_in[%d]" % (bank, i) - add_connection(source=source, destinations=["%s->rx_in[%d]" % (gbox_hpio_name, i)]) + add_connection(source=source, destinations=["%s->rx_in[%d]" % (gbox_hpio_name, i)]) source = "%s->core_clk[%d]" % (gbox_hpio_name, i) - add_connection(source=source, destinations=["%s->core_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) + add_connection(source=source, destinations=["%s->core_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) source = "%s->cdr_clk[%d]" % (gbox_hpio_name, i) - add_connection(source=source, destinations=["%s->cdr_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) + add_connection(source=source, destinations=["%s->cdr_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) for i in range(2) : - add_connection(source="bank%s_hpio->rx_io_clk[%d]" % (bank, i), destinations=["bank%d_rx_io_clk[%d]" % (bank, i)]) + add_connection(source="bank%s_hpio->rx_io_clk[%d]" % (bank, i), destinations=["bank%d_rx_io_clk[%d]" % (bank, i)]) # root bank core clk + cdr clk --> pin core clk + cdr clk -add_connection(source="bank0_root_bank_clkmux->core_clk[0]", destinations=["bank0_root_core_clk[0]"]) -add_connection(source="bank0_root_bank_clkmux->core_clk[1]", destinations=["bank0_root_core_clk[1]"]) -add_connection(source="bank1_root_bank_clkmux->core_clk[0]", destinations=["bank1_root_core_clk[0]"]) -add_connection(source="bank1_root_bank_clkmux->core_clk[1]", destinations=["bank1_root_core_clk[1]"]) -add_connection(source="bank0_root_bank_clkmux->cdr_clk[0]", destinations=["bank0_root_cdr_clk[0]"]) -add_connection(source="bank0_root_bank_clkmux->cdr_clk[1]", destinations=["bank0_root_cdr_clk[1]"]) -add_connection(source="bank1_root_bank_clkmux->cdr_clk[0]", destinations=["bank1_root_cdr_clk[0]"]) -add_connection(source="bank1_root_bank_clkmux->cdr_clk[1]", destinations=["bank1_root_cdr_clk[1]"]) +add_connection(source="bank0_root_bank_clkmux->core_clk[0]", destinations=["bank0_root_core_clk[0]"]) +add_connection(source="bank0_root_bank_clkmux->core_clk[1]", destinations=["bank0_root_core_clk[1]"]) +add_connection(source="bank1_root_bank_clkmux->core_clk[0]", destinations=["bank1_root_core_clk[0]"]) +add_connection(source="bank1_root_bank_clkmux->core_clk[1]", destinations=["bank1_root_core_clk[1]"]) +add_connection(source="bank0_root_bank_clkmux->cdr_clk[0]", destinations=["bank0_root_cdr_clk[0]"]) +add_connection(source="bank0_root_bank_clkmux->cdr_clk[1]", destinations=["bank0_root_cdr_clk[1]"]) +add_connection(source="bank1_root_bank_clkmux->cdr_clk[0]", destinations=["bank1_root_cdr_clk[0]"]) +add_connection(source="bank1_root_bank_clkmux->cdr_clk[1]", destinations=["bank1_root_cdr_clk[1]"]) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_osc.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_osc.py index b5ca877e..18ff792e 100644 --- a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_osc.py +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_osc.py @@ -2,8 +2,8 @@ create_block(name="rc_osc_50mhz") # Ports -add_port(name="osc", dir=DIR_IN) -add_port(name="o_osc", dir=DIR_OUT) +add_port(name="osc", dir=DIR_IN) +add_port(name="o_osc", dir=DIR_OUT) # Connections -add_connection(source="osc", destinations=["o_osc"]) +add_connection(source="osc", destinations=["o_osc"]) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_pll.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_pll.py index 78be46e3..152acf2c 100644 --- a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_pll.py +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_pll.py @@ -2,9 +2,9 @@ create_block(name="PLL") # Ports -add_port(name="fref", dir=DIR_IN) -add_port(name="fout", dir=DIR_OUT, bit=4) -add_port(name="foutvco", dir=DIR_OUT, bit=1) +add_port(name="fref", dir=DIR_IN) +add_port(name="fout", dir=DIR_OUT, bit=4) +add_port(name="foutvco", dir=DIR_OUT, bit=1) # Connections add_connection(source="fref", destinations=["fout[0]", "fout[1]", "fout[2]", "fout[3]", "foutvco"]) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_root_bank_clkmux.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_root_bank_clkmux.py index e5795189..3fc49fb9 100644 --- a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_root_bank_clkmux.py +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_root_bank_clkmux.py @@ -2,10 +2,10 @@ create_block(name="gbox_root_bank_clkmux") # Ports -add_port(name="core_clk_in", dir=DIR_IN, bit=40) -add_port(name="cdr_clk_in", dir=DIR_IN, bit=40) -add_port(name="core_clk", dir=DIR_OUT, bit=2) -add_port(name="cdr_clk", dir=DIR_OUT, bit=2) +add_port(name="core_clk_in", dir=DIR_IN, bit=40) +add_port(name="cdr_clk_in", dir=DIR_IN, bit=40) +add_port(name="core_clk", dir=DIR_OUT, bit=2) +add_port(name="cdr_clk", dir=DIR_OUT, bit=2) # Config core_clk_selection_A = {} diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_top.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_top.py index 43944cc4..7d00a56a 100644 --- a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_top.py +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gbox_top.py @@ -2,11 +2,11 @@ create_block(name="gbox_top") # Ports -add_port(name="fast_clk", dir=DIR_IN) -add_port(name="rx_in", dir=DIR_IN) -add_port(name="core_clk", dir=DIR_OUT) -add_port(name="cdr_clk", dir=DIR_OUT) -add_port(name="tx_clk", dir=DIR_OUT) +add_port(name="fast_clk", dir=DIR_IN) +add_port(name="rx_in", dir=DIR_IN) +add_port(name="core_clk", dir=DIR_OUT) +add_port(name="cdr_clk", dir=DIR_OUT) +add_port(name="tx_clk", dir=DIR_OUT) # Connections add_connection(source="fast_clk", destinations=["cdr_clk"]) diff --git a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gemini_compact_22x4_gbox_hp_40x1.py b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gemini_compact_22x4_gbox_hp_40x1.py index ef7e4dbd..07a248cb 100644 --- a/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gemini_compact_22x4_gbox_hp_40x1.py +++ b/src/ConfigurationRaptor/CFGDeviceDatabase/Virgo/routing_library/gemini_compact_22x4_gbox_hp_40x1.py @@ -11,41 +11,41 @@ create_block(name="gbox_hp_40x1") # Ports -add_port(name="bank0_rx_in", dir=DIR_IN, bit=40) -add_port(name="fclk_buf", dir=DIR_IN, bit=8) -add_port(name="fabric_clk", dir=DIR_OUT, bit=16) +add_port(name="bank0_rx_in", dir=DIR_IN, bit=40) +add_port(name="fclk_buf", dir=DIR_IN, bit=8) +add_port(name="fabric_clk", dir=DIR_OUT, bit=16) # Instances -add_instance(name="rc_osc_50mhz", block="rc_osc_50mhz") -add_instance(name="pll_refmux[0]", block="gbox_pll_refmux") -add_instance(name="pll_refmux[1]", block="gbox_pll_refmux") -add_instance(name="pll[0]", block="PLL") -add_instance(name="pll[1]", block="PLL") -add_instance(name="bank0_fclk_mux_A", block="gbox_fclk_mux") -add_instance(name="bank0_fclk_mux_B", block="gbox_fclk_mux") -add_instance(name="bank0_hpio", block="gbox_hpio") -add_instance(name="bank0_root_bank_clkmux", block="gbox_root_bank_clkmux") +add_instance(name="rc_osc_50mhz", block="rc_osc_50mhz") +add_instance(name="pll_refmux[0]", block="gbox_pll_refmux") +add_instance(name="pll_refmux[1]", block="gbox_pll_refmux") +add_instance(name="pll[0]", block="PLL") +add_instance(name="pll[1]", block="PLL") +add_instance(name="bank0_fclk_mux_A", block="gbox_fclk_mux") +add_instance(name="bank0_fclk_mux_B", block="gbox_fclk_mux") +add_instance(name="bank0_hpio", block="gbox_hpio") +add_instance(name="bank0_root_bank_clkmux", block="gbox_root_bank_clkmux") # Connections # osc + pin --> refmux -add_connection(source="rc_osc_50mhz->o_osc", destinations=["pll_refmux[0]->rosc_clk", "pll_refmux[1]->rosc_clk"]) -add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["pll_refmux[0]->bank0_hp_rx_io_clk[0]", "pll_refmux[1]->bank0_hp_rx_io_clk[0]"]) -add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["pll_refmux[0]->bank0_hp_rx_io_clk[1]", "pll_refmux[1]->bank0_hp_rx_io_clk[1]"]) +add_connection(source="rc_osc_50mhz->o_osc", destinations=["pll_refmux[0]->rosc_clk", "pll_refmux[1]->rosc_clk"]) +add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["pll_refmux[0]->bank0_hp_rx_io_clk[0]", "pll_refmux[1]->bank0_hp_rx_io_clk[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["pll_refmux[0]->bank0_hp_rx_io_clk[1]", "pll_refmux[1]->bank0_hp_rx_io_clk[1]"]) # refmux --> pll -add_connection(source="pll_refmux[0]->out", destinations=["pll[0]->fref"]) -add_connection(source="pll_refmux[1]->out", destinations=["pll[1]->fref"]) +add_connection(source="pll_refmux[0]->out", destinations=["pll[0]->fref"]) +add_connection(source="pll_refmux[1]->out", destinations=["pll[1]->fref"]) # pll + pin --> fclk mux -add_connection(source="pll[0]->foutvco", destinations=["bank0_fclk_mux_A->vco_clk[0]"]) -add_connection(source="pll[0]->fout[0]", destinations=["bank0_fclk_mux_A->vco_clk[1]"]) -add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_A->rx_io_clk[0]"]) -add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_A->rx_io_clk[1]"]) -add_connection(source="pll[0]->foutvco", destinations=["bank0_fclk_mux_B->vco_clk[0]"]) -add_connection(source="pll[0]->fout[0]", destinations=["bank0_fclk_mux_B->vco_clk[1]"]) -add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_B->rx_io_clk[0]"]) -add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_B->rx_io_clk[1]"]) +add_connection(source="pll[0]->foutvco", destinations=["bank0_fclk_mux_A->vco_clk[0]"]) +add_connection(source="pll[0]->fout[0]", destinations=["bank0_fclk_mux_A->vco_clk[1]"]) +add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_A->rx_io_clk[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_A->rx_io_clk[1]"]) +add_connection(source="pll[0]->foutvco", destinations=["bank0_fclk_mux_B->vco_clk[0]"]) +add_connection(source="pll[0]->fout[0]", destinations=["bank0_fclk_mux_B->vco_clk[1]"]) +add_connection(source="bank0_hpio->rx_io_clk[0]", destinations=["bank0_fclk_mux_B->rx_io_clk[0]"]) +add_connection(source="bank0_hpio->rx_io_clk[1]", destinations=["bank0_fclk_mux_B->rx_io_clk[1]"]) # fclk mux --> gearbox fast clk -add_connection(source="bank0_fclk_mux_A->fast_clk", destinations=["bank0_hpio->fast_clk_A"]) -add_connection(source="bank0_fclk_mux_B->fast_clk", destinations=["bank0_hpio->fast_clk_B"]) +add_connection(source="bank0_fclk_mux_A->fast_clk", destinations=["bank0_hpio->fast_clk_A"]) +add_connection(source="bank0_fclk_mux_B->fast_clk", destinations=["bank0_hpio->fast_clk_B"]) # pin --> gearbox pin # gearbox core clk + cdr clk --> root bank core clk + cdr clk gbox_hpio_name = "bank0_hpio" @@ -54,9 +54,9 @@ if i in [16, 17, 36, 37]: continue gbox_pin = "bank0_rx_in[%d]" % (i) - add_connection(source=gbox_pin, destinations=["%s->rx_in[%d]" % (gbox_hpio_name, i)]) - add_connection(source="%s->core_clk[%d]" % (gbox_hpio_name, i), destinations=["%s->core_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) - add_connection(source="%s->cdr_clk[%d]" % (gbox_hpio_name, i), destinations=["%s->cdr_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) + add_connection(source=gbox_pin, destinations=["%s->rx_in[%d]" % (gbox_hpio_name, i)]) + add_connection(source="%s->core_clk[%d]" % (gbox_hpio_name, i), destinations=["%s->core_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) + add_connection(source="%s->cdr_clk[%d]" % (gbox_hpio_name, i), destinations=["%s->cdr_clk_in[%d]" % (gbox_root_bank_clkmux_name, i)]) # Config # Root selection for fabric_clk From c8bbdbaba8253baa30efdab948d1d49835fba42a Mon Sep 17 00:00:00 2001 From: alaindargelas Date: Fri, 27 Sep 2024 08:14:53 -0700 Subject: [PATCH 4/4] New way to decide the IO routing configuration bits, vpr_latest --- FOEDAG_rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FOEDAG_rs b/FOEDAG_rs index 59a14fd1..eb05be1e 160000 --- a/FOEDAG_rs +++ b/FOEDAG_rs @@ -1 +1 @@ -Subproject commit 59a14fd108a2b9668f5f58895cc942fa6caa8613 +Subproject commit eb05be1e23a3de1209446c30c4077dbe95207295