aboutsummaryrefslogtreecommitdiffstats
path: root/machxo2/arch.cc
diff options
context:
space:
mode:
authorgatecat <gatecat@ds0.me>2021-02-11 11:10:32 +0000
committergatecat <gatecat@ds0.me>2021-02-12 10:36:59 +0000
commit510969ab9704865f87c7c0bd09e0185b729feffc (patch)
tree39a6e35998d92f5066f21f8055a17fb7a7428f98 /machxo2/arch.cc
parentc956cae8244c094783edc7101fd0ca542c24e55b (diff)
downloadnextpnr-510969ab9704865f87c7c0bd09e0185b729feffc.tar.gz
nextpnr-510969ab9704865f87c7c0bd09e0185b729feffc.tar.bz2
nextpnr-510969ab9704865f87c7c0bd09e0185b729feffc.zip
Create machxo2 backend (renamed from generic).
Signed-off-by: William D. Jones <thor0505@comcast.net>
Diffstat (limited to 'machxo2/arch.cc')
-rw-r--r--machxo2/arch.cc730
1 files changed, 730 insertions, 0 deletions
diff --git a/machxo2/arch.cc b/machxo2/arch.cc
new file mode 100644
index 00000000..6979673a
--- /dev/null
+++ b/machxo2/arch.cc
@@ -0,0 +1,730 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <iostream>
+#include <math.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)
+{
+ 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.x + 1);
+ tilePipDimZ[loc.x][loc.y] = std::max(tilePipDimZ[loc.x][loc.y], loc.z + 1);
+}
+
+void Arch::addAlias(IdString name, IdString type, IdString srcWire, IdString dstWire, DelayInfo delay)
+{
+ 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;
+
+ wire_info(srcWire).aliases.push_back(name);
+ pip_ids.push_back(name);
+}
+
+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::setLutK(int K) { args.K = K; }
+
+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;
+}
+
+// ---------------------------------------------------------------
+
+Arch::Arch(ArchArgs args) : chipName("generic"), args(args)
+{
+ // Dummy for empty decals
+ decal_graphics[IdString()];
+}
+
+void IdString::initialize_arch(const BaseCtx *ctx) {}
+
+// ---------------------------------------------------------------
+
+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
+{
+ // FIXME
+ return 0;
+}
+
+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
+{
+ // FIXME
+ return 0;
+}
+
+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; }
+
+const std::vector<PipId> &Arch::getWireAliases(WireId wire) const { return wires.at(wire).aliases; }
+
+// ---------------------------------------------------------------
+
+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("GENERIC_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("GENERIC_IOB"));
+ 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("Generic 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("iCE40 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) {
+ CellInfo *ci = cell.second.get();
+ if (ci->type == id("GENERIC_SLICE")) {
+ ci->is_slice = true;
+ ci->slice_clk = get_net_or_empty(ci, id("CLK"));
+ } else {
+ ci->is_slice = false;
+ }
+ ci->user_group = int_or_default(ci->attrs, id("PACK_GROUP"), -1);
+ }
+}
+
+bool Arch::cellsCompatible(const CellInfo **cells, int count) const
+{
+ const NetInfo *clk = nullptr;
+ int group = -1;
+ for (int i = 0; i < count; i++) {
+ const CellInfo *ci = cells[i];
+ if (ci->is_slice && ci->slice_clk != nullptr) {
+ if (clk == nullptr)
+ clk = ci->slice_clk;
+ else if (clk != ci->slice_clk)
+ return false;
+ }
+ if (ci->user_group != -1) {
+ if (group == -1)
+ group = ci->user_group;
+ else if (group != ci->user_group)
+ return false;
+ }
+ }
+ return true;
+}
+
+NEXTPNR_NAMESPACE_END