Adding missing java files.
diff --git a/dump_io.java b/dump_io.java
new file mode 100644
index 0000000..6bfa143
--- /dev/null
+++ b/dump_io.java
@@ -0,0 +1,559 @@
+// Copyright 2020 Project U-Ray Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.fpga.rapidwright;
+
+import com.xilinx.rapidwright.design.*;
+import com.xilinx.rapidwright.design.tools.LUTTools;
+import com.xilinx.rapidwright.device.*;
+import com.xilinx.rapidwright.edif.*;
+import org.json.Property;
+import org.python.antlr.ast.Str;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.math.BigInteger;
+import java.util.*;
+
+public class dump_io {
+
+    static boolean is_hpio(SiteTypeEnum ste) {
+        return ste == SiteTypeEnum.HPIOB || ste == SiteTypeEnum.HPIOB_S || ste == SiteTypeEnum.HPIOB_SNGL || ste ==
+                SiteTypeEnum.HPIOB_M;
+    }
+    static boolean is_hdio(SiteTypeEnum ste) {
+        return ste == SiteTypeEnum.HDIOB_M || ste == SiteTypeEnum.HDIOB_S;
+    }
+    static boolean is_hdiol(SiteTypeEnum ste) {
+        return ste == SiteTypeEnum.HDIOLOGIC_M || ste == SiteTypeEnum.HDIOLOGIC_S;
+    }
+
+    static String site_index_in_tile(Tile t, Site s) {
+        int min_x = s.getInstanceX(), min_y = s.getInstanceY();
+        for (Site s2 : t.getSites()) {
+            if (s2.getSiteTypeEnum() == s.getSiteTypeEnum() ||
+                    (is_hpio(s2.getSiteTypeEnum()) && is_hpio(s.getSiteTypeEnum())) ||
+                    (is_hdio(s2.getSiteTypeEnum()) && is_hdio(s.getSiteTypeEnum())) ||
+                    (is_hdiol(s2.getSiteTypeEnum()) && is_hdiol(s.getSiteTypeEnum())))
+            {
+                min_x = Math.min(min_x, s2.getInstanceX());
+                min_y = Math.min(min_y, s2.getInstanceY());
+            }
+        }
+        return s.getSiteTypeEnum().toString() + "_X" + (s.getInstanceX() - min_x) + "Y" + (s.getInstanceY() - min_y);
+    }
+    public static String clean_value(String orig) {
+        if (orig.startsWith("1'b"))
+            return orig.substring(3);
+        else
+            return orig;
+    }
+
+    static HashSet<String> inverted_pips = new HashSet<>();
+
+    static boolean is_sink_inverted(SitePinInst spi) {
+        Net n = spi.getNet();
+        HashMap<Node, PIP> pip_uphill = new HashMap<>();
+        for (PIP p : n.getPIPs()) {
+            pip_uphill.putIfAbsent(p.getEndNode(), p);
+            if (p.isBidirectional()) {
+                pip_uphill.putIfAbsent(p.getStartNode(), p);
+            }
+        }
+        boolean inv = false;
+        Node cursor = spi.getConnectedNode();
+        while (pip_uphill.containsKey(cursor)) {
+            PIP p = pip_uphill.get(cursor);
+            String name = p.getTile().getName() + "/" + p.getTile().getTileTypeEnum().toString() + "." + p.getStartWireName() + (p.isBidirectional() ? "<<->>" : "->>") + p.getEndWireName();
+            if (inverted_pips.contains(name))
+                inv = !inv;
+            //System.out.println(name + " " + inv);
+            if (p.isBidirectional() && p.getStartNode().equals(cursor))
+                cursor = p.getEndNode();
+            else
+                cursor = p.getStartNode();
+        }
+        return inv;
+    }
+
+
+    public static boolean process_inv(ArrayList<String> features, String prefix, String feature, Cell c, String value) {
+        if (!feature.endsWith("INVERTED"))
+            return false;
+        int p0 = feature.indexOf('_'), p1 = feature.lastIndexOf('_');
+        String port = feature.substring(p0 + 1, p1);
+        String phys = c.getPhysicalPinMapping(port);
+        if (phys == null)
+            return true;
+        String site_wire = c.getSiteWireNameFromPhysicalPin(phys);
+        if (site_wire == null)
+            return true;
+        Net n = c.getSiteInst().getNetFromSiteWire(site_wire);
+        if (n == null)
+            return true;
+        if (n.isStaticNet() || n.getLogicalNet() == null)
+            return true;
+        int prop_value = Integer.parseInt(clean_value(value));
+        ArrayList<String> sw = new ArrayList<>();
+        if (is_sink_inverted((c.getSitePinFromLogicalPin(port, sw))))
+            prop_value ^= 1;
+        features.add(prefix + "." + feature + "." + prop_value);
+        return true;
+    }
+    public static void add_oserdes(ArrayList<String> features, String prefix, Cell c) {
+        if (c.getType().equals("<LOCKED>"))
+            return;
+
+        features.add(prefix + ".USED");
+        String cprefix = prefix + "." + c.getType();
+        features.add(cprefix + ".USED");
+        String[] basic_props = {"ODDR_MODE", "OSERDES_D_BYPASS", "OSERDES_T_BYPASS", "INIT",
+            "IS_CLKDIV_INVERTED", "IS_CLK_INVERTED", "IS_RST_INVERTED", "DATA_WIDTH"};
+        for (var prop : basic_props) {
+            var value = c.getProperty(prop);
+            if (value == null)
+                continue;
+            if (process_inv(features, cprefix, prop, c, value.getValue()))
+                continue;
+            features.add(cprefix + "." + prop + "." + clean_value(value.getValue()));
+        }
+    }
+    public static void add_iserdes(ArrayList<String> features, String prefix, Cell c) {
+        if (c.getType().equals("<LOCKED>"))
+            return;
+        features.add(prefix + ".USED");
+        String cprefix = prefix + "." + c.getType();
+        features.add(cprefix + ".USED");
+        String[] basic_props = {"IDDR_MODE", "DDR_CLK_EDGE",
+                "IS_CLK_B_INVERTED", "IS_CLK_INVERTED",
+                "IS_RST_INVERTED", "DATA_WIDTH"};
+        for (var prop : basic_props) {
+            var value = c.getProperty(prop);
+            if (value == null)
+                continue;
+            if (process_inv(features, cprefix, prop, c, value.getValue()))
+                continue;
+            features.add(cprefix + "." + prop + "." + clean_value(value.getValue()));
+        }
+        process_inv(features, cprefix, "IS_CLKDIV_INVERTED", c, "1'b0");
+    }
+    public static void add_iddr(ArrayList<String> features, String prefix, Cell c) {
+        if (c.getType().equals("<LOCKED>"))
+            return;
+        features.add(prefix + ".USED");
+        String cprefix = prefix + "." + c.getType();
+        features.add(cprefix + ".USED");
+        String[] basic_props = {"DDR_CLK_EDGE",
+                "IS_C_INVERTED", "IS_CB_INVERTED", "IS_RST_INVERTED"};
+        for (var prop : basic_props) {
+            var value = c.getProperty(prop);
+            if (value == null)
+                continue;
+            if (process_inv(features, cprefix, prop, c, value.getValue()))
+                continue;
+            features.add(cprefix + "." + prop + "." + clean_value(value.getValue()));
+        }
+    }
+    public static void add_delay(ArrayList<String> features, String prefix, Cell c) {
+        if (c.getType().equals("<LOCKED>"))
+            return;
+        features.add(prefix + ".USED");
+        String[] basic_props = {"DELAY_FORMAT", "DELAY_SRC",
+                "DELAY_TYPE", "IS_CLK_INVERTED", "IS_RST_INVERTED", "LOOPBACK",
+                "UPDATE_MODE"};
+        for (var prop : basic_props) {
+            var value = c.getProperty(prop);
+            if (value == null)
+                continue;
+            if (process_inv(features, prefix, prop, c, value.getValue()))
+                continue;
+            features.add(prefix + "." + prop + "." + clean_value(value.getValue()));
+        }
+        if (c.getProperty("DELAY_FORMAT") != null && c.getProperty("DELAY_FORMAT").getValue().equals("COUNT")) {
+            var counts = c.getProperty("DELAY_VALUE");
+            if (counts != null) {
+                int x = Integer.parseInt(counts.getValue());
+                for (int i = 0; i < 9; i++) {
+                    if (((x >> i) & 0x1) == 0x1)
+                        features.add(prefix + ".DELAY_VALUE[" + i + "]");
+                }
+            }
+        } else if (c.getProperty("DELAY_FORMAT") != null && c.getProperty("DELAY_FORMAT").getValue().equals("TIME")) {
+            var del = c.getProperty("DELAY_VALUE");
+            if (del != null) {
+                int ps = Integer.parseInt(del.getValue());
+                if (ps >= 8) {
+                    int refdiv = Math.min(511, (int)Math.floor((2000000.0/300.0) / ((double)ps)));
+                    for (int i = 0; i < 9; i++) {
+                        if (((refdiv >> i) & 0x1) == 0x1)
+                            features.add(prefix + ".REFCLK_MULT[" + i + "]");
+                    }
+                }
+            }
+        }
+    }
+    public static void add_ff(ArrayList<String> features, String prefix, Cell c) {
+        String ct = c.getType();
+        if (!ct.equals("FDRE") && !ct.equals("FDPE") && !ct.equals("FDCE") && !ct.equals("FDSE"))
+            return;
+        features.add(prefix + ".USED");
+        features.add(prefix + "." + c.getType());
+        var init = c.getProperty("INIT");
+        if (init != null)
+            features.add(prefix + ".INIT." + init.getIntValue().toString());
+    }
+
+    public static void main(String[] args) throws IOException {
+
+        Scanner scanner = new Scanner(new File("mig/invpips.txt"));
+        while (scanner.hasNextLine()) {
+            String nl = scanner.nextLine().trim();
+            inverted_pips.add(nl);
+        }
+
+        File folder = new File("mig/specimen_io");
+        File[] listOfFiles = folder.listFiles();
+        for (File f : listOfFiles) {
+            if (f.getName().endsWith(".dcp")) {
+                Design des = Design.readCheckpoint(f.getAbsolutePath());
+                HashMap<String, ArrayList<String>> pips_by_tile = new HashMap<>();
+                for (Net n : des.getNets()) {
+
+                    HashMap<Node, PIP> uphill = new HashMap<>();
+                    HashSet<Node> allnodes = new HashSet<>();
+
+                    if (n.getSource() != null)
+                        uphill.put(n.getSource().getConnectedNode(), null);
+                    for (PIP p : n.getPIPs()) {
+                        uphill.put(p.getEndNode(), p);
+                        allnodes.add(p.getStartNode());
+                        allnodes.add(p.getEndNode());
+                    }
+
+
+                    for (PIP p : n.getPIPs()) {
+
+                        String tile = p.getTile().getName() + ":" + p.getTile().getTileTypeEnum().toString();
+                        TileTypeEnum ptt = p.getTile().getTileTypeEnum();
+
+                        if (!pips_by_tile.containsKey(tile))
+                            pips_by_tile.put(tile, new ArrayList<>());
+
+                        String startName = p.getStartWireName(), endName = p.getEndWireName();
+                        if (ptt == TileTypeEnum.XIPHY_BYTE_L) {
+                            if (!endName.contains("ODT") && (startName.contains("CLB2PHY") || endName.contains("CLB2PHY")))
+                                continue;
+                            if (endName.contains("CLK_PIN") || endName.contains("CLKDIV_PIN")  || endName.contains("D_PIN"))
+                                continue;
+                            SitePin sp = p.getEndNode().getSitePin();
+                            if (sp != null) {
+                                Site s = sp.getSite();
+                                if (s.getSiteTypeEnum() == SiteTypeEnum.BITSLICE_RX_TX && endName.contains("Q5")) {
+                                    SiteInst si = des.getSiteInstFromSite(s);
+                                    boolean bypass = true;
+                                    if (si != null) {
+                                        for (Cell c : si.getCells()) {
+                                            if (!c.getType().equals("<LOCKED>")) {
+                                                bypass = false;
+                                                break;
+                                            }
+                                        }
+                                    }
+                                    if (bypass) {
+                                        String sn = site_index_in_tile(s.getTile(), s);
+                                        pips_by_tile.get(tile).add(sn + ".BYPASS." + sp.getPinName());
+                                    }
+                                }
+                            }
+                        }
+
+                        if (p.isBidirectional()) {
+                            boolean is_fwd = uphill.containsKey(p.getStartNode());
+                            pips_by_tile.get(tile).add("PIP." + p.getEndWireName() + "." + p.getStartWireName() + (is_fwd ? ".FWD" : ".REV"));
+                        } else {
+                            pips_by_tile.get(tile).add("PIP." + p.getEndWireName() + "." + p.getStartWireName());
+                        }
+                    }
+                }
+                HashMap<String, ArrayList<String>> sitefeatures_by_tile = new HashMap<>();
+                for (SiteInst si : des.getSiteInsts()) {
+                    SiteTypeEnum ste = si.getSiteTypeEnum();
+                    if (ste != SiteTypeEnum.HPIOB &&
+                    ste != SiteTypeEnum.HPIOB_M &&
+                    ste != SiteTypeEnum.HPIOB_S &&
+                    ste != SiteTypeEnum.HPIOB_SNGL &&
+                    ste != SiteTypeEnum.HDIOB_M && ste != SiteTypeEnum.HDIOB_S)
+                        continue;
+                    String tile = si.getTile().getName() + ":" + si.getTile().getTileTypeEnum().toString();
+                    String sn = site_index_in_tile(si.getTile(), si.getSite());
+                    if (!sitefeatures_by_tile.containsKey(tile))
+                        sitefeatures_by_tile.put(tile, new ArrayList<>());
+                    ArrayList<String> tfeat = sitefeatures_by_tile.get(tile);
+                    Net ionet = si.getNetFromSiteWire("OUTBUF_O");
+                    if (ionet == null)
+                        continue;
+                    Cell pad = si.getCell(si.getBEL("PAD"));
+                    if (pad == null)
+                        continue;
+                    EDIFPort p = des.getTopEDIFCell().getPort(pad.getName());
+                    var prop = des.getTopEDIFCell().getNet(pad.getName()).getProperties();
+                    var pprop = p.getProperties();
+                    String iostd = prop.get(new EDIFName("IOSTANDARD")).getValue();
+                    String prefix = sn + "." + iostd;
+                    tfeat.add(sn + ".USED");
+                    tfeat.add(prefix + ".USED");
+                    Cell inbuf = si.getCell(si.getBEL("INBUF"));
+                    Cell outbuf = si.getCell(si.getBEL("OUTBUF"));
+                    Cell pull = si.getCell(si.getBEL("PULL"));
+                    if (pull != null)
+                        System.out.println(pull.getType());
+
+                    if (inbuf != null) {
+                        tfeat.add(sn + ".IN");
+                        tfeat.add(prefix + ".IN");
+                        if (outbuf == null) {
+                            tfeat.add(sn + ".IN_ONLY");
+                            tfeat.add(prefix + ".IN_ONLY");
+                        }
+                        if (iostd.startsWith("SSTL") || iostd.startsWith("POD") || iostd.startsWith("HSUL") || iostd.startsWith("HSTL"))
+                        {
+                            if (pprop.containsKey(new EDIFName("X_internal_vref"))) {
+                                double vref = Double.parseDouble(pprop.get(new EDIFName("X_internal_vref")).getValue());
+                                tfeat.add("VREF.INTERNAL." + (int)(1000 * vref) + "_MV");
+                            } else if (!pprop.containsKey(new EDIFName("X_internal_vref")) && !pprop.containsKey(new EDIFName("X_int_vref"))) {
+                                tfeat.add("VREF.EXTERNAL");
+                            }
+                        }
+                        if (iostd.endsWith("DCI") && (si.getTile().getTileYCoordinate() % 60) == 0) {
+                            tfeat.add("VRP_USED");
+                        }
+                        Cell ibufctrl = si.getCell(si.getBEL("IBUFCTRL"));
+                        if (ibufctrl != null && ibufctrl.getLogicalPinMapping("IBUFDISABLE") != null)
+                            tfeat.add(sn + ".IBUFDISABLE_USED");
+                    }
+                    if (outbuf != null) {
+                        tfeat.add(sn + ".OUT");
+                        tfeat.add(prefix + ".OUT");
+                        if (inbuf != null) {
+                            tfeat.add(sn + ".IN_OUT");
+                            tfeat.add(prefix + ".IN_OUT");
+                        }
+                        if (iostd.endsWith("DCI") && (si.getTile().getTileYCoordinate() % 60) == 0) {
+                            tfeat.add("VRP_USED");
+                        }
+                        if (outbuf.getLogicalPinMapping("DCITERMDISABLE") != null) {
+                            tfeat.add(sn + ".DCITERMDISABLE_USED");
+                        }
+                    }
+
+                    if (pprop.containsKey(new EDIFName("X_PULLTYPE")))
+                        tfeat.add(sn + ".PULLTYPE." + pprop.get(new EDIFName("X_PULLTYPE")).getValue());
+                    if (pprop.containsKey(new EDIFName("X_DRIVE")))
+                        tfeat.add(prefix + ".DRIVE." + pprop.get(new EDIFName("X_DRIVE")).getValue());
+                    if (pprop.containsKey(new EDIFName("X_SLEW")) && outbuf != null) {
+                        if (pprop.containsKey(new EDIFName("X_DRIVE"))) {
+                            tfeat.add(prefix + ".DRIVE." + pprop.get(new EDIFName("X_DRIVE")).getValue() + ".SLEW." + pprop.get(new EDIFName("X_SLEW")).getValue());
+                        }else {
+                            tfeat.add(prefix + ".SLEW." + pprop.get(new EDIFName("X_SLEW")).getValue());
+                        }
+                    }
+                    if (pprop.containsKey(new EDIFName("X_OUTPUT_IMPEDANCE")) && outbuf != null)
+                        tfeat.add(prefix + ".OUTPUT_IMPEDANCE." + pprop.get(new EDIFName("X_OUTPUT_IMPEDANCE")).getValue());
+                    if (pprop.containsKey(new EDIFName("X_EQUALIZATION")))
+                        tfeat.add(prefix + ".EQUALIZATION." + pprop.get(new EDIFName("X_EQUALIZATION")).getValue());
+                    if (pprop.containsKey(new EDIFName("X_ODT")))
+                        tfeat.add(prefix + ".ODT." + pprop.get(new EDIFName("X_ODT")).getValue());
+                    for (SitePIP pip : si.getUsedSitePIPs()) {
+                        if (pip.getBEL().getBELType().contains("INV"))
+                            continue;
+                        tfeat.add(sn + "." + pip.getBEL().getName() + "." + pip.getInputPinName());
+                    }
+                }
+                for (SiteInst si : des.getSiteInsts()) {
+                    SiteTypeEnum ste = si.getSiteTypeEnum();
+                    if (ste != SiteTypeEnum.HPIOBDIFFINBUF && ste != SiteTypeEnum.HPIOBDIFFOUTBUF)
+                        continue;
+                    String tile = si.getTile().getName() + ":" + si.getTile().getTileTypeEnum().toString();
+                    String sn = site_index_in_tile(si.getTile(), si.getSite());
+                    if (!sitefeatures_by_tile.containsKey(tile))
+                        sitefeatures_by_tile.put(tile, new ArrayList<>());
+                    ArrayList<String> tfeat = sitefeatures_by_tile.get(tile);
+                    boolean used = false;
+                    for (Cell c : si.getCells())
+                        if (!c.getType().equals("<LOCKED>"))
+                            used = true;
+                    if (!used)
+                        continue;;
+                    if (ste == SiteTypeEnum.HPIOBDIFFINBUF) {
+                        Node cursor = si.getSite().getConnectedNode("PAD_RES_0");
+                        while (cursor.getSitePin() == null || cursor.getSitePin().getSite().getSiteTypeEnum() != SiteTypeEnum.HPIOB_M) {
+                            for (Wire w : cursor.getAllWiresInNode()) {
+                                if (w.getBackwardPIPs().isEmpty())
+                                    continue;
+                                cursor = w.getBackwardPIPs().get(0).getStartNode();
+                                break;
+                            }
+                        }
+                        Site m = cursor.getSitePin().getSite();
+                        SiteInst m_si = des.getSiteInstFromSite(m);
+                        Cell pad = m_si.getCell(m_si.getBEL("PAD"));
+                        EDIFPort p = des.getTopEDIFCell().getPort(pad.getName());
+                        var prop = des.getTopEDIFCell().getNet(pad.getName()).getProperties();
+                        var pprop = p.getProperties();
+                        String iostd = prop.get(new EDIFName("IOSTANDARD")).getValue();
+                        tfeat.add(sn + "." + iostd + ".DIFF_IN");
+                        if (pprop.containsKey(new EDIFName("X_DIFF_TERM_ADV")))
+                            tfeat.add(sn + "." + iostd + ".DIFF_TERM_ADV." + pprop.get(new EDIFName("X_DIFF_TERM_ADV")).getValue());
+                    } else if (ste == SiteTypeEnum.HPIOBDIFFOUTBUF) {
+                        Node cursor = si.getSite().getConnectedNode("AOUT");
+                        while (cursor.getSitePin() == null || cursor.getSitePin().getSite().getSiteTypeEnum() != SiteTypeEnum.HPIOB_M) {
+                            for (Wire w : cursor.getAllWiresInNode()) {
+                                if (w.getForwardPIPs().isEmpty())
+                                    continue;
+                                cursor = w.getForwardPIPs().get(0).getEndNode();
+                                break;
+                            }
+                        }
+                        Site m = cursor.getSitePin().getSite();
+                        SiteInst m_si = des.getSiteInstFromSite(m);
+                        Cell pad = m_si.getCell(m_si.getBEL("PAD"));
+                        EDIFPort p = des.getTopEDIFCell().getPort(pad.getName());
+                        var prop = des.getTopEDIFCell().getNet(pad.getName()).getProperties();
+                        var pprop = p.getProperties();
+                        String iostd = prop.get(new EDIFName("IOSTANDARD")).getValue();
+                        tfeat.add(sn + "." + iostd + ".DIFF_OUT");
+                    }
+                }
+                for (SiteInst si : des.getSiteInsts()) {
+                    String tile = si.getTile().getName() + ":" + si.getTile().getTileTypeEnum().toString();
+                    if (si.getSiteTypeEnum() == SiteTypeEnum.BITSLICE_COMPONENT_RX_TX) {
+                        if (!sitefeatures_by_tile.containsKey(tile))
+                            sitefeatures_by_tile.put(tile, new ArrayList<>());
+                        ArrayList<String> tfeat = sitefeatures_by_tile.get(tile);
+                        String sp = site_index_in_tile(si.getTile(), si.getSite()) + ".";
+                        boolean only_locked = true;
+                        for (Cell c : si.getCells())
+                            if (!c.getType().equals("<LOCKED>"))
+                                only_locked = false;
+                        if (!only_locked)
+                            tfeat.add(sp + "COMPONENT_MODE");
+                        else
+                            continue;
+                        Cell idelay = si.getCell("IDELAY");
+                        if (idelay != null)
+                            add_delay(tfeat, sp + "IDELAY", idelay);
+                        Cell odelay = si.getCell("ODELAY");
+                        if (odelay != null)
+                            add_delay(tfeat, sp + "ODELAY", odelay);
+                        Cell iserdes = si.getCell("ISERDES");
+                        if (iserdes != null)
+                            add_iserdes(tfeat, sp + "ISERDES", iserdes);
+                        Cell oserdes = si.getCell("OSERDES");
+                        if (oserdes != null)
+                            add_oserdes(tfeat, sp + "OSERDES", oserdes);
+                        Cell iff = si.getCell("IN_FF");
+                        if (iff != null)
+                            add_ff(tfeat, sp + "IN_FF", iff);
+                        Cell off = si.getCell("OUT_FF");
+                        if (off != null)
+                            add_ff(tfeat, sp + "OUT_FF", off);
+                        for (SitePIP pip : si.getUsedSitePIPs()) {
+                            if (pip.getBEL().getBELType().contains("INV"))
+                                continue;
+                            tfeat.add(sp + pip.getBEL().getName() + "." + pip.getInputPinName());
+                        }
+                        if (odelay != null && oserdes == null && off == null && si.getNetFromSiteWire("OSERDES_OQ") != null)
+                            tfeat.add(sp + "OSERDES.BYPASSED");
+                        if (idelay == null && si.getNetFromSiteWire("IDELAY_DATAOUT") != null)
+                            tfeat.add(sp + "IDELAY.BYPASSED");
+                        String[] used_sitewires = {"OSERDES_OQ", "TX_Q", "RX_D"};
+                        for (String sw : used_sitewires) {
+                            boolean active = false;
+                            if (si.getNetFromSiteWire(sw) != null)
+                                active = true;
+                            for (SitePIP p : si.getUsedSitePIPs())
+                                if (p.getInputPin().getSiteWireName().equals(sw))
+                                    active = true;
+                            if (active)
+                                tfeat.add(sp + sw + ".USED");
+                        }
+                    } else if (si.getSiteTypeEnum() == SiteTypeEnum.BITSLICE_CONTROL) {
+                        if (!sitefeatures_by_tile.containsKey(tile))
+                            sitefeatures_by_tile.put(tile, new ArrayList<>());
+                        ArrayList<String> tfeat = sitefeatures_by_tile.get(tile);
+                        String sp = site_index_in_tile(si.getTile(), si.getSite()) + ".";
+                        for (Cell c : si.getCells()) {
+                            if (c.getType().equals("IDELAYCTRL")) {
+                                tfeat.add(sp + "IDELAYCTRL.USED");
+                            }
+                        }
+                    } else if (si.getSiteTypeEnum() == SiteTypeEnum.HDIOLOGIC_M || si.getSiteTypeEnum() == SiteTypeEnum.HDIOLOGIC_S) {
+                        if (!sitefeatures_by_tile.containsKey(tile))
+                            sitefeatures_by_tile.put(tile, new ArrayList<>());
+                        ArrayList<String> tfeat = sitefeatures_by_tile.get(tile);
+                        String sp = site_index_in_tile(si.getTile(), si.getSite()) + ".";
+                        boolean only_locked = true;
+                        for (Cell c : si.getCells())
+                            if (!c.getType().equals("<LOCKED>"))
+                                only_locked = false;
+                        if (!only_locked)
+                            tfeat.add(sp + "USED");
+                        else
+                            continue;
+                        Cell iff = si.getCell("IFF");
+                        if (iff != null)
+                            add_ff(tfeat, sp + "IFF", iff);
+                        Cell off = si.getCell("OPFF");
+                        if (off != null)
+                            add_ff(tfeat, sp + "OPFF", off);
+                        Cell tff = si.getCell("TFF");
+                        if (tff != null)
+                            add_ff(tfeat, sp + "TFF", tff);
+
+                        Cell iddr = si.getCell("IDDR");
+                        if (iddr != null)
+                            add_iddr(tfeat, sp + "IDDR", iddr);
+                        Cell optff = si.getCell("OPTFF");
+                        if (optff != null)
+                            add_oserdes(tfeat, sp + "OPTFF", optff);
+                    }
+                }
+
+
+                FileWriter vf = new FileWriter(f.getAbsolutePath().replace(".dcp", ".features"), false);
+                PrintWriter v = new PrintWriter(vf);
+                for (Tile t : des.getDevice().getAllTiles()) {
+                    TileTypeEnum tt = t.getTileTypeEnum();
+                    if (tt == TileTypeEnum.HPIO_L || tt == TileTypeEnum.HPIO_RIGHT || tt == TileTypeEnum.HDIO_TOP_RIGHT
+                        || tt == TileTypeEnum.HDIO_BOT_RIGHT || tt == TileTypeEnum.XIPHY_BYTE_L) {
+                        String tname = t.getName() + ":" + tt.toString();
+                        if (sitefeatures_by_tile.containsKey(tname) || pips_by_tile.containsKey(tname)) {
+                            v.println(".tile " + tname);
+                            if (sitefeatures_by_tile.containsKey(tname))
+                                for (String sf : sitefeatures_by_tile.get(tname))
+                                    v.println(sf);
+                            if (pips_by_tile.containsKey(tname))
+                                for (String pip : pips_by_tile.get(tname))
+                                    v.println(pip);
+                        }
+                    }
+                }
+                v.close();
+                vf.close();
+            }
+        }
+    }
+}
diff --git a/dump_laguna.java b/dump_laguna.java
new file mode 100644
index 0000000..0575628
--- /dev/null
+++ b/dump_laguna.java
@@ -0,0 +1,227 @@
+// Copyright 2020 Project U-Ray Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package dev.fpga.rapidwright;
+
+import com.xilinx.rapidwright.design.Cell;
+import com.xilinx.rapidwright.design.Design;
+import com.xilinx.rapidwright.design.Net;
+import com.xilinx.rapidwright.design.SiteInst;
+import com.xilinx.rapidwright.device.*;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Scanner;
+
+public class dump_laguna {
+
+    static String site_index_in_tile(Tile t, Site s) {
+        int min_x = s.getInstanceX(), min_y = s.getInstanceY();
+        for (Site s2 : t.getSites()) {
+            if (s2.getSiteTypeEnum() == s.getSiteTypeEnum()) {
+                min_x = Math.min(min_x, s2.getInstanceX());
+                min_y = Math.min(min_y, s2.getInstanceY());
+            }
+        }
+        return s.getSiteTypeEnum().toString() + "_X" + (s.getInstanceX() - min_x) + "Y" + (s.getInstanceY() - min_y);
+    }
+
+    public static void main(String[] args) throws IOException {
+
+
+        File folder = new File("specimen_lag");
+        File[] listOfFiles = folder.listFiles();
+        for (File f : listOfFiles) {
+            if (f.getName().endsWith(".dcp")) {
+                Design des = Design.readCheckpoint(f.getAbsolutePath());
+                HashMap<String, ArrayList<String>> pips_by_tile = new HashMap<>();
+                HashMap<String, ArrayList<String>> sitefeatures_by_tile = new HashMap<>();
+                HashSet<Node> usednodes = new HashSet<>();
+                for (Net n : des.getNets()) {
+                    HashMap<Node, PIP> uphill = new HashMap<>();
+
+                    if (n.getSource() != null)
+                        uphill.put(n.getSource().getConnectedNode(), null);
+                    for (PIP p : n.getPIPs()) {
+                        if (!p.isBidirectional()) {
+                            uphill.put(p.getEndNode(), p);
+                        }
+
+                        usednodes.add(p.getStartNode());
+                        usednodes.add(p.getEndNode());
+                    }
+                    for (PIP p : n.getPIPs()) {
+
+                        String tile = p.getTile().getName() + ":" + p.getTile().getTileTypeEnum().toString();
+                        if (p.getTile().getTileTypeEnum() == TileTypeEnum.BRAM || p.getTile().getTileTypeEnum() == TileTypeEnum.INT)
+                            continue;
+                        if (!pips_by_tile.containsKey(tile))
+                            pips_by_tile.put(tile, new ArrayList<>());
+                        if (p.isBidirectional()) {
+                            Node cursor = p.getStartNode();
+                            boolean is_fwd = false;
+                            if (uphill.containsKey(p.getStartNode())) {
+                                is_fwd = true;
+                                uphill.put(p.getEndNode(), p);
+                            } else if (uphill.containsKey(p.getEndNode())) {
+                                is_fwd = false;
+                                uphill.put(p.getStartNode(), p);
+                            } else {
+                                continue;
+                            }
+                            pips_by_tile.get(tile).add("PIP." + p.getEndWireName() + "." + p.getStartWireName() + (is_fwd ? ".FWD" : ".REV"));
+                        } else {
+                            pips_by_tile.get(tile).add("PIP." + p.getEndWireName() + "." + p.getStartWireName());
+                            SitePin sp = p.getEndNode().getSitePin();
+                            if (sp != null && p.isRouteThru() && sp.getSite().getSiteTypeEnum() == SiteTypeEnum.LAGUNA && sp.getPinName().startsWith("RXQ")) {
+                                String prefix = site_index_in_tile(p.getTile(), sp.getSite());
+                                String pin = sp.getPinName();
+                                pips_by_tile.get(tile).add(prefix + ".RX_REG" + pin.charAt(pin.length()-1) + ".BYPASS");
+                            }
+                        }
+                    }
+                }
+
+                for (Tile t : des.getDevice().getAllTiles()) {
+                    if (t.getTileTypeEnum() == TileTypeEnum.LAG_LAG) {
+                        String tile = t.getName() + ":" + t.getTileTypeEnum().toString();
+                        if (!sitefeatures_by_tile.containsKey(tile))
+                            sitefeatures_by_tile.put(tile, new ArrayList<>());
+                        ArrayList<String> tfeat = sitefeatures_by_tile.get(tile);
+                        for (Site s : t.getSites()) {
+                            if (s.getSiteTypeEnum() != SiteTypeEnum.LAGUNA)
+                                continue;
+                            boolean[] no_rx_used = {true, true, true, true, true, true};
+                            SiteInst si = des.getSiteInstFromSite(s);
+                            if (si != null) {
+                                for (BEL b : si.getBELs()) {
+                                    if (!b.getName().startsWith("RX_REG"))
+                                        continue;
+                                    if (si.getCell(b) != null && si.getCell(b).getType() != null && !si.getCell(b).getType().equals("<LOCKED>")) {
+                                        String qw = si.getCell(b).getSiteWireNameFromLogicalPin("Q");
+                                        if (qw != null && si.getNetFromSiteWire(qw) != null && !si.getNetFromSiteWire(qw).getSinkPins().isEmpty())
+                                            no_rx_used[Integer.parseInt(b.getName().substring(6))] = false;
+                                    }
+                                }
+                            }
+                            String prefix = site_index_in_tile(t, s);
+                            for (int i = 0; i < 6; i++)
+                                if (no_rx_used[i])
+                                    tfeat.add(prefix + ".RX_REG" + i + ".BYPASS");
+
+                        }
+                    }
+                }
+                for (SiteInst si : des.getSiteInsts()) {
+                    if (si.getSiteTypeEnum() != SiteTypeEnum.LAGUNA)
+                        continue;
+                    String prefix = site_index_in_tile(si.getTile(), si.getSite());
+                    String tile = si.getTile().getName() + ":" + si.getTile().getTileTypeEnum().toString();
+                    if (!sitefeatures_by_tile.containsKey(tile))
+                        sitefeatures_by_tile.put(tile, new ArrayList<>());
+                    ArrayList<String> tfeat = sitefeatures_by_tile.get(tile);
+                    for (BEL b : si.getBELs()) {
+                        Cell c = si.getCell(b);
+                        if (c != null && c.getType() != null && !c.getType().equals("<LOCKED>")) {
+                            boolean is_latch = false, is_sync = false, srval = false;
+                            String clkpin, srpin, cepin;
+                            if (c.getType().equals("FDPE")) {
+                                is_sync = false;
+                                srval = true;
+                                clkpin = "C";
+                                cepin = "CE";
+                                srpin = "PRE";
+                            } else if (c.getType().equals("FDCE")) {
+                                is_sync = false;
+                                srval = false;
+                                clkpin = "C";
+                                cepin = "CE";
+                                srpin = "CLR";
+                            } else if (c.getType().equals("FDSE")) {
+                                is_sync = true;
+                                srval = true;
+                                clkpin = "C";
+                                cepin = "CE";
+                                srpin = "S";
+                            } else if (c.getType().equals("FDRE")) {
+                                is_sync = true;
+                                srval = false;
+                                clkpin = "C";
+                                cepin = "CE";
+                                srpin = "R";
+                            } else {
+                                continue;
+                            }
+                            String group = b.getName().substring(0, 6);
+                            tfeat.add(prefix + "." + group + ".USED");
+                            if (is_sync)
+                                tfeat.add(prefix + "." + group + ".SYNC");
+                            else
+                                tfeat.add(prefix + "." + group + ".ASYNC");
+                            tfeat.add(prefix + "." + b.getName() + ".SRVAL." + (srval ? "1" : "0"));
+                            if (c.getProperty("INIT") != null) {
+                                String init = c.getProperty("INIT").getValue();
+                                tfeat.add(prefix + "." + b.getName() + ".INIT." + init.substring(init.length() - 1));
+                            }
+                            String clk = c.getSiteWireNameFromLogicalPin(clkpin);
+                            if (clk != null && si.getNetFromSiteWire(clk) != null && !si.getNetFromSiteWire(clk).isStaticNet()) {
+                                if (c.getProperty("IS_C_INVERTED") != null) {
+                                    String init = c.getProperty("IS_C_INVERTED").getValue();
+                                    tfeat.add(prefix + "." + group + ".CLKINV." + init.substring(init.length() - 1));
+                                }
+                            }
+                            String srw = c.getSiteWireNameFromLogicalPin(srpin);
+                            if (srw != null && si.getNetFromSiteWire(srw) != null && !si.getNetFromSiteWire(srw).isStaticNet()) {
+                                if (c.getProperty("IS_" + srpin + "_INVERTED") != null) {
+                                    String init = c.getProperty("IS_" + srpin + "_INVERTED").getValue();
+                                    tfeat.add(prefix + "." + group + ".SRINV." + init.substring(init.length() - 1));
+                                }
+                                tfeat.add(prefix + "." + group + ".SRUSED");
+                            }
+                            String cew = c.getSiteWireNameFromLogicalPin(cepin);
+                            if (cew != null && si.getNetFromSiteWire(srw) != null && !si.getNetFromSiteWire(srw).isStaticNet()) {
+                                tfeat.add(prefix + "." + group + ".CEUSED");
+                            }
+                        }
+                    }
+                }
+                FileWriter vf = new FileWriter(f.getAbsolutePath().replace(".dcp", ".features"), false);
+                PrintWriter v = new PrintWriter(vf);
+                for (Tile t : des.getDevice().getAllTiles()) {
+                    TileTypeEnum tt = t.getTileTypeEnum();
+                    if (tt == TileTypeEnum.LAG_LAG) {
+                        String tname = t.getName() + ":" + tt.toString();
+                        if (!pips_by_tile.containsKey(tname) && !sitefeatures_by_tile.containsKey(tname))
+                            continue;
+                        v.println(".tile " + tname);
+                        if (pips_by_tile.containsKey(tname))
+                            for (String p : pips_by_tile.get(tname))
+                                v.println(p);
+                        if (sitefeatures_by_tile.containsKey(tname))
+                            for (String sf : sitefeatures_by_tile.get(tname))
+                                v.println(sf);
+                    }
+                }
+                vf.close();
+            }
+
+        }
+
+    }
+}