Merge branch 'master' of github.com:SymbiFlow/prjxray into tilegrid-ng
diff --git a/fuzzers/001-part-yaml/.gitignore b/fuzzers/001-part-yaml/.gitignore
index 9d45622..1c23272 100644
--- a/fuzzers/001-part-yaml/.gitignore
+++ b/fuzzers/001-part-yaml/.gitignore
@@ -1,2 +1,3 @@
 /specimen_*/
 *.yaml
+/run.ok
diff --git a/fuzzers/005-tilegrid/.gitignore b/fuzzers/005-tilegrid/.gitignore
index c81cafb..6595473 100644
--- a/fuzzers/005-tilegrid/.gitignore
+++ b/fuzzers/005-tilegrid/.gitignore
@@ -1,2 +1,3 @@
 /specimen_*/
 /tilegrid.json
+/run.ok
diff --git a/fuzzers/005-tilegrid/generate.py b/fuzzers/005-tilegrid/generate.py
index 954459f..76d6b39 100644
--- a/fuzzers/005-tilegrid/generate.py
+++ b/fuzzers/005-tilegrid/generate.py
@@ -200,6 +200,39 @@
         database["segments"][segname]["baseaddr"] = [framebase, wordbase]
 
 #######################################
+# Transfer segment data into tiles
+
+for segment_name in database["segments"].keys():
+    baseaddr, offset = database["segments"][segment_name]["baseaddr"]
+    for tile_name in database["segments"][segment_name]["tiles"]:
+        tile_type = database["tiles"][tile_name]["type"]
+        if tile_type in ["CLBLL_L", "CLBLL_R", "CLBLM_L", "CLBLM_R", "INT_L",
+                         "INT_R"]:
+            database["tiles"][tile_name]["baseaddr"] = baseaddr
+            database["tiles"][tile_name]["offset"] = offset
+            database["tiles"][tile_name]["height"] = 2
+        elif tile_type in ["HCLK_L", "HCLK_R"]:
+            database["tiles"][tile_name]["baseaddr"] = baseaddr
+            database["tiles"][tile_name]["offset"] = offset
+            database["tiles"][tile_name]["height"] = 1
+        elif tile_type in ["BRAM_L", "BRAM_R", "DSP_L", "DSP_R"]:
+            database["tiles"][tile_name]["baseaddr"] = baseaddr
+            database["tiles"][tile_name]["offset"] = offset
+            database["tiles"][tile_name]["height"] = 10
+        elif tile_type in ["INT_INTERFACE_L", "INT_INTERFACE_R",
+                           "BRAM_INT_INTERFACE_L", "BRAM_INT_INTERFACE_R"]:
+            continue
+        else:
+            # print(tile_type, offset)
+            assert False
+
+database = database["tiles"]
+
+for tiledata in database.values():
+    if "segment" in tiledata:
+        del tiledata["segment"]
+
+#######################################
 # Write
 
 print(json.dumps(database, sort_keys=True, indent="\t"))
diff --git a/fuzzers/010-lutinit/.gitignore b/fuzzers/010-lutinit/.gitignore
index 932efba..e424f3a 100644
--- a/fuzzers/010-lutinit/.gitignore
+++ b/fuzzers/010-lutinit/.gitignore
@@ -1,2 +1,3 @@
 /specimen_[0-9][0-9][0-9]/
 /seg_clbl[lm].segbits
