From 01732c365a65dc65c444749dbb09eea15131fb8e Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Wed, 3 Apr 2019 16:37:43 -0700 Subject: [PATCH 1/2] This modification makes several interworking changes to leverage the 7-series dedicated constant sources: - Change yosys to use new VCC and GND black boxes for the $true and $false nets. - Implement synthetic tiles BLK_SY-VCC and BLK_SY-GND that connect to a global VCC and GND constant network respectively. - Connect TIEOFF HARD0 and HARD1 pins to the new global constant networks. - Add additional CARRY0 techmap to handle constant CYINIT signals. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- utils/lib/connection_database.py | 5 +- utils/lib/connection_database.sql | 11 ++ utils/lib/rr_graph/graph2.py | 2 +- .../xc7a50t-arty-roi-virt/CMakeLists.txt | 2 +- .../xc7a50t-basys3-roi-virt/CMakeLists.txt | 2 +- xc7/archs/artix7/tiles/CMakeLists.txt | 2 - .../xc7z010-zybo-roi-virt/CMakeLists.txt | 2 +- xc7/archs/zynq7/tiles/CMakeLists.txt | 2 - xc7/make/arch_define.cmake | 1 - .../common_slice/carry/CMakeLists.txt | 17 +- .../common_slice/carry/carry0.model.xml | 15 ++ .../common_slice/carry/carry0.pb_type.xml | 26 +++ .../common_slice/carry/carry0.sim.v | 2 +- xc7/primitives/ff/ff.model.xml | 43 ++--- xc7/primitives/ff/ff.pb_type.xml | 8 + .../slicem/Ndram/d_dram.pb_type.xml | 8 + .../slicem/Ndram/ntemplate.N_dram.model.xml | 6 + xc7/primitives/slicem/slicem.pb_type.xml | 36 ++-- xc7/techmap/cells_map.v | 48 ++++++ xc7/techmap/cells_sim.v | 12 +- xc7/utils/prjxray_arch_import.py | 93 +++++++++- .../prjxray_assign_tile_pin_direction.py | 22 ++- xc7/utils/prjxray_constant_site_pins.py | 67 ++++++++ xc7/utils/prjxray_create_edges.py | 83 ++++++++- xc7/utils/prjxray_create_synth_tiles.py | 73 +++++++- xc7/utils/prjxray_form_channels.py | 160 +++++++++++++++++- xc7/utils/prjxray_routing_import.py | 53 ++++-- xc7/yosys/synth.tcl | 6 +- 28 files changed, 709 insertions(+), 98 deletions(-) create mode 100644 xc7/primitives/common_slice/carry/carry0.model.xml create mode 100644 xc7/primitives/common_slice/carry/carry0.pb_type.xml create mode 100644 xc7/utils/prjxray_constant_site_pins.py diff --git a/utils/lib/connection_database.py b/utils/lib/connection_database.py index 5b42616b9f..5591af9d18 100644 --- a/utils/lib/connection_database.py +++ b/utils/lib/connection_database.py @@ -76,8 +76,9 @@ def get_wire_pkey(conn, tile_name, wire): """, (tile_name, wire) ) - (wire_pkey, ) = c.fetchone() - return wire_pkey + results = c.fetchone() + assert results is not None, (tile_name, wire) + return results[0] def get_track_model(conn, track_pkey): diff --git a/utils/lib/connection_database.sql b/utils/lib/connection_database.sql index 54f0934d36..f9cc68a253 100644 --- a/utils/lib/connection_database.sql +++ b/utils/lib/connection_database.sql @@ -238,3 +238,14 @@ CREATE TABLE y_list( idx INT, info INT ); + +-- Table that represents the (optional) VCC and GND global sources. +-- VPR cannot natively take advantage of local VCC and GND sources, so +-- a global source is generated, and local sources will connect to the global +-- sources. +CREATE TABLE constant_sources( + vcc_track_pkey INT, + gnd_track_pkey INT, + FOREIGN KEY(vcc_track_pkey) REFERENCES track(pkey), + FOREIGN KEY(gnd_track_pkey) REFERENCES track(pkey) +); diff --git a/utils/lib/rr_graph/graph2.py b/utils/lib/rr_graph/graph2.py index 33eb60c06f..25cf988b24 100644 --- a/utils/lib/rr_graph/graph2.py +++ b/utils/lib/rr_graph/graph2.py @@ -349,7 +349,7 @@ def add_switch(self, switch): switch = Switch(**switch_dict) assert switch.name not in self.switch_name_map - self.switch_name_map[switch.name] = switch + self.switch_name_map[switch.name] = switch.id self.switches.append(switch) return switch.id diff --git a/xc7/archs/artix7/devices/xc7a50t-arty-roi-virt/CMakeLists.txt b/xc7/archs/artix7/devices/xc7a50t-arty-roi-virt/CMakeLists.txt index 785e99d8cd..9dbea2a406 100644 --- a/xc7/archs/artix7/devices/xc7a50t-arty-roi-virt/CMakeLists.txt +++ b/xc7/archs/artix7/devices/xc7a50t-arty-roi-virt/CMakeLists.txt @@ -3,7 +3,7 @@ add_xc7_device_define_type( DEVICE xc7a50t-arty ROI_PART xc7a35tcsg324-1 ROI_DIR ${PRJXRAY_DB_DIR}/artix7/harness/arty-a7/uart - TILE_TYPES INT_R INT_L CLBLL_R CLBLL_L CLBLM_R CLBLM_L BRAM_L + TILE_TYPES CLBLL_R CLBLL_L CLBLM_R CLBLM_L BRAM_L NAME arty ) diff --git a/xc7/archs/artix7/devices/xc7a50t-basys3-roi-virt/CMakeLists.txt b/xc7/archs/artix7/devices/xc7a50t-basys3-roi-virt/CMakeLists.txt index 210218e1a8..6bf4ce7ae7 100644 --- a/xc7/archs/artix7/devices/xc7a50t-basys3-roi-virt/CMakeLists.txt +++ b/xc7/archs/artix7/devices/xc7a50t-basys3-roi-virt/CMakeLists.txt @@ -3,7 +3,7 @@ add_xc7_device_define_type( DEVICE xc7a50t-basys3 ROI_PART xc7a35tcpg236-1 ROI_DIR ${PRJXRAY_DB_DIR}/artix7/harness/basys3/swbut - TILE_TYPES INT_R INT_L CLBLL_R CLBLL_L CLBLM_R CLBLM_L BRAM_L + TILE_TYPES CLBLL_R CLBLL_L CLBLM_R CLBLM_L BRAM_L NAME basys3 ) diff --git a/xc7/archs/artix7/tiles/CMakeLists.txt b/xc7/archs/artix7/tiles/CMakeLists.txt index fc27c4b42b..2d107651e6 100644 --- a/xc7/archs/artix7/tiles/CMakeLists.txt +++ b/xc7/archs/artix7/tiles/CMakeLists.txt @@ -4,5 +4,3 @@ add_subdirectory(clbll_l) add_subdirectory(clbll_r) add_subdirectory(clblm_l) add_subdirectory(clblm_r) -add_subdirectory(int_l) -add_subdirectory(int_r) diff --git a/xc7/archs/zynq7/devices/xc7z010-zybo-roi-virt/CMakeLists.txt b/xc7/archs/zynq7/devices/xc7z010-zybo-roi-virt/CMakeLists.txt index 0eeb015551..f7a86905aa 100644 --- a/xc7/archs/zynq7/devices/xc7z010-zybo-roi-virt/CMakeLists.txt +++ b/xc7/archs/zynq7/devices/xc7z010-zybo-roi-virt/CMakeLists.txt @@ -3,6 +3,6 @@ add_xc7_device_define_type( DEVICE xc7z010-zybo ROI_PART xc7z010clg400-1 ROI_DIR ${PRJXRAY_DB_DIR}/zynq7/harness/zybo/swbut - TILE_TYPES INT_R INT_L CLBLL_R CLBLL_L CLBLM_R CLBLM_L BRAM_L + TILE_TYPES CLBLL_R CLBLL_L CLBLM_R CLBLM_L BRAM_L NAME zybo ) diff --git a/xc7/archs/zynq7/tiles/CMakeLists.txt b/xc7/archs/zynq7/tiles/CMakeLists.txt index fc27c4b42b..2d107651e6 100644 --- a/xc7/archs/zynq7/tiles/CMakeLists.txt +++ b/xc7/archs/zynq7/tiles/CMakeLists.txt @@ -4,5 +4,3 @@ add_subdirectory(clbll_l) add_subdirectory(clbll_r) add_subdirectory(clblm_l) add_subdirectory(clblm_r) -add_subdirectory(int_l) -add_subdirectory(int_r) diff --git a/xc7/make/arch_define.cmake b/xc7/make/arch_define.cmake index 9dcac655a4..3f591cdb09 100644 --- a/xc7/make/arch_define.cmake +++ b/xc7/make/arch_define.cmake @@ -23,7 +23,6 @@ function(ADD_XC7_ARCH_DEFINE) --place_algorithm bounding_box --enable_timing_computations off --allow_unrelated_clustering on - --round_robin_prepacking on RR_PATCH_TOOL ${symbiflow-arch-defs_SOURCE_DIR}/xc7/utils/prjxray_routing_import.py RR_PATCH_CMD "${CMAKE_COMMAND} -E env \ diff --git a/xc7/primitives/common_slice/carry/CMakeLists.txt b/xc7/primitives/common_slice/carry/CMakeLists.txt index 82e2437c73..e3e2376eb6 100644 --- a/xc7/primitives/common_slice/carry/CMakeLists.txt +++ b/xc7/primitives/common_slice/carry/CMakeLists.txt @@ -2,16 +2,17 @@ add_file_target( FILE carry.sim.v SCANNER_TYPE verilog ) -add_file_target( - FILE carry0.sim.v - SCANNER_TYPE verilog - ) v2x( NAME carry SRCS carry.sim.v ) -v2x( - NAME carry0 - SRCS carry0.sim.v -) + +add_file_target( + FILE carry0.pb_type.xml + SCANNER_TYPE xml + ) +add_file_target( + FILE carry0.model.xml + SCANNER_TYPE xml + ) diff --git a/xc7/primitives/common_slice/carry/carry0.model.xml b/xc7/primitives/common_slice/carry/carry0.model.xml new file mode 100644 index 0000000000..835c08515c --- /dev/null +++ b/xc7/primitives/common_slice/carry/carry0.model.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/xc7/primitives/common_slice/carry/carry0.pb_type.xml b/xc7/primitives/common_slice/carry/carry0.pb_type.xml new file mode 100644 index 0000000000..ee0731314f --- /dev/null +++ b/xc7/primitives/common_slice/carry/carry0.pb_type.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + PRECYINIT.C0 = CYINIT_C0 + PRECYINIT.C1 = CYINIT_C1 + + + diff --git a/xc7/primitives/common_slice/carry/carry0.sim.v b/xc7/primitives/common_slice/carry/carry0.sim.v index 7e60e847c5..b0e41c5927 100644 --- a/xc7/primitives/common_slice/carry/carry0.sim.v +++ b/xc7/primitives/common_slice/carry/carry0.sim.v @@ -1,5 +1,5 @@ (*blackbox*) -module CARRY0(O, CO_CHAIN, CO_FABRIC, CI_INIT, CI, DI, S); +module CARRY0_CONST(O, CO_CHAIN, CO_FABRIC, CI_INIT, CI, DI, S); (* DELAY_CONST_CI="10e-12" *) (* DELAY_CONST_CI_INIT="10e-12" *) (* DELAY_CONST_S="10e-12" *) diff --git a/xc7/primitives/ff/ff.model.xml b/xc7/primitives/ff/ff.model.xml index bca64b9716..0c891b3d01 100644 --- a/xc7/primitives/ff/ff.model.xml +++ b/xc7/primitives/ff/ff.model.xml @@ -1,10 +1,10 @@ - - - - + + + + @@ -34,35 +34,40 @@ - - - - + + + + - + - - - - + + + + - + - - - - + + + + - + + + + + + diff --git a/xc7/primitives/ff/ff.pb_type.xml b/xc7/primitives/ff/ff.pb_type.xml index 70e2512aa5..9811c922b4 100644 --- a/xc7/primitives/ff/ff.pb_type.xml +++ b/xc7/primitives/ff/ff.pb_type.xml @@ -17,6 +17,14 @@ + + + + + + + + diff --git a/xc7/primitives/slicem/Ndram/d_dram.pb_type.xml b/xc7/primitives/slicem/Ndram/d_dram.pb_type.xml index 50c75fbf6e..02c69395b8 100644 --- a/xc7/primitives/slicem/Ndram/d_dram.pb_type.xml +++ b/xc7/primitives/slicem/Ndram/d_dram.pb_type.xml @@ -13,6 +13,14 @@ + + + + + + + + diff --git a/xc7/primitives/slicem/Ndram/ntemplate.N_dram.model.xml b/xc7/primitives/slicem/Ndram/ntemplate.N_dram.model.xml index 7335cce2dc..dcf5a416e8 100644 --- a/xc7/primitives/slicem/Ndram/ntemplate.N_dram.model.xml +++ b/xc7/primitives/slicem/Ndram/ntemplate.N_dram.model.xml @@ -1,5 +1,11 @@ + + + + + + diff --git a/xc7/primitives/slicem/slicem.pb_type.xml b/xc7/primitives/slicem/slicem.pb_type.xml index d0ff8c16e9..f8b518e426 100644 --- a/xc7/primitives/slicem/slicem.pb_type.xml +++ b/xc7/primitives/slicem/slicem.pb_type.xml @@ -184,10 +184,10 @@ - - - + + + @@ -432,18 +432,12 @@ - - - - DLUT.RAM - - - - - + + + @@ -582,12 +576,6 @@ - - - - DLUT.RAM - - @@ -629,7 +617,17 @@ - + + + + + BLK_IG-SLICEM.CLK = DLUT.RAM + + + diff --git a/xc7/techmap/cells_map.v b/xc7/techmap/cells_map.v index 700715511a..e93b069950 100644 --- a/xc7/techmap/cells_map.v +++ b/xc7/techmap/cells_map.v @@ -926,3 +926,51 @@ module RAMB18E1 ( .DOPBDOP(DOPBDOP) ); endmodule + +module CARRY0(output CO_CHAIN, CO_FABRIC, O, input CI, CI_INIT, DI, S); + parameter CYINIT_FABRIC = 0; + parameter _TECHMAP_CONSTMSK_CI_INIT_ = 0; + parameter _TECHMAP_CONSTVAL_CI_INIT_ = 0; + + // Only connect CI_INIT for non-constant signals. + wire CI_INIT_INNER; + if(_TECHMAP_CONSTMSK_CI_INIT_ == 0 && CYINIT_FABRIC) begin + CARRY0_CONST #( + .CYINIT_AX(1'b1), + .CYINIT_C0(1'b0), + .CYINIT_C1(1'b0) + ) _TECHMAP_REPLACE_ ( + .CO_CHAIN(CO_CHAIN), + .CO_FABRIC(CO_FABRIC), + .O(O), + .CI_INIT(CI_INIT), + .DI(DI), + .S(S) + ); + end else if(!CYINIT_FABRIC) begin + CARRY0_CONST #( + .CYINIT_AX(1'b0), + .CYINIT_C0(1'b0), + .CYINIT_C1(1'b0) + ) _TECHMAP_REPLACE_ ( + .CO_CHAIN(CO_CHAIN), + .CO_FABRIC(CO_FABRIC), + .O(O), + .CI(CI), + .DI(DI), + .S(S) + ); + end else begin + CARRY0_CONST #( + .CYINIT_AX(1'b0), + .CYINIT_C0(_TECHMAP_CONSTVAL_CI_INIT_ == 0), + .CYINIT_C0(_TECHMAP_CONSTVAL_CI_INIT_ == 1) + ) _TECHMAP_REPLACE_ ( + .CO_CHAIN(CO_CHAIN), + .CO_FABRIC(CO_FABRIC), + .O(O), + .DI(DI), + .S(S) + ); + end +endmodule diff --git a/xc7/techmap/cells_sim.v b/xc7/techmap/cells_sim.v index 6f0e817100..429288c7d6 100644 --- a/xc7/techmap/cells_sim.v +++ b/xc7/techmap/cells_sim.v @@ -51,11 +51,17 @@ endmodule // ============================================================================ // Carry chain primitives -module CARRY0(output CO_CHAIN, CO_FABRIC, O, input CI, CI_INIT, DI, S); - parameter CYINIT_FABRIC = 0; +module CARRY0_CONST(output CO_CHAIN, CO_FABRIC, O, input CI, CI_INIT, DI, S); + parameter CYINIT_AX = 1'b0; + parameter CYINIT_C0 = 1'b0; + parameter CYINIT_C1 = 1'b0; wire CI_COMBINE; - if(CYINIT_FABRIC) begin + if(CYINIT_AX) begin assign CI_COMBINE = CI_INIT; + end else if(CYINIT_C0) begin + assign CI_COMBINE = 0; + end else if(CYINIT_C1) begin + assign CI_COMBINE = 1; end else begin assign CI_COMBINE = CI; end diff --git a/xc7/utils/prjxray_arch_import.py b/xc7/utils/prjxray_arch_import.py index 8303b60047..ec94d04522 100644 --- a/xc7/utils/prjxray_arch_import.py +++ b/xc7/utils/prjxray_arch_import.py @@ -95,15 +95,104 @@ def create_synth_io_tiles(complexblocklist_xml, pb_name, is_input): ) -def add_synthetic_tile(complexblocklist_xml): +def create_synth_constant_tiles( + model_xml, complexblocklist_xml, pb_name, signal +): + """ Creates synthetic constant tile generates some constant signal. + + Routing import will create a global network to fan this signal to local + constant sources. + """ + pb_xml = ET.SubElement( + complexblocklist_xml, 'pb_type', { + 'name': pb_name, + } + ) + + ET.SubElement( + pb_xml, 'fc', { + 'in_type': 'abs', + 'in_val': '2', + 'out_type': 'abs', + 'out_val': '2', + } + ) + + interconnect_xml = ET.SubElement(pb_xml, 'interconnect') + + blif_model = '.subckt ' + signal + port_type = 'output' + pin_name = signal + + ET.SubElement(pb_xml, port_type, { + 'name': pin_name, + 'num_pins': '1', + }) + + port_pin = '{}.{}'.format(pb_name, pin_name) + pad_pin = '{}.{}'.format(pin_name, pin_name) + + input_name = pad_pin + output_name = port_pin + + pin_pb_type = ET.SubElement( + pb_xml, 'pb_type', { + 'name': pin_name, + 'blif_model': blif_model, + 'num_pb': '1', + } + ) + ET.SubElement( + pin_pb_type, port_type, { + 'name': pin_name, + 'num_pins': '1', + } + ) + + direct_xml = ET.SubElement( + interconnect_xml, 'direct', { + 'name': '{}_to_{}'.format(input_name, output_name), + 'input': input_name, + 'output': output_name, + } + ) + + ET.SubElement( + direct_xml, 'delay_constant', { + 'max': '1e-11', + 'in_port': input_name, + 'out_port': output_name, + } + ) + + model = ET.SubElement(model_xml, 'model', { + 'name': signal, + }) + + ET.SubElement(model, 'input_ports') + output_ports = ET.SubElement(model, 'output_ports') + ET.SubElement(output_ports, 'port', { + 'name': pin_name, + }) + + +def add_synthetic_tiles(model_xml, complexblocklist_xml): create_synth_io_tiles(complexblocklist_xml, 'BLK_SY-INPAD', is_input=True) create_synth_io_tiles( complexblocklist_xml, 'BLK_SY-OUTPAD', is_input=False ) + create_synth_constant_tiles( + model_xml, complexblocklist_xml, 'BLK_SY-VCC', 'VCC' + ) + create_synth_constant_tiles( + model_xml, complexblocklist_xml, 'BLK_SY-GND', 'GND' + ) return { 'output': 'BLK_SY-INPAD', 'input': 'BLK_SY-OUTPAD', + 'VCC': 'BLK_SY-VCC', + 'GND': 'BLK_SY-GND', } @@ -205,7 +294,7 @@ def main(): y2=j['info']['GRID_Y_MAX'], ) - synth_tile_map = add_synthetic_tile(complexblocklist_xml) + synth_tile_map = add_synthetic_tiles(model_xml, complexblocklist_xml) for loc in g.tile_locations(): gridinfo = g.gridinfo_at_loc(loc) diff --git a/xc7/utils/prjxray_assign_tile_pin_direction.py b/xc7/utils/prjxray_assign_tile_pin_direction.py index 832f013397..92bd8c2fab 100644 --- a/xc7/utils/prjxray_assign_tile_pin_direction.py +++ b/xc7/utils/prjxray_assign_tile_pin_direction.py @@ -23,8 +23,8 @@ NodeClassification, yield_wire_info_from_node, get_track_model, node_to_site_pins ) +from prjxray_constant_site_pins import yield_ties_to_wire import progressbar -import sqlite3 import datetime from prjxray_db_cache import DatabaseCache @@ -150,6 +150,17 @@ def handle_edges_to_channels( ): c = conn.cursor() + c.execute( + """ +SELECT vcc_track_pkey, gnd_track_pkey FROM constant_sources; + """ + ) + vcc_track_pkey, gnd_track_pkey = c.fetchone() + const_tracks = { + 0: gnd_track_pkey, + 1: vcc_track_pkey, + } + for node_pkey, classification in progressbar.progressbar(c.execute(""" SELECT pkey, classification FROM node WHERE classification != ?; """, (NodeClassification.CHANNEL.value, ))): @@ -265,6 +276,15 @@ def handle_edges_to_channels( ) edge_assignments[(tile_type, wire)].append(available_pins) + for constant in yield_ties_to_wire(wire): + tracks_model = channel_wires_to_tracks[ + const_tracks[constant]] + available_pins = set( + pin_dir for _, pin_dir in tracks_model. + get_tracks_for_wire_at_coord((grid_x, grid_y)) + ) + edge_assignments[(tile_type, wire)].append(available_pins) + def main(): parser = argparse.ArgumentParser() diff --git a/xc7/utils/prjxray_constant_site_pins.py b/xc7/utils/prjxray_constant_site_pins.py new file mode 100644 index 0000000000..67e0269cb9 --- /dev/null +++ b/xc7/utils/prjxray_constant_site_pins.py @@ -0,0 +1,67 @@ +""" This file defines tieable wires in 7-series designs. + +FEATURES_WHEN_ROUTED is a map of wires connected to site pins to a FASM feature. +If the wire is routed from the general routing network, then this feature must +also be emitted. + +WIRE_TIE_OPTIONS is a map of wires connected to site pins to the list of tie +options available for the pin. + +""" + +# Using these wires requires a mux to be enabled. +FEATURES_WHEN_ROUTED = { + #'CLBLL_L_SR': 'SLICEL_X1.SRUSEDMUX', + #'CLBLM_L_SR': 'SLICEL_X1.SRUSEDMUX', + #'CLBLL_LL_SR': 'SLICEL_X0.SRUSEDMUX', + #'CLBLM_M_SR': 'SLICEM_X0.SRUSEDMUX', + #'CLBLL_L_CE': 'SLICEL_X1.CEUSEDMUX', + #'CLBLM_L_CE': 'SLICEL_X1.CEUSEDMUX', + #'CLBLL_LL_CE': 'SLICEL_X0.CEUSEDMUX', + #'CLBLM_M_CE': 'SLICEM_X0.CEUSEDMUX', +} + +# What tie options are available for this site pin. +WIRE_TIE_OPTIONS = { + #'CLBLL_L_SR': [0], + #'CLBLM_L_SR': [0], + #'CLBLL_LL_SR': [0], + #'CLBLM_M_SR': [0], + #'CLBLL_L_CE': [1], + #'CLBLM_L_CE': [1], + #'CLBLL_LL_CE': [1], + #'CLBLM_M_CE': [1], +} + + +def yield_ties_to_wire(wire): + """ Given the name of the wire, yields available constant tie options. + + Args: + wire(str): The name of the tile wire connected to a site pin. + If the wire name is not found, yields nothing. + + It is not an error to call this function with wires that are not connected + to site pins, as the function will yield nothing. + + """ + if wire not in WIRE_TIE_OPTIONS: + return + + for constant in WIRE_TIE_OPTIONS[wire]: + yield constant + + +def feature_when_routed(wire): + """ Given the name of the wire, returns None or a required feature. + + Args: + wire(str): The name of the tile wire connected to a site pin. + If the wire name is not found, returns None. + + It is not an error to call this function with wires that are not connected + to site pins, as the function will return None. + + """ + if wire in FEATURES_WHEN_ROUTED: + return FEATURES_WHEN_ROUTED[wire] diff --git a/xc7/utils/prjxray_create_edges.py b/xc7/utils/prjxray_create_edges.py index 6db41cd192..e444e5fb1c 100644 --- a/xc7/utils/prjxray_create_edges.py +++ b/xc7/utils/prjxray_create_edges.py @@ -27,12 +27,12 @@ import simplejson as json import progressbar import datetime -import sqlite3 import functools from collections import namedtuple from lib.rr_graph import tracks from lib.rr_graph import graph2 from prjxray.site_type import SitePinDirection +from prjxray_constant_site_pins import yield_ties_to_wire from lib.connection_database import get_track_model from lib.rr_graph.graph2 import NodeType import multiprocessing @@ -207,7 +207,9 @@ def find_wire_in_tile(tile_type, wire): );""", (wire, tile_type) ) - return c.fetchone()[0] + result = c.fetchone() + assert result is not None, (tile_type, wire) + return result[0] @functools.lru_cache(maxsize=None) def find_wire(tile, tile_type, wire): @@ -413,6 +415,22 @@ def find_connector(wire_pkey, node_pkey): return Connector(tracks=get_track_model(conn, track_pkey)) + # Check if this node has a special track. This is being used to + # denote the GND and VCC track connections on TIEOFF HARD0 and HARD1. + c.execute( + """ +SELECT + track_pkey, + site_wire_pkey +FROM + node +WHERE + pkey = ?;""", (node_pkey, ) + ) + for track_pkey, site_wire_pkey in c: + if track_pkey is not None and site_wire_pkey is not None: + return Connector(tracks=get_track_model(conn, track_pkey)) + # This is not a track, so it must be a site pin. Make sure the # graph_nodes share a type and verify that it is in fact a site pin. node_type = graph2.NodeType(graph_nodes[0][2]) @@ -496,9 +514,30 @@ def find_connector(wire_pkey, node_pkey): return find_connector +def create_const_connectors(conn): + c = conn.cursor() + c.execute( + """ +SELECT vcc_track_pkey, gnd_track_pkey FROM constant_sources; + """ + ) + vcc_track_pkey, gnd_track_pkey = c.fetchone() + + const_connectors = {} + const_connectors[0] = Connector( + tracks=get_track_model(conn, gnd_track_pkey) + ) + const_connectors[1] = Connector( + tracks=get_track_model(conn, vcc_track_pkey) + ) + + return const_connectors + + def make_connection( conn, input_only_nodes, output_only_nodes, find_wire, find_pip, - find_connector, tile_name, loc, tile_type, pip, switch_pkey + find_connector, tile_name, loc, tile_type, pip, switch_pkey, + delayless_switch_pkey, const_connectors ): """ Attempt to connect graph nodes on either side of a pip. @@ -562,7 +601,7 @@ def make_connection( loc, sink_connector ) - return [ + edges = [ ( src_graph_node_pkey, dest_graph_node_pkey, @@ -572,6 +611,23 @@ def make_connection( ) ] + # Make additional connections to constant network if the sink needs it. + for constant_src in yield_ties_to_wire(pip.net_to): + src_graph_node_pkey, dest_graph_node_pkey = const_connectors[ + constant_src].connect_at(loc, sink_connector) + + edges.append( + ( + src_graph_node_pkey, + dest_graph_node_pkey, + delayless_switch_pkey, + tile_pkey, + None, + ) + ) + + return edges + def mark_track_liveness(conn, pool, input_only_nodes, output_only_nodes): """ Checks tracks for liveness. @@ -685,14 +741,14 @@ def build_channels(conn, pool, active_tracks): node_type = graph2.NodeType(graph_node_type) if node_type == graph2.NodeType.CHANX: - assert y_low == y_high + assert y_low == y_high, (pkey, track_pkey) if y_low not in x_tracks: x_tracks[y_low] = [] x_tracks[y_low].append((x_low, x_high, pkey)) elif node_type == graph2.NodeType.CHANY: - assert x_low == x_high + assert x_low == x_high, (pkey, track_pkey) if x_low not in y_tracks: y_tracks[x_low] = [] @@ -884,6 +940,8 @@ def main(): find_wire = create_find_wire(conn) find_connector = create_find_connector(conn) + const_connectors = create_const_connectors(conn) + print('{} Finding nodes belonging to ROI'.format(now())) if use_roi: for loc in progressbar.progressbar(grid.tile_locations()): @@ -893,6 +951,9 @@ def main(): if tile_name in synth_tiles['tiles']: assert len(synth_tiles['tiles'][tile_name]['pins']) == 1 for pin in synth_tiles['tiles'][tile_name]['pins']: + if pin['port_type'] not in ['input', 'output']: + continue + _, _, node_pkey = find_wire( tile_name, gridinfo.tile_type, pin['wire'] ) @@ -910,6 +971,12 @@ def main(): c.execute('SELECT pkey FROM switch WHERE name = ?;', ('routing', )) switch_pkey = c.fetchone()[0] + c.execute( + 'SELECT pkey FROM switch WHERE name = ?;', + ('__vpr_delayless_switch__', ) + ) + delayless_switch_pkey = c.fetchone()[0] + edges = [] edge_set = set() @@ -943,7 +1010,9 @@ def main(): loc=loc, tile_type=gridinfo.tile_type, pip=pip, - switch_pkey=switch_pkey + switch_pkey=switch_pkey, + delayless_switch_pkey=delayless_switch_pkey, + const_connectors=const_connectors ) if connections: # TODO: Skip duplicate connections, until they have unique diff --git a/xc7/utils/prjxray_create_synth_tiles.py b/xc7/utils/prjxray_create_synth_tiles.py index 83361db887..cd5f69d3a8 100644 --- a/xc7/utils/prjxray_create_synth_tiles.py +++ b/xc7/utils/prjxray_create_synth_tiles.py @@ -8,7 +8,7 @@ def main(): parser = argparse.ArgumentParser(description="Generate synth_tiles.json") parser.add_argument('--db_root', required=True) parser.add_argument('--roi', required=True) - parser.add_argument('--synth_tiles', required=False) + parser.add_argument('--synth_tiles', required=True) args = parser.parse_args() @@ -30,6 +30,7 @@ def main(): ) synth_tiles['info'] = j['info'] + vbrk_in_use = set() for port in j['ports']: if port['name'].startswith('dout['): port_type = 'input' @@ -45,6 +46,8 @@ def main(): tile, wire = port['wire'].split('/') + vbrk_in_use.add(tile) + # Make sure connecting wire is not in ROI! loc = g.loc_of_tilename(tile) if roi.tile_in_roi(loc): @@ -70,8 +73,74 @@ def main(): } ) + # Find two VBRK's in the corner of the fabric to use as the synthetic VCC/ + # GND source. + vbrk_loc = None + vbrk_tile = None + vbrk2_loc = None + vbrk2_tile = None + for tile in g.tiles(): + if tile in vbrk_in_use: + continue + + loc = g.loc_of_tilename(tile) + if not roi.tile_in_roi(loc): + continue + + gridinfo = g.gridinfo_at_tilename(tile) + if 'VBRK' not in gridinfo.tile_type: + continue + + assert len(db.get_tile_type(gridinfo.tile_type).get_sites()) == 0, tile + + if vbrk_loc is None: + vbrk2_loc = vbrk_loc + vbrk2_tile = vbrk_tile + vbrk_loc = loc + vbrk_tile = tile + else: + if loc.grid_x < vbrk_loc.grid_x and loc.grid_y < vbrk_loc.grid_y or vbrk2_loc is None: + vbrk2_loc = vbrk_loc + vbrk2_tile = vbrk_tile + vbrk_loc = loc + vbrk_tile = tile + + assert vbrk_loc is not None + assert vbrk_tile is not None + assert vbrk_tile not in synth_tiles['tiles'] + synth_tiles['tiles'][vbrk_tile] = { + 'loc': + vbrk_loc, + 'pins': + [ + { + 'wire': 'VCC', + 'pad': 'VCC', + 'port_type': 'VCC', + 'is_clock': False, + }, + ], + } + + assert vbrk2_loc is not None + assert vbrk2_tile is not None + assert vbrk2_tile not in synth_tiles['tiles'] + synth_tiles['tiles'][vbrk2_tile] = { + 'loc': + vbrk2_loc, + 'pins': + [ + { + 'wire': 'GND', + 'pad': 'GND', + 'port_type': 'GND', + 'is_clock': False, + }, + ], + } + with open(args.synth_tiles, 'w') as f: - json.dump(synth_tiles, f) + json.dump(synth_tiles, f, indent=2) if __name__ == "__main__": diff --git a/xc7/utils/prjxray_form_channels.py b/xc7/utils/prjxray_form_channels.py index f0c02ab6f4..2a8b7beeb8 100644 --- a/xc7/utils/prjxray_form_channels.py +++ b/xc7/utils/prjxray_form_channels.py @@ -37,7 +37,6 @@ from lib.rr_graph import points from lib.rr_graph import tracks from lib.rr_graph import graph2 -import sqlite3 import datetime import os import os.path @@ -50,6 +49,9 @@ def import_site_type(db, c, site_types, site_type_name): assert site_type_name not in site_types site_type = db.get_site_type(site_type_name) + if site_type_name in site_types: + return + c.execute("INSERT INTO site_type(name) VALUES (?)", (site_type_name, )) site_types[site_type_name] = c.lastrowid @@ -622,10 +624,12 @@ def insert_tracks(conn, tracks_to_insert): short_pkey = c.fetchone()[0] track_graph_nodes = {} + track_pkeys = [] for node, tracks_list, track_connections, tracks_model in progressbar.progressbar( tracks_to_insert): c.execute("""INSERT INTO track DEFAULT VALUES""") track_pkey = c.lastrowid + track_pkeys.append(track_pkey) c.execute( """UPDATE node SET track_pkey = ? WHERE pkey = ?""", @@ -732,6 +736,15 @@ def insert_tracks(conn, tracks_to_insert): c.execute("""CREATE INDEX graph_edge_tracks ON graph_edge(track_pkey);""") conn.commit() + return track_pkeys + + +def create_track(node, unique_pos): + xs, ys = points.decompose_points_into_tracks(unique_pos) + tracks_list, track_connections = tracks.make_tracks(xs, ys, unique_pos) + tracks_model = tracks.Tracks(tracks_list, track_connections) + + return [node, tracks_list, track_connections, tracks_model] def form_tracks(conn): @@ -773,17 +786,148 @@ def form_tracks(conn): """, (node, )): unique_pos.add((grid_x, grid_y)) - xs, ys = points.decompose_points_into_tracks(unique_pos) - tracks_list, track_connections = tracks.make_tracks( - xs, ys, unique_pos + tracks_to_insert.append(create_track(node, unique_pos)) + + # Create constant tracks + vcc_track_to_insert, gnd_track_to_insert = create_constant_tracks(conn) + vcc_idx = len(tracks_to_insert) + tracks_to_insert.append(vcc_track_to_insert) + gnd_idx = len(tracks_to_insert) + tracks_to_insert.append(gnd_track_to_insert) + + track_pkeys = insert_tracks(conn, tracks_to_insert) + vcc_track_pkey = track_pkeys[vcc_idx] + gnd_track_pkey = track_pkeys[gnd_idx] + + c.execute( + """ +INSERT INTO constant_sources(vcc_track_pkey, gnd_track_pkey) VALUES (?, ?) + """, ( + vcc_track_pkey, + gnd_track_pkey, + ) + ) + + conn.commit() + + connect_hardpins_to_constant_network(conn, vcc_track_pkey, gnd_track_pkey) + +def connect_hardpins_to_constant_network(conn, vcc_track_pkey, gnd_track_pkey): + """ Connect TIEOFF HARD1 and HARD0 pins. + + Update nodes connected to to HARD1 or HARD0 pins to point to the new + VCC or GND track. This should connect the pips to the constant + network instead of the TIEOFF site. + """ + + c = conn.cursor() + c.execute(""" +SELECT pkey FROM site_type WHERE name = ? +""", ("TIEOFF", )) + results = c.fetchall() + assert len(results) == 1, results + tieoff_site_type_pkey = results[0][0] + + c.execute( + """ +SELECT pkey FROM site_pin WHERE site_type_pkey = ? and name = ? +""", (tieoff_site_type_pkey, "HARD1") + ) + vcc_site_pin_pkey = c.fetchone()[0] + c.execute( + """ +SELECT pkey FROM wire_in_tile WHERE site_pin_pkey = ? +""", (vcc_site_pin_pkey, ) + ) + + c.execute( + """ +SELECT pkey FROM wire_in_tile WHERE site_pin_pkey = ? +""", (vcc_site_pin_pkey, ) + ) + + c2 = conn.cursor() + c2.execute("""BEGIN EXCLUSIVE TRANSACTION;""") + + for (wire_in_tile_pkey, ) in c: + c2.execute( + """ +UPDATE node SET track_pkey = ? WHERE pkey IN ( + SELECT node_pkey FROM wire WHERE wire_in_tile_pkey = ? +) + """, ( + vcc_track_pkey, + wire_in_tile_pkey, ) - tracks_model = tracks.Tracks(tracks_list, track_connections) + ) + + c.execute( + """ +SELECT pkey FROM site_pin WHERE site_type_pkey = ? and name = ? +""", (tieoff_site_type_pkey, "HARD0") + ) + gnd_site_pin_pkey = c.fetchone()[0] + c.execute( + """ +SELECT pkey FROM wire_in_tile WHERE site_pin_pkey = ? +""", (gnd_site_pin_pkey, ) + ) - tracks_to_insert.append( - [node, tracks_list, track_connections, tracks_model] + c.execute( + """ +SELECT pkey FROM wire_in_tile WHERE site_pin_pkey = ? +""", (gnd_site_pin_pkey, ) + ) + for (wire_in_tile_pkey, ) in c: + c2.execute( + """ +UPDATE node SET track_pkey = ? WHERE pkey IN ( + SELECT node_pkey FROM wire WHERE wire_in_tile_pkey = ? +) + """, ( + gnd_track_pkey, + wire_in_tile_pkey, ) + ) + + c2.execute("""COMMIT TRANSACTION""") + + +def create_constant_tracks(conn): + """ Create two tracks that go to all TIEOFF sites to route constants. + + Returns (vcc_track_to_insert, gnd_track_to_insert), suitable for insert + via insert_tracks function. + + """ + + # Make constant track available to all tiles. + c = conn.cursor() + unique_pos = set() + c.execute('SELECT grid_x, grid_y FROM tile') + for grid_x, grid_y in c: + if grid_x == 0 or grid_y == 0: + continue + unique_pos.add((grid_x, grid_y)) + + c.execute( + """ +INSERT INTO node(classification) VALUES (?) +""", (NodeClassification.CHANNEL.value, ) + ) + vcc_node = c.lastrowid + + c.execute( + """ +INSERT INTO node(classification) VALUES (?) +""", (NodeClassification.CHANNEL.value, ) + ) + gnd_node = c.lastrowid + + conn.commit() - insert_tracks(conn, tracks_to_insert) + return create_track(vcc_node, unique_pos), \ + create_track(gnd_node, unique_pos) def main(): diff --git a/xc7/utils/prjxray_routing_import.py b/xc7/utils/prjxray_routing_import.py index 1bf700a3fd..7ece09b31b 100755 --- a/xc7/utils/prjxray_routing_import.py +++ b/xc7/utils/prjxray_routing_import.py @@ -28,11 +28,11 @@ from lib.connection_database import get_wire_pkey, get_track_model import lib.rr_graph_xml.graph2 as xml_graph2 from lib.rr_graph_xml.utils import read_xml_file +from prjxray_constant_site_pins import feature_when_routed import simplejson as json import progressbar import datetime import re -import sqlite3 import functools from prjxray_db_cache import DatabaseCache @@ -70,6 +70,12 @@ def check_feature(feature): return ' '.join((feature, enable_cascout)) + parts = feature.split('.') + + wire_feature = feature_when_routed(parts[1]) + if wire_feature is not None: + return '{} {}.{}'.format(feature, parts[0], wire_feature) + return feature @@ -283,7 +289,7 @@ def create_track_rr_graph( def add_synthetic_edges(conn, graph, node_mapping, grid, synth_tiles): c = conn.cursor() - routing_switch = graph.get_switch_id('routing') + delayless_switch = graph.get_switch_id('__vpr_delayless_switch__') for loc in grid.tile_locations(): tile_name = grid.tilename_at_loc(loc) @@ -291,9 +297,10 @@ def add_synthetic_edges(conn, graph, node_mapping, grid, synth_tiles): if tile_name in synth_tiles['tiles']: assert len(synth_tiles['tiles'][tile_name]['pins']) == 1 for pin in synth_tiles['tiles'][tile_name]['pins']: - wire_pkey = get_wire_pkey(conn, tile_name, pin['wire']) - c.execute( - """ + if pin['port_type'] in ['input', 'output']: + wire_pkey = get_wire_pkey(conn, tile_name, pin['wire']) + c.execute( + """ SELECT track_pkey FROM @@ -307,15 +314,23 @@ def add_synthetic_edges(conn, graph, node_mapping, grid, synth_tiles): WHERE pkey = ? );""", (wire_pkey, ) - ) - (track_pkey, ) = c.fetchone() - assert track_pkey is not None, ( - tile_name, pin['wire'], wire_pkey - ) + ) + (track_pkey, ) = c.fetchone() + assert track_pkey is not None, ( + tile_name, pin['wire'], wire_pkey + ) + elif pin['port_type'] == 'VCC': + c.execute('SELECT vcc_track_pkey FROM constant_sources') + (track_pkey, ) = c.fetchone() + elif pin['port_type'] == 'GND': + c.execute('SELECT gnd_track_pkey FROM constant_sources') + (track_pkey, ) = c.fetchone() + else: + assert False, pin['port_type'] tracks_model, track_nodes = get_track_model(conn, track_pkey) option = list(tracks_model.get_tracks_for_wire_at_coord(loc)) - assert len(option) > 0 + assert len(option) > 0, (pin, len(option)) if pin['port_type'] == 'input': tile_type = 'BLK_SY-OUTPAD' @@ -323,6 +338,12 @@ def add_synthetic_edges(conn, graph, node_mapping, grid, synth_tiles): elif pin['port_type'] == 'output': tile_type = 'BLK_SY-INPAD' wire = 'inpad' + elif pin['port_type'] == 'VCC': + tile_type = 'BLK_SY-VCC' + wire = 'VCC' + elif pin['port_type'] == 'GND': + tile_type = 'BLK_SY-GND' + wire = 'GND' else: assert False, pin @@ -338,14 +359,14 @@ def add_synthetic_edges(conn, graph, node_mapping, grid, synth_tiles): graph.add_edge( src_node=node_mapping[track_node], sink_node=pin_node[0][0], - switch_id=routing_switch, + switch_id=delayless_switch, name='synth_{}_{}'.format(tile_name, pin['wire']), ) - elif pin['port_type'] == 'output': + elif pin['port_type'] in ['VCC', 'GND', 'output']: graph.add_edge( src_node=pin_node[0][0], sink_node=node_mapping[track_node], - switch_id=routing_switch, + switch_id=delayless_switch, name='synth_{}_{}'.format(tile_name, pin['wire']), ) else: @@ -574,9 +595,9 @@ def main(): # https://github.com/verilog-to-routing/vtr-verilog-to-routing/issues/354 # is fixed. try: - short = graph.get_switch_id('short') + graph.get_switch_id('short') except KeyError: - short = xml_graph.add_switch( + xml_graph.add_switch( graph2.Switch( id=None, name='short', diff --git a/xc7/yosys/synth.tcl b/xc7/yosys/synth.tcl index f37743296c..15741e1397 100644 --- a/xc7/yosys/synth.tcl +++ b/xc7/yosys/synth.tcl @@ -15,5 +15,9 @@ opt_clean setundef -zero -params stat -write_blif -attr -cname -param $::env(OUT_EBLIF) +write_blif -attr -cname -param \ + -true VCC VCC \ + -false GND GND \ + -undef VCC VCC \ + $::env(OUT_EBLIF) write_verilog $::env(OUT_SYNTH_V) From a21a6f68c743f15410af846abda67f5b07608b5e Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Thu, 4 Apr 2019 13:20:27 -0700 Subject: [PATCH 2/2] This commit adds proper track support to points.decompose_points_into_tracks Multiple changes are required to add this support: - Temporary bounds on min x_low and y_low are removed. - Added basic test to channel2.Channel and fix bug due to unstable track ordering. - Add track model support to points.decompose_points_into_tracks, in particular adding support for tracks that connect a N-1 if available. - Adds more robust channel verification logic in prjxray_create_edges. - Add "right-only" mode to points.decompose_points_into_tracks. This is useful for arches where all pins are statically determined on the right of the tile, rather than choosen. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- testarch/utils/testarch_graph.py | 19 ++- utils/lib/rr_graph/channel2.py | 38 +++++- utils/lib/rr_graph/points.py | 180 ++++++++++++++++++++++------ utils/lib/rr_graph/tracks.py | 12 +- xc7/utils/prjxray_create_edges.py | 74 ++++++++++-- xc7/utils/prjxray_form_channels.py | 5 +- xc7/utils/prjxray_routing_import.py | 4 +- 7 files changed, 278 insertions(+), 54 deletions(-) diff --git a/testarch/utils/testarch_graph.py b/testarch/utils/testarch_graph.py index 986af2f1ed..7813f04025 100755 --- a/testarch/utils/testarch_graph.py +++ b/testarch/utils/testarch_graph.py @@ -66,7 +66,12 @@ def alt_pos(begin, end, swap): def create_tracks_from_points( name, graph, unique_pos, short, grid_width, grid_height ): - xs, ys = points.decompose_points_into_tracks(unique_pos) + xs, ys = points.decompose_points_into_tracks( + unique_pos, + grid_width, + grid_height, + right_only=True, + ) tracks_list, track_connections = tracks.make_tracks( xs, ys, unique_pos, grid_width, grid_height ) @@ -103,13 +108,15 @@ def create_global_constant_tracks(graph, mux, short, grid_width, grid_height): for x in range(grid_width): for y in range(grid_height): - if x == 0 and y == 0: + if x == 0: + continue + if y == 0: continue - if x == 0 and y == grid_height - 1: + if x == grid_width - 1: continue - if x == grid_width - 1 and y == grid_height - 1: + if y == grid_height - 1: continue - if x == grid_width - 1 and y == 0: + if x == grid_width - 2 and y == grid_height - 2: continue unique_pos.add((x, y)) @@ -161,7 +168,7 @@ def create_global_constant_tracks(graph, mux, short, grid_width, grid_height): ) break - assert made_connection + assert made_connection, (pin, pin_map) assert found_vcc assert found_gnd diff --git a/utils/lib/rr_graph/channel2.py b/utils/lib/rr_graph/channel2.py index 467676f5eb..3971ccbdb2 100644 --- a/utils/lib/rr_graph/channel2.py +++ b/utils/lib/rr_graph/channel2.py @@ -9,13 +9,47 @@ class Channel(object): + """ Packs tracks into ptc tracks + + >>> tracks = [ + ... (1, 3, 0), + ... (1, 1, 1), + ... (4, 5, 2), + ... (4, 4, 3), + ... (0, 10, 4), + ... ] + >>> channel_model = Channel(tracks) + >>> channel_model.pack_tracks() + >>> for ptc, tree in enumerate(channel_model.trees): + ... print('ptc={}'.format(ptc)) + ... for itr in tree: + ... x, y, idx = tracks[itr[2]] + ... assert idx == itr[2] + ... print(' tracks[{}] = ({}, {})'.format(itr[2], x, y)) + ptc=0 + tracks[4] = (0, 10) + ptc=1 + tracks[2] = (4, 5) + tracks[0] = (1, 3) + ptc=2 + tracks[3] = (4, 4) + tracks[1] = (1, 1) + >>> for ptc, min_v, max_v in channel_model.fill_empty(0, 10): + ... print('ptc={} ({}, {})'.format(ptc, min_v, max_v)) + ptc=1 (0, 0) + ptc=1 (6, 10) + ptc=2 (0, 0) + ptc=2 (2, 3) + ptc=2 (5, 10) + """ + def __init__(self, tracks): self.trees = [] self.tracks = sorted(tracks, key=lambda x: x[1] - x[0]) def place_track(self, track): for idx, tree in enumerate(self.trees): - if not tree.overlaps(track[0], track[1]): + if not tree.overlaps(track[0], track[1] + 1): tree.add( Interval(begin=track[0], end=track[1] + 1, data=track[2]) ) @@ -32,7 +66,7 @@ def pack_tracks(self): def fill_empty(self, min_value, max_value): for idx, tree in enumerate(self.trees): - tracks = list(tree.items()) + tracks = sorted(tree.items(), key=lambda x: x[0]) if min_value <= tracks[0].begin - 1: yield (idx, min_value, tracks[0].begin - 1) diff --git a/utils/lib/rr_graph/points.py b/utils/lib/rr_graph/points.py index d0e0064444..a5029989e5 100755 --- a/utils/lib/rr_graph/points.py +++ b/utils/lib/rr_graph/points.py @@ -3,7 +3,6 @@ Functions for dealing with groups of points. """ -import logging import math import string import sys @@ -58,7 +57,7 @@ def NP(x, y, *n): n = list(n) if not n: n = ["{}+{}".format(string.ascii_letters[x], string.ascii_letters[y])] - return NamedPosition(Position(x, y), n) + return NamedPosition(P(x, y), n) class StraightSegment(list): @@ -749,9 +748,9 @@ def straight_closet(line1, line2): class Point(object): - def __init__(self, coord, tracks=2): + def __init__(self, coord): self.x, self.y = coord - self.tracks = tracks + self.tracks = 0 def __repr__(self): return 'Point(coord=({},{}),tracks={})'.format( @@ -763,9 +762,15 @@ class Track(object): def __init__(self, dim, tracks=None, other_tracks=None, points=[]): self.dim = dim - self.points = list(points) + self.points = [] self.tracks = tracks self.other_tracks = other_tracks + for p in points: + self.add_point(p) + + def add_point(self, p): + self.points.append(p) + p.tracks += 1 def __repr__(self): return 'Track(dim={},points={})'.format( @@ -773,19 +778,29 @@ def __repr__(self): ) -def decompose_points_into_tracks(points): +def decompose_points_into_tracks( + points, grid_width=None, grid_height=None, right_only=False +): """ This function takes a bag of points and returns a set of x lines and y lines that cover all points, and all lines touch each other. This is the first step to forming VPR tracks from points. VPR tracks have limited length, whereas this function returns lines of infinite length. + Args: + points: List of (x, y) tuples that are points to be connected into the + the track + grid_width (int): Optional maximum x dimension + grid_height (int): Optional maximum y dimension + right_only (bool): Assume that points are only available via pins on + the right. Some arches restrict pins to the right side only. + >>> # Single element >>> pos = [(1,0)] >>> ret = decompose_points_into_tracks(pos) >>> print_tracks(ret) - x = [1] - y = [] + x = [] + y = [0] >>> # Single horizontal line >>> pos = [(1,0), (2,0)] @@ -803,14 +818,25 @@ def decompose_points_into_tracks(points): >>> # Cross shape >>> pos = [ + ... (2,1), + ... (1,2), (2,2), (3, 2), + ... (2,3), + ... ] + >>> ret = decompose_points_into_tracks(pos) + >>> print_tracks(ret) + x = [2] + y = [2] + + >>> # Cross shape at edge + >>> pos = [ ... (1,0), ... (0,1), (1,1), (2, 1), ... (1,2), ... ] >>> ret = decompose_points_into_tracks(pos) >>> print_tracks(ret) - x = [1] - y = [1] + x = [0] + y = [0] >>> # Cross with two horizontal bars >>> pos = [ @@ -821,8 +847,8 @@ def decompose_points_into_tracks(points): ... ] >>> ret = decompose_points_into_tracks(pos) >>> print_tracks(ret) - x = [1] - y = [1, 2] + x = [0] + y = [0, 1] >>> # Cross with unequal horizontal bars >>> pos = [ @@ -833,8 +859,8 @@ def decompose_points_into_tracks(points): ... ] >>> ret = decompose_points_into_tracks(pos) >>> print_tracks(ret) - x = [1] - y = [1, 2] + x = [0] + y = [0, 1] >>> pos = [ ... (1,0), @@ -843,12 +869,12 @@ def decompose_points_into_tracks(points): ... ] >>> ret = decompose_points_into_tracks(pos) >>> print_tracks(ret) - x = [1] - y = [1] + x = [0] + y = [0] >>> # 3 straight lines >>> pos = [ - ... (0,0), (1,0), + ... (1,0), ... (0,1), (2,1), ... (0,2), (1,2), ... (0,3), (1,3), (2,3), @@ -858,12 +884,12 @@ def decompose_points_into_tracks(points): ... ] >>> ret = decompose_points_into_tracks(pos) >>> print_tracks(ret) - x = [0, 1, 2] - y = [3] + x = [0] + y = [0, 3, 5] >>> # H shaped >>> pos = [ - ... (0,0), (2,0), + ... (2,0), ... (0,1), (2,1), ... (0,2), (2,2), ... (0,3), (1,3), (2,3), @@ -871,8 +897,21 @@ def decompose_points_into_tracks(points): ... ] >>> ret = decompose_points_into_tracks(pos) >>> print_tracks(ret) - x = [0, 2] - y = [3] + x = [0] + y = [0, 2, 3] + + >>> # H shaped + >>> pos = [ + ... (1,1), (3,1), + ... (1,2), (3,2), + ... (1,3), (3,3), + ... (1,4), (2,4), (3,4), + ... (1,5), (3,5), + ... ] + >>> ret = decompose_points_into_tracks(pos) + >>> print_tracks(ret) + x = [1, 2] + y = [4] >>> # Corner shape >>> pos = [ @@ -881,8 +920,8 @@ def decompose_points_into_tracks(points): ... ] >>> ret = decompose_points_into_tracks(pos) >>> print_tracks(ret) - x = [1] - y = [1] + x = [0] + y = [] >>> # Going around corners >>> pos = [ @@ -916,7 +955,7 @@ def decompose_points_into_tracks(points): >>> # Make sure (1,2) isn't in the output... >>> pos = [ - ... (0,0), (2,0), + ... (2,0), ... (0,1), (1,1), (2,1), ... (0,2), (2,2), ... (0,3), (1,3), (2,3), @@ -924,8 +963,8 @@ def decompose_points_into_tracks(points): ... ] >>> ret = decompose_points_into_tracks(pos) >>> print_tracks(ret) - x = [0, 2] - y = [1, 3] + x = [0] + y = [0, 2, 3] >>> pos = [ ... (0,16), @@ -934,7 +973,7 @@ def decompose_points_into_tracks(points): >>> ret = decompose_points_into_tracks(pos) >>> print_tracks(ret) x = [0] - y = [17] + y = [] >>> pos = [ ... (68,48), (69,48), @@ -945,16 +984,22 @@ def decompose_points_into_tracks(points): ... (69,53), (70,53), (71,53), (72,53)] >>> ret = decompose_points_into_tracks(pos) >>> print_tracks(ret) - x = [68, 69] - y = [53] + x = [68] + y = [52] """ + xs, ys = zip(*points) - x_min = min(xs) + x_min = max(0, min(xs) - 1) x_max = max(xs) - y_min = min(ys) + if grid_width is not None: + x_max = min(x_max, grid_width - 2) + + y_min = max(0, min(ys) - 1) y_max = max(ys) + if grid_height is not None: + y_max = min(y_max, grid_height - 2) points = [Point(p) for p in points] x_tracks = {} @@ -965,9 +1010,59 @@ def decompose_points_into_tracks(points): for y in range(y_min, y_max + 1): y_tracks[y] = Track(dim=y, tracks=y_tracks, other_tracks=x_tracks) + def on_x_track(p): + # x tracks extend from 1 to grid_height - 1 + if p.y <= 0: + return False + + if grid_height is not None and p.y >= grid_height - 2: + return False + + return True + + def on_y_track(p): + # y tracks extend from 1 to grid_width - 1 + if p.x <= 0: + return False + + if grid_width is not None and p.x >= grid_width - 2: + return False + + return True + + def is_corner_point(p): + if p.x == 0 and p.y == 0: + return True + + if grid_width is not None: + assert grid_height is not None + if p.x == grid_width - 1 and p.y == 0: + return True + + if p.x == 0 and p.y == grid_height - 1: + return True + + if p.x == grid_width - 1 and p.y == grid_height - 1: + return True + + return False + for p in points: - x_tracks[p.x].points.append(p) - y_tracks[p.y].points.append(p) + # No points in corner + assert not is_corner_point(p), p + + if on_x_track(p) and p.x in x_tracks: + # The x-1 connection is for left pins. + if p.x > 0 and not right_only: + x_tracks[p.x - 1].add_point(p) + x_tracks[p.x].add_point(p) + + # If all pins are on the right, then the y_tracks are used for cross + # bar only, and points are not connected. + if on_y_track(p) and not right_only and p.y in y_tracks: + if p.y > 0: + y_tracks[p.y - 1].add_point(p) + y_tracks[p.y].add_point(p) def try_remove_track(track): assert track.dim in track.tracks @@ -1027,6 +1122,23 @@ def try_remove_track(track): continue break + # Sanity check results + for p in points: + on_a_track = False + if on_x_track(p): + if p.x > 0: + on_a_track = on_a_track or p.x - 1 in x_tracks + + on_a_track = on_a_track or p.x in x_tracks + + if on_y_track(p): + if p.y > 0: + on_a_track = on_a_track or p.y - 1 in y_tracks + + on_a_track = on_a_track or p.y in y_tracks + + assert on_a_track, p + return list(x_tracks.keys()), list(y_tracks.keys()) diff --git a/utils/lib/rr_graph/tracks.py b/utils/lib/rr_graph/tracks.py index cd2bbcc645..3aaa491345 100644 --- a/utils/lib/rr_graph/tracks.py +++ b/utils/lib/rr_graph/tracks.py @@ -71,7 +71,17 @@ def make_tracks(xs, ys, points, grid_width=None, grid_height=None): y_set = set(ys) for x, y in points: - assert x in x_set or y in y_set + if x > 0: + in_x_set = x in x_set or x - 1 in x_set, (x, x_set) + else: + in_x_set = x in x_set + + if y > 0: + in_y_set = y in y_set or y - 1 in y_set, (y, y_set) + else: + in_y_set = y in y_set + + assert in_x_set or in_y_set all_xs, all_ys = zip(*points) x_min = min(all_xs) diff --git a/xc7/utils/prjxray_create_edges.py b/xc7/utils/prjxray_create_edges.py index e444e5fb1c..83bccc123e 100644 --- a/xc7/utils/prjxray_create_edges.py +++ b/xc7/utils/prjxray_create_edges.py @@ -629,7 +629,9 @@ def make_connection( return edges -def mark_track_liveness(conn, pool, input_only_nodes, output_only_nodes): +def mark_track_liveness( + conn, pool, input_only_nodes, output_only_nodes, alive_tracks +): """ Checks tracks for liveness. Iterates over all graph nodes that are routing tracks and determines if @@ -639,7 +641,6 @@ def mark_track_liveness(conn, pool, input_only_nodes, output_only_nodes): conn (sqlite3.Connection): Connection database """ - alive_tracks = set() c = conn.cursor() c2 = conn.cursor() @@ -731,9 +732,6 @@ def build_channels(conn, pool, active_tracks): if track_pkey not in active_tracks: continue - x_low = max(x_low, 1) - y_low = max(y_low, 1) - xs.append(x_low) xs.append(x_high) ys.append(y_low) @@ -813,7 +811,7 @@ def build_channels(conn, pool, active_tracks): num_padding = 0 capacity = 0 for chan, channel_model in x_channel_models.items(): - for ptc, start, end in channel_model.fill_empty(x_min, x_max): + for ptc, start, end in channel_model.fill_empty(max(x_min, 1), x_max): assert ptc < x_list[chan] num_padding += 1 @@ -832,7 +830,7 @@ def build_channels(conn, pool, active_tracks): ) for chan, channel_model in y_channel_models.items(): - for ptc, start, end in channel_model.fill_empty(y_min, y_max): + for ptc, start, end in channel_model.fill_empty(max(y_min, 1), y_max): assert ptc < y_list[chan] num_padding += 1 @@ -874,6 +872,58 @@ def build_channels(conn, pool, active_tracks): c.execute("""COMMIT TRANSACTION;""") +def verify_channels(conn, alive_tracks): + """ Verify PTC numbers in channels. + + There is a very specific requirement from VPR for PTC numbers: + + max(chanx.ptc @ (X, Y) < len(chanx @ (X, Y)) + max(chany.ptc @ (X, Y) < len(chany @ (X, Y)) + + And no duplicate PTC's. + + Violation of these requirements results in a check failure during rr graph + loading. + + """ + + c = conn.cursor() + + chan_ptcs = {} + + for (graph_node_pkey, track_pkey, graph_node_type, x_low, x_high, y_low, + y_high, ptc, capacity) in c.execute( + """ + SELECT pkey, track_pkey, graph_node_type, x_low, x_high, y_low, y_high, ptc, capacity FROM + graph_node WHERE (graph_node_type = ? or graph_node_type = ?);""", + (graph2.NodeType.CHANX.value, graph2.NodeType.CHANY.value)): + + if track_pkey not in alive_tracks and capacity != 0: + assert ptc is None, graph_node_pkey + continue + + assert ptc is not None, graph_node_pkey + + for x in range(x_low, x_high + 1): + for y in range(y_low, y_high + 1): + key = (graph_node_type, x, y) + if key not in chan_ptcs: + chan_ptcs[key] = [] + + chan_ptcs[key].append((graph_node_pkey, ptc)) + + for key in chan_ptcs: + ptcs = {} + for graph_node_pkey, ptc in chan_ptcs[key]: + assert ptc not in ptcs, (ptcs[ptc], graph_node_pkey) + ptcs[ptc] = graph_node_pkey + + assert max(ptcs) < len(ptcs), key + assert len(ptcs) == len(set(ptcs)), key + assert min(ptcs) == 0, key + assert max(ptcs) == len(ptcs) - 1, key + + def main(): parser = argparse.ArgumentParser() parser.add_argument( @@ -1051,7 +1101,11 @@ def main(): c.connection.commit() print('{} Indices created, marking track liveness'.format(now())) - mark_track_liveness(conn, pool, input_only_nodes, output_only_nodes) + + alive_tracks = set() + mark_track_liveness( + conn, pool, input_only_nodes, output_only_nodes, alive_tracks + ) print( '{} Flushing database back to file "{}"'.format( @@ -1059,6 +1113,10 @@ def main(): ) ) + with DatabaseCache(args.connection_database, read_only=True) as conn: + verify_channels(conn, alive_tracks) + print("{}: Channels verified".format(datetime.datetime.now())) + if __name__ == '__main__': main() diff --git a/xc7/utils/prjxray_form_channels.py b/xc7/utils/prjxray_form_channels.py index 2a8b7beeb8..6d73276390 100644 --- a/xc7/utils/prjxray_form_channels.py +++ b/xc7/utils/prjxray_form_channels.py @@ -716,7 +716,9 @@ def insert_tracks(conn, tracks_to_insert): connections = list( tracks_model.get_tracks_for_wire_at_coord((grid_x, grid_y)) ) - assert len(connections) > 0 + assert len(connections) > 0, ( + wire_pkey, track_pkey, grid_x, grid_y + ) graph_node_pkey = track_graph_node_pkey[connections[0][0]] wire_to_graph[wire_pkey] = graph_node_pkey @@ -812,6 +814,7 @@ def form_tracks(conn): connect_hardpins_to_constant_network(conn, vcc_track_pkey, gnd_track_pkey) + def connect_hardpins_to_constant_network(conn, vcc_track_pkey, gnd_track_pkey): """ Connect TIEOFF HARD1 and HARD0 pins. diff --git a/xc7/utils/prjxray_routing_import.py b/xc7/utils/prjxray_routing_import.py index 7ece09b31b..45be62b361 100755 --- a/xc7/utils/prjxray_routing_import.py +++ b/xc7/utils/prjxray_routing_import.py @@ -239,10 +239,10 @@ def import_dummy_tracks(conn, graph, segment_id): if node_type == graph2.NodeType.CHANX: direction = 'X' - x_low = max(x_low, 1) + x_low = x_low elif node_type == graph2.NodeType.CHANY: direction = 'Y' - y_low = max(y_low, 1) + y_low = y_low else: assert False, node_type