aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPepijn de Vos <pepijndevos@gmail.com>2020-12-30 15:59:55 +0100
committerGitHub <noreply@github.com>2020-12-30 14:59:55 +0000
commit3611f549024ed8b6a0d714f25d10951351097745 (patch)
treeeeaf0672cb60498371d8f0cb0a955ab177cb28f0
parent5e53a182921dad0e128186a1fe8766062c7cae61 (diff)
downloadnextpnr-3611f549024ed8b6a0d714f25d10951351097745.tar.gz
nextpnr-3611f549024ed8b6a0d714f25d10951351097745.tar.bz2
nextpnr-3611f549024ed8b6a0d714f25d10951351097745.zip
Gowin target (#542)
* load wires * add slice bels * add IOB * add aliases * local aliases * broken packing stuff * working packer * add constraints * pnr runs1111 * add timing info * constraints * more constraint stuff * add copyright * remove generic reference * remove parameters * remove generic python api * add newline to end of file * some small refactoring * warn on invalid constraints * don't error on missing cell * comment out debugging print * typo * avoid copy * faster empty idstring * remove intermediate variable * no more deadnames * fix cst warnings * increase ripup and epsilon a bit * take single device parameter * add info to readme * gui stubs * Revert 4d03b681a8634e978bd5af73c97665500047e055 * assign ff_used in assignArchInfo * decrease beta for better routability * try to fix CI
-rw-r--r--.cirrus.yml4
-rw-r--r--.cirrus/Dockerfile.ubuntu20.04 (renamed from .cirrus/Dockerfile.ubuntu16.04)9
-rw-r--r--CMakeLists.txt4
-rw-r--r--README.md13
-rw-r--r--common/timing_opt.cc2
-rw-r--r--gowin/CMakeLists.txt55
-rw-r--r--gowin/arch.cc1237
-rw-r--r--gowin/arch.h436
-rw-r--r--gowin/arch_pybindings.cc163
-rw-r--r--gowin/arch_pybindings.h31
-rw-r--r--gowin/archdefs.h94
-rw-r--r--gowin/cells.cc142
-rw-r--r--gowin/cells.h94
-rw-r--r--gowin/constids.inc429
-rw-r--r--gowin/family.cmake53
-rw-r--r--gowin/main.cc90
-rw-r--r--gowin/pack.cc291
-rw-r--r--gui/gowin/family.cmake0
-rw-r--r--gui/gowin/mainwindow.cc49
-rw-r--r--gui/gowin/mainwindow.h45
-rw-r--r--gui/gowin/nextpnr.qrc2
21 files changed, 3235 insertions, 8 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index bf9b0c1b..0c2d8b78 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -1,9 +1,9 @@
task:
- name: build-test-ubuntu1604
+ name: build-test-ubuntu2004
container:
cpu: 4
memory: 20
- dockerfile: .cirrus/Dockerfile.ubuntu16.04
+ dockerfile: .cirrus/Dockerfile.ubuntu20.04
build_script: mkdir build && cd build && cmake .. -DARCH=all+alpha -DOXIDE_INSTALL_PREFIX=$HOME/.cargo -DBUILD_TESTS=on -DBUILD_GUI=on && make -j3
submodule_script: git submodule sync --recursive && git submodule update --init --recursive
diff --git a/.cirrus/Dockerfile.ubuntu16.04 b/.cirrus/Dockerfile.ubuntu20.04
index a62cf4c6..188cd8f4 100644
--- a/.cirrus/Dockerfile.ubuntu16.04
+++ b/.cirrus/Dockerfile.ubuntu20.04
@@ -1,4 +1,4 @@
-FROM ubuntu:xenial-20181113
+FROM ubuntu:focal-20201106
ENV DEBIAN_FRONTEND=noninteractive
@@ -9,14 +9,14 @@ RUN set -e -x ;\
build-essential autoconf cmake clang bison wget flex gperf \
libreadline-dev gawk tcl-dev libffi-dev graphviz xdot python3-dev \
libboost-all-dev qt5-default git libftdi-dev pkg-config libeigen3-dev \
- zlib1g-dev curl
+ zlib1g-dev curl python3-pip
RUN set -e -x ;\
mkdir -p /usr/local/src ;\
cd /usr/local/src ;\
git clone --recursive https://github.com/steveicarus/iverilog.git ;\
cd iverilog ;\
- git reset --hard 172d7eb0a3665f89b91d601b5912c33acedc81e5 ;\
+ git reset --hard 84b4ebee0cfcda28a242d89a07020cd70b1d3e7f ;\
sh autoconf.sh ;\
./configure ;\
make -j $(nproc) ;\
@@ -62,3 +62,6 @@ RUN set -e -x ;\
git reset --hard 72dbb7973f31a30c3b9d18f3bac97caaea9a7f33 ;\
cd libprjoxide ;\
PATH=$PATH:$HOME/.cargo/bin cargo install --path prjoxide
+
+RUN set -e -x ;\
+ pip3 install apycula==0.0.1a3
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 289a83b1..344db1fb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -66,9 +66,9 @@ endif()
set(PROGRAM_PREFIX "" CACHE STRING "Name prefix for executables")
# List of families to build
-set(FAMILIES generic ice40 ecp5 nexus)
+set(FAMILIES generic ice40 ecp5 nexus gowin)
set(STABLE_FAMILIES generic ice40 ecp5)
-set(EXPERIMENTAL_FAMILIES nexus)
+set(EXPERIMENTAL_FAMILIES nexus gowin)
set(ARCH "" CACHE STRING "Architecture family for nextpnr build")
set_property(CACHE ARCH PROPERTY STRINGS ${FAMILIES})
diff --git a/README.md b/README.md
index 058760d5..97d4c62f 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@ Currently nextpnr supports:
* Lattice iCE40 devices supported by [Project IceStorm](http://www.clifford.at/icestorm/)
* Lattice ECP5 devices supported by [Project Trellis](https://github.com/YosysHQ/prjtrellis)
* Lattice Nexus devices supported by [Project Oxide](https://github.com/daveshah1/prjoxide)
+ * Gowin LittleBee devices supported by [Project Apicula](https://github.com/YosysHQ/apicula)
* *(experimental)* a "generic" back-end for user-defined architectures
There is some work in progress towards [support for Xilinx devices](https://github.com/daveshah1/nextpnr-xilinx/) but it is not upstream and not intended for end users at the present time. We hope to see more FPGA families supported in the future. We would love your help in developing this awesome new project!
@@ -114,6 +115,18 @@ sudo make install
Nexus support is currently experimental, and has only been tested with engineering sample silicon.
+### nextpnr-gowin
+
+For Gowin support, install [Project Apicula](https://github.com/YosysHQ/apicula). If a virtualenv is used, the python paths need to be provided as follows:
+
+```
+cmake . -DARCH=gowin -DPYTHON_EXECUTABLE=path -DGOWIN_BBA_EXECUTABLE=path
+make -j$(nproc)
+sudo make install
+```
+
+ - Examples of the Gowin flow for a range of boards can be found in the [Project Apicula Examples](https://github.com/YosysHQ/apicula/tree/master/examples).
+
### nextpnr-generic
The generic target allows running placement and routing for arbitrary custom architectures.
diff --git a/common/timing_opt.cc b/common/timing_opt.cc
index 309eedf2..eae15fb2 100644
--- a/common/timing_opt.cc
+++ b/common/timing_opt.cc
@@ -59,7 +59,7 @@ template <> struct hash<std::pair<int, NEXTPNR_NAMESPACE_PREFIX BelId>>
return seed;
}
};
-#ifndef ARCH_GENERIC
+#if !defined(ARCH_GENERIC) && !defined(ARCH_GOWIN)
template <> struct hash<std::pair<NEXTPNR_NAMESPACE_PREFIX IdString, NEXTPNR_NAMESPACE_PREFIX BelId>>
{
std::size_t
diff --git a/gowin/CMakeLists.txt b/gowin/CMakeLists.txt
new file mode 100644
index 00000000..17484b29
--- /dev/null
+++ b/gowin/CMakeLists.txt
@@ -0,0 +1,55 @@
+cmake_minimum_required(VERSION 3.5)
+project(chipdb-gowin NONE)
+
+set(ALL_GOWIN_DEVICES GW1N-1 GW1N-9)
+set(GOWIN_DEVICES ${ALL_GOWIN_DEVICES} CACHE STRING
+ "Include support for these Gowin devices (available: ${ALL_GOWIN_DEVICES})")
+message(STATUS "Enabled Gowin devices: ${GOWIN_DEVICES}")
+
+find_program (GOWIN_BBA_EXECUTABLE gowin_bba)
+message(STATUS "gowin_bba executable: ${GOWIN_BBA_EXECUTABLE}")
+
+if(DEFINED GOWIN_CHIPDB)
+ add_custom_target(chipdb-gowin-bbas ALL)
+else()
+ find_package(PythonInterp 3.6 REQUIRED)
+
+ # shared among all families
+ set(SERIALIZE_CHIPDBS TRUE CACHE BOOL
+ "Serialize device data preprocessing to minimize memory use")
+
+ set(all_device_bbas)
+ file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chipdb)
+ foreach(device ${GOWIN_DEVICES})
+ if(NOT device IN_LIST ALL_GOWIN_DEVICES)
+ message(FATAL_ERROR "Device ${device} is not a supported Gowin device")
+ endif()
+
+ set(device_bba chipdb/chipdb-${device}.bba)
+ add_custom_command(
+ OUTPUT ${device_bba}
+ COMMAND ${GOWIN_BBA_EXECUTABLE} -d ${device} -i ${CMAKE_CURRENT_SOURCE_DIR}/constids.inc -o ${device_bba}.new
+ # atomically update
+ COMMAND ${CMAKE_COMMAND} -E rename ${device_bba}.new ${device_bba}
+ DEPENDS
+ ${GOWIN_BBA_EXECUTABLE}
+ ${PREVIOUS_CHIPDB_TARGET}
+ ${CMAKE_CURRENT_SOURCE_DIR}/constids.inc
+ VERBATIM)
+ list(APPEND all_device_bbas ${device_bba})
+ if(SERIALIZE_CHIPDBS)
+ set(PREVIOUS_CHIPDB_TARGET ${CMAKE_CURRENT_BINARY_DIR}/${device_bba})
+ endif()
+ endforeach()
+
+ add_custom_target(chipdb-gowin-bbas ALL DEPENDS ${all_device_bbas})
+
+ get_directory_property(has_parent PARENT_DIRECTORY)
+ if(has_parent)
+ set(GOWIN_CHIPDB ${CMAKE_CURRENT_BINARY_DIR}/chipdb PARENT_SCOPE)
+ # serialize chipdb build across multiple architectures
+ set(PREVIOUS_CHIPDB_TARGET chipdb-gowin-bbas PARENT_SCOPE)
+ else()
+ message(STATUS "Build nextpnr with -DGOWIN_CHIPDB=${CMAKE_CURRENT_BINARY_DIR}/chipdb")
+ endif()
+endif()
diff --git a/gowin/arch.cc b/gowin/arch.cc
new file mode 100644
index 00000000..8b39cd27
--- /dev/null
+++ b/gowin/arch.cc
@@ -0,0 +1,1237 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Claire Wolf <claire@symbioticeda.com>
+ * Copyright (C) 2020 Pepijn de Vos <pepijn@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 <iostream>
+#include <math.h>
+#include <regex>
+#include "embed.h"
+#include "nextpnr.h"
+#include "placer1.h"
+#include "placer_heap.h"
+#include "router1.h"
+#include "router2.h"
+#include "util.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+WireInfo &Arch::wire_info(IdString wire)
+{
+ auto w = wires.find(wire);
+ if (w == wires.end())
+ NPNR_ASSERT_FALSE_STR("no wire named " + wire.str(this));
+ return w->second;
+}
+
+PipInfo &Arch::pip_info(IdString pip)
+{
+ auto p = pips.find(pip);
+ if (p == pips.end())
+ NPNR_ASSERT_FALSE_STR("no pip named " + pip.str(this));
+ return p->second;
+}
+
+BelInfo &Arch::bel_info(IdString bel)
+{
+ auto b = bels.find(bel);
+ if (b == bels.end())
+ NPNR_ASSERT_FALSE_STR("no bel named " + bel.str(this));
+ return b->second;
+}
+
+void Arch::addWire(IdString name, IdString type, int x, int y)
+{
+ // std::cout << name.str(this) << std::endl;
+ NPNR_ASSERT(wires.count(name) == 0);
+ WireInfo &wi = wires[name];
+ wi.name = name;
+ wi.type = type;
+ wi.x = x;
+ wi.y = y;
+
+ wire_ids.push_back(name);
+}
+
+void Arch::addPip(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayInfo delay, Loc loc)
+{
+ NPNR_ASSERT(pips.count(name) == 0);
+ PipInfo &pi = pips[name];
+ pi.name = name;
+ pi.type = type;
+ pi.srcWire = srcWire;
+ pi.dstWire = dstWire;
+ pi.delay = delay;
+ pi.loc = loc;
+
+ wire_info(srcWire).downhill.push_back(name);
+ wire_info(dstWire).uphill.push_back(name);
+ pip_ids.push_back(name);
+
+ if (int(tilePipDimZ.size()) <= loc.x)
+ tilePipDimZ.resize(loc.x + 1);
+
+ if (int(tilePipDimZ[loc.x].size()) <= loc.y)
+ tilePipDimZ[loc.x].resize(loc.y + 1);
+
+ gridDimX = std::max(gridDimX, loc.x + 1);
+ gridDimY = std::max(gridDimY, loc.y + 1);
+ tilePipDimZ[loc.x][loc.y] = std::max(tilePipDimZ[loc.x][loc.y], loc.z + 1);
+}
+
+void Arch::addBel(IdString name, IdString type, Loc loc, bool gb)
+{
+ NPNR_ASSERT(bels.count(name) == 0);
+ NPNR_ASSERT(bel_by_loc.count(loc) == 0);
+ BelInfo &bi = bels[name];
+ bi.name = name;
+ bi.type = type;
+ bi.x = loc.x;
+ bi.y = loc.y;
+ bi.z = loc.z;
+ bi.gb = gb;
+
+ bel_ids.push_back(name);
+ bel_by_loc[loc] = name;
+
+ if (int(bels_by_tile.size()) <= loc.x)
+ bels_by_tile.resize(loc.x + 1);
+
+ if (int(bels_by_tile[loc.x].size()) <= loc.y)
+ bels_by_tile[loc.x].resize(loc.y + 1);
+
+ bels_by_tile[loc.x][loc.y].push_back(name);
+
+ if (int(tileBelDimZ.size()) <= loc.x)
+ tileBelDimZ.resize(loc.x + 1);
+
+ if (int(tileBelDimZ[loc.x].size()) <= loc.y)
+ tileBelDimZ[loc.x].resize(loc.y + 1);
+
+ gridDimX = std::max(gridDimX, loc.x + 1);
+ gridDimY = std::max(gridDimY, loc.x + 1);
+ tileBelDimZ[loc.x][loc.y] = std::max(tileBelDimZ[loc.x][loc.y], loc.z + 1);
+}
+
+void Arch::addBelInput(IdString bel, IdString name, IdString wire)
+{
+ NPNR_ASSERT(bel_info(bel).pins.count(name) == 0);
+ PinInfo &pi = bel_info(bel).pins[name];
+ pi.name = name;
+ pi.wire = wire;
+ pi.type = PORT_IN;
+
+ wire_info(wire).downhill_bel_pins.push_back(BelPin{bel, name});
+ wire_info(wire).bel_pins.push_back(BelPin{bel, name});
+}
+
+void Arch::addBelOutput(IdString bel, IdString name, IdString wire)
+{
+ NPNR_ASSERT(bel_info(bel).pins.count(name) == 0);
+ PinInfo &pi = bel_info(bel).pins[name];
+ pi.name = name;
+ pi.wire = wire;
+ pi.type = PORT_OUT;
+
+ wire_info(wire).uphill_bel_pin = BelPin{bel, name};
+ wire_info(wire).bel_pins.push_back(BelPin{bel, name});
+}
+
+void Arch::addBelInout(IdString bel, IdString name, IdString wire)
+{
+ NPNR_ASSERT(bel_info(bel).pins.count(name) == 0);
+ PinInfo &pi = bel_info(bel).pins[name];
+ pi.name = name;
+ pi.wire = wire;
+ pi.type = PORT_INOUT;
+
+ wire_info(wire).downhill_bel_pins.push_back(BelPin{bel, name});
+ wire_info(wire).bel_pins.push_back(BelPin{bel, name});
+}
+
+void Arch::addGroupBel(IdString group, IdString bel) { groups[group].bels.push_back(bel); }
+
+void Arch::addGroupWire(IdString group, IdString wire) { groups[group].wires.push_back(wire); }
+
+void Arch::addGroupPip(IdString group, IdString pip) { groups[group].pips.push_back(pip); }
+
+void Arch::addGroupGroup(IdString group, IdString grp) { groups[group].groups.push_back(grp); }
+
+void Arch::addDecalGraphic(DecalId decal, const GraphicElement &graphic)
+{
+ decal_graphics[decal].push_back(graphic);
+ refreshUi();
+}
+
+void Arch::setWireDecal(WireId wire, DecalXY decalxy)
+{
+ wire_info(wire).decalxy = decalxy;
+ refreshUiWire(wire);
+}
+
+void Arch::setPipDecal(PipId pip, DecalXY decalxy)
+{
+ pip_info(pip).decalxy = decalxy;
+ refreshUiPip(pip);
+}
+
+void Arch::setBelDecal(BelId bel, DecalXY decalxy)
+{
+ bel_info(bel).decalxy = decalxy;
+ refreshUiBel(bel);
+}
+
+void Arch::setGroupDecal(GroupId group, DecalXY decalxy)
+{
+ groups[group].decalxy = decalxy;
+ refreshUiGroup(group);
+}
+
+void Arch::setWireAttr(IdString wire, IdString key, const std::string &value) { wire_info(wire).attrs[key] = value; }
+
+void Arch::setPipAttr(IdString pip, IdString key, const std::string &value) { pip_info(pip).attrs[key] = value; }
+
+void Arch::setBelAttr(IdString bel, IdString key, const std::string &value) { bel_info(bel).attrs[key] = value; }
+
+void Arch::setDelayScaling(double scale, double offset)
+{
+ args.delayScale = scale;
+ args.delayOffset = offset;
+}
+
+void Arch::addCellTimingClock(IdString cell, IdString port) { cellTiming[cell].portClasses[port] = TMG_CLOCK_INPUT; }
+
+void Arch::addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayInfo delay)
+{
+ if (get_or_default(cellTiming[cell].portClasses, fromPort, TMG_IGNORE) == TMG_IGNORE)
+ cellTiming[cell].portClasses[fromPort] = TMG_COMB_INPUT;
+ if (get_or_default(cellTiming[cell].portClasses, toPort, TMG_IGNORE) == TMG_IGNORE)
+ cellTiming[cell].portClasses[toPort] = TMG_COMB_OUTPUT;
+ cellTiming[cell].combDelays[CellDelayKey{fromPort, toPort}] = delay;
+}
+
+void Arch::addCellTimingSetupHold(IdString cell, IdString port, IdString clock, DelayInfo setup, DelayInfo hold)
+{
+ TimingClockingInfo ci;
+ ci.clock_port = clock;
+ ci.edge = RISING_EDGE;
+ ci.setup = setup;
+ ci.hold = hold;
+ cellTiming[cell].clockingInfo[port].push_back(ci);
+ cellTiming[cell].portClasses[port] = TMG_REGISTER_INPUT;
+}
+
+void Arch::addCellTimingClockToOut(IdString cell, IdString port, IdString clock, DelayInfo clktoq)
+{
+ TimingClockingInfo ci;
+ ci.clock_port = clock;
+ ci.edge = RISING_EDGE;
+ ci.clockToQ = clktoq;
+ cellTiming[cell].clockingInfo[port].push_back(ci);
+ cellTiming[cell].portClasses[port] = TMG_REGISTER_OUTPUT;
+}
+
+// ---------------------------------------------------------------
+
+// TODO represent wires more intelligently.
+IdString Arch::wireToGlobal(int &row, int &col, const DatabasePOD *db, IdString &wire)
+{
+ const std::string &wirename = wire.str(this);
+ char buf[32];
+ if (wirename == "VCC" || wirename == "GND") {
+ return wire;
+ }
+ if (!isdigit(wirename[1]) || !isdigit(wirename[2]) || !isdigit(wirename[3])) {
+ snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, wirename.c_str());
+ return id(buf);
+ }
+ char direction = wirename[0];
+ int num = std::stoi(wirename.substr(1, 2));
+ int segment = std::stoi(wirename.substr(3, 1));
+ switch (direction) {
+ case 'N':
+ row += segment;
+ break;
+ case 'S':
+ row -= segment;
+ break;
+ case 'E':
+ col -= segment;
+ break;
+ case 'W':
+ col += segment;
+ break;
+ default:
+ snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, wirename.c_str());
+ return id(buf);
+ break;
+ }
+ // wires wrap around the edges
+ // assumes 0-based indexes
+ if (row < 0) {
+ row = -1 - row;
+ direction = 'N';
+ } else if (col < 0) {
+ col = -1 - col;
+ direction = 'W';
+ } else if (row >= db->rows) {
+ row = 2 * db->rows - 1 - row;
+ direction = 'S';
+ } else if (col >= db->cols) {
+ col = 2 * db->cols - 1 - col;
+ direction = 'E';
+ }
+ snprintf(buf, 32, "%c%d0", direction, num);
+ wire = id(buf);
+ snprintf(buf, 32, "R%dC%d_%c%d", row + 1, col + 1, direction, num);
+ return id(buf);
+}
+
+const PairPOD *pairLookup(const PairPOD *list, const size_t len, const int dest)
+{
+ for (size_t i = 0; i < len; i++) {
+ const PairPOD *pair = &list[i];
+ if (pair->dest_id == dest) {
+ return pair;
+ }
+ }
+ return nullptr;
+}
+
+bool aliasCompare(GlobalAliasPOD i, GlobalAliasPOD j)
+{
+ return (i.dest_row < j.dest_row) || (i.dest_row == j.dest_row && i.dest_col < j.dest_col) ||
+ (i.dest_row == j.dest_row && i.dest_col == j.dest_col && i.dest_id < j.dest_id);
+}
+bool timingCompare(TimingPOD i, TimingPOD j) { return i.name_id < j.name_id; }
+
+template <class T, class C> const T *genericLookup(const T *first, int len, const T val, C compare)
+{
+ auto res = std::lower_bound(first, first + len, val, compare);
+ if (res - first != len && !compare(val, *res)) {
+ return res;
+ } else {
+ return nullptr;
+ }
+}
+
+DelayInfo delayLookup(const TimingPOD *first, int len, IdString name)
+{
+ TimingPOD needle;
+ needle.name_id = name.index;
+ const TimingPOD *timing = genericLookup(first, len, needle, timingCompare);
+ DelayInfo info;
+ if (timing != nullptr) {
+ info.maxFall = std::max(timing->ff, timing->rf) / 1000;
+ info.minFall = std::min(timing->ff, timing->rf) / 1000;
+ info.maxRaise = std::max(timing->rr, timing->fr) / 1000;
+ info.minRaise = std::min(timing->rr, timing->fr) / 1000;
+ } else {
+ info.maxFall = 0;
+ info.minFall = 0;
+ info.maxRaise = 0;
+ info.minRaise = 0;
+ }
+ return info;
+}
+
+DelayInfo Arch::getWireTypeDelay(IdString wire)
+{
+ IdString len;
+ IdString glbsrc;
+ switch (wire.index) {
+ case ID_X01:
+ case ID_X02:
+ case ID_X03:
+ case ID_X04:
+ case ID_X05:
+ case ID_X06:
+ case ID_X07:
+ case ID_X08:
+ len = id_X0;
+ break;
+ case ID_N100:
+ case ID_N130:
+ case ID_S100:
+ case ID_S130:
+ case ID_E100:
+ case ID_E130:
+ case ID_W100:
+ case ID_W130:
+ case ID_E110:
+ case ID_W110:
+ case ID_E120:
+ case ID_W120:
+ case ID_S110:
+ case ID_N110:
+ case ID_S120:
+ case ID_N120:
+ case ID_SN10:
+ case ID_SN20:
+ case ID_EW10:
+ case ID_EW20:
+ len = id_FX1;
+ break;
+ case ID_N200:
+ case ID_N210:
+ case ID_N220:
+ case ID_N230:
+ case ID_N240:
+ case ID_N250:
+ case ID_N260:
+ case ID_N270:
+ case ID_S200:
+ case ID_S210:
+ case ID_S220:
+ case ID_S230:
+ case ID_S240:
+ case ID_S250:
+ case ID_S260:
+ case ID_S270:
+ case ID_E200:
+ case ID_E210:
+ case ID_E220:
+ case ID_E230:
+ case ID_E240:
+ case ID_E250:
+ case ID_E260:
+ case ID_E270:
+ case ID_W200:
+ case ID_W210:
+ case ID_W220:
+ case ID_W230:
+ case ID_W240:
+ case ID_W250:
+ case ID_W260:
+ case ID_W270:
+ len = id_X2;
+ break;
+ case ID_N800:
+ case ID_N810:
+ case ID_N820:
+ case ID_N830:
+ case ID_S800:
+ case ID_S810:
+ case ID_S820:
+ case ID_S830:
+ case ID_E800:
+ case ID_E810:
+ case ID_E820:
+ case ID_E830:
+ case ID_W800:
+ case ID_W810:
+ case ID_W820:
+ case ID_W830:
+ len = id_X8;
+ break;
+ case ID_GT00:
+ case ID_GT10:
+ glbsrc = id_SPINE_TAP_PCLK;
+ break;
+ case ID_GBO0:
+ case ID_GBO1:
+ glbsrc = id_TAP_BRANCH_PCLK;
+ break;
+ case ID_GB00:
+ case ID_GB10:
+ case ID_GB20:
+ case ID_GB30:
+ case ID_GB40:
+ case ID_GB50:
+ case ID_GB60:
+ case ID_GB70:
+ glbsrc = id_BRANCH_PCLK;
+ break;
+ default:
+ if (wire.str(this).rfind("SPINE", 0) == 0) {
+ glbsrc = ID_CENT_SPINE_PCLK;
+ } else if (wire.str(this).rfind("UNK", 0) == 0) {
+ glbsrc = ID_PIO_CENT_PCLK;
+ }
+ break;
+ }
+ if (len != IdString()) {
+ return delayLookup(speed->wire.timings.get(), speed->wire.num_timings, len);
+ } else if (glbsrc != IdString()) {
+ return delayLookup(speed->glbsrc.timings.get(), speed->glbsrc.num_timings, glbsrc);
+ } else {
+ DelayInfo info;
+ info.maxFall = 0;
+ info.minFall = 0;
+ info.maxRaise = 0;
+ info.minRaise = 0;
+ return info;
+ }
+}
+
+void Arch::read_cst(std::istream &in) {
+ std::regex iobre = std::regex("IO_LOC +\"([^\"]+)\" +([^ ;]+);");
+ std::smatch match;
+ std::string line;
+ while (!in.eof()) {
+ std::getline(in, line);
+ if(!std::regex_match(line, match, iobre)) {
+ // empty line or comment
+ if(line.empty() || line.rfind("//", 0) == 0) {
+ continue;
+ } else {
+ log_warning("Invalid constraint: %s\n", line.c_str());
+ continue;
+ }
+ }
+ //std::cout << match[1] << " " << match[2] << std::endl;
+ IdString net = id(match[1]);
+ IdString pinname = id(match[2]);
+ const PairPOD *belname = pairLookup(package->pins.get(), package->num_pins, pinname.index);
+ if ( belname == nullptr)
+ log_error("Pin %s not found\n", pinname.c_str(this));
+ //BelId bel = getBelByName(belname->src_id);
+ // for (auto cell : sorted(cells)) {
+ // std::cout << cell.first.str(this) << std::endl;
+ // }
+ auto it = cells.find(net);
+ if (it == cells.end()) {
+ log_info("Cell %s not found\n", net.c_str(this));
+ continue;
+ }
+ std::string bel = IdString(belname->src_id).str(this);
+ it->second->attrs[ID_BEL] = bel;
+ }
+}
+
+Arch::Arch(ArchArgs args) : args(args)
+{
+ family = args.family;
+ device = args.device;
+
+ // Load database
+ std::string chipdb = stringf("gowin/chipdb-%s.bin", family.c_str());
+ auto db = reinterpret_cast<const DatabasePOD *>(get_chipdb(chipdb));
+ if (db == nullptr)
+ log_error("Failed to load chipdb '%s'\n", chipdb.c_str());
+ if (db->family.get() != family) {
+ log_error("Database is for family '%s' but provided device is family '%s'.\n", db->family.get(),
+ family.c_str());
+ }
+ // setup id strings
+ for (size_t i = 0; i < db->num_ids; i++) {
+ IdString::initialize_add(this, db->id_strs[i].get(), uint32_t(i) + db->num_constids);
+ }
+ // setup timing info
+ speed = nullptr;
+ for (unsigned int i = 0; i < db->num_speeds; i++) {
+ const TimingClassPOD *tc = &db->speeds[i];
+ // std::cout << IdString(tc->name_id).str(this) << std::endl;
+ if (IdString(tc->name_id) == id(args.speed)) {
+ speed = tc->groups.get();
+ break;
+ }
+ }
+ if (speed == nullptr) {
+ log_error("Unsuported speed grade '%s'.\n", args.speed.c_str());
+ }
+ const VariantPOD* variant = nullptr;
+ for (unsigned int i = 0; i < db->num_variants; i++) {
+ auto var = &db->variants[i];
+ //std::cout << IdString(var->name_id).str(this) << std::endl;
+ if (IdString(var->name_id) == id(args.device)) {
+ variant = var;
+ break;
+ }
+ }
+ if (variant == nullptr) {
+ log_error("Unsuported device grade '%s'.\n", args.device.c_str());
+ }
+
+ package = nullptr;
+ for (unsigned int i = 0; i < variant->num_packages; i++) {
+ auto pkg = &variant->packages[i];
+ // std::cout << IdString(pkg->name_id).str(this) << std::endl;
+ if (IdString(pkg->name_id) == id(args.package)) {
+ package = pkg;
+ break;
+ }
+ // for (int j=0; j < pkg->num_pins; j++) {
+ // auto pin = pkg->pins[j];
+ // std::cout << IdString(pin.src_id).str(this) << " " << IdString(pin.dest_id).str(this) << std::endl;
+ // }
+ }
+ if (package == nullptr) {
+ log_error("Unsuported package '%s'.\n", args.package.c_str());
+ }
+ // setup db
+ char buf[32];
+ for (int i = 0; i < db->rows * db->cols; i++) {
+ int row = i / db->cols;
+ int col = i % db->cols;
+ const TilePOD *tile = db->grid[i].get();
+ // setup wires
+ const PairPOD *pips[2] = {tile->pips.get(), tile->clock_pips.get()};
+ unsigned int num_pips[2] = {tile->num_pips, tile->num_clock_pips};
+ for (int p = 0; p < 2; p++) {
+ for (unsigned int j = 0; j < num_pips[p]; j++) {
+ const PairPOD pip = pips[p][j];
+ int destrow = row;
+ int destcol = col;
+ IdString destid = pip.dest_id;
+ IdString gdestname = wireToGlobal(destrow, destcol, db, destid);
+ if (wires.count(gdestname) == 0)
+ addWire(gdestname, pip.dest_id, destcol, destrow);
+ int srcrow = row;
+ int srccol = col;
+ IdString srcid = pip.src_id;
+ IdString gsrcname = wireToGlobal(srcrow, srccol, db, srcid);
+ if (wires.count(gsrcname) == 0)
+ addWire(gsrcname, pip.src_id, srccol, srcrow);
+ }
+ }
+ for (unsigned int j = 0; j < tile->num_bels; j++) {
+ const BelsPOD *bel = &tile->bels[j];
+ IdString belname;
+ IdString portname;
+ int z = 0;
+ bool dff = true;
+ switch (static_cast<ConstIds>(bel->type_id)) {
+ // fall through the ++
+ case ID_LUT7:
+ z++;
+ dff = false;
+ case ID_LUT6:
+ z++;
+ dff = false;
+ case ID_LUT5:
+ z++;
+ case ID_LUT4:
+ z++;
+ case ID_LUT3:
+ z++;
+ case ID_LUT2:
+ z++;
+ case ID_LUT1:
+ z++;
+ case ID_LUT0:
+ // common LUT+DFF code
+ snprintf(buf, 32, "R%dC%d_SLICE%d", row + 1, col + 1, z);
+ belname = id(buf);
+ addBel(belname, id_SLICE, Loc(col, row, z), false);
+ snprintf(buf, 32, "R%dC%d_F%d", row + 1, col + 1, z);
+ addBelOutput(belname, id_F, id(buf));
+ snprintf(buf, 32, "R%dC%d_A%d", row + 1, col + 1, z);
+ addBelInput(belname, id_A, id(buf));
+ snprintf(buf, 32, "R%dC%d_B%d", row + 1, col + 1, z);
+ addBelInput(belname, id_B, id(buf));
+ snprintf(buf, 32, "R%dC%d_C%d", row + 1, col + 1, z);
+ addBelInput(belname, id_C, id(buf));
+ snprintf(buf, 32, "R%dC%d_D%d", row + 1, col + 1, z);
+ addBelInput(belname, id_D, id(buf));
+ if (dff) {
+ snprintf(buf, 32, "R%dC%d_CLK%d", row + 1, col + 1, z / 2);
+ addBelInput(belname, id_CLK, id(buf));
+ snprintf(buf, 32, "R%dC%d_LSR%d", row + 1, col + 1, z / 2);
+ addBelInput(belname, id_LSR, id(buf));
+ snprintf(buf, 32, "R%dC%d_CE%d", row + 1, col + 1, z / 2);
+ addBelInput(belname, id_CE, id(buf));
+ snprintf(buf, 32, "R%dC%d_Q%d", row + 1, col + 1, z);
+ addBelOutput(belname, id_Q, id(buf));
+ }
+ break;
+ case ID_IOBJ:
+ z++;
+ case ID_IOBI:
+ z++;
+ case ID_IOBH:
+ z++;
+ case ID_IOBG:
+ z++;
+ case ID_IOBF:
+ z++;
+ case ID_IOBE:
+ z++;
+ case ID_IOBD:
+ z++;
+ case ID_IOBC:
+ z++;
+ case ID_IOBB:
+ z++;
+ case ID_IOBA:
+ snprintf(buf, 32, "R%dC%d_IOB%c", row + 1, col + 1, 'A' + z);
+ belname = id(buf);
+ addBel(belname, id_IOB, Loc(col, row, z), false);
+ portname = pairLookup(bel->ports.get(), bel->num_ports, ID_O)->src_id;
+ snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
+ addBelOutput(belname, id_O, id(buf));
+ portname = pairLookup(bel->ports.get(), bel->num_ports, ID_I)->src_id;
+ snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
+ addBelInput(belname, id_I, id(buf));
+ portname = pairLookup(bel->ports.get(), bel->num_ports, ID_OE)->src_id;
+ snprintf(buf, 32, "R%dC%d_%s", row + 1, col + 1, portname.c_str(this));
+ addBelInput(belname, id_OEN, id(buf));
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ // setup pips
+ for (int i = 0; i < db->rows * db->cols; i++) {
+ int row = i / db->cols;
+ int col = i % db->cols;
+ const TilePOD *tile = db->grid[i].get();
+ const PairPOD *pips[2] = {tile->pips.get(), tile->clock_pips.get()};
+ unsigned int num_pips[2] = {tile->num_pips, tile->num_clock_pips};
+ for (int p = 0; p < 2; p++) {
+ for (unsigned int j = 0; j < num_pips[p]; j++) {
+ const PairPOD pip = pips[p][j];
+ int destrow = row;
+ int destcol = col;
+ IdString destid = pip.dest_id;
+ IdString gdestname = wireToGlobal(destrow, destcol, db, destid);
+ int srcrow = row;
+ int srccol = col;
+ IdString srcid = pip.src_id;
+ IdString gsrcname = wireToGlobal(srcrow, srccol, db, srcid);
+
+ snprintf(buf, 32, "R%dC%d_%s_%s", row + 1, col + 1, IdString(pip.src_id).c_str(this),
+ IdString(pip.dest_id).c_str(this));
+ IdString pipname = id(buf);
+ DelayInfo delay = getWireTypeDelay(pip.dest_id);
+ // local alias
+ auto local_alias = pairLookup(tile->aliases.get(), tile->num_aliases, srcid.index);
+ // std::cout << "srcid " << srcid.str(this) << std::endl;
+ if (local_alias != nullptr) {
+ srcid = local_alias->src_id;
+ gsrcname = wireToGlobal(srcrow, srccol, db, srcid);
+ }
+ // global alias
+ srcid = pip.src_id;
+ GlobalAliasPOD alias;
+ alias.dest_col = srccol;
+ alias.dest_row = srcrow;
+ alias.dest_id = srcid.index;
+ auto alias_src = genericLookup(db->aliases.get(), db->num_aliases, alias, aliasCompare);
+ if (alias_src != nullptr) {
+ srccol = alias_src->src_col;
+ srcrow = alias_src->src_row;
+ srcid = alias_src->src_id;
+ gsrcname = wireToGlobal(srcrow, srccol, db, srcid);
+ // std::cout << buf << std::endl;
+ }
+ addPip(pipname, pip.dest_id, gsrcname, gdestname, delay, Loc(col, row, j));
+ }
+ }
+ }
+ // Dummy for empty decals
+ decal_graphics[IdString()];
+}
+
+void IdString::initialize_arch(const BaseCtx *ctx)
+{
+#define X(t) initialize_add(ctx, #t, ID_##t);
+#include "constids.inc"
+#undef X
+}
+
+// ---------------------------------------------------------------
+
+BelId Arch::getBelByName(IdString name) const
+{
+ if (bels.count(name))
+ return name;
+ return BelId();
+}
+
+IdString Arch::getBelName(BelId bel) const { return bel; }
+
+Loc Arch::getBelLocation(BelId bel) const
+{
+ auto &info = bels.at(bel);
+ return Loc(info.x, info.y, info.z);
+}
+
+BelId Arch::getBelByLocation(Loc loc) const
+{
+ auto it = bel_by_loc.find(loc);
+ if (it != bel_by_loc.end())
+ return it->second;
+ return BelId();
+}
+
+const std::vector<BelId> &Arch::getBelsByTile(int x, int y) const { return bels_by_tile.at(x).at(y); }
+
+bool Arch::getBelGlobalBuf(BelId bel) const { return bels.at(bel).gb; }
+
+uint32_t Arch::getBelChecksum(BelId bel) const
+{
+ return bel.index;
+}
+
+void Arch::bindBel(BelId bel, CellInfo *cell, PlaceStrength strength)
+{
+ bels.at(bel).bound_cell = cell;
+ cell->bel = bel;
+ cell->belStrength = strength;
+ refreshUiBel(bel);
+}
+
+void Arch::unbindBel(BelId bel)
+{
+ bels.at(bel).bound_cell->bel = BelId();
+ bels.at(bel).bound_cell->belStrength = STRENGTH_NONE;
+ bels.at(bel).bound_cell = nullptr;
+ refreshUiBel(bel);
+}
+
+bool Arch::checkBelAvail(BelId bel) const { return bels.at(bel).bound_cell == nullptr; }
+
+CellInfo *Arch::getBoundBelCell(BelId bel) const { return bels.at(bel).bound_cell; }
+
+CellInfo *Arch::getConflictingBelCell(BelId bel) const { return bels.at(bel).bound_cell; }
+
+const std::vector<BelId> &Arch::getBels() const { return bel_ids; }
+
+IdString Arch::getBelType(BelId bel) const { return bels.at(bel).type; }
+
+const std::map<IdString, std::string> &Arch::getBelAttrs(BelId bel) const { return bels.at(bel).attrs; }
+
+WireId Arch::getBelPinWire(BelId bel, IdString pin) const
+{
+ const auto &bdata = bels.at(bel);
+ if (!bdata.pins.count(pin))
+ log_error("bel '%s' has no pin '%s'\n", bel.c_str(this), pin.c_str(this));
+ return bdata.pins.at(pin).wire;
+}
+
+PortType Arch::getBelPinType(BelId bel, IdString pin) const { return bels.at(bel).pins.at(pin).type; }
+
+std::vector<IdString> Arch::getBelPins(BelId bel) const
+{
+ std::vector<IdString> ret;
+ for (auto &it : bels.at(bel).pins)
+ ret.push_back(it.first);
+ return ret;
+}
+
+// ---------------------------------------------------------------
+
+WireId Arch::getWireByName(IdString name) const
+{
+ if (wires.count(name))
+ return name;
+ return WireId();
+}
+
+IdString Arch::getWireName(WireId wire) const { return wire; }
+
+IdString Arch::getWireType(WireId wire) const { return wires.at(wire).type; }
+
+const std::map<IdString, std::string> &Arch::getWireAttrs(WireId wire) const { return wires.at(wire).attrs; }
+
+uint32_t Arch::getWireChecksum(WireId wire) const
+{
+ // FIXME
+ return 0;
+}
+
+void Arch::bindWire(WireId wire, NetInfo *net, PlaceStrength strength)
+{
+ wires.at(wire).bound_net = net;
+ net->wires[wire].pip = PipId();
+ net->wires[wire].strength = strength;
+ refreshUiWire(wire);
+}
+
+void Arch::unbindWire(WireId wire)
+{
+ auto &net_wires = wires.at(wire).bound_net->wires;
+
+ auto pip = net_wires.at(wire).pip;
+ if (pip != PipId()) {
+ pips.at(pip).bound_net = nullptr;
+ refreshUiPip(pip);
+ }
+
+ net_wires.erase(wire);
+ wires.at(wire).bound_net = nullptr;
+ refreshUiWire(wire);
+}
+
+bool Arch::checkWireAvail(WireId wire) const { return wires.at(wire).bound_net == nullptr; }
+
+NetInfo *Arch::getBoundWireNet(WireId wire) const { return wires.at(wire).bound_net; }
+
+NetInfo *Arch::getConflictingWireNet(WireId wire) const { return wires.at(wire).bound_net; }
+
+const std::vector<BelPin> &Arch::getWireBelPins(WireId wire) const { return wires.at(wire).bel_pins; }
+
+const std::vector<WireId> &Arch::getWires() const { return wire_ids; }
+
+// ---------------------------------------------------------------
+
+PipId Arch::getPipByName(IdString name) const
+{
+ if (pips.count(name))
+ return name;
+ return PipId();
+}
+
+IdString Arch::getPipName(PipId pip) const { return pip; }
+
+IdString Arch::getPipType(PipId pip) const { return pips.at(pip).type; }
+
+const std::map<IdString, std::string> &Arch::getPipAttrs(PipId pip) const { return pips.at(pip).attrs; }
+
+uint32_t Arch::getPipChecksum(PipId wire) const
+{
+ return wire.index;
+}
+
+void Arch::bindPip(PipId pip, NetInfo *net, PlaceStrength strength)
+{
+ WireId wire = pips.at(pip).dstWire;
+ pips.at(pip).bound_net = net;
+ wires.at(wire).bound_net = net;
+ net->wires[wire].pip = pip;
+ net->wires[wire].strength = strength;
+ refreshUiPip(pip);
+ refreshUiWire(wire);
+}
+
+void Arch::unbindPip(PipId pip)
+{
+ WireId wire = pips.at(pip).dstWire;
+ wires.at(wire).bound_net->wires.erase(wire);
+ pips.at(pip).bound_net = nullptr;
+ wires.at(wire).bound_net = nullptr;
+ refreshUiPip(pip);
+ refreshUiWire(wire);
+}
+
+bool Arch::checkPipAvail(PipId pip) const { return pips.at(pip).bound_net == nullptr; }
+
+NetInfo *Arch::getBoundPipNet(PipId pip) const { return pips.at(pip).bound_net; }
+
+NetInfo *Arch::getConflictingPipNet(PipId pip) const { return pips.at(pip).bound_net; }
+
+WireId Arch::getConflictingPipWire(PipId pip) const { return pips.at(pip).bound_net ? pips.at(pip).dstWire : WireId(); }
+
+const std::vector<PipId> &Arch::getPips() const { return pip_ids; }
+
+Loc Arch::getPipLocation(PipId pip) const { return pips.at(pip).loc; }
+
+WireId Arch::getPipSrcWire(PipId pip) const { return pips.at(pip).srcWire; }
+
+WireId Arch::getPipDstWire(PipId pip) const { return pips.at(pip).dstWire; }
+
+DelayInfo Arch::getPipDelay(PipId pip) const { return pips.at(pip).delay; }
+
+const std::vector<PipId> &Arch::getPipsDownhill(WireId wire) const { return wires.at(wire).downhill; }
+
+const std::vector<PipId> &Arch::getPipsUphill(WireId wire) const { return wires.at(wire).uphill; }
+
+// ---------------------------------------------------------------
+
+GroupId Arch::getGroupByName(IdString name) const { return name; }
+
+IdString Arch::getGroupName(GroupId group) const { return group; }
+
+std::vector<GroupId> Arch::getGroups() const
+{
+ std::vector<GroupId> ret;
+ for (auto &it : groups)
+ ret.push_back(it.first);
+ return ret;
+}
+
+const std::vector<BelId> &Arch::getGroupBels(GroupId group) const { return groups.at(group).bels; }
+
+const std::vector<WireId> &Arch::getGroupWires(GroupId group) const { return groups.at(group).wires; }
+
+const std::vector<PipId> &Arch::getGroupPips(GroupId group) const { return groups.at(group).pips; }
+
+const std::vector<GroupId> &Arch::getGroupGroups(GroupId group) const { return groups.at(group).groups; }
+
+// ---------------------------------------------------------------
+
+delay_t Arch::estimateDelay(WireId src, WireId dst) const
+{
+ const WireInfo &s = wires.at(src);
+ const WireInfo &d = wires.at(dst);
+ int dx = abs(s.x - d.x);
+ int dy = abs(s.y - d.y);
+ return (dx + dy) * args.delayScale + args.delayOffset;
+}
+
+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);
+
+ int dx = abs(sink_loc.x - driver_loc.x);
+ int dy = abs(sink_loc.y - driver_loc.y);
+ return (dx + dy) * args.delayScale + args.delayOffset;
+}
+
+bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const { return false; }
+
+ArcBounds Arch::getRouteBoundingBox(WireId src, WireId dst) const
+{
+ ArcBounds bb;
+
+ int src_x = wires.at(src).x;
+ int src_y = wires.at(src).y;
+ int dst_x = wires.at(dst).x;
+ int dst_y = wires.at(dst).y;
+
+ bb.x0 = src_x;
+ bb.y0 = src_y;
+ bb.x1 = src_x;
+ bb.y1 = src_y;
+
+ auto extend = [&](int x, int y) {
+ bb.x0 = std::min(bb.x0, x);
+ bb.x1 = std::max(bb.x1, x);
+ bb.y0 = std::min(bb.y0, y);
+ bb.y1 = std::max(bb.y1, y);
+ };
+ extend(dst_x, dst_y);
+ return bb;
+}
+
+// ---------------------------------------------------------------
+
+bool Arch::place()
+{
+ std::string placer = str_or_default(settings, id("placer"), defaultPlacer);
+ if (placer == "heap") {
+ bool have_iobuf_or_constr = false;
+ for (auto cell : sorted(cells)) {
+ CellInfo *ci = cell.second;
+ if (ci->type == id("IOB") || ci->bel != BelId() || ci->attrs.count(id("BEL"))) {
+ have_iobuf_or_constr = true;
+ break;
+ }
+ }
+ bool retVal;
+ if (!have_iobuf_or_constr) {
+ log_warning("Unable to use HeAP due to a lack of IO buffers or constrained cells as anchors; reverting to "
+ "SA.\n");
+ retVal = placer1(getCtx(), Placer1Cfg(getCtx()));
+ } else {
+ PlacerHeapCfg cfg(getCtx());
+ cfg.ioBufTypes.insert(id("IOB"));
+ cfg.beta = 0.5;
+ retVal = placer_heap(getCtx(), cfg);
+ }
+ getCtx()->settings[getCtx()->id("place")] = 1;
+ archInfoToAttributes();
+ return retVal;
+ } else if (placer == "sa") {
+ bool retVal = placer1(getCtx(), Placer1Cfg(getCtx()));
+ getCtx()->settings[getCtx()->id("place")] = 1;
+ archInfoToAttributes();
+ return retVal;
+ } else {
+ log_error("Gowin architecture does not support placer '%s'\n", placer.c_str());
+ }
+}
+
+bool Arch::route()
+{
+ std::string router = str_or_default(settings, id("router"), defaultRouter);
+ bool result;
+ if (router == "router1") {
+ result = router1(getCtx(), Router1Cfg(getCtx()));
+ } else if (router == "router2") {
+ router2(getCtx(), Router2Cfg(getCtx()));
+ result = true;
+ } else {
+ log_error("Gowin architecture does not support router '%s'\n", router.c_str());
+ }
+ getCtx()->settings[getCtx()->id("route")] = 1;
+ archInfoToAttributes();
+ return result;
+}
+
+// ---------------------------------------------------------------
+
+const std::vector<GraphicElement> &Arch::getDecalGraphics(DecalId decal) const
+{
+ if (!decal_graphics.count(decal)) {
+ std::cerr << "No decal named " << decal.str(this) << std::endl;
+ log_error("No decal named %s!\n", decal.c_str(this));
+ }
+ return decal_graphics.at(decal);
+}
+
+DecalXY Arch::getBelDecal(BelId bel) const { return bels.at(bel).decalxy; }
+
+DecalXY Arch::getWireDecal(WireId wire) const { return wires.at(wire).decalxy; }
+
+DecalXY Arch::getPipDecal(PipId pip) const { return pips.at(pip).decalxy; }
+
+DecalXY Arch::getGroupDecal(GroupId group) const { return groups.at(group).decalxy; }
+
+// ---------------------------------------------------------------
+
+bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
+{
+ if (!cellTiming.count(cell->name))
+ return false;
+ const auto &tmg = cellTiming.at(cell->name);
+ auto fnd = tmg.combDelays.find(CellDelayKey{fromPort, toPort});
+ if (fnd != tmg.combDelays.end()) {
+ delay = fnd->second;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// Get the port class, also setting clockPort if applicable
+TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const
+{
+ if (!cellTiming.count(cell->name))
+ return TMG_IGNORE;
+ const auto &tmg = cellTiming.at(cell->name);
+ if (tmg.clockingInfo.count(port))
+ clockInfoCount = int(tmg.clockingInfo.at(port).size());
+ else
+ clockInfoCount = 0;
+ return get_or_default(tmg.portClasses, port, TMG_IGNORE);
+}
+
+TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
+{
+ NPNR_ASSERT(cellTiming.count(cell->name));
+ const auto &tmg = cellTiming.at(cell->name);
+ NPNR_ASSERT(tmg.clockingInfo.count(port));
+ return tmg.clockingInfo.at(port).at(index);
+}
+
+bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
+{
+ std::vector<const CellInfo *> cells;
+ cells.push_back(cell);
+ Loc loc = getBelLocation(bel);
+ for (auto tbel : getBelsByTile(loc.x, loc.y)) {
+ if (tbel == bel)
+ continue;
+ CellInfo *bound = getBoundBelCell(tbel);
+ if (bound != nullptr)
+ cells.push_back(bound);
+ }
+ return cellsCompatible(cells.data(), int(cells.size()));
+}
+
+bool Arch::isBelLocationValid(BelId bel) const
+{
+ std::vector<const CellInfo *> cells;
+ Loc loc = getBelLocation(bel);
+ for (auto tbel : getBelsByTile(loc.x, loc.y)) {
+ CellInfo *bound = getBoundBelCell(tbel);
+ if (bound != nullptr)
+ cells.push_back(bound);
+ }
+ return cellsCompatible(cells.data(), int(cells.size()));
+}
+
+#ifdef WITH_HEAP
+const std::string Arch::defaultPlacer = "heap";
+#else
+const std::string Arch::defaultPlacer = "sa";
+#endif
+
+const std::vector<std::string> Arch::availablePlacers = {"sa",
+#ifdef WITH_HEAP
+ "heap"
+#endif
+};
+
+const std::string Arch::defaultRouter = "router1";
+const std::vector<std::string> Arch::availableRouters = {"router1", "router2"};
+
+void Arch::assignArchInfo()
+{
+ for (auto &cell : getCtx()->cells) {
+ IdString cname = cell.first;
+ CellInfo *ci = cell.second.get();
+ if (ci->type == id("SLICE")) {
+ ci->is_slice = true;
+ ci->ff_used = ci->params.at(id_FF_USED).as_bool();
+ ci->slice_clk = get_net_or_empty(ci, id("CLK"));
+ ci->slice_ce = get_net_or_empty(ci, id("CE"));
+ ci->slice_lsr = get_net_or_empty(ci, id("LSR"));
+
+ // add timing paths
+ addCellTimingClock(cname, id_CLK);
+ IdString ports[4] = {id_A, id_B, id_C, id_D};
+ for (int i = 0; i < 4; i++) {
+ DelayInfo setup = delayLookup(speed->dff.timings.get(), speed->dff.num_timings, id_clksetpos);
+ DelayInfo hold = delayLookup(speed->dff.timings.get(), speed->dff.num_timings, id_clkholdpos);
+ // DelayInfo setup = getDelayFromNS(0.1);
+ // DelayInfo hold = getDelayFromNS(0.1);
+ addCellTimingSetupHold(cname, ports[i], id_CLK, setup, hold);
+ }
+ DelayInfo clkout = delayLookup(speed->dff.timings.get(), speed->dff.num_timings, id_clk_qpos);
+ // DelayInfo clkout = getDelayFromNS(0.1);
+ addCellTimingClockToOut(cname, id_Q, id_CLK, clkout);
+ IdString port_delay[4] = {id_a_f, id_b_f, id_c_f, id_d_f};
+ for (int i = 0; i < 4; i++) {
+ DelayInfo delay = delayLookup(speed->lut.timings.get(), speed->lut.num_timings, port_delay[i]);
+ // DelayInfo delay = getDelayFromNS(0.1);
+ addCellTimingDelay(cname, ports[i], id_F, delay);
+ }
+
+ } else {
+ ci->is_slice = false;
+ }
+ }
+}
+
+bool Arch::cellsCompatible(const CellInfo **cells, int count) const
+{
+ const NetInfo *clk[4] = {nullptr, nullptr, nullptr, nullptr};
+ const NetInfo *ce[4] = {nullptr, nullptr, nullptr, nullptr};
+ const NetInfo *lsr[4] = {nullptr, nullptr, nullptr, nullptr};
+ for (int i = 0; i < count; i++) {
+ const CellInfo *ci = cells[i];
+ if (ci->is_slice && ci->slice_clk != nullptr) {
+ Loc loc = getBelLocation(ci->bel);
+ int cls = loc.z / 2;
+ if (loc.z >= 6 && ci->ff_used) // top slice have no ff
+ return false;
+ if (clk[cls] == nullptr)
+ clk[cls] = ci->slice_clk;
+ else if (clk[cls] != ci->slice_clk)
+ return false;
+ if (ce[cls] == nullptr)
+ ce[cls] = ci->slice_ce;
+ else if (ce[cls] != ci->slice_ce)
+ return false;
+ if (lsr[cls] == nullptr)
+ lsr[cls] = ci->slice_lsr;
+ else if (lsr[cls] != ci->slice_lsr)
+ return false;
+ }
+ }
+ return true;
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/gowin/arch.h b/gowin/arch.h
new file mode 100644
index 00000000..05c9e125
--- /dev/null
+++ b/gowin/arch.h
@@ -0,0 +1,436 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Claire Wolf <claire@symbioticeda.com>
+ * Copyright (C) 2020 Pepijn de Vos <pepijn@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
+
+NEXTPNR_NAMESPACE_BEGIN
+
+template <typename T> struct RelPtr
+{
+ int32_t offset;
+
+ // void set(const T *ptr) {
+ // offset = reinterpret_cast<const char*>(ptr) -
+ // reinterpret_cast<const char*>(this);
+ // }
+
+ const T *get() const { return reinterpret_cast<const T *>(reinterpret_cast<const char *>(this) + offset); }
+
+ T *get_mut() const { return const_cast<T *>(reinterpret_cast<const T *>(reinterpret_cast<const char *>(this) + offset)); }
+
+ const T &operator[](size_t index) const { return get()[index]; }
+
+ const T &operator*() const { return *(get()); }
+
+ const T *operator->() const { return get(); }
+
+ RelPtr(const RelPtr &) = delete;
+ RelPtr &operator=(const RelPtr &) = delete;
+};
+
+NPNR_PACKED_STRUCT(struct PairPOD {
+ uint16_t dest_id;
+ uint16_t src_id;
+});
+
+NPNR_PACKED_STRUCT(struct BelsPOD {
+ uint16_t type_id;
+ uint16_t num_ports;
+ RelPtr<PairPOD> ports;
+});
+
+NPNR_PACKED_STRUCT(struct TilePOD /*TidePOD*/ {
+ uint32_t num_bels;
+ RelPtr<BelsPOD> bels;
+ uint32_t num_pips;
+ RelPtr<PairPOD> pips;
+ uint32_t num_clock_pips;
+ RelPtr<PairPOD> clock_pips;
+ uint32_t num_aliases;
+ RelPtr<PairPOD> aliases;
+});
+
+NPNR_PACKED_STRUCT(struct GlobalAliasPOD {
+ uint16_t dest_row;
+ uint16_t dest_col;
+ uint16_t dest_id;
+ uint16_t src_row;
+ uint16_t src_col;
+ uint16_t src_id;
+});
+
+NPNR_PACKED_STRUCT(struct TimingPOD {
+ uint32_t name_id;
+ // input, output
+ uint32_t ff;
+ uint32_t fr;
+ uint32_t rf;
+ uint32_t rr;
+});
+
+NPNR_PACKED_STRUCT(struct TimingGroupPOD {
+ uint32_t name_id;
+ uint32_t num_timings;
+ RelPtr<TimingPOD> timings;
+});
+
+NPNR_PACKED_STRUCT(struct TimingGroupsPOD {
+ TimingGroupPOD lut;
+ TimingGroupPOD alu;
+ TimingGroupPOD sram;
+ TimingGroupPOD dff;
+ //TimingGroupPOD dl;
+ //TimingGroupPOD iddroddr;
+ //TimingGroupPOD pll;
+ //TimingGroupPOD dll;
+ TimingGroupPOD bram;
+ //TimingGroupPOD dsp;
+ TimingGroupPOD fanout;
+ TimingGroupPOD glbsrc;
+ TimingGroupPOD hclk;
+ TimingGroupPOD iodelay;
+ //TimingGroupPOD io;
+ //TimingGroupPOD iregoreg;
+ TimingGroupPOD wire;
+});
+
+NPNR_PACKED_STRUCT(struct TimingClassPOD {
+ uint32_t name_id;
+ uint32_t num_groups;
+ RelPtr<TimingGroupsPOD> groups;
+});
+
+NPNR_PACKED_STRUCT(struct PackagePOD {
+ uint32_t name_id;
+ uint32_t num_pins;
+ RelPtr<PairPOD> pins;
+});
+
+NPNR_PACKED_STRUCT(struct VariantPOD {
+ uint32_t name_id;
+ uint32_t num_packages;
+ RelPtr<PackagePOD> packages;
+});
+
+NPNR_PACKED_STRUCT(struct DatabasePOD {
+ RelPtr<char> family;
+ uint32_t version;
+ uint16_t rows;
+ uint16_t cols;
+ RelPtr<RelPtr<TilePOD>> grid;
+ uint32_t num_aliases;
+ RelPtr<GlobalAliasPOD> aliases;
+ uint32_t num_speeds;
+ RelPtr<TimingClassPOD> speeds;
+ uint32_t num_variants;
+ RelPtr<VariantPOD> variants;
+ uint16_t num_constids;
+ uint16_t num_ids;
+ RelPtr<RelPtr<char>> id_strs;
+});
+
+struct ArchArgs
+{
+ std::string device;
+ std::string family;
+ std::string speed;
+ std::string package;
+ // y = mx + c relationship between distance and delay for interconnect
+ // delay estimates
+ double delayScale = 0.4, delayOffset = 0.4;
+};
+
+struct WireInfo;
+
+struct PipInfo
+{
+ IdString name, type;
+ std::map<IdString, std::string> attrs;
+ NetInfo *bound_net;
+ WireId srcWire, dstWire;
+ DelayInfo delay;
+ DecalXY decalxy;
+ Loc loc;
+};
+
+struct WireInfo
+{
+ IdString name, type;
+ std::map<IdString, std::string> attrs;
+ NetInfo *bound_net;
+ std::vector<PipId> downhill, uphill;
+ BelPin uphill_bel_pin;
+ std::vector<BelPin> downhill_bel_pins;
+ std::vector<BelPin> bel_pins;
+ DecalXY decalxy;
+ int x, y;
+};
+
+struct PinInfo
+{
+ IdString name;
+ WireId wire;
+ PortType type;
+};
+
+struct BelInfo
+{
+ IdString name, type;
+ std::map<IdString, std::string> attrs;
+ CellInfo *bound_cell;
+ std::unordered_map<IdString, PinInfo> pins;
+ DecalXY decalxy;
+ int x, y, z;
+ bool gb;
+};
+
+struct GroupInfo
+{
+ IdString name;
+ std::vector<BelId> bels;
+ std::vector<WireId> wires;
+ std::vector<PipId> pips;
+ std::vector<GroupId> groups;
+ DecalXY decalxy;
+};
+
+struct CellDelayKey
+{
+ IdString from, to;
+ inline bool operator==(const CellDelayKey &other) const { return from == other.from && to == other.to; }
+};
+
+NEXTPNR_NAMESPACE_END
+namespace std {
+template <> struct hash<NEXTPNR_NAMESPACE_PREFIX CellDelayKey>
+{
+ std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX CellDelayKey &dk) const noexcept
+ {
+ std::size_t seed = std::hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(dk.from);
+ seed ^= std::hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(dk.to) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+ return seed;
+ }
+};
+} // namespace std
+NEXTPNR_NAMESPACE_BEGIN
+
+struct CellTiming
+{
+ std::unordered_map<IdString, TimingPortClass> portClasses;
+ std::unordered_map<CellDelayKey, DelayInfo> combDelays;
+ std::unordered_map<IdString, std::vector<TimingClockingInfo>> clockingInfo;
+};
+
+struct Arch : BaseCtx
+{
+ std::string family;
+ std::string device;
+ const PackagePOD *package;
+ const TimingGroupsPOD *speed;
+
+ std::unordered_map<IdString, WireInfo> wires;
+ std::unordered_map<IdString, PipInfo> pips;
+ std::unordered_map<IdString, BelInfo> bels;
+ std::unordered_map<GroupId, GroupInfo> groups;
+
+ // These functions include useful errors if not found
+ WireInfo &wire_info(IdString wire);
+ PipInfo &pip_info(IdString wire);
+ BelInfo &bel_info(IdString wire);
+
+ std::vector<IdString> bel_ids, wire_ids, pip_ids;
+
+ std::unordered_map<Loc, BelId> bel_by_loc;
+ std::vector<std::vector<std::vector<BelId>>> bels_by_tile;
+
+ std::unordered_map<DecalId, std::vector<GraphicElement>> decal_graphics;
+
+ int gridDimX, gridDimY;
+ std::vector<std::vector<int>> tileBelDimZ;
+ std::vector<std::vector<int>> tilePipDimZ;
+
+ std::unordered_map<IdString, CellTiming> cellTiming;
+
+ void addWire(IdString name, IdString type, int x, int y);
+ void addPip(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayInfo delay, Loc loc);
+
+ void addBel(IdString name, IdString type, Loc loc, bool gb);
+ void addBelInput(IdString bel, IdString name, IdString wire);
+ void addBelOutput(IdString bel, IdString name, IdString wire);
+ void addBelInout(IdString bel, IdString name, IdString wire);
+
+ void addGroupBel(IdString group, IdString bel);
+ void addGroupWire(IdString group, IdString wire);
+ void addGroupPip(IdString group, IdString pip);
+ void addGroupGroup(IdString group, IdString grp);
+
+ void addDecalGraphic(DecalId decal, const GraphicElement &graphic);
+ void setWireDecal(WireId wire, DecalXY decalxy);
+ void setPipDecal(PipId pip, DecalXY decalxy);
+ void setBelDecal(BelId bel, DecalXY decalxy);
+ void setGroupDecal(GroupId group, DecalXY decalxy);
+
+ void setWireAttr(IdString wire, IdString key, const std::string &value);
+ void setPipAttr(IdString pip, IdString key, const std::string &value);
+ void setBelAttr(IdString bel, IdString key, const std::string &value);
+
+ void setDelayScaling(double scale, double offset);
+
+ void addCellTimingClock(IdString cell, IdString port);
+ void addCellTimingDelay(IdString cell, IdString fromPort, IdString toPort, DelayInfo delay);
+ void addCellTimingSetupHold(IdString cell, IdString port, IdString clock, DelayInfo setup, DelayInfo hold);
+ void addCellTimingClockToOut(IdString cell, IdString port, IdString clock, DelayInfo clktoq);
+
+ IdString wireToGlobal(int &row, int &col, const DatabasePOD* db, IdString &wire);
+ DelayInfo getWireTypeDelay(IdString wire);
+ void read_cst(std::istream &in);
+
+ // ---------------------------------------------------------------
+ // Common Arch API. Every arch must provide the following methods.
+
+ ArchArgs args;
+ Arch(ArchArgs args);
+
+ std::string getChipName() const { return device; }
+
+ IdString archId() const { return id("gowin"); }
+ ArchArgs archArgs() const { return args; }
+ IdString archArgsToId(ArchArgs args) const { return id("none"); }
+
+ int getGridDimX() const { return gridDimX; }
+ int getGridDimY() const { return gridDimY; }
+ int getTileBelDimZ(int x, int y) const { return tileBelDimZ[x][y]; }
+ int getTilePipDimZ(int x, int y) const { return tilePipDimZ[x][y]; }
+
+ BelId getBelByName(IdString name) const;
+ IdString getBelName(BelId bel) const;
+ Loc getBelLocation(BelId bel) const;
+ BelId getBelByLocation(Loc loc) const;
+ const std::vector<BelId> &getBelsByTile(int x, int y) const;
+ bool getBelGlobalBuf(BelId bel) const;
+ uint32_t getBelChecksum(BelId bel) const;
+ void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength);
+ void unbindBel(BelId bel);
+ bool checkBelAvail(BelId bel) const;
+ CellInfo *getBoundBelCell(BelId bel) const;
+ CellInfo *getConflictingBelCell(BelId bel) const;
+ const std::vector<BelId> &getBels() const;
+ IdString getBelType(BelId bel) const;
+ const std::map<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;
+ IdString getWireType(WireId wire) const;
+ const std::map<IdString, std::string> &getWireAttrs(WireId wire) const;
+ uint32_t getWireChecksum(WireId wire) const;
+ void bindWire(WireId wire, NetInfo *net, PlaceStrength strength);
+ void unbindWire(WireId wire);
+ bool checkWireAvail(WireId wire) const;
+ NetInfo *getBoundWireNet(WireId wire) const;
+ WireId getConflictingWireWire(WireId wire) const { return wire; }
+ NetInfo *getConflictingWireNet(WireId wire) const;
+ DelayInfo getWireDelay(WireId wire) const { return DelayInfo(); }
+ const std::vector<WireId> &getWires() const;
+ const std::vector<BelPin> &getWireBelPins(WireId wire) const;
+
+ PipId getPipByName(IdString name) const;
+ IdString getPipName(PipId pip) const;
+ IdString getPipType(PipId pip) const;
+ const std::map<IdString, std::string> &getPipAttrs(PipId pip) const;
+ uint32_t getPipChecksum(PipId pip) const;
+ void bindPip(PipId pip, NetInfo *net, PlaceStrength strength);
+ void unbindPip(PipId pip);
+ bool checkPipAvail(PipId pip) const;
+ NetInfo *getBoundPipNet(PipId pip) const;
+ WireId getConflictingPipWire(PipId pip) const;
+ NetInfo *getConflictingPipNet(PipId pip) const;
+ const std::vector<PipId> &getPips() const;
+ Loc getPipLocation(PipId pip) const;
+ WireId getPipSrcWire(PipId pip) const;
+ WireId getPipDstWire(PipId pip) const;
+ DelayInfo getPipDelay(PipId pip) const;
+ const std::vector<PipId> &getPipsDownhill(WireId wire) const;
+ const std::vector<PipId> &getPipsUphill(WireId wire) const;
+
+ GroupId getGroupByName(IdString name) const;
+ IdString getGroupName(GroupId group) const;
+ std::vector<GroupId> getGroups() const;
+ const std::vector<BelId> &getGroupBels(GroupId group) const;
+ const std::vector<WireId> &getGroupWires(GroupId group) const;
+ const std::vector<PipId> &getGroupPips(GroupId group) const;
+ 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 0.01; }
+ delay_t getRipupDelayPenalty() const { return 0.4; }
+ float getDelayNS(delay_t v) const { return v; }
+
+ DelayInfo getDelayFromNS(float ns) const
+ {
+ DelayInfo del;
+ del.maxRaise = ns;
+ del.maxFall = ns;
+ del.minRaise = ns;
+ del.minFall = ns;
+ return del;
+ }
+
+ uint32_t getDelayChecksum(delay_t v) const { return 0; }
+ bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const;
+
+ ArcBounds getRouteBoundingBox(WireId src, WireId dst) const;
+
+ bool pack();
+ bool place();
+ bool route();
+
+ const 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;
+
+ bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const;
+ // Get the port class, also setting clockInfoCount to the number of TimingClockingInfos associated with a port
+ 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;
+
+ bool isValidBelForCell(CellInfo *cell, BelId bel) const;
+ bool isBelLocationValid(BelId bel) const;
+
+ static const std::string defaultPlacer;
+ static const std::vector<std::string> availablePlacers;
+ static const std::string defaultRouter;
+ static const std::vector<std::string> availableRouters;
+
+ // ---------------------------------------------------------------
+ // Internal usage
+ void assignArchInfo();
+ bool cellsCompatible(const CellInfo **cells, int count) const;
+};
+
+NEXTPNR_NAMESPACE_END
diff --git a/gowin/arch_pybindings.cc b/gowin/arch_pybindings.cc
new file mode 100644
index 00000000..b968f55f
--- /dev/null
+++ b/gowin/arch_pybindings.cc
@@ -0,0 +1,163 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Claire Wolf <claire@symbioticeda.com>
+ * Copyright (C) 2018 David Shah <dave@ds0.me>
+ *
+ * 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"
+#include "pywrappers.h"
+
+using namespace pybind11::literals;
+
+NEXTPNR_NAMESPACE_BEGIN
+namespace PythonConversion {
+template <> struct string_converter<const IdString &>
+{
+ const IdString &from_str(Context *ctx, std::string name) { NPNR_ASSERT_FALSE("unsupported"); }
+
+ std::string to_str(Context *ctx, const IdString &id) { return id.str(ctx); }
+};
+} // namespace PythonConversion
+
+void arch_wrap_python(py::module &m)
+{
+ using namespace PythonConversion;
+
+ auto arch_cls = py::class_<Arch, BaseCtx>(m, "Arch").def(py::init<ArchArgs>());
+
+ auto dxy_cls = py::class_<ContextualWrapper<DecalXY>>(m, "DecalXY_");
+ readwrite_wrapper<DecalXY, decltype(&DecalXY::decal), &DecalXY::decal, conv_to_str<DecalId>,
+ conv_from_str<DecalId>>::def_wrap(dxy_cls, "decal");
+ readwrite_wrapper<DecalXY, decltype(&DecalXY::x), &DecalXY::x, pass_through<float>, pass_through<float>>::def_wrap(
+ dxy_cls, "x");
+ readwrite_wrapper<DecalXY, decltype(&DecalXY::y), &DecalXY::y, pass_through<float>, pass_through<float>>::def_wrap(
+ dxy_cls, "y");
+
+ auto ctx_cls = py::class_<Context, Arch>(m, "Context")
+ .def("checksum", &Context::checksum)
+ .def("pack", &Context::pack)
+ .def("place", &Context::place)
+ .def("route", &Context::route);
+
+ py::class_<BelPin>(m, "BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin);
+
+ py::class_<DelayInfo>(m, "DelayInfo").def("maxDelay", &DelayInfo::maxDelay).def("minDelay", &DelayInfo::minDelay);
+
+ 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<const std::vector<BelId> &>>::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<const std::vector<BelPin> &>, 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<const std::vector<WireId> &>>::def_wrap(ctx_cls, "getWires");
+
+ fn_wrapper_0a<Context, decltype(&Context::getPips), &Context::getPips,
+ wrap_context<const std::vector<PipId> &>>::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<const std::vector<PipId> &>, conv_from_str<WireId>>::def_wrap(ctx_cls,
+ "getPipsDownhill");
+ fn_wrapper_1a<Context, decltype(&Context::getPipsUphill), &Context::getPipsUphill,
+ wrap_context<const std::vector<PipId> &>, conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsUphill");
+
+ 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::getDelayFromNS), &Context::getDelayFromNS, pass_through<DelayInfo>,
+ pass_through<double>>::def_wrap(ctx_cls, "getDelayFromNS");
+
+ 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");
+
+ fn_wrapper_3a<Context, decltype(&Context::constructDecalXY), &Context::constructDecalXY, wrap_context<DecalXY>,
+ conv_from_str<DecalId>, pass_through<float>, pass_through<float>>::def_wrap(ctx_cls, "DecalXY");
+
+ typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
+ typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
+ typedef std::unordered_map<IdString, HierarchicalCell> HierarchyMap;
+
+ 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");
+
+ fn_wrapper_2a_v<Context, decltype(&Context::addClock), &Context::addClock, conv_from_str<IdString>,
+ pass_through<float>>::def_wrap(ctx_cls, "addClock");
+
+
+ WRAP_MAP_UPTR(m, CellMap, "IdCellMap");
+ WRAP_MAP_UPTR(m, NetMap, "IdNetMap");
+ WRAP_MAP(m, HierarchyMap, wrap_context<HierarchicalCell &>, "HierarchyMap");
+ WRAP_VECTOR(m, const std::vector<IdString>, conv_to_str<IdString>);
+}
+
+NEXTPNR_NAMESPACE_END
+
+#endif
diff --git a/gowin/arch_pybindings.h b/gowin/arch_pybindings.h
new file mode 100644
index 00000000..ccc443aa
--- /dev/null
+++ b/gowin/arch_pybindings.h
@@ -0,0 +1,31 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Claire Wolf <claire@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"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+NEXTPNR_NAMESPACE_END
+#endif
+#endif
diff --git a/gowin/archdefs.h b/gowin/archdefs.h
new file mode 100644
index 00000000..adeb8a0d
--- /dev/null
+++ b/gowin/archdefs.h
@@ -0,0 +1,94 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Claire Wolf <claire@symbioticeda.com>
+ * Copyright (C) 2020 Pepijn de Vos <pepijn@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
+
+NEXTPNR_NAMESPACE_BEGIN
+
+typedef float delay_t;
+
+struct DelayInfo
+{
+ delay_t minRaise = 0;
+ delay_t minFall = 0;
+ delay_t maxRaise = 0;
+ delay_t maxFall = 0;
+
+ delay_t minRaiseDelay() const { return minRaise; }
+ delay_t maxRaiseDelay() const { return maxRaise; }
+
+ delay_t minFallDelay() const { return minFall; }
+ delay_t maxFallDelay() const { return maxFall; }
+
+ delay_t minDelay() const { return std::min(minFall, minRaise); }
+ delay_t maxDelay() const { return std::max(maxFall, maxRaise); }
+
+ DelayInfo operator+(const DelayInfo &other) const
+ {
+ DelayInfo ret;
+ ret.minRaise = this->minRaise + other.minRaise;
+ ret.maxRaise = this->maxRaise + other.maxRaise;
+ ret.minFall = this->minFall + other.minFall;
+ ret.maxFall = this->maxFall + other.maxFall;
+ return ret;
+ }
+};
+
+#ifndef Q_MOC_RUN
+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
+#endif
+
+typedef IdString BelId;
+typedef IdString WireId;
+typedef IdString PipId;
+typedef IdString GroupId;
+typedef IdString DecalId;
+
+struct ArchNetInfo
+{
+};
+
+struct NetInfo;
+
+struct ArchCellInfo
+{
+ // Is the flip-flop of this slice used
+ bool ff_used;
+ // Is a slice type primitive
+ bool is_slice;
+ // Only packing rule for slice type primitives is a single clock per tile
+ const NetInfo *slice_clk;
+ const NetInfo *slice_ce;
+ const NetInfo *slice_lsr;
+};
+
+NEXTPNR_NAMESPACE_END
diff --git a/gowin/cells.cc b/gowin/cells.cc
new file mode 100644
index 00000000..db7154a0
--- /dev/null
+++ b/gowin/cells.cc
@@ -0,0 +1,142 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2019 David Shah <david@symbioticeda.com>
+ * Copyright (C) 2020 Pepijn de Vos <pepijn@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"
+#include <iostream>
+
+NEXTPNR_NAMESPACE_BEGIN
+
+void add_port(const Context *ctx, CellInfo *cell, IdString id, PortType dir)
+{
+ NPNR_ASSERT(cell->ports.count(id) == 0);
+ cell->ports[id] = PortInfo{id, nullptr, dir};
+}
+
+std::unique_ptr<CellInfo> create_generic_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 == id_SLICE) {
+ new_cell->params[id_INIT] = 0;
+ new_cell->params[id_FF_USED] = 0;
+ new_cell->params[id_FF_TYPE] = id_DFF.str(ctx);
+
+ IdString names[4] = {id_A, id_B, id_C, id_D};
+ for (int i = 0; i < 4; i++) {
+ add_port(ctx, new_cell.get(), names[i], PORT_IN);
+ }
+
+ add_port(ctx, new_cell.get(), id_CLK, PORT_IN);
+
+ add_port(ctx, new_cell.get(), id_F, PORT_OUT);
+ add_port(ctx, new_cell.get(), id_Q, PORT_OUT);
+ add_port(ctx, new_cell.get(), id_CE, PORT_IN);
+ add_port(ctx, new_cell.get(), id_LSR, PORT_IN);
+ } else if (type == id_IOB) {
+ new_cell->params[id_INPUT_USED] = 0;
+ new_cell->params[id_OUTPUT_USED] = 0;
+ new_cell->params[id_ENABLE_USED] = 0;
+
+ add_port(ctx, new_cell.get(), id_PAD, PORT_INOUT);
+ add_port(ctx, new_cell.get(), id_I, PORT_IN);
+ add_port(ctx, new_cell.get(), id_EN, PORT_IN);
+ add_port(ctx, new_cell.get(), id_O, PORT_OUT);
+ } else {
+ log_error("unable to create generic cell of type %s", type.c_str(ctx));
+ }
+ return new_cell;
+}
+
+void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
+{
+ lc->params[id_INIT] = lut->params[id_INIT];
+
+ IdString sim_names[4] = {id_I0, id_I1, id_I2, id_I3};
+ IdString wire_names[4] = {id_A, id_B, id_C, id_D};
+ for (int i = 0; i < 4; i++) {
+ replace_port(lut, sim_names[i], lc, wire_names[i]);
+ }
+
+ if (no_dff) {
+ lc->params[id_FF_USED] = 0;
+ replace_port(lut, id_F, lc, id_F);
+ }
+}
+
+void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut)
+{
+ lc->params[id_FF_USED] = 1;
+ lc->params[id_FF_TYPE] = dff->type.str(ctx);
+ replace_port(dff, id_CLK, lc, id_CLK);
+ replace_port(dff, id_CE, lc, id_CE);
+ replace_port(dff, id_SET, lc, id_LSR);
+ replace_port(dff, id_RESET, lc, id_LSR);
+ replace_port(dff, id_CLEAR, lc, id_LSR);
+ replace_port(dff, id_PRESET, lc, id_LSR);
+ if (pass_thru_lut) {
+ // Fill LUT with alternating 10
+ const int init_size = 1 << 4;
+ std::string init;
+ init.reserve(init_size);
+ for (int i = 0; i < init_size; i += 2)
+ init.append("10");
+ lc->params[id_INIT] = Property::from_string(init);
+
+ replace_port(dff, id_D, lc, id_A);
+ }
+
+ replace_port(dff, id_Q, lc, id_Q);
+}
+
+void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *iob, std::unordered_set<IdString> &todelete_cells)
+{
+ if (nxio->type == id_IBUF) {
+ iob->params[id_INPUT_USED] = 1;
+ replace_port(nxio, id_O, iob, id_O);
+ } else if (nxio->type == id_OBUF) {
+ iob->params[id_OUTPUT_USED] = 1;
+ replace_port(nxio, id_I, iob, id_I);
+ } else if (nxio->type == id_TBUF) {
+ iob->params[id_ENABLE_USED] = 1;
+ iob->params[id_OUTPUT_USED] = 1;
+ replace_port(nxio, id_I, iob, id_I);
+ replace_port(nxio, id_OEN, iob, id_OEN);
+ } else if (nxio->type == id_IOBUF) {
+ iob->params[id_ENABLE_USED] = 1;
+ iob->params[id_INPUT_USED] = 1;
+ iob->params[id_OUTPUT_USED] = 1;
+ replace_port(nxio, id_I, iob, id_I);
+ replace_port(nxio, id_O, iob, id_O);
+ replace_port(nxio, id_OEN, iob, id_OEN);
+ } else {
+ NPNR_ASSERT(false);
+ }
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/gowin/cells.h b/gowin/cells.h
new file mode 100644
index 00000000..30b29f5b
--- /dev/null
+++ b/gowin/cells.h
@@ -0,0 +1,94 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2019 David Shah <david@symbioticeda.com>
+ * Copyright (C) 2020 Pepijn de Vos <pepijn@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 GENERIC_CELLS_H
+#define GENERIC_CELLS_H
+
+NEXTPNR_NAMESPACE_BEGIN
+
+// Create a generic arch cell and return it
+// Name will be automatically assigned if not specified
+std::unique_ptr<CellInfo> create_generic_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)
+{
+ switch (cell->type.index) {
+ case ID_LUT1:
+ case ID_LUT2:
+ case ID_LUT3:
+ case ID_LUT4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+// Return true if a cell is a flipflop
+inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell)
+{
+ switch (cell->type.index) {
+ case ID_DFF:
+ case ID_DFFE:
+ case ID_DFFS:
+ case ID_DFFSE:
+ case ID_DFFR:
+ case ID_DFFRE:
+ case ID_DFFP:
+ case ID_DFFPE:
+ case ID_DFFC:
+ case ID_DFFCE:
+ case ID_DFFN:
+ case ID_DFFNE:
+ case ID_DFFNS:
+ case ID_DFFNSE:
+ case ID_DFFNR:
+ case ID_DFFNRE:
+ case ID_DFFNP:
+ case ID_DFFNPE:
+ case ID_DFFNC:
+ case ID_DFFNCE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == id_SLICE; }
+
+// Convert a LUT primitive to (part of) an GENERIC_SLICE, 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 DFF primitive to (part of) an GENERIC_SLICE, 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 Gowin IO buffer to a IOB bel
+void gwio_to_iob(Context *ctx, CellInfo *nxio, CellInfo *sbio, std::unordered_set<IdString> &todelete_cells);
+
+NEXTPNR_NAMESPACE_END
+
+#endif
diff --git a/gowin/constids.inc b/gowin/constids.inc
new file mode 100644
index 00000000..bf26e9ca
--- /dev/null
+++ b/gowin/constids.inc
@@ -0,0 +1,429 @@
+X(A0)
+X(B0)
+X(C0)
+X(D0)
+X(A1)
+X(B1)
+X(C1)
+X(D1)
+X(A2)
+X(B2)
+X(C2)
+X(D2)
+X(A3)
+X(B3)
+X(C3)
+X(D3)
+X(A4)
+X(B4)
+X(C4)
+X(D4)
+X(A5)
+X(B5)
+X(C5)
+X(D5)
+X(A6)
+X(B6)
+X(C6)
+X(D6)
+X(A7)
+X(B7)
+X(C7)
+X(D7)
+X(F0)
+X(F1)
+X(F2)
+X(F3)
+X(F4)
+X(F5)
+X(F6)
+X(F7)
+X(Q0)
+X(Q1)
+X(Q2)
+X(Q3)
+X(Q4)
+X(Q5)
+X(Q6)
+X(Q7)
+X(OF0)
+X(OF1)
+X(OF2)
+X(OF3)
+X(OF4)
+X(OF5)
+X(OF6)
+X(OF7)
+X(X01)
+X(X02)
+X(X03)
+X(X04)
+X(X05)
+X(X06)
+X(X07)
+X(X08)
+X(N100)
+X(SN10)
+X(SN20)
+X(N130)
+X(S100)
+X(S130)
+X(E100)
+X(EW10)
+X(EW20)
+X(E130)
+X(W100)
+X(W130)
+X(N200)
+X(N210)
+X(N220)
+X(N230)
+X(N240)
+X(N250)
+X(N260)
+X(N270)
+X(S200)
+X(S210)
+X(S220)
+X(S230)
+X(S240)
+X(S250)
+X(S260)
+X(S270)
+X(E200)
+X(E210)
+X(E220)
+X(E230)
+X(E240)
+X(E250)
+X(E260)
+X(E270)
+X(W200)
+X(W210)
+X(W220)
+X(W230)
+X(W240)
+X(W250)
+X(W260)
+X(W270)
+X(N800)
+X(N810)
+X(N820)
+X(N830)
+X(S800)
+X(S810)
+X(S820)
+X(S830)
+X(E800)
+X(E810)
+X(E820)
+X(E830)
+X(W800)
+X(W810)
+X(W820)
+X(W830)
+X(CLK0)
+X(CLK1)
+X(CLK2)
+X(LSR0)
+X(LSR1)
+X(LSR2)
+X(CE0)
+X(CE1)
+X(CE2)
+X(SEL0)
+X(SEL1)
+X(SEL2)
+X(SEL3)
+X(SEL4)
+X(SEL5)
+X(SEL6)
+X(SEL7)
+X(N101)
+X(N131)
+X(S101)
+X(S131)
+X(E101)
+X(E131)
+X(W101)
+X(W131)
+X(N201)
+X(N211)
+X(N221)
+X(N231)
+X(N241)
+X(N251)
+X(N261)
+X(N271)
+X(S201)
+X(S211)
+X(S221)
+X(S231)
+X(S241)
+X(S251)
+X(S261)
+X(S271)
+X(E201)
+X(E211)
+X(E221)
+X(E231)
+X(E241)
+X(E251)
+X(E261)
+X(E271)
+X(W201)
+X(W211)
+X(W221)
+X(W231)
+X(W241)
+X(W251)
+X(W261)
+X(W271)
+X(N202)
+X(N212)
+X(N222)
+X(N232)
+X(N242)
+X(N252)
+X(N262)
+X(N272)
+X(S202)
+X(S212)
+X(S222)
+X(S232)
+X(S242)
+X(S252)
+X(S262)
+X(S272)
+X(E202)
+X(E212)
+X(E222)
+X(E232)
+X(E242)
+X(E252)
+X(E262)
+X(E272)
+X(W202)
+X(W212)
+X(W222)
+X(W232)
+X(W242)
+X(W252)
+X(W262)
+X(W272)
+X(N804)
+X(N814)
+X(N824)
+X(N834)
+X(S804)
+X(S814)
+X(S824)
+X(S834)
+X(E804)
+X(E814)
+X(E824)
+X(E834)
+X(W804)
+X(W814)
+X(W824)
+X(W834)
+X(N808)
+X(N818)
+X(N828)
+X(N838)
+X(S808)
+X(S818)
+X(S828)
+X(S838)
+X(E808)
+X(E818)
+X(E828)
+X(E838)
+X(W808)
+X(W818)
+X(W828)
+X(W838)
+X(E110)
+X(W110)
+X(E120)
+X(W120)
+X(S110)
+X(N110)
+X(S120)
+X(N120)
+X(E111)
+X(W111)
+X(E121)
+X(W121)
+X(S111)
+X(N111)
+X(S121)
+X(N121)
+X(LB01)
+X(LB11)
+X(LB21)
+X(LB31)
+X(LB41)
+X(LB51)
+X(LB61)
+X(LB71)
+X(GB00)
+X(GB10)
+X(GB20)
+X(GB30)
+X(GB40)
+X(GB50)
+X(GB60)
+X(GB70)
+X(VCC)
+X(VSS)
+X(LT00)
+X(LT10)
+X(LT20)
+X(LT30)
+X(LT02)
+X(LT13)
+X(LT01)
+X(LT04)
+X(LBO0)
+X(LBO1)
+X(SS00)
+X(SS40)
+X(GT00)
+X(GT10)
+X(GBO0)
+X(GBO1)
+X(DI0)
+X(DI1)
+X(DI2)
+X(DI3)
+X(DI4)
+X(DI5)
+X(DI6)
+X(DI7)
+X(CIN0)
+X(CIN1)
+X(CIN2)
+X(CIN3)
+X(CIN4)
+X(CIN5)
+X(COUT0)
+X(COUT1)
+X(COUT2)
+X(COUT3)
+X(COUT4)
+X(COUT5)
+// slice items
+X(SLICE)
+X(CLK)
+X(LSR)
+X(CE)
+X(Q)
+X(F)
+X(A)
+X(B)
+X(C)
+X(D)
+// iob items
+X(IOB)
+X(I)
+X(O)
+X(IO)
+X(OE)
+// bels
+X(DFF0)
+X(DFF1)
+X(DFF2)
+X(DFF3)
+X(DFF4)
+X(DFF5)
+
+X(LUT0)
+X(LUT1)
+X(LUT2)
+X(LUT3)
+X(LUT4)
+X(LUT5)
+X(LUT6)
+X(LUT7)
+
+X(IOBA)
+X(IOBB)
+X(IOBC)
+X(IOBD)
+X(IOBE)
+X(IOBF)
+X(IOBG)
+X(IOBH)
+X(IOBI)
+X(IOBJ)
+
+// DFF types
+X(DFF)
+X(DFFE)
+X(DFFS)
+X(DFFSE)
+X(DFFR)
+X(DFFRE)
+X(DFFP)
+X(DFFPE)
+X(DFFC)
+X(DFFCE)
+X(DFFN)
+X(DFFNE)
+X(DFFNS)
+X(DFFNSE)
+X(DFFNR)
+X(DFFNRE)
+X(DFFNP)
+X(DFFNPE)
+X(DFFNC)
+X(DFFNCE)
+
+// IOB types
+X(IBUF)
+X(OBUF)
+X(IOBUF)
+X(TBUF)
+
+// primitive attributes
+X(INIT)
+X(FF_USED)
+X(FF_TYPE)
+X(INPUT_USED)
+X(OUTPUT_USED)
+X(ENABLE_USED)
+X(BEL)
+
+// ports
+X(EN)
+X(E)
+X(Y)
+X(PAD)
+X(RESET)
+X(SET)
+X(PRESET)
+X(CLEAR)
+X(I0)
+X(I1)
+X(I2)
+X(I3)
+X(OEN)
+
+// timing
+X(X0)
+X(FX1)
+X(X2)
+X(X8)
+X(PIO_CENT_PCLK)
+X(CENT_SPINE_PCLK)
+X(SPINE_TAP_PCLK)
+X(TAP_BRANCH_PCLK)
+X(BRANCH_PCLK)
+X(clksetpos)
+X(clkholdpos)
+X(clk_qpos)
+X(a_f)
+X(b_f)
+X(c_f)
+X(d_f)
diff --git a/gowin/family.cmake b/gowin/family.cmake
new file mode 100644
index 00000000..be607e6a
--- /dev/null
+++ b/gowin/family.cmake
@@ -0,0 +1,53 @@
+add_subdirectory(${family})
+message(STATUS "Using Gowin chipdb: ${GOWIN_CHIPDB}")
+
+set(chipdb_sources)
+set(chipdb_binaries)
+file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${family}/chipdb)
+foreach(device ${GOWIN_DEVICES})
+ set(chipdb_bba ${GOWIN_CHIPDB}/chipdb-${device}.bba)
+ set(chipdb_bin ${family}/chipdb/chipdb-${device}.bin)
+ set(chipdb_cc ${family}/chipdb/chipdb-${device}.cc)
+ if(BBASM_MODE STREQUAL "binary")
+ add_custom_command(
+ OUTPUT ${chipdb_bin}
+ COMMAND bbasm ${BBASM_ENDIAN_FLAG} ${chipdb_bba} ${chipdb_bin}
+ DEPENDS bbasm chipdb-${family}-bbas ${chipdb_bba})
+ list(APPEND chipdb_binaries ${chipdb_bin})
+ elseif(BBASM_MODE STREQUAL "embed")
+ add_custom_command(
+ OUTPUT ${chipdb_cc} ${chipdb_bin}
+ COMMAND bbasm ${BBASM_ENDIAN_FLAG} --e ${chipdb_bba} ${chipdb_cc} ${chipdb_bin}
+ DEPENDS bbasm chipdb-${family}-bbas ${chipdb_bba})
+ list(APPEND chipdb_sources ${chipdb_cc})
+ list(APPEND chipdb_binaries ${chipdb_bin})
+ elseif(BBASM_MODE STREQUAL "string")
+ add_custom_command(
+ OUTPUT ${chipdb_cc}
+ COMMAND bbasm ${BBASM_ENDIAN_FLAG} --c ${chipdb_bba} ${chipdb_cc}
+ DEPENDS bbasm chipdb-${family}-bbas ${chipdb_bba})
+ list(APPEND chipdb_sources ${chipdb_cc})
+ endif()
+endforeach()
+if(WIN32)
+ set(chipdb_rc ${CMAKE_CURRENT_BINARY_DIR}/${family}/resource/chipdb.rc)
+ list(APPEND chipdb_sources ${chipdb_rc})
+
+ file(WRITE ${chipdb_rc})
+ foreach(device ${GOWIN_DEVICES})
+ file(APPEND ${chipdb_rc}
+ "${family}/chipdb-${device}.bin RCDATA \"${CMAKE_CURRENT_BINARY_DIR}/${family}/chipdb/chipdb-${device}.bin\"")
+ endforeach()
+endif()
+
+add_custom_target(chipdb-${family}-bins DEPENDS ${chipdb_sources} ${chipdb_binaries})
+
+add_library(chipdb-${family} OBJECT ${GOWIN_CHIPDB} ${chipdb_sources})
+add_dependencies(chipdb-${family} chipdb-${family}-bins)
+target_compile_options(chipdb-${family} PRIVATE -g0 -O0 -w)
+target_compile_definitions(chipdb-${family} PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family})
+target_include_directories(chipdb-${family} PRIVATE ${family})
+
+foreach(family_target ${family_targets})
+ target_sources(${family_target} PRIVATE $<TARGET_OBJECTS:chipdb-${family}>)
+endforeach()
diff --git a/gowin/main.cc b/gowin/main.cc
new file mode 100644
index 00000000..c845821d
--- /dev/null
+++ b/gowin/main.cc
@@ -0,0 +1,90 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Claire Wolf <claire@symbioticeda.com>
+ * Copyright (C) 2020 Pepijn de Vos <pepijn@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 <regex>
+#include "command.h"
+#include "design_utils.h"
+#include "log.h"
+#include "timing.h"
+
+USING_NEXTPNR_NAMESPACE
+
+class GowinCommandHandler : public CommandHandler
+{
+ public:
+ GowinCommandHandler(int argc, char **argv);
+ virtual ~GowinCommandHandler(){};
+ std::unique_ptr<Context> createContext(std::unordered_map<std::string, Property> &values) override;
+ void setupArchContext(Context *ctx) override{};
+ void customAfterLoad(Context *ctx) override;
+
+ protected:
+ po::options_description getArchOptions() override;
+};
+
+GowinCommandHandler::GowinCommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {}
+
+po::options_description GowinCommandHandler::getArchOptions()
+{
+ po::options_description specific("Architecture specific options");
+ specific.add_options()("device", po::value<std::string>(), "device name");
+ specific.add_options()("cst", po::value<std::string>(), "physical constraints file");
+ return specific;
+}
+
+std::unique_ptr<Context> GowinCommandHandler::createContext(std::unordered_map<std::string, Property> &values)
+{
+ std::regex devicere = std::regex("GW1N([A-Z]*)-(LV|UV)([0-9])([A-Z]{2}[0-9]+)(C[0-9]/I[0-9])");
+ std::smatch match;
+ std::string device = vm["device"].as<std::string>();
+ if(!std::regex_match(device, match, devicere)) {
+ log_error("Invalid device %s\n", device.c_str());
+ }
+ ArchArgs chipArgs;
+ char buf[32];
+ snprintf(buf, 32, "GW1N%s-%s", match[1].str().c_str(), match[3].str().c_str());
+ chipArgs.device = buf;
+ snprintf(buf, 32, "GW1N-%s", match[3].str().c_str());
+ chipArgs.family = buf;
+ chipArgs.package = match[4];
+ chipArgs.speed = match[5];
+ return std::unique_ptr<Context>(new Context(chipArgs));
+}
+
+void GowinCommandHandler::customAfterLoad(Context *ctx)
+{
+ if (vm.count("cst")) {
+ std::string filename = vm["cst"].as<std::string>();
+ std::ifstream in(filename);
+ if (!in)
+ log_error("Failed to open input CST file %s.\n", filename.c_str());
+ ctx->read_cst(in);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ GowinCommandHandler handler(argc, argv);
+ return handler.exec();
+}
+#endif
diff --git a/gowin/pack.cc b/gowin/pack.cc
new file mode 100644
index 00000000..ce96fd62
--- /dev/null
+++ b/gowin/pack.cc
@@ -0,0 +1,291 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018-19 David Shah <david@symbioticeda.com>
+ * Copyright (C) 2020 Pepijn de Vos <pepijn@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 "design_utils.h"
+#include "log.h"
+#include "util.h"
+#include <iostream>
+
+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", ctx->nameOf(ci), ci->type.c_str(ctx));
+ if (is_lut(ctx, ci)) {
+ std::unique_ptr<CellInfo> packed =
+ create_generic_cell(ctx, ctx->id("SLICE"), 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", ctx->nameOf(ci), ctx->nameOf(packed.get()));
+ // See if we can pack into a DFF
+ // TODO: LUT cascade
+ NetInfo *o = ci->ports.at(ctx->id("F")).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", ctx->nameOf(dff));
+ 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", ctx->nameOf(dff), ctx->nameOf(packed.get()));
+ 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_generic_cell(ctx, ctx->id("SLICE"), 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", ctx->nameOf(ci), ctx->nameOf(packed.get()));
+ 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);
+ }
+}
+
+// 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", ctx->nameOf(orig), ctx->nameOf(uc));
+ if ((is_lut(ctx, uc) || is_lc(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_generic_cell(ctx, ctx->id("SLICE"), "$PACKER_GND");
+ gnd_cell->params[ctx->id("INIT")] = Property(0, 1 << 4);
+ 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 = ctx->id("F");
+ gnd_cell->ports.at(ctx->id("F")).net = gnd_net.get();
+
+ std::unique_ptr<CellInfo> vcc_cell = create_generic_cell(ctx, ctx->id("SLICE"), "$PACKER_VCC");
+ // Fill with 1s
+ vcc_cell->params[ctx->id("INIT")] = Property(Property::S1).extract(0, (1 << 4), Property::S1);
+ 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 = ctx->id("F");
+ vcc_cell->ports.at(ctx->id("F")).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(const Context *ctx, CellInfo *cell)
+{
+ return cell->type == ctx->id("$nextpnr_ibuf") || cell->type == ctx->id("$nextpnr_obuf") ||
+ cell->type == ctx->id("$nextpnr_iobuf");
+}
+
+static bool is_gowin_iob(const Context *ctx, const CellInfo *cell) {
+ switch (cell->type.index)
+ {
+ case ID_IBUF:
+ case ID_OBUF:
+ case ID_IOBUF:
+ case ID_TBUF:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+// Pack IO buffers
+static void pack_io(Context *ctx)
+{
+ std::unordered_set<IdString> packed_cells;
+ std::unordered_set<IdString> delete_nets;
+
+ 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_gowin_iob(ctx, ci)) {
+ CellInfo *iob = nullptr;
+ switch (ci->type.index)
+ {
+ case ID_IBUF:
+ iob = net_driven_by(ctx, ci->ports.at(id_I).net, is_nextpnr_iob, id_O);
+ break;
+ case ID_OBUF:
+ iob = net_only_drives(ctx, ci->ports.at(id_O).net, is_nextpnr_iob, id_I);
+ break;
+ case ID_IOBUF:
+ case ID_TBUF:
+ log_error("untested tristate stuff");
+ break;
+ default:
+ break;
+ }
+ if (iob != nullptr) {
+ // delete the $nexpnr_[io]buf
+ for (auto &p : iob->ports) {
+ IdString netname = p.second.net->name;
+ disconnect_port(ctx, iob, p.first);
+ delete_nets.insert(netname);
+ }
+ packed_cells.insert(iob->name);
+ }
+ // Create a IOB buffer
+ std::unique_ptr<CellInfo> ice_cell =
+ create_generic_cell(ctx, id_IOB, ci->name.str(ctx) + "$iob");
+ gwio_to_iob(ctx, ci, ice_cell.get(), packed_cells);
+ new_cells.push_back(std::move(ice_cell));
+ auto gwiob = new_cells.back().get();
+
+ packed_cells.insert(ci->name);
+ if (iob != nullptr)
+ std::copy(iob->attrs.begin(), iob->attrs.end(), std::inserter(gwiob->attrs, gwiob->attrs.begin()));
+ }
+ }
+ for (auto pcell : packed_cells) {
+ ctx->cells.erase(pcell);
+ }
+ for (auto dnet : delete_nets) {
+ ctx->nets.erase(dnet);
+ }
+ 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);
+ pack_io(ctx);
+ pack_lut_lutffs(ctx);
+ pack_nonlut_ffs(ctx);
+ ctx->settings[ctx->id("pack")] = 1;
+ ctx->assignArchInfo();
+ log_info("Checksum: 0x%08x\n", ctx->checksum());
+ return true;
+ } catch (log_execution_error_exception) {
+ return false;
+ }
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/gui/gowin/family.cmake b/gui/gowin/family.cmake
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/gui/gowin/family.cmake
diff --git a/gui/gowin/mainwindow.cc b/gui/gowin/mainwindow.cc
new file mode 100644
index 00000000..3017ded6
--- /dev/null
+++ b/gui/gowin/mainwindow.cc
@@ -0,0 +1,49 @@
+/*
+ * 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"
+
+#include <QMessageBox>
+#include <cstdlib>
+
+static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
+
+NEXTPNR_NAMESPACE_BEGIN
+
+MainWindow::MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
+ : BaseMainWindow(std::move(context), handler, parent)
+{
+ initMainResource();
+ QMessageBox::critical(0, "Error - FIXME", "No GUI support for nextpnr-gowin");
+ std::exit(1);
+}
+
+MainWindow::~MainWindow() {}
+
+void MainWindow::newContext(Context *ctx)
+{
+ std::string title = "nextpnr-gowin - " + ctx->getChipName();
+ setWindowTitle(title.c_str());
+}
+
+void MainWindow::createMenu() {}
+
+void MainWindow::new_proj() {}
+
+NEXTPNR_NAMESPACE_END
diff --git a/gui/gowin/mainwindow.h b/gui/gowin/mainwindow.h
new file mode 100644
index 00000000..4d1cf598
--- /dev/null
+++ b/gui/gowin/mainwindow.h
@@ -0,0 +1,45 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include "../basewindow.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+class MainWindow : public BaseMainWindow
+{
+ Q_OBJECT
+
+ public:
+ explicit MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent = 0);
+ virtual ~MainWindow();
+
+ public:
+ void createMenu();
+
+ protected Q_SLOTS:
+ void new_proj() override;
+ void newContext(Context *ctx);
+};
+
+NEXTPNR_NAMESPACE_END
+
+#endif // MAINWINDOW_H
diff --git a/gui/gowin/nextpnr.qrc b/gui/gowin/nextpnr.qrc
new file mode 100644
index 00000000..03585ec0
--- /dev/null
+++ b/gui/gowin/nextpnr.qrc
@@ -0,0 +1,2 @@
+<RCC>
+</RCC>