+/run.ok
diff --git a/fuzzers/011-ffconfig/.gitignore b/fuzzers/011-ffconfig/.gitignore
index 68b96e1..5996573 100644
--- a/fuzzers/011-ffconfig/.gitignore
+++ b/fuzzers/011-ffconfig/.gitignore
@@ -8,3 +8,4 @@
 /specimen_*
 /__pycache__/
 /*.segbits
+/run.ok
diff --git a/fuzzers/012-clbn5ffmux/.gitignore b/fuzzers/012-clbn5ffmux/.gitignore
index 93b5bef..93a7f16 100644
--- a/fuzzers/012-clbn5ffmux/.gitignore
+++ b/fuzzers/012-clbn5ffmux/.gitignore
@@ -2,3 +2,4 @@
 /*.segbits
 /vivado.log
 /vivado.jou
+/run.ok
diff --git a/fuzzers/013-clbncy0/.gitignore b/fuzzers/013-clbncy0/.gitignore
index ce77578..ba2275d 100644
--- a/fuzzers/013-clbncy0/.gitignore
+++ b/fuzzers/013-clbncy0/.gitignore
@@ -1,2 +1,3 @@
 /specimen_*/
 /*.segbits
+/run.ok
diff --git a/fuzzers/014-ffsrcemux/.gitignore b/fuzzers/014-ffsrcemux/.gitignore
index bdbf9bd..1f98dc9 100644
--- a/fuzzers/014-ffsrcemux/.gitignore
+++ b/fuzzers/014-ffsrcemux/.gitignore
@@ -7,3 +7,4 @@
 /vivado*
 /specimen_*
 /*.segbits
+/run.ok
diff --git a/fuzzers/017-clbprecyinit/.gitignore b/fuzzers/017-clbprecyinit/.gitignore
index 93b5bef..93a7f16 100644
--- a/fuzzers/017-clbprecyinit/.gitignore
+++ b/fuzzers/017-clbprecyinit/.gitignore
@@ -2,3 +2,4 @@
 /*.segbits
 /vivado.log
 /vivado.jou
+/run.ok
diff --git a/fuzzers/050-intpips/.gitignore b/fuzzers/050-intpips/.gitignore
index dbd7d84..c906555 100644
--- a/fuzzers/050-intpips/.gitignore
+++ b/fuzzers/050-intpips/.gitignore
@@ -1,3 +1,4 @@
 /specimen_[0-9][0-9][0-9]/
 /seg_int_[lr].segbits
 /mask_clbl[lm]_[lr].segbits
+/run.ok
diff --git a/fuzzers/050-intpips/Makefile b/fuzzers/050-intpips/Makefile
index 6e3b0f0..171d299 100644
--- a/fuzzers/050-intpips/Makefile
+++ b/fuzzers/050-intpips/Makefile
@@ -4,12 +4,12 @@
 SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
 
 database: $(SPECIMENS_OK)
-	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_l.segbits $(addsuffix /segdata_clbl[lm]_l.txt,$(SPECIMENS))
-	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_r.segbits $(addsuffix /segdata_clbl[lm]_r.txt,$(SPECIMENS))
-	${XRAY_MASKMERGE} mask_clbll_l.segbits $(addsuffix /segdata_clbll_l.txt,$(SPECIMENS))
-	${XRAY_MASKMERGE} mask_clbll_r.segbits $(addsuffix /segdata_clbll_r.txt,$(SPECIMENS))
-	${XRAY_MASKMERGE} mask_clblm_l.segbits $(addsuffix /segdata_clblm_l.txt,$(SPECIMENS))
-	${XRAY_MASKMERGE} mask_clblm_r.segbits $(addsuffix /segdata_clblm_r.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_l.segbits $(addsuffix /segdata_int_l.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_r.segbits $(addsuffix /segdata_int_r.txt,$(SPECIMENS))
+	${XRAY_MASKMERGE} mask_clbll_l.segbits $(addsuffix /segdata_int_l.txt,$(SPECIMENS))
+	${XRAY_MASKMERGE} mask_clbll_r.segbits $(addsuffix /segdata_int_r.txt,$(SPECIMENS))
+	${XRAY_MASKMERGE} mask_clblm_l.segbits $(addsuffix /segdata_int_l.txt,$(SPECIMENS))
+	${XRAY_MASKMERGE} mask_clblm_r.segbits $(addsuffix /segdata_int_r.txt,$(SPECIMENS))
 
 pushdb:
 	${XRAY_MERGEDB} int_l seg_int_l.segbits
diff --git a/fuzzers/051-imuxlout/.gitignore b/fuzzers/051-imuxlout/.gitignore
index 9c92648..145bd74 100644
--- a/fuzzers/051-imuxlout/.gitignore
+++ b/fuzzers/051-imuxlout/.gitignore
@@ -11,3 +11,4 @@
 /specimen_[0-9][0-9][0-9]/
 /todo.txt
 /vivado*
+/run.ok
diff --git a/fuzzers/051-imuxlout/Makefile b/fuzzers/051-imuxlout/Makefile
index da59695..1016152 100644
--- a/fuzzers/051-imuxlout/Makefile
+++ b/fuzzers/051-imuxlout/Makefile
@@ -4,8 +4,8 @@
 SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
 
 database: $(SPECIMENS_OK)
-	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_l.segbits $(addsuffix /segdata_clbl[lm]_l.txt,$(SPECIMENS))
-	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_r.segbits $(addsuffix /segdata_clbl[lm]_r.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_l.segbits $(addsuffix /segdata_int_l.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_r.segbits $(addsuffix /segdata_int_r.txt,$(SPECIMENS))
 
 pushdb:
 	${XRAY_MERGEDB} int_l seg_int_l.segbits
diff --git a/fuzzers/052-clkin/.gitignore b/fuzzers/052-clkin/.gitignore
index 9c92648..145bd74 100644
--- a/fuzzers/052-clkin/.gitignore
+++ b/fuzzers/052-clkin/.gitignore
@@ -11,3 +11,4 @@
 /specimen_[0-9][0-9][0-9]/
 /todo.txt
 /vivado*
+/run.ok
diff --git a/fuzzers/052-clkin/Makefile b/fuzzers/052-clkin/Makefile
index 5d71a14..a0f9a3e 100644
--- a/fuzzers/052-clkin/Makefile
+++ b/fuzzers/052-clkin/Makefile
@@ -4,8 +4,8 @@
 SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
 
 database: $(SPECIMENS_OK)
-	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_l.segbits $(addsuffix /segdata_clbl[lm]_l.txt,$(SPECIMENS))
-	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_r.segbits $(addsuffix /segdata_clbl[lm]_r.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_l.segbits $(addsuffix /segdata_int_l.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_r.segbits $(addsuffix /segdata_int_r.txt,$(SPECIMENS))
 
 pushdb:
 	${XRAY_MERGEDB} int_l seg_int_l.segbits
diff --git a/fuzzers/053-ctrlin/.gitignore b/fuzzers/053-ctrlin/.gitignore
index 9c92648..145bd74 100644
--- a/fuzzers/053-ctrlin/.gitignore
+++ b/fuzzers/053-ctrlin/.gitignore
@@ -11,3 +11,4 @@
 /specimen_[0-9][0-9][0-9]/
 /todo.txt
 /vivado*
+/run.ok
diff --git a/fuzzers/053-ctrlin/Makefile b/fuzzers/053-ctrlin/Makefile
index 5d71a14..a0f9a3e 100644
--- a/fuzzers/053-ctrlin/Makefile
+++ b/fuzzers/053-ctrlin/Makefile
@@ -4,8 +4,8 @@
 SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
 
 database: $(SPECIMENS_OK)
-	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_l.segbits $(addsuffix /segdata_clbl[lm]_l.txt,$(SPECIMENS))
-	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_r.segbits $(addsuffix /segdata_clbl[lm]_r.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_l.segbits $(addsuffix /segdata_int_l.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_r.segbits $(addsuffix /segdata_int_r.txt,$(SPECIMENS))
 
 pushdb:
 	${XRAY_MERGEDB} int_l seg_int_l.segbits
diff --git a/fuzzers/054-gfan/Makefile b/fuzzers/054-gfan/Makefile
index 5d71a14..a0f9a3e 100644
--- a/fuzzers/054-gfan/Makefile
+++ b/fuzzers/054-gfan/Makefile
@@ -4,8 +4,8 @@
 SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
 
 database: $(SPECIMENS_OK)
-	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_l.segbits $(addsuffix /segdata_clbl[lm]_l.txt,$(SPECIMENS))
-	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_r.segbits $(addsuffix /segdata_clbl[lm]_r.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_l.segbits $(addsuffix /segdata_int_l.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_r.segbits $(addsuffix /segdata_int_r.txt,$(SPECIMENS))
 
 pushdb:
 	${XRAY_MERGEDB} int_l seg_int_l.segbits
diff --git a/fuzzers/055-gnd/.gitignore b/fuzzers/055-gnd/.gitignore
index 9c92648..145bd74 100644
--- a/fuzzers/055-gnd/.gitignore
+++ b/fuzzers/055-gnd/.gitignore
@@ -11,3 +11,4 @@
 /specimen_[0-9][0-9][0-9]/
 /todo.txt
 /vivado*
+/run.ok
diff --git a/fuzzers/055-gnd/Makefile b/fuzzers/055-gnd/Makefile
index 069063e..c7a87af 100644
--- a/fuzzers/055-gnd/Makefile
+++ b/fuzzers/055-gnd/Makefile
@@ -4,8 +4,8 @@
 SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
 
 database: $(SPECIMENS_OK)
-	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_l.segbits $(addsuffix /segdata_clbl[lm]_l.txt,$(SPECIMENS))
-	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_r.segbits $(addsuffix /segdata_clbl[lm]_r.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_l.segbits $(addsuffix /segdata_int_l.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_r.segbits $(addsuffix /segdata_int_r.txt,$(SPECIMENS))
 
 pushdb:
 	${XRAY_MERGEDB} int_l seg_int_l.segbits
diff --git a/fuzzers/056-rempips/.gitignore b/fuzzers/056-rempips/.gitignore
index 9c92648..145bd74 100644
--- a/fuzzers/056-rempips/.gitignore
+++ b/fuzzers/056-rempips/.gitignore
@@ -11,3 +11,4 @@
 /specimen_[0-9][0-9][0-9]/
 /todo.txt
 /vivado*
+/run.ok
diff --git a/fuzzers/056-rempips/Makefile b/fuzzers/056-rempips/Makefile
index 9c96fe6..f6e6806 100644
--- a/fuzzers/056-rempips/Makefile
+++ b/fuzzers/056-rempips/Makefile
@@ -4,8 +4,8 @@
 SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
 
 database: $(SPECIMENS_OK)
-	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_l.segbits $(addsuffix /segdata_clbl[lm]_l.txt,$(SPECIMENS))
-	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_r.segbits $(addsuffix /segdata_clbl[lm]_r.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_l.segbits $(addsuffix /segdata_int_l.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_r.segbits $(addsuffix /segdata_int_r.txt,$(SPECIMENS))
 
 pushdb:
 	${XRAY_MERGEDB} int_l seg_int_l.segbits
diff --git a/fuzzers/057-bipips/.gitignore b/fuzzers/057-bipips/.gitignore
index 274ed6e..82d86d4 100644
--- a/fuzzers/057-bipips/.gitignore
+++ b/fuzzers/057-bipips/.gitignore
@@ -11,3 +11,4 @@
 /specimen_[0-9][0-9][0-9]/
 /todo.txt
 /vivado*
+/run.ok
diff --git a/fuzzers/057-bipips/Makefile b/fuzzers/057-bipips/Makefile
index 4ade569..efbdfd0 100644
--- a/fuzzers/057-bipips/Makefile
+++ b/fuzzers/057-bipips/Makefile
@@ -4,8 +4,8 @@
 SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
 
 database: $(SPECIMENS_OK)
-	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_l.segbits $(addsuffix /segdata_clbl[lm]_l.txt,$(SPECIMENS))
-	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_r.segbits $(addsuffix /segdata_clbl[lm]_r.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_l.segbits $(addsuffix /segdata_int_l.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -m 5 -M 15 -o seg_int_r.segbits $(addsuffix /segdata_int_r.txt,$(SPECIMENS))
 
 pushdb:
 	${XRAY_MERGEDB} int_l seg_int_l.segbits
diff --git a/fuzzers/070-tileconn/.gitignore b/fuzzers/070-tileconn/.gitignore
index 15ab97d..6e99ca4 100644
--- a/fuzzers/070-tileconn/.gitignore
+++ b/fuzzers/070-tileconn/.gitignore
@@ -1,2 +1,3 @@
 /specimen_*/
 /tileconn.json
+/run.ok
diff --git a/fuzzers/070-tileconn/generate.py b/fuzzers/070-tileconn/generate.py
index 1c94746..7a707ac 100644
--- a/fuzzers/070-tileconn/generate.py
+++ b/fuzzers/070-tileconn/generate.py
@@ -11,7 +11,7 @@
                                    os.getenv("XRAY_DATABASE")), "r") as f:
     grid = json.load(f)
 
-for tile, tiledata in grid["tiles"].items():
+for tile, tiledata in grid.items():
     grid_xy = (tiledata["grid_x"], tiledata["grid_y"])
     grid2tile[grid_xy] = tile
 
@@ -61,8 +61,8 @@
     if tile1 not in tilenodes: return
     if tile2 not in tilenodes: return
 
-    tile1data = grid["tiles"][tile1]
-    tile2data = grid["tiles"][tile2]
+    tile1data = grid[tile1]
+    tile2data = grid[tile2]
 
     grid1_xy = (tile1data["grid_x"], tile1data["grid_y"])
     grid2_xy = (tile2data["grid_x"], tile2data["grid_y"])
