aboutsummaryrefslogtreecommitdiffstats
path: root/gowin
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 /gowin
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
Diffstat (limited to 'gowin')
-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
12 files changed, 3115 insertions, 0 deletions
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