Compare commits
222 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7125961e51 | ||
![]() |
04c08b7bc6 | ||
![]() |
3f0ad5915a | ||
![]() |
74e84cf65e | ||
![]() |
940843f7f3 | ||
![]() |
2c4fd3ce81 | ||
![]() |
ef35b41e0f | ||
![]() |
1a9b2d9803 | ||
![]() |
efd35a2dd8 | ||
![]() |
e393187d68 | ||
![]() |
1e2a7c9fca | ||
![]() |
3d79eee05a | ||
![]() |
ae1bbbdbdd | ||
![]() |
36edbdcbbd | ||
![]() |
078285fe7f | ||
![]() |
55ac41046d | ||
![]() |
0bc1587407 | ||
![]() |
af0bc8ab4c | ||
![]() |
c28b1ee6bd | ||
![]() |
57f8c216b5 | ||
![]() |
c51d89f2af | ||
![]() |
39a5019f88 | ||
![]() |
84485152cc | ||
![]() |
bbef0182f8 | ||
![]() |
0514fb9042 | ||
![]() |
ba646ac8c4 | ||
![]() |
4c9f62e1e0 | ||
![]() |
592022c180 | ||
![]() |
cb6ea8ba84 | ||
![]() |
04757865fe | ||
![]() |
84d038360d | ||
![]() |
e15aa8d3f4 | ||
![]() |
ada9076114 | ||
![]() |
5cf0c559fa | ||
![]() |
e00d6000d7 | ||
![]() |
88e7267510 | ||
![]() |
afc89a60e8 | ||
![]() |
5b438116e5 | ||
![]() |
9790cea186 | ||
![]() |
cbc01f0f71 | ||
![]() |
ab35983000 | ||
![]() |
64f3f25270 | ||
![]() |
60fc4735bf | ||
![]() |
5072d184ab | ||
![]() |
ede0e93206 | ||
![]() |
a0a6281d0d | ||
![]() |
a630758ca7 | ||
![]() |
463d9a6920 | ||
![]() |
6a09f2a856 | ||
![]() |
f2b6e525d5 | ||
![]() |
a8a00ff3fb | ||
![]() |
b2b02c9c3b | ||
![]() |
8d069c0115 | ||
![]() |
e87b96fe4a | ||
![]() |
097062c5cb | ||
![]() |
d37a57800b | ||
![]() |
9c0fd6816e | ||
![]() |
16e001b679 | ||
![]() |
0146312707 | ||
![]() |
cadafffbec | ||
![]() |
550271b338 | ||
![]() |
236ca15b49 | ||
![]() |
8c44888466 | ||
![]() |
904860b2b4 | ||
![]() |
66f22150b1 | ||
![]() |
c708a4e0d3 | ||
![]() |
557011cfe6 | ||
![]() |
5f75a8447f | ||
![]() |
20f0353f76 | ||
![]() |
b1b8183967 | ||
![]() |
8f0e888815 | ||
![]() |
5aff7bbbc4 | ||
![]() |
5ddfc32c75 | ||
![]() |
4574a57efc | ||
![]() |
fdca3d6d77 | ||
![]() |
cac7ce2747 | ||
![]() |
f4e7f4e690 | ||
![]() |
2fdf937259 | ||
![]() |
0327fa554a | ||
![]() |
c5165f7830 | ||
![]() |
d7dd945f55 | ||
![]() |
d8b6b231de | ||
![]() |
6985e80c01 | ||
![]() |
535fc953d4 | ||
![]() |
9f03d9eed3 | ||
![]() |
1f387d44fb | ||
![]() |
b7a06a02c4 | ||
![]() |
4161856d49 | ||
![]() |
105c148848 | ||
![]() |
bfa2157ae6 | ||
![]() |
f2fecc3c69 | ||
![]() |
13e7798b34 | ||
![]() |
212b03999b | ||
![]() |
440802bf9d | ||
![]() |
662733c171 | ||
![]() |
a0b6d3b19b | ||
![]() |
ae9ccfa5ad | ||
![]() |
664c48f5e4 | ||
![]() |
fc015d28d3 | ||
![]() |
c3dc8696eb | ||
![]() |
ab9cb99f52 | ||
![]() |
fa3e390e5f | ||
![]() |
18cee5d279 | ||
![]() |
75b48dfe1e | ||
![]() |
92ea676fc4 | ||
![]() |
934c5d5f8b | ||
![]() |
e19ad23ef4 | ||
![]() |
9a498c26d1 | ||
![]() |
bccf95296c | ||
![]() |
ca94aa1915 | ||
![]() |
fa3d366ddb | ||
![]() |
d5ca744ca0 | ||
![]() |
97f1901fcf | ||
![]() |
72a16004e1 | ||
![]() |
ec96897c1d | ||
![]() |
75654a69f0 | ||
![]() |
53f025c03f | ||
![]() |
dd85e11abb | ||
![]() |
83117bef66 | ||
![]() |
19561fde52 | ||
![]() |
99f5836b0e | ||
![]() |
a0c6c64be7 | ||
![]() |
533c730418 | ||
![]() |
10d6db6d94 | ||
![]() |
f6bdd92640 | ||
![]() |
540765a14e | ||
![]() |
3d8b88deef | ||
![]() |
2b2254f514 | ||
![]() |
3b86c3a381 | ||
![]() |
3576509684 | ||
![]() |
4619b2d82f | ||
![]() |
c67f1091ca | ||
![]() |
31ae470414 | ||
![]() |
c6f66b4468 | ||
![]() |
11b1309e62 | ||
![]() |
2711ff9cdc | ||
![]() |
a5be1bbe6e | ||
![]() |
e53f8364ec | ||
![]() |
1ee041e63f | ||
![]() |
f67cf32d0d | ||
![]() |
61939dca11 | ||
![]() |
f1b454b96c | ||
![]() |
42a1b1a750 | ||
![]() |
e137a9c507 | ||
![]() |
1e41183c5e | ||
![]() |
5c56fab0ab | ||
![]() |
aa7f7d6a97 | ||
![]() |
d80b63cc55 | ||
![]() |
4239e3668a | ||
![]() |
ac919421f0 | ||
![]() |
c6bf8aff43 | ||
![]() |
8f1c91151b | ||
![]() |
0e1c23a07b | ||
![]() |
e758d8befe | ||
![]() |
5d9019994e | ||
![]() |
324ff41f13 | ||
![]() |
af4f9680e2 | ||
![]() |
735c7c7c9c | ||
![]() |
382b52fc88 | ||
![]() |
834f5f58c2 | ||
![]() |
6e2d215e6a | ||
![]() |
5214d1dbb5 | ||
![]() |
e0e5604958 | ||
![]() |
d0916943c5 | ||
![]() |
0721a15c33 | ||
![]() |
c7f0bdfc1b | ||
![]() |
beb533b005 | ||
![]() |
db6e81d6c3 | ||
![]() |
7da5e2b525 | ||
![]() |
6c2247b4f6 | ||
![]() |
24d702d0be | ||
![]() |
d78f5a1d5b | ||
![]() |
30fe1f229a | ||
![]() |
2eeb59d9f1 | ||
![]() |
3a5665c1cb | ||
![]() |
4f61d2dae7 | ||
![]() |
86fa032b63 | ||
![]() |
7f1c1ecaf0 | ||
![]() |
bf5a4717f5 | ||
![]() |
6d17810dde | ||
![]() |
001806f317 | ||
![]() |
c128df127b | ||
![]() |
d2597bcd8d | ||
![]() |
3f865f9049 | ||
![]() |
7e693ff27d | ||
![]() |
ca7eef26ac | ||
![]() |
df2f295545 | ||
![]() |
82fbc551f8 | ||
![]() |
3a177c72c6 | ||
![]() |
b658a39d73 | ||
![]() |
5b6255abf1 | ||
![]() |
0a16e24c82 | ||
![]() |
3e1085ecb5 | ||
![]() |
f7be783a32 | ||
![]() |
718f5b81f0 | ||
![]() |
699bd3ef5a | ||
![]() |
a87f26b254 | ||
![]() |
dcc08b27cc | ||
![]() |
07fb4702ce | ||
![]() |
a7ccc01c45 | ||
![]() |
17918b5992 | ||
![]() |
d05ac75fda | ||
![]() |
b8b9813056 | ||
![]() |
72c785db0e | ||
![]() |
7b15569c69 | ||
![]() |
56b7299cca | ||
![]() |
f6f20dce0c | ||
![]() |
8dedd7a83c | ||
![]() |
13e30a4eb1 | ||
![]() |
0fe579f046 | ||
![]() |
57c273898c | ||
![]() |
32f5346378 | ||
![]() |
67a0fa11e6 | ||
![]() |
2bc7ffc2ea | ||
![]() |
8cddc49abc | ||
![]() |
45009ac09d | ||
![]() |
74ff630922 | ||
![]() |
fbeb039f39 | ||
![]() |
8357417787 | ||
![]() |
6425032ec4 | ||
![]() |
a0d72a6f8e | ||
![]() |
d53658a079 |
@ -5,8 +5,9 @@ task:
|
||||
memory: 16
|
||||
dockerfile: .cirrus/Dockerfile.ubuntu16.04
|
||||
|
||||
build_script: mkdir build && cd build && cmake .. -DARCH=all -DTRELLIS_ROOT=/usr/local/src/prjtrellis -DBUILD_TESTS=on && make -j $(nproc)
|
||||
build_script: mkdir build && cd build && cmake .. -DARCH=all -DTRELLIS_ROOT=/usr/local/src/prjtrellis -DTORC_ROOT=/usr/local/src/torc -DBUILD_TESTS=on && make -j $(nproc)
|
||||
test_generic_script: cd build && ./nextpnr-generic-test
|
||||
test_ice40_script: cd build && ./nextpnr-ice40-test
|
||||
smoketest_ice40_script: export NEXTPNR=$(pwd)/build/nextpnr-ice40 && cd ice40/smoketest/attosoc && ./smoketest.sh
|
||||
test_ecp5_script: cd build && ./nextpnr-ecp5-test
|
||||
test_ecp5_script: cd build && ./nextpnr-ecp5-test
|
||||
test_xc7_script: cd build && ./nextpnr-xc7-test
|
||||
|
@ -52,4 +52,10 @@ RUN set -e -x ;\
|
||||
make -j $(nproc) ;\
|
||||
make install
|
||||
|
||||
|
||||
RUN set -e -x ;\
|
||||
mkdir -p /usr/local/src ;\
|
||||
cd /usr/local/src ;\
|
||||
git clone --recursive https://github.com/eddiehung/torc.git ;\
|
||||
cd torc ;\
|
||||
git reset --hard 90874d1293fbeba77bae41b5c38168cd91e1bf00 ;\
|
||||
make -j $(nproc) -C src/torc
|
||||
|
20
.gitignore
vendored
20
.gitignore
vendored
@ -3,6 +3,7 @@
|
||||
/nextpnr-generic*
|
||||
/nextpnr-ice40*
|
||||
/nextpnr-ecp5*
|
||||
/nextpnr-xc7*
|
||||
cmake-build-*/
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
@ -29,3 +30,22 @@ install_manifest.txt
|
||||
/ImportExecutables.cmake
|
||||
*-coverage/
|
||||
*-coverage.info
|
||||
|
||||
# nextpnr-xc7
|
||||
*.xdl
|
||||
*.bit
|
||||
|
||||
# ise
|
||||
_xmsgs/
|
||||
*.bgn
|
||||
*.drc
|
||||
*.ncd
|
||||
*.xwbt
|
||||
usage_statistics_webtalk.html
|
||||
webtalk.log
|
||||
xilinx_device_details.xml
|
||||
*_fpga_editor.*
|
||||
*.twr
|
||||
*.twx
|
||||
*.nlf
|
||||
*.sdf
|
||||
|
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()
|
||||
|
||||
# List of families to build
|
||||
set(FAMILIES generic ice40 ecp5)
|
||||
set(FAMILIES generic ice40 ecp5 xc7)
|
||||
|
||||
set(ARCH "" CACHE STRING "Architecture family for nextpnr build")
|
||||
set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES})
|
||||
@ -120,9 +120,10 @@ if (BUILD_PYTHON)
|
||||
# Original source: https://github.com/BVLC/caffe/blob/master/cmake/Dependencies.cmake#L148
|
||||
set(version ${PYTHONLIBS_VERSION_STRING})
|
||||
|
||||
STRING(REGEX REPLACE "[^0-9]" "" boost_py_version ${version})
|
||||
STRING(REGEX REPLACE "[^0-9]" "" boost_py_version ${version})
|
||||
find_package(Boost QUIET COMPONENTS "python-py${boost_py_version}" ${boost_libs})
|
||||
set(Boost_PYTHON_FOUND ${Boost_PYTHON-PY${boost_py_version}_FOUND})
|
||||
set(boost_python_lib "python-py${boost_py_version}")
|
||||
|
||||
while (NOT "${version}" STREQUAL "" AND NOT Boost_PYTHON_FOUND)
|
||||
STRING(REGEX REPLACE "([0-9.]+).[0-9]+" "\\1" version ${version})
|
||||
@ -130,6 +131,7 @@ if (BUILD_PYTHON)
|
||||
STRING(REGEX REPLACE "[^0-9]" "" boost_py_version ${version})
|
||||
find_package(Boost QUIET COMPONENTS "python-py${boost_py_version}" ${boost_libs})
|
||||
set(Boost_PYTHON_FOUND ${Boost_PYTHON-PY${boost_py_version}_FOUND})
|
||||
set(boost_python_lib "python-py${boost_py_version}")
|
||||
|
||||
STRING(REGEX MATCHALL "([0-9.]+).[0-9]+" has_more_version ${version})
|
||||
if ("${has_more_version}" STREQUAL "")
|
||||
@ -139,6 +141,7 @@ if (BUILD_PYTHON)
|
||||
|
||||
if (NOT Boost_PYTHON_FOUND)
|
||||
find_package(Boost QUIET COMPONENTS python3 ${boost_libs})
|
||||
set(boost_python_lib python3)
|
||||
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
|
||||
set(Boost_PYTHON_FOUND TRUE)
|
||||
endif ()
|
||||
@ -146,6 +149,7 @@ if (BUILD_PYTHON)
|
||||
|
||||
if (NOT Boost_PYTHON_FOUND)
|
||||
find_package(Boost QUIET COMPONENTS python36 ${boost_libs})
|
||||
set(boost_python_lib python36)
|
||||
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
|
||||
set(Boost_PYTHON_FOUND TRUE)
|
||||
endif ()
|
||||
@ -153,6 +157,7 @@ if (BUILD_PYTHON)
|
||||
|
||||
if (NOT Boost_PYTHON_FOUND)
|
||||
find_package(Boost QUIET COMPONENTS python37 ${boost_libs})
|
||||
set(boost_python_lib python37)
|
||||
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
|
||||
set(Boost_PYTHON_FOUND TRUE)
|
||||
endif ()
|
||||
@ -161,6 +166,7 @@ if (BUILD_PYTHON)
|
||||
if (NOT Boost_PYTHON_FOUND)
|
||||
STRING(REGEX REPLACE "([0-9]+\\.[0-9]+).*" "\\1" gentoo_version ${PYTHONLIBS_VERSION_STRING})
|
||||
find_package(Boost QUIET COMPONENTS python-${gentoo_version} ${boost_libs})
|
||||
set(boost_python_lib python-${gentoo_version})
|
||||
if ("${Boost_LIBRARIES}" MATCHES ".*(python|PYTHON).*" )
|
||||
set(Boost_PYTHON_FOUND TRUE)
|
||||
endif ()
|
||||
|
13
README.md
13
README.md
@ -1,6 +1,19 @@
|
||||
nextpnr -- a portable FPGA place and route tool
|
||||
===============================================
|
||||
|
||||
***
|
||||
### NB: This nextpnr-xc7 branch is *unofficial*, *very* proof-of-concept, *very* experimental, *very* unoptimised, and is provided with *no support whatsoever*. Use at your own risk!
|
||||
#### It leverages a [torc](https://github.com/torc-isi/torc) fork with minimal changes (those necessary to support building on later versions of gcc) to target XDL-compatible devices.
|
||||
### Note that torc is licensed under GPLv3 which differs from nextpnr's ISC license, thus please respect the limitations imposed by both licenses.
|
||||
Currently, only LUT1-6, IOB, BUFGCTRL, MMCME2_ADV are supported for xc7z020 and xc7vx680t (but trivial to add others).
|
||||
The following example shell scripts are available:
|
||||
* blinky.sh -- generates blinky.bit that flashes (with a delay) the 4 LEDs on a ZYBO Z7
|
||||
* blinky_sim.sh -- post place-and-route simulation, without any delays (requires [GHDL](https://github.com/ghdl/ghdl))
|
||||
* picorv32.sh -- just places-and-routes picorv32.ncd (no testbench)
|
||||
* attosoc.sh -- generates attosoc.bit of a self-stimulating picorv32 device that displays (with a delay) prime numbers to the LEDs -- when testing on hardware, consider using a PLL (MMCM) to meet timing
|
||||
* attosoc_sim.sh -- post place-and-route simulation of a self-stimulating picorv32 device, without any delays (requires [GHDL](https://github.com/ghdl/ghdl))
|
||||
***
|
||||
|
||||
nextpnr aims to be a vendor neutral, timing driven, FOSS FPGA place and route
|
||||
tool.
|
||||
|
||||
|
@ -37,6 +37,8 @@ wirelen_t get_net_metric(const Context *ctx, const NetInfo *net, MetricType type
|
||||
if (driver_gb)
|
||||
return 0;
|
||||
int clock_count;
|
||||
if (ctx->getPortTimingClass(driver_cell, net->driver.port, clock_count) == TMG_IGNORE)
|
||||
return 0;
|
||||
bool timing_driven = ctx->timing_driven && type == MetricType::COST &&
|
||||
ctx->getPortTimingClass(driver_cell, net->driver.port, clock_count) != TMG_IGNORE;
|
||||
delay_t negative_slack = 0;
|
||||
|
@ -512,6 +512,21 @@ struct Router1
|
||||
WireId next_wire = ctx->getPipDstWire(pip);
|
||||
next_delay += ctx->getWireDelay(next_wire).maxDelay();
|
||||
|
||||
#ifdef ARCH_XC7
|
||||
// For BUFG routing, do not exit the global network until the destination tile is reached
|
||||
if (ctx->isGlobalNet(net_info)) {
|
||||
if (torc_info->wire_is_global[src_wire.index] && !torc_info->wire_is_global[next_wire.index]) {
|
||||
const auto &arc = torc_info->pip_to_arc[pip.index];
|
||||
const auto &next_tw = arc.getSinkTilewire();
|
||||
const auto &next_loc = torc_info->tile_to_xy[next_tw.getTileIndex()];
|
||||
const auto &dst_tw = torc_info->wire_to_tilewire[dst_wire.index];
|
||||
const auto &dst_loc = torc_info->tile_to_xy[dst_tw.getTileIndex()];
|
||||
if (next_loc.second != dst_loc.second || next_loc.first != dst_loc.first)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
WireId conflictWireWire = WireId(), conflictPipWire = WireId();
|
||||
NetInfo *conflictWireNet = nullptr, *conflictPipNet = nullptr;
|
||||
|
||||
|
@ -51,9 +51,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
bool Application::notify(QObject *receiver, QEvent *event)
|
||||
{
|
||||
bool retVal = true;
|
||||
try {
|
||||
retVal = QApplication::notify(receiver, event);
|
||||
} catch (assertion_failure ex) {
|
||||
// try {
|
||||
retVal = QApplication::notify(receiver, event);
|
||||
/*} catch (assertion_failure ex) {
|
||||
QString msg;
|
||||
QTextStream out(&msg);
|
||||
out << ex.filename.c_str() << " at " << ex.line << "\n";
|
||||
@ -61,7 +61,7 @@ bool Application::notify(QObject *receiver, QEvent *event)
|
||||
QMessageBox::critical(0, "Error", msg);
|
||||
} catch (...) {
|
||||
QMessageBox::critical(0, "Error", "Fatal error !!!");
|
||||
}
|
||||
}*/
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
@ -317,6 +317,12 @@ void DesignWidget::newContext(Context *ctx)
|
||||
for (const auto &wire : ctx->getWires()) {
|
||||
wireMap[std::pair<int, int>(wire.location.x, wire.location.y)].push_back(wire);
|
||||
}
|
||||
#endif
|
||||
#ifdef ARCH_XC7
|
||||
for (const auto &wire : ctx->getWires()) {
|
||||
const auto loc = torc_info->wire_to_loc(wire.index);
|
||||
wireMap[std::pair<int, int>(loc.x, loc.y)].push_back(wire);
|
||||
}
|
||||
#endif
|
||||
auto wireGetter = [](Context *ctx, WireId id) { return ctx->getWireName(id); };
|
||||
getTreeByElementType(ElementType::WIRE)
|
||||
@ -706,8 +712,9 @@ void DesignWidget::onSelectionChanged(int num, const QItemSelection &, const QIt
|
||||
addProperty(topItem, QVariant::String, "Type", ctx->getPipType(pip).c_str(ctx));
|
||||
addProperty(topItem, QVariant::Bool, "Available", ctx->checkPipAvail(pip));
|
||||
addProperty(topItem, QVariant::String, "Bound Net", ctx->nameOf(ctx->getBoundPipNet(pip)), ElementType::NET);
|
||||
WireId conflict = ctx->getConflictingPipWire(pip);
|
||||
addProperty(topItem, QVariant::String, "Conflicting Wire",
|
||||
ctx->getWireName(ctx->getConflictingPipWire(pip)).c_str(ctx), ElementType::WIRE);
|
||||
(conflict != WireId() ? ctx->getWireName(conflict).c_str(ctx) : ""), ElementType::WIRE);
|
||||
addProperty(topItem, QVariant::String, "Conflicting Net", ctx->nameOf(ctx->getConflictingPipNet(pip)),
|
||||
ElementType::NET);
|
||||
addProperty(topItem, QVariant::String, "Src Wire", ctx->getWireName(ctx->getPipSrcWire(pip)).c_str(ctx),
|
||||
|
0
gui/xc7/family.cmake
Normal file
0
gui/xc7/family.cmake
Normal file
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 90874d1293fbeba77bae41b5c38168cd91e1bf00
|
879
xc7/arch.cc
Normal file
879
xc7/arch.cc
Normal file
@ -0,0 +1,879 @@
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << TORC_ROOT << "/src/torc";
|
||||
torc::common::DirectoryTree directoryTree(ss.str().c_str());
|
||||
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");
|
||||
}
|
||||
|
||||
// TODO: FIXME
|
||||
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 = 0;
|
||||
br.e.cursor = 0;
|
||||
NPNR_ASSERT("TODO");
|
||||
return br;
|
||||
}
|
||||
|
||||
PortType Arch::getBelPinType(BelId bel, IdString pin) const
|
||||
{
|
||||
NPNR_ASSERT(bel != BelId());
|
||||
NPNR_ASSERT("TODO");
|
||||
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
|
722
xc7/arch.h
Normal file
722
xc7/arch.h
Normal file
@ -0,0 +1,722 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
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;
|
||||
|
||||
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
|
||||
{
|
||||
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
|
77
xc7/arch_place.cc
Normal file
77
xc7/arch_place.cc
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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
|
190
xc7/archdefs.h
Normal file
190
xc7/archdefs.h
Normal file
@ -0,0 +1,190 @@
|
||||
/*
|
||||
* 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 = 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;
|
238
xc7/cells.cc
Normal file
238
xc7/cells.cc
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* 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 = (torc_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
|
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)
|
84
xc7/delay.cc
Normal file
84
xc7/delay.cc
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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
|
108
xc7/family.cmake
Normal file
108
xc7/family.cmake
Normal file
@ -0,0 +1,108 @@
|
||||
if (NOT DEFINED TORC_ROOT)
|
||||
# Adapted from https://cliutils.gitlab.io/modern-cmake/chapters/projects/submodule.html
|
||||
find_package(Git QUIET)
|
||||
if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.gitmodules")
|
||||
# Update submodules as needed
|
||||
option(GIT_SUBMODULE "Check submodules during build" ON)
|
||||
if(GIT_SUBMODULE)
|
||||
message(STATUS "Submodule update")
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
RESULT_VARIABLE GIT_SUBMOD_RESULT)
|
||||
if(NOT GIT_SUBMOD_RESULT EQUAL "0")
|
||||
message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_dependencies(nextpnr-${family} torc)
|
||||
if (BUILD_TESTS)
|
||||
add_dependencies(nextpnr-${family}-test torc)
|
||||
endif()
|
||||
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)
|
||||
set(TORC_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/torc)
|
||||
endif()
|
||||
|
||||
find_package(Boost REQUIRED COMPONENTS regex ${boost_libs} ${boost_python_lib})
|
||||
|
||||
target_compile_definitions(nextpnr-${family} PRIVATE -DTORC_ROOT="${TORC_ROOT}")
|
||||
target_include_directories(nextpnr-${family} PUBLIC ${TORC_ROOT}/src)
|
||||
if (BUILD_TESTS)
|
||||
target_compile_definitions(nextpnr-${family}-test PRIVATE -DTORC_ROOT="${TORC_ROOT}")
|
||||
target_include_directories(nextpnr-${family}-test PUBLIC ${TORC_ROOT}/src)
|
||||
endif()
|
||||
if (BUILD_GUI)
|
||||
target_include_directories(gui_${family} PUBLIC ${TORC_ROOT}/src)
|
||||
endif()
|
||||
|
||||
set(TORC_OBJS
|
||||
${TORC_ROOT}/src/torc/architecture/Arc.o
|
||||
${TORC_ROOT}/src/torc/architecture/ArcUsage.o
|
||||
${TORC_ROOT}/src/torc/architecture/Array.o
|
||||
${TORC_ROOT}/src/torc/architecture/DDB.o
|
||||
${TORC_ROOT}/src/torc/architecture/DDBConsoleStreams.o
|
||||
${TORC_ROOT}/src/torc/architecture/DDBStreamHelper.o
|
||||
${TORC_ROOT}/src/torc/architecture/DigestStream.o
|
||||
${TORC_ROOT}/src/torc/architecture/ExtendedWireInfo.o
|
||||
${TORC_ROOT}/src/torc/architecture/InstancePin.o
|
||||
${TORC_ROOT}/src/torc/architecture/OutputStreamHelpers.o
|
||||
${TORC_ROOT}/src/torc/architecture/Package.o
|
||||
${TORC_ROOT}/src/torc/architecture/Pad.o
|
||||
${TORC_ROOT}/src/torc/architecture/PrimitiveConn.o
|
||||
${TORC_ROOT}/src/torc/architecture/PrimitiveDef.o
|
||||
${TORC_ROOT}/src/torc/architecture/PrimitiveElement.o
|
||||
${TORC_ROOT}/src/torc/architecture/PrimitiveElementPin.o
|
||||
${TORC_ROOT}/src/torc/architecture/PrimitivePin.o
|
||||
${TORC_ROOT}/src/torc/architecture/Segments.o
|
||||
${TORC_ROOT}/src/torc/architecture/Site.o
|
||||
${TORC_ROOT}/src/torc/architecture/Sites.o
|
||||
${TORC_ROOT}/src/torc/architecture/Tiles.o
|
||||
${TORC_ROOT}/src/torc/architecture/TileInfo.o
|
||||
${TORC_ROOT}/src/torc/architecture/Tilewire.o
|
||||
${TORC_ROOT}/src/torc/architecture/Versions.o
|
||||
${TORC_ROOT}/src/torc/architecture/VprExporter.o
|
||||
${TORC_ROOT}/src/torc/architecture/WireInfo.o
|
||||
${TORC_ROOT}/src/torc/architecture/WireUsage.o
|
||||
${TORC_ROOT}/src/torc/architecture/XdlImporter.o
|
||||
${TORC_ROOT}/src/torc/architecture/XilinxDatabaseTypes.o
|
||||
|
||||
${TORC_ROOT}/src/torc/common/Annotated.o
|
||||
${TORC_ROOT}/src/torc/common/DeviceDesignator.o
|
||||
${TORC_ROOT}/src/torc/common/Devices.o
|
||||
${TORC_ROOT}/src/torc/common/DirectoryTree.o
|
||||
${TORC_ROOT}/src/torc/common/DottedVersion.o
|
||||
${TORC_ROOT}/src/torc/common/NullOutputStream.o
|
||||
|
||||
${TORC_ROOT}/src/torc/externals/zlib/zfstream.o
|
||||
z
|
||||
|
||||
${TORC_ROOT}/src/torc/physical/Circuit.o
|
||||
${TORC_ROOT}/src/torc/physical/ConfigMap.o
|
||||
${TORC_ROOT}/src/torc/physical/Config.o
|
||||
${TORC_ROOT}/src/torc/physical/Design.o
|
||||
${TORC_ROOT}/src/torc/physical/Factory.o
|
||||
${TORC_ROOT}/src/torc/physical/Instance.o
|
||||
${TORC_ROOT}/src/torc/physical/InstancePin.o
|
||||
${TORC_ROOT}/src/torc/physical/InstanceReference.o
|
||||
${TORC_ROOT}/src/torc/physical/Module.o
|
||||
${TORC_ROOT}/src/torc/physical/ModuleTransformer.o
|
||||
${TORC_ROOT}/src/torc/physical/Named.o
|
||||
${TORC_ROOT}/src/torc/physical/Net.o
|
||||
${TORC_ROOT}/src/torc/physical/OutputStreamHelpers.o
|
||||
${TORC_ROOT}/src/torc/physical/Pip.o
|
||||
${TORC_ROOT}/src/torc/physical/Port.o
|
||||
${TORC_ROOT}/src/torc/physical/Progenitor.o
|
||||
${TORC_ROOT}/src/torc/physical/Progeny.o
|
||||
${TORC_ROOT}/src/torc/physical/Renamable.o
|
||||
${TORC_ROOT}/src/torc/physical/Routethrough.o
|
||||
${TORC_ROOT}/src/torc/physical/TilewirePlaceholder.o
|
||||
${TORC_ROOT}/src/torc/physical/XdlExporter.o
|
||||
)
|
||||
|
||||
target_link_libraries(nextpnr-${family} PRIVATE ${TORC_OBJS})
|
||||
if (BUILD_TESTS)
|
||||
target_link_libraries(nextpnr-${family}-test PRIVATE ${TORC_OBJS})
|
||||
endif()
|
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
|
24
xc7/gfx.cc
Normal file
24
xc7/gfx.cc
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
48
xc7/gfx.h
Normal file
48
xc7/gfx.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif // GFX_H
|
119
xc7/main.cc
Normal file
119
xc7/main.cc
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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) {}
|
||||
|
||||
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
|
286
xc7/xdl.cc
Normal file
286
xc7/xdl.cc
Normal file
@ -0,0 +1,286 @@
|
||||
/*
|
||||
* 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 <boost/range/adaptor/reversed.hpp>
|
||||
#include <cctype>
|
||||
#include <vector>
|
||||
#include "cells.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
#include "util.h"
|
||||
|
||||
#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