@@ -91,7 +91,7 @@
         database[key] &= wire_pairs
 
 
-for tile, tiledata in grid["tiles"].items():
+for tile, tiledata in grid.items():
     grid_right_xy = (tiledata["grid_x"] + 1, tiledata["grid_y"])
     grid_below_xy = (tiledata["grid_x"], tiledata["grid_y"] + 1)
 
diff --git a/fuzzers/071-ppips/.gitignore b/fuzzers/071-ppips/.gitignore
index 4503981..a9be277 100644
--- a/fuzzers/071-ppips/.gitignore
+++ b/fuzzers/071-ppips/.gitignore
@@ -1,3 +1,4 @@
 /specimen_*/
 /ppips_clbl[ml]_[lr].txt
 /ppips_int_[lr].txt
+/run.ok
diff --git a/fuzzers/100-dsp-mskpat/.gitignore b/fuzzers/100-dsp-mskpat/.gitignore
new file mode 100644
index 0000000..4a41bd9
--- /dev/null
+++ b/fuzzers/100-dsp-mskpat/.gitignore
@@ -0,0 +1,3 @@
+/specimen_[0-9][0-9][0-9]/
+/seg_dsp_[lr].segbits
+/run.ok
diff --git a/fuzzers/100-dsp-mskpat/Makefile b/fuzzers/100-dsp-mskpat/Makefile
new file mode 100644
index 0000000..42c02da
--- /dev/null
+++ b/fuzzers/100-dsp-mskpat/Makefile
@@ -0,0 +1,29 @@
+
+N := 1
+SPECIMENS := $(addprefix specimen_,$(shell seq -f '%03.0f' $(N)))
+SPECIMENS_OK := $(addsuffix /OK,$(SPECIMENS))
+
+database: $(SPECIMENS_OK)
+	${XRAY_SEGMATCH} -o seg_dsp_l.segbits $(addsuffix /segdata_dsp_l_*.txt,$(SPECIMENS))
+	${XRAY_SEGMATCH} -o seg_dsp_r.segbits $(addsuffix /segdata_dsp_r_*.txt,$(SPECIMENS))
+
+pushdb:
+	${XRAY_MERGEDB} dsp_l seg_dsp_l.segbits
+	${XRAY_MERGEDB} dsp_r seg_dsp_r.segbits
+	${XRAY_DBFIXUP}
+
+$(SPECIMENS_OK):
+	bash generate.sh $(subst /OK,,$@)
+	touch $@
+
+run:
+	$(MAKE) clean
+	$(MAKE) database
+	$(MAKE) pushdb
+	touch run.ok
+
+clean:
+	rm -rf specimen_[0-9][0-9][0-9]/ seg_dsp_l.segbits seg_dsp_r.segbits run.ok
+
+.PHONY: database pushdb run clean
+
diff --git a/fuzzers/100-dsp-mskpat/generate.py b/fuzzers/100-dsp-mskpat/generate.py
new file mode 100644
index 0000000..989ade1
--- /dev/null
+++ b/fuzzers/100-dsp-mskpat/generate.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python3
+
+import sys, os, re
+
+sys.path.append("../../../utils/")
+from segmaker import segmaker
+
+segmk = segmaker("design_%s.bits" % sys.argv[1])
+
+pipdata = dict()
+ignpip = set()
+
+print("Loading tags from design.txt.")
+with open("design_%s.txt" % sys.argv[1], "r") as f:
+    for line in f:
+        tile, loc, mask, pattern = line.split()
+        dsp = "DSP_0" if loc[-1] in "02468" else "DSP_1"
+
+        mask = int(mask.replace("48'h", ""), 16)
+        pattern = int(pattern.replace("48'h", ""), 16)
+
+        for i in range(48):
+            segmk.addtag(tile, "%s.MASK[%d]" % (dsp, i), (mask >> i) & 1)
+            segmk.addtag(tile, "%s.PATTERN[%d]" % (dsp, i), (pattern >> i) & 1)
+
+segmk.compile()
+segmk.write(suffix=sys.argv[1])
diff --git a/fuzzers/100-dsp-mskpat/generate.sh b/fuzzers/100-dsp-mskpat/generate.sh
new file mode 100644
index 0000000..8ee7167
--- /dev/null
+++ b/fuzzers/100-dsp-mskpat/generate.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+source ${XRAY_GENHEADER}
+
+vivado -mode batch -source ../generate.tcl
+
+for i in {10..29}; do
+	${XRAY_BITREAD} -F $XRAY_ROI_FRAMES -o design_${i}.bits -z -y design_${i}.bit
+	python3 ../generate.py $i
+done
+
diff --git a/fuzzers/100-dsp-mskpat/generate.tcl b/fuzzers/100-dsp-mskpat/generate.tcl
new file mode 100644
index 0000000..fe98aa6
--- /dev/null
+++ b/fuzzers/100-dsp-mskpat/generate.tcl
@@ -0,0 +1,71 @@
+create_project -force -part $::env(XRAY_PART) design design
+
+read_verilog ../top.v
+synth_design -top top
+
+set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_00) IOSTANDARD LVCMOS33" [get_ports i]
+set_property -dict "PACKAGE_PIN $::env(XRAY_PIN_01) IOSTANDARD LVCMOS33" [get_ports o]
+
+create_pblock roi
+resize_pblock [get_pblocks roi] -add "$::env(XRAY_ROI)"
+
+set_property CFGBVS VCCO [current_design]
+set_property CONFIG_VOLTAGE 3.3 [current_design]
+set_property BITSTREAM.GENERAL.PERFRAMECRC YES [current_design]
+set_param tcl.collectionResultDisplayLimit 0
+
+place_design
+route_design
+
+write_checkpoint -force design.dcp
+
+source ../../../utils/utils.tcl
+set cells [list]
+
+set gnd_net [create_net gnd_net]
+set gnd_cell [create_cell -reference GND gnd_cell]
+connect_net -net $gnd_net -objects [get_pins $gnd_cell/G]
+
+foreach site [get_sites -of_objects [filter [roi_tiles] -filter {TYPE == "DSP_L" || TYPE == "DSP_R"}] -filter {SITE_TYPE =~ DSP*}] {
+	set cell [create_cell -reference DSP48E1 ${site}_cell]
+	lappend cells $cell
+	set_property LOC $site $cell
+	foreach pin [get_pins -of_objects $cell -filter {DIRECTION == "IN"}] {
+		connect_net -net $gnd_net -objects $pin
+	}
+}
+
+route_design
+
+proc write_txtdata {filename} {
+	upvar 1 cells cells
+	puts "Writing $filename."
+	set fp [open $filename w]
+	foreach cell $cells {
+		set loc [get_property LOC $cell]
+		set mask [get_property MASK $cell]
+		set pattern [get_property PATTERN $cell]
+		set tile [get_tiles -of_objects [get_sites -filter "NAME == $loc"]]
+		puts $fp "$tile $loc $mask $pattern"
+	}
+	close $fp
+}
+
+proc randhex {len} {
+	set s ""
+	for {set i 0} {$i < $len} {incr i} {
+		set s "$s[format %x [expr {int(rand()*16)}]]"
+	}
+	return $s
+}
+
+for {set i 10} {$i < 30} {incr i} {
+	foreach cell $cells {
+		set_property MASK "48'h[randhex 12]" $cell
+		set_property PATTERN "48'h[randhex 12]" $cell
+	}
+	write_checkpoint -force design_${i}.dcp
+	write_bitstream -force design_${i}.bit
+	write_txtdata design_${i}.txt
+}
+
diff --git a/fuzzers/100-dsp-mskpat/top.v b/fuzzers/100-dsp-mskpat/top.v
new file mode 100644
index 0000000..c0e91c5
--- /dev/null
+++ b/fuzzers/100-dsp-mskpat/top.v
@@ -0,0 +1,3 @@
+module top (input i, output o);
+	assign o = i;
+endmodule
diff --git a/htmlgen/htmlgen.py b/htmlgen/htmlgen.py
index fdc78d3..28fa34e 100755
--- a/htmlgen/htmlgen.py
+++ b/htmlgen/htmlgen.py
@@ -147,93 +147,110 @@
 # Loading Raw Source Data
 
 grid = None
-segbits = dict()
-segbits_r = dict()
-segframes = dict()
-segtiles = dict()
+cfgbits = dict()
+cfgbits_r = dict()
+maskbits = dict()
+ppips = dict()
 routebits = dict()
 routezbits = dict()
