Merge pull request #2046 from kintex-chatter/k7-iob18

add support for the kintex/virtex high performance banks
diff --git a/.github/workflows/Pipeline.yml b/.github/workflows/Pipeline.yml
index 96aef1a..3653ed9 100644
--- a/.github/workflows/Pipeline.yml
+++ b/.github/workflows/Pipeline.yml
@@ -13,6 +13,7 @@
     container: ubuntu:focal
 
     runs-on: [self-hosted, Linux, X64]
+    timeout-minutes: 600
 
     strategy:
       fail-fast: false
diff --git a/fuzzers/000-init-db/Makefile b/fuzzers/000-init-db/Makefile
index 033ed98..4b1e09a 100644
--- a/fuzzers/000-init-db/Makefile
+++ b/fuzzers/000-init-db/Makefile
@@ -50,6 +50,7 @@
     segbits_cmt_top_r_lower_b \
     segbits_rioi3 \
     segbits_riob33 \
+    segbits_riob18 \
     segbits_hclk_cmt \
     segbits_hclk_ioi3 \
 
diff --git a/fuzzers/001-part-yaml/generate.tcl b/fuzzers/001-part-yaml/generate.tcl
index 5cce0a9..a140980 100644
--- a/fuzzers/001-part-yaml/generate.tcl
+++ b/fuzzers/001-part-yaml/generate.tcl
@@ -11,7 +11,7 @@
         set sample_site [lindex [get_sites -of $iobank] 0]
         if {[llength $sample_site] == 0} continue
         set clock_region [get_property CLOCK_REGION $sample_site]
-        foreach tile [get_tiles -filter {TYPE=~HCLK_IOI3}] {
+        foreach tile [concat [get_tiles -filter {TYPE=~HCLK_IOI3}] [get_tiles -filter {TYPE=~HCLK_IOI}]] {
             set tile_sites [get_sites -of_object $tile]
             if {[llength $tile_sites] == 0} continue
             set hclk_tile_clock_region [get_property CLOCK_REGION [lindex [get_sites -of_object $tile] 0]]
diff --git a/fuzzers/005-tilegrid/Makefile b/fuzzers/005-tilegrid/Makefile
index f0ad42b..5771695 100644
--- a/fuzzers/005-tilegrid/Makefile
+++ b/fuzzers/005-tilegrid/Makefile
@@ -49,6 +49,13 @@
 
 # Kintex7 only fuzzers
 ifeq (${XRAY_DATABASE}, kintex7)
+# xc7k420t/xc7k480t have no high performance banks
+ifneq (${XRAY_FABRIC}, $(filter ${XRAY_FABRIC}, xc7k480t))
+TILEGRID_TDB_DEPENDENCIES += iob18/$(BUILD_FOLDER)/segbits_tilegrid.tdb
+TILEGRID_TDB_DEPENDENCIES += iob18_int/$(BUILD_FOLDER)/segbits_tilegrid.tdb
+TILEGRID_TDB_DEPENDENCIES += ioi18/$(BUILD_FOLDER)/segbits_tilegrid.tdb
+endif
+
 # These kintex parts give an empty design
 ifneq (${XRAY_FABRIC}, $(filter ${XRAY_FABRIC}, xc7k160t xc7k325t xc7k480t))
 TILEGRID_TDB_DEPENDENCIES += orphan_int_column/$(BUILD_FOLDER)/segbits_tilegrid.tdb
@@ -85,12 +92,21 @@
 iob/$(BUILD_FOLDER)/segbits_tilegrid.tdb: ${BASICDB_TILEGRID}
 	cd iob && $(MAKE)
 
+iob18/$(BUILD_FOLDER)/segbits_tilegrid.tdb: ${BASICDB_TILEGRID}
+	cd iob18 && $(MAKE)
+
 iob_int/$(BUILD_FOLDER)/segbits_tilegrid.tdb: ${BASICDB_TILEGRID}
 	cd iob_int && $(MAKE)
 
+iob18_int/$(BUILD_FOLDER)/segbits_tilegrid.tdb: ${BASICDB_TILEGRID}
+	cd iob18_int && $(MAKE)
+
 ioi/$(BUILD_FOLDER)/segbits_tilegrid.tdb: ${BASICDB_TILEGRID}
 	cd ioi && $(MAKE)
 
+ioi18/$(BUILD_FOLDER)/segbits_tilegrid.tdb: ${BASICDB_TILEGRID}
+	cd ioi18 && $(MAKE)
+
 mmcm/$(BUILD_FOLDER)/segbits_tilegrid.tdb: ${BASICDB_TILEGRID}
 	cd mmcm && $(MAKE)
 
@@ -177,8 +193,11 @@
 	cd clb_int && $(MAKE) clean
 	cd cfg && $(MAKE) clean
 	cd iob && $(MAKE) clean
+	cd iob18 && $(MAKE) clean
 	cd iob_int && $(MAKE) clean
+	cd iob18_int && $(MAKE) clean
 	cd ioi && $(MAKE) clean
+	cd ioi18 && $(MAKE) clean
 	cd mmcm && $(MAKE) clean
 	cd pll && $(MAKE) clean
 	cd ps7_int && $(MAKE) clean
@@ -208,8 +227,11 @@
 	cd clb_int && $(MAKE) clean_part
 	cd cfg && $(MAKE) clean_part
 	cd iob && $(MAKE) clean_part
+	cd iob18 && $(MAKE) clean_part
 	cd iob_int && $(MAKE) clean_part
+	cd iob18_int && $(MAKE) clean_part
 	cd ioi && $(MAKE) clean_part
+	cd ioi18 && $(MAKE) clean_part
 	cd mmcm && $(MAKE) clean_part
 	cd pll && $(MAKE) clean_part
 	cd ps7_int && $(MAKE) clean_part
diff --git a/fuzzers/005-tilegrid/add_tdb.py b/fuzzers/005-tilegrid/add_tdb.py
index 021f14f..e483f77 100644
--- a/fuzzers/005-tilegrid/add_tdb.py
+++ b/fuzzers/005-tilegrid/add_tdb.py
@@ -94,7 +94,9 @@
     int_frames, int_words = localutil.get_int_params()
     tdb_fns = [
         ("iob", 42, 4),
+        ("iob18", 42, 4),
         ("ioi", 42, 4),
+        ("ioi18", 42, 4),
         ("mmcm", 30, 49),
         ("pll", 30, 27),
         ("monitor", 30, 101),
@@ -112,6 +114,7 @@
         ("gtp_channel", 32, 22),
         ("clb_int", int_frames, int_words),
         ("iob_int", int_frames, int_words),
+        ("iob18_int", int_frames, int_words),
         ("bram_int", int_frames, int_words),
         ("dsp_int", int_frames, int_words),
         ("fifo_int", int_frames, int_words),
diff --git a/fuzzers/005-tilegrid/generate_full.py b/fuzzers/005-tilegrid/generate_full.py
index 4022edc..454f648 100644
--- a/fuzzers/005-tilegrid/generate_full.py
+++ b/fuzzers/005-tilegrid/generate_full.py
@@ -369,7 +369,7 @@
         if tile in seen_iobs:
             continue
 
-        if database[tile]["type"] not in ["LIOB33", "RIOB33"]:
+        if database[tile]["type"] not in ["LIOB33", "RIOB33", "RIOB18"]:
             continue
 
         while True:
@@ -439,7 +439,7 @@
         if tile in seen_iois:
             continue
 
-        if database[tile]["type"] not in ["LIOI3", "RIOI3"]:
+        if database[tile]["type"] not in ["LIOI3", "RIOI3", "RIOI"]:
             continue
 
         while True:
diff --git a/fuzzers/005-tilegrid/iob18/Makefile b/fuzzers/005-tilegrid/iob18/Makefile
new file mode 100644
index 0000000..9251dba
--- /dev/null
+++ b/fuzzers/005-tilegrid/iob18/Makefile
@@ -0,0 +1,10 @@
+# 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
+N ?= 30
+GENERATE_ARGS?="--oneval 1 --design params.csv --dframe 26 --dword 0"
+include ../fuzzaddr/common.mk
diff --git a/fuzzers/005-tilegrid/iob18/generate.tcl b/fuzzers/005-tilegrid/iob18/generate.tcl
new file mode 100644
index 0000000..d4f790b
--- /dev/null
+++ b/fuzzers/005-tilegrid/iob18/generate.tcl
@@ -0,0 +1,86 @@
+# 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
+source "$::env(XRAY_DIR)/utils/utils.tcl"
+
+proc make_io_pin_sites {} {
+    # get all possible IOB pins
+    foreach pad [get_package_pins -filter "IS_GENERAL_PURPOSE == 1"] {
+        set site [get_sites -of_objects $pad]
+        if {[llength $site] == 0} {
+            continue
+        }
+        if [string match IOB18* [get_property SITE_TYPE $site]] {
+            dict append io_pin_sites $site $pad
+        }
+    }
+    return $io_pin_sites
+}
+
+proc load_pin_lines {} {
+    # IOB_X0Y103 clk input
+    # IOB_X0Y129 do[0] output
+
+    set fp [open "params.csv" r]
+    gets $fp line
+
+    set pin_lines {}
+    for {gets $fp line} {$line != ""} {gets $fp line} {
+        lappend pin_lines [split $line ","]
+    }
+    close $fp
+    return $pin_lines
+}
+
+proc loc_pins {} {
+    set pin_lines [load_pin_lines]
+    set io_pin_sites [make_io_pin_sites]
+
+    puts "Looping"
+    for {set idx 0} {$idx < [llength $pin_lines]} {incr idx} {
+        set line [lindex $pin_lines $idx]
+        puts "$line"
+
+        set site_str [lindex $line 2]
+        set pin_str [lindex $line 3]
+
+        # Have: site
+        # Want: pin for site
+
+        set site [get_sites $site_str]
+        set pad_bel [get_bels -of_objects $site -filter {TYPE =~ PAD && NAME =~ IOB_*}]
+        # set port [get_ports -of_objects $site]
+        set port [get_ports $pin_str]
+        set tile [get_tiles -of_objects $site]
+
+        set pin [dict get $io_pin_sites $site]
+        set_property -dict "PACKAGE_PIN $pin IOSTANDARD LVCMOS18" $port
+    }
+}
+
+proc run {} {
+    create_project -force -part $::env(XRAY_PART) design design
+    read_verilog top.v
+    synth_design -top top
+
+    loc_pins
+
+    set_property CFGBVS GND [current_design]
+    set_property CONFIG_VOLTAGE 1.8 [current_design]
+    set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-79}]
+    set_property IS_ENABLED 0 [get_drc_checks {NSTD-1}]
+    set_property IS_ENABLED 0 [get_drc_checks {UCIO-1}]
+
+    place_design
+    route_design
+
+    write_checkpoint -force design.dcp
+    write_bitstream -force design.bit
+}
+
+run
diff --git a/fuzzers/005-tilegrid/iob18/top.py b/fuzzers/005-tilegrid/iob18/top.py
new file mode 100644
index 0000000..1e568cd
--- /dev/null
+++ b/fuzzers/005-tilegrid/iob18/top.py
@@ -0,0 +1,84 @@
+#!/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
+import os
+import random
+random.seed(int(os.getenv("SEED"), 16))
+from prjxray import util
+from prjxray.db import Database
+
+
+def gen_sites():
+    '''
+    IOB18S: main IOB of a diff pair
+    IOB18M: secondary IOB of a diff pair
+    IOB18: not a diff pair. Relatively rare (at least in ROI...2 of them?)
+    Focus on IOB18S to start
+    '''
+    db = Database(util.get_db_root(), util.get_part())
+    grid = db.grid()
+    for tile_name in sorted(grid.tiles()):
+        loc = grid.loc_of_tilename(tile_name)
+        gridinfo = grid.gridinfo_at_loc(loc)
+
+        for site_name, site_type in gridinfo.sites.items():
+            if site_type == 'IOB18S':
+                yield tile_name, site_name
+
+
+def write_params(params):
+    pinstr = 'tile,val,site,pin\n'
+    for tile, (site, val, pin) in sorted(params.items()):
+        pinstr += '%s,%s,%s,%s\n' % (tile, val, site, pin)
+    open('params.csv', 'w').write(pinstr)
+
+
+def run():
+    sites = list(gen_sites())
+    print(
+        '''
+`define N_DI {}
+
+module top(input wire [`N_DI-1:0] di);
+    wire [`N_DI-1:0] di_buf;
+    '''.format(len(sites)))
+
+    params = {}
+    print('''
+        (* KEEP, DONT_TOUCH *)
+        LUT6 dummy_lut();''')
+
+    for idx, ((tile_name, site_name), isone) in enumerate(zip(
+            sites, util.gen_fuzz_states(len(sites)))):
+        params[tile_name] = (site_name, isone, "di[%u]" % idx)
+        print(
+            '''
+    (* KEEP, DONT_TOUCH *)
+    IBUF #(
+    ) ibuf_{site_name} (
+        .I(di[{idx}]),
+        .O(di_buf[{idx}])
+        );'''.format(site_name=site_name, idx=idx))
+
+        if isone:
+            print(
+                '''
+    (* KEEP, DONT_TOUCH *)
+    PULLUP #(
+    ) pullup_{site_name} (
+        .O(di[{idx}])
+        );'''.format(site_name=site_name, idx=idx))
+
+    print("endmodule")
+    write_params(params)
+
+
+if __name__ == '__main__':
+    run()
diff --git a/fuzzers/005-tilegrid/iob18_int/Makefile b/fuzzers/005-tilegrid/iob18_int/Makefile
new file mode 100644
index 0000000..9f2ad50
--- /dev/null
+++ b/fuzzers/005-tilegrid/iob18_int/Makefile
@@ -0,0 +1,10 @@
+# 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
+N ?= 16
+GENERATE_ARGS?="--oneval 0 --design params.csv --dframe 14 --dword 1"
+include ../fuzzaddr/common.mk
diff --git a/fuzzers/005-tilegrid/iob18_int/generate.tcl b/fuzzers/005-tilegrid/iob18_int/generate.tcl
new file mode 100644
index 0000000..3c705da
--- /dev/null
+++ b/fuzzers/005-tilegrid/iob18_int/generate.tcl
@@ -0,0 +1,82 @@
+# 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
+source "$::env(XRAY_DIR)/utils/utils.tcl"
+
+proc make_io_pin_sites {} {
+    # get all possible IOB pins
+    foreach pad [get_package_pins -filter "IS_GENERAL_PURPOSE == 1"] {
+        set site [get_sites -of_objects $pad]
+        if {[llength $site] == 0} {
+            continue
+        }
+        if [string match IOB18* [get_property SITE_TYPE $site]] {
+            dict append io_pin_sites $site $pad
+        }
+    }
+    return $io_pin_sites
+}
+
+proc load_pin_lines {} {
+    # IOB_X0Y103 clk input
+    # IOB_X0Y129 do[0] output
+
+    set fp [open "params.csv" r]
+    set pin_lines {}
+    for {gets $fp line} {$line != ""} {gets $fp line} {
+        lappend pin_lines [split $line ","]
+    }
+    close $fp
+    return $pin_lines
+}
+
+proc loc_pins {} {
+    set pin_lines [load_pin_lines]
+    set io_pin_sites [make_io_pin_sites]
+
+    puts "Looping"
+    for {set idx 0} {$idx < [llength $pin_lines]} {incr idx} {
+        set line [lindex $pin_lines $idx]
+        puts "$line"
+
+        set site_str [lindex $line 3]
+        set pin_str [lindex $line 4]
+
+        # Have: site
+        # Want: pin for site
+
+        set site [get_sites $site_str]
+        set pad_bel [get_bels -of_objects $site -filter {TYPE =~ PAD && NAME =~ IOB_*}]
+        # set port [get_ports -of_objects $site]
+        set port [get_ports $pin_str]
+        set tile [get_tiles -of_objects $site]
+
+        set pin [dict get $io_pin_sites $site]
+        set_property -dict "PACKAGE_PIN $pin IOSTANDARD LVCMOS18" $port
+    }
+}
+
+proc run {} {
+    create_project -force -part $::env(XRAY_PART) design design
+    read_verilog top.v
+    synth_design -top top
+
+    loc_pins
+
+    set_property CFGBVS GND [current_design]
+    set_property CONFIG_VOLTAGE 1.8 [current_design]
+    set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-79}]
+
+    place_design
+    route_design
+
+    write_checkpoint -force design.dcp
+    write_bitstream -force design.bit
+}
+
+run
diff --git a/fuzzers/005-tilegrid/iob18_int/top.py b/fuzzers/005-tilegrid/iob18_int/top.py
new file mode 100644
index 0000000..5cb360b
--- /dev/null
+++ b/fuzzers/005-tilegrid/iob18_int/top.py
@@ -0,0 +1,139 @@
+#!/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
+'''
+Generate a primitive to place at every I/O
+Unlike CLB tests, the LFSR for this is inside the ROI, not driving it
+'''
+
+import os
+import random
+random.seed(int(os.getenv("SEED"), 16))
+from prjxray import util
+from prjxray.db import Database
+import re
+
+
+def gen_sites():
+    '''
+    IOB18S: main IOB of a diff pair
+    IOB18M: secondary IOB of a diff pair
+    IOB18: not a diff pair. Relatively rare (at least in ROI...2 of them?)
+    Focus on IOB18S to start
+    '''
+    db = Database(util.get_db_root(), util.get_part())
+    grid = db.grid()
+    for tile_name in sorted(grid.tiles()):
+        loc = grid.loc_of_tilename(tile_name)
+        gridinfo = grid.gridinfo_at_loc(loc)
+
+        sites = []
+        for site_name, site_type in gridinfo.sites.items():
+            if site_type == 'IDELAYE2_FINEDELAY':
+                sites.append(site_name)
+
+        if len(sites) == 0:
+            continue
+
+        sites_y = [
+            int(re.match('IDELAY_X[0-9]+Y([0-9]+)', site).group(1))
+            for site in sites
+        ]
+
+        sites, _ = zip(*sorted(zip(sites, sites_y), key=lambda x: x[1]))
+
+        if gridinfo.tile_type[0] == 'L':
+            int_grid_x = loc.grid_x + 3
+            pad_grid_x = loc.grid_x - 1
+            int_tile_type = 'INT_L'
+        else:
+            int_grid_x = loc.grid_x - 3
+            pad_grid_x = loc.grid_x + 1
+            int_tile_type = 'INT_R'
+
+        int_tile_locs = [
+            (int_grid_x, loc.grid_y),
+        ]
+
+        pad_gridinfo = grid.gridinfo_at_loc((pad_grid_x, loc.grid_y))
+
+        pad_sites = pad_gridinfo.sites.keys()
+        pad_sites_y = [
+            int(re.match('IOB_X[0-9]+Y([0-9]+)', site).group(1))
+            for site in pad_sites
+        ]
+        pad_sites, _ = zip(
+            *sorted(zip(pad_sites, pad_sites_y), key=lambda x: x[1]))
+
+        if not gridinfo.tile_type.endswith("_SING"):
+            int_tile_locs.append((int_grid_x, loc.grid_y - 1))
+
+        assert len(sites) == len(int_tile_locs), (
+            tile_name, sites, int_tile_locs)
+        assert len(sites) == len(pad_sites), (sites, pad_sites)
+
+        for site_name, pad_site, int_tile_loc in zip(sites, pad_sites,
+                                                     int_tile_locs):
+            int_tile_name = grid.tilename_at_loc(int_tile_loc)
+            assert int_tile_name.startswith(int_tile_type), (
+                int_tile_name, site_name, int_tile_loc)
+            yield int_tile_name, site_name, pad_site
+
+
+def write_params(params):
+    pinstr = ''
+    for tile, (site, val, pad_site_name, pin) in sorted(params.items()):
+        pinstr += '%s,%s,%s,%s,%s\n' % (tile, val, site, pad_site_name, pin)
+    open('params.csv', 'w').write(pinstr)
+
+
+def run():
+    sites = list(gen_sites())
+    print(
+        '''
+`define N_DI {}
+
+module top(input wire [`N_DI-1:0] di);
+    wire [`N_DI-1:0] di_buf;
+
+    (* KEEP, DONT_TOUCH, IODELAY_GROUP = "iodelays" *)
+    IDELAYCTRL idelayctrl (
+        .REFCLK()
+    );
+    '''.format(len(sites)))
+
+    params = {}
+
+    for idx, ((tile_name, site_name, pad_site_name), isone) in enumerate(zip(
+            sites, util.gen_fuzz_states(len(sites)))):
+        params[tile_name] = (site_name, isone, pad_site_name, "di[%u]" % idx)
+
+        # Force HARD0 -> GFAN1 with CNTVALUEIN4 = 0
+        # Toggle 1 pip with CNTVALUEIN3 = ?
+        print(
+            '''
+
+            // Solving for {3}
+            (* KEEP, DONT_TOUCH *)
+            IBUF ibuf_{0}(.I(di[{2}]), .O(di_buf[{2}]));
+
+            (* KEEP, DONT_TOUCH, LOC = "{0}", IODELAY_GROUP = "iodelays" *)
+            IDELAYE2 idelay_{0} (
+                .CNTVALUEIN(5'b0{1}111),
+                .IDATAIN(di_buf[{2}])
+                );
+'''.format(site_name, isone, idx, tile_name))
+
+    print("endmodule")
+    write_params(params)
+
+
+if __name__ == '__main__':
+    run()
diff --git a/fuzzers/005-tilegrid/ioi18/Makefile b/fuzzers/005-tilegrid/ioi18/Makefile
new file mode 100644
index 0000000..9fd5b94
--- /dev/null
+++ b/fuzzers/005-tilegrid/ioi18/Makefile
@@ -0,0 +1,10 @@
+# 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
+N ?= 24
+GENERATE_ARGS?="--oneval 1 --design params.csv --dframe 20 --dword 3"
+include ../fuzzaddr/common.mk
diff --git a/fuzzers/005-tilegrid/ioi18/generate.tcl b/fuzzers/005-tilegrid/ioi18/generate.tcl
new file mode 100644
index 0000000..96e843a
--- /dev/null
+++ b/fuzzers/005-tilegrid/ioi18/generate.tcl
@@ -0,0 +1,88 @@
+# 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
+source "$::env(XRAY_DIR)/utils/utils.tcl"
+
+proc make_io_pin_sites {} {
+    # get all possible IOB pins
+    foreach pad [get_package_pins -filter "IS_GENERAL_PURPOSE == 1"] {
+        set site [get_sites -of_objects $pad]
+        if {[llength $site] == 0} {
+            continue
+        }
+        if [string match IOB18* [get_property SITE_TYPE $site]] {
+            dict append io_pin_sites $site $pad
+        }
+    }
+    return $io_pin_sites
+}
+
+proc load_pin_lines {} {
+    # IOB_X0Y103 clk input
+    # IOB_X0Y129 do[0] output
+
+    set fp [open "params.csv" r]
+    gets $fp line
+
+    set pin_lines {}
+    for {gets $fp line} {$line != ""} {gets $fp line} {
+        lappend pin_lines [split $line ","]
+    }
+    close $fp
+    return $pin_lines
+}
+
+proc loc_pins {} {
+    set pin_lines [load_pin_lines]
+    set io_pin_sites [make_io_pin_sites]
+    set package_pin_keys [dict keys $io_pin_sites]
+
+    puts "Looping"
+    for {set idx 0} {$idx < [llength $pin_lines]} {incr idx} {
+        set line [lindex $pin_lines $idx]
+        puts "$line"
+
+        set site_str [lindex $line 2]
+        set pin_str [lindex $line 3]
+        set pad_str [lindex $line 4]
+
+        # Have: site
+        # Want: pin for site
+
+        set site [get_sites $site_str]
+        set port [get_ports $pin_str]
+        set tile [get_tiles -of_objects $site]
+
+
+        set pin [dict get $io_pin_sites $pad_str]
+        set_property -dict "PACKAGE_PIN $pin IOSTANDARD LVCMOS18" $port
+    }
+}
+
+proc run {} {
+    create_project -force -part $::env(XRAY_PART) design design
+    read_verilog top.v
+    synth_design -top top
+
+    #loc_pins
+
+    set_property CFGBVS GND [current_design]
+    set_property CONFIG_VOLTAGE 1.8 [current_design]
+    set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-79}]
+    set_property SEVERITY {Warning} [get_drc_checks NSTD-1]
+    set_property SEVERITY {Warning} [get_drc_checks UCIO-1]
+    #set_property IS_ENABLED 0 [get_drc_checks {REQP-83}]
+
+    place_design
+    route_design
+
+    write_checkpoint -force design.dcp
+    write_bitstream -force design.bit
+}
+
+run
diff --git a/fuzzers/005-tilegrid/ioi18/top.py b/fuzzers/005-tilegrid/ioi18/top.py
new file mode 100644
index 0000000..be162cd
--- /dev/null
+++ b/fuzzers/005-tilegrid/ioi18/top.py
@@ -0,0 +1,119 @@
+#!/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
+import json
+import io
+import os
+import random
+import re
+random.seed(int(os.getenv("SEED"), 16))
+from prjxray import util
+from prjxray import lut_maker
+from prjxray import verilog
+from prjxray.db import Database
+
+
+def gen_sites():
+    db = Database(util.get_db_root(), util.get_part())
+    grid = db.grid()
+    for tile_name in sorted(grid.tiles()):
+        loc = grid.loc_of_tilename(tile_name)
+        gridinfo = grid.gridinfo_at_loc(loc)
+        if gridinfo.tile_type.endswith("_SING"):
+            continue
+        # Y9 tiles have frame address 1 frame higher than the rest
+        # Need to investigate what is so special about them
+        if tile_name.endswith("Y9"):
+            continue
+
+        sites = []
+        for site_name, site_type in gridinfo.sites.items():
+            if site_type == 'IDELAYE2_FINEDELAY':
+                yield tile_name, site_name
+
+
+def write_params(params):
+    pinstr = 'tile,isone,site\n'
+    for vals in params:
+        pinstr += ','.join(map(str, vals)) + '\n'
+
+    open('params.csv', 'w').write(pinstr)
+
+
+def use_idelay(p, luts, connects):
+    print(
+        '''
+    wire idelay_{site};
+
+    (* KEEP, DONT_TOUCH, LOC = "{site}" *)
+    IDELAYE2_FINEDELAY #(
+        .HIGH_PERFORMANCE_MODE("{param}"),
+        .DELAY_SRC("DATAIN")
+    ) idelay_site_{site} (
+        .DATAIN({onet}),
+        .DATAOUT(idelay_{site})
+        );
+    assign {net} = idelay_{site};
+       '''.format(
+            onet=luts.get_next_output_net(),
+            net=luts.get_next_input_net(),
+            param="TRUE" if p['isone'] else "FALSE",
+            **p),
+        file=connects)
+
+
+def run():
+    luts = lut_maker.LutMaker()
+    connects = io.StringIO()
+
+    tile_params = []
+    params = []
+    sites = sorted(list(gen_sites()))
+    for idx, ((tile, site), isone) in enumerate(zip(
+            sites, util.gen_fuzz_states(len(sites)))):
+
+        p = {}
+        p['tile'] = tile
+        p['site'] = site
+        p['isone'] = isone
+        params.append(p)
+        tile_params.append((tile, p['isone'], site))
+
+    write_params(tile_params)
+
+    print('''
+module top();
+    ''')
+
+    # Always output a LUT6 to make placer happy.
+    print('''
+     (* KEEP, DONT_TOUCH *)
+     LUT6 dummy_lut();
+     ''')
+
+    # Need IDELAYCTRL for IDEALAYs
+    print('''
+     (* KEEP, DONT_TOUCH *)
+     IDELAYCTRL();
+     ''')
+
+    for p in params:
+        use_idelay(p, luts, connects)
+
+    for l in luts.create_wires_and_luts():
+        print(l)
+
+    print(connects.getvalue())
+
+    print("endmodule")
+
+
+if __name__ == '__main__':
+    run()
diff --git a/fuzzers/005-tilegrid/util.py b/fuzzers/005-tilegrid/util.py
index 0f65e00..56e18ce 100644
--- a/fuzzers/005-tilegrid/util.py
+++ b/fuzzers/005-tilegrid/util.py
@@ -96,8 +96,8 @@
     max_frames = tile_frames.get_tile_frames(baseaddr)
     if frames > max_frames:
         print(
-            "Warning: The number of frames specified for the tile {} ({}) exceeds the maximum allowed value ({}). Falling back to the maximum value."
-            .format(tile_name, frames, max_frames))
+            "Warning: The number of frames for base address {} specified for the tile {} ({}) exceeds the maximum allowed value ({}). Falling back to the maximum value."
+            .format(hex(baseaddr), tile_name, frames, max_frames))
         frames = max_frames
     # If frames count is None then use the maximum
     if frames is None:
