Merge pull request #1614 from antmicro/add-gtp-ports-attrs-file

gtp: generate attributes and ports files to add to the db
diff --git a/docs/dev_database/common/cell_data.rst b/docs/dev_database/common/cell_data.rst
new file mode 100644
index 0000000..07dbf8d
--- /dev/null
+++ b/docs/dev_database/common/cell_data.rst
@@ -0,0 +1,82 @@
+===============
+cell data files
+===============
+
+The *cell data* files are meant for specific primitives which have a common attribute format. The data contained in these files is generated/copied from the corresponding primitives' fuzzers.
+
+Naming convention
+-----------------
+
+The naming scheme for the cell data files is the following::
+
+    <primitive_name>_<data_file_type>.json
+
+Example files:
+
+    - ``gtpe2_common_attrs.json``
+    - ``gtpe2_channel_ports.json``
+
+File format
+-----------
+
+There are two main data file types:
+
+    - *Ports*
+    - *Attributes*
+
+Attributes files
+~~~~~~~~~~~~~~~~
+
+This is a JSON file containing a dictionary of parameters, each one with, at most, four attributes:
+
+    - Type: one of BIN, INT, STR, BOOL.
+    - Values: all possible values that this parameter can assume. In case of `BIN` types, the values list contains only the maximum value reachable.
+    - Digits: number of digits (or bits) required to enable a parameter.
+    - Encoding: This is present only for `INT` types of parameters. These reflect the actual encoding of the parameter value in the bit array.
+
+As an example of parameter please, refer to the following::
+
+    {
+        "PLL0_REFCLK_DIV": {
+            "type": "INT",
+            "values": [1, 2],
+            "encoding": [16, 0],
+            "digits": 5
+        },
+        "RXLPMRESET_TIME": {
+            "type": "BIN",
+            "values": [127],
+            "digits": 7
+        },
+        "RX_XCLK_SEL": {
+            "type": "STR",
+            "values": ["RXREC", "RXUSR"],
+            "digits": 1
+        },
+        "TX_LOOPBACK_DRIVE_HIZ": {
+            "type": "BOOL",
+            "values": ["FALSE", "TRUE"],
+            "digits": 1
+        },
+    }
+
+Ports files
+~~~~~~~~~~~
+
+This is a JSON file containing a dictionary of ports, each one with two attributes:
+
+    - Direction: Corresponds to the port directiona and can have the ``input`` or ``output`` values.
+    - Width: Indicates the width of the port bus.
+
+As an example of parameter please, refer to the following::
+
+    {
+        "CFGRESET": {
+            "direction": "input",
+            "width": 1
+        },
+        "CLKRSVD0": {
+            "direction": "input",
+            "width": 1
+        }
+    }
diff --git a/docs/dev_database/common/index.rst b/docs/dev_database/common/index.rst
index 939999e..0817f9d 100644
--- a/docs/dev_database/common/index.rst
+++ b/docs/dev_database/common/index.rst
@@ -13,3 +13,4 @@
    tile_type
    ppips
    mask
+   cell_data
diff --git a/fuzzers/063-gtp-common-conf/Makefile b/fuzzers/063-gtp-common-conf/Makefile
index 4e93957..f865c85 100644
--- a/fuzzers/063-gtp-common-conf/Makefile
+++ b/fuzzers/063-gtp-common-conf/Makefile
@@ -16,6 +16,7 @@
 SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
 FUZDIR ?= ${PWD}
 
+CELLS_DATA_DIR = ${XRAY_FAMILY_DIR}/cells_data
 
 all: database
 
@@ -36,7 +37,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 +59,9 @@
 	${XRAY_MASKMERGE} ${BUILD_DIR}/mask_gtp_common.db $$(find $(SPECIMENS) -name "segdata_gtp_common*")
 
 pushdb:
+	mkdir -p $(CELLS_DATA_DIR)
+	cp attrs.json $(CELLS_DATA_DIR)/gtpe2_common_attrs.json
+	cp $(BUILD_DIR)/gtpe2_common_ports.json $(CELLS_DATA_DIR)/gtpe2_common_ports.json
 	BUILD_DIR=$(BUILD_DIR) source pushdb.sh
 
 .PHONY: database pushdb
diff --git a/fuzzers/063-gtp-common-conf/generate.py b/fuzzers/063-gtp-common-conf/generate.py
index 073d696..06cc007 100644
--- a/fuzzers/063-gtp-common-conf/generate.py
+++ b/fuzzers/063-gtp-common-conf/generate.py
@@ -62,6 +62,8 @@
         tile_type = params_dict["tile_type"]
         params_list = params_dict["params"]
 
+    sites_in_tile = dict()
+
     for params in params_list:
         site = params["site"]
         tile = params["tile"]
@@ -69,6 +71,8 @@
         if "GTPE2_COMMON" not in site:
             continue
 
+        sites_in_tile[tile] = site
+
         in_use = params["IN_USE"]
 
         segmk.add_site_tag(site, "IN_USE", in_use)
@@ -109,7 +113,7 @@
                           "BOTH_GTREFCLK_USED"]:
                 segmk.add_site_tag(site, param, params[param])
 
-            segmk.add_tile_tag(tile, "ENABLE_DRP", params["ENABLE_DRP"])
+            segmk.add_site_tag(site, "ENABLE_DRP", params["ENABLE_DRP"])
 
     for params in params_list:
         site = params["site"]
@@ -132,9 +136,11 @@
                     value=params["CLKSWING_CFG"], digits=2)[::-1]
             ]
 
+            gtp_common_site = sites_in_tile[tile]
             for i in range(2):
-                segmk.add_tile_tag(
-                    tile, "IBUFDS_GTE2.CLKSWING_CFG[%u]" % (i), bitstr[i])
+                segmk.add_site_tag(
+                    gtp_common_site, "IBUFDS_GTE2.CLKSWING_CFG[%u]" % (i),
+                    bitstr[i])
 
     if tile_type.startswith("GTP_COMMON_MID"):
         bitfilter = bitfilter_gtp_common_mid
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..ecc6eb9 100644
--- a/fuzzers/064-gtp-channel-conf/Makefile
+++ b/fuzzers/064-gtp-channel-conf/Makefile
@@ -16,6 +16,7 @@
 SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
 FUZDIR ?= ${PWD}
 
+CELLS_DATA_DIR = ${XRAY_FAMILY_DIR}/cells_data
 
 all: database
 
@@ -34,7 +35,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 +57,9 @@
 	${XRAY_MASKMERGE} ${BUILD_DIR}/mask_gtp_channelx.db $$(find $(SPECIMENS) -name "segdata_gtp_channel_[0123]*")
 
 pushdb:
+	mkdir -p $(CELLS_DATA_DIR)
+	cp attrs.json $(CELLS_DATA_DIR)/gtpe2_channel_attrs.json
+	cp $(BUILD_DIR)/gtpe2_channel_ports.json $(CELLS_DATA_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()