-maskbits = dict()
 
 print("Loading tilegrid.")
 with db_open("tilegrid.json") as f:
     data = f.read()
     if not data:
         grid = {
-            "segments": {},
-            "tiles": {
-                "NULL": {
-                    "grid_x": 0,
-                    "grid_y": 0,
-                    "type": "NULL",
-                }
+            "NULL": {
+                "grid_x": 0,
+                "grid_y": 0,
+                "type": "NULL",
             }
         }
     else:
         grid = json.loads(data)
 
-for segname, segdata in grid["segments"].items():
-    segtype = segdata["type"].lower()
+def db_read(tiletype):
+    cfgbits[tiletype] = dict()
+    cfgbits_r[tiletype] = dict()
+    maskbits[tiletype] = set()
+    ppips[tiletype] = dict()
+    routebits[tiletype] = dict()
+    routezbits[tiletype] = dict()
 
-    if segtype not in segbits:
-        print("Loading data for %s segments:" % segtype)
+    def add_pip_bits(tag, bits):
+        if tag not in routebits[tiletype]:
+            routebits[tiletype][tag] = set()
+            routezbits[tiletype][tag] = set()
+        for bit in bits:
+            if bit[0] == "!":
+                if bit[1:] not in routezbits[tiletype]:
+                    routezbits[tiletype][bit[1:]] = set()
+                routezbits[tiletype][bit[1:]].add(tag)
+            else:
+                if bit not in routebits[tiletype]:
+                    routebits[tiletype][bit] = set()
+                routebits[tiletype][bit].add(tag)
 
-        segbits[segtype] = dict()
-        segbits_r[segtype] = dict()
-        routebits[segtype] = dict()
-        routezbits[segtype] = dict()
-        maskbits[segtype] = set()
-        segframes[segtype] = segdata["frames"]
+    def add_cfg_bits(tag, bits):
+        if tag not in cfgbits[tiletype]:
+            cfgbits[tiletype][tag] = set()
+        for bit in bits:
+            cfgbits[tiletype][tag].add(bit)
+            if bit not in cfgbits_r[tiletype]:
+                cfgbits_r[tiletype][bit] = set()
+            cfgbits_r[tiletype][bit].add(tag)
 
-        segtiles[segtype] = set()
-        for t in segdata["tiles"]:
-            segtiles[segtype].add(grid["tiles"][t]["type"])
+    with db_open("segbits_%s.db" % tiletype) as f:
+        for line in f:
+            line = line.split()
+            tag, bits = line[0], line[1:]
 
-        def add_pip_bits(line):
-            bit_name, *bit_pos = line.split()
-            for bit in bit_pos:
-                if bit[0] == "!":
-                    if bit[1:] not in routezbits[segtype]:
-                        routezbits[segtype][bit[1:]] = set()
-                    routezbits[segtype][bit[1:]].add(bit_name)
-                else:
-                    if bit not in routebits[segtype]:
-                        routebits[segtype][bit] = set()
-                    routebits[segtype][bit].add(bit_name)
+            if tiletype in ["int_l", "int_r", "hclk_l", "hclk_r"]:
+                add_pip_bits(tag, bits)
 
-        def add_single_bit(line):
-            print(line)
-            bit_name, bit_pos = line.split()
-            assert bit_pos[0] != "!"
-            segbits[segtype][bit_name] = bit_pos
-            segbits_r[segtype][bit_pos] = bit_name
+            elif tiletype in ["clbll_l", "clbll_r", "clblm_l", "clblm_r"] and \
+                    re.search(r"(\.[ABCD]MUX\.)|(\.PRECYINIT\.)", tag):
+                add_pip_bits(tag, bits)
 
-        if segtype not in ["hclk_l", "hclk_r"]:
-            print("  loading %s segbits." % segtype)
-            with db_open("segbits_%s.db" % segtype) as f:
+            else:
+                add_cfg_bits(tag, bits)
+
+    with db_open("ppips_%s.db" % tiletype) as f:
+        for line in f:
+            tag, typ = line.split()
+            ppips[tiletype][tag] = typ
+
+    if tiletype not in ["int_l", "int_r"]:
+        with db_open("mask_%s.db" % tiletype) as f:
+            for line in f:
+                tag, bit = line.split()
+                assert tag == "bit"
+                maskbits[tiletype].add(bit)
+    else:
+        for t in ["clbll_l", "clbll_r", "clblm_l", "clblm_r", "dsp_l", "dsp_r", "bram_l", "bram_r"]:
+            with db_open("mask_%s.db" % t) as f:
                 for line in f:
-                    if re.search(r"(\.[ABCD](5?FF|OUT)MUX\.)|(\.PRECYINIT\.)",
-                                 line):
-                        add_pip_bits(line)
-                    else:
-                        add_single_bit(line)
+                    tag, bit = line.split()
+                    assert tag == "bit"
+                    frameidx, bitidx = bit.split("_")
+                    maskbits[tiletype].add("%02d_%02d" % (int(frameidx), int(bitidx) % 64))
 
-        int_tile_type = re.sub("clbl[lm]|bram[0-4]|dsp[0-4]", "int", segtype)
+db_read("int_l")
+db_read("int_r")
 
-        print("  loading %s segbits." % int_tile_type)
-        with db_open("segbits_%s.db" % int_tile_type) as f:
-            for line in f:
-                if segtype in ["hclk_l", "hclk_r"
-                               ] and ".ENABLE_BUFFER." in line:
-                    add_single_bit(line)
-                else:
-                    add_pip_bits(line)
+db_read("hclk_l")
+db_read("hclk_r")
 
-        print("  loading %s maskbits." % segtype)
-        with db_open("mask_%s.db" % segtype) as f:
-            for line in f:
-                _, bit = line.split()
-                maskbits[segtype].add(bit)
+db_read("clbll_l")
+db_read("clbll_r")
+
+db_read("clblm_l")
+db_read("clblm_r")
+
+db_read("dsp_l")
+db_read("dsp_r")
+
+db_read("bram_l")
+db_read("bram_r")
 
 #################################################
 # Create Tilegrid Page
@@ -256,7 +273,7 @@
             get_setting("XRAY_ROI_FRAMES")),
         file=f)
 
-    for tilename, tiledata in grid["tiles"].items():
+    for tilename, tiledata in grid.items():
         grid_x = tiledata["grid_x"]
         grid_y = tiledata["grid_y"]
         grid_map[(grid_x, grid_y)] = tilename
@@ -276,7 +293,7 @@
 
         for grid_x in range(grid_range[0], grid_range[2] + 1):
             tilename = grid_map[(grid_x, grid_y)]
-            tiledata = grid["tiles"][tilename]
+            tiledata = grid[tilename]
             segdata = None
 
             bgcolor = "#aaaaaa"
@@ -324,11 +341,10 @@
                 "<td bgcolor=\"%s\" align=\"center\" title=\"%s\"><span style=\"font-size:10px\">"
                 % (bgcolor, "\n".join(title)),
                 file=f)