diff --git a/fuzzers/030-iob/write_io_banks.tcl b/fuzzers/030-iob/write_io_banks.tcl
index 9ba176f..284e0fe 100644
--- a/fuzzers/030-iob/write_io_banks.tcl
+++ b/fuzzers/030-iob/write_io_banks.tcl
@@ -20,7 +20,10 @@
 foreach site_type { IOB33M IOB33S IDELAYCTRL} {
     foreach site [get_sites -filter "SITE_TYPE == $site_type"] {
         set tile [get_tiles -of $site]
-        puts $fp "$site,$tile,[get_property CLOCK_REGION $site]"
+        # exclude IDELAYCTRL from high speed banks
+        if {![string match "*_IOI_*" $tile]} {
+            puts $fp "$site,$tile,[get_property CLOCK_REGION $site]"
+        }
     }
 }
 close $fp
diff --git a/fuzzers/030-iob18/Makefile b/fuzzers/030-iob18/Makefile
new file mode 100644
index 0000000..f90dbfd
--- /dev/null
+++ b/fuzzers/030-iob18/Makefile
@@ -0,0 +1,41 @@
+# Copyright (C) 2017-2022  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
+N := 80
+SPECIMENS_DEPS := build/iobanks.txt
+include ../fuzzer.mk
+
+database: build/segbits_riob18.db build/segbits_hclk_ioi.db
+
+build/iobanks.txt: write_io_banks.tcl
+	mkdir -p build
+	cd build/ && ${XRAY_VIVADO} -mode batch -source ../write_io_banks.tcl
+
+build/segbits_riob18.rdb: $(SPECIMENS_OK)
+	${XRAY_SEGMATCH} -c 23 -o build/segbits_riob18.rdb $$(find -name segdata_riob18.txt)
+
+build/segbits_riob18.db: build/segbits_riob18.rdb process_rdb.py bits.dbf
+#   delete the Y1 LVDS tags because they are empty anyway and are missing the DRIVE tag which upsets process_rdb.py
+	sed '/IOB18.IOB_Y1.LVDS/d' -i build/segbits_riob18.rdb
+	python3 process_rdb.py build/segbits_riob18.rdb > build/segbits_riob18_processed.rdb
+	${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf --groups tag_groups.txt --seg-fn-in build/segbits_riob18_processed.rdb --seg-fn-out $@
+	${XRAY_MASKMERGE} build/mask_riob18.db $$(find -name segdata_riob18.txt)
+
+build/segbits_hclk_ioi.rdb: $(SPECIMENS_OK)
+	${XRAY_SEGMATCH} -c 10 -o build/segbits_hclk_ioi.rdb $$(find -name segdata_hclk_ioi.txt)
+
+build/segbits_hclk_ioi.db: build/segbits_hclk_ioi.rdb
+	${XRAY_DBFIXUP} --db-root build --zero-db hclk_bits.dbf --seg-fn-in build/segbits_hclk_ioi.rdb --seg-fn-out $@
+
+pushdb:
+	${XRAY_MERGEDB} riob18 build/segbits_riob18.db
+	${XRAY_MERGEDB} mask_riob18 build/mask_riob18.db
+
+	${XRAY_MERGEDB} hclk_ioi build/segbits_hclk_ioi.db
+
+.PHONY: database pushdb
+
diff --git a/fuzzers/030-iob18/README.md b/fuzzers/030-iob18/README.md
new file mode 100644
index 0000000..8b62051
--- /dev/null
+++ b/fuzzers/030-iob18/README.md
@@ -0,0 +1,20 @@
+# IOB18 Fuzzer
+
+This fuzzer solves the bits related to the IO pads in the
+high performance banks, which are present in most of the
+Kintex and Virtex devices (a notable exception are K420T and K480T)
+using bank numbers of 30 and above.
+
+These banks have a maximum voltage of 1.8V which is the reason
+why many of the BEL names end in 18 (as opposed to 33 for the
+high range banks, which run on a maximum voltage of 3.3V).
+
+Currently the focus is on supporting the most used IO standards;
+On most boards, the high performance banks are connected to DDR3
+memory and this mandates the support of single ended and differential
+SSTL15. 
+Of course, all the LVCMOS variants and LVDS are a must have too.
+
+Currently unsupported are digitally controlled impedance
+(usually indicated by the regular IO standard names with a
+_DCI and _T_DCI suffix).
diff --git a/fuzzers/030-iob18/bits.dbf b/fuzzers/030-iob18/bits.dbf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fuzzers/030-iob18/bits.dbf
diff --git a/fuzzers/030-iob18/check_results.py b/fuzzers/030-iob18/check_results.py
new file mode 100644
index 0000000..5a66bfd
--- /dev/null
+++ b/fuzzers/030-iob18/check_results.py
@@ -0,0 +1,235 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017-2022  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
+""" Sanity checks FASM output from IOB fuzzer.
+The IOB fuzzer is fairly complicated, and it's output is hard to verify by
+inspection.  For this reason, check_results.py was written to compare the
+specimen's generated and their FASM output.  The FASM output does pose a
+chicken and egg issue.  The test procedure is a follows:
+
+1. Build the database (e.g. make -j<N> run)
+2. Build the database again (e.g. make -j<N> run)
+3. Run check_results.py
+
+The second time that the database is run, the FASM files in the specimen's
+will have the bits documented by fuzzer.
+
+"""
+import argparse
+import os
+import os.path
+from prjxray import verilog
+import json
+import generate
+
+
+def process_parts(parts):
+    if len(parts) == 0:
+        return
+
+    if parts[-1] == 'IN_ONLY':
+        yield 'type', ['IBUF', 'IBUFDS']
+
+    if len(parts) > 2 and parts[-2] == 'SLEW':
+        yield 'SLEW', verilog.quote(parts[-1])
+
+    if parts[0] == 'PULLTYPE':
+        yield 'PULLTYPE', verilog.quote(parts[1])
+
+    if len(parts) > 1 and parts[1] == 'IN':
+        yield 'IOSTANDARDS', parts[0].split('_')
+        yield 'IN', True
+
+    if len(parts) > 1 and parts[1] == 'IN_DIFF':
+        yield 'IOSTANDARDS', parts[0].split('_')
+        yield 'IN_DIFF', True
+
+    if len(parts) > 1 and parts[1] == 'DRIVE':
+        yield 'IOSTANDARDS', parts[0].split('_')
+
+        if parts[2] == 'I_FIXED':
+            yield 'DRIVES', [None]
+        else:
+            yield 'DRIVES', parts[2].split('_')
+
+
+def create_sites_from_fasm(fasm_file):
+    sites = {}
+
+    diff_tiles = set()
+
+    with open(fasm_file) as f:
+        for l in f:
+            if 'IOB18' not in l:
+                continue
+
+            parts = l.strip().split('.')
+            tile = parts[0]
+            site = parts[1]
+
+            if 'OUT_DIFF' == site:
+                diff_tiles.add(tile)
+                continue
+
+            if (tile, site) not in sites:
+                sites[(tile, site)] = {
+                    'tile': tile,
+                    'site_key': site,
+                }
+
+            if len(parts) > 3 and 'IN_DIFF' == parts[3]:
+                diff_tiles.add(tile)
+
+            for key, value in process_parts(parts[2:]):
+                sites[(tile, site)][key] = value
+
+    for key in sites:
+        if 'type' not in sites[key]:
+            if 'IOSTANDARDS' not in sites[key]:
+                sites[key]['type'] = [None]
+            else:
+                assert 'IOSTANDARDS' in sites[key], sites[key]
+                assert 'DRIVES' in sites[key], sites[key]
+
+                if 'IN' in sites[key]:
+                    sites[key]['type'] = ['IOBUF', 'IOBUF_DCIEN']
+                else:
+                    sites[key]['type'] = [
+                        "OBUF",
+                        "OBUFDS",
+                        "OBUFTDS",
+                        "OBUFDS_DUAL_BUF",
+                        "OBUFTDS_DUAL_BUF",
+                    ]
+
+    return sites, diff_tiles
+
+
+def process_specimen(fasm_file, params_json):
+    sites, diff_tiles = create_sites_from_fasm(fasm_file)
+
+    with open(params_json) as f:
+        params = json.load(f)
+
+    count = 0
+    for p in params['tiles']:
+        tile = p['tile']
+        for site in p['site'].split(' '):
+            site_y = int(site[site.find('Y') + 1:]) % 2
+
+            if generate.skip_broken_tiles(p):
+                continue
+
+            site_key = 'IOB_Y{}'.format(site_y)
+
+            if (tile, site_key) not in sites:
+                assert p['type'] is None, p
+                continue
+
+            site_from_fasm = sites[(tile, site_key)]
+
+            if site_y == 0 or tile not in diff_tiles:
+                assert p['type'] in site_from_fasm['type'], (
+                    tile, site_key, p['type'], site_from_fasm['type'])
+            else:
+                # Y1 on DIFF tiles is always none.
+                assert p['type'] is None, p
+
+            if p['type'] is None:
+                continue
+
+            assert 'PULLTYPE' in p, p
+            assert 'PULLTYPE' in site_from_fasm, site_from_fasm
+
+            if verilog.unquote(p['PULLTYPE']) == '':
+                # Default is None.
+                pulltype = verilog.quote('NONE')
+            else:
+                pulltype = p['PULLTYPE']
+
+            assert pulltype == site_from_fasm['PULLTYPE'], (
+                tile, site_key, p, site_from_fasm)
+
+            assert 'IOSTANDARDS' in site_from_fasm, (tile, site)
+
+            iostandard = verilog.unquote(p['IOSTANDARD'])
+            if iostandard.startswith('DIFF_'):
+                iostandard = iostandard[5:]
+
+            assert iostandard in site_from_fasm['IOSTANDARDS'], (
+                p['IOSTANDARD'],
+                site_from_fasm['IOSTANDARDS'],
+            )
+
+            if p['type'] not in ['IBUF', 'IBUFDS']:
+                if verilog.unquote(p['SLEW']) == '':
+                    # Default is None.
+                    slew = verilog.quote('SLOW')
+                else:
+                    slew = p['SLEW']
+
+                assert slew == site_from_fasm['SLEW'], (
+                    tile, site_key, p, site_from_fasm)
+
+                assert 'DRIVES' not in p, p
+                assert 'DRIVES' in site_from_fasm, (
+                    tile, site, p['type'], site_from_fasm)
+
+                if p['DRIVE'] is None:
+                    assert None in site_from_fasm['DRIVES'], (
+                        tile, site_key, p['DRIVE'], site_from_fasm['DRIVES'])
+                elif p['DRIVE'] == '':
+                    if None in site_from_fasm['DRIVES']:
+                        # IOSTANDARD has not DRIVE setting, ignore
+                        pass
+                    else:
+                        # Check that drive is at default
+                        assert 'I12' in site_from_fasm['DRIVES'], (
+                            tile, site_key, p['DRIVE'],
+                            site_from_fasm['DRIVES'])
+                else:
+                    assert 'I{}'.format(
+                        p['DRIVE']) in site_from_fasm['DRIVES'], (
+                            tile, site_key, p['DRIVE'],
+                            site_from_fasm['DRIVES'])
+
+            count += 1
+
+    return count
+
+
+def scan_specimens():
+    for root, dirs, files in os.walk('build'):
+        if os.path.basename(root).startswith('specimen_'):
+            print('Processing', os.path.basename(root))
+            process_specimen(
+                fasm_file=os.path.join(root, 'design.fasm'),
+                params_json=os.path.join(root, 'params.json'))
+
+    print('No errors found!')
+
+
+def main():
+    parser = argparse.ArgumentParser(description="Verify IOB FASM vs BELs.")
+
+    parser.add_argument('--fasm')
+    parser.add_argument('--params')
+
+    args = parser.parse_args()
+
+    if not args.fasm and not args.params:
+        scan_specimens()
+    else:
+        count = process_specimen(fasm_file=args.fasm, params_json=args.params)
+        print('No errors found in {} IO sites'.format(count))
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/030-iob18/generate.py b/fuzzers/030-iob18/generate.py
new file mode 100644
index 0000000..fc0af32
--- /dev/null
+++ b/fuzzers/030-iob18/generate.py
@@ -0,0 +1,298 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017-2022  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
+
+from prjxray.segmaker import Segmaker
+from prjxray import segmaker
+from prjxray import verilog
+import os
+import json
+import csv
+
+from iostandards import *
+
+def bitfilter(frame, word):
+    return 38 <= frame
+
+def mk_drive_opt(iostandard, drive):
+    if drive is None:
+        drive = '_FIXED'
+    return '{}.DRIVE.I{}'.format(iostandard, drive)
+
+def drives_for_iostandard(iostandard):
+    if iostandard in ['LVCMOS18', 'LVCMOS15']:
+        drives = [2, 4, 6, 8, 12, 16]
+    elif iostandard == 'LVCMOS12':
+        drives = [2, 4, 6, 8]
+    elif iostandard in SSTL + DIFF_SSTL:
+        return ['_FIXED']
+    else:
+        assert False, "this line should be unreachable"
+
+    return drives
+
+STEPDOWN_IOSTANDARDS   = LVCMOS + SSTL
+IBUF_LOW_PWR_SUPPORTED = LVDS + DIFF_SSTL
+ONLY_DIFF_IOSTANDARDS  = LVDS
+
+
+def main():
+    # Create map of iobank -> sites
+    iobanks = {}
+    site_to_iobank = {}
+    iobank_iostandards = {}
+    with open(os.path.join(os.getenv('FUZDIR'), 'build', 'iobanks.txt')) as f:
+        for l in f:
+            iob_site, iobank = l.strip().split(',')
+            iobank = int(iobank)
+
+            if iobank not in iobanks:
+                iobanks[iobank] = set()
+
+            iobanks[iobank].add(iob_site)
+            assert iob_site not in site_to_iobank
+            site_to_iobank[iob_site] = iobank
+
+    for iobank in iobanks:
+        iobank_iostandards[iobank] = set()
+
+    # Load a list of PUDC_B pin function tiles. They are configured differently
+    # by the vendor tools so need to be skipped
+    pudc_tiles = set()
+    with open(os.path.join(os.getenv('FUZDIR'), 'build',
+                           'pudc_sites.csv')) as f:
+        for l in csv.DictReader(f):
+            pudc_tiles.add(l["tile"])
+
+    print("Loading tags")
+    segmk = Segmaker("design.bits")
+    '''
+    port,site,tile,pin,slew,drive,pulltype
+    di[0],IOB_X1Y107,RIOB18_X1Y107,AF4,PULLDOWN
+    di[10],IOB_X1Y147,RIOB18_X1Y147,U5,PULLUP
+    '''
+    with open('params.json', 'r') as f:
+        design = json.load(f)
+
+        diff_pairs = set()
+        for d in design['tiles']:
+            iostandard = verilog.unquote(d['IOSTANDARD'])
+            if iostandard.startswith('DIFF_'):
+                diff_pairs.add(d['pair_site'])
+
+        for d in design['tiles']:
+            site = d['site']
+            tile = d['tile']
+
+            if tile in pudc_tiles:
+                continue
+
+            if site in diff_pairs:
+                continue
+
+            iostandard = verilog.unquote(d['IOSTANDARD'])
+            if iostandard.startswith('DIFF_'):
+                iostandard = iostandard[5:]
+
+            iobank_iostandards[site_to_iobank[site]].add(iostandard)
+
+            only_diff_io = iostandard in ONLY_DIFF_IOSTANDARDS
+
+            if d['type'] is None:
+                segmk.add_site_tag(site, 'INOUT', 0)
+                segmk.add_site_tag(site, '{}.IN_USE'.format(iostandard), 0)
+                segmk.add_site_tag(site, '{}.IN'.format(iostandard), 0)
+                segmk.add_site_tag(site, '{}.OUT'.format(iostandard), 0)
+                segmk.add_site_tag(site, '{}.IN_ONLY'.format(iostandard), 0)
+            elif d['type'] == 'IBUF':
+                segmk.add_site_tag(site, 'INOUT', 0)
+                segmk.add_site_tag(site, '{}.IN_USE'.format(iostandard), 1)
+                segmk.add_site_tag(site, '{}.IN'.format(iostandard), 1)
+                segmk.add_site_tag(site, '{}.IN_DIFF'.format(iostandard), 0)
+                segmk.add_site_tag(site, '{}.OUT'.format(iostandard), 0)
+                segmk.add_site_tag(site, '{}.IN_ONLY'.format(iostandard), 1)
+                segmk.add_tile_tag(tile, 'IN_DIFF', 0)
+
+                if iostandard in IBUF_LOW_PWR_SUPPORTED:
+                    segmk.add_site_tag(site, 'IBUF_LOW_PWR', d['IBUF_LOW_PWR'])
+                    segmk.add_site_tag(site, 'ZIBUF_LOW_PWR', 1 ^ d['IBUF_LOW_PWR'])
+
+            elif d['type'] == 'IBUFDS':
+                psite = d['pair_site']
+                segmk.add_site_tag(site,  'INOUT', 0)
+                segmk.add_site_tag(site,  '{}.IN_USE'.format(iostandard), 1)
+                segmk.add_site_tag(site,  '{}.IN'.format(iostandard), 1)
+                segmk.add_site_tag(site,  '{}.IN_DIFF'.format(iostandard), 1)
+                segmk.add_site_tag(psite, '{}.IN_DIFF'.format(iostandard), 1)
+                segmk.add_site_tag(site,  '{}.OUT'.format(iostandard), 0)
+                segmk.add_site_tag(site,  '{}.IN_ONLY'.format(iostandard), 1)
+                segmk.add_tile_tag(tile,  'IN_DIFF', 1)
+
+                if iostandard in IBUF_LOW_PWR_SUPPORTED:
+                    segmk.add_tile_tag(tile, 'DIFF.IBUF_LOW_PWR', d['IBUF_LOW_PWR'])
+                    segmk.add_tile_tag(tile, 'DIFF.ZIBUF_LOW_PWR', 1 ^ d['IBUF_LOW_PWR'])
+
+            elif d['type'] == 'OBUF':
+                segmk.add_site_tag(site, 'INOUT', 0)
+                segmk.add_site_tag(site, '{}.IN_USE'.format(iostandard), 1)
+                segmk.add_site_tag(site, '{}.IN'.format(iostandard), 0)
+                segmk.add_site_tag(site, '{}.OUT'.format(iostandard), 1)
+                segmk.add_tile_tag(tile, 'OUT_DIFF', 0)
+
+            elif d['type'] == 'OBUFDS':
+                segmk.add_site_tag(site, 'INOUT', 0)
+                segmk.add_site_tag(site, '{}.IN_USE'.format(iostandard), 1)
+                segmk.add_site_tag(site, '{}.IN'.format(iostandard), 0)
+                segmk.add_site_tag(site, '{}.OUT'.format(iostandard), 1)
+                segmk.add_tile_tag(tile, 'OUT_DIFF', 1 and not only_diff_io)
+                segmk.add_tile_tag(tile, 'OUT_TDIFF', 0)
+
+            elif d['type'] == 'OBUFTDS':
+                segmk.add_site_tag(site, 'INOUT', 0)
+                segmk.add_site_tag(site, '{}.IN_USE'.format(iostandard), 1)
+                segmk.add_site_tag(site, '{}.IN'.format(iostandard), 0)
+                segmk.add_site_tag(site, '{}.OUT'.format(iostandard), 1)
+                segmk.add_tile_tag(tile, 'OUT_DIFF', 1 and not only_diff_io)
+                segmk.add_tile_tag(tile, 'OUT_TDIFF', 1 and not only_diff_io)
+
+            elif d['type'] == 'IOBUF_DCIEN':
+                segmk.add_site_tag(site, 'INOUT', 1)
+                segmk.add_site_tag(site, '{}.IN_USE'.format(iostandard), 1)
+                segmk.add_site_tag(site, '{}.IN'.format(iostandard), 1)
+                segmk.add_site_tag(site, '{}.OUT'.format(iostandard), 1)
+
+
+            if d['type'] is not None:
+                segmaker.add_site_group_zero(
+                    segmk, site, "PULLTYPE.",
+                    ("NONE", "KEEPER", "PULLDOWN", "PULLUP"), "PULLDOWN",
+                    verilog.unquote(d['PULLTYPE']))
+
+            if d['type'] in [None, 'IBUF', 'IBUFDS']:
+                continue
+
+            drive_opts = set()
+            for opt in LVCMOS:
+                for drive_opt in ("2", "4", "6", "8", "12", "16"):
+                    if drive_opt in ["12", "16"] and opt == "LVCMOS12":
+                        continue
+
+                    drive_opts.add(mk_drive_opt(opt, drive_opt))
+
+            for sstl in SSTL:
+                drive_opts.add(mk_drive_opt(sstl, None))
+
+            drive_opts.add(mk_drive_opt("LVDS", None))
+
+            segmaker.add_site_group_zero(
+                segmk, site, '', drive_opts, mk_drive_opt('LVCMOS25', '12'),
+                mk_drive_opt(iostandard, d['DRIVE']))
+
+            if d['SLEW']:
+                for opt in ["SLOW", "FAST"]:
+                    segmk.add_site_tag(
+                        site, iostandard + ".SLEW." + opt,
+                        opt == verilog.unquote(d['SLEW']))
+
+            if 'ibufdisable_wire' in d:
+                segmk.add_site_tag(
+                    site, 'IBUFDISABLE.I', d['ibufdisable_wire'] != '0')
+
+            if 'dcitermdisable_wire' in d:
+                segmk.add_site_tag(
+                    site, 'DCITERMDISABLE.I', d['dcitermdisable_wire'] != '0')
+
+    site_to_cmt = {}
+    site_to_tile = {}
+    tile_to_cmt = {}
+    cmt_to_idelay = {}
+    with open(os.path.join(os.getenv('FUZDIR'), 'build', 'cmt_regions.csv')) as f:
+        for l in f:
+            site, tile, cmt = l.strip().split(',')
+            site_to_tile[site] = tile
+
+            site_to_cmt[site] = cmt
+            tile_to_cmt[tile] = cmt
+
+            # Given IDELAYCTRL's are only located in HCLK_IOI tiles, and
+            # there is only on HCLK_IOI tile per CMT, update
+            # CMT -> IDELAYCTRL / tile map.
+            if 'IDELAYCTRL' in site:
+                assert cmt not in cmt_to_idelay
+                cmt_to_idelay[cmt] = site, tile
+
+    # For each IOBANK with an active VREF set the feature
+    cmt_vref_active = set()
+    with open('iobank_vref.csv') as f:
+        for l in f:
+            iobank, vref = l.strip().split(',')
+            iobank = int(iobank)
+
+            cmt = None
+            for cmt_site in iobanks[iobank]:
+                if cmt_site in site_to_cmt:
+                    cmt = site_to_cmt[cmt_site]
+                    break
+
+            if cmt is None:
+                continue
+
+            cmt_vref_active.add(cmt)
+
+            _, hclk_cmt_tile = cmt_to_idelay[cmt]
+
+            opt = 'VREF.V_{:d}_MV'.format(int(float(vref) * 1000))
+            segmk.add_tile_tag(hclk_cmt_tile, opt, 1)
+
+    for iobank in iobank_iostandards:
+        if len(iobank_iostandards[iobank]) == 0:
+            continue
+
+        for cmt_site in iobanks[iobank]:
+            if cmt_site in site_to_cmt:
+                cmt = site_to_cmt[cmt_site]
+                break
+
+        if cmt is None:
+            continue
+
+        _, hclk_cmt_tile = cmt_to_idelay[cmt]
+
+        assert len(iobank_iostandards[iobank]) == 1, iobank_iostandards[iobank]
+
+        iostandard = list(iobank_iostandards[iobank])[0]
+        for only_diff_io in ONLY_DIFF_IOSTANDARDS:
+            segmk.add_tile_tag(
+                hclk_cmt_tile, '{}_IN_USE'.format(only_diff_io),
+                iostandard == only_diff_io)
+
+        segmk.add_tile_tag(
+            hclk_cmt_tile, 'ONLY_DIFF_IN_USE',
+            iostandard in ONLY_DIFF_IOSTANDARDS)
+
+    # For IOBANK's with no active VREF, clear all VREF options.
+    for cmt, (_, hclk_cmt_tile) in cmt_to_idelay.items():
+        if cmt in cmt_vref_active:
+            continue
+
+        for vref in (
+                .600,
+                .675,
+                .75,
+                .90,
+        ):
+            opt = 'VREF.V_{:d}_MV'.format(int(vref * 1000))
+            segmk.add_tile_tag(hclk_cmt_tile, opt, 0)
+
+    segmk.compile(bitfilter=bitfilter)
+    segmk.write(allow_empty=True)
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/030-iob18/generate.tcl b/fuzzers/030-iob18/generate.tcl
new file mode 100644
index 0000000..9534624
--- /dev/null
+++ b/fuzzers/030-iob18/generate.tcl
@@ -0,0 +1,122 @@
+# Copyright (C) 2017-2022  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
+source "$::env(XRAY_DIR)/utils/utils.tcl"
+
+proc make_io_pin_sites {} {
+    # get all possible IOB pins
+    foreach pad [get_package_pins -filter "IS_GENERAL_PURPOSE == 1"] {
+        set site [get_sites -of_objects $pad]
+        if {[llength $site] == 0} {
+            continue
+        }
+        if [string match IOB18* [get_property SITE_TYPE $site]] {
+            dict append io_pin_sites $site $pad
+        }
+    }
+    return $io_pin_sites
+}
+
+proc load_pin_lines {} {
+    # IOB_X0Y103 clk input
+    # IOB_X0Y129 do[0] output
+
+    set fp [open "params.csv" r]
+    gets $fp line
+
+    set pin_lines {}
+    for {gets $fp line} {$line != ""} {gets $fp line} {
+        lappend pin_lines [split $line ","]
+    }
+    close $fp
+    return $pin_lines
+}
+
+proc loc_pins {} {
+    set pin_lines [load_pin_lines]
+    set io_pin_sites [make_io_pin_sites]
+
+    puts "Looping"
+    for {set idx 0} {$idx < [llength $pin_lines]} {incr idx} {
+        set line [lindex $pin_lines $idx]
+        puts "$line"
+
+        set site_str [lindex $line 1]
+        set pin_str [lindex $line 2]
+        set iostandard [lindex $line 3]
+        set drive [lindex $line 4]
+        set slew [lindex $line 5]
+        set pulltype [lindex $line 6]
+
+        # Have: site
+        # Want: pin for site
+
+        set site [get_sites $site_str]
+        set pad_bel [get_bels -of_objects $site -filter {TYPE =~ PAD && NAME =~ IOB_*}]
+        # set port [get_ports -of_objects $site]
+        set port [get_ports $pin_str]
+        set tile [get_tiles -of_objects $site]
+
+        set pin [dict get $io_pin_sites $site]
+
+        set props {}
+        lappend props PACKAGE_PIN $pin
+        lappend props IOSTANDARD $iostandard
+        lappend props PULLTYPE $pulltype
+
+        if {$drive != "None"} {
+            lappend props DRIVE $drive
+        }
+
+        if {$slew != "None"} {
+            lappend props SLEW $slew
+        }
+
+        puts $props
+
+        set_property -dict "$props" $port
+    }
+}
+
+proc set_vref {} {
+    set fp [open "iobank_vref.csv" r]
+    for {gets $fp line} {$line != ""} {gets $fp line} {
+        set parts [split $line ","]
+        set iobank [lindex $parts 0]
+        set vref [lindex $parts 1]
+        puts "setting $iobank ([get_iobanks $iobank]) to INTERNAL_VREF $vref"
+        set_property INTERNAL_VREF $vref [get_iobanks $iobank]
+    }
+}
+
+proc run {} {
+    create_project -force -part $::env(XRAY_PART) design design
+    read_verilog top.v
+    synth_design -top top
+
+    loc_pins
+    set_vref
+
+    set_property CFGBVS GND [current_design]
+    set_property CONFIG_VOLTAGE 1.8 [current_design]
+    set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
+    set_property IS_ENABLED 0 [get_drc_checks {NSTD-1}]
+    set_property IS_ENABLED 0 [get_drc_checks {UCIO-1}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-79}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-144}]
+    set_property IS_ENABLED 0 [get_drc_checks {AVAL-74}]
+
+    write_checkpoint -force design_pre_place.dcp
+
+    place_design
+    route_design
+
+    write_checkpoint -force design.dcp
+    write_bitstream -force design.bit
+}
+
+run
diff --git a/fuzzers/030-iob18/hclk_bits.dbf b/fuzzers/030-iob18/hclk_bits.dbf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fuzzers/030-iob18/hclk_bits.dbf
diff --git a/fuzzers/030-iob18/iostandards.py b/fuzzers/030-iob18/iostandards.py
new file mode 100644
index 0000000..c657763
--- /dev/null
+++ b/fuzzers/030-iob18/iostandards.py
@@ -0,0 +1,17 @@
+LVCMOS       = ['LVCMOS12', 'LVCMOS15', 'LVCMOS18']
+
+# TODO: add support for digitally controlled impedance (_DCI) and termination (_T)
+SSTL         = ['SSTL15',  #'SSTL15_DCI',  'SSTL15_T_DCI'
+                'SSTL135', #'SSTL135_DCI', 'SSTL135_T_DCI'
+                'SSTL12',  #'SSTL12_DCI',  'SSTL12_T_DCI'
+               ]
+
+# TODO: add support for digitally controlled impedance (_DCI) and termination (_T)
+DIFF_SSTL15  = ['DIFF_SSTL15',  ] # 'DIFF_SSTL15_DCI',  'DIFF_SSTL15_T_DCI']
+DIFF_SSTL135 = ['DIFF_SSTL135', ] # 'DIFF_SSTL135_DCI', 'DIFF_SSTL135_T_DCI']
+DIFF_SSTL12  = ['DIFF_SSTL12',  ] # 'DIFF_SSTL12_DCI',  'DIFF_SSTL12_T_DCI']
+DIFF_SSTL    = DIFF_SSTL15 + DIFF_SSTL135 + DIFF_SSTL12
+
+LVDS         = ['LVDS']
+
+DIFF         = DIFF_SSTL + LVDS
\ No newline at end of file
diff --git a/fuzzers/030-iob18/process_rdb.py b/fuzzers/030-iob18/process_rdb.py
new file mode 100644
index 0000000..2cacb46
--- /dev/null
+++ b/fuzzers/030-iob18/process_rdb.py
@@ -0,0 +1,251 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017-2022  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
+""" IOB bits are more complicated than can be easily expressed to segmaker.
+There are couple cases that need to be handled here:
+
+- There are some bits that are always set for IN-only ports, but are cleared
+  selectively for OUT and INOUT ports.
+- There are bits per each IOSTANDARD, in addition to drive patterns.  These
+  can be merged to provide unique "(IOSTANDARD, DRIVE)" bit sets.
+"""
+import argparse
+
+
+def get_name(l):
+    parts = l.strip().split(' ')
+    return parts[0]
+
+
+def get_site(l):
+    return get_name(l).split('.')[1]
+
+
+def parse_bits(l):
+    parts = l.strip().split(' ')
+    if parts[1] in ['<0', '<const0>', '<const1>']:
+        return frozenset()
+    else:
+        return frozenset(parts[1:])
+
+
+def filter_negbits(site, feature, bits):
+    lvds_bits = frozenset(['!38_24', "!38_48", "!39_33", '!39_47', '!39_49'])
+    if "IOB_Y0" in feature and not "LVDS" in feature:
+        bits = bits.difference(lvds_bits)
+    if feature.endswith("IOB_Y0.LVCMOS12_LVCMOS15_LVCMOS18.IN"):
+        bits = bits.difference(frozenset(['!39_01']))
+    if feature.endswith("IOB_Y1.LVCMOS12_LVCMOS15_LVCMOS18.IN"):
+        bits = bits.difference(frozenset(['!38_126']))
+
+    return bits
+
+
+def process_features_sets(iostandard_lines):
+    sites = {}
+
+    for iostd_type, iostd_list in iostandard_lines.items():
+        for iostd_line in iostd_list:
+            feature = get_name(iostd_line)
+            feature_parts = feature.split('.')
+            site = get_site(iostd_line)
+            iostandard = feature_parts[2]
+
+            bits = parse_bits(iostd_line)
+
+            key = (site, iostd_type)
+            if key not in sites:
+                sites[key] = {}
+
+            group = feature_parts[3]
+            if group not in sites[key]:
+                sites[key][group] = {}
+
+            if group in ['DRIVE', 'SLEW']:
+                enum = feature_parts[4]
+                sites[key][group][(iostandard, enum)] = bits
+            elif group in ['IN', 'IN_DIFF', 'IN_ONLY', 'IN_USE', 'OUT',
+                           'STEPDOWN', 'ZIBUF_LOW_PWR']:
+                sites[key][group][(iostandard, None)] = bits
+            else:
+                assert False, group
+
+    for site in sites:
+        for iostandard, enum in sites[site]['DRIVE']:
+            sites[site]['DRIVE'][(iostandard, enum)] |= sites[site]['OUT'][(
+                iostandard, None)]
+
+        for iostandard, enum in sites[site]['IN']:
+            sites[site]['IN_ONLY'][(iostandard, enum)] -= sites[site]['IN'][(
+                iostandard, enum)]
+
+    common_bits = {}
+    for site, iostd_type in sites:
+        for group in sites[(site, iostd_type)]:
+            if (site, group) not in common_bits:
+                common_bits[(site, group)] = set()
+
+            for bits in sites[(site, iostd_type)][group].values():
+                common_bits[(site, group)] |= bits
+
+    slew_in_drives = {}
+
+    for site, iostd_type in sites:
+        common_bits[(site, 'IN')] |= common_bits[(site, 'IN_DIFF')]
+        common_bits[(site, 'IN_DIFF')] |= common_bits[(site, 'IN')]
+
+        # Only DIFF IOSTANDARDS such as LVDS or TMDS do not have DRIVE or SLEW features
+        if iostd_type == "NORMAL":
+            key = (site, iostd_type)
+            common_bits[(site, 'DRIVE')] -= common_bits[(site, 'SLEW')]
+            common_bits[(site, 'IN_ONLY')] |= common_bits[(site, 'DRIVE')]
+
+            for iostandard, enum in sites[key]['DRIVE']:
+                slew_in_drive = common_bits[
+                    (site, 'SLEW')] & sites[key]['DRIVE'][(iostandard, enum)]
+                if slew_in_drive:
+                    if (key, iostandard) not in slew_in_drives:
+                        slew_in_drives[(key, iostandard)] = set()
+
+                    slew_in_drives[(key, iostandard)] |= slew_in_drive
+                    sites[key]['DRIVE'][(iostandard, enum)] -= slew_in_drive
+
+    for site, iostandard in slew_in_drives:
+        for _, enum in sites[site]['SLEW']:
+            sites[site]['SLEW'][(iostandard,
+                                 enum)] |= slew_in_drives[(site, iostandard)]
+
+    for site in sites:
+        for iostandard, enum in sites[site]['DRIVE']:
+            sites[site]['DRIVE'][(iostandard, enum)] |= sites[site]['IN_USE'][(
+                iostandard, None)]
+
+        for iostandard, enum in sites[site]['IN']:
+            _, iostd_type = site
+            if iostd_type == "ONLY_DIFF":
+                sites[site]['IN_DIFF'][(iostandard, enum)] = \
+                        sites[site]['IN'][(iostandard, enum)]
+            elif sites[site]['IN_DIFF'][(iostandard, enum)]:
+                sites[site]['IN_DIFF'][(iostandard, enum)] |= \
+                        sites[site]['IN'][(iostandard, enum)]
+
+    for site, iostd_type in sites:
+        if iostd_type == "NORMAL":
+            del sites[(site, iostd_type)]['OUT']
+
+    allow_zero = ['SLEW']
+
+    common_groups = dict()
+    for site, iostd_type in sites:
+        if site not in common_groups:
+            common_groups[site] = dict()
+
+        key = (site, iostd_type)
+        for group in sites[key]:
+            if iostd_type == "ONLY_DIFF" and group == "IN":
+                continue
+
+            # Merge features that are identical.
+            #
+            # For example:
+            #
+            #  IOB18.IOB_Y1.LVCMOS15.IN 38_42 39_41
+            #  IOB18.IOB_Y1.LVCMOS18.IN 38_42 39_41
+            #
+            # Must be grouped.
+            for (iostandard, enum), bits in sites[key][group].items():
+                if (bits, group) not in common_groups[site]:
+                    common_groups[site][(bits, group)] = {
+                        'IOSTANDARDS': set(),
+                        'enums': set(),
+                    }
+
+                common_groups[site][(bits,
+                                     group)]['IOSTANDARDS'].add(iostandard)
+                if enum is not None:
+                    common_groups[site][(bits, group)]['enums'].add(enum)
+
+    visited_iostandards = list()
+    for site, groups in common_groups.items():
+        for (bits, group), v in groups.items():
+            iostandards = v['IOSTANDARDS']
+            enums = v['enums']
+
+            # It happens that some features appear only in one of the IOB sites and not
+            # in the other. This makes it hard to assign the correct features to the correct
+            # site in the P&R toolchain.
+            #
+            # The following code makes sure that the same set of iostandards
+            # (even if not really present at a site location) appears for each site
+            for visited_iostandard, visited_group, visited_enums in visited_iostandards:
+                same_enum = enums == visited_enums
+                same_group = group == visited_group
+                compatible_iostd = any(
+                    x in iostandards for x in visited_iostandard)
+                take_visited_iostd = len(visited_iostandard) > len(iostandards)
+                if same_enum and same_group and compatible_iostd and take_visited_iostd:
+                    iostandards = visited_iostandard
+                    break
+
+            visited_iostandards.append((iostandards, group, enums))
+
+            iostandards_string = '_'.join(sorted(iostandards))
+
+            if enums:
+                feature = 'IOB18.{site}.{iostandards}.{group}.{enums}'.format(
+                    site=site,
+                    iostandards=iostandards_string,
+                    group=group,
+                    enums='_'.join(sorted(enums)),
+                )
+            else:
+                feature = 'IOB18.{site}.{iostandards}.{group}'.format(
+                    site=site,
+                    iostandards=iostandards_string,
+                    group=group,
+                )
+
+            if not bits and group not in allow_zero:
+                continue
+
+            neg_bits = frozenset(
+                '!{}'.format(b) for b in (common_bits[(site, group)] - bits))
+            neg_bits = filter_negbits(site, feature, neg_bits)
+            print('{} {}'.format(feature, ' '.join(sorted(bits | neg_bits))))
+
+
+def main():
+    parser = argparse.ArgumentParser(
+        description="Convert IOB rdb into good rdb."
+        "")
+    parser.add_argument('input_rdb')
+
+    args = parser.parse_args()
+
+    iostandard_lines = {
+        "NORMAL": list(),
+        "ONLY_DIFF": list(),
+    }
+
+    with open(args.input_rdb) as f:
+        for l in f:
+            if ('.SSTL' in l or '.LVCMOS' in l
+                    or '.LVTTL' in l) and 'IOB_' in l:
+                iostandard_lines["NORMAL"].append(l)
+            elif ('.TMDS' in l or 'LVDS' in l):
+                iostandard_lines["ONLY_DIFF"].append(l)
+            else:
+                print(l.strip())
+
+    process_features_sets(iostandard_lines)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/030-iob18/tag_groups.txt b/fuzzers/030-iob18/tag_groups.txt
new file mode 100644
index 0000000..83bfc45
--- /dev/null
+++ b/fuzzers/030-iob18/tag_groups.txt
@@ -0,0 +1,2 @@
+IOB18.IOB_Y0.PULLTYPE.KEEPER IOB18.IOB_Y0.PULLTYPE.NONE IOB18.IOB_Y0.PULLTYPE.PULLDOWN IOB18.IOB_Y0.PULLTYPE.PULLUP
+IOB18.IOB_Y1.PULLTYPE.KEEPER IOB18.IOB_Y1.PULLTYPE.NONE IOB18.IOB_Y1.PULLTYPE.PULLDOWN IOB18.IOB_Y1.PULLTYPE.PULLUP
diff --git a/fuzzers/030-iob18/top.py b/fuzzers/030-iob18/top.py
new file mode 100644
index 0000000..e8f3980
--- /dev/null
+++ b/fuzzers/030-iob18/top.py
@@ -0,0 +1,425 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017-2022  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
+import json
+import io
+import os
+import random
+random.seed(int(os.getenv("SEED"), 16))
+from prjxray import util
+from prjxray import lut_maker
+from prjxray import verilog
+from prjxray.db import Database
+
+from iostandards import *
+
+def gen_sites():
+    '''
+    IOB18S: main IOB of a diff pair
+    IOB18M: secondary IOB of a diff pair
+    IOB18: not a diff pair. Relatively rare (at least in ROI...2 of them?)
+    Focus on IOB18S to start
+    '''
+    db = Database(util.get_db_root(), util.get_part())
+    grid = db.grid()
+    for tile_name in sorted(grid.tiles()):
+        loc = grid.loc_of_tilename(tile_name)
+        gridinfo = grid.gridinfo_at_loc(loc)
+
+        sites = {}
+        for site_name, site_type in gridinfo.sites.items():
+            if site_type in ['IOB18S', 'IOB18M']:
+                sites[site_type] = site_name
+
+        if sites:
+            yield tile_name, sites
+
+
+def write_params(params):
+    pinstr = 'tile,site,pin,iostandard,drive,slew,pulltype\n'
+    for vals in params:
+        pinstr += ','.join(map(str, vals)) + '\n'
+
+    open('params.csv', 'w').write(pinstr)
+
+
+def run():
+    tile_types = ['IBUF', 'OBUF', 'IOBUF_DCIEN', None, None]
+
+    i_idx = 0
+    o_idx = 0
+    io_idx = 0
+
+    iostandards = LVCMOS + SSTL + LVDS
+
+    diff_map = {
+        "SSTL15":  DIFF_SSTL15,
+        "SSTL135": DIFF_SSTL135,
+        "SSTL12":  DIFF_SSTL12,
+    }
+
+    only_diff_map = {
+        "LVDS": ["LVDS"],
+    }
+
+    slews = ['FAST', 'SLOW']
+    pulls = ["NONE", "KEEPER", "PULLDOWN", "PULLUP"]
+
+    luts = lut_maker.LutMaker()
+
+    connects = io.StringIO()
+
+    tile_params = []
+    params = {
+        "tiles": [],
+        'INTERNAL_VREF': {},
+    }
+
+    with open(os.path.join(os.getenv('FUZDIR'), 'build', 'iobanks.txt')) as f:
+        iobanks = set()
+        iob_sites = dict()
+        for l in f:
+            fields = l.split(',')
+            iob_site = fields[0]
+            iobank = fields[1].rstrip()
+
+            iobanks.add(iobank)
+            iob_sites[iob_site] = iobank
+
+    params['iobanks'] = iobanks
+
+    iostandard_map = dict()
+    for iobank in iobanks:
+        iostandard = random.choice(iostandards)
+        if iostandard in SSTL:
+            params['INTERNAL_VREF'][iobank] = random.choice(
+                (
+                    .600,
+                    .675,
+                    .75,
+                    .90,
+                ))
+
+        iostandard_map[iobank] = iostandard
+
+    params['iobanks'] = list(iobanks)
+
+    any_idelay = False
+    for tile, sites in gen_sites():
+        iostandard = None
+
+        site_bels = {}
+        for site_type, site_name in sites.items():
+            iobank = iob_sites[site_name]
+            iostandard = iostandard_map[iobank]
+
+            if iostandard in ['LVCMOS12']:
+                drives = [2, 4, 6, 8]
+            elif iostandard in ['LVCMOS15', 'LVCMOS18']:
+                drives = [2, 4, 6, 8, 12, 16]
+            elif iostandard in LVDS + SSTL:
+                drives = None
+            else:
+                assert False, f"Unhandled iostandard: {iostandard}"
+
+            if site_type.endswith('M'):
+                if iostandard in diff_map:
+                    site_bels[site_type] = random.choice(
+                        tile_types + ['IBUFDS', 'OBUFDS', 'OBUFTDS'])
+                elif iostandard in only_diff_map:
+                    site_bels[site_type] = random.choice(
+                        ['IBUFDS', 'OBUFDS', 'OBUFTDS', None, None])
+                else:
+                    site_bels[site_type] = random.choice(tile_types)
+                is_m_diff = site_bels[site_type] is not None and site_bels[
+                    site_type].endswith('DS')
+            else:
+                site_bels[site_type] = random.choice(tile_types)
+
+        if is_m_diff or iostandard in only_diff_map:
+            site_bels['IOB18S'] = None
+
+        for site_type, site in sites.items():
+            p = {}
+            p['tile'] = tile
+            p['site'] = site
+            p['type'] = site_bels[site_type]
+
+            if p['type'] is not None and p['type'].endswith('DS'):
+                if iostandard in diff_map:
+                    iostandard_site = random.choice(diff_map[iostandard])
+                elif iostandard in only_diff_map:
+                    iostandard_site = random.choice(only_diff_map[iostandard])
+                p['pair_site'] = sites['IOB18S']
+            else:
+                iostandard_site = iostandard
+
+            p['IOSTANDARD'] = verilog.quote(iostandard_site)
+            p['PULLTYPE'] = verilog.quote(random.choice(pulls))
+
+            if p['type'] is None:
+                p['pad_wire'] = None
+            elif p['type'] == 'IBUF':
+                p['pad_wire'] = 'di[{}]'.format(i_idx)
+                p['IDELAY_ONLY'] = random.randint(0, 1)
+                if not p['IDELAY_ONLY']:
+                    p['owire'] = luts.get_next_input_net()
+                else:
+                    any_idelay = True
+                    p['owire'] = 'idelay_{site}'.format(**p)
+
+                p['DRIVE'] = None
+                p['SLEW'] = None
+                p['IBUF_LOW_PWR'] = random.randint(0, 1)
+                i_idx += 1
+            elif p['type'] == 'IBUFDS':
+                p['pad_wire'] = 'di[{}]'.format(i_idx)
+                i_idx += 1
+                p['bpad_wire'] = 'di[{}]'.format(i_idx)
+                i_idx += 1
+
+                p['IDELAY_ONLY'] = random.randint(0, 1)
+                p['DIFF_TERM'] = random.randint(0, 1) if iostandard in LVDS else 0
+                if not p['IDELAY_ONLY']:
+                    p['owire'] = luts.get_next_input_net()
+                else:
+                    any_idelay = True
+                    p['owire'] = 'idelay_{site}'.format(**p)
+
+                p['DRIVE'] = None
+                p['SLEW'] = None
+                p['IBUF_LOW_PWR'] = random.randint(0, 1)
+
+            elif p['type'] == 'OBUF':
+                p['pad_wire'] = 'do[{}]'.format(o_idx)
+                p['iwire'] = luts.get_next_output_net()
+                if drives is not None:
+                    p['DRIVE'] = random.choice(drives)
+                else:
+                    p['DRIVE'] = None
+                p['SLEW'] = verilog.quote(random.choice(slews))
+
+                o_idx += 1
+            elif p['type'] == 'OBUFDS':
+                p['pad_wire'] = 'do[{}]'.format(o_idx)
+                o_idx += 1
+                p['bpad_wire'] = 'do[{}]'.format(o_idx)
+                o_idx += 1
+                p['iwire'] = luts.get_next_output_net()
+                if drives is not None:
+                    p['DRIVE'] = random.choice(drives)
+                else:
+                    p['DRIVE'] = None
+                p['SLEW'] = verilog.quote(random.choice(slews))
+            elif p['type'] == 'OBUFTDS':
+                p['pad_wire'] = 'do[{}]'.format(o_idx)
+                o_idx += 1
+                p['bpad_wire'] = 'do[{}]'.format(o_idx)
+                o_idx += 1
+                p['tristate_wire'] = random.choice(
+                    ('0', luts.get_next_output_net()))
+                p['iwire'] = luts.get_next_output_net()
+                if drives is not None:
+                    p['DRIVE'] = random.choice(drives)
+                else:
+                    p['DRIVE'] = None
+                p['SLEW'] = verilog.quote(random.choice(slews))
+            elif p['type'] == 'IOBUF_DCIEN':
+                p['pad_wire'] = 'dio[{}]'.format(io_idx)
+                p['iwire'] = luts.get_next_output_net()
+                p['owire'] = luts.get_next_input_net()
+                if drives is not None:
+                    p['DRIVE'] = random.choice(drives)
+                else:
+                    p['DRIVE'] = None
+                p['SLEW'] = verilog.quote(random.choice(slews))
+                p['tristate_wire'] = random.choice(
+                    ('0', luts.get_next_output_net()))
+                p['ibufdisable_wire'] = random.choice(
+                    ('0', luts.get_next_output_net()))
+                p['dcitermdisable_wire'] = random.choice(
+                    ('0', luts.get_next_output_net()))
+                io_idx += 1
+
+            if 'DRIVE' in p:
+                if p['DRIVE'] is not None:
+                    p['DRIVE_STR'] = '.DRIVE({}),'.format(p['DRIVE'])
+                else:
+                    p['DRIVE_STR'] = ''
+
+            if 'SLEW' in p:
+                p['SLEW_STR'] = ''
+                if iostandard in only_diff_map:
+                    p['SLEW'] = None
+                elif p['DRIVE'] is not None:
+                    p['SLEW_STR'] = '.SLEW({}),'.format(p['SLEW'])
+
+            if p['type'] is not None:
+                tile_params.append(
+                    (
+                        tile,
+                        site,
+                        p['pad_wire'],
+                        iostandard_site,
+                        p['DRIVE'],
+                        verilog.unquote(p['SLEW']) if p['SLEW'] else None,
+                        verilog.unquote(p['PULLTYPE']),
+                    ))
+            params['tiles'].append(p)
+
+    write_params(tile_params)
+
+    with open('iobank_vref.csv', 'w') as f:
+        for iobank, vref in params['INTERNAL_VREF'].items():
+            f.write('{},{}\n'.format(iobank, vref))
+
+    print(
+        '''
+`define N_DI {n_di}
+`define N_DO {n_do}
+`define N_DIO {n_dio}
+
+module top(input wire [`N_DI-1:0] di, output wire [`N_DO-1:0] do, inout wire [`N_DIO-1:0] dio, input refclk);
+    '''.format(n_di=i_idx, n_do=o_idx, n_dio=io_idx))
+
+    if any_idelay:
+        print('''
+        (* KEEP, DONT_TOUCH *)
+        IDELAYCTRL(.REFCLK(refclk));''')
+
+    # Always output a LUT6 to make placer happy.
+    print('''
+        (* KEEP, DONT_TOUCH *)
+        LUT6 dummy_lut();''')
+
+    for p in params['tiles']:
+        if p['type'] is None:
+            continue
+        elif p['type'] == 'IBUF':
+            print(
+                '''
+        wire idelay_{site};
+
+        (* KEEP, DONT_TOUCH *)
+        IBUF #(
+            .IBUF_LOW_PWR({IBUF_LOW_PWR}),
+            .IOSTANDARD({IOSTANDARD})
+        ) ibuf_{site} (
+            .I({pad_wire}),
+            .O({owire})
+            );'''.format(**p),
+                file=connects)
+            if p['IDELAY_ONLY']:
+                print(
+                    """
+        (* KEEP, DONT_TOUCH *)
+        IDELAYE2 idelay_site_{site} (
+            .IDATAIN(idelay_{site})
+            );""".format(**p),
+                    file=connects)
+
+        elif p['type'] == 'IBUFDS':
+            print(
+                '''
+        wire idelay_{site};
+
+        (* KEEP, DONT_TOUCH *)
+        IBUFDS #(
+            .IBUF_LOW_PWR({IBUF_LOW_PWR}),
+            .DIFF_TERM({DIFF_TERM}),
+            .IOSTANDARD({IOSTANDARD})
+        ) ibuf_{site} (
+            .I({pad_wire}),
+            .IB({bpad_wire}),
+            .O({owire})
+            );'''.format(**p),
+                file=connects)
+            if p['IDELAY_ONLY']:
+                print(
+                    """
+        (* KEEP, DONT_TOUCH *)
+        IDELAYE2 idelay_site_{site} (
+            .IDATAIN(idelay_{site})
+            );""".format(**p),
+                    file=connects)
+
+        elif p['type'] == 'OBUF':
+            print(
+                '''
+        (* KEEP, DONT_TOUCH *)
+        OBUF #(
+            {DRIVE_STR}
+            {SLEW_STR}
+            .IOSTANDARD({IOSTANDARD})
+        ) obuf_{site} (
+            .O({pad_wire}),
+            .I({iwire})
+            );'''.format(**p),
+                file=connects)
+        elif p['type'] == 'OBUFDS':
+            print(
+                '''
+        (* KEEP, DONT_TOUCH *)
+        OBUFDS #(
+            {DRIVE_STR}
+            {SLEW_STR}
+            .IOSTANDARD({IOSTANDARD})
+        ) obufds_{site} (
+            .O({pad_wire}),
+            .OB({bpad_wire}),
+            .I({iwire})
+            );'''.format(**p),
+                file=connects)
+        elif p['type'] == 'OBUFTDS':
+            print(
+                '''
+        (* KEEP, DONT_TOUCH *)
+        OBUFTDS #(
+            {DRIVE_STR}
+            {SLEW_STR}
+            .IOSTANDARD({IOSTANDARD})
+        ) obufds_{site} (
+            .O({pad_wire}),
+            .OB({bpad_wire}),
+            .T({tristate_wire}),
+            .I({iwire})
+            );'''.format(**p),
+                file=connects)
+        elif p['type'] == 'IOBUF_DCIEN':
+            print(
+                '''
+        (* KEEP, DONT_TOUCH *)
+        IOBUF_DCIEN #(
+            {DRIVE_STR}
+            {SLEW_STR}
+            .IOSTANDARD({IOSTANDARD})
+        ) ibuf_{site} (
+            .IO({pad_wire}),
+            .I({iwire}),
+            .O({owire}),
+            .T({tristate_wire}),
+            .IBUFDISABLE({ibufdisable_wire}),
+            .DCITERMDISABLE({dcitermdisable_wire})
+            );'''.format(**p),
+                file=connects)
+
+    for l in luts.create_wires_and_luts():
+        print(l)
+
+    print(connects.getvalue())
+
+    print("endmodule")
+
+    with open('params.json', 'w') as f:
+        json.dump(params, f, indent=2)
+
+
+if __name__ == '__main__':
+    run()
diff --git a/fuzzers/030-iob18/write_io_banks.tcl b/fuzzers/030-iob18/write_io_banks.tcl
new file mode 100644
index 0000000..eac330d
--- /dev/null
+++ b/fuzzers/030-iob18/write_io_banks.tcl
@@ -0,0 +1,44 @@
+# Copyright (C) 2017-2022  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
+create_project -force -part $::env(XRAY_PART) design design
+set_property design_mode PinPlanning [current_fileset]
+open_io_design -name io_1
+set fp [open "iobanks.txt" "w"]
+foreach iobank [get_iobanks] {
+    foreach site [get_sites -of $iobank] {
+        puts $fp "$site,$iobank"
+    }
+}
+close $fp
+
+set fp [open "cmt_regions.csv" "w"]
+foreach site_type { IOB18M IOB18S IDELAYCTRL} {
+    foreach site [get_sites -filter "SITE_TYPE == $site_type"] {
+        set tile [get_tiles -of $site]
+        if {![string match "*IOI3*" $tile]} {
+            puts $fp "$site,$tile,[get_property CLOCK_REGION $site]"
+        }
+    }
+}
+close $fp
+
+set fp [open "pudc_sites.csv" "w"]
+puts $fp "tile,site"
+foreach tile [get_tiles *IOB18*] {
+    foreach site [get_sites -of_objects $tile] {
+        set site_type [get_property SITE_TYPE $site]
+
+        set pin [get_package_pins -of_objects $site]
+        set pin_func [get_property PIN_FUNC $pin]
+
+        if {[string first "PUDC_B" $pin_func] != -1} {
+            puts $fp "$tile,$site,$site_type"
+        }
+    }
+}
+close $fp
diff --git a/fuzzers/035-iob-ilogic/Makefile b/fuzzers/035-iob-ilogic/Makefile
index 3984213..8ac9380 100644
--- a/fuzzers/035-iob-ilogic/Makefile
+++ b/fuzzers/035-iob-ilogic/Makefile
@@ -22,7 +22,11 @@
 	${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf --groups tag_groups.txt --seg-fn-in $^ --seg-fn-out $@
 	${XRAY_MASKMERGE} build/mask_xioi3.db $$(find -name segdata_*)
 
-pushdb:
+# fuzzers of the RIOI tiles give near identical results, so reuse these
+build/segbits_rioi.db: build/segbits_xioi3.db
+	sed 's/IOI3/IOI/g' < $< > $@
+
+pushdb: build/segbits_rioi.db
 	${XRAY_MERGEDB} lioi3 build/segbits_xioi3.db
 	${XRAY_MERGEDB} lioi3_tbytesrc build/segbits_xioi3.db
 	${XRAY_MERGEDB} lioi3_tbyteterm build/segbits_xioi3.db
@@ -35,6 +39,13 @@
 	${XRAY_MERGEDB} mask_rioi3 build/mask_xioi3.db
 	${XRAY_MERGEDB} mask_rioi3_tbytesrc build/mask_xioi3.db
 	${XRAY_MERGEDB} mask_rioi3_tbyteterm build/mask_xioi3.db
+ifeq ($(XRAY_DATABASE),kintex7)
+	${XRAY_MERGEDB} rioi build/segbits_rioi.db
+	${XRAY_MERGEDB} rioi_tbytesrc build/segbits_rioi.db
+	${XRAY_MERGEDB} rioi_tbyteterm build/segbits_rioi.db
+	${XRAY_MERGEDB} mask_rioi build/mask_xioi3.db
+	${XRAY_MERGEDB} mask_rioi_tbytesrc build/mask_xioi3.db
+	${XRAY_MERGEDB} mask_rioi_tbyteterm build/mask_xioi3.db
+endif
 
 .PHONY: database pushdb
-
diff --git a/fuzzers/035-iob-ilogic/generate.tcl b/fuzzers/035-iob-ilogic/generate.tcl
index 18eaf70..48dcb52 100644
--- a/fuzzers/035-iob-ilogic/generate.tcl
+++ b/fuzzers/035-iob-ilogic/generate.tcl
@@ -1,1416 +1,3 @@
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
-# 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
 # Copyright (C) 2017-2020  The Project X-Ray Authors
 #
diff --git a/fuzzers/035a-iob18-idelay/Makefile b/fuzzers/035a-iob18-idelay/Makefile
new file mode 100644
index 0000000..44365e4
--- /dev/null
+++ b/fuzzers/035a-iob18-idelay/Makefile
@@ -0,0 +1,29 @@
+# Copyright (C) 2017-2022  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
+N := 5
+include ../fuzzer.mk
+
+database: build/segbits_riob18.db
+
+build/segbits_riob18.rdb: $(SPECIMENS_OK)
+	${XRAY_SEGMATCH} -m 1 -M 1 -o build/segbits_riob18.rdb $$(find -name segdata_*.txt)
+
+build/segbits_riob18.db: build/segbits_riob18.rdb
+	${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf --seg-fn-in $^ --seg-fn-out $@
+	${XRAY_MASKMERGE} build/mask_riob18.db $$(find -name segdata_*.txt)
+
+pushdb:
+	${XRAY_MERGEDB} rioi build/segbits_riob18.db
+	${XRAY_MERGEDB} rioi_tbytesrc build/segbits_riob18.db
+	${XRAY_MERGEDB} rioi_tbyteterm build/segbits_riob18.db
+	${XRAY_MERGEDB} mask_rioi build/mask_riob18.db
+	${XRAY_MERGEDB} mask_rioi_tbytesrc build/mask_riob18.db
+	${XRAY_MERGEDB} mask_rioi_tbyteterm build/mask_riob18.db
+
+.PHONY: database pushdb
+
diff --git a/fuzzers/035a-iob18-idelay/bits.dbf b/fuzzers/035a-iob18-idelay/bits.dbf
new file mode 100644
index 0000000..059168f
--- /dev/null
+++ b/fuzzers/035a-iob18-idelay/bits.dbf
@@ -0,0 +1,14 @@
+34_08 34_14   ,IOI.IDELAY_Y1.IDELAY_TYPE_FIXED
+35_113 35_119 ,IOI.IDELAY_Y0.IDELAY_TYPE_FIXED
+34_120 34_122
+34_114 34_116
+34_108 34_110
+34_100 34_102
+34_94 34_96
+35_05 35_07
+35_11 35_13
+35_17 35_19
+35_25 35_27
+35_31 35_33
+34_72 35_69
+34_58 35_55
diff --git a/fuzzers/035a-iob18-idelay/generate.py b/fuzzers/035a-iob18-idelay/generate.py
new file mode 100644
index 0000000..5c4bf4d
--- /dev/null
+++ b/fuzzers/035a-iob18-idelay/generate.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017-2022  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
+import json
+
+from prjxray.segmaker import Segmaker, add_site_group_zero
+from prjxray import verilog
+
+
+def bitfilter(frame, word):
+    if frame < 26:
+        return False
+
+    return True
+
+
+def main():
+    segmk = Segmaker("design.bits", verbose=True)
+
+    # Load tags
+    with open("params.json", "r") as fp:
+        data = json.load(fp)
+
+    idelay_types = ["FIXED", "VARIABLE", "VAR_LOAD"]
+    delay_srcs = ["IDATAIN", "DATAIN"]
+
+    # Output tags
+    for params in data:
+        segmk.add_site_tag(params['IDELAY_IN_USE'], 'IN_USE', True)
+        segmk.add_site_tag(params['IDELAY_NOT_IN_USE'], 'IN_USE', False)
+
+        loc = verilog.unquote(params["LOC"])
+
+        # Delay type
+        value = verilog.unquote(params["IDELAY_TYPE"])
+        value = value.replace(
+            "_PIPE", "")  # VAR_LOAD and VAR_LOAD_PIPE are the same
+        add_site_group_zero(
+            segmk, loc, "IDELAY_TYPE_", idelay_types, "FIXED", value)
+
+        # Delay value
+        value = int(params["IDELAY_VALUE"])
+        for i in range(5):
+            segmk.add_site_tag(
+                loc, "IDELAY_VALUE[%01d]" % i, ((value >> i) & 1) != 0)
+            segmk.add_site_tag(
+                loc, "ZIDELAY_VALUE[%01d]" % i, ((value >> i) & 1) == 0)
+
+        # Delay source
+        value = verilog.unquote(params["DELAY_SRC"])
+        for x in delay_srcs:
+            segmk.add_site_tag(loc, "DELAY_SRC_%s" % x, int(value == x))
+
+        value = verilog.unquote(params["CINVCTRL_SEL"])
+        segmk.add_site_tag(loc, "CINVCTRL_SEL", int(value == "TRUE"))
+
+        value = verilog.unquote(params["PIPE_SEL"])
+        segmk.add_site_tag(loc, "PIPE_SEL", int(value == "TRUE"))
+
+        if "IS_C_INVERTED" in params:
+            segmk.add_site_tag(
+                loc, "IS_C_INVERTED", int(params["IS_C_INVERTED"]))
+            segmk.add_site_tag(loc, "ZINV_C", 1 ^ int(params["IS_C_INVERTED"]))
+
+        segmk.add_site_tag(
+            loc, "IS_DATAIN_INVERTED", int(params["IS_DATAIN_INVERTED"]))
+        if params['IBUF_IN_USE']:
+            value = verilog.unquote(params["HIGH_PERFORMANCE_MODE"])
+            segmk.add_site_tag(
+                loc, "HIGH_PERFORMANCE_MODE", int(value == "TRUE"))
+
+            segmk.add_site_tag(
+                loc, "IS_IDATAIN_INVERTED", int(params["IS_IDATAIN_INVERTED"]))
+
+    segmk.compile(bitfilter=bitfilter)
+    segmk.write()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/035a-iob18-idelay/generate.tcl b/fuzzers/035a-iob18-idelay/generate.tcl
new file mode 100644
index 0000000..b2de3dc
--- /dev/null
+++ b/fuzzers/035a-iob18-idelay/generate.tcl
@@ -0,0 +1,31 @@
+# Copyright (C) 2017-2022  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
+create_project -force -part $::env(XRAY_PART) design design
+read_verilog top.v
+synth_design -top top
+
+set_property CFGBVS GND [current_design]
+set_property CONFIG_VOLTAGE 1.8 [current_design]
+set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
+set_param tcl.collectionResultDisplayLimit 0
+
+set_property IS_ENABLED 0 [get_drc_checks {NSTD-1}]
+set_property IS_ENABLED 0 [get_drc_checks {UCIO-1}]
+set_property IS_ENABLED 0 [get_drc_checks {REQP-79}]
+set_property IS_ENABLED 0 [get_drc_checks {REQP-81}]
+set_property IS_ENABLED 0 [get_drc_checks {REQP-84}]
+set_property IS_ENABLED 0 [get_drc_checks {REQP-85}]
+set_property IS_ENABLED 0 [get_drc_checks {REQP-87}]
+set_property IS_ENABLED 0 [get_drc_checks {REQP-85}]
+set_property IS_ENABLED 0 [get_drc_checks {AVAL-28}]
+
+place_design
+route_design
+
+write_checkpoint -force design.dcp
+write_bitstream -force design.bit
diff --git a/fuzzers/035a-iob18-idelay/top.py b/fuzzers/035a-iob18-idelay/top.py
new file mode 100644
index 0000000..3c48130
--- /dev/null
+++ b/fuzzers/035a-iob18-idelay/top.py
@@ -0,0 +1,237 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017-2022  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
+
+import os, random
+random.seed(int(os.getenv("SEED"), 16))
+
+import re
+import json
+
+from prjxray import util
+from prjxray.db import Database
+
+# =============================================================================
+
+
+def gen_sites():
+    db = Database(util.get_db_root(), util.get_part())
+    grid = db.grid()
+
+    tile_list = []
+    for tile_name in sorted(grid.tiles()):
+        if "IOB18" not in tile_name or "SING" in tile_name:
+            continue
+        tile_list.append(tile_name)
+
+    get_xy = util.create_xy_fun('RIOB18_')
+    tile_list.sort(key=get_xy)
+
+    for iob_tile_name in tile_list:
+        iob_gridinfo = grid.gridinfo_at_loc(
+            grid.loc_of_tilename(iob_tile_name))
+
+        # Find IOI tile adjacent to IOB
+        for suffix in ["IOI", "IOI_TBYTESRC", "IOI_TBYTETERM"]:
+            try:
+                ioi_tile_name = iob_tile_name.replace("IOB18", suffix)
+                ioi_gridinfo = grid.gridinfo_at_loc(
+                    grid.loc_of_tilename(ioi_tile_name))
+                break
+            except KeyError:
+                pass
+
+        iob18s = [k for k, v in iob_gridinfo.sites.items() if v == "IOB18S"][0]
+        iob18m = [k for k, v in iob_gridinfo.sites.items() if v == "IOB18M"][0]
+        idelay_s = iob18s.replace("IOB", "IDELAY")
+        idelay_m = iob18m.replace("IOB", "IDELAY")
+
+        yield iob18m, idelay_m, iob18s, idelay_s
+
+
+def run():
+
+    # Get all [LR]IOI3 tiles
+    tiles = list(gen_sites())
+
+    # Header
+    print("// Tile count: %d" % len(tiles))
+    print("// Seed: '%s'" % os.getenv("SEED"))
+
+    ninputs = 0
+    di_idx = []
+    for i, sites in enumerate(tiles):
+        if random.randint(0, 1):
+            di_idx.append(ninputs)
+            ninputs += 1
+        else:
+            di_idx.append(None)
+
+    print(
+        '''
+module top (
+  (* CLOCK_BUFFER_TYPE = "NONE" *)
+  input  wire clk,
+  input  wire [{N}:0] di
+);
+
+wire clk_buf = clk;
+
+wire [{N}:0] di_buf;
+    '''.format(N=ninputs - 1))
+
+    # LOCes IOBs
+    data = []
+    for i, (sites, ibuf_idx) in enumerate(zip(tiles, di_idx)):
+
+        if random.randint(0, 1):
+            iob_i = sites[0]
+            iob_o = sites[2]
+            idelay = sites[1]
+            other_idelay = sites[3]
+        else:
+            iob_i = sites[2]
+            iob_o = sites[0]
+            idelay = sites[3]
+            other_idelay = sites[1]
+
+        use_ibuf = ibuf_idx is not None
+
+        DELAY_SRC = random.choice(["IDATAIN", "DATAIN"])
+        if not use_ibuf:
+            DELAY_SRC = 'DATAIN'
+
+        params = {
+            "LOC":
+            "\"" + idelay + "\"",
+            "IDELAY_TYPE":
+            "\"" + random.choice(
+                ["FIXED", "VARIABLE", "VAR_LOAD", "VAR_LOAD_PIPE"]) + "\"",
+            "IDELAY_VALUE":
+            random.randint(0, 31),
+            "DELAY_SRC":
+            "\"" + DELAY_SRC + "\"",
+            "HIGH_PERFORMANCE_MODE":
+            "\"" + random.choice(["TRUE", "FALSE"]) + "\"",
+            "CINVCTRL_SEL":
+            "\"" + random.choice(["TRUE", "FALSE"]) + "\"",
+            "PIPE_SEL":
+            "\"" + random.choice(["TRUE", "FALSE"]) + "\"",
+            "IS_C_INVERTED":
+            random.randint(0, 1),
+            "IS_DATAIN_INVERTED":
+            random.randint(0, 1),
+            "IS_IDATAIN_INVERTED":
+            random.randint(0, 1),
+        }
+
+        if params["IDELAY_TYPE"] != "\"VAR_LOAD_PIPE\"":
+            params["PIPE_SEL"] = "\"FALSE\""
+
+        # The datasheet says that for these two modes the delay is set to 0
+        if params["IDELAY_TYPE"] == "\"VAR_LOAD\"":
+            params["IDELAY_VALUE"] = 0
+        if params["IDELAY_TYPE"] == "\"VAR_LOAD_PIPE\"":
+            params["IDELAY_VALUE"] = 0
+
+        if params["IDELAY_TYPE"] == "\"FIXED\"":
+            params["IS_C_INVERTED"] = 0
+
+        param_str = ",".join(".%s(%s)" % (k, v) for k, v in params.items())
+
+        if use_ibuf:
+            print('')
+            print('(* LOC="%s", KEEP, DONT_TOUCH *)' % iob_i)
+            print(
+                'IBUF ibuf_%03d (.I(di[%3d]), .O(di_buf[%3d]));' %
+                (ibuf_idx, ibuf_idx, ibuf_idx))
+            print(
+                'mod #(%s) mod_%03d (.clk(clk_buf), .I(di_buf[%3d]));' %
+                (param_str, i, ibuf_idx))
+        else:
+            print('mod #(%s) mod_%03d (.clk(clk_buf), .I());' % (param_str, i))
+
+        params['IBUF_IN_USE'] = use_ibuf
+        params["IDELAY_IN_USE"] = idelay
+        params["IDELAY_NOT_IN_USE"] = other_idelay
+
+        data.append(params)
+
+    # Store params
+    with open("params.json", "w") as fp:
+        json.dump(data, fp, sort_keys=True, indent=1)
+
+    print(
+        '''
+// IDELAYCTRL
+(* KEEP, DONT_TOUCH *)
+IDELAYCTRL idelayctrl();
+
+endmodule
+
+(* KEEP, DONT_TOUCH *)
+module mod(
+  input  wire clk,
+  input  wire I
+);
+
+parameter LOC = "";
+parameter IDELAY_TYPE = "FIXED";
+parameter IDELAY_VALUE = 0;
+parameter DELAY_SRC = "IDATAIN";
+parameter HIGH_PERFORMANCE_MODE = "TRUE";
+parameter SIGNAL_PATTERN = "DATA";
+parameter CINVCTRL_SEL = "FALSE";
+parameter PIPE_SEL = "FALSE";
+parameter IS_C_INVERTED = 0;
+parameter IS_DATAIN_INVERTED = 0;
+parameter IS_IDATAIN_INVERTED = 0;
+
+wire x;
+wire lut;
+
+(* KEEP, DONT_TOUCH *)
+LUT2 l( .O(lut) );
+
+// IDELAY
+(* LOC=LOC, KEEP, DONT_TOUCH *)
+IDELAYE2 #(
+  .IDELAY_TYPE(IDELAY_TYPE),
+  .IDELAY_VALUE(IDELAY_VALUE),
+  .DELAY_SRC(DELAY_SRC),
+  .HIGH_PERFORMANCE_MODE(HIGH_PERFORMANCE_MODE),
+  .SIGNAL_PATTERN(SIGNAL_PATTERN),
+  .CINVCTRL_SEL(CINVCTRL_SEL),
+  .PIPE_SEL(PIPE_SEL),
+  .IS_C_INVERTED(IS_C_INVERTED),
+  .IS_DATAIN_INVERTED(IS_DATAIN_INVERTED),
+  .IS_IDATAIN_INVERTED(IS_IDATAIN_INVERTED)
+)
+idelay
+(
+  .C(clk),
+  .REGRST(),
+  .LD(),
+  .CE(),
+  .INC(),
+  .CINVCTRL(),
+  .CNTVALUEIN(),
+  .IDATAIN(I),
+  .DATAIN(lut),
+  .LDPIPEEN(),
+  .DATAOUT(x),
+  .CNTVALUEOUT()
+);
+
+endmodule
+    ''')
+
+
+run()
diff --git a/fuzzers/035b-iob-iserdes/Makefile b/fuzzers/035b-iob-iserdes/Makefile
index 446482e..a803305 100644
--- a/fuzzers/035b-iob-iserdes/Makefile
+++ b/fuzzers/035b-iob-iserdes/Makefile
@@ -22,7 +22,12 @@
 	${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf --groups tag_groups.txt --seg-fn-in $^ --seg-fn-out $@
 	${XRAY_MASKMERGE} build/mask_xioi3.db $$(find -name segdata_*.txt)
 
-pushdb:
+# running the fuzzers for RIOI tiles yields mostly identical
+# results, so reuse the results of IOI3 tiles
+build/segbits_rioi.db: build/segbits_xioi3.db
+	sed 's/IOI3/IOI/g' < $< > $@
+
+pushdb: build/segbits_rioi.db
 	${XRAY_MERGEDB} lioi3 build/segbits_xioi3.db
 	${XRAY_MERGEDB} lioi3_tbytesrc build/segbits_xioi3.db
 	${XRAY_MERGEDB} lioi3_tbyteterm build/segbits_xioi3.db
@@ -35,6 +40,14 @@
 	${XRAY_MERGEDB} mask_rioi3 build/mask_xioi3.db
 	${XRAY_MERGEDB} mask_rioi3_tbytesrc build/mask_xioi3.db
 	${XRAY_MERGEDB} mask_rioi3_tbyteterm build/mask_xioi3.db
+ifeq ($(XRAY_DATABASE),kintex7)
+	${XRAY_MERGEDB} rioi build/segbits_rioi.db
+	${XRAY_MERGEDB} rioi_tbytesrc build/segbits_rioi.db
+	${XRAY_MERGEDB} rioi_tbyteterm build/segbits_rioi.db
+	${XRAY_MERGEDB} mask_rioi build/mask_xioi3.db
+	${XRAY_MERGEDB} mask_rioi_tbytesrc build/mask_xioi3.db
+	${XRAY_MERGEDB} mask_rioi_tbyteterm build/mask_xioi3.db
+endif
 
 .PHONY: database pushdb
 
diff --git a/fuzzers/036-iob18-ologic/Makefile b/fuzzers/036-iob18-ologic/Makefile
new file mode 100644
index 0000000..8c10625
--- /dev/null
+++ b/fuzzers/036-iob18-ologic/Makefile
@@ -0,0 +1,29 @@
+# Copyright (C) 2017-2022  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
+N := 40
+include ../fuzzer.mk
+
+database: build/segbits_rioi.db
+
+build/segbits_rioi.rdb: $(SPECIMENS_OK)
+	${XRAY_SEGMATCH} -c 7 -o build/segbits_rioi.rdb $$(find -name segdata_*)
+
+build/segbits_rioi.db: build/segbits_rioi.rdb
+	${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf --seg-fn-in $^ --seg-fn-out $@
+	${XRAY_MASKMERGE} build/mask_rioi.db $$(find -name segdata_*)
+
+pushdb:
+	${XRAY_MERGEDB} rioi build/segbits_rioi.db
+	${XRAY_MERGEDB} rioi_tbytesrc build/segbits_rioi.db
+	${XRAY_MERGEDB} rioi_tbyteterm build/segbits_rioi.db
+	${XRAY_MERGEDB} mask_rioi build/mask_rioi.db
+	${XRAY_MERGEDB} mask_rioi_tbytesrc build/mask_rioi.db
+	${XRAY_MERGEDB} mask_rioi_tbyteterm build/mask_rioi.db
+
+.PHONY: database pushdb
+
diff --git a/fuzzers/036-iob18-ologic/bits.dbf b/fuzzers/036-iob18-ologic/bits.dbf
new file mode 100644
index 0000000..8e902b0
--- /dev/null
+++ b/fuzzers/036-iob18-ologic/bits.dbf
@@ -0,0 +1,6 @@
+33_91 33_93
+32_36 32_34
+33_61 32_58 33_57
+32_70 32_66 33_69
+30_95 30_99 30_127 31_126 31_124 30_121 31_120 30_123 31_116 31_100
+31_00 30_01 30_03 31_06 30_07 31_04 30_11 31_28 31_32 30_29 30_27
diff --git a/fuzzers/036-iob18-ologic/generate.py b/fuzzers/036-iob18-ologic/generate.py
new file mode 100644
index 0000000..5590af5
--- /dev/null
+++ b/fuzzers/036-iob18-ologic/generate.py
@@ -0,0 +1,189 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017-2022  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
+
+from prjxray.segmaker import Segmaker
+from prjxray import verilog
+import json
+
+# Set to true to enable additional tags useful for tracing bit toggles.
+DEBUG_FUZZER = False
+
+
+def bitfilter(frame, word):
+    return 30 <= frame and frame <= 33
+
+def handle_data_width(segmk, d):
+    if 'DATA_WIDTH' not in d:
+        return
+
+    site = d['ologic_loc']
+
+    data_rate = verilog.unquote(d['DATA_RATE_OQ'])
+    segmk.add_site_tag(
+        site, 'OSERDES.DATA_WIDTH.{}.W{}'.format(data_rate, d['DATA_WIDTH']),
+        1)
+
+
+def no_oserdes(segmk, site):
+    for mode in ['SDR', 'DDR']:
+        if mode == 'SDR':
+            widths = [2, 3, 4, 5, 6, 7, 8]
+        else:
+            assert mode == 'DDR'
+            widths = [4, 6, 8]
+
+        for opt in widths:
+            segmk.add_site_tag(
+                site, 'OSERDES.DATA_WIDTH.{}.W{}'.format(mode, opt), 0)
+
+
+def main():
+    print("Loading tags")
+    segmk = Segmaker("design.bits")
+
+    with open('params.jl', 'r') as f:
+        design = json.load(f)
+
+        for d in design:
+            site = d['ologic_loc']
+
+            handle_data_width(segmk, d)
+
+            segmk.add_site_tag(site, 'OSERDES.IN_USE', d['use_oserdese2'])
+
+            if d['use_oserdese2']:
+                segmk.add_site_tag(site, 'OQUSED', 1)
+
+                for opt in ['SDR', 'DDR']:
+                    segmk.add_site_tag(
+                        site, 'OSERDES.DATA_RATE_OQ.{}'.format(opt),
+                        verilog.unquote(d['DATA_RATE_OQ']) == opt)
+
+                data_rate_tq = verilog.unquote(d['DATA_RATE_TQ'])
+                segmk.add_site_tag(
+                    site, 'OSERDES.DATA_RATE_TQ.{}'.format(data_rate_tq), 1)
+                for opt in ['BUF', 'SDR', 'DDR']:
+                    segmk.add_site_tag(
+                        site, 'OSERDES.DATA_RATE_TQ.{}'.format(opt),
+                        opt == data_rate_tq)
+
+                for opt in ['SRVAL_OQ', 'SRVAL_TQ', 'INIT_OQ', 'INIT_TQ']:
+                    segmk.add_site_tag(site, opt, d[opt])
+                    segmk.add_site_tag(site, 'Z' + opt, 1 ^ d[opt])
+
+                for opt in ['CLK', 'CLKDIV']:
+                    if d['{}_USED'.format(opt)]:
+                        k = 'IS_{}_INVERTED'.format(opt)
+                        segmk.add_site_tag(site, k, d[k])
+                        segmk.add_site_tag(
+                            site, 'ZINV_{}'.format(opt), 1 ^ d[k])
+
+                if d['io']:
+                    for idx in range(4):
+                        k = 'IS_T{}_INVERTED'.format(idx + 1)
+                        segmk.add_site_tag(site, k, d[k])
+                        segmk.add_site_tag(
+                            site, 'ZINV_T{}'.format(idx + 1), 1 ^ d[k])
+
+                for idx in range(8):
+                    k = 'IS_D{}_INVERTED'.format(idx + 1)
+                    segmk.add_site_tag(site, k, d[k])
+                    segmk.add_site_tag(
+                        site, 'ZINV_D{}'.format(idx + 1), 1 ^ d[k])
+
+                for tristate_width in [1, 4]:
+                    segmk.add_site_tag(
+                        site,
+                        'OSERDES.TRISTATE_WIDTH.W{}'.format(tristate_width),
+                        d['TRISTATE_WIDTH'] == tristate_width)
+
+                for opt in ['MASTER', 'SLAVE']:
+                    segmk.add_site_tag(
+                        site, 'OSERDES.SERDES_MODE.{}'.format(opt),
+                        opt == verilog.unquote(d['OSERDES_MODE']))
+
+            if 'o_sr_used' in d:
+                if d['o_sr_used'] in ['S', 'R']:
+                    segmk.add_site_tag(site, 'ODDR.SRUSED', 1)
+                    segmk.add_site_tag(site, 'ODDR.ZSRUSED', 0)
+                else:
+                    assert d['o_sr_used'] == 'None'
+                    segmk.add_site_tag(site, 'ODDR.SRUSED', 0)
+                    segmk.add_site_tag(site, 'ODDR.ZSRUSED', 1)
+
+            if 't_sr_used' in d:
+                if d['t_sr_used'] in ['S', 'R']:
+                    segmk.add_site_tag(site, 'TDDR.SRUSED', 1)
+                    segmk.add_site_tag(site, 'TDDR.ZSRUSED', 0)
+                else:
+                    assert d['t_sr_used'] == 'None'
+                    segmk.add_site_tag(site, 'TDDR.SRUSED', 0)
+                    segmk.add_site_tag(site, 'TDDR.ZSRUSED', 1)
+
+            if d['oddr_mux_config'] == 'direct':
+                segmk.add_site_tag(site, 'ODDR_TDDR.IN_USE', 1)
+
+            if d['tddr_mux_config'] == 'direct':
+                segmk.add_site_tag(site, 'ODDR_TDDR.IN_USE', 1)
+
+            if d['oddr_mux_config'] == 'direct' and d[
+                    'tddr_mux_config'] == 'direct':
+                segmk.add_site_tag(site, 'ZINV_CLK', 1 ^ d['IS_CLK_INVERTED'])
+
+                if d['IS_CLK_INVERTED'] == 0:
+                    for opt in ['OPPOSITE_EDGE', 'SAME_EDGE']:
+                        segmk.add_site_tag(
+                            site, 'ODDR.DDR_CLK_EDGE.{}'.format(opt),
+                            verilog.unquote(d['ODDR_CLK_EDGE']) == opt)
+
+                segmk.add_site_tag(
+                    site, 'TDDR.DDR_CLK_EDGE.INV',
+                    d['ODDR_CLK_EDGE'] != d['TDDR_CLK_EDGE'])
+                segmk.add_site_tag(
+                    site, 'TDDR.DDR_CLK_EDGE.ZINV',
+                    d['ODDR_CLK_EDGE'] == d['TDDR_CLK_EDGE'])
+
+                if 'SRTYPE' in d:
+                    for opt in ['ASYNC', 'SYNC']:
+                        segmk.add_site_tag(
+                            site, 'OSERDES.SRTYPE.{}'.format(opt),
+                            verilog.unquote(d['SRTYPE']) == opt)
+
+                for opt in ['ASYNC', 'SYNC']:
+                    segmk.add_site_tag(
+                        site, 'OSERDES.TSRTYPE.{}'.format(opt),
+                        verilog.unquote(d['TSRTYPE']) == opt)
+
+            if not d['use_oserdese2']:
+                no_oserdes(segmk, site)
+                if d['oddr_mux_config'] == 'lut':
+                    segmk.add_site_tag(site, 'ODDR_TDDR.IN_USE', 0)
+                    segmk.add_site_tag(site, 'OMUX.D1', 1)
+                    segmk.add_site_tag(site, 'OQUSED', 1)
+                elif d['oddr_mux_config'] == 'direct':
+                    segmk.add_site_tag(site, 'OMUX.D1', 0)
+                elif d['oddr_mux_config'] == 'none' and not d['io']:
+                    segmk.add_site_tag(site, 'OQUSED', 0)
+
+            segmk.add_site_tag(site, 'TQUSED', d['io'])
+
+            if DEBUG_FUZZER:
+                for k in d:
+                    segmk.add_site_tag(
+                        site, 'param_' + k + '_' + str(d[k]).replace(
+                            ' ', '').replace('\n', ''), 1)
+
+    segmk.compile(bitfilter=bitfilter)
+    segmk.write(allow_empty=True)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/036-iob18-ologic/generate.tcl b/fuzzers/036-iob18-ologic/generate.tcl
new file mode 100644
index 0000000..413215b
--- /dev/null
+++ b/fuzzers/036-iob18-ologic/generate.tcl
@@ -0,0 +1,107 @@
+# Copyright (C) 2017-2022  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
+source "$::env(XRAY_DIR)/utils/utils.tcl"
+
+proc make_io_pin_sites {} {
+    # get all possible IOB pins
+    foreach pad [get_package_pins -filter "IS_GENERAL_PURPOSE == 1"] {
+        set site [get_sites -of_objects $pad]
+        if {[llength $site] == 0} {
+            continue
+        }
+        if [string match IOB18* [get_property SITE_TYPE $site]] {
+            dict append io_pin_sites $site $pad
+        }
+    }
+    return $io_pin_sites
+}
+
+proc load_pin_lines {} {
+    # IOB_X0Y103 clk input
+    # IOB_X0Y129 do[0] output
+
+    set fp [open "params.csv" r]
+    gets $fp line
+
+    set pin_lines {}
+    for {gets $fp line} {$line != ""} {gets $fp line} {
+        lappend pin_lines [split $line ","]
+    }
+    close $fp
+    return $pin_lines
+}
+
+proc loc_pins {} {
+    set pin_lines [load_pin_lines]
+    set io_pin_sites [make_io_pin_sites]
+
+    puts "Looping"
+    for {set idx 0} {$idx < [llength $pin_lines]} {incr idx} {
+        set line [lindex $pin_lines $idx]
+        puts "$line"
+
+        set site_str [lindex $line 1]
+        set pin_str [lindex $line 2]
+        set iostandard [lindex $line 3]
+        set drive [lindex $line 4]
+        set slew [lindex $line 5]
+        set pulltype [lindex $line 6]
+
+        # Have: site
+        # Want: pin for site
+
+        set site [get_sites $site_str]
+        set pad_bel [get_bels -of_objects $site -filter {TYPE =~ PAD && NAME =~ IOB_*}]
+        # set port [get_ports -of_objects $site]
+        set port [get_ports $pin_str]
+        set tile [get_tiles -of_objects $site]
+
+        set pin [dict get $io_pin_sites $site]
+
+        set props {}
+        #lappend props PACKAGE_PIN $pin
+        lappend props IOSTANDARD $iostandard
+        lappend props PULLTYPE $pulltype
+
+        if {$drive != "None"} {
+            lappend props DRIVE $drive
+        }
+
+        if {$slew != "None"} {
+            lappend props SLEW $slew
+        }
+
+        puts $props
+
+        set_property -dict "$props" $port
+    }
+}
+
+proc run {} {
+    create_project -force -part $::env(XRAY_PART) design design
+    read_verilog top.v
+    synth_design -top top
+
+    loc_pins
+
+    set_property CFGBVS GND [current_design]
+    set_property CONFIG_VOLTAGE 1.8 [current_design]
+    set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-79}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-144}]
+
+    write_checkpoint -force design_pre_place.dcp
+
+    place_design
+    route_design
+
+    write_checkpoint -force design.dcp
+    write_bitstream -force design.bit
+}
+
+run
diff --git a/fuzzers/036-iob18-ologic/top.py b/fuzzers/036-iob18-ologic/top.py
new file mode 100644
index 0000000..4491956
--- /dev/null
+++ b/fuzzers/036-iob18-ologic/top.py
@@ -0,0 +1,474 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017-2022  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
+import json
+import io
+import os
+import random
+random.seed(int(os.getenv("SEED"), 16))
+from prjxray import util
+from prjxray import lut_maker
+from prjxray import verilog
+from prjxray.db import Database
+
+
+def gen_sites():
+    '''
+    IOB18S: main IOB of a diff pair
+    IOB18M: secondary IOB of a diff pair
+    IOB18: not a diff pair. Relatively rare (at least in ROI...2 of them?)
+    Focus on IOB18S to start
+    '''
+    db = Database(util.get_db_root(), util.get_part())
+    grid = db.grid()
+    for tile_name in sorted(grid.tiles()):
+        loc = grid.loc_of_tilename(tile_name)
+        gridinfo = grid.gridinfo_at_loc(loc)
+
+        for site_name, site_type in gridinfo.sites.items():
+            if site_type in ['IOB18S', 'IOB18M']:
+                yield tile_name, site_name
+
+
+def write_params(params):
+    pinstr = 'tile,site,pin,iostandard,drive,slew\n'
+    for vals in params:
+        pinstr += ','.join(map(str, vals)) + '\n'
+
+    open('params.csv', 'w').write(pinstr)
+
+
+def use_oserdese2(p, luts, connects):
+
+    p['oddr_mux_config'] = 'none'
+    p['tddr_mux_config'] = 'none'
+
+    p['DATA_RATE_OQ'] = verilog.quote(random.choice((
+        'SDR',
+        'DDR',
+    )))
+
+    p['DATA_RATE_TQ'] = verilog.quote(random.choice((
+        'BUF',
+        'SDR',
+        'DDR',
+    )))
+
+    if verilog.unquote(p['DATA_RATE_OQ']) == 'SDR':
+        data_widths = [2, 3, 4, 5, 6, 7, 8]
+    else:
+        data_widths = [4, 6, 8]
+
+    p['DATA_WIDTH'] = random.choice(data_widths)
+
+    if p['DATA_WIDTH'] == 4 and verilog.unquote(
+            p['DATA_RATE_OQ']) == 'DDR' and verilog.unquote(
+                p['DATA_RATE_TQ']) == 'DDR':
+        tristate_width = 4
+    else:
+        tristate_width = 1
+    p['SERDES_MODE'] = verilog.quote(random.choice(('MASTER', 'SLAVE')))
+
+    p['TRISTATE_WIDTH'] = tristate_width
+    p['OSERDES_MODE'] = verilog.quote(random.choice(('MASTER', 'SLAVE')))
+
+    if p['io']:
+        p['TFB'] = '.TFB(tfb_{site}),'.format(**p)
+        p['TQ'] = '.TQ({twire}),'.format(**p)
+        p['t1net'] = luts.get_next_output_net()
+        p['t2net'] = luts.get_next_output_net()
+        p['t3net'] = luts.get_next_output_net()
+        p['t4net'] = luts.get_next_output_net()
+        p['tcenet'] = luts.get_next_output_net()
+
+        for idx in range(4):
+            p['IS_T{}_INVERTED'.format(idx + 1)] = random.randint(0, 1)
+    else:
+        p['TFB'] = '.TFB(),'
+        p['TQ'] = '.TQ(),'
+        p['t1net'] = ''
+        p['t2net'] = ''
+        p['t3net'] = ''
+        p['t4net'] = ''
+        p['tcenet'] = ''
+
+        for idx in range(4):
+            p['IS_T{}_INVERTED'.format(idx + 1)] = 0
+
+    p['SRVAL_OQ'] = random.randint(0, 1)
+    p['SRVAL_TQ'] = random.randint(0, 1)
+    p['INIT_OQ'] = random.randint(0, 1)
+    p['INIT_TQ'] = random.randint(0, 1)
+
+    for idx in range(8):
+        p['IS_D{}_INVERTED'.format(idx + 1)] = random.randint(0, 1)
+
+    p['IS_CLK_INVERTED'] = random.randint(0, 1)
+    p['IS_CLKDIV_INVERTED'] = random.randint(0, 1)
+
+    clk_connections = ''
+    p['CLK_USED'] = random.randint(0, 1)
+    p['CLKDIV_USED'] = random.randint(0, 1)
+    if p['CLK_USED']:
+        clk_connections += '''
+        .CLK({}),'''.format(luts.get_next_output_net())
+    if p['CLKDIV_USED']:
+        clk_connections += '''
+        .CLKDIV({}),'''.format(luts.get_next_output_net())
+
+    print(
+        '''
+    (* KEEP, DONT_TOUCH, LOC = "{ologic_loc}" *)
+    OSERDESE2 #(
+        .SERDES_MODE({OSERDES_MODE}),
+        .DATA_RATE_TQ({DATA_RATE_TQ}),
+        .DATA_RATE_OQ({DATA_RATE_OQ}),
+        .DATA_WIDTH({DATA_WIDTH}),
+        .TRISTATE_WIDTH({TRISTATE_WIDTH}),
+        .SRVAL_OQ({SRVAL_OQ}),
+        .SRVAL_TQ({SRVAL_TQ}),
+        .INIT_OQ({INIT_OQ}),
+        .INIT_TQ({INIT_TQ}),
+        .IS_T1_INVERTED({IS_T1_INVERTED}),
+        .IS_T2_INVERTED({IS_T2_INVERTED}),
+        .IS_T3_INVERTED({IS_T3_INVERTED}),
+        .IS_T4_INVERTED({IS_T4_INVERTED}),
+        .IS_D1_INVERTED({IS_D1_INVERTED}),
+        .IS_D2_INVERTED({IS_D2_INVERTED}),
+        .IS_D3_INVERTED({IS_D3_INVERTED}),
+        .IS_D4_INVERTED({IS_D4_INVERTED}),
+        .IS_D5_INVERTED({IS_D5_INVERTED}),
+        .IS_D6_INVERTED({IS_D6_INVERTED}),
+        .IS_D7_INVERTED({IS_D7_INVERTED}),
+        .IS_D8_INVERTED({IS_D8_INVERTED}),
+        .IS_CLK_INVERTED({IS_CLK_INVERTED}),
+        .IS_CLKDIV_INVERTED({IS_CLKDIV_INVERTED})
+    ) oserdese2_{site} (
+        .OQ({owire}),
+        {TFB}
+        {TQ}
+        {clk_connections}
+        .D1({d1net}),
+        .D2({d2net}),
+        .D3({d3net}),
+        .D4({d4net}),
+        .D5({d5net}),
+        .D6({d6net}),
+        .D7({d7net}),
+        .D8({d8net}),
+        .OCE({ocenet}),
+        .RST({rstnet}),
+        .T1({t1net}),
+        .T2({t2net}),
+        .T3({t3net}),
+        .T4({t4net}),
+        .TCE({tcenet})
+        );'''.format(
+            clk_connections=clk_connections,
+            rstnet=luts.get_next_output_net(),
+            d1net=luts.get_next_output_net(),
+            d2net=luts.get_next_output_net(),
+            d3net=luts.get_next_output_net(),
+            d4net=luts.get_next_output_net(),
+            d5net=luts.get_next_output_net(),
+            d6net=luts.get_next_output_net(),
+            d7net=luts.get_next_output_net(),
+            d8net=luts.get_next_output_net(),
+            ocenet=luts.get_next_output_net(),
+            ofb_wire=luts.get_next_input_net(),
+            **p),
+        file=connects)
+
+
+def use_direct_and_oddr(p, luts, connects):
+    p['oddr_mux_config'] = random.choice((
+        'direct',
+        'lut',
+        'none',
+    ))
+
+    if p['io']:
+        if p['oddr_mux_config'] != 'lut':
+            p['tddr_mux_config'] = random.choice((
+                'direct',
+                'lut',
+                'none',
+            ))
+        else:
+            p['tddr_mux_config'] = random.choice((
+                'lut',
+                'none',
+            ))
+    else:
+        p['tddr_mux_config'] = 'none'
+
+    # toddr and oddr share the same clk
+    if random.randint(0, 1):
+        clknet = luts.get_next_output_net()
+        p['IS_CLK_INVERTED'] = 0
+    else:
+        clknet = 'bufg_o'
+        p['IS_CLK_INVERTED'] = random.randint(0, 1)
+
+    if p['tddr_mux_config'] == 'direct':
+        p['TINIT'] = random.randint(0, 1)
+        p['TSRTYPE'] = verilog.quote(random.choice(('SYNC', 'ASYNC')))
+        p['TDDR_CLK_EDGE'] = verilog.quote('OPPOSITE_EDGE')
+
+        # Note: it seems that CLK_EDGE setting is ignored for TDDR
+        p['TDDR_CLK_EDGE'] = verilog.quote(
+            random.choice(('OPPOSITE_EDGE', 'SAME_EDGE')))
+
+        p['t_sr_used'] = random.choice(('None', 'S', 'R'))
+        if p['t_sr_used'] == 'None':
+            p['t_srnet'] = ''
+        elif p['t_sr_used'] == 'S':
+            p['srnet'] = luts.get_next_output_net()
+            p['t_srnet'] = '.S({}),\n'.format(p['srnet'])
+        elif p['t_sr_used'] == 'R':
+            p['srnet'] = luts.get_next_output_net()
+            p['t_srnet'] = '.R({}),\n'.format(p['srnet'])
+
+        print(
+            '''
+    (* KEEP, DONT_TOUCH, LOC = "{ologic_loc}" *)
+    ODDR #(
+        .INIT({TINIT}),
+        .SRTYPE({TSRTYPE}),
+        .DDR_CLK_EDGE({TDDR_CLK_EDGE}),
+        .IS_C_INVERTED({IS_CLK_INVERTED})
+    ) toddr_{site} (
+        .C({cnet}),
+        .D1({d1net}),
+        .D2({d2net}),
+        .CE({cenet}),
+        {t_srnet}
+        .Q(tddr_d_{site})
+        );
+        '''.format(
+                cnet=clknet,
+                d1net=luts.get_next_output_net(),
+                d2net=luts.get_next_output_net(),
+                cenet=luts.get_next_output_net(),
+                **p),
+            file=connects)
+
+    if p['tddr_mux_config'] == 'direct':
+        print(
+            '''
+    assign {twire} = tddr_d_{site};'''.format(**p, ),
+            file=connects)
+    elif p['tddr_mux_config'] == 'lut':
+        print(
+            '''
+    assign {twire} = {lut};'''.format(lut=luts.get_next_output_net(), **p),
+            file=connects)
+        pass
+    elif p['tddr_mux_config'] == 'none':
+        pass
+    else:
+        assert False, p['tddr_mux_config']
+
+    if p['oddr_mux_config'] == 'direct':
+        p['QINIT'] = random.randint(0, 1)
+        p['SRTYPE'] = verilog.quote(random.choice(('SYNC', 'ASYNC')))
+        p['ODDR_CLK_EDGE'] = verilog.quote(
+            random.choice((
+                'OPPOSITE_EDGE',
+                'SAME_EDGE',
+            )))
+
+        p['o_sr_used'] = random.choice(('None', 'S', 'R'))
+        if p['o_sr_used'] == 'None':
+            p['o_srnet'] = ''
+        elif p['o_sr_used'] == 'S':
+            if 'srnet' not in p:
+                p['srnet'] = luts.get_next_output_net()
+            p['o_srnet'] = '.S({}),\n'.format(p['srnet'])
+        elif p['o_sr_used'] == 'R':
+            if 'srnet' not in p:
+                p['srnet'] = luts.get_next_output_net()
+            p['o_srnet'] = '.R({}),\n'.format(p['srnet'])
+
+        print(
+            '''
+    (* KEEP, DONT_TOUCH, LOC = "{ologic_loc}" *)
+    ODDR #(
+        .INIT({QINIT}),
+        .SRTYPE({SRTYPE}),
+        .DDR_CLK_EDGE({ODDR_CLK_EDGE}),
+        .IS_C_INVERTED({IS_CLK_INVERTED})
+    ) oddr_{site} (
+        .C({cnet}),
+        .D1({d1net}),
+        .D2({d2net}),
+        .CE({cenet}),
+        {o_srnet}
+        .Q(oddr_d_{site})
+        );
+        '''.format(
+                cnet=clknet,
+                d1net=luts.get_next_output_net(),
+                d2net=luts.get_next_output_net(),
+                cenet=luts.get_next_output_net(),
+                **p),
+            file=connects)
+
+    if p['oddr_mux_config'] == 'direct':
+        print(
+            '''
+    assign {owire} = oddr_d_{site};'''.format(**p, ),
+            file=connects)
+    elif p['oddr_mux_config'] == 'lut':
+        print(
+            '''
+    assign {owire} = {lut};'''.format(lut=luts.get_next_output_net(), **p),
+            file=connects)
+        pass
+    elif p['oddr_mux_config'] == 'none':
+        pass
+    else:
+        assert False, p['oddr_mux_config']
+
+
+def run():
+    iostandards = [
+        'LVCMOS12', 'LVCMOS15', 'LVCMOS18'
+    ]
+    iostandard = random.choice(iostandards)
+
+    if iostandard in ['LVCMOS12']:
+        drives = [2, 4, 6, 8]
+    elif iostandard in ['LVCMOS15', 'LVCMOS18']:
+        drives = [2, 4, 6, 8, 12, 16]
+    else:
+        assert False, "This should be unreachable"
+
+    slews = ['FAST', 'SLOW']
+    pulls = ["NONE", "KEEPER", "PULLDOWN", "PULLUP"]
+
+    luts = lut_maker.LutMaker()
+
+    connects = io.StringIO()
+
+    tile_params = []
+    params = []
+
+    ndio = 0
+    ndo = 0
+    for idx, (tile, site) in enumerate(gen_sites()):
+        if idx == 0:
+            continue
+
+        p = {}
+        p['tile'] = tile
+        p['site'] = site
+        p['ilogic_loc'] = site.replace('IOB', 'ILOGIC')
+        p['ologic_loc'] = site.replace('IOB', 'OLOGIC')
+        p['IOSTANDARD'] = verilog.quote(iostandard)
+        p['PULLTYPE'] = verilog.quote(random.choice(pulls))
+        p['DRIVE'] = random.choice(drives)
+        p['SLEW'] = verilog.quote(random.choice(slews))
+
+        p['io'] = random.randint(0, 1)
+        p['owire'] = 'do_buf[{}]'.format(idx - 1)
+
+        if p['io']:
+            p['pad_wire'] = 'dio[{}]'.format(ndio)
+            ndio += 1
+
+            p['iwire'] = 'di_buf[{}]'.format(idx - 1)
+            p['twire'] = 't[{}]'.format(idx - 1)
+        else:
+            p['pad_wire'] = 'do[{}]'.format(ndo)
+            ndo += 1
+
+        params.append(p)
+        tile_params.append(
+            (
+                tile, site, p['pad_wire'], iostandard, p['DRIVE'],
+                verilog.unquote(p['SLEW']) if p['SLEW'] else None,
+                verilog.unquote(p['PULLTYPE'])))
+
+    write_params(tile_params)
+
+    print(
+        '''
+`define N_DO {n_do}
+`define N_DIO {n_dio}
+
+module top(input clk, output wire [`N_DO-1:0] do, inout wire [`N_DIO-1:0] dio);
+    wire [(`N_DIO+`N_DO)-1:0] di_buf;
+    wire [(`N_DIO+`N_DO)-1:0] do_buf;
+    wire [(`N_DIO+`N_DO)-1:0] t;
+    '''.format(n_dio=ndio, n_do=ndo))
+
+    # Always output a LUT6 to make placer happy.
+    print(
+        '''
+        (* KEEP, DONT_TOUCH *)
+        LUT6 dummy_lut();
+
+        wire bufg_o;
+        (* KEEP, DONT_TOUCH *)
+        BUFG (.O(bufg_o));
+        ''')
+
+    for p in params:
+        if p['io']:
+            print(
+                '''
+        wire oddr_d_{site};
+
+        (* KEEP, DONT_TOUCH, LOC = "{site}" *)
+        IOBUF #(
+            .IOSTANDARD({IOSTANDARD})
+        ) obuf_{site} (
+            .IO({pad_wire}),
+            .I({owire}),
+            .O({iwire}),
+            .T({twire})
+            );
+            '''.format(**p),
+                file=connects)
+        else:
+            print(
+                '''
+        wire oddr_d_{site};
+
+        (* KEEP, DONT_TOUCH, LOC = "{site}" *)
+        OBUF #(
+            .IOSTANDARD({IOSTANDARD})
+        ) obuf_{site} (
+            .O({pad_wire}),
+            .I({owire})
+            );
+            '''.format(**p),
+                file=connects)
+
+        p['use_oserdese2'] = random.randint(0, 1)
+        if p['use_oserdese2']:
+            use_oserdese2(p, luts, connects)
+        else:
+            use_direct_and_oddr(p, luts, connects)
+
+    for l in luts.create_wires_and_luts():
+        print(l)
+
+    print(connects.getvalue())
+
+    print("endmodule")
+
+    with open('params.jl', 'w') as f:
+        json.dump(params, f, indent=2)
+
+
+if __name__ == '__main__':
+    run()
diff --git a/fuzzers/037-iob18-pips/Makefile b/fuzzers/037-iob18-pips/Makefile
new file mode 100644
index 0000000..cef6198
--- /dev/null
+++ b/fuzzers/037-iob18-pips/Makefile
@@ -0,0 +1,58 @@
+# Copyright (C) 2017-2022  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
+export FUZDIR=$(shell pwd)
+PIP_TYPE?=ioi
+PIPLIST_TCL=$(FUZDIR)/ioi_pip_list.tcl
+
+TODO_RE=".*"
+EXCLUDE_RE=".*((PHASER)|(CLKDIVF)|(CLKDIVP)|(CLKDIVB)|(IOI_ILOGIC[01]_O)|(IOI_OLOGIC[01]_CLKB?\.)|(IOI_IMUX_RC)|(IOI_OLOGIC[01]_[OT]FB)|(OCLKM.*IMUX31)|IOI_ODELAY[01]_CLKIN.IOI_OCLK_[01]|RIOI_O[01].RIOI_ODELAY[01]_DATAOUT|RIOI_O[01].RIOI_OLOGIC[01]_OQ).*"
+
+MAKETODO_FLAGS=--pip-type ${PIP_TYPE} --seg-type $(PIP_TYPE) --re $(TODO_RE) --sides "xr" --exclude-re $(EXCLUDE_RE)
+N = 120
+
+SPECIMENS_DEPS=build/cmt_regions.csv
+
+include ../pip_loop.mk
+
+SIX_BIT_PIPS="OLOGIC[01]_CLKDIV"
+
+build/segbits_ioi_x.rdb: $(SPECIMENS_OK)
+	# Most pips are 3 bits, force a 3 bit solution
+	${XRAY_SEGMATCH} -c 3 -m 20 -M 50 -o build/segbits_ioi_x_match_3.rdb \
+		$(shell find build -name segdata_rioi*.txt)
+	# Some are 6 bit solutions, solve for 6 bits and merge respectively
+	${XRAY_SEGMATCH} -c 6 -m 20 -M 50 -o build/segbits_ioi_x_match_6.rdb \
+		$(shell find build -name segdata_rioi*.txt)
+	grep -v ${SIX_BIT_PIPS} build/segbits_ioi_x_match_3.rdb  > build/segbits_ioi_x.rdb
+	grep    ${SIX_BIT_PIPS} build/segbits_ioi_x_match_6.rdb >> build/segbits_ioi_x.rdb
+
+RDBS = build/segbits_ioi_x.rdb
+
+database: ${RDBS}
+	${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \
+		--seg-fn-in build/segbits_ioi_x.rdb \
+		--seg-fn-out build/segbits_ioi_x.db
+
+	# Keep a copy to track iter progress
+	cp build/segbits_ioi_x.rdb build/$(ITER)/segbits_ioi_x.rdb
+	cp build/segbits_ioi_x.db build/$(ITER)/segbits_ioi_x.db
+
+	# Clobber existing .db to eliminate potential conflicts
+	cp ${XRAY_DATABASE_DIR}/${XRAY_DATABASE}/segbits*.db build/database/${XRAY_DATABASE}
+	XRAY_DATABASE_DIR=${FUZDIR}/build/database ${XRAY_MERGEDB} rioi build/segbits_ioi_x.db
+
+build/cmt_regions.csv: output_cmt.tcl
+	mkdir -p build
+	cd build/ && ${XRAY_VIVADO} -mode batch -source ${FUZDIR}/output_cmt.tcl
+
+pushdb: database
+	${XRAY_MERGEDB} rioi build/segbits_ioi_x.db
+	${XRAY_MERGEDB} rioi_tbytesrc build/segbits_ioi_x.db
+	${XRAY_MERGEDB} rioi_tbyteterm build/segbits_ioi_x.db
+
+.PHONY: database pushdb
diff --git a/fuzzers/037-iob18-pips/bits.dbf b/fuzzers/037-iob18-pips/bits.dbf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fuzzers/037-iob18-pips/bits.dbf
diff --git a/fuzzers/037-iob18-pips/generate.py b/fuzzers/037-iob18-pips/generate.py
new file mode 100644
index 0000000..b21218c
--- /dev/null
+++ b/fuzzers/037-iob18-pips/generate.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017-2022  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
+
+from prjxray.segmaker import Segmaker
+import os, re
+import os.path
+
+
+def bitfilter(frame, word):
+    if frame < 28:
+        return False
+    return True
+
+
+def main():
+    segmk = Segmaker("design.bits")
+
+    tiledata = {}
+    pipdata = {}
+    ignpip = set()
+
+    with open(os.path.join(os.getenv('FUZDIR'), '..', 'piplist', 'build',
+                           'ioi', 'rioi.txt')) as f:
+        for l in f:
+            tile_type, dst, src = l.strip().split('.')
+            if tile_type not in pipdata:
+                pipdata[tile_type] = []
+
+            pipdata[tile_type].append((src, dst))
+
+    print("Loading tags from design.txt.")
+    with open("design.txt", "r") as f:
+        for line in f:
+            tile, pip, src, dst, pnum, pdir = line.split()
+            if not tile.startswith('RIOI') or "X43Y9" in tile:
+                continue
+
+            log = open("log.txt", "a")
+            print(line, file=log)
+            pip_prefix, _ = pip.split(".")
+            tile_from_pip, tile_type = pip_prefix.split('/')
+
+            _, src = src.split("/")
+            _, dst = dst.split("/")
+            pnum = int(pnum)
+            pdir = int(pdir)
+
+            if tile not in tiledata:
+                tiledata[tile] = {
+                    "type": tile_type,
+                    "pips": set(),
+                    "srcs": set(),
+                    "dsts": set(),
+                }
+
+            tiledata[tile]["pips"].add((src, dst))
+            tiledata[tile]["srcs"].add(src)
+            tiledata[tile]["dsts"].add(dst)
+
+            if pdir == 0:
+                tiledata[tile]["srcs"].add(dst)
+                tiledata[tile]["dsts"].add(src)
+
+    for tile, pips_srcs_dsts in tiledata.items():
+        tile_type = pips_srcs_dsts["type"]
+
+        if tile_type.startswith('RIOI'):
+            tile_type = 'RIOI'
+
+        for src, dst in pipdata[tile_type]:
+            if (src, dst) in ignpip:
+                pass
+            if re.match(r'.*PHASER.*', src) or re.match(r'.*CLKDIV[PFB].*',
+                                                        dst):
+                pass
+            elif (src, dst) in tiledata[tile]["pips"]:
+                segmk.add_tile_tag(tile, "%s.%s" % (dst, src), 1)
+            elif dst not in tiledata[tile]["dsts"]:
+                disable_pip = True
+
+                if dst == 'IOI_OCLKM_0' and 'IOI_OCLK_0' in tiledata[tile][
+                        "dsts"]:
+                    disable_pip = False
+
+                if dst == 'IOI_OCLKM_1' and 'IOI_OCLK_1' in tiledata[tile][
+                        "dsts"]:
+                    disable_pip = False
+
+                if disable_pip:
+                    segmk.add_tile_tag(tile, "%s.%s" % (dst, src), 0)
+
+    segmk.compile(bitfilter=bitfilter)
+    segmk.write()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/037-iob18-pips/generate.tcl b/fuzzers/037-iob18-pips/generate.tcl
new file mode 100644
index 0000000..65d692d
--- /dev/null
+++ b/fuzzers/037-iob18-pips/generate.tcl
@@ -0,0 +1,109 @@
+# Copyright (C) 2017-2022  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
+source "$::env(XRAY_DIR)/utils/utils.tcl"
+
+proc write_pip_txtdata {filename} {
+    puts "FUZ([pwd]): Writing $filename."
+    set fp [open $filename w]
+    set nets [get_nets -hierarchical]
+    set nnets [llength $nets]
+    set neti 0
+    foreach net $nets {
+        incr neti
+        if {($neti % 100) == 0 } {
+            puts "FUZ([pwd]): Dumping pips from net $net ($neti / $nnets)"
+        }
+        foreach pip [get_pips -of_objects $net] {
+            set tile [get_tiles -of_objects $pip]
+            set src_wire [get_wires -uphill -of_objects $pip]
+            set dst_wire [get_wires -downhill -of_objects $pip]
+            set num_pips [llength [get_nodes -uphill -of_objects [get_nodes -of_objects $dst_wire]]]
+            set dir_prop [get_property IS_DIRECTIONAL $pip]
+            puts $fp "$tile $pip $src_wire $dst_wire $num_pips $dir_prop"
+        }
+    }
+    close $fp
+}
+
+proc make_manual_routes {filename} {
+    puts "MANROUTE: Loading routes from $filename"
+
+    set fp [open $filename r]
+    foreach line [split [read $fp] "\n"] {
+        if {$line eq ""} {
+            continue
+        }
+
+        puts "MANROUTE: Line: $line"
+
+        # Parse the line
+        set fields [split $line " "]
+        set net_name [lindex $fields 0]
+        set wire_name [lindex $fields 1]
+
+        # Check if that net exists
+        if {[get_nets $net_name] eq ""} {
+            puts "MANROUTE: net $net_name does not exist"
+            continue
+        }
+
+        set net [get_nets $net_name]
+
+        # Rip it up
+        set_property -quiet FIXED_ROUTE "" $net
+        set_property IS_ROUTE_FIXED 0 $net
+        route_design -unroute -nets $net
+
+        # Make the route
+        set nodes [get_nodes -of_objects [get_wires $wire_name]]
+        set status [route_via $net_name [list $nodes] 0]
+
+        # Failure, skip manual routing of this net
+        if { $status != 1 } {
+            puts "MANROUTE: Manual routing failed!"
+            set_property -quiet FIXED_ROUTE "" $net
+            set_property IS_ROUTE_FIXED 0 $net
+            continue
+        }
+
+        puts "MANROUTE: Success!"
+    }
+}
+
+proc run {} {
+    create_project -force -part $::env(XRAY_PART) design design
+    read_verilog top.v
+    synth_design -top top
+
+    set_property CFGBVS GND [current_design]
+    set_property CONFIG_VOLTAGE 1.8 [current_design]
+    set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
+    set_property IS_ENABLED 0 [get_drc_checks {AVAL-74}]
+    set_property IS_ENABLED 0 [get_drc_checks {PDRC-26}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-4}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-5}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-13}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-98}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-99}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-105}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-115}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-144}]
+
+    set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets]
+
+    place_design -directive Quick
+    write_checkpoint -force design_before_route.dcp
+    make_manual_routes routes.txt
+    route_design -directive Quick -preserve
+    write_checkpoint -force design.dcp
+
+    write_bitstream -force design.bit
+    write_pip_txtdata design.txt
+}
+
+run
diff --git a/fuzzers/037-iob18-pips/ioi_pip_list.tcl b/fuzzers/037-iob18-pips/ioi_pip_list.tcl
new file mode 100644
index 0000000..854873c
--- /dev/null
+++ b/fuzzers/037-iob18-pips/ioi_pip_list.tcl
@@ -0,0 +1,47 @@
+# Copyright (C) 2017-2022  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 print_tile_pips {tile_type filename} {
+    set fp [open $filename w]
+    set pips [dict create]
+    foreach tile [get_tiles -filter "TYPE == $tile_type"] {
+        foreach pip [lsort [get_pips -of_objects  $tile]] {
+            set src [get_wires -uphill -of_objects $pip]
+            set dst [get_wires -downhill -of_objects $pip]
+
+            # Skip pips with disconnected nodes
+            set src_node [get_nodes -of_objects $src]
+
+            if { $src_node == {} } {
+                continue
+            }
+
+            set dst_node [get_nodes -of_objects $src]
+            if { $dst_node == {} } {
+                continue
+            }
+
+            set src_wire [regsub {.*/} $src ""]
+            set src_match [regexp {IOI_OCLKM?_[01]} $src_wire]
+
+            if { [llength [get_nodes -uphill -of_objects [get_nodes -of_objects $dst]]] != 1 || $src_match } {
+                set pip_string "$tile_type.[regsub {.*/} $dst ""].[regsub {.*/} $src ""]"
+                if ![dict exists $pips $pip_string] {
+                    puts $fp $pip_string
+                    dict set pips $pip_string 1
+                }
+            }
+        }
+    }
+    close $fp
+}
+
+create_project -force -part $::env(XRAY_PART) design design
+set_property design_mode PinPlanning [current_fileset]
+open_io_design -name io_1
+
+print_tile_pips RIOI rioi.txt
diff --git a/fuzzers/037-iob18-pips/output_cmt.tcl b/fuzzers/037-iob18-pips/output_cmt.tcl
new file mode 100644
index 0000000..9ab8cf8
--- /dev/null
+++ b/fuzzers/037-iob18-pips/output_cmt.tcl
@@ -0,0 +1,18 @@
+# Copyright (C) 2017-2022  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
+create_project -force -part $::env(XRAY_PART) design design
+set_property design_mode PinPlanning [current_fileset]
+open_io_design -name io_1
+
+set fp [open "cmt_regions.csv" "w"]
+foreach site_type {MMCME2_ADV BUFHCE BUFR BUFMRCE BUFIO ILOGICE2 OLOGICE2 IDELAYE2 IDELAYCTRL PLLE2_ADV} {
+    foreach site [get_sites -filter "SITE_TYPE == $site_type"] {
+        puts $fp "$site,[get_property CLOCK_REGION $site]"
+    }
+}
+close $fp
diff --git a/fuzzers/037-iob18-pips/top.py b/fuzzers/037-iob18-pips/top.py
new file mode 100644
index 0000000..f7ecea0
--- /dev/null
+++ b/fuzzers/037-iob18-pips/top.py
@@ -0,0 +1,391 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017-2022  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
+import os
+import random
+import math
+random.seed(int(os.getenv("SEED"), 16))
+from prjxray import util
+from prjxray import verilog
+from prjxray import lut_maker
+from prjxray.db import Database
+
+NOT_INCLUDED_TILES = ['RIOI_SING']
+
+SITE_TYPES = ['OLOGICE2', 'ILOGICE2']
+
+
+def read_site_to_cmt():
+    """ Yields clock sources and which CMT they route within. """
+    with open(os.path.join(os.getenv('FUZDIR'), 'build',
+                           'cmt_regions.csv')) as f:
+        for l in f:
+            site, cmt = l.strip().split(',')
+            yield (site, cmt)
+
+
+def gen_sites():
+    ''' Return dict of ISERDES/OSERDES locations. '''
+    db = Database(util.get_db_root(), util.get_part())
+    grid = db.grid()
+
+    xy_fun = util.create_xy_fun('\S+')
+
+    tiles = grid.tiles()
+
+    for tile_name in sorted(tiles):
+        loc = grid.loc_of_tilename(tile_name)
+        gridinfo = grid.gridinfo_at_loc(loc)
+        tile_type = gridinfo.tile_type
+
+        tile = {'tile': tile_name, 'tile_type': tile_type, 'ioi_sites': {}}
+
+        for site_name, site_type in gridinfo.sites.items():
+            if site_type in SITE_TYPES:
+                xy = xy_fun(site_name)
+                if xy not in tile['ioi_sites']:
+                    tile['ioi_sites'][xy] = {}
+
+                tile['ioi_sites'][xy][site_type] = site_name
+
+        yield tile
+
+
+class ClockSources(object):
+    def __init__(self):
+        self.site_to_cmt = dict(read_site_to_cmt())
+
+        self.leaf_gclks = {}
+        self.ioclks = {}
+        self.rclks = {}
+        self.selected_leaf_gclks = {}
+        self.lut_maker = lut_maker.LutMaker()
+
+        for cmt in set(self.site_to_cmt.values()):
+            self.leaf_gclks[cmt] = []
+            self.ioclks[cmt] = []
+            self.rclks[cmt] = []
+
+    def init_clocks(self):
+        """ Initialize all IOI clock sources. """
+        for site, cmt in self.site_to_cmt.items():
+            clk = 'clk_' + site
+            if 'BUFHCE' in site:
+                print(
+                    """
+            wire {clk};
+            (* KEEP, DONT_TOUCH, LOC = "{site}" *)
+            BUFH bufh_{site}(
+                .O({clk})
+                );
+                """.format(
+                        clk=clk,
+                        site=site,
+                    ))
+
+                self.leaf_gclks[cmt].append(clk)
+
+            if 'BUFIO' in site:
+                print(
+                    """
+            wire {clk};
+            (* KEEP, DONT_TOUCH, LOC = "{site}" *)
+            BUFIO bufio_{site}(
+                .O({clk})
+                );
+                """.format(
+                        clk=clk,
+                        site=site,
+                    ))
+
+                self.ioclks[cmt].append(clk)
+
+            if 'BUFR' in site:
+                print(
+                    """
+            wire {clk};
+            (* KEEP, DONT_TOUCH, LOC = "{site}" *)
+            BUFR bufr_{site}(
+                .O({clk})
+                );
+                """.format(
+                        clk=clk,
+                        site=site,
+                    ))
+
+                self.rclks[cmt].append(clk)
+
+        # Choose 6 leaf_gclks to be used in each CMT.
+        for cmt in self.leaf_gclks:
+            self.selected_leaf_gclks[cmt] = random.sample(
+                self.leaf_gclks[cmt], 6)
+
+    def get_clock(
+            self,
+            site,
+            allow_ioclks,
+            allow_rclks,
+            allow_fabric=True,
+            allow_empty=True):
+        cmt = self.site_to_cmt[site]
+        choices = []
+        if allow_fabric:
+            choices.append('lut')
+
+        if allow_empty:
+            choices.append('')
+
+        choices.extend(self.selected_leaf_gclks[cmt])
+        if allow_ioclks:
+            choices.extend(self.ioclks[cmt])
+
+        if allow_rclks:
+            choices.extend(self.rclks[cmt])
+
+        clock = random.choice(choices)
+        is_lut = False
+        if clock == "lut":
+            clock = self.lut_maker.get_next_output_net()
+            is_lut = True
+        return clock, is_lut
+
+
+def add_port(ports, port, signal):
+    ports.append('.{}({})'.format(port, signal))
+
+
+def run():
+    print("module top();")
+
+    clocks = ClockSources()
+    clocks.init_clocks()
+    """
+
+    ISERDESE2 clock sources:
+
+    CLK/CLKB:
+     - Allows LEAF_GCLK, IOCLKS, RCLKS and fabric
+     - Dedicated pips
+
+    CLKDIV:
+     - No dedicated pips, uses fabric clock in.
+
+    CLKDIVP:
+     - Has pips, MIG only, PHASER or fabric.
+
+    OCLK/OCLKB:
+     - Allows LEAF_GCLK, IOCLKS, RCLKS and fabric
+     - Must match OSERDESE2:CLK/CLKB
+
+     OSERDESE2 clock sources:
+
+     CLKDIV/CLKDIVB:
+      - Allows LEAF_GCLK and RCLKS and fabric
+      - Dedicated pips
+
+     CLKDIVF/CLKDIVFB:
+      - Allows LEAF_GCLK and RCLKS and fabric
+      - No explicit port, follows CLKDIV/CLKDIVB?
+      """
+
+    output = []
+    route_file = open("routes.txt", "w")
+
+    for tile in gen_sites():
+        if tile['tile_type'] in NOT_INCLUDED_TILES:
+            continue
+
+        for xy in tile['ioi_sites']:
+            ilogic_site_type = random.choice([None, 'ISERDESE2', 'IDDR'])
+            use_oserdes = random.randint(0, 1)
+
+            ilogic_site = tile['ioi_sites'][xy]['ILOGICE2']
+            ologic_site = tile['ioi_sites'][xy]['OLOGICE2']
+
+            if use_oserdes:
+                oclk, _ = clocks.get_clock(
+                    ologic_site, allow_ioclks=True, allow_rclks=True)
+
+                oclkb = oclk
+            else:
+                oclk, is_lut = clocks.get_clock(
+                    ilogic_site, allow_ioclks=True, allow_rclks=True)
+
+                if random.randint(0, 1):
+                    oclkb = oclk
+                else:
+                    if random.randint(0, 1):
+                        oclkb, _ = clocks.get_clock(
+                            ilogic_site,
+                            allow_ioclks=True,
+                            allow_rclks=True,
+                            allow_fabric=not is_lut)
+                    else:
+                        # Explicitly provide IMUX stimulus to resolve IMUX pips
+                        oclk = random.randint(0, 1)
+                        oclkb = random.randint(0, 1)
+
+            DATA_RATE = random.choice(['DDR', 'SDR'])
+            clk, clk_is_lut = clocks.get_clock(
+                ilogic_site,
+                allow_ioclks=True,
+                allow_rclks=True,
+                allow_empty=DATA_RATE == 'SDR')
+
+            clkb = clk
+            while clkb == clk:
+                clkb, clkb_is_lut = clocks.get_clock(
+                    ilogic_site,
+                    allow_ioclks=True,
+                    allow_rclks=True,
+                    allow_empty=False)
+
+            imux_available = {
+                0: set(("IOI_IMUX20_0", "IOI_IMUX22_0")),
+                1: set(("IOI_IMUX20_1", "IOI_IMUX22_1")),
+            }
+
+            # Force CLK route through IMUX when connected to a LUT
+            if clk_is_lut:
+                y = (xy[1] + 1) % 2
+
+                route = random.choice(list(imux_available[y]))
+                imux_available[y].remove(route)
+
+                route = "{}/{}".format(tile["tile"], route)
+                route_file.write("{} {}\n".format(clk, route))
+
+            # Force CLKB route through IMUX when connected to a LUT
+            if clkb_is_lut:
+                y = (xy[1] + 1) % 2
+
+                route = random.choice(list(imux_available[y]))
+                imux_available[y].remove(route)
+
+                route = "{}/{}".format(tile["tile"], route)
+                route_file.write("{} {}\n".format(clkb, route))
+
+            if ilogic_site_type is None:
+                pass
+
+            elif ilogic_site_type == 'ISERDESE2':
+                INTERFACE_TYPE = random.choice(
+                    [
+                        'MEMORY',
+                        'MEMORY_DDR3',
+                        'MEMORY_QDR',
+                        'NETWORKING',
+                        'OVERSAMPLE',
+                    ])
+                ports = []
+
+                add_port(ports, 'CLK', clk)
+                add_port(ports, 'CLKB', clkb)
+                add_port(ports, 'OCLK', oclk)
+                add_port(ports, 'OCLKB', oclkb)
+
+                output.append(
+                    """
+            (* KEEP, DONT_TOUCH, LOC="{site}" *)
+            ISERDESE2 #(
+                .DATA_RATE({DATA_RATE}),
+                .INTERFACE_TYPE({INTERFACE_TYPE}),
+                .IS_CLK_INVERTED({IS_CLK_INVERTED}),
+                .IS_CLKB_INVERTED({IS_CLKB_INVERTED}),
+                .IS_OCLK_INVERTED({IS_OCLK_INVERTED}),
+                .IS_OCLKB_INVERTED({IS_OCLKB_INVERTED}),
+                .INIT_Q1({INIT_Q1}),
+                .INIT_Q2({INIT_Q2}),
+                .INIT_Q3({INIT_Q3}),
+                .INIT_Q4({INIT_Q4}),
+                .SRVAL_Q1({SRVAL_Q1}),
+                .SRVAL_Q2({SRVAL_Q2}),
+                .SRVAL_Q3({SRVAL_Q3}),
+                .SRVAL_Q4({SRVAL_Q4})
+            ) iserdes_{site}(
+                {ports});""".format(
+                        site=ilogic_site,
+                        ports=',\n'.join(ports),
+                        DATA_RATE=verilog.quote(DATA_RATE),
+                        INTERFACE_TYPE=verilog.quote(INTERFACE_TYPE),
+                        IS_CLK_INVERTED=random.randint(0, 1),
+                        IS_CLKB_INVERTED=random.randint(0, 1),
+                        IS_OCLK_INVERTED=random.randint(0, 1),
+                        IS_OCLKB_INVERTED=random.randint(0, 1),
+                        INIT_Q1=random.randint(0, 1),
+                        INIT_Q2=random.randint(0, 1),
+                        INIT_Q3=random.randint(0, 1),
+                        INIT_Q4=random.randint(0, 1),
+                        SRVAL_Q1=random.randint(0, 1),
+                        SRVAL_Q2=random.randint(0, 1),
+                        SRVAL_Q3=random.randint(0, 1),
+                        SRVAL_Q4=random.randint(0, 1),
+                    ))
+            elif ilogic_site_type == 'IDDR':
+                ports = []
+                add_port(ports, 'C', clk)
+                add_port(ports, 'CB', clkb)
+
+                output.append(
+                    """
+            (* KEEP, DONT_TOUCH, LOC="{site}" *)
+            IDDR_2CLK #(
+                .INIT_Q1({INIT_Q1}),
+                .INIT_Q2({INIT_Q2}),
+                .SRTYPE({SRTYPE})
+            ) iserdes_{site}(
+                {ports});""".format(
+                        site=ilogic_site,
+                        ports=',\n'.join(ports),
+                        INIT_Q1=random.randint(0, 1),
+                        INIT_Q2=random.randint(0, 1),
+                        SRTYPE=verilog.quote(random.choice(['ASYNC', 'SYNC'])),
+                    ))
+            else:
+                assert False, ilogic_site_type
+
+            if use_oserdes:
+                ports = []
+
+                add_port(
+                    ports, 'CLKDIV',
+                    clocks.get_clock(
+                        ologic_site,
+                        allow_ioclks=False,
+                        allow_rclks=True,
+                    )[0])
+
+                add_port(ports, 'CLK', oclk)
+
+                output.append(
+                    """
+            (* KEEP, DONT_TOUCH, LOC = "{site}" *)
+            OSERDESE2 #(
+                .IS_CLK_INVERTED({IS_CLK_INVERTED}),
+                .DATA_RATE_OQ("SDR"),
+                .DATA_RATE_TQ("SDR")
+            ) oserdes_{site} (
+                {ports});""".format(
+                        IS_CLK_INVERTED=random.randint(0, 1),
+                        site=ologic_site,
+                        ports=',\n'.join(ports),
+                    ))
+
+    for s in clocks.lut_maker.create_wires_and_luts():
+        print(s)
+
+    for s in output:
+        print(s)
+
+    print("endmodule")
+
+
+if __name__ == '__main__':
+    run()
diff --git a/fuzzers/039-hclk-config/Makefile b/fuzzers/039-hclk-config/Makefile
index ae18a36..4828018 100644
--- a/fuzzers/039-hclk-config/Makefile
+++ b/fuzzers/039-hclk-config/Makefile
@@ -9,7 +9,11 @@
 
 include ../fuzzer.mk
 
+ifeq ($(XRAY_DATABASE),kintex7)
+database: build/segbits_hclk_ioi.db
+else
 database: build/segbits_hclk_ioi3.db
+endif
 
 build/segbits_hclk_ioi3.rdb: $(SPECIMENS_OK)
 	${XRAY_SEGMATCH} -c 5 -o build/segbits_hclk_ioi3.rdb \
@@ -22,8 +26,20 @@
 	${XRAY_MASKMERGE} build/mask_hclk_ioi3.db \
 		$(addsuffix /segdata_hclk_ioi3.txt,$(SPECIMENS))
 
+# The fuzzer results for the high performance banks
+# are identical, so just copy those
+ifeq ($(XRAY_DATABASE),kintex7)
+build/segbits_hclk_ioi.db: build/segbits_hclk_ioi3.db
+	sed -e 's/HCLK_IOI3/HCLK_IOI/g' $< > $@
+	cp build/mask_hclk_ioi3.db build/mask_hclk_ioi.db
+endif
+
 pushdb: database
 	${XRAY_MERGEDB} hclk_ioi3 build/segbits_hclk_ioi3.db
 	${XRAY_MERGEDB} mask_hclk_ioi3 build/mask_hclk_ioi3.db
+ifeq ($(XRAY_DATABASE),kintex7)
+	${XRAY_MERGEDB} hclk_ioi build/segbits_hclk_ioi.db
+	${XRAY_MERGEDB} mask_hclk_ioi build/mask_hclk_ioi.db
+endif
 
 .PHONY: database pushdb
