Merge pull request #199 from eddiehung/xc7
Unofficial xc7 support
diff --git a/.gitignore b/.gitignore
index 9b3ffd0..bf22d03 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
/nextpnr-generic*
/nextpnr-ice40*
/nextpnr-ecp5*
+/nextpnr-xc7*
cmake-build-*/
Makefile
cmake_install.cmake
@@ -29,3 +30,22 @@
/ImportExecutables.cmake
*-coverage/
*-coverage.info
+
+# nextpnr-xc7
+*.xdl
+*.bit
+
+# ise
+_xmsgs/
+*.bgn
+*.drc
+*.ncd
+*.xwbt
+usage_statistics_webtalk.html
+webtalk.log
+xilinx_device_details.xml
+*_fpga_editor.*
+*.twr
+*.twx
+*.nlf
+*.sdf
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..c0b8228
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "torc"]
+ path = torc
+ url = https://github.com/eddiehung/torc
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 33a703d..c75a32f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -17,7 +17,7 @@
endif()
# List of families to build
-set(FAMILIES generic ice40 ecp5)
+set(FAMILIES generic ice40 ecp5 xc7)
set(ARCH "" CACHE STRING "Architecture family for nextpnr build")
set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES})
@@ -120,9 +120,10 @@
# Original source: https://github.com/BVLC/caffe/blob/master/cmake/Dependencies.cmake#L148
set(version ${PYTHONLIBS_VERSION_STRING})
- STRING(REGEX REPLACE "[^0-9]" "" boost_py_version ${version})
+ STRING(REGEX REPLACE "[^0-9]" "" boost_py_version ${version})
find_package(Boost QUIET COMPONENTS "python-py${boost_py_version}" ${boost_libs})
set(Boost_PYTHON_FOUND ${Boost_PYTHON-PY${boost_py_version}_FOUND})
+ set(boost_python_lib "python-py${boost_py_version}")
while (NOT "${version}" STREQUAL "" AND NOT Boost_PYTHON_FOUND)
STRING(REGEX REPLACE "([0-9.]+).[0-9]+" "\\1" version ${version})
@@ -130,6 +131,7 @@
STRING(REGEX REPLACE "[^0-9]" "" boost_py_version ${version})
find_package(Boost QUIET COMPONENTS "python-py${boost_py_version}" ${boost_libs})
set(Boost_PYTHON_FOUND ${Boost_PYTHON-PY${boost_py_version}_FOUND})
+ set(boost_python_lib "python-py${boost_py_version}")
STRING(REGEX MATCHALL "([0-9.]+).[0-9]+" has_more_version ${version})
if ("${has_more_version}" STREQUAL "")
@@ -139,6 +141,7 @@
if (NOT Boost_PYTHON_FOUND)
find_package(Boost QUIET COMPONENTS python3 ${boost_libs})
+ set(boost_python_lib python3)
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
set(Boost_PYTHON_FOUND TRUE)
endif ()
@@ -146,6 +149,7 @@
if (NOT Boost_PYTHON_FOUND)
find_package(Boost QUIET COMPONENTS python36 ${boost_libs})
+ set(boost_python_lib python36)
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
set(Boost_PYTHON_FOUND TRUE)
endif ()
@@ -153,6 +157,7 @@
if (NOT Boost_PYTHON_FOUND)
find_package(Boost QUIET COMPONENTS python37 ${boost_libs})
+ set(boost_python_lib python37)
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
set(Boost_PYTHON_FOUND TRUE)
endif ()
@@ -161,6 +166,7 @@
if (NOT Boost_PYTHON_FOUND)
STRING(REGEX REPLACE "([0-9]+\\.[0-9]+).*" "\\1" gentoo_version ${PYTHONLIBS_VERSION_STRING})
find_package(Boost QUIET COMPONENTS python-${gentoo_version} ${boost_libs})
+ set(boost_python_lib python-${gentoo_version})
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
set(Boost_PYTHON_FOUND TRUE)
endif ()
diff --git a/README.md b/README.md
index 5b79d1f..5733a40 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,19 @@
nextpnr -- a portable FPGA place and route tool
===============================================
+***
+### NB: This nextpnr-xc7 branch is *unofficial*, *very* proof-of-concept, *very* experimental, *very* unoptimised, and is provided with *no support whatsoever*. Use at your own risk!
+#### It leverages a [torc](https://github.com/torc-isi/torc) fork with minimal changes (those necessary to support building on later versions of gcc) to target XDL-compatible devices.
+### Note that torc is licensed under GPLv3 which differs from nextpnr's ISC license, thus please respect the limitations imposed by both licenses.
+Currently, only LUT1-6, IOB, BUFGCTRL, MMCME2_ADV are supported for xc7z020 and xc7vx680t (but trivial to add others).
+The following example shell scripts are available:
+* blinky.sh -- generates blinky.bit that flashes (with a delay) the 4 LEDs on a ZYBO Z7
+* blinky_sim.sh -- post place-and-route simulation, without any delays (requires [GHDL](https://github.com/ghdl/ghdl))
+* picorv32.sh -- just places-and-routes picorv32.ncd (no testbench)
+* attosoc.sh -- generates attosoc.bit of a self-stimulating picorv32 device that displays (with a delay) prime numbers to the LEDs -- when testing on hardware, consider using a PLL (MMCM) to meet timing
+* attosoc_sim.sh -- post place-and-route simulation of a self-stimulating picorv32 device, without any delays (requires [GHDL](https://github.com/ghdl/ghdl))
+***
+
nextpnr aims to be a vendor neutral, timing driven, FOSS FPGA place and route
tool.
diff --git a/common/place_common.cc b/common/place_common.cc
index b3eb426..2a18a1a 100644
--- a/common/place_common.cc
+++ b/common/place_common.cc
@@ -37,6 +37,8 @@
if (driver_gb)
return 0;
int clock_count;
+ if (ctx->getPortTimingClass(driver_cell, net->driver.port, clock_count) == TMG_IGNORE)
+ return 0;
bool timing_driven = ctx->timing_driven && type == MetricType::COST &&
ctx->getPortTimingClass(driver_cell, net->driver.port, clock_count) != TMG_IGNORE;
delay_t negative_slack = 0;
diff --git a/common/router1.cc b/common/router1.cc
index cbc0df9..2c3f668 100644
--- a/common/router1.cc
+++ b/common/router1.cc
@@ -512,6 +512,21 @@
WireId next_wire = ctx->getPipDstWire(pip);
next_delay += ctx->getWireDelay(next_wire).maxDelay();
+#ifdef ARCH_XC7
+ // For BUFG routing, do not exit the global network until the destination tile is reached
+ if (ctx->isGlobalNet(net_info)) {
+ if (torc_info->wire_is_global[src_wire.index] && !torc_info->wire_is_global[next_wire.index]) {
+ const auto &arc = torc_info->pip_to_arc[pip.index];
+ const auto &next_tw = arc.getSinkTilewire();
+ const auto &next_loc = torc_info->tile_to_xy[next_tw.getTileIndex()];
+ const auto &dst_tw = torc_info->wire_to_tilewire[dst_wire.index];
+ const auto &dst_loc = torc_info->tile_to_xy[dst_tw.getTileIndex()];
+ if (next_loc.second != dst_loc.second || next_loc.first != dst_loc.first)
+ continue;
+ }
+ }
+#endif
+
WireId conflictWireWire = WireId(), conflictPipWire = WireId();
NetInfo *conflictWireNet = nullptr, *conflictPipNet = nullptr;
diff --git a/gui/application.cc b/gui/application.cc
index 7751e6f..738b7c6 100644
--- a/gui/application.cc
+++ b/gui/application.cc
@@ -51,9 +51,9 @@
bool Application::notify(QObject *receiver, QEvent *event)
{
bool retVal = true;
- try {
+ //try {
retVal = QApplication::notify(receiver, event);
- } catch (assertion_failure ex) {
+ /*} catch (assertion_failure ex) {
QString msg;
QTextStream out(&msg);
out << ex.filename.c_str() << " at " << ex.line << "\n";
@@ -61,7 +61,7 @@
QMessageBox::critical(0, "Error", msg);
} catch (...) {
QMessageBox::critical(0, "Error", "Fatal error !!!");
- }
+ }*/
return retVal;
}
diff --git a/gui/designwidget.cc b/gui/designwidget.cc
index 235dd2c..958d884 100644
--- a/gui/designwidget.cc
+++ b/gui/designwidget.cc
@@ -318,6 +318,12 @@
wireMap[std::pair<int, int>(wire.location.x, wire.location.y)].push_back(wire);
}
#endif
+#ifdef ARCH_XC7
+ for (const auto &wire : ctx->getWires()) {
+ const auto loc = torc_info->wire_to_loc(wire.index);
+ wireMap[std::pair<int, int>(loc.x, loc.y)].push_back(wire);
+ }
+#endif
auto wireGetter = [](Context *ctx, WireId id) { return ctx->getWireName(id); };
getTreeByElementType(ElementType::WIRE)
->loadData(ctx,
@@ -706,8 +712,9 @@
addProperty(topItem, QVariant::String, "Type", ctx->getPipType(pip).c_str(ctx));
addProperty(topItem, QVariant::Bool, "Available", ctx->checkPipAvail(pip));
addProperty(topItem, QVariant::String, "Bound Net", ctx->nameOf(ctx->getBoundPipNet(pip)), ElementType::NET);
+ WireId conflict = ctx->getConflictingPipWire(pip);
addProperty(topItem, QVariant::String, "Conflicting Wire",
- ctx->getWireName(ctx->getConflictingPipWire(pip)).c_str(ctx), ElementType::WIRE);
+ (conflict!=WireId() ? ctx->getWireName(conflict).c_str(ctx) : ""), ElementType::WIRE);
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->nameOf(ctx->getConflictingPipNet(pip)),
ElementType::NET);
addProperty(topItem, QVariant::String, "Src Wire", ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx),
diff --git a/gui/xc7/family.cmake b/gui/xc7/family.cmake
new file mode 100644
index 0000000..3b010c7
--- /dev/null
+++ b/gui/xc7/family.cmake
@@ -0,0 +1 @@
+include_directories(/opt/torc/src)
\ No newline at end of file
diff --git a/gui/xc7/mainwindow.cc b/gui/xc7/mainwindow.cc
new file mode 100644
index 0000000..3ee6472
--- /dev/null
+++ b/gui/xc7/mainwindow.cc
@@ -0,0 +1,51 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "mainwindow.h"
+
+static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
+
+NEXTPNR_NAMESPACE_BEGIN
+
+MainWindow::MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
+ : BaseMainWindow(std::move(context), args, parent)
+{
+ initMainResource();
+
+ std::string title = "nextpnr-xc7 - [EMPTY]";
+ setWindowTitle(title.c_str());
+
+ connect(this, &BaseMainWindow::contextChanged, this, &MainWindow::newContext);
+
+ createMenu();
+}
+
+MainWindow::~MainWindow() {}
+
+void MainWindow::newContext(Context *ctx)
+{
+ std::string title = "nextpnr-xc7 - " + ctx->getChipName();
+ setWindowTitle(title.c_str());
+}
+
+void MainWindow::createMenu() {}
+
+void MainWindow::new_proj() {}
+
+NEXTPNR_NAMESPACE_END
diff --git a/gui/xc7/mainwindow.h b/gui/xc7/mainwindow.h
new file mode 100644
index 0000000..bb6a4cf
--- /dev/null
+++ b/gui/xc7/mainwindow.h
@@ -0,0 +1,45 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include "../basewindow.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+class MainWindow : public BaseMainWindow
+{
+ Q_OBJECT
+
+ public:
+ explicit MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent = 0);
+ virtual ~MainWindow();
+
+ public:
+ void createMenu();
+
+ protected Q_SLOTS:
+ void new_proj() override;
+ void newContext(Context *ctx);
+};
+
+NEXTPNR_NAMESPACE_END
+
+#endif // MAINWINDOW_H
diff --git a/gui/xc7/nextpnr.qrc b/gui/xc7/nextpnr.qrc
new file mode 100644
index 0000000..03585ec
--- /dev/null
+++ b/gui/xc7/nextpnr.qrc
@@ -0,0 +1,2 @@
+<RCC>
+</RCC>
diff --git a/torc b/torc
new file mode 160000
index 0000000..a5a4ec0
--- /dev/null
+++ b/torc
@@ -0,0 +1 @@
+Subproject commit a5a4ec057eae3ed07ed2acb9da13404808e37211
diff --git a/xc7/arch.cc b/xc7/arch.cc
new file mode 100644
index 0000000..c4178b0
--- /dev/null
+++ b/xc7/arch.cc
@@ -0,0 +1,904 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <algorithm>
+#include <cmath>
+#include <regex>
+#include "cells.h"
+#include "gfx.h"
+#include "log.h"
+#include "nextpnr.h"
+#include "placer1.h"
+#include "router1.h"
+#include "util.h"
+
+#include "torc/common/DirectoryTree.hpp"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+std::unique_ptr<const TorcInfo> torc_info;
+TorcInfo::TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::string &inPackageName)
+ : TorcInfo(inDeviceName, inPackageName)
+{
+ static const std::regex re_loc(".+_X(\\d+)Y(\\d+)");
+ std::cmatch what;
+ tile_to_xy.resize(tiles.getTileCount());
+ for (TileIndex tileIndex(0); tileIndex < tiles.getTileCount(); tileIndex++) {
+ const auto &tileInfo = tiles.getTileInfo(tileIndex);
+ if (!std::regex_match(tileInfo.getName(), what, re_loc))
+ throw;
+ const auto x = boost::lexical_cast<int>(what.str(1));
+ const auto y = boost::lexical_cast<int>(what.str(2));
+ tile_to_xy[tileIndex] = std::make_pair(x,y);
+ }
+
+ bel_to_site_index.reserve(sites.getSiteCount() * 4);
+ bel_to_loc.reserve(sites.getSiteCount() * 4);
+ site_index_to_bel.resize(sites.getSiteCount());
+ site_index_to_type.resize(sites.getSiteCount());
+ BelId b;
+ b.index = 0;
+ for (SiteIndex i(0); i < sites.getSiteCount(); ++i) {
+ const auto &site = sites.getSite(i);
+ const auto &pd = site.getPrimitiveDefPtr();
+ const auto &type = pd->getName();
+ int x, y;
+ std::tie(x,y) = tile_to_xy[site.getTileIndex()];
+
+ if (type == "SLICEL" || type == "SLICEM") {
+ bel_to_site_index.push_back(i);
+ bel_to_site_index.push_back(i);
+ bel_to_site_index.push_back(i);
+ bel_to_site_index.push_back(i);
+ site_index_to_type[i] = id_SLICE_LUT6;
+ const auto site_name = site.getName();
+ if (!std::regex_match(site_name.c_str(), what, re_loc))
+ throw;
+ const auto sx = boost::lexical_cast<int>(what.str(1));
+ if ((sx & 1) == 0) {
+ bel_to_loc.emplace_back(x, y, 0);
+ bel_to_loc.emplace_back(x, y, 1);
+ bel_to_loc.emplace_back(x, y, 2);
+ bel_to_loc.emplace_back(x, y, 3);
+ } else {
+ bel_to_loc.emplace_back(x, y, 4);
+ bel_to_loc.emplace_back(x, y, 5);
+ bel_to_loc.emplace_back(x, y, 6);
+ bel_to_loc.emplace_back(x, y, 7);
+ }
+ site_index_to_bel[i] = b;
+ b.index += 4;
+ } else if (type == "IOB33S" || type == "IOB33M") {
+ bel_to_site_index.push_back(i);
+ site_index_to_type[i] = id_IOB33;
+ // TODO: Fix z when two IOBs on same tile
+ bel_to_loc.emplace_back(x, y, 0);
+ site_index_to_bel[i] = b;
+ ++b.index;
+ } else if (type == "IOB18S" || type == "IOB18M") {
+ bel_to_site_index.push_back(i);
+ site_index_to_type[i] = id_IOB18;
+ // TODO: Fix z when two IOBs on same tile
+ bel_to_loc.emplace_back(x, y, 0);
+ site_index_to_bel[i] = b;
+ ++b.index;
+ } else {
+ bel_to_site_index.push_back(i);
+ site_index_to_type[i] = ctx->id(type);
+ bel_to_loc.emplace_back(x, y, 0);
+ site_index_to_bel[i] = b;
+ ++b.index;
+ }
+ }
+ num_bels = bel_to_site_index.size();
+ bel_to_site_index.shrink_to_fit();
+ bel_to_loc.shrink_to_fit();
+
+ const std::regex re_124("(.+_)?[NESW][NESWLR](\\d)((BEG(_[NS])?)|(END(_[NS])?)|[A-E])?\\d(_\\d)?");
+ const std::regex re_L("(.+_)?L(H|V|VB)(_L)?\\d+(_\\d)?");
+ const std::regex re_BYP("BYP(_ALT)?\\d");
+ const std::regex re_BYP_B("BYP_[BL]\\d");
+ const std::regex re_BOUNCE_NS("(BYP|FAN)_BOUNCE_[NS]3_\\d");
+ const std::regex re_FAN("FAN(_ALT)?\\d");
+ const std::regex re_CLB_I1_6("CLBL[LM]_(L|LL|M)_[A-D]([1-6])");
+ const std::regex bufg_i("CLK_BUFG_BUFGCTRL\\d+_I0");
+ const std::regex bufg_o("CLK_BUFG_BUFGCTRL\\d+_O");
+ const std::regex hrow("CLK_HROW_CLK[01]_[34]");
+ std::unordered_map</*TileTypeIndex*/ unsigned, std::vector<delay_t>> delay_lookup;
+ std::unordered_map<Segments::SegmentReference, TileIndex> segment_to_anchor;
+ Tilewire currentTilewire;
+ WireId w;
+ w.index = 0;
+ for (TileIndex tileIndex(0); tileIndex < tiles.getTileCount(); tileIndex++) {
+ // iterate over every wire in the tile
+ const auto &tileInfo = tiles.getTileInfo(tileIndex);
+ auto tileTypeIndex = tileInfo.getTypeIndex();
+ auto wireCount = tiles.getWireCount(tileTypeIndex);
+ currentTilewire.setTileIndex(tileIndex);
+ for (WireIndex wireIndex(0); wireIndex < wireCount; wireIndex++) {
+ currentTilewire.setWireIndex(wireIndex);
+ const auto ¤tSegment = segments.getTilewireSegment(currentTilewire);
+
+ if (!currentSegment.isTrivial()) {
+ auto r = segment_to_anchor.emplace(currentSegment, currentSegment.getAnchorTileIndex());
+ if (r.second) {
+ TilewireVector segment;
+ const_cast<DDB &>(*ddb).expandSegment(currentTilewire, segment, DDB::eExpandDirectionNone);
+ // expand all of the arcs
+ TilewireVector::const_iterator sep = segment.begin();
+ TilewireVector::const_iterator see = segment.end();
+ while(sep < see) {
+ // expand the tilewire sinks
+ const Tilewire& tilewire = *sep++;
+
+ const auto &tileInfo = tiles.getTileInfo(tilewire.getTileIndex());
+ const auto &tileTypeName = tiles.getTileTypeName(tileInfo.getTypeIndex());
+ if (boost::starts_with(tileTypeName, "INT") || boost::starts_with(tileTypeName, "CLB")) {
+ r.first->second = tilewire.getTileIndex();
+ break;
+ }
+ }
+ }
+ if (r.first->second != tileIndex)
+ continue;
+
+ segment_to_wire.emplace(currentSegment, w);
+ } else
+ trivial_to_wire.emplace(currentTilewire, w);
+
+ wire_to_tilewire.push_back(currentTilewire);
+
+ auto it = delay_lookup.find(tileTypeIndex);
+ if (it == delay_lookup.end()) {
+ auto wireCount = tiles.getWireCount(tileTypeIndex);
+ std::vector<delay_t> tile_delays(wireCount);
+ for (WireIndex wireIndex(0); wireIndex < wireCount; wireIndex++) {
+ const WireInfo &wireInfo = tiles.getWireInfo(tileTypeIndex, wireIndex);
+ auto wire_name = wireInfo.getName();
+ if (std::regex_match(wire_name, what, re_124)) {
+ switch (what.str(2)[0]) {
+ case '1':
+ tile_delays[wireIndex] = 150;
+ break;
+ case '2':
+ tile_delays[wireIndex] = 170;
+ break;
+ case '4':
+ tile_delays[wireIndex] = 210;
+ break;
+ case '6':
+ tile_delays[wireIndex] = 210;
+ break;
+ default:
+ throw;
+ }
+ } else if (std::regex_match(wire_name, what, re_L)) {
+ std::string l(what[2]);
+ if (l == "H")
+ tile_delays[wireIndex] = 360;
+ else if (l == "VB")
+ tile_delays[wireIndex] = 300;
+ else if (l == "V")
+ tile_delays[wireIndex] = 350;
+ else
+ throw;
+ } else if (std::regex_match(wire_name, what, re_BYP)) {
+ tile_delays[wireIndex] = 190;
+ } else if (std::regex_match(wire_name, what, re_BYP_B)) {
+ } else if (std::regex_match(wire_name, what, re_FAN)) {
+ tile_delays[wireIndex] = 190;
+ } else if (std::regex_match(wire_name, what, re_CLB_I1_6)) {
+ switch (what.str(2)[0]) {
+ case '1':
+ tile_delays[wireIndex] = 280;
+ break;
+ case '2':
+ tile_delays[wireIndex] = 280;
+ break;
+ case '3':
+ tile_delays[wireIndex] = 180;
+ break;
+ case '4':
+ tile_delays[wireIndex] = 180;
+ break;
+ case '5':
+ tile_delays[wireIndex] = 80;
+ break;
+ case '6':
+ tile_delays[wireIndex] = 40;
+ break;
+ default:
+ throw;
+ }
+ }
+ }
+ it = delay_lookup.emplace(tileTypeIndex, std::move(tile_delays)).first;
+ }
+ assert(it != delay_lookup.end());
+
+ DelayInfo d;
+ d.delay = it->second[currentTilewire.getWireIndex()];
+ wire_to_delay.emplace_back(std::move(d));
+
+ ++w.index;
+ }
+ }
+ segment_to_anchor.clear();
+ wire_to_tilewire.shrink_to_fit();
+ wire_to_delay.shrink_to_fit();
+ num_wires = wire_to_tilewire.size();
+ wire_is_global.resize(num_wires);
+
+ wire_to_pips_downhill.resize(num_wires);
+ // std::unordered_map<Arc, int> arc_to_pip;
+ ArcVector arcs;
+ ExtendedWireInfo ewi(*ddb);
+ PipId p;
+ p.index = 0;
+ for (w.index = 0; w.index < num_wires; ++w.index) {
+ const auto ¤tTilewire = wire_to_tilewire[w.index];
+ if (currentTilewire.isUndefined())
+ continue;
+
+ const auto &tileInfo = tiles.getTileInfo(currentTilewire.getTileIndex());
+ const auto tileTypeName = tiles.getTileTypeName(tileInfo.getTypeIndex());
+ const bool clb = boost::starts_with(
+ tileTypeName, "CLB"); // Disable all CLB route-throughs (i.e. LUT in->out, LUT A->AMUX, for now)
+
+ auto &pips = wire_to_pips_downhill[w.index];
+ const bool clk_tile = boost::starts_with(tileTypeName, "CLK");
+
+ bool global_tile = false;
+
+ arcs.clear();
+ //const_cast<DDB &>(*ddb).expandSegmentSinks(currentTilewire, arcs, DDB::eExpandDirectionNone,
+ // false /* inUseTied */, true /*inUseRegular */,
+ // true /* inUseIrregular */, !clb /* inUseRoutethrough */);
+ {
+ // expand the segment
+ TilewireVector segment;
+ const_cast<DDB &>(*ddb).expandSegment(currentTilewire, segment, DDB::eExpandDirectionNone);
+ // expand all of the arcs
+ TilewireVector::const_iterator sep = segment.begin();
+ TilewireVector::const_iterator see = segment.end();
+ while(sep < see) {
+ // expand the tilewire sinks
+ const Tilewire& tilewire = *sep++;
+
+ const auto &tileInfo = tiles.getTileInfo(tilewire.getTileIndex());
+ const auto &tileTypeName = tiles.getTileTypeName(tileInfo.getTypeIndex());
+ global_tile = global_tile || boost::starts_with(tileTypeName, "CLK") || boost::starts_with(tileTypeName, "HCLK") || boost::starts_with(tileTypeName, "CFG");
+
+ TilewireVector sinks;
+ const_cast<DDB &>(*ddb).expandTilewireSinks(tilewire, sinks, false /*inUseTied*/, true /*inUseRegular*/, true /*inUseIrregular*/,
+ !clb /* inUseRoutethrough */);
+ // rewrite the sinks as arcs
+ TilewireVector::const_iterator sip = sinks.begin();
+ TilewireVector::const_iterator sie = sinks.end();
+ while(sip < sie) {
+ Arc a(tilewire, *sip++);
+
+ // Disable BUFG I0 -> O routethrough
+ if (clk_tile) {
+ ewi.set(a.getSourceTilewire());
+ if (std::regex_match(ewi.mWireName, bufg_i)) {
+ ewi.set(a.getSinkTilewire());
+ if (std::regex_match(ewi.mWireName, bufg_o))
+ continue;
+ }
+ }
+
+ // Disable entering HROW from INT_[LR].CLK[01]
+ if (boost::starts_with(tileTypeName, "CLK_HROW")) {
+ ewi.set(a.getSourceTilewire());
+ if (std::regex_match(ewi.mWireName, hrow))
+ continue;
+ }
+
+ pips.emplace_back(p);
+ pip_to_arc.emplace_back(a);
+ // arc_to_pip.emplace(a, p.index);
+ ++p.index;
+ }
+ }
+ }
+ pips.shrink_to_fit();
+
+ if (global_tile)
+ wire_is_global[w.index] = true;
+ }
+ pip_to_arc.shrink_to_fit();
+ num_pips = pip_to_arc.size();
+
+ height = (int)tiles.getRowCount();
+ width = (int)tiles.getColCount();
+}
+TorcInfo::TorcInfo(const std::string& inDeviceName, const std::string &inPackageName)
+ : ddb(new DDB(inDeviceName, inPackageName)), sites(ddb->getSites()), tiles(ddb->getTiles()),
+ segments(ddb->getSegments())
+{
+}
+
+// -----------------------------------------------------------------------
+
+void IdString::initialize_arch(const BaseCtx *ctx)
+{
+#define X(t) initialize_add(ctx, #t, ID_##t);
+#include "constids.inc"
+#undef X
+}
+
+// -----------------------------------------------------------------------
+
+Arch::Arch(ArchArgs args) : args(args)
+{
+ torc::common::DirectoryTree directoryTree("/opt/torc/src/torc");
+ if (args.type == ArchArgs::Z020) {
+ torc_info = std::unique_ptr<TorcInfo>(new TorcInfo(this, "xc7z020", args.package));
+ } else if (args.type == ArchArgs::VX980) {
+ torc_info = std::unique_ptr<TorcInfo>(new TorcInfo(this, "xc7vx980t", args.package));
+ } else {
+ log_error("Unsupported XC7 chip type.\n");
+ }
+
+ width = torc_info->width;
+ height = torc_info->height;
+ /*if (getCtx()->verbose)*/ {
+ log_info("Number of bels: %d\n", torc_info->num_bels);
+ log_info("Number of wires: %d\n", torc_info->num_wires);
+ log_info("Number of pips: %d\n", torc_info->num_pips);
+ }
+
+ bel_to_cell.resize(torc_info->num_bels);
+ wire_to_net.resize(torc_info->num_wires);
+ pip_to_net.resize(torc_info->num_pips);
+}
+
+// -----------------------------------------------------------------------
+
+std::string Arch::getChipName() const
+{
+ if (args.type == ArchArgs::Z020) {
+ return "z020";
+ } else if (args.type == ArchArgs::VX980) {
+ return "vx980";
+ } else {
+ log_error("Unsupported XC7 chip type.\n");
+ }
+}
+
+// -----------------------------------------------------------------------
+
+IdString Arch::archArgsToId(ArchArgs args) const
+{
+ if (args.type == ArchArgs::Z020)
+ return id("z020");
+ if (args.type == ArchArgs::VX980)
+ return id("vx980");
+ return IdString();
+}
+
+// -----------------------------------------------------------------------
+
+static bool endsWith(const std::string& str, const std::string& suffix)
+{
+ return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix);
+}
+
+BelId Arch::getBelByName(IdString name) const
+{
+ std::string n = name.str(this);
+ int ndx = 0;
+ if (endsWith(n,"_A") || endsWith(n,"_B") || endsWith(n,"_C") || endsWith(n,"_D"))
+ {
+ ndx = (int)(n.back() - 'A');
+ n = n.substr(0,n.size()-2);
+ }
+ auto it = torc_info->sites.findSiteIndex(n);
+ if (it != SiteIndex(-1)) {
+ BelId id = torc_info->site_index_to_bel.at(it);
+ id.index += ndx;
+ return id;
+ }
+ return BelId();
+}
+
+BelId Arch::getBelByLocation(Loc loc) const
+{
+ BelId bel;
+
+ if (bel_by_loc.empty()) {
+ for (int i = 0; i < torc_info->num_bels; i++) {
+ BelId b;
+ b.index = i;
+ bel_by_loc[getBelLocation(b)] = b;
+ }
+ }
+
+ auto it = bel_by_loc.find(loc);
+ if (it != bel_by_loc.end())
+ bel = it->second;
+
+ return bel;
+}
+
+BelRange Arch::getBelsByTile(int x, int y) const
+{
+ BelRange br;
+
+ br.b.cursor = Arch::getBelByLocation(Loc(x, y, 0)).index;
+ br.e.cursor = br.b.cursor;
+
+ if (br.e.cursor != -1) {
+ while (br.e.cursor < chip_info->num_bels && chip_info->bel_data[br.e.cursor].x == x &&
+ chip_info->bel_data[br.e.cursor].y == y)
+ br.e.cursor++;
+ }
+
+ return br;
+}
+
+PortType Arch::getBelPinType(BelId bel, IdString pin) const
+{
+ NPNR_ASSERT(bel != BelId());
+
+ int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires;
+ const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get();
+
+ if (num_bel_wires < 7) {
+ for (int i = 0; i < num_bel_wires; i++) {
+ if (bel_wires[i].port == pin.index)
+ return PortType(bel_wires[i].type);
+ }
+ } else {
+ int b = 0, e = num_bel_wires - 1;
+ while (b <= e) {
+ int i = (b + e) / 2;
+ if (bel_wires[i].port == pin.index)
+ return PortType(bel_wires[i].type);
+ if (bel_wires[i].port > pin.index)
+ e = i - 1;
+ else
+ b = i + 1;
+ }
+ }
+
+ return PORT_INOUT;
+}
+
+std::vector<std::pair<IdString, std::string>> Arch::getBelAttrs(BelId bel) const
+{
+ std::vector<std::pair<IdString, std::string>> ret;
+ return ret;
+}
+
+WireId Arch::getBelPinWire(BelId bel, IdString pin) const
+{
+ auto pin_name = pin.str(this);
+ auto bel_type = getBelType(bel);
+ if (bel_type == id_SLICE_LUT6) {
+ // For all LUT based inputs and outputs (I1-I6,O,OQ,OMUX) then change the I/O into the LUT
+ if (pin_name[0] == 'I' || pin_name[0] == 'O') {
+ switch (torc_info->bel_to_loc[bel.index].z) {
+ case 0:
+ case 4:
+ pin_name[0] = 'A';
+ break;
+ case 1:
+ case 5:
+ pin_name[0] = 'B';
+ break;
+ case 2:
+ case 6:
+ pin_name[0] = 'C';
+ break;
+ case 3:
+ case 7:
+ pin_name[0] = 'D';
+ break;
+ default:
+ throw;
+ }
+ }
+ } else if (bel_type == id_PS7 || bel_type == id_MMCME2_ADV) {
+ // e.g. Convert DDRARB[0] -> DDRARB0
+ pin_name.erase(std::remove_if(pin_name.begin(), pin_name.end(), boost::is_any_of("[]")), pin_name.end());
+ }
+
+ auto site_index = torc_info->bel_to_site_index[bel.index];
+ const auto &site = torc_info->sites.getSite(site_index);
+ auto &tw = site.getPinTilewire(pin_name);
+
+ if (tw.isUndefined())
+ log_error("no wire found for site '%s' pin '%s' \n", torc_info->bel_to_name(bel.index).c_str(),
+ pin_name.c_str());
+
+ return torc_info->tilewire_to_wire(tw);
+}
+
+std::vector<IdString> Arch::getBelPins(BelId bel) const
+{
+ std::vector<IdString> ret;
+ NPNR_ASSERT("TODO");
+ return ret;
+}
+
+// -----------------------------------------------------------------------
+
+WireId Arch::getWireByName(IdString name) const
+{
+ WireId ret;
+ if (wire_by_name.empty()) {
+ for (int i = 0; i < torc_info->num_wires; i++)
+ wire_by_name[id(torc_info->wire_to_name(i))] = i;
+ }
+
+ auto it = wire_by_name.find(name);
+ if (it != wire_by_name.end())
+ ret.index = it->second;
+
+ return ret;
+}
+
+IdString Arch::getWireType(WireId wire) const
+{
+ NPNR_ASSERT(wire != WireId());
+ NPNR_ASSERT("TODO");
+ return IdString();
+}
+
+// -----------------------------------------------------------------------
+std::vector<std::pair<IdString, std::string>> Arch::getWireAttrs(WireId wire) const
+{
+ std::vector<std::pair<IdString, std::string>> ret;
+ NPNR_ASSERT("TODO");
+ return ret;
+}
+
+// -----------------------------------------------------------------------
+
+PipId Arch::getPipByName(IdString name) const
+{
+ PipId ret;
+
+ if (pip_by_name.empty()) {
+ for (int i = 0; i < torc_info->num_pips; i++) {
+ PipId pip;
+ pip.index = i;
+ pip_by_name[getPipName(pip)] = i;
+ }
+ }
+
+ auto it = pip_by_name.find(name);
+ if (it != pip_by_name.end())
+ ret.index = it->second;
+
+ return ret;
+}
+
+IdString Arch::getPipName(PipId pip) const
+{
+ NPNR_ASSERT(pip != PipId());
+
+ ExtendedWireInfo ewi_src(*torc_info->ddb, torc_info->pip_to_arc[pip.index].getSourceTilewire());
+ ExtendedWireInfo ewi_dst(*torc_info->ddb, torc_info->pip_to_arc[pip.index].getSinkTilewire());
+ std::stringstream pip_name;
+ pip_name << ewi_src.mTileName << "." << ewi_src.mWireName << ".->." << ewi_dst.mWireName;
+ return id(pip_name.str());
+}
+
+std::vector<std::pair<IdString, std::string>> Arch::getPipAttrs(PipId pip) const
+{
+ std::vector<std::pair<IdString, std::string>> ret;
+ NPNR_ASSERT("TODO");
+ return ret;
+}
+
+// -----------------------------------------------------------------------
+
+BelId Arch::getPackagePinBel(const std::string &pin) const { return getBelByName(id(pin)); }
+
+std::string Arch::getBelPackagePin(BelId bel) const
+{
+ NPNR_ASSERT("TODO");
+ return "";
+}
+
+// -----------------------------------------------------------------------
+
+GroupId Arch::getGroupByName(IdString name) const
+{
+ for (auto g : getGroups())
+ if (getGroupName(g) == name)
+ return g;
+ return GroupId();
+}
+
+IdString Arch::getGroupName(GroupId group) const
+{
+ std::string suffix;
+
+ switch (group.type) {
+ NPNR_ASSERT("TODO");
+ default:
+ return IdString();
+ }
+
+ return id("X" + std::to_string(group.x) + "/Y" + std::to_string(group.y) + "/" + suffix);
+}
+
+std::vector<GroupId> Arch::getGroups() const
+{
+ std::vector<GroupId> ret;
+ NPNR_ASSERT("TODO");
+ return ret;
+}
+
+std::vector<BelId> Arch::getGroupBels(GroupId group) const
+{
+ std::vector<BelId> ret;
+ return ret;
+}
+
+std::vector<WireId> Arch::getGroupWires(GroupId group) const
+{
+ std::vector<WireId> ret;
+ return ret;
+}
+
+std::vector<PipId> Arch::getGroupPips(GroupId group) const
+{
+ std::vector<PipId> ret;
+ NPNR_ASSERT("TODO");
+ return ret;
+}
+
+std::vector<GroupId> Arch::getGroupGroups(GroupId group) const
+{
+ std::vector<GroupId> ret;
+ NPNR_ASSERT("TODO");
+ return ret;
+}
+
+// -----------------------------------------------------------------------
+
+bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; }
+
+// -----------------------------------------------------------------------
+
+bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); }
+
+bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); }
+
+// -----------------------------------------------------------------------
+
+DecalXY Arch::getBelDecal(BelId bel) const
+{
+ DecalXY decalxy;
+ decalxy.decal.type = DecalId::TYPE_BEL;
+ decalxy.decal.index = bel.index;
+ decalxy.decal.active = bel_to_cell.at(bel.index) != nullptr;
+ return decalxy;
+}
+
+DecalXY Arch::getWireDecal(WireId wire) const
+{
+ DecalXY decalxy;
+ decalxy.decal.type = DecalId::TYPE_WIRE;
+ decalxy.decal.index = wire.index;
+ decalxy.decal.active = wire_to_net.at(wire.index) != nullptr;
+ return decalxy;
+}
+
+DecalXY Arch::getPipDecal(PipId pip) const
+{
+ DecalXY decalxy;
+ decalxy.decal.type = DecalId::TYPE_PIP;
+ decalxy.decal.index = pip.index;
+ decalxy.decal.active = pip_to_net.at(pip.index) != nullptr;
+ return decalxy;
+};
+
+DecalXY Arch::getGroupDecal(GroupId group) const
+{
+ DecalXY decalxy;
+ decalxy.decal.type = DecalId::TYPE_GROUP;
+ decalxy.decal.index = (group.type << 16) | (group.x << 8) | (group.y);
+ decalxy.decal.active = true;
+ return decalxy;
+};
+
+std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
+{
+ std::vector<GraphicElement> ret;
+
+ if (decal.type == DecalId::TYPE_BEL) {
+ BelId bel;
+ bel.index = decal.index;
+ auto bel_type = getBelType(bel);
+ int x = torc_info->bel_to_loc[bel.index].x;
+ int y = torc_info->bel_to_loc[bel.index].y;
+ int z = torc_info->bel_to_loc[bel.index].z;
+ if (bel_type == id_SLICE_LUT6) {
+ GraphicElement el;
+ /*if (z>3) {
+ z = z - 4;
+ x -= logic_cell_x2- logic_cell_x1;
+ }*/
+ el.type = GraphicElement::TYPE_BOX;
+ el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
+ el.x1 = x + logic_cell_x1;
+ el.x2 = x + logic_cell_x2;
+ el.y1 = y + logic_cell_y1 + (z)*logic_cell_pitch;
+ el.y2 = y + logic_cell_y2 + (z)*logic_cell_pitch;
+ ret.push_back(el);
+ }
+
+ }
+
+ return ret;
+}
+
+// -----------------------------------------------------------------------
+
+bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
+{
+ if (cell->type == id_SLICE_LUT6) {
+ if (fromPort.index >= id_I1.index && fromPort.index <= id_I6.index) {
+ if (toPort == id_O) {
+ delay.delay = 124; // Tilo
+ return true;
+ }
+ if (toPort == id_OQ) {
+ delay.delay = 95; // Tas
+ return true;
+ }
+ }
+ if (fromPort == id_CLK) {
+ if (toPort == id_OQ) {
+ delay.delay = 456; // Tcko
+ return true;
+ }
+ }
+ } else if (cell->type == id_BUFGCTRL) {
+ return true;
+ }
+ return false;
+}
+
+// Get the port class, also setting clockPort to associated clock if applicable
+TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const
+{
+ if (cell->type == id_SLICE_LUT6) {
+ if (port == id_CLK)
+ return TMG_CLOCK_INPUT;
+ if (port == id_CIN)
+ return TMG_COMB_INPUT;
+ if (port == id_COUT)
+ return TMG_COMB_OUTPUT;
+ if (port == id_O) {
+ // LCs with no inputs are constant drivers
+ if (cell->lcInfo.inputCount == 0)
+ return TMG_IGNORE;
+ return TMG_COMB_OUTPUT;
+ }
+ if (cell->lcInfo.dffEnable) {
+ clockInfoCount = 1;
+ if (port == id_OQ)
+ return TMG_REGISTER_OUTPUT;
+ return TMG_REGISTER_INPUT;
+ } else {
+ return TMG_COMB_INPUT;
+ }
+ // TODO
+ // if (port == id_OMUX)
+ } else if (cell->type == id_IOB33 || cell->type == id_IOB18) {
+ if (port == id_I)
+ return TMG_STARTPOINT;
+ else if (port == id_O)
+ return TMG_ENDPOINT;
+ } else if (cell->type == id_BUFGCTRL) {
+ if (port == id_O)
+ return TMG_COMB_OUTPUT;
+ return TMG_COMB_INPUT;
+ } else if (cell->type == id_PS7) {
+ // TODO
+ return TMG_IGNORE;
+ } else if (cell->type == id_MMCME2_ADV) {
+ return TMG_IGNORE;
+ }
+ log_error("no timing info for port '%s' of cell type '%s'\n", port.c_str(this), cell->type.c_str(this));
+}
+
+TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
+{
+ TimingClockingInfo info;
+ if (cell->type == id_SLICE_LUT6) {
+ info.clock_port = id_CLK;
+ info.edge = cell->lcInfo.negClk ? FALLING_EDGE : RISING_EDGE;
+ if (port == id_OQ) {
+ bool has_clktoq = getCellDelay(cell, id_CLK, id_OQ, info.clockToQ);
+ NPNR_ASSERT(has_clktoq);
+ } else {
+ info.setup.delay = 124; // Tilo
+ info.hold.delay = 0;
+ }
+ } else {
+ NPNR_ASSERT_FALSE("unhandled cell type in getPortClockingInfo");
+ }
+ return info;
+}
+
+bool Arch::isGlobalNet(const NetInfo *net) const
+{
+ if (net == nullptr)
+ return false;
+ return net->driver.cell != nullptr && net->driver.cell->type == id_BUFGCTRL && net->driver.port == id_O;
+}
+
+// Assign arch arg info
+void Arch::assignArchInfo()
+{
+ for (auto &net : getCtx()->nets) {
+ NetInfo *ni = net.second.get();
+ if (isGlobalNet(ni))
+ ni->is_global = true;
+ ni->is_enable = false;
+ ni->is_reset = false;
+ for (auto usr : ni->users) {
+ if (is_enable_port(this, usr))
+ ni->is_enable = true;
+ if (is_reset_port(this, usr))
+ ni->is_reset = true;
+ }
+ }
+ for (auto &cell : getCtx()->cells) {
+ CellInfo *ci = cell.second.get();
+ assignCellInfo(ci);
+ }
+}
+
+void Arch::assignCellInfo(CellInfo *cell)
+{
+ cell->belType = cell->type;
+ if (cell->type == id_SLICE_LUT6) {
+ cell->lcInfo.dffEnable = bool_or_default(cell->params, id_DFF_ENABLE);
+ cell->lcInfo.carryEnable = bool_or_default(cell->params, id_CARRY_ENABLE);
+ cell->lcInfo.negClk = bool_or_default(cell->params, id_NEG_CLK);
+ cell->lcInfo.clk = get_net_or_empty(cell, id_CLK);
+ cell->lcInfo.cen = get_net_or_empty(cell, id_CEN);
+ cell->lcInfo.sr = get_net_or_empty(cell, id_SR);
+ cell->lcInfo.inputCount = 0;
+ if (get_net_or_empty(cell, id_I1))
+ cell->lcInfo.inputCount++;
+ if (get_net_or_empty(cell, id_I2))
+ cell->lcInfo.inputCount++;
+ if (get_net_or_empty(cell, id_I3))
+ cell->lcInfo.inputCount++;
+ if (get_net_or_empty(cell, id_I4))
+ cell->lcInfo.inputCount++;
+ if (get_net_or_empty(cell, id_I5))
+ cell->lcInfo.inputCount++;
+ if (get_net_or_empty(cell, id_I6))
+ cell->lcInfo.inputCount++;
+ }
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/xc7/arch.h b/xc7/arch.h
new file mode 100644
index 0000000..699d357
--- /dev/null
+++ b/xc7/arch.h
@@ -0,0 +1,934 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef NEXTPNR_H
+#error Include "arch.h" via "nextpnr.h" only.
+#endif
+
+#include "torc/Architecture.hpp"
+#include "torc/Common.hpp"
+using namespace torc::architecture;
+using namespace torc::architecture::xilinx;
+
+namespace std {
+template <> struct hash<Segments::SegmentReference>
+{
+ size_t operator()(const Segments::SegmentReference &s) const
+ {
+ size_t seed = 0;
+ boost::hash_combine(seed, hash<unsigned>()(s.getCompactSegmentIndex()));
+ boost::hash_combine(seed, hash<unsigned>()(s.getAnchorTileIndex()));
+ return seed;
+ }
+};
+template <> struct equal_to<Segments::SegmentReference>
+{
+ bool operator()(const Segments::SegmentReference &lhs, const Segments::SegmentReference &rhs) const
+ {
+ return lhs.getAnchorTileIndex() == rhs.getAnchorTileIndex() &&
+ lhs.getCompactSegmentIndex() == rhs.getCompactSegmentIndex();
+ }
+};
+template <> struct hash<Tilewire>
+{
+ size_t operator()(const Tilewire &t) const { return hash_value(t); }
+};
+
+template <> struct hash<Arc>
+{
+ size_t operator()(const Arc &a) const
+ {
+ size_t seed = 0;
+ boost::hash_combine(seed, hash_value(a.getSourceTilewire()));
+ boost::hash_combine(seed, hash_value(a.getSinkTilewire()));
+ return seed;
+ }
+};
+} // namespace std
+
+NEXTPNR_NAMESPACE_BEGIN
+
+/**** Everything in this section must be kept in sync with chipdb.py ****/
+
+template <typename T> struct RelPtr
+{
+ int32_t offset;
+
+ // void set(const T *ptr) {
+ // offset = reinterpret_cast<const char*>(ptr) -
+ // reinterpret_cast<const char*>(this);
+ // }
+
+ const T *get() const { return reinterpret_cast<const T *>(reinterpret_cast<const char *>(this) + offset); }
+
+ const T &operator[](size_t index) const { return get()[index]; }
+
+ const T &operator*() const { return *(get()); }
+
+ const T *operator->() const { return get(); }
+};
+
+NPNR_PACKED_STRUCT(struct BelWirePOD {
+ int32_t port;
+ int32_t type;
+ int32_t wire_index;
+});
+
+NPNR_PACKED_STRUCT(struct BelInfoPOD {
+ RelPtr<char> name;
+ int32_t type;
+ int32_t num_bel_wires;
+ RelPtr<BelWirePOD> bel_wires;
+ int8_t x, y, z;
+ int8_t padding_0;
+});
+
+NPNR_PACKED_STRUCT(struct BelPortPOD {
+ int32_t bel_index;
+ int32_t port;
+});
+
+NPNR_PACKED_STRUCT(struct PipInfoPOD {
+ enum PipFlags : uint32_t
+ {
+ FLAG_NONE = 0,
+ FLAG_ROUTETHRU = 1,
+ FLAG_NOCARRY = 2
+ };
+
+ // RelPtr<char> name;
+ int32_t src, dst;
+ int32_t fast_delay;
+ int32_t slow_delay;
+ int8_t x, y;
+ int16_t src_seg, dst_seg;
+ int16_t switch_mask;
+ int32_t switch_index;
+ PipFlags flags;
+});
+
+NPNR_PACKED_STRUCT(struct WireSegmentPOD {
+ int8_t x, y;
+ int16_t index;
+});
+
+NPNR_PACKED_STRUCT(struct WireInfoPOD {
+ enum WireType : int8_t
+ {
+ WIRE_TYPE_NONE = 0,
+ WIRE_TYPE_GLB2LOCAL = 1,
+ WIRE_TYPE_GLB_NETWK = 2,
+ WIRE_TYPE_LOCAL = 3,
+ WIRE_TYPE_LUTFF_IN = 4,
+ WIRE_TYPE_LUTFF_IN_LUT = 5,
+ WIRE_TYPE_LUTFF_LOUT = 6,
+ WIRE_TYPE_LUTFF_OUT = 7,
+ WIRE_TYPE_LUTFF_COUT = 8,
+ WIRE_TYPE_LUTFF_GLOBAL = 9,
+ WIRE_TYPE_CARRY_IN_MUX = 10,
+ WIRE_TYPE_SP4_V = 11,
+ WIRE_TYPE_SP4_H = 12,
+ WIRE_TYPE_SP12_V = 13,
+ WIRE_TYPE_SP12_H = 14
+ };
+
+ RelPtr<char> name;
+ int32_t num_uphill, num_downhill;
+ RelPtr<int32_t> pips_uphill, pips_downhill;
+
+ int32_t num_bel_pins;
+ RelPtr<BelPortPOD> bel_pins;
+
+ int32_t num_segments;
+ RelPtr<WireSegmentPOD> segments;
+
+ int32_t fast_delay;
+ int32_t slow_delay;
+
+ int8_t x, y, z;
+ WireType type;
+});
+
+NPNR_PACKED_STRUCT(struct PackagePinPOD {
+ RelPtr<char> name;
+ int32_t bel_index;
+});
+
+NPNR_PACKED_STRUCT(struct PackageInfoPOD {
+ RelPtr<char> name;
+ int32_t num_pins;
+ RelPtr<PackagePinPOD> pins;
+});
+
+enum TileType : uint32_t
+{
+ TILE_NONE = 0,
+ TILE_LOGIC = 1,
+ TILE_IO = 2,
+ TILE_RAMB = 3,
+ TILE_RAMT = 4,
+ TILE_DSP0 = 5,
+ TILE_DSP1 = 6,
+ TILE_DSP2 = 7,
+ TILE_DSP3 = 8,
+ TILE_IPCON = 9
+};
+
+NPNR_PACKED_STRUCT(struct ConfigBitPOD { int8_t row, col; });
+
+NPNR_PACKED_STRUCT(struct ConfigEntryPOD {
+ RelPtr<char> name;
+ int32_t num_bits;
+ RelPtr<ConfigBitPOD> bits;
+});
+
+NPNR_PACKED_STRUCT(struct TileInfoPOD {
+ int8_t cols, rows;
+ int16_t num_config_entries;
+ RelPtr<ConfigEntryPOD> entries;
+});
+
+static const int max_switch_bits = 5;
+
+NPNR_PACKED_STRUCT(struct SwitchInfoPOD {
+ int32_t num_bits;
+ int32_t bel;
+ int8_t x, y;
+ ConfigBitPOD cbits[max_switch_bits];
+});
+
+NPNR_PACKED_STRUCT(struct IerenInfoPOD {
+ int8_t iox, ioy, ioz;
+ int8_t ierx, iery, ierz;
+});
+
+NPNR_PACKED_STRUCT(struct BitstreamInfoPOD {
+ int32_t num_switches, num_ierens;
+ RelPtr<TileInfoPOD> tiles_nonrouting;
+ RelPtr<SwitchInfoPOD> switches;
+ RelPtr<IerenInfoPOD> ierens;
+});
+
+NPNR_PACKED_STRUCT(struct BelConfigEntryPOD {
+ RelPtr<char> entry_name;
+ RelPtr<char> cbit_name;
+ int8_t x, y;
+ int16_t padding;
+});
+
+// Stores mapping between bel parameters and config bits,
+// for extra cells where this mapping is non-trivial
+NPNR_PACKED_STRUCT(struct BelConfigPOD {
+ int32_t bel_index;
+ int32_t num_entries;
+ RelPtr<BelConfigEntryPOD> entries;
+});
+
+NPNR_PACKED_STRUCT(struct CellPathDelayPOD {
+ int32_t from_port;
+ int32_t to_port;
+ int32_t fast_delay;
+ int32_t slow_delay;
+});
+
+NPNR_PACKED_STRUCT(struct CellTimingPOD {
+ int32_t type;
+ int32_t num_paths;
+ RelPtr<CellPathDelayPOD> path_delays;
+});
+
+NPNR_PACKED_STRUCT(struct ChipInfoPOD {
+ int32_t width, height;
+ int32_t num_bels, num_wires, num_pips;
+ int32_t num_switches, num_belcfgs, num_packages;
+ int32_t num_timing_cells;
+ RelPtr<BelInfoPOD> bel_data;
+ RelPtr<WireInfoPOD> wire_data;
+ RelPtr<PipInfoPOD> pip_data;
+ RelPtr<TileType> tile_grid;
+ RelPtr<BitstreamInfoPOD> bits_info;
+ RelPtr<BelConfigPOD> bel_config;
+ RelPtr<PackageInfoPOD> packages_data;
+ RelPtr<CellTimingPOD> cell_timing;
+});
+
+struct TorcInfo
+{
+ TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::string &inPackageName);
+ TorcInfo() = delete;
+ std::unique_ptr<const DDB> ddb;
+ const Sites &sites;
+ const Tiles &tiles;
+ const Segments &segments;
+
+ const TileInfo &bel_to_tile_info(int32_t index) const
+ {
+ auto si = bel_to_site_index[index];
+ const auto &site = sites.getSite(si);
+ return tiles.getTileInfo(site.getTileIndex());
+ }
+ const std::string &bel_to_name(int32_t index) const
+ {
+ auto si = bel_to_site_index[index];
+ return sites.getSite(si).getName();
+ }
+ std::string wire_to_name(int32_t index) const
+ {
+ const auto &tw = wire_to_tilewire[index];
+ ExtendedWireInfo ewi(*ddb, tw);
+ std::stringstream ss;
+ ss << ewi.mTileName << "/" << ewi.mWireName;
+ ss << "(" << tw.getWireIndex() << "@" << tw.getTileIndex() << ")";
+ return ss.str();
+ }
+
+ Loc wire_to_loc(int32_t index) const
+ {
+ const auto &tw = wire_to_tilewire[index];
+ ExtendedWireInfo ewi(*ddb, tw);
+ Loc l;
+ l.x = (int)ewi.mTileCol;
+ l.y = (int)ewi.mTileRow;
+ return l;
+ }
+
+ WireId tilewire_to_wire(const Tilewire &tw) const
+ {
+ const auto &segment = segments.getTilewireSegment(tw);
+ if (!segment.isTrivial())
+ return segment_to_wire.at(segment);
+ return trivial_to_wire.at(tw);
+ }
+
+ std::vector<SiteIndex> bel_to_site_index;
+ int num_bels;
+ std::vector<BelId> site_index_to_bel;
+ std::vector<IdString> site_index_to_type;
+ std::vector<Loc> bel_to_loc;
+ std::unordered_map<Segments::SegmentReference, WireId> segment_to_wire;
+ std::unordered_map<Tilewire, WireId> trivial_to_wire;
+ std::vector<Tilewire> wire_to_tilewire;
+ int num_wires;
+ std::vector<DelayInfo> wire_to_delay;
+ //std::vector<std::vector<int>> wire_to_pips_uphill;
+ std::vector<std::vector<PipId>> wire_to_pips_downhill;
+ std::vector<Arc> pip_to_arc;
+ int num_pips;
+ int width;
+ int height;
+ std::vector<bool> wire_is_global;
+ std::vector<std::pair<int,int>> tile_to_xy;
+
+ TorcInfo(const std::string &inDeviceName, const std::string &inPackageName);
+};
+extern std::unique_ptr<const TorcInfo> torc_info;
+
+/************************ End of chipdb section. ************************/
+
+struct BelIterator
+{
+ int cursor;
+
+ BelIterator operator++()
+ {
+ cursor++;
+ return *this;
+ }
+ BelIterator operator++(int)
+ {
+ BelIterator prior(*this);
+ cursor++;
+ return prior;
+ }
+
+ bool operator!=(const BelIterator &other) const { return cursor != other.cursor; }
+
+ bool operator==(const BelIterator &other) const { return cursor == other.cursor; }
+
+ BelId operator*() const
+ {
+ BelId ret;
+ ret.index = cursor;
+ return ret;
+ }
+};
+
+struct BelRange
+{
+ BelIterator b, e;
+ BelIterator begin() const { return b; }
+ BelIterator end() const { return e; }
+};
+
+// -----------------------------------------------------------------------
+
+struct BelPinIterator
+{
+ const BelId bel;
+ Array<const WireIndex>::iterator it;
+
+ void operator++() { it++; }
+ bool operator!=(const BelPinIterator &other) const { return it != other.it && bel != other.bel; }
+
+ BelPin operator*() const
+ {
+ BelPin ret;
+ ret.bel = bel;
+ ret.pin = IdString();
+ return ret;
+ }
+};
+
+struct BelPinRange
+{
+ BelPinIterator b, e;
+ BelPinIterator begin() const { return b; }
+ BelPinIterator end() const { return e; }
+};
+
+// -----------------------------------------------------------------------
+
+struct WireIterator
+{
+ int cursor = -1;
+
+ void operator++() { cursor++; }
+ bool operator!=(const WireIterator &other) const { return cursor != other.cursor; }
+
+ WireId operator*() const
+ {
+ WireId ret;
+ ret.index = cursor;
+ return ret;
+ }
+};
+
+struct WireRange
+{
+ WireIterator b, e;
+ WireIterator begin() const { return b; }
+ WireIterator end() const { return e; }
+};
+
+// -----------------------------------------------------------------------
+
+struct AllPipIterator
+{
+ int cursor = -1;
+
+ void operator++() { cursor++; }
+ bool operator!=(const AllPipIterator &other) const { return cursor != other.cursor; }
+
+ PipId operator*() const
+ {
+ PipId ret;
+ ret.index = cursor;
+ return ret;
+ }
+};
+
+struct AllPipRange
+{
+ AllPipIterator b, e;
+ AllPipIterator begin() const { return b; }
+ AllPipIterator end() const { return e; }
+};
+
+// -----------------------------------------------------------------------
+
+struct PipIterator
+{
+ const PipId *cursor = nullptr;
+
+ void operator++() { cursor++; }
+ bool operator!=(const PipIterator &other) const { return cursor != other.cursor; }
+
+ PipId operator*() const
+ {
+ return *cursor;
+ }
+};
+
+struct PipRange
+{
+ PipIterator b, e;
+ PipIterator begin() const { return b; }
+ PipIterator end() const { return e; }
+};
+
+struct ArchArgs
+{
+ enum ArchArgsTypes
+ {
+ NONE,
+ Z020,
+ VX980
+ } type = NONE;
+ std::string package;
+};
+
+struct Arch : BaseCtx
+{
+ bool fast_part;
+ const ChipInfoPOD *chip_info;
+ const PackageInfoPOD *package_info;
+ int width;
+ int height;
+
+ mutable std::unordered_map<IdString, int> wire_by_name;
+ mutable std::unordered_map<IdString, int> pip_by_name;
+ mutable std::unordered_map<Loc, BelId> bel_by_loc;
+
+ // std::vector<bool> bel_carry;
+ std::vector<CellInfo *> bel_to_cell;
+ std::vector<NetInfo *> wire_to_net;
+ std::vector<NetInfo *> pip_to_net;
+ // std::vector<NetInfo *> switches_locked;
+
+ ArchArgs args;
+ Arch(ArchArgs args);
+
+ std::string getChipName() const;
+
+ IdString archId() const { return id("xc7"); }
+ ArchArgs archArgs() const { return args; }
+ IdString archArgsToId(ArchArgs args) const;
+
+ // -------------------------------------------------
+
+ int getGridDimX() const { return width; }
+ int getGridDimY() const { return height; }
+ int getTileBelDimZ(int, int) const { return 8; }
+ int getTilePipDimZ(int, int) const { return 1; }
+
+ // -------------------------------------------------
+
+ BelId getBelByName(IdString name) const;
+
+ IdString getBelName(BelId bel) const
+ {
+ NPNR_ASSERT(bel != BelId());
+ auto name = torc_info->bel_to_name(bel.index);
+ if (getBelType(bel) == id_SLICE_LUT6) {
+ // Append LUT name to name
+ name.reserve(name.size() + 2);
+ name += "_";
+ switch (torc_info->bel_to_loc[bel.index].z) {
+ case 0:
+ case 4:
+ name += 'A';
+ break;
+ case 1:
+ case 5:
+ name += 'B';
+ break;
+ case 2:
+ case 6:
+ name += 'C';
+ break;
+ case 3:
+ case 7:
+ name += 'D';
+ break;
+ default:
+ throw;
+ }
+ }
+ return id(name);
+ }
+
+ uint32_t getBelChecksum(BelId bel) const { return bel.index; }
+
+ void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength)
+ {
+ NPNR_ASSERT(bel != BelId());
+ NPNR_ASSERT(bel_to_cell[bel.index] == nullptr);
+
+ bel_to_cell[bel.index] = cell;
+ // bel_carry[bel.index] = (cell->type == id_ICESTORM_LC && cell->lcInfo.carryEnable);
+ cell->bel = bel;
+ cell->belStrength = strength;
+ refreshUiBel(bel);
+ }
+
+ void unbindBel(BelId bel)
+ {
+ NPNR_ASSERT(bel != BelId());
+ NPNR_ASSERT(bel_to_cell[bel.index] != nullptr);
+ bel_to_cell[bel.index]->bel = BelId();
+ bel_to_cell[bel.index]->belStrength = STRENGTH_NONE;
+ bel_to_cell[bel.index] = nullptr;
+ // bel_carry[bel.index] = false;
+ refreshUiBel(bel);
+ }
+
+ bool checkBelAvail(BelId bel) const
+ {
+ NPNR_ASSERT(bel != BelId());
+ return bel_to_cell[bel.index] == nullptr;
+ }
+
+ CellInfo *getBoundBelCell(BelId bel) const
+ {
+ NPNR_ASSERT(bel != BelId());
+ return bel_to_cell[bel.index];
+ }
+
+ CellInfo *getConflictingBelCell(BelId bel) const
+ {
+ NPNR_ASSERT(bel != BelId());
+ return bel_to_cell[bel.index];
+ }
+
+ BelRange getBels() const
+ {
+ BelRange range;
+ range.b.cursor = 0;
+ range.e.cursor = torc_info->num_bels;
+ return range;
+ }
+
+ Loc getBelLocation(BelId bel) const { return torc_info->bel_to_loc[bel.index]; }
+
+ BelId getBelByLocation(Loc loc) const;
+ BelRange getBelsByTile(int x, int y) const;
+
+ bool getBelGlobalBuf(BelId bel) const { return getBelType(bel) == id_BUFGCTRL; }
+
+ IdString getBelType(BelId bel) const
+ {
+ NPNR_ASSERT(bel != BelId());
+ auto site_index = torc_info->bel_to_site_index[bel.index];
+ return torc_info->site_index_to_type[site_index];
+ }
+
+ std::vector<std::pair<IdString, std::string>> getBelAttrs(BelId bel) const;
+
+ WireId getBelPinWire(BelId bel, IdString pin) const;
+ PortType getBelPinType(BelId bel, IdString pin) const;
+ std::vector<IdString> getBelPins(BelId bel) const;
+
+ // -------------------------------------------------
+
+ WireId getWireByName(IdString name) const;
+
+ IdString getWireName(WireId wire) const
+ {
+ NPNR_ASSERT(wire != WireId());
+ return id(torc_info->wire_to_name(wire.index));
+ }
+
+ IdString getWireType(WireId wire) const;
+ std::vector<std::pair<IdString, std::string>> getWireAttrs(WireId wire) const;
+
+ uint32_t getWireChecksum(WireId wire) const { return wire.index; }
+
+ void bindWire(WireId wire, NetInfo *net, PlaceStrength strength)
+ {
+ NPNR_ASSERT(wire != WireId());
+ NPNR_ASSERT(wire_to_net[wire.index] == nullptr);
+ wire_to_net[wire.index] = net;
+ net->wires[wire].pip = PipId();
+ net->wires[wire].strength = strength;
+ refreshUiWire(wire);
+ }
+
+ void unbindWire(WireId wire)
+ {
+ NPNR_ASSERT(wire != WireId());
+ NPNR_ASSERT(wire_to_net[wire.index] != nullptr);
+
+ auto &net_wires = wire_to_net[wire.index]->wires;
+ auto it = net_wires.find(wire);
+ NPNR_ASSERT(it != net_wires.end());
+
+ auto pip = it->second.pip;
+ if (pip != PipId()) {
+ pip_to_net[pip.index] = nullptr;
+ }
+
+ net_wires.erase(it);
+ wire_to_net[wire.index] = nullptr;
+ refreshUiWire(wire);
+ }
+
+ bool checkWireAvail(WireId wire) const
+ {
+ NPNR_ASSERT(wire != WireId());
+ return wire_to_net[wire.index] == nullptr;
+ }
+
+ NetInfo *getBoundWireNet(WireId wire) const
+ {
+ NPNR_ASSERT(wire != WireId());
+ return wire_to_net[wire.index];
+ }
+
+ WireId getConflictingWireWire(WireId wire) const { return wire; }
+
+ NetInfo *getConflictingWireNet(WireId wire) const
+ {
+ NPNR_ASSERT(wire != WireId());
+ return wire_to_net[wire.index];
+ }
+
+ DelayInfo getWireDelay(WireId wire) const { return {}; }
+
+ BelPinRange getWireBelPins(WireId wire) const
+ {
+ BelPinRange range;
+ // TODO
+ return range;
+ }
+
+ WireRange getWires() const
+ {
+ WireRange range;
+ range.b.cursor = 0;
+ range.e.cursor = torc_info->num_wires;
+ return range;
+ }
+
+ // -------------------------------------------------
+
+ PipId getPipByName(IdString name) const;
+
+ void bindPip(PipId pip, NetInfo *net, PlaceStrength strength)
+ {
+ NPNR_ASSERT(pip != PipId());
+ NPNR_ASSERT(pip_to_net[pip.index] == nullptr);
+
+ pip_to_net[pip.index] = net;
+
+ WireId dst = getPipDstWire(pip);
+ NPNR_ASSERT(wire_to_net[dst.index] == nullptr);
+ wire_to_net[dst.index] = net;
+ net->wires[dst].pip = pip;
+ net->wires[dst].strength = strength;
+ refreshUiPip(pip);
+ refreshUiWire(dst);
+ }
+
+ void unbindPip(PipId pip)
+ {
+ NPNR_ASSERT(pip != PipId());
+ NPNR_ASSERT(pip_to_net[pip.index] != nullptr);
+
+ WireId dst = getPipDstWire(pip);
+ NPNR_ASSERT(wire_to_net[dst.index] != nullptr);
+ wire_to_net[dst.index] = nullptr;
+ pip_to_net[pip.index]->wires.erase(dst);
+
+ pip_to_net[pip.index] = nullptr;
+ refreshUiPip(pip);
+ refreshUiWire(dst);
+ }
+
+ bool checkPipAvail(PipId pip) const
+ {
+ NPNR_ASSERT(pip != PipId());
+ return pip_to_net[pip.index] == nullptr;
+ }
+
+ NetInfo *getBoundPipNet(PipId pip) const
+ {
+ NPNR_ASSERT(pip != PipId());
+ return pip_to_net[pip.index];
+ }
+
+ WireId getConflictingPipWire(PipId pip) const { return WireId(); }
+
+ NetInfo *getConflictingPipNet(PipId pip) const
+ {
+ NPNR_ASSERT(pip != PipId());
+ return pip_to_net[pip.index];
+ }
+
+ AllPipRange getPips() const
+ {
+ AllPipRange range;
+ range.b.cursor = 0;
+ range.e.cursor = torc_info->num_pips;
+ return range;
+ }
+
+ Loc getPipLocation(PipId pip) const
+ {
+ Loc loc;
+ NPNR_ASSERT("TODO");
+ return loc;
+ }
+
+ IdString getPipName(PipId pip) const;
+
+ IdString getPipType(PipId pip) const { return IdString(); }
+ std::vector<std::pair<IdString, std::string>> getPipAttrs(PipId pip) const;
+
+ uint32_t getPipChecksum(PipId pip) const { return pip.index; }
+
+ WireId getPipSrcWire(PipId pip) const
+ {
+ NPNR_ASSERT(pip != PipId());
+
+ const auto &arc = torc_info->pip_to_arc[pip.index];
+ const auto &tw = arc.getSourceTilewire();
+ return torc_info->tilewire_to_wire(tw);
+ }
+
+ WireId getPipDstWire(PipId pip) const
+ {
+ NPNR_ASSERT(pip != PipId());
+ const auto &arc = torc_info->pip_to_arc[pip.index];
+ const auto &tw = arc.getSinkTilewire();
+ return torc_info->tilewire_to_wire(tw);
+ }
+
+ DelayInfo getPipDelay(PipId pip) const
+ {
+ NPNR_ASSERT(pip != PipId());
+ auto wire = getPipDstWire(pip);
+ return torc_info->wire_to_delay[wire.index];
+ }
+
+ PipRange getPipsDownhill(WireId wire) const
+ {
+ PipRange range;
+ NPNR_ASSERT(wire != WireId());
+ const auto &pips = torc_info->wire_to_pips_downhill[wire.index];
+ range.b.cursor = pips.data();
+ range.e.cursor = range.b.cursor + pips.size();
+ return range;
+ }
+
+ PipRange getPipsUphill(WireId wire) const
+ {
+ PipRange range;
+ // NPNR_ASSERT(wire != WireId());
+ // const auto &pips = torc_info->wire_to_pips_uphill[wire.index];
+ // range.b.cursor = pips.data();
+ // range.e.cursor = range.b.cursor + pips.size();
+ return range;
+ }
+
+ PipRange getWireAliases(WireId wire) const
+ {
+ PipRange range;
+ NPNR_ASSERT(wire != WireId());
+ range.b.cursor = nullptr;
+ range.e.cursor = nullptr;
+ return range;
+ }
+
+ BelId getPackagePinBel(const std::string &pin) const;
+ std::string getBelPackagePin(BelId bel) const;
+
+ // -------------------------------------------------
+
+ GroupId getGroupByName(IdString name) const;
+ IdString getGroupName(GroupId group) const;
+ std::vector<GroupId> getGroups() const;
+ std::vector<BelId> getGroupBels(GroupId group) const;
+ std::vector<WireId> getGroupWires(GroupId group) const;
+ std::vector<PipId> getGroupPips(GroupId group) const;
+ std::vector<GroupId> getGroupGroups(GroupId group) const;
+
+ // -------------------------------------------------
+
+ delay_t estimateDelay(WireId src, WireId dst) const;
+ delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const;
+ delay_t getDelayEpsilon() const { return 20; }
+ delay_t getRipupDelayPenalty() const { return 200; }
+ float getDelayNS(delay_t v) const { return v * 0.001; }
+
+ DelayInfo getDelayFromNS(float ns) const
+ {
+ DelayInfo del;
+ del.delay = delay_t(ns * 1000);
+ return del;
+ }
+
+ uint32_t getDelayChecksum(delay_t v) const { return v; }
+ bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const;
+
+ // -------------------------------------------------
+
+ bool pack();
+ bool place();
+ bool route();
+
+ // -------------------------------------------------
+
+ std::vector<GraphicElement> getDecalGraphics(DecalId decal) const;
+
+ DecalXY getBelDecal(BelId bel) const;
+ DecalXY getWireDecal(WireId wire) const;
+ DecalXY getPipDecal(PipId pip) const;
+ DecalXY getGroupDecal(GroupId group) const;
+
+ // -------------------------------------------------
+
+ // Get the delay through a cell from one port to another, returning false
+ // if no path exists
+ bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const;
+ // Get the port class, also setting clockDomain if applicable
+ TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const;
+ // Get the TimingClockingInfo of a port
+ TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const;
+ // Return true if a port is a net
+ bool isGlobalNet(const NetInfo *net) const;
+
+ // -------------------------------------------------
+
+ // Perform placement validity checks, returning false on failure (all
+ // implemented in arch_place.cc)
+
+ // Whether or not a given cell can be placed at a given Bel
+ // This is not intended for Bel type checks, but finer-grained constraints
+ // such as conflicting set/reset signals, etc
+ bool isValidBelForCell(CellInfo *cell, BelId bel) const;
+
+ // Return true whether all Bels at a given location are valid
+ bool isBelLocationValid(BelId bel) const;
+
+ // Helper function for above
+ bool logicCellsCompatible(const CellInfo **it, const size_t size) const;
+
+ // -------------------------------------------------
+ // Assign architecure-specific arguments to nets and cells, which must be
+ // called between packing or further
+ // netlist modifications, and validity checks
+ void assignArchInfo();
+ void assignCellInfo(CellInfo *cell);
+
+ // -------------------------------------------------
+ BelPin getIOBSharingPLLPin(BelId pll, IdString pll_pin) const
+ {
+ auto wire = getBelPinWire(pll, pll_pin);
+ for (auto src_bel : getWireBelPins(wire)) {
+ if (getBelType(src_bel.bel) == id_SB_IO && src_bel.pin == id_D_IN_0) {
+ return src_bel;
+ }
+ }
+ NPNR_ASSERT_FALSE("Expected PLL pin to share an output with an SB_IO D_IN_{0,1}");
+ }
+
+ float placer_constraintWeight = 10;
+};
+
+NEXTPNR_NAMESPACE_END
diff --git a/xc7/arch_place.cc b/xc7/arch_place.cc
new file mode 100644
index 0000000..7d1e32e
--- /dev/null
+++ b/xc7/arch_place.cc
@@ -0,0 +1,78 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "cells.h"
+#include "nextpnr.h"
+#include "util.h"
+
+#include <boost/range/iterator_range.hpp>
+
+NEXTPNR_NAMESPACE_BEGIN
+
+bool Arch::logicCellsCompatible(const CellInfo **it, const size_t size) const
+{
+ // TODO: Check clock, clock-enable, and set-reset compatiility
+ return true;
+}
+
+bool Arch::isBelLocationValid(BelId bel) const
+{
+ if (getBelType(bel) == id("XC7_LC")) {
+ std::array<const CellInfo *, 4> bel_cells;
+ size_t num_cells = 0;
+ Loc bel_loc = getBelLocation(bel);
+ for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
+ CellInfo *ci_other = getBoundBelCell(bel_other);
+ if (ci_other != nullptr)
+ bel_cells[num_cells++] = ci_other;
+ }
+ return logicCellsCompatible(bel_cells.data(), num_cells);
+ } else {
+ CellInfo *ci = getBoundBelCell(bel);
+ if (ci == nullptr)
+ return true;
+ else
+ return isValidBelForCell(ci, bel);
+ }
+}
+
+bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
+{
+ if (cell->type == id("XC7_LC")) {
+ std::array<const CellInfo *, 4> bel_cells;
+ size_t num_cells = 0;
+
+ Loc bel_loc = getBelLocation(bel);
+ for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
+ CellInfo *ci_other = getBoundBelCell(bel_other);
+ if (ci_other != nullptr && bel_other != bel)
+ bel_cells[num_cells++] = ci_other;
+ }
+
+ bel_cells[num_cells++] = cell;
+ return logicCellsCompatible(bel_cells.data(), num_cells);
+ }
+ else {
+ return true;
+ }
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/xc7/arch_pybindings.cc b/xc7/arch_pybindings.cc
new file mode 100644
index 0000000..04d9d5d
--- /dev/null
+++ b/xc7/arch_pybindings.cc
@@ -0,0 +1,144 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef NO_PYTHON
+
+#include "arch_pybindings.h"
+#include "nextpnr.h"
+#include "pybindings.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+void arch_wrap_python()
+{
+ using namespace PythonConversion;
+ class_<ArchArgs>("ArchArgs").def_readwrite("type", &ArchArgs::type);
+
+ class_<BelId>("BelId").def_readwrite("index", &BelId::index);
+
+ class_<WireId>("WireId").def_readwrite("index", &WireId::index);
+
+ class_<PipId>("PipId").def_readwrite("index", &PipId::index);
+
+ class_<BelPin>("BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin);
+
+ auto arch_cls = class_<Arch, Arch *, bases<BaseCtx>, boost::noncopyable>("Arch", init<ArchArgs>());
+ auto ctx_cls = class_<Context, Context *, bases<Arch>, boost::noncopyable>("Context", no_init)
+ .def("checksum", &Context::checksum)
+ .def("pack", &Context::pack)
+ .def("place", &Context::place)
+ .def("route", &Context::route);
+
+ fn_wrapper_1a<Context, decltype(&Context::getBelType), &Context::getBelType, conv_to_str<IdString>,
+ conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelType");
+ fn_wrapper_1a<Context, decltype(&Context::checkBelAvail), &Context::checkBelAvail, pass_through<bool>,
+ conv_from_str<BelId>>::def_wrap(ctx_cls, "checkBelAvail");
+ fn_wrapper_1a<Context, decltype(&Context::getBelChecksum), &Context::getBelChecksum, pass_through<uint32_t>,
+ conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelChecksum");
+ fn_wrapper_3a_v<Context, decltype(&Context::bindBel), &Context::bindBel, conv_from_str<BelId>,
+ addr_and_unwrap<CellInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindBel");
+ fn_wrapper_1a_v<Context, decltype(&Context::unbindBel), &Context::unbindBel, conv_from_str<BelId>>::def_wrap(
+ ctx_cls, "unbindBel");
+ fn_wrapper_1a<Context, decltype(&Context::getBoundBelCell), &Context::getBoundBelCell, deref_and_wrap<CellInfo>,
+ conv_from_str<BelId>>::def_wrap(ctx_cls, "getBoundBelCell");
+ fn_wrapper_1a<Context, decltype(&Context::getConflictingBelCell), &Context::getConflictingBelCell,
+ deref_and_wrap<CellInfo>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell");
+ fn_wrapper_0a<Context, decltype(&Context::getBels), &Context::getBels, wrap_context<BelRange>>::def_wrap(ctx_cls,
+ "getBels");
+
+ fn_wrapper_2a<Context, decltype(&Context::getBelPinWire), &Context::getBelPinWire, conv_to_str<WireId>,
+ conv_from_str<BelId>, conv_from_str<IdString>>::def_wrap(ctx_cls, "getBelPinWire");
+ fn_wrapper_1a<Context, decltype(&Context::getWireBelPins), &Context::getWireBelPins, wrap_context<BelPinRange>,
+ conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireBelPins");
+
+ fn_wrapper_1a<Context, decltype(&Context::getWireChecksum), &Context::getWireChecksum, pass_through<uint32_t>,
+ conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireChecksum");
+ fn_wrapper_3a_v<Context, decltype(&Context::bindWire), &Context::bindWire, conv_from_str<WireId>,
+ addr_and_unwrap<NetInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindWire");
+ fn_wrapper_1a_v<Context, decltype(&Context::unbindWire), &Context::unbindWire, conv_from_str<WireId>>::def_wrap(
+ ctx_cls, "unbindWire");
+ fn_wrapper_1a<Context, decltype(&Context::checkWireAvail), &Context::checkWireAvail, pass_through<bool>,
+ conv_from_str<WireId>>::def_wrap(ctx_cls, "checkWireAvail");
+ fn_wrapper_1a<Context, decltype(&Context::getBoundWireNet), &Context::getBoundWireNet, deref_and_wrap<NetInfo>,
+ conv_from_str<WireId>>::def_wrap(ctx_cls, "getBoundWireNet");
+ fn_wrapper_1a<Context, decltype(&Context::getConflictingWireNet), &Context::getConflictingWireNet,
+ deref_and_wrap<NetInfo>, conv_from_str<WireId>>::def_wrap(ctx_cls, "getConflictingWireNet");
+
+ fn_wrapper_0a<Context, decltype(&Context::getWires), &Context::getWires, wrap_context<WireRange>>::def_wrap(
+ ctx_cls, "getWires");
+
+ fn_wrapper_0a<Context, decltype(&Context::getPips), &Context::getPips, wrap_context<AllPipRange>>::def_wrap(
+ ctx_cls, "getPips");
+ fn_wrapper_1a<Context, decltype(&Context::getPipChecksum), &Context::getPipChecksum, pass_through<uint32_t>,
+ conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipChecksum");
+ fn_wrapper_3a_v<Context, decltype(&Context::bindPip), &Context::bindPip, conv_from_str<PipId>,
+ addr_and_unwrap<NetInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindPip");
+ fn_wrapper_1a_v<Context, decltype(&Context::unbindPip), &Context::unbindPip, conv_from_str<PipId>>::def_wrap(
+ ctx_cls, "unbindPip");
+ fn_wrapper_1a<Context, decltype(&Context::checkPipAvail), &Context::checkPipAvail, pass_through<bool>,
+ conv_from_str<PipId>>::def_wrap(ctx_cls, "checkPipAvail");
+ fn_wrapper_1a<Context, decltype(&Context::getBoundPipNet), &Context::getBoundPipNet, deref_and_wrap<NetInfo>,
+ conv_from_str<PipId>>::def_wrap(ctx_cls, "getBoundPipNet");
+ fn_wrapper_1a<Context, decltype(&Context::getConflictingPipNet), &Context::getConflictingPipNet,
+ deref_and_wrap<NetInfo>, conv_from_str<PipId>>::def_wrap(ctx_cls, "getConflictingPipNet");
+
+ fn_wrapper_1a<Context, decltype(&Context::getPipsDownhill), &Context::getPipsDownhill, wrap_context<PipRange>,
+ conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsDownhill");
+ fn_wrapper_1a<Context, decltype(&Context::getPipsUphill), &Context::getPipsUphill, wrap_context<PipRange>,
+ conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsUphill");
+ fn_wrapper_1a<Context, decltype(&Context::getWireAliases), &Context::getWireAliases, wrap_context<PipRange>,
+ conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireAliases");
+
+ fn_wrapper_1a<Context, decltype(&Context::getPipSrcWire), &Context::getPipSrcWire, conv_to_str<WireId>,
+ conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipSrcWire");
+ fn_wrapper_1a<Context, decltype(&Context::getPipDstWire), &Context::getPipDstWire, conv_to_str<WireId>,
+ conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDstWire");
+ fn_wrapper_1a<Context, decltype(&Context::getPipDelay), &Context::getPipDelay, pass_through<DelayInfo>,
+ conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDelay");
+
+ fn_wrapper_1a<Context, decltype(&Context::getPackagePinBel), &Context::getPackagePinBel, conv_to_str<BelId>,
+ pass_through<std::string>>::def_wrap(ctx_cls, "getPackagePinBel");
+ fn_wrapper_1a<Context, decltype(&Context::getBelPackagePin), &Context::getBelPackagePin, pass_through<std::string>,
+ conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelPackagePin");
+
+ fn_wrapper_0a<Context, decltype(&Context::getChipName), &Context::getChipName, pass_through<std::string>>::def_wrap(
+ ctx_cls, "getChipName");
+ fn_wrapper_0a<Context, decltype(&Context::archId), &Context::archId, conv_to_str<IdString>>::def_wrap(ctx_cls,
+ "archId");
+
+ typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
+ typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
+
+ readonly_wrapper<Context, decltype(&Context::cells), &Context::cells, wrap_context<CellMap &>>::def_wrap(ctx_cls,
+ "cells");
+ readonly_wrapper<Context, decltype(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls,
+ "nets");
+ WRAP_RANGE(Bel, conv_to_str<BelId>);
+ WRAP_RANGE(Wire, conv_to_str<WireId>);
+ WRAP_RANGE(AllPip, conv_to_str<PipId>);
+ WRAP_RANGE(Pip, conv_to_str<PipId>);
+
+ WRAP_MAP_UPTR(CellMap, "IdCellMap");
+ WRAP_MAP_UPTR(NetMap, "IdNetMap");
+}
+
+NEXTPNR_NAMESPACE_END
+
+#endif // NO_PYTHON
diff --git a/xc7/arch_pybindings.h b/xc7/arch_pybindings.h
new file mode 100644
index 0000000..c2c67aa
--- /dev/null
+++ b/xc7/arch_pybindings.h
@@ -0,0 +1,69 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+#ifndef ARCH_PYBINDINGS_H
+#define ARCH_PYBINDINGS_H
+#ifndef NO_PYTHON
+
+#include "nextpnr.h"
+#include "pybindings.h"
+#include "pywrappers.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+namespace PythonConversion {
+
+template <> struct string_converter<BelId>
+{
+ BelId from_str(Context *ctx, std::string name) { return ctx->getBelByName(ctx->id(name)); }
+
+ std::string to_str(Context *ctx, BelId id)
+ {
+ if (id == BelId())
+ throw bad_wrap();
+ return ctx->getBelName(id).str(ctx);
+ }
+};
+
+template <> struct string_converter<WireId>
+{
+ WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); }
+
+ std::string to_str(Context *ctx, WireId id) { return ctx->getWireName(id).str(ctx); }
+};
+
+template <> struct string_converter<const WireId>
+{
+ WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); }
+
+ std::string to_str(Context *ctx, WireId id) { return ctx->getWireName(id).str(ctx); }
+};
+
+template <> struct string_converter<PipId>
+{
+ PipId from_str(Context *ctx, std::string name) { return ctx->getPipByName(ctx->id(name)); }
+
+ std::string to_str(Context *ctx, PipId id) { return ctx->getPipName(id).str(ctx); }
+};
+
+} // namespace PythonConversion
+
+NEXTPNR_NAMESPACE_END
+#endif
+#endif
diff --git a/xc7/archdefs.h b/xc7/archdefs.h
new file mode 100644
index 0000000..1c2a752
--- /dev/null
+++ b/xc7/archdefs.h
@@ -0,0 +1,201 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef NEXTPNR_H
+#error Include "archdefs.h" via "nextpnr.h" only.
+#endif
+
+#include "torc/Architecture.hpp"
+using namespace torc::architecture;
+using namespace torc::architecture::xilinx;
+
+NEXTPNR_NAMESPACE_BEGIN
+
+typedef int delay_t;
+
+struct DelayInfo
+{
+ delay_t delay = 0;
+
+ delay_t minRaiseDelay() const { return delay; }
+ delay_t maxRaiseDelay() const { return delay; }
+
+ delay_t minFallDelay() const { return delay; }
+ delay_t maxFallDelay() const { return delay; }
+
+ delay_t minDelay() const { return delay; }
+ delay_t maxDelay() const { return delay; }
+
+ DelayInfo operator+(const DelayInfo &other) const
+ {
+ DelayInfo ret;
+ ret.delay = this->delay + other.delay;
+ return ret;
+ }
+};
+
+// -----------------------------------------------------------------------
+
+enum ConstIds
+{
+ ID_NONE
+#define X(t) , ID_##t
+#include "constids.inc"
+#undef X
+};
+
+#define X(t) static constexpr auto id_##t = IdString(ID_##t);
+#include "constids.inc"
+#undef X
+
+struct BelId
+{
+ int32_t index = -1;
+
+ bool operator==(const BelId &other) const { return index == other.index; }
+ bool operator!=(const BelId &other) const { return index != other.index; }
+ bool operator<(const BelId &other) const { return index < other.index; }
+};
+
+struct WireId
+{
+ int32_t index = -1;
+
+ bool operator==(const WireId &other) const { return index == other.index; }
+ bool operator!=(const WireId &other) const { return index != other.index; }
+ bool operator<(const WireId &other) const { return index < other.index; }
+};
+
+struct PipId
+{
+ int32_t index = -1;
+
+ bool operator==(const PipId &other) const { return index == other.index; }
+ bool operator!=(const PipId &other) const { return index != other.index; }
+ bool operator<(const PipId &other) const { return index < other.index; }
+};
+
+struct GroupId
+{
+ enum : int8_t
+ {
+ TYPE_NONE,
+ TYPE_FRAME,
+ TYPE_MAIN_SW,
+ TYPE_LOCAL_SW,
+ TYPE_LC0_SW,
+ TYPE_LC1_SW,
+ TYPE_LC2_SW,
+ TYPE_LC3_SW,
+ TYPE_LC4_SW,
+ TYPE_LC5_SW,
+ TYPE_LC6_SW,
+ TYPE_LC7_SW
+ } type = TYPE_NONE;
+ int8_t x = 0, y = 0;
+
+ bool operator==(const GroupId &other) const { return (type == other.type) && (x == other.x) && (y == other.y); }
+ bool operator!=(const GroupId &other) const { return (type != other.type) || (x != other.x) || (y == other.y); }
+};
+
+struct DecalId
+{
+ enum : int8_t
+ {
+ TYPE_NONE,
+ TYPE_BEL,
+ TYPE_WIRE,
+ TYPE_PIP,
+ TYPE_GROUP
+ } type = TYPE_NONE;
+ int32_t index = -1;
+ bool active = false;
+
+ bool operator==(const DecalId &other) const { return (type == other.type) && (index == other.index); }
+ bool operator!=(const DecalId &other) const { return (type != other.type) || (index != other.index); }
+};
+
+struct ArchNetInfo
+{
+ bool is_global = false;
+ bool is_reset = false, is_enable = false;
+};
+
+struct NetInfo;
+
+struct ArchCellInfo
+{
+ IdString belType;
+ union
+ {
+ struct
+ {
+ bool dffEnable;
+ bool carryEnable;
+ bool negClk;
+ int inputCount;
+ const NetInfo *clk, *cen, *sr;
+ } lcInfo;
+ };
+};
+
+NEXTPNR_NAMESPACE_END
+
+namespace std {
+template <> struct hash<NEXTPNR_NAMESPACE_PREFIX BelId>
+{
+ std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelId &bel) const noexcept { return hash<int>()(bel.index); }
+};
+
+template <> struct hash<NEXTPNR_NAMESPACE_PREFIX WireId>
+{
+ std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX WireId &wire) const noexcept
+ {
+ return hash<int>()(wire.index);
+ }
+};
+
+template <> struct hash<NEXTPNR_NAMESPACE_PREFIX PipId>
+{
+ std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept { return hash<int>()(pip.index); }
+};
+
+template <> struct hash<NEXTPNR_NAMESPACE_PREFIX GroupId>
+{
+ std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX GroupId &group) const noexcept
+ {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, hash<int>()(group.type));
+ boost::hash_combine(seed, hash<int>()(group.x));
+ boost::hash_combine(seed, hash<int>()(group.y));
+ return seed;
+ }
+};
+
+template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
+{
+ std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX DecalId &decal) const noexcept
+ {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, hash<int>()(decal.type));
+ boost::hash_combine(seed, hash<int>()(decal.index));
+ return seed;
+ }
+};
+} // namespace std
diff --git a/xc7/attosoc.pcf b/xc7/attosoc.pcf
new file mode 100644
index 0000000..fd14331
--- /dev/null
+++ b/xc7/attosoc.pcf
@@ -0,0 +1,8 @@
+COMP "led[0]" LOCATE = SITE "M14" LEVEL 1;
+COMP "led[1]" LOCATE = SITE "M15" LEVEL 1;
+COMP "led[2]" LOCATE = SITE "G14" LEVEL 1;
+COMP "led[3]" LOCATE = SITE "D18" LEVEL 1;
+COMP "clki" LOCATE = SITE "K17" LEVEL 1;
+NET "pll.clkin1" PERIOD = 8 nS ;
+PIN "clki_pin" = BEL "clki.PAD" PINNAME PAD;
+PIN "clki_pin" CLOCK_DEDICATED_ROUTE = FALSE;
diff --git a/xc7/attosoc.sh b/xc7/attosoc.sh
new file mode 100755
index 0000000..cb04bda
--- /dev/null
+++ b/xc7/attosoc.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+set -ex
+rm -f picorv32.v attosoc.v
+wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v
+wget https://raw.githubusercontent.com/SymbiFlow/prjtrellis/master/examples/picorv32_versa5g/attosoc.v
+ln -sf firmware_slow.hex firmware.hex
+yosys attosoc.ys
+set +e
+../nextpnr-xc7 --json attosoc.json --xdl attosoc.xdl --pcf attosoc.pcf --freq 125
+set -e
+xdl -xdl2ncd attosoc.xdl
+bitgen -w attosoc.ncd -g UnconstrainedPins:Allow
+trce attosoc.ncd -v 10
diff --git a/xc7/attosoc.ys b/xc7/attosoc.ys
new file mode 100644
index 0000000..7ec2517
--- /dev/null
+++ b/xc7/attosoc.ys
@@ -0,0 +1,55 @@
+read_verilog attosoc_top.v
+read_verilog attosoc.v
+read_verilog picorv32.v
+
+#synth_xilinx -top picorv32
+
+#begin:
+ read_verilog -lib +/xilinx/cells_sim.v
+ read_verilog -lib +/xilinx/cells_xtra.v
+# read_verilog -lib +/xilinx/brams_bb.v
+# read_verilog -lib +/xilinx/drams_bb.v
+ hierarchy -check -top top
+
+#flatten: (only if -flatten)
+ proc
+ flatten
+
+#coarse:
+ synth -run coarse
+
+#bram:
+# memory_bram -rules +/xilinx/brams.txt
+# techmap -map +/xilinx/brams_map.v
+#
+#dram:
+# memory_bram -rules +/xilinx/drams.txt
+# techmap -map +/xilinx/drams_map.v
+
+fine:
+ opt -fast -full
+ memory_map
+ dffsr2dff
+# dff2dffe
+ opt -full
+ techmap -map +/techmap.v #-map +/xilinx/arith_map.v
+ opt -fast
+
+map_luts:
+ abc -luts 2:2,3,6:5 #,10,20 [-dff]
+ clean
+
+map_cells:
+ techmap -map +/xilinx/cells_map.v
+ dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT
+ clean
+
+check:
+ hierarchy -check
+ stat
+ check -noinit
+
+#edif: (only if -edif)
+# write_edif <file-name>
+
+write_json attosoc.json
diff --git a/xc7/attosoc_sim.sh b/xc7/attosoc_sim.sh
new file mode 100755
index 0000000..61d668a
--- /dev/null
+++ b/xc7/attosoc_sim.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+set -ex
+rm -f picorv32.v attosoc.v
+wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v
+wget https://raw.githubusercontent.com/SymbiFlow/prjtrellis/master/examples/picorv32_versa5g/attosoc.v
+ln -sf firmware_fast.hex firmware.hex
+yosys attosoc.ys
+set +e
+../nextpnr-xc7 --json attosoc.json --xdl attosoc.xdl --pcf attosoc.pcf --freq 125
+set -e
+xdl -xdl2ncd attosoc.xdl
+#bitgen -w attosoc.ncd -g UnconstrainedPins:Allow
+trce attosoc.ncd -v 10
+
+netgen -sim -ofmt vhdl attosoc.ncd -w attosoc_pnr.vhd
+ghdl -c -fexplicit --no-vital-checks --ieee=synopsys -Pxilinx-ise attosoc_tb.vhd attosoc_pnr.vhd -r testbench
diff --git a/xc7/attosoc_tb.vhd b/xc7/attosoc_tb.vhd
new file mode 100644
index 0000000..6899618
--- /dev/null
+++ b/xc7/attosoc_tb.vhd
@@ -0,0 +1,25 @@
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+
+entity testbench is
+end entity;
+architecture rtl of testbench is
+ signal clk : STD_LOGIC;
+ signal led : STD_LOGIC_VECTOR(3 downto 0);
+begin
+ process begin
+ clk <= '0';
+ wait for 4 ns;
+ clk <= '1';
+ wait for 4 ns;
+ end process;
+
+ uut: entity work.name port map(clki_PAD_PAD => clk, led_0_OUTBUF_OUT => led(0), led_1_OUTBUF_OUT => led(1), led_2_OUTBUF_OUT => led(2), led_3_OUTBUF_OUT => led(3));
+
+process
+begin
+report "led = " & std_logic'image(led(3)) & std_logic'image(led(2)) & std_logic'image(led(1)) & std_logic'image(led(0));
+wait on led;
+end process;
+
+end rtl;
diff --git a/xc7/attosoc_top.v b/xc7/attosoc_top.v
new file mode 100644
index 0000000..bbf75a0
--- /dev/null
+++ b/xc7/attosoc_top.v
@@ -0,0 +1,24 @@
+module top (
+ input clki,
+ output [3:0] led
+);
+
+ (* keep *)
+ wire led_unused;
+
+ wire clk;
+ BUFGCTRL clk_gb (
+ .I0(clki),
+ .CE0(1'b1),
+ .CE1(1'b0),
+ .S0(1'b1),
+ .S1(1'b0),
+ .IGNORE0(1'b0),
+ .IGNORE1(1'b0),
+ .O(clk)
+ );
+
+ attosoc soc(.clk(clk), .led({led_unused, led}));
+
+endmodule
+
diff --git a/xc7/blinky.pcf b/xc7/blinky.pcf
new file mode 100644
index 0000000..1b85ac5
--- /dev/null
+++ b/xc7/blinky.pcf
@@ -0,0 +1,9 @@
+COMP "led0" LOCATE = SITE "M14" LEVEL 1;
+COMP "led1" LOCATE = SITE "M15" LEVEL 1;
+COMP "led2" LOCATE = SITE "G14" LEVEL 1;
+COMP "led3" LOCATE = SITE "D18" LEVEL 1;
+COMP "clki" LOCATE = SITE "K17" LEVEL 1;
+COMP "clk_gb" LOCATE = SITE "BUFGCTRL_X0Y31" LEVEL 1;
+NET "clki" PERIOD = 8 nS ;
+PIN "clki_pin" = BEL "clki.PAD" PINNAME PAD;
+PIN "clki_pin" CLOCK_DEDICATED_ROUTE = FALSE;
diff --git a/xc7/blinky.proj b/xc7/blinky.proj
new file mode 100644
index 0000000..f5bb9f8
--- /dev/null
+++ b/xc7/blinky.proj
@@ -0,0 +1,15 @@
+{
+ "project": {
+ "version": "1",
+ "name": "blinky",
+ "arch": {
+ "name": "ice40",
+ "type": "hx1k",
+ "package": "tq144"
+ },
+ "input": {
+ "json": "blinky.json",
+ "pcf": "blinky.pcf"
+ }
+ }
+}
diff --git a/xc7/blinky.sh b/xc7/blinky.sh
new file mode 100755
index 0000000..6bc6857
--- /dev/null
+++ b/xc7/blinky.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+set -ex
+yosys blinky.ys
+../nextpnr-xc7 --json blinky.json --pcf blinky.pcf --xdl blinky.xdl --freq 125
+xdl -xdl2ncd blinky.xdl
+bitgen -w blinky.ncd -g UnconstrainedPins:Allow
diff --git a/xc7/blinky.v b/xc7/blinky.v
new file mode 100644
index 0000000..692c7ab
--- /dev/null
+++ b/xc7/blinky.v
@@ -0,0 +1,32 @@
+module blinky (
+ input clki,
+ output led0,
+ output led1,
+ output led2,
+ output led3
+);
+ wire clk;
+ BUFGCTRL clk_gb (
+ .I0(clki),
+ .CE0(1'b1),
+ .CE1(1'b0),
+ .S0(1'b1),
+ .S1(1'b0),
+ .IGNORE0(1'b0),
+ .IGNORE1(1'b0),
+ .O(clk)
+ );
+
+ localparam BITS = 4;
+ parameter LOG2DELAY = 23;
+
+ reg [BITS+LOG2DELAY-1:0] counter = 0;
+ reg [BITS-1:0] outcnt;
+
+ always @(posedge clk) begin
+ counter <= counter + 1;
+ outcnt <= counter >> LOG2DELAY;
+ end
+
+ assign {led0, led1, led2, led3} = outcnt ^ (outcnt >> 1);
+endmodule
diff --git a/xc7/blinky.ys b/xc7/blinky.ys
new file mode 100644
index 0000000..090b0ab
--- /dev/null
+++ b/xc7/blinky.ys
@@ -0,0 +1,53 @@
+read_verilog blinky.v
+
+#synth_xilinx -top blinky
+
+#begin:
+ read_verilog -lib +/xilinx/cells_sim.v
+ read_verilog -lib +/xilinx/cells_xtra.v
+# read_verilog -lib +/xilinx/brams_bb.v
+# read_verilog -lib +/xilinx/drams_bb.v
+ hierarchy -check -top blinky
+
+#flatten: (only if -flatten)
+ proc
+ flatten
+
+#coarse:
+ synth -run coarse
+
+#bram:
+# memory_bram -rules +/xilinx/brams.txt
+# techmap -map +/xilinx/brams_map.v
+#
+#dram:
+# memory_bram -rules +/xilinx/drams.txt
+# techmap -map +/xilinx/drams_map.v
+
+fine:
+ opt -fast -full
+ memory_map
+ dffsr2dff
+# dff2dffe
+ opt -full
+ techmap -map +/techmap.v #-map +/xilinx/arith_map.v
+ opt -fast
+
+map_luts:
+ abc -luts 2:2,3,6:5 #,10,20 [-dff]
+ clean
+
+map_cells:
+ techmap -map +/xilinx/cells_map.v
+ dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT
+ clean
+
+check:
+ hierarchy -check
+ stat
+ check -noinit
+
+#edif: (only if -edif)
+# write_edif <file-name>
+
+write_json blinky.json
diff --git a/xc7/blinky_sim.sh b/xc7/blinky_sim.sh
new file mode 100755
index 0000000..e353a40
--- /dev/null
+++ b/xc7/blinky_sim.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+set -ex
+yosys blinky_sim.ys
+../nextpnr-xc7 --json blinky.json --pcf blinky.pcf --xdl blinky.xdl --freq 125
+xdl -xdl2ncd blinky.xdl
+trce blinky.ncd -v 10
+netgen -sim -ofmt vhdl blinky.ncd -w blinky_pnr.vhd
+ghdl -c -fexplicit --no-vital-checks --ieee=synopsys -Pxilinx-ise blinky_tb.vhd blinky_pnr.vhd -r testbench
diff --git a/xc7/blinky_sim.ys b/xc7/blinky_sim.ys
new file mode 100644
index 0000000..732e1e3
--- /dev/null
+++ b/xc7/blinky_sim.ys
@@ -0,0 +1,54 @@
+read_verilog blinky.v
+chparam -set LOG2DELAY 0
+
+#synth_xilinx -top blinky
+
+#begin:
+ read_verilog -lib +/xilinx/cells_sim.v
+ read_verilog -lib +/xilinx/cells_xtra.v
+# read_verilog -lib +/xilinx/brams_bb.v
+# read_verilog -lib +/xilinx/drams_bb.v
+ hierarchy -check -top blinky
+
+#flatten: (only if -flatten)
+ proc
+ flatten
+
+#coarse:
+ synth -run coarse
+
+#bram:
+# memory_bram -rules +/xilinx/brams.txt
+# techmap -map +/xilinx/brams_map.v
+#
+#dram:
+# memory_bram -rules +/xilinx/drams.txt
+# techmap -map +/xilinx/drams_map.v
+
+fine:
+ opt -fast -full
+ memory_map
+ dffsr2dff
+# dff2dffe
+ opt -full
+ techmap -map +/techmap.v #-map +/xilinx/arith_map.v
+ opt -fast
+
+map_luts:
+ abc -luts 2:2,3,6:5 #,10,20 [-dff]
+ clean
+
+map_cells:
+ techmap -map +/xilinx/cells_map.v
+ dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT
+ clean
+
+check:
+ hierarchy -check
+ stat
+ check -noinit
+
+#edif: (only if -edif)
+# write_edif <file-name>
+
+write_json blinky.json
diff --git a/xc7/blinky_tb.vhd b/xc7/blinky_tb.vhd
new file mode 100644
index 0000000..29b5030
--- /dev/null
+++ b/xc7/blinky_tb.vhd
@@ -0,0 +1,25 @@
+library IEEE;
+use IEEE.STD_LOGIC_1164.ALL;
+
+entity testbench is
+end entity;
+architecture rtl of testbench is
+ signal clk : STD_LOGIC;
+ signal led : STD_LOGIC_VECTOR(3 downto 0);
+begin
+ process begin
+ clk <= '0';
+ wait for 4 ns;
+ clk <= '1';
+ wait for 4 ns;
+ end process;
+
+ uut: entity work.name port map(clki_PAD_PAD => clk, led0_OUTBUF_OUT => led(0), led1_OUTBUF_OUT => led(1), led2_OUTBUF_OUT => led(2), led3_OUTBUF_OUT => led(3));
+
+process
+begin
+report std_logic'image(led(3)) & std_logic'image(led(2)) & std_logic'image(led(1)) & std_logic'image(led(0));
+wait on led;
+end process;
+
+end rtl;
diff --git a/xc7/cells.cc b/xc7/cells.cc
new file mode 100644
index 0000000..601aacc
--- /dev/null
+++ b/xc7/cells.cc
@@ -0,0 +1,242 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "cells.h"
+#include "design_utils.h"
+#include "log.h"
+#include "util.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir)
+{
+ IdString id = ctx->id(name);
+ cell->ports[id] = PortInfo{id, nullptr, dir};
+}
+
+std::unique_ptr<CellInfo> create_xc7_cell(Context *ctx, IdString type, std::string name)
+{
+ static int auto_idx = 0;
+ std::unique_ptr<CellInfo> new_cell = std::unique_ptr<CellInfo>(new CellInfo());
+ if (name.empty()) {
+ new_cell->name = ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++));
+ } else {
+ new_cell->name = ctx->id(name);
+ }
+ new_cell->type = type;
+ if (type == ctx->id("XC7_LC")) {
+ new_cell->type = id_SLICE_LUT6;
+ new_cell->params[ctx->id("INIT")] = "0";
+ new_cell->params[ctx->id("NEG_CLK")] = "0";
+ new_cell->params[ctx->id("CARRY_ENABLE")] = "0";
+ new_cell->params[ctx->id("DFF_ENABLE")] = "0";
+ new_cell->params[ctx->id("CIN_CONST")] = "0";
+ new_cell->params[ctx->id("CIN_SET")] = "0";
+
+ add_port(ctx, new_cell.get(), "I1", PORT_IN);
+ add_port(ctx, new_cell.get(), "I2", PORT_IN);
+ add_port(ctx, new_cell.get(), "I3", PORT_IN);
+ add_port(ctx, new_cell.get(), "I4", PORT_IN);
+ add_port(ctx, new_cell.get(), "I5", PORT_IN);
+ add_port(ctx, new_cell.get(), "I6", PORT_IN);
+ add_port(ctx, new_cell.get(), "CIN", PORT_IN);
+
+ add_port(ctx, new_cell.get(), "CLK", PORT_IN);
+ add_port(ctx, new_cell.get(), "CE", PORT_IN);
+ add_port(ctx, new_cell.get(), "SR", PORT_IN);
+
+ add_port(ctx, new_cell.get(), "O", PORT_OUT);
+ add_port(ctx, new_cell.get(), "OQ", PORT_OUT);
+ add_port(ctx, new_cell.get(), "OMUX", PORT_OUT);
+ add_port(ctx, new_cell.get(), "COUT", PORT_OUT);
+ } else if (type == ctx->id("IOBUF")) {
+ if (ctx->args.type == ArchArgs::Z020)
+ new_cell->type = id_IOB33;
+ else
+ new_cell->type = id_IOB18;
+ add_port(ctx, new_cell.get(), "I", PORT_OUT);
+ add_port(ctx, new_cell.get(), "O", PORT_IN);
+ } else if (type == id_BUFGCTRL) {
+ add_port(ctx, new_cell.get(), "I0", PORT_IN);
+ add_port(ctx, new_cell.get(), "O", PORT_OUT);
+ } else {
+ log_error("unable to create XC7 cell of type %s\n", type.c_str(ctx));
+ }
+ return new_cell;
+}
+
+void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
+{
+ lc->params[ctx->id("INIT")] = lut->params[ctx->id("INIT")];
+ int i = 6;
+ if (get_net_or_empty(lut, id_I5))
+ replace_port(lut, id_I5, lc, ctx->id("I" + std::to_string(i--)));
+ if (get_net_or_empty(lut, id_I4))
+ replace_port(lut, id_I4, lc, ctx->id("I" + std::to_string(i--)));
+ if (get_net_or_empty(lut, id_I3))
+ replace_port(lut, id_I3, lc, ctx->id("I" + std::to_string(i--)));
+ if (get_net_or_empty(lut, id_I2))
+ replace_port(lut, id_I2, lc, ctx->id("I" + std::to_string(i--)));
+ if (get_net_or_empty(lut, id_I1))
+ replace_port(lut, id_I1, lc, ctx->id("I" + std::to_string(i--)));
+ replace_port(lut, ctx->id("I0"), lc, ctx->id("I" + std::to_string(i--)));
+ if (no_dff) {
+ replace_port(lut, id_O, lc, id_O);
+ lc->params[ctx->id("DFF_ENABLE")] = "0";
+ }
+ lc->params[ctx->id("LUT_NAME")] = lut->name.str(ctx);
+}
+
+void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut)
+{
+ lc->params[ctx->id("DFF_ENABLE")] = "1";
+ std::string config = dff->type.str(ctx).substr(2);
+ auto citer = config.begin();
+ replace_port(dff, ctx->id("C"), lc, id_CLK);
+
+ if (citer != config.end()) {
+ auto gnd_net = ctx->nets.at(ctx->id("$PACKER_GND_NET")).get();
+
+ if (*citer == 'S') {
+ citer++;
+ if (get_net_or_empty(dff, id_S) != gnd_net) {
+ lc->params[id_SR] = "SRHIGH";
+ replace_port(dff, id_S, lc, id_SR);
+ }
+ else
+ disconnect_port(ctx, dff, id_S);
+ lc->params[ctx->id("SYNC_ATTR")] = "SYNC";
+ } else if (*citer == 'R') {
+ citer++;
+ if (get_net_or_empty(dff, id_R) != gnd_net) {
+ lc->params[id_SR] = "SRLOW";
+ replace_port(dff, id_R, lc, id_SR);
+ }
+ else
+ disconnect_port(ctx, dff, id_R);
+ lc->params[ctx->id("SYNC_ATTR")] = "SYNC";
+ } else if (*citer == 'C') {
+ citer++;
+ if (get_net_or_empty(dff, id_CLR) != gnd_net) {
+ lc->params[id_SR] = "SRLOW";
+ replace_port(dff, id_CLR, lc, id_SR);
+ }
+ else
+ disconnect_port(ctx, dff, id_CLR);
+ lc->params[ctx->id("SYNC_ATTR")] = "ASYNC";
+ } else {
+ NPNR_ASSERT(*citer == 'P');
+ citer++;
+ if (get_net_or_empty(dff, id_PRE) != gnd_net) {
+ lc->params[id_SR] = "SRHIGH";
+ replace_port(dff, id_PRE, lc, id_SR);
+ }
+ else
+ disconnect_port(ctx, dff, id_PRE);
+ lc->params[ctx->id("SYNC_ATTR")] = "ASYNC";
+ }
+ }
+
+ if (citer != config.end() && *citer == 'E') {
+ auto vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC_NET")).get();
+
+ ++citer;
+ if (get_net_or_empty(dff, ctx->id("CE")) != vcc_net)
+ replace_port(dff, ctx->id("CE"), lc, ctx->id("CE"));
+ else
+ disconnect_port(ctx, dff, ctx->id("CE"));
+ }
+
+ NPNR_ASSERT(citer == config.end());
+
+ if (pass_thru_lut) {
+ lc->params[ctx->id("INIT")] = "2";
+ replace_port(dff, ctx->id("D"), lc, id_I1);
+ }
+
+ replace_port(dff, ctx->id("Q"), lc, id_OQ);
+
+ auto it = dff->params.find(ctx->id("INIT"));
+ if (it != dff->params.end())
+ lc->params[ctx->id("FFINIT")] = it->second == "1" ? "INIT1" : "INIT0";
+}
+
+void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio)
+{
+ if (nxio->type == ctx->id("$nextpnr_ibuf")) {
+ sbio->params[ctx->id("PIN_TYPE")] = "1";
+ auto pu_attr = nxio->attrs.find(ctx->id("PULLUP"));
+ if (pu_attr != nxio->attrs.end())
+ sbio->params[ctx->id("PULLUP")] = pu_attr->second;
+ replace_port(nxio, id_O, sbio, id_I);
+ } else if (nxio->type == ctx->id("$nextpnr_obuf")) {
+ sbio->params[ctx->id("PIN_TYPE")] = "25";
+ replace_port(nxio, id_I, sbio, id_O);
+ } else if (nxio->type == ctx->id("$nextpnr_iobuf")) {
+ // N.B. tristate will be dealt with below
+ sbio->params[ctx->id("PIN_TYPE")] = "25";
+ replace_port(nxio, id_I, sbio, id_O);
+ replace_port(nxio, id_O, sbio, id_I);
+ } else {
+ NPNR_ASSERT(false);
+ }
+ NetInfo *donet = sbio->ports.at(id_O).net;
+ CellInfo *tbuf = net_driven_by(
+ ctx, donet, [](const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("$_TBUF_"); },
+ ctx->id("Y"));
+ if (tbuf) {
+ sbio->params[ctx->id("PIN_TYPE")] = "41";
+ replace_port(tbuf, ctx->id("A"), sbio, id_O);
+ replace_port(tbuf, ctx->id("E"), sbio, ctx->id("OUTPUT_ENABLE"));
+ ctx->nets.erase(donet->name);
+ if (!donet->users.empty())
+ log_error("unsupported tristate IO pattern for IO buffer '%s', "
+ "instantiate SB_IO manually to ensure correct behaviour\n",
+ nxio->name.c_str(ctx));
+ ctx->cells.erase(tbuf->name);
+ }
+}
+
+bool is_clock_port(const BaseCtx *ctx, const PortRef &port)
+{
+ if (port.cell == nullptr)
+ return false;
+ NPNR_ASSERT("TODO");
+ return false;
+}
+
+bool is_reset_port(const BaseCtx *ctx, const PortRef &port)
+{
+ if (port.cell == nullptr)
+ return false;
+ NPNR_ASSERT("TODO");
+ return false;
+}
+
+bool is_enable_port(const BaseCtx *ctx, const PortRef &port)
+{
+ if (port.cell == nullptr)
+ return false;
+ NPNR_ASSERT("TODO");
+ return false;
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/xc7/cells.h b/xc7/cells.h
new file mode 100644
index 0000000..c5956eb
--- /dev/null
+++ b/xc7/cells.h
@@ -0,0 +1,110 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "nextpnr.h"
+
+#ifndef ICE40_CELLS_H
+#define ICE40_CELLS_H
+
+NEXTPNR_NAMESPACE_BEGIN
+
+// Create a standard xc7 cell and return it
+// Name will be automatically assigned if not specified
+std::unique_ptr<CellInfo> create_xc7_cell(Context *ctx, IdString type, std::string name = "");
+
+// Return true if a cell is a LUT
+inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell)
+{
+ return cell->type == id_LUT1 || cell->type == id_LUT2 || cell->type == id_LUT3 || cell->type == id_LUT4 ||
+ cell->type == id_LUT5 || cell->type == id_LUT6;
+}
+
+// Return true if a cell is a flipflop
+inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell)
+{
+ return cell->type == id_FDRE || cell->type == id_FDSE || cell->type == id_FDCE || cell->type == id_FDPE;
+}
+
+inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_CARRY"); }
+
+inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("XC7_LC"); }
+
+// Return true if a cell is a SB_IO
+inline bool is_sb_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_IO"); }
+
+// Return true if a cell is a global buffer
+inline bool is_gbuf(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_BUFGCTRL; }
+
+// Return true if a cell is a RAM
+inline bool is_ram(const BaseCtx *ctx, const CellInfo *cell)
+{
+ return cell->type == ctx->id("SB_RAM40_4K") || cell->type == ctx->id("SB_RAM40_4KNR") ||
+ cell->type == ctx->id("SB_RAM40_4KNW") || cell->type == ctx->id("SB_RAM40_4KNRNW");
+}
+
+inline bool is_sb_lfosc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_LFOSC"); }
+
+inline bool is_sb_hfosc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_HFOSC"); }
+
+inline bool is_sb_spram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_SPRAM256KA"); }
+
+inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_MAC16"); }
+
+inline bool is_sb_pll40(const BaseCtx *ctx, const CellInfo *cell)
+{
+ return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") ||
+ cell->type == ctx->id("SB_PLL40_2F_PAD") || cell->type == ctx->id("SB_PLL40_CORE") ||
+ cell->type == ctx->id("SB_PLL40_2F_CORE");
+}
+
+inline bool is_sb_pll40_pad(const BaseCtx *ctx, const CellInfo *cell)
+{
+ return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") ||
+ cell->type == ctx->id("SB_PLL40_2F_PAD");
+}
+
+uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell);
+
+// Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports
+// as needed. Set no_dff if a DFF is not being used, so that the output
+// can be reconnected
+void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff = true);
+
+// Convert a SB_DFFx primitive to (part of) an ICESTORM_LC, setting parameters
+// and reconnecting signals as necessary. If pass_thru_lut is True, the LUT will
+// be configured as pass through and D connected to I0, otherwise D will be
+// ignored
+void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut = false);
+
+// Convert a nextpnr IO buffer to a SB_IO
+void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio);
+
+// Return true if a port is a clock port
+bool is_clock_port(const BaseCtx *ctx, const PortRef &port);
+
+// Return true if a port is a reset port
+bool is_reset_port(const BaseCtx *ctx, const PortRef &port);
+
+// Return true if a port is a clock enable port
+bool is_enable_port(const BaseCtx *ctx, const PortRef &port);
+
+NEXTPNR_NAMESPACE_END
+
+#endif
diff --git a/xc7/chains.cc b/xc7/chains.cc
new file mode 100644
index 0000000..be1d676
--- /dev/null
+++ b/xc7/chains.cc
@@ -0,0 +1,288 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "chains.h"
+#include <algorithm>
+#include <vector>
+#include "cells.h"
+#include "design_utils.h"
+#include "log.h"
+#include "place_common.h"
+#include "util.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+struct CellChain
+{
+ std::vector<CellInfo *> cells;
+};
+
+// Generic chain finder
+template <typename F1, typename F2, typename F3>
+std::vector<CellChain> find_chains(const Context *ctx, F1 cell_type_predicate, F2 get_previous, F3 get_next,
+ size_t min_length = 2)
+{
+ std::set<IdString> chained;
+ std::vector<CellChain> chains;
+ for (auto cell : sorted(ctx->cells)) {
+ if (chained.find(cell.first) != chained.end())
+ continue;
+ CellInfo *ci = cell.second;
+ if (cell_type_predicate(ctx, ci)) {
+ CellInfo *start = ci;
+ CellInfo *prev_start = ci;
+ while (prev_start != nullptr) {
+ start = prev_start;
+ prev_start = get_previous(ctx, start);
+ }
+ CellChain chain;
+ CellInfo *end = start;
+ while (end != nullptr) {
+ chain.cells.push_back(end);
+ end = get_next(ctx, end);
+ }
+ if (chain.cells.size() >= min_length) {
+ chains.push_back(chain);
+ for (auto c : chain.cells)
+ chained.insert(c->name);
+ }
+ }
+ }
+ return chains;
+}
+
+class ChainConstrainer
+{
+ private:
+ Context *ctx;
+ // Split a carry chain into multiple legal chains
+ std::vector<CellChain> split_carry_chain(CellChain &carryc)
+ {
+ bool start_of_chain = true;
+ std::vector<CellChain> chains;
+ std::vector<const CellInfo *> tile;
+ const int max_length = (ctx->chip_info->height - 2) * 8 - 2;
+ auto curr_cell = carryc.cells.begin();
+ while (curr_cell != carryc.cells.end()) {
+ CellInfo *cell = *curr_cell;
+ if (tile.size() >= 8) {
+ tile.clear();
+ }
+ if (start_of_chain) {
+ tile.clear();
+ chains.emplace_back();
+ start_of_chain = false;
+ if (cell->ports.at(ctx->id("CIN")).net) {
+ // CIN is not constant and not part of a chain. Must feed in from fabric
+ CellInfo *feedin = make_carry_feed_in(cell, cell->ports.at(ctx->id("CIN")));
+ chains.back().cells.push_back(feedin);
+ tile.push_back(feedin);
+ }
+ }
+ tile.push_back(cell);
+ chains.back().cells.push_back(cell);
+ bool split_chain = (!ctx->logicCellsCompatible(tile.data(), tile.size())) ||
+ (int(chains.back().cells.size()) > max_length);
+ if (split_chain) {
+ CellInfo *passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")));
+ tile.pop_back();
+ chains.back().cells.back() = passout;
+ start_of_chain = true;
+ } else {
+ NetInfo *carry_net = cell->ports.at(ctx->id("COUT")).net;
+ bool at_end = (curr_cell == carryc.cells.end() - 1);
+ if (carry_net != nullptr && (carry_net->users.size() > 1 || at_end)) {
+ if (carry_net->users.size() > 2 ||
+ (net_only_drives(ctx, carry_net, is_lc, ctx->id("I3"), false) !=
+ net_only_drives(ctx, carry_net, is_lc, ctx->id("CIN"), false)) ||
+ (at_end && !net_only_drives(ctx, carry_net, is_lc, ctx->id("I3"), true))) {
+ CellInfo *passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")));
+ chains.back().cells.push_back(passout);
+ tile.push_back(passout);
+ start_of_chain = true;
+ }
+ }
+ ++curr_cell;
+ }
+ }
+ return chains;
+ }
+
+ // Insert a logic cell to legalise a COUT->fabric connection
+ CellInfo *make_carry_pass_out(PortInfo &cout_port)
+ {
+ NPNR_ASSERT(cout_port.net != nullptr);
+ std::unique_ptr<CellInfo> lc = create_xc7_cell(ctx, ctx->id("ICESTORM_LC"));
+ lc->params[ctx->id("LUT_INIT")] = "65280"; // 0xff00: O = I3
+ lc->params[ctx->id("CARRY_ENABLE")] = "1";
+ lc->ports.at(ctx->id("O")).net = cout_port.net;
+ std::unique_ptr<NetInfo> co_i3_net(new NetInfo());
+ co_i3_net->name = ctx->id(lc->name.str(ctx) + "$I3");
+ co_i3_net->driver = cout_port.net->driver;
+ PortRef i3_r;
+ i3_r.port = ctx->id("I3");
+ i3_r.cell = lc.get();
+ co_i3_net->users.push_back(i3_r);
+ PortRef o_r;
+ o_r.port = ctx->id("O");
+ o_r.cell = lc.get();
+ cout_port.net->driver = o_r;
+ lc->ports.at(ctx->id("I3")).net = co_i3_net.get();
+ cout_port.net = co_i3_net.get();
+
+ IdString co_i3_name = co_i3_net->name;
+ NPNR_ASSERT(ctx->nets.find(co_i3_name) == ctx->nets.end());
+ ctx->nets[co_i3_name] = std::move(co_i3_net);
+ IdString name = lc->name;
+ ctx->assignCellInfo(lc.get());
+ ctx->cells[lc->name] = std::move(lc);
+ return ctx->cells[name].get();
+ }
+
+ // Insert a logic cell to legalise a CIN->fabric connection
+ CellInfo *make_carry_feed_in(CellInfo *cin_cell, PortInfo &cin_port)
+ {
+ NPNR_ASSERT(cin_port.net != nullptr);
+ std::unique_ptr<CellInfo> lc = create_xc7_cell(ctx, ctx->id("ICESTORM_LC"));
+ lc->params[ctx->id("CARRY_ENABLE")] = "1";
+ lc->params[ctx->id("CIN_CONST")] = "1";
+ lc->params[ctx->id("CIN_SET")] = "1";
+ lc->ports.at(ctx->id("I1")).net = cin_port.net;
+ cin_port.net->users.erase(std::remove_if(cin_port.net->users.begin(), cin_port.net->users.end(),
+ [cin_cell, cin_port](const PortRef &usr) {
+ return usr.cell == cin_cell && usr.port == cin_port.name;
+ }));
+
+ PortRef i1_ref;
+ i1_ref.cell = lc.get();
+ i1_ref.port = ctx->id("I1");
+ lc->ports.at(ctx->id("I1")).net->users.push_back(i1_ref);
+
+ std::unique_ptr<NetInfo> out_net(new NetInfo());
+ out_net->name = ctx->id(lc->name.str(ctx) + "$O");
+
+ PortRef drv_ref;
+ drv_ref.port = ctx->id("COUT");
+ drv_ref.cell = lc.get();
+ out_net->driver = drv_ref;
+ lc->ports.at(ctx->id("COUT")).net = out_net.get();
+
+ PortRef usr_ref;
+ usr_ref.port = cin_port.name;
+ usr_ref.cell = cin_cell;
+ out_net->users.push_back(usr_ref);
+ cin_cell->ports.at(cin_port.name).net = out_net.get();
+
+ IdString out_net_name = out_net->name;
+ NPNR_ASSERT(ctx->nets.find(out_net_name) == ctx->nets.end());
+ ctx->nets[out_net_name] = std::move(out_net);
+
+ IdString name = lc->name;
+ ctx->assignCellInfo(lc.get());
+ ctx->cells[lc->name] = std::move(lc);
+ return ctx->cells[name].get();
+ }
+
+ void process_carries()
+ {
+ std::vector<CellChain> carry_chains =
+ find_chains(ctx, [](const Context *ctx, const CellInfo *cell) { return is_lc(ctx, cell); },
+ [](const Context *ctx, const
+
+ CellInfo *cell) {
+ CellInfo *carry_prev =
+ net_driven_by(ctx, cell->ports.at(ctx->id("CIN")).net, is_lc, ctx->id("COUT"));
+ if (carry_prev != nullptr)
+ return carry_prev;
+ /*CellInfo *i3_prev = net_driven_by(ctx, cell->ports.at(ctx->id("I3")).net, is_lc,
+ ctx->id("COUT")); if (i3_prev != nullptr) return i3_prev;*/
+ return (CellInfo *)nullptr;
+ },
+ [](const Context *ctx, const CellInfo *cell) {
+ CellInfo *carry_next = net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_lc,
+ ctx->id("CIN"), false);
+ if (carry_next != nullptr)
+ return carry_next;
+ /*CellInfo *i3_next =
+ net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_lc, ctx->id("I3"),
+ false); if (i3_next != nullptr) return i3_next;*/
+ return (CellInfo *)nullptr;
+ });
+ std::unordered_set<IdString> chained;
+ for (auto &base_chain : carry_chains) {
+ for (auto c : base_chain.cells)
+ chained.insert(c->name);
+ }
+ // Any cells not in chains, but with carry enabled, must also be put in a single-carry chain
+ // for correct processing
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (chained.find(cell.first) == chained.end() && is_lc(ctx, ci) &&
+ bool_or_default(ci->params, ctx->id("CARRY_ENABLE"))) {
+ CellChain sChain;
+ sChain.cells.push_back(ci);
+ chained.insert(cell.first);
+ carry_chains.push_back(sChain);
+ }
+ }
+ std::vector<CellChain> all_chains;
+ // Chain splitting
+ for (auto &base_chain : carry_chains) {
+ if (ctx->verbose) {
+ log_info("Found carry chain: \n");
+ for (auto entry : base_chain.cells)
+ log_info(" %s\n", entry->name.c_str(ctx));
+ log_info("\n");
+ }
+ std::vector<CellChain> split_chains = split_carry_chain(base_chain);
+ for (auto &chain : split_chains) {
+ all_chains.push_back(chain);
+ }
+ }
+ // Actual chain placement
+ for (auto &chain : all_chains) {
+ if (ctx->verbose)
+ log_info("Placing carry chain starting at '%s'\n", chain.cells.front()->name.c_str(ctx));
+
+ // Place carry chain
+ chain.cells.at(0)->constr_abs_z = true;
+ chain.cells.at(0)->constr_z = 0;
+ for (int i = 1; i < int(chain.cells.size()); i++) {
+ chain.cells.at(i)->constr_x = 0;
+ chain.cells.at(i)->constr_y = (i / 8);
+ chain.cells.at(i)->constr_z = i % 8;
+ chain.cells.at(i)->constr_abs_z = true;
+ chain.cells.at(i)->constr_parent = chain.cells.at(0);
+ chain.cells.at(0)->constr_children.push_back(chain.cells.at(i));
+ }
+ }
+ }
+
+ public:
+ ChainConstrainer(Context *ctx) : ctx(ctx){};
+ void constrain_chains() { process_carries(); }
+};
+
+void constrain_chains(Context *ctx)
+{
+ log_info("Constraining chains...\n");
+ ChainConstrainer(ctx).constrain_chains();
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/xc7/chains.h b/xc7/chains.h
new file mode 100644
index 0000000..9811230
--- /dev/null
+++ b/xc7/chains.h
@@ -0,0 +1,27 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "nextpnr.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+// This finds chains, inserts LCs to legalise them as needed, and sets relative constraints as appropriate
+void constrain_chains(Context *ctx);
+
+NEXTPNR_NAMESPACE_END
diff --git a/xc7/chipdb.py b/xc7/chipdb.py
new file mode 100644
index 0000000..7bdf82f
--- /dev/null
+++ b/xc7/chipdb.py
@@ -0,0 +1,1280 @@
+#!/usr/bin/env python3
+
+import sys
+import re
+import textwrap
+import argparse
+
+parser = argparse.ArgumentParser(description="convert ICE40 chip database")
+parser.add_argument("filename", type=str, help="chipdb input filename")
+parser.add_argument("-p", "--constids", type=str, help="path to constids.inc")
+parser.add_argument("-g", "--gfxh", type=str, help="path to gfx.h")
+parser.add_argument("--fast", type=str, help="path to timing data for fast part")
+parser.add_argument("--slow", type=str, help="path to timing data for slow part")
+args = parser.parse_args()
+
+dev_name = None
+dev_width = None
+dev_height = None
+num_wires = None
+
+tiles = dict()
+
+wire_uphill = dict()
+wire_downhill = dict()
+pip_xy = dict()
+
+bel_name = list()
+bel_type = list()
+bel_pos = list()
+bel_wires = list()
+
+switches = list()
+
+ierens = list()
+
+extra_cells = dict()
+extra_cell_config = dict()
+packages = list()
+
+wire_belports = dict()
+
+wire_names = dict()
+wire_names_r = dict()
+wire_xy = dict()
+
+cbit_re = re.compile(r'B(\d+)\[(\d+)\]')
+
+constids = dict()
+tiletypes = dict()
+wiretypes = dict()
+
+gfx_wire_ids = dict()
+wire_segments = dict()
+
+fast_timings = None
+slow_timings = None
+
+with open(args.constids) as f:
+ for line in f:
+ if line.startswith("//"):
+ continue
+ line = line.replace("(", " ")
+ line = line.replace(")", " ")
+ line = line.split()
+ if len(line) == 0:
+ continue
+ assert len(line) == 2
+ assert line[0] == "X"
+ idx = len(constids) + 1
+ constids[line[1]] = idx
+
+constids["PLL"] = constids["ICESTORM_PLL"]
+constids["WARMBOOT"] = constids["SB_WARMBOOT"]
+constids["MAC16"] = constids["ICESTORM_DSP"]
+constids["HFOSC"] = constids["ICESTORM_HFOSC"]
+constids["LFOSC"] = constids["ICESTORM_LFOSC"]
+constids["I2C"] = constids["SB_I2C"]
+constids["SPI"] = constids["SB_SPI"]
+constids["LEDDA_IP"] = constids["SB_LEDDA_IP"]
+constids["RGBA_DRV"] = constids["SB_RGBA_DRV"]
+constids["SPRAM"] = constids["ICESTORM_SPRAM"]
+
+with open(args.gfxh) as f:
+ state = 0
+ for line in f:
+ if state == 0 and line.startswith("enum GfxTileWireId"):
+ state = 1
+ elif state == 1 and line.startswith("};"):
+ state = 0
+ elif state == 1 and (line.startswith("{") or line.strip() == ""):
+ pass
+ elif state == 1:
+ idx = len(gfx_wire_ids)
+ name = line.strip().rstrip(",")
+ gfx_wire_ids[name] = idx
+
+def read_timings(filename):
+ db = dict()
+ with open(filename) as f:
+ cell = None
+ for line in f:
+ line = line.split()
+ if len(line) == 0:
+ continue
+ if line[0] == "CELL":
+ cell = line[1]
+ if line[0] == "IOPATH":
+ key = "%s.%s.%s" % (cell, line[1], line[2])
+ v1 = line[3].split(":")[2]
+ v2 = line[4].split(":")[2]
+ v1 = 0 if v1 == "*" else float(v1)
+ v2 = 0 if v2 == "*" else float(v2)
+ db[key] = max(v1, v2)
+ return db
+
+if args.fast is not None:
+ fast_timings = read_timings(args.fast)
+
+if args.slow is not None:
+ slow_timings = read_timings(args.slow)
+
+tiletypes["NONE"] = 0
+tiletypes["LOGIC"] = 1
+tiletypes["IO"] = 2
+tiletypes["RAMB"] = 3
+tiletypes["RAMT"] = 4
+tiletypes["DSP0"] = 5
+tiletypes["DSP1"] = 6
+tiletypes["DSP2"] = 7
+tiletypes["DSP3"] = 8
+tiletypes["IPCON"] = 9
+
+wiretypes["NONE"] = 0
+wiretypes["GLB2LOCAL"] = 1
+wiretypes["GLB_NETWK"] = 2
+wiretypes["LOCAL"] = 3
+wiretypes["LUTFF_IN"] = 4
+wiretypes["LUTFF_IN_LUT"] = 5
+wiretypes["LUTFF_LOUT"] = 6
+wiretypes["LUTFF_OUT"] = 7
+wiretypes["LUTFF_COUT"] = 8
+wiretypes["LUTFF_GLOBAL"] = 9
+wiretypes["CARRY_IN_MUX"] = 10
+wiretypes["SP4_V"] = 11
+wiretypes["SP4_H"] = 12
+wiretypes["SP12_V"] = 13
+wiretypes["SP12_H"] = 14
+
+def maj_wire_name(name):
+ if name[2].startswith("lutff_"):
+ return True
+ if name[2].startswith("io_"):
+ return True
+ if name[2].startswith("ram/"):
+ return True
+ if name[2].startswith("sp4_h_r_"):
+ return name[2] in ("sp4_h_r_0", "sp4_h_r_1", "sp4_h_r_2", "sp4_h_r_3", "sp4_h_r_4", "sp4_h_r_5",
+ "sp4_h_r_6", "sp4_h_r_7", "sp4_h_r_8", "sp4_h_r_9", "sp4_h_r_10", "sp4_h_r_11")
+ if name[2].startswith("sp4_v_b_"):
+ return name[2] in ("sp4_v_b_0", "sp4_v_b_1", "sp4_v_b_2", "sp4_v_b_3", "sp4_v_b_4", "sp4_v_b_5",
+ "sp4_v_b_6", "sp4_v_b_7", "sp4_v_b_8", "sp4_v_b_9", "sp4_v_b_10", "sp4_v_b_11")
+ if name[2].startswith("sp12_h_r_"):
+ return name[2] in ("sp12_h_r_0", "sp12_h_r_1")
+ if name[2].startswith("sp12_v_b_"):
+ return name[2] in ("sp12_v_b_0", "sp12_v_b_1")
+ return False
+
+def cmp_wire_names(newname, oldname):
+ if maj_wire_name(newname):
+ return True
+ if maj_wire_name(oldname):
+ return False
+
+ if newname[2].startswith("sp") and oldname[2].startswith("sp"):
+ m1 = re.match(r".*_(\d+)$", newname[2])
+ m2 = re.match(r".*_(\d+)$", oldname[2])
+ if m1 and m2:
+ idx1 = int(m1.group(1))
+ idx2 = int(m2.group(1))
+ if idx1 != idx2:
+ return idx1 < idx2
+
+ return newname < oldname
+
+def wire_type(name):
+ longname = name
+ name = name.split('/')
+
+ if name[0].startswith("X") and name[1].startswith("Y"):
+ name = name[2:]
+
+ if name[0].startswith("sp4_v_") or name[0].startswith("sp4_r_v_") or name[0].startswith("span4_vert_"):
+ return "SP4_V"
+
+ if name[0].startswith("sp4_h_") or name[0].startswith("span4_horz_"):
+ return "SP4_H"
+
+ if name[0].startswith("sp12_v_") or name[0].startswith("span12_vert_"):
+ return "SP12_V"
+
+ if name[0].startswith("sp12_h_") or name[0].startswith("span12_horz_"):
+ return "SP12_H"
+
+ if name[0].startswith("glb2local"):
+ return "GLB2LOCAL"
+
+ if name[0].startswith("glb_netwk_"):
+ return "GLB_NETWK"
+
+ if name[0].startswith("local_"):
+ return "LOCAL"
+
+ if name[0].startswith("lutff_"):
+ if name[1].startswith("in_"):
+ return "LUTFF_IN_LUT" if name[1].endswith("_lut") else "LUTFF_IN"
+
+ if name[1] == "lout":
+ return "LUTFF_LOUT"
+ if name[1] == "out":
+ return "LUTFF_OUT"
+ if name[1] == "cout":
+ return "LUTFF_COUT"
+
+ if name[0] == "ram":
+ if name[1].startswith("RADDR_"):
+ return "LUTFF_IN"
+ if name[1].startswith("WADDR_"):
+ return "LUTFF_IN"
+ if name[1].startswith("WDATA_"):
+ return "LUTFF_IN"
+ if name[1].startswith("MASK_"):
+ return "LUTFF_IN"
+ if name[1].startswith("RDATA_"):
+ return "LUTFF_OUT"
+ if name[1] in ("WCLK", "WCLKE", "WE", "RCLK", "RCLKE", "RE"):
+ return "LUTFF_GLOBAL"
+
+ if name[0].startswith("io_"):
+ if name[1].startswith("D_IN_") or name[1] == "OUT_ENB":
+ return "LUTFF_IN"
+ if name[1].startswith("D_OUT_"):
+ return "LUTFF_OUT"
+ if name[0] == "fabout":
+ return "LUTFF_IN"
+
+ if name[0] == "lutff_global" or name[0] == "io_global":
+ return "LUTFF_GLOBAL"
+
+ if name[0] == "carry_in_mux":
+ return "CARRY_IN_MUX"
+
+ if name[0] == "carry_in":
+ return "LUTFF_COUT"
+
+ if name[0].startswith("neigh_op_"):
+ return "NONE"
+
+ if name[0].startswith("padin_"):
+ return "NONE"
+
+ # print("No type for wire: %s (%s)" % (longname, name), file=sys.stderr)
+ # assert 0
+
+ return "NONE"
+
+def pipdelay(src_idx, dst_idx, db):
+ if db is None:
+ return 0
+
+ src = wire_names_r[src_idx]
+ dst = wire_names_r[dst_idx]
+ src_type = wire_type(src[2])
+ dst_type = wire_type(dst[2])
+
+ if dst[2].startswith("sp4_") or dst[2].startswith("span4_"):
+ if src[2].startswith("sp12_") or src[2].startswith("span12_"):
+ return db["Sp12to4.I.O"]
+
+ if src[2].startswith("span4_"):
+ return db["IoSpan4Mux.I.O"]
+
+ if dst[2].startswith("sp4_h_"):
+ return db["Span4Mux_h4.I.O"]
+ else:
+ return db["Span4Mux_v4.I.O"]
+
+ if dst[2].startswith("sp12_") or dst[2].startswith("span12_"):
+ if dst[2].startswith("sp12_h_"):
+ return db["Span12Mux_h12.I.O"]
+ else:
+ return db["Span12Mux_v12.I.O"]
+
+ if dst[2] in ("fabout", "clk"):
+ return 0 # FIXME?
+
+ if src[2].startswith("glb_netwk_") and dst[2].startswith("glb2local_"):
+ return 0 # FIXME?
+
+ if dst[2] == "carry_in_mux":
+ return db["ICE_CARRY_IN_MUX.carryinitin.carryinitout"]
+
+ if dst[2] in ("lutff_global/clk", "io_global/inclk", "io_global/outclk", "ram/RCLK", "ram/WCLK"):
+ return db["ClkMux.I.O"]
+
+ if dst[2] in ("lutff_global/s_r", "io_global/latch", "ram/RE", "ram/WE"):
+ return db["SRMux.I.O"]
+
+ if dst[2] in ("lutff_global/cen", "io_global/cen", "ram/RCLKE", "ram/WCLKE"):
+ return db["CEMux.I.O"]
+
+ if dst[2].startswith("local_"):
+ return db["LocalMux.I.O"]
+
+ if src[2].startswith("local_") and dst[2] in ("io_0/D_OUT_0", "io_0/D_OUT_1", "io_0/OUT_ENB", "io_1/D_OUT_0", "io_1/D_OUT_1", "io_1/OUT_ENB"):
+ return db["IoInMux.I.O"]
+
+ if re.match(r"lutff_\d+/in_\d+$", dst[2]):
+ return db["InMux.I.O"]
+
+ if re.match(r"lutff_\d+/in_\d+_lut", dst[2]):
+ return 0
+
+ if re.match(r"ram/(MASK|RADDR|WADDR|WDATA)_", dst[2]):
+ return db["InMux.I.O"]
+
+ if re.match(r"lutff_\d+/out", dst[2]):
+ if re.match(r"lutff_\d+/in_0", src[2]):
+ return db["LogicCell40.in0.lcout"]
+ if re.match(r"lutff_\d+/in_1", src[2]):
+ return db["LogicCell40.in1.lcout"]
+ if re.match(r"lutff_\d+/in_2", src[2]):
+ return db["LogicCell40.in2.lcout"]
+ if re.match(r"lutff_\d+/in_3", src[2]):
+ return db["LogicCell40.in3.lcout"]
+
+ print(src, dst, src_idx, dst_idx, src_type, dst_type, file=sys.stderr)
+ assert 0
+
+def wiredelay(wire_idx, db):
+ if db is None:
+ return 0
+
+ wire = wire_names_r[wire_idx]
+ wtype = wire_type(wire[2])
+
+ # FIXME
+ return 0
+
+def init_tiletypes(device):
+ global num_tile_types, tile_sizes, tile_bits
+ if device == "5k":
+ num_tile_types = 10
+ else:
+ num_tile_types = 5
+ tile_sizes = {i: (0, 0) for i in range(num_tile_types)}
+ tile_bits = [[] for _ in range(num_tile_types)]
+
+with open(args.filename, "r") as f:
+ mode = None
+
+ for line in f:
+ line = line.split()
+
+ if len(line) == 0 or line[0] == "#":
+ continue
+
+ if line[0] == ".device":
+ dev_name = line[1]
+ init_tiletypes(dev_name)
+ dev_width = int(line[2])
+ dev_height = int(line[3])
+ num_wires = int(line[4])
+ continue
+
+ if line[0] == ".net":
+ mode = ("net", int(line[1]))
+ continue
+
+ if line[0] == ".buffer":
+ mode = ("buffer", int(line[3]), int(line[1]), int(line[2]))
+ switches.append((int(line[1]), int(line[2]), line[4:], -1))
+ continue
+
+ if line[0] == ".routing":
+ mode = ("routing", int(line[3]), int(line[1]), int(line[2]))
+ switches.append((int(line[1]), int(line[2]), line[4:], -1))
+ continue
+
+ if line[0] == ".io_tile":
+ tiles[(int(line[1]), int(line[2]))] = "io"
+ mode = None
+ continue
+
+ if line[0] == ".logic_tile":
+ tiles[(int(line[1]), int(line[2]))] = "logic"
+ mode = None
+ continue
+
+ if line[0] == ".ramb_tile":
+ tiles[(int(line[1]), int(line[2]))] = "ramb"
+ mode = None
+ continue
+
+ if line[0] == ".ramt_tile":
+ tiles[(int(line[1]), int(line[2]))] = "ramt"
+ mode = None
+ continue
+
+ if line[0] == ".dsp0_tile":
+ tiles[(int(line[1]), int(line[2]))] = "dsp0"
+ mode = None
+ continue
+
+ if line[0] == ".dsp1_tile":
+ tiles[(int(line[1]), int(line[2]))] = "dsp1"
+ mode = None
+ continue
+
+ if line[0] == ".dsp2_tile":
+ tiles[(int(line[1]), int(line[2]))] = "dsp2"
+ mode = None
+ continue
+
+ if line[0] == ".dsp3_tile":
+ tiles[(int(line[1]), int(line[2]))] = "dsp3"
+ mode = None
+ continue
+
+ if line[0] == ".ipcon_tile":
+ tiles[(int(line[1]), int(line[2]))] = "ipcon"
+ mode = None
+ continue
+
+ if line[0] == ".logic_tile_bits":
+ mode = ("bits", 1)
+ tile_sizes[1] = (int(line[1]), int(line[2]))
+ continue
+
+ if line[0] == ".io_tile_bits":
+ mode = ("bits", 2)
+ tile_sizes[2] = (int(line[1]), int(line[2]))
+ continue
+
+ if line[0] == ".ramb_tile_bits":
+ mode = ("bits", 3)
+ tile_sizes[3] = (int(line[1]), int(line[2]))
+ continue
+
+ if line[0] == ".ramt_tile_bits":
+ mode = ("bits", 4)
+ tile_sizes[4] = (int(line[1]), int(line[2]))
+ continue
+
+ if line[0] == ".dsp0_tile_bits":
+ mode = ("bits", 5)
+ tile_sizes[5] = (int(line[1]), int(line[2]))
+ continue
+
+ if line[0] == ".dsp1_tile_bits":
+ mode = ("bits", 6)
+ tile_sizes[6] = (int(line[1]), int(line[2]))
+ continue
+
+ if line[0] == ".dsp2_tile_bits":
+ mode = ("bits", 7)
+ tile_sizes[7] = (int(line[1]), int(line[2]))
+ continue
+
+ if line[0] == ".dsp3_tile_bits":
+ mode = ("bits", 8)
+ tile_sizes[8] = (int(line[1]), int(line[2]))
+ continue
+
+ if line[0] == ".ipcon_tile_bits":
+ mode = ("bits", 9)
+ tile_sizes[9] = (int(line[1]), int(line[2]))
+ continue
+
+ if line[0] == ".ieren":
+ mode = ("ieren",)
+ continue
+
+ if line[0] == ".pins":
+ mode = ("pins", line[1])
+ packages.append((line[1], []))
+ continue
+
+ if line[0] == ".extra_cell":
+ if len(line) >= 5:
+ mode = ("extra_cell", (line[4], int(line[1]), int(line[2]), int(line[3])))
+ elif line[3] == "WARMBOOT":
+ mode = ("extra_cell", (line[3], int(line[1]), int(line[2]), 0))
+ elif line[3] == "PLL":
+ mode = ("extra_cell", (line[3], int(line[1]), int(line[2]), 3))
+ else:
+ assert 0
+ extra_cells[mode[1]] = []
+ continue
+
+ if (line[0][0] == ".") or (mode is None):
+ mode = None
+ continue
+
+ if mode[0] == "net":
+ wname = (int(line[0]), int(line[1]), line[2])
+ wire_names[wname] = mode[1]
+ if (mode[1] not in wire_names_r) or cmp_wire_names(wname, wire_names_r[mode[1]]):
+ wire_names_r[mode[1]] = wname
+ if mode[1] not in wire_xy:
+ wire_xy[mode[1]] = list()
+ wire_xy[mode[1]].append((int(line[0]), int(line[1])))
+ if mode[1] not in wire_segments:
+ wire_segments[mode[1]] = dict()
+ if ("TILE_WIRE_" + wname[2].upper().replace("/", "_")) in gfx_wire_ids:
+ wire_segments[mode[1]][(wname[0], wname[1])] = wname[2]
+ continue
+
+ if mode[0] in ("buffer", "routing"):
+ wire_a = int(line[1])
+ wire_b = mode[1]
+ if wire_a not in wire_downhill:
+ wire_downhill[wire_a] = set()
+ if wire_b not in wire_uphill:
+ wire_uphill[wire_b] = set()
+ wire_downhill[wire_a].add(wire_b)
+ wire_uphill[wire_b].add(wire_a)
+ pip_xy[(wire_a, wire_b)] = (mode[2], mode[3], int(line[0], 2), len(switches) - 1, 0)
+ continue
+
+ if mode[0] == "bits":
+ name = line[0]
+ bits = []
+ for b in line[1:]:
+ m = cbit_re.match(b)
+ assert m
+ bits.append((int(m.group(1)), int(m.group(2))))
+ tile_bits[mode[1]].append((name, bits))
+ continue
+
+ if mode[0] == "ieren":
+ ierens.append(tuple([int(_) for _ in line]))
+ continue
+
+ if mode[0] == "pins":
+ packages[-1][1].append((line[0], int(line[1]), int(line[2]), int(line[3])))
+ continue
+
+ if mode[0] == "extra_cell":
+ if line[0] == "LOCKED":
+ extra_cells[mode[1]].append((("LOCKED_" + line[1]), (0, 0, "LOCKED")))
+ else:
+ extra_cells[mode[1]].append((line[0], (int(line[1]), int(line[2]), line[3])))
+ continue
+
+def add_wire(x, y, name):
+ global num_wires
+ wire_idx = num_wires
+ num_wires = num_wires + 1
+ wname = (x, y, name)
+ wire_names[wname] = wire_idx
+ wire_names_r[wire_idx] = wname
+ wire_segments[wire_idx] = dict()
+ if ("TILE_WIRE_" + wname[2].upper().replace("/", "_")) in gfx_wire_ids:
+ wire_segments[wire_idx][(wname[0], wname[1])] = wname[2]
+ return wire_idx
+
+def add_switch(x, y, bel=-1):
+ switches.append((x, y, [], bel))
+
+def add_pip(src, dst, flags=0):
+ x, y, _, _ = switches[-1]
+
+ if src not in wire_downhill:
+ wire_downhill[src] = set()
+ wire_downhill[src].add(dst)
+
+ if dst not in wire_uphill:
+ wire_uphill[dst] = set()
+ wire_uphill[dst].add(src)
+
+ pip_xy[(src, dst)] = (x, y, 0, len(switches) - 1, flags)
+
+# Add virtual padin wires
+for i in range(8):
+ add_wire(0, 0, "padin_%d" % i)
+
+def add_bel_input(bel, wire, port):
+ if wire not in wire_belports:
+ wire_belports[wire] = set()
+ wire_belports[wire].add((bel, port))
+ bel_wires[bel].append((constids[port], 0, wire))
+
+def add_bel_output(bel, wire, port):
+ if wire not in wire_belports:
+ wire_belports[wire] = set()
+ wire_belports[wire].add((bel, port))
+ bel_wires[bel].append((constids[port], 1, wire))
+
+def add_bel_lc(x, y, z):
+ bel = len(bel_name)
+ bel_name.append("X%d/Y%d/lc%d" % (x, y, z))
+ bel_type.append("ICESTORM_LC")
+ bel_pos.append((x, y, z))
+ bel_wires.append(list())
+
+ wire_cen = wire_names[(x, y, "lutff_global/cen")]
+ wire_clk = wire_names[(x, y, "lutff_global/clk")]
+ wire_s_r = wire_names[(x, y, "lutff_global/s_r")]
+
+ if z == 0:
+ wire_cin = wire_names[(x, y, "carry_in_mux")]
+ else:
+ wire_cin = wire_names[(x, y, "lutff_%d/cout" % (z-1))]
+
+ wire_in_0 = add_wire(x, y, "lutff_%d/in_0_lut" % z)
+ wire_in_1 = add_wire(x, y, "lutff_%d/in_1_lut" % z)
+ wire_in_2 = add_wire(x, y, "lutff_%d/in_2_lut" % z)
+ wire_in_3 = add_wire(x, y, "lutff_%d/in_3_lut" % z)
+
+ wire_out = wire_names[(x, y, "lutff_%d/out" % z)]
+ wire_cout = wire_names[(x, y, "lutff_%d/cout" % z)]
+ wire_lout = wire_names[(x, y, "lutff_%d/lout" % z)] if z < 7 else None
+
+ add_bel_input(bel, wire_cen, "CEN")
+ add_bel_input(bel, wire_clk, "CLK")
+ add_bel_input(bel, wire_s_r, "SR")
+ add_bel_input(bel, wire_cin, "CIN")
+
+ add_bel_input(bel, wire_in_0, "I0")
+ add_bel_input(bel, wire_in_1, "I1")
+ add_bel_input(bel, wire_in_2, "I2")
+ add_bel_input(bel, wire_in_3, "I3")
+
+ add_bel_output(bel, wire_out, "O")
+ add_bel_output(bel, wire_cout, "COUT")
+
+ if wire_lout is not None:
+ add_bel_output(bel, wire_lout, "LO")
+
+ # route-through LUTs
+ add_switch(x, y, bel)
+ add_pip(wire_in_0, wire_out, 1)
+ add_pip(wire_in_1, wire_out, 1)
+ add_pip(wire_in_2, wire_out, 1)
+ add_pip(wire_in_3, wire_out, 1)
+
+ # LUT permutation pips
+ for i in range(4):
+ add_switch(x, y, bel)
+ for j in range(4):
+ if (i == j) or ((i, j) == (1, 2)) or ((i, j) == (2, 1)):
+ flags = 0
+ else:
+ flags = 2
+ add_pip(wire_names[(x, y, "lutff_%d/in_%d" % (z, i))],
+ wire_names[(x, y, "lutff_%d/in_%d_lut" % (z, j))], flags)
+
+def add_bel_io(x, y, z):
+ bel = len(bel_name)
+ bel_name.append("X%d/Y%d/io%d" % (x, y, z))
+ bel_type.append("SB_IO")
+ bel_pos.append((x, y, z))
+ bel_wires.append(list())
+
+ wire_cen = wire_names[(x, y, "io_global/cen")]
+ wire_iclk = wire_names[(x, y, "io_global/inclk")]
+ wire_latch = wire_names[(x, y, "io_global/latch")]
+ wire_oclk = wire_names[(x, y, "io_global/outclk")]
+
+ wire_din_0 = wire_names[(x, y, "io_%d/D_IN_0" % z)]
+ wire_din_1 = wire_names[(x, y, "io_%d/D_IN_1" % z)]
+ wire_dout_0 = wire_names[(x, y, "io_%d/D_OUT_0" % z)]
+ wire_dout_1 = wire_names[(x, y, "io_%d/D_OUT_1" % z)]
+ wire_out_en = wire_names[(x, y, "io_%d/OUT_ENB" % z)]
+
+ add_bel_input(bel, wire_cen, "CLOCK_ENABLE")
+ add_bel_input(bel, wire_iclk, "INPUT_CLK")
+ add_bel_input(bel, wire_oclk, "OUTPUT_CLK")
+ add_bel_input(bel, wire_latch, "LATCH_INPUT_VALUE")
+
+ add_bel_output(bel, wire_din_0, "D_IN_0")
+ add_bel_output(bel, wire_din_1, "D_IN_1")
+
+ add_bel_input(bel, wire_dout_0, "D_OUT_0")
+ add_bel_input(bel, wire_dout_1, "D_OUT_1")
+ add_bel_input(bel, wire_out_en, "OUTPUT_ENABLE")
+
+def add_bel_ram(x, y):
+ bel = len(bel_name)
+ bel_name.append("X%d/Y%d/ram" % (x, y))
+ bel_type.append("ICESTORM_RAM")
+ bel_pos.append((x, y, 0))
+ bel_wires.append(list())
+
+ if (x, y, "ram/WE") in wire_names:
+ # iCE40 1K-style memories
+ y0, y1 = y, y+1
+ else:
+ # iCE40 8K-style memories
+ y1, y0 = y, y+1
+
+ for i in range(16):
+ add_bel_input (bel, wire_names[(x, y0 if i < 8 else y1, "ram/MASK_%d" % i)], "MASK_%d" % i)
+ add_bel_input (bel, wire_names[(x, y0 if i < 8 else y1, "ram/WDATA_%d" % i)], "WDATA_%d" % i)
+ add_bel_output(bel, wire_names[(x, y0 if i < 8 else y1, "ram/RDATA_%d" % i)], "RDATA_%d" % i)
+
+ for i in range(11):
+ add_bel_input(bel, wire_names[(x, y0, "ram/WADDR_%d" % i)], "WADDR_%d" % i)
+ add_bel_input(bel, wire_names[(x, y1, "ram/RADDR_%d" % i)], "RADDR_%d" % i)
+
+ add_bel_input(bel, wire_names[(x, y0, "ram/WCLK")], "WCLK")
+ add_bel_input(bel, wire_names[(x, y0, "ram/WCLKE")], "WCLKE")
+ add_bel_input(bel, wire_names[(x, y0, "ram/WE")], "WE")
+
+ add_bel_input(bel, wire_names[(x, y1, "ram/RCLK")], "RCLK")
+ add_bel_input(bel, wire_names[(x, y1, "ram/RCLKE")], "RCLKE")
+ add_bel_input(bel, wire_names[(x, y1, "ram/RE")], "RE")
+
+def add_bel_gb(xy, x, y, g):
+ if xy[0] != x or xy[1] != y:
+ return
+
+ bel = len(bel_name)
+ bel_name.append("X%d/Y%d/gb" % (x, y))
+ bel_type.append("SB_GB")
+ bel_pos.append((x, y, 2))
+ bel_wires.append(list())
+
+ add_bel_input(bel, wire_names[(x, y, "fabout")], "USER_SIGNAL_TO_GLOBAL_BUFFER")
+ add_bel_output(bel, wire_names[(x, y, "glb_netwk_%d" % g)], "GLOBAL_BUFFER_OUTPUT")
+
+def is_ec_wire(ec_entry):
+ return ec_entry[1] in wire_names
+
+def is_ec_output(ec_entry):
+ wirename = ec_entry[1][2]
+ if "O_" in wirename or "slf_op_" in wirename: return True
+ if "neigh_op_" in wirename: return True
+ if "glb_netwk_" in wirename: return True
+ return False
+
+def is_ec_pll_clock_output(ec, ec_entry):
+ return ec[0] == 'PLL' and ec_entry[0] in ('PLLOUT_A', 'PLLOUT_B')
+
+def add_bel_ec(ec):
+ ectype, x, y, z = ec
+ bel = len(bel_name)
+ extra_cell_config[bel] = []
+ bel_name.append("X%d/Y%d/%s_%d" % (x, y, ectype.lower(), z))
+ bel_type.append(ectype)
+ bel_pos.append((x, y, z))
+ bel_wires.append(list())
+ for entry in extra_cells[ec]:
+ if is_ec_wire(entry) and "glb_netwk_" not in entry[1][2]: # TODO: osc glb output conflicts with GB
+ if is_ec_output(entry):
+ add_bel_output(bel, wire_names[entry[1]], entry[0])
+ else:
+ add_bel_input(bel, wire_names[entry[1]], entry[0])
+ elif is_ec_pll_clock_output(ec, entry):
+ x, y, z = entry[1]
+ z = 'io_{}/D_IN_0'.format(z)
+ add_bel_output(bel, wire_names[(x, y, z)], entry[0])
+ else:
+ extra_cell_config[bel].append(entry)
+
+cell_timings = {}
+tmport_to_constids = {
+ "posedge:clk": "CLK",
+ "ce": "CEN",
+ "sr": "SR",
+ "in0": "I0",
+ "in1": "I1",
+ "in2": "I2",
+ "in3": "I3",
+ "carryin": "CIN",
+ "carryout": "COUT",
+ "lcout": "O",
+ "ltout": "LO",
+ "posedge:RCLK": "RCLK",
+ "posedge:WCLK": "WCLK",
+ "RCLKE": "RCLKE",
+ "RE": "RE",
+ "WCLKE": "WCLKE",
+ "WE": "WE",
+ "posedge:CLOCK": "CLOCK",
+ "posedge:SLEEP": "SLEEP",
+ "USERSIGNALTOGLOBALBUFFER": "USER_SIGNAL_TO_GLOBAL_BUFFER",
+ "GLOBALBUFFEROUTPUT": "GLOBAL_BUFFER_OUTPUT"
+}
+
+for i in range(16):
+ tmport_to_constids["RDATA[%d]" % i] = "RDATA_%d" % i
+ tmport_to_constids["WDATA[%d]" % i] = "WDATA_%d" % i
+ tmport_to_constids["MASK[%d]" % i] = "MASK_%d" % i
+ tmport_to_constids["DATAOUT[%d]" % i] = "DATAOUT_%d" % i
+
+for i in range(11):
+ tmport_to_constids["RADDR[%d]" % i] = "RADDR_%d" % i
+ tmport_to_constids["WADDR[%d]" % i] = "WADDR_%d" % i
+
+def add_cell_timingdata(bel_type, timing_cell, fast_db, slow_db):
+ timing_entries = []
+ database = slow_db if slow_db is not None else fast_db
+ for key in database.keys():
+ skey = key.split(".")
+ if skey[0] == timing_cell:
+ if skey[1] in tmport_to_constids and skey[2] in tmport_to_constids:
+ iport = tmport_to_constids[skey[1]]
+ oport = tmport_to_constids[skey[2]]
+ fastdel = fast_db[key] if fast_db is not None else 0
+ slowdel = slow_db[key] if slow_db is not None else 0
+ timing_entries.append((iport, oport, fastdel, slowdel))
+ cell_timings[bel_type] = timing_entries
+
+add_cell_timingdata("ICESTORM_LC", "LogicCell40", fast_timings, slow_timings)
+add_cell_timingdata("SB_GB", "ICE_GB", fast_timings, slow_timings)
+
+if dev_name != "384":
+ add_cell_timingdata("ICESTORM_RAM", "SB_RAM40_4K", fast_timings, slow_timings)
+if dev_name == "5k":
+ add_cell_timingdata("SPRAM", "SB_SPRAM256KA", fast_timings, slow_timings)
+
+
+for tile_xy, tile_type in sorted(tiles.items()):
+ if tile_type == "logic":
+ for i in range(8):
+ add_bel_lc(tile_xy[0], tile_xy[1], i)
+
+ if tile_type == "io":
+ for i in range(2):
+ add_bel_io(tile_xy[0], tile_xy[1], i)
+
+ if dev_name == "1k":
+ add_bel_gb(tile_xy, 7, 0, 0)
+ add_bel_gb(tile_xy, 7, 17, 1)
+ add_bel_gb(tile_xy, 13, 9, 2)
+ add_bel_gb(tile_xy, 0, 9, 3)
+ add_bel_gb(tile_xy, 6, 17, 4)
+ add_bel_gb(tile_xy, 6, 0, 5)
+ add_bel_gb(tile_xy, 0, 8, 6)
+ add_bel_gb(tile_xy, 13, 8, 7)
+ elif dev_name == "5k":
+ add_bel_gb(tile_xy, 13, 0, 0)
+ add_bel_gb(tile_xy, 13, 31, 1)
+ add_bel_gb(tile_xy, 19, 31, 2)
+ add_bel_gb(tile_xy, 6, 31, 3)
+ add_bel_gb(tile_xy, 12, 31, 4)
+ add_bel_gb(tile_xy, 12, 0, 5)
+ add_bel_gb(tile_xy, 6, 0, 6)
+ add_bel_gb(tile_xy, 19, 0, 7)
+ elif dev_name == "8k":
+ add_bel_gb(tile_xy, 33, 16, 7)
+ add_bel_gb(tile_xy, 0, 16, 6)
+ add_bel_gb(tile_xy, 17, 33, 1)
+ add_bel_gb(tile_xy, 17, 0, 0)
+ add_bel_gb(tile_xy, 0, 17, 3)
+ add_bel_gb(tile_xy, 33, 17, 2)
+ add_bel_gb(tile_xy, 16, 0, 5)
+ add_bel_gb(tile_xy, 16, 33, 4)
+ elif dev_name == "384":
+ add_bel_gb(tile_xy, 7, 4, 7)
+ add_bel_gb(tile_xy, 0, 4, 6)
+ add_bel_gb(tile_xy, 4, 9, 1)
+ add_bel_gb(tile_xy, 4, 0, 0)
+ add_bel_gb(tile_xy, 0, 5, 3)
+ add_bel_gb(tile_xy, 7, 5, 2)
+ add_bel_gb(tile_xy, 3, 0, 5)
+ add_bel_gb(tile_xy, 3, 9, 4)
+
+ if tile_type == "ramb":
+ add_bel_ram(tile_xy[0], tile_xy[1])
+
+ for ec in sorted(extra_cells.keys()):
+ if ec[1] == tile_xy[0] and ec[2] == tile_xy[1]:
+ add_bel_ec(ec)
+
+for ec in sorted(extra_cells.keys()):
+ if ec[1] in (0, dev_width - 1) and ec[2] in (0, dev_height - 1):
+ add_bel_ec(ec)
+
+class BinaryBlobAssembler:
+ def l(self, name, ltype = None, export = False):
+ if ltype is None:
+ print("label %s" % (name,))
+ else:
+ print("label %s %s" % (name, ltype))
+
+ def r(self, name, comment):
+ if comment is None:
+ print("ref %s" % (name,))
+ else:
+ print("ref %s %s" % (name, comment))
+
+ def s(self, s, comment):
+ assert "|" not in s
+ print("str |%s| %s" % (s, comment))
+
+ def u8(self, v, comment):
+ if comment is None:
+ print("u8 %d" % (v,))
+ else:
+ print("u8 %d %s" % (v, comment))
+
+ def u16(self, v, comment):
+ if comment is None:
+ print("u16 %d" % (v,))
+ else:
+ print("u16 %d %s" % (v, comment))
+
+ def u32(self, v, comment):
+ if comment is None:
+ print("u32 %d" % (v,))
+ else:
+ print("u32 %d %s" % (v, comment))
+
+ def pre(self, s):
+ print("pre %s" % s)
+
+ def post(self, s):
+ print("post %s" % s)
+
+ def push(self, name):
+ print("push %s" % name)
+
+ def pop(self):
+ print("pop")
+
+bba = BinaryBlobAssembler()
+bba.pre('#include "nextpnr.h"')
+bba.pre('NEXTPNR_NAMESPACE_BEGIN')
+bba.post('NEXTPNR_NAMESPACE_END')
+bba.push("chipdb_blob_%s" % dev_name)
+bba.r("chip_info_%s" % dev_name, "chip_info")
+
+for bel in range(len(bel_name)):
+ bba.l("bel_wires_%d" % bel, "BelWirePOD")
+ for data in sorted(bel_wires[bel]):
+ bba.u32(data[0], "port")
+ bba.u32(data[1], "type")
+ bba.u32(data[2], "wire_index")
+
+bba.l("bel_data_%s" % dev_name, "BelInfoPOD")
+for bel in range(len(bel_name)):
+ bba.s(bel_name[bel], "name")
+ bba.u32(constids[bel_type[bel]], "type")
+ bba.u32(len(bel_wires[bel]), "num_bel_wires")
+ bba.r("bel_wires_%d" % bel, "bel_wires")
+ bba.u8(bel_pos[bel][0], "x")
+ bba.u8(bel_pos[bel][1], "y")
+ bba.u8(bel_pos[bel][2], "z")
+ bba.u8(0, "padding")
+
+wireinfo = list()
+pipinfo = list()
+pipcache = dict()
+
+for wire in range(num_wires):
+ if wire in wire_uphill:
+ pips = list()
+ for src in wire_uphill[wire]:
+ if (src, wire) not in pipcache:
+ pipcache[(src, wire)] = len(pipinfo)
+ pi = dict()
+ pi["src"] = src
+ pi["dst"] = wire
+ pi["fast_delay"] = pipdelay(src, wire, fast_timings)
+ pi["slow_delay"] = pipdelay(src, wire, slow_timings)
+ pi["x"] = pip_xy[(src, wire)][0]
+ pi["y"] = pip_xy[(src, wire)][1]
+ pi["switch_mask"] = pip_xy[(src, wire)][2]
+ pi["switch_index"] = pip_xy[(src, wire)][3]
+ pi["flags"] = pip_xy[(src, wire)][4]
+ pipinfo.append(pi)
+ pips.append(pipcache[(src, wire)])
+ num_uphill = len(pips)
+ list_uphill = "wire%d_uppips" % wire
+ bba.l(list_uphill, "int32_t")
+ for p in pips:
+ bba.u32(p, None)
+ else:
+ num_uphill = 0
+ list_uphill = None
+
+ if wire in wire_downhill:
+ pips = list()
+ for dst in wire_downhill[wire]:
+ if (wire, dst) not in pipcache:
+ pipcache[(wire, dst)] = len(pipinfo)
+ pi = dict()
+ pi["src"] = wire
+ pi["dst"] = dst
+ pi["fast_delay"] = pipdelay(wire, dst, fast_timings)
+ pi["slow_delay"] = pipdelay(wire, dst, slow_timings)
+ pi["x"] = pip_xy[(wire, dst)][0]
+ pi["y"] = pip_xy[(wire, dst)][1]
+ pi["switch_mask"] = pip_xy[(wire, dst)][2]
+ pi["switch_index"] = pip_xy[(wire, dst)][3]
+ pi["flags"] = pip_xy[(wire, dst)][4]
+ pipinfo.append(pi)
+ pips.append(pipcache[(wire, dst)])
+ num_downhill = len(pips)
+ list_downhill = "wire%d_downpips" % wire
+ bba.l(list_downhill, "int32_t")
+ for p in pips:
+ bba.u32(p, None)
+ else:
+ num_downhill = 0
+ list_downhill = None
+
+ if wire in wire_belports:
+ num_bel_pins = len(wire_belports[wire])
+ bba.l("wire%d_bels" % wire, "BelPortPOD")
+ for belport in sorted(wire_belports[wire]):
+ bba.u32(belport[0], "bel_index")
+ bba.u32(constids[belport[1]], "port")
+ else:
+ num_bel_pins = 0
+
+ info = dict()
+ info["name"] = "X%d/Y%d/%s" % wire_names_r[wire]
+
+ info["num_uphill"] = num_uphill
+ info["list_uphill"] = list_uphill
+
+ info["num_downhill"] = num_downhill
+ info["list_downhill"] = list_downhill
+
+ info["num_bel_pins"] = num_bel_pins
+ info["list_bel_pins"] = ("wire%d_bels" % wire) if num_bel_pins > 0 else None
+
+ if wire in wire_xy:
+ avg_x, avg_y = 0, 0
+
+ for x, y in wire_xy[wire]:
+ avg_x += x
+ avg_y += y
+ avg_x /= len(wire_xy[wire])
+ avg_y /= len(wire_xy[wire])
+
+ info["x"] = int(round(avg_x))
+ info["y"] = int(round(avg_y))
+ else:
+ info["x"] = wire_names_r[wire][0]
+ info["y"] = wire_names_r[wire][1]
+
+ wireinfo.append(info)
+
+packageinfo = []
+
+for package in packages:
+ name, pins = package
+ safename = re.sub("[^A-Za-z0-9]", "_", name)
+ pins_info = []
+ for pin in pins:
+ pinname, x, y, z = pin
+ pin_bel = "X%d/Y%d/io%d" % (x, y, z)
+ bel_idx = bel_name.index(pin_bel)
+ pins_info.append((pinname, bel_idx))
+ bba.l("package_%s_pins" % safename, "PackagePinPOD")
+ for pi in pins_info:
+ bba.s(pi[0], "name")
+ bba.u32(pi[1], "bel_index")
+ packageinfo.append((name, len(pins_info), "package_%s_pins" % safename))
+
+tilegrid = []
+for y in range(dev_height):
+ for x in range(dev_width):
+ if (x, y) in tiles:
+ tilegrid.append(tiles[x, y].upper())
+ else:
+ tilegrid.append("NONE")
+
+tileinfo = []
+for t in range(num_tile_types):
+ centries_info = []
+ for cb in tile_bits[t]:
+ name, bits = cb
+ safename = re.sub("[^A-Za-z0-9]", "_", name)
+ bba.l("tile%d_%s_bits" % (t, safename), "ConfigBitPOD")
+ for row, col in bits:
+ bba.u8(row, "row")
+ bba.u8(col, "col")
+ if len(bits) == 0:
+ bba.u32(0, "padding")
+ elif len(bits) % 2 == 1:
+ bba.u16(0, "padding")
+ centries_info.append((name, len(bits), t, safename))
+ bba.l("tile%d_config" % t, "ConfigEntryPOD")
+ for name, num_bits, t, safename in centries_info:
+ bba.s(name, "name")
+ bba.u32(num_bits, "num_bits")
+ bba.r("tile%d_%s_bits" % (t, safename), "num_bits")
+ if len(centries_info) == 0:
+ bba.u32(0, "padding")
+ ti = dict()
+ ti["cols"] = tile_sizes[t][0]
+ ti["rows"] = tile_sizes[t][1]
+ ti["num_entries"] = len(centries_info)
+ ti["entries"] = "tile%d_config" % t
+ tileinfo.append(ti)
+
+bba.l("wire_data_%s" % dev_name, "WireInfoPOD")
+for wire, info in enumerate(wireinfo):
+ bba.s(info["name"], "name")
+ bba.u32(info["num_uphill"], "num_uphill")
+ bba.u32(info["num_downhill"], "num_downhill")
+ bba.r(info["list_uphill"], "pips_uphill")
+ bba.r(info["list_downhill"], "pips_downhill")
+ bba.u32(info["num_bel_pins"], "num_bel_pins")
+ bba.r(info["list_bel_pins"], "bel_pins")
+ bba.u32(len(wire_segments[wire]), "num_segments")
+ if len(wire_segments[wire]):
+ bba.r("wire_segments_%d" % wire, "segments")
+ else:
+ bba.u32(0, "segments")
+
+ bba.u32(wiredelay(wire, fast_timings), "fast_delay")
+ bba.u32(wiredelay(wire, slow_timings), "slow_delay")
+
+ bba.u8(info["x"], "x")
+ bba.u8(info["y"], "y")
+ bba.u8(0, "z") # FIXME
+ bba.u8(wiretypes[wire_type(info["name"])], "type")
+
+for wire in range(num_wires):
+ if len(wire_segments[wire]):
+ bba.l("wire_segments_%d" % wire, "WireSegmentPOD")
+ for xy, seg in sorted(wire_segments[wire].items()):
+ bba.u8(xy[0], "x")
+ bba.u8(xy[1], "y")
+ bba.u16(gfx_wire_ids["TILE_WIRE_" + seg.upper().replace("/", "_")], "index")
+
+bba.l("pip_data_%s" % dev_name, "PipInfoPOD")
+for info in pipinfo:
+ src_seg = -1
+ src_segname = wire_names_r[info["src"]]
+ if (info["x"], info["y"]) in wire_segments[info["src"]]:
+ src_segname = wire_segments[info["src"]][(info["x"], info["y"])]
+ src_seg = gfx_wire_ids["TILE_WIRE_" + src_segname.upper().replace("/", "_")]
+ src_segname = src_segname.replace("/", ".")
+
+ dst_seg = -1
+ dst_segname = wire_names_r[info["dst"]]
+ if (info["x"], info["y"]) in wire_segments[info["dst"]]:
+ dst_segname = wire_segments[info["dst"]][(info["x"], info["y"])]
+ dst_seg = gfx_wire_ids["TILE_WIRE_" + dst_segname.upper().replace("/", "_")]
+ dst_segname = dst_segname.replace("/", ".")
+
+ # bba.s("X%d/Y%d/%s->%s" % (info["x"], info["y"], src_segname, dst_segname), "name")
+ bba.u32(info["src"], "src")
+ bba.u32(info["dst"], "dst")
+ bba.u32(info["fast_delay"], "fast_delay")
+ bba.u32(info["slow_delay"], "slow_delay")
+ bba.u8(info["x"], "x")
+ bba.u8(info["y"], "y")
+ bba.u16(src_seg, "src_seg")
+ bba.u16(dst_seg, "dst_seg")
+ bba.u16(info["switch_mask"], "switch_mask")
+ bba.u32(info["switch_index"], "switch_index")
+ bba.u32(info["flags"], "flags")
+
+switchinfo = []
+for switch in switches:
+ x, y, bits, bel = switch
+ bitlist = []
+ for b in bits:
+ m = cbit_re.match(b)
+ assert m
+ bitlist.append((int(m.group(1)), int(m.group(2))))
+ si = dict()
+ si["x"] = x
+ si["y"] = y
+ si["bits"] = bitlist
+ si["bel"] = bel
+ switchinfo.append(si)
+
+bba.l("switch_data_%s" % dev_name, "SwitchInfoPOD")
+for info in switchinfo:
+ bba.u32(len(info["bits"]), "num_bits")
+ bba.u32(info["bel"], "bel")
+ bba.u8(info["x"], "x")
+ bba.u8(info["y"], "y")
+ for i in range(5):
+ if i < len(info["bits"]):
+ bba.u8(info["bits"][i][0], "row<%d>" % i)
+ bba.u8(info["bits"][i][1], "col<%d>" % i)
+ else:
+ bba.u8(0, "row<%d> (unused)" % i)
+ bba.u8(0, "col<%d> (unused)" % i)
+
+bba.l("tile_data_%s" % dev_name, "TileInfoPOD")
+for info in tileinfo:
+ bba.u8(info["cols"], "cols")
+ bba.u8(info["rows"], "rows")
+ bba.u16(info["num_entries"], "num_entries")
+ bba.r(info["entries"], "entries")
+
+bba.l("ieren_data_%s" % dev_name, "IerenInfoPOD")
+for ieren in ierens:
+ bba.u8(ieren[0], "iox")
+ bba.u8(ieren[1], "ioy")
+ bba.u8(ieren[2], "ioz")
+ bba.u8(ieren[3], "ierx")
+ bba.u8(ieren[4], "iery")
+ bba.u8(ieren[5], "ierz")
+
+if len(ierens) % 2 == 1:
+ bba.u16(0, "padding")
+
+bba.l("bits_info_%s" % dev_name, "BitstreamInfoPOD")
+bba.u32(len(switchinfo), "num_switches")
+bba.u32(len(ierens), "num_ierens")
+bba.r("tile_data_%s" % dev_name, "tiles_nonrouting")
+bba.r("switch_data_%s" % dev_name, "switches")
+bba.r("ieren_data_%s" % dev_name, "ierens")
+
+bba.l("tile_grid_%s" % dev_name, "TileType")
+for t in tilegrid:
+ bba.u32(tiletypes[t], "tiletype")
+
+for bel_idx, entries in sorted(extra_cell_config.items()):
+ if len(entries) > 0:
+ bba.l("bel%d_config_entries" % bel_idx, "BelConfigEntryPOD")
+ for entry in entries:
+ bba.s(entry[0], "entry_name")
+ bba.s(entry[1][2], "cbit_name")
+ bba.u8(entry[1][0], "x")
+ bba.u8(entry[1][1], "y")
+ bba.u16(0, "padding")
+
+if len(extra_cell_config) > 0:
+ bba.l("bel_config_%s" % dev_name, "BelConfigPOD")
+ for bel_idx, entries in sorted(extra_cell_config.items()):
+ bba.u32(bel_idx, "bel_index")
+ bba.u32(len(entries), "num_entries")
+ bba.r("bel%d_config_entries" % bel_idx if len(entries) > 0 else None, "entries")
+
+bba.l("package_info_%s" % dev_name, "PackageInfoPOD")
+for info in packageinfo:
+ bba.s(info[0], "name")
+ bba.u32(info[1], "num_pins")
+ bba.r(info[2], "pins")
+
+for cell, timings in sorted(cell_timings.items()):
+ beltype = constids[cell]
+ bba.l("cell_paths_%d" % beltype, "CellPathDelayPOD")
+ for entry in timings:
+ fromport, toport, fast, slow = entry
+ bba.u32(constids[fromport], "from_port")
+ bba.u32(constids[toport], "to_port")
+ bba.u32(fast, "fast_delay")
+ bba.u32(slow, "slow_delay")
+
+bba.l("cell_timings_%s" % dev_name, "CellTimingPOD")
+for cell, timings in sorted(cell_timings.items()):
+ beltype = constids[cell]
+ bba.u32(beltype, "type")
+ bba.u32(len(timings), "num_paths")
+ bba.r("cell_paths_%d" % beltype, "path_delays")
+
+bba.l("chip_info_%s" % dev_name)
+bba.u32(dev_width, "dev_width")
+bba.u32(dev_height, "dev_height")
+bba.u32(len(bel_name), "num_bels")
+bba.u32(num_wires, "num_wires")
+bba.u32(len(pipinfo), "num_pips")
+bba.u32(len(switchinfo), "num_switches")
+bba.u32(len(extra_cell_config), "num_belcfgs")
+bba.u32(len(packageinfo), "num_packages")
+bba.u32(len(cell_timings), "num_timing_cells")
+bba.r("bel_data_%s" % dev_name, "bel_data")
+bba.r("wire_data_%s" % dev_name, "wire_data")
+bba.r("pip_data_%s" % dev_name, "pip_data")
+bba.r("tile_grid_%s" % dev_name, "tile_grid")
+bba.r("bits_info_%s" % dev_name, "bits_info")
+bba.r("bel_config_%s" % dev_name if len(extra_cell_config) > 0 else None, "bel_config")
+bba.r("package_info_%s" % dev_name, "packages_data")
+bba.r("cell_timings_%s" % dev_name, "cell_timing")
+
+bba.pop()
diff --git a/xc7/constids.inc b/xc7/constids.inc
new file mode 100644
index 0000000..7fae17e
--- /dev/null
+++ b/xc7/constids.inc
@@ -0,0 +1,467 @@
+// pin and port names
+//X(I0)
+X(I1)
+X(I2)
+X(I3)
+X(I4)
+X(I5)
+X(I6)
+X(O)
+X(OQ)
+X(OMUX)
+X(CIN)
+X(COUT)
+X(CEN)
+X(CLK)
+X(SR)
+X(S)
+X(R)
+X(PRE)
+X(CLR)
+
+X(MASK_0)
+X(MASK_1)
+X(MASK_2)
+X(MASK_3)
+X(MASK_4)
+X(MASK_5)
+X(MASK_6)
+X(MASK_7)
+X(MASK_8)
+X(MASK_9)
+X(MASK_10)
+X(MASK_11)
+X(MASK_12)
+X(MASK_13)
+X(MASK_14)
+X(MASK_15)
+
+X(RDATA_0)
+X(RDATA_1)
+X(RDATA_2)
+X(RDATA_3)
+X(RDATA_4)
+X(RDATA_5)
+X(RDATA_6)
+X(RDATA_7)
+X(RDATA_8)
+X(RDATA_9)
+X(RDATA_10)
+X(RDATA_11)
+X(RDATA_12)
+X(RDATA_13)
+X(RDATA_14)
+X(RDATA_15)
+
+X(WDATA_0)
+X(WDATA_1)
+X(WDATA_2)
+X(WDATA_3)
+X(WDATA_4)
+X(WDATA_5)
+X(WDATA_6)
+X(WDATA_7)
+X(WDATA_8)
+X(WDATA_9)
+X(WDATA_10)
+X(WDATA_11)
+X(WDATA_12)
+X(WDATA_13)
+X(WDATA_14)
+X(WDATA_15)
+
+X(WADDR_0)
+X(WADDR_1)
+X(WADDR_2)
+X(WADDR_3)
+X(WADDR_4)
+X(WADDR_5)
+X(WADDR_6)
+X(WADDR_7)
+X(WADDR_8)
+X(WADDR_9)
+X(WADDR_10)
+
+X(RADDR_0)
+X(RADDR_1)
+X(RADDR_2)
+X(RADDR_3)
+X(RADDR_4)
+X(RADDR_5)
+X(RADDR_6)
+X(RADDR_7)
+X(RADDR_8)
+X(RADDR_9)
+X(RADDR_10)
+
+X(WCLK)
+X(WCLKE)
+X(WE)
+
+X(RCLK)
+X(RCLKE)
+X(RE)
+
+X(PACKAGE_PIN)
+X(LATCH_INPUT_VALUE)
+X(CLOCK_ENABLE)
+X(INPUT_CLK)
+X(OUTPUT_CLK)
+X(OUTPUT_ENABLE)
+X(D_OUT_0)
+X(D_OUT_1)
+X(D_IN_0)
+X(D_IN_1)
+
+X(USER_SIGNAL_TO_GLOBAL_BUFFER)
+X(GLOBAL_BUFFER_OUTPUT)
+
+X(REFERENCECLK)
+X(EXTFEEDBACK)
+X(DYNAMICDELAY_0)
+X(DYNAMICDELAY_1)
+X(DYNAMICDELAY_2)
+X(DYNAMICDELAY_3)
+X(DYNAMICDELAY_4)
+X(DYNAMICDELAY_5)
+X(DYNAMICDELAY_6)
+X(DYNAMICDELAY_7)
+X(LOCK)
+X(PLLOUT_A)
+X(PLLOUT_B)
+X(BYPASS)
+X(RESETB)
+X(LATCHINPUTVALUE)
+X(SDO)
+X(SDI)
+X(SCLK)
+
+X(BOOT)
+X(S0)
+X(S1)
+
+X(ADDSUBBOT)
+X(ADDSUBTOP)
+X(AHOLD)
+X(A_0)
+X(A_1)
+X(A_10)
+X(A_11)
+X(A_12)
+X(A_13)
+X(A_14)
+X(A_15)
+X(A_2)
+X(A_3)
+X(A_4)
+X(A_5)
+X(A_6)
+X(A_7)
+X(A_8)
+X(A_9)
+X(BHOLD)
+X(B_0)
+X(B_1)
+X(B_10)
+X(B_11)
+X(B_12)
+X(B_13)
+X(B_14)
+X(B_15)
+X(B_2)
+X(B_3)
+X(B_4)
+X(B_5)
+X(B_6)
+X(B_7)
+X(B_8)
+X(B_9)
+X(CE)
+X(CHOLD)
+X(CI)
+X(CO)
+X(C_0)
+X(C_1)
+X(C_10)
+X(C_11)
+X(C_12)
+X(C_13)
+X(C_14)
+X(C_15)
+X(C_2)
+X(C_3)
+X(C_4)
+X(C_5)
+X(C_6)
+X(C_7)
+X(C_8)
+X(C_9)
+X(DHOLD)
+X(D_0)
+X(D_1)
+X(D_10)
+X(D_11)
+X(D_12)
+X(D_13)
+X(D_14)
+X(D_15)
+X(D_2)
+X(D_3)
+X(D_4)
+X(D_5)
+X(D_6)
+X(D_7)
+X(D_8)
+X(D_9)
+X(IRSTBOT)
+X(IRSTTOP)
+X(OHOLDBOT)
+X(OHOLDTOP)
+X(OLOADBOT)
+X(OLOADTOP)
+X(ORSTBOT)
+X(ORSTTOP)
+X(O_0)
+X(O_1)
+X(O_10)
+X(O_11)
+X(O_12)
+X(O_13)
+X(O_14)
+X(O_15)
+X(O_16)
+X(O_17)
+X(O_18)
+X(O_19)
+X(O_2)
+X(O_20)
+X(O_21)
+X(O_22)
+X(O_23)
+X(O_24)
+X(O_25)
+X(O_26)
+X(O_27)
+X(O_28)
+X(O_29)
+X(O_3)
+X(O_30)
+X(O_31)
+X(O_4)
+X(O_5)
+X(O_6)
+X(O_7)
+X(O_8)
+X(O_9)
+
+X(CLKHF)
+X(CLKHFEN)
+X(CLKHFPU)
+X(CLKHF_FABRIC)
+X(TRIM0)
+X(TRIM1)
+X(TRIM2)
+X(TRIM3)
+X(TRIM4)
+X(TRIM5)
+X(TRIM6)
+X(TRIM7)
+X(TRIM8)
+X(TRIM9)
+
+X(CLKLF)
+X(CLKLFEN)
+X(CLKLFPU)
+X(CLKLF_FABRIC)
+
+X(I2CIRQ)
+X(I2CWKUP)
+X(SBACKO)
+X(SBADRI0)
+X(SBADRI1)
+X(SBADRI2)
+X(SBADRI3)
+X(SBADRI4)
+X(SBADRI5)
+X(SBADRI6)
+X(SBADRI7)
+X(SBCLKI)
+X(SBDATI0)
+X(SBDATI1)
+X(SBDATI2)
+X(SBDATI3)
+X(SBDATI4)
+X(SBDATI5)
+X(SBDATI6)
+X(SBDATI7)
+X(SBDATO0)
+X(SBDATO1)
+X(SBDATO2)
+X(SBDATO3)
+X(SBDATO4)
+X(SBDATO5)
+X(SBDATO6)
+X(SBDATO7)
+X(SBRWI)
+X(SBSTBI)
+X(SCLI)
+X(SCLO)
+X(SCLOE)
+X(SDAI)
+X(SDAO)
+X(SDAOE)
+
+X(MCSNO0)
+X(MCSNO1)
+X(MCSNO2)
+X(MCSNO3)
+X(MCSNOE0)
+X(MCSNOE1)
+X(MCSNOE2)
+X(MCSNOE3)
+X(MI)
+X(MO)
+X(MOE)
+X(SCKI)
+X(SCKO)
+X(SCKOE)
+X(SCSNI)
+X(SI)
+X(SO)
+X(SOE)
+X(SPIIRQ)
+X(SPIWKUP)
+
+X(PU_ENB)
+X(WEAK_PU_ENB)
+
+X(LEDDADDR0)
+X(LEDDADDR1)
+X(LEDDADDR2)
+X(LEDDADDR3)
+X(LEDDCLK)
+X(LEDDCS)
+X(LEDDDAT0)
+X(LEDDDAT1)
+X(LEDDDAT2)
+X(LEDDDAT3)
+X(LEDDDAT4)
+X(LEDDDAT5)
+X(LEDDDAT6)
+X(LEDDDAT7)
+X(LEDDDEN)
+X(LEDDEXE)
+X(LEDDON)
+X(PWMOUT0)
+X(PWMOUT1)
+X(PWMOUT2)
+
+X(CURREN)
+X(RGB0PWM)
+X(RGB1PWM)
+X(RGB2PWM)
+X(RGBLEDEN)
+X(RGB0)
+X(RGB1)
+X(RGB2)
+
+X(ADDRESS_0)
+X(ADDRESS_1)
+X(ADDRESS_10)
+X(ADDRESS_11)
+X(ADDRESS_12)
+X(ADDRESS_13)
+X(ADDRESS_2)
+X(ADDRESS_3)
+X(ADDRESS_4)
+X(ADDRESS_5)
+X(ADDRESS_6)
+X(ADDRESS_7)
+X(ADDRESS_8)
+X(ADDRESS_9)
+X(CHIPSELECT)
+X(CLOCK)
+X(DATAIN_0)
+X(DATAIN_1)
+X(DATAIN_10)
+X(DATAIN_11)
+X(DATAIN_12)
+X(DATAIN_13)
+X(DATAIN_14)
+X(DATAIN_15)
+X(DATAIN_2)
+X(DATAIN_3)
+X(DATAIN_4)
+X(DATAIN_5)
+X(DATAIN_6)
+X(DATAIN_7)
+X(DATAIN_8)
+X(DATAIN_9)
+X(DATAOUT_0)
+X(DATAOUT_1)
+X(DATAOUT_10)
+X(DATAOUT_11)
+X(DATAOUT_12)
+X(DATAOUT_13)
+X(DATAOUT_14)
+X(DATAOUT_15)
+X(DATAOUT_2)
+X(DATAOUT_3)
+X(DATAOUT_4)
+X(DATAOUT_5)
+X(DATAOUT_6)
+X(DATAOUT_7)
+X(DATAOUT_8)
+X(DATAOUT_9)
+X(MASKWREN_0)
+X(MASKWREN_1)
+X(MASKWREN_2)
+X(MASKWREN_3)
+X(POWEROFF)
+X(SLEEP)
+X(STANDBY)
+X(WREN)
+
+// cell and bel types
+X(ICESTORM_LC)
+X(ICESTORM_RAM)
+X(SB_IO)
+X(SB_GB)
+X(ICESTORM_PLL)
+X(SB_WARMBOOT)
+X(ICESTORM_DSP)
+X(ICESTORM_HFOSC)
+X(ICESTORM_LFOSC)
+X(SB_I2C)
+X(SB_SPI)
+X(IO_I3C)
+X(SB_LEDDA_IP)
+X(SB_RGBA_DRV)
+X(ICESTORM_SPRAM)
+
+// cell parameters
+X(DFF_ENABLE)
+X(CARRY_ENABLE)
+X(NEG_CLK)
+
+// XC7
+X(I)
+
+X(LUT1)
+X(LUT2)
+X(LUT3)
+X(LUT4)
+X(LUT5)
+X(LUT6)
+
+X(FDRE)
+X(FDSE)
+X(FDCE)
+X(FDPE)
+
+X(BUFGCTRL)
+X(SLICE_LUT6)
+X(IOB33)
+X(IOB18)
+X(PS7)
+X(MMCME2_ADV)
diff --git a/xc7/delay.cc b/xc7/delay.cc
new file mode 100644
index 0000000..67f96b9
--- /dev/null
+++ b/xc7/delay.cc
@@ -0,0 +1,85 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "nextpnr.h"
+#include "router1.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+#define NUM_FUZZ_ROUTES 100000
+
+delay_t Arch::estimateDelay(WireId src, WireId dst) const
+{
+ const auto &src_tw = torc_info->wire_to_tilewire[src.index];
+ const auto &src_loc = torc_info->tile_to_xy[src_tw.getTileIndex()];
+ const auto &dst_tw = torc_info->wire_to_tilewire[dst.index];
+ const auto &dst_loc = torc_info->tile_to_xy[dst_tw.getTileIndex()];
+
+ if (!torc_info->wire_is_global[src.index]) {
+ auto abs_delta_x = abs(dst_loc.first - src_loc.first);
+ auto abs_delta_y = abs(dst_loc.second - src_loc.second);
+ auto div_LH = std::div(abs_delta_x, 12);
+ auto div_LV = std::div(abs_delta_y, 18);
+ auto div_LVB = std::div(div_LV.rem, 12);
+ auto div_H6 = std::div(div_LH.rem, 6);
+ auto div_V6 = std::div(div_LVB.rem, 6);
+ auto div_H4 = std::div(div_H6.rem, 4);
+ auto div_V4 = std::div(div_V6.rem, 4);
+ auto div_H2 = std::div(div_H4.rem, 2);
+ auto div_V2 = std::div(div_V4.rem, 2);
+ auto num_H1 = div_H2.rem;
+ auto num_V1 = div_V2.rem;
+ return div_LH.quot * 360 + div_LVB.quot * 300 + div_LV.quot * 350 +
+ (div_H6.quot + div_H4.quot + div_V6.quot + div_V4.quot) * 210 + (div_H2.quot + div_V2.quot) * 170 +
+ (num_H1 + num_V1) * 150;
+ }
+ else {
+ auto src_y = src_loc.second;
+ auto dst_y = dst_loc.second;
+ auto div_src_y = std::div(src_y, 52);
+ auto div_dst_y = std::div(dst_y, 52);
+ return abs(div_dst_y.quot - div_src_y.quot) * 52 + abs(div_dst_y.rem - div_src_y.rem);
+ }
+}
+
+delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
+{
+ const auto &driver = net_info->driver;
+ auto driver_loc = getBelLocation(driver.cell->bel);
+ auto sink_loc = getBelLocation(sink.cell->bel);
+ auto abs_delta_x = abs(driver_loc.x - sink_loc.x);
+ auto abs_delta_y = abs(driver_loc.y - sink_loc.y);
+ auto div_LH = std::div(abs_delta_x, 12);
+ auto div_LV = std::div(abs_delta_y, 18);
+ auto div_LVB = std::div(div_LV.rem, 12);
+ auto div_H6 = std::div(div_LH.rem, 6);
+ auto div_V6 = std::div(div_LVB.rem, 6);
+ auto div_H4 = std::div(div_H6.rem, 4);
+ auto div_V4 = std::div(div_V6.rem, 4);
+ auto div_H2 = std::div(div_H4.rem, 2);
+ auto div_V2 = std::div(div_V4.rem, 2);
+ auto num_H1 = div_H2.rem;
+ auto num_V1 = div_V2.rem;
+ return div_LH.quot * 360 + div_LVB.quot * 300 + div_LV.quot * 350 +
+ (div_H6.quot + div_H4.quot + div_V6.quot + div_V4.quot) * 210 + (div_H2.quot + div_V2.quot) * 170 +
+ (num_H1 + num_V1) * 150;
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/xc7/family.cmake b/xc7/family.cmake
new file mode 100644
index 0000000..9de869e
--- /dev/null
+++ b/xc7/family.cmake
@@ -0,0 +1,73 @@
+add_dependencies(nextpnr-${family} torc)
+add_custom_target(torc ALL
+ COMMAND $(MAKE) > /dev/null 2> /dev/null
+ COMMENT "Building torc (may take some time...)"
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/torc/src)
+find_package(Boost REQUIRED COMPONENTS serialization iostreams ${boost_libs} ${boost_python_lib})
+
+include_directories(torc/src)
+target_link_libraries(
+ nextpnr-${family} PRIVATE
+
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/Arc.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/ArcUsage.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/Array.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/DDB.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/DDBConsoleStreams.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/DDBStreamHelper.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/DigestStream.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/ExtendedWireInfo.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/InstancePin.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/OutputStreamHelpers.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/Package.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/Pad.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/PrimitiveConn.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/PrimitiveDef.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/PrimitiveElement.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/PrimitiveElementPin.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/PrimitivePin.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/Segments.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/Site.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/Sites.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/Tiles.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/TileInfo.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/Tilewire.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/Versions.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/VprExporter.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/WireInfo.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/WireUsage.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/XdlImporter.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/architecture/XilinxDatabaseTypes.o
+
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/common/Annotated.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/common/DeviceDesignator.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/common/Devices.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/common/DirectoryTree.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/common/DottedVersion.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/common/NullOutputStream.o
+
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/externals/zlib/zfstream.o
+ z
+
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/Circuit.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/ConfigMap.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/Config.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/Design.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/Factory.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/Instance.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/InstancePin.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/InstanceReference.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/Module.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/ModuleTransformer.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/Named.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/Net.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/OutputStreamHelpers.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/Pip.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/Port.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/Progenitor.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/Progeny.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/Renamable.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/Routethrough.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/TilewirePlaceholder.o
+ ${CMAKE_CURRENT_SOURCE_DIR}/torc/src/torc/physical/XdlExporter.o
+)
diff --git a/xc7/firmware_fast.hex b/xc7/firmware_fast.hex
new file mode 100644
index 0000000..bf6fe43
--- /dev/null
+++ b/xc7/firmware_fast.hex
@@ -0,0 +1,6 @@
+@00000000
+13 04 20 00 B7 04 00 02 13 04 14 00 13 74 F4 0F
+13 09 20 00 63 5E 89 00 13 05 04 00 93 05 09 00
+EF 00 80 01 63 08 05 00 13 09 19 00 6F F0 9F FE
+23 A0 84 00 6F F0 5F FD 93 02 10 00 33 05 B5 40
+E3 5E 55 FE 67 80 00 00
diff --git a/xc7/firmware_slow.hex b/xc7/firmware_slow.hex
new file mode 100644
index 0000000..ef9aa3c
--- /dev/null
+++ b/xc7/firmware_slow.hex
@@ -0,0 +1,8 @@
+@00000000
+13 04 20 00 B7 04 00 02 93 09 00 10 13 04 14 00
+63 44 34 01 13 04 20 00 13 09 20 00 63 5E 89 00
+13 05 04 00 93 05 09 00 EF 00 C0 01 63 0A 05 00
+13 09 19 00 6F F0 9F FE 23 A0 84 00 EF 00 80 01
+6F F0 DF FC 93 02 10 00 33 05 B5 40 E3 5E 55 FE
+67 80 00 00 B7 82 05 00 93 82 02 E4 93 82 F2 FF
+E3 9E 02 FE 67 80 00 00
diff --git a/xc7/gfx.cc b/xc7/gfx.cc
new file mode 100644
index 0000000..1ab2fb3
--- /dev/null
+++ b/xc7/gfx.cc
@@ -0,0 +1,766 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "gfx.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style)
+{
+ GraphicElement el;
+ el.type = GraphicElement::TYPE_LINE;
+ el.style = style;
+
+ // Horizontal Span-4 Wires
+
+ if (id >= TILE_WIRE_SP4_H_L_36 && id <= TILE_WIRE_SP4_H_L_47) {
+ int idx = (id - TILE_WIRE_SP4_H_L_36) + 48;
+
+ float y1 = y + 1.0 - (0.03 + 0.0025 * (60 - (idx ^ 1)));
+ float y2 = y + 1.0 - (0.03 + 0.0025 * (60 - idx));
+
+ el.x1 = x;
+ el.x2 = x + 0.01;
+ el.y1 = y1;
+ el.y2 = y1;
+ g.push_back(el);
+
+ el.x1 = x + 0.01;
+ el.x2 = x + 0.02;
+ el.y1 = y1;
+ el.y2 = y2;
+ g.push_back(el);
+
+ el.x1 = x + 0.02;
+ el.x2 = x + 0.9;
+ el.y1 = y2;
+ el.y2 = y2;
+ g.push_back(el);
+
+ el.x1 = x + main_swbox_x1 + 0.0025 * (idx + 35);
+ el.x2 = el.x1;
+ el.y1 = y2;
+ el.y2 = y + main_swbox_y2;
+ g.push_back(el);
+ }
+
+ if (id >= TILE_WIRE_SP4_H_R_0 && id <= TILE_WIRE_SP4_H_R_47) {
+ int idx = id - TILE_WIRE_SP4_H_R_0;
+
+ float y1 = y + 1.0 - (0.03 + 0.0025 * (60 - idx));
+ float y2 = y + 1.0 - (0.03 + 0.0025 * (60 - (idx ^ 1)));
+ float y3 = y + 1.0 - (0.03 + 0.0025 * (60 - (idx ^ 1) - 12));
+
+ if (idx >= 12) {
+ el.x1 = x;
+ el.x2 = x + 0.01;
+ el.y1 = y1;
+ el.y2 = y1;
+ g.push_back(el);
+
+ el.x1 = x + 0.01;
+ el.x2 = x + 0.02;
+ el.y1 = y1;
+ el.y2 = y2;
+ g.push_back(el);
+ }
+
+ el.x1 = x + 0.02;
+ el.x2 = x + 0.9;
+ el.y1 = y2;
+ el.y2 = y2;
+ g.push_back(el);
+
+ el.x1 = x + 0.9;
+ el.x2 = x + 1.0;
+ el.y1 = y2;
+ el.y2 = y3;
+ g.push_back(el);
+
+ el.x1 = x + main_swbox_x1 + 0.0025 * ((idx ^ 1) + 35);
+ el.x2 = el.x1;
+ el.y1 = y2;
+ el.y2 = y + main_swbox_y2;
+ g.push_back(el);
+ }
+
+ // Vertical Span-4 Wires
+
+ if (id >= TILE_WIRE_SP4_V_T_36 && id <= TILE_WIRE_SP4_V_T_47) {
+ int idx = (id - TILE_WIRE_SP4_V_T_36) + 48;
+
+ float x1 = x + 0.03 + 0.0025 * (60 - (idx ^ 1));
+ float x2 = x + 0.03 + 0.0025 * (60 - idx);
+
+ el.y1 = y + 1.00;
+ el.y2 = y + 0.99;
+ el.x1 = x1;
+ el.x2 = x1;
+ g.push_back(el);
+
+ el.y1 = y + 0.99;
+ el.y2 = y + 0.98;
+ el.x1 = x1;
+ el.x2 = x2;
+ g.push_back(el);
+
+ el.y1 = y + 0.98;
+ el.y2 = y + 0.10;
+ el.x1 = x2;
+ el.x2 = x2;
+ g.push_back(el);
+
+ el.y1 = y + 1.0 - (0.03 + 0.0025 * (270 - idx));
+ el.y2 = el.y1;
+ el.x1 = x2;
+ el.x2 = x + main_swbox_x1;
+ g.push_back(el);
+ }
+
+ if (id >= TILE_WIRE_SP4_V_B_0 && id <= TILE_WIRE_SP4_V_B_47) {
+ int idx = id - TILE_WIRE_SP4_V_B_0;
+
+ float x1 = x + 0.03 + 0.0025 * (60 - idx);
+ float x2 = x + 0.03 + 0.0025 * (60 - (idx ^ 1));
+ float x3 = x + 0.03 + 0.0025 * (60 - (idx ^ 1) - 12);
+
+ if (idx >= 12) {
+ el.y1 = y + 1.00;
+ el.y2 = y + 0.99;
+ el.x1 = x1;
+ el.x2 = x1;
+ g.push_back(el);
+
+ el.y1 = y + 0.99;
+ el.y2 = y + 0.98;
+ el.x1 = x1;
+ el.x2 = x2;
+ g.push_back(el);
+ }
+
+ el.y1 = y + 0.98;
+ el.y2 = y + 0.10;
+ el.x1 = x2;
+ el.x2 = x2;
+ g.push_back(el);
+
+ el.y1 = y + 0.10;
+ el.y2 = y;
+ el.x1 = x2;
+ el.x2 = x3;
+ g.push_back(el);
+
+ el.y1 = y + 1.0 - (0.03 + 0.0025 * (145 - (idx ^ 1)));
+ el.y2 = el.y1;
+ el.x1 = x;
+ el.x2 = x2;
+ g.push_back(el);
+
+ el.y1 = y + 1.0 - (0.03 + 0.0025 * (270 - (idx ^ 1)));
+ el.y2 = el.y1;
+ el.x1 = x2;
+ el.x2 = x + main_swbox_x1;
+ g.push_back(el);
+ }
+
+ // Horizontal Span-12 Wires
+
+ if (id >= TILE_WIRE_SP12_H_L_22 && id <= TILE_WIRE_SP12_H_L_23) {
+ int idx = (id - TILE_WIRE_SP12_H_L_22) + 24;
+
+ float y1 = y + 1.0 - (0.03 + 0.0025 * (90 - (idx ^ 1)));
+ float y2 = y + 1.0 - (0.03 + 0.0025 * (90 - idx));
+
+ el.x1 = x;
+ el.x2 = x + 0.01;
+ el.y1 = y1;
+ el.y2 = y1;
+ g.push_back(el);
+
+ el.x1 = x + 0.01;
+ el.x2 = x + 0.02;
+ el.y1 = y1;
+ el.y2 = y2;
+ g.push_back(el);
+
+ el.x1 = x + 0.02;
+ el.x2 = x + 0.98333;
+ el.y1 = y2;
+ el.y2 = y2;
+ g.push_back(el);
+
+ el.x1 = x + main_swbox_x1 + 0.0025 * (idx + 5);
+ el.x2 = el.x1;
+ el.y1 = y2;
+ el.y2 = y + main_swbox_y2;
+ g.push_back(el);
+ }
+
+ if (id >= TILE_WIRE_SP12_H_R_0 && id <= TILE_WIRE_SP12_H_R_23) {
+ int idx = id - TILE_WIRE_SP12_H_R_0;
+
+ float y1 = y + 1.0 - (0.03 + 0.0025 * (90 - idx));
+ float y2 = y + 1.0 - (0.03 + 0.0025 * (90 - (idx ^ 1)));
+ float y3 = y + 1.0 - (0.03 + 0.0025 * (90 - (idx ^ 1) - 2));
+
+ if (idx >= 2) {
+ el.x1 = x;
+ el.x2 = x + 0.01;
+ el.y1 = y1;
+ el.y2 = y1;
+ g.push_back(el);
+
+ el.x1 = x + 0.01;
+ el.x2 = x + 0.02;
+ el.y1 = y1;
+ el.y2 = y2;
+ g.push_back(el);
+ }
+
+ el.x1 = x + 0.02;
+ el.x2 = x + 0.98333;
+ el.y1 = y2;
+ el.y2 = y2;
+ g.push_back(el);
+
+ el.x1 = x + 0.98333;
+ el.x2 = x + 1.0;
+ el.y1 = y2;
+ el.y2 = y3;
+ g.push_back(el);
+
+ el.x1 = x + main_swbox_x1 + 0.0025 * ((idx ^ 1) + 5);
+ el.x2 = el.x1;
+ el.y1 = y2;
+ el.y2 = y + main_swbox_y2;
+ g.push_back(el);
+ }
+
+ // Vertical Right Span-4
+
+ if (id >= TILE_WIRE_SP4_R_V_B_0 && id <= TILE_WIRE_SP4_R_V_B_47) {
+ int idx = id - TILE_WIRE_SP4_R_V_B_0;
+
+ float y1 = y + 1.0 - (0.03 + 0.0025 * (145 - (idx ^ 1)));
+
+ el.y1 = y1;
+ el.y2 = y1;
+ el.x1 = x + main_swbox_x2;
+ el.x2 = x + 1.0;
+ g.push_back(el);
+ }
+
+ // Vertical Span-12 Wires
+
+ if (id >= TILE_WIRE_SP12_V_T_22 && id <= TILE_WIRE_SP12_V_T_23) {
+ int idx = (id - TILE_WIRE_SP12_V_T_22) + 24;
+
+ float x1 = x + 0.03 + 0.0025 * (90 - (idx ^ 1));
+ float x2 = x + 0.03 + 0.0025 * (90 - idx);
+
+ el.y1 = y + 1.00;
+ el.y2 = y + 0.99;
+ el.x1 = x1;
+ el.x2 = x1;
+ g.push_back(el);
+
+ el.y1 = y + 0.99;
+ el.y2 = y + 0.98;
+ el.x1 = x1;
+ el.x2 = x2;
+ g.push_back(el);
+
+ el.y1 = y + 0.98;
+ el.y2 = y + 0.01667;
+ el.x1 = x2;
+ el.x2 = x2;
+ g.push_back(el);
+
+ el.y1 = y + 1.0 - (0.03 + 0.0025 * (300 - idx));
+ el.y2 = el.y1;
+ el.x1 = x2;
+ el.x2 = x + main_swbox_x1;
+ g.push_back(el);
+ }
+
+ if (id >= TILE_WIRE_SP12_V_B_0 && id <= TILE_WIRE_SP12_V_B_23) {
+ int idx = id - TILE_WIRE_SP12_V_B_0;
+
+ float x1 = x + 0.03 + 0.0025 * (90 - idx);
+ float x2 = x + 0.03 + 0.0025 * (90 - (idx ^ 1));
+ float x3 = x + 0.03 + 0.0025 * (90 - (idx ^ 1) - 2);
+
+ if (idx >= 2) {
+ el.y1 = y + 1.00;
+ el.y2 = y + 0.99;
+ el.x1 = x1;
+ el.x2 = x1;
+ g.push_back(el);
+
+ el.y1 = y + 0.99;
+ el.y2 = y + 0.98;
+ el.x1 = x1;
+ el.x2 = x2;
+ g.push_back(el);
+ }
+
+ el.y1 = y + 0.98;
+ el.y2 = y + 0.01667;
+ el.x1 = x2;
+ el.x2 = x2;
+ g.push_back(el);
+
+ el.y1 = y + 0.01667;
+ el.y2 = y;
+ el.x1 = x2;
+ el.x2 = x3;
+ g.push_back(el);
+
+ el.y1 = y + 1.0 - (0.03 + 0.0025 * (300 - (idx ^ 1)));
+ el.y2 = el.y1;
+ el.x1 = x2;
+ el.x2 = x + main_swbox_x1;
+ g.push_back(el);
+ }
+
+ // Global2Local
+
+ if (id >= TILE_WIRE_GLB2LOCAL_0 && id <= TILE_WIRE_GLB2LOCAL_3) {
+ int idx = id - TILE_WIRE_GLB2LOCAL_0;
+ el.x1 = x + main_swbox_x1 + 0.005 * (idx + 5);
+ el.x2 = el.x1;
+ el.y1 = y + main_swbox_y1;
+ el.y2 = el.y1 - 0.02;
+ g.push_back(el);
+ }
+
+ // GlobalNets
+
+ if (id >= TILE_WIRE_GLB_NETWK_0 && id <= TILE_WIRE_GLB_NETWK_7) {
+ int idx = id - TILE_WIRE_GLB_NETWK_0;
+ el.x1 = x + main_swbox_x1 - 0.05;
+ el.x2 = x + main_swbox_x1;
+ el.y1 = y + main_swbox_y1 + 0.005 * (13 - idx);
+ el.y2 = el.y1;
+ g.push_back(el);
+ }
+
+ // Neighbours
+
+ if (id >= TILE_WIRE_NEIGH_OP_BNL_0 && id <= TILE_WIRE_NEIGH_OP_TOP_7) {
+ int idx = id - TILE_WIRE_NEIGH_OP_BNL_0;
+ el.y1 = y + main_swbox_y2 - (0.0025 * (idx + 10) + 0.01 * (idx / 8));
+ el.y2 = el.y1;
+ el.x1 = x + main_swbox_x1 - 0.05;
+ el.x2 = x + main_swbox_x1;
+ g.push_back(el);
+ }
+
+ // Local Tracks
+
+ if (id >= TILE_WIRE_LOCAL_G0_0 && id <= TILE_WIRE_LOCAL_G3_7) {
+ int idx = id - TILE_WIRE_LOCAL_G0_0;
+ el.x1 = x + main_swbox_x2;
+ el.x2 = x + local_swbox_x1;
+ float yoff = y + (local_swbox_y1 + local_swbox_y2) / 2 - 0.005 * 16 - 0.075;
+ el.y1 = yoff + 0.005 * idx + 0.05 * (idx / 8);
+ el.y2 = el.y1;
+ g.push_back(el);
+ }
+
+ // LC Inputs
+
+ if (id >= TILE_WIRE_LUTFF_0_IN_0 && id <= TILE_WIRE_LUTFF_7_IN_3) {
+ int idx = id - TILE_WIRE_LUTFF_0_IN_0;
+ int z = idx / 4;
+ int input = idx % 4;
+ el.x1 = x + local_swbox_x2;
+ el.x2 = x + lut_swbox_x1;
+ el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * input) + z * logic_cell_pitch;
+ el.y2 = el.y1;
+ g.push_back(el);
+ }
+
+ if (id >= TILE_WIRE_LUTFF_0_IN_0_LUT && id <= TILE_WIRE_LUTFF_7_IN_3_LUT) {
+ int idx = id - TILE_WIRE_LUTFF_0_IN_0_LUT;
+ int z = idx / 4;
+ int input = idx % 4;
+ el.x1 = x + lut_swbox_x2;
+ el.x2 = x + logic_cell_x1;
+ el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * input) + z * logic_cell_pitch;
+ el.y2 = el.y1;
+ g.push_back(el);
+ }
+
+ // LC Outputs
+
+ if (id >= TILE_WIRE_LUTFF_0_OUT && id <= TILE_WIRE_LUTFF_7_OUT) {
+ int idx = id - TILE_WIRE_LUTFF_0_OUT;
+
+ float y1 = y + 1.0 - (0.03 + 0.0025 * (152 + idx));
+
+ el.y1 = y1;
+ el.y2 = y1;
+ el.x1 = x + main_swbox_x2;
+ el.x2 = x + 0.97 + 0.0025 * (7 - idx);
+ g.push_back(el);
+
+ el.y1 = y1;
+ el.y2 = y + (logic_cell_y1 + logic_cell_y2) / 2 + idx * logic_cell_pitch;
+ el.x1 = el.x2;
+ g.push_back(el);
+
+ el.y1 = el.y2;
+ el.x1 = x + logic_cell_x2;
+ g.push_back(el);
+ }
+
+ // LC Control
+
+ if (id >= TILE_WIRE_LUTFF_GLOBAL_CEN && id <= TILE_WIRE_LUTFF_GLOBAL_S_R) {
+ int idx = id - TILE_WIRE_LUTFF_GLOBAL_CEN;
+
+ el.x1 = x + main_swbox_x2 - 0.005 * (idx + 5);
+ el.x2 = el.x1;
+ el.y1 = y + main_swbox_y1;
+ el.y2 = el.y1 - 0.005 * (idx + 2);
+ g.push_back(el);
+
+ el.y1 = el.y2;
+ el.x2 = x + logic_cell_x2 - 0.005 * (2 - idx + 5);
+ g.push_back(el);
+
+ el.y2 = y + logic_cell_y1;
+ el.x1 = el.x2;
+ g.push_back(el);
+
+ for (int i = 0; i < 7; i++) {
+ el.y1 = y + logic_cell_y2 + i * logic_cell_pitch;
+ el.y2 = y + logic_cell_y1 + (i + 1) * logic_cell_pitch;
+ g.push_back(el);
+ }
+ }
+
+ // LC Cascade
+
+ if (id >= TILE_WIRE_LUTFF_0_LOUT && id <= TILE_WIRE_LUTFF_6_LOUT) {
+ int idx = id - TILE_WIRE_LUTFF_0_LOUT;
+ el.x1 = x + logic_cell_x1 + 0.005 * 5;
+ el.x2 = el.x1;
+ el.y1 = y + logic_cell_y2 + idx * logic_cell_pitch;
+ el.y2 = y + logic_cell_y1 + (idx + 1) * logic_cell_pitch;
+ g.push_back(el);
+ }
+
+ // Carry Chain
+
+ if (id >= TILE_WIRE_LUTFF_0_COUT && id <= TILE_WIRE_LUTFF_7_COUT) {
+ int idx = id - TILE_WIRE_LUTFF_0_COUT;
+ el.x1 = x + logic_cell_x1 + 0.005 * 3;
+ el.x2 = el.x1;
+ el.y1 = y + logic_cell_y2 + idx * logic_cell_pitch;
+ el.y2 = y + (idx < 7 ? logic_cell_y1 + (idx + 1) * logic_cell_pitch : 1.0);
+ g.push_back(el);
+ }
+
+ if (id == TILE_WIRE_CARRY_IN) {
+ el.x1 = x + logic_cell_x1 + 0.005 * 3;
+ el.x2 = el.x1;
+ el.y1 = y;
+ el.y2 = y + 0.01;
+ g.push_back(el);
+ }
+
+ if (id == TILE_WIRE_CARRY_IN_MUX) {
+ el.x1 = x + logic_cell_x1 + 0.005 * 3;
+ el.x2 = el.x1;
+ el.y1 = y + 0.02;
+ el.y2 = y + logic_cell_y1;
+ g.push_back(el);
+ }
+}
+
+static bool getWireXY_main(GfxTileWireId id, float &x, float &y)
+{
+ // Horizontal Span-4 Wires
+
+ if (id >= TILE_WIRE_SP4_H_L_36 && id <= TILE_WIRE_SP4_H_L_47) {
+ int idx = (id - TILE_WIRE_SP4_H_L_36) + 48;
+ x = main_swbox_x1 + 0.0025 * (idx + 35);
+ y = main_swbox_y2;
+ return true;
+ }
+
+ if (id >= TILE_WIRE_SP4_H_R_0 && id <= TILE_WIRE_SP4_H_R_47) {
+ int idx = id - TILE_WIRE_SP4_H_R_0;
+ x = main_swbox_x1 + 0.0025 * ((idx ^ 1) + 35);
+ y = main_swbox_y2;
+ return true;
+ }
+
+ // Vertical Span-4 Wires
+
+ if (id >= TILE_WIRE_SP4_V_T_36 && id <= TILE_WIRE_SP4_V_T_47) {
+ int idx = (id - TILE_WIRE_SP4_V_T_36) + 48;
+ y = 1.0 - (0.03 + 0.0025 * (270 - idx));
+ x = main_swbox_x1;
+ return true;
+ }
+
+ if (id >= TILE_WIRE_SP4_V_B_0 && id <= TILE_WIRE_SP4_V_B_47) {
+ int idx = id - TILE_WIRE_SP4_V_B_0;
+ y = 1.0 - (0.03 + 0.0025 * (270 - (idx ^ 1)));
+ x = main_swbox_x1;
+ return true;
+ }
+
+ // Horizontal Span-12 Wires
+
+ if (id >= TILE_WIRE_SP12_H_L_22 && id <= TILE_WIRE_SP12_H_L_23) {
+ int idx = (id - TILE_WIRE_SP12_H_L_22) + 24;
+ x = main_swbox_x1 + 0.0025 * (idx + 5);
+ y = main_swbox_y2;
+ return true;
+ }
+
+ if (id >= TILE_WIRE_SP12_H_R_0 && id <= TILE_WIRE_SP12_H_R_23) {
+ int idx = id - TILE_WIRE_SP12_H_R_0;
+ x = main_swbox_x1 + 0.0025 * ((idx ^ 1) + 5);
+ y = main_swbox_y2;
+ return true;
+ }
+
+ // Vertical Right Span-4
+
+ if (id >= TILE_WIRE_SP4_R_V_B_0 && id <= TILE_WIRE_SP4_R_V_B_47) {
+ int idx = id - TILE_WIRE_SP4_R_V_B_0;
+ y = 1.0 - (0.03 + 0.0025 * (145 - (idx ^ 1)));
+ x = main_swbox_x2;
+ return true;
+ }
+
+ // Vertical Span-12 Wires
+
+ if (id >= TILE_WIRE_SP12_V_T_22 && id <= TILE_WIRE_SP12_V_T_23) {
+ int idx = (id - TILE_WIRE_SP12_V_T_22) + 24;
+ y = 1.0 - (0.03 + 0.0025 * (300 - idx));
+ x = main_swbox_x1;
+ return true;
+ }
+
+ if (id >= TILE_WIRE_SP12_V_B_0 && id <= TILE_WIRE_SP12_V_B_23) {
+ int idx = id - TILE_WIRE_SP12_V_B_0;
+ y = 1.0 - (0.03 + 0.0025 * (300 - (idx ^ 1)));
+ x = main_swbox_x1;
+ return true;
+ }
+
+ // Global2Local
+
+ if (id >= TILE_WIRE_GLB2LOCAL_0 && id <= TILE_WIRE_GLB2LOCAL_3) {
+ int idx = id - TILE_WIRE_GLB2LOCAL_0;
+ x = main_swbox_x1 + 0.005 * (idx + 5);
+ y = main_swbox_y1;
+ return true;
+ }
+
+ // GlobalNets
+
+ if (id >= TILE_WIRE_GLB_NETWK_0 && id <= TILE_WIRE_GLB_NETWK_7) {
+ int idx = id - TILE_WIRE_GLB_NETWK_0;
+ x = main_swbox_x1;
+ y = main_swbox_y1 + 0.005 * (13 - idx);
+ return true;
+ }
+
+ // Neighbours
+
+ if (id >= TILE_WIRE_NEIGH_OP_BNL_0 && id <= TILE_WIRE_NEIGH_OP_TOP_7) {
+ int idx = id - TILE_WIRE_NEIGH_OP_BNL_0;
+ y = main_swbox_y2 - (0.0025 * (idx + 10) + 0.01 * (idx / 8));
+ x = main_swbox_x1;
+ return true;
+ }
+
+ // Local Tracks
+
+ if (id >= TILE_WIRE_LOCAL_G0_0 && id <= TILE_WIRE_LOCAL_G3_7) {
+ int idx = id - TILE_WIRE_LOCAL_G0_0;
+ float yoff = (local_swbox_y1 + local_swbox_y2) / 2 - 0.005 * 16 - 0.075;
+ x = main_swbox_x2;
+ y = yoff + 0.005 * idx + 0.05 * (idx / 8);
+ return true;
+ }
+
+ // LC Outputs
+
+ if (id >= TILE_WIRE_LUTFF_0_OUT && id <= TILE_WIRE_LUTFF_7_OUT) {
+ int idx = id - TILE_WIRE_LUTFF_0_OUT;
+ y = 1.0 - (0.03 + 0.0025 * (152 + idx));
+ x = main_swbox_x2;
+ return true;
+ }
+
+ // LC Control
+
+ if (id >= TILE_WIRE_LUTFF_GLOBAL_CEN && id <= TILE_WIRE_LUTFF_GLOBAL_S_R) {
+ int idx = id - TILE_WIRE_LUTFF_GLOBAL_CEN;
+ x = main_swbox_x2 - 0.005 * (idx + 5);
+ y = main_swbox_y1;
+ return true;
+ }
+
+ return false;
+}
+
+static bool getWireXY_local(GfxTileWireId id, float &x, float &y)
+{
+ if (id >= TILE_WIRE_LOCAL_G0_0 && id <= TILE_WIRE_LOCAL_G3_7) {
+ int idx = id - TILE_WIRE_LOCAL_G0_0;
+ float yoff = (local_swbox_y1 + local_swbox_y2) / 2 - 0.005 * 16 - 0.075;
+ x = local_swbox_x1;
+ y = yoff + 0.005 * idx + 0.05 * (idx / 8);
+ return true;
+ }
+
+ if (id >= TILE_WIRE_LUTFF_0_IN_0 && id <= TILE_WIRE_LUTFF_7_IN_3) {
+ int idx = id - TILE_WIRE_LUTFF_0_IN_0;
+ int z = idx / 4;
+ int input = idx % 4;
+ x = local_swbox_x2;
+ y = (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * input) + z * logic_cell_pitch;
+ return true;
+ }
+
+ return false;
+}
+
+void pipGfx(std::vector<GraphicElement> &g, int x, int y, float x1, float y1, float x2, float y2, float swx1,
+ float swy1, float swx2, float swy2, GraphicElement::style_t style)
+{
+ float tx = 0.5 * (x1 + x2);
+ float ty = 0.5 * (y1 + y2);
+
+ GraphicElement el;
+ el.type = GraphicElement::TYPE_ARROW;
+ el.style = style;
+
+ if (fabsf(x1 - swx1) < 0.001 && fabsf(x2 - swx1) < 0.001) {
+ tx = x1 + 0.25 * fabsf(y1 - y2);
+ goto edge_pip;
+ }
+
+ if (fabsf(x1 - swx2) < 0.001 && fabsf(x2 - swx2) < 0.001) {
+ tx = x1 - 0.25 * fabsf(y1 - y2);
+ goto edge_pip;
+ }
+
+ if (fabsf(y1 - swy1) < 0.001 && fabsf(y2 - swy1) < 0.001) {
+ ty = y1 + 0.25 * fabsf(x1 - x2);
+ goto edge_pip;
+ }
+
+ if (fabsf(y1 - swy1) < 0.001 && fabsf(y2 - swy1) < 0.001) {
+ ty = y1 + 0.25 * fabsf(x1 - x2);
+ goto edge_pip;
+ }
+
+ el.x1 = x + x1;
+ el.y1 = y + y1;
+ el.x2 = x + x2;
+ el.y2 = y + y2;
+ g.push_back(el);
+ return;
+
+edge_pip:
+ el.x1 = x + x1;
+ el.y1 = y + y1;
+ el.x2 = x + tx;
+ el.y2 = y + ty;
+ g.push_back(el);
+
+ el.x1 = x + tx;
+ el.y1 = y + ty;
+ el.x2 = x + x2;
+ el.y2 = y + y2;
+ g.push_back(el);
+}
+
+void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, GfxTileWireId dst,
+ GraphicElement::style_t style)
+{
+ float x1, y1, x2, y2;
+
+ if (getWireXY_main(src, x1, y1) && getWireXY_main(dst, x2, y2)) {
+ pipGfx(g, x, y, x1, y1, x2, y2, main_swbox_x1, main_swbox_y1, main_swbox_x2, main_swbox_y2, style);
+ return;
+ }
+
+ if (getWireXY_local(src, x1, y1) && getWireXY_local(dst, x2, y2)) {
+ pipGfx(g, x, y, x1, y1, x2, y2, local_swbox_x1, local_swbox_y1, local_swbox_x2, local_swbox_y2, style);
+ return;
+ }
+
+ if (TILE_WIRE_LUTFF_0_IN_0_LUT <= src && src <= TILE_WIRE_LUTFF_7_IN_3_LUT && TILE_WIRE_LUTFF_0_OUT <= dst &&
+ dst <= TILE_WIRE_LUTFF_7_OUT) {
+ int lut_idx = (src - TILE_WIRE_LUTFF_0_IN_0_LUT) / 4;
+ int in_idx = (src - TILE_WIRE_LUTFF_0_IN_0_LUT) % 4;
+
+ GraphicElement el;
+ el.type = GraphicElement::TYPE_ARROW;
+ el.style = style;
+ el.x1 = x + logic_cell_x1;
+ el.x2 = x + logic_cell_x2;
+ el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * in_idx) + lut_idx * logic_cell_pitch;
+ el.y2 = y + (logic_cell_y1 + logic_cell_y2) / 2 + lut_idx * logic_cell_pitch;
+ g.push_back(el);
+ return;
+ }
+
+ if (TILE_WIRE_LUTFF_0_IN_0 <= src && src <= TILE_WIRE_LUTFF_7_IN_3 && TILE_WIRE_LUTFF_0_IN_0_LUT <= dst &&
+ dst <= TILE_WIRE_LUTFF_7_IN_3_LUT) {
+ int lut_idx = (src - TILE_WIRE_LUTFF_0_IN_0) / 4;
+ int in_idx = (src - TILE_WIRE_LUTFF_0_IN_0) % 4;
+ int out_idx = (dst - TILE_WIRE_LUTFF_0_IN_0_LUT) % 4;
+
+ GraphicElement el;
+ el.type = GraphicElement::TYPE_ARROW;
+ el.style = style;
+ el.x1 = x + lut_swbox_x1;
+ el.x2 = x + lut_swbox_x2;
+ el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * in_idx) + lut_idx * logic_cell_pitch;
+ el.y2 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * out_idx) + lut_idx * logic_cell_pitch;
+ g.push_back(el);
+ return;
+ }
+
+ if (src == TILE_WIRE_CARRY_IN && dst == TILE_WIRE_CARRY_IN_MUX) {
+ GraphicElement el;
+ el.type = GraphicElement::TYPE_ARROW;
+ el.style = style;
+ el.x1 = x + logic_cell_x1 + 0.005 * 3;
+ el.x2 = el.x1;
+ el.y1 = y + 0.01;
+ el.y2 = y + 0.02;
+ g.push_back(el);
+ return;
+ }
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/xc7/gfx.h b/xc7/gfx.h
new file mode 100644
index 0000000..8ee7b0b
--- /dev/null
+++ b/xc7/gfx.h
@@ -0,0 +1,523 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef GFX_H
+#define GFX_H
+
+#include "nextpnr.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+const float main_swbox_x1 = 0.35;
+const float main_swbox_x2 = 0.60;
+const float main_swbox_y1 = 0.05;
+const float main_swbox_y2 = 0.73;
+
+const float local_swbox_x1 = 0.63;
+const float local_swbox_x2 = 0.73;
+const float local_swbox_y1 = 0.05;
+const float local_swbox_y2 = 0.55;
+
+const float lut_swbox_x1 = 0.76;
+const float lut_swbox_x2 = 0.80;
+
+const float logic_cell_x1 = 0.83;
+const float logic_cell_x2 = 0.95;
+const float logic_cell_y1 = 0.05;
+const float logic_cell_y2 = 0.10;
+const float logic_cell_pitch = 0.0625;
+
+enum GfxTileWireId
+{
+ TILE_WIRE_GLB2LOCAL_0,
+ TILE_WIRE_GLB2LOCAL_1,
+ TILE_WIRE_GLB2LOCAL_2,
+ TILE_WIRE_GLB2LOCAL_3,
+
+ TILE_WIRE_GLB_NETWK_0,
+ TILE_WIRE_GLB_NETWK_1,
+ TILE_WIRE_GLB_NETWK_2,
+ TILE_WIRE_GLB_NETWK_3,
+ TILE_WIRE_GLB_NETWK_4,
+ TILE_WIRE_GLB_NETWK_5,
+ TILE_WIRE_GLB_NETWK_6,
+ TILE_WIRE_GLB_NETWK_7,
+
+ TILE_WIRE_LOCAL_G0_0,
+ TILE_WIRE_LOCAL_G0_1,
+ TILE_WIRE_LOCAL_G0_2,
+ TILE_WIRE_LOCAL_G0_3,
+ TILE_WIRE_LOCAL_G0_4,
+ TILE_WIRE_LOCAL_G0_5,
+ TILE_WIRE_LOCAL_G0_6,
+ TILE_WIRE_LOCAL_G0_7,
+
+ TILE_WIRE_LOCAL_G1_0,
+ TILE_WIRE_LOCAL_G1_1,
+ TILE_WIRE_LOCAL_G1_2,
+ TILE_WIRE_LOCAL_G1_3,
+ TILE_WIRE_LOCAL_G1_4,
+ TILE_WIRE_LOCAL_G1_5,
+ TILE_WIRE_LOCAL_G1_6,
+ TILE_WIRE_LOCAL_G1_7,
+
+ TILE_WIRE_LOCAL_G2_0,
+ TILE_WIRE_LOCAL_G2_1,
+ TILE_WIRE_LOCAL_G2_2,
+ TILE_WIRE_LOCAL_G2_3,
+ TILE_WIRE_LOCAL_G2_4,
+ TILE_WIRE_LOCAL_G2_5,
+ TILE_WIRE_LOCAL_G2_6,
+ TILE_WIRE_LOCAL_G2_7,
+
+ TILE_WIRE_LOCAL_G3_0,
+ TILE_WIRE_LOCAL_G3_1,
+ TILE_WIRE_LOCAL_G3_2,
+ TILE_WIRE_LOCAL_G3_3,
+ TILE_WIRE_LOCAL_G3_4,
+ TILE_WIRE_LOCAL_G3_5,
+ TILE_WIRE_LOCAL_G3_6,
+ TILE_WIRE_LOCAL_G3_7,
+
+ TILE_WIRE_LUTFF_0_IN_0,
+ TILE_WIRE_LUTFF_0_IN_1,
+ TILE_WIRE_LUTFF_0_IN_2,
+ TILE_WIRE_LUTFF_0_IN_3,
+
+ TILE_WIRE_LUTFF_1_IN_0,
+ TILE_WIRE_LUTFF_1_IN_1,
+ TILE_WIRE_LUTFF_1_IN_2,
+ TILE_WIRE_LUTFF_1_IN_3,
+
+ TILE_WIRE_LUTFF_2_IN_0,
+ TILE_WIRE_LUTFF_2_IN_1,
+ TILE_WIRE_LUTFF_2_IN_2,
+ TILE_WIRE_LUTFF_2_IN_3,
+
+ TILE_WIRE_LUTFF_3_IN_0,
+ TILE_WIRE_LUTFF_3_IN_1,
+ TILE_WIRE_LUTFF_3_IN_2,
+ TILE_WIRE_LUTFF_3_IN_3,
+
+ TILE_WIRE_LUTFF_4_IN_0,
+ TILE_WIRE_LUTFF_4_IN_1,
+ TILE_WIRE_LUTFF_4_IN_2,
+ TILE_WIRE_LUTFF_4_IN_3,
+
+ TILE_WIRE_LUTFF_5_IN_0,
+ TILE_WIRE_LUTFF_5_IN_1,
+ TILE_WIRE_LUTFF_5_IN_2,
+ TILE_WIRE_LUTFF_5_IN_3,
+
+ TILE_WIRE_LUTFF_6_IN_0,
+ TILE_WIRE_LUTFF_6_IN_1,
+ TILE_WIRE_LUTFF_6_IN_2,
+ TILE_WIRE_LUTFF_6_IN_3,
+
+ TILE_WIRE_LUTFF_7_IN_0,
+ TILE_WIRE_LUTFF_7_IN_1,
+ TILE_WIRE_LUTFF_7_IN_2,
+ TILE_WIRE_LUTFF_7_IN_3,
+
+ TILE_WIRE_LUTFF_0_IN_0_LUT,
+ TILE_WIRE_LUTFF_0_IN_1_LUT,
+ TILE_WIRE_LUTFF_0_IN_2_LUT,
+ TILE_WIRE_LUTFF_0_IN_3_LUT,
+
+ TILE_WIRE_LUTFF_1_IN_0_LUT,
+ TILE_WIRE_LUTFF_1_IN_1_LUT,
+ TILE_WIRE_LUTFF_1_IN_2_LUT,
+ TILE_WIRE_LUTFF_1_IN_3_LUT,
+
+ TILE_WIRE_LUTFF_2_IN_0_LUT,
+ TILE_WIRE_LUTFF_2_IN_1_LUT,
+ TILE_WIRE_LUTFF_2_IN_2_LUT,
+ TILE_WIRE_LUTFF_2_IN_3_LUT,
+
+ TILE_WIRE_LUTFF_3_IN_0_LUT,
+ TILE_WIRE_LUTFF_3_IN_1_LUT,
+ TILE_WIRE_LUTFF_3_IN_2_LUT,
+ TILE_WIRE_LUTFF_3_IN_3_LUT,
+
+ TILE_WIRE_LUTFF_4_IN_0_LUT,
+ TILE_WIRE_LUTFF_4_IN_1_LUT,
+ TILE_WIRE_LUTFF_4_IN_2_LUT,
+ TILE_WIRE_LUTFF_4_IN_3_LUT,
+
+ TILE_WIRE_LUTFF_5_IN_0_LUT,
+ TILE_WIRE_LUTFF_5_IN_1_LUT,
+ TILE_WIRE_LUTFF_5_IN_2_LUT,
+ TILE_WIRE_LUTFF_5_IN_3_LUT,
+
+ TILE_WIRE_LUTFF_6_IN_0_LUT,
+ TILE_WIRE_LUTFF_6_IN_1_LUT,
+ TILE_WIRE_LUTFF_6_IN_2_LUT,
+ TILE_WIRE_LUTFF_6_IN_3_LUT,
+
+ TILE_WIRE_LUTFF_7_IN_0_LUT,
+ TILE_WIRE_LUTFF_7_IN_1_LUT,
+ TILE_WIRE_LUTFF_7_IN_2_LUT,
+ TILE_WIRE_LUTFF_7_IN_3_LUT,
+
+ TILE_WIRE_LUTFF_0_LOUT,
+ TILE_WIRE_LUTFF_1_LOUT,
+ TILE_WIRE_LUTFF_2_LOUT,
+ TILE_WIRE_LUTFF_3_LOUT,
+ TILE_WIRE_LUTFF_4_LOUT,
+ TILE_WIRE_LUTFF_5_LOUT,
+ TILE_WIRE_LUTFF_6_LOUT,
+
+ TILE_WIRE_LUTFF_0_OUT,
+ TILE_WIRE_LUTFF_1_OUT,
+ TILE_WIRE_LUTFF_2_OUT,
+ TILE_WIRE_LUTFF_3_OUT,
+ TILE_WIRE_LUTFF_4_OUT,
+ TILE_WIRE_LUTFF_5_OUT,
+ TILE_WIRE_LUTFF_6_OUT,
+ TILE_WIRE_LUTFF_7_OUT,
+
+ TILE_WIRE_LUTFF_0_COUT,
+ TILE_WIRE_LUTFF_1_COUT,
+ TILE_WIRE_LUTFF_2_COUT,
+ TILE_WIRE_LUTFF_3_COUT,
+ TILE_WIRE_LUTFF_4_COUT,
+ TILE_WIRE_LUTFF_5_COUT,
+ TILE_WIRE_LUTFF_6_COUT,
+ TILE_WIRE_LUTFF_7_COUT,
+
+ TILE_WIRE_LUTFF_GLOBAL_CEN,
+ TILE_WIRE_LUTFF_GLOBAL_CLK,
+ TILE_WIRE_LUTFF_GLOBAL_S_R,
+
+ TILE_WIRE_CARRY_IN,
+ TILE_WIRE_CARRY_IN_MUX,
+
+ TILE_WIRE_NEIGH_OP_BNL_0,
+ TILE_WIRE_NEIGH_OP_BNL_1,
+ TILE_WIRE_NEIGH_OP_BNL_2,
+ TILE_WIRE_NEIGH_OP_BNL_3,
+ TILE_WIRE_NEIGH_OP_BNL_4,
+ TILE_WIRE_NEIGH_OP_BNL_5,
+ TILE_WIRE_NEIGH_OP_BNL_6,
+ TILE_WIRE_NEIGH_OP_BNL_7,
+
+ TILE_WIRE_NEIGH_OP_BNR_0,
+ TILE_WIRE_NEIGH_OP_BNR_1,
+ TILE_WIRE_NEIGH_OP_BNR_2,
+ TILE_WIRE_NEIGH_OP_BNR_3,
+ TILE_WIRE_NEIGH_OP_BNR_4,
+ TILE_WIRE_NEIGH_OP_BNR_5,
+ TILE_WIRE_NEIGH_OP_BNR_6,
+ TILE_WIRE_NEIGH_OP_BNR_7,
+
+ TILE_WIRE_NEIGH_OP_BOT_0,
+ TILE_WIRE_NEIGH_OP_BOT_1,
+ TILE_WIRE_NEIGH_OP_BOT_2,
+ TILE_WIRE_NEIGH_OP_BOT_3,
+ TILE_WIRE_NEIGH_OP_BOT_4,
+ TILE_WIRE_NEIGH_OP_BOT_5,
+ TILE_WIRE_NEIGH_OP_BOT_6,
+ TILE_WIRE_NEIGH_OP_BOT_7,
+
+ TILE_WIRE_NEIGH_OP_LFT_0,
+ TILE_WIRE_NEIGH_OP_LFT_1,
+ TILE_WIRE_NEIGH_OP_LFT_2,
+ TILE_WIRE_NEIGH_OP_LFT_3,
+ TILE_WIRE_NEIGH_OP_LFT_4,
+ TILE_WIRE_NEIGH_OP_LFT_5,
+ TILE_WIRE_NEIGH_OP_LFT_6,
+ TILE_WIRE_NEIGH_OP_LFT_7,
+
+ TILE_WIRE_NEIGH_OP_RGT_0,
+ TILE_WIRE_NEIGH_OP_RGT_1,
+ TILE_WIRE_NEIGH_OP_RGT_2,
+ TILE_WIRE_NEIGH_OP_RGT_3,
+ TILE_WIRE_NEIGH_OP_RGT_4,
+ TILE_WIRE_NEIGH_OP_RGT_5,
+ TILE_WIRE_NEIGH_OP_RGT_6,
+ TILE_WIRE_NEIGH_OP_RGT_7,
+
+ TILE_WIRE_NEIGH_OP_TNL_0,
+ TILE_WIRE_NEIGH_OP_TNL_1,
+ TILE_WIRE_NEIGH_OP_TNL_2,
+ TILE_WIRE_NEIGH_OP_TNL_3,
+ TILE_WIRE_NEIGH_OP_TNL_4,
+ TILE_WIRE_NEIGH_OP_TNL_5,
+ TILE_WIRE_NEIGH_OP_TNL_6,
+ TILE_WIRE_NEIGH_OP_TNL_7,
+
+ TILE_WIRE_NEIGH_OP_TNR_0,
+ TILE_WIRE_NEIGH_OP_TNR_1,
+ TILE_WIRE_NEIGH_OP_TNR_2,
+ TILE_WIRE_NEIGH_OP_TNR_3,
+ TILE_WIRE_NEIGH_OP_TNR_4,
+ TILE_WIRE_NEIGH_OP_TNR_5,
+ TILE_WIRE_NEIGH_OP_TNR_6,
+ TILE_WIRE_NEIGH_OP_TNR_7,
+
+ TILE_WIRE_NEIGH_OP_TOP_0,
+ TILE_WIRE_NEIGH_OP_TOP_1,
+ TILE_WIRE_NEIGH_OP_TOP_2,
+ TILE_WIRE_NEIGH_OP_TOP_3,
+ TILE_WIRE_NEIGH_OP_TOP_4,
+ TILE_WIRE_NEIGH_OP_TOP_5,
+ TILE_WIRE_NEIGH_OP_TOP_6,
+ TILE_WIRE_NEIGH_OP_TOP_7,
+
+ TILE_WIRE_SP4_V_B_0,
+ TILE_WIRE_SP4_V_B_1,
+ TILE_WIRE_SP4_V_B_2,
+ TILE_WIRE_SP4_V_B_3,
+ TILE_WIRE_SP4_V_B_4,
+ TILE_WIRE_SP4_V_B_5,
+ TILE_WIRE_SP4_V_B_6,
+ TILE_WIRE_SP4_V_B_7,
+ TILE_WIRE_SP4_V_B_8,
+ TILE_WIRE_SP4_V_B_9,
+ TILE_WIRE_SP4_V_B_10,
+ TILE_WIRE_SP4_V_B_11,
+ TILE_WIRE_SP4_V_B_12,
+ TILE_WIRE_SP4_V_B_13,
+ TILE_WIRE_SP4_V_B_14,
+ TILE_WIRE_SP4_V_B_15,
+ TILE_WIRE_SP4_V_B_16,
+ TILE_WIRE_SP4_V_B_17,
+ TILE_WIRE_SP4_V_B_18,
+ TILE_WIRE_SP4_V_B_19,
+ TILE_WIRE_SP4_V_B_20,
+ TILE_WIRE_SP4_V_B_21,
+ TILE_WIRE_SP4_V_B_22,
+ TILE_WIRE_SP4_V_B_23,
+ TILE_WIRE_SP4_V_B_24,
+ TILE_WIRE_SP4_V_B_25,
+ TILE_WIRE_SP4_V_B_26,
+ TILE_WIRE_SP4_V_B_27,
+ TILE_WIRE_SP4_V_B_28,
+ TILE_WIRE_SP4_V_B_29,
+ TILE_WIRE_SP4_V_B_30,
+ TILE_WIRE_SP4_V_B_31,
+ TILE_WIRE_SP4_V_B_32,
+ TILE_WIRE_SP4_V_B_33,
+ TILE_WIRE_SP4_V_B_34,
+ TILE_WIRE_SP4_V_B_35,
+ TILE_WIRE_SP4_V_B_36,
+ TILE_WIRE_SP4_V_B_37,
+ TILE_WIRE_SP4_V_B_38,
+ TILE_WIRE_SP4_V_B_39,
+ TILE_WIRE_SP4_V_B_40,
+ TILE_WIRE_SP4_V_B_41,
+ TILE_WIRE_SP4_V_B_42,
+ TILE_WIRE_SP4_V_B_43,
+ TILE_WIRE_SP4_V_B_44,
+ TILE_WIRE_SP4_V_B_45,
+ TILE_WIRE_SP4_V_B_46,
+ TILE_WIRE_SP4_V_B_47,
+
+ TILE_WIRE_SP4_V_T_36,
+ TILE_WIRE_SP4_V_T_37,
+ TILE_WIRE_SP4_V_T_38,
+ TILE_WIRE_SP4_V_T_39,
+ TILE_WIRE_SP4_V_T_40,
+ TILE_WIRE_SP4_V_T_41,
+ TILE_WIRE_SP4_V_T_42,
+ TILE_WIRE_SP4_V_T_43,
+ TILE_WIRE_SP4_V_T_44,
+ TILE_WIRE_SP4_V_T_45,
+ TILE_WIRE_SP4_V_T_46,
+ TILE_WIRE_SP4_V_T_47,
+
+ TILE_WIRE_SP4_R_V_B_0,
+ TILE_WIRE_SP4_R_V_B_1,
+ TILE_WIRE_SP4_R_V_B_2,
+ TILE_WIRE_SP4_R_V_B_3,
+ TILE_WIRE_SP4_R_V_B_4,
+ TILE_WIRE_SP4_R_V_B_5,
+ TILE_WIRE_SP4_R_V_B_6,
+ TILE_WIRE_SP4_R_V_B_7,
+ TILE_WIRE_SP4_R_V_B_8,
+ TILE_WIRE_SP4_R_V_B_9,
+ TILE_WIRE_SP4_R_V_B_10,
+ TILE_WIRE_SP4_R_V_B_11,
+ TILE_WIRE_SP4_R_V_B_12,
+ TILE_WIRE_SP4_R_V_B_13,
+ TILE_WIRE_SP4_R_V_B_14,
+ TILE_WIRE_SP4_R_V_B_15,
+ TILE_WIRE_SP4_R_V_B_16,
+ TILE_WIRE_SP4_R_V_B_17,
+ TILE_WIRE_SP4_R_V_B_18,
+ TILE_WIRE_SP4_R_V_B_19,
+ TILE_WIRE_SP4_R_V_B_20,
+ TILE_WIRE_SP4_R_V_B_21,
+ TILE_WIRE_SP4_R_V_B_22,
+ TILE_WIRE_SP4_R_V_B_23,
+ TILE_WIRE_SP4_R_V_B_24,
+ TILE_WIRE_SP4_R_V_B_25,
+ TILE_WIRE_SP4_R_V_B_26,
+ TILE_WIRE_SP4_R_V_B_27,
+ TILE_WIRE_SP4_R_V_B_28,
+ TILE_WIRE_SP4_R_V_B_29,
+ TILE_WIRE_SP4_R_V_B_30,
+ TILE_WIRE_SP4_R_V_B_31,
+ TILE_WIRE_SP4_R_V_B_32,
+ TILE_WIRE_SP4_R_V_B_33,
+ TILE_WIRE_SP4_R_V_B_34,
+ TILE_WIRE_SP4_R_V_B_35,
+ TILE_WIRE_SP4_R_V_B_36,
+ TILE_WIRE_SP4_R_V_B_37,
+ TILE_WIRE_SP4_R_V_B_38,
+ TILE_WIRE_SP4_R_V_B_39,
+ TILE_WIRE_SP4_R_V_B_40,
+ TILE_WIRE_SP4_R_V_B_41,
+ TILE_WIRE_SP4_R_V_B_42,
+ TILE_WIRE_SP4_R_V_B_43,
+ TILE_WIRE_SP4_R_V_B_44,
+ TILE_WIRE_SP4_R_V_B_45,
+ TILE_WIRE_SP4_R_V_B_46,
+ TILE_WIRE_SP4_R_V_B_47,
+
+ TILE_WIRE_SP4_H_L_36,
+ TILE_WIRE_SP4_H_L_37,
+ TILE_WIRE_SP4_H_L_38,
+ TILE_WIRE_SP4_H_L_39,
+ TILE_WIRE_SP4_H_L_40,
+ TILE_WIRE_SP4_H_L_41,
+ TILE_WIRE_SP4_H_L_42,
+ TILE_WIRE_SP4_H_L_43,
+ TILE_WIRE_SP4_H_L_44,
+ TILE_WIRE_SP4_H_L_45,
+ TILE_WIRE_SP4_H_L_46,
+ TILE_WIRE_SP4_H_L_47,
+
+ TILE_WIRE_SP4_H_R_0,
+ TILE_WIRE_SP4_H_R_1,
+ TILE_WIRE_SP4_H_R_2,
+ TILE_WIRE_SP4_H_R_3,
+ TILE_WIRE_SP4_H_R_4,
+ TILE_WIRE_SP4_H_R_5,
+ TILE_WIRE_SP4_H_R_6,
+ TILE_WIRE_SP4_H_R_7,
+ TILE_WIRE_SP4_H_R_8,
+ TILE_WIRE_SP4_H_R_9,
+ TILE_WIRE_SP4_H_R_10,
+ TILE_WIRE_SP4_H_R_11,
+ TILE_WIRE_SP4_H_R_12,
+ TILE_WIRE_SP4_H_R_13,
+ TILE_WIRE_SP4_H_R_14,
+ TILE_WIRE_SP4_H_R_15,
+ TILE_WIRE_SP4_H_R_16,
+ TILE_WIRE_SP4_H_R_17,
+ TILE_WIRE_SP4_H_R_18,
+ TILE_WIRE_SP4_H_R_19,
+ TILE_WIRE_SP4_H_R_20,
+ TILE_WIRE_SP4_H_R_21,
+ TILE_WIRE_SP4_H_R_22,
+ TILE_WIRE_SP4_H_R_23,
+ TILE_WIRE_SP4_H_R_24,
+ TILE_WIRE_SP4_H_R_25,
+ TILE_WIRE_SP4_H_R_26,
+ TILE_WIRE_SP4_H_R_27,
+ TILE_WIRE_SP4_H_R_28,
+ TILE_WIRE_SP4_H_R_29,
+ TILE_WIRE_SP4_H_R_30,
+ TILE_WIRE_SP4_H_R_31,
+ TILE_WIRE_SP4_H_R_32,
+ TILE_WIRE_SP4_H_R_33,
+ TILE_WIRE_SP4_H_R_34,
+ TILE_WIRE_SP4_H_R_35,
+ TILE_WIRE_SP4_H_R_36,
+ TILE_WIRE_SP4_H_R_37,
+ TILE_WIRE_SP4_H_R_38,
+ TILE_WIRE_SP4_H_R_39,
+ TILE_WIRE_SP4_H_R_40,
+ TILE_WIRE_SP4_H_R_41,
+ TILE_WIRE_SP4_H_R_42,
+ TILE_WIRE_SP4_H_R_43,
+ TILE_WIRE_SP4_H_R_44,
+ TILE_WIRE_SP4_H_R_45,
+ TILE_WIRE_SP4_H_R_46,
+ TILE_WIRE_SP4_H_R_47,
+
+ TILE_WIRE_SP12_V_B_0,
+ TILE_WIRE_SP12_V_B_1,
+ TILE_WIRE_SP12_V_B_2,
+ TILE_WIRE_SP12_V_B_3,
+ TILE_WIRE_SP12_V_B_4,
+ TILE_WIRE_SP12_V_B_5,
+ TILE_WIRE_SP12_V_B_6,
+ TILE_WIRE_SP12_V_B_7,
+ TILE_WIRE_SP12_V_B_8,
+ TILE_WIRE_SP12_V_B_9,
+ TILE_WIRE_SP12_V_B_10,
+ TILE_WIRE_SP12_V_B_11,
+ TILE_WIRE_SP12_V_B_12,
+ TILE_WIRE_SP12_V_B_13,
+ TILE_WIRE_SP12_V_B_14,
+ TILE_WIRE_SP12_V_B_15,
+ TILE_WIRE_SP12_V_B_16,
+ TILE_WIRE_SP12_V_B_17,
+ TILE_WIRE_SP12_V_B_18,
+ TILE_WIRE_SP12_V_B_19,
+ TILE_WIRE_SP12_V_B_20,
+ TILE_WIRE_SP12_V_B_21,
+ TILE_WIRE_SP12_V_B_22,
+ TILE_WIRE_SP12_V_B_23,
+
+ TILE_WIRE_SP12_V_T_22,
+ TILE_WIRE_SP12_V_T_23,
+
+ TILE_WIRE_SP12_H_R_0,
+ TILE_WIRE_SP12_H_R_1,
+ TILE_WIRE_SP12_H_R_2,
+ TILE_WIRE_SP12_H_R_3,
+ TILE_WIRE_SP12_H_R_4,
+ TILE_WIRE_SP12_H_R_5,
+ TILE_WIRE_SP12_H_R_6,
+ TILE_WIRE_SP12_H_R_7,
+ TILE_WIRE_SP12_H_R_8,
+ TILE_WIRE_SP12_H_R_9,
+ TILE_WIRE_SP12_H_R_10,
+ TILE_WIRE_SP12_H_R_11,
+ TILE_WIRE_SP12_H_R_12,
+ TILE_WIRE_SP12_H_R_13,
+ TILE_WIRE_SP12_H_R_14,
+ TILE_WIRE_SP12_H_R_15,
+ TILE_WIRE_SP12_H_R_16,
+ TILE_WIRE_SP12_H_R_17,
+ TILE_WIRE_SP12_H_R_18,
+ TILE_WIRE_SP12_H_R_19,
+ TILE_WIRE_SP12_H_R_20,
+ TILE_WIRE_SP12_H_R_21,
+ TILE_WIRE_SP12_H_R_22,
+ TILE_WIRE_SP12_H_R_23,
+
+ TILE_WIRE_SP12_H_L_22,
+ TILE_WIRE_SP12_H_L_23,
+
+ TILE_WIRE_PLLIN,
+ TILE_WIRE_PLLOUT_A,
+ TILE_WIRE_PLLOUT_B
+};
+
+void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style);
+void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, GfxTileWireId dst,
+ GraphicElement::style_t style);
+
+NEXTPNR_NAMESPACE_END
+
+#endif // GFX_H
diff --git a/xc7/main.cc b/xc7/main.cc
new file mode 100644
index 0000000..cac9a39
--- /dev/null
+++ b/xc7/main.cc
@@ -0,0 +1,124 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifdef MAIN_EXECUTABLE
+
+#include <fstream>
+#include "command.h"
+#include "design_utils.h"
+#include "jsonparse.h"
+#include "log.h"
+#include "pcf.h"
+#include "timing.h"
+#include "xdl.h"
+
+USING_NEXTPNR_NAMESPACE
+
+class Xc7CommandHandler : public CommandHandler
+{
+ public:
+ Xc7CommandHandler(int argc, char **argv);
+ virtual ~Xc7CommandHandler(){};
+ std::unique_ptr<Context> createContext() override;
+ void setupArchContext(Context *ctx) override;
+ void validate() override;
+ void customAfterLoad(Context *ctx) override;
+ void customBitstream(Context *ctx) override;
+
+ protected:
+ po::options_description getArchOptions();
+};
+
+Xc7CommandHandler::Xc7CommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {}
+
+po::options_description Xc7CommandHandler::getArchOptions()
+{
+ po::options_description specific("Architecture specific options");
+ specific.add_options()("z020", "set device type to xc7z020");
+ specific.add_options()("vx980", "set device type to xc7v980");
+ specific.add_options()("package", po::value<std::string>(), "set device package");
+ specific.add_options()("pcf", po::value<std::string>(), "PCF constraints file to ingest");
+ specific.add_options()("xdl", po::value<std::string>(), "XDL file to write");
+ // specific.add_options()("tmfuzz", "run path delay estimate fuzzer");
+ return specific;
+}
+void Xc7CommandHandler::validate()
+{
+ conflicting_options(vm, "read", "json");
+ // if ((vm.count("lp384") + vm.count("lp1k") + vm.count("lp8k") + vm.count("hx1k") + vm.count("hx8k") +
+ // vm.count("up5k")) > 1)
+ // log_error("Only one device type can be set\n");
+}
+
+void Xc7CommandHandler::customAfterLoad(Context *ctx)
+{
+ if (vm.count("pcf")) {
+ std::string filename = vm["pcf"].as<std::string>();
+ std::ifstream pcf(filename);
+ if (!apply_pcf(ctx, filename, pcf))
+ log_error("Loading PCF failed.\n");
+ }
+}
+void Xc7CommandHandler::customBitstream(Context *ctx)
+{
+ if (vm.count("xdl")) {
+ std::string filename = vm["xdl"].as<std::string>();
+ std::ofstream f(filename);
+ write_xdl(ctx, f);
+ }
+}
+
+void Xc7CommandHandler::setupArchContext(Context *ctx)
+{
+ // if (vm.count("tmfuzz"))
+ // ice40DelayFuzzerMain(ctx);
+}
+
+std::unique_ptr<Context> Xc7CommandHandler::createContext()
+{
+ if (vm.count("z020")) {
+ chipArgs.type = ArchArgs::Z020;
+ chipArgs.package = "clg400";
+ }
+
+ if (vm.count("vx980")) {
+ chipArgs.type = ArchArgs::VX980;
+ chipArgs.package = "ffg1926";
+ }
+
+
+ if (chipArgs.type == ArchArgs::NONE) {
+ chipArgs.type = ArchArgs::Z020;
+ chipArgs.package = "clg400";
+ }
+
+ if (vm.count("package"))
+ chipArgs.package = vm["package"].as<std::string>();
+
+ return std::unique_ptr<Context>(new Context(chipArgs));
+}
+
+int main(int argc, char *argv[])
+{
+ Xc7CommandHandler handler(argc, argv);
+ return handler.exec();
+}
+
+#endif
diff --git a/xc7/pack.cc b/xc7/pack.cc
new file mode 100644
index 0000000..a4b57f2
--- /dev/null
+++ b/xc7/pack.cc
@@ -0,0 +1,728 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <algorithm>
+#include <iterator>
+#include <unordered_set>
+#include "cells.h"
+#include "chains.h"
+#include "design_utils.h"
+#include "log.h"
+#include "util.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+// Pack LUTs and LUT-FF pairs
+static void pack_lut_lutffs(Context *ctx)
+{
+ log_info("Packing LUT-FFs..\n");
+
+ std::unordered_set<IdString> packed_cells;
+ std::vector<std::unique_ptr<CellInfo>> new_cells;
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (ctx->verbose)
+ log_info("cell '%s' is of type '%s'\n", ci->name.c_str(ctx), ci->type.c_str(ctx));
+ if (is_lut(ctx, ci)) {
+ std::unique_ptr<CellInfo> packed = create_xc7_cell(ctx, ctx->id("XC7_LC"), ci->name.str(ctx) + "_LC");
+ std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(packed->attrs, packed->attrs.begin()));
+ packed_cells.insert(ci->name);
+ if (ctx->verbose)
+ log_info("packed cell %s into %s\n", ci->name.c_str(ctx), packed->name.c_str(ctx));
+ // See if we can pack into a DFF
+ // TODO: LUT cascade
+ NetInfo *o = ci->ports.at(ctx->id("O")).net;
+ CellInfo *dff = net_only_drives(ctx, o, is_ff, ctx->id("D"), true);
+ auto lut_bel = ci->attrs.find(ctx->id("BEL"));
+ bool packed_dff = false;
+ if (dff) {
+ if (ctx->verbose)
+ log_info("found attached dff %s\n", dff->name.c_str(ctx));
+ auto dff_bel = dff->attrs.find(ctx->id("BEL"));
+ if (lut_bel != ci->attrs.end() && dff_bel != dff->attrs.end() && lut_bel->second != dff_bel->second) {
+ // Locations don't match, can't pack
+ } else {
+ lut_to_lc(ctx, ci, packed.get(), false);
+ dff_to_lc(ctx, dff, packed.get(), false);
+ ctx->nets.erase(o->name);
+ if (dff_bel != dff->attrs.end())
+ packed->attrs[ctx->id("BEL")] = dff_bel->second;
+ packed_cells.insert(dff->name);
+ if (ctx->verbose)
+ log_info("packed cell %s into %s\n", dff->name.c_str(ctx), packed->name.c_str(ctx));
+ packed_dff = true;
+ }
+ }
+ if (!packed_dff) {
+ lut_to_lc(ctx, ci, packed.get(), true);
+ }
+ new_cells.push_back(std::move(packed));
+ }
+ }
+ for (auto pcell : packed_cells) {
+ ctx->cells.erase(pcell);
+ }
+ for (auto &ncell : new_cells) {
+ ctx->cells[ncell->name] = std::move(ncell);
+ }
+}
+
+// Pack FFs not packed as LUTFFs
+static void pack_nonlut_ffs(Context *ctx)
+{
+ log_info("Packing non-LUT FFs..\n");
+
+ std::unordered_set<IdString> packed_cells;
+ std::vector<std::unique_ptr<CellInfo>> new_cells;
+
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (is_ff(ctx, ci)) {
+ std::unique_ptr<CellInfo> packed = create_xc7_cell(ctx, ctx->id("XC7_LC"), ci->name.str(ctx) + "_DFFLC");
+ std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(packed->attrs, packed->attrs.begin()));
+ if (ctx->verbose)
+ log_info("packed cell %s into %s\n", ci->name.c_str(ctx), packed->name.c_str(ctx));
+ packed_cells.insert(ci->name);
+ dff_to_lc(ctx, ci, packed.get(), true);
+ new_cells.push_back(std::move(packed));
+ }
+ }
+ for (auto pcell : packed_cells) {
+ ctx->cells.erase(pcell);
+ }
+ for (auto &ncell : new_cells) {
+ ctx->cells[ncell->name] = std::move(ncell);
+ }
+}
+
+static bool net_is_constant(const Context *ctx, NetInfo *net, bool &value)
+{
+ if (net == nullptr)
+ return false;
+ if (net->name == ctx->id("$PACKER_GND_NET") || net->name == ctx->id("$PACKER_VCC_NET")) {
+ value = (net->name == ctx->id("$PACKER_VCC_NET"));
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// Pack carry logic
+static void pack_carries(Context *ctx)
+{
+ //log_info("Packing carries..\n");
+ // TODO
+}
+
+// "Pack" RAMs
+static void pack_ram(Context *ctx)
+{
+ //log_info("Packing RAMs..\n");
+ // TODO
+}
+
+// Merge a net into a constant net
+static void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constnet, bool constval)
+{
+ orig->driver.cell = nullptr;
+ for (auto user : orig->users) {
+ if (user.cell != nullptr) {
+ CellInfo *uc = user.cell;
+ if (ctx->verbose)
+ log_info("%s user %s\n", orig->name.c_str(ctx), uc->name.c_str(ctx));
+ if ((is_lut(ctx, uc) || is_lc(ctx, uc) || is_carry(ctx, uc)) && (user.port.str(ctx).at(0) == 'I') &&
+ !constval) {
+ uc->ports[user.port].net = nullptr;
+ } else {
+ uc->ports[user.port].net = constnet;
+ constnet->users.push_back(user);
+ }
+ }
+ }
+ orig->users.clear();
+}
+
+// Pack constants (simple implementation)
+static void pack_constants(Context *ctx)
+{
+ log_info("Packing constants..\n");
+
+ std::unique_ptr<CellInfo> gnd_cell = create_xc7_cell(ctx, ctx->id("XC7_LC"), "$PACKER_GND");
+ gnd_cell->params[ctx->id("INIT")] = "0";
+ std::unique_ptr<NetInfo> gnd_net = std::unique_ptr<NetInfo>(new NetInfo);
+ gnd_net->name = ctx->id("$PACKER_GND_NET");
+ gnd_net->driver.cell = gnd_cell.get();
+ gnd_net->driver.port = id_O;
+ gnd_cell->ports.at(id_O).net = gnd_net.get();
+
+ std::unique_ptr<CellInfo> vcc_cell = create_xc7_cell(ctx, ctx->id("XC7_LC"), "$PACKER_VCC");
+ vcc_cell->params[ctx->id("INIT")] = "1";
+ std::unique_ptr<NetInfo> vcc_net = std::unique_ptr<NetInfo>(new NetInfo);
+ vcc_net->name = ctx->id("$PACKER_VCC_NET");
+ vcc_net->driver.cell = vcc_cell.get();
+ vcc_net->driver.port = id_O;
+ vcc_cell->ports.at(id_O).net = vcc_net.get();
+
+ std::vector<IdString> dead_nets;
+
+ bool gnd_used = false;
+
+ for (auto net : sorted(ctx->nets)) {
+ NetInfo *ni = net.second;
+ if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("GND")) {
+ IdString drv_cell = ni->driver.cell->name;
+ set_net_constant(ctx, ni, gnd_net.get(), false);
+ gnd_used = true;
+ dead_nets.push_back(net.first);
+ ctx->cells.erase(drv_cell);
+ } else if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("VCC")) {
+ IdString drv_cell = ni->driver.cell->name;
+ set_net_constant(ctx, ni, vcc_net.get(), true);
+ dead_nets.push_back(net.first);
+ ctx->cells.erase(drv_cell);
+ }
+ }
+
+ if (gnd_used) {
+ ctx->cells[gnd_cell->name] = std::move(gnd_cell);
+ ctx->nets[gnd_net->name] = std::move(gnd_net);
+ }
+ // Vcc cell always inserted for now, as it may be needed during carry legalisation (TODO: trim later if actually
+ // never used?)
+ ctx->cells[vcc_cell->name] = std::move(vcc_cell);
+ ctx->nets[vcc_net->name] = std::move(vcc_net);
+
+ for (auto dn : dead_nets) {
+ ctx->nets.erase(dn);
+ }
+}
+
+static bool is_nextpnr_iob(Context *ctx, CellInfo *cell)
+{
+ return cell->type == ctx->id("$nextpnr_ibuf") || cell->type == ctx->id("$nextpnr_obuf") ||
+ cell->type == ctx->id("$nextpnr_iobuf");
+}
+
+// Pack IO buffers
+static void pack_io(Context *ctx)
+{
+ std::unordered_set<IdString> packed_cells;
+ std::vector<std::unique_ptr<CellInfo>> new_cells;
+ log_info("Packing IOs..\n");
+
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (is_nextpnr_iob(ctx, ci)) {
+ CellInfo *sb = nullptr;
+ if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) {
+ sb = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_sb_io, ctx->id("PACKAGE_PIN"), true, ci);
+
+ } else if (ci->type == ctx->id("$nextpnr_obuf")) {
+ sb = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_sb_io, ctx->id("PACKAGE_PIN"), true, ci);
+ }
+ if (sb != nullptr) {
+ // Trivial case, IOBUF used. Just destroy the net and the
+ // iobuf
+ log_info("%s feeds IOBUF %s, removing %s %s.\n", ci->name.c_str(ctx), sb->name.c_str(ctx),
+ ci->type.c_str(ctx), ci->name.c_str(ctx));
+ NetInfo *net = sb->ports.at(ctx->id("PACKAGE_PIN")).net;
+ if (net != nullptr) {
+ ctx->nets.erase(net->name);
+ sb->ports.at(ctx->id("PACKAGE_PIN")).net = nullptr;
+ }
+ if (ci->type == ctx->id("$nextpnr_iobuf")) {
+ NetInfo *net2 = ci->ports.at(ctx->id("I")).net;
+ if (net2 != nullptr) {
+ ctx->nets.erase(net2->name);
+ }
+ }
+ } else {
+ // Create a IOBUF buffer
+ std::unique_ptr<CellInfo> xc7_cell = create_xc7_cell(ctx, ctx->id("IOBUF"), ci->name.str(ctx));
+ nxio_to_sb(ctx, ci, xc7_cell.get());
+ new_cells.push_back(std::move(xc7_cell));
+ sb = new_cells.back().get();
+ }
+ packed_cells.insert(ci->name);
+ std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(sb->attrs, sb->attrs.begin()));
+ }
+ }
+ for (auto pcell : packed_cells) {
+ ctx->cells.erase(pcell);
+ }
+ for (auto &ncell : new_cells) {
+ ctx->cells[ncell->name] = std::move(ncell);
+ }
+}
+
+// Return true if a port counts as "logic" for global promotion
+static bool is_logic_port(BaseCtx *ctx, const PortRef &port)
+{
+ if (is_clock_port(ctx, port) || is_reset_port(ctx, port) || is_enable_port(ctx, port))
+ return false;
+ return !is_sb_io(ctx, port.cell) && port.cell->type != id_BUFGCTRL;
+}
+
+static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen, bool is_logic)
+{
+ std::string glb_name = net->name.str(ctx) + std::string("_$glb_") + (is_reset ? "sr" : (is_cen ? "ce" : "clk"));
+ std::unique_ptr<CellInfo> gb = create_xc7_cell(ctx, id_BUFGCTRL, "$bufg_" + glb_name);
+ gb->ports[ctx->id("I0")].net = net;
+ PortRef pr;
+ pr.cell = gb.get();
+ pr.port = ctx->id("I0");
+ net->users.push_back(pr);
+
+ pr.cell = gb.get();
+ pr.port = ctx->id("O");
+ std::unique_ptr<NetInfo> glbnet = std::unique_ptr<NetInfo>(new NetInfo());
+ glbnet->name = ctx->id(glb_name);
+ glbnet->driver = pr;
+ gb->ports[ctx->id("O")].net = glbnet.get();
+ std::vector<PortRef> keep_users;
+ for (auto user : net->users) {
+ if (is_clock_port(ctx, user) || (is_reset && is_reset_port(ctx, user)) ||
+ (is_cen && is_enable_port(ctx, user)) || (is_logic && is_logic_port(ctx, user))) {
+ user.cell->ports[user.port].net = glbnet.get();
+ glbnet->users.push_back(user);
+ } else {
+ keep_users.push_back(user);
+ }
+ }
+ net->users = keep_users;
+ ctx->nets[glbnet->name] = std::move(glbnet);
+ ctx->cells[gb->name] = std::move(gb);
+}
+
+// Simple global promoter (clock only)
+static void promote_globals(Context *ctx)
+{
+ log_info("Promoting globals..\n");
+ const int logic_fanout_thresh = 15;
+ const int enable_fanout_thresh = 5;
+ std::map<IdString, int> clock_count, reset_count, cen_count, logic_count;
+ for (auto net : sorted(ctx->nets)) {
+ NetInfo *ni = net.second;
+ if (ni->driver.cell != nullptr && !ctx->isGlobalNet(ni)) {
+ clock_count[net.first] = 0;
+ reset_count[net.first] = 0;
+ cen_count[net.first] = 0;
+
+ for (auto user : ni->users) {
+ if (is_clock_port(ctx, user))
+ clock_count[net.first]++;
+ if (is_reset_port(ctx, user))
+ reset_count[net.first]++;
+ if (is_enable_port(ctx, user))
+ cen_count[net.first]++;
+ if (is_logic_port(ctx, user))
+ logic_count[net.first]++;
+ }
+ }
+ }
+ int prom_globals = 0, prom_resets = 0, prom_cens = 0, prom_logics = 0;
+ int gbs_available = 8;
+ for (auto &cell : ctx->cells)
+ if (is_gbuf(ctx, cell.second.get()))
+ --gbs_available;
+ while (prom_globals < gbs_available) {
+ auto global_clock = std::max_element(clock_count.begin(), clock_count.end(),
+ [](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
+ return a.second < b.second;
+ });
+
+ auto global_reset = std::max_element(reset_count.begin(), reset_count.end(),
+ [](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
+ return a.second < b.second;
+ });
+ auto global_cen = std::max_element(cen_count.begin(), cen_count.end(),
+ [](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
+ return a.second < b.second;
+ });
+ auto global_logic = std::max_element(logic_count.begin(), logic_count.end(),
+ [](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
+ return a.second < b.second;
+ });
+ if (global_clock->second == 0 && prom_logics < 4 && global_logic->second > logic_fanout_thresh &&
+ (global_logic->second > global_cen->second || prom_cens >= 4) &&
+ (global_logic->second > global_reset->second || prom_resets >= 4)) {
+ NetInfo *logicnet = ctx->nets[global_logic->first].get();
+ insert_global(ctx, logicnet, false, false, true);
+ ++prom_globals;
+ ++prom_logics;
+ clock_count.erase(logicnet->name);
+ reset_count.erase(logicnet->name);
+ cen_count.erase(logicnet->name);
+ logic_count.erase(logicnet->name);
+ } else if (global_reset->second > global_clock->second && prom_resets < 4) {
+ NetInfo *rstnet = ctx->nets[global_reset->first].get();
+ insert_global(ctx, rstnet, true, false, false);
+ ++prom_globals;
+ ++prom_resets;
+ clock_count.erase(rstnet->name);
+ reset_count.erase(rstnet->name);
+ cen_count.erase(rstnet->name);
+ logic_count.erase(rstnet->name);
+ } else if (global_cen->second > global_clock->second && prom_cens < 4 &&
+ global_cen->second > enable_fanout_thresh) {
+ NetInfo *cennet = ctx->nets[global_cen->first].get();
+ insert_global(ctx, cennet, false, true, false);
+ ++prom_globals;
+ ++prom_cens;
+ clock_count.erase(cennet->name);
+ reset_count.erase(cennet->name);
+ cen_count.erase(cennet->name);
+ logic_count.erase(cennet->name);
+ } else if (global_clock->second != 0) {
+ NetInfo *clknet = ctx->nets[global_clock->first].get();
+ insert_global(ctx, clknet, false, false, false);
+ ++prom_globals;
+ clock_count.erase(clknet->name);
+ reset_count.erase(clknet->name);
+ cen_count.erase(clknet->name);
+ logic_count.erase(clknet->name);
+ } else {
+ break;
+ }
+ }
+}
+
+// spliceLUT adds a pass-through LUT LC between the given cell's output port
+// and either all users or only non_LUT users.
+static std::unique_ptr<CellInfo> spliceLUT(Context *ctx, CellInfo *ci, IdString portId, bool onlyNonLUTs)
+{
+ auto port = ci->ports[portId];
+
+ NPNR_ASSERT(port.net != nullptr);
+
+ // Create pass-through LUT.
+ std::unique_ptr<CellInfo> pt =
+ create_xc7_cell(ctx, ctx->id("XC7_LC"), ci->name.str(ctx) + "$nextpnr_" + portId.str(ctx) + "_lut_through");
+ pt->params[ctx->id("INIT")] = "65280"; // output is always I3
+
+ // Create LUT output net.
+ std::unique_ptr<NetInfo> out_net = std::unique_ptr<NetInfo>(new NetInfo);
+ out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_" + portId.str(ctx) + "_lut_through_net");
+ out_net->driver.cell = pt.get();
+ out_net->driver.port = ctx->id("O");
+ pt->ports.at(ctx->id("O")).net = out_net.get();
+
+ // New users of the original cell's port
+ std::vector<PortRef> new_users;
+ for (const auto &user : port.net->users) {
+ if (onlyNonLUTs && user.cell->type == ctx->id("XC7_LC")) {
+ new_users.push_back(user);
+ continue;
+ }
+ // Rewrite pointer into net in user.
+ user.cell->ports[user.port].net = out_net.get();
+ // Add user to net.
+ PortRef pr;
+ pr.cell = user.cell;
+ pr.port = user.port;
+ out_net->users.push_back(pr);
+ }
+
+ // Add LUT to new users.
+ PortRef pr;
+ pr.cell = pt.get();
+ pr.port = ctx->id("I3");
+ new_users.push_back(pr);
+ pt->ports.at(ctx->id("I3")).net = port.net;
+
+ // Replace users of the original net.
+ port.net->users = new_users;
+
+ ctx->nets[out_net->name] = std::move(out_net);
+ return pt;
+}
+
+// Pack special functions
+static void pack_special(Context *ctx)
+{
+ log_info("Packing special functions..\n");
+
+ std::unordered_set<IdString> packed_cells;
+ std::vector<std::unique_ptr<CellInfo>> new_cells;
+
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (ci->type == id_BUFGCTRL) {
+ ci->params.emplace(ctx->id("PRESELECT_I0"), "FALSE");
+ ci->params.emplace(ctx->id("CE0INV"), "CE0");
+ ci->params.emplace(ctx->id("S0INV"), "S0");
+ ci->params.emplace(ctx->id("IGNORE0INV"), "IGNORE0");
+ ci->params.emplace(ctx->id("CE1INV"), "CE1");
+ ci->params.emplace(ctx->id("S1INV"), "S1");
+ ci->params.emplace(ctx->id("IGNORE1INV"), "IGNORE1");
+ } else if (ci->type == id_MMCME2_ADV) {
+ ci->params.emplace(ctx->id("BANDWIDTH"), "OPTIMIZED");
+ ci->params.emplace(ctx->id("CLKBURST_ENABLE"), "FALSE");
+ ci->params.emplace(ctx->id("CLKBURST_REPEAT"), "FALSE");
+ ci->params.emplace(ctx->id("CLKFBIN_EDGE"), "FALSE");
+ ci->params.emplace(ctx->id("CLKFBIN_NOCOUNT"), "TRUE");
+ ci->params.emplace(ctx->id("CLKFBOUT_EDGE"), "FALSE");
+ ci->params.emplace(ctx->id("CLKFBOUT_EN"), "TRUE");
+ ci->params.emplace(ctx->id("CLKFBOUT_FRAC_EN"), "FALSE");
+ ci->params.emplace(ctx->id("CLKFBOUT_FRAC_WF_FALL"), "FALSE");
+ ci->params.emplace(ctx->id("CLKFBOUT_FRAC_WF_RISE"), "FALSE");
+ ci->params.emplace(ctx->id("CLKFBOUT_NOCOUNT"), "TRUE");
+ ci->params.emplace(ctx->id("CLKFBOUT_USE_FINE_PS"), "FALSE");
+ ci->params.emplace(ctx->id("CLKINSELINV"), "CLKINSEL");
+ ci->params.emplace(ctx->id("CLKOUT0_EDGE"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT0_EN"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT0_FRAC_EN"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT0_FRAC_WF_FALL"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT0_FRAC_WF_RISE"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT0_NOCOUNT"), "TRUE");
+ ci->params.emplace(ctx->id("CLKOUT0_USE_FINE_PS"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT1_EDGE"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT1_EN"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT1_NOCOUNT"), "TRUE");
+ ci->params.emplace(ctx->id("CLKOUT1_USE_FINE_PS"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT2_EDGE"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT2_EN"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT2_NOCOUNT"), "TRUE");
+ ci->params.emplace(ctx->id("CLKOUT2_USE_FINE_PS"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT3_EDGE"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT3_EN"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT3_NOCOUNT"), "TRUE");
+ ci->params.emplace(ctx->id("CLKOUT3_USE_FINE_PS"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT4_CASCADE"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT4_EDGE"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT4_EN"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT4_NOCOUNT"), "TRUE");
+ ci->params.emplace(ctx->id("CLKOUT4_USE_FINE_PS"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT5_EDGE"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT5_EN"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT5_NOCOUNT"), "TRUE");
+ ci->params.emplace(ctx->id("CLKOUT5_USE_FINE_PS"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT6_EDGE"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT6_EN"), "FALSE");
+ ci->params.emplace(ctx->id("CLKOUT6_NOCOUNT"), "TRUE");
+ ci->params.emplace(ctx->id("CLKOUT6_USE_FINE_PS"), "FALSE");
+ ci->params.emplace(ctx->id("COMPENSATION"), "INTERNAL");
+ ci->params.emplace(ctx->id("DIRECT_PATH_CNTRL"), "FALSE");
+ ci->params.emplace(ctx->id("DIVCLK_EDGE"), "FALSE");
+ ci->params.emplace(ctx->id("DIVCLK_NOCOUNT"), "TRUE");
+ ci->params.emplace(ctx->id("EN_VCO_DIV1"), "FALSE");
+ ci->params.emplace(ctx->id("EN_VCO_DIV6"), "FALSE");
+ ci->params.emplace(ctx->id("GTS_WAIT"), "FALSE");
+ ci->params.emplace(ctx->id("HVLF_CNT_TEST_EN"), "FALSE");
+ ci->params.emplace(ctx->id("INTERP_TEST"), "FALSE");
+ ci->params.emplace(ctx->id("IN_DLY_EN"), "TRUE");
+ ci->params.emplace(ctx->id("LF_LOW_SEL"), "FALSE");
+ ci->params.emplace(ctx->id("MMCM_EN"), "TRUE");
+ ci->params.emplace(ctx->id("PERF0_USE_CLK"), "FALSE");
+ ci->params.emplace(ctx->id("PERF1_USE_CLK"), "FALSE");
+ ci->params.emplace(ctx->id("PERF2_USE_CLK"), "FALSE");
+ ci->params.emplace(ctx->id("PERF3_USE_CLK"), "FALSE");
+ ci->params.emplace(ctx->id("PSENINV"), "PSEN");
+ ci->params.emplace(ctx->id("PSINCDECINV"), "PSINCDEC");
+ ci->params.emplace(ctx->id("PWRDWNINV"), "PWRDWN");
+ ci->params.emplace(ctx->id("RSTINV"), "RST");
+ ci->params.emplace(ctx->id("SEL_HV_NMOS"), "FALSE");
+ ci->params.emplace(ctx->id("SEL_LV_NMOS"), "FALSE");
+ ci->params.emplace(ctx->id("SEL_SLIPD"), "FALSE");
+ ci->params.emplace(ctx->id("SS_EN"), "FALSE");
+ ci->params.emplace(ctx->id("SS_MODE"), "CENTER_HIGH");
+ ci->params.emplace(ctx->id("STARTUP_WAIT"), "FALSE");
+ ci->params.emplace(ctx->id("SUP_SEL_AREG"), "FALSE");
+ ci->params.emplace(ctx->id("SUP_SEL_DREG"), "FALSE");
+ ci->params.emplace(ctx->id("TMUX_MUX_SEL"), "00");
+ ci->params.emplace(ctx->id("VLF_HIGH_DIS_B"), "TRUE");
+ ci->params.emplace(ctx->id("VLF_HIGH_PWDN_B"), "TRUE");
+ //ci->params.emplace(ctx->id("MMCME2_ADV:mmcm_adv_inst:");
+ ci->params.emplace(ctx->id("ANALOG_MISC"), "0000");
+ ci->params.emplace(ctx->id("AVDD_COMP_SET"), "011");
+ ci->params.emplace(ctx->id("AVDD_VBG_PD"), "110");
+ ci->params.emplace(ctx->id("AVDD_VBG_SEL"), "1001");
+ ci->params.emplace(ctx->id("CLKBURST_CNT"), "1");
+ ci->params.emplace(ctx->id("CLKFBIN_HT"), "1");
+ ci->params.emplace(ctx->id("CLKFBIN_LT"), "1");
+ ci->params.emplace(ctx->id("CLKFBIN_MULT"), "1");
+ ci->params.emplace(ctx->id("CLKFBOUT_DT"), "0");
+ ci->params.emplace(ctx->id("CLKFBOUT_FRAC"), "0");
+ ci->params.emplace(ctx->id("CLKFBOUT_HT"), "1");
+ ci->params.emplace(ctx->id("CLKFBOUT_LT"), "1");
+ ci->params.emplace(ctx->id("CLKFBOUT_MULT_F"), "40.5");
+ ci->params.emplace(ctx->id("CLKFBOUT_MX"), "00");
+ ci->params.emplace(ctx->id("CLKFBOUT_PHASE"), "0.0");
+ ci->params.emplace(ctx->id("CLKFBOUT_PM_FALL"), "000");
+ ci->params.emplace(ctx->id("CLKFBOUT_PM_RISE"), "000");
+ ci->params.emplace(ctx->id("CLKFB_MUX_SEL"), "000");
+ ci->params.emplace(ctx->id("CLKIN1_MUX_SEL"), "000");
+ ci->params.emplace(ctx->id("CLKIN1_PERIOD"), "8");
+ ci->params.emplace(ctx->id("CLKIN2_MUX_SEL"), "000");
+ ci->params.emplace(ctx->id("CLKIN2_PERIOD"), "0");
+ ci->params.emplace(ctx->id("CLKOUT0_DIVIDE_F"), "16.875");
+ ci->params.emplace(ctx->id("CLKOUT0_DT"), "0");
+ ci->params.emplace(ctx->id("CLKOUT0_DUTY_CYCLE"), "0.5");
+ ci->params.emplace(ctx->id("CLKOUT0_FRAC"), "0");
+ ci->params.emplace(ctx->id("CLKOUT0_HT"), "1");
+ ci->params.emplace(ctx->id("CLKOUT0_LT"), "1");
+ ci->params.emplace(ctx->id("CLKOUT0_MX"), "00");
+ ci->params.emplace(ctx->id("CLKOUT0_PHASE"), "0.0");
+ ci->params.emplace(ctx->id("CLKOUT0_PM_FALL"), "000");
+ ci->params.emplace(ctx->id("CLKOUT0_PM_RISE"), "000");
+ ci->params.emplace(ctx->id("CLKOUT1_DIVIDE"), "1");
+ ci->params.emplace(ctx->id("CLKOUT1_DT"), "0");
+ ci->params.emplace(ctx->id("CLKOUT1_DUTY_CYCLE"), "0.5");
+ ci->params.emplace(ctx->id("CLKOUT1_HT"), "1");
+ ci->params.emplace(ctx->id("CLKOUT1_LT"), "1");
+ ci->params.emplace(ctx->id("CLKOUT1_MX"), "00");
+ ci->params.emplace(ctx->id("CLKOUT1_PHASE"), "0.0");
+ ci->params.emplace(ctx->id("CLKOUT1_PM"), "000");
+ ci->params.emplace(ctx->id("CLKOUT2_DIVIDE"), "1");
+ ci->params.emplace(ctx->id("CLKOUT2_DT"), "0");
+ ci->params.emplace(ctx->id("CLKOUT2_DUTY_CYCLE"), "0.5");
+ ci->params.emplace(ctx->id("CLKOUT2_HT"), "1");
+ ci->params.emplace(ctx->id("CLKOUT2_LT"), "1");
+ ci->params.emplace(ctx->id("CLKOUT2_MX"), "00");
+ ci->params.emplace(ctx->id("CLKOUT2_PHASE"), "0.0");
+ ci->params.emplace(ctx->id("CLKOUT2_PM"), "000");
+ ci->params.emplace(ctx->id("CLKOUT3_DIVIDE"), "1");
+ ci->params.emplace(ctx->id("CLKOUT3_DT"), "0");
+ ci->params.emplace(ctx->id("CLKOUT3_DUTY_CYCLE"), "0.5");
+ ci->params.emplace(ctx->id("CLKOUT3_HT"), "1");
+ ci->params.emplace(ctx->id("CLKOUT3_LT"), "1");
+ ci->params.emplace(ctx->id("CLKOUT3_MX"), "00");
+ ci->params.emplace(ctx->id("CLKOUT3_PHASE"), "0.0");
+ ci->params.emplace(ctx->id("CLKOUT3_PM"), "000");
+ ci->params.emplace(ctx->id("CLKOUT4_DIVIDE"), "1");
+ ci->params.emplace(ctx->id("CLKOUT4_DT"), "0");
+ ci->params.emplace(ctx->id("CLKOUT4_DUTY_CYCLE"), "0.5");
+ ci->params.emplace(ctx->id("CLKOUT4_HT"), "1");
+ ci->params.emplace(ctx->id("CLKOUT4_LT"), "1");
+ ci->params.emplace(ctx->id("CLKOUT4_MX"), "00");
+ ci->params.emplace(ctx->id("CLKOUT4_PHASE"), "0.0");
+ ci->params.emplace(ctx->id("CLKOUT4_PM"), "000");
+ ci->params.emplace(ctx->id("CLKOUT5_DIVIDE"), "1");
+ ci->params.emplace(ctx->id("CLKOUT5_DT"), "0");
+ ci->params.emplace(ctx->id("CLKOUT5_DUTY_CYCLE"), "0.5");
+ ci->params.emplace(ctx->id("CLKOUT5_HT"), "1");
+ ci->params.emplace(ctx->id("CLKOUT5_LT"), "1");
+ ci->params.emplace(ctx->id("CLKOUT5_MX"), "00");
+ ci->params.emplace(ctx->id("CLKOUT5_PHASE"), "0.0");
+ ci->params.emplace(ctx->id("CLKOUT5_PM"), "000");
+ ci->params.emplace(ctx->id("CLKOUT6_DIVIDE"), "1");
+ ci->params.emplace(ctx->id("CLKOUT6_DT"), "0");
+ ci->params.emplace(ctx->id("CLKOUT6_DUTY_CYCLE"), "0.5");
+ ci->params.emplace(ctx->id("CLKOUT6_HT"), "1");
+ ci->params.emplace(ctx->id("CLKOUT6_LT"), "1");
+ ci->params.emplace(ctx->id("CLKOUT6_MX"), "00");
+ ci->params.emplace(ctx->id("CLKOUT6_PHASE"), "0.0");
+ ci->params.emplace(ctx->id("CLKOUT6_PM"), "000");
+ ci->params.emplace(ctx->id("CONTROL_0"), "1111001101111100");
+ ci->params.emplace(ctx->id("CONTROL_1"), "0111110101001101");
+ ci->params.emplace(ctx->id("CONTROL_2"), "0101000001000010");
+ ci->params.emplace(ctx->id("CONTROL_3"), "1110101111001000");
+ ci->params.emplace(ctx->id("CONTROL_4"), "1101010011011111");
+ ci->params.emplace(ctx->id("CONTROL_5"), "1010110111111011");
+ ci->params.emplace(ctx->id("CONTROL_6"), "1011001011000011");
+ ci->params.emplace(ctx->id("CONTROL_7"), "0100110000101110");
+ ci->params.emplace(ctx->id("CP"), "0000");
+ ci->params.emplace(ctx->id("CP_BIAS_TRIP_SET"), "0");
+ ci->params.emplace(ctx->id("CP_RES"), "01");
+ ci->params.emplace(ctx->id("DIVCLK_DIVIDE"), "5");
+ ci->params.emplace(ctx->id("DIVCLK_HT"), "1");
+ ci->params.emplace(ctx->id("DIVCLK_LT"), "1");
+ ci->params.emplace(ctx->id("DVDD_COMP_SET"), "011");
+ ci->params.emplace(ctx->id("DVDD_VBG_PD"), "110");
+ ci->params.emplace(ctx->id("DVDD_VBG_SEL"), "1001");
+ ci->params.emplace(ctx->id("EN_CURR_SINK"), "11");
+ ci->params.emplace(ctx->id("FINE_PS_FRAC"), "0");
+ ci->params.emplace(ctx->id("FREQ_BB_USE_CLK0"), "0");
+ ci->params.emplace(ctx->id("FREQ_BB_USE_CLK1"), "0");
+ ci->params.emplace(ctx->id("FREQ_BB_USE_CLK2"), "0");
+ ci->params.emplace(ctx->id("FREQ_BB_USE_CLK3"), "0");
+ ci->params.emplace(ctx->id("FREQ_COMP"), "01");
+ ci->params.emplace(ctx->id("HROW_DLY_SET"), "0");
+ ci->params.emplace(ctx->id("HVLF_CNT_TEST"), "0");
+ ci->params.emplace(ctx->id("INTERP_EN"), "00010000");
+ ci->params.emplace(ctx->id("IN_DLY_MX_CVDD"), "011000");
+ ci->params.emplace(ctx->id("IN_DLY_MX_DVDD"), "000001");
+ ci->params.emplace(ctx->id("IN_DLY_SET"), "38");
+ ci->params.emplace(ctx->id("LFHF"), "11");
+ ci->params.emplace(ctx->id("LF_NEN"), "10");
+ ci->params.emplace(ctx->id("LF_PEN"), "00");
+ ci->params.emplace(ctx->id("LOCK_CNT"), "128");
+ ci->params.emplace(ctx->id("LOCK_FB_DLY"), "3");
+ ci->params.emplace(ctx->id("LOCK_REF_DLY"), "3");
+ ci->params.emplace(ctx->id("LOCK_SAT_HIGH"), "160");
+ ci->params.emplace(ctx->id("MAN_LF"), "000");
+ ci->params.emplace(ctx->id("MVDD_SEL"), "11");
+ ci->params.emplace(ctx->id("PERF0_MUX_SEL"), "000");
+ ci->params.emplace(ctx->id("PERF1_MUX_SEL"), "000");
+ ci->params.emplace(ctx->id("PERF2_MUX_SEL"), "000");
+ ci->params.emplace(ctx->id("PERF3_MUX_SEL"), "000");
+ ci->params.emplace(ctx->id("PFD"), "0100001");
+ ci->params.emplace(ctx->id("REF_JITTER1"), "0.01");
+ ci->params.emplace(ctx->id("REF_JITTER2"), "0.01");
+ ci->params.emplace(ctx->id("RES"), "0000");
+ ci->params.emplace(ctx->id("SKEW_FLOP_INV"), "0000");
+ ci->params.emplace(ctx->id("SPARE_ANALOG"), "00000");
+ ci->params.emplace(ctx->id("SPARE_DIGITAL"), "00000");
+ ci->params.emplace(ctx->id("SS_MOD_PERIOD"), "10000");
+ ci->params.emplace(ctx->id("SS_STEPS"), "011");
+ ci->params.emplace(ctx->id("SS_STEPS_INIT"), "010");
+ ci->params.emplace(ctx->id("SYNTH_CLK_DIV"), "11");
+ ci->params.emplace(ctx->id("UNLOCK_CNT"), "64");
+ ci->params.emplace(ctx->id("VREF_START"), "01");
+
+ ci->params[ctx->id("COMPENSATION")] = "INTERNAL";
+ }
+ }
+
+ for (auto pcell : packed_cells) {
+ ctx->cells.erase(pcell);
+ }
+ for (auto &ncell : new_cells) {
+ ctx->cells[ncell->name] = std::move(ncell);
+ }
+}
+
+// Main pack function
+bool Arch::pack()
+{
+ Context *ctx = getCtx();
+ try {
+ log_break();
+ pack_constants(ctx);
+ // TODO
+ // promote_globals(ctx);
+ pack_io(ctx);
+ pack_lut_lutffs(ctx);
+ pack_nonlut_ffs(ctx);
+ pack_carries(ctx);
+ pack_ram(ctx);
+ pack_special(ctx);
+ ctx->assignArchInfo();
+ constrain_chains(ctx);
+ ctx->assignArchInfo();
+ log_info("Checksum: 0x%08x\n", ctx->checksum());
+ return true;
+ } catch (log_execution_error_exception) {
+ return false;
+ }
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/xc7/pcf.cc b/xc7/pcf.cc
new file mode 100644
index 0000000..f56a177
--- /dev/null
+++ b/xc7/pcf.cc
@@ -0,0 +1,84 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "pcf.h"
+#include <sstream>
+#include "log.h"
+
+#include <boost/algorithm/string.hpp>
+
+NEXTPNR_NAMESPACE_BEGIN
+
+// Read a w
+
+// Apply PCF constraints to a pre-packing design
+bool apply_pcf(Context *ctx, std::string filename, std::istream &in)
+{
+ try {
+ if (!in)
+ log_error("failed to open PCF file\n");
+ std::string line;
+ while (std::getline(in, line)) {
+ size_t cstart = line.find("#");
+ if (cstart != std::string::npos)
+ line = line.substr(0, cstart);
+ std::stringstream ss(line);
+ std::vector<std::string> words;
+ std::string tmp;
+ while (ss >> tmp)
+ words.push_back(tmp);
+ if (words.size() == 0)
+ continue;
+ std::string cmd = words.at(0);
+ if (cmd == "COMP") {
+ size_t args_end = 1;
+ while (args_end < words.size() && words.at(args_end).at(0) == '-')
+ args_end++;
+ std::string cell = words.at(args_end);
+ boost::trim_if(cell, boost::is_any_of("\""));
+ std::string pin = words.at(args_end + 4);
+ boost::trim_if(pin, boost::is_any_of("\""));
+ auto fnd_cell = ctx->cells.find(ctx->id(cell));
+ if (fnd_cell == ctx->cells.end()) {
+ log_warning("unmatched pcf constraint %s\n", cell.c_str());
+ } else {
+ BelId pin_bel = ctx->getPackagePinBel(pin);
+ if (pin_bel == BelId())
+ log_error("package does not have a pin named %s\n", pin.c_str());
+ fnd_cell->second->attrs[ctx->id("BEL")] = ctx->getBelName(pin_bel).str(ctx);
+ log_info("constrained '%s' to bel '%s'\n", cell.c_str(),
+ fnd_cell->second->attrs[ctx->id("BEL")].c_str());
+ }
+ } else if (cmd == "NET") {
+ // TODO
+ } else if (cmd == "PIN") {
+ // TODO
+ } else {
+ log_error("unsupported pcf command '%s'\n", cmd.c_str());
+ }
+ }
+ ctx->settings.emplace(ctx->id("project/input/pcf"), filename);
+ return true;
+ } catch (log_execution_error_exception) {
+ return false;
+ }
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/xc7/pcf.h b/xc7/pcf.h
new file mode 100644
index 0000000..ecc81e5
--- /dev/null
+++ b/xc7/pcf.h
@@ -0,0 +1,34 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef PCF_H
+#define PCF_H
+
+#include <iostream>
+#include "nextpnr.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+// Apply PCF constraints to a pre-packing design
+bool apply_pcf(Context *ctx, std::string filename, std::istream &in);
+
+NEXTPNR_NAMESPACE_END
+
+#endif // ROUTE_H
diff --git a/xc7/picorv32.pcf b/xc7/picorv32.pcf
new file mode 100644
index 0000000..bc8e084
--- /dev/null
+++ b/xc7/picorv32.pcf
@@ -0,0 +1,3 @@
+NET "clki" PERIOD = 8 nS ;
+PIN "clki_pin" = BEL "clki.PAD" PINNAME PAD;
+PIN "clki_pin" CLOCK_DEDICATED_ROUTE = FALSE;
diff --git a/xc7/picorv32.proj b/xc7/picorv32.proj
new file mode 100644
index 0000000..a8c83bd
--- /dev/null
+++ b/xc7/picorv32.proj
@@ -0,0 +1,15 @@
+{
+ "project": {
+ "version": "1",
+ "name": "picorv32",
+ "arch": {
+ "name": "ice40",
+ "type": "hx8k",
+ "package": "ct256"
+ },
+ "input": {
+ "json": "picorv32.json",
+ "pcf": "icebreaker.pcf"
+ }
+ }
+}
diff --git a/xc7/picorv32.sh b/xc7/picorv32.sh
new file mode 100755
index 0000000..ec2aee8
--- /dev/null
+++ b/xc7/picorv32.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+set -ex
+rm -f picorv32.v
+wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v
+yosys picorv32.ys
+set +e
+../nextpnr-xc7 --json picorv32.json --xdl picorv32.xdl --pcf picorv32.pcf --freq 125
+set -e
+xdl -xdl2ncd picorv32.xdl
+#bitgen -w blinky.ncd -g UnconstrainedPins:Allow
+trce picorv32.ncd -v 10
diff --git a/xc7/picorv32.ys b/xc7/picorv32.ys
new file mode 100644
index 0000000..e6eec6c
--- /dev/null
+++ b/xc7/picorv32.ys
@@ -0,0 +1,55 @@
+read_verilog picorv32.v
+read_verilog picorv32_top.v
+read_verilog 125MHz_to_60MHz.v
+
+#synth_xilinx -top picorv32
+
+#begin:
+ read_verilog -lib +/xilinx/cells_sim.v
+ read_verilog -lib +/xilinx/cells_xtra.v
+# read_verilog -lib +/xilinx/brams_bb.v
+# read_verilog -lib +/xilinx/drams_bb.v
+ hierarchy -check -top top
+
+#flatten: (only if -flatten)
+ proc
+ flatten
+
+#coarse:
+ synth -run coarse
+
+#bram:
+# memory_bram -rules +/xilinx/brams.txt
+# techmap -map +/xilinx/brams_map.v
+#
+#dram:
+# memory_bram -rules +/xilinx/drams.txt
+# techmap -map +/xilinx/drams_map.v
+
+fine:
+ opt -fast -full
+ memory_map
+ dffsr2dff
+# dff2dffe
+ opt -full
+ techmap -map +/techmap.v #-map +/xilinx/arith_map.v
+ opt -fast
+
+map_luts:
+ abc -luts 2:2,3,6:5 #,10,20 [-dff]
+ clean
+
+map_cells:
+ techmap -map +/xilinx/cells_map.v
+ dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT
+ clean
+
+check:
+ hierarchy -check
+ stat
+ check -noinit
+
+#edif: (only if -edif)
+# write_edif <file-name>
+
+write_json picorv32.json
diff --git a/xc7/picorv32_benchmark.py b/xc7/picorv32_benchmark.py
new file mode 100755
index 0000000..a4ec581
--- /dev/null
+++ b/xc7/picorv32_benchmark.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+import os, sys, threading
+from os import path
+import subprocess
+import re
+
+num_runs = 8
+
+if not path.exists("picorv32.json"):
+ subprocess.run(["wget", "https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v"], check=True)
+ subprocess.run(["yosys", "-q", "-p", "synth_ice40 -json picorv32.json -top top", "picorv32.v", "picorv32_top.v"], check=True)
+
+fmax = {}
+
+if not path.exists("picorv32_work"):
+ os.mkdir("picorv32_work")
+
+threads = []
+
+for i in range(num_runs):
+ def runner(run):
+ ascfile = "picorv32_work/picorv32_s{}.asc".format(run)
+ if path.exists(ascfile):
+ os.remove(ascfile)
+ result = subprocess.run(["../nextpnr-ice40", "--hx8k", "--seed", str(run), "--json", "picorv32.json", "--asc", ascfile, "--freq", "70"], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
+ if result.returncode != 0:
+ print("Run {} failed!".format(run))
+ else:
+ icetime_res = subprocess.check_output(["icetime", "-d", "hx8k", ascfile])
+ fmax_m = re.search(r'\(([0-9.]+) MHz\)', icetime_res.decode('utf-8'))
+ fmax[run] = float(fmax_m.group(1))
+ threads.append(threading.Thread(target=runner, args=[i+1]))
+
+for t in threads: t.start()
+for t in threads: t.join()
+
+fmax_min = min(fmax.values())
+fmax_max = max(fmax.values())
+fmax_avg = sum(fmax.values()) / len(fmax)
+
+print("{}/{} runs passed".format(len(fmax), num_runs))
+print("icetime: min = {} MHz, avg = {} MHz, max = {} MHz".format(fmax_min, fmax_avg, fmax_max))
diff --git a/xc7/picorv32_top.v b/xc7/picorv32_top.v
new file mode 100644
index 0000000..3735aa1
--- /dev/null
+++ b/xc7/picorv32_top.v
@@ -0,0 +1,44 @@
+module top (
+ input clki, resetn,
+ output trap,
+
+ output mem_valid,
+ output mem_instr,
+ input mem_ready,
+
+ output [31:0] mem_addr,
+ output [31:0] mem_wdata,
+ output [ 3:0] mem_wstrb,
+ input [31:0] mem_rdata
+);
+
+ wire clk;
+ BUFGCTRL clk_gb (
+ .I0(clki),
+ .CE0(1'b1),
+ .CE1(1'b0),
+ .S0(1'b1),
+ .S1(1'b0),
+ .IGNORE0(1'b0),
+ .IGNORE1(1'b0),
+ .O(clk)
+ );
+
+ picorv32 #(
+ .ENABLE_COUNTERS(0),
+ .TWO_STAGE_SHIFT(0),
+ .CATCH_MISALIGN(0),
+ .CATCH_ILLINSN(0)
+ ) cpu (
+ .clk (clk ),
+ .resetn (resetn ),
+ .trap (trap ),
+ .mem_valid(mem_valid),
+ .mem_instr(mem_instr),
+ .mem_ready(mem_ready),
+ .mem_addr (mem_addr ),
+ .mem_wdata(mem_wdata),
+ .mem_wstrb(mem_wstrb),
+ .mem_rdata(mem_rdata)
+ );
+endmodule
diff --git a/xc7/project.cc b/xc7/project.cc
new file mode 100644
index 0000000..5c8def1
--- /dev/null
+++ b/xc7/project.cc
@@ -0,0 +1,58 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "project.h"
+#include <boost/filesystem/convenience.hpp>
+#include <fstream>
+#include "log.h"
+#include "pcf.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path)
+{
+ root.put("project.arch.package", ctx->archArgs().package);
+ if (ctx->settings.find(ctx->id("project/input/pcf")) != ctx->settings.end()) {
+ std::string fn = ctx->settings[ctx->id("project/input/pcf")];
+ root.put("project.input.pcf", make_relative(fn, path).string());
+ }
+}
+
+std::unique_ptr<Context> ProjectHandler::createContext(pt::ptree &root)
+{
+ ArchArgs chipArgs;
+ std::string arch_type = root.get<std::string>("project.arch.type");
+ if (arch_type == "z020") {
+ chipArgs.type = ArchArgs::Z020;
+ }
+ chipArgs.package = root.get<std::string>("project.arch.package");
+
+ return std::unique_ptr<Context>(new Context(chipArgs));
+}
+
+void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path)
+{
+ auto input = root.get_child("project").get_child("input");
+ boost::filesystem::path pcf = boost::filesystem::path(path) / input.get<std::string>("pcf");
+ std::ifstream f(pcf.string());
+ if (!apply_pcf(ctx, input.get<std::string>("pcf"), f))
+ log_error("Loading PCF failed.\n");
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/xc7/tmfuzz.py b/xc7/tmfuzz.py
new file mode 100644
index 0000000..4ec2a54
--- /dev/null
+++ b/xc7/tmfuzz.py
@@ -0,0 +1,357 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# ../nextpnr-ice40 --hx8k --tmfuzz > tmfuzz_hx8k.txt
+# ../nextpnr-ice40 --lp8k --tmfuzz > tmfuzz_lp8k.txt
+# ../nextpnr-ice40 --up5k --tmfuzz > tmfuzz_up5k.txt
+
+import numpy as np
+import matplotlib.pyplot as plt
+from collections import defaultdict
+
+device = "hx8k"
+# device = "lp8k"
+# device = "up5k"
+
+sel_src_type = "LUTFF_OUT"
+sel_dst_type = "LUTFF_IN_LUT"
+
+#%% Read fuzz data
+
+src_dst_pairs = defaultdict(lambda: 0)
+
+delay_data = list()
+all_delay_data = list()
+
+delay_map_sum = np.zeros((41, 41))
+delay_map_sum2 = np.zeros((41, 41))
+delay_map_count = np.zeros((41, 41))
+
+same_tile_delays = list()
+neighbour_tile_delays = list()
+
+type_delta_data = dict()
+
+with open("tmfuzz_%s.txt" % device, "r") as f:
+ for line in f:
+ line = line.split()
+
+ if line[0] == "dst":
+ dst_xy = (int(line[1]), int(line[2]))
+ dst_type = line[3]
+ dst_wire = line[4]
+
+ src_xy = (int(line[1]), int(line[2]))
+ src_type = line[3]
+ src_wire = line[4]
+
+ delay = int(line[5])
+ estdelay = int(line[6])
+
+ all_delay_data.append((delay, estdelay))
+
+ src_dst_pairs[src_type, dst_type] += 1
+
+ dx = dst_xy[0] - src_xy[0]
+ dy = dst_xy[1] - src_xy[1]
+
+ if src_type == sel_src_type and dst_type == sel_dst_type:
+ if dx == 0 and dy == 0:
+ same_tile_delays.append(delay)
+
+ elif abs(dx) <= 1 and abs(dy) <= 1:
+ neighbour_tile_delays.append(delay)
+
+ else:
+ delay_data.append((delay, estdelay, dx, dy, 0, 0, 0))
+
+ relx = 20 + dst_xy[0] - src_xy[0]
+ rely = 20 + dst_xy[1] - src_xy[1]
+
+ if (0 <= relx <= 40) and (0 <= rely <= 40):
+ delay_map_sum[relx, rely] += delay
+ delay_map_sum2[relx, rely] += delay*delay
+ delay_map_count[relx, rely] += 1
+
+ if dst_type == sel_dst_type:
+ if src_type not in type_delta_data:
+ type_delta_data[src_type] = list()
+
+ type_delta_data[src_type].append((dx, dy, delay))
+
+delay_data = np.array(delay_data)
+all_delay_data = np.array(all_delay_data)
+max_delay = np.max(delay_data[:, 0:2])
+
+mean_same_tile_delays = np.mean(neighbour_tile_delays)
+mean_neighbour_tile_delays = np.mean(neighbour_tile_delays)
+
+print("Avg same tile delay: %.2f (%.2f std, N=%d)" % \
+ (mean_same_tile_delays, np.std(same_tile_delays), len(same_tile_delays)))
+print("Avg neighbour tile delay: %.2f (%.2f std, N=%d)" % \
+ (mean_neighbour_tile_delays, np.std(neighbour_tile_delays), len(neighbour_tile_delays)))
+
+#%% Apply simple low-weight bluring to fill gaps
+
+for i in range(0):
+ neigh_sum = np.zeros((41, 41))
+ neigh_sum2 = np.zeros((41, 41))
+ neigh_count = np.zeros((41, 41))
+
+ for x in range(41):
+ for y in range(41):
+ for p in range(-1, 2):
+ for q in range(-1, 2):
+ if p == 0 and q == 0:
+ continue
+ if 0 <= (x+p) <= 40:
+ if 0 <= (y+q) <= 40:
+ neigh_sum[x, y] += delay_map_sum[x+p, y+q]
+ neigh_sum2[x, y] += delay_map_sum2[x+p, y+q]
+ neigh_count[x, y] += delay_map_count[x+p, y+q]
+
+ delay_map_sum += 0.1 * neigh_sum
+ delay_map_sum2 += 0.1 * neigh_sum2
+ delay_map_count += 0.1 * neigh_count
+
+delay_map = delay_map_sum / delay_map_count
+delay_map_std = np.sqrt(delay_map_count*delay_map_sum2 - delay_map_sum**2) / delay_map_count
+
+#%% Print src-dst-pair summary
+
+print("Src-Dst-Type pair summary:")
+for cnt, src, dst in sorted([(v, k[0], k[1]) for k, v in src_dst_pairs.items()]):
+ print("%20s %20s %5d%s" % (src, dst, cnt, " *" if src == sel_src_type and dst == sel_dst_type else ""))
+print()
+
+#%% Plot estimate vs actual delay
+
+plt.figure(figsize=(8, 3))
+plt.title("Estimate vs Actual Delay")
+plt.plot(all_delay_data[:, 0], all_delay_data[:, 1], ".")
+plt.plot(delay_data[:, 0], delay_data[:, 1], ".")
+plt.plot([0, max_delay], [0, max_delay], "k")
+plt.ylabel("Estimated Delay")
+plt.xlabel("Actual Delay")
+plt.grid()
+plt.show()
+
+#%% Plot delay heatmap and std dev heatmap
+
+plt.figure(figsize=(9, 3))
+plt.subplot(121)
+plt.title("Actual Delay Map")
+plt.imshow(delay_map)
+plt.colorbar()
+plt.subplot(122)
+plt.title("Standard Deviation")
+plt.imshow(delay_map_std)
+plt.colorbar()
+plt.show()
+
+#%% Generate Model #0
+
+def nonlinearPreprocessor0(dx, dy):
+ dx, dy = abs(dx), abs(dy)
+ values = [1.0]
+ values.append(dx + dy)
+ return np.array(values)
+
+A = np.zeros((41*41, len(nonlinearPreprocessor0(0, 0))))
+b = np.zeros(41*41)
+
+index = 0
+for x in range(41):
+ for y in range(41):
+ if delay_map_count[x, y] > 0:
+ A[index, :] = nonlinearPreprocessor0(x-20, y-20)
+ b[index] = delay_map[x, y]
+ index += 1
+
+model0_params, _, _, _ = np.linalg.lstsq(A, b)
+print("Model #0 parameters:", model0_params)
+
+model0_map = np.zeros((41, 41))
+for x in range(41):
+ for y in range(41):
+ v = np.dot(model0_params, nonlinearPreprocessor0(x-20, y-20))
+ model0_map[x, y] = v
+
+plt.figure(figsize=(9, 3))
+plt.subplot(121)
+plt.title("Model #0 Delay Map")
+plt.imshow(model0_map)
+plt.colorbar()
+plt.subplot(122)
+plt.title("Model #0 Error Map")
+plt.imshow(model0_map - delay_map)
+plt.colorbar()
+plt.show()
+
+for i in range(delay_data.shape[0]):
+ dx = delay_data[i, 2]
+ dy = delay_data[i, 3]
+ delay_data[i, 4] = np.dot(model0_params, nonlinearPreprocessor0(dx, dy))
+
+plt.figure(figsize=(8, 3))
+plt.title("Model #0 vs Actual Delay")
+plt.plot(delay_data[:, 0], delay_data[:, 4], ".")
+plt.plot(delay_map.flat, model0_map.flat, ".")
+plt.plot([0, max_delay], [0, max_delay], "k")
+plt.ylabel("Model #0 Delay")
+plt.xlabel("Actual Delay")
+plt.grid()
+plt.show()
+
+print("In-sample RMS error: %f" % np.sqrt(np.nanmean((delay_map - model0_map)**2)))
+print("Out-of-sample RMS error: %f" % np.sqrt(np.nanmean((delay_data[:, 0] - delay_data[:, 4])**2)))
+print()
+
+#%% Generate Model #1
+
+def nonlinearPreprocessor1(dx, dy):
+ dx, dy = abs(dx), abs(dy)
+ values = [1.0]
+ values.append(dx + dy) # 1-norm
+ values.append((dx**2 + dy**2)**(1/2)) # 2-norm
+ values.append((dx**3 + dy**3)**(1/3)) # 3-norm
+ return np.array(values)
+
+A = np.zeros((41*41, len(nonlinearPreprocessor1(0, 0))))
+b = np.zeros(41*41)
+
+index = 0
+for x in range(41):
+ for y in range(41):
+ if delay_map_count[x, y] > 0:
+ A[index, :] = nonlinearPreprocessor1(x-20, y-20)
+ b[index] = delay_map[x, y]
+ index += 1
+
+model1_params, _, _, _ = np.linalg.lstsq(A, b)
+print("Model #1 parameters:", model1_params)
+
+model1_map = np.zeros((41, 41))
+for x in range(41):
+ for y in range(41):
+ v = np.dot(model1_params, nonlinearPreprocessor1(x-20, y-20))
+ model1_map[x, y] = v
+
+plt.figure(figsize=(9, 3))
+plt.subplot(121)
+plt.title("Model #1 Delay Map")
+plt.imshow(model1_map)
+plt.colorbar()
+plt.subplot(122)
+plt.title("Model #1 Error Map")
+plt.imshow(model1_map - delay_map)
+plt.colorbar()
+plt.show()
+
+for i in range(delay_data.shape[0]):
+ dx = delay_data[i, 2]
+ dy = delay_data[i, 3]
+ delay_data[i, 5] = np.dot(model1_params, nonlinearPreprocessor1(dx, dy))
+
+plt.figure(figsize=(8, 3))
+plt.title("Model #1 vs Actual Delay")
+plt.plot(delay_data[:, 0], delay_data[:, 5], ".")
+plt.plot(delay_map.flat, model1_map.flat, ".")
+plt.plot([0, max_delay], [0, max_delay], "k")
+plt.ylabel("Model #1 Delay")
+plt.xlabel("Actual Delay")
+plt.grid()
+plt.show()
+
+print("In-sample RMS error: %f" % np.sqrt(np.nanmean((delay_map - model1_map)**2)))
+print("Out-of-sample RMS error: %f" % np.sqrt(np.nanmean((delay_data[:, 0] - delay_data[:, 5])**2)))
+print()
+
+#%% Generate Model #2
+
+def nonlinearPreprocessor2(v):
+ return np.array([1, v, np.sqrt(v)])
+
+A = np.zeros((41*41, len(nonlinearPreprocessor2(0))))
+b = np.zeros(41*41)
+
+index = 0
+for x in range(41):
+ for y in range(41):
+ if delay_map_count[x, y] > 0:
+ A[index, :] = nonlinearPreprocessor2(model1_map[x, y])
+ b[index] = delay_map[x, y]
+ index += 1
+
+model2_params, _, _, _ = np.linalg.lstsq(A, b)
+print("Model #2 parameters:", model2_params)
+
+model2_map = np.zeros((41, 41))
+for x in range(41):
+ for y in range(41):
+ v = np.dot(model1_params, nonlinearPreprocessor1(x-20, y-20))
+ v = np.dot(model2_params, nonlinearPreprocessor2(v))
+ model2_map[x, y] = v
+
+plt.figure(figsize=(9, 3))
+plt.subplot(121)
+plt.title("Model #2 Delay Map")
+plt.imshow(model2_map)
+plt.colorbar()
+plt.subplot(122)
+plt.title("Model #2 Error Map")
+plt.imshow(model2_map - delay_map)
+plt.colorbar()
+plt.show()
+
+for i in range(delay_data.shape[0]):
+ dx = delay_data[i, 2]
+ dy = delay_data[i, 3]
+ delay_data[i, 6] = np.dot(model2_params, nonlinearPreprocessor2(delay_data[i, 5]))
+
+plt.figure(figsize=(8, 3))
+plt.title("Model #2 vs Actual Delay")
+plt.plot(delay_data[:, 0], delay_data[:, 6], ".")
+plt.plot(delay_map.flat, model2_map.flat, ".")
+plt.plot([0, max_delay], [0, max_delay], "k")
+plt.ylabel("Model #2 Delay")
+plt.xlabel("Actual Delay")
+plt.grid()
+plt.show()
+
+print("In-sample RMS error: %f" % np.sqrt(np.nanmean((delay_map - model2_map)**2)))
+print("Out-of-sample RMS error: %f" % np.sqrt(np.nanmean((delay_data[:, 0] - delay_data[:, 6])**2)))
+print()
+
+#%% Generate deltas for different source net types
+
+type_deltas = dict()
+
+print("Delay deltas for different src types:")
+for src_type in sorted(type_delta_data.keys()):
+ deltas = list()
+
+ for dx, dy, delay in type_delta_data[src_type]:
+ dx = abs(dx)
+ dy = abs(dy)
+
+ if dx > 1 or dy > 1:
+ est = model0_params[0] + model0_params[1] * (dx + dy)
+ else:
+ est = mean_neighbour_tile_delays
+ deltas.append(delay - est)
+
+ print("%15s: %8.2f (std %6.2f)" % (\
+ src_type, np.mean(deltas), np.std(deltas)))
+
+ type_deltas[src_type] = np.mean(deltas)
+
+#%% Print C defs of model parameters
+
+print("--snip--")
+print("%d, %d, %d," % (mean_neighbour_tile_delays, 128 * model0_params[0], 128 * model0_params[1]))
+print("%d, %d, %d, %d," % (128 * model1_params[0], 128 * model1_params[1], 128 * model1_params[2], 128 * model1_params[3]))
+print("%d, %d, %d," % (128 * model2_params[0], 128 * model2_params[1], 128 * model2_params[2]))
+print("%d, %d, %d, %d" % (type_deltas["LOCAL"], type_deltas["LUTFF_IN"], \
+ (type_deltas["SP4_H"] + type_deltas["SP4_V"]) / 2,
+ (type_deltas["SP12_H"] + type_deltas["SP12_V"]) / 2))
+print("--snap--")
diff --git a/xc7/xdl.cc b/xc7/xdl.cc
new file mode 100644
index 0000000..2687d73
--- /dev/null
+++ b/xc7/xdl.cc
@@ -0,0 +1,285 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+#include "xdl.h"
+#include <cctype>
+#include <vector>
+#include "cells.h"
+#include "log.h"
+#include "nextpnr.h"
+#include "util.h"
+#include <boost/range/adaptor/reversed.hpp>
+
+#include "torc/Physical.hpp"
+using namespace torc::architecture::xilinx;
+using namespace torc::physical;
+
+NEXTPNR_NAMESPACE_BEGIN
+
+DesignSharedPtr create_torc_design(const Context *ctx)
+{
+ auto designPtr = Factory::newDesignPtr("name", torc_info->ddb->getDeviceName(), ctx->args.package, "-1", "");
+
+ std::unordered_map<int32_t, InstanceSharedPtr> site_to_instance;
+ std::vector<std::pair<std::string, std::string>> lut_inputs;
+ lut_inputs.reserve(6);
+
+ auto bel_to_lut = [](const BelId bel) {
+ switch (torc_info->bel_to_loc[bel.index].z) {
+ case 0:
+ case 4:
+ return "A";
+ break;
+ case 1:
+ case 5:
+ return "B";
+ break;
+ case 2:
+ case 6:
+ return "C";
+ break;
+ case 3:
+ case 7:
+ return "D";
+ break;
+ default:
+ throw;
+ }
+ };
+
+ for (const auto &cell : ctx->cells) {
+ const char *type;
+ if (cell.second->type == id_SLICE_LUT6)
+ type = "SLICEL";
+ else if (cell.second->type == id_IOB33 || cell.second->type == id_IOB18 || cell.second->type == id_BUFGCTRL || cell.second->type == id_PS7 || cell.second->type == id_MMCME2_ADV)
+ type = cell.second->type.c_str(ctx);
+ else
+ log_error("Unsupported cell type '%s'.\n", cell.second->type.c_str(ctx));
+
+ auto site_index = torc_info->bel_to_site_index[cell.second->bel.index];
+ auto ret = site_to_instance.emplace(site_index, nullptr);
+ InstanceSharedPtr instPtr;
+ if (ret.second) {
+ instPtr = Factory::newInstancePtr(cell.second->name.str(ctx), type, "", "");
+ auto b = designPtr->addInstance(instPtr);
+ assert(b);
+ ret.first->second = instPtr;
+
+ const auto &tile_info = torc_info->bel_to_tile_info(cell.second->bel.index);
+ instPtr->setTile(tile_info.getName());
+ instPtr->setSite(torc_info->bel_to_name(cell.second->bel.index));
+ } else
+ instPtr = ret.first->second;
+
+ if (cell.second->type == id_SLICE_LUT6) {
+ std::string setting, name, value;
+ const std::string lut = bel_to_lut(cell.second->bel);
+
+ setting = lut + "6LUT";
+ value = "#LUT:O6=";
+ lut_inputs.clear();
+ if (get_net_or_empty(cell.second.get(), id_I1))
+ lut_inputs.emplace_back("A1", "~A1");
+ if (get_net_or_empty(cell.second.get(), id_I2))
+ lut_inputs.emplace_back("A2", "~A2");
+ if (get_net_or_empty(cell.second.get(), id_I3))
+ lut_inputs.emplace_back("A3", "~A3");
+ if (get_net_or_empty(cell.second.get(), id_I4))
+ lut_inputs.emplace_back("A4", "~A4");
+ if (get_net_or_empty(cell.second.get(), id_I5))
+ lut_inputs.emplace_back("A5", "~A5");
+ if (get_net_or_empty(cell.second.get(), id_I6))
+ lut_inputs.emplace_back("A6", "~A6");
+ const auto &init = cell.second->params[ctx->id("INIT")];
+ // Assume from Yosys that INIT masks of less than 32 bits are output as uint32_t
+ if (lut_inputs.size() < 6) {
+ auto init_as_uint = boost::lexical_cast<uint32_t>(init);
+ NPNR_ASSERT(init_as_uint <= ((1ull << (1u << lut_inputs.size())) - 1));
+ if (lut_inputs.empty())
+ value += init;
+ else {
+ unsigned n = 0;
+ for (unsigned o = 0; o < (1u << lut_inputs.size()); ++o) {
+ if (!((init_as_uint >> o) & 1))
+ continue;
+ if (n++ > 0)
+ value += "+";
+ value += "(";
+ value += (o & 1) ? lut_inputs[0].first : lut_inputs[0].second;
+ for (unsigned i = 1; i < lut_inputs.size(); ++i) {
+ value += "*";
+ value += o & (1 << i) ? lut_inputs[i].first : lut_inputs[i].second;
+ }
+ value += ")";
+ }
+ }
+ }
+ // Otherwise as a bit string
+ else {
+ NPNR_ASSERT(init.size() == (1u << lut_inputs.size()));
+ unsigned n = 0;
+ for (unsigned i = 0; i < init.size(); ++i) {
+ if (init[init.size() - 1 - i] == '0')
+ continue;
+ if (n++ > 0)
+ value += "+";
+ value += "(";
+ value += (i & 1) ? lut_inputs[0].first : lut_inputs[0].second;
+ for (unsigned j = 1; j < lut_inputs.size(); ++j) {
+ value += "*";
+ value += i & (1 << j) ? lut_inputs[j].first : lut_inputs[j].second;
+ }
+ value += ")";
+ }
+ }
+
+ auto it = cell.second->params.find(ctx->id("LUT_NAME"));
+ if (it != cell.second->params.end())
+ name = it->second;
+ else
+ name = cell.second->name.str(ctx);
+ boost::replace_all(name, ":", "\\:");
+ instPtr->setConfig(setting, name, value);
+
+ auto O = get_net_or_empty(cell.second.get(), id_O);
+ if (O) {
+ setting = lut;
+ setting += "USED";
+ instPtr->setConfig(setting, "", "0");
+ }
+
+ auto OQ = get_net_or_empty(cell.second.get(), id_OQ);
+ if (OQ) {
+ setting = lut;
+ setting += "FF";
+ name = OQ->name.str(ctx);
+ boost::replace_all(name, ":", "\\:");
+ instPtr->setConfig(setting, name, "#FF");
+ instPtr->setConfig(setting + "MUX", "", "O6");
+ instPtr->setConfig(setting + "INIT", "", cell.second->params.at(ctx->id("FFINIT")));
+
+ if (cell.second->lcInfo.negClk)
+ instPtr->setConfig("CLKINV", "", "CLK_B");
+ else
+ instPtr->setConfig("CLKINV", "", "CLK");
+
+ if (get_net_or_empty(cell.second.get(), id_SR)) {
+ instPtr->setConfig(setting + "SR", "", cell.second->params.at(id_SR));
+ instPtr->setConfig("SRUSEDMUX", "", "IN");
+ }
+ instPtr->setConfig("SYNC_ATTR", "", cell.second->params.at(ctx->id("SYNC_ATTR")));
+ if (get_net_or_empty(cell.second.get(), ctx->id("CE")))
+ instPtr->setConfig("CEUSEDMUX", "", "IN");
+
+ }
+ } else if (cell.second->type == id_IOB33) {
+ if (get_net_or_empty(cell.second.get(), id_I)) {
+ instPtr->setConfig("IUSED", "", "0");
+ instPtr->setConfig("IBUF_LOW_PWR", "", "TRUE");
+ instPtr->setConfig("ISTANDARD", "", "LVCMOS33");
+ } else {
+ instPtr->setConfig("OUSED", "", "0");
+ instPtr->setConfig("OSTANDARD", "", "LVCMOS33");
+ instPtr->setConfig("DRIVE", "", "12");
+ instPtr->setConfig("SLEW", "", "SLOW");
+ }
+ } else if (cell.second->type == id_IOB18) {
+ if (get_net_or_empty(cell.second.get(), id_I)) {
+ instPtr->setConfig("IUSED", "", "0");
+ instPtr->setConfig("IBUF_LOW_PWR", "", "TRUE");
+ instPtr->setConfig("ISTANDARD", "", "LVCMOS18");
+ } else {
+ instPtr->setConfig("OUSED", "", "0");
+ instPtr->setConfig("OSTANDARD", "", "LVCMOS18");
+ instPtr->setConfig("DRIVE", "", "12");
+ instPtr->setConfig("SLEW", "", "SLOW");
+ }
+ } else if (cell.second->type == id_BUFGCTRL || cell.second->type == id_PS7 || cell.second->type == id_MMCME2_ADV) {
+ for (const auto& i : cell.second->params)
+ instPtr->setConfig(i.first.str(ctx), "", i.second);
+ } else
+ log_error("Unsupported cell type '%s'.\n", cell.second->type.c_str(ctx));
+ }
+
+ for (const auto &net : ctx->nets) {
+ const auto &driver = net.second->driver;
+
+ auto site_index = torc_info->bel_to_site_index[driver.cell->bel.index];
+ auto instPtr = site_to_instance.at(site_index);
+
+ auto netPtr = Factory::newNetPtr(net.second->name.str(ctx));
+
+ auto pin_name = driver.port.str(ctx);
+ // For all LUT based inputs and outputs (I1-I6,O,OQ,OMUX) then change the I/O into the LUT
+ if (driver.cell->type == id_SLICE_LUT6 && (pin_name[0] == 'I' || pin_name[0] == 'O')) {
+ const auto lut = bel_to_lut(driver.cell->bel);
+ pin_name[0] = lut[0];
+ }
+ // e.g. Convert DDRARB[0] -> DDRARB0
+ pin_name.erase(std::remove_if(pin_name.begin(), pin_name.end(), boost::is_any_of("[]")), pin_name.end());
+ auto pinPtr = Factory::newInstancePinPtr(instPtr, pin_name);
+ netPtr->addSource(pinPtr);
+
+ if (!net.second->users.empty()) {
+ for (const auto &user : net.second->users) {
+ site_index = torc_info->bel_to_site_index[user.cell->bel.index];
+ instPtr = site_to_instance.at(site_index);
+
+ pin_name = user.port.str(ctx);
+ // For all LUT based inputs and outputs (I1-I6,O,OQ,OMUX) then change the I/O into the LUT
+ if (user.cell->type == id_SLICE_LUT6 && (pin_name[0] == 'I' || pin_name[0] == 'O')) {
+ const auto lut = bel_to_lut(user.cell->bel);
+ pin_name[0] = lut[0];
+ }
+ else {
+ // e.g. Convert DDRARB[0] -> DDRARB0
+ pin_name.erase(std::remove_if(pin_name.begin(), pin_name.end(), boost::is_any_of("[]")), pin_name.end());
+ }
+ pinPtr = Factory::newInstancePinPtr(instPtr, pin_name);
+ netPtr->addSink(pinPtr);
+ }
+
+ auto b = designPtr->addNet(netPtr);
+ assert(b);
+
+ for (const auto &i : net.second->wires) {
+ const auto &pip_map = i.second;
+ if (pip_map.pip == PipId())
+ continue;
+ ExtendedWireInfo ewi_src(*torc_info->ddb, torc_info->pip_to_arc[pip_map.pip.index].getSourceTilewire());
+ ExtendedWireInfo ewi_dst(*torc_info->ddb, torc_info->pip_to_arc[pip_map.pip.index].getSinkTilewire());
+ auto p = Factory::newPip(ewi_src.mTileName, ewi_src.mWireName, ewi_dst.mWireName,
+ ePipUnidirectionalBuffered);
+ netPtr->addPip(p);
+ }
+ }
+ }
+
+ return designPtr;
+}
+
+void write_xdl(const Context *ctx, std::ostream &out)
+{
+ XdlExporter exporter(out);
+ auto designPtr = create_torc_design(ctx);
+ exporter(designPtr);
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/xc7/xdl.h b/xc7/xdl.h
new file mode 100644
index 0000000..19bc547
--- /dev/null
+++ b/xc7/xdl.h
@@ -0,0 +1,33 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 David Shah <david@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef XC7_BITSTREAM_H
+#define XC7_BITSTREAM_H
+
+#include <iostream>
+#include "nextpnr.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+void write_xdl(const Context *ctx, std::ostream &out);
+
+NEXTPNR_NAMESPACE_END
+
+#endif