-            if "segment" in tiledata:
-                segtype = segdata["type"].lower()
+            if tiledata["type"].lower() in cfgbits:
                 print(
-                    "<a style=\"text-decoration: none; color: black\" href=\"seg_%s.html\">%s</a></span></td>"
-                    % (segtype, tilename.replace("_X", "<br/>X")),
+                    "<a style=\"text-decoration: none; color: black\" href=\"tile_%s.html\">%s</a></span></td>"
+                    % (tiledata["type"].lower(), tilename.replace("_X", "<br/>X")),
                     file=f)
             else:
                 print(
@@ -344,28 +360,223 @@
 #################################################
 # Create Segment Pages
 
-for segtype in sorted(segbits.keys()):
-    with out_open("seg_%s.html" % segtype) as f:
-        print(
-            "<html><title>X-Ray %s Database: %s</title><body>" %
-            (get_setting("XRAY_DATABASE").upper(), segtype.upper()),
-            file=f)
-        if segtype in ["hclk_l", "hclk_r"]:
-            print(
-                "<h3>X-Ray %s Database: %s Segment</h3>" %
-                (get_setting("XRAY_DATABASE").upper(), segtype.upper()),
-                file=f)
-        else:
-            print(
-                "<h3>X-Ray %s Database: %s Segment (%s Tile + %s Tile)</h3>" %
-                (
-                    get_setting("XRAY_DATABASE").upper(), segtype.upper(),
-                    segtype.upper(),
-                    re.sub("clbl[lm]|bram[0-4]|dsp[0-4]", "int",
-                           segtype).upper()),
-                file=f)
+def get_bit_info(frameidx, bitidx, tiletype):
+    bit_pos = "%02d_%02d" % (frameidx, bitidx)
+    bit_name = cfgbits_r[tiletype][bit_pos] if bit_pos in cfgbits_r[tiletype] else None
 
-        print(
+    if bit_name is None and bit_pos in routebits[tiletype]:
+        bit_name = routebits[tiletype][bit_pos]
+
+    if bit_name is None and bit_pos in routezbits[tiletype]:
+        bit_name = routezbits[tiletype][bit_pos]
+
+    if bit_name is None and tiletype in ["clbll_l", "clbll_r", "clblm_l", "clblm_r", "dsp_l", "dsp_r", "bram_l", "bram_r"]:
+        int_tile_type = "int_" + tiletype[-1]
+        bit_int_pos = "%02d_%02d" % (frameidx, bitidx % 64)
+        bit_name = cfgbits_r[int_tile_type][bit_int_pos] if bit_int_pos in cfgbits_r[int_tile_type] else None
+
+        if bit_name is None and bit_int_pos in routebits[int_tile_type]:
+            bit_name = routebits[int_tile_type][bit_int_pos]
+
+        if bit_name is None and bit_int_pos in routezbits[int_tile_type]:
+            bit_name = routezbits[int_tile_type][bit_int_pos]
+
+        if bit_name is not None:
+            return bit_pos, "INT", [bit_pos], "#88aaff"
+
+    if bit_name is not None:
+        if len(bit_name) <= 1:
+            bit_name = "".join(bit_name)
+        else:
+            for n in bit_name:
+                bit_name = ".".join(n.split(".")[:-1])
+
+    label = None
+    title = [bit_pos]
+    bgcolor = "#aaaaaa"
+
+    if bit_pos not in maskbits[tiletype]:
+        label = "&nbsp;"
+        bgcolor = "#444444"
+        title.append("UNUSED ?")
+
+    if (tiletype in ["hclk_l", "hclk_r"]) and bitidx < 13:
+        label = "ECC"
+        bgcolor = "#44aa44"
+
+    if bit_name is not None:
+        bgcolor = "#ff0000"
+        title.append(bit_name)
+
+        if "LUT.INIT" in bit_name:
+            bgcolor = "#ffffaa"
+            m = re.search(r"(.)LUT.INIT\[(..)\]", bit_name)
+            label = m.group(1) + m.group(2)
+
+        m = re.search(r"\.([ABCD])LUT\.([A-Z]+)$", bit_name)
+        if m:
+            bgcolor = "#ffffaa"
+            if m.group(2) == "RAM":
+                label = m.group(1) + "LR"
+            elif m.group(2) == "SMALL":
+                label = m.group(1) + "LS"
+            elif m.group(2) == "SRL":
+                label = m.group(1) + "SR"
+            else:
+                bgcolor = "#ff0000"
+
+        m = re.search(r"\.([ABCD])LUT\.([A-Z]+)$", bit_name)
+        if m:
+            bgcolor = "#ffffaa"
+            if m.group(2) == "RAM":
+                label = m.group(1) + "LR"
+            elif m.group(2) == "SMALL":
+                label = m.group(1) + "LS"
+            elif m.group(2) == "SRL":
+                label = m.group(1) + "SR"
+            else:
+                bgcolor = "#ff0000"
+
+        m = re.search(
+            r"\.([ABCD]5?)FF\.([A-Z]+(\.A|\.B)?)$", bit_name)
+        if m:
+            bgcolor = "#aaffaa"
+            if m.group(2) == "ZINI":
+                label = m.group(1) + "ZI"
+            elif m.group(2) == "ZRST":
+                label = m.group(1) + "ZR"
+            elif m.group(2) == "MUX.A":
+                label = m.group(1) + "MA"
+            elif m.group(2) == "MUX.B":
+                label = m.group(1) + "MB"
+            else:
+                bgcolor = "#ff0000"
+
+        m = re.search(r"\.([ABCD])DI1MUX\.", bit_name)
+        if m:
+            bgcolor = "#ffffaa"
+            label = m.group(1) + "DI1"
+
+        m = re.search(r"\.(WA[78])USED$", bit_name)
+        if m:
+            bgcolor = "#ffffaa"
+            label = m.group(1)
+
+        if ".WEMUX." in bit_name:
+            bgcolor = "#ffffaa"
+            label = "WE"
+
+        m = re.search(r"\.CARRY4\.([A-Z0-9]+)$", bit_name)
+        if m:
+            bgcolor = "#88cc00"
+            label = m.group(1)
+
+        if re.search(r"\.LATCH$", bit_name):
+            bgcolor = "#aaffaa"
+            label = "LAT"
+
+        if re.search(r"\.FFSYNC$", bit_name):
+            bgcolor = "#aaffaa"
+            label = "SYN"
+
+        if re.search(r"\.[ABCD]LUT5$", bit_name):
+            bgcolor = "#cccc88"
+            label = bit_name[-5] + "5"
+
+        if re.search(r"\.(CE|SR)USEDMUX$", bit_name):
+            bgcolor = "#ffaa00"
+            label = bit_name[-9:-7] + "M"
+
+        if re.search(r"\.CLKINV$", bit_name):
+            bgcolor = "#ffaa00"
+            label = "CLKI"
+
+        if ".ENABLE_BUFFER." in bit_name:
+            bgcolor = "#ffaa00"
+            label = "BUF"
+
+        if re.match("^INT_[LR].[SNWE][SNWERL]", bit_name):
+            if bit_name[8] == "1":
+                bgcolor = "#4466bb"
+            elif bit_name[8] == "2":
+                bgcolor = "#aa88ff"
+            elif bit_name[6:9] in "SS6 SE6 NN6 NW6".split():
+                bgcolor = "#7755ff"
+            else:
+                bgcolor = "#88aaff"
+            label = bit_name[6:9]
+
+        if re.match("^INT_[LR].IMUX", bit_name):
+            m = re.match("^INT_[LR].IMUX(_L)?(\d+)", bit_name)
+            bgcolor = "#88aaff"
+            label = "IM" + m.group(2)
+
+        if re.match("^INT_[LR].BYP_ALT", bit_name):
+            bgcolor = "#7755ff"
+            label = "BA" + bit_name[13]
+
+        if re.match("^INT_[LR].FAN_ALT", bit_name):
+            bgcolor = "#4466bb"
+            label = "FA" + bit_name[13]
+
+        if re.match("^INT_[LR].CLK", bit_name):
+            bgcolor = "#4466bb"
+            label = "CLK"
+
+        if re.match("^INT_[LR].CTRL", bit_name):
+            bgcolor = "#7755ff"
+            label = "CTRL"
+
+        if re.match("^INT_[LR].GFAN", bit_name):
+            bgcolor = "#7755ff"
+            label = "GFAN"
+
+        if re.match("^INT_[LR].LVB", bit_name):
+            bgcolor = "#88aaff"
+            label = "LVB"
+
+        if re.match("^INT_[LR].LV", bit_name):
+            bgcolor = "#88aaff"
+            label = "LV"
+
+        if re.match("^INT_[LR].LH", bit_name):
+            bgcolor = "#4466bb"
+            label = "LH"
+
+        if re.match(
+                "^CLBL[LM]_[LR].SLICE[LM]_X[01].[ABCD]FF.DMUX",
+                bit_name):
+            bgcolor = "#88aaff"
+            label = "DMX"
+
+        if re.match(
+                "^CLBL[LM]_[LR].SLICE[LM]_X[01].[ABCD]MUX",
+                bit_name):
+            bgcolor = "#aa88ff"
+            label = "OMX"
+
+        if re.match(
+                "^CLBL[LM]_[LR].SLICE[LM]_X[01].PRECYINIT",
+                bit_name):
+            bgcolor = "#88aaff"
+            label = "CYI"
+
+        if re.match("^HCLK_[LR]", bit_name) and "_B_BOT" in bit_name:
+            bgcolor = "#4466bb"
+            label = "BOT"
+
+        if re.match("^HCLK_[LR]", bit_name) and "_B_TOP" in bit_name:
+            bgcolor = "#7755ff"
+            label = "TOP"
+
+        if re.match("^DSP_[LR].DSP_[01].(PATTERN|MASK)", bit_name):
+            bgcolor = "#ffffaa"
+            label = bit_name[12] + bit_name[10]
+
+    return bit_pos, label, title, bgcolor
+
+def gen_table(tiletype, f):
+    print(
             """
 <script><!--
 var grp2bits = { };
@@ -402,253 +613,127 @@
     highlight_bits.length = 0;
 }
 //--></script>
-        """,
-            file=f)
+    """,
+        file=f)
 
+    num_frames = 36
+    unused_bits = 0
+    unknown_bits = 0
+    known_bits = 0
+    hideframes = set()
+
+    if tiletype in ["int_l", "int_r", "hclk_l", "hclk_r"]:
+        num_frames = 26
+
+    if tiletype in ["bram_l", "bram_r", "dsp_l", "dsp_r"]:
+        for i in range(5, 25):
+            hideframes.add(i)
+        num_frames = 28
+
+    height = 2
+    if tiletype in ["hclk_l", "hclk_r"]:
+        height = 1
+    if tiletype in ["dsp_l", "dsp_r", "bram_l", "bram_r"]:
+        height = 10
+
+    if height > 2:
+        print("<table><tr><td>", file=f)
+
+    def print_table_header():
         print("<table border>", file=f)
-
-        unused_bits = 0
-        unknown_bits = 0
-        known_bits = 0
-        piptypes = dict()
-
         print("<tr>", file=f)
         print("<th width=\"30\"></th>", file=f)
-        for frameidx in range(segframes[segtype]):
+        for frameidx in range(num_frames):
+            if frameidx in hideframes:
+                continue
             print(
                 "<th width=\"30\"><span style=\"font-size:10px\">%d</span></th>"
                 % frameidx,
                 file=f)
         print("</tr>", file=f)
 
-        for bitidx in range(31 if (segtype in ["hclk_l", "hclk_r"]) else 63,
-                            -1, -1):
-            print("<tr>", file=f)
+    print_table_header()
+
+    for bitidx in range(32*height-1, -1, -1):
+        print("<tr>", file=f)
+        print(
+            "<th align=\"right\"><span style=\"font-size:10px\">%d</span></th>"
+            % bitidx,
+            file=f)
+        for frameidx in range(num_frames):
+            if frameidx in hideframes:
+                continue
+
+            bit_pos, label, title, bgcolor = get_bit_info(frameidx, bitidx, tiletype)
+
+            if label is None:
+                label = "&nbsp;"
+                onclick = ""
+
+            if label == "INT":
+                onclick = " onmousedown=\"location.href = 'tile_int_%s.html#b%s'\"" % (tiletype[-1], bit_pos)
+            else:
+                onclick = " onmousedown=\"location.href = '#b%s'\"" % bit_pos
+
+            if bgcolor == "#aaaaaa":
+                unknown_bits += 1
+            elif bgcolor == "#444444":
+                unused_bits += 1
+            else:
+                known_bits += 1
+
             print(
-                "<th align=\"right\"><span style=\"font-size:10px\">%d</span></th>"
-                % bitidx,
+                "<td id=\"bit%s\" onmouseenter=\"ome('%s');\" onmouseleave=\"oml();\" bgcolor=\"%s\" align=\"center\" title=\"%s\"%s><span style=\"font-size:10px\">%s</span></td>"
+                % (
+                    bit_pos, bit_pos, bgcolor, "\n".join(title), onclick,
+                    label),
                 file=f)
-            for frameidx in range(segframes[segtype]):
-                bit_pos = "%02d_%02d" % (frameidx, bitidx)
-                bit_name = segbits_r[segtype][bit_pos] if bit_pos in segbits_r[
-                    segtype] else None
+        print("</tr>", file=f)
 
-                label = None
-                title = [bit_pos]
-                bgcolor = "#aaaaaa"
+        if bitidx > 0 and bitidx % 80 == 0:
+            print("</table></td><td>", file=f)
+            print_table_header()
 
-                if bit_pos not in maskbits[segtype]:
-                    label = "&nbsp;"
-                    bgcolor = "#444444"
-                    title.append("UNUSED ?")
+    print("</table>", file=f)
 
-                if (segtype in ["hclk_l", "hclk_r"]) and bitidx < 13:
-                    label = "ECC"
-                    bgcolor = "#44aa44"
+    if height > 2:
+        print("</td></tr></table>", file=f)
 
-                if bit_name is not None:
-                    bgcolor = "#ff0000"
-                    title.append(bit_name)
+    print(
+        "  unused: %d, unknown: %d, known: %d, total: %d, percentage: %.2f%% (%.2f%%)"
+        % (
+            unused_bits, unknown_bits, known_bits,
+            unused_bits + unknown_bits + known_bits, 100 * known_bits /
+            (unknown_bits + unused_bits + known_bits),
+            100 * (known_bits + unused_bits) /
+            (unknown_bits + unused_bits + known_bits)))
 
-                    if "LUT.INIT" in bit_name:
-                        bgcolor = "#ffffaa"
-                        m = re.search(r"(.)LUT.INIT\[(..)\]", bit_name)
-                        label = m.group(1) + m.group(2)
 
-                    m = re.search(r"\.([ABCD])LUT\.([A-Z]+)$", bit_name)
-                    if m:
-                        bgcolor = "#ffffaa"
-                        if m.group(2) == "RAM":
-                            label = m.group(1) + "LR"
-                        elif m.group(2) == "SMALL":
-                            label = m.group(1) + "LS"
-                        elif m.group(2) == "SRL":
-                            label = m.group(1) + "SR"
-                        else:
-                            bgcolor = "#ff0000"
+for tiletype in sorted(cfgbits.keys()):
+    with out_open("tile_%s.html" % tiletype) as f:
+        print(
+            "<html><title>X-Ray %s Database: %s</title><body>" %
+            (get_setting("XRAY_DATABASE").upper(), tiletype.upper()),
+            file=f)
+        print(
+            "<h3><a href=\"index.html\">X-Ray %s Database</a>: %s Segment</h3>" %
+            (get_setting("XRAY_DATABASE").upper(), tiletype.upper()),
+            file=f)
 
-                    m = re.search(r"\.([ABCD])LUT\.([A-Z]+)$", bit_name)
-                    if m:
-                        bgcolor = "#ffffaa"
-                        if m.group(2) == "RAM":
-                            label = m.group(1) + "LR"
-                        elif m.group(2) == "SMALL":
-                            label = m.group(1) + "LS"
-                        elif m.group(2) == "SRL":
-                            label = m.group(1) + "SR"
-                        else:
-                            bgcolor = "#ff0000"
-
-                    m = re.search(
-                        r"\.([ABCD]5?)FF\.([A-Z]+(\.A|\.B)?)$", bit_name)
-                    if m:
-                        bgcolor = "#aaffaa"
-                        if m.group(2) == "ZINI":
-                            label = m.group(1) + "ZI"
-                        elif m.group(2) == "ZRST":
-                            label = m.group(1) + "ZR"
-                        elif m.group(2) == "MUX.A":
-                            label = m.group(1) + "MA"
-                        elif m.group(2) == "MUX.B":
-                            label = m.group(1) + "MB"
-                        else:
-                            bgcolor = "#ff0000"
-
-                    m = re.search(r"\.([ABCD])DI1MUX\.", bit_name)
-                    if m:
-                        bgcolor = "#ffffaa"
-                        label = m.group(1) + "DI1"
-
-                    m = re.search(r"\.(WA[78])USED$", bit_name)
-                    if m:
-                        bgcolor = "#ffffaa"
-                        label = m.group(1)
-
-                    if ".WEMUX." in bit_name:
-                        bgcolor = "#ffffaa"
-                        label = "WE"
-
-                    m = re.search(r"\.CARRY4\.([A-Z0-9]+)$", bit_name)
-                    if m:
-                        bgcolor = "#88cc00"
-                        label = m.group(1)
-
-                    if re.search(r"\.LATCH$", bit_name):
-                        bgcolor = "#aaffaa"
-                        label = "LAT"
-
-                    if re.search(r"\.FFSYNC$", bit_name):
-                        bgcolor = "#aaffaa"
-                        label = "SYN"
-
-                    if re.search(r"\.[ABCD]LUT5$", bit_name):
-                        bgcolor = "#cccc88"
-                        label = bit_name[-5] + "5"
-
-                    if re.search(r"\.(CE|SR)USEDMUX$", bit_name):
-                        bgcolor = "#ffaa00"
-                        label = bit_name[-9:-7] + "M"
-
-                    if re.search(r"\.CLKINV$", bit_name):
-                        bgcolor = "#ffaa00"
-                        label = "CLKI"
-
-                    if ".ENABLE_BUFFER." in bit_name:
-                        bgcolor = "#ffaa00"
-                        label = "BUF"
-
-                elif bit_pos in routebits[segtype]:
-                    bgcolor = "#0000ff"
-                    label = "R"
-                    for bn in sorted(routebits[segtype][bit_pos]):
-                        if re.match("^INT_[LR].[SNWE][SNWERL]", bn):
-                            if bn[8] == "1":
-                                bgcolor = "#4466bb"
-                            elif bn[8] == "2":
-                                bgcolor = "#aa88ff"
-                            elif bn[6:9] in "SS6 SE6 NN6 NW6".split():
-                                bgcolor = "#7755ff"
-                            else:
-                                bgcolor = "#88aaff"
-                            label = bn[6:9]
-                        elif re.match("^INT_[LR].IMUX", bn):
-                            m = re.match("^INT_[LR].IMUX(_L)?(\d+)", bn)
-                            bgcolor = "#88aaff"
-                            label = "IM" + m.group(2)
-                        elif re.match("^INT_[LR].BYP_ALT", bn):
-                            bgcolor = "#7755ff"
-                            label = "BA" + bn[13]
-                        elif re.match("^INT_[LR].FAN_ALT", bn):
-                            bgcolor = "#4466bb"
-                            label = "FA" + bn[13]
-                        elif re.match("^INT_[LR].CLK", bn):
-                            bgcolor = "#4466bb"
-                            label = "CLK"
-                        elif re.match("^INT_[LR].CTRL", bn):
-                            bgcolor = "#7755ff"
-                            label = "CTRL"
-                        elif re.match("^INT_[LR].GFAN", bn):
-                            bgcolor = "#7755ff"
-                            label = "GFAN"
-                        elif re.match("^INT_[LR].LVB", bn):
-                            bgcolor = "#88aaff"
-                            label = "LVB"
-                        elif re.match("^INT_[LR].LV", bn):
-                            bgcolor = "#88aaff"
-                            label = "LV"
-                        elif re.match("^INT_[LR].LH", bn):
-                            bgcolor = "#4466bb"
-                            label = "LH"
-                        elif re.match(
-                                "^CLBL[LM]_[LR].SLICE[LM]_X[01].[ABCD]FFMUX",
-                                bn):
-                            bgcolor = "#88aaff"
-                            label = "DMX"
-                        elif re.match(
-                                "^CLBL[LM]_[LR].SLICE[LM]_X[01].[ABCD]OUTMUX",
-                                bn):
-                            bgcolor = "#aa88ff"
-                            label = "OMX"
-                        elif re.match(
-                                "^CLBL[LM]_[LR].SLICE[LM]_X[01].PRECYINIT",
-                                bn):
-                            bgcolor = "#88aaff"
-                            label = "CYI"
-                        elif re.match("^HCLK_[LR]", bn) and "_B_BOT" in bn:
-                            bgcolor = "#4466bb"
-                            label = "BOT"
-                        elif re.match("^HCLK_[LR]", bn) and "_B_TOP" in bn:
-                            bgcolor = "#7755ff"
-                            label = "TOP"
-                        piptypes[bit_pos] = label
-                        title.append(bn)
-
-                if label is None:
-                    label = "&nbsp;"
-                    title.append("UNKNOWN")
-                    onclick = ""
-                else:
-                    onclick = " onmousedown=\"location.href = '#b%s'\"" % bit_pos
-
-                if bgcolor == "#aaaaaa":
-                    unknown_bits += 1
-                elif bgcolor == "#444444":
-                    unused_bits += 1
-                else:
-                    known_bits += 1
-
-                print(
-                    "<td id=\"bit%s\" onmouseenter=\"ome('%s');\" onmouseleave=\"oml();\" bgcolor=\"%s\" align=\"center\" title=\"%s\"%s><span style=\"font-size:10px\">%s</span></td>"
-                    % (
-                        bit_pos, bit_pos, bgcolor, "\n".join(title), onclick,
-                        label),
-                    file=f)
-            print("</tr>", file=f)
-        print("</table>", file=f)
+        gen_table(tiletype, f)
 
         print("<div>", file=f)
 
-        print("<h3>Segment Configuration Bits</h3>", file=f)
-
-        if True:
-            print(
-                "  unused: %d, unknown: %d, known: %d, total: %d, percentage: %.2f%% (%.2f%%)"
-                % (
-                    unused_bits, unknown_bits, known_bits,
-                    unused_bits + unknown_bits + known_bits, 100 * known_bits /
-                    (unknown_bits + unused_bits + known_bits),
-                    100 * (known_bits + unused_bits) /
-                    (unknown_bits + unused_bits + known_bits)))
-
         bits_by_prefix = dict()
 
-        for bit_name, bit_pos in segbits[segtype].items():
+        for bit_name, bits_pos in cfgbits[tiletype].items():
             prefix = ".".join(bit_name.split(".")[0:-1])
 
             if prefix not in bits_by_prefix:
                 bits_by_prefix[prefix] = set()
 
-            bits_by_prefix[prefix].add((bit_name, bit_pos))
+            for bit_pos in bits_pos:
+                bits_by_prefix[prefix].add((bit_name, bit_pos))
 
         for prefix, bits in sorted(bits_by_prefix.items()):
             for bit_name, bit_pos in sorted(bits):
@@ -672,8 +757,8 @@
             print("</table>", file=f)
 
         ruf = UnionFind()
-        routebits_routezbits = list(routebits[segtype].items())
-        routebits_routezbits += list(routezbits[segtype].items())
+        routebits_routezbits = list(routebits[tiletype].items())
+        routebits_routezbits += list(routezbits[tiletype].items())
 
         for bit, pips in routebits_routezbits:
             for pip in pips:
@@ -707,15 +792,7 @@
         rgroups_with_title = list()
 
         for grp, gdata in sorted(rgroups.items()):
-            title = ""
-            for pip, bits in gdata.items():
-                for bit in bits:
-                    if bit in piptypes:
-                        # title = piptypes[bit] + "-"
-                        pass
-                    else:
-                        print("Detected DB error in PIP %s %s" % (pip, bits))
-            title += "PIPs driving " + ", ".join(sorted(rgroup_names[grp]))
+            title = "PIPs driving " + ", ".join(sorted(rgroup_names[grp]))
             rgroups_with_title.append((title, grp, gdata))
 
         for title, grp, gdata in sorted(rgroups_with_title):
@@ -724,12 +801,12 @@
                 grp_bits |= bits
 
             def bit_key(b):
-                if segtype in ["hclk_l", "hclk_r"]:
+                if tiletype in ["hclk_l", "hclk_r"]:
                     if b in hclk_left_bits:
                         return "a" + b
                     if b in hclk_right_bits:
                         return "c" + b
-                if segtype in ["clblm_l", "clblm_r", "clbll_l", "clbll_r"]:
+                if tiletype in ["clblm_l", "clblm_r", "clbll_l", "clbll_r", "int_l", "int_r"]:
                     if b in clb_left_bits:
                         return "a" + b
                     if b in clb_right_bits:
@@ -764,9 +841,9 @@
                 line = " --><td>%s</td>" % (pip)
                 for bit in grp_bits:
                     c = "-"
-                    if bit in routebits[segtype] and pip in routebits[segtype][bit]:
+                    if bit in routebits[tiletype] and pip in routebits[tiletype][bit]:
                         c = "1"
-                    if bit in routezbits[segtype] and pip in routezbits[segtype][bit]:
+                    if bit in routezbits[tiletype] and pip in routezbits[tiletype][bit]:
                         c = "0"
                     line = "%s%s<td align=\"center\">%s</td>" % (c, line, c)
                 lines.append(line)
@@ -794,21 +871,16 @@
             if not first_note:
                 print("</p>", file=f)
 
-        for tile_type in segtiles[segtype]:
-            print("<h3>Tile %s Pseudo PIPs</h3>" % tile_type, file=f)
+        if len(ppips[tiletype]) > 0:
+            print("<h4>Pseudo PIPs</h4>", file=f)
             print("<table cellspacing=0>", file=f)
             print(
                 "<tr><th width=\"500\" align=\"left\">PIP</th><th>Type</th></tr>",
                 file=f)
             trstyle = ""
-            with db_open("ppips_%s.db" % tile_type.lower()) as fi:
-                for line in fi:
-                    pip_name, pip_type = line.split()
-                    trstyle = " bgcolor=\"#dddddd\"" if trstyle == "" else ""
-                    print(
-                        "<tr%s><td>%s</td><td>%s</td></tr>" %
-                        (trstyle, pip_name, pip_type),
-                        file=f)
+            for typ, tag in sorted([(b, a) for a, b in ppips[tiletype].items()]):
+                trstyle = " bgcolor=\"#dddddd\"" if trstyle == "" else ""
+                print("<tr%s><td>%s</td><td>%s</td></tr>" % (trstyle, tag, typ), file=f)
             print("</table>", file=f)
 
         print("</div>", file=f)
diff --git a/tools/simpleroute.py b/tools/simpleroute.py
index 8613961..d8293fd 100755
--- a/tools/simpleroute.py
+++ b/tools/simpleroute.py
@@ -27,7 +27,7 @@
 
     with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"),
                                        os.getenv("XRAY_DATABASE")), "r") as f:
-        tiles = json.load(f)["tiles"]
+        tilegrid = json.load(f)
 
     with open("%s/%s/tileconn.json" % (os.getenv("XRAY_DATABASE_DIR"),
                                        os.getenv("XRAY_DATABASE")), "r") as f:
@@ -37,7 +37,7 @@
     grid_to_tile = dict()
     nodes = MergeFind()
 
-    for tile, tiledata in tiles.items():
+    for tile, tiledata in tilegrid.items():
         if tiledata["type"] not in type_to_tiles:
             type_to_tiles[tiledata["type"]] = list()
         type_to_tiles[tiledata["type"]].append(tile)
@@ -48,7 +48,7 @@
     for entry in tileconn:
         type_a, type_b = entry["tile_types"]
         for tile_a in type_to_tiles[type_a]:
-            tiledata_a = tiles[tile_a]
+            tiledata_a = tilegrid[tile_a]
             grid_a = (tiledata_a["grid_x"], tiledata_a["grid_y"])
             grid_b = (
                 grid_a[0] + entry["grid_deltas"][0],
@@ -58,7 +58,7 @@
                 continue
 
             tile_b = grid_to_tile[grid_b]
-            tiledata_b = tiles[tile_b]
+            tiledata_b = tilegrid[tile_b]
 
             if tiledata_b["type"] != type_b:
                 continue
diff --git a/utils/dbfixup.py b/utils/dbfixup.py
index 859375c..4159c56 100644
--- a/utils/dbfixup.py
+++ b/utils/dbfixup.py
@@ -87,7 +87,7 @@
             print(line, file=f)
 
 
-def update_mask(mask_db, *src_dbs):
+def update_mask(mask_db, *src_dbs, offset=0):
     bits = set()
     mask_db_file = "%s/%s/mask_%s.db" % (
         os.getenv("XRAY_DATABASE_DIR"), os.getenv("XRAY_DATABASE"), mask_db)
@@ -111,8 +111,12 @@
             for line in f:
                 line = line.split()
                 for bit in line[1:]:
-                    if bit[0] != "!":
-                        bits.add(bit)
+                    if bit[0] == "!":
+                        continue
+                    if offset != 0:
+                        m = re.match(r"(\d+)_(\d+)", bit)
+                        bit = "%02d_%02d" % (int(m.group(1)), int(m.group(2)) + offset)
+                    bits.add(bit)
 
     if len(bits) > 0:
         with open(mask_db_file, "w") as f:
@@ -134,8 +138,13 @@
 update_mask("hclk_l", "hclk_l")
 update_mask("hclk_r", "hclk_r")
 
+update_mask("bram_l", "bram_l")
+update_mask("bram_r", "bram_r")
+update_mask("dsp_l", "dsp_l")
+update_mask("dsp_r", "dsp_r")
+
 for k in range(5):
-    update_mask("bram%d_l" % k, "bram%d_l" % k, "int_l")
-    update_mask("bram%d_r" % k, "bram%d_r" % k, "int_r")
-    update_mask("dsp%d_l" % k, "dsp%d_l" % k, "int_l")
-    update_mask("dsp%d_r" % k, "dsp%d_r" % k, "int_r")
+    update_mask("bram_l", "int_l", offset=64*k)
+    update_mask("bram_r", "int_r", offset=64*k)
+    update_mask("dsp_l", "int_l", offset=64*k)
+    update_mask("dsp_r", "int_r", offset=64*k)
diff --git a/utils/mergedb.sh b/utils/mergedb.sh
index a873560..94014ac 100644
--- a/utils/mergedb.sh
+++ b/utils/mergedb.sh
@@ -27,6 +27,16 @@
 			-e 's/^CLB\.SLICE_X0\./CLBLM_R.SLICEM_X0./' \
 			-e 's/^CLB\.SLICE_X1\./CLBLM_R.SLICEL_X1./' ;;
 
+	dsp_l)
+		sed < "$2" > "$tmp1" -e 's/^DSP\./DSP_L./' ;;
+	dsp_r)
+		sed < "$2" > "$tmp1" -e 's/^DSP\./DSP_R./' ;;
+
+	bram_l)
+		sed < "$2" > "$tmp1" -e 's/^BRAM\./BRAM_L./' ;;
+	bram_r)
+		sed < "$2" > "$tmp1" -e 's/^BRAM\./BRAM_R./' ;;
+
 	int_l)
 		sed < "$2" > "$tmp1" -e 's/^INT\./INT_L./' ;;
 	int_r)
diff --git a/utils/segmaker.py b/utils/segmaker.py
index 580edf1..bca2054 100644
--- a/utils/segmaker.py
+++ b/utils/segmaker.py
@@ -39,31 +39,26 @@
 
         self.segments_by_type = dict()
 
-        for tilename, tiledata in self.grid["tiles"].items():
-            if "segment" not in tiledata:
+        for tilename, tiledata in self.grid.items():
+            if "baseaddr" not in tiledata:
                 continue
 
-            segdata = self.grid["segments"][tiledata["segment"]]
-
-            if "baseaddr" not in segdata:
-                continue
-
-            if segdata["type"] not in self.segments_by_type:
-                self.segments_by_type[segdata["type"]] = dict()
-            segments = self.segments_by_type[segdata["type"]]
+            if tiledata["type"] not in self.segments_by_type:
+                self.segments_by_type[tiledata["type"]] = dict()
+            segments = self.segments_by_type[tiledata["type"]]
 
             tile_type = tiledata["type"]
             segname = "%s_%03d" % (
-                segdata["baseaddr"][0][2:], segdata["baseaddr"][1])
+                tiledata["baseaddr"][2:], tiledata["offset"])
 
             def add_segbits():
                 if not segname in segments:
                     segments[segname] = {"bits": set(), "tags": dict()}
 
-                    base_frame = int(segdata["baseaddr"][0][2:], 16)
+                    base_frame = int(tiledata["baseaddr"][2:], 16)
                     for wordidx in range(
-                            segdata["baseaddr"][1],
-                            segdata["baseaddr"][1] + segdata["words"]):
+                            tiledata["offset"],
+                            tiledata["offset"] + tiledata["height"]):
                         if base_frame not in self.bits:
                             continue
                         if wordidx not in self.bits[base_frame]:
@@ -72,8 +67,7 @@
                                 base_frame][wordidx]:
                             bitname_frame = bit_frame - base_frame
                             bitname_bit = 32 * (
-                                bit_wordidx - segdata["baseaddr"][1]
-                            ) + bit_bitidx
+                                bit_wordidx - tiledata["offset"]) + bit_bitidx
                             if bitfilter is None or bitfilter(bitname_frame,
                                                               bitname_bit):
                                 bitname = "%02d_%02d" % (
@@ -111,9 +105,9 @@
     def write(self, suffix=None, roi=False):
         for segtype in self.segments_by_type.keys():
             if suffix is not None:
-                filename = "segdata_%s_%s.txt" % (segtype, suffix)
+                filename = "segdata_%s_%s.txt" % (segtype.lower(), suffix)
             else:
-                filename = "segdata_%s.txt" % (segtype)
+                filename = "segdata_%s.txt" % (segtype.lower())
 
             print("Writing %s." % filename)
 
diff --git a/utils/tileconnloops.py b/utils/tileconnloops.py
index 1bb825a..9499ab1 100644
--- a/utils/tileconnloops.py
+++ b/utils/tileconnloops.py
@@ -7,7 +7,7 @@
 
 with open("%s/%s/tilegrid.json" % (os.getenv("XRAY_DATABASE_DIR"),
                                    os.getenv("XRAY_DATABASE")), "r") as f:
-    tilegrid = json.load(f)["tiles"]
+    tilegrid = json.load(f)
 
 with open("%s/%s/tileconn.json" % (os.getenv("XRAY_DATABASE_DIR"),
                                    os.getenv("XRAY_DATABASE")), "r") as f: