commit
bbef0182f8
20
.gitignore
vendored
20
.gitignore
vendored
@ -3,6 +3,7 @@
|
|||||||
/nextpnr-generic*
|
/nextpnr-generic*
|
||||||
/nextpnr-ice40*
|
/nextpnr-ice40*
|
||||||
/nextpnr-ecp5*
|
/nextpnr-ecp5*
|
||||||
|
/nextpnr-xc7*
|
||||||
cmake-build-*/
|
cmake-build-*/
|
||||||
Makefile
|
Makefile
|
||||||
cmake_install.cmake
|
cmake_install.cmake
|
||||||
@ -29,3 +30,22 @@ install_manifest.txt
|
|||||||
/ImportExecutables.cmake
|
/ImportExecutables.cmake
|
||||||
*-coverage/
|
*-coverage/
|
||||||
*-coverage.info
|
*-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
|
||||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "torc"]
|
||||||
|
path = torc
|
||||||
|
url = https://github.com/eddiehung/torc
|
@ -17,7 +17,7 @@ if (STATIC_BUILD)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# List of families to build
|
# 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(ARCH "" CACHE STRING "Architecture family for nextpnr build")
|
||||||
set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES})
|
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
|
# Original source: https://github.com/BVLC/caffe/blob/master/cmake/Dependencies.cmake#L148
|
||||||
set(version ${PYTHONLIBS_VERSION_STRING})
|
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})
|
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_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)
|
while (NOT "${version}" STREQUAL "" AND NOT Boost_PYTHON_FOUND)
|
||||||
STRING(REGEX REPLACE "([0-9.]+).[0-9]+" "\\1" version ${version})
|
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})
|
STRING(REGEX REPLACE "[^0-9]" "" boost_py_version ${version})
|
||||||
find_package(Boost QUIET COMPONENTS "python-py${boost_py_version}" ${boost_libs})
|
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_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})
|
STRING(REGEX MATCHALL "([0-9.]+).[0-9]+" has_more_version ${version})
|
||||||
if ("${has_more_version}" STREQUAL "")
|
if ("${has_more_version}" STREQUAL "")
|
||||||
@ -139,6 +141,7 @@ if (BUILD_PYTHON)
|
|||||||
|
|
||||||
if (NOT Boost_PYTHON_FOUND)
|
if (NOT Boost_PYTHON_FOUND)
|
||||||
find_package(Boost QUIET COMPONENTS python3 ${boost_libs})
|
find_package(Boost QUIET COMPONENTS python3 ${boost_libs})
|
||||||
|
set(boost_python_lib python3)
|
||||||
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
|
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
|
||||||
set(Boost_PYTHON_FOUND TRUE)
|
set(Boost_PYTHON_FOUND TRUE)
|
||||||
endif ()
|
endif ()
|
||||||
@ -146,6 +149,7 @@ if (BUILD_PYTHON)
|
|||||||
|
|
||||||
if (NOT Boost_PYTHON_FOUND)
|
if (NOT Boost_PYTHON_FOUND)
|
||||||
find_package(Boost QUIET COMPONENTS python36 ${boost_libs})
|
find_package(Boost QUIET COMPONENTS python36 ${boost_libs})
|
||||||
|
set(boost_python_lib python36)
|
||||||
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
|
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
|
||||||
set(Boost_PYTHON_FOUND TRUE)
|
set(Boost_PYTHON_FOUND TRUE)
|
||||||
endif ()
|
endif ()
|
||||||
@ -153,6 +157,7 @@ if (BUILD_PYTHON)
|
|||||||
|
|
||||||
if (NOT Boost_PYTHON_FOUND)
|
if (NOT Boost_PYTHON_FOUND)
|
||||||
find_package(Boost QUIET COMPONENTS python37 ${boost_libs})
|
find_package(Boost QUIET COMPONENTS python37 ${boost_libs})
|
||||||
|
set(boost_python_lib python37)
|
||||||
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
|
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
|
||||||
set(Boost_PYTHON_FOUND TRUE)
|
set(Boost_PYTHON_FOUND TRUE)
|
||||||
endif ()
|
endif ()
|
||||||
@ -161,6 +166,7 @@ if (BUILD_PYTHON)
|
|||||||
if (NOT Boost_PYTHON_FOUND)
|
if (NOT Boost_PYTHON_FOUND)
|
||||||
STRING(REGEX REPLACE "([0-9]+\\.[0-9]+).*" "\\1" gentoo_version ${PYTHONLIBS_VERSION_STRING})
|
STRING(REGEX REPLACE "([0-9]+\\.[0-9]+).*" "\\1" gentoo_version ${PYTHONLIBS_VERSION_STRING})
|
||||||
find_package(Boost QUIET COMPONENTS python-${gentoo_version} ${boost_libs})
|
find_package(Boost QUIET COMPONENTS python-${gentoo_version} ${boost_libs})
|
||||||
|
set(boost_python_lib python-${gentoo_version})
|
||||||
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
|
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
|
||||||
set(Boost_PYTHON_FOUND TRUE)
|
set(Boost_PYTHON_FOUND TRUE)
|
||||||
endif ()
|
endif ()
|
||||||
|
13
README.md
13
README.md
@ -1,6 +1,19 @@
|
|||||||
nextpnr -- a portable FPGA place and route tool
|
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
|
nextpnr aims to be a vendor neutral, timing driven, FOSS FPGA place and route
|
||||||
tool.
|
tool.
|
||||||
|
|
||||||
|
@ -37,6 +37,8 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type
|
|||||||
if (driver_gb)
|
if (driver_gb)
|
||||||
return 0;
|
return 0;
|
||||||
int clock_count;
|
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 &&
|
bool timing_driven = ctx->timing_driven && type == MetricType::COST &&
|
||||||
ctx->getPortTimingClass(driver_cell, net->driver.port, clock_count) != TMG_IGNORE;
|
ctx->getPortTimingClass(driver_cell, net->driver.port, clock_count) != TMG_IGNORE;
|
||||||
delay_t negative_slack = 0;
|
delay_t negative_slack = 0;
|
||||||
|
@ -512,6 +512,21 @@ struct Router1
|
|||||||
WireId next_wire = ctx->getPipDstWire(pip);
|
WireId next_wire = ctx->getPipDstWire(pip);
|
||||||
next_delay += ctx->getWireDelay(next_wire).maxDelay();
|
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();
|
WireId conflictWireWire = WireId(), conflictPipWire = WireId();
|
||||||
NetInfo *conflictWireNet = nullptr, *conflictPipNet = nullptr;
|
NetInfo *conflictWireNet = nullptr, *conflictPipNet = nullptr;
|
||||||
|
|
||||||
|
@ -51,9 +51,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
|||||||
bool Application::notify(QObject *receiver, QEvent *event)
|
bool Application::notify(QObject *receiver, QEvent *event)
|
||||||
{
|
{
|
||||||
bool retVal = true;
|
bool retVal = true;
|
||||||
try {
|
//try {
|
||||||
retVal = QApplication::notify(receiver, event);
|
retVal = QApplication::notify(receiver, event);
|
||||||
} catch (assertion_failure ex) {
|
/*} catch (assertion_failure ex) {
|
||||||
QString msg;
|
QString msg;
|
||||||
QTextStream out(&msg);
|
QTextStream out(&msg);
|
||||||
out << ex.filename.c_str() << " at " << ex.line << "\n";
|
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);
|
QMessageBox::critical(0, "Error", msg);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
QMessageBox::critical(0, "Error", "Fatal error !!!");
|
QMessageBox::critical(0, "Error", "Fatal error !!!");
|
||||||
}
|
}*/
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,6 +317,12 @@ void DesignWidget::newContext(Context *ctx)
|
|||||||
for (const auto &wire : ctx->getWires()) {
|
for (const auto &wire : ctx->getWires()) {
|
||||||
wireMap[std::pair<int, int>(wire.location.x, wire.location.y)].push_back(wire);
|
wireMap[std::pair<int, int>(wire.location.x, wire.location.y)].push_back(wire);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef ARCH_XC7
|
||||||
|
for (const auto &wire : ctx->getWires()) {
|
||||||
|
const auto loc = torc_info->wire_to_loc(wire.index);
|
||||||
|
wireMap[std::pair<int, int>(loc.x, loc.y)].push_back(wire);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
auto wireGetter = [](Context *ctx, WireId id) { return ctx->getWireName(id); };
|
auto wireGetter = [](Context *ctx, WireId id) { return ctx->getWireName(id); };
|
||||||
getTreeByElementType(ElementType::WIRE)
|
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::String, "Type", ctx->getPipType(pip).c_str(ctx));
|
||||||
addProperty(topItem, QVariant::Bool, "Available", ctx->checkPipAvail(pip));
|
addProperty(topItem, QVariant::Bool, "Available", ctx->checkPipAvail(pip));
|
||||||
addProperty(topItem, QVariant::String, "Bound Net", ctx->nameOf(ctx->getBoundPipNet(pip)), ElementType::NET);
|
addProperty(topItem, QVariant::String, "Bound Net", ctx->nameOf(ctx->getBoundPipNet(pip)), ElementType::NET);
|
||||||
|
WireId conflict = ctx->getConflictingPipWire(pip);
|
||||||
addProperty(topItem, QVariant::String, "Conflicting Wire",
|
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)),
|
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->nameOf(ctx->getConflictingPipNet(pip)),
|
||||||
ElementType::NET);
|
ElementType::NET);
|
||||||
addProperty(topItem, QVariant::String, "Src Wire", ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx),
|
addProperty(topItem, QVariant::String, "Src Wire", ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx),
|
||||||
|
1
gui/xc7/family.cmake
Normal file
1
gui/xc7/family.cmake
Normal file
@ -0,0 +1 @@
|
|||||||
|
include_directories(/opt/torc/src)
|
51
gui/xc7/mainwindow.cc
Normal file
51
gui/xc7/mainwindow.cc
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
MainWindow::MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent)
|
||||||
|
: BaseMainWindow(std::move(context), args, parent)
|
||||||
|
{
|
||||||
|
initMainResource();
|
||||||
|
|
||||||
|
std::string title = "nextpnr-xc7 - [EMPTY]";
|
||||||
|
setWindowTitle(title.c_str());
|
||||||
|
|
||||||
|
connect(this, &BaseMainWindow::contextChanged, this, &MainWindow::newContext);
|
||||||
|
|
||||||
|
createMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow() {}
|
||||||
|
|
||||||
|
void MainWindow::newContext(Context *ctx)
|
||||||
|
{
|
||||||
|
std::string title = "nextpnr-xc7 - " + ctx->getChipName();
|
||||||
|
setWindowTitle(title.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::createMenu() {}
|
||||||
|
|
||||||
|
void MainWindow::new_proj() {}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
45
gui/xc7/mainwindow.h
Normal file
45
gui/xc7/mainwindow.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include "../basewindow.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class MainWindow : public BaseMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MainWindow(std::unique_ptr<Context> context, ArchArgs args, QWidget *parent = 0);
|
||||||
|
virtual ~MainWindow();
|
||||||
|
|
||||||
|
public:
|
||||||
|
void createMenu();
|
||||||
|
|
||||||
|
protected Q_SLOTS:
|
||||||
|
void new_proj() override;
|
||||||
|
void newContext(Context *ctx);
|
||||||
|
};
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // MAINWINDOW_H
|
2
gui/xc7/nextpnr.qrc
Normal file
2
gui/xc7/nextpnr.qrc
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<RCC>
|
||||||
|
</RCC>
|
1
torc
Submodule
1
torc
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit a5a4ec057eae3ed07ed2acb9da13404808e37211
|
904
xc7/arch.cc
Normal file
904
xc7/arch.cc
Normal file
@ -0,0 +1,904 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <regex>
|
||||||
|
#include "cells.h"
|
||||||
|
#include "gfx.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "nextpnr.h"
|
||||||
|
#include "placer1.h"
|
||||||
|
#include "router1.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include "torc/common/DirectoryTree.hpp"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
std::unique_ptr<const TorcInfo> torc_info;
|
||||||
|
TorcInfo::TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::string &inPackageName)
|
||||||
|
: TorcInfo(inDeviceName, inPackageName)
|
||||||
|
{
|
||||||
|
static const std::regex re_loc(".+_X(\\d+)Y(\\d+)");
|
||||||
|
std::cmatch what;
|
||||||
|
tile_to_xy.resize(tiles.getTileCount());
|
||||||
|
for (TileIndex tileIndex(0); tileIndex < tiles.getTileCount(); tileIndex++) {
|
||||||
|
const auto &tileInfo = tiles.getTileInfo(tileIndex);
|
||||||
|
if (!std::regex_match(tileInfo.getName(), what, re_loc))
|
||||||
|
throw;
|
||||||
|
const auto x = boost::lexical_cast<int>(what.str(1));
|
||||||
|
const auto y = boost::lexical_cast<int>(what.str(2));
|
||||||
|
tile_to_xy[tileIndex] = std::make_pair(x,y);
|
||||||
|
}
|
||||||
|
|
||||||
|
bel_to_site_index.reserve(sites.getSiteCount() * 4);
|
||||||
|
bel_to_loc.reserve(sites.getSiteCount() * 4);
|
||||||
|
site_index_to_bel.resize(sites.getSiteCount());
|
||||||
|
site_index_to_type.resize(sites.getSiteCount());
|
||||||
|
BelId b;
|
||||||
|
b.index = 0;
|
||||||
|
for (SiteIndex i(0); i < sites.getSiteCount(); ++i) {
|
||||||
|
const auto &site = sites.getSite(i);
|
||||||
|
const auto &pd = site.getPrimitiveDefPtr();
|
||||||
|
const auto &type = pd->getName();
|
||||||
|
int x, y;
|
||||||
|
std::tie(x,y) = tile_to_xy[site.getTileIndex()];
|
||||||
|
|
||||||
|
if (type == "SLICEL" || type == "SLICEM") {
|
||||||
|
bel_to_site_index.push_back(i);
|
||||||
|
bel_to_site_index.push_back(i);
|
||||||
|
bel_to_site_index.push_back(i);
|
||||||
|
bel_to_site_index.push_back(i);
|
||||||
|
site_index_to_type[i] = id_SLICE_LUT6;
|
||||||
|
const auto site_name = site.getName();
|
||||||
|
if (!std::regex_match(site_name.c_str(), what, re_loc))
|
||||||
|
throw;
|
||||||
|
const auto sx = boost::lexical_cast<int>(what.str(1));
|
||||||
|
if ((sx & 1) == 0) {
|
||||||
|
bel_to_loc.emplace_back(x, y, 0);
|
||||||
|
bel_to_loc.emplace_back(x, y, 1);
|
||||||
|
bel_to_loc.emplace_back(x, y, 2);
|
||||||
|
bel_to_loc.emplace_back(x, y, 3);
|
||||||
|
} else {
|
||||||
|
bel_to_loc.emplace_back(x, y, 4);
|
||||||
|
bel_to_loc.emplace_back(x, y, 5);
|
||||||
|
bel_to_loc.emplace_back(x, y, 6);
|
||||||
|
bel_to_loc.emplace_back(x, y, 7);
|
||||||
|
}
|
||||||
|
site_index_to_bel[i] = b;
|
||||||
|
b.index += 4;
|
||||||
|
} else if (type == "IOB33S" || type == "IOB33M") {
|
||||||
|
bel_to_site_index.push_back(i);
|
||||||
|
site_index_to_type[i] = id_IOB33;
|
||||||
|
// TODO: Fix z when two IOBs on same tile
|
||||||
|
bel_to_loc.emplace_back(x, y, 0);
|
||||||
|
site_index_to_bel[i] = b;
|
||||||
|
++b.index;
|
||||||
|
} else if (type == "IOB18S" || type == "IOB18M") {
|
||||||
|
bel_to_site_index.push_back(i);
|
||||||
|
site_index_to_type[i] = id_IOB18;
|
||||||
|
// TODO: Fix z when two IOBs on same tile
|
||||||
|
bel_to_loc.emplace_back(x, y, 0);
|
||||||
|
site_index_to_bel[i] = b;
|
||||||
|
++b.index;
|
||||||
|
} else {
|
||||||
|
bel_to_site_index.push_back(i);
|
||||||
|
site_index_to_type[i] = ctx->id(type);
|
||||||
|
bel_to_loc.emplace_back(x, y, 0);
|
||||||
|
site_index_to_bel[i] = b;
|
||||||
|
++b.index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
num_bels = bel_to_site_index.size();
|
||||||
|
bel_to_site_index.shrink_to_fit();
|
||||||
|
bel_to_loc.shrink_to_fit();
|
||||||
|
|
||||||
|
const std::regex re_124("(.+_)?[NESW][NESWLR](\\d)((BEG(_[NS])?)|(END(_[NS])?)|[A-E])?\\d(_\\d)?");
|
||||||
|
const std::regex re_L("(.+_)?L(H|V|VB)(_L)?\\d+(_\\d)?");
|
||||||
|
const std::regex re_BYP("BYP(_ALT)?\\d");
|
||||||
|
const std::regex re_BYP_B("BYP_[BL]\\d");
|
||||||
|
const std::regex re_BOUNCE_NS("(BYP|FAN)_BOUNCE_[NS]3_\\d");
|
||||||
|
const std::regex re_FAN("FAN(_ALT)?\\d");
|
||||||
|
const std::regex re_CLB_I1_6("CLBL[LM]_(L|LL|M)_[A-D]([1-6])");
|
||||||
|
const std::regex bufg_i("CLK_BUFG_BUFGCTRL\\d+_I0");
|
||||||
|
const std::regex bufg_o("CLK_BUFG_BUFGCTRL\\d+_O");
|
||||||
|
const std::regex hrow("CLK_HROW_CLK[01]_[34]");
|
||||||
|
std::unordered_map</*TileTypeIndex*/ unsigned, std::vector<delay_t>> delay_lookup;
|
||||||
|
std::unordered_map<Segments::SegmentReference, TileIndex> segment_to_anchor;
|
||||||
|
Tilewire currentTilewire;
|
||||||
|
WireId w;
|
||||||
|
w.index = 0;
|
||||||
|
for (TileIndex tileIndex(0); tileIndex < tiles.getTileCount(); tileIndex++) {
|
||||||
|
// iterate over every wire in the tile
|
||||||
|
const auto &tileInfo = tiles.getTileInfo(tileIndex);
|
||||||
|
auto tileTypeIndex = tileInfo.getTypeIndex();
|
||||||
|
auto wireCount = tiles.getWireCount(tileTypeIndex);
|
||||||
|
currentTilewire.setTileIndex(tileIndex);
|
||||||
|
for (WireIndex wireIndex(0); wireIndex < wireCount; wireIndex++) {
|
||||||
|
currentTilewire.setWireIndex(wireIndex);
|
||||||
|
const auto ¤tSegment = segments.getTilewireSegment(currentTilewire);
|
||||||
|
|
||||||
|
if (!currentSegment.isTrivial()) {
|
||||||
|
auto r = segment_to_anchor.emplace(currentSegment, currentSegment.getAnchorTileIndex());
|
||||||
|
if (r.second) {
|
||||||
|
TilewireVector segment;
|
||||||
|
const_cast<DDB &>(*ddb).expandSegment(currentTilewire, segment, DDB::eExpandDirectionNone);
|
||||||
|
// expand all of the arcs
|
||||||
|
TilewireVector::const_iterator sep = segment.begin();
|
||||||
|
TilewireVector::const_iterator see = segment.end();
|
||||||
|
while(sep < see) {
|
||||||
|
// expand the tilewire sinks
|
||||||
|
const Tilewire& tilewire = *sep++;
|
||||||
|
|
||||||
|
const auto &tileInfo = tiles.getTileInfo(tilewire.getTileIndex());
|
||||||
|
const auto &tileTypeName = tiles.getTileTypeName(tileInfo.getTypeIndex());
|
||||||
|
if (boost::starts_with(tileTypeName, "INT") || boost::starts_with(tileTypeName, "CLB")) {
|
||||||
|
r.first->second = tilewire.getTileIndex();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (r.first->second != tileIndex)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
segment_to_wire.emplace(currentSegment, w);
|
||||||
|
} else
|
||||||
|
trivial_to_wire.emplace(currentTilewire, w);
|
||||||
|
|
||||||
|
wire_to_tilewire.push_back(currentTilewire);
|
||||||
|
|
||||||
|
auto it = delay_lookup.find(tileTypeIndex);
|
||||||
|
if (it == delay_lookup.end()) {
|
||||||
|
auto wireCount = tiles.getWireCount(tileTypeIndex);
|
||||||
|
std::vector<delay_t> tile_delays(wireCount);
|
||||||
|
for (WireIndex wireIndex(0); wireIndex < wireCount; wireIndex++) {
|
||||||
|
const WireInfo &wireInfo = tiles.getWireInfo(tileTypeIndex, wireIndex);
|
||||||
|
auto wire_name = wireInfo.getName();
|
||||||
|
if (std::regex_match(wire_name, what, re_124)) {
|
||||||
|
switch (what.str(2)[0]) {
|
||||||
|
case '1':
|
||||||
|
tile_delays[wireIndex] = 150;
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
tile_delays[wireIndex] = 170;
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
tile_delays[wireIndex] = 210;
|
||||||
|
break;
|
||||||
|
case '6':
|
||||||
|
tile_delays[wireIndex] = 210;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
} else if (std::regex_match(wire_name, what, re_L)) {
|
||||||
|
std::string l(what[2]);
|
||||||
|
if (l == "H")
|
||||||
|
tile_delays[wireIndex] = 360;
|
||||||
|
else if (l == "VB")
|
||||||
|
tile_delays[wireIndex] = 300;
|
||||||
|
else if (l == "V")
|
||||||
|
tile_delays[wireIndex] = 350;
|
||||||
|
else
|
||||||
|
throw;
|
||||||
|
} else if (std::regex_match(wire_name, what, re_BYP)) {
|
||||||
|
tile_delays[wireIndex] = 190;
|
||||||
|
} else if (std::regex_match(wire_name, what, re_BYP_B)) {
|
||||||
|
} else if (std::regex_match(wire_name, what, re_FAN)) {
|
||||||
|
tile_delays[wireIndex] = 190;
|
||||||
|
} else if (std::regex_match(wire_name, what, re_CLB_I1_6)) {
|
||||||
|
switch (what.str(2)[0]) {
|
||||||
|
case '1':
|
||||||
|
tile_delays[wireIndex] = 280;
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
tile_delays[wireIndex] = 280;
|
||||||
|
break;
|
||||||
|
case '3':
|
||||||
|
tile_delays[wireIndex] = 180;
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
tile_delays[wireIndex] = 180;
|
||||||
|
break;
|
||||||
|
case '5':
|
||||||
|
tile_delays[wireIndex] = 80;
|
||||||
|
break;
|
||||||
|
case '6':
|
||||||
|
tile_delays[wireIndex] = 40;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it = delay_lookup.emplace(tileTypeIndex, std::move(tile_delays)).first;
|
||||||
|
}
|
||||||
|
assert(it != delay_lookup.end());
|
||||||
|
|
||||||
|
DelayInfo d;
|
||||||
|
d.delay = it->second[currentTilewire.getWireIndex()];
|
||||||
|
wire_to_delay.emplace_back(std::move(d));
|
||||||
|
|
||||||
|
++w.index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
segment_to_anchor.clear();
|
||||||
|
wire_to_tilewire.shrink_to_fit();
|
||||||
|
wire_to_delay.shrink_to_fit();
|
||||||
|
num_wires = wire_to_tilewire.size();
|
||||||
|
wire_is_global.resize(num_wires);
|
||||||
|
|
||||||
|
wire_to_pips_downhill.resize(num_wires);
|
||||||
|
// std::unordered_map<Arc, int> arc_to_pip;
|
||||||
|
ArcVector arcs;
|
||||||
|
ExtendedWireInfo ewi(*ddb);
|
||||||
|
PipId p;
|
||||||
|
p.index = 0;
|
||||||
|
for (w.index = 0; w.index < num_wires; ++w.index) {
|
||||||
|
const auto ¤tTilewire = wire_to_tilewire[w.index];
|
||||||
|
if (currentTilewire.isUndefined())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto &tileInfo = tiles.getTileInfo(currentTilewire.getTileIndex());
|
||||||
|
const auto tileTypeName = tiles.getTileTypeName(tileInfo.getTypeIndex());
|
||||||
|
const bool clb = boost::starts_with(
|
||||||
|
tileTypeName, "CLB"); // Disable all CLB route-throughs (i.e. LUT in->out, LUT A->AMUX, for now)
|
||||||
|
|
||||||
|
auto &pips = wire_to_pips_downhill[w.index];
|
||||||
|
const bool clk_tile = boost::starts_with(tileTypeName, "CLK");
|
||||||
|
|
||||||
|
bool global_tile = false;
|
||||||
|
|
||||||
|
arcs.clear();
|
||||||
|
//const_cast<DDB &>(*ddb).expandSegmentSinks(currentTilewire, arcs, DDB::eExpandDirectionNone,
|
||||||
|
// false /* inUseTied */, true /*inUseRegular */,
|
||||||
|
// true /* inUseIrregular */, !clb /* inUseRoutethrough */);
|
||||||
|
{
|
||||||
|
// expand the segment
|
||||||
|
TilewireVector segment;
|
||||||
|
const_cast<DDB &>(*ddb).expandSegment(currentTilewire, segment, DDB::eExpandDirectionNone);
|
||||||
|
// expand all of the arcs
|
||||||
|
TilewireVector::const_iterator sep = segment.begin();
|
||||||
|
TilewireVector::const_iterator see = segment.end();
|
||||||
|
while(sep < see) {
|
||||||
|
// expand the tilewire sinks
|
||||||
|
const Tilewire& tilewire = *sep++;
|
||||||
|
|
||||||
|
const auto &tileInfo = tiles.getTileInfo(tilewire.getTileIndex());
|
||||||
|
const auto &tileTypeName = tiles.getTileTypeName(tileInfo.getTypeIndex());
|
||||||
|
global_tile = global_tile || boost::starts_with(tileTypeName, "CLK") || boost::starts_with(tileTypeName, "HCLK") || boost::starts_with(tileTypeName, "CFG");
|
||||||
|
|
||||||
|
TilewireVector sinks;
|
||||||
|
const_cast<DDB &>(*ddb).expandTilewireSinks(tilewire, sinks, false /*inUseTied*/, true /*inUseRegular*/, true /*inUseIrregular*/,
|
||||||
|
!clb /* inUseRoutethrough */);
|
||||||
|
// rewrite the sinks as arcs
|
||||||
|
TilewireVector::const_iterator sip = sinks.begin();
|
||||||
|
TilewireVector::const_iterator sie = sinks.end();
|
||||||
|
while(sip < sie) {
|
||||||
|
Arc a(tilewire, *sip++);
|
||||||
|
|
||||||
|
// Disable BUFG I0 -> O routethrough
|
||||||
|
if (clk_tile) {
|
||||||
|
ewi.set(a.getSourceTilewire());
|
||||||
|
if (std::regex_match(ewi.mWireName, bufg_i)) {
|
||||||
|
ewi.set(a.getSinkTilewire());
|
||||||
|
if (std::regex_match(ewi.mWireName, bufg_o))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable entering HROW from INT_[LR].CLK[01]
|
||||||
|
if (boost::starts_with(tileTypeName, "CLK_HROW")) {
|
||||||
|
ewi.set(a.getSourceTilewire());
|
||||||
|
if (std::regex_match(ewi.mWireName, hrow))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pips.emplace_back(p);
|
||||||
|
pip_to_arc.emplace_back(a);
|
||||||
|
// arc_to_pip.emplace(a, p.index);
|
||||||
|
++p.index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pips.shrink_to_fit();
|
||||||
|
|
||||||
|
if (global_tile)
|
||||||
|
wire_is_global[w.index] = true;
|
||||||
|
}
|
||||||
|
pip_to_arc.shrink_to_fit();
|
||||||
|
num_pips = pip_to_arc.size();
|
||||||
|
|
||||||
|
height = (int)tiles.getRowCount();
|
||||||
|
width = (int)tiles.getColCount();
|
||||||
|
}
|
||||||
|
TorcInfo::TorcInfo(const std::string& inDeviceName, const std::string &inPackageName)
|
||||||
|
: ddb(new DDB(inDeviceName, inPackageName)), sites(ddb->getSites()), tiles(ddb->getTiles()),
|
||||||
|
segments(ddb->getSegments())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
void IdString::initialize_arch(const BaseCtx *ctx)
|
||||||
|
{
|
||||||
|
#define X(t) initialize_add(ctx, #t, ID_##t);
|
||||||
|
#include "constids.inc"
|
||||||
|
#undef X
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
Arch::Arch(ArchArgs args) : args(args)
|
||||||
|
{
|
||||||
|
torc::common::DirectoryTree directoryTree("/opt/torc/src/torc");
|
||||||
|
if (args.type == ArchArgs::Z020) {
|
||||||
|
torc_info = std::unique_ptr<TorcInfo>(new TorcInfo(this, "xc7z020", args.package));
|
||||||
|
} else if (args.type == ArchArgs::VX980) {
|
||||||
|
torc_info = std::unique_ptr<TorcInfo>(new TorcInfo(this, "xc7vx980t", args.package));
|
||||||
|
} else {
|
||||||
|
log_error("Unsupported XC7 chip type.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
width = torc_info->width;
|
||||||
|
height = torc_info->height;
|
||||||
|
/*if (getCtx()->verbose)*/ {
|
||||||
|
log_info("Number of bels: %d\n", torc_info->num_bels);
|
||||||
|
log_info("Number of wires: %d\n", torc_info->num_wires);
|
||||||
|
log_info("Number of pips: %d\n", torc_info->num_pips);
|
||||||
|
}
|
||||||
|
|
||||||
|
bel_to_cell.resize(torc_info->num_bels);
|
||||||
|
wire_to_net.resize(torc_info->num_wires);
|
||||||
|
pip_to_net.resize(torc_info->num_pips);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
std::string Arch::getChipName() const
|
||||||
|
{
|
||||||
|
if (args.type == ArchArgs::Z020) {
|
||||||
|
return "z020";
|
||||||
|
} else if (args.type == ArchArgs::VX980) {
|
||||||
|
return "vx980";
|
||||||
|
} else {
|
||||||
|
log_error("Unsupported XC7 chip type.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
IdString Arch::archArgsToId(ArchArgs args) const
|
||||||
|
{
|
||||||
|
if (args.type == ArchArgs::Z020)
|
||||||
|
return id("z020");
|
||||||
|
if (args.type == ArchArgs::VX980)
|
||||||
|
return id("vx980");
|
||||||
|
return IdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
static bool endsWith(const std::string& str, const std::string& suffix)
|
||||||
|
{
|
||||||
|
return str.size() >= suffix.size() && 0 == str.compare(str.size()-suffix.size(), suffix.size(), suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
BelId Arch::getBelByName(IdString name) const
|
||||||
|
{
|
||||||
|
std::string n = name.str(this);
|
||||||
|
int ndx = 0;
|
||||||
|
if (endsWith(n,"_A") || endsWith(n,"_B") || endsWith(n,"_C") || endsWith(n,"_D"))
|
||||||
|
{
|
||||||
|
ndx = (int)(n.back() - 'A');
|
||||||
|
n = n.substr(0,n.size()-2);
|
||||||
|
}
|
||||||
|
auto it = torc_info->sites.findSiteIndex(n);
|
||||||
|
if (it != SiteIndex(-1)) {
|
||||||
|
BelId id = torc_info->site_index_to_bel.at(it);
|
||||||
|
id.index += ndx;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
return BelId();
|
||||||
|
}
|
||||||
|
|
||||||
|
BelId Arch::getBelByLocation(Loc loc) const
|
||||||
|
{
|
||||||
|
BelId bel;
|
||||||
|
|
||||||
|
if (bel_by_loc.empty()) {
|
||||||
|
for (int i = 0; i < torc_info->num_bels; i++) {
|
||||||
|
BelId b;
|
||||||
|
b.index = i;
|
||||||
|
bel_by_loc[getBelLocation(b)] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = bel_by_loc.find(loc);
|
||||||
|
if (it != bel_by_loc.end())
|
||||||
|
bel = it->second;
|
||||||
|
|
||||||
|
return bel;
|
||||||
|
}
|
||||||
|
|
||||||
|
BelRange Arch::getBelsByTile(int x, int y) const
|
||||||
|
{
|
||||||
|
BelRange br;
|
||||||
|
|
||||||
|
br.b.cursor = Arch::getBelByLocation(Loc(x, y, 0)).index;
|
||||||
|
br.e.cursor = br.b.cursor;
|
||||||
|
|
||||||
|
if (br.e.cursor != -1) {
|
||||||
|
while (br.e.cursor < chip_info->num_bels && chip_info->bel_data[br.e.cursor].x == x &&
|
||||||
|
chip_info->bel_data[br.e.cursor].y == y)
|
||||||
|
br.e.cursor++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return br;
|
||||||
|
}
|
||||||
|
|
||||||
|
PortType Arch::getBelPinType(BelId bel, IdString pin) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(bel != BelId());
|
||||||
|
|
||||||
|
int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires;
|
||||||
|
const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get();
|
||||||
|
|
||||||
|
if (num_bel_wires < 7) {
|
||||||
|
for (int i = 0; i < num_bel_wires; i++) {
|
||||||
|
if (bel_wires[i].port == pin.index)
|
||||||
|
return PortType(bel_wires[i].type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int b = 0, e = num_bel_wires - 1;
|
||||||
|
while (b <= e) {
|
||||||
|
int i = (b + e) / 2;
|
||||||
|
if (bel_wires[i].port == pin.index)
|
||||||
|
return PortType(bel_wires[i].type);
|
||||||
|
if (bel_wires[i].port > pin.index)
|
||||||
|
e = i - 1;
|
||||||
|
else
|
||||||
|
b = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PORT_INOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<IdString, std::string>> Arch::getBelAttrs(BelId bel) const
|
||||||
|
{
|
||||||
|
std::vector<std::pair<IdString, std::string>> ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
WireId Arch::getBelPinWire(BelId bel, IdString pin) const
|
||||||
|
{
|
||||||
|
auto pin_name = pin.str(this);
|
||||||
|
auto bel_type = getBelType(bel);
|
||||||
|
if (bel_type == id_SLICE_LUT6) {
|
||||||
|
// For all LUT based inputs and outputs (I1-I6,O,OQ,OMUX) then change the I/O into the LUT
|
||||||
|
if (pin_name[0] == 'I' || pin_name[0] == 'O') {
|
||||||
|
switch (torc_info->bel_to_loc[bel.index].z) {
|
||||||
|
case 0:
|
||||||
|
case 4:
|
||||||
|
pin_name[0] = 'A';
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
case 5:
|
||||||
|
pin_name[0] = 'B';
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 6:
|
||||||
|
pin_name[0] = 'C';
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
case 7:
|
||||||
|
pin_name[0] = 'D';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (bel_type == id_PS7 || bel_type == id_MMCME2_ADV) {
|
||||||
|
// e.g. Convert DDRARB[0] -> DDRARB0
|
||||||
|
pin_name.erase(std::remove_if(pin_name.begin(), pin_name.end(), boost::is_any_of("[]")), pin_name.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto site_index = torc_info->bel_to_site_index[bel.index];
|
||||||
|
const auto &site = torc_info->sites.getSite(site_index);
|
||||||
|
auto &tw = site.getPinTilewire(pin_name);
|
||||||
|
|
||||||
|
if (tw.isUndefined())
|
||||||
|
log_error("no wire found for site '%s' pin '%s' \n", torc_info->bel_to_name(bel.index).c_str(),
|
||||||
|
pin_name.c_str());
|
||||||
|
|
||||||
|
return torc_info->tilewire_to_wire(tw);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<IdString> Arch::getBelPins(BelId bel) const
|
||||||
|
{
|
||||||
|
std::vector<IdString> ret;
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
WireId Arch::getWireByName(IdString name) const
|
||||||
|
{
|
||||||
|
WireId ret;
|
||||||
|
if (wire_by_name.empty()) {
|
||||||
|
for (int i = 0; i < torc_info->num_wires; i++)
|
||||||
|
wire_by_name[id(torc_info->wire_to_name(i))] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = wire_by_name.find(name);
|
||||||
|
if (it != wire_by_name.end())
|
||||||
|
ret.index = it->second;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdString Arch::getWireType(WireId wire) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(wire != WireId());
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
|
return IdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
std::vector<std::pair<IdString, std::string>> Arch::getWireAttrs(WireId wire) const
|
||||||
|
{
|
||||||
|
std::vector<std::pair<IdString, std::string>> ret;
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
PipId Arch::getPipByName(IdString name) const
|
||||||
|
{
|
||||||
|
PipId ret;
|
||||||
|
|
||||||
|
if (pip_by_name.empty()) {
|
||||||
|
for (int i = 0; i < torc_info->num_pips; i++) {
|
||||||
|
PipId pip;
|
||||||
|
pip.index = i;
|
||||||
|
pip_by_name[getPipName(pip)] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = pip_by_name.find(name);
|
||||||
|
if (it != pip_by_name.end())
|
||||||
|
ret.index = it->second;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdString Arch::getPipName(PipId pip) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(pip != PipId());
|
||||||
|
|
||||||
|
ExtendedWireInfo ewi_src(*torc_info->ddb, torc_info->pip_to_arc[pip.index].getSourceTilewire());
|
||||||
|
ExtendedWireInfo ewi_dst(*torc_info->ddb, torc_info->pip_to_arc[pip.index].getSinkTilewire());
|
||||||
|
std::stringstream pip_name;
|
||||||
|
pip_name << ewi_src.mTileName << "." << ewi_src.mWireName << ".->." << ewi_dst.mWireName;
|
||||||
|
return id(pip_name.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<IdString, std::string>> Arch::getPipAttrs(PipId pip) const
|
||||||
|
{
|
||||||
|
std::vector<std::pair<IdString, std::string>> ret;
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
BelId Arch::getPackagePinBel(const std::string &pin) const { return getBelByName(id(pin)); }
|
||||||
|
|
||||||
|
std::string Arch::getBelPackagePin(BelId bel) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
GroupId Arch::getGroupByName(IdString name) const
|
||||||
|
{
|
||||||
|
for (auto g : getGroups())
|
||||||
|
if (getGroupName(g) == name)
|
||||||
|
return g;
|
||||||
|
return GroupId();
|
||||||
|
}
|
||||||
|
|
||||||
|
IdString Arch::getGroupName(GroupId group) const
|
||||||
|
{
|
||||||
|
std::string suffix;
|
||||||
|
|
||||||
|
switch (group.type) {
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
|
default:
|
||||||
|
return IdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return id("X" + std::to_string(group.x) + "/Y" + std::to_string(group.y) + "/" + suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<GroupId> Arch::getGroups() const
|
||||||
|
{
|
||||||
|
std::vector<GroupId> ret;
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<BelId> Arch::getGroupBels(GroupId group) const
|
||||||
|
{
|
||||||
|
std::vector<BelId> ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<WireId> Arch::getGroupWires(GroupId group) const
|
||||||
|
{
|
||||||
|
std::vector<WireId> ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<PipId> Arch::getGroupPips(GroupId group) const
|
||||||
|
{
|
||||||
|
std::vector<PipId> ret;
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<GroupId> Arch::getGroupGroups(GroupId group) const
|
||||||
|
{
|
||||||
|
std::vector<GroupId> ret;
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; }
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool Arch::place() { return placer1(getCtx(), Placer1Cfg(getCtx())); }
|
||||||
|
|
||||||
|
bool Arch::route() { return router1(getCtx(), Router1Cfg(getCtx())); }
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
DecalXY Arch::getBelDecal(BelId bel) const
|
||||||
|
{
|
||||||
|
DecalXY decalxy;
|
||||||
|
decalxy.decal.type = DecalId::TYPE_BEL;
|
||||||
|
decalxy.decal.index = bel.index;
|
||||||
|
decalxy.decal.active = bel_to_cell.at(bel.index) != nullptr;
|
||||||
|
return decalxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
DecalXY Arch::getWireDecal(WireId wire) const
|
||||||
|
{
|
||||||
|
DecalXY decalxy;
|
||||||
|
decalxy.decal.type = DecalId::TYPE_WIRE;
|
||||||
|
decalxy.decal.index = wire.index;
|
||||||
|
decalxy.decal.active = wire_to_net.at(wire.index) != nullptr;
|
||||||
|
return decalxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
DecalXY Arch::getPipDecal(PipId pip) const
|
||||||
|
{
|
||||||
|
DecalXY decalxy;
|
||||||
|
decalxy.decal.type = DecalId::TYPE_PIP;
|
||||||
|
decalxy.decal.index = pip.index;
|
||||||
|
decalxy.decal.active = pip_to_net.at(pip.index) != nullptr;
|
||||||
|
return decalxy;
|
||||||
|
};
|
||||||
|
|
||||||
|
DecalXY Arch::getGroupDecal(GroupId group) const
|
||||||
|
{
|
||||||
|
DecalXY decalxy;
|
||||||
|
decalxy.decal.type = DecalId::TYPE_GROUP;
|
||||||
|
decalxy.decal.index = (group.type << 16) | (group.x << 8) | (group.y);
|
||||||
|
decalxy.decal.active = true;
|
||||||
|
return decalxy;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
|
||||||
|
{
|
||||||
|
std::vector<GraphicElement> ret;
|
||||||
|
|
||||||
|
if (decal.type == DecalId::TYPE_BEL) {
|
||||||
|
BelId bel;
|
||||||
|
bel.index = decal.index;
|
||||||
|
auto bel_type = getBelType(bel);
|
||||||
|
int x = torc_info->bel_to_loc[bel.index].x;
|
||||||
|
int y = torc_info->bel_to_loc[bel.index].y;
|
||||||
|
int z = torc_info->bel_to_loc[bel.index].z;
|
||||||
|
if (bel_type == id_SLICE_LUT6) {
|
||||||
|
GraphicElement el;
|
||||||
|
/*if (z>3) {
|
||||||
|
z = z - 4;
|
||||||
|
x -= logic_cell_x2- logic_cell_x1;
|
||||||
|
}*/
|
||||||
|
el.type = GraphicElement::TYPE_BOX;
|
||||||
|
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
|
||||||
|
el.x1 = x + logic_cell_x1;
|
||||||
|
el.x2 = x + logic_cell_x2;
|
||||||
|
el.y1 = y + logic_cell_y1 + (z)*logic_cell_pitch;
|
||||||
|
el.y2 = y + logic_cell_y2 + (z)*logic_cell_pitch;
|
||||||
|
ret.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
|
||||||
|
{
|
||||||
|
if (cell->type == id_SLICE_LUT6) {
|
||||||
|
if (fromPort.index >= id_I1.index && fromPort.index <= id_I6.index) {
|
||||||
|
if (toPort == id_O) {
|
||||||
|
delay.delay = 124; // Tilo
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (toPort == id_OQ) {
|
||||||
|
delay.delay = 95; // Tas
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fromPort == id_CLK) {
|
||||||
|
if (toPort == id_OQ) {
|
||||||
|
delay.delay = 456; // Tcko
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (cell->type == id_BUFGCTRL) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the port class, also setting clockPort to associated clock if applicable
|
||||||
|
TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const
|
||||||
|
{
|
||||||
|
if (cell->type == id_SLICE_LUT6) {
|
||||||
|
if (port == id_CLK)
|
||||||
|
return TMG_CLOCK_INPUT;
|
||||||
|
if (port == id_CIN)
|
||||||
|
return TMG_COMB_INPUT;
|
||||||
|
if (port == id_COUT)
|
||||||
|
return TMG_COMB_OUTPUT;
|
||||||
|
if (port == id_O) {
|
||||||
|
// LCs with no inputs are constant drivers
|
||||||
|
if (cell->lcInfo.inputCount == 0)
|
||||||
|
return TMG_IGNORE;
|
||||||
|
return TMG_COMB_OUTPUT;
|
||||||
|
}
|
||||||
|
if (cell->lcInfo.dffEnable) {
|
||||||
|
clockInfoCount = 1;
|
||||||
|
if (port == id_OQ)
|
||||||
|
return TMG_REGISTER_OUTPUT;
|
||||||
|
return TMG_REGISTER_INPUT;
|
||||||
|
} else {
|
||||||
|
return TMG_COMB_INPUT;
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
// if (port == id_OMUX)
|
||||||
|
} else if (cell->type == id_IOB33 || cell->type == id_IOB18) {
|
||||||
|
if (port == id_I)
|
||||||
|
return TMG_STARTPOINT;
|
||||||
|
else if (port == id_O)
|
||||||
|
return TMG_ENDPOINT;
|
||||||
|
} else if (cell->type == id_BUFGCTRL) {
|
||||||
|
if (port == id_O)
|
||||||
|
return TMG_COMB_OUTPUT;
|
||||||
|
return TMG_COMB_INPUT;
|
||||||
|
} else if (cell->type == id_PS7) {
|
||||||
|
// TODO
|
||||||
|
return TMG_IGNORE;
|
||||||
|
} else if (cell->type == id_MMCME2_ADV) {
|
||||||
|
return TMG_IGNORE;
|
||||||
|
}
|
||||||
|
log_error("no timing info for port '%s' of cell type '%s'\n", port.c_str(this), cell->type.c_str(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
|
||||||
|
{
|
||||||
|
TimingClockingInfo info;
|
||||||
|
if (cell->type == id_SLICE_LUT6) {
|
||||||
|
info.clock_port = id_CLK;
|
||||||
|
info.edge = cell->lcInfo.negClk ? FALLING_EDGE : RISING_EDGE;
|
||||||
|
if (port == id_OQ) {
|
||||||
|
bool has_clktoq = getCellDelay(cell, id_CLK, id_OQ, info.clockToQ);
|
||||||
|
NPNR_ASSERT(has_clktoq);
|
||||||
|
} else {
|
||||||
|
info.setup.delay = 124; // Tilo
|
||||||
|
info.hold.delay = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NPNR_ASSERT_FALSE("unhandled cell type in getPortClockingInfo");
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Arch::isGlobalNet(const NetInfo *net) const
|
||||||
|
{
|
||||||
|
if (net == nullptr)
|
||||||
|
return false;
|
||||||
|
return net->driver.cell != nullptr && net->driver.cell->type == id_BUFGCTRL && net->driver.port == id_O;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign arch arg info
|
||||||
|
void Arch::assignArchInfo()
|
||||||
|
{
|
||||||
|
for (auto &net : getCtx()->nets) {
|
||||||
|
NetInfo *ni = net.second.get();
|
||||||
|
if (isGlobalNet(ni))
|
||||||
|
ni->is_global = true;
|
||||||
|
ni->is_enable = false;
|
||||||
|
ni->is_reset = false;
|
||||||
|
for (auto usr : ni->users) {
|
||||||
|
if (is_enable_port(this, usr))
|
||||||
|
ni->is_enable = true;
|
||||||
|
if (is_reset_port(this, usr))
|
||||||
|
ni->is_reset = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto &cell : getCtx()->cells) {
|
||||||
|
CellInfo *ci = cell.second.get();
|
||||||
|
assignCellInfo(ci);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arch::assignCellInfo(CellInfo *cell)
|
||||||
|
{
|
||||||
|
cell->belType = cell->type;
|
||||||
|
if (cell->type == id_SLICE_LUT6) {
|
||||||
|
cell->lcInfo.dffEnable = bool_or_default(cell->params, id_DFF_ENABLE);
|
||||||
|
cell->lcInfo.carryEnable = bool_or_default(cell->params, id_CARRY_ENABLE);
|
||||||
|
cell->lcInfo.negClk = bool_or_default(cell->params, id_NEG_CLK);
|
||||||
|
cell->lcInfo.clk = get_net_or_empty(cell, id_CLK);
|
||||||
|
cell->lcInfo.cen = get_net_or_empty(cell, id_CEN);
|
||||||
|
cell->lcInfo.sr = get_net_or_empty(cell, id_SR);
|
||||||
|
cell->lcInfo.inputCount = 0;
|
||||||
|
if (get_net_or_empty(cell, id_I1))
|
||||||
|
cell->lcInfo.inputCount++;
|
||||||
|
if (get_net_or_empty(cell, id_I2))
|
||||||
|
cell->lcInfo.inputCount++;
|
||||||
|
if (get_net_or_empty(cell, id_I3))
|
||||||
|
cell->lcInfo.inputCount++;
|
||||||
|
if (get_net_or_empty(cell, id_I4))
|
||||||
|
cell->lcInfo.inputCount++;
|
||||||
|
if (get_net_or_empty(cell, id_I5))
|
||||||
|
cell->lcInfo.inputCount++;
|
||||||
|
if (get_net_or_empty(cell, id_I6))
|
||||||
|
cell->lcInfo.inputCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
934
xc7/arch.h
Normal file
934
xc7/arch.h
Normal file
@ -0,0 +1,934 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NEXTPNR_H
|
||||||
|
#error Include "arch.h" via "nextpnr.h" only.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "torc/Architecture.hpp"
|
||||||
|
#include "torc/Common.hpp"
|
||||||
|
using namespace torc::architecture;
|
||||||
|
using namespace torc::architecture::xilinx;
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <> struct hash<Segments::SegmentReference>
|
||||||
|
{
|
||||||
|
size_t operator()(const Segments::SegmentReference &s) const
|
||||||
|
{
|
||||||
|
size_t seed = 0;
|
||||||
|
boost::hash_combine(seed, hash<unsigned>()(s.getCompactSegmentIndex()));
|
||||||
|
boost::hash_combine(seed, hash<unsigned>()(s.getAnchorTileIndex()));
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <> struct equal_to<Segments::SegmentReference>
|
||||||
|
{
|
||||||
|
bool operator()(const Segments::SegmentReference &lhs, const Segments::SegmentReference &rhs) const
|
||||||
|
{
|
||||||
|
return lhs.getAnchorTileIndex() == rhs.getAnchorTileIndex() &&
|
||||||
|
lhs.getCompactSegmentIndex() == rhs.getCompactSegmentIndex();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <> struct hash<Tilewire>
|
||||||
|
{
|
||||||
|
size_t operator()(const Tilewire &t) const { return hash_value(t); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct hash<Arc>
|
||||||
|
{
|
||||||
|
size_t operator()(const Arc &a) const
|
||||||
|
{
|
||||||
|
size_t seed = 0;
|
||||||
|
boost::hash_combine(seed, hash_value(a.getSourceTilewire()));
|
||||||
|
boost::hash_combine(seed, hash_value(a.getSinkTilewire()));
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
/**** Everything in this section must be kept in sync with chipdb.py ****/
|
||||||
|
|
||||||
|
template <typename T> struct RelPtr
|
||||||
|
{
|
||||||
|
int32_t offset;
|
||||||
|
|
||||||
|
// void set(const T *ptr) {
|
||||||
|
// offset = reinterpret_cast<const char*>(ptr) -
|
||||||
|
// reinterpret_cast<const char*>(this);
|
||||||
|
// }
|
||||||
|
|
||||||
|
const T *get() const { return reinterpret_cast<const T *>(reinterpret_cast<const char *>(this) + offset); }
|
||||||
|
|
||||||
|
const T &operator[](size_t index) const { return get()[index]; }
|
||||||
|
|
||||||
|
const T &operator*() const { return *(get()); }
|
||||||
|
|
||||||
|
const T *operator->() const { return get(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct BelWirePOD {
|
||||||
|
int32_t port;
|
||||||
|
int32_t type;
|
||||||
|
int32_t wire_index;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct BelInfoPOD {
|
||||||
|
RelPtr<char> name;
|
||||||
|
int32_t type;
|
||||||
|
int32_t num_bel_wires;
|
||||||
|
RelPtr<BelWirePOD> bel_wires;
|
||||||
|
int8_t x, y, z;
|
||||||
|
int8_t padding_0;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct BelPortPOD {
|
||||||
|
int32_t bel_index;
|
||||||
|
int32_t port;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct PipInfoPOD {
|
||||||
|
enum PipFlags : uint32_t
|
||||||
|
{
|
||||||
|
FLAG_NONE = 0,
|
||||||
|
FLAG_ROUTETHRU = 1,
|
||||||
|
FLAG_NOCARRY = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
// RelPtr<char> name;
|
||||||
|
int32_t src, dst;
|
||||||
|
int32_t fast_delay;
|
||||||
|
int32_t slow_delay;
|
||||||
|
int8_t x, y;
|
||||||
|
int16_t src_seg, dst_seg;
|
||||||
|
int16_t switch_mask;
|
||||||
|
int32_t switch_index;
|
||||||
|
PipFlags flags;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct WireSegmentPOD {
|
||||||
|
int8_t x, y;
|
||||||
|
int16_t index;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct WireInfoPOD {
|
||||||
|
enum WireType : int8_t
|
||||||
|
{
|
||||||
|
WIRE_TYPE_NONE = 0,
|
||||||
|
WIRE_TYPE_GLB2LOCAL = 1,
|
||||||
|
WIRE_TYPE_GLB_NETWK = 2,
|
||||||
|
WIRE_TYPE_LOCAL = 3,
|
||||||
|
WIRE_TYPE_LUTFF_IN = 4,
|
||||||
|
WIRE_TYPE_LUTFF_IN_LUT = 5,
|
||||||
|
WIRE_TYPE_LUTFF_LOUT = 6,
|
||||||
|
WIRE_TYPE_LUTFF_OUT = 7,
|
||||||
|
WIRE_TYPE_LUTFF_COUT = 8,
|
||||||
|
WIRE_TYPE_LUTFF_GLOBAL = 9,
|
||||||
|
WIRE_TYPE_CARRY_IN_MUX = 10,
|
||||||
|
WIRE_TYPE_SP4_V = 11,
|
||||||
|
WIRE_TYPE_SP4_H = 12,
|
||||||
|
WIRE_TYPE_SP12_V = 13,
|
||||||
|
WIRE_TYPE_SP12_H = 14
|
||||||
|
};
|
||||||
|
|
||||||
|
RelPtr<char> name;
|
||||||
|
int32_t num_uphill, num_downhill;
|
||||||
|
RelPtr<int32_t> pips_uphill, pips_downhill;
|
||||||
|
|
||||||
|
int32_t num_bel_pins;
|
||||||
|
RelPtr<BelPortPOD> bel_pins;
|
||||||
|
|
||||||
|
int32_t num_segments;
|
||||||
|
RelPtr<WireSegmentPOD> segments;
|
||||||
|
|
||||||
|
int32_t fast_delay;
|
||||||
|
int32_t slow_delay;
|
||||||
|
|
||||||
|
int8_t x, y, z;
|
||||||
|
WireType type;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct PackagePinPOD {
|
||||||
|
RelPtr<char> name;
|
||||||
|
int32_t bel_index;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct PackageInfoPOD {
|
||||||
|
RelPtr<char> name;
|
||||||
|
int32_t num_pins;
|
||||||
|
RelPtr<PackagePinPOD> pins;
|
||||||
|
});
|
||||||
|
|
||||||
|
enum TileType : uint32_t
|
||||||
|
{
|
||||||
|
TILE_NONE = 0,
|
||||||
|
TILE_LOGIC = 1,
|
||||||
|
TILE_IO = 2,
|
||||||
|
TILE_RAMB = 3,
|
||||||
|
TILE_RAMT = 4,
|
||||||
|
TILE_DSP0 = 5,
|
||||||
|
TILE_DSP1 = 6,
|
||||||
|
TILE_DSP2 = 7,
|
||||||
|
TILE_DSP3 = 8,
|
||||||
|
TILE_IPCON = 9
|
||||||
|
};
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct ConfigBitPOD { int8_t row, col; });
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct ConfigEntryPOD {
|
||||||
|
RelPtr<char> name;
|
||||||
|
int32_t num_bits;
|
||||||
|
RelPtr<ConfigBitPOD> bits;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct TileInfoPOD {
|
||||||
|
int8_t cols, rows;
|
||||||
|
int16_t num_config_entries;
|
||||||
|
RelPtr<ConfigEntryPOD> entries;
|
||||||
|
});
|
||||||
|
|
||||||
|
static const int max_switch_bits = 5;
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct SwitchInfoPOD {
|
||||||
|
int32_t num_bits;
|
||||||
|
int32_t bel;
|
||||||
|
int8_t x, y;
|
||||||
|
ConfigBitPOD cbits[max_switch_bits];
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct IerenInfoPOD {
|
||||||
|
int8_t iox, ioy, ioz;
|
||||||
|
int8_t ierx, iery, ierz;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct BitstreamInfoPOD {
|
||||||
|
int32_t num_switches, num_ierens;
|
||||||
|
RelPtr<TileInfoPOD> tiles_nonrouting;
|
||||||
|
RelPtr<SwitchInfoPOD> switches;
|
||||||
|
RelPtr<IerenInfoPOD> ierens;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct BelConfigEntryPOD {
|
||||||
|
RelPtr<char> entry_name;
|
||||||
|
RelPtr<char> cbit_name;
|
||||||
|
int8_t x, y;
|
||||||
|
int16_t padding;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stores mapping between bel parameters and config bits,
|
||||||
|
// for extra cells where this mapping is non-trivial
|
||||||
|
NPNR_PACKED_STRUCT(struct BelConfigPOD {
|
||||||
|
int32_t bel_index;
|
||||||
|
int32_t num_entries;
|
||||||
|
RelPtr<BelConfigEntryPOD> entries;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct CellPathDelayPOD {
|
||||||
|
int32_t from_port;
|
||||||
|
int32_t to_port;
|
||||||
|
int32_t fast_delay;
|
||||||
|
int32_t slow_delay;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct CellTimingPOD {
|
||||||
|
int32_t type;
|
||||||
|
int32_t num_paths;
|
||||||
|
RelPtr<CellPathDelayPOD> path_delays;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
|
||||||
|
int32_t width, height;
|
||||||
|
int32_t num_bels, num_wires, num_pips;
|
||||||
|
int32_t num_switches, num_belcfgs, num_packages;
|
||||||
|
int32_t num_timing_cells;
|
||||||
|
RelPtr<BelInfoPOD> bel_data;
|
||||||
|
RelPtr<WireInfoPOD> wire_data;
|
||||||
|
RelPtr<PipInfoPOD> pip_data;
|
||||||
|
RelPtr<TileType> tile_grid;
|
||||||
|
RelPtr<BitstreamInfoPOD> bits_info;
|
||||||
|
RelPtr<BelConfigPOD> bel_config;
|
||||||
|
RelPtr<PackageInfoPOD> packages_data;
|
||||||
|
RelPtr<CellTimingPOD> cell_timing;
|
||||||
|
});
|
||||||
|
|
||||||
|
struct TorcInfo
|
||||||
|
{
|
||||||
|
TorcInfo(BaseCtx *ctx, const std::string &inDeviceName, const std::string &inPackageName);
|
||||||
|
TorcInfo() = delete;
|
||||||
|
std::unique_ptr<const DDB> ddb;
|
||||||
|
const Sites &sites;
|
||||||
|
const Tiles &tiles;
|
||||||
|
const Segments &segments;
|
||||||
|
|
||||||
|
const TileInfo &bel_to_tile_info(int32_t index) const
|
||||||
|
{
|
||||||
|
auto si = bel_to_site_index[index];
|
||||||
|
const auto &site = sites.getSite(si);
|
||||||
|
return tiles.getTileInfo(site.getTileIndex());
|
||||||
|
}
|
||||||
|
const std::string &bel_to_name(int32_t index) const
|
||||||
|
{
|
||||||
|
auto si = bel_to_site_index[index];
|
||||||
|
return sites.getSite(si).getName();
|
||||||
|
}
|
||||||
|
std::string wire_to_name(int32_t index) const
|
||||||
|
{
|
||||||
|
const auto &tw = wire_to_tilewire[index];
|
||||||
|
ExtendedWireInfo ewi(*ddb, tw);
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << ewi.mTileName << "/" << ewi.mWireName;
|
||||||
|
ss << "(" << tw.getWireIndex() << "@" << tw.getTileIndex() << ")";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
Loc wire_to_loc(int32_t index) const
|
||||||
|
{
|
||||||
|
const auto &tw = wire_to_tilewire[index];
|
||||||
|
ExtendedWireInfo ewi(*ddb, tw);
|
||||||
|
Loc l;
|
||||||
|
l.x = (int)ewi.mTileCol;
|
||||||
|
l.y = (int)ewi.mTileRow;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
WireId tilewire_to_wire(const Tilewire &tw) const
|
||||||
|
{
|
||||||
|
const auto &segment = segments.getTilewireSegment(tw);
|
||||||
|
if (!segment.isTrivial())
|
||||||
|
return segment_to_wire.at(segment);
|
||||||
|
return trivial_to_wire.at(tw);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SiteIndex> bel_to_site_index;
|
||||||
|
int num_bels;
|
||||||
|
std::vector<BelId> site_index_to_bel;
|
||||||
|
std::vector<IdString> site_index_to_type;
|
||||||
|
std::vector<Loc> bel_to_loc;
|
||||||
|
std::unordered_map<Segments::SegmentReference, WireId> segment_to_wire;
|
||||||
|
std::unordered_map<Tilewire, WireId> trivial_to_wire;
|
||||||
|
std::vector<Tilewire> wire_to_tilewire;
|
||||||
|
int num_wires;
|
||||||
|
std::vector<DelayInfo> wire_to_delay;
|
||||||
|
//std::vector<std::vector<int>> wire_to_pips_uphill;
|
||||||
|
std::vector<std::vector<PipId>> wire_to_pips_downhill;
|
||||||
|
std::vector<Arc> pip_to_arc;
|
||||||
|
int num_pips;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
std::vector<bool> wire_is_global;
|
||||||
|
std::vector<std::pair<int,int>> tile_to_xy;
|
||||||
|
|
||||||
|
TorcInfo(const std::string &inDeviceName, const std::string &inPackageName);
|
||||||
|
};
|
||||||
|
extern std::unique_ptr<const TorcInfo> torc_info;
|
||||||
|
|
||||||
|
/************************ End of chipdb section. ************************/
|
||||||
|
|
||||||
|
struct BelIterator
|
||||||
|
{
|
||||||
|
int cursor;
|
||||||
|
|
||||||
|
BelIterator operator++()
|
||||||
|
{
|
||||||
|
cursor++;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
BelIterator operator++(int)
|
||||||
|
{
|
||||||
|
BelIterator prior(*this);
|
||||||
|
cursor++;
|
||||||
|
return prior;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const BelIterator &other) const { return cursor != other.cursor; }
|
||||||
|
|
||||||
|
bool operator==(const BelIterator &other) const { return cursor == other.cursor; }
|
||||||
|
|
||||||
|
BelId operator*() const
|
||||||
|
{
|
||||||
|
BelId ret;
|
||||||
|
ret.index = cursor;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BelRange
|
||||||
|
{
|
||||||
|
BelIterator b, e;
|
||||||
|
BelIterator begin() const { return b; }
|
||||||
|
BelIterator end() const { return e; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct BelPinIterator
|
||||||
|
{
|
||||||
|
const BelId bel;
|
||||||
|
Array<const WireIndex>::iterator it;
|
||||||
|
|
||||||
|
void operator++() { it++; }
|
||||||
|
bool operator!=(const BelPinIterator &other) const { return it != other.it && bel != other.bel; }
|
||||||
|
|
||||||
|
BelPin operator*() const
|
||||||
|
{
|
||||||
|
BelPin ret;
|
||||||
|
ret.bel = bel;
|
||||||
|
ret.pin = IdString();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BelPinRange
|
||||||
|
{
|
||||||
|
BelPinIterator b, e;
|
||||||
|
BelPinIterator begin() const { return b; }
|
||||||
|
BelPinIterator end() const { return e; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct WireIterator
|
||||||
|
{
|
||||||
|
int cursor = -1;
|
||||||
|
|
||||||
|
void operator++() { cursor++; }
|
||||||
|
bool operator!=(const WireIterator &other) const { return cursor != other.cursor; }
|
||||||
|
|
||||||
|
WireId operator*() const
|
||||||
|
{
|
||||||
|
WireId ret;
|
||||||
|
ret.index = cursor;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WireRange
|
||||||
|
{
|
||||||
|
WireIterator b, e;
|
||||||
|
WireIterator begin() const { return b; }
|
||||||
|
WireIterator end() const { return e; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct AllPipIterator
|
||||||
|
{
|
||||||
|
int cursor = -1;
|
||||||
|
|
||||||
|
void operator++() { cursor++; }
|
||||||
|
bool operator!=(const AllPipIterator &other) const { return cursor != other.cursor; }
|
||||||
|
|
||||||
|
PipId operator*() const
|
||||||
|
{
|
||||||
|
PipId ret;
|
||||||
|
ret.index = cursor;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AllPipRange
|
||||||
|
{
|
||||||
|
AllPipIterator b, e;
|
||||||
|
AllPipIterator begin() const { return b; }
|
||||||
|
AllPipIterator end() const { return e; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct PipIterator
|
||||||
|
{
|
||||||
|
const PipId *cursor = nullptr;
|
||||||
|
|
||||||
|
void operator++() { cursor++; }
|
||||||
|
bool operator!=(const PipIterator &other) const { return cursor != other.cursor; }
|
||||||
|
|
||||||
|
PipId operator*() const
|
||||||
|
{
|
||||||
|
return *cursor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PipRange
|
||||||
|
{
|
||||||
|
PipIterator b, e;
|
||||||
|
PipIterator begin() const { return b; }
|
||||||
|
PipIterator end() const { return e; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ArchArgs
|
||||||
|
{
|
||||||
|
enum ArchArgsTypes
|
||||||
|
{
|
||||||
|
NONE,
|
||||||
|
Z020,
|
||||||
|
VX980
|
||||||
|
} type = NONE;
|
||||||
|
std::string package;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Arch : BaseCtx
|
||||||
|
{
|
||||||
|
bool fast_part;
|
||||||
|
const ChipInfoPOD *chip_info;
|
||||||
|
const PackageInfoPOD *package_info;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
|
||||||
|
mutable std::unordered_map<IdString, int> wire_by_name;
|
||||||
|
mutable std::unordered_map<IdString, int> pip_by_name;
|
||||||
|
mutable std::unordered_map<Loc, BelId> bel_by_loc;
|
||||||
|
|
||||||
|
// std::vector<bool> bel_carry;
|
||||||
|
std::vector<CellInfo *> bel_to_cell;
|
||||||
|
std::vector<NetInfo *> wire_to_net;
|
||||||
|
std::vector<NetInfo *> pip_to_net;
|
||||||
|
// std::vector<NetInfo *> switches_locked;
|
||||||
|
|
||||||
|
ArchArgs args;
|
||||||
|
Arch(ArchArgs args);
|
||||||
|
|
||||||
|
std::string getChipName() const;
|
||||||
|
|
||||||
|
IdString archId() const { return id("xc7"); }
|
||||||
|
ArchArgs archArgs() const { return args; }
|
||||||
|
IdString archArgsToId(ArchArgs args) const;
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
int getGridDimX() const { return width; }
|
||||||
|
int getGridDimY() const { return height; }
|
||||||
|
int getTileBelDimZ(int, int) const { return 8; }
|
||||||
|
int getTilePipDimZ(int, int) const { return 1; }
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
BelId getBelByName(IdString name) const;
|
||||||
|
|
||||||
|
IdString getBelName(BelId bel) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(bel != BelId());
|
||||||
|
auto name = torc_info->bel_to_name(bel.index);
|
||||||
|
if (getBelType(bel) == id_SLICE_LUT6) {
|
||||||
|
// Append LUT name to name
|
||||||
|
name.reserve(name.size() + 2);
|
||||||
|
name += "_";
|
||||||
|
switch (torc_info->bel_to_loc[bel.index].z) {
|
||||||
|
case 0:
|
||||||
|
case 4:
|
||||||
|
name += 'A';
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
case 5:
|
||||||
|
name += 'B';
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 6:
|
||||||
|
name += 'C';
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
case 7:
|
||||||
|
name += 'D';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return id(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getBelChecksum(BelId bel) const { return bel.index; }
|
||||||
|
|
||||||
|
void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength)
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(bel != BelId());
|
||||||
|
NPNR_ASSERT(bel_to_cell[bel.index] == nullptr);
|
||||||
|
|
||||||
|
bel_to_cell[bel.index] = cell;
|
||||||
|
// bel_carry[bel.index] = (cell->type == id_ICESTORM_LC && cell->lcInfo.carryEnable);
|
||||||
|
cell->bel = bel;
|
||||||
|
cell->belStrength = strength;
|
||||||
|
refreshUiBel(bel);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unbindBel(BelId bel)
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(bel != BelId());
|
||||||
|
NPNR_ASSERT(bel_to_cell[bel.index] != nullptr);
|
||||||
|
bel_to_cell[bel.index]->bel = BelId();
|
||||||
|
bel_to_cell[bel.index]->belStrength = STRENGTH_NONE;
|
||||||
|
bel_to_cell[bel.index] = nullptr;
|
||||||
|
// bel_carry[bel.index] = false;
|
||||||
|
refreshUiBel(bel);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkBelAvail(BelId bel) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(bel != BelId());
|
||||||
|
return bel_to_cell[bel.index] == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CellInfo *getBoundBelCell(BelId bel) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(bel != BelId());
|
||||||
|
return bel_to_cell[bel.index];
|
||||||
|
}
|
||||||
|
|
||||||
|
CellInfo *getConflictingBelCell(BelId bel) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(bel != BelId());
|
||||||
|
return bel_to_cell[bel.index];
|
||||||
|
}
|
||||||
|
|
||||||
|
BelRange getBels() const
|
||||||
|
{
|
||||||
|
BelRange range;
|
||||||
|
range.b.cursor = 0;
|
||||||
|
range.e.cursor = torc_info->num_bels;
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loc getBelLocation(BelId bel) const { return torc_info->bel_to_loc[bel.index]; }
|
||||||
|
|
||||||
|
BelId getBelByLocation(Loc loc) const;
|
||||||
|
BelRange getBelsByTile(int x, int y) const;
|
||||||
|
|
||||||
|
bool getBelGlobalBuf(BelId bel) const { return getBelType(bel) == id_BUFGCTRL; }
|
||||||
|
|
||||||
|
IdString getBelType(BelId bel) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(bel != BelId());
|
||||||
|
auto site_index = torc_info->bel_to_site_index[bel.index];
|
||||||
|
return torc_info->site_index_to_type[site_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<IdString, std::string>> getBelAttrs(BelId bel) const;
|
||||||
|
|
||||||
|
WireId getBelPinWire(BelId bel, IdString pin) const;
|
||||||
|
PortType getBelPinType(BelId bel, IdString pin) const;
|
||||||
|
std::vector<IdString> getBelPins(BelId bel) const;
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
WireId getWireByName(IdString name) const;
|
||||||
|
|
||||||
|
IdString getWireName(WireId wire) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(wire != WireId());
|
||||||
|
return id(torc_info->wire_to_name(wire.index));
|
||||||
|
}
|
||||||
|
|
||||||
|
IdString getWireType(WireId wire) const;
|
||||||
|
std::vector<std::pair<IdString, std::string>> getWireAttrs(WireId wire) const;
|
||||||
|
|
||||||
|
uint32_t getWireChecksum(WireId wire) const { return wire.index; }
|
||||||
|
|
||||||
|
void bindWire(WireId wire, NetInfo *net, PlaceStrength strength)
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(wire != WireId());
|
||||||
|
NPNR_ASSERT(wire_to_net[wire.index] == nullptr);
|
||||||
|
wire_to_net[wire.index] = net;
|
||||||
|
net->wires[wire].pip = PipId();
|
||||||
|
net->wires[wire].strength = strength;
|
||||||
|
refreshUiWire(wire);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unbindWire(WireId wire)
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(wire != WireId());
|
||||||
|
NPNR_ASSERT(wire_to_net[wire.index] != nullptr);
|
||||||
|
|
||||||
|
auto &net_wires = wire_to_net[wire.index]->wires;
|
||||||
|
auto it = net_wires.find(wire);
|
||||||
|
NPNR_ASSERT(it != net_wires.end());
|
||||||
|
|
||||||
|
auto pip = it->second.pip;
|
||||||
|
if (pip != PipId()) {
|
||||||
|
pip_to_net[pip.index] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
net_wires.erase(it);
|
||||||
|
wire_to_net[wire.index] = nullptr;
|
||||||
|
refreshUiWire(wire);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkWireAvail(WireId wire) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(wire != WireId());
|
||||||
|
return wire_to_net[wire.index] == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetInfo *getBoundWireNet(WireId wire) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(wire != WireId());
|
||||||
|
return wire_to_net[wire.index];
|
||||||
|
}
|
||||||
|
|
||||||
|
WireId getConflictingWireWire(WireId wire) const { return wire; }
|
||||||
|
|
||||||
|
NetInfo *getConflictingWireNet(WireId wire) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(wire != WireId());
|
||||||
|
return wire_to_net[wire.index];
|
||||||
|
}
|
||||||
|
|
||||||
|
DelayInfo getWireDelay(WireId wire) const { return {}; }
|
||||||
|
|
||||||
|
BelPinRange getWireBelPins(WireId wire) const
|
||||||
|
{
|
||||||
|
BelPinRange range;
|
||||||
|
// TODO
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
WireRange getWires() const
|
||||||
|
{
|
||||||
|
WireRange range;
|
||||||
|
range.b.cursor = 0;
|
||||||
|
range.e.cursor = torc_info->num_wires;
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
PipId getPipByName(IdString name) const;
|
||||||
|
|
||||||
|
void bindPip(PipId pip, NetInfo *net, PlaceStrength strength)
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(pip != PipId());
|
||||||
|
NPNR_ASSERT(pip_to_net[pip.index] == nullptr);
|
||||||
|
|
||||||
|
pip_to_net[pip.index] = net;
|
||||||
|
|
||||||
|
WireId dst = getPipDstWire(pip);
|
||||||
|
NPNR_ASSERT(wire_to_net[dst.index] == nullptr);
|
||||||
|
wire_to_net[dst.index] = net;
|
||||||
|
net->wires[dst].pip = pip;
|
||||||
|
net->wires[dst].strength = strength;
|
||||||
|
refreshUiPip(pip);
|
||||||
|
refreshUiWire(dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unbindPip(PipId pip)
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(pip != PipId());
|
||||||
|
NPNR_ASSERT(pip_to_net[pip.index] != nullptr);
|
||||||
|
|
||||||
|
WireId dst = getPipDstWire(pip);
|
||||||
|
NPNR_ASSERT(wire_to_net[dst.index] != nullptr);
|
||||||
|
wire_to_net[dst.index] = nullptr;
|
||||||
|
pip_to_net[pip.index]->wires.erase(dst);
|
||||||
|
|
||||||
|
pip_to_net[pip.index] = nullptr;
|
||||||
|
refreshUiPip(pip);
|
||||||
|
refreshUiWire(dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkPipAvail(PipId pip) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(pip != PipId());
|
||||||
|
return pip_to_net[pip.index] == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetInfo *getBoundPipNet(PipId pip) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(pip != PipId());
|
||||||
|
return pip_to_net[pip.index];
|
||||||
|
}
|
||||||
|
|
||||||
|
WireId getConflictingPipWire(PipId pip) const { return WireId(); }
|
||||||
|
|
||||||
|
NetInfo *getConflictingPipNet(PipId pip) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(pip != PipId());
|
||||||
|
return pip_to_net[pip.index];
|
||||||
|
}
|
||||||
|
|
||||||
|
AllPipRange getPips() const
|
||||||
|
{
|
||||||
|
AllPipRange range;
|
||||||
|
range.b.cursor = 0;
|
||||||
|
range.e.cursor = torc_info->num_pips;
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loc getPipLocation(PipId pip) const
|
||||||
|
{
|
||||||
|
Loc loc;
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
|
return loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdString getPipName(PipId pip) const;
|
||||||
|
|
||||||
|
IdString getPipType(PipId pip) const { return IdString(); }
|
||||||
|
std::vector<std::pair<IdString, std::string>> getPipAttrs(PipId pip) const;
|
||||||
|
|
||||||
|
uint32_t getPipChecksum(PipId pip) const { return pip.index; }
|
||||||
|
|
||||||
|
WireId getPipSrcWire(PipId pip) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(pip != PipId());
|
||||||
|
|
||||||
|
const auto &arc = torc_info->pip_to_arc[pip.index];
|
||||||
|
const auto &tw = arc.getSourceTilewire();
|
||||||
|
return torc_info->tilewire_to_wire(tw);
|
||||||
|
}
|
||||||
|
|
||||||
|
WireId getPipDstWire(PipId pip) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(pip != PipId());
|
||||||
|
const auto &arc = torc_info->pip_to_arc[pip.index];
|
||||||
|
const auto &tw = arc.getSinkTilewire();
|
||||||
|
return torc_info->tilewire_to_wire(tw);
|
||||||
|
}
|
||||||
|
|
||||||
|
DelayInfo getPipDelay(PipId pip) const
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(pip != PipId());
|
||||||
|
auto wire = getPipDstWire(pip);
|
||||||
|
return torc_info->wire_to_delay[wire.index];
|
||||||
|
}
|
||||||
|
|
||||||
|
PipRange getPipsDownhill(WireId wire) const
|
||||||
|
{
|
||||||
|
PipRange range;
|
||||||
|
NPNR_ASSERT(wire != WireId());
|
||||||
|
const auto &pips = torc_info->wire_to_pips_downhill[wire.index];
|
||||||
|
range.b.cursor = pips.data();
|
||||||
|
range.e.cursor = range.b.cursor + pips.size();
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
PipRange getPipsUphill(WireId wire) const
|
||||||
|
{
|
||||||
|
PipRange range;
|
||||||
|
// NPNR_ASSERT(wire != WireId());
|
||||||
|
// const auto &pips = torc_info->wire_to_pips_uphill[wire.index];
|
||||||
|
// range.b.cursor = pips.data();
|
||||||
|
// range.e.cursor = range.b.cursor + pips.size();
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
PipRange getWireAliases(WireId wire) const
|
||||||
|
{
|
||||||
|
PipRange range;
|
||||||
|
NPNR_ASSERT(wire != WireId());
|
||||||
|
range.b.cursor = nullptr;
|
||||||
|
range.e.cursor = nullptr;
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
BelId getPackagePinBel(const std::string &pin) const;
|
||||||
|
std::string getBelPackagePin(BelId bel) const;
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
GroupId getGroupByName(IdString name) const;
|
||||||
|
IdString getGroupName(GroupId group) const;
|
||||||
|
std::vector<GroupId> getGroups() const;
|
||||||
|
std::vector<BelId> getGroupBels(GroupId group) const;
|
||||||
|
std::vector<WireId> getGroupWires(GroupId group) const;
|
||||||
|
std::vector<PipId> getGroupPips(GroupId group) const;
|
||||||
|
std::vector<GroupId> getGroupGroups(GroupId group) const;
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
delay_t estimateDelay(WireId src, WireId dst) const;
|
||||||
|
delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const;
|
||||||
|
delay_t getDelayEpsilon() const { return 20; }
|
||||||
|
delay_t getRipupDelayPenalty() const { return 200; }
|
||||||
|
float getDelayNS(delay_t v) const { return v * 0.001; }
|
||||||
|
|
||||||
|
DelayInfo getDelayFromNS(float ns) const
|
||||||
|
{
|
||||||
|
DelayInfo del;
|
||||||
|
del.delay = delay_t(ns * 1000);
|
||||||
|
return del;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t getDelayChecksum(delay_t v) const { return v; }
|
||||||
|
bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const;
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
bool pack();
|
||||||
|
bool place();
|
||||||
|
bool route();
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
std::vector<GraphicElement> getDecalGraphics(DecalId decal) const;
|
||||||
|
|
||||||
|
DecalXY getBelDecal(BelId bel) const;
|
||||||
|
DecalXY getWireDecal(WireId wire) const;
|
||||||
|
DecalXY getPipDecal(PipId pip) const;
|
||||||
|
DecalXY getGroupDecal(GroupId group) const;
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
// Get the delay through a cell from one port to another, returning false
|
||||||
|
// if no path exists
|
||||||
|
bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const;
|
||||||
|
// Get the port class, also setting clockDomain if applicable
|
||||||
|
TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const;
|
||||||
|
// Get the TimingClockingInfo of a port
|
||||||
|
TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const;
|
||||||
|
// Return true if a port is a net
|
||||||
|
bool isGlobalNet(const NetInfo *net) const;
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
// Perform placement validity checks, returning false on failure (all
|
||||||
|
// implemented in arch_place.cc)
|
||||||
|
|
||||||
|
// Whether or not a given cell can be placed at a given Bel
|
||||||
|
// This is not intended for Bel type checks, but finer-grained constraints
|
||||||
|
// such as conflicting set/reset signals, etc
|
||||||
|
bool isValidBelForCell(CellInfo *cell, BelId bel) const;
|
||||||
|
|
||||||
|
// Return true whether all Bels at a given location are valid
|
||||||
|
bool isBelLocationValid(BelId bel) const;
|
||||||
|
|
||||||
|
// Helper function for above
|
||||||
|
bool logicCellsCompatible(const CellInfo **it, const size_t size) const;
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
// Assign architecure-specific arguments to nets and cells, which must be
|
||||||
|
// called between packing or further
|
||||||
|
// netlist modifications, and validity checks
|
||||||
|
void assignArchInfo();
|
||||||
|
void assignCellInfo(CellInfo *cell);
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
BelPin getIOBSharingPLLPin(BelId pll, IdString pll_pin) const
|
||||||
|
{
|
||||||
|
auto wire = getBelPinWire(pll, pll_pin);
|
||||||
|
for (auto src_bel : getWireBelPins(wire)) {
|
||||||
|
if (getBelType(src_bel.bel) == id_SB_IO && src_bel.pin == id_D_IN_0) {
|
||||||
|
return src_bel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NPNR_ASSERT_FALSE("Expected PLL pin to share an output with an SB_IO D_IN_{0,1}");
|
||||||
|
}
|
||||||
|
|
||||||
|
float placer_constraintWeight = 10;
|
||||||
|
};
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
78
xc7/arch_place.cc
Normal file
78
xc7/arch_place.cc
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cells.h"
|
||||||
|
#include "nextpnr.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <boost/range/iterator_range.hpp>
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
bool Arch::logicCellsCompatible(const CellInfo **it, const size_t size) const
|
||||||
|
{
|
||||||
|
// TODO: Check clock, clock-enable, and set-reset compatiility
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Arch::isBelLocationValid(BelId bel) const
|
||||||
|
{
|
||||||
|
if (getBelType(bel) == id("XC7_LC")) {
|
||||||
|
std::array<const CellInfo *, 4> bel_cells;
|
||||||
|
size_t num_cells = 0;
|
||||||
|
Loc bel_loc = getBelLocation(bel);
|
||||||
|
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
|
||||||
|
CellInfo *ci_other = getBoundBelCell(bel_other);
|
||||||
|
if (ci_other != nullptr)
|
||||||
|
bel_cells[num_cells++] = ci_other;
|
||||||
|
}
|
||||||
|
return logicCellsCompatible(bel_cells.data(), num_cells);
|
||||||
|
} else {
|
||||||
|
CellInfo *ci = getBoundBelCell(bel);
|
||||||
|
if (ci == nullptr)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return isValidBelForCell(ci, bel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
|
||||||
|
{
|
||||||
|
if (cell->type == id("XC7_LC")) {
|
||||||
|
std::array<const CellInfo *, 4> bel_cells;
|
||||||
|
size_t num_cells = 0;
|
||||||
|
|
||||||
|
Loc bel_loc = getBelLocation(bel);
|
||||||
|
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
|
||||||
|
CellInfo *ci_other = getBoundBelCell(bel_other);
|
||||||
|
if (ci_other != nullptr && bel_other != bel)
|
||||||
|
bel_cells[num_cells++] = ci_other;
|
||||||
|
}
|
||||||
|
|
||||||
|
bel_cells[num_cells++] = cell;
|
||||||
|
return logicCellsCompatible(bel_cells.data(), num_cells);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
144
xc7/arch_pybindings.cc
Normal file
144
xc7/arch_pybindings.cc
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NO_PYTHON
|
||||||
|
|
||||||
|
#include "arch_pybindings.h"
|
||||||
|
#include "nextpnr.h"
|
||||||
|
#include "pybindings.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
void arch_wrap_python()
|
||||||
|
{
|
||||||
|
using namespace PythonConversion;
|
||||||
|
class_<ArchArgs>("ArchArgs").def_readwrite("type", &ArchArgs::type);
|
||||||
|
|
||||||
|
class_<BelId>("BelId").def_readwrite("index", &BelId::index);
|
||||||
|
|
||||||
|
class_<WireId>("WireId").def_readwrite("index", &WireId::index);
|
||||||
|
|
||||||
|
class_<PipId>("PipId").def_readwrite("index", &PipId::index);
|
||||||
|
|
||||||
|
class_<BelPin>("BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin);
|
||||||
|
|
||||||
|
auto arch_cls = class_<Arch, Arch *, bases<BaseCtx>, boost::noncopyable>("Arch", init<ArchArgs>());
|
||||||
|
auto ctx_cls = class_<Context, Context *, bases<Arch>, boost::noncopyable>("Context", no_init)
|
||||||
|
.def("checksum", &Context::checksum)
|
||||||
|
.def("pack", &Context::pack)
|
||||||
|
.def("place", &Context::place)
|
||||||
|
.def("route", &Context::route);
|
||||||
|
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getBelType), &Context::getBelType, conv_to_str<IdString>,
|
||||||
|
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelType");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::checkBelAvail), &Context::checkBelAvail, pass_through<bool>,
|
||||||
|
conv_from_str<BelId>>::def_wrap(ctx_cls, "checkBelAvail");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getBelChecksum), &Context::getBelChecksum, pass_through<uint32_t>,
|
||||||
|
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelChecksum");
|
||||||
|
fn_wrapper_3a_v<Context, decltype(&Context::bindBel), &Context::bindBel, conv_from_str<BelId>,
|
||||||
|
addr_and_unwrap<CellInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindBel");
|
||||||
|
fn_wrapper_1a_v<Context, decltype(&Context::unbindBel), &Context::unbindBel, conv_from_str<BelId>>::def_wrap(
|
||||||
|
ctx_cls, "unbindBel");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getBoundBelCell), &Context::getBoundBelCell, deref_and_wrap<CellInfo>,
|
||||||
|
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBoundBelCell");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getConflictingBelCell), &Context::getConflictingBelCell,
|
||||||
|
deref_and_wrap<CellInfo>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell");
|
||||||
|
fn_wrapper_0a<Context, decltype(&Context::getBels), &Context::getBels, wrap_context<BelRange>>::def_wrap(ctx_cls,
|
||||||
|
"getBels");
|
||||||
|
|
||||||
|
fn_wrapper_2a<Context, decltype(&Context::getBelPinWire), &Context::getBelPinWire, conv_to_str<WireId>,
|
||||||
|
conv_from_str<BelId>, conv_from_str<IdString>>::def_wrap(ctx_cls, "getBelPinWire");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getWireBelPins), &Context::getWireBelPins, wrap_context<BelPinRange>,
|
||||||
|
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireBelPins");
|
||||||
|
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getWireChecksum), &Context::getWireChecksum, pass_through<uint32_t>,
|
||||||
|
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireChecksum");
|
||||||
|
fn_wrapper_3a_v<Context, decltype(&Context::bindWire), &Context::bindWire, conv_from_str<WireId>,
|
||||||
|
addr_and_unwrap<NetInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindWire");
|
||||||
|
fn_wrapper_1a_v<Context, decltype(&Context::unbindWire), &Context::unbindWire, conv_from_str<WireId>>::def_wrap(
|
||||||
|
ctx_cls, "unbindWire");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::checkWireAvail), &Context::checkWireAvail, pass_through<bool>,
|
||||||
|
conv_from_str<WireId>>::def_wrap(ctx_cls, "checkWireAvail");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getBoundWireNet), &Context::getBoundWireNet, deref_and_wrap<NetInfo>,
|
||||||
|
conv_from_str<WireId>>::def_wrap(ctx_cls, "getBoundWireNet");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getConflictingWireNet), &Context::getConflictingWireNet,
|
||||||
|
deref_and_wrap<NetInfo>, conv_from_str<WireId>>::def_wrap(ctx_cls, "getConflictingWireNet");
|
||||||
|
|
||||||
|
fn_wrapper_0a<Context, decltype(&Context::getWires), &Context::getWires, wrap_context<WireRange>>::def_wrap(
|
||||||
|
ctx_cls, "getWires");
|
||||||
|
|
||||||
|
fn_wrapper_0a<Context, decltype(&Context::getPips), &Context::getPips, wrap_context<AllPipRange>>::def_wrap(
|
||||||
|
ctx_cls, "getPips");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getPipChecksum), &Context::getPipChecksum, pass_through<uint32_t>,
|
||||||
|
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipChecksum");
|
||||||
|
fn_wrapper_3a_v<Context, decltype(&Context::bindPip), &Context::bindPip, conv_from_str<PipId>,
|
||||||
|
addr_and_unwrap<NetInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindPip");
|
||||||
|
fn_wrapper_1a_v<Context, decltype(&Context::unbindPip), &Context::unbindPip, conv_from_str<PipId>>::def_wrap(
|
||||||
|
ctx_cls, "unbindPip");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::checkPipAvail), &Context::checkPipAvail, pass_through<bool>,
|
||||||
|
conv_from_str<PipId>>::def_wrap(ctx_cls, "checkPipAvail");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getBoundPipNet), &Context::getBoundPipNet, deref_and_wrap<NetInfo>,
|
||||||
|
conv_from_str<PipId>>::def_wrap(ctx_cls, "getBoundPipNet");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getConflictingPipNet), &Context::getConflictingPipNet,
|
||||||
|
deref_and_wrap<NetInfo>, conv_from_str<PipId>>::def_wrap(ctx_cls, "getConflictingPipNet");
|
||||||
|
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getPipsDownhill), &Context::getPipsDownhill, wrap_context<PipRange>,
|
||||||
|
conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsDownhill");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getPipsUphill), &Context::getPipsUphill, wrap_context<PipRange>,
|
||||||
|
conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsUphill");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getWireAliases), &Context::getWireAliases, wrap_context<PipRange>,
|
||||||
|
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireAliases");
|
||||||
|
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getPipSrcWire), &Context::getPipSrcWire, conv_to_str<WireId>,
|
||||||
|
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipSrcWire");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getPipDstWire), &Context::getPipDstWire, conv_to_str<WireId>,
|
||||||
|
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDstWire");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getPipDelay), &Context::getPipDelay, pass_through<DelayInfo>,
|
||||||
|
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDelay");
|
||||||
|
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getPackagePinBel), &Context::getPackagePinBel, conv_to_str<BelId>,
|
||||||
|
pass_through<std::string>>::def_wrap(ctx_cls, "getPackagePinBel");
|
||||||
|
fn_wrapper_1a<Context, decltype(&Context::getBelPackagePin), &Context::getBelPackagePin, pass_through<std::string>,
|
||||||
|
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelPackagePin");
|
||||||
|
|
||||||
|
fn_wrapper_0a<Context, decltype(&Context::getChipName), &Context::getChipName, pass_through<std::string>>::def_wrap(
|
||||||
|
ctx_cls, "getChipName");
|
||||||
|
fn_wrapper_0a<Context, decltype(&Context::archId), &Context::archId, conv_to_str<IdString>>::def_wrap(ctx_cls,
|
||||||
|
"archId");
|
||||||
|
|
||||||
|
typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
|
||||||
|
typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
|
||||||
|
|
||||||
|
readonly_wrapper<Context, decltype(&Context::cells), &Context::cells, wrap_context<CellMap &>>::def_wrap(ctx_cls,
|
||||||
|
"cells");
|
||||||
|
readonly_wrapper<Context, decltype(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls,
|
||||||
|
"nets");
|
||||||
|
WRAP_RANGE(Bel, conv_to_str<BelId>);
|
||||||
|
WRAP_RANGE(Wire, conv_to_str<WireId>);
|
||||||
|
WRAP_RANGE(AllPip, conv_to_str<PipId>);
|
||||||
|
WRAP_RANGE(Pip, conv_to_str<PipId>);
|
||||||
|
|
||||||
|
WRAP_MAP_UPTR(CellMap, "IdCellMap");
|
||||||
|
WRAP_MAP_UPTR(NetMap, "IdNetMap");
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // NO_PYTHON
|
69
xc7/arch_pybindings.h
Normal file
69
xc7/arch_pybindings.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef ARCH_PYBINDINGS_H
|
||||||
|
#define ARCH_PYBINDINGS_H
|
||||||
|
#ifndef NO_PYTHON
|
||||||
|
|
||||||
|
#include "nextpnr.h"
|
||||||
|
#include "pybindings.h"
|
||||||
|
#include "pywrappers.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
namespace PythonConversion {
|
||||||
|
|
||||||
|
template <> struct string_converter<BelId>
|
||||||
|
{
|
||||||
|
BelId from_str(Context *ctx, std::string name) { return ctx->getBelByName(ctx->id(name)); }
|
||||||
|
|
||||||
|
std::string to_str(Context *ctx, BelId id)
|
||||||
|
{
|
||||||
|
if (id == BelId())
|
||||||
|
throw bad_wrap();
|
||||||
|
return ctx->getBelName(id).str(ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct string_converter<WireId>
|
||||||
|
{
|
||||||
|
WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); }
|
||||||
|
|
||||||
|
std::string to_str(Context *ctx, WireId id) { return ctx->getWireName(id).str(ctx); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct string_converter<const WireId>
|
||||||
|
{
|
||||||
|
WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); }
|
||||||
|
|
||||||
|
std::string to_str(Context *ctx, WireId id) { return ctx->getWireName(id).str(ctx); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct string_converter<PipId>
|
||||||
|
{
|
||||||
|
PipId from_str(Context *ctx, std::string name) { return ctx->getPipByName(ctx->id(name)); }
|
||||||
|
|
||||||
|
std::string to_str(Context *ctx, PipId id) { return ctx->getPipName(id).str(ctx); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace PythonConversion
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
#endif
|
||||||
|
#endif
|
201
xc7/archdefs.h
Normal file
201
xc7/archdefs.h
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NEXTPNR_H
|
||||||
|
#error Include "archdefs.h" via "nextpnr.h" only.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "torc/Architecture.hpp"
|
||||||
|
using namespace torc::architecture;
|
||||||
|
using namespace torc::architecture::xilinx;
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
typedef int delay_t;
|
||||||
|
|
||||||
|
struct DelayInfo
|
||||||
|
{
|
||||||
|
delay_t delay = 0;
|
||||||
|
|
||||||
|
delay_t minRaiseDelay() const { return delay; }
|
||||||
|
delay_t maxRaiseDelay() const { return delay; }
|
||||||
|
|
||||||
|
delay_t minFallDelay() const { return delay; }
|
||||||
|
delay_t maxFallDelay() const { return delay; }
|
||||||
|
|
||||||
|
delay_t minDelay() const { return delay; }
|
||||||
|
delay_t maxDelay() const { return delay; }
|
||||||
|
|
||||||
|
DelayInfo operator+(const DelayInfo &other) const
|
||||||
|
{
|
||||||
|
DelayInfo ret;
|
||||||
|
ret.delay = this->delay + other.delay;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
enum ConstIds
|
||||||
|
{
|
||||||
|
ID_NONE
|
||||||
|
#define X(t) , ID_##t
|
||||||
|
#include "constids.inc"
|
||||||
|
#undef X
|
||||||
|
};
|
||||||
|
|
||||||
|
#define X(t) static constexpr auto id_##t = IdString(ID_##t);
|
||||||
|
#include "constids.inc"
|
||||||
|
#undef X
|
||||||
|
|
||||||
|
struct BelId
|
||||||
|
{
|
||||||
|
int32_t index = -1;
|
||||||
|
|
||||||
|
bool operator==(const BelId &other) const { return index == other.index; }
|
||||||
|
bool operator!=(const BelId &other) const { return index != other.index; }
|
||||||
|
bool operator<(const BelId &other) const { return index < other.index; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WireId
|
||||||
|
{
|
||||||
|
int32_t index = -1;
|
||||||
|
|
||||||
|
bool operator==(const WireId &other) const { return index == other.index; }
|
||||||
|
bool operator!=(const WireId &other) const { return index != other.index; }
|
||||||
|
bool operator<(const WireId &other) const { return index < other.index; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PipId
|
||||||
|
{
|
||||||
|
int32_t index = -1;
|
||||||
|
|
||||||
|
bool operator==(const PipId &other) const { return index == other.index; }
|
||||||
|
bool operator!=(const PipId &other) const { return index != other.index; }
|
||||||
|
bool operator<(const PipId &other) const { return index < other.index; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GroupId
|
||||||
|
{
|
||||||
|
enum : int8_t
|
||||||
|
{
|
||||||
|
TYPE_NONE,
|
||||||
|
TYPE_FRAME,
|
||||||
|
TYPE_MAIN_SW,
|
||||||
|
TYPE_LOCAL_SW,
|
||||||
|
TYPE_LC0_SW,
|
||||||
|
TYPE_LC1_SW,
|
||||||
|
TYPE_LC2_SW,
|
||||||
|
TYPE_LC3_SW,
|
||||||
|
TYPE_LC4_SW,
|
||||||
|
TYPE_LC5_SW,
|
||||||
|
TYPE_LC6_SW,
|
||||||
|
TYPE_LC7_SW
|
||||||
|
} type = TYPE_NONE;
|
||||||
|
int8_t x = 0, y = 0;
|
||||||
|
|
||||||
|
bool operator==(const GroupId &other) const { return (type == other.type) && (x == other.x) && (y == other.y); }
|
||||||
|
bool operator!=(const GroupId &other) const { return (type != other.type) || (x != other.x) || (y == other.y); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DecalId
|
||||||
|
{
|
||||||
|
enum : int8_t
|
||||||
|
{
|
||||||
|
TYPE_NONE,
|
||||||
|
TYPE_BEL,
|
||||||
|
TYPE_WIRE,
|
||||||
|
TYPE_PIP,
|
||||||
|
TYPE_GROUP
|
||||||
|
} type = TYPE_NONE;
|
||||||
|
int32_t index = -1;
|
||||||
|
bool active = false;
|
||||||
|
|
||||||
|
bool operator==(const DecalId &other) const { return (type == other.type) && (index == other.index); }
|
||||||
|
bool operator!=(const DecalId &other) const { return (type != other.type) || (index != other.index); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ArchNetInfo
|
||||||
|
{
|
||||||
|
bool is_global = false;
|
||||||
|
bool is_reset = false, is_enable = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NetInfo;
|
||||||
|
|
||||||
|
struct ArchCellInfo
|
||||||
|
{
|
||||||
|
IdString belType;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
bool dffEnable;
|
||||||
|
bool carryEnable;
|
||||||
|
bool negClk;
|
||||||
|
int inputCount;
|
||||||
|
const NetInfo *clk, *cen, *sr;
|
||||||
|
} lcInfo;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX BelId>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelId &bel) const noexcept { return hash<int>()(bel.index); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX WireId>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX WireId &wire) const noexcept
|
||||||
|
{
|
||||||
|
return hash<int>()(wire.index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX PipId>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept { return hash<int>()(pip.index); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX GroupId>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX GroupId &group) const noexcept
|
||||||
|
{
|
||||||
|
std::size_t seed = 0;
|
||||||
|
boost::hash_combine(seed, hash<int>()(group.type));
|
||||||
|
boost::hash_combine(seed, hash<int>()(group.x));
|
||||||
|
boost::hash_combine(seed, hash<int>()(group.y));
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX DecalId &decal) const noexcept
|
||||||
|
{
|
||||||
|
std::size_t seed = 0;
|
||||||
|
boost::hash_combine(seed, hash<int>()(decal.type));
|
||||||
|
boost::hash_combine(seed, hash<int>()(decal.index));
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace std
|
8
xc7/attosoc.pcf
Normal file
8
xc7/attosoc.pcf
Normal file
@ -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;
|
13
xc7/attosoc.sh
Executable file
13
xc7/attosoc.sh
Executable file
@ -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
|
55
xc7/attosoc.ys
Normal file
55
xc7/attosoc.ys
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
read_verilog attosoc_top.v
|
||||||
|
read_verilog attosoc.v
|
||||||
|
read_verilog picorv32.v
|
||||||
|
|
||||||
|
#synth_xilinx -top picorv32
|
||||||
|
|
||||||
|
#begin:
|
||||||
|
read_verilog -lib +/xilinx/cells_sim.v
|
||||||
|
read_verilog -lib +/xilinx/cells_xtra.v
|
||||||
|
# read_verilog -lib +/xilinx/brams_bb.v
|
||||||
|
# read_verilog -lib +/xilinx/drams_bb.v
|
||||||
|
hierarchy -check -top top
|
||||||
|
|
||||||
|
#flatten: (only if -flatten)
|
||||||
|
proc
|
||||||
|
flatten
|
||||||
|
|
||||||
|
#coarse:
|
||||||
|
synth -run coarse
|
||||||
|
|
||||||
|
#bram:
|
||||||
|
# memory_bram -rules +/xilinx/brams.txt
|
||||||
|
# techmap -map +/xilinx/brams_map.v
|
||||||
|
#
|
||||||
|
#dram:
|
||||||
|
# memory_bram -rules +/xilinx/drams.txt
|
||||||
|
# techmap -map +/xilinx/drams_map.v
|
||||||
|
|
||||||
|
fine:
|
||||||
|
opt -fast -full
|
||||||
|
memory_map
|
||||||
|
dffsr2dff
|
||||||
|
# dff2dffe
|
||||||
|
opt -full
|
||||||
|
techmap -map +/techmap.v #-map +/xilinx/arith_map.v
|
||||||
|
opt -fast
|
||||||
|
|
||||||
|
map_luts:
|
||||||
|
abc -luts 2:2,3,6:5 #,10,20 [-dff]
|
||||||
|
clean
|
||||||
|
|
||||||
|
map_cells:
|
||||||
|
techmap -map +/xilinx/cells_map.v
|
||||||
|
dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT
|
||||||
|
clean
|
||||||
|
|
||||||
|
check:
|
||||||
|
hierarchy -check
|
||||||
|
stat
|
||||||
|
check -noinit
|
||||||
|
|
||||||
|
#edif: (only if -edif)
|
||||||
|
# write_edif <file-name>
|
||||||
|
|
||||||
|
write_json attosoc.json
|
16
xc7/attosoc_sim.sh
Executable file
16
xc7/attosoc_sim.sh
Executable file
@ -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
|
25
xc7/attosoc_tb.vhd
Normal file
25
xc7/attosoc_tb.vhd
Normal file
@ -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;
|
24
xc7/attosoc_top.v
Normal file
24
xc7/attosoc_top.v
Normal file
@ -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
|
||||||
|
|
9
xc7/blinky.pcf
Normal file
9
xc7/blinky.pcf
Normal file
@ -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;
|
15
xc7/blinky.proj
Normal file
15
xc7/blinky.proj
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"project": {
|
||||||
|
"version": "1",
|
||||||
|
"name": "blinky",
|
||||||
|
"arch": {
|
||||||
|
"name": "ice40",
|
||||||
|
"type": "hx1k",
|
||||||
|
"package": "tq144"
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"json": "blinky.json",
|
||||||
|
"pcf": "blinky.pcf"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
xc7/blinky.sh
Executable file
6
xc7/blinky.sh
Executable file
@ -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
|
32
xc7/blinky.v
Normal file
32
xc7/blinky.v
Normal file
@ -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
|
53
xc7/blinky.ys
Normal file
53
xc7/blinky.ys
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
read_verilog blinky.v
|
||||||
|
|
||||||
|
#synth_xilinx -top blinky
|
||||||
|
|
||||||
|
#begin:
|
||||||
|
read_verilog -lib +/xilinx/cells_sim.v
|
||||||
|
read_verilog -lib +/xilinx/cells_xtra.v
|
||||||
|
# read_verilog -lib +/xilinx/brams_bb.v
|
||||||
|
# read_verilog -lib +/xilinx/drams_bb.v
|
||||||
|
hierarchy -check -top blinky
|
||||||
|
|
||||||
|
#flatten: (only if -flatten)
|
||||||
|
proc
|
||||||
|
flatten
|
||||||
|
|
||||||
|
#coarse:
|
||||||
|
synth -run coarse
|
||||||
|
|
||||||
|
#bram:
|
||||||
|
# memory_bram -rules +/xilinx/brams.txt
|
||||||
|
# techmap -map +/xilinx/brams_map.v
|
||||||
|
#
|
||||||
|
#dram:
|
||||||
|
# memory_bram -rules +/xilinx/drams.txt
|
||||||
|
# techmap -map +/xilinx/drams_map.v
|
||||||
|
|
||||||
|
fine:
|
||||||
|
opt -fast -full
|
||||||
|
memory_map
|
||||||
|
dffsr2dff
|
||||||
|
# dff2dffe
|
||||||
|
opt -full
|
||||||
|
techmap -map +/techmap.v #-map +/xilinx/arith_map.v
|
||||||
|
opt -fast
|
||||||
|
|
||||||
|
map_luts:
|
||||||
|
abc -luts 2:2,3,6:5 #,10,20 [-dff]
|
||||||
|
clean
|
||||||
|
|
||||||
|
map_cells:
|
||||||
|
techmap -map +/xilinx/cells_map.v
|
||||||
|
dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT
|
||||||
|
clean
|
||||||
|
|
||||||
|
check:
|
||||||
|
hierarchy -check
|
||||||
|
stat
|
||||||
|
check -noinit
|
||||||
|
|
||||||
|
#edif: (only if -edif)
|
||||||
|
# write_edif <file-name>
|
||||||
|
|
||||||
|
write_json blinky.json
|
8
xc7/blinky_sim.sh
Executable file
8
xc7/blinky_sim.sh
Executable file
@ -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
|
54
xc7/blinky_sim.ys
Normal file
54
xc7/blinky_sim.ys
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
read_verilog blinky.v
|
||||||
|
chparam -set LOG2DELAY 0
|
||||||
|
|
||||||
|
#synth_xilinx -top blinky
|
||||||
|
|
||||||
|
#begin:
|
||||||
|
read_verilog -lib +/xilinx/cells_sim.v
|
||||||
|
read_verilog -lib +/xilinx/cells_xtra.v
|
||||||
|
# read_verilog -lib +/xilinx/brams_bb.v
|
||||||
|
# read_verilog -lib +/xilinx/drams_bb.v
|
||||||
|
hierarchy -check -top blinky
|
||||||
|
|
||||||
|
#flatten: (only if -flatten)
|
||||||
|
proc
|
||||||
|
flatten
|
||||||
|
|
||||||
|
#coarse:
|
||||||
|
synth -run coarse
|
||||||
|
|
||||||
|
#bram:
|
||||||
|
# memory_bram -rules +/xilinx/brams.txt
|
||||||
|
# techmap -map +/xilinx/brams_map.v
|
||||||
|
#
|
||||||
|
#dram:
|
||||||
|
# memory_bram -rules +/xilinx/drams.txt
|
||||||
|
# techmap -map +/xilinx/drams_map.v
|
||||||
|
|
||||||
|
fine:
|
||||||
|
opt -fast -full
|
||||||
|
memory_map
|
||||||
|
dffsr2dff
|
||||||
|
# dff2dffe
|
||||||
|
opt -full
|
||||||
|
techmap -map +/techmap.v #-map +/xilinx/arith_map.v
|
||||||
|
opt -fast
|
||||||
|
|
||||||
|
map_luts:
|
||||||
|
abc -luts 2:2,3,6:5 #,10,20 [-dff]
|
||||||
|
clean
|
||||||
|
|
||||||
|
map_cells:
|
||||||
|
techmap -map +/xilinx/cells_map.v
|
||||||
|
dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT
|
||||||
|
clean
|
||||||
|
|
||||||
|
check:
|
||||||
|
hierarchy -check
|
||||||
|
stat
|
||||||
|
check -noinit
|
||||||
|
|
||||||
|
#edif: (only if -edif)
|
||||||
|
# write_edif <file-name>
|
||||||
|
|
||||||
|
write_json blinky.json
|
25
xc7/blinky_tb.vhd
Normal file
25
xc7/blinky_tb.vhd
Normal file
@ -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;
|
242
xc7/cells.cc
Normal file
242
xc7/cells.cc
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cells.h"
|
||||||
|
#include "design_utils.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir)
|
||||||
|
{
|
||||||
|
IdString id = ctx->id(name);
|
||||||
|
cell->ports[id] = PortInfo{id, nullptr, dir};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<CellInfo> create_xc7_cell(Context *ctx, IdString type, std::string name)
|
||||||
|
{
|
||||||
|
static int auto_idx = 0;
|
||||||
|
std::unique_ptr<CellInfo> new_cell = std::unique_ptr<CellInfo>(new CellInfo());
|
||||||
|
if (name.empty()) {
|
||||||
|
new_cell->name = ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++));
|
||||||
|
} else {
|
||||||
|
new_cell->name = ctx->id(name);
|
||||||
|
}
|
||||||
|
new_cell->type = type;
|
||||||
|
if (type == ctx->id("XC7_LC")) {
|
||||||
|
new_cell->type = id_SLICE_LUT6;
|
||||||
|
new_cell->params[ctx->id("INIT")] = "0";
|
||||||
|
new_cell->params[ctx->id("NEG_CLK")] = "0";
|
||||||
|
new_cell->params[ctx->id("CARRY_ENABLE")] = "0";
|
||||||
|
new_cell->params[ctx->id("DFF_ENABLE")] = "0";
|
||||||
|
new_cell->params[ctx->id("CIN_CONST")] = "0";
|
||||||
|
new_cell->params[ctx->id("CIN_SET")] = "0";
|
||||||
|
|
||||||
|
add_port(ctx, new_cell.get(), "I1", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "I2", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "I3", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "I4", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "I5", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "I6", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "CIN", PORT_IN);
|
||||||
|
|
||||||
|
add_port(ctx, new_cell.get(), "CLK", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "CE", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "SR", PORT_IN);
|
||||||
|
|
||||||
|
add_port(ctx, new_cell.get(), "O", PORT_OUT);
|
||||||
|
add_port(ctx, new_cell.get(), "OQ", PORT_OUT);
|
||||||
|
add_port(ctx, new_cell.get(), "OMUX", PORT_OUT);
|
||||||
|
add_port(ctx, new_cell.get(), "COUT", PORT_OUT);
|
||||||
|
} else if (type == ctx->id("IOBUF")) {
|
||||||
|
if (ctx->args.type == ArchArgs::Z020)
|
||||||
|
new_cell->type = id_IOB33;
|
||||||
|
else
|
||||||
|
new_cell->type = id_IOB18;
|
||||||
|
add_port(ctx, new_cell.get(), "I", PORT_OUT);
|
||||||
|
add_port(ctx, new_cell.get(), "O", PORT_IN);
|
||||||
|
} else if (type == id_BUFGCTRL) {
|
||||||
|
add_port(ctx, new_cell.get(), "I0", PORT_IN);
|
||||||
|
add_port(ctx, new_cell.get(), "O", PORT_OUT);
|
||||||
|
} else {
|
||||||
|
log_error("unable to create XC7 cell of type %s\n", type.c_str(ctx));
|
||||||
|
}
|
||||||
|
return new_cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
|
||||||
|
{
|
||||||
|
lc->params[ctx->id("INIT")] = lut->params[ctx->id("INIT")];
|
||||||
|
int i = 6;
|
||||||
|
if (get_net_or_empty(lut, id_I5))
|
||||||
|
replace_port(lut, id_I5, lc, ctx->id("I" + std::to_string(i--)));
|
||||||
|
if (get_net_or_empty(lut, id_I4))
|
||||||
|
replace_port(lut, id_I4, lc, ctx->id("I" + std::to_string(i--)));
|
||||||
|
if (get_net_or_empty(lut, id_I3))
|
||||||
|
replace_port(lut, id_I3, lc, ctx->id("I" + std::to_string(i--)));
|
||||||
|
if (get_net_or_empty(lut, id_I2))
|
||||||
|
replace_port(lut, id_I2, lc, ctx->id("I" + std::to_string(i--)));
|
||||||
|
if (get_net_or_empty(lut, id_I1))
|
||||||
|
replace_port(lut, id_I1, lc, ctx->id("I" + std::to_string(i--)));
|
||||||
|
replace_port(lut, ctx->id("I0"), lc, ctx->id("I" + std::to_string(i--)));
|
||||||
|
if (no_dff) {
|
||||||
|
replace_port(lut, id_O, lc, id_O);
|
||||||
|
lc->params[ctx->id("DFF_ENABLE")] = "0";
|
||||||
|
}
|
||||||
|
lc->params[ctx->id("LUT_NAME")] = lut->name.str(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut)
|
||||||
|
{
|
||||||
|
lc->params[ctx->id("DFF_ENABLE")] = "1";
|
||||||
|
std::string config = dff->type.str(ctx).substr(2);
|
||||||
|
auto citer = config.begin();
|
||||||
|
replace_port(dff, ctx->id("C"), lc, id_CLK);
|
||||||
|
|
||||||
|
if (citer != config.end()) {
|
||||||
|
auto gnd_net = ctx->nets.at(ctx->id("$PACKER_GND_NET")).get();
|
||||||
|
|
||||||
|
if (*citer == 'S') {
|
||||||
|
citer++;
|
||||||
|
if (get_net_or_empty(dff, id_S) != gnd_net) {
|
||||||
|
lc->params[id_SR] = "SRHIGH";
|
||||||
|
replace_port(dff, id_S, lc, id_SR);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
disconnect_port(ctx, dff, id_S);
|
||||||
|
lc->params[ctx->id("SYNC_ATTR")] = "SYNC";
|
||||||
|
} else if (*citer == 'R') {
|
||||||
|
citer++;
|
||||||
|
if (get_net_or_empty(dff, id_R) != gnd_net) {
|
||||||
|
lc->params[id_SR] = "SRLOW";
|
||||||
|
replace_port(dff, id_R, lc, id_SR);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
disconnect_port(ctx, dff, id_R);
|
||||||
|
lc->params[ctx->id("SYNC_ATTR")] = "SYNC";
|
||||||
|
} else if (*citer == 'C') {
|
||||||
|
citer++;
|
||||||
|
if (get_net_or_empty(dff, id_CLR) != gnd_net) {
|
||||||
|
lc->params[id_SR] = "SRLOW";
|
||||||
|
replace_port(dff, id_CLR, lc, id_SR);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
disconnect_port(ctx, dff, id_CLR);
|
||||||
|
lc->params[ctx->id("SYNC_ATTR")] = "ASYNC";
|
||||||
|
} else {
|
||||||
|
NPNR_ASSERT(*citer == 'P');
|
||||||
|
citer++;
|
||||||
|
if (get_net_or_empty(dff, id_PRE) != gnd_net) {
|
||||||
|
lc->params[id_SR] = "SRHIGH";
|
||||||
|
replace_port(dff, id_PRE, lc, id_SR);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
disconnect_port(ctx, dff, id_PRE);
|
||||||
|
lc->params[ctx->id("SYNC_ATTR")] = "ASYNC";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (citer != config.end() && *citer == 'E') {
|
||||||
|
auto vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC_NET")).get();
|
||||||
|
|
||||||
|
++citer;
|
||||||
|
if (get_net_or_empty(dff, ctx->id("CE")) != vcc_net)
|
||||||
|
replace_port(dff, ctx->id("CE"), lc, ctx->id("CE"));
|
||||||
|
else
|
||||||
|
disconnect_port(ctx, dff, ctx->id("CE"));
|
||||||
|
}
|
||||||
|
|
||||||
|
NPNR_ASSERT(citer == config.end());
|
||||||
|
|
||||||
|
if (pass_thru_lut) {
|
||||||
|
lc->params[ctx->id("INIT")] = "2";
|
||||||
|
replace_port(dff, ctx->id("D"), lc, id_I1);
|
||||||
|
}
|
||||||
|
|
||||||
|
replace_port(dff, ctx->id("Q"), lc, id_OQ);
|
||||||
|
|
||||||
|
auto it = dff->params.find(ctx->id("INIT"));
|
||||||
|
if (it != dff->params.end())
|
||||||
|
lc->params[ctx->id("FFINIT")] = it->second == "1" ? "INIT1" : "INIT0";
|
||||||
|
}
|
||||||
|
|
||||||
|
void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio)
|
||||||
|
{
|
||||||
|
if (nxio->type == ctx->id("$nextpnr_ibuf")) {
|
||||||
|
sbio->params[ctx->id("PIN_TYPE")] = "1";
|
||||||
|
auto pu_attr = nxio->attrs.find(ctx->id("PULLUP"));
|
||||||
|
if (pu_attr != nxio->attrs.end())
|
||||||
|
sbio->params[ctx->id("PULLUP")] = pu_attr->second;
|
||||||
|
replace_port(nxio, id_O, sbio, id_I);
|
||||||
|
} else if (nxio->type == ctx->id("$nextpnr_obuf")) {
|
||||||
|
sbio->params[ctx->id("PIN_TYPE")] = "25";
|
||||||
|
replace_port(nxio, id_I, sbio, id_O);
|
||||||
|
} else if (nxio->type == ctx->id("$nextpnr_iobuf")) {
|
||||||
|
// N.B. tristate will be dealt with below
|
||||||
|
sbio->params[ctx->id("PIN_TYPE")] = "25";
|
||||||
|
replace_port(nxio, id_I, sbio, id_O);
|
||||||
|
replace_port(nxio, id_O, sbio, id_I);
|
||||||
|
} else {
|
||||||
|
NPNR_ASSERT(false);
|
||||||
|
}
|
||||||
|
NetInfo *donet = sbio->ports.at(id_O).net;
|
||||||
|
CellInfo *tbuf = net_driven_by(
|
||||||
|
ctx, donet, [](const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("$_TBUF_"); },
|
||||||
|
ctx->id("Y"));
|
||||||
|
if (tbuf) {
|
||||||
|
sbio->params[ctx->id("PIN_TYPE")] = "41";
|
||||||
|
replace_port(tbuf, ctx->id("A"), sbio, id_O);
|
||||||
|
replace_port(tbuf, ctx->id("E"), sbio, ctx->id("OUTPUT_ENABLE"));
|
||||||
|
ctx->nets.erase(donet->name);
|
||||||
|
if (!donet->users.empty())
|
||||||
|
log_error("unsupported tristate IO pattern for IO buffer '%s', "
|
||||||
|
"instantiate SB_IO manually to ensure correct behaviour\n",
|
||||||
|
nxio->name.c_str(ctx));
|
||||||
|
ctx->cells.erase(tbuf->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_clock_port(const BaseCtx *ctx, const PortRef &port)
|
||||||
|
{
|
||||||
|
if (port.cell == nullptr)
|
||||||
|
return false;
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_reset_port(const BaseCtx *ctx, const PortRef &port)
|
||||||
|
{
|
||||||
|
if (port.cell == nullptr)
|
||||||
|
return false;
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_enable_port(const BaseCtx *ctx, const PortRef &port)
|
||||||
|
{
|
||||||
|
if (port.cell == nullptr)
|
||||||
|
return false;
|
||||||
|
NPNR_ASSERT("TODO");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
110
xc7/cells.h
Normal file
110
xc7/cells.h
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "nextpnr.h"
|
||||||
|
|
||||||
|
#ifndef ICE40_CELLS_H
|
||||||
|
#define ICE40_CELLS_H
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// Create a standard xc7 cell and return it
|
||||||
|
// Name will be automatically assigned if not specified
|
||||||
|
std::unique_ptr<CellInfo> create_xc7_cell(Context *ctx, IdString type, std::string name = "");
|
||||||
|
|
||||||
|
// Return true if a cell is a LUT
|
||||||
|
inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell)
|
||||||
|
{
|
||||||
|
return cell->type == id_LUT1 || cell->type == id_LUT2 || cell->type == id_LUT3 || cell->type == id_LUT4 ||
|
||||||
|
cell->type == id_LUT5 || cell->type == id_LUT6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if a cell is a flipflop
|
||||||
|
inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell)
|
||||||
|
{
|
||||||
|
return cell->type == id_FDRE || cell->type == id_FDSE || cell->type == id_FDCE || cell->type == id_FDPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_CARRY"); }
|
||||||
|
|
||||||
|
inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("XC7_LC"); }
|
||||||
|
|
||||||
|
// Return true if a cell is a SB_IO
|
||||||
|
inline bool is_sb_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_IO"); }
|
||||||
|
|
||||||
|
// Return true if a cell is a global buffer
|
||||||
|
inline bool is_gbuf(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_BUFGCTRL; }
|
||||||
|
|
||||||
|
// Return true if a cell is a RAM
|
||||||
|
inline bool is_ram(const BaseCtx *ctx, const CellInfo *cell)
|
||||||
|
{
|
||||||
|
return cell->type == ctx->id("SB_RAM40_4K") || cell->type == ctx->id("SB_RAM40_4KNR") ||
|
||||||
|
cell->type == ctx->id("SB_RAM40_4KNW") || cell->type == ctx->id("SB_RAM40_4KNRNW");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_sb_lfosc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_LFOSC"); }
|
||||||
|
|
||||||
|
inline bool is_sb_hfosc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_HFOSC"); }
|
||||||
|
|
||||||
|
inline bool is_sb_spram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_SPRAM256KA"); }
|
||||||
|
|
||||||
|
inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_MAC16"); }
|
||||||
|
|
||||||
|
inline bool is_sb_pll40(const BaseCtx *ctx, const CellInfo *cell)
|
||||||
|
{
|
||||||
|
return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") ||
|
||||||
|
cell->type == ctx->id("SB_PLL40_2F_PAD") || cell->type == ctx->id("SB_PLL40_CORE") ||
|
||||||
|
cell->type == ctx->id("SB_PLL40_2F_CORE");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_sb_pll40_pad(const BaseCtx *ctx, const CellInfo *cell)
|
||||||
|
{
|
||||||
|
return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") ||
|
||||||
|
cell->type == ctx->id("SB_PLL40_2F_PAD");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell);
|
||||||
|
|
||||||
|
// Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports
|
||||||
|
// as needed. Set no_dff if a DFF is not being used, so that the output
|
||||||
|
// can be reconnected
|
||||||
|
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff = true);
|
||||||
|
|
||||||
|
// Convert a SB_DFFx primitive to (part of) an ICESTORM_LC, setting parameters
|
||||||
|
// and reconnecting signals as necessary. If pass_thru_lut is True, the LUT will
|
||||||
|
// be configured as pass through and D connected to I0, otherwise D will be
|
||||||
|
// ignored
|
||||||
|
void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut = false);
|
||||||
|
|
||||||
|
// Convert a nextpnr IO buffer to a SB_IO
|
||||||
|
void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio);
|
||||||
|
|
||||||
|
// Return true if a port is a clock port
|
||||||
|
bool is_clock_port(const BaseCtx *ctx, const PortRef &port);
|
||||||
|
|
||||||
|
// Return true if a port is a reset port
|
||||||
|
bool is_reset_port(const BaseCtx *ctx, const PortRef &port);
|
||||||
|
|
||||||
|
// Return true if a port is a clock enable port
|
||||||
|
bool is_enable_port(const BaseCtx *ctx, const PortRef &port);
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif
|
288
xc7/chains.cc
Normal file
288
xc7/chains.cc
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "chains.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
#include "cells.h"
|
||||||
|
#include "design_utils.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "place_common.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
struct CellChain
|
||||||
|
{
|
||||||
|
std::vector<CellInfo *> cells;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generic chain finder
|
||||||
|
template <typename F1, typename F2, typename F3>
|
||||||
|
std::vector<CellChain> find_chains(const Context *ctx, F1 cell_type_predicate, F2 get_previous, F3 get_next,
|
||||||
|
size_t min_length = 2)
|
||||||
|
{
|
||||||
|
std::set<IdString> chained;
|
||||||
|
std::vector<CellChain> chains;
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
if (chained.find(cell.first) != chained.end())
|
||||||
|
continue;
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (cell_type_predicate(ctx, ci)) {
|
||||||
|
CellInfo *start = ci;
|
||||||
|
CellInfo *prev_start = ci;
|
||||||
|
while (prev_start != nullptr) {
|
||||||
|
start = prev_start;
|
||||||
|
prev_start = get_previous(ctx, start);
|
||||||
|
}
|
||||||
|
CellChain chain;
|
||||||
|
CellInfo *end = start;
|
||||||
|
while (end != nullptr) {
|
||||||
|
chain.cells.push_back(end);
|
||||||
|
end = get_next(ctx, end);
|
||||||
|
}
|
||||||
|
if (chain.cells.size() >= min_length) {
|
||||||
|
chains.push_back(chain);
|
||||||
|
for (auto c : chain.cells)
|
||||||
|
chained.insert(c->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chains;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChainConstrainer
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Context *ctx;
|
||||||
|
// Split a carry chain into multiple legal chains
|
||||||
|
std::vector<CellChain> split_carry_chain(CellChain &carryc)
|
||||||
|
{
|
||||||
|
bool start_of_chain = true;
|
||||||
|
std::vector<CellChain> chains;
|
||||||
|
std::vector<const CellInfo *> tile;
|
||||||
|
const int max_length = (ctx->chip_info->height - 2) * 8 - 2;
|
||||||
|
auto curr_cell = carryc.cells.begin();
|
||||||
|
while (curr_cell != carryc.cells.end()) {
|
||||||
|
CellInfo *cell = *curr_cell;
|
||||||
|
if (tile.size() >= 8) {
|
||||||
|
tile.clear();
|
||||||
|
}
|
||||||
|
if (start_of_chain) {
|
||||||
|
tile.clear();
|
||||||
|
chains.emplace_back();
|
||||||
|
start_of_chain = false;
|
||||||
|
if (cell->ports.at(ctx->id("CIN")).net) {
|
||||||
|
// CIN is not constant and not part of a chain. Must feed in from fabric
|
||||||
|
CellInfo *feedin = make_carry_feed_in(cell, cell->ports.at(ctx->id("CIN")));
|
||||||
|
chains.back().cells.push_back(feedin);
|
||||||
|
tile.push_back(feedin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tile.push_back(cell);
|
||||||
|
chains.back().cells.push_back(cell);
|
||||||
|
bool split_chain = (!ctx->logicCellsCompatible(tile.data(), tile.size())) ||
|
||||||
|
(int(chains.back().cells.size()) > max_length);
|
||||||
|
if (split_chain) {
|
||||||
|
CellInfo *passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")));
|
||||||
|
tile.pop_back();
|
||||||
|
chains.back().cells.back() = passout;
|
||||||
|
start_of_chain = true;
|
||||||
|
} else {
|
||||||
|
NetInfo *carry_net = cell->ports.at(ctx->id("COUT")).net;
|
||||||
|
bool at_end = (curr_cell == carryc.cells.end() - 1);
|
||||||
|
if (carry_net != nullptr && (carry_net->users.size() > 1 || at_end)) {
|
||||||
|
if (carry_net->users.size() > 2 ||
|
||||||
|
(net_only_drives(ctx, carry_net, is_lc, ctx->id("I3"), false) !=
|
||||||
|
net_only_drives(ctx, carry_net, is_lc, ctx->id("CIN"), false)) ||
|
||||||
|
(at_end && !net_only_drives(ctx, carry_net, is_lc, ctx->id("I3"), true))) {
|
||||||
|
CellInfo *passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")));
|
||||||
|
chains.back().cells.push_back(passout);
|
||||||
|
tile.push_back(passout);
|
||||||
|
start_of_chain = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++curr_cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chains;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert a logic cell to legalise a COUT->fabric connection
|
||||||
|
CellInfo *make_carry_pass_out(PortInfo &cout_port)
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(cout_port.net != nullptr);
|
||||||
|
std::unique_ptr<CellInfo> lc = create_xc7_cell(ctx, ctx->id("ICESTORM_LC"));
|
||||||
|
lc->params[ctx->id("LUT_INIT")] = "65280"; // 0xff00: O = I3
|
||||||
|
lc->params[ctx->id("CARRY_ENABLE")] = "1";
|
||||||
|
lc->ports.at(ctx->id("O")).net = cout_port.net;
|
||||||
|
std::unique_ptr<NetInfo> co_i3_net(new NetInfo());
|
||||||
|
co_i3_net->name = ctx->id(lc->name.str(ctx) + "$I3");
|
||||||
|
co_i3_net->driver = cout_port.net->driver;
|
||||||
|
PortRef i3_r;
|
||||||
|
i3_r.port = ctx->id("I3");
|
||||||
|
i3_r.cell = lc.get();
|
||||||
|
co_i3_net->users.push_back(i3_r);
|
||||||
|
PortRef o_r;
|
||||||
|
o_r.port = ctx->id("O");
|
||||||
|
o_r.cell = lc.get();
|
||||||
|
cout_port.net->driver = o_r;
|
||||||
|
lc->ports.at(ctx->id("I3")).net = co_i3_net.get();
|
||||||
|
cout_port.net = co_i3_net.get();
|
||||||
|
|
||||||
|
IdString co_i3_name = co_i3_net->name;
|
||||||
|
NPNR_ASSERT(ctx->nets.find(co_i3_name) == ctx->nets.end());
|
||||||
|
ctx->nets[co_i3_name] = std::move(co_i3_net);
|
||||||
|
IdString name = lc->name;
|
||||||
|
ctx->assignCellInfo(lc.get());
|
||||||
|
ctx->cells[lc->name] = std::move(lc);
|
||||||
|
return ctx->cells[name].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert a logic cell to legalise a CIN->fabric connection
|
||||||
|
CellInfo *make_carry_feed_in(CellInfo *cin_cell, PortInfo &cin_port)
|
||||||
|
{
|
||||||
|
NPNR_ASSERT(cin_port.net != nullptr);
|
||||||
|
std::unique_ptr<CellInfo> lc = create_xc7_cell(ctx, ctx->id("ICESTORM_LC"));
|
||||||
|
lc->params[ctx->id("CARRY_ENABLE")] = "1";
|
||||||
|
lc->params[ctx->id("CIN_CONST")] = "1";
|
||||||
|
lc->params[ctx->id("CIN_SET")] = "1";
|
||||||
|
lc->ports.at(ctx->id("I1")).net = cin_port.net;
|
||||||
|
cin_port.net->users.erase(std::remove_if(cin_port.net->users.begin(), cin_port.net->users.end(),
|
||||||
|
[cin_cell, cin_port](const PortRef &usr) {
|
||||||
|
return usr.cell == cin_cell && usr.port == cin_port.name;
|
||||||
|
}));
|
||||||
|
|
||||||
|
PortRef i1_ref;
|
||||||
|
i1_ref.cell = lc.get();
|
||||||
|
i1_ref.port = ctx->id("I1");
|
||||||
|
lc->ports.at(ctx->id("I1")).net->users.push_back(i1_ref);
|
||||||
|
|
||||||
|
std::unique_ptr<NetInfo> out_net(new NetInfo());
|
||||||
|
out_net->name = ctx->id(lc->name.str(ctx) + "$O");
|
||||||
|
|
||||||
|
PortRef drv_ref;
|
||||||
|
drv_ref.port = ctx->id("COUT");
|
||||||
|
drv_ref.cell = lc.get();
|
||||||
|
out_net->driver = drv_ref;
|
||||||
|
lc->ports.at(ctx->id("COUT")).net = out_net.get();
|
||||||
|
|
||||||
|
PortRef usr_ref;
|
||||||
|
usr_ref.port = cin_port.name;
|
||||||
|
usr_ref.cell = cin_cell;
|
||||||
|
out_net->users.push_back(usr_ref);
|
||||||
|
cin_cell->ports.at(cin_port.name).net = out_net.get();
|
||||||
|
|
||||||
|
IdString out_net_name = out_net->name;
|
||||||
|
NPNR_ASSERT(ctx->nets.find(out_net_name) == ctx->nets.end());
|
||||||
|
ctx->nets[out_net_name] = std::move(out_net);
|
||||||
|
|
||||||
|
IdString name = lc->name;
|
||||||
|
ctx->assignCellInfo(lc.get());
|
||||||
|
ctx->cells[lc->name] = std::move(lc);
|
||||||
|
return ctx->cells[name].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_carries()
|
||||||
|
{
|
||||||
|
std::vector<CellChain> carry_chains =
|
||||||
|
find_chains(ctx, [](const Context *ctx, const CellInfo *cell) { return is_lc(ctx, cell); },
|
||||||
|
[](const Context *ctx, const
|
||||||
|
|
||||||
|
CellInfo *cell) {
|
||||||
|
CellInfo *carry_prev =
|
||||||
|
net_driven_by(ctx, cell->ports.at(ctx->id("CIN")).net, is_lc, ctx->id("COUT"));
|
||||||
|
if (carry_prev != nullptr)
|
||||||
|
return carry_prev;
|
||||||
|
/*CellInfo *i3_prev = net_driven_by(ctx, cell->ports.at(ctx->id("I3")).net, is_lc,
|
||||||
|
ctx->id("COUT")); if (i3_prev != nullptr) return i3_prev;*/
|
||||||
|
return (CellInfo *)nullptr;
|
||||||
|
},
|
||||||
|
[](const Context *ctx, const CellInfo *cell) {
|
||||||
|
CellInfo *carry_next = net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_lc,
|
||||||
|
ctx->id("CIN"), false);
|
||||||
|
if (carry_next != nullptr)
|
||||||
|
return carry_next;
|
||||||
|
/*CellInfo *i3_next =
|
||||||
|
net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_lc, ctx->id("I3"),
|
||||||
|
false); if (i3_next != nullptr) return i3_next;*/
|
||||||
|
return (CellInfo *)nullptr;
|
||||||
|
});
|
||||||
|
std::unordered_set<IdString> chained;
|
||||||
|
for (auto &base_chain : carry_chains) {
|
||||||
|
for (auto c : base_chain.cells)
|
||||||
|
chained.insert(c->name);
|
||||||
|
}
|
||||||
|
// Any cells not in chains, but with carry enabled, must also be put in a single-carry chain
|
||||||
|
// for correct processing
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (chained.find(cell.first) == chained.end() && is_lc(ctx, ci) &&
|
||||||
|
bool_or_default(ci->params, ctx->id("CARRY_ENABLE"))) {
|
||||||
|
CellChain sChain;
|
||||||
|
sChain.cells.push_back(ci);
|
||||||
|
chained.insert(cell.first);
|
||||||
|
carry_chains.push_back(sChain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<CellChain> all_chains;
|
||||||
|
// Chain splitting
|
||||||
|
for (auto &base_chain : carry_chains) {
|
||||||
|
if (ctx->verbose) {
|
||||||
|
log_info("Found carry chain: \n");
|
||||||
|
for (auto entry : base_chain.cells)
|
||||||
|
log_info(" %s\n", entry->name.c_str(ctx));
|
||||||
|
log_info("\n");
|
||||||
|
}
|
||||||
|
std::vector<CellChain> split_chains = split_carry_chain(base_chain);
|
||||||
|
for (auto &chain : split_chains) {
|
||||||
|
all_chains.push_back(chain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Actual chain placement
|
||||||
|
for (auto &chain : all_chains) {
|
||||||
|
if (ctx->verbose)
|
||||||
|
log_info("Placing carry chain starting at '%s'\n", chain.cells.front()->name.c_str(ctx));
|
||||||
|
|
||||||
|
// Place carry chain
|
||||||
|
chain.cells.at(0)->constr_abs_z = true;
|
||||||
|
chain.cells.at(0)->constr_z = 0;
|
||||||
|
for (int i = 1; i < int(chain.cells.size()); i++) {
|
||||||
|
chain.cells.at(i)->constr_x = 0;
|
||||||
|
chain.cells.at(i)->constr_y = (i / 8);
|
||||||
|
chain.cells.at(i)->constr_z = i % 8;
|
||||||
|
chain.cells.at(i)->constr_abs_z = true;
|
||||||
|
chain.cells.at(i)->constr_parent = chain.cells.at(0);
|
||||||
|
chain.cells.at(0)->constr_children.push_back(chain.cells.at(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
ChainConstrainer(Context *ctx) : ctx(ctx){};
|
||||||
|
void constrain_chains() { process_carries(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
void constrain_chains(Context *ctx)
|
||||||
|
{
|
||||||
|
log_info("Constraining chains...\n");
|
||||||
|
ChainConstrainer(ctx).constrain_chains();
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
27
xc7/chains.h
Normal file
27
xc7/chains.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "nextpnr.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// This finds chains, inserts LCs to legalise them as needed, and sets relative constraints as appropriate
|
||||||
|
void constrain_chains(Context *ctx);
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
1280
xc7/chipdb.py
Normal file
1280
xc7/chipdb.py
Normal file
File diff suppressed because it is too large
Load Diff
467
xc7/constids.inc
Normal file
467
xc7/constids.inc
Normal file
@ -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)
|
85
xc7/delay.cc
Normal file
85
xc7/delay.cc
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "nextpnr.h"
|
||||||
|
#include "router1.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
#define NUM_FUZZ_ROUTES 100000
|
||||||
|
|
||||||
|
delay_t Arch::estimateDelay(WireId src, WireId dst) const
|
||||||
|
{
|
||||||
|
const auto &src_tw = torc_info->wire_to_tilewire[src.index];
|
||||||
|
const auto &src_loc = torc_info->tile_to_xy[src_tw.getTileIndex()];
|
||||||
|
const auto &dst_tw = torc_info->wire_to_tilewire[dst.index];
|
||||||
|
const auto &dst_loc = torc_info->tile_to_xy[dst_tw.getTileIndex()];
|
||||||
|
|
||||||
|
if (!torc_info->wire_is_global[src.index]) {
|
||||||
|
auto abs_delta_x = abs(dst_loc.first - src_loc.first);
|
||||||
|
auto abs_delta_y = abs(dst_loc.second - src_loc.second);
|
||||||
|
auto div_LH = std::div(abs_delta_x, 12);
|
||||||
|
auto div_LV = std::div(abs_delta_y, 18);
|
||||||
|
auto div_LVB = std::div(div_LV.rem, 12);
|
||||||
|
auto div_H6 = std::div(div_LH.rem, 6);
|
||||||
|
auto div_V6 = std::div(div_LVB.rem, 6);
|
||||||
|
auto div_H4 = std::div(div_H6.rem, 4);
|
||||||
|
auto div_V4 = std::div(div_V6.rem, 4);
|
||||||
|
auto div_H2 = std::div(div_H4.rem, 2);
|
||||||
|
auto div_V2 = std::div(div_V4.rem, 2);
|
||||||
|
auto num_H1 = div_H2.rem;
|
||||||
|
auto num_V1 = div_V2.rem;
|
||||||
|
return div_LH.quot * 360 + div_LVB.quot * 300 + div_LV.quot * 350 +
|
||||||
|
(div_H6.quot + div_H4.quot + div_V6.quot + div_V4.quot) * 210 + (div_H2.quot + div_V2.quot) * 170 +
|
||||||
|
(num_H1 + num_V1) * 150;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto src_y = src_loc.second;
|
||||||
|
auto dst_y = dst_loc.second;
|
||||||
|
auto div_src_y = std::div(src_y, 52);
|
||||||
|
auto div_dst_y = std::div(dst_y, 52);
|
||||||
|
return abs(div_dst_y.quot - div_src_y.quot) * 52 + abs(div_dst_y.rem - div_src_y.rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
|
||||||
|
{
|
||||||
|
const auto &driver = net_info->driver;
|
||||||
|
auto driver_loc = getBelLocation(driver.cell->bel);
|
||||||
|
auto sink_loc = getBelLocation(sink.cell->bel);
|
||||||
|
auto abs_delta_x = abs(driver_loc.x - sink_loc.x);
|
||||||
|
auto abs_delta_y = abs(driver_loc.y - sink_loc.y);
|
||||||
|
auto div_LH = std::div(abs_delta_x, 12);
|
||||||
|
auto div_LV = std::div(abs_delta_y, 18);
|
||||||
|
auto div_LVB = std::div(div_LV.rem, 12);
|
||||||
|
auto div_H6 = std::div(div_LH.rem, 6);
|
||||||
|
auto div_V6 = std::div(div_LVB.rem, 6);
|
||||||
|
auto div_H4 = std::div(div_H6.rem, 4);
|
||||||
|
auto div_V4 = std::div(div_V6.rem, 4);
|
||||||
|
auto div_H2 = std::div(div_H4.rem, 2);
|
||||||
|
auto div_V2 = std::div(div_V4.rem, 2);
|
||||||
|
auto num_H1 = div_H2.rem;
|
||||||
|
auto num_V1 = div_V2.rem;
|
||||||
|
return div_LH.quot * 360 + div_LVB.quot * 300 + div_LV.quot * 350 +
|
||||||
|
(div_H6.quot + div_H4.quot + div_V6.quot + div_V4.quot) * 210 + (div_H2.quot + div_V2.quot) * 170 +
|
||||||
|
(num_H1 + num_V1) * 150;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
73
xc7/family.cmake
Normal file
73
xc7/family.cmake
Normal file
@ -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
|
||||||
|
)
|
6
xc7/firmware_fast.hex
Normal file
6
xc7/firmware_fast.hex
Normal file
@ -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
|
8
xc7/firmware_slow.hex
Normal file
8
xc7/firmware_slow.hex
Normal file
@ -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
|
766
xc7/gfx.cc
Normal file
766
xc7/gfx.cc
Normal file
@ -0,0 +1,766 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gfx.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style)
|
||||||
|
{
|
||||||
|
GraphicElement el;
|
||||||
|
el.type = GraphicElement::TYPE_LINE;
|
||||||
|
el.style = style;
|
||||||
|
|
||||||
|
// Horizontal Span-4 Wires
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP4_H_L_36 && id <= TILE_WIRE_SP4_H_L_47) {
|
||||||
|
int idx = (id - TILE_WIRE_SP4_H_L_36) + 48;
|
||||||
|
|
||||||
|
float y1 = y + 1.0 - (0.03 + 0.0025 * (60 - (idx ^ 1)));
|
||||||
|
float y2 = y + 1.0 - (0.03 + 0.0025 * (60 - idx));
|
||||||
|
|
||||||
|
el.x1 = x;
|
||||||
|
el.x2 = x + 0.01;
|
||||||
|
el.y1 = y1;
|
||||||
|
el.y2 = y1;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.x1 = x + 0.01;
|
||||||
|
el.x2 = x + 0.02;
|
||||||
|
el.y1 = y1;
|
||||||
|
el.y2 = y2;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.x1 = x + 0.02;
|
||||||
|
el.x2 = x + 0.9;
|
||||||
|
el.y1 = y2;
|
||||||
|
el.y2 = y2;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.x1 = x + main_swbox_x1 + 0.0025 * (idx + 35);
|
||||||
|
el.x2 = el.x1;
|
||||||
|
el.y1 = y2;
|
||||||
|
el.y2 = y + main_swbox_y2;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP4_H_R_0 && id <= TILE_WIRE_SP4_H_R_47) {
|
||||||
|
int idx = id - TILE_WIRE_SP4_H_R_0;
|
||||||
|
|
||||||
|
float y1 = y + 1.0 - (0.03 + 0.0025 * (60 - idx));
|
||||||
|
float y2 = y + 1.0 - (0.03 + 0.0025 * (60 - (idx ^ 1)));
|
||||||
|
float y3 = y + 1.0 - (0.03 + 0.0025 * (60 - (idx ^ 1) - 12));
|
||||||
|
|
||||||
|
if (idx >= 12) {
|
||||||
|
el.x1 = x;
|
||||||
|
el.x2 = x + 0.01;
|
||||||
|
el.y1 = y1;
|
||||||
|
el.y2 = y1;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.x1 = x + 0.01;
|
||||||
|
el.x2 = x + 0.02;
|
||||||
|
el.y1 = y1;
|
||||||
|
el.y2 = y2;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
el.x1 = x + 0.02;
|
||||||
|
el.x2 = x + 0.9;
|
||||||
|
el.y1 = y2;
|
||||||
|
el.y2 = y2;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.x1 = x + 0.9;
|
||||||
|
el.x2 = x + 1.0;
|
||||||
|
el.y1 = y2;
|
||||||
|
el.y2 = y3;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.x1 = x + main_swbox_x1 + 0.0025 * ((idx ^ 1) + 35);
|
||||||
|
el.x2 = el.x1;
|
||||||
|
el.y1 = y2;
|
||||||
|
el.y2 = y + main_swbox_y2;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertical Span-4 Wires
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP4_V_T_36 && id <= TILE_WIRE_SP4_V_T_47) {
|
||||||
|
int idx = (id - TILE_WIRE_SP4_V_T_36) + 48;
|
||||||
|
|
||||||
|
float x1 = x + 0.03 + 0.0025 * (60 - (idx ^ 1));
|
||||||
|
float x2 = x + 0.03 + 0.0025 * (60 - idx);
|
||||||
|
|
||||||
|
el.y1 = y + 1.00;
|
||||||
|
el.y2 = y + 0.99;
|
||||||
|
el.x1 = x1;
|
||||||
|
el.x2 = x1;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y1 = y + 0.99;
|
||||||
|
el.y2 = y + 0.98;
|
||||||
|
el.x1 = x1;
|
||||||
|
el.x2 = x2;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y1 = y + 0.98;
|
||||||
|
el.y2 = y + 0.10;
|
||||||
|
el.x1 = x2;
|
||||||
|
el.x2 = x2;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y1 = y + 1.0 - (0.03 + 0.0025 * (270 - idx));
|
||||||
|
el.y2 = el.y1;
|
||||||
|
el.x1 = x2;
|
||||||
|
el.x2 = x + main_swbox_x1;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP4_V_B_0 && id <= TILE_WIRE_SP4_V_B_47) {
|
||||||
|
int idx = id - TILE_WIRE_SP4_V_B_0;
|
||||||
|
|
||||||
|
float x1 = x + 0.03 + 0.0025 * (60 - idx);
|
||||||
|
float x2 = x + 0.03 + 0.0025 * (60 - (idx ^ 1));
|
||||||
|
float x3 = x + 0.03 + 0.0025 * (60 - (idx ^ 1) - 12);
|
||||||
|
|
||||||
|
if (idx >= 12) {
|
||||||
|
el.y1 = y + 1.00;
|
||||||
|
el.y2 = y + 0.99;
|
||||||
|
el.x1 = x1;
|
||||||
|
el.x2 = x1;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y1 = y + 0.99;
|
||||||
|
el.y2 = y + 0.98;
|
||||||
|
el.x1 = x1;
|
||||||
|
el.x2 = x2;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
el.y1 = y + 0.98;
|
||||||
|
el.y2 = y + 0.10;
|
||||||
|
el.x1 = x2;
|
||||||
|
el.x2 = x2;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y1 = y + 0.10;
|
||||||
|
el.y2 = y;
|
||||||
|
el.x1 = x2;
|
||||||
|
el.x2 = x3;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y1 = y + 1.0 - (0.03 + 0.0025 * (145 - (idx ^ 1)));
|
||||||
|
el.y2 = el.y1;
|
||||||
|
el.x1 = x;
|
||||||
|
el.x2 = x2;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y1 = y + 1.0 - (0.03 + 0.0025 * (270 - (idx ^ 1)));
|
||||||
|
el.y2 = el.y1;
|
||||||
|
el.x1 = x2;
|
||||||
|
el.x2 = x + main_swbox_x1;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Horizontal Span-12 Wires
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP12_H_L_22 && id <= TILE_WIRE_SP12_H_L_23) {
|
||||||
|
int idx = (id - TILE_WIRE_SP12_H_L_22) + 24;
|
||||||
|
|
||||||
|
float y1 = y + 1.0 - (0.03 + 0.0025 * (90 - (idx ^ 1)));
|
||||||
|
float y2 = y + 1.0 - (0.03 + 0.0025 * (90 - idx));
|
||||||
|
|
||||||
|
el.x1 = x;
|
||||||
|
el.x2 = x + 0.01;
|
||||||
|
el.y1 = y1;
|
||||||
|
el.y2 = y1;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.x1 = x + 0.01;
|
||||||
|
el.x2 = x + 0.02;
|
||||||
|
el.y1 = y1;
|
||||||
|
el.y2 = y2;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.x1 = x + 0.02;
|
||||||
|
el.x2 = x + 0.98333;
|
||||||
|
el.y1 = y2;
|
||||||
|
el.y2 = y2;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.x1 = x + main_swbox_x1 + 0.0025 * (idx + 5);
|
||||||
|
el.x2 = el.x1;
|
||||||
|
el.y1 = y2;
|
||||||
|
el.y2 = y + main_swbox_y2;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP12_H_R_0 && id <= TILE_WIRE_SP12_H_R_23) {
|
||||||
|
int idx = id - TILE_WIRE_SP12_H_R_0;
|
||||||
|
|
||||||
|
float y1 = y + 1.0 - (0.03 + 0.0025 * (90 - idx));
|
||||||
|
float y2 = y + 1.0 - (0.03 + 0.0025 * (90 - (idx ^ 1)));
|
||||||
|
float y3 = y + 1.0 - (0.03 + 0.0025 * (90 - (idx ^ 1) - 2));
|
||||||
|
|
||||||
|
if (idx >= 2) {
|
||||||
|
el.x1 = x;
|
||||||
|
el.x2 = x + 0.01;
|
||||||
|
el.y1 = y1;
|
||||||
|
el.y2 = y1;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.x1 = x + 0.01;
|
||||||
|
el.x2 = x + 0.02;
|
||||||
|
el.y1 = y1;
|
||||||
|
el.y2 = y2;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
el.x1 = x + 0.02;
|
||||||
|
el.x2 = x + 0.98333;
|
||||||
|
el.y1 = y2;
|
||||||
|
el.y2 = y2;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.x1 = x + 0.98333;
|
||||||
|
el.x2 = x + 1.0;
|
||||||
|
el.y1 = y2;
|
||||||
|
el.y2 = y3;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.x1 = x + main_swbox_x1 + 0.0025 * ((idx ^ 1) + 5);
|
||||||
|
el.x2 = el.x1;
|
||||||
|
el.y1 = y2;
|
||||||
|
el.y2 = y + main_swbox_y2;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertical Right Span-4
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP4_R_V_B_0 && id <= TILE_WIRE_SP4_R_V_B_47) {
|
||||||
|
int idx = id - TILE_WIRE_SP4_R_V_B_0;
|
||||||
|
|
||||||
|
float y1 = y + 1.0 - (0.03 + 0.0025 * (145 - (idx ^ 1)));
|
||||||
|
|
||||||
|
el.y1 = y1;
|
||||||
|
el.y2 = y1;
|
||||||
|
el.x1 = x + main_swbox_x2;
|
||||||
|
el.x2 = x + 1.0;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertical Span-12 Wires
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP12_V_T_22 && id <= TILE_WIRE_SP12_V_T_23) {
|
||||||
|
int idx = (id - TILE_WIRE_SP12_V_T_22) + 24;
|
||||||
|
|
||||||
|
float x1 = x + 0.03 + 0.0025 * (90 - (idx ^ 1));
|
||||||
|
float x2 = x + 0.03 + 0.0025 * (90 - idx);
|
||||||
|
|
||||||
|
el.y1 = y + 1.00;
|
||||||
|
el.y2 = y + 0.99;
|
||||||
|
el.x1 = x1;
|
||||||
|
el.x2 = x1;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y1 = y + 0.99;
|
||||||
|
el.y2 = y + 0.98;
|
||||||
|
el.x1 = x1;
|
||||||
|
el.x2 = x2;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y1 = y + 0.98;
|
||||||
|
el.y2 = y + 0.01667;
|
||||||
|
el.x1 = x2;
|
||||||
|
el.x2 = x2;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y1 = y + 1.0 - (0.03 + 0.0025 * (300 - idx));
|
||||||
|
el.y2 = el.y1;
|
||||||
|
el.x1 = x2;
|
||||||
|
el.x2 = x + main_swbox_x1;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP12_V_B_0 && id <= TILE_WIRE_SP12_V_B_23) {
|
||||||
|
int idx = id - TILE_WIRE_SP12_V_B_0;
|
||||||
|
|
||||||
|
float x1 = x + 0.03 + 0.0025 * (90 - idx);
|
||||||
|
float x2 = x + 0.03 + 0.0025 * (90 - (idx ^ 1));
|
||||||
|
float x3 = x + 0.03 + 0.0025 * (90 - (idx ^ 1) - 2);
|
||||||
|
|
||||||
|
if (idx >= 2) {
|
||||||
|
el.y1 = y + 1.00;
|
||||||
|
el.y2 = y + 0.99;
|
||||||
|
el.x1 = x1;
|
||||||
|
el.x2 = x1;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y1 = y + 0.99;
|
||||||
|
el.y2 = y + 0.98;
|
||||||
|
el.x1 = x1;
|
||||||
|
el.x2 = x2;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
el.y1 = y + 0.98;
|
||||||
|
el.y2 = y + 0.01667;
|
||||||
|
el.x1 = x2;
|
||||||
|
el.x2 = x2;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y1 = y + 0.01667;
|
||||||
|
el.y2 = y;
|
||||||
|
el.x1 = x2;
|
||||||
|
el.x2 = x3;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y1 = y + 1.0 - (0.03 + 0.0025 * (300 - (idx ^ 1)));
|
||||||
|
el.y2 = el.y1;
|
||||||
|
el.x1 = x2;
|
||||||
|
el.x2 = x + main_swbox_x1;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global2Local
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_GLB2LOCAL_0 && id <= TILE_WIRE_GLB2LOCAL_3) {
|
||||||
|
int idx = id - TILE_WIRE_GLB2LOCAL_0;
|
||||||
|
el.x1 = x + main_swbox_x1 + 0.005 * (idx + 5);
|
||||||
|
el.x2 = el.x1;
|
||||||
|
el.y1 = y + main_swbox_y1;
|
||||||
|
el.y2 = el.y1 - 0.02;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalNets
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_GLB_NETWK_0 && id <= TILE_WIRE_GLB_NETWK_7) {
|
||||||
|
int idx = id - TILE_WIRE_GLB_NETWK_0;
|
||||||
|
el.x1 = x + main_swbox_x1 - 0.05;
|
||||||
|
el.x2 = x + main_swbox_x1;
|
||||||
|
el.y1 = y + main_swbox_y1 + 0.005 * (13 - idx);
|
||||||
|
el.y2 = el.y1;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neighbours
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_NEIGH_OP_BNL_0 && id <= TILE_WIRE_NEIGH_OP_TOP_7) {
|
||||||
|
int idx = id - TILE_WIRE_NEIGH_OP_BNL_0;
|
||||||
|
el.y1 = y + main_swbox_y2 - (0.0025 * (idx + 10) + 0.01 * (idx / 8));
|
||||||
|
el.y2 = el.y1;
|
||||||
|
el.x1 = x + main_swbox_x1 - 0.05;
|
||||||
|
el.x2 = x + main_swbox_x1;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local Tracks
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_LOCAL_G0_0 && id <= TILE_WIRE_LOCAL_G3_7) {
|
||||||
|
int idx = id - TILE_WIRE_LOCAL_G0_0;
|
||||||
|
el.x1 = x + main_swbox_x2;
|
||||||
|
el.x2 = x + local_swbox_x1;
|
||||||
|
float yoff = y + (local_swbox_y1 + local_swbox_y2) / 2 - 0.005 * 16 - 0.075;
|
||||||
|
el.y1 = yoff + 0.005 * idx + 0.05 * (idx / 8);
|
||||||
|
el.y2 = el.y1;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
// LC Inputs
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_LUTFF_0_IN_0 && id <= TILE_WIRE_LUTFF_7_IN_3) {
|
||||||
|
int idx = id - TILE_WIRE_LUTFF_0_IN_0;
|
||||||
|
int z = idx / 4;
|
||||||
|
int input = idx % 4;
|
||||||
|
el.x1 = x + local_swbox_x2;
|
||||||
|
el.x2 = x + lut_swbox_x1;
|
||||||
|
el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * input) + z * logic_cell_pitch;
|
||||||
|
el.y2 = el.y1;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_LUTFF_0_IN_0_LUT && id <= TILE_WIRE_LUTFF_7_IN_3_LUT) {
|
||||||
|
int idx = id - TILE_WIRE_LUTFF_0_IN_0_LUT;
|
||||||
|
int z = idx / 4;
|
||||||
|
int input = idx % 4;
|
||||||
|
el.x1 = x + lut_swbox_x2;
|
||||||
|
el.x2 = x + logic_cell_x1;
|
||||||
|
el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * input) + z * logic_cell_pitch;
|
||||||
|
el.y2 = el.y1;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
// LC Outputs
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_LUTFF_0_OUT && id <= TILE_WIRE_LUTFF_7_OUT) {
|
||||||
|
int idx = id - TILE_WIRE_LUTFF_0_OUT;
|
||||||
|
|
||||||
|
float y1 = y + 1.0 - (0.03 + 0.0025 * (152 + idx));
|
||||||
|
|
||||||
|
el.y1 = y1;
|
||||||
|
el.y2 = y1;
|
||||||
|
el.x1 = x + main_swbox_x2;
|
||||||
|
el.x2 = x + 0.97 + 0.0025 * (7 - idx);
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y1 = y1;
|
||||||
|
el.y2 = y + (logic_cell_y1 + logic_cell_y2) / 2 + idx * logic_cell_pitch;
|
||||||
|
el.x1 = el.x2;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y1 = el.y2;
|
||||||
|
el.x1 = x + logic_cell_x2;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
// LC Control
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_LUTFF_GLOBAL_CEN && id <= TILE_WIRE_LUTFF_GLOBAL_S_R) {
|
||||||
|
int idx = id - TILE_WIRE_LUTFF_GLOBAL_CEN;
|
||||||
|
|
||||||
|
el.x1 = x + main_swbox_x2 - 0.005 * (idx + 5);
|
||||||
|
el.x2 = el.x1;
|
||||||
|
el.y1 = y + main_swbox_y1;
|
||||||
|
el.y2 = el.y1 - 0.005 * (idx + 2);
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y1 = el.y2;
|
||||||
|
el.x2 = x + logic_cell_x2 - 0.005 * (2 - idx + 5);
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.y2 = y + logic_cell_y1;
|
||||||
|
el.x1 = el.x2;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
el.y1 = y + logic_cell_y2 + i * logic_cell_pitch;
|
||||||
|
el.y2 = y + logic_cell_y1 + (i + 1) * logic_cell_pitch;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LC Cascade
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_LUTFF_0_LOUT && id <= TILE_WIRE_LUTFF_6_LOUT) {
|
||||||
|
int idx = id - TILE_WIRE_LUTFF_0_LOUT;
|
||||||
|
el.x1 = x + logic_cell_x1 + 0.005 * 5;
|
||||||
|
el.x2 = el.x1;
|
||||||
|
el.y1 = y + logic_cell_y2 + idx * logic_cell_pitch;
|
||||||
|
el.y2 = y + logic_cell_y1 + (idx + 1) * logic_cell_pitch;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Carry Chain
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_LUTFF_0_COUT && id <= TILE_WIRE_LUTFF_7_COUT) {
|
||||||
|
int idx = id - TILE_WIRE_LUTFF_0_COUT;
|
||||||
|
el.x1 = x + logic_cell_x1 + 0.005 * 3;
|
||||||
|
el.x2 = el.x1;
|
||||||
|
el.y1 = y + logic_cell_y2 + idx * logic_cell_pitch;
|
||||||
|
el.y2 = y + (idx < 7 ? logic_cell_y1 + (idx + 1) * logic_cell_pitch : 1.0);
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id == TILE_WIRE_CARRY_IN) {
|
||||||
|
el.x1 = x + logic_cell_x1 + 0.005 * 3;
|
||||||
|
el.x2 = el.x1;
|
||||||
|
el.y1 = y;
|
||||||
|
el.y2 = y + 0.01;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id == TILE_WIRE_CARRY_IN_MUX) {
|
||||||
|
el.x1 = x + logic_cell_x1 + 0.005 * 3;
|
||||||
|
el.x2 = el.x1;
|
||||||
|
el.y1 = y + 0.02;
|
||||||
|
el.y2 = y + logic_cell_y1;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool getWireXY_main(GfxTileWireId id, float &x, float &y)
|
||||||
|
{
|
||||||
|
// Horizontal Span-4 Wires
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP4_H_L_36 && id <= TILE_WIRE_SP4_H_L_47) {
|
||||||
|
int idx = (id - TILE_WIRE_SP4_H_L_36) + 48;
|
||||||
|
x = main_swbox_x1 + 0.0025 * (idx + 35);
|
||||||
|
y = main_swbox_y2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP4_H_R_0 && id <= TILE_WIRE_SP4_H_R_47) {
|
||||||
|
int idx = id - TILE_WIRE_SP4_H_R_0;
|
||||||
|
x = main_swbox_x1 + 0.0025 * ((idx ^ 1) + 35);
|
||||||
|
y = main_swbox_y2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertical Span-4 Wires
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP4_V_T_36 && id <= TILE_WIRE_SP4_V_T_47) {
|
||||||
|
int idx = (id - TILE_WIRE_SP4_V_T_36) + 48;
|
||||||
|
y = 1.0 - (0.03 + 0.0025 * (270 - idx));
|
||||||
|
x = main_swbox_x1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP4_V_B_0 && id <= TILE_WIRE_SP4_V_B_47) {
|
||||||
|
int idx = id - TILE_WIRE_SP4_V_B_0;
|
||||||
|
y = 1.0 - (0.03 + 0.0025 * (270 - (idx ^ 1)));
|
||||||
|
x = main_swbox_x1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Horizontal Span-12 Wires
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP12_H_L_22 && id <= TILE_WIRE_SP12_H_L_23) {
|
||||||
|
int idx = (id - TILE_WIRE_SP12_H_L_22) + 24;
|
||||||
|
x = main_swbox_x1 + 0.0025 * (idx + 5);
|
||||||
|
y = main_swbox_y2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP12_H_R_0 && id <= TILE_WIRE_SP12_H_R_23) {
|
||||||
|
int idx = id - TILE_WIRE_SP12_H_R_0;
|
||||||
|
x = main_swbox_x1 + 0.0025 * ((idx ^ 1) + 5);
|
||||||
|
y = main_swbox_y2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertical Right Span-4
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP4_R_V_B_0 && id <= TILE_WIRE_SP4_R_V_B_47) {
|
||||||
|
int idx = id - TILE_WIRE_SP4_R_V_B_0;
|
||||||
|
y = 1.0 - (0.03 + 0.0025 * (145 - (idx ^ 1)));
|
||||||
|
x = main_swbox_x2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertical Span-12 Wires
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP12_V_T_22 && id <= TILE_WIRE_SP12_V_T_23) {
|
||||||
|
int idx = (id - TILE_WIRE_SP12_V_T_22) + 24;
|
||||||
|
y = 1.0 - (0.03 + 0.0025 * (300 - idx));
|
||||||
|
x = main_swbox_x1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_SP12_V_B_0 && id <= TILE_WIRE_SP12_V_B_23) {
|
||||||
|
int idx = id - TILE_WIRE_SP12_V_B_0;
|
||||||
|
y = 1.0 - (0.03 + 0.0025 * (300 - (idx ^ 1)));
|
||||||
|
x = main_swbox_x1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global2Local
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_GLB2LOCAL_0 && id <= TILE_WIRE_GLB2LOCAL_3) {
|
||||||
|
int idx = id - TILE_WIRE_GLB2LOCAL_0;
|
||||||
|
x = main_swbox_x1 + 0.005 * (idx + 5);
|
||||||
|
y = main_swbox_y1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalNets
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_GLB_NETWK_0 && id <= TILE_WIRE_GLB_NETWK_7) {
|
||||||
|
int idx = id - TILE_WIRE_GLB_NETWK_0;
|
||||||
|
x = main_swbox_x1;
|
||||||
|
y = main_swbox_y1 + 0.005 * (13 - idx);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neighbours
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_NEIGH_OP_BNL_0 && id <= TILE_WIRE_NEIGH_OP_TOP_7) {
|
||||||
|
int idx = id - TILE_WIRE_NEIGH_OP_BNL_0;
|
||||||
|
y = main_swbox_y2 - (0.0025 * (idx + 10) + 0.01 * (idx / 8));
|
||||||
|
x = main_swbox_x1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Local Tracks
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_LOCAL_G0_0 && id <= TILE_WIRE_LOCAL_G3_7) {
|
||||||
|
int idx = id - TILE_WIRE_LOCAL_G0_0;
|
||||||
|
float yoff = (local_swbox_y1 + local_swbox_y2) / 2 - 0.005 * 16 - 0.075;
|
||||||
|
x = main_swbox_x2;
|
||||||
|
y = yoff + 0.005 * idx + 0.05 * (idx / 8);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// LC Outputs
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_LUTFF_0_OUT && id <= TILE_WIRE_LUTFF_7_OUT) {
|
||||||
|
int idx = id - TILE_WIRE_LUTFF_0_OUT;
|
||||||
|
y = 1.0 - (0.03 + 0.0025 * (152 + idx));
|
||||||
|
x = main_swbox_x2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// LC Control
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_LUTFF_GLOBAL_CEN && id <= TILE_WIRE_LUTFF_GLOBAL_S_R) {
|
||||||
|
int idx = id - TILE_WIRE_LUTFF_GLOBAL_CEN;
|
||||||
|
x = main_swbox_x2 - 0.005 * (idx + 5);
|
||||||
|
y = main_swbox_y1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool getWireXY_local(GfxTileWireId id, float &x, float &y)
|
||||||
|
{
|
||||||
|
if (id >= TILE_WIRE_LOCAL_G0_0 && id <= TILE_WIRE_LOCAL_G3_7) {
|
||||||
|
int idx = id - TILE_WIRE_LOCAL_G0_0;
|
||||||
|
float yoff = (local_swbox_y1 + local_swbox_y2) / 2 - 0.005 * 16 - 0.075;
|
||||||
|
x = local_swbox_x1;
|
||||||
|
y = yoff + 0.005 * idx + 0.05 * (idx / 8);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id >= TILE_WIRE_LUTFF_0_IN_0 && id <= TILE_WIRE_LUTFF_7_IN_3) {
|
||||||
|
int idx = id - TILE_WIRE_LUTFF_0_IN_0;
|
||||||
|
int z = idx / 4;
|
||||||
|
int input = idx % 4;
|
||||||
|
x = local_swbox_x2;
|
||||||
|
y = (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * input) + z * logic_cell_pitch;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pipGfx(std::vector<GraphicElement> &g, int x, int y, float x1, float y1, float x2, float y2, float swx1,
|
||||||
|
float swy1, float swx2, float swy2, GraphicElement::style_t style)
|
||||||
|
{
|
||||||
|
float tx = 0.5 * (x1 + x2);
|
||||||
|
float ty = 0.5 * (y1 + y2);
|
||||||
|
|
||||||
|
GraphicElement el;
|
||||||
|
el.type = GraphicElement::TYPE_ARROW;
|
||||||
|
el.style = style;
|
||||||
|
|
||||||
|
if (fabsf(x1 - swx1) < 0.001 && fabsf(x2 - swx1) < 0.001) {
|
||||||
|
tx = x1 + 0.25 * fabsf(y1 - y2);
|
||||||
|
goto edge_pip;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fabsf(x1 - swx2) < 0.001 && fabsf(x2 - swx2) < 0.001) {
|
||||||
|
tx = x1 - 0.25 * fabsf(y1 - y2);
|
||||||
|
goto edge_pip;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fabsf(y1 - swy1) < 0.001 && fabsf(y2 - swy1) < 0.001) {
|
||||||
|
ty = y1 + 0.25 * fabsf(x1 - x2);
|
||||||
|
goto edge_pip;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fabsf(y1 - swy1) < 0.001 && fabsf(y2 - swy1) < 0.001) {
|
||||||
|
ty = y1 + 0.25 * fabsf(x1 - x2);
|
||||||
|
goto edge_pip;
|
||||||
|
}
|
||||||
|
|
||||||
|
el.x1 = x + x1;
|
||||||
|
el.y1 = y + y1;
|
||||||
|
el.x2 = x + x2;
|
||||||
|
el.y2 = y + y2;
|
||||||
|
g.push_back(el);
|
||||||
|
return;
|
||||||
|
|
||||||
|
edge_pip:
|
||||||
|
el.x1 = x + x1;
|
||||||
|
el.y1 = y + y1;
|
||||||
|
el.x2 = x + tx;
|
||||||
|
el.y2 = y + ty;
|
||||||
|
g.push_back(el);
|
||||||
|
|
||||||
|
el.x1 = x + tx;
|
||||||
|
el.y1 = y + ty;
|
||||||
|
el.x2 = x + x2;
|
||||||
|
el.y2 = y + y2;
|
||||||
|
g.push_back(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, GfxTileWireId dst,
|
||||||
|
GraphicElement::style_t style)
|
||||||
|
{
|
||||||
|
float x1, y1, x2, y2;
|
||||||
|
|
||||||
|
if (getWireXY_main(src, x1, y1) && getWireXY_main(dst, x2, y2)) {
|
||||||
|
pipGfx(g, x, y, x1, y1, x2, y2, main_swbox_x1, main_swbox_y1, main_swbox_x2, main_swbox_y2, style);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getWireXY_local(src, x1, y1) && getWireXY_local(dst, x2, y2)) {
|
||||||
|
pipGfx(g, x, y, x1, y1, x2, y2, local_swbox_x1, local_swbox_y1, local_swbox_x2, local_swbox_y2, style);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TILE_WIRE_LUTFF_0_IN_0_LUT <= src && src <= TILE_WIRE_LUTFF_7_IN_3_LUT && TILE_WIRE_LUTFF_0_OUT <= dst &&
|
||||||
|
dst <= TILE_WIRE_LUTFF_7_OUT) {
|
||||||
|
int lut_idx = (src - TILE_WIRE_LUTFF_0_IN_0_LUT) / 4;
|
||||||
|
int in_idx = (src - TILE_WIRE_LUTFF_0_IN_0_LUT) % 4;
|
||||||
|
|
||||||
|
GraphicElement el;
|
||||||
|
el.type = GraphicElement::TYPE_ARROW;
|
||||||
|
el.style = style;
|
||||||
|
el.x1 = x + logic_cell_x1;
|
||||||
|
el.x2 = x + logic_cell_x2;
|
||||||
|
el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * in_idx) + lut_idx * logic_cell_pitch;
|
||||||
|
el.y2 = y + (logic_cell_y1 + logic_cell_y2) / 2 + lut_idx * logic_cell_pitch;
|
||||||
|
g.push_back(el);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TILE_WIRE_LUTFF_0_IN_0 <= src && src <= TILE_WIRE_LUTFF_7_IN_3 && TILE_WIRE_LUTFF_0_IN_0_LUT <= dst &&
|
||||||
|
dst <= TILE_WIRE_LUTFF_7_IN_3_LUT) {
|
||||||
|
int lut_idx = (src - TILE_WIRE_LUTFF_0_IN_0) / 4;
|
||||||
|
int in_idx = (src - TILE_WIRE_LUTFF_0_IN_0) % 4;
|
||||||
|
int out_idx = (dst - TILE_WIRE_LUTFF_0_IN_0_LUT) % 4;
|
||||||
|
|
||||||
|
GraphicElement el;
|
||||||
|
el.type = GraphicElement::TYPE_ARROW;
|
||||||
|
el.style = style;
|
||||||
|
el.x1 = x + lut_swbox_x1;
|
||||||
|
el.x2 = x + lut_swbox_x2;
|
||||||
|
el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * in_idx) + lut_idx * logic_cell_pitch;
|
||||||
|
el.y2 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * out_idx) + lut_idx * logic_cell_pitch;
|
||||||
|
g.push_back(el);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src == TILE_WIRE_CARRY_IN && dst == TILE_WIRE_CARRY_IN_MUX) {
|
||||||
|
GraphicElement el;
|
||||||
|
el.type = GraphicElement::TYPE_ARROW;
|
||||||
|
el.style = style;
|
||||||
|
el.x1 = x + logic_cell_x1 + 0.005 * 3;
|
||||||
|
el.x2 = el.x1;
|
||||||
|
el.y1 = y + 0.01;
|
||||||
|
el.y2 = y + 0.02;
|
||||||
|
g.push_back(el);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
523
xc7/gfx.h
Normal file
523
xc7/gfx.h
Normal file
@ -0,0 +1,523 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GFX_H
|
||||||
|
#define GFX_H
|
||||||
|
|
||||||
|
#include "nextpnr.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
const float main_swbox_x1 = 0.35;
|
||||||
|
const float main_swbox_x2 = 0.60;
|
||||||
|
const float main_swbox_y1 = 0.05;
|
||||||
|
const float main_swbox_y2 = 0.73;
|
||||||
|
|
||||||
|
const float local_swbox_x1 = 0.63;
|
||||||
|
const float local_swbox_x2 = 0.73;
|
||||||
|
const float local_swbox_y1 = 0.05;
|
||||||
|
const float local_swbox_y2 = 0.55;
|
||||||
|
|
||||||
|
const float lut_swbox_x1 = 0.76;
|
||||||
|
const float lut_swbox_x2 = 0.80;
|
||||||
|
|
||||||
|
const float logic_cell_x1 = 0.83;
|
||||||
|
const float logic_cell_x2 = 0.95;
|
||||||
|
const float logic_cell_y1 = 0.05;
|
||||||
|
const float logic_cell_y2 = 0.10;
|
||||||
|
const float logic_cell_pitch = 0.0625;
|
||||||
|
|
||||||
|
enum GfxTileWireId
|
||||||
|
{
|
||||||
|
TILE_WIRE_GLB2LOCAL_0,
|
||||||
|
TILE_WIRE_GLB2LOCAL_1,
|
||||||
|
TILE_WIRE_GLB2LOCAL_2,
|
||||||
|
TILE_WIRE_GLB2LOCAL_3,
|
||||||
|
|
||||||
|
TILE_WIRE_GLB_NETWK_0,
|
||||||
|
TILE_WIRE_GLB_NETWK_1,
|
||||||
|
TILE_WIRE_GLB_NETWK_2,
|
||||||
|
TILE_WIRE_GLB_NETWK_3,
|
||||||
|
TILE_WIRE_GLB_NETWK_4,
|
||||||
|
TILE_WIRE_GLB_NETWK_5,
|
||||||
|
TILE_WIRE_GLB_NETWK_6,
|
||||||
|
TILE_WIRE_GLB_NETWK_7,
|
||||||
|
|
||||||
|
TILE_WIRE_LOCAL_G0_0,
|
||||||
|
TILE_WIRE_LOCAL_G0_1,
|
||||||
|
TILE_WIRE_LOCAL_G0_2,
|
||||||
|
TILE_WIRE_LOCAL_G0_3,
|
||||||
|
TILE_WIRE_LOCAL_G0_4,
|
||||||
|
TILE_WIRE_LOCAL_G0_5,
|
||||||
|
TILE_WIRE_LOCAL_G0_6,
|
||||||
|
TILE_WIRE_LOCAL_G0_7,
|
||||||
|
|
||||||
|
TILE_WIRE_LOCAL_G1_0,
|
||||||
|
TILE_WIRE_LOCAL_G1_1,
|
||||||
|
TILE_WIRE_LOCAL_G1_2,
|
||||||
|
TILE_WIRE_LOCAL_G1_3,
|
||||||
|
TILE_WIRE_LOCAL_G1_4,
|
||||||
|
TILE_WIRE_LOCAL_G1_5,
|
||||||
|
TILE_WIRE_LOCAL_G1_6,
|
||||||
|
TILE_WIRE_LOCAL_G1_7,
|
||||||
|
|
||||||
|
TILE_WIRE_LOCAL_G2_0,
|
||||||
|
TILE_WIRE_LOCAL_G2_1,
|
||||||
|
TILE_WIRE_LOCAL_G2_2,
|
||||||
|
TILE_WIRE_LOCAL_G2_3,
|
||||||
|
TILE_WIRE_LOCAL_G2_4,
|
||||||
|
TILE_WIRE_LOCAL_G2_5,
|
||||||
|
TILE_WIRE_LOCAL_G2_6,
|
||||||
|
TILE_WIRE_LOCAL_G2_7,
|
||||||
|
|
||||||
|
TILE_WIRE_LOCAL_G3_0,
|
||||||
|
TILE_WIRE_LOCAL_G3_1,
|
||||||
|
TILE_WIRE_LOCAL_G3_2,
|
||||||
|
TILE_WIRE_LOCAL_G3_3,
|
||||||
|
TILE_WIRE_LOCAL_G3_4,
|
||||||
|
TILE_WIRE_LOCAL_G3_5,
|
||||||
|
TILE_WIRE_LOCAL_G3_6,
|
||||||
|
TILE_WIRE_LOCAL_G3_7,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_0_IN_0,
|
||||||
|
TILE_WIRE_LUTFF_0_IN_1,
|
||||||
|
TILE_WIRE_LUTFF_0_IN_2,
|
||||||
|
TILE_WIRE_LUTFF_0_IN_3,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_1_IN_0,
|
||||||
|
TILE_WIRE_LUTFF_1_IN_1,
|
||||||
|
TILE_WIRE_LUTFF_1_IN_2,
|
||||||
|
TILE_WIRE_LUTFF_1_IN_3,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_2_IN_0,
|
||||||
|
TILE_WIRE_LUTFF_2_IN_1,
|
||||||
|
TILE_WIRE_LUTFF_2_IN_2,
|
||||||
|
TILE_WIRE_LUTFF_2_IN_3,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_3_IN_0,
|
||||||
|
TILE_WIRE_LUTFF_3_IN_1,
|
||||||
|
TILE_WIRE_LUTFF_3_IN_2,
|
||||||
|
TILE_WIRE_LUTFF_3_IN_3,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_4_IN_0,
|
||||||
|
TILE_WIRE_LUTFF_4_IN_1,
|
||||||
|
TILE_WIRE_LUTFF_4_IN_2,
|
||||||
|
TILE_WIRE_LUTFF_4_IN_3,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_5_IN_0,
|
||||||
|
TILE_WIRE_LUTFF_5_IN_1,
|
||||||
|
TILE_WIRE_LUTFF_5_IN_2,
|
||||||
|
TILE_WIRE_LUTFF_5_IN_3,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_6_IN_0,
|
||||||
|
TILE_WIRE_LUTFF_6_IN_1,
|
||||||
|
TILE_WIRE_LUTFF_6_IN_2,
|
||||||
|
TILE_WIRE_LUTFF_6_IN_3,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_7_IN_0,
|
||||||
|
TILE_WIRE_LUTFF_7_IN_1,
|
||||||
|
TILE_WIRE_LUTFF_7_IN_2,
|
||||||
|
TILE_WIRE_LUTFF_7_IN_3,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_0_IN_0_LUT,
|
||||||
|
TILE_WIRE_LUTFF_0_IN_1_LUT,
|
||||||
|
TILE_WIRE_LUTFF_0_IN_2_LUT,
|
||||||
|
TILE_WIRE_LUTFF_0_IN_3_LUT,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_1_IN_0_LUT,
|
||||||
|
TILE_WIRE_LUTFF_1_IN_1_LUT,
|
||||||
|
TILE_WIRE_LUTFF_1_IN_2_LUT,
|
||||||
|
TILE_WIRE_LUTFF_1_IN_3_LUT,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_2_IN_0_LUT,
|
||||||
|
TILE_WIRE_LUTFF_2_IN_1_LUT,
|
||||||
|
TILE_WIRE_LUTFF_2_IN_2_LUT,
|
||||||
|
TILE_WIRE_LUTFF_2_IN_3_LUT,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_3_IN_0_LUT,
|
||||||
|
TILE_WIRE_LUTFF_3_IN_1_LUT,
|
||||||
|
TILE_WIRE_LUTFF_3_IN_2_LUT,
|
||||||
|
TILE_WIRE_LUTFF_3_IN_3_LUT,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_4_IN_0_LUT,
|
||||||
|
TILE_WIRE_LUTFF_4_IN_1_LUT,
|
||||||
|
TILE_WIRE_LUTFF_4_IN_2_LUT,
|
||||||
|
TILE_WIRE_LUTFF_4_IN_3_LUT,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_5_IN_0_LUT,
|
||||||
|
TILE_WIRE_LUTFF_5_IN_1_LUT,
|
||||||
|
TILE_WIRE_LUTFF_5_IN_2_LUT,
|
||||||
|
TILE_WIRE_LUTFF_5_IN_3_LUT,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_6_IN_0_LUT,
|
||||||
|
TILE_WIRE_LUTFF_6_IN_1_LUT,
|
||||||
|
TILE_WIRE_LUTFF_6_IN_2_LUT,
|
||||||
|
TILE_WIRE_LUTFF_6_IN_3_LUT,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_7_IN_0_LUT,
|
||||||
|
TILE_WIRE_LUTFF_7_IN_1_LUT,
|
||||||
|
TILE_WIRE_LUTFF_7_IN_2_LUT,
|
||||||
|
TILE_WIRE_LUTFF_7_IN_3_LUT,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_0_LOUT,
|
||||||
|
TILE_WIRE_LUTFF_1_LOUT,
|
||||||
|
TILE_WIRE_LUTFF_2_LOUT,
|
||||||
|
TILE_WIRE_LUTFF_3_LOUT,
|
||||||
|
TILE_WIRE_LUTFF_4_LOUT,
|
||||||
|
TILE_WIRE_LUTFF_5_LOUT,
|
||||||
|
TILE_WIRE_LUTFF_6_LOUT,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_0_OUT,
|
||||||
|
TILE_WIRE_LUTFF_1_OUT,
|
||||||
|
TILE_WIRE_LUTFF_2_OUT,
|
||||||
|
TILE_WIRE_LUTFF_3_OUT,
|
||||||
|
TILE_WIRE_LUTFF_4_OUT,
|
||||||
|
TILE_WIRE_LUTFF_5_OUT,
|
||||||
|
TILE_WIRE_LUTFF_6_OUT,
|
||||||
|
TILE_WIRE_LUTFF_7_OUT,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_0_COUT,
|
||||||
|
TILE_WIRE_LUTFF_1_COUT,
|
||||||
|
TILE_WIRE_LUTFF_2_COUT,
|
||||||
|
TILE_WIRE_LUTFF_3_COUT,
|
||||||
|
TILE_WIRE_LUTFF_4_COUT,
|
||||||
|
TILE_WIRE_LUTFF_5_COUT,
|
||||||
|
TILE_WIRE_LUTFF_6_COUT,
|
||||||
|
TILE_WIRE_LUTFF_7_COUT,
|
||||||
|
|
||||||
|
TILE_WIRE_LUTFF_GLOBAL_CEN,
|
||||||
|
TILE_WIRE_LUTFF_GLOBAL_CLK,
|
||||||
|
TILE_WIRE_LUTFF_GLOBAL_S_R,
|
||||||
|
|
||||||
|
TILE_WIRE_CARRY_IN,
|
||||||
|
TILE_WIRE_CARRY_IN_MUX,
|
||||||
|
|
||||||
|
TILE_WIRE_NEIGH_OP_BNL_0,
|
||||||
|
TILE_WIRE_NEIGH_OP_BNL_1,
|
||||||
|
TILE_WIRE_NEIGH_OP_BNL_2,
|
||||||
|
TILE_WIRE_NEIGH_OP_BNL_3,
|
||||||
|
TILE_WIRE_NEIGH_OP_BNL_4,
|
||||||
|
TILE_WIRE_NEIGH_OP_BNL_5,
|
||||||
|
TILE_WIRE_NEIGH_OP_BNL_6,
|
||||||
|
TILE_WIRE_NEIGH_OP_BNL_7,
|
||||||
|
|
||||||
|
TILE_WIRE_NEIGH_OP_BNR_0,
|
||||||
|
TILE_WIRE_NEIGH_OP_BNR_1,
|
||||||
|
TILE_WIRE_NEIGH_OP_BNR_2,
|
||||||
|
TILE_WIRE_NEIGH_OP_BNR_3,
|
||||||
|
TILE_WIRE_NEIGH_OP_BNR_4,
|
||||||
|
TILE_WIRE_NEIGH_OP_BNR_5,
|
||||||
|
TILE_WIRE_NEIGH_OP_BNR_6,
|
||||||
|
TILE_WIRE_NEIGH_OP_BNR_7,
|
||||||
|
|
||||||
|
TILE_WIRE_NEIGH_OP_BOT_0,
|
||||||
|
TILE_WIRE_NEIGH_OP_BOT_1,
|
||||||
|
TILE_WIRE_NEIGH_OP_BOT_2,
|
||||||
|
TILE_WIRE_NEIGH_OP_BOT_3,
|
||||||
|
TILE_WIRE_NEIGH_OP_BOT_4,
|
||||||
|
TILE_WIRE_NEIGH_OP_BOT_5,
|
||||||
|
TILE_WIRE_NEIGH_OP_BOT_6,
|
||||||
|
TILE_WIRE_NEIGH_OP_BOT_7,
|
||||||
|
|
||||||
|
TILE_WIRE_NEIGH_OP_LFT_0,
|
||||||
|
TILE_WIRE_NEIGH_OP_LFT_1,
|
||||||
|
TILE_WIRE_NEIGH_OP_LFT_2,
|
||||||
|
TILE_WIRE_NEIGH_OP_LFT_3,
|
||||||
|
TILE_WIRE_NEIGH_OP_LFT_4,
|
||||||
|
TILE_WIRE_NEIGH_OP_LFT_5,
|
||||||
|
TILE_WIRE_NEIGH_OP_LFT_6,
|
||||||
|
TILE_WIRE_NEIGH_OP_LFT_7,
|
||||||
|
|
||||||
|
TILE_WIRE_NEIGH_OP_RGT_0,
|
||||||
|
TILE_WIRE_NEIGH_OP_RGT_1,
|
||||||
|
TILE_WIRE_NEIGH_OP_RGT_2,
|
||||||
|
TILE_WIRE_NEIGH_OP_RGT_3,
|
||||||
|
TILE_WIRE_NEIGH_OP_RGT_4,
|
||||||
|
TILE_WIRE_NEIGH_OP_RGT_5,
|
||||||
|
TILE_WIRE_NEIGH_OP_RGT_6,
|
||||||
|
TILE_WIRE_NEIGH_OP_RGT_7,
|
||||||
|
|
||||||
|
TILE_WIRE_NEIGH_OP_TNL_0,
|
||||||
|
TILE_WIRE_NEIGH_OP_TNL_1,
|
||||||
|
TILE_WIRE_NEIGH_OP_TNL_2,
|
||||||
|
TILE_WIRE_NEIGH_OP_TNL_3,
|
||||||
|
TILE_WIRE_NEIGH_OP_TNL_4,
|
||||||
|
TILE_WIRE_NEIGH_OP_TNL_5,
|
||||||
|
TILE_WIRE_NEIGH_OP_TNL_6,
|
||||||
|
TILE_WIRE_NEIGH_OP_TNL_7,
|
||||||
|
|
||||||
|
TILE_WIRE_NEIGH_OP_TNR_0,
|
||||||
|
TILE_WIRE_NEIGH_OP_TNR_1,
|
||||||
|
TILE_WIRE_NEIGH_OP_TNR_2,
|
||||||
|
TILE_WIRE_NEIGH_OP_TNR_3,
|
||||||
|
TILE_WIRE_NEIGH_OP_TNR_4,
|
||||||
|
TILE_WIRE_NEIGH_OP_TNR_5,
|
||||||
|
TILE_WIRE_NEIGH_OP_TNR_6,
|
||||||
|
TILE_WIRE_NEIGH_OP_TNR_7,
|
||||||
|
|
||||||
|
TILE_WIRE_NEIGH_OP_TOP_0,
|
||||||
|
TILE_WIRE_NEIGH_OP_TOP_1,
|
||||||
|
TILE_WIRE_NEIGH_OP_TOP_2,
|
||||||
|
TILE_WIRE_NEIGH_OP_TOP_3,
|
||||||
|
TILE_WIRE_NEIGH_OP_TOP_4,
|
||||||
|
TILE_WIRE_NEIGH_OP_TOP_5,
|
||||||
|
TILE_WIRE_NEIGH_OP_TOP_6,
|
||||||
|
TILE_WIRE_NEIGH_OP_TOP_7,
|
||||||
|
|
||||||
|
TILE_WIRE_SP4_V_B_0,
|
||||||
|
TILE_WIRE_SP4_V_B_1,
|
||||||
|
TILE_WIRE_SP4_V_B_2,
|
||||||
|
TILE_WIRE_SP4_V_B_3,
|
||||||
|
TILE_WIRE_SP4_V_B_4,
|
||||||
|
TILE_WIRE_SP4_V_B_5,
|
||||||
|
TILE_WIRE_SP4_V_B_6,
|
||||||
|
TILE_WIRE_SP4_V_B_7,
|
||||||
|
TILE_WIRE_SP4_V_B_8,
|
||||||
|
TILE_WIRE_SP4_V_B_9,
|
||||||
|
TILE_WIRE_SP4_V_B_10,
|
||||||
|
TILE_WIRE_SP4_V_B_11,
|
||||||
|
TILE_WIRE_SP4_V_B_12,
|
||||||
|
TILE_WIRE_SP4_V_B_13,
|
||||||
|
TILE_WIRE_SP4_V_B_14,
|
||||||
|
TILE_WIRE_SP4_V_B_15,
|
||||||
|
TILE_WIRE_SP4_V_B_16,
|
||||||
|
TILE_WIRE_SP4_V_B_17,
|
||||||
|
TILE_WIRE_SP4_V_B_18,
|
||||||
|
TILE_WIRE_SP4_V_B_19,
|
||||||
|
TILE_WIRE_SP4_V_B_20,
|
||||||
|
TILE_WIRE_SP4_V_B_21,
|
||||||
|
TILE_WIRE_SP4_V_B_22,
|
||||||
|
TILE_WIRE_SP4_V_B_23,
|
||||||
|
TILE_WIRE_SP4_V_B_24,
|
||||||
|
TILE_WIRE_SP4_V_B_25,
|
||||||
|
TILE_WIRE_SP4_V_B_26,
|
||||||
|
TILE_WIRE_SP4_V_B_27,
|
||||||
|
TILE_WIRE_SP4_V_B_28,
|
||||||
|
TILE_WIRE_SP4_V_B_29,
|
||||||
|
TILE_WIRE_SP4_V_B_30,
|
||||||
|
TILE_WIRE_SP4_V_B_31,
|
||||||
|
TILE_WIRE_SP4_V_B_32,
|
||||||
|
TILE_WIRE_SP4_V_B_33,
|
||||||
|
TILE_WIRE_SP4_V_B_34,
|
||||||
|
TILE_WIRE_SP4_V_B_35,
|
||||||
|
TILE_WIRE_SP4_V_B_36,
|
||||||
|
TILE_WIRE_SP4_V_B_37,
|
||||||
|
TILE_WIRE_SP4_V_B_38,
|
||||||
|
TILE_WIRE_SP4_V_B_39,
|
||||||
|
TILE_WIRE_SP4_V_B_40,
|
||||||
|
TILE_WIRE_SP4_V_B_41,
|
||||||
|
TILE_WIRE_SP4_V_B_42,
|
||||||
|
TILE_WIRE_SP4_V_B_43,
|
||||||
|
TILE_WIRE_SP4_V_B_44,
|
||||||
|
TILE_WIRE_SP4_V_B_45,
|
||||||
|
TILE_WIRE_SP4_V_B_46,
|
||||||
|
TILE_WIRE_SP4_V_B_47,
|
||||||
|
|
||||||
|
TILE_WIRE_SP4_V_T_36,
|
||||||
|
TILE_WIRE_SP4_V_T_37,
|
||||||
|
TILE_WIRE_SP4_V_T_38,
|
||||||
|
TILE_WIRE_SP4_V_T_39,
|
||||||
|
TILE_WIRE_SP4_V_T_40,
|
||||||
|
TILE_WIRE_SP4_V_T_41,
|
||||||
|
TILE_WIRE_SP4_V_T_42,
|
||||||
|
TILE_WIRE_SP4_V_T_43,
|
||||||
|
TILE_WIRE_SP4_V_T_44,
|
||||||
|
TILE_WIRE_SP4_V_T_45,
|
||||||
|
TILE_WIRE_SP4_V_T_46,
|
||||||
|
TILE_WIRE_SP4_V_T_47,
|
||||||
|
|
||||||
|
TILE_WIRE_SP4_R_V_B_0,
|
||||||
|
TILE_WIRE_SP4_R_V_B_1,
|
||||||
|
TILE_WIRE_SP4_R_V_B_2,
|
||||||
|
TILE_WIRE_SP4_R_V_B_3,
|
||||||
|
TILE_WIRE_SP4_R_V_B_4,
|
||||||
|
TILE_WIRE_SP4_R_V_B_5,
|
||||||
|
TILE_WIRE_SP4_R_V_B_6,
|
||||||
|
TILE_WIRE_SP4_R_V_B_7,
|
||||||
|
TILE_WIRE_SP4_R_V_B_8,
|
||||||
|
TILE_WIRE_SP4_R_V_B_9,
|
||||||
|
TILE_WIRE_SP4_R_V_B_10,
|
||||||
|
TILE_WIRE_SP4_R_V_B_11,
|
||||||
|
TILE_WIRE_SP4_R_V_B_12,
|
||||||
|
TILE_WIRE_SP4_R_V_B_13,
|
||||||
|
TILE_WIRE_SP4_R_V_B_14,
|
||||||
|
TILE_WIRE_SP4_R_V_B_15,
|
||||||
|
TILE_WIRE_SP4_R_V_B_16,
|
||||||
|
TILE_WIRE_SP4_R_V_B_17,
|
||||||
|
TILE_WIRE_SP4_R_V_B_18,
|
||||||
|
TILE_WIRE_SP4_R_V_B_19,
|
||||||
|
TILE_WIRE_SP4_R_V_B_20,
|
||||||
|
TILE_WIRE_SP4_R_V_B_21,
|
||||||
|
TILE_WIRE_SP4_R_V_B_22,
|
||||||
|
TILE_WIRE_SP4_R_V_B_23,
|
||||||
|
TILE_WIRE_SP4_R_V_B_24,
|
||||||
|
TILE_WIRE_SP4_R_V_B_25,
|
||||||
|
TILE_WIRE_SP4_R_V_B_26,
|
||||||
|
TILE_WIRE_SP4_R_V_B_27,
|
||||||
|
TILE_WIRE_SP4_R_V_B_28,
|
||||||
|
TILE_WIRE_SP4_R_V_B_29,
|
||||||
|
TILE_WIRE_SP4_R_V_B_30,
|
||||||
|
TILE_WIRE_SP4_R_V_B_31,
|
||||||
|
TILE_WIRE_SP4_R_V_B_32,
|
||||||
|
TILE_WIRE_SP4_R_V_B_33,
|
||||||
|
TILE_WIRE_SP4_R_V_B_34,
|
||||||
|
TILE_WIRE_SP4_R_V_B_35,
|
||||||
|
TILE_WIRE_SP4_R_V_B_36,
|
||||||
|
TILE_WIRE_SP4_R_V_B_37,
|
||||||
|
TILE_WIRE_SP4_R_V_B_38,
|
||||||
|
TILE_WIRE_SP4_R_V_B_39,
|
||||||
|
TILE_WIRE_SP4_R_V_B_40,
|
||||||
|
TILE_WIRE_SP4_R_V_B_41,
|
||||||
|
TILE_WIRE_SP4_R_V_B_42,
|
||||||
|
TILE_WIRE_SP4_R_V_B_43,
|
||||||
|
TILE_WIRE_SP4_R_V_B_44,
|
||||||
|
TILE_WIRE_SP4_R_V_B_45,
|
||||||
|
TILE_WIRE_SP4_R_V_B_46,
|
||||||
|
TILE_WIRE_SP4_R_V_B_47,
|
||||||
|
|
||||||
|
TILE_WIRE_SP4_H_L_36,
|
||||||
|
TILE_WIRE_SP4_H_L_37,
|
||||||
|
TILE_WIRE_SP4_H_L_38,
|
||||||
|
TILE_WIRE_SP4_H_L_39,
|
||||||
|
TILE_WIRE_SP4_H_L_40,
|
||||||
|
TILE_WIRE_SP4_H_L_41,
|
||||||
|
TILE_WIRE_SP4_H_L_42,
|
||||||
|
TILE_WIRE_SP4_H_L_43,
|
||||||
|
TILE_WIRE_SP4_H_L_44,
|
||||||
|
TILE_WIRE_SP4_H_L_45,
|
||||||
|
TILE_WIRE_SP4_H_L_46,
|
||||||
|
TILE_WIRE_SP4_H_L_47,
|
||||||
|
|
||||||
|
TILE_WIRE_SP4_H_R_0,
|
||||||
|
TILE_WIRE_SP4_H_R_1,
|
||||||
|
TILE_WIRE_SP4_H_R_2,
|
||||||
|
TILE_WIRE_SP4_H_R_3,
|
||||||
|
TILE_WIRE_SP4_H_R_4,
|
||||||
|
TILE_WIRE_SP4_H_R_5,
|
||||||
|
TILE_WIRE_SP4_H_R_6,
|
||||||
|
TILE_WIRE_SP4_H_R_7,
|
||||||
|
TILE_WIRE_SP4_H_R_8,
|
||||||
|
TILE_WIRE_SP4_H_R_9,
|
||||||
|
TILE_WIRE_SP4_H_R_10,
|
||||||
|
TILE_WIRE_SP4_H_R_11,
|
||||||
|
TILE_WIRE_SP4_H_R_12,
|
||||||
|
TILE_WIRE_SP4_H_R_13,
|
||||||
|
TILE_WIRE_SP4_H_R_14,
|
||||||
|
TILE_WIRE_SP4_H_R_15,
|
||||||
|
TILE_WIRE_SP4_H_R_16,
|
||||||
|
TILE_WIRE_SP4_H_R_17,
|
||||||
|
TILE_WIRE_SP4_H_R_18,
|
||||||
|
TILE_WIRE_SP4_H_R_19,
|
||||||
|
TILE_WIRE_SP4_H_R_20,
|
||||||
|
TILE_WIRE_SP4_H_R_21,
|
||||||
|
TILE_WIRE_SP4_H_R_22,
|
||||||
|
TILE_WIRE_SP4_H_R_23,
|
||||||
|
TILE_WIRE_SP4_H_R_24,
|
||||||
|
TILE_WIRE_SP4_H_R_25,
|
||||||
|
TILE_WIRE_SP4_H_R_26,
|
||||||
|
TILE_WIRE_SP4_H_R_27,
|
||||||
|
TILE_WIRE_SP4_H_R_28,
|
||||||
|
TILE_WIRE_SP4_H_R_29,
|
||||||
|
TILE_WIRE_SP4_H_R_30,
|
||||||
|
TILE_WIRE_SP4_H_R_31,
|
||||||
|
TILE_WIRE_SP4_H_R_32,
|
||||||
|
TILE_WIRE_SP4_H_R_33,
|
||||||
|
TILE_WIRE_SP4_H_R_34,
|
||||||
|
TILE_WIRE_SP4_H_R_35,
|
||||||
|
TILE_WIRE_SP4_H_R_36,
|
||||||
|
TILE_WIRE_SP4_H_R_37,
|
||||||
|
TILE_WIRE_SP4_H_R_38,
|
||||||
|
TILE_WIRE_SP4_H_R_39,
|
||||||
|
TILE_WIRE_SP4_H_R_40,
|
||||||
|
TILE_WIRE_SP4_H_R_41,
|
||||||
|
TILE_WIRE_SP4_H_R_42,
|
||||||
|
TILE_WIRE_SP4_H_R_43,
|
||||||
|
TILE_WIRE_SP4_H_R_44,
|
||||||
|
TILE_WIRE_SP4_H_R_45,
|
||||||
|
TILE_WIRE_SP4_H_R_46,
|
||||||
|
TILE_WIRE_SP4_H_R_47,
|
||||||
|
|
||||||
|
TILE_WIRE_SP12_V_B_0,
|
||||||
|
TILE_WIRE_SP12_V_B_1,
|
||||||
|
TILE_WIRE_SP12_V_B_2,
|
||||||
|
TILE_WIRE_SP12_V_B_3,
|
||||||
|
TILE_WIRE_SP12_V_B_4,
|
||||||
|
TILE_WIRE_SP12_V_B_5,
|
||||||
|
TILE_WIRE_SP12_V_B_6,
|
||||||
|
TILE_WIRE_SP12_V_B_7,
|
||||||
|
TILE_WIRE_SP12_V_B_8,
|
||||||
|
TILE_WIRE_SP12_V_B_9,
|
||||||
|
TILE_WIRE_SP12_V_B_10,
|
||||||
|
TILE_WIRE_SP12_V_B_11,
|
||||||
|
TILE_WIRE_SP12_V_B_12,
|
||||||
|
TILE_WIRE_SP12_V_B_13,
|
||||||
|
TILE_WIRE_SP12_V_B_14,
|
||||||
|
TILE_WIRE_SP12_V_B_15,
|
||||||
|
TILE_WIRE_SP12_V_B_16,
|
||||||
|
TILE_WIRE_SP12_V_B_17,
|
||||||
|
TILE_WIRE_SP12_V_B_18,
|
||||||
|
TILE_WIRE_SP12_V_B_19,
|
||||||
|
TILE_WIRE_SP12_V_B_20,
|
||||||
|
TILE_WIRE_SP12_V_B_21,
|
||||||
|
TILE_WIRE_SP12_V_B_22,
|
||||||
|
TILE_WIRE_SP12_V_B_23,
|
||||||
|
|
||||||
|
TILE_WIRE_SP12_V_T_22,
|
||||||
|
TILE_WIRE_SP12_V_T_23,
|
||||||
|
|
||||||
|
TILE_WIRE_SP12_H_R_0,
|
||||||
|
TILE_WIRE_SP12_H_R_1,
|
||||||
|
TILE_WIRE_SP12_H_R_2,
|
||||||
|
TILE_WIRE_SP12_H_R_3,
|
||||||
|
TILE_WIRE_SP12_H_R_4,
|
||||||
|
TILE_WIRE_SP12_H_R_5,
|
||||||
|
TILE_WIRE_SP12_H_R_6,
|
||||||
|
TILE_WIRE_SP12_H_R_7,
|
||||||
|
TILE_WIRE_SP12_H_R_8,
|
||||||
|
TILE_WIRE_SP12_H_R_9,
|
||||||
|
TILE_WIRE_SP12_H_R_10,
|
||||||
|
TILE_WIRE_SP12_H_R_11,
|
||||||
|
TILE_WIRE_SP12_H_R_12,
|
||||||
|
TILE_WIRE_SP12_H_R_13,
|
||||||
|
TILE_WIRE_SP12_H_R_14,
|
||||||
|
TILE_WIRE_SP12_H_R_15,
|
||||||
|
TILE_WIRE_SP12_H_R_16,
|
||||||
|
TILE_WIRE_SP12_H_R_17,
|
||||||
|
TILE_WIRE_SP12_H_R_18,
|
||||||
|
TILE_WIRE_SP12_H_R_19,
|
||||||
|
TILE_WIRE_SP12_H_R_20,
|
||||||
|
TILE_WIRE_SP12_H_R_21,
|
||||||
|
TILE_WIRE_SP12_H_R_22,
|
||||||
|
TILE_WIRE_SP12_H_R_23,
|
||||||
|
|
||||||
|
TILE_WIRE_SP12_H_L_22,
|
||||||
|
TILE_WIRE_SP12_H_L_23,
|
||||||
|
|
||||||
|
TILE_WIRE_PLLIN,
|
||||||
|
TILE_WIRE_PLLOUT_A,
|
||||||
|
TILE_WIRE_PLLOUT_B
|
||||||
|
};
|
||||||
|
|
||||||
|
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style);
|
||||||
|
void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, GfxTileWireId dst,
|
||||||
|
GraphicElement::style_t style);
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // GFX_H
|
124
xc7/main.cc
Normal file
124
xc7/main.cc
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef MAIN_EXECUTABLE
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include "command.h"
|
||||||
|
#include "design_utils.h"
|
||||||
|
#include "jsonparse.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "pcf.h"
|
||||||
|
#include "timing.h"
|
||||||
|
#include "xdl.h"
|
||||||
|
|
||||||
|
USING_NEXTPNR_NAMESPACE
|
||||||
|
|
||||||
|
class Xc7CommandHandler : public CommandHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Xc7CommandHandler(int argc, char **argv);
|
||||||
|
virtual ~Xc7CommandHandler(){};
|
||||||
|
std::unique_ptr<Context> createContext() override;
|
||||||
|
void setupArchContext(Context *ctx) override;
|
||||||
|
void validate() override;
|
||||||
|
void customAfterLoad(Context *ctx) override;
|
||||||
|
void customBitstream(Context *ctx) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
po::options_description getArchOptions();
|
||||||
|
};
|
||||||
|
|
||||||
|
Xc7CommandHandler::Xc7CommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {}
|
||||||
|
|
||||||
|
po::options_description Xc7CommandHandler::getArchOptions()
|
||||||
|
{
|
||||||
|
po::options_description specific("Architecture specific options");
|
||||||
|
specific.add_options()("z020", "set device type to xc7z020");
|
||||||
|
specific.add_options()("vx980", "set device type to xc7v980");
|
||||||
|
specific.add_options()("package", po::value<std::string>(), "set device package");
|
||||||
|
specific.add_options()("pcf", po::value<std::string>(), "PCF constraints file to ingest");
|
||||||
|
specific.add_options()("xdl", po::value<std::string>(), "XDL file to write");
|
||||||
|
// specific.add_options()("tmfuzz", "run path delay estimate fuzzer");
|
||||||
|
return specific;
|
||||||
|
}
|
||||||
|
void Xc7CommandHandler::validate()
|
||||||
|
{
|
||||||
|
conflicting_options(vm, "read", "json");
|
||||||
|
// if ((vm.count("lp384") + vm.count("lp1k") + vm.count("lp8k") + vm.count("hx1k") + vm.count("hx8k") +
|
||||||
|
// vm.count("up5k")) > 1)
|
||||||
|
// log_error("Only one device type can be set\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Xc7CommandHandler::customAfterLoad(Context *ctx)
|
||||||
|
{
|
||||||
|
if (vm.count("pcf")) {
|
||||||
|
std::string filename = vm["pcf"].as<std::string>();
|
||||||
|
std::ifstream pcf(filename);
|
||||||
|
if (!apply_pcf(ctx, filename, pcf))
|
||||||
|
log_error("Loading PCF failed.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void Xc7CommandHandler::customBitstream(Context *ctx)
|
||||||
|
{
|
||||||
|
if (vm.count("xdl")) {
|
||||||
|
std::string filename = vm["xdl"].as<std::string>();
|
||||||
|
std::ofstream f(filename);
|
||||||
|
write_xdl(ctx, f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Xc7CommandHandler::setupArchContext(Context *ctx)
|
||||||
|
{
|
||||||
|
// if (vm.count("tmfuzz"))
|
||||||
|
// ice40DelayFuzzerMain(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Context> Xc7CommandHandler::createContext()
|
||||||
|
{
|
||||||
|
if (vm.count("z020")) {
|
||||||
|
chipArgs.type = ArchArgs::Z020;
|
||||||
|
chipArgs.package = "clg400";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("vx980")) {
|
||||||
|
chipArgs.type = ArchArgs::VX980;
|
||||||
|
chipArgs.package = "ffg1926";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (chipArgs.type == ArchArgs::NONE) {
|
||||||
|
chipArgs.type = ArchArgs::Z020;
|
||||||
|
chipArgs.package = "clg400";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vm.count("package"))
|
||||||
|
chipArgs.package = vm["package"].as<std::string>();
|
||||||
|
|
||||||
|
return std::unique_ptr<Context>(new Context(chipArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
Xc7CommandHandler handler(argc, argv);
|
||||||
|
return handler.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
728
xc7/pack.cc
Normal file
728
xc7/pack.cc
Normal file
@ -0,0 +1,728 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include "cells.h"
|
||||||
|
#include "chains.h"
|
||||||
|
#include "design_utils.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// Pack LUTs and LUT-FF pairs
|
||||||
|
static void pack_lut_lutffs(Context *ctx)
|
||||||
|
{
|
||||||
|
log_info("Packing LUT-FFs..\n");
|
||||||
|
|
||||||
|
std::unordered_set<IdString> packed_cells;
|
||||||
|
std::vector<std::unique_ptr<CellInfo>> new_cells;
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (ctx->verbose)
|
||||||
|
log_info("cell '%s' is of type '%s'\n", ci->name.c_str(ctx), ci->type.c_str(ctx));
|
||||||
|
if (is_lut(ctx, ci)) {
|
||||||
|
std::unique_ptr<CellInfo> packed = create_xc7_cell(ctx, ctx->id("XC7_LC"), ci->name.str(ctx) + "_LC");
|
||||||
|
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(packed->attrs, packed->attrs.begin()));
|
||||||
|
packed_cells.insert(ci->name);
|
||||||
|
if (ctx->verbose)
|
||||||
|
log_info("packed cell %s into %s\n", ci->name.c_str(ctx), packed->name.c_str(ctx));
|
||||||
|
// See if we can pack into a DFF
|
||||||
|
// TODO: LUT cascade
|
||||||
|
NetInfo *o = ci->ports.at(ctx->id("O")).net;
|
||||||
|
CellInfo *dff = net_only_drives(ctx, o, is_ff, ctx->id("D"), true);
|
||||||
|
auto lut_bel = ci->attrs.find(ctx->id("BEL"));
|
||||||
|
bool packed_dff = false;
|
||||||
|
if (dff) {
|
||||||
|
if (ctx->verbose)
|
||||||
|
log_info("found attached dff %s\n", dff->name.c_str(ctx));
|
||||||
|
auto dff_bel = dff->attrs.find(ctx->id("BEL"));
|
||||||
|
if (lut_bel != ci->attrs.end() && dff_bel != dff->attrs.end() && lut_bel->second != dff_bel->second) {
|
||||||
|
// Locations don't match, can't pack
|
||||||
|
} else {
|
||||||
|
lut_to_lc(ctx, ci, packed.get(), false);
|
||||||
|
dff_to_lc(ctx, dff, packed.get(), false);
|
||||||
|
ctx->nets.erase(o->name);
|
||||||
|
if (dff_bel != dff->attrs.end())
|
||||||
|
packed->attrs[ctx->id("BEL")] = dff_bel->second;
|
||||||
|
packed_cells.insert(dff->name);
|
||||||
|
if (ctx->verbose)
|
||||||
|
log_info("packed cell %s into %s\n", dff->name.c_str(ctx), packed->name.c_str(ctx));
|
||||||
|
packed_dff = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!packed_dff) {
|
||||||
|
lut_to_lc(ctx, ci, packed.get(), true);
|
||||||
|
}
|
||||||
|
new_cells.push_back(std::move(packed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto pcell : packed_cells) {
|
||||||
|
ctx->cells.erase(pcell);
|
||||||
|
}
|
||||||
|
for (auto &ncell : new_cells) {
|
||||||
|
ctx->cells[ncell->name] = std::move(ncell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pack FFs not packed as LUTFFs
|
||||||
|
static void pack_nonlut_ffs(Context *ctx)
|
||||||
|
{
|
||||||
|
log_info("Packing non-LUT FFs..\n");
|
||||||
|
|
||||||
|
std::unordered_set<IdString> packed_cells;
|
||||||
|
std::vector<std::unique_ptr<CellInfo>> new_cells;
|
||||||
|
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (is_ff(ctx, ci)) {
|
||||||
|
std::unique_ptr<CellInfo> packed = create_xc7_cell(ctx, ctx->id("XC7_LC"), ci->name.str(ctx) + "_DFFLC");
|
||||||
|
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(packed->attrs, packed->attrs.begin()));
|
||||||
|
if (ctx->verbose)
|
||||||
|
log_info("packed cell %s into %s\n", ci->name.c_str(ctx), packed->name.c_str(ctx));
|
||||||
|
packed_cells.insert(ci->name);
|
||||||
|
dff_to_lc(ctx, ci, packed.get(), true);
|
||||||
|
new_cells.push_back(std::move(packed));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto pcell : packed_cells) {
|
||||||
|
ctx->cells.erase(pcell);
|
||||||
|
}
|
||||||
|
for (auto &ncell : new_cells) {
|
||||||
|
ctx->cells[ncell->name] = std::move(ncell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool net_is_constant(const Context *ctx, NetInfo *net, bool &value)
|
||||||
|
{
|
||||||
|
if (net == nullptr)
|
||||||
|
return false;
|
||||||
|
if (net->name == ctx->id("$PACKER_GND_NET") || net->name == ctx->id("$PACKER_VCC_NET")) {
|
||||||
|
value = (net->name == ctx->id("$PACKER_VCC_NET"));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pack carry logic
|
||||||
|
static void pack_carries(Context *ctx)
|
||||||
|
{
|
||||||
|
//log_info("Packing carries..\n");
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// "Pack" RAMs
|
||||||
|
static void pack_ram(Context *ctx)
|
||||||
|
{
|
||||||
|
//log_info("Packing RAMs..\n");
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge a net into a constant net
|
||||||
|
static void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constnet, bool constval)
|
||||||
|
{
|
||||||
|
orig->driver.cell = nullptr;
|
||||||
|
for (auto user : orig->users) {
|
||||||
|
if (user.cell != nullptr) {
|
||||||
|
CellInfo *uc = user.cell;
|
||||||
|
if (ctx->verbose)
|
||||||
|
log_info("%s user %s\n", orig->name.c_str(ctx), uc->name.c_str(ctx));
|
||||||
|
if ((is_lut(ctx, uc) || is_lc(ctx, uc) || is_carry(ctx, uc)) && (user.port.str(ctx).at(0) == 'I') &&
|
||||||
|
!constval) {
|
||||||
|
uc->ports[user.port].net = nullptr;
|
||||||
|
} else {
|
||||||
|
uc->ports[user.port].net = constnet;
|
||||||
|
constnet->users.push_back(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
orig->users.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pack constants (simple implementation)
|
||||||
|
static void pack_constants(Context *ctx)
|
||||||
|
{
|
||||||
|
log_info("Packing constants..\n");
|
||||||
|
|
||||||
|
std::unique_ptr<CellInfo> gnd_cell = create_xc7_cell(ctx, ctx->id("XC7_LC"), "$PACKER_GND");
|
||||||
|
gnd_cell->params[ctx->id("INIT")] = "0";
|
||||||
|
std::unique_ptr<NetInfo> gnd_net = std::unique_ptr<NetInfo>(new NetInfo);
|
||||||
|
gnd_net->name = ctx->id("$PACKER_GND_NET");
|
||||||
|
gnd_net->driver.cell = gnd_cell.get();
|
||||||
|
gnd_net->driver.port = id_O;
|
||||||
|
gnd_cell->ports.at(id_O).net = gnd_net.get();
|
||||||
|
|
||||||
|
std::unique_ptr<CellInfo> vcc_cell = create_xc7_cell(ctx, ctx->id("XC7_LC"), "$PACKER_VCC");
|
||||||
|
vcc_cell->params[ctx->id("INIT")] = "1";
|
||||||
|
std::unique_ptr<NetInfo> vcc_net = std::unique_ptr<NetInfo>(new NetInfo);
|
||||||
|
vcc_net->name = ctx->id("$PACKER_VCC_NET");
|
||||||
|
vcc_net->driver.cell = vcc_cell.get();
|
||||||
|
vcc_net->driver.port = id_O;
|
||||||
|
vcc_cell->ports.at(id_O).net = vcc_net.get();
|
||||||
|
|
||||||
|
std::vector<IdString> dead_nets;
|
||||||
|
|
||||||
|
bool gnd_used = false;
|
||||||
|
|
||||||
|
for (auto net : sorted(ctx->nets)) {
|
||||||
|
NetInfo *ni = net.second;
|
||||||
|
if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("GND")) {
|
||||||
|
IdString drv_cell = ni->driver.cell->name;
|
||||||
|
set_net_constant(ctx, ni, gnd_net.get(), false);
|
||||||
|
gnd_used = true;
|
||||||
|
dead_nets.push_back(net.first);
|
||||||
|
ctx->cells.erase(drv_cell);
|
||||||
|
} else if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("VCC")) {
|
||||||
|
IdString drv_cell = ni->driver.cell->name;
|
||||||
|
set_net_constant(ctx, ni, vcc_net.get(), true);
|
||||||
|
dead_nets.push_back(net.first);
|
||||||
|
ctx->cells.erase(drv_cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gnd_used) {
|
||||||
|
ctx->cells[gnd_cell->name] = std::move(gnd_cell);
|
||||||
|
ctx->nets[gnd_net->name] = std::move(gnd_net);
|
||||||
|
}
|
||||||
|
// Vcc cell always inserted for now, as it may be needed during carry legalisation (TODO: trim later if actually
|
||||||
|
// never used?)
|
||||||
|
ctx->cells[vcc_cell->name] = std::move(vcc_cell);
|
||||||
|
ctx->nets[vcc_net->name] = std::move(vcc_net);
|
||||||
|
|
||||||
|
for (auto dn : dead_nets) {
|
||||||
|
ctx->nets.erase(dn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_nextpnr_iob(Context *ctx, CellInfo *cell)
|
||||||
|
{
|
||||||
|
return cell->type == ctx->id("$nextpnr_ibuf") || cell->type == ctx->id("$nextpnr_obuf") ||
|
||||||
|
cell->type == ctx->id("$nextpnr_iobuf");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pack IO buffers
|
||||||
|
static void pack_io(Context *ctx)
|
||||||
|
{
|
||||||
|
std::unordered_set<IdString> packed_cells;
|
||||||
|
std::vector<std::unique_ptr<CellInfo>> new_cells;
|
||||||
|
log_info("Packing IOs..\n");
|
||||||
|
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (is_nextpnr_iob(ctx, ci)) {
|
||||||
|
CellInfo *sb = nullptr;
|
||||||
|
if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) {
|
||||||
|
sb = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_sb_io, ctx->id("PACKAGE_PIN"), true, ci);
|
||||||
|
|
||||||
|
} else if (ci->type == ctx->id("$nextpnr_obuf")) {
|
||||||
|
sb = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_sb_io, ctx->id("PACKAGE_PIN"), true, ci);
|
||||||
|
}
|
||||||
|
if (sb != nullptr) {
|
||||||
|
// Trivial case, IOBUF used. Just destroy the net and the
|
||||||
|
// iobuf
|
||||||
|
log_info("%s feeds IOBUF %s, removing %s %s.\n", ci->name.c_str(ctx), sb->name.c_str(ctx),
|
||||||
|
ci->type.c_str(ctx), ci->name.c_str(ctx));
|
||||||
|
NetInfo *net = sb->ports.at(ctx->id("PACKAGE_PIN")).net;
|
||||||
|
if (net != nullptr) {
|
||||||
|
ctx->nets.erase(net->name);
|
||||||
|
sb->ports.at(ctx->id("PACKAGE_PIN")).net = nullptr;
|
||||||
|
}
|
||||||
|
if (ci->type == ctx->id("$nextpnr_iobuf")) {
|
||||||
|
NetInfo *net2 = ci->ports.at(ctx->id("I")).net;
|
||||||
|
if (net2 != nullptr) {
|
||||||
|
ctx->nets.erase(net2->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Create a IOBUF buffer
|
||||||
|
std::unique_ptr<CellInfo> xc7_cell = create_xc7_cell(ctx, ctx->id("IOBUF"), ci->name.str(ctx));
|
||||||
|
nxio_to_sb(ctx, ci, xc7_cell.get());
|
||||||
|
new_cells.push_back(std::move(xc7_cell));
|
||||||
|
sb = new_cells.back().get();
|
||||||
|
}
|
||||||
|
packed_cells.insert(ci->name);
|
||||||
|
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(sb->attrs, sb->attrs.begin()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto pcell : packed_cells) {
|
||||||
|
ctx->cells.erase(pcell);
|
||||||
|
}
|
||||||
|
for (auto &ncell : new_cells) {
|
||||||
|
ctx->cells[ncell->name] = std::move(ncell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if a port counts as "logic" for global promotion
|
||||||
|
static bool is_logic_port(BaseCtx *ctx, const PortRef &port)
|
||||||
|
{
|
||||||
|
if (is_clock_port(ctx, port) || is_reset_port(ctx, port) || is_enable_port(ctx, port))
|
||||||
|
return false;
|
||||||
|
return !is_sb_io(ctx, port.cell) && port.cell->type != id_BUFGCTRL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen, bool is_logic)
|
||||||
|
{
|
||||||
|
std::string glb_name = net->name.str(ctx) + std::string("_$glb_") + (is_reset ? "sr" : (is_cen ? "ce" : "clk"));
|
||||||
|
std::unique_ptr<CellInfo> gb = create_xc7_cell(ctx, id_BUFGCTRL, "$bufg_" + glb_name);
|
||||||
|
gb->ports[ctx->id("I0")].net = net;
|
||||||
|
PortRef pr;
|
||||||
|
pr.cell = gb.get();
|
||||||
|
pr.port = ctx->id("I0");
|
||||||
|
net->users.push_back(pr);
|
||||||
|
|
||||||
|
pr.cell = gb.get();
|
||||||
|
pr.port = ctx->id("O");
|
||||||
|
std::unique_ptr<NetInfo> glbnet = std::unique_ptr<NetInfo>(new NetInfo());
|
||||||
|
glbnet->name = ctx->id(glb_name);
|
||||||
|
glbnet->driver = pr;
|
||||||
|
gb->ports[ctx->id("O")].net = glbnet.get();
|
||||||
|
std::vector<PortRef> keep_users;
|
||||||
|
for (auto user : net->users) {
|
||||||
|
if (is_clock_port(ctx, user) || (is_reset && is_reset_port(ctx, user)) ||
|
||||||
|
(is_cen && is_enable_port(ctx, user)) || (is_logic && is_logic_port(ctx, user))) {
|
||||||
|
user.cell->ports[user.port].net = glbnet.get();
|
||||||
|
glbnet->users.push_back(user);
|
||||||
|
} else {
|
||||||
|
keep_users.push_back(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
net->users = keep_users;
|
||||||
|
ctx->nets[glbnet->name] = std::move(glbnet);
|
||||||
|
ctx->cells[gb->name] = std::move(gb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple global promoter (clock only)
|
||||||
|
static void promote_globals(Context *ctx)
|
||||||
|
{
|
||||||
|
log_info("Promoting globals..\n");
|
||||||
|
const int logic_fanout_thresh = 15;
|
||||||
|
const int enable_fanout_thresh = 5;
|
||||||
|
std::map<IdString, int> clock_count, reset_count, cen_count, logic_count;
|
||||||
|
for (auto net : sorted(ctx->nets)) {
|
||||||
|
NetInfo *ni = net.second;
|
||||||
|
if (ni->driver.cell != nullptr && !ctx->isGlobalNet(ni)) {
|
||||||
|
clock_count[net.first] = 0;
|
||||||
|
reset_count[net.first] = 0;
|
||||||
|
cen_count[net.first] = 0;
|
||||||
|
|
||||||
|
for (auto user : ni->users) {
|
||||||
|
if (is_clock_port(ctx, user))
|
||||||
|
clock_count[net.first]++;
|
||||||
|
if (is_reset_port(ctx, user))
|
||||||
|
reset_count[net.first]++;
|
||||||
|
if (is_enable_port(ctx, user))
|
||||||
|
cen_count[net.first]++;
|
||||||
|
if (is_logic_port(ctx, user))
|
||||||
|
logic_count[net.first]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int prom_globals = 0, prom_resets = 0, prom_cens = 0, prom_logics = 0;
|
||||||
|
int gbs_available = 8;
|
||||||
|
for (auto &cell : ctx->cells)
|
||||||
|
if (is_gbuf(ctx, cell.second.get()))
|
||||||
|
--gbs_available;
|
||||||
|
while (prom_globals < gbs_available) {
|
||||||
|
auto global_clock = std::max_element(clock_count.begin(), clock_count.end(),
|
||||||
|
[](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
|
||||||
|
return a.second < b.second;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto global_reset = std::max_element(reset_count.begin(), reset_count.end(),
|
||||||
|
[](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
|
||||||
|
return a.second < b.second;
|
||||||
|
});
|
||||||
|
auto global_cen = std::max_element(cen_count.begin(), cen_count.end(),
|
||||||
|
[](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
|
||||||
|
return a.second < b.second;
|
||||||
|
});
|
||||||
|
auto global_logic = std::max_element(logic_count.begin(), logic_count.end(),
|
||||||
|
[](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
|
||||||
|
return a.second < b.second;
|
||||||
|
});
|
||||||
|
if (global_clock->second == 0 && prom_logics < 4 && global_logic->second > logic_fanout_thresh &&
|
||||||
|
(global_logic->second > global_cen->second || prom_cens >= 4) &&
|
||||||
|
(global_logic->second > global_reset->second || prom_resets >= 4)) {
|
||||||
|
NetInfo *logicnet = ctx->nets[global_logic->first].get();
|
||||||
|
insert_global(ctx, logicnet, false, false, true);
|
||||||
|
++prom_globals;
|
||||||
|
++prom_logics;
|
||||||
|
clock_count.erase(logicnet->name);
|
||||||
|
reset_count.erase(logicnet->name);
|
||||||
|
cen_count.erase(logicnet->name);
|
||||||
|
logic_count.erase(logicnet->name);
|
||||||
|
} else if (global_reset->second > global_clock->second && prom_resets < 4) {
|
||||||
|
NetInfo *rstnet = ctx->nets[global_reset->first].get();
|
||||||
|
insert_global(ctx, rstnet, true, false, false);
|
||||||
|
++prom_globals;
|
||||||
|
++prom_resets;
|
||||||
|
clock_count.erase(rstnet->name);
|
||||||
|
reset_count.erase(rstnet->name);
|
||||||
|
cen_count.erase(rstnet->name);
|
||||||
|
logic_count.erase(rstnet->name);
|
||||||
|
} else if (global_cen->second > global_clock->second && prom_cens < 4 &&
|
||||||
|
global_cen->second > enable_fanout_thresh) {
|
||||||
|
NetInfo *cennet = ctx->nets[global_cen->first].get();
|
||||||
|
insert_global(ctx, cennet, false, true, false);
|
||||||
|
++prom_globals;
|
||||||
|
++prom_cens;
|
||||||
|
clock_count.erase(cennet->name);
|
||||||
|
reset_count.erase(cennet->name);
|
||||||
|
cen_count.erase(cennet->name);
|
||||||
|
logic_count.erase(cennet->name);
|
||||||
|
} else if (global_clock->second != 0) {
|
||||||
|
NetInfo *clknet = ctx->nets[global_clock->first].get();
|
||||||
|
insert_global(ctx, clknet, false, false, false);
|
||||||
|
++prom_globals;
|
||||||
|
clock_count.erase(clknet->name);
|
||||||
|
reset_count.erase(clknet->name);
|
||||||
|
cen_count.erase(clknet->name);
|
||||||
|
logic_count.erase(clknet->name);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// spliceLUT adds a pass-through LUT LC between the given cell's output port
|
||||||
|
// and either all users or only non_LUT users.
|
||||||
|
static std::unique_ptr<CellInfo> spliceLUT(Context *ctx, CellInfo *ci, IdString portId, bool onlyNonLUTs)
|
||||||
|
{
|
||||||
|
auto port = ci->ports[portId];
|
||||||
|
|
||||||
|
NPNR_ASSERT(port.net != nullptr);
|
||||||
|
|
||||||
|
// Create pass-through LUT.
|
||||||
|
std::unique_ptr<CellInfo> pt =
|
||||||
|
create_xc7_cell(ctx, ctx->id("XC7_LC"), ci->name.str(ctx) + "$nextpnr_" + portId.str(ctx) + "_lut_through");
|
||||||
|
pt->params[ctx->id("INIT")] = "65280"; // output is always I3
|
||||||
|
|
||||||
|
// Create LUT output net.
|
||||||
|
std::unique_ptr<NetInfo> out_net = std::unique_ptr<NetInfo>(new NetInfo);
|
||||||
|
out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_" + portId.str(ctx) + "_lut_through_net");
|
||||||
|
out_net->driver.cell = pt.get();
|
||||||
|
out_net->driver.port = ctx->id("O");
|
||||||
|
pt->ports.at(ctx->id("O")).net = out_net.get();
|
||||||
|
|
||||||
|
// New users of the original cell's port
|
||||||
|
std::vector<PortRef> new_users;
|
||||||
|
for (const auto &user : port.net->users) {
|
||||||
|
if (onlyNonLUTs && user.cell->type == ctx->id("XC7_LC")) {
|
||||||
|
new_users.push_back(user);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Rewrite pointer into net in user.
|
||||||
|
user.cell->ports[user.port].net = out_net.get();
|
||||||
|
// Add user to net.
|
||||||
|
PortRef pr;
|
||||||
|
pr.cell = user.cell;
|
||||||
|
pr.port = user.port;
|
||||||
|
out_net->users.push_back(pr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add LUT to new users.
|
||||||
|
PortRef pr;
|
||||||
|
pr.cell = pt.get();
|
||||||
|
pr.port = ctx->id("I3");
|
||||||
|
new_users.push_back(pr);
|
||||||
|
pt->ports.at(ctx->id("I3")).net = port.net;
|
||||||
|
|
||||||
|
// Replace users of the original net.
|
||||||
|
port.net->users = new_users;
|
||||||
|
|
||||||
|
ctx->nets[out_net->name] = std::move(out_net);
|
||||||
|
return pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pack special functions
|
||||||
|
static void pack_special(Context *ctx)
|
||||||
|
{
|
||||||
|
log_info("Packing special functions..\n");
|
||||||
|
|
||||||
|
std::unordered_set<IdString> packed_cells;
|
||||||
|
std::vector<std::unique_ptr<CellInfo>> new_cells;
|
||||||
|
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
if (ci->type == id_BUFGCTRL) {
|
||||||
|
ci->params.emplace(ctx->id("PRESELECT_I0"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CE0INV"), "CE0");
|
||||||
|
ci->params.emplace(ctx->id("S0INV"), "S0");
|
||||||
|
ci->params.emplace(ctx->id("IGNORE0INV"), "IGNORE0");
|
||||||
|
ci->params.emplace(ctx->id("CE1INV"), "CE1");
|
||||||
|
ci->params.emplace(ctx->id("S1INV"), "S1");
|
||||||
|
ci->params.emplace(ctx->id("IGNORE1INV"), "IGNORE1");
|
||||||
|
} else if (ci->type == id_MMCME2_ADV) {
|
||||||
|
ci->params.emplace(ctx->id("BANDWIDTH"), "OPTIMIZED");
|
||||||
|
ci->params.emplace(ctx->id("CLKBURST_ENABLE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKBURST_REPEAT"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBIN_EDGE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBIN_NOCOUNT"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_EDGE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_EN"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_FRAC_EN"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_FRAC_WF_FALL"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_FRAC_WF_RISE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_NOCOUNT"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_USE_FINE_PS"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKINSELINV"), "CLKINSEL");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_EDGE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_EN"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_FRAC_EN"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_FRAC_WF_FALL"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_FRAC_WF_RISE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_NOCOUNT"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_USE_FINE_PS"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_EDGE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_EN"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_NOCOUNT"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_USE_FINE_PS"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_EDGE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_EN"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_NOCOUNT"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_USE_FINE_PS"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_EDGE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_EN"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_NOCOUNT"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_USE_FINE_PS"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_CASCADE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_EDGE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_EN"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_NOCOUNT"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_USE_FINE_PS"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_EDGE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_EN"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_NOCOUNT"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_USE_FINE_PS"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_EDGE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_EN"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_NOCOUNT"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_USE_FINE_PS"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("COMPENSATION"), "INTERNAL");
|
||||||
|
ci->params.emplace(ctx->id("DIRECT_PATH_CNTRL"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("DIVCLK_EDGE"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("DIVCLK_NOCOUNT"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("EN_VCO_DIV1"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("EN_VCO_DIV6"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("GTS_WAIT"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("HVLF_CNT_TEST_EN"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("INTERP_TEST"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("IN_DLY_EN"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("LF_LOW_SEL"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("MMCM_EN"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("PERF0_USE_CLK"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("PERF1_USE_CLK"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("PERF2_USE_CLK"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("PERF3_USE_CLK"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("PSENINV"), "PSEN");
|
||||||
|
ci->params.emplace(ctx->id("PSINCDECINV"), "PSINCDEC");
|
||||||
|
ci->params.emplace(ctx->id("PWRDWNINV"), "PWRDWN");
|
||||||
|
ci->params.emplace(ctx->id("RSTINV"), "RST");
|
||||||
|
ci->params.emplace(ctx->id("SEL_HV_NMOS"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("SEL_LV_NMOS"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("SEL_SLIPD"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("SS_EN"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("SS_MODE"), "CENTER_HIGH");
|
||||||
|
ci->params.emplace(ctx->id("STARTUP_WAIT"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("SUP_SEL_AREG"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("SUP_SEL_DREG"), "FALSE");
|
||||||
|
ci->params.emplace(ctx->id("TMUX_MUX_SEL"), "00");
|
||||||
|
ci->params.emplace(ctx->id("VLF_HIGH_DIS_B"), "TRUE");
|
||||||
|
ci->params.emplace(ctx->id("VLF_HIGH_PWDN_B"), "TRUE");
|
||||||
|
//ci->params.emplace(ctx->id("MMCME2_ADV:mmcm_adv_inst:");
|
||||||
|
ci->params.emplace(ctx->id("ANALOG_MISC"), "0000");
|
||||||
|
ci->params.emplace(ctx->id("AVDD_COMP_SET"), "011");
|
||||||
|
ci->params.emplace(ctx->id("AVDD_VBG_PD"), "110");
|
||||||
|
ci->params.emplace(ctx->id("AVDD_VBG_SEL"), "1001");
|
||||||
|
ci->params.emplace(ctx->id("CLKBURST_CNT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBIN_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBIN_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBIN_MULT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_DT"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_FRAC"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_MULT_F"), "40.5");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_MX"), "00");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_PHASE"), "0.0");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_PM_FALL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKFBOUT_PM_RISE"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKFB_MUX_SEL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKIN1_MUX_SEL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKIN1_PERIOD"), "8");
|
||||||
|
ci->params.emplace(ctx->id("CLKIN2_MUX_SEL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKIN2_PERIOD"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_DIVIDE_F"), "16.875");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_DT"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_DUTY_CYCLE"), "0.5");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_FRAC"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_MX"), "00");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_PHASE"), "0.0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_PM_FALL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT0_PM_RISE"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_DIVIDE"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_DT"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_DUTY_CYCLE"), "0.5");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_MX"), "00");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_PHASE"), "0.0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT1_PM"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_DIVIDE"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_DT"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_DUTY_CYCLE"), "0.5");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_MX"), "00");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_PHASE"), "0.0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT2_PM"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_DIVIDE"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_DT"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_DUTY_CYCLE"), "0.5");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_MX"), "00");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_PHASE"), "0.0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT3_PM"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_DIVIDE"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_DT"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_DUTY_CYCLE"), "0.5");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_MX"), "00");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_PHASE"), "0.0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT4_PM"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_DIVIDE"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_DT"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_DUTY_CYCLE"), "0.5");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_MX"), "00");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_PHASE"), "0.0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT5_PM"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_DIVIDE"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_DT"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_DUTY_CYCLE"), "0.5");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_MX"), "00");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_PHASE"), "0.0");
|
||||||
|
ci->params.emplace(ctx->id("CLKOUT6_PM"), "000");
|
||||||
|
ci->params.emplace(ctx->id("CONTROL_0"), "1111001101111100");
|
||||||
|
ci->params.emplace(ctx->id("CONTROL_1"), "0111110101001101");
|
||||||
|
ci->params.emplace(ctx->id("CONTROL_2"), "0101000001000010");
|
||||||
|
ci->params.emplace(ctx->id("CONTROL_3"), "1110101111001000");
|
||||||
|
ci->params.emplace(ctx->id("CONTROL_4"), "1101010011011111");
|
||||||
|
ci->params.emplace(ctx->id("CONTROL_5"), "1010110111111011");
|
||||||
|
ci->params.emplace(ctx->id("CONTROL_6"), "1011001011000011");
|
||||||
|
ci->params.emplace(ctx->id("CONTROL_7"), "0100110000101110");
|
||||||
|
ci->params.emplace(ctx->id("CP"), "0000");
|
||||||
|
ci->params.emplace(ctx->id("CP_BIAS_TRIP_SET"), "0");
|
||||||
|
ci->params.emplace(ctx->id("CP_RES"), "01");
|
||||||
|
ci->params.emplace(ctx->id("DIVCLK_DIVIDE"), "5");
|
||||||
|
ci->params.emplace(ctx->id("DIVCLK_HT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("DIVCLK_LT"), "1");
|
||||||
|
ci->params.emplace(ctx->id("DVDD_COMP_SET"), "011");
|
||||||
|
ci->params.emplace(ctx->id("DVDD_VBG_PD"), "110");
|
||||||
|
ci->params.emplace(ctx->id("DVDD_VBG_SEL"), "1001");
|
||||||
|
ci->params.emplace(ctx->id("EN_CURR_SINK"), "11");
|
||||||
|
ci->params.emplace(ctx->id("FINE_PS_FRAC"), "0");
|
||||||
|
ci->params.emplace(ctx->id("FREQ_BB_USE_CLK0"), "0");
|
||||||
|
ci->params.emplace(ctx->id("FREQ_BB_USE_CLK1"), "0");
|
||||||
|
ci->params.emplace(ctx->id("FREQ_BB_USE_CLK2"), "0");
|
||||||
|
ci->params.emplace(ctx->id("FREQ_BB_USE_CLK3"), "0");
|
||||||
|
ci->params.emplace(ctx->id("FREQ_COMP"), "01");
|
||||||
|
ci->params.emplace(ctx->id("HROW_DLY_SET"), "0");
|
||||||
|
ci->params.emplace(ctx->id("HVLF_CNT_TEST"), "0");
|
||||||
|
ci->params.emplace(ctx->id("INTERP_EN"), "00010000");
|
||||||
|
ci->params.emplace(ctx->id("IN_DLY_MX_CVDD"), "011000");
|
||||||
|
ci->params.emplace(ctx->id("IN_DLY_MX_DVDD"), "000001");
|
||||||
|
ci->params.emplace(ctx->id("IN_DLY_SET"), "38");
|
||||||
|
ci->params.emplace(ctx->id("LFHF"), "11");
|
||||||
|
ci->params.emplace(ctx->id("LF_NEN"), "10");
|
||||||
|
ci->params.emplace(ctx->id("LF_PEN"), "00");
|
||||||
|
ci->params.emplace(ctx->id("LOCK_CNT"), "128");
|
||||||
|
ci->params.emplace(ctx->id("LOCK_FB_DLY"), "3");
|
||||||
|
ci->params.emplace(ctx->id("LOCK_REF_DLY"), "3");
|
||||||
|
ci->params.emplace(ctx->id("LOCK_SAT_HIGH"), "160");
|
||||||
|
ci->params.emplace(ctx->id("MAN_LF"), "000");
|
||||||
|
ci->params.emplace(ctx->id("MVDD_SEL"), "11");
|
||||||
|
ci->params.emplace(ctx->id("PERF0_MUX_SEL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("PERF1_MUX_SEL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("PERF2_MUX_SEL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("PERF3_MUX_SEL"), "000");
|
||||||
|
ci->params.emplace(ctx->id("PFD"), "0100001");
|
||||||
|
ci->params.emplace(ctx->id("REF_JITTER1"), "0.01");
|
||||||
|
ci->params.emplace(ctx->id("REF_JITTER2"), "0.01");
|
||||||
|
ci->params.emplace(ctx->id("RES"), "0000");
|
||||||
|
ci->params.emplace(ctx->id("SKEW_FLOP_INV"), "0000");
|
||||||
|
ci->params.emplace(ctx->id("SPARE_ANALOG"), "00000");
|
||||||
|
ci->params.emplace(ctx->id("SPARE_DIGITAL"), "00000");
|
||||||
|
ci->params.emplace(ctx->id("SS_MOD_PERIOD"), "10000");
|
||||||
|
ci->params.emplace(ctx->id("SS_STEPS"), "011");
|
||||||
|
ci->params.emplace(ctx->id("SS_STEPS_INIT"), "010");
|
||||||
|
ci->params.emplace(ctx->id("SYNTH_CLK_DIV"), "11");
|
||||||
|
ci->params.emplace(ctx->id("UNLOCK_CNT"), "64");
|
||||||
|
ci->params.emplace(ctx->id("VREF_START"), "01");
|
||||||
|
|
||||||
|
ci->params[ctx->id("COMPENSATION")] = "INTERNAL";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto pcell : packed_cells) {
|
||||||
|
ctx->cells.erase(pcell);
|
||||||
|
}
|
||||||
|
for (auto &ncell : new_cells) {
|
||||||
|
ctx->cells[ncell->name] = std::move(ncell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main pack function
|
||||||
|
bool Arch::pack()
|
||||||
|
{
|
||||||
|
Context *ctx = getCtx();
|
||||||
|
try {
|
||||||
|
log_break();
|
||||||
|
pack_constants(ctx);
|
||||||
|
// TODO
|
||||||
|
// promote_globals(ctx);
|
||||||
|
pack_io(ctx);
|
||||||
|
pack_lut_lutffs(ctx);
|
||||||
|
pack_nonlut_ffs(ctx);
|
||||||
|
pack_carries(ctx);
|
||||||
|
pack_ram(ctx);
|
||||||
|
pack_special(ctx);
|
||||||
|
ctx->assignArchInfo();
|
||||||
|
constrain_chains(ctx);
|
||||||
|
ctx->assignArchInfo();
|
||||||
|
log_info("Checksum: 0x%08x\n", ctx->checksum());
|
||||||
|
return true;
|
||||||
|
} catch (log_execution_error_exception) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
84
xc7/pcf.cc
Normal file
84
xc7/pcf.cc
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pcf.h"
|
||||||
|
#include <sstream>
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// Read a w
|
||||||
|
|
||||||
|
// Apply PCF constraints to a pre-packing design
|
||||||
|
bool apply_pcf(Context *ctx, std::string filename, std::istream &in)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if (!in)
|
||||||
|
log_error("failed to open PCF file\n");
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(in, line)) {
|
||||||
|
size_t cstart = line.find("#");
|
||||||
|
if (cstart != std::string::npos)
|
||||||
|
line = line.substr(0, cstart);
|
||||||
|
std::stringstream ss(line);
|
||||||
|
std::vector<std::string> words;
|
||||||
|
std::string tmp;
|
||||||
|
while (ss >> tmp)
|
||||||
|
words.push_back(tmp);
|
||||||
|
if (words.size() == 0)
|
||||||
|
continue;
|
||||||
|
std::string cmd = words.at(0);
|
||||||
|
if (cmd == "COMP") {
|
||||||
|
size_t args_end = 1;
|
||||||
|
while (args_end < words.size() && words.at(args_end).at(0) == '-')
|
||||||
|
args_end++;
|
||||||
|
std::string cell = words.at(args_end);
|
||||||
|
boost::trim_if(cell, boost::is_any_of("\""));
|
||||||
|
std::string pin = words.at(args_end + 4);
|
||||||
|
boost::trim_if(pin, boost::is_any_of("\""));
|
||||||
|
auto fnd_cell = ctx->cells.find(ctx->id(cell));
|
||||||
|
if (fnd_cell == ctx->cells.end()) {
|
||||||
|
log_warning("unmatched pcf constraint %s\n", cell.c_str());
|
||||||
|
} else {
|
||||||
|
BelId pin_bel = ctx->getPackagePinBel(pin);
|
||||||
|
if (pin_bel == BelId())
|
||||||
|
log_error("package does not have a pin named %s\n", pin.c_str());
|
||||||
|
fnd_cell->second->attrs[ctx->id("BEL")] = ctx->getBelName(pin_bel).str(ctx);
|
||||||
|
log_info("constrained '%s' to bel '%s'\n", cell.c_str(),
|
||||||
|
fnd_cell->second->attrs[ctx->id("BEL")].c_str());
|
||||||
|
}
|
||||||
|
} else if (cmd == "NET") {
|
||||||
|
// TODO
|
||||||
|
} else if (cmd == "PIN") {
|
||||||
|
// TODO
|
||||||
|
} else {
|
||||||
|
log_error("unsupported pcf command '%s'\n", cmd.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx->settings.emplace(ctx->id("project/input/pcf"), filename);
|
||||||
|
return true;
|
||||||
|
} catch (log_execution_error_exception) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
34
xc7/pcf.h
Normal file
34
xc7/pcf.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PCF_H
|
||||||
|
#define PCF_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "nextpnr.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
// Apply PCF constraints to a pre-packing design
|
||||||
|
bool apply_pcf(Context *ctx, std::string filename, std::istream &in);
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif // ROUTE_H
|
3
xc7/picorv32.pcf
Normal file
3
xc7/picorv32.pcf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
NET "clki" PERIOD = 8 nS ;
|
||||||
|
PIN "clki_pin" = BEL "clki.PAD" PINNAME PAD;
|
||||||
|
PIN "clki_pin" CLOCK_DEDICATED_ROUTE = FALSE;
|
15
xc7/picorv32.proj
Normal file
15
xc7/picorv32.proj
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"project": {
|
||||||
|
"version": "1",
|
||||||
|
"name": "picorv32",
|
||||||
|
"arch": {
|
||||||
|
"name": "ice40",
|
||||||
|
"type": "hx8k",
|
||||||
|
"package": "ct256"
|
||||||
|
},
|
||||||
|
"input": {
|
||||||
|
"json": "picorv32.json",
|
||||||
|
"pcf": "icebreaker.pcf"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
xc7/picorv32.sh
Executable file
11
xc7/picorv32.sh
Executable file
@ -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
|
55
xc7/picorv32.ys
Normal file
55
xc7/picorv32.ys
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
read_verilog picorv32.v
|
||||||
|
read_verilog picorv32_top.v
|
||||||
|
read_verilog 125MHz_to_60MHz.v
|
||||||
|
|
||||||
|
#synth_xilinx -top picorv32
|
||||||
|
|
||||||
|
#begin:
|
||||||
|
read_verilog -lib +/xilinx/cells_sim.v
|
||||||
|
read_verilog -lib +/xilinx/cells_xtra.v
|
||||||
|
# read_verilog -lib +/xilinx/brams_bb.v
|
||||||
|
# read_verilog -lib +/xilinx/drams_bb.v
|
||||||
|
hierarchy -check -top top
|
||||||
|
|
||||||
|
#flatten: (only if -flatten)
|
||||||
|
proc
|
||||||
|
flatten
|
||||||
|
|
||||||
|
#coarse:
|
||||||
|
synth -run coarse
|
||||||
|
|
||||||
|
#bram:
|
||||||
|
# memory_bram -rules +/xilinx/brams.txt
|
||||||
|
# techmap -map +/xilinx/brams_map.v
|
||||||
|
#
|
||||||
|
#dram:
|
||||||
|
# memory_bram -rules +/xilinx/drams.txt
|
||||||
|
# techmap -map +/xilinx/drams_map.v
|
||||||
|
|
||||||
|
fine:
|
||||||
|
opt -fast -full
|
||||||
|
memory_map
|
||||||
|
dffsr2dff
|
||||||
|
# dff2dffe
|
||||||
|
opt -full
|
||||||
|
techmap -map +/techmap.v #-map +/xilinx/arith_map.v
|
||||||
|
opt -fast
|
||||||
|
|
||||||
|
map_luts:
|
||||||
|
abc -luts 2:2,3,6:5 #,10,20 [-dff]
|
||||||
|
clean
|
||||||
|
|
||||||
|
map_cells:
|
||||||
|
techmap -map +/xilinx/cells_map.v
|
||||||
|
dffinit -ff FDRE Q INIT -ff FDCE Q INIT -ff FDPE Q INIT
|
||||||
|
clean
|
||||||
|
|
||||||
|
check:
|
||||||
|
hierarchy -check
|
||||||
|
stat
|
||||||
|
check -noinit
|
||||||
|
|
||||||
|
#edif: (only if -edif)
|
||||||
|
# write_edif <file-name>
|
||||||
|
|
||||||
|
write_json picorv32.json
|
42
xc7/picorv32_benchmark.py
Executable file
42
xc7/picorv32_benchmark.py
Executable file
@ -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))
|
44
xc7/picorv32_top.v
Normal file
44
xc7/picorv32_top.v
Normal file
@ -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
|
58
xc7/project.cc
Normal file
58
xc7/project.cc
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "project.h"
|
||||||
|
#include <boost/filesystem/convenience.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
#include "log.h"
|
||||||
|
#include "pcf.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path)
|
||||||
|
{
|
||||||
|
root.put("project.arch.package", ctx->archArgs().package);
|
||||||
|
if (ctx->settings.find(ctx->id("project/input/pcf")) != ctx->settings.end()) {
|
||||||
|
std::string fn = ctx->settings[ctx->id("project/input/pcf")];
|
||||||
|
root.put("project.input.pcf", make_relative(fn, path).string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Context> ProjectHandler::createContext(pt::ptree &root)
|
||||||
|
{
|
||||||
|
ArchArgs chipArgs;
|
||||||
|
std::string arch_type = root.get<std::string>("project.arch.type");
|
||||||
|
if (arch_type == "z020") {
|
||||||
|
chipArgs.type = ArchArgs::Z020;
|
||||||
|
}
|
||||||
|
chipArgs.package = root.get<std::string>("project.arch.package");
|
||||||
|
|
||||||
|
return std::unique_ptr<Context>(new Context(chipArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path)
|
||||||
|
{
|
||||||
|
auto input = root.get_child("project").get_child("input");
|
||||||
|
boost::filesystem::path pcf = boost::filesystem::path(path) / input.get<std::string>("pcf");
|
||||||
|
std::ifstream f(pcf.string());
|
||||||
|
if (!apply_pcf(ctx, input.get<std::string>("pcf"), f))
|
||||||
|
log_error("Loading PCF failed.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
357
xc7/tmfuzz.py
Normal file
357
xc7/tmfuzz.py
Normal file
@ -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--")
|
285
xc7/xdl.cc
Normal file
285
xc7/xdl.cc
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "xdl.h"
|
||||||
|
#include <cctype>
|
||||||
|
#include <vector>
|
||||||
|
#include "cells.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "nextpnr.h"
|
||||||
|
#include "util.h"
|
||||||
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
|
||||||
|
#include "torc/Physical.hpp"
|
||||||
|
using namespace torc::architecture::xilinx;
|
||||||
|
using namespace torc::physical;
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
DesignSharedPtr create_torc_design(const Context *ctx)
|
||||||
|
{
|
||||||
|
auto designPtr = Factory::newDesignPtr("name", torc_info->ddb->getDeviceName(), ctx->args.package, "-1", "");
|
||||||
|
|
||||||
|
std::unordered_map<int32_t, InstanceSharedPtr> site_to_instance;
|
||||||
|
std::vector<std::pair<std::string, std::string>> lut_inputs;
|
||||||
|
lut_inputs.reserve(6);
|
||||||
|
|
||||||
|
auto bel_to_lut = [](const BelId bel) {
|
||||||
|
switch (torc_info->bel_to_loc[bel.index].z) {
|
||||||
|
case 0:
|
||||||
|
case 4:
|
||||||
|
return "A";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
case 5:
|
||||||
|
return "B";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 6:
|
||||||
|
return "C";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
case 7:
|
||||||
|
return "D";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto &cell : ctx->cells) {
|
||||||
|
const char *type;
|
||||||
|
if (cell.second->type == id_SLICE_LUT6)
|
||||||
|
type = "SLICEL";
|
||||||
|
else if (cell.second->type == id_IOB33 || cell.second->type == id_IOB18 || cell.second->type == id_BUFGCTRL || cell.second->type == id_PS7 || cell.second->type == id_MMCME2_ADV)
|
||||||
|
type = cell.second->type.c_str(ctx);
|
||||||
|
else
|
||||||
|
log_error("Unsupported cell type '%s'.\n", cell.second->type.c_str(ctx));
|
||||||
|
|
||||||
|
auto site_index = torc_info->bel_to_site_index[cell.second->bel.index];
|
||||||
|
auto ret = site_to_instance.emplace(site_index, nullptr);
|
||||||
|
InstanceSharedPtr instPtr;
|
||||||
|
if (ret.second) {
|
||||||
|
instPtr = Factory::newInstancePtr(cell.second->name.str(ctx), type, "", "");
|
||||||
|
auto b = designPtr->addInstance(instPtr);
|
||||||
|
assert(b);
|
||||||
|
ret.first->second = instPtr;
|
||||||
|
|
||||||
|
const auto &tile_info = torc_info->bel_to_tile_info(cell.second->bel.index);
|
||||||
|
instPtr->setTile(tile_info.getName());
|
||||||
|
instPtr->setSite(torc_info->bel_to_name(cell.second->bel.index));
|
||||||
|
} else
|
||||||
|
instPtr = ret.first->second;
|
||||||
|
|
||||||
|
if (cell.second->type == id_SLICE_LUT6) {
|
||||||
|
std::string setting, name, value;
|
||||||
|
const std::string lut = bel_to_lut(cell.second->bel);
|
||||||
|
|
||||||
|
setting = lut + "6LUT";
|
||||||
|
value = "#LUT:O6=";
|
||||||
|
lut_inputs.clear();
|
||||||
|
if (get_net_or_empty(cell.second.get(), id_I1))
|
||||||
|
lut_inputs.emplace_back("A1", "~A1");
|
||||||
|
if (get_net_or_empty(cell.second.get(), id_I2))
|
||||||
|
lut_inputs.emplace_back("A2", "~A2");
|
||||||
|
if (get_net_or_empty(cell.second.get(), id_I3))
|
||||||
|
lut_inputs.emplace_back("A3", "~A3");
|
||||||
|
if (get_net_or_empty(cell.second.get(), id_I4))
|
||||||
|
lut_inputs.emplace_back("A4", "~A4");
|
||||||
|
if (get_net_or_empty(cell.second.get(), id_I5))
|
||||||
|
lut_inputs.emplace_back("A5", "~A5");
|
||||||
|
if (get_net_or_empty(cell.second.get(), id_I6))
|
||||||
|
lut_inputs.emplace_back("A6", "~A6");
|
||||||
|
const auto &init = cell.second->params[ctx->id("INIT")];
|
||||||
|
// Assume from Yosys that INIT masks of less than 32 bits are output as uint32_t
|
||||||
|
if (lut_inputs.size() < 6) {
|
||||||
|
auto init_as_uint = boost::lexical_cast<uint32_t>(init);
|
||||||
|
NPNR_ASSERT(init_as_uint <= ((1ull << (1u << lut_inputs.size())) - 1));
|
||||||
|
if (lut_inputs.empty())
|
||||||
|
value += init;
|
||||||
|
else {
|
||||||
|
unsigned n = 0;
|
||||||
|
for (unsigned o = 0; o < (1u << lut_inputs.size()); ++o) {
|
||||||
|
if (!((init_as_uint >> o) & 1))
|
||||||
|
continue;
|
||||||
|
if (n++ > 0)
|
||||||
|
value += "+";
|
||||||
|
value += "(";
|
||||||
|
value += (o & 1) ? lut_inputs[0].first : lut_inputs[0].second;
|
||||||
|
for (unsigned i = 1; i < lut_inputs.size(); ++i) {
|
||||||
|
value += "*";
|
||||||
|
value += o & (1 << i) ? lut_inputs[i].first : lut_inputs[i].second;
|
||||||
|
}
|
||||||
|
value += ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Otherwise as a bit string
|
||||||
|
else {
|
||||||
|
NPNR_ASSERT(init.size() == (1u << lut_inputs.size()));
|
||||||
|
unsigned n = 0;
|
||||||
|
for (unsigned i = 0; i < init.size(); ++i) {
|
||||||
|
if (init[init.size() - 1 - i] == '0')
|
||||||
|
continue;
|
||||||
|
if (n++ > 0)
|
||||||
|
value += "+";
|
||||||
|
value += "(";
|
||||||
|
value += (i & 1) ? lut_inputs[0].first : lut_inputs[0].second;
|
||||||
|
for (unsigned j = 1; j < lut_inputs.size(); ++j) {
|
||||||
|
value += "*";
|
||||||
|
value += i & (1 << j) ? lut_inputs[j].first : lut_inputs[j].second;
|
||||||
|
}
|
||||||
|
value += ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = cell.second->params.find(ctx->id("LUT_NAME"));
|
||||||
|
if (it != cell.second->params.end())
|
||||||
|
name = it->second;
|
||||||
|
else
|
||||||
|
name = cell.second->name.str(ctx);
|
||||||
|
boost::replace_all(name, ":", "\\:");
|
||||||
|
instPtr->setConfig(setting, name, value);
|
||||||
|
|
||||||
|
auto O = get_net_or_empty(cell.second.get(), id_O);
|
||||||
|
if (O) {
|
||||||
|
setting = lut;
|
||||||
|
setting += "USED";
|
||||||
|
instPtr->setConfig(setting, "", "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto OQ = get_net_or_empty(cell.second.get(), id_OQ);
|
||||||
|
if (OQ) {
|
||||||
|
setting = lut;
|
||||||
|
setting += "FF";
|
||||||
|
name = OQ->name.str(ctx);
|
||||||
|
boost::replace_all(name, ":", "\\:");
|
||||||
|
instPtr->setConfig(setting, name, "#FF");
|
||||||
|
instPtr->setConfig(setting + "MUX", "", "O6");
|
||||||
|
instPtr->setConfig(setting + "INIT", "", cell.second->params.at(ctx->id("FFINIT")));
|
||||||
|
|
||||||
|
if (cell.second->lcInfo.negClk)
|
||||||
|
instPtr->setConfig("CLKINV", "", "CLK_B");
|
||||||
|
else
|
||||||
|
instPtr->setConfig("CLKINV", "", "CLK");
|
||||||
|
|
||||||
|
if (get_net_or_empty(cell.second.get(), id_SR)) {
|
||||||
|
instPtr->setConfig(setting + "SR", "", cell.second->params.at(id_SR));
|
||||||
|
instPtr->setConfig("SRUSEDMUX", "", "IN");
|
||||||
|
}
|
||||||
|
instPtr->setConfig("SYNC_ATTR", "", cell.second->params.at(ctx->id("SYNC_ATTR")));
|
||||||
|
if (get_net_or_empty(cell.second.get(), ctx->id("CE")))
|
||||||
|
instPtr->setConfig("CEUSEDMUX", "", "IN");
|
||||||
|
|
||||||
|
}
|
||||||
|
} else if (cell.second->type == id_IOB33) {
|
||||||
|
if (get_net_or_empty(cell.second.get(), id_I)) {
|
||||||
|
instPtr->setConfig("IUSED", "", "0");
|
||||||
|
instPtr->setConfig("IBUF_LOW_PWR", "", "TRUE");
|
||||||
|
instPtr->setConfig("ISTANDARD", "", "LVCMOS33");
|
||||||
|
} else {
|
||||||
|
instPtr->setConfig("OUSED", "", "0");
|
||||||
|
instPtr->setConfig("OSTANDARD", "", "LVCMOS33");
|
||||||
|
instPtr->setConfig("DRIVE", "", "12");
|
||||||
|
instPtr->setConfig("SLEW", "", "SLOW");
|
||||||
|
}
|
||||||
|
} else if (cell.second->type == id_IOB18) {
|
||||||
|
if (get_net_or_empty(cell.second.get(), id_I)) {
|
||||||
|
instPtr->setConfig("IUSED", "", "0");
|
||||||
|
instPtr->setConfig("IBUF_LOW_PWR", "", "TRUE");
|
||||||
|
instPtr->setConfig("ISTANDARD", "", "LVCMOS18");
|
||||||
|
} else {
|
||||||
|
instPtr->setConfig("OUSED", "", "0");
|
||||||
|
instPtr->setConfig("OSTANDARD", "", "LVCMOS18");
|
||||||
|
instPtr->setConfig("DRIVE", "", "12");
|
||||||
|
instPtr->setConfig("SLEW", "", "SLOW");
|
||||||
|
}
|
||||||
|
} else if (cell.second->type == id_BUFGCTRL || cell.second->type == id_PS7 || cell.second->type == id_MMCME2_ADV) {
|
||||||
|
for (const auto& i : cell.second->params)
|
||||||
|
instPtr->setConfig(i.first.str(ctx), "", i.second);
|
||||||
|
} else
|
||||||
|
log_error("Unsupported cell type '%s'.\n", cell.second->type.c_str(ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &net : ctx->nets) {
|
||||||
|
const auto &driver = net.second->driver;
|
||||||
|
|
||||||
|
auto site_index = torc_info->bel_to_site_index[driver.cell->bel.index];
|
||||||
|
auto instPtr = site_to_instance.at(site_index);
|
||||||
|
|
||||||
|
auto netPtr = Factory::newNetPtr(net.second->name.str(ctx));
|
||||||
|
|
||||||
|
auto pin_name = driver.port.str(ctx);
|
||||||
|
// For all LUT based inputs and outputs (I1-I6,O,OQ,OMUX) then change the I/O into the LUT
|
||||||
|
if (driver.cell->type == id_SLICE_LUT6 && (pin_name[0] == 'I' || pin_name[0] == 'O')) {
|
||||||
|
const auto lut = bel_to_lut(driver.cell->bel);
|
||||||
|
pin_name[0] = lut[0];
|
||||||
|
}
|
||||||
|
// e.g. Convert DDRARB[0] -> DDRARB0
|
||||||
|
pin_name.erase(std::remove_if(pin_name.begin(), pin_name.end(), boost::is_any_of("[]")), pin_name.end());
|
||||||
|
auto pinPtr = Factory::newInstancePinPtr(instPtr, pin_name);
|
||||||
|
netPtr->addSource(pinPtr);
|
||||||
|
|
||||||
|
if (!net.second->users.empty()) {
|
||||||
|
for (const auto &user : net.second->users) {
|
||||||
|
site_index = torc_info->bel_to_site_index[user.cell->bel.index];
|
||||||
|
instPtr = site_to_instance.at(site_index);
|
||||||
|
|
||||||
|
pin_name = user.port.str(ctx);
|
||||||
|
// For all LUT based inputs and outputs (I1-I6,O,OQ,OMUX) then change the I/O into the LUT
|
||||||
|
if (user.cell->type == id_SLICE_LUT6 && (pin_name[0] == 'I' || pin_name[0] == 'O')) {
|
||||||
|
const auto lut = bel_to_lut(user.cell->bel);
|
||||||
|
pin_name[0] = lut[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// e.g. Convert DDRARB[0] -> DDRARB0
|
||||||
|
pin_name.erase(std::remove_if(pin_name.begin(), pin_name.end(), boost::is_any_of("[]")), pin_name.end());
|
||||||
|
}
|
||||||
|
pinPtr = Factory::newInstancePinPtr(instPtr, pin_name);
|
||||||
|
netPtr->addSink(pinPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto b = designPtr->addNet(netPtr);
|
||||||
|
assert(b);
|
||||||
|
|
||||||
|
for (const auto &i : net.second->wires) {
|
||||||
|
const auto &pip_map = i.second;
|
||||||
|
if (pip_map.pip == PipId())
|
||||||
|
continue;
|
||||||
|
ExtendedWireInfo ewi_src(*torc_info->ddb, torc_info->pip_to_arc[pip_map.pip.index].getSourceTilewire());
|
||||||
|
ExtendedWireInfo ewi_dst(*torc_info->ddb, torc_info->pip_to_arc[pip_map.pip.index].getSinkTilewire());
|
||||||
|
auto p = Factory::newPip(ewi_src.mTileName, ewi_src.mWireName, ewi_dst.mWireName,
|
||||||
|
ePipUnidirectionalBuffered);
|
||||||
|
netPtr->addPip(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return designPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_xdl(const Context *ctx, std::ostream &out)
|
||||||
|
{
|
||||||
|
XdlExporter exporter(out);
|
||||||
|
auto designPtr = create_torc_design(ctx);
|
||||||
|
exporter(designPtr);
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
33
xc7/xdl.h
Normal file
33
xc7/xdl.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||||
|
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef XC7_BITSTREAM_H
|
||||||
|
#define XC7_BITSTREAM_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "nextpnr.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
void write_xdl(const Context *ctx, std::ostream &out);
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user