Initial no release repo.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0bd33b4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+Do not release these files at the moment.
+
+They use the RapidWright API -- we want to rewrite them to use Vivado instead.
diff --git a/allffs.java b/allffs.java
new file mode 100644
index 0000000..27dc217
--- /dev/null
+++ b/allffs.java
@@ -0,0 +1,62 @@
+// 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.device.Site;
+import com.xilinx.rapidwright.device.SiteTypeEnum;
+import com.xilinx.rapidwright.device.Tile;
+
+import java.io.IOException;
+
+public class allffs {
+    public static void main(String[] args) throws IOException {
+        Design des = new Design("top", "xczu7ev-ffvc1156-2-e");
+
+        Cell d_iob = des.createAndPlaceIOB("d", PinType.IN , "B1", "LVCMOS33");
+        Cell clk_iob = des.createAndPlaceIOB("clk", PinType.IN , "B3", "LVCMOS33");
+        Cell q_iob = des.createAndPlaceIOB("q", PinType.OUT , "A2", "LVCMOS33");
+        q_iob.getSiteInst().addSitePIP("OUTMUX", "OP");
+        Net curr_d = des.createNet("d_IBUF");
+        curr_d.connect(d_iob, "O");
+        Net clk = des.createNet("clk_IBUF");
+        clk.connect(clk_iob, "O");
+        int idx = 0;
+
+        for (Tile t : des.getDevice().getAllTiles()) {
+            for (Site s : t.getSites()) {
+                if (s.getSiteTypeEnum() == SiteTypeEnum.SLICEL || s.getSiteTypeEnum() == SiteTypeEnum.SLICEM) {
+                    Cell ff = des.createAndPlaceCell("ff_" + idx, Unisim.FDCE, s.getName() + "/AFF");
+                    ff.getEDIFCellInst().addProperty("INIT", "1'b1");
+                    clk.connect(ff, "C");
+                    clk.addPin(new SitePinInst(false, "CLK1", des.getSiteInstFromSite(s)));
+                    curr_d.connect(ff, "D");
+                    curr_d.addPin(new SitePinInst(false, "AX", des.getSiteInstFromSite(s)));
+                    des.getSiteInstFromSite(s).addSitePIP("FFMUXA1", "BYP");
+                    des.getSiteInstFromSite(s).addSitePIP("CLK1INV", "CLK");
+
+                    curr_d = des.createNet("ff_q_" + idx);
+                    curr_d.connect(ff, "Q");
+                    idx += 1;
+                }
+            }
+        }
+
+        curr_d.connect(q_iob, "I");
+        des.routeSites();
+        des.writeCheckpoint("rapidwright_test/ffs.dcp");
+    }
+
+}
diff --git a/dump_bram.java b/dump_bram.java
new file mode 100644
index 0000000..501a66c
--- /dev/null
+++ b/dump_bram.java
@@ -0,0 +1,257 @@
+// 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 jnr.ffi.annotations.In;
+import org.json.Property;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+
+public class dump_bram {
+
+    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_bram");
+        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<>();
+                for (Net n : des.getNets()) {
+                    for (PIP p : n.getPIPs()) {
+                        String tile = p.getTile().getName() + ":" + p.getTile().getTileTypeEnum().toString();
+                        if (p.getTile().getTileTypeEnum() == TileTypeEnum.BRAM)
+                            continue;
+                        if (!pips_by_tile.containsKey(tile))
+                            pips_by_tile.put(tile, new ArrayList<>());
+                        pips_by_tile.get(tile).add("PIP." + p.getEndWireName() + "." + p.getStartWireName());
+                    }
+                }
+                for (SiteInst si : des.getSiteInsts()) {
+                    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);
+                   if (si.getSiteTypeEnum() == SiteTypeEnum.RAMB180 || si.getSiteTypeEnum() == SiteTypeEnum.RAMB181 || si.getSiteTypeEnum() == SiteTypeEnum.RAMB36) {
+                        for (BEL b : si.getBELs()) {
+                            String belName = b.getName();
+                            Cell c = si.getCell(b);
+                            if (c == null || c.getType() == null)
+                                continue;
+                            if ((si.getSiteTypeEnum() == SiteTypeEnum.RAMB36 && !c.getType().equals("RAMB36E2")) ||
+                                    ((si.getSiteTypeEnum() == SiteTypeEnum.RAMB180 || si.getSiteTypeEnum() == SiteTypeEnum.RAMB181) && !c.getType().equals("RAMB18E2")))
+                                continue;
+                            int prim_width = si.getSiteTypeEnum() == SiteTypeEnum.RAMB36 ? 36 : 18;
+                            tfeat.add("BRAM.IN_USE");
+                            tfeat.add(belName + ".IN_USE");
+                            boolean have_ecc = false;
+
+                            boolean is_sdp = false;
+                            if (c.getProperty("READ_WIDTH_A") != null && Integer.parseInt(c.getProperty("READ_WIDTH_A").getValue()) > prim_width)
+                                is_sdp = true;
+                            if (c.getProperty("WRITE_WIDTH_B") != null && Integer.parseInt(c.getProperty("WRITE_WIDTH_B").getValue()) > prim_width)
+                                is_sdp = true;
+
+                            String[] enum_props = {
+                                    "CLOCK_DOMAINS",
+                                    "DOA_REG", "DOB_REG",
+                                    "READ_WIDTH_A", "READ_WIDTH_B", "WRITE_WIDTH_A", "WRITE_WIDTH_B",
+                                    "WRITE_MODE_A", "WRITE_MODE_B",
+                                    "ENADDRENA", "ENADDRENB", "RDADDRCHANGEA", "RDADDRCHANGEB",
+                                    "RSTREG_PRIORITY_A", "RSTREG_PRIORITY_B",
+                                    "SLEEP_ASYNC", "EN_ECC_PIPE", "EN_ECC_READ", "EN_ECC_WRITE",
+                                    "CASCADE_ORDER_A", "CASCADE_ORDER_B"};
+                            for (String p : enum_props) {
+                                EDIFPropertyValue ep = c.getProperty(p);
+                                if (ep == null)
+                                    continue;
+                                String v = ep.getValue();
+                                int base_pos = v.indexOf("'b");
+                                if (base_pos != -1)
+                                    v = v.substring(base_pos + 2);
+                                if (p.startsWith("RSTREG_") && is_sdp)
+                                    continue;
+                                tfeat.add(belName + "." + p + "." + v);
+                                if (p.contains("EN_ECC") && v.equals("TRUE"))
+                                    have_ecc = true;
+                            }
+
+                            var all_props = c.getProperties();
+                            for (var e : all_props.entrySet()) {
+                                String name = e.getKey().getName();
+                                if (name.startsWith("IS_") && name.endsWith("_INVERTED")) {
+                                    String pinname = name.substring(3, name.length() - 9);
+                                    String v = e.getValue().getValue();
+                                    int base_pos = v.indexOf("'b");
+                                    if (base_pos != -1)
+                                        v = v.substring(base_pos + 2);
+                                    tfeat.add(belName + "." + pinname + "INV." + v);
+                                }
+                            }
+
+                            String[] word_props = {"INIT_A", "INIT_B", "SRVAL_A", "SRVAL_B"};
+                            for (String p : word_props) {
+                                EDIFPropertyValue width = c.getProperty("READ_WIDTH_" + p.substring(p.length() - 1));
+                                if (width == null || !width.getValue().equals(Integer.toString(prim_width)) || have_ecc)
+                                    continue;
+                                if (c.getProperty(p) != null) {
+                                    long lval = LUTTools.getInitValue(c.getProperty(p).getValue());
+                                    for (int i = 0; i < prim_width; i++) {
+                                        if ((lval & (1L << i)) != 0)
+                                            tfeat.add(belName + "." + p + "[" + i + "]");
+                                    }
+                                }
+                            }
+                            for (EDIFPortInst epi : c.getEDIFCellInst().getPortInsts()) {
+                                if (epi.getName().startsWith("DOUT") && epi.getNet() != null)
+                                    tfeat.add(belName + "." + epi.getName() + ".USED");
+                            }
+                        }
+                    } else if (si.getSiteTypeEnum() == SiteTypeEnum.DSP48E2) {
+                       for (Cell c : si.getCells()) {
+                           if (!c.getName().contains("/"))
+                               continue;
+                           EDIFCellInst eci = des.getTopEDIFCell().getCellInst(c.getName().split("/")[0]);
+                           if (eci == null)
+                               continue;
+                           if (eci.getCellType().getName() != null && eci.getCellType().getName().equals("DSP48E2")) {
+                               String name = site_index_in_tile(si.getTile(), si.getSite());
+                               tfeat.add("DSP.IN_USE");
+                               tfeat.add(name + ".IN_USE");
+                               boolean have_patdet = false;
+                               if (eci.getProperty("USE_PATTERN_DETECT") != null && eci.getProperty("USE_PATTERN_DETECT").getValue().equals("PATDET"))
+                                   have_patdet = true;
+                               boolean have_xor = false;
+                               if (eci.getProperty("USE_WIDEXOR") != null && eci.getProperty("USE_WIDEXOR").getValue().equals("TRUE"))
+                                   have_xor = true;
+
+                               String[] enum_props = {"ADREG", "ALUMODEREG", "AMULTSEL", "AREG", "AUTORESET_PATDET", "AUTORESET_PRIORITY",
+                                    "BMULTSEL", "BREG", "CARRYINREG", "CARRYINSELREG", "CREG", "DREG", "INMODEREG", "MREG", "OPMODEREG",
+                                       "PREADDINSEL", "PREG", "SEL_MASK", "SEL_PATTERN", "USE_PATTERN_DETECT", "USE_SIMD", "USE_WIDEXOR", "XORSIMD"};
+                               for (String p : enum_props) {
+                                   EDIFPropertyValue ep = eci.getProperty(p);
+                                   if (ep == null)
+                                       continue;
+                                   if (!have_patdet && (p.equals("AUTORESET_PATDET") || p.equals("AUTORESET_PRIORITY") || p.equals("SEL_MASK") || p.equals("SEL_PATTERN")))
+                                       continue;
+                                   if (!have_xor && p.equals("XORSIMD"))
+                                       continue;
+                                   String v = ep.getValue();
+                                   int base_pos = v.indexOf("'b");
+                                   if (base_pos != -1)
+                                       v = v.substring(base_pos + 2);
+                                   tfeat.add(name + "." + p + "." + v);
+                               }
+
+                               var all_props = eci.getProperties();
+                               for (var e : all_props.entrySet()) {
+                                   String pname = e.getKey().getName();
+                                   if (pname.startsWith("IS_") && pname.endsWith("_INVERTED")) {
+                                       String pinname = pname.substring(3, pname.length() - 9);
+                                       int width = 1;
+                                       if (pinname.equals("ALUMODE"))
+                                           width = 4;
+                                       else if (pinname.equals("INMODE"))
+                                           width = 5;
+                                       else if (pinname.equals("OPMODE"))
+                                           width = 9;
+                                       if (pinname.contains("RST") && (eci.getPortInst(pinname).getNet() == null || eci.getPortInst(pinname).getNet().getName().contains("const")))
+                                           continue;
+                                       String v = e.getValue().getValue();
+                                       if (width == 1) {
+                                           int base_pos = v.indexOf("'b");
+                                           if (base_pos != -1)
+                                               v = v.substring(base_pos + 2);
+                                           tfeat.add(name + "." + pinname + "INV." + v);
+                                       } else {
+                                           long lval = LUTTools.getInitValue(v);
+                                           for (int i = 0; i < width; i++) {
+                                               tfeat.add(name + "." + pinname + "INV" + "[" + i + "]." + (((lval & (1L << i)) != 0) ? "1" : "0"));
+                                           }
+                                       }
+
+                                   }
+                               }
+
+                               if (/*have_patdet*/true) {
+                                   String[] word_props = {"MASK", "PATTERN", "RND"};
+                                   for (String p : word_props) {
+                                       if (eci.getProperty(p) != null) {
+                                           long lval = LUTTools.getInitValue(eci.getProperty(p).getValue());
+                                           for (int i = 0; i < 48; i++) {
+                                               if ((lval & (1L << i)) != 0 || (p.equals("MASK") && !have_patdet))
+                                                   tfeat.add(name + "." + p + "[" + i + "]");
+                                           }
+                                       }
+                                   }
+                               }
+
+                               for (EDIFPortInst epi : eci.getPortInsts()) {
+                                   if (epi.isOutput() && epi.getNet() != null)
+                                       tfeat.add(name + "." + epi.getName() + ".USED");
+                               }
+
+                               break;
+                           }
+                       }
+
+                   }
+                }
+                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.BRAM || tt == TileTypeEnum.DSP || tt == TileTypeEnum.DSP_L || tt == TileTypeEnum.DSP_R) {
+                        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();
+            }
+        }
+
+    }
+}
diff --git a/dump_clocking.java b/dump_clocking.java
new file mode 100644
index 0000000..f611410
--- /dev/null
+++ b/dump_clocking.java
@@ -0,0 +1,285 @@
+// 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.EDIFPortInst;
+
+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_clocking {
+    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);
+    }
+
+    static HashSet<String> inverted_pips = new HashSet<>();
+
+    static boolean is_sink_inverted(SitePinInst spi) {
+        if (spi == null || spi.getNet() == null)
+            return false;
+        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 void main(String[] args) throws IOException {
+
+
+        Scanner scanner = new Scanner(new File("/invpips.txt"));
+        while (scanner.hasNextLine()) {
+            String nl = scanner.nextLine().trim();
+            inverted_pips.add(nl);
+        }
+
+        File folder = new File("/specimen_clk");
+        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<>();
+                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()) {
+                        if (!p.isBidirectional()) {
+                            uphill.put(p.getEndNode(), p);
+                        }
+
+                        allnodes.add(p.getStartNode());
+                        allnodes.add(p.getEndNode());
+
+                        TileTypeEnum ptt = p.getTile().getTileTypeEnum();
+                        if (ptt == TileTypeEnum.RCLK_INT_L || ptt == TileTypeEnum.RCLK_INT_R) {
+                            if (p.getEndWireName().startsWith("CLK_LEAF_SITES_") && p.getEndWireName().endsWith("_CLK_LEAF")) {
+                                String tile = p.getTile().getName() + ":" + p.getTile().getTileTypeEnum().toString();
+                                if (!pips_by_tile.containsKey(tile))
+                                    pips_by_tile.put(tile, new ArrayList<>());
+                                pips_by_tile.get(tile).add("WIRE." + p.getEndWireName() + ".DRIVEN");
+                                if (p.getStartWireName().endsWith("_CLK_IN") && p.isRouteThru()) {
+                                    String site = site_index_in_tile(p.getTile(), p.getStartNode().getSitePin().getSite());
+                                    if (!sitefeatures_by_tile.containsKey(tile))
+                                        sitefeatures_by_tile.put(tile, new ArrayList<>());
+                                    sitefeatures_by_tile.get(tile).add(site + ".BUFCE_LEAF.IN_USE");
+                                }
+                            }
+                        }
+                    }
+
+                    for (Node node : allnodes) {
+                        for (Wire w : node.getAllWiresInNode()) {
+                            TileTypeEnum wtt = w.getTile().getTileTypeEnum();
+                            if (wtt == TileTypeEnum.RCLK_BRAM_INTF_L || wtt == TileTypeEnum.RCLK_BRAM_INTF_TD_L || wtt == TileTypeEnum.RCLK_BRAM_INTF_TD_R ||
+                                    wtt == TileTypeEnum.RCLK_CLEL_L_L || wtt == TileTypeEnum.RCLK_CLEL_L || wtt == TileTypeEnum.RCLK_CLEL_L_R ||
+                                    wtt == TileTypeEnum.RCLK_CLEM_CLKBUF_L || wtt == TileTypeEnum.RCLK_CLEM_L || wtt == TileTypeEnum.RCLK_DSP_INTF_L ||
+                                    wtt == TileTypeEnum.RCLK_CLEM_R || wtt == TileTypeEnum.RCLK_CLEL_R) {
+                                if (w.getWireName().startsWith("CLK_TEST_BUF_SITE_") || w.getWireName().startsWith("CLK_HROUTE_CORE_OPT")) {
+                                    String tile = w.getTile().getName() + ":" + w.getTile().getTileTypeEnum().toString();
+                                    if (!pips_by_tile.containsKey(tile))
+                                        pips_by_tile.put(tile, new ArrayList<>());
+                                    pips_by_tile.get(tile).add("WIRE." + w.getWireName() + ".USED");
+
+                                }
+                            }
+                            if (wtt == TileTypeEnum.RCLK_DSP_INTF_CLKBUF_L || wtt == TileTypeEnum.RCLK_CLEM_CLKBUF_L || wtt == TileTypeEnum.RCLK_BRAM_INTF_L) {
+                                if (w.getWireName().startsWith("CLK_HROUTE") || w.getWireName().startsWith("CLK_HDISTR")) {
+                                    String tile = w.getTile().getName() + ":" + w.getTile().getTileTypeEnum().toString();
+                                    if (!pips_by_tile.containsKey(tile))
+                                        pips_by_tile.put(tile, new ArrayList<>());
+                                    pips_by_tile.get(tile).add("WIRE." + w.getWireName() + ".USED");
+                                }
+                            }
+                            if (wtt == TileTypeEnum.CMT_L) {
+                                //CLK_HROUTE_
+                                if (w.getWireName().startsWith("CLK_HROUTE_") || w.getWireName().startsWith("CLK_HDISTR_") || w.getWireName().startsWith("CLK_VDISTR_") ||
+                                        w.getWireName().startsWith("CLK_TEST_BUF_SITE_")) {
+                                    String tile = w.getTile().getName() + ":" + w.getTile().getTileTypeEnum().toString();
+                                    if (!pips_by_tile.containsKey(tile))
+                                        pips_by_tile.put(tile, new ArrayList<>());
+                                    pips_by_tile.get(tile).add("WIRE." + w.getWireName() + ".USED");
+                                }
+                            }
+                        }
+                    }
+
+                    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());
+                        }
+                    }
+                }
+                for (SiteInst si : des.getSiteInsts()) {
+                    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);
+                    if (si.getSiteTypeEnum() == SiteTypeEnum.BUFCE_LEAF) {
+                        String name = site_index_in_tile(si.getTile(), si.getSite());
+                        for (Cell c : si.getCells()) {
+                            if (c.getType() != null && c.getType().equals("BUFCE_LEAF")) {
+                                tfeat.add(name + ".BUFCE_LEAF.IN_USE");
+                                if (c.getProperty("CE_TYPE") != null)
+                                    tfeat.add(name + ".BUFCE_LEAF.CE_TYPE." + c.getProperty("CE_TYPE").getValue());
+                                var all_props = c.getProperties();
+                                for (var e : all_props.entrySet()) {
+                                    String propname = e.getKey().getName();
+                                    if (propname.startsWith("IS_") && propname.endsWith("_INVERTED")) {
+                                        String pinname = propname.substring(3, propname.length() - 9);
+                                        String v = e.getValue().getValue();
+                                        int base_pos = v.indexOf("'b");
+                                        if (base_pos != -1)
+                                            v = v.substring(base_pos + 2);
+                                        if (pinname.equals("I"))
+                                            if (is_sink_inverted(si.getSitePinInst("CLK_IN")))
+                                                v = (v.equals("1") ? "0" : "1");
+                                        tfeat.add(name + ".BUFCE_LEAF." + pinname + "INV." + v);
+                                    }
+                                }
+                            }
+                        }
+                    } else if (si.getSiteTypeEnum() == SiteTypeEnum.BUFGCE || si.getSiteTypeEnum() == SiteTypeEnum.BUFGCE_DIV) {
+                        String name = site_index_in_tile(si.getTile(), si.getSite());
+                        for (Cell c : si.getCells()) {
+                            if (c.getType() != null && c.getType().startsWith("BUFGCE")) {
+                                tfeat.add(name + "." + c.getType() + ".IN_USE");
+                                var all_props = c.getProperties();
+                                for (var e : all_props.entrySet()) {
+                                    String propname = e.getKey().getName();
+                                    String v = e.getValue().getValue();
+                                    int base_pos = v.indexOf("'b");
+                                    if (base_pos != -1)
+                                        v = v.substring(base_pos + 2);
+                                    if (propname.startsWith("IS_") && propname.endsWith("_INVERTED")) {
+                                        String pinname = propname.substring(3, propname.length() - 9);
+                                        if (pinname.equals("I"))
+                                            if (is_sink_inverted(si.getSitePinInst("CLK_IN")))
+                                                v = (v.equals("1") ? "0" : "1");
+                                        tfeat.add(name + "." + si.getSiteTypeEnum().toString() + "." + pinname + "INV." + v);
+                                    } else if ((si.getSiteTypeEnum() == SiteTypeEnum.BUFGCE && propname.equals("CE_TYPE")) || propname.equals("BUFGCE_DIVIDE")) {
+                                        tfeat.add(name +  "." + si.getSiteTypeEnum().toString() + "." + propname + "." + v);
+                                    }
+                                }
+                            }
+                        }
+                    } else if (si.getSiteTypeEnum() == SiteTypeEnum.BUFGCTRL) {
+                        String name = site_index_in_tile(si.getTile(), si.getSite());
+                        for (Cell c : si.getCells()) {
+                            if (c.getType() != null && c.getType().startsWith("BUFGCTRL")) {
+                                tfeat.add(name + "." + c.getType() + ".IN_USE");
+                                var all_props = c.getProperties();
+                                for (var e : all_props.entrySet()) {
+                                    String propname = e.getKey().getName();
+                                    String v = e.getValue().getValue();
+                                    int base_pos = v.indexOf("'b");
+                                    if (base_pos != -1)
+                                        v = v.substring(base_pos + 2);
+                                    if (propname.startsWith("IS_") && propname.endsWith("_INVERTED")) {
+                                        String pinname = propname.substring(3, propname.length() - 9);
+                                        if (pinname.equals("I0") || pinname.equals("I1"))
+                                            if (is_sink_inverted(si.getSitePinInst("CLK_" + pinname)))
+                                                v = (v.equals("1") ? "0" : "1");
+                                        tfeat.add(name + "." + si.getSiteTypeEnum().toString() + "." + pinname + "INV." + v);
+                                    } else if (propname.equals("INIT_OUT") || propname.startsWith("PRESELECT_")) {
+                                        tfeat.add(name +  "." + si.getSiteTypeEnum().toString() + "." + propname + "." + v);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                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.RCLK_INT_L|| tt == TileTypeEnum.RCLK_CLEM_L || tt == TileTypeEnum.RCLK_INT_R || tt == TileTypeEnum.RCLK_CLEM_CLKBUF_L || tt == TileTypeEnum.RCLK_CLEM_R ||
+                            tt == TileTypeEnum.RCLK_DSP_INTF_L || tt == TileTypeEnum.RCLK_BRAM_L || tt == TileTypeEnum.RCLK_BRAM_R || tt == TileTypeEnum.RCLK_BRAM_INTF_L ||
+                            tt == TileTypeEnum.RCLK_CLEL_L || tt == TileTypeEnum.RCLK_CLEL_R || tt == TileTypeEnum.RCLK_CLEL_L_L || tt == TileTypeEnum.RCLK_CLEL_L_R ||
+                            tt == TileTypeEnum.RCLK_DSP_INTF_CLKBUF_L || tt == TileTypeEnum.RCLK_BRAM_INTF_TD_L || tt == TileTypeEnum.RCLK_BRAM_INTF_TD_R || tt == TileTypeEnum.CMT_L || tt == TileTypeEnum.CMT_RIGHT) {
+                        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();
+            }
+        }
+
+    }
+}
diff --git a/dump_features.java b/dump_features.java
new file mode 100644
index 0000000..56cfc11
--- /dev/null
+++ b/dump_features.java
@@ -0,0 +1,409 @@
+// 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.EDIFPortInst;
+import com.xilinx.rapidwright.edif.EDIFPropertyValue;
+import com.xilinx.rapidwright.edif.EDIFValueType;
+import jnr.ffi.annotations.In;
+import org.json.Property;
+
+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_features {
+
+    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);
+    }
+
+    static HashSet<String> inverted_pips = new HashSet<>();
+
+    static boolean is_sink_inverted(SitePinInst spi) {
+        if (spi == null || spi.getNet() == null)
+            return false;
+        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 void main(String[] args) throws IOException {
+
+
+        Scanner scanner = new Scanner(new File("/invpips.txt"));
+        while (scanner.hasNextLine()) {
+            String nl = scanner.nextLine().trim();
+            inverted_pips.add(nl);
+        }
+
+        File folder = new File("/specimen");
+        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<>();
+                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 (Node node : allnodes) {
+                        for (Wire w : node.getAllWiresInNode()) {
+                            TileTypeEnum wtt = w.getTile().getTileTypeEnum();
+                            if (wtt == TileTypeEnum.RCLK_BRAM_INTF_L || wtt == TileTypeEnum.RCLK_BRAM_INTF_TD_L || wtt == TileTypeEnum.RCLK_BRAM_INTF_TD_R ||
+                                wtt == TileTypeEnum.RCLK_CLEL_L_L || wtt == TileTypeEnum.RCLK_CLEL_L || wtt == TileTypeEnum.RCLK_CLEL_L_R ||
+                                    wtt == TileTypeEnum.RCLK_CLEM_CLKBUF_L || wtt == TileTypeEnum.RCLK_CLEM_L || wtt == TileTypeEnum.RCLK_DSP_INTF_L) {
+                                if (w.getWireName().startsWith("CLK_TEST_BUF_SITE_") || w.getWireName().startsWith("CLK_HROUTE_CORE_OPT")) {
+                                    String tile = w.getTile().getName() + ":" + w.getTile().getTileTypeEnum().toString();
+                                    if (!pips_by_tile.containsKey(tile))
+                                        pips_by_tile.put(tile, new ArrayList<>());
+                                    pips_by_tile.get(tile).add("WIRE." + w.getWireName() + ".USED");
+
+                                }
+                            }
+                            if (wtt == TileTypeEnum.RCLK_DSP_INTF_CLKBUF_L || wtt == TileTypeEnum.RCLK_CLEM_CLKBUF_L || wtt == TileTypeEnum.RCLK_BRAM_INTF_L) {
+                                if (w.getWireName().startsWith("CLK_HROUTE") || w.getWireName().startsWith("CLK_HDISTR") ) {
+                                    String tile = w.getTile().getName() + ":" + w.getTile().getTileTypeEnum().toString();
+                                    if (!pips_by_tile.containsKey(tile))
+                                        pips_by_tile.put(tile, new ArrayList<>());
+                                    pips_by_tile.get(tile).add("WIRE." + w.getWireName() + ".USED");
+                                }
+                            }
+                        }
+                    }
+
+                    for (PIP p : n.getPIPs()) {
+
+                        String tile = p.getTile().getName() + ":" + p.getTile().getTileTypeEnum().toString();
+                        TileTypeEnum ptt = p.getTile().getTileTypeEnum();
+                        if (ptt == TileTypeEnum.BRAM)
+                            continue;
+                        if (!pips_by_tile.containsKey(tile))
+                            pips_by_tile.put(tile, new ArrayList<>());
+                        if (ptt == TileTypeEnum.INT_INTF_L_IO) {
+                            if (p.getEndWireName().contains("LOGIC_OUTS"))
+                                pips_by_tile.get(tile).add("WIRE.LOGIC_OUTS_R.DRIVEN");
+                            continue;
+                        }
+                        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());
+                        }
+                    }
+                }
+                for (SiteInst si : des.getSiteInsts()) {
+                    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);
+                    if (si.getSiteTypeEnum() == SiteTypeEnum.SLICEL || si.getSiteTypeEnum() == SiteTypeEnum.SLICEM) {
+                        boolean[] clkinv = {false, false}, srinv = {false, false}, ffsync = {false, false}, latch = {false, false};
+                        boolean[] haveff = {false, false};
+                        boolean[] srused = {false, false, false, false};
+                        boolean[] ceused = {false, false, false, false};
+
+                        for (BEL b : si.getBELs()) {
+                            String belName = b.getName();
+                            Cell c = si.getCell(b);
+                            if (c == null)
+                                continue;
+
+                            if (belName.contains("LUT")) {
+                                if (c.getType().startsWith("RAM")) {
+                                    tfeat.add(belName.replaceAll("[56]", "") + ".RAM");
+                                    if (c.getType().contains("32"))
+                                        tfeat.add(belName.replaceAll("[56]", "") + ".RAM.SMALL");
+                                    else
+                                        tfeat.add(belName.replaceAll("[56]", "") + ".RAM.LARGE");
+                                }
+                                if (c.getType().contains("SRL"))
+                                    tfeat.add(belName.replaceAll("[56]", "") + ".SRL");
+                                if (c.getType().contains("SRL16"))
+                                    tfeat.add(belName.replaceAll("[56]", "") + ".SRL.SMALL");
+                                if (c.getType().contains("SRLC32"))
+                                    tfeat.add(belName.replaceAll("[56]", "") + ".SRL.LARGE");
+
+                                for (int i = 6; i <= 8; i++) {
+                                    if (c.getPhysicalPinMapping("WADR" + i) != null)
+                                        tfeat.add("WA" + (i+1) + "USED");
+                                }
+                                if (c.getType().contains("SRLC32") && c.getEDIFCellInst() != null) {
+                                    EDIFPortInst epi = c.getEDIFCellInst().getPortInst("D");
+                                    if (epi != null && c.getPhysicalPinMapping("D") != null)
+                                        tfeat.add(belName.replaceAll("[56]", "") + ".DI1." + c.getPhysicalPinMapping("D"));
+                                } else if (c.getLogicalPinMapping("DI1") != null) {
+                                    tfeat.add(belName.replaceAll("[56]", "") + ".DI1.DI1");
+                                }
+                                if ((c.getType().contains("RAM") || c.getType().contains("SRL")) && c.getEDIFCellInst() != null && c.getProperty("IS_CLK_INVERTED") != null) {
+                                    String lclkinv = c.getProperty("IS_CLK_INVERTED").getValue();
+                                    tfeat.add("LCLKINV." + lclkinv.substring(lclkinv.length() - 1));
+                                }
+
+                            }
+
+                            var p2l = c.getPinMappingsP2L();
+                            if (belName.contains("LUT") && c.getEDIFCellInst() != null && c.getProperty("INIT") != null) {
+                                boolean has_56lut = false;
+                                if (belName.contains("6LUT") && si.getCell(si.getBEL(belName.replace('6', '5'))) != null)
+                                    has_56lut = true;
+                                if (!has_56lut && (c.getType().startsWith("LUT"))) {
+                                    // LUT init bits
+                                    long long_init = LUTTools.getInitValue(c.getProperty("INIT").getValue());
+                                    for (int i = 0; i < 64; i++) {
+                                        if (belName.contains("5LUT") && (i >= 32))
+                                            continue;
+                                        String sw = belName.substring(0, 1) + "6";
+                                        Net sn = si.getNetFromSiteWire(sw);
+                                        if (belName.contains("6LUT") && sn != null && sn.getName().equals(Net.VCC_NET))
+                                            continue;
+                                        int init_index = 0;
+                                        boolean valid_index = true;
+                                        for (int j = 0; j < 6; j++) {
+                                            if ((i & (1 << j)) == 0)
+                                                continue;
+                                            String log = p2l.getOrDefault("A" + Integer.toString(j + 1), "");
+                                            if (log.isEmpty())
+                                                continue;
+                                            if (log.startsWith("RADR")) {
+                                                init_index |= (1 << (Integer.parseInt(log.substring(4))));
+                                            } else if (log.startsWith("A[")) {
+                                                init_index |= (1 << (Integer.parseInt(log.substring(2, 3))));
+                                            } else if (!log.startsWith("I") && !log.startsWith("A")) {
+                                                valid_index = false;
+                                                break;
+                                            } else {
+                                                init_index |= (1 << (Integer.parseInt(log.substring(1))));
+                                            }
+                                        }
+                                        if (!valid_index)
+                                            continue;
+                                        if ((long_init & (1L << init_index)) == 0)
+                                            continue;
+                                        tfeat.add(belName.replaceAll("[56]", "") + ".INIT[" + i + "]");
+                                    }
+                                }
+
+                            } else if (belName.contains("FF")) {
+                                boolean is_latch = false, is_sync = false, srval = false;
+                                String clkpin, srpin, cepin;
+                                if (c.getType().equals("FDPE")) {
+                                    is_latch = false;
+                                    is_sync = false;
+                                    srval = true;
+                                    clkpin = "C";
+                                    cepin = "CE";
+                                    srpin = "PRE";
+                                } else if (c.getType().equals("FDCE")) {
+                                    is_latch = false;
+                                    is_sync = false;
+                                    srval = false;
+                                    clkpin = "C";
+                                    cepin = "CE";
+                                    srpin = "CLR";
+                                } else if (c.getType().equals("FDSE")) {
+                                    is_latch = false;
+                                    is_sync = true;
+                                    srval = true;
+                                    clkpin = "C";
+                                    cepin = "CE";
+                                    srpin = "S";
+                                } else if (c.getType().equals("FDRE")) {
+                                    is_latch = false;
+                                    is_sync = true;
+                                    srval = false;
+                                    clkpin = "C";
+                                    cepin = "CE";
+                                    srpin = "R";
+                                } else if (c.getType().equals("LDPE")) {
+                                    is_latch = true;
+                                    is_sync = false;
+                                    srval = true;
+                                    clkpin = "G";
+                                    cepin = "GE";
+                                    srpin = "PRE";
+                                } else if (c.getType().equals("LDCE")) {
+                                    is_latch = true;
+                                    is_sync = false;
+                                    srval = false;
+                                    clkpin = "G";
+                                    cepin = "GE";
+                                    srpin = "CLR";
+                                } else {
+                                    continue;
+                                }
+                                String primtype = is_latch ? "LATCH" : "FF";
+                                if (c.getProperty("INIT") != null) {
+                                    String init = c.getProperty("INIT").getValue();
+                                    tfeat.add(belName + "." + primtype + ".INIT." + init.substring(init.length() - 1));
+                                }
+                                tfeat.add(belName + "." + primtype + ".SRVAL." + (srval ? "1" : "0"));
+                                int half = ("EFGH".contains(belName.substring(0, 1))) ? 1 : 0;
+                                latch[half] = is_latch;
+                                ffsync[half] = is_sync;
+                                clkinv[half] = (c.getProperty("IS_" + clkpin + "_INVERTED") != null && c.getProperty("IS_" + clkpin + "_INVERTED").getValue().equals("1'b1"));
+                                srinv[half] = (c.getProperty("IS_" + srpin + "_INVERTED") != null && c.getProperty("IS_" + srpin + "_INVERTED").getValue().equals("1'b1"));
+                                haveff[half] = true;
+                                int two = belName.endsWith("2") ? 1 : 0;
+                                srused[half * 2 + two] = (c.getPhysicalPinMapping(srpin) != null);
+                                ceused[half * 2 + two] = (c.getPhysicalPinMapping(cepin) != null);
+
+                            } else if (belName.contains("CARRY8")) {
+                                if (!c.getType().equals("CARRY8"))
+                                    continue;
+                                if (c.getProperty("CARRY_TYPE") != null)
+                                    tfeat.add("CARRY8.CARRY_TYPE." + c.getProperty("CARRY_TYPE").getValue());
+                                String[] pins = {"CI", "DI[0]", "DI[1]", "DI[2]", "DI[3]", "DI[4]", "DI[5]", "DI[6]", "DI[7]", "CI_TOP"};
+                                for (String p : pins) {
+                                    String phys = c.getPhysicalPinMapping(p);
+                                    if (phys != null && (!p.equals("CI") || !phys.equals("CIN"))) {
+                                        EDIFPortInst epi = c.getEDIFCellInst().getPortInst(p);
+                                        if (epi != null && epi.getNet() != null) {
+                                            String pn = p.replace("[", "").replace("]", "");
+                                            tfeat.add("CARRY8." + pn + "." + phys);
+                                        }
+                                    } else if (p.equals("CI") || p.equals("CI_TOP")) {
+                                        EDIFPortInst epi = c.getEDIFCellInst().getPortInst(p);
+                                        if (epi != null && epi.getNet() != null) {
+                                            String netname = epi.getNet().getName();
+                                            if (netname.equals("<const0>"))
+                                                tfeat.add("CARRY8." + p + ".0");
+                                            else if (netname.equals("<const1>"))
+                                                tfeat.add("CARRY8." + p + ".1");
+                                        }
+                                    }
+                                }
+                            }
+
+                        }
+                        for (int i = 0; i < 2; i++) {
+                            String half = "ABCDEFGH".substring(4 * i, 4 * (i + 1));
+                            if (!haveff[i])
+                                continue;
+                            String primtype = latch[i] ? "LATCH" : "FF";
+                            tfeat.add(half + "FF." + primtype);
+                            tfeat.add(half + "FF." + primtype + ".CLKINV." + (clkinv[i] ? "1" : "0"));
+                            tfeat.add(half + "FF." + primtype + ".SRINV." + (srinv[i] ? "1" : "0"));
+                            if (ffsync[i])
+                                tfeat.add(half + "FF." + primtype + ".SYNC");
+                            for (int j = 0; j < 2; j++) {
+                                if (srused[i * 2 + j])
+                                    tfeat.add(half + "FF" + (j == 1 ? "2" : "") + ".SRUSED");
+                                if (ceused[i * 2 + j])
+                                    tfeat.add(half + "FF" + (j == 1 ? "2" : "") + ".CEUSED");
+                            }
+                        }
+                        for (SitePIP sp : si.getUsedSitePIPs()) {
+                            if (sp.getBELName().contains("INV"))
+                                continue;
+                            if (sp.getBELName().startsWith("OUTMUX") && sp.getInputPinName().equals("D6")) {
+                                if (si.getSitePinInst(sp.getOutputPin().getConnectedSitePinName()) == null)
+                                    continue;
+                            }
+                            tfeat.add(sp.getBELName() + "." + sp.getInputPinName());
+                        }
+                    } else if (si.getSiteTypeEnum() == SiteTypeEnum.BUFCE_LEAF) {
+                        String name = site_index_in_tile(si.getTile(), si.getSite());
+                        for (Cell c : si.getCells()) {
+                            if (c.getType() != null && c.getType().equals("BUFCE_LEAF")) {
+                                tfeat.add(name + ".BUFCE_LEAF.IN_USE");
+                                if (c.getProperty("CE_TYPE") != null)
+                                    tfeat.add(name + ".BUFCE_LEAF.CE_TYPE." + c.getProperty("CE_TYPE").getValue());
+                                var all_props = c.getProperties();
+                                for (var e : all_props.entrySet()) {
+                                    String propname = e.getKey().getName();
+                                    if (propname.startsWith("IS_") && propname.endsWith("_INVERTED")) {
+                                        String pinname = propname.substring(3, propname.length() - 9);
+                                        String v = e.getValue().getValue();
+                                        int base_pos = v.indexOf("'b");
+                                        if (base_pos != -1)
+                                            v = v.substring(base_pos + 2);
+                                        if (pinname.equals("I"))
+                                            if (is_sink_inverted(si.getSitePinInst("CLK_IN")))
+                                                v = (v.equals("1") ? "0" : "1");
+                                        tfeat.add(name + ".BUFCE_LEAF." + pinname + "INV." + v);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+                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.CLEL_L || tt == TileTypeEnum.CLEL_R || tt == TileTypeEnum.CLEM_R || tt == TileTypeEnum.CLEM
+                            || tt == TileTypeEnum.INT || tt == TileTypeEnum.INT_INTF_L_IO) {
+                        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();
+            }
+        }
+
+    }
+}
diff --git a/dump_tiles.java b/dump_tiles.java
new file mode 100644
index 0000000..d135469
--- /dev/null
+++ b/dump_tiles.java
@@ -0,0 +1,40 @@
+// 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.device.Site;
+import com.xilinx.rapidwright.device.SiteTypeEnum;
+import com.xilinx.rapidwright.device.Tile;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+public class dump_tiles {
+    public static void main(String[] args) throws IOException {
+        Design des = new Design("top", "xczu7ev-ffvc1156-2-e");
+        FileWriter vf = new FileWriter("/tiles.txt", false);
+        PrintWriter v = new PrintWriter(vf);
+
+        for (Tile t : des.getDevice().getAllTiles()) {
+            v.printf("%d,%d,%s,%s", t.getColumn(), t.getRow(), t.getName(), t.getTileTypeEnum().toString());
+            for (Site s : t.getSites()) {
+                v.printf(",%s:%s", s.getName(), s.getSiteTypeEnum().toString());
+            }
+            v.println();
+        }
+    }
+}