diff --git a/.gitignore b/.gitignore index 9b3ffd02..bf22d038 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 @@ install_manifest.txt /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 00000000..c0b8228d --- /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 33a703d5..c75a32fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ if (STATIC_BUILD) 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 @@ if (BUILD_PYTHON) # 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 @@ if (BUILD_PYTHON) 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 (BUILD_PYTHON) 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 (BUILD_PYTHON) 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 (BUILD_PYTHON) 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 (BUILD_PYTHON) 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 5b79d1fb..5733a40d 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 b3eb4267..2a18a1a2 100644 --- a/common/place_common.cc +++ b/common/place_common.cc @@ -37,6 +37,8 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type 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 cbc0df90..2c3f6683 100644 --- a/common/router1.cc +++ b/common/router1.cc @@ -512,6 +512,21 @@ struct Router1 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 7751e6f1..738b7c68 100644 --- a/gui/application.cc +++ b/gui/application.cc @@ -51,9 +51,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) 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 @@ bool Application::notify(QObject *receiver, QEvent *event) 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 235dd2cb..958d8840 100644 --- a/gui/designwidget.cc +++ b/gui/designwidget.cc @@ -317,6 +317,12 @@ void DesignWidget::newContext(Context *ctx) for (const auto &wire : ctx->getWires()) { wireMap[std::pair(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(loc.x, loc.y)].push_back(wire); + } #endif auto wireGetter = [](Context *ctx, WireId id) { return ctx->getWireName(id); }; getTreeByElementType(ElementType::WIRE) @@ -706,8 +712,9 @@ void DesignWidget::onSelectionChanged(int num, const QItemSelection &, const QIt 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 00000000..3b010c7b --- /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 00000000..3ee64727 --- /dev/null +++ b/gui/xc7/mainwindow.cc @@ -0,0 +1,51 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * 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, 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 00000000..bb6a4cf1 --- /dev/null +++ b/gui/xc7/mainwindow.h @@ -0,0 +1,45 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * 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, 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 00000000..03585ec0 --- /dev/null +++ b/gui/xc7/nextpnr.qrc @@ -0,0 +1,2 @@ + + diff --git a/torc b/torc new file mode 160000 index 00000000..a5a4ec05 --- /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 00000000..c4178b0a --- /dev/null +++ b/xc7/arch.cc @@ -0,0 +1,904 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 Serge Bazanski + * + * 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 +#include +#include +#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 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(what.str(1)); + const auto y = boost::lexical_cast(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(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> delay_lookup; + std::unordered_map 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).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 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_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).expandSegmentSinks(currentTilewire, arcs, DDB::eExpandDirectionNone, + // false /* inUseTied */, true /*inUseRegular */, + // true /* inUseIrregular */, !clb /* inUseRoutethrough */); + { + // expand the segment + TilewireVector segment; + const_cast(*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).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(new TorcInfo(this, "xc7z020", args.package)); + } else if (args.type == ArchArgs::VX980) { + torc_info = std::unique_ptr(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> Arch::getBelAttrs(BelId bel) const +{ + std::vector> 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 Arch::getBelPins(BelId bel) const +{ + std::vector 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> Arch::getWireAttrs(WireId wire) const +{ + std::vector> 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> Arch::getPipAttrs(PipId pip) const +{ + std::vector> 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 Arch::getGroups() const +{ + std::vector ret; + NPNR_ASSERT("TODO"); + return ret; +} + +std::vector Arch::getGroupBels(GroupId group) const +{ + std::vector ret; + return ret; +} + +std::vector Arch::getGroupWires(GroupId group) const +{ + std::vector ret; + return ret; +} + +std::vector Arch::getGroupPips(GroupId group) const +{ + std::vector ret; + NPNR_ASSERT("TODO"); + return ret; +} + +std::vector Arch::getGroupGroups(GroupId group) const +{ + std::vector 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 Arch::getDecalGraphics(DecalId decal) const +{ + std::vector 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 00000000..699d357e --- /dev/null +++ b/xc7/arch.h @@ -0,0 +1,934 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * + * 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 +{ + size_t operator()(const Segments::SegmentReference &s) const + { + size_t seed = 0; + boost::hash_combine(seed, hash()(s.getCompactSegmentIndex())); + boost::hash_combine(seed, hash()(s.getAnchorTileIndex())); + return seed; + } +}; +template <> struct equal_to +{ + bool operator()(const Segments::SegmentReference &lhs, const Segments::SegmentReference &rhs) const + { + return lhs.getAnchorTileIndex() == rhs.getAnchorTileIndex() && + lhs.getCompactSegmentIndex() == rhs.getCompactSegmentIndex(); + } +}; +template <> struct hash +{ + size_t operator()(const Tilewire &t) const { return hash_value(t); } +}; + +template <> struct hash +{ + 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 struct RelPtr +{ + int32_t offset; + + // void set(const T *ptr) { + // offset = reinterpret_cast(ptr) - + // reinterpret_cast(this); + // } + + const T *get() const { return reinterpret_cast(reinterpret_cast(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 name; + int32_t type; + int32_t num_bel_wires; + RelPtr 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 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 name; + int32_t num_uphill, num_downhill; + RelPtr pips_uphill, pips_downhill; + + int32_t num_bel_pins; + RelPtr bel_pins; + + int32_t num_segments; + RelPtr segments; + + int32_t fast_delay; + int32_t slow_delay; + + int8_t x, y, z; + WireType type; +}); + +NPNR_PACKED_STRUCT(struct PackagePinPOD { + RelPtr name; + int32_t bel_index; +}); + +NPNR_PACKED_STRUCT(struct PackageInfoPOD { + RelPtr name; + int32_t num_pins; + RelPtr 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 name; + int32_t num_bits; + RelPtr bits; +}); + +NPNR_PACKED_STRUCT(struct TileInfoPOD { + int8_t cols, rows; + int16_t num_config_entries; + RelPtr 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 tiles_nonrouting; + RelPtr switches; + RelPtr ierens; +}); + +NPNR_PACKED_STRUCT(struct BelConfigEntryPOD { + RelPtr entry_name; + RelPtr 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 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 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 bel_data; + RelPtr wire_data; + RelPtr pip_data; + RelPtr tile_grid; + RelPtr bits_info; + RelPtr bel_config; + RelPtr packages_data; + RelPtr cell_timing; +}); + +struct TorcInfo +{ + TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::string &inPackageName); + TorcInfo() = delete; + std::unique_ptr 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 bel_to_site_index; + int num_bels; + std::vector site_index_to_bel; + std::vector site_index_to_type; + std::vector bel_to_loc; + std::unordered_map segment_to_wire; + std::unordered_map trivial_to_wire; + std::vector wire_to_tilewire; + int num_wires; + std::vector wire_to_delay; + //std::vector> wire_to_pips_uphill; + std::vector> wire_to_pips_downhill; + std::vector pip_to_arc; + int num_pips; + int width; + int height; + std::vector wire_is_global; + std::vector> tile_to_xy; + + TorcInfo(const std::string &inDeviceName, const std::string &inPackageName); +}; +extern std::unique_ptr 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::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 wire_by_name; + mutable std::unordered_map pip_by_name; + mutable std::unordered_map bel_by_loc; + + // std::vector bel_carry; + std::vector bel_to_cell; + std::vector wire_to_net; + std::vector pip_to_net; + // std::vector 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> getBelAttrs(BelId bel) const; + + WireId getBelPinWire(BelId bel, IdString pin) const; + PortType getBelPinType(BelId bel, IdString pin) const; + std::vector 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> 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> 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 getGroups() const; + std::vector getGroupBels(GroupId group) const; + std::vector getGroupWires(GroupId group) const; + std::vector getGroupPips(GroupId group) const; + std::vector 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 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 00000000..7d1e32e4 --- /dev/null +++ b/xc7/arch_place.cc @@ -0,0 +1,78 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 David Shah + * Copyright (C) 2018 Serge Bazanski + * + * 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 + +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 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 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 00000000..04d9d5d9 --- /dev/null +++ b/xc7/arch_pybindings.cc @@ -0,0 +1,144 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 David Shah + * + * 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").def_readwrite("type", &ArchArgs::type); + + class_("BelId").def_readwrite("index", &BelId::index); + + class_("WireId").def_readwrite("index", &WireId::index); + + class_("PipId").def_readwrite("index", &PipId::index); + + class_("BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin); + + auto arch_cls = class_, boost::noncopyable>("Arch", init()); + auto ctx_cls = class_, boost::noncopyable>("Context", no_init) + .def("checksum", &Context::checksum) + .def("pack", &Context::pack) + .def("place", &Context::place) + .def("route", &Context::route); + + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBelType"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "checkBelAvail"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBelChecksum"); + fn_wrapper_3a_v, + addr_and_unwrap, pass_through>::def_wrap(ctx_cls, "bindBel"); + fn_wrapper_1a_v>::def_wrap( + ctx_cls, "unbindBel"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBoundBelCell"); + fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getConflictingBelCell"); + fn_wrapper_0a>::def_wrap(ctx_cls, + "getBels"); + + fn_wrapper_2a, + conv_from_str, conv_from_str>::def_wrap(ctx_cls, "getBelPinWire"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getWireBelPins"); + + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getWireChecksum"); + fn_wrapper_3a_v, + addr_and_unwrap, pass_through>::def_wrap(ctx_cls, "bindWire"); + fn_wrapper_1a_v>::def_wrap( + ctx_cls, "unbindWire"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "checkWireAvail"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBoundWireNet"); + fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getConflictingWireNet"); + + fn_wrapper_0a>::def_wrap( + ctx_cls, "getWires"); + + fn_wrapper_0a>::def_wrap( + ctx_cls, "getPips"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipChecksum"); + fn_wrapper_3a_v, + addr_and_unwrap, pass_through>::def_wrap(ctx_cls, "bindPip"); + fn_wrapper_1a_v>::def_wrap( + ctx_cls, "unbindPip"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "checkPipAvail"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBoundPipNet"); + fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getConflictingPipNet"); + + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipsDownhill"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipsUphill"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getWireAliases"); + + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipSrcWire"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipDstWire"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipDelay"); + + fn_wrapper_1a, + pass_through>::def_wrap(ctx_cls, "getPackagePinBel"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBelPackagePin"); + + fn_wrapper_0a>::def_wrap( + ctx_cls, "getChipName"); + fn_wrapper_0a>::def_wrap(ctx_cls, + "archId"); + + typedef std::unordered_map> CellMap; + typedef std::unordered_map> NetMap; + + readonly_wrapper>::def_wrap(ctx_cls, + "cells"); + readonly_wrapper>::def_wrap(ctx_cls, + "nets"); + WRAP_RANGE(Bel, conv_to_str); + WRAP_RANGE(Wire, conv_to_str); + WRAP_RANGE(AllPip, conv_to_str); + WRAP_RANGE(Pip, conv_to_str); + + 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 00000000..c2c67aa2 --- /dev/null +++ b/xc7/arch_pybindings.h @@ -0,0 +1,69 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 David Shah + * + * 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 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 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 +{ + 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 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 00000000..1c2a752d --- /dev/null +++ b/xc7/archdefs.h @@ -0,0 +1,201 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * + * 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 +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelId &bel) const noexcept { return hash()(bel.index); } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX WireId &wire) const noexcept + { + return hash()(wire.index); + } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept { return hash()(pip.index); } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX GroupId &group) const noexcept + { + std::size_t seed = 0; + boost::hash_combine(seed, hash()(group.type)); + boost::hash_combine(seed, hash()(group.x)); + boost::hash_combine(seed, hash()(group.y)); + return seed; + } +}; + +template <> struct hash +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX DecalId &decal) const noexcept + { + std::size_t seed = 0; + boost::hash_combine(seed, hash()(decal.type)); + boost::hash_combine(seed, hash()(decal.index)); + return seed; + } +}; +} // namespace std diff --git a/xc7/attosoc.pcf b/xc7/attosoc.pcf new file mode 100644 index 00000000..fd14331c --- /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 00000000..cb04bda4 --- /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 00000000..7ec25173 --- /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 + +write_json attosoc.json diff --git a/xc7/attosoc_sim.sh b/xc7/attosoc_sim.sh new file mode 100755 index 00000000..61d668af --- /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 00000000..68996189 --- /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 00000000..bbf75a05 --- /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 00000000..1b85ac5f --- /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 00000000..f5bb9f88 --- /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 00000000..6bc68573 --- /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 00000000..692c7ab9 --- /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 00000000..090b0ab5 --- /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 + +write_json blinky.json diff --git a/xc7/blinky_sim.sh b/xc7/blinky_sim.sh new file mode 100755 index 00000000..e353a407 --- /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 00000000..732e1e31 --- /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 + +write_json blinky.json diff --git a/xc7/blinky_tb.vhd b/xc7/blinky_tb.vhd new file mode 100644 index 00000000..29b5030b --- /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 00000000..601aacca --- /dev/null +++ b/xc7/cells.cc @@ -0,0 +1,242 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 David Shah + * Copyright (C) 2018 Serge Bazanski + * + * 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 create_xc7_cell(Context *ctx, IdString type, std::string name) +{ + static int auto_idx = 0; + std::unique_ptr new_cell = std::unique_ptr(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 00000000..c5956eb8 --- /dev/null +++ b/xc7/cells.h @@ -0,0 +1,110 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 David Shah + * + * 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 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 00000000..be1d6762 --- /dev/null +++ b/xc7/chains.cc @@ -0,0 +1,288 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 David Shah + * + * 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 +#include +#include "cells.h" +#include "design_utils.h" +#include "log.h" +#include "place_common.h" +#include "util.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct CellChain +{ + std::vector cells; +}; + +// Generic chain finder +template +std::vector find_chains(const Context *ctx, F1 cell_type_predicate, F2 get_previous, F3 get_next, + size_t min_length = 2) +{ + std::set chained; + std::vector 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 split_carry_chain(CellChain &carryc) + { + bool start_of_chain = true; + std::vector chains; + std::vector 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 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 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 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 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 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 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 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 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 00000000..98112303 --- /dev/null +++ b/xc7/chains.h @@ -0,0 +1,27 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 David Shah + * + * 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 00000000..7bdf82f0 --- /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 00000000..7fae17e6 --- /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 00000000..67f96b98 --- /dev/null +++ b/xc7/delay.cc @@ -0,0 +1,85 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 Serge Bazanski + * + * 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 00000000..9de869ee --- /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 00000000..bf6fe43c --- /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 00000000..ef9aa3c5 --- /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 00000000..1ab2fb3c --- /dev/null +++ b/xc7/gfx.cc @@ -0,0 +1,766 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * + * 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 &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 &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 &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 00000000..8ee7b0b6 --- /dev/null +++ b/xc7/gfx.h @@ -0,0 +1,523 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * + * 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 &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style); +void gfxTilePip(std::vector &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 00000000..cac9a397 --- /dev/null +++ b/xc7/main.cc @@ -0,0 +1,124 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 +#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 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(), "set device package"); + specific.add_options()("pcf", po::value(), "PCF constraints file to ingest"); + specific.add_options()("xdl", po::value(), "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::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::ofstream f(filename); + write_xdl(ctx, f); + } +} + +void Xc7CommandHandler::setupArchContext(Context *ctx) +{ + // if (vm.count("tmfuzz")) + // ice40DelayFuzzerMain(ctx); +} + +std::unique_ptr 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(); + + return std::unique_ptr(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 00000000..a4b57f2a --- /dev/null +++ b/xc7/pack.cc @@ -0,0 +1,728 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 David Shah + * Copyright (C) 2018 Serge Bazanski + * + * 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 +#include +#include +#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 packed_cells; + std::vector> 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 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 packed_cells; + std::vector> new_cells; + + for (auto cell : sorted(ctx->cells)) { + CellInfo *ci = cell.second; + if (is_ff(ctx, ci)) { + std::unique_ptr 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 gnd_cell = create_xc7_cell(ctx, ctx->id("XC7_LC"), "$PACKER_GND"); + gnd_cell->params[ctx->id("INIT")] = "0"; + std::unique_ptr gnd_net = std::unique_ptr(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 vcc_cell = create_xc7_cell(ctx, ctx->id("XC7_LC"), "$PACKER_VCC"); + vcc_cell->params[ctx->id("INIT")] = "1"; + std::unique_ptr vcc_net = std::unique_ptr(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 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 packed_cells; + std::vector> 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 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 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 glbnet = std::unique_ptr(new NetInfo()); + glbnet->name = ctx->id(glb_name); + glbnet->driver = pr; + gb->ports[ctx->id("O")].net = glbnet.get(); + std::vector 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 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 &a, const std::pair &b) { + return a.second < b.second; + }); + + auto global_reset = std::max_element(reset_count.begin(), reset_count.end(), + [](const std::pair &a, const std::pair &b) { + return a.second < b.second; + }); + auto global_cen = std::max_element(cen_count.begin(), cen_count.end(), + [](const std::pair &a, const std::pair &b) { + return a.second < b.second; + }); + auto global_logic = std::max_element(logic_count.begin(), logic_count.end(), + [](const std::pair &a, const std::pair &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 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 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 out_net = std::unique_ptr(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 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 packed_cells; + std::vector> 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 00000000..f56a177b --- /dev/null +++ b/xc7/pcf.cc @@ -0,0 +1,84 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 David Shah + * + * 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 +#include "log.h" + +#include + +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 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 00000000..ecc81e59 --- /dev/null +++ b/xc7/pcf.h @@ -0,0 +1,34 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 David Shah + * + * 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 +#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 00000000..bc8e084a --- /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 00000000..a8c83bd9 --- /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 00000000..ec2aee80 --- /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 00000000..e6eec6cd --- /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 + +write_json picorv32.json diff --git a/xc7/picorv32_benchmark.py b/xc7/picorv32_benchmark.py new file mode 100755 index 00000000..a4ec581e --- /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 00000000..3735aa1b --- /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 00000000..5c8def1f --- /dev/null +++ b/xc7/project.cc @@ -0,0 +1,58 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Miodrag Milanovic + * + * 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 +#include +#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 ProjectHandler::createContext(pt::ptree &root) +{ + ArchArgs chipArgs; + std::string arch_type = root.get("project.arch.type"); + if (arch_type == "z020") { + chipArgs.type = ArchArgs::Z020; + } + chipArgs.package = root.get("project.arch.package"); + + return std::unique_ptr(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("pcf"); + std::ifstream f(pcf.string()); + if (!apply_pcf(ctx, input.get("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 00000000..4ec2a546 --- /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 00000000..2687d738 --- /dev/null +++ b/xc7/xdl.cc @@ -0,0 +1,285 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 David Shah + * Copyright (C) 2018 Serge Bazanski + * + * 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 +#include +#include "cells.h" +#include "log.h" +#include "nextpnr.h" +#include "util.h" +#include + +#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 site_to_instance; + std::vector> 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(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 00000000..19bc5478 --- /dev/null +++ b/xc7/xdl.h @@ -0,0 +1,33 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 David Shah + * + * 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 +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +void write_xdl(const Context *ctx, std::ostream &out); + +NEXTPNR_NAMESPACE_END + +#endif