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(); + } + } +}