diff --git a/fuzzers/047-hclk-ioi18-pips/Makefile b/fuzzers/047-hclk-ioi18-pips/Makefile
new file mode 100644
index 0000000..3d04344
--- /dev/null
+++ b/fuzzers/047-hclk-ioi18-pips/Makefile
@@ -0,0 +1,53 @@
+# Copyright (C) 2017-2022  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
+export FUZDIR=$(shell pwd)
+PIP_TYPE?=hclk_ioi
+PIPLIST_TCL=$(FUZDIR)/hclk_ioi_pip_list.tcl
+TODO_RE=".*"
+# FIXME Modify fuzzer to solve PIPs that drive the BUFIO and BUFR from ILOGIC clock or through the IOI tile.
+EXCLUDE_RE=".*\.HCLK_IOI_((I2IOCLK)|(IDELAYCTRL))"
+
+MAKETODO_FLAGS= --sides "" --pip-type ${PIP_TYPE} --seg-type ${PIP_TYPE} --re $(TODO_RE) --exclude-re $(EXCLUDE_RE)
+N = 499
+
+SEGMATCH_FLAGS=-c 4 -m 4 -M 4
+SPECIMENS_DEPS=build/cmt_regions.csv
+A_PIPLIST=hclk_ioi.txt
+
+include ../pip_loop.mk
+
+build/segbits_hclk_ioi.rdb: $(SPECIMENS_OK)
+	${XRAY_SEGMATCH} ${SEGMATCH_FLAGS} -o build/segbits_hclk_ioi.rdb \
+		$(shell find build -name segdata_hclk_ioi.txt)
+
+build/segbits_hclk_ioi.db: build/segbits_hclk_ioi.rdb
+	${XRAY_DBFIXUP} --db-root build --zero-db bits.dbf \
+		--seg-fn-in build/segbits_hclk_ioi.rdb \
+		--seg-fn-out build/segbits_hclk_ioi.db
+
+	# Keep a copy to track iter progress
+	cp build/segbits_hclk_ioi.rdb build/$(ITER)/segbits_hclk_ioi.rdb
+
+
+	${XRAY_MASKMERGE} build/mask_hclk_ioi.db \
+		$(shell find build -name segdata_hclk_ioi.txt)
+
+database: build/segbits_hclk_ioi.db
+	# Clobber existing .db to eliminate potential conflicts
+	cp ${XRAY_DATABASE_DIR}/${XRAY_DATABASE}/segbits*.db build/database/${XRAY_DATABASE}
+	XRAY_DATABASE_DIR=${FUZDIR}/build/database ${XRAY_MERGEDB} hclk_ioi build/segbits_hclk_ioi.db
+
+build/cmt_regions.csv: output_cmt.tcl
+	mkdir -p build
+	cd build/ && ${XRAY_VIVADO} -mode batch -source ${FUZDIR}/output_cmt.tcl
+
+pushdb: database
+	${XRAY_MERGEDB} hclk_ioi build/segbits_hclk_ioi.db
+	${XRAY_MERGEDB} mask_hclk_ioi build/mask_hclk_ioi.db
+
+.PHONY: database pushdb
diff --git a/fuzzers/047-hclk-ioi18-pips/README.md b/fuzzers/047-hclk-ioi18-pips/README.md
new file mode 100644
index 0000000..8f1c36a
--- /dev/null
+++ b/fuzzers/047-hclk-ioi18-pips/README.md
@@ -0,0 +1,3 @@
+# HCLK_IOI interconnect fuzzer
+
+Solves pips located within the HCLK_IOI switch box.
diff --git a/fuzzers/047-hclk-ioi18-pips/bits.dbf b/fuzzers/047-hclk-ioi18-pips/bits.dbf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/fuzzers/047-hclk-ioi18-pips/bits.dbf
diff --git a/fuzzers/047-hclk-ioi18-pips/generate.py b/fuzzers/047-hclk-ioi18-pips/generate.py
new file mode 100644
index 0000000..eef904e
--- /dev/null
+++ b/fuzzers/047-hclk-ioi18-pips/generate.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017-2022  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
+
+from prjxray.segmaker import Segmaker
+import os
+import os.path
+
+
+def bitfilter(frame, word):
+    if frame < 26:
+        return False
+    return True
+
+
+def main():
+    segmk = Segmaker("design.bits")
+
+    tiledata = {}
+    pipdata = {}
+    ignpip = set()
+    tile_ports = {}
+
+    with open(os.path.join(os.getenv('FUZDIR'), '..', 'piplist', 'build',
+                           'hclk_ioi', 'hclk_ioi.txt')) as f:
+        for l in f:
+            tile_type, dst, src = l.strip().split('.')
+            if tile_type not in pipdata:
+                pipdata[tile_type] = []
+                tile_ports[tile_type] = set()
+
+            pipdata[tile_type].append((src, dst))
+            tile_ports[tile_type].add(src)
+            tile_ports[tile_type].add(dst)
+
+    print("Loading tags from design.txt.")
+    with open("design.txt", "r") as f:
+        for line in f:
+            tile, pip, src, dst, pnum, pdir = line.split()
+
+            if not tile.startswith('HCLK_IOI_'):
+                continue
+
+            pip_prefix, _ = pip.split(".")
+            tile_from_pip, tile_type = pip_prefix.split('/')
+            assert tile == tile_from_pip
+            _, src = src.split("/")
+            _, dst = dst.split("/")
+            pnum = int(pnum)
+            pdir = int(pdir)
+
+            if tile not in tiledata:
+                tiledata[tile] = {
+                    "type": tile_type,
+                    "pips": set(),
+                    "srcs": set(),
+                    "dsts": set()
+                }
+
+            tiledata[tile]["pips"].add((src, dst))
+            tiledata[tile]["srcs"].add(src)
+            tiledata[tile]["dsts"].add(dst)
+
+            if pdir == 0:
+                tiledata[tile]["srcs"].add(dst)
+                tiledata[tile]["dsts"].add(src)
+
+    for tile, pips_srcs_dsts in tiledata.items():
+        tile_type = pips_srcs_dsts["type"]
+        pips = pips_srcs_dsts["pips"]
+
+        for src, dst in pipdata[tile_type]:
+            if (src, dst) in ignpip:
+                pass
+            elif (src, dst) in pips:
+                segmk.add_tile_tag(tile, "%s.%s" % (dst, src), 1)
+            elif dst not in tiledata[tile]["dsts"]:
+                segmk.add_tile_tag(tile, "%s.%s" % (dst, src), 0)
+
+    segmk.compile(bitfilter=bitfilter)
+    segmk.write()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fuzzers/047-hclk-ioi18-pips/generate.tcl b/fuzzers/047-hclk-ioi18-pips/generate.tcl
new file mode 100644
index 0000000..f75db3c
--- /dev/null
+++ b/fuzzers/047-hclk-ioi18-pips/generate.tcl
@@ -0,0 +1,358 @@
+# Copyright (C) 2017-2022  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
+source "$::env(XRAY_DIR)/utils/utils.tcl"
+
+proc load_todo {{dir "dst"}} {
+    set fp [open "../../todo_all.txt" r]
+
+    # Create map of pip source to remaining destinations for that pip
+    set todo_map [dict create]
+    for {gets $fp line} {$line != ""} {gets $fp line} {
+        set parts [split $line .]
+        if {$dir == "dsts"} {
+            dict lappend todo_map [lindex $parts 2] [list [lindex $parts 0] [lindex $parts 1]]
+        } elseif {$dir == "srcs"} {
+            dict lappend todo_map [lindex $parts 1] [list [lindex $parts 0] [lindex $parts 2]]
+        } else {
+            error "Incorrect argument. Available options: src, dst"
+        }
+    }
+    close $fp
+    return $todo_map
+}
+
+proc shuffle_list {list} {
+    set l [llength $list]
+    for {set i 0} {$i<=$l} {incr i} {
+        set x [lindex $list [set p [expr {int(rand()*$l)}]]]
+        set list [lreplace $list $p $p]
+        set list [linsert $list [expr {int(rand()*$l)}] $x]
+    }
+
+    return $list
+}
+
+# Get the dictionary of nets with one corresponding source wire
+# of a PIP from the todo list
+proc get_nets_with_todo_pip_wires {direction net_regexp wire_regexp used_destinations {verbose false}} {
+    set todo_map [load_todo $direction]
+    puts $todo_map
+    set nets [get_nets]
+    set todo_nets [dict create]
+
+    foreach net $nets {
+        if {![regexp $net_regexp $net]} {
+            continue
+        }
+        # Check to see if this net is one we are interested in*
+        set wires [get_wires -of_objects $net -filter {TILE_NAME =~ "*HCLK_IOI*" } -quiet]
+
+        set wire_found 0
+        foreach wire $wires {
+            if [regexp $wire_regexp $wire] {
+                set wire_found 1
+                break
+            }
+        }
+
+        if {$wire_found == 0} {
+            if {$verbose} {
+                puts "$net not going to a HCLK port, skipping."
+            }
+            continue
+        }
+
+        set tile [lindex [split $wire /] 0]
+        set wire [lindex [split $wire /] 1]
+        set tile_type [get_property TILE_TYPE [get_tiles $tile]]
+
+
+        if { ![dict exists $todo_map $wire] } {
+            continue
+        }
+
+        set candidates [dict get $todo_map $wire]
+
+        # This net is interesting, see if it is already going somewhere we
+        # want.
+        set found_target 0
+        foreach other_wire $wires {
+            if { $found_target == 1 } {
+                break
+            }
+
+            set other_wire [lindex [split $other_wire /] 1]
+
+            if { $wire == $other_wire } {
+                continue
+            }
+
+            foreach candidate $candidates {
+                set candidate_tile_type [lindex $candidate 0]
+
+                if {$candidate_tile_type != $tile_type} {
+                    continue
+                }
+
+                set candidate_wire [lindex $candidate 1]
+
+                if { $other_wire == $candidate } {
+                    set found_target 1
+                    if {$verbose} {
+                        puts "Interesting net $net already going from $wire to $other_wire."
+                    }
+                    set_property IS_ROUTE_FIXED 1 $net
+                    dict set used_destinations "$tile/$candidate_wire" 1
+                    break
+                }
+            }
+        }
+
+        if { $found_target == 1 } {
+            # Net already has an interesting feature - don't reroute.
+            continue
+        }
+
+        dict set todo_nets $net [list $tile $wire]
+        if {$verbose} {
+            puts "Interesting net $net (including $wire) is being rerouted."
+        }
+    }
+    return $todo_nets
+}
+
+proc route_todo {} {
+    set used_destinations [dict create]
+    set todo_map [load_todo "dsts"]
+    set serdes_nets [get_nets_with_todo_pip_wires "dsts" "serdes_clk_ILOGIC" "HCLK_IOI_CK_IGCLK" $used_destinations]
+    puts "Serdes nets: $serdes_nets"
+    dict for {net tile_wire} $serdes_nets {
+        set tile [lindex $tile_wire 0]
+        set wire [lindex $tile_wire 1]
+        set dsts [dict get $todo_map $wire]
+        set tile_type [get_property TILE_TYPE [get_tiles $tile]]
+        set todos {}
+
+        set old_target_wire [get_wires -of_objects $net -filter {TILE_NAME =~ "*HCLK_IOI*" && NAME =~ "*HCLK_IOI_LEAF_GCLK_*"}]
+        if {$old_target_wire == {}} {
+            continue
+        }
+        if {[dict exists $used_destinations $old_target_wire]} {
+            puts "Not routing to $old_target_wire, in use."
+            continue
+        }
+        puts "Rerouting net $net at $tile / $wire (type $tile_type)"
+        puts "Previous target wire: $old_target_wire"
+        set old_target_node [get_nodes -of_objects $old_target_wire]
+        if [regexp "HCLK_IOI_LEAF_GCLK_\(\(TOP\)|\(BOT\)\).*" $old_target_wire match group] {
+            set old_target_side $group
+        }
+        foreach dst $dsts {
+            set dst_tile_type [lindex $dst 0]
+            if {$dst_tile_type != $tile_type} {
+                continue
+            }
+
+            set dst_wire [lindex $dst 1]
+
+            set is_gclk_net 0
+            if [regexp "HCLK_IOI_LEAF_GCLK_\(\(TOP\)|\(BOT\)\).*" $dst_wire match group] {
+                set is_gclk_net 1
+                set dst_side $group
+            }
+
+            if {$is_gclk_net == 0 || $dst_side != $old_target_side} {
+                continue
+            }
+
+            lappend todos $dst_wire
+        }
+
+        set todos_length [llength $todos]
+        if {$todos_length == 0} {
+            continue
+        }
+
+        puts "All todos for $tile_type / $wire"
+        foreach dst_wire $todos {
+            puts "  - $dst_wire"
+        }
+
+        set todos [shuffle_list $todos]
+
+        set origin_node [get_nodes -of_objects [get_site_pins -filter {DIRECTION == OUT} -of_objects $net]]
+        puts "Origin node: $origin_node"
+        route_design -unroute -nets $net
+
+        # Find an input in the todo list that this can can drive.
+        foreach dst_wire $todos {
+            if { [dict exists $used_destinations "$tile/$dst_wire"] } {
+                puts "Not routing to $tile / $dst_wire, in use."
+                continue
+            }
+
+            set target_wire [get_wires "$tile/$dst_wire"]
+            set target_node [get_nodes -of_objects $target_wire]
+            if {[llength $target_node] == 0} {
+                error "Failed to find node for $tile/$dst_wire."
+            }
+
+            set old_net [get_nets -of_objects $target_node -quiet]
+            if {$old_net == {}} {
+                continue
+            }
+            puts "Unrouting the old net: $old_net"
+            route_design -unroute -nets $old_net
+            set old_origin_node [get_nodes -of_objects [get_site_pins -filter {DIRECTION == OUT} -of_objects $old_net]]
+
+            # Route the net through the desired node
+            puts "Attempting to route to $target_node for net $net."
+            route_via $net [list $target_node]
+
+            puts "Attempting to route to $old_target_node for net $old_net."
+            # Route the old net through the old target node
+            route_via $old_net [list $old_target_node ]
+
+            puts "Origin node: $origin_node, Old origin node: $old_origin_node"
+            puts "Target wire: $target_wire, Old target wire: $old_target_wire"
+            puts "Target node: $target_node, Old target node: $old_target_node"
+
+            dict set used_destinations "$target_wire" 1
+            dict set used_destinations "$old_target_wire" 1
+
+            break
+        }
+    }
+
+    set todo_map [load_todo "srcs"]
+    set before_div_nets [get_nets_with_todo_pip_wires "srcs" "I_BUFR" "HCLK_IOI_RCLK_BEFORE_DIV" $used_destinations]
+    puts "Before div nets: $before_div_nets"
+    dict for {net tile_wire} $before_div_nets {
+        set tile [lindex $tile_wire 0]
+        set wire [lindex $tile_wire 1]
+        set srcs [dict get $todo_map $wire]
+        set tile_type [get_property TILE_TYPE [get_tiles $tile]]
+        set todos {}
+
+        set old_origin_wire [get_wires -of_objects $net -filter {TILE_NAME =~ "*HCLK_IOI*" && NAME =~ "*HCLK_IOI_RCLK_IMUX*"}]
+        if {$old_origin_wire == {}} {
+            continue
+        }
+
+        puts "Rerouting net $net at $tile / $wire (type $tile_type)"
+        puts "Previous target wire: $old_origin_wire"
+
+        set old_origin_node [get_nodes -of_objects $old_origin_wire]
+        if [regexp "HCLK_IOI_RCLK_IMUX.*" $old_origin_wire match group] {
+            set old_target_side $group
+        }
+        foreach src $srcs {
+            set src_tile_type [lindex $src 0]
+            if {$src_tile_type != $tile_type} {
+                continue
+            }
+
+            set src_wire [lindex $src 1]
+
+            set is_gclk_net 0
+            if [regexp "HCLK_IOI_RCLK_IMUX.*" $src_wire match group] {
+                set is_gclk_net 1
+            }
+
+            if {$is_gclk_net == 0} {
+                continue
+            }
+
+            lappend todos $src_wire
+        }
+
+        set todos_length [llength $todos]
+        if {$todos_length == 0} {
+            continue
+        }
+
+        puts "All todos for $tile_type / $wire"
+        foreach src_wire $todos {
+            puts "  - $src_wire"
+        }
+
+        set todos [shuffle_list $todos]
+
+        set target_node [get_nodes -of_objects [get_site_pins -filter {DIRECTION == IN} -of_objects $net]]
+        puts "Target node: $target_node"
+        route_design -unroute -nets $net
+
+        # Find an output in the todo list that can drive.
+        foreach src_wire $todos {
+            if { [dict exists $used_destinations "$tile/$src_wire"] } {
+                puts "Not routing to $tile / $src_wire, in use."
+                continue
+            }
+
+            set origin_wire [get_wires "$tile/$src_wire"]
+            set origin_node [get_nodes -of_objects $origin_wire]
+            if {[llength $origin_node] == 0} {
+                error "Failed to find node for $tile/$src_wire."
+            }
+
+            set old_net [get_nets -of_objects $origin_node -quiet]
+            if {$old_net != {}} {
+                puts "Unrouting the old net: $old_net"
+                route_design -unroute -nets $old_net
+            }
+
+            # Route the net through the desired node
+            puts "Attempting to route to $src_wire for net $net."
+            route_via $net [list $origin_node]
+
+            puts "Target node: $target_node"
+            puts "Origin wire: $origin_wire, Old origin wire: $old_origin_wire"
+            puts "Origin node: $origin_node, Old origin node: $old_origin_node"
+
+            dict set used_destinations "$origin_wire" 1
+
+            break
+        }
+    }
+}
+
+proc run {} {
+    create_project -force -part $::env(XRAY_PART) design design
+    read_verilog top.v
+    synth_design -top top
+
+    set_property CFGBVS VCCO [current_design]
+    set_property CONFIG_VOLTAGE 3.3 [current_design]
+    set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
+    set_property IS_ENABLED 0 [get_drc_checks {PDRC-29}]
+    set_property IS_ENABLED 0 [get_drc_checks {PDRC-38}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-13}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-123}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-161}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-1575}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-1684}]
+    set_property IS_ENABLED 0 [get_drc_checks {REQP-1712}]
+    set_property IS_ENABLED 0 [get_drc_checks {AVAL-50}]
+    set_property IS_ENABLED 0 [get_drc_checks {AVAL-78}]
+    set_property IS_ENABLED 0 [get_drc_checks {AVAL-81}]
+
+
+    set_property CLOCK_DEDICATED_ROUTE FALSE [get_nets]
+
+    place_design
+    route_design
+    route_todo
+    route_design
+
+
+    write_checkpoint -force design.dcp
+    write_bitstream -force design.bit
+    write_pip_txtdata design.txt
+}
+
+run
diff --git a/fuzzers/047-hclk-ioi18-pips/hclk_ioi_pip_list.tcl b/fuzzers/047-hclk-ioi18-pips/hclk_ioi_pip_list.tcl
new file mode 100644
index 0000000..555f10c
--- /dev/null
+++ b/fuzzers/047-hclk-ioi18-pips/hclk_ioi_pip_list.tcl
@@ -0,0 +1,49 @@
+# Copyright (C) 2017-2022  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 print_tile_pips {tile_type filename} {
+    set fp [open $filename w]
+    set pips [dict create]
+    foreach tile [get_tiles -filter "TYPE == $tile_type"] {
+        puts "Dumping PIPs for tile $tile ($tile_type) to $filename."
+        foreach pip [lsort [get_pips -of_objects  $tile]] {
+            set src [get_wires -uphill -of_objects $pip]
+            set dst [get_wires -downhill -of_objects $pip]
+
+            # Skip pips with disconnected nodes
+            set src_node [get_nodes -of_objects $src]
+            if { $src_node == {} } {
+                continue
+            }
+
+            set dst_node [get_nodes -of_objects $dst]
+            if { $dst_node == {} } {
+                continue
+            }
+
+            set dst_wire [regsub {.*/} $dst ""]
+            set dst_match [regexp {HCLK_IOI_CK_IGCLK[0-9]+} $dst_wire]
+
+            if {[llength [get_nodes -uphill -of_objects $dst_node]] > 1 || $dst_match} {
+                set pip_string "$tile_type.[regsub {.*/} $dst ""].[regsub {.*/} $src ""]"
+                if ![dict exists $pips $pip_string] {
+                    puts $fp $pip_string
+                    dict set pips $pip_string 1
+                }
+            } else {
+                puts "Ignoring PIP: $pip"
+            }
+        }
+    }
+    close $fp
+}
+
+create_project -force -part $::env(XRAY_PART) design design
+set_property design_mode PinPlanning [current_fileset]
+open_io_design -name io_1
+
+print_tile_pips HCLK_IOI hclk_ioi.txt
diff --git a/fuzzers/047-hclk-ioi18-pips/output_cmt.tcl b/fuzzers/047-hclk-ioi18-pips/output_cmt.tcl
new file mode 100644
index 0000000..7bb9e52
--- /dev/null
+++ b/fuzzers/047-hclk-ioi18-pips/output_cmt.tcl
@@ -0,0 +1,18 @@
+# Copyright (C) 2017-2022  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
+create_project -force -part $::env(XRAY_PART) design design
+set_property design_mode PinPlanning [current_fileset]
+open_io_design -name io_1
+
+set fp [open "cmt_regions.csv" "w"]
+foreach site_type {MMCME2_ADV PLLE2_ADV BUFHCE IOB18M BUFR BUFMRCE BUFIO ILOGICE2 IDELAYCTRL} {
+    foreach site [get_sites -filter "SITE_TYPE == $site_type"] {
+        puts $fp "$site,[get_property CLOCK_REGION $site]"
+    }
+}
+close $fp
diff --git a/fuzzers/047-hclk-ioi18-pips/top.py b/fuzzers/047-hclk-ioi18-pips/top.py
new file mode 100644
index 0000000..bf84a50
--- /dev/null
+++ b/fuzzers/047-hclk-ioi18-pips/top.py
@@ -0,0 +1,479 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2017-2022  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
+""" Emits top.v's for various BUFHCE routing inputs. """
+import os
+import random
+random.seed(int(os.getenv("SEED"), 16))
+from prjxray import util
+from prjxray.lut_maker import LutMaker
+from prjxray.db import Database
+from io import StringIO
+
+CMT_XY_FUN = util.create_xy_fun(prefix='')
+
+
+def read_site_to_cmt():
+    """ Yields clock sources and which CMT they route within. """
+    with open(os.path.join(os.getenv('FUZDIR'), 'build',
+                           'cmt_regions.csv')) as f:
+        for l in f:
+            site, cmt = l.strip().split(',')
+            yield (site, cmt)
+
+
+class ClockSources(object):
+    """ Class for tracking clock sources.
+    """
+
+    def __init__(self, limit=14):
+        self.sources = {}
+        self.source_to_cmt = {}
+        self.used_sources_from_cmt = {}
+        self.limit = limit
+
+    def add_clock_source(self, source, cmt):
+        """ Adds a source from a specific CMT.
+
+        cmt='ANY' indicates that this source can be routed to any CMT.
+        """
+        if cmt not in self.sources:
+            self.sources[cmt] = []
+
+        self.sources[cmt].append(source)
+        self.source_to_cmt[source] = cmt
+
+    def get_random_source(self, cmt, no_repeats=False):
+        """ Get a random source that is routable to the specific CMT.
+
+        get_random_source will return a source that is either cmt='ANY',
+        cmt equal to the input CMT, or the adjecent CMT.
+
+        """
+
+        choices = []
+
+        if cmt in self.sources:
+            choices.extend(self.sources[cmt])
+
+        random.shuffle(choices)
+        for source in choices:
+
+            source_cmt = self.source_to_cmt[source]
+
+            if source_cmt not in self.used_sources_from_cmt:
+                self.used_sources_from_cmt[source_cmt] = set()
+
+            if no_repeats and source in self.used_sources_from_cmt[source_cmt]:
+                continue
+
+            if len(self.used_sources_from_cmt[source_cmt]) >= self.limit:
+                continue
+
+            self.used_sources_from_cmt[source_cmt].add(source)
+            return source
+
+        return None
+
+
+def main():
+    """
+    HCLK_IOI has the following inputs:
+
+    12 (east) BUFH from the right side of the HROW
+    12 (west) Bounce PIPs from one BUFH to any of 6 GCLK_BOT and 6 GCLK_TOP
+    4 (east) PHSR_PERFCLK (IOCLK_PLL) from HCLK_CLB to input of BUFIO
+    8 (4 north and 4 south) BUFR CLR and CE
+    2 (south) I2IOCLK to input of BUFR
+    2 (north) I2IOCLK to input of BUFR
+    2 RCLK IMUX (IMUX0 and IMUX1) choosing input of BUFR
+
+    outputs:
+    4 (east) BUFRCLK - from BUFR to HROW
+    4 (north) BUFR2IO - from BUFR
+    4 (north) IOCLK from BUFIO
+
+    """
+
+    global_clock_sources = ClockSources()
+    cmt_clock_sources = ClockSources()
+    cmt_fast_clock_sources = ClockSources(4)
+    bufr_clock_sources = ClockSources()
+    bufio_clock_sources = ClockSources()
+    site_to_cmt = dict(read_site_to_cmt())
+    clock_region_limit = dict()
+    clock_region_serdes_location = dict()
+
+    db = Database(util.get_db_root(), util.get_part())
+    grid = db.grid()
+
+    def gen_sites(desired_site_type):
+        for tile_name in sorted(grid.tiles()):
+            loc = grid.loc_of_tilename(tile_name)
+            gridinfo = grid.gridinfo_at_loc(loc)
+            for site, site_type in gridinfo.sites.items():
+                if site_type == desired_site_type:
+                    yield tile_name, site
+
+    def serdes_relative_location(tile, site):
+        (serdes_loc_x, serdes_loc_y) = grid.loc_of_tilename(tile)
+        serdes_clk_reg = site_to_cmt[site]
+        for tile_name in sorted(grid.tiles()):
+            if 'HCLK_IOI' in tile_name:
+                (hclk_tile_loc_x,
+                 hclk_tile_loc_y) = grid.loc_of_tilename(tile_name)
+                if hclk_tile_loc_x == serdes_loc_x:
+                    gridinfo = grid.gridinfo_at_loc(
+                        (hclk_tile_loc_x, hclk_tile_loc_y))
+                    random_site = next(iter(gridinfo.sites.keys()))
+                    hclk_clk_reg = site_to_cmt[random_site]
+                    if hclk_clk_reg == serdes_clk_reg:
+                        if serdes_loc_y < hclk_tile_loc_y:
+                            return "TOP"
+                        elif serdes_loc_y > hclk_tile_loc_y:
+                            return "BOTTOM"
+                        else:
+                            assert False
+
+    clock_region_sites = set()
+
+    def get_clock_region_site(site_type, clk_reg):
+        for site_name, reg in site_to_cmt.items():
+            if site_name.startswith(site_type) and reg in clk_reg:
+                if site_name not in clock_region_sites:
+                    clock_region_sites.add(site_name)
+                    return site_name
+
+    print(
+        '''
+module top();
+    (* KEEP, DONT_TOUCH *)
+    LUT6 dummy();
+    ''')
+
+    luts = LutMaker()
+    bufs = StringIO()
+
+    for _, site in gen_sites('MMCME2_ADV'):
+        mmcm_clocks = [
+            'mmcm_clock_{site}_{idx}'.format(site=site, idx=idx)
+            for idx in range(13)
+        ]
+
+        for idx, clk in enumerate(mmcm_clocks):
+            if idx < 4:
+                cmt_fast_clock_sources.add_clock_source(clk, site_to_cmt[site])
+            else:
+                cmt_clock_sources.add_clock_source(clk, site_to_cmt[site])
+
+        print(
+            """
+    wire cin1_{site}, cin2_{site}, clkfbin_{site}, {c0}, {c1}, {c2}, {c3}, {c4}, {c5};
+    (* KEEP, DONT_TOUCH, LOC = "{site}" *)
+    MMCME2_ADV pll_{site} (
+        .CLKIN1(cin1_{site}),
+        .CLKIN2(cin2_{site}),
+        .CLKFBIN(clkfbin_{site}),
+        .CLKOUT0({c0}),
+        .CLKOUT0B({c4}),
+        .CLKOUT1({c1}),
+        .CLKOUT1B({c5}),
+        .CLKOUT2({c2}),
+        .CLKOUT2B({c6}),
+        .CLKOUT3({c3}),
+        .CLKOUT3B({c7}),
+        .CLKOUT4({c8}),
+        .CLKOUT5({c9}),
+        .CLKOUT6({c10}),
+        .CLKFBOUT({c11}),
+        .CLKFBOUTB({c12})
+    );
+        """.format(
+                site=site,
+                c0=mmcm_clocks[0],
+                c1=mmcm_clocks[1],
+                c2=mmcm_clocks[2],
+                c3=mmcm_clocks[3],
+                c4=mmcm_clocks[4],
+                c5=mmcm_clocks[5],
+                c6=mmcm_clocks[6],
+                c7=mmcm_clocks[7],
+                c8=mmcm_clocks[8],
+                c9=mmcm_clocks[9],
+                c10=mmcm_clocks[10],
+                c11=mmcm_clocks[11],
+                c12=mmcm_clocks[12],
+            ))
+
+    for _, site in gen_sites('PLLE2_ADV'):
+        pll_clocks = [
+            'pll_clock_{site}_{idx}'.format(site=site, idx=idx)
+            for idx in range(7)
+        ]
+
+        for clk in pll_clocks:
+            cmt_clock_sources.add_clock_source(clk, site_to_cmt[site])
+
+        print(
+            """
+    wire cin1_{site}, cin2_{site}, clkfbin_{site}, {c0}, {c1}, {c2}, {c3}, {c4}, {c5}, {c6};
+    (* KEEP, DONT_TOUCH, LOC = "{site}" *)
+    PLLE2_ADV pll_{site} (
+        .CLKIN1(cin1_{site}),
+        .CLKIN2(cin2_{site}),
+        .CLKFBIN(clkfbin_{site}),
+        .CLKOUT0({c0}),
+        .CLKOUT1({c1}),
+        .CLKOUT2({c2}),
+        .CLKOUT3({c3}),
+        .CLKOUT4({c4}),
+        .CLKOUT5({c5}),
+        .CLKFBOUT({c6})
+    );
+        """.format(
+                site=site,
+                c0=pll_clocks[0],
+                c1=pll_clocks[1],
+                c2=pll_clocks[2],
+                c3=pll_clocks[3],
+                c4=pll_clocks[4],
+                c5=pll_clocks[5],
+                c6=pll_clocks[6],
+            ))
+
+    for tile_name, site in gen_sites('BUFHCE'):
+        print(
+            """
+    wire I_{site};
+    wire O_{site};
+    (* KEEP, DONT_TOUCH, LOC = "{site}" *)
+    BUFHCE buf_{site} (
+        .I(I_{site}),
+        .O(O_{site})
+    );""".format(site=site),
+            file=bufs)
+        global_clock_sources.add_clock_source(
+            'O_{site}'.format(site=site), site_to_cmt[site])
+
+    hclks_used_by_clock_region = {}
+    for cmt in site_to_cmt.values():
+        hclks_used_by_clock_region[cmt] = set()
+
+    def check_hclk_src(src, src_cmt):
+        if len(hclks_used_by_clock_region[src_cmt]
+               ) >= 12 and src not in hclks_used_by_clock_region[src_cmt]:
+            return None
+        else:
+            hclks_used_by_clock_region[src_cmt].add(src)
+            return src
+
+    cmt_clks_used_by_clock_region = {}
+    for cmt in site_to_cmt.values():
+        cmt_clks_used_by_clock_region[cmt] = list()
+
+    def check_cmt_clk_src(src, src_clock_region):
+        print(
+            "//src: {}, clk_reg: {}, len {}".format(
+                src, src_clock_region,
+                len(cmt_clks_used_by_clock_region[src_clock_region])))
+        if len(cmt_clks_used_by_clock_region[src_clock_region]) >= 4:
+            return None
+        else:
+            cmt_clks_used_by_clock_region[src_clock_region].append(src)
+            return src
+
+    #Add IDELAYCTRL
+    idelayctrl_in_clock_region = {}
+    for cmt in site_to_cmt.values():
+        idelayctrl_in_clock_region[cmt] = False
+    for _, site in gen_sites('IDELAYCTRL'):
+        if random.random() < 0.5:
+            wire_name = global_clock_sources.get_random_source(
+                site_to_cmt[site], no_repeats=False)
+            if wire_name is None:
+                continue
+            src_cmt = global_clock_sources.source_to_cmt[wire_name]
+            wire_name = check_hclk_src(wire_name, src_cmt)
+
+            if wire_name is None:
+                continue
+            idelayctrl_in_clock_region[src_cmt] = True
+            print(
+                """
+        assign I_{site} = {clock_source};
+        (* KEEP, DONT_TOUCH, LOC = "{site}" *)
+        IDELAYCTRL idelay_ctrl_{site} (
+            .RDY(),
+            .REFCLK(I_{site}),
+            .RST()
+            );""".format(site=site, clock_source=wire_name))
+
+    # Add SERDES driven by BUFH or MMCM
+    for tile, site in gen_sites('ILOGICE2'):
+        wire_name = None
+        clock_region = site_to_cmt[site]
+        if clock_region not in clock_region_limit:
+            # Select serdes limit and relative location per clock region
+            serdes_location = random.choice(["TOP", "BOTTOM", "ANY"])
+            if serdes_location in "ANY":
+                #We want TOP and BOTTOM IGCLK PIPs occupied but leave one slot for IDELAYCTRL
+                if idelayctrl_in_clock_region[clock_region]:
+                    clock_region_limit[clock_region] = 0 if random.random(
+                    ) < 0.2 else 11
+                else:
+                    clock_region_limit[clock_region] = 0 if random.random(
+                    ) < 0.2 else 12
+            else:
+                if idelayctrl_in_clock_region[clock_region]:
+                    clock_region_limit[clock_region] = 0 if random.random(
+                    ) < 0.2 else 5
+                else:
+                    clock_region_limit[clock_region] = 0 if random.random(
+                    ) < 0.2 else 6
+
+            clock_region_serdes_location[clock_region] = serdes_location
+
+        # We reached the limit of hclks in this clock region
+        if clock_region_limit[clock_region] == 0:
+            continue
+
+        # Add a serdes if it's located at the correct side from the HCLK_IOI tile
+        if clock_region_serdes_location[clock_region] not in "ANY" and \
+                serdes_relative_location(tile, site) != clock_region_serdes_location[clock_region]:
+            continue
+        if random.random() > 0.3:
+            wire_name = global_clock_sources.get_random_source(
+                site_to_cmt[site], no_repeats=True)
+            if wire_name is None:
+                continue
+            src_cmt = global_clock_sources.source_to_cmt[wire_name]
+            wire_name = check_hclk_src(wire_name, src_cmt)
+            if wire_name is None:
+                print("//wire is None")
+                continue
+            clock_region_limit[clock_region] -= 1
+            print(
+                """
+    assign serdes_clk_{site} = {clock_source};""".format(
+                    site=site, clock_source=wire_name))
+        else:
+            wire_name = cmt_fast_clock_sources.get_random_source(
+                site_to_cmt[site], no_repeats=False)
+            if wire_name is None:
+                continue
+            src_cmt = cmt_fast_clock_sources.source_to_cmt[wire_name]
+            wire_name = check_cmt_clk_src(wire_name, src_cmt)
+            if wire_name is None:
+                continue
+            bufio_site = get_clock_region_site("BUFIO", clock_region)
+            if bufio_site is None:
+                continue
+            print(
+                """
+    assign serdes_clk_{serdes_loc} = O_{site};
+    assign I_{site} = {clock_source};
+    (* KEEP, DONT_TOUCH, LOC = "{site}" *)
+    BUFIO bufio_{site} (
+        .O(O_{site}),
+        .I(I_{site})
+    );""".format(site=bufio_site, clock_source=wire_name, serdes_loc=site))
+
+        print(
+            "// clock_region: {} {}".format(
+                clock_region, clock_region_serdes_location[clock_region]))
+        print(
+            """
+    (* KEEP, DONT_TOUCH, LOC = "{loc}" *)
+    ISERDESE2 #(
+    .DATA_RATE("SDR"),
+    .DATA_WIDTH(4),
+    .DYN_CLKDIV_INV_EN("FALSE"),
+    .DYN_CLK_INV_EN("FALSE"),
+    .INIT_Q1(1'b0),
+    .INIT_Q2(1'b0),
+    .INIT_Q3(1'b0),
+    .INIT_Q4(1'b0),
+    .INTERFACE_TYPE("OVERSAMPLE"),
+    .IOBDELAY("NONE"),
+    .NUM_CE(2),
+    .OFB_USED("FALSE"),
+    .SERDES_MODE("MASTER"),
+    .SRVAL_Q1(1'b0),
+    .SRVAL_Q2(1'b0),
+    .SRVAL_Q3(1'b0),
+    .SRVAL_Q4(1'b0)
+    )
+    ISERDESE2_inst_{loc} (
+    .CLK(serdes_clk_{loc}),
+    .CLKB(),
+    .CLKDIV(),
+    .D(1'b0),
+    .DDLY(),
+    .OFB(),
+    .OCLKB(),
+    .RST(),
+    .SHIFTIN1(),
+    .SHIFTIN2()
+    );
+    """.format(loc=site, clock_source=wire_name))
+
+    # BUFRs
+    for _, site in gen_sites('BUFR'):
+        if random.random() < 0.5:
+            if random.random() < 0.5:
+                wire_name = luts.get_next_output_net()
+            else:
+                wire_name = cmt_fast_clock_sources.get_random_source(
+                    site_to_cmt[site], no_repeats=False)
+                if wire_name is None:
+                    continue
+                src_cmt = cmt_fast_clock_sources.source_to_cmt[wire_name]
+                wire_name = check_cmt_clk_src(wire_name, src_cmt)
+                if wire_name is None:
+                    continue
+            bufr_clock_sources.add_clock_source(
+                'O_{site}'.format(site=site), site_to_cmt[site])
+
+            # Add DIVIDE
+            divide = "BYPASS"
+            if random.random() < 0.8:
+                divide = "{}".format(random.randint(2, 8))
+
+            print(
+                """
+    assign I_{site} = {clock_source};
+    (* KEEP, DONT_TOUCH, LOC = "{site}" *)
+    BUFR #(.BUFR_DIVIDE("{divide}")) bufr_{site} (
+        .O(O_{site}),
+        .I(I_{site})
+        );""".format(site=site, clock_source=wire_name, divide=divide),
+                file=bufs)
+
+    for _, site in gen_sites('MMCME2_ADV'):
+        wire_name = bufr_clock_sources.get_random_source(
+            site_to_cmt[site], no_repeats=True)
+
+        if wire_name is None:
+            continue
+        print(
+            """
+    assign cin1_{site} = {wire_name};""".format(
+                site=site, wire_name=wire_name))
+
+    print(bufs.getvalue())
+
+    for l in luts.create_wires_and_luts():
+        print(l)
+
+    print("endmodule")
+
+
+if __name__ == '__main__':
+    main()
diff --git a/fuzzers/071-ppips/generate.tcl b/fuzzers/071-ppips/generate.tcl
index 94be82f..a424380 100644
--- a/fuzzers/071-ppips/generate.tcl
+++ b/fuzzers/071-ppips/generate.tcl
@@ -322,7 +322,7 @@
 }
 
 foreach tile_type {RIOI3 LIOI3 LIOI3_TBYTETERM RIOI3_TBYTETERM \
-            LIOI3_TBYTESRC RIOI3_TBYTESRC LIOI3_SING RIOI3_SING} {
+            LIOI3_TBYTESRC RIOI3_TBYTESRC LIOI3_SING RIOI3_SING RIOI RIOI_SING RIOI_TBYTESRC RIOI_TBYTETERM} {
     set tiles [get_tiles -filter "TILE_TYPE == $tile_type"]
     if {[llength $tiles] != 0} {
         set tile [lindex $tiles 0]
@@ -392,7 +392,7 @@
     }
 }
 
-foreach tile_type {RIOB33 LIOB33 RIOB33_SING LIOB33_SING} {
+foreach tile_type {RIOB33 LIOB33 RIOB33_SING LIOB33_SING RIOB18 RIOB18_SING} {
     set tiles [get_tiles -filter "TILE_TYPE == $tile_type"]
     if {[llength $tiles] != 0} {
         set tile [lindex $tiles 0]
diff --git a/fuzzers/Makefile b/fuzzers/Makefile
index ee43c95..94a1094 100644
--- a/fuzzers/Makefile
+++ b/fuzzers/Makefile
@@ -113,6 +113,9 @@
 $(eval $(call fuzzer,028-fifo-config,005-tilegrid,all))
 $(eval $(call fuzzer,029-bram-fifo-config,005-tilegrid,all))
 $(eval $(call fuzzer,030-iob,005-tilegrid,all))
+ifeq ($(XRAY_DATABASE),kintex7)
+$(eval $(call fuzzer,030-iob18,005-tilegrid,all))
+endif
 $(eval $(call fuzzer,031-cmt-mmcm,005-tilegrid,all))
 $(eval $(call fuzzer,032-cmt-pll,005-tilegrid,all))
 $(eval $(call fuzzer,034-cmt-pll-pips,005-tilegrid 071-ppips,all))
@@ -123,9 +126,18 @@
 endif
 $(eval $(call fuzzer,035-iob-ilogic,005-tilegrid,all))
 $(eval $(call fuzzer,035a-iob-idelay,005-tilegrid,all))
+ifeq ($(XRAY_DATABASE),kintex7)
+$(eval $(call fuzzer,035a-iob18-idelay,005-tilegrid,all))
+endif
 $(eval $(call fuzzer,035b-iob-iserdes,005-tilegrid,all))
 $(eval $(call fuzzer,036-iob-ologic,005-tilegrid,all))
+ifeq ($(XRAY_DATABASE),kintex7)
+$(eval $(call fuzzer,036-iob18-ologic,005-tilegrid,all))
+endif
 $(eval $(call fuzzer,037-iob-pips,005-tilegrid 035b-iob-iserdes,all))
+ifeq ($(XRAY_DATABASE),kintex7)
+$(eval $(call fuzzer,037-iob18-pips,005-tilegrid 035b-iob-iserdes,all))
+endif
 $(eval $(call fuzzer,038-cfg,005-tilegrid,all))
 $(eval $(call fuzzer,039-hclk-config,005-tilegrid,all))
 $(eval $(call fuzzer,040-clk-hrow-config,005-tilegrid,all))
@@ -136,6 +148,9 @@
 $(eval $(call fuzzer,045-hclk-cmt-pips,005-tilegrid,all))
 $(eval $(call fuzzer,046-clk-bufg-muxed-pips,005-tilegrid,all))
 $(eval $(call fuzzer,047-hclk-ioi-pips,005-tilegrid,all))
+ifeq ($(XRAY_DATABASE),kintex7)
+$(eval $(call fuzzer,047-hclk-ioi18-pips,005-tilegrid,all))
+endif
 $(eval $(call fuzzer,047a-hclk-idelayctrl-pips,047-hclk-ioi-pips,all))
 $(eval $(call fuzzer,048-int-piplist,005-tilegrid,all))
 $(eval $(call fuzzer,049-int-imux-gfan,048-int-piplist,all))
diff --git a/prjxray/segmaker.py b/prjxray/segmaker.py
index 542fcc8..c362bef 100644
--- a/prjxray/segmaker.py
+++ b/prjxray/segmaker.py
@@ -359,6 +359,8 @@
             -CLK_HROW_TOP_R => CLK_HROW
             -LIOB33 => IOB33
             -LIOI3 => IOI3
+            -RIOB18 => IOB18
+            -RIOI => IOI
             '''
             tile_type_norm = re.sub("(_TOP|_BOT|LL|LM)?_[LR]$", "", tile_type)
             tile_type_norm = re.sub(
@@ -366,13 +368,20 @@
 
             if tile_type_norm in ['LIOB33', 'RIOB33']:
                 tile_type_norm = 'IOB33'
-
+            if tile_type_norm in ['LIOB18', 'RIOB18']:
+                tile_type_norm = 'IOB18'
             if tile_type_norm in ['LIOI3', 'RIOI3']:
                 tile_type_norm = 'IOI3'
+            if tile_type_norm in ['LIOI', 'RIOI']:
+                tile_type_norm = 'IOI'
             if tile_type_norm in ['LIOI3_TBYTESRC', 'RIOI3_TBYTESRC']:
                 tile_type_norm = 'IOI3'
             if tile_type_norm in ['LIOI3_TBYTETERM', 'RIOI3_TBYTETERM']:
                 tile_type_norm = 'IOI3'
+            if tile_type_norm in ['LIOI_TBYTESRC', 'RIOI_TBYTESRC']:
+                tile_type_norm = 'IOI'
+            if tile_type_norm in ['LIOI_TBYTETERM', 'RIOI_TBYTETERM']:
+                tile_type_norm = 'IOI'
             if tile_type_norm in ['CMT_TOP_L_LOWER_B', 'CMT_TOP_R_LOWER_B']:
                 tile_type_norm = 'CMT_LOWER_B'
             if 'GTP_CHANNEL' in tile_type_norm:
diff --git a/prjxray/util.py b/prjxray/util.py
index bb2efb0..615bf72 100644
--- a/prjxray/util.py
+++ b/prjxray/util.py
@@ -86,8 +86,8 @@
 def get_part_information(db_root, part):
     filename = os.path.join(db_root, "mapping", "parts.yaml")
     assert os.path.isfile(filename), \
-        "Mapping file {} does not exists".format(filename)
-    with OpenSafeFile(filename, 'r') as stream:
+        "Mapping file {} does not exist".format(filename)
+    with open(filename, 'r') as stream:
         part_mapping = yaml.load(stream, Loader=yaml.FullLoader)
     part = part_mapping.get(part, None)
     assert part, "Part {} not found in {}".format(part, part_mapping)
@@ -99,14 +99,14 @@
     with OpenSafeFile(filename, 'w+') as stream:
         yaml.dump(information, stream)
     assert os.path.isfile(filename), \
-        "Mapping file {} does not exists".format(filename)
+        "Mapping file {} does not exist".format(filename)
 
 
 def get_part_resources(file_path, part):
     filename = os.path.join(file_path, "resources.yaml")
     assert os.path.isfile(filename), \
-        "Mapping file {} does not exists".format(filename)
-    with OpenSafeFile(filename, 'r') as stream:
+        "Mapping file {} does not exist".format(filename)
+    with open(filename, 'r') as stream:
         res_mapping = yaml.load(stream, Loader=yaml.FullLoader)
     res = res_mapping.get(part, None)
     assert res, "Part {} not found in {}".format(part, part_mapping)
@@ -118,13 +118,13 @@
     with OpenSafeFile(filename, 'w+') as stream:
         yaml.dump(information, stream)
     assert os.path.isfile(filename), \
-        "Mapping file {} does not exists".format(filename)
+        "Mapping file {} does not exist".format(filename)
 
 
 def get_fabric_for_part(db_root, part):
     filename = os.path.join(db_root, "mapping", "devices.yaml")
     assert os.path.isfile(filename), \
-        "Mapping file {} does not exists".format(filename)
+        "Mapping file {} does not exist".format(filename)
     part = get_part_information(db_root, part)
     with OpenSafeFile(filename, 'r') as stream:
         device_mapping = yaml.load(stream, Loader=yaml.FullLoader)
@@ -137,8 +137,8 @@
 def get_devices(db_root):
     filename = os.path.join(db_root, "mapping", "devices.yaml")
     assert os.path.isfile(filename), \
-        "Mapping file {} does not exists".format(filename)
-    with OpenSafeFile(filename, 'r') as stream:
+        "Mapping file {} does not exist".format(filename)
+    with open(filename, 'r') as stream:
         device_mapping = yaml.load(stream, Loader=yaml.FullLoader)
     return device_mapping
 
@@ -146,8 +146,8 @@
 def get_parts(db_root):
     filename = os.path.join(db_root, "mapping", "parts.yaml")
     assert os.path.isfile(filename), \
-        "Mapping file {} does not exists".format(filename)
-    with OpenSafeFile(filename, 'r') as stream:
+        "Mapping file {} does not exist".format(filename)
+    with open(filename, 'r') as stream:
         part_mapping = yaml.load(stream, Loader=yaml.FullLoader)
     return part_mapping
 
diff --git a/settings/kintex7.sh b/settings/kintex7.sh
index 85a705e..66e8199 100644
--- a/settings/kintex7.sh
+++ b/settings/kintex7.sh
@@ -10,17 +10,17 @@
 export XRAY_ROI_FRAMES="0x00000000:0xffffffff"
 
 # FIXME: make entire part
-export XRAY_ROI_TILEGRID="SLICE_X0Y50:SLICE_X19Y99 DSP48_X0Y20:DSP48_X0Y39 RAMB18_X0Y20:RAMB18_X0Y39 RAMB36_X0Y10:RAMB36_X0Y19"
+export XRAY_ROI_TILEGRID="SLICE_X0Y50:SLICE_X19Y99 DSP48_X0Y20:DSP48_X0Y39 RAMB18_X0Y0:RAMB18_X3Y39 RAMB36_X0Y0:RAMB36_X3Y19"
 
 export XRAY_EXCLUDE_ROI_TILEGRID=""
 
-export XRAY_IOI3_TILES="LIOI3_X0Y9"
+export XRAY_IOI3_TILES="LIOI3_X0Y9 RIOI_X43Y9"
 
 # These settings must remain in sync
-export XRAY_ROI="SLICE_X0Y50:SLICE_X19Y99 DSP48_X0Y20:DSP48_X0Y39 RAMB18_X0Y20:RAMB18_X0Y39 RAMB36_X0Y10:RAMB36_X0Y19 IOB_X0Y50:IOB_X0Y99"
-# Part of CMT X0Y1
+export XRAY_ROI="SLICE_X0Y50:SLICE_X23Y99 DSP48_X0Y20:DSP48_X0Y39 RAMB18_X0Y0:RAMB18_X3Y39 RAMB36_X0Y0:RAMB36_X3Y19 IOB_X0Y50:IOB_X0Y99 IOB_X1Y50:IOB_X1Y99"
+# CMT X0Y1
 export XRAY_ROI_GRID_X1="0"
-export XRAY_ROI_GRID_X2="38"
+export XRAY_ROI_GRID_X2="65"
 # Include VBRK / VTERM
 export XRAY_ROI_GRID_Y1="104"
 export XRAY_ROI_GRID_Y2="156"
diff --git a/tools/segmatch.cc b/tools/segmatch.cc
index 1af59cd..d62cc27 100644
--- a/tools/segmatch.cc
+++ b/tools/segmatch.cc
@@ -255,7 +255,7 @@
 			read_input(f, argv[optind]);
 		}
 	} else {
-		printf("Reading from stding.\n");
+		printf("Reading from stdin...\n");
 		read_input(std::cin, "stdin");
 	}
 
diff --git a/utils/mergedb.sh b/utils/mergedb.sh
index 98383d4..eae4c6d 100755
--- a/utils/mergedb.sh
+++ b/utils/mergedb.sh
@@ -121,6 +121,9 @@
 	riob33)
 		sed < "$2" > "$tmp1" -e 's/^IOB33\./RIOB33./' ;;
 
+	riob18)
+		sed < "$2" > "$tmp1" -e 's/^IOB18\./RIOB18./' ;;
+
 	lioi3)
 		sed < "$2" > "$tmp1" -e 's/^IOI3\./LIOI3./' ;;
 
@@ -133,12 +136,21 @@
 	rioi3)
 		sed < "$2" > "$tmp1" -e 's/^IOI3\./RIOI3./' ;;
 
+	rioi)
+		sed < "$2" > "$tmp1" -e 's/^IOI\./RIOI./' ;;
+
 	rioi3_tbytesrc)
 		sed < "$2" > "$tmp1" -e 's/^IOI3\./RIOI3_TBYTESRC./' ;;
 
+	rioi_tbytesrc)
+		sed < "$2" > "$tmp1" -e 's/^IOI\./RIOI_TBYTESRC./' ;;
+
 	rioi3_tbyteterm)
 		sed < "$2" > "$tmp1" -e 's/^IOI3\./RIOI3_TBYTETERM./' ;;
 
+	rioi_tbyteterm)
+		sed < "$2" > "$tmp1" -e 's/^IOI\./RIOI_TBYTETERM./' ;;
+
 	cmt_top_r_upper_t)
 		sed < "$2" > "$tmp1" -e 's/^CMT_UPPER_T\./CMT_TOP_R_UPPER_T./' ;;
 
@@ -157,6 +169,9 @@
 	hclk_ioi3)
 		cp "$2" "$tmp1" ;;
 
+	hclk_ioi)
+		cp "$2" "$tmp1" ;;
+
 	pcie_bot)
 		cp "$2" "$tmp1" ;;