gtp: generate attributes and ports files to add to the db

Signed-off-by: Alessandro Comodi <acomodi@antmicro.com>
diff --git a/fuzzers/063-gtp-common-conf/Makefile b/fuzzers/063-gtp-common-conf/Makefile
index 4e93957..c653ccc 100644
--- a/fuzzers/063-gtp-common-conf/Makefile
+++ b/fuzzers/063-gtp-common-conf/Makefile
@@ -36,7 +36,17 @@
 
 .PHONY: all run clean
 
-database: ${BUILD_DIR}/segbits_gtp_common.db
+# These are pins that are hard to parse as a regexp given that the port name ends with a number, which is misinterpreted
+# as the index in the port bus
+SPECIAL_PINS = PLLRSVD1,PLLRSVD2,GTREFCLK0,GTREFCLK1,GTGREFCLK0,GTGREFCLK1,GTEASTREFCLK0,GTEASTREFCLK1,GTWESTREFCLK0,GTWESTREFCLK1,REFCLKOUTMONITOR0,REFCLKOUTMONITOR1
+
+$(BUILD_DIR)/gtpe2_common_ports.csv: generate_ports.tcl
+	env FILE_NAME=$(BUILD_DIR)/gtpe2_common_pins.csv ${XRAY_VIVADO} -mode batch -source generate_ports.tcl
+
+$(BUILD_DIR)/gtpe2_common_ports.json: $(BUILD_DIR)/gtpe2_common_ports.csv
+	python3 ${XRAY_UTILS_DIR}/make_ports.py $(BUILD_DIR)/gtpe2_common_pins.csv $(BUILD_DIR)/gtpe2_common_ports.json --special-pins $(SPECIAL_PINS)
+
+database: ${BUILD_DIR}/segbits_gtp_common.db $(BUILD_DIR)/gtpe2_common_ports.json
 
 ${BUILD_DIR}/segbits_gtp_common.rdb: $(SPECIMENS_OK)
 	${XRAY_SEGMATCH} -o ${BUILD_DIR}/segbits_gtp_common.rdb $$(find $(SPECIMENS) -name "segdata_gtp_common*")
@@ -48,6 +58,8 @@
 	${XRAY_MASKMERGE} ${BUILD_DIR}/mask_gtp_common.db $$(find $(SPECIMENS) -name "segdata_gtp_common*")
 
 pushdb:
+	cp attrs.json ${XRAY_FAMILY_DIR}/gtpe2_common_attrs.json
+	cp $(BUILD_DIR)/gtpe2_common_ports.json ${XRAY_FAMILY_DIR}/gtpe2_common_ports.json
 	BUILD_DIR=$(BUILD_DIR) source pushdb.sh
 
 .PHONY: database pushdb
diff --git a/fuzzers/063-gtp-common-conf/generate_ports.tcl b/fuzzers/063-gtp-common-conf/generate_ports.tcl
new file mode 100644
index 0000000..75f7c7f
--- /dev/null
+++ b/fuzzers/063-gtp-common-conf/generate_ports.tcl
@@ -0,0 +1,36 @@
+# Copyright (C) 2017-2020  The Project X-Ray Authors
+#
+# Use of this source code is governed by a ISC-style
+# license that can be found in the LICENSE file or at
+# https://opensource.org/licenses/ISC
+#
+# SPDX-License-Identifier: ISC
+
+proc dump_pins {file_name site_prefix} {
+    set fp [open $file_name w]
+
+    puts $fp "name,is_input,is_output"
+    set site [lindex [get_sites $site_prefix*] 0]
+
+    set pins [get_site_pins -of_objects $site]
+    foreach pin $pins {
+        set connected_pip [get_pips -of_objects [get_nodes -of_objects $pin]]
+
+        if { $connected_pip == "" } {
+            continue
+        }
+
+        set pin_name [lindex [split $pin "/"] 1]
+        set is_input [get_property IS_INPUT $pin]
+        set is_output [get_property IS_OUTPUT $pin]
+
+        puts $fp "$pin_name,$is_input,$is_output"
+    }
+    close $fp
+}
+
+create_project -force -name design -part $::env(XRAY_PART)
+set_property design_mode PinPlanning [current_fileset]
+open_io_design -name io_1
+
+dump_pins $::env(FILE_NAME) GTPE2_COMMON
diff --git a/fuzzers/064-gtp-channel-conf/Makefile b/fuzzers/064-gtp-channel-conf/Makefile
index ebf729c..1381816 100644
--- a/fuzzers/064-gtp-channel-conf/Makefile
+++ b/fuzzers/064-gtp-channel-conf/Makefile
@@ -34,7 +34,17 @@
 
 .PHONY: all run clean
 
-database: ${BUILD_DIR}/segbits_gtp_channelx.db
+# These are pins that are hard to parse as a regexp given that the port name ends with a number, which is misinterpreted
+# as the index in the port bus
+SPECIAL_PINS = CLKRSVD0,CLKRSVD1,GTREFCLK0,GTREFCLK1,GTNORTHREFCLK0,GTNORTHREFCLK1,GTSOUTHREFCLK0,GTSOUTHREFCLK1,RXUSRCLK,RXUSRCLK2,TXUSRCLK,TXUSRCLK2,RXOSINTID0,PMARSVDIN0,PMARSVDIN1,PMARSVDIN2,PMARSVDIN3,PMARSVDIN4,PMARSVDOUT0,PMARSVDOUT1
+
+$(BUILD_DIR)/gtpe2_channel_ports.csv:
+	env FILE_NAME=$(BUILD_DIR)/gtpe2_channel_pins.csv ${XRAY_VIVADO} -mode batch -source generate_ports.tcl
+
+$(BUILD_DIR)/gtpe2_channel_ports.json: $(BUILD_DIR)/gtpe2_channel_ports.csv
+	python3 ${XRAY_UTILS_DIR}/make_ports.py $(BUILD_DIR)/gtpe2_channel_pins.csv $(BUILD_DIR)/gtpe2_channel_ports.json --special-pins $(SPECIAL_PINS)
+
+database: ${BUILD_DIR}/segbits_gtp_channelx.db $(BUILD_DIR)/gtpe2_channel_ports.json
 
 ${BUILD_DIR}/segbits_gtp_channelx.rdb: $(SPECIMENS_OK)
 	${XRAY_SEGMATCH} -c 9 -o ${BUILD_DIR}/segbits_gtp_channelx.rdb $$(find $(SPECIMENS) -name "segdata_gtp_channel_[0123]*")
@@ -46,6 +56,8 @@
 	${XRAY_MASKMERGE} ${BUILD_DIR}/mask_gtp_channelx.db $$(find $(SPECIMENS) -name "segdata_gtp_channel_[0123]*")
 
 pushdb:
+	cp attrs.json ${XRAY_FAMILY_DIR}/gtpe2_channel_attrs.json
+	cp $(BUILD_DIR)/gtpe2_channel_ports.json ${XRAY_FAMILY_DIR}/gtpe2_channel_ports.json
 	BUILD_DIR=$(BUILD_DIR) source pushdb.sh
 
 .PHONY: database pushdb
diff --git a/fuzzers/064-gtp-channel-conf/generate_ports.tcl b/fuzzers/064-gtp-channel-conf/generate_ports.tcl
new file mode 100644
index 0000000..f847924
--- /dev/null
+++ b/fuzzers/064-gtp-channel-conf/generate_ports.tcl
@@ -0,0 +1,36 @@
+# Copyright (C) 2017-2020  The Project X-Ray Authors
+#
+# Use of this source code is governed by a ISC-style
+# license that can be found in the LICENSE file or at
+# https://opensource.org/licenses/ISC
+#
+# SPDX-License-Identifier: ISC
+
+proc dump_pins {file_name site_prefix} {
+    set fp [open $file_name w]
+
+    puts $fp "name,is_input,is_output"
+    set site [lindex [get_sites $site_prefix*] 0]
+
+    set pins [get_site_pins -of_objects $site]
+    foreach pin $pins {
+        set connected_pip [get_pips -of_objects [get_nodes -of_objects $pin]]
+
+        if { $connected_pip == "" } {
+            continue
+        }
+
+        set pin_name [lindex [split $pin "/"] 1]
+        set is_input [get_property IS_INPUT $pin]
+        set is_output [get_property IS_OUTPUT $pin]
+
+        puts $fp "$pin_name,$is_input,$is_output"
+    }
+    close $fp
+}
+
+create_project -force -name design -part $::env(XRAY_PART)
+set_property design_mode PinPlanning [current_fileset]
+open_io_design -name io_1
+
+dump_pins $::env(FILE_NAME) GTPE2_CHANNEL
diff --git a/utils/make_ports.py b/utils/make_ports.py
new file mode 100644
index 0000000..e31600c
--- /dev/null
+++ b/utils/make_ports.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017-2020  The Project X-Ray Authors.
+#
+# Use of this source code is governed by a ISC-style
+# license that can be found in the LICENSE file or at
+# https://opensource.org/licenses/ISC
+#
+# SPDX-License-Identifier: ISC
+"""
+This script loads the pin dump of the desired BEL from Vivado and groups pins into ports.
+
+Ports that are not connected to the PL are not included. These ports are usually test and
+debug related ports, which are not useful from a P&R perspective.
+
+Ports are then written to a JSON file.
+"""
+import argparse
+import csv
+import json
+import re
+
+from collections import defaultdict
+
+
+def main():
+
+    BUS_REGEX = re.compile("(.*[A-Z_])([0-9]+)$")
+
+    # Parse arguments
+    parser = argparse.ArgumentParser(
+        description=__doc__,
+        formatter_class=argparse.RawDescriptionHelpFormatter)
+
+    parser.add_argument("csv", type=str, help="BEL pin dump file")
+    parser.add_argument(
+        "json",
+        type=str,
+        help="Output JSON file with BEL pins grouped into ports")
+
+    parser.add_argument(
+        "--special-pins",
+        default="",
+        type=str,
+        required=False,
+        help="Some pins cannot be decoded with the regex")
+
+    args = parser.parse_args()
+
+    # Load pin dump
+    with open(args.csv, "r") as fp:
+        pin_dump = list(csv.DictReader(fp))
+
+    # Group pins into ports
+    ports = defaultdict(lambda: {"direction": None, "width": 0})
+
+    special_pins = args.special_pins.split(",")
+
+    for pin in list(pin_dump):
+        pin_name = pin["name"]
+
+        name = None
+        if pin_name in special_pins:
+            # Full match
+            name = pin_name
+        else:
+            # Partial match
+            for special_pin in special_pins:
+                if pin_name.startswith(special_pin):
+                    name = special_pin
+                    break
+
+        if name is None:
+            match = BUS_REGEX.match(pin_name)
+            if match:
+                name = match.group(1)
+            else:
+                name = pin_name
+
+        # Get direction
+        is_input = int(pin["is_input"])
+        is_output = int(pin["is_output"])
+
+        if is_input:
+            direction = "input"
+        elif is_output:
+            direction = "output"
+        else:
+            assert False, pin
+
+        # Add to port
+        port = ports[name]
+
+        if port["direction"] is None:
+            port["direction"] = direction
+        else:
+            assert port["direction"] == direction, (port, direction, name)
+
+        port["width"] += 1
+
+    # Write pin ports to a JSON file
+    with open(args.json, "w") as fp:
+        json.dump(ports, fp, indent=1, sort_keys=True)
+
+
+if __name__ == "__main__":
+    main()