aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgatecat <gatecat@ds0.me>2021-03-15 17:00:52 +0000
committerGitHub <noreply@github.com>2021-03-15 17:00:52 +0000
commita8e35062c6a1a21838346dd7536bb2fcc7f820ed (patch)
treec5466a8ed8f9108410561eb8d9d9ff5e2810d297
parent3cf4a336665e07f8d210aa9d3336f3d5b0e82ea7 (diff)
parentfe4608386eb163c70a75ed84beb07516af378b36 (diff)
downloadnextpnr-a8e35062c6a1a21838346dd7536bb2fcc7f820ed.tar.gz
nextpnr-a8e35062c6a1a21838346dd7536bb2fcc7f820ed.tar.bz2
nextpnr-a8e35062c6a1a21838346dd7536bb2fcc7f820ed.zip
Merge pull request #621 from litghost/fix_header_nightmare
Split nextpnr.h to allow for linear inclusion.
-rw-r--r--common/arch_api.h150
-rw-r--r--common/base_arch.h388
-rw-r--r--common/basectx.cc334
-rw-r--r--common/basectx.h238
-rw-r--r--common/constraints.h15
-rw-r--r--common/context.cc409
-rw-r--r--common/context.h103
-rw-r--r--common/deterministic_rng.h103
-rw-r--r--common/exclusive_state_groups.h12
-rw-r--r--common/exclusive_state_groups.impl.h10
-rw-r--r--common/idstring.cc51
-rw-r--r--common/idstring.h73
-rw-r--r--common/idstringlist.cc61
-rw-r--r--common/idstringlist.h85
-rw-r--r--common/log.h3
-rw-r--r--common/nextpnr.cc866
-rw-r--r--common/nextpnr.h1513
-rw-r--r--common/nextpnr_assertions.cc33
-rw-r--r--common/nextpnr_assertions.h62
-rw-r--r--common/nextpnr_base_types.h146
-rw-r--r--common/nextpnr_namespaces.cc23
-rw-r--r--common/nextpnr_namespaces.h53
-rw-r--r--common/nextpnr_types.cc69
-rw-r--r--common/nextpnr_types.h259
-rw-r--r--common/property.cc81
-rw-r--r--common/property.h131
-rw-r--r--common/relptr.h37
-rw-r--r--common/sso_array.h112
-rw-r--r--common/str_ring_buffer.cc34
-rw-r--r--common/str_ring_buffer.h45
-rw-r--r--ecp5/arch.h13
-rw-r--r--ecp5/archdefs.h14
-rw-r--r--ecp5/lpf.cc4
-rw-r--r--fpga_interchange/arch.h16
-rw-r--r--fpga_interchange/archdefs.h13
-rw-r--r--fpga_interchange/dedicated_interconnect.h15
-rw-r--r--fpga_interchange/luts.h14
-rw-r--r--fpga_interchange/site_router.h9
-rw-r--r--generic/arch.h15
-rw-r--r--generic/archdefs.h11
-rw-r--r--gowin/arch.h19
-rw-r--r--gowin/archdefs.h10
-rw-r--r--ice40/arch.h16
-rw-r--r--ice40/archdefs.h12
-rw-r--r--machxo2/arch.h15
-rw-r--r--machxo2/archdefs.h10
-rw-r--r--nexus/arch.h15
-rw-r--r--nexus/archdefs.h13
-rw-r--r--nexus/fasm.cc1
49 files changed, 3267 insertions, 2467 deletions
diff --git a/common/arch_api.h b/common/arch_api.h
new file mode 100644
index 00000000..83872b7d
--- /dev/null
+++ b/common/arch_api.h
@@ -0,0 +1,150 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef ARCH_API_H
+#define ARCH_API_H
+
+#include <algorithm>
+
+#include "basectx.h"
+#include "idstring.h"
+#include "idstringlist.h"
+#include "nextpnr_assertions.h"
+#include "nextpnr_namespaces.h"
+#include "nextpnr_types.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+// The specification of the Arch API (pure virtual)
+template <typename R> struct ArchAPI : BaseCtx
+{
+ // Basic config
+ virtual IdString archId() const = 0;
+ virtual std::string getChipName() const = 0;
+ virtual typename R::ArchArgsT archArgs() const = 0;
+ virtual IdString archArgsToId(typename R::ArchArgsT args) const = 0;
+ virtual int getGridDimX() const = 0;
+ virtual int getGridDimY() const = 0;
+ virtual int getTileBelDimZ(int x, int y) const = 0;
+ virtual int getTilePipDimZ(int x, int y) const = 0;
+ virtual char getNameDelimiter() const = 0;
+ // Bel methods
+ virtual typename R::AllBelsRangeT getBels() const = 0;
+ virtual IdStringList getBelName(BelId bel) const = 0;
+ virtual BelId getBelByName(IdStringList name) const = 0;
+ virtual uint32_t getBelChecksum(BelId bel) const = 0;
+ virtual void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) = 0;
+ virtual void unbindBel(BelId bel) = 0;
+ virtual Loc getBelLocation(BelId bel) const = 0;
+ virtual BelId getBelByLocation(Loc loc) const = 0;
+ virtual typename R::TileBelsRangeT getBelsByTile(int x, int y) const = 0;
+ virtual bool getBelGlobalBuf(BelId bel) const = 0;
+ virtual bool checkBelAvail(BelId bel) const = 0;
+ virtual CellInfo *getBoundBelCell(BelId bel) const = 0;
+ virtual CellInfo *getConflictingBelCell(BelId bel) const = 0;
+ virtual IdString getBelType(BelId bel) const = 0;
+ virtual bool getBelHidden(BelId bel) const = 0;
+ virtual typename R::BelAttrsRangeT getBelAttrs(BelId bel) const = 0;
+ virtual WireId getBelPinWire(BelId bel, IdString pin) const = 0;
+ virtual PortType getBelPinType(BelId bel, IdString pin) const = 0;
+ virtual typename R::BelPinsRangeT getBelPins(BelId bel) const = 0;
+ virtual typename R::CellBelPinRangeT getBelPinsForCellPin(const CellInfo *cell_info, IdString pin) const = 0;
+ // Wire methods
+ virtual typename R::AllWiresRangeT getWires() const = 0;
+ virtual WireId getWireByName(IdStringList name) const = 0;
+ virtual IdStringList getWireName(WireId wire) const = 0;
+ virtual IdString getWireType(WireId wire) const = 0;
+ virtual typename R::WireAttrsRangeT getWireAttrs(WireId) const = 0;
+ virtual typename R::DownhillPipRangeT getPipsDownhill(WireId wire) const = 0;
+ virtual typename R::UphillPipRangeT getPipsUphill(WireId wire) const = 0;
+ virtual typename R::WireBelPinRangeT getWireBelPins(WireId wire) const = 0;
+ virtual uint32_t getWireChecksum(WireId wire) const = 0;
+ virtual void bindWire(WireId wire, NetInfo *net, PlaceStrength strength) = 0;
+ virtual void unbindWire(WireId wire) = 0;
+ virtual bool checkWireAvail(WireId wire) const = 0;
+ virtual NetInfo *getBoundWireNet(WireId wire) const = 0;
+ virtual WireId getConflictingWireWire(WireId wire) const = 0;
+ virtual NetInfo *getConflictingWireNet(WireId wire) const = 0;
+ virtual DelayQuad getWireDelay(WireId wire) const = 0;
+ // Pip methods
+ virtual typename R::AllPipsRangeT getPips() const = 0;
+ virtual PipId getPipByName(IdStringList name) const = 0;
+ virtual IdStringList getPipName(PipId pip) const = 0;
+ virtual IdString getPipType(PipId pip) const = 0;
+ virtual typename R::PipAttrsRangeT getPipAttrs(PipId) const = 0;
+ virtual uint32_t getPipChecksum(PipId pip) const = 0;
+ virtual void bindPip(PipId pip, NetInfo *net, PlaceStrength strength) = 0;
+ virtual void unbindPip(PipId pip) = 0;
+ virtual bool checkPipAvail(PipId pip) const = 0;
+ virtual NetInfo *getBoundPipNet(PipId pip) const = 0;
+ virtual WireId getConflictingPipWire(PipId pip) const = 0;
+ virtual NetInfo *getConflictingPipNet(PipId pip) const = 0;
+ virtual WireId getPipSrcWire(PipId pip) const = 0;
+ virtual WireId getPipDstWire(PipId pip) const = 0;
+ virtual DelayQuad getPipDelay(PipId pip) const = 0;
+ virtual Loc getPipLocation(PipId pip) const = 0;
+ // Group methods
+ virtual GroupId getGroupByName(IdStringList name) const = 0;
+ virtual IdStringList getGroupName(GroupId group) const = 0;
+ virtual typename R::AllGroupsRangeT getGroups() const = 0;
+ virtual typename R::GroupBelsRangeT getGroupBels(GroupId group) const = 0;
+ virtual typename R::GroupWiresRangeT getGroupWires(GroupId group) const = 0;
+ virtual typename R::GroupPipsRangeT getGroupPips(GroupId group) const = 0;
+ virtual typename R::GroupGroupsRangeT getGroupGroups(GroupId group) const = 0;
+ // Delay Methods
+ virtual delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const = 0;
+ virtual delay_t getDelayEpsilon() const = 0;
+ virtual delay_t getRipupDelayPenalty() const = 0;
+ virtual float getDelayNS(delay_t v) const = 0;
+ virtual delay_t getDelayFromNS(float ns) const = 0;
+ virtual uint32_t getDelayChecksum(delay_t v) const = 0;
+ virtual bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const = 0;
+ virtual delay_t estimateDelay(WireId src, WireId dst) const = 0;
+ virtual ArcBounds getRouteBoundingBox(WireId src, WireId dst) const = 0;
+ // Decal methods
+ virtual typename R::DecalGfxRangeT getDecalGraphics(DecalId decal) const = 0;
+ virtual DecalXY getBelDecal(BelId bel) const = 0;
+ virtual DecalXY getWireDecal(WireId wire) const = 0;
+ virtual DecalXY getPipDecal(PipId pip) const = 0;
+ virtual DecalXY getGroupDecal(GroupId group) const = 0;
+ // Cell timing methods
+ virtual bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const = 0;
+ virtual TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const = 0;
+ virtual TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const = 0;
+ // Placement validity checks
+ virtual bool isValidBelForCellType(IdString cell_type, BelId bel) const = 0;
+ virtual IdString getBelBucketName(BelBucketId bucket) const = 0;
+ virtual BelBucketId getBelBucketByName(IdString name) const = 0;
+ virtual BelBucketId getBelBucketForBel(BelId bel) const = 0;
+ virtual BelBucketId getBelBucketForCellType(IdString cell_type) const = 0;
+ virtual bool isBelLocationValid(BelId bel) const = 0;
+ virtual typename R::CellTypeRangeT getCellTypes() const = 0;
+ virtual typename R::BelBucketRangeT getBelBuckets() const = 0;
+ virtual typename R::BucketBelRangeT getBelsInBucket(BelBucketId bucket) const = 0;
+ // Flow methods
+ virtual bool pack() = 0;
+ virtual bool place() = 0;
+ virtual bool route() = 0;
+ virtual void assignArchInfo() = 0;
+};
+
+NEXTPNR_NAMESPACE_END
+
+#endif /* ARCH_API_H */
diff --git a/common/base_arch.h b/common/base_arch.h
new file mode 100644
index 00000000..84629977
--- /dev/null
+++ b/common/base_arch.h
@@ -0,0 +1,388 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef BASE_ARCH_H
+#define BASE_ARCH_H
+
+#include <array>
+#include <vector>
+
+#include "arch_api.h"
+#include "idstring.h"
+#include "nextpnr_types.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+namespace {
+// For several functions; such as bel/wire/pip attributes; the trivial implementation is to return an empty vector
+// But an arch might want to do something fancy with a custom range type that doesn't provide a constructor
+// So some cursed C++ is needed to return an empty object if possible; or error out if not; is needed
+template <typename Tc> typename std::enable_if<std::is_constructible<Tc>::value, Tc>::type empty_if_possible()
+{
+ return Tc();
+}
+template <typename Tc> typename std::enable_if<!std::is_constructible<Tc>::value, Tc>::type empty_if_possible()
+{
+ NPNR_ASSERT_FALSE("attempting to use default implementation of range-returning function with range type lacking "
+ "default constructor!");
+}
+
+// Provide a default implementation of bel bucket name if typedef'd to IdString
+template <typename Tbbid>
+typename std::enable_if<std::is_same<Tbbid, IdString>::value, IdString>::type bbid_to_name(Tbbid id)
+{
+ return id;
+}
+template <typename Tbbid>
+typename std::enable_if<!std::is_same<Tbbid, IdString>::value, IdString>::type bbid_to_name(Tbbid id)
+{
+ NPNR_ASSERT_FALSE("getBelBucketName must be implemented when BelBucketId is a type other than IdString!");
+}
+template <typename Tbbid>
+typename std::enable_if<std::is_same<Tbbid, IdString>::value, BelBucketId>::type bbid_from_name(IdString name)
+{
+ return name;
+}
+template <typename Tbbid>
+typename std::enable_if<!std::is_same<Tbbid, IdString>::value, BelBucketId>::type bbid_from_name(IdString name)
+{
+ NPNR_ASSERT_FALSE("getBelBucketByName must be implemented when BelBucketId is a type other than IdString!");
+}
+
+// For the cell type and bel type ranges; we want to return our stored vectors only if the type matches
+template <typename Tret, typename Tc>
+typename std::enable_if<std::is_same<Tret, Tc>::value, Tret>::type return_if_match(Tret r)
+{
+ return r;
+}
+
+template <typename Tret, typename Tc>
+typename std::enable_if<!std::is_same<Tret, Tc>::value, Tret>::type return_if_match(Tret r)
+{
+ NPNR_ASSERT_FALSE("default implementations of cell type and bel bucket range functions only available when the "
+ "respective range types are 'const std::vector&'");
+}
+
+} // namespace
+
+// This contains the relevant range types for the default implementations of Arch functions
+struct BaseArchRanges
+{
+ // Bels
+ using CellBelPinRangeT = std::array<IdString, 1>;
+ // Attributes
+ using BelAttrsRangeT = std::vector<std::pair<IdString, std::string>>;
+ using WireAttrsRangeT = std::vector<std::pair<IdString, std::string>>;
+ using PipAttrsRangeT = std::vector<std::pair<IdString, std::string>>;
+ // Groups
+ using AllGroupsRangeT = std::vector<GroupId>;
+ using GroupBelsRangeT = std::vector<BelId>;
+ using GroupWiresRangeT = std::vector<WireId>;
+ using GroupPipsRangeT = std::vector<PipId>;
+ using GroupGroupsRangeT = std::vector<GroupId>;
+ // Decals
+ using DecalGfxRangeT = std::vector<GraphicElement>;
+ // Placement validity
+ using CellTypeRangeT = const std::vector<IdString> &;
+ using BelBucketRangeT = const std::vector<BelBucketId> &;
+ using BucketBelRangeT = const std::vector<BelId> &;
+};
+
+template <typename R> struct BaseArch : ArchAPI<R>
+{
+ // --------------------------------------------------------------
+ // Default, trivial, implementations of Arch API functions for arches that don't need complex behaviours
+
+ // Basic config
+ virtual IdString archId() const override { return this->id(NPNR_STRINGIFY(ARCHNAME)); }
+ virtual IdString archArgsToId(typename R::ArchArgsT args) const override { return IdString(); }
+ virtual int getTilePipDimZ(int x, int y) const override { return 1; }
+ virtual char getNameDelimiter() const override { return ' '; }
+
+ // Bel methods
+ virtual uint32_t getBelChecksum(BelId bel) const override { return uint32_t(std::hash<BelId>()(bel)); }
+ virtual void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) override
+ {
+ NPNR_ASSERT(bel != BelId());
+ auto &entry = base_bel2cell[bel];
+ NPNR_ASSERT(entry == nullptr);
+ cell->bel = bel;
+ cell->belStrength = strength;
+ entry = cell;
+ this->refreshUiBel(bel);
+ }
+ virtual void unbindBel(BelId bel) override
+ {
+ NPNR_ASSERT(bel != BelId());
+ auto &entry = base_bel2cell[bel];
+ NPNR_ASSERT(entry != nullptr);
+ entry->bel = BelId();
+ entry->belStrength = STRENGTH_NONE;
+ entry = nullptr;
+ this->refreshUiBel(bel);
+ }
+
+ virtual bool getBelHidden(BelId bel) const override { return false; }
+
+ virtual bool getBelGlobalBuf(BelId bel) const override { return false; }
+ virtual bool checkBelAvail(BelId bel) const override { return getBoundBelCell(bel) == nullptr; };
+ virtual CellInfo *getBoundBelCell(BelId bel) const override
+ {
+ auto fnd = base_bel2cell.find(bel);
+ return fnd == base_bel2cell.end() ? nullptr : fnd->second;
+ }
+ virtual CellInfo *getConflictingBelCell(BelId bel) const override { return getBoundBelCell(bel); }
+ virtual typename R::BelAttrsRangeT getBelAttrs(BelId bel) const override
+ {
+ return empty_if_possible<typename R::BelAttrsRangeT>();
+ }
+
+ virtual typename R::CellBelPinRangeT getBelPinsForCellPin(const CellInfo *cell_info, IdString pin) const override
+ {
+ return return_if_match<std::array<IdString, 1>, typename R::CellBelPinRangeT>({pin});
+ }
+
+ // Wire methods
+ virtual IdString getWireType(WireId wire) const override { return IdString(); }
+ virtual typename R::WireAttrsRangeT getWireAttrs(WireId) const override
+ {
+ return empty_if_possible<typename R::WireAttrsRangeT>();
+ }
+ virtual uint32_t getWireChecksum(WireId wire) const override { return uint32_t(std::hash<WireId>()(wire)); }
+
+ virtual void bindWire(WireId wire, NetInfo *net, PlaceStrength strength) override
+ {
+ NPNR_ASSERT(wire != WireId());
+ auto &w2n_entry = base_wire2net[wire];
+ NPNR_ASSERT(w2n_entry == nullptr);
+ net->wires[wire].pip = PipId();
+ net->wires[wire].strength = strength;
+ w2n_entry = net;
+ this->refreshUiWire(wire);
+ }
+ virtual void unbindWire(WireId wire) override
+ {
+ NPNR_ASSERT(wire != WireId());
+ auto &w2n_entry = base_wire2net[wire];
+ NPNR_ASSERT(w2n_entry != nullptr);
+
+ auto &net_wires = w2n_entry->wires;
+ auto it = net_wires.find(wire);
+ NPNR_ASSERT(it != net_wires.end());
+
+ auto pip = it->second.pip;
+ if (pip != PipId()) {
+ base_pip2net[pip] = nullptr;
+ }
+
+ net_wires.erase(it);
+ base_wire2net[wire] = nullptr;
+
+ w2n_entry = nullptr;
+ this->refreshUiWire(wire);
+ }
+ virtual bool checkWireAvail(WireId wire) const override { return getBoundWireNet(wire) == nullptr; }
+ virtual NetInfo *getBoundWireNet(WireId wire) const override
+ {
+ auto fnd = base_wire2net.find(wire);
+ return fnd == base_wire2net.end() ? nullptr : fnd->second;
+ }
+ virtual WireId getConflictingWireWire(WireId wire) const override { return wire; };
+ virtual NetInfo *getConflictingWireNet(WireId wire) const override { return getBoundWireNet(wire); }
+
+ // Pip methods
+ virtual IdString getPipType(PipId pip) const override { return IdString(); }
+ virtual typename R::PipAttrsRangeT getPipAttrs(PipId) const override
+ {
+ return empty_if_possible<typename R::PipAttrsRangeT>();
+ }
+ virtual uint32_t getPipChecksum(PipId pip) const override { return uint32_t(std::hash<PipId>()(pip)); }
+ virtual void bindPip(PipId pip, NetInfo *net, PlaceStrength strength) override
+ {
+ NPNR_ASSERT(pip != PipId());
+ auto &p2n_entry = base_pip2net[pip];
+ NPNR_ASSERT(p2n_entry == nullptr);
+ p2n_entry = net;
+
+ WireId dst = this->getPipDstWire(pip);
+ auto &w2n_entry = base_wire2net[dst];
+ NPNR_ASSERT(w2n_entry == nullptr);
+ w2n_entry = net;
+ net->wires[dst].pip = pip;
+ net->wires[dst].strength = strength;
+ }
+ virtual void unbindPip(PipId pip) override
+ {
+ NPNR_ASSERT(pip != PipId());
+ auto &p2n_entry = base_pip2net[pip];
+ NPNR_ASSERT(p2n_entry != nullptr);
+ WireId dst = this->getPipDstWire(pip);
+
+ auto &w2n_entry = base_wire2net[dst];
+ NPNR_ASSERT(w2n_entry != nullptr);
+ w2n_entry = nullptr;
+
+ p2n_entry->wires.erase(dst);
+ p2n_entry = nullptr;
+ }
+ virtual bool checkPipAvail(PipId pip) const override { return getBoundPipNet(pip) == nullptr; }
+ virtual NetInfo *getBoundPipNet(PipId pip) const override
+ {
+ auto fnd = base_pip2net.find(pip);
+ return fnd == base_pip2net.end() ? nullptr : fnd->second;
+ }
+ virtual WireId getConflictingPipWire(PipId pip) const override { return WireId(); }
+ virtual NetInfo *getConflictingPipNet(PipId pip) const override { return getBoundPipNet(pip); }
+
+ // Group methods
+ virtual GroupId getGroupByName(IdStringList name) const override { return GroupId(); };
+ virtual IdStringList getGroupName(GroupId group) const override { return IdStringList(); };
+ virtual typename R::AllGroupsRangeT getGroups() const override
+ {
+ return empty_if_possible<typename R::AllGroupsRangeT>();
+ }
+ // Default implementation of these assumes no groups so never called
+ virtual typename R::GroupBelsRangeT getGroupBels(GroupId group) const override
+ {
+ NPNR_ASSERT_FALSE("unreachable");
+ };
+ virtual typename R::GroupWiresRangeT getGroupWires(GroupId group) const override
+ {
+ NPNR_ASSERT_FALSE("unreachable");
+ };
+ virtual typename R::GroupPipsRangeT getGroupPips(GroupId group) const override
+ {
+ NPNR_ASSERT_FALSE("unreachable");
+ };
+ virtual typename R::GroupGroupsRangeT getGroupGroups(GroupId group) const override
+ {
+ NPNR_ASSERT_FALSE("unreachable");
+ };
+
+ // Delay methods
+ virtual bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const override
+ {
+ return false;
+ }
+
+ // Decal methods
+ virtual typename R::DecalGfxRangeT getDecalGraphics(DecalId decal) const override
+ {
+ return empty_if_possible<typename R::DecalGfxRangeT>();
+ };
+ virtual DecalXY getBelDecal(BelId bel) const override { return DecalXY(); }
+ virtual DecalXY getWireDecal(WireId wire) const override { return DecalXY(); }
+ virtual DecalXY getPipDecal(PipId pip) const override { return DecalXY(); }
+ virtual DecalXY getGroupDecal(GroupId group) const override { return DecalXY(); }
+
+ // Cell timing methods
+ virtual bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const override
+ {
+ return false;
+ }
+ virtual TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const override
+ {
+ return TMG_IGNORE;
+ }
+ virtual TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const override
+ {
+ NPNR_ASSERT_FALSE("unreachable");
+ }
+
+ // Placement validity checks
+ virtual bool isValidBelForCellType(IdString cell_type, BelId bel) const override
+ {
+ return cell_type == this->getBelType(bel);
+ }
+ virtual IdString getBelBucketName(BelBucketId bucket) const override { return bbid_to_name<BelBucketId>(bucket); }
+ virtual BelBucketId getBelBucketByName(IdString name) const override { return bbid_from_name<BelBucketId>(name); }
+ virtual BelBucketId getBelBucketForBel(BelId bel) const override
+ {
+ return getBelBucketForCellType(this->getBelType(bel));
+ };
+ virtual BelBucketId getBelBucketForCellType(IdString cell_type) const override
+ {
+ return getBelBucketByName(cell_type);
+ };
+ virtual bool isBelLocationValid(BelId bel) const override { return true; }
+ virtual typename R::CellTypeRangeT getCellTypes() const override
+ {
+ NPNR_ASSERT(cell_types_initialised);
+ return return_if_match<const std::vector<IdString> &, typename R::CellTypeRangeT>(cell_types);
+ }
+ virtual typename R::BelBucketRangeT getBelBuckets() const override
+ {
+ NPNR_ASSERT(bel_buckets_initialised);
+ return return_if_match<const std::vector<BelBucketId> &, typename R::BelBucketRangeT>(bel_buckets);
+ }
+ virtual typename R::BucketBelRangeT getBelsInBucket(BelBucketId bucket) const override
+ {
+ NPNR_ASSERT(bel_buckets_initialised);
+ return return_if_match<const std::vector<BelId> &, typename R::BucketBelRangeT>(bucket_bels.at(bucket));
+ }
+
+ // Flow methods
+ virtual void assignArchInfo() override{};
+
+ // --------------------------------------------------------------
+ // These structures are used to provide default implementations of bel/wire/pip binding. Arches might want to
+ // replace them with their own, for example to use faster access structures than unordered_map. Arches might also
+ // want to add extra checks around these functions
+ std::unordered_map<BelId, CellInfo *> base_bel2cell;
+ std::unordered_map<WireId, NetInfo *> base_wire2net;
+ std::unordered_map<PipId, NetInfo *> base_pip2net;
+
+ // For the default cell/bel bucket implementations
+ std::vector<IdString> cell_types;
+ std::vector<BelBucketId> bel_buckets;
+ std::unordered_map<BelBucketId, std::vector<BelId>> bucket_bels;
+
+ // Arches that want to use the default cell types and bel buckets *must* call these functions in their constructor
+ bool cell_types_initialised = false;
+ bool bel_buckets_initialised = false;
+ void init_cell_types()
+ {
+ std::unordered_set<IdString> bel_types;
+ for (auto bel : this->getBels())
+ bel_types.insert(this->getBelType(bel));
+ std::copy(bel_types.begin(), bel_types.end(), std::back_inserter(cell_types));
+ std::sort(cell_types.begin(), cell_types.end());
+ cell_types_initialised = true;
+ }
+ void init_bel_buckets()
+ {
+ for (auto cell_type : this->getCellTypes()) {
+ auto bucket = this->getBelBucketForCellType(cell_type);
+ bucket_bels[bucket]; // create empty bucket
+ }
+ for (auto bel : this->getBels()) {
+ auto bucket = this->getBelBucketForBel(bel);
+ bucket_bels[bucket].push_back(bel);
+ }
+ for (auto &b : bucket_bels)
+ bel_buckets.push_back(b.first);
+ std::sort(bel_buckets.begin(), bel_buckets.end());
+ bel_buckets_initialised = true;
+ }
+};
+
+NEXTPNR_NAMESPACE_END
+
+#endif /* BASE_ARCH_H */
diff --git a/common/basectx.cc b/common/basectx.cc
new file mode 100644
index 00000000..f271f0f3
--- /dev/null
+++ b/common/basectx.cc
@@ -0,0 +1,334 @@
+/*
+ * 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 "basectx.h"
+
+#include <boost/algorithm/string.hpp>
+
+#include "design_utils.h"
+#include "context.h"
+#include "log.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+const char *BaseCtx::nameOfBel(BelId bel) const
+{
+ const Context *ctx = getCtx();
+ std::string &s = ctx->log_strs.next();
+ ctx->getBelName(bel).build_str(ctx, s);
+ return s.c_str();
+}
+
+const char *BaseCtx::nameOfWire(WireId wire) const
+{
+ const Context *ctx = getCtx();
+ std::string &s = ctx->log_strs.next();
+ ctx->getWireName(wire).build_str(ctx, s);
+ return s.c_str();
+}
+
+const char *BaseCtx::nameOfPip(PipId pip) const
+{
+ const Context *ctx = getCtx();
+ std::string &s = ctx->log_strs.next();
+ ctx->getPipName(pip).build_str(ctx, s);
+ return s.c_str();
+}
+
+const char *BaseCtx::nameOfGroup(GroupId group) const
+{
+ const Context *ctx = getCtx();
+ std::string &s = ctx->log_strs.next();
+ ctx->getGroupName(group).build_str(ctx, s);
+ return s.c_str();
+}
+
+BelId BaseCtx::getBelByNameStr(const std::string &str)
+{
+ Context *ctx = getCtx();
+ return ctx->getBelByName(IdStringList::parse(ctx, str));
+}
+
+WireId BaseCtx::getWireByNameStr(const std::string &str)
+{
+ Context *ctx = getCtx();
+ return ctx->getWireByName(IdStringList::parse(ctx, str));
+}
+
+PipId BaseCtx::getPipByNameStr(const std::string &str)
+{
+ Context *ctx = getCtx();
+ return ctx->getPipByName(IdStringList::parse(ctx, str));
+}
+
+GroupId BaseCtx::getGroupByNameStr(const std::string &str)
+{
+ Context *ctx = getCtx();
+ return ctx->getGroupByName(IdStringList::parse(ctx, str));
+}
+
+void BaseCtx::addClock(IdString net, float freq)
+{
+ std::unique_ptr<ClockConstraint> cc(new ClockConstraint());
+ cc->period = DelayPair(getCtx()->getDelayFromNS(1000 / freq));
+ cc->high = DelayPair(getCtx()->getDelayFromNS(500 / freq));
+ cc->low = DelayPair(getCtx()->getDelayFromNS(500 / freq));
+ if (!net_aliases.count(net)) {
+ log_warning("net '%s' does not exist in design, ignoring clock constraint\n", net.c_str(this));
+ } else {
+ getNetByAlias(net)->clkconstr = std::move(cc);
+ log_info("constraining clock net '%s' to %.02f MHz\n", net.c_str(this), freq);
+ }
+}
+
+void BaseCtx::createRectangularRegion(IdString name, int x0, int y0, int x1, int y1)
+{
+ std::unique_ptr<Region> new_region(new Region());
+ new_region->name = name;
+ new_region->constr_bels = true;
+ new_region->constr_pips = false;
+ new_region->constr_wires = false;
+ for (int x = x0; x <= x1; x++) {
+ for (int y = y0; y <= y1; y++) {
+ for (auto bel : getCtx()->getBelsByTile(x, y))
+ new_region->bels.insert(bel);
+ }
+ }
+ region[name] = std::move(new_region);
+}
+void BaseCtx::addBelToRegion(IdString name, BelId bel) { region[name]->bels.insert(bel); }
+void BaseCtx::constrainCellToRegion(IdString cell, IdString region_name)
+{
+ // Support hierarchical cells as well as leaf ones
+ bool matched = false;
+ if (hierarchy.count(cell)) {
+ auto &hc = hierarchy.at(cell);
+ for (auto &lc : hc.leaf_cells)
+ constrainCellToRegion(lc.second, region_name);
+ for (auto &hsc : hc.hier_cells)
+ constrainCellToRegion(hsc.second, region_name);
+ matched = true;
+ }
+ if (cells.count(cell)) {
+ cells.at(cell)->region = region[region_name].get();
+ matched = true;
+ }
+ if (!matched)
+ log_warning("No cell matched '%s' when constraining to region '%s'\n", nameOf(cell), nameOf(region_name));
+}
+DecalXY BaseCtx::constructDecalXY(DecalId decal, float x, float y)
+{
+ DecalXY dxy;
+ dxy.decal = decal;
+ dxy.x = x;
+ dxy.y = y;
+ return dxy;
+}
+
+void BaseCtx::archInfoToAttributes()
+{
+ for (auto &cell : cells) {
+ auto ci = cell.second.get();
+ if (ci->bel != BelId()) {
+ if (ci->attrs.find(id("BEL")) != ci->attrs.end()) {
+ ci->attrs.erase(ci->attrs.find(id("BEL")));
+ }
+ ci->attrs[id("NEXTPNR_BEL")] = getCtx()->getBelName(ci->bel).str(getCtx());
+ ci->attrs[id("BEL_STRENGTH")] = (int)ci->belStrength;
+ }
+ if (ci->constr_x != ci->UNCONSTR)
+ ci->attrs[id("CONSTR_X")] = ci->constr_x;
+ if (ci->constr_y != ci->UNCONSTR)
+ ci->attrs[id("CONSTR_Y")] = ci->constr_y;
+ if (ci->constr_z != ci->UNCONSTR) {
+ ci->attrs[id("CONSTR_Z")] = ci->constr_z;
+ ci->attrs[id("CONSTR_ABS_Z")] = ci->constr_abs_z ? 1 : 0;
+ }
+ if (ci->constr_parent != nullptr)
+ ci->attrs[id("CONSTR_PARENT")] = ci->constr_parent->name.str(this);
+ if (!ci->constr_children.empty()) {
+ std::string constr = "";
+ for (auto &item : ci->constr_children) {
+ if (!constr.empty())
+ constr += std::string(";");
+ constr += item->name.c_str(this);
+ }
+ ci->attrs[id("CONSTR_CHILDREN")] = constr;
+ }
+ }
+ for (auto &net : getCtx()->nets) {
+ auto ni = net.second.get();
+ std::string routing;
+ bool first = true;
+ for (auto &item : ni->wires) {
+ if (!first)
+ routing += ";";
+ routing += getCtx()->getWireName(item.first).str(getCtx());
+ routing += ";";
+ if (item.second.pip != PipId())
+ routing += getCtx()->getPipName(item.second.pip).str(getCtx());
+ routing += ";" + std::to_string(item.second.strength);
+ first = false;
+ }
+ ni->attrs[id("ROUTING")] = routing;
+ }
+}
+
+void BaseCtx::attributesToArchInfo()
+{
+ for (auto &cell : cells) {
+ auto ci = cell.second.get();
+ auto val = ci->attrs.find(id("NEXTPNR_BEL"));
+ if (val != ci->attrs.end()) {
+ auto str = ci->attrs.find(id("BEL_STRENGTH"));
+ PlaceStrength strength = PlaceStrength::STRENGTH_USER;
+ if (str != ci->attrs.end())
+ strength = (PlaceStrength)str->second.as_int64();
+
+ BelId b = getCtx()->getBelByNameStr(val->second.as_string());
+ getCtx()->bindBel(b, ci, strength);
+ }
+
+ val = ci->attrs.find(id("CONSTR_PARENT"));
+ if (val != ci->attrs.end()) {
+ auto parent = cells.find(id(val->second.str));
+ if (parent != cells.end())
+ ci->constr_parent = parent->second.get();
+ else
+ continue;
+ }
+
+ val = ci->attrs.find(id("CONSTR_X"));
+ if (val != ci->attrs.end())
+ ci->constr_x = val->second.as_int64();
+
+ val = ci->attrs.find(id("CONSTR_Y"));
+ if (val != ci->attrs.end())
+ ci->constr_y = val->second.as_int64();
+
+ val = ci->attrs.find(id("CONSTR_Z"));
+ if (val != ci->attrs.end())
+ ci->constr_z = val->second.as_int64();
+
+ val = ci->attrs.find(id("CONSTR_ABS_Z"));
+ if (val != ci->attrs.end())
+ ci->constr_abs_z = val->second.as_int64() == 1;
+
+ val = ci->attrs.find(id("CONSTR_PARENT"));
+ if (val != ci->attrs.end()) {
+ auto parent = cells.find(id(val->second.as_string()));
+ if (parent != cells.end())
+ ci->constr_parent = parent->second.get();
+ }
+ val = ci->attrs.find(id("CONSTR_CHILDREN"));
+ if (val != ci->attrs.end()) {
+ std::vector<std::string> strs;
+ auto children = val->second.as_string();
+ boost::split(strs, children, boost::is_any_of(";"));
+ for (auto val : strs) {
+ if (cells.count(id(val.c_str())))
+ ci->constr_children.push_back(cells.find(id(val.c_str()))->second.get());
+ }
+ }
+ }
+ for (auto &net : getCtx()->nets) {
+ auto ni = net.second.get();
+ auto val = ni->attrs.find(id("ROUTING"));
+ if (val != ni->attrs.end()) {
+ std::vector<std::string> strs;
+ auto routing = val->second.as_string();
+ boost::split(strs, routing, boost::is_any_of(";"));
+ for (size_t i = 0; i < strs.size() / 3; i++) {
+ std::string wire = strs[i * 3];
+ std::string pip = strs[i * 3 + 1];
+ PlaceStrength strength = (PlaceStrength)std::stoi(strs[i * 3 + 2]);
+ if (pip.empty())
+ getCtx()->bindWire(getCtx()->getWireByName(IdStringList::parse(getCtx(), wire)), ni, strength);
+ else
+ getCtx()->bindPip(getCtx()->getPipByName(IdStringList::parse(getCtx(), pip)), ni, strength);
+ }
+ }
+ }
+ getCtx()->assignArchInfo();
+}
+
+NetInfo *BaseCtx::createNet(IdString name)
+{
+ NPNR_ASSERT(!nets.count(name));
+ NPNR_ASSERT(!net_aliases.count(name));
+ std::unique_ptr<NetInfo> net{new NetInfo};
+ net->name = name;
+ net_aliases[name] = name;
+ NetInfo *ptr = net.get();
+ nets[name] = std::move(net);
+ refreshUi();
+ return ptr;
+}
+
+void BaseCtx::connectPort(IdString net, IdString cell, IdString port)
+{
+ NetInfo *net_info = getNetByAlias(net);
+ CellInfo *cell_info = cells.at(cell).get();
+ connect_port(getCtx(), net_info, cell_info, port);
+}
+
+void BaseCtx::disconnectPort(IdString cell, IdString port)
+{
+ CellInfo *cell_info = cells.at(cell).get();
+ disconnect_port(getCtx(), cell_info, port);
+}
+
+void BaseCtx::ripupNet(IdString name)
+{
+ NetInfo *net_info = getNetByAlias(name);
+ std::vector<WireId> to_unbind;
+ for (auto &wire : net_info->wires)
+ to_unbind.push_back(wire.first);
+ for (auto &unbind : to_unbind)
+ getCtx()->unbindWire(unbind);
+}
+void BaseCtx::lockNetRouting(IdString name)
+{
+ NetInfo *net_info = getNetByAlias(name);
+ for (auto &wire : net_info->wires)
+ wire.second.strength = STRENGTH_USER;
+}
+
+CellInfo *BaseCtx::createCell(IdString name, IdString type)
+{
+ NPNR_ASSERT(!cells.count(name));
+ std::unique_ptr<CellInfo> cell{new CellInfo};
+ cell->name = name;
+ cell->type = type;
+ CellInfo *ptr = cell.get();
+ cells[name] = std::move(cell);
+ refreshUi();
+ return ptr;
+}
+
+void BaseCtx::copyBelPorts(IdString cell, BelId bel)
+{
+ CellInfo *cell_info = cells.at(cell).get();
+ for (auto pin : getCtx()->getBelPins(bel)) {
+ cell_info->ports[pin].name = pin;
+ cell_info->ports[pin].type = getCtx()->getBelPinType(bel, pin);
+ }
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/common/basectx.h b/common/basectx.h
new file mode 100644
index 00000000..fccd12af
--- /dev/null
+++ b/common/basectx.h
@@ -0,0 +1,238 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef BASECTX_H
+#define BASECTX_H
+
+#include <mutex>
+#include <unordered_map>
+#include <vector>
+#ifndef NPNR_DISABLE_THREADS
+#include <boost/thread.hpp>
+#endif
+
+#include "idstring.h"
+#include "nextpnr_namespaces.h"
+#include "nextpnr_types.h"
+#include "property.h"
+#include "str_ring_buffer.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+struct Context;
+
+struct BaseCtx
+{
+#ifndef NPNR_DISABLE_THREADS
+ // Lock to perform mutating actions on the Context.
+ std::mutex mutex;
+ boost::thread::id mutex_owner;
+
+ // Lock to be taken by UI when wanting to access context - the yield()
+ // method will lock/unlock it when its' released the main mutex to make
+ // sure the UI is not starved.
+ std::mutex ui_mutex;
+#endif
+
+ // ID String database.
+ mutable std::unordered_map<std::string, int> *idstring_str_to_idx;
+ mutable std::vector<const std::string *> *idstring_idx_to_str;
+
+ // Temporary string backing store for logging
+ mutable StrRingBuffer log_strs;
+
+ // Project settings and config switches
+ std::unordered_map<IdString, Property> settings;
+
+ // Placed nets and cells.
+ std::unordered_map<IdString, std::unique_ptr<NetInfo>> nets;
+ std::unordered_map<IdString, std::unique_ptr<CellInfo>> cells;
+
+ // Hierarchical (non-leaf) cells by full path
+ std::unordered_map<IdString, HierarchicalCell> hierarchy;
+ // This is the root of the above structure
+ IdString top_module;
+
+ // Aliases for nets, which may have more than one name due to assignments and hierarchy
+ std::unordered_map<IdString, IdString> net_aliases;
+
+ // Top-level ports
+ std::unordered_map<IdString, PortInfo> ports;
+ std::unordered_map<IdString, CellInfo *> port_cells;
+
+ // Floorplanning regions
+ std::unordered_map<IdString, std::unique_ptr<Region>> region;
+
+ // Context meta data
+ std::unordered_map<IdString, Property> attrs;
+
+ Context *as_ctx = nullptr;
+
+ // Has the frontend loaded a design?
+ bool design_loaded;
+
+ BaseCtx()
+ {
+ idstring_str_to_idx = new std::unordered_map<std::string, int>;
+ idstring_idx_to_str = new std::vector<const std::string *>;
+ IdString::initialize_add(this, "", 0);
+ IdString::initialize_arch(this);
+
+ design_loaded = false;
+ }
+
+ virtual ~BaseCtx()
+ {
+ delete idstring_str_to_idx;
+ delete idstring_idx_to_str;
+ }
+
+ // Must be called before performing any mutating changes on the Ctx/Arch.
+ void lock(void)
+ {
+#ifndef NPNR_DISABLE_THREADS
+ mutex.lock();
+ mutex_owner = boost::this_thread::get_id();
+#endif
+ }
+
+ void unlock(void)
+ {
+#ifndef NPNR_DISABLE_THREADS
+ NPNR_ASSERT(boost::this_thread::get_id() == mutex_owner);
+ mutex.unlock();
+#endif
+ }
+
+ // Must be called by the UI before rendering data. This lock will be
+ // prioritized when processing code calls yield().
+ void lock_ui(void)
+ {
+#ifndef NPNR_DISABLE_THREADS
+ ui_mutex.lock();
+ mutex.lock();
+#endif
+ }
+
+ void unlock_ui(void)
+ {
+#ifndef NPNR_DISABLE_THREADS
+ mutex.unlock();
+ ui_mutex.unlock();
+#endif
+ }
+
+ // Yield to UI by unlocking the main mutex, flashing the UI mutex and
+ // relocking the main mutex. Call this when you're performing a
+ // long-standing action while holding a lock to let the UI show
+ // visualization updates.
+ // Must be called with the main lock taken.
+ void yield(void)
+ {
+#ifndef NPNR_DISABLE_THREADS
+ unlock();
+ ui_mutex.lock();
+ ui_mutex.unlock();
+ lock();
+#endif
+ }
+
+ IdString id(const std::string &s) const { return IdString(this, s); }
+
+ IdString id(const char *s) const { return IdString(this, s); }
+
+ Context *getCtx() { return as_ctx; }
+
+ const Context *getCtx() const { return as_ctx; }
+
+ const char *nameOf(IdString name) const { return name.c_str(this); }
+
+ template <typename T> const char *nameOf(const T *obj) const
+ {
+ if (obj == nullptr)
+ return "";
+ return obj->name.c_str(this);
+ }
+
+ const char *nameOfBel(BelId bel) const;
+ const char *nameOfWire(WireId wire) const;
+ const char *nameOfPip(PipId pip) const;
+ const char *nameOfGroup(GroupId group) const;
+
+ // Wrappers of arch functions that take a string and handle IdStringList parsing
+ BelId getBelByNameStr(const std::string &str);
+ WireId getWireByNameStr(const std::string &str);
+ PipId getPipByNameStr(const std::string &str);
+ GroupId getGroupByNameStr(const std::string &str);
+
+ // --------------------------------------------------------------
+
+ bool allUiReload = true;
+ bool frameUiReload = false;
+ std::unordered_set<BelId> belUiReload;
+ std::unordered_set<WireId> wireUiReload;
+ std::unordered_set<PipId> pipUiReload;
+ std::unordered_set<GroupId> groupUiReload;
+
+ void refreshUi() { allUiReload = true; }
+
+ void refreshUiFrame() { frameUiReload = true; }
+
+ void refreshUiBel(BelId bel) { belUiReload.insert(bel); }
+
+ void refreshUiWire(WireId wire) { wireUiReload.insert(wire); }
+
+ void refreshUiPip(PipId pip) { pipUiReload.insert(pip); }
+
+ void refreshUiGroup(GroupId group) { groupUiReload.insert(group); }
+
+ // --------------------------------------------------------------
+
+ NetInfo *getNetByAlias(IdString alias) const
+ {
+ return nets.count(alias) ? nets.at(alias).get() : nets.at(net_aliases.at(alias)).get();
+ }
+
+ // Intended to simplify Python API
+ void addClock(IdString net, float freq);
+ void createRectangularRegion(IdString name, int x0, int y0, int x1, int y1);
+ void addBelToRegion(IdString name, BelId bel);
+ void constrainCellToRegion(IdString cell, IdString region_name);
+
+ // Helper functions for Python bindings
+ NetInfo *createNet(IdString name);
+ void connectPort(IdString net, IdString cell, IdString port);
+ void disconnectPort(IdString cell, IdString port);
+ void ripupNet(IdString name);
+ void lockNetRouting(IdString name);
+
+ CellInfo *createCell(IdString name, IdString type);
+ void copyBelPorts(IdString cell, BelId bel);
+
+ // Workaround for lack of wrappable constructors
+ DecalXY constructDecalXY(DecalId decal, float x, float y);
+
+ void archInfoToAttributes();
+ void attributesToArchInfo();
+};
+
+NEXTPNR_NAMESPACE_END
+
+#endif /* BASECTX_H */
diff --git a/common/constraints.h b/common/constraints.h
index dfb108f8..9ec8372d 100644
--- a/common/constraints.h
+++ b/common/constraints.h
@@ -20,15 +20,20 @@
#ifndef CONSTRAINTS_H
#define CONSTRAINTS_H
-#ifndef NEXTPNR_H
-#error Include after "nextpnr.h" only.
-#endif
+#include <cstdint>
+#include <unordered_map>
+#include <vector>
+#include "archdefs.h"
#include "exclusive_state_groups.h"
+#include "idstring.h"
+#include "nextpnr_namespaces.h"
NEXTPNR_NAMESPACE_BEGIN
-template <size_t StateCount, typename StateType = int8_t, typename CountType = uint8_t> struct Constraints
+struct Context;
+
+template <std::size_t StateCount, typename StateType = int8_t, typename CountType = uint8_t> struct Constraints
{
using ConstraintStateType = StateType;
using ConstraintCountType = CountType;
@@ -41,7 +46,7 @@ template <size_t StateCount, typename StateType = int8_t, typename CountType = u
template <typename StateRange> struct Constraint
{
- virtual size_t tag() const = 0;
+ virtual std::size_t tag() const = 0;
virtual ConstraintType constraint_type() const = 0;
virtual StateType state() const = 0;
virtual StateRange states() const = 0;
diff --git a/common/context.cc b/common/context.cc
new file mode 100644
index 00000000..c85f40de
--- /dev/null
+++ b/common/context.cc
@@ -0,0 +1,409 @@
+/*
+ * 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 "context.h"
+
+#include "nextpnr_namespaces.h"
+#include "log.h"
+#include "util.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+WireId Context::getNetinfoSourceWire(const NetInfo *net_info) const
+{
+ if (net_info->driver.cell == nullptr)
+ return WireId();
+
+ auto src_bel = net_info->driver.cell->bel;
+
+ if (src_bel == BelId())
+ return WireId();
+
+ auto bel_pins = getBelPinsForCellPin(net_info->driver.cell, net_info->driver.port);
+ auto iter = bel_pins.begin();
+ if (iter == bel_pins.end())
+ return WireId();
+ WireId driver = getBelPinWire(src_bel, *iter);
+ ++iter;
+ NPNR_ASSERT(iter == bel_pins.end()); // assert there is only one driver bel pin;
+ return driver;
+}
+
+SSOArray<WireId, 2> Context::getNetinfoSinkWires(const NetInfo *net_info, const PortRef &user_info) const
+{
+ auto dst_bel = user_info.cell->bel;
+ if (dst_bel == BelId())
+ return SSOArray<WireId, 2>(0, WireId());
+ size_t bel_pin_count = 0;
+ // We use an SSOArray here because it avoids any heap allocation for the 99.9% case of 1 or 2 sink wires
+ // but as SSOArray doesn't (currently) support resizing to keep things simple it does mean we have to do
+ // two loops
+ for (auto s : getBelPinsForCellPin(user_info.cell, user_info.port)) {
+ (void)s; // unused
+ ++bel_pin_count;
+ }
+ SSOArray<WireId, 2> result(bel_pin_count, WireId());
+ bel_pin_count = 0;
+ for (auto pin : getBelPinsForCellPin(user_info.cell, user_info.port)) {
+ result[bel_pin_count++] = getBelPinWire(dst_bel, pin);
+ }
+ return result;
+}
+
+size_t Context::getNetinfoSinkWireCount(const NetInfo *net_info, const PortRef &sink) const
+{
+ size_t count = 0;
+ for (auto s : getNetinfoSinkWires(net_info, sink)) {
+ (void)s; // unused
+ ++count;
+ }
+ return count;
+}
+
+WireId Context::getNetinfoSinkWire(const NetInfo *net_info, const PortRef &sink, size_t phys_idx) const
+{
+ size_t count = 0;
+ for (auto s : getNetinfoSinkWires(net_info, sink)) {
+ if (count == phys_idx)
+ return s;
+ ++count;
+ }
+ /* TODO: This should be an assertion failure, but for the zero-wire case of unplaced sinks; legacy code currently
+ assumes WireId Remove once the refactoring process is complete.
+ */
+ return WireId();
+}
+
+delay_t Context::getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &user_info) const
+{
+#ifdef ARCH_ECP5
+ if (net_info->is_global)
+ return 0;
+#endif
+
+ if (net_info->wires.empty())
+ return predictDelay(net_info, user_info);
+
+ WireId src_wire = getNetinfoSourceWire(net_info);
+ if (src_wire == WireId())
+ return 0;
+
+ delay_t max_delay = 0;
+
+ for (auto dst_wire : getNetinfoSinkWires(net_info, user_info)) {
+ WireId cursor = dst_wire;
+ delay_t delay = 0;
+
+ while (cursor != WireId() && cursor != src_wire) {
+ auto it = net_info->wires.find(cursor);
+
+ if (it == net_info->wires.end())
+ break;
+
+ PipId pip = it->second.pip;
+ if (pip == PipId())
+ break;
+
+ delay += getPipDelay(pip).maxDelay();
+ delay += getWireDelay(cursor).maxDelay();
+ cursor = getPipSrcWire(pip);
+ }
+
+ if (cursor == src_wire)
+ max_delay = std::max(max_delay, delay + getWireDelay(src_wire).maxDelay()); // routed
+ else
+ max_delay = std::max(max_delay, predictDelay(net_info, user_info)); // unrouted
+ }
+ return max_delay;
+}
+
+static uint32_t xorshift32(uint32_t x)
+{
+ x ^= x << 13;
+ x ^= x >> 17;
+ x ^= x << 5;
+ return x;
+}
+
+uint32_t Context::checksum() const
+{
+ uint32_t cksum = xorshift32(123456789);
+
+ uint32_t cksum_nets_sum = 0;
+ for (auto &it : nets) {
+ auto &ni = *it.second;
+ uint32_t x = 123456789;
+ x = xorshift32(x + xorshift32(it.first.index));
+ x = xorshift32(x + xorshift32(ni.name.index));
+ if (ni.driver.cell)
+ x = xorshift32(x + xorshift32(ni.driver.cell->name.index));
+ x = xorshift32(x + xorshift32(ni.driver.port.index));
+ x = xorshift32(x + xorshift32(getDelayChecksum(ni.driver.budget)));
+
+ for (auto &u : ni.users) {
+ if (u.cell)
+ x = xorshift32(x + xorshift32(u.cell->name.index));
+ x = xorshift32(x + xorshift32(u.port.index));
+ x = xorshift32(x + xorshift32(getDelayChecksum(u.budget)));
+ }
+
+ uint32_t attr_x_sum = 0;
+ for (auto &a : ni.attrs) {
+ uint32_t attr_x = 123456789;
+ attr_x = xorshift32(attr_x + xorshift32(a.first.index));
+ for (char ch : a.second.str)
+ attr_x = xorshift32(attr_x + xorshift32((int)ch));
+ attr_x_sum += attr_x;
+ }
+ x = xorshift32(x + xorshift32(attr_x_sum));
+
+ uint32_t wire_x_sum = 0;
+ for (auto &w : ni.wires) {
+ uint32_t wire_x = 123456789;
+ wire_x = xorshift32(wire_x + xorshift32(getWireChecksum(w.first)));
+ wire_x = xorshift32(wire_x + xorshift32(getPipChecksum(w.second.pip)));
+ wire_x = xorshift32(wire_x + xorshift32(int(w.second.strength)));
+ wire_x_sum += wire_x;
+ }
+ x = xorshift32(x + xorshift32(wire_x_sum));
+
+ cksum_nets_sum += x;
+ }
+ cksum = xorshift32(cksum + xorshift32(cksum_nets_sum));
+
+ uint32_t cksum_cells_sum = 0;
+ for (auto &it : cells) {
+ auto &ci = *it.second;
+ uint32_t x = 123456789;
+ x = xorshift32(x + xorshift32(it.first.index));
+ x = xorshift32(x + xorshift32(ci.name.index));
+ x = xorshift32(x + xorshift32(ci.type.index));
+
+ uint32_t port_x_sum = 0;
+ for (auto &p : ci.ports) {
+ uint32_t port_x = 123456789;
+ port_x = xorshift32(port_x + xorshift32(p.first.index));
+ port_x = xorshift32(port_x + xorshift32(p.second.name.index));
+ if (p.second.net)
+ port_x = xorshift32(port_x + xorshift32(p.second.net->name.index));
+ port_x = xorshift32(port_x + xorshift32(p.second.type));
+ port_x_sum += port_x;
+ }
+ x = xorshift32(x + xorshift32(port_x_sum));
+
+ uint32_t attr_x_sum = 0;
+ for (auto &a : ci.attrs) {
+ uint32_t attr_x = 123456789;
+ attr_x = xorshift32(attr_x + xorshift32(a.first.index));
+ for (char ch : a.second.str)
+ attr_x = xorshift32(attr_x + xorshift32((int)ch));
+ attr_x_sum += attr_x;
+ }
+ x = xorshift32(x + xorshift32(attr_x_sum));
+
+ uint32_t param_x_sum = 0;
+ for (auto &p : ci.params) {
+ uint32_t param_x = 123456789;
+ param_x = xorshift32(param_x + xorshift32(p.first.index));
+ for (char ch : p.second.str)
+ param_x = xorshift32(param_x + xorshift32((int)ch));
+ param_x_sum += param_x;
+ }
+ x = xorshift32(x + xorshift32(param_x_sum));
+
+ x = xorshift32(x + xorshift32(getBelChecksum(ci.bel)));
+ x = xorshift32(x + xorshift32(ci.belStrength));
+
+ cksum_cells_sum += x;
+ }
+ cksum = xorshift32(cksum + xorshift32(cksum_cells_sum));
+
+ return cksum;
+}
+
+void Context::check() const
+{
+ bool check_failed = false;
+
+#define CHECK_FAIL(...) \
+ do { \
+ log_nonfatal_error(__VA_ARGS__); \
+ check_failed = true; \
+ } while (false)
+
+ for (auto &n : nets) {
+ auto ni = n.second.get();
+ if (n.first != ni->name)
+ CHECK_FAIL("net key '%s' not equal to name '%s'\n", nameOf(n.first), nameOf(ni->name));
+ for (auto &w : ni->wires) {
+ if (ni != getBoundWireNet(w.first))
+ CHECK_FAIL("net '%s' not bound to wire '%s' in wires map\n", nameOf(n.first), nameOfWire(w.first));
+ if (w.second.pip != PipId()) {
+ if (w.first != getPipDstWire(w.second.pip))
+ CHECK_FAIL("net '%s' has dest mismatch '%s' vs '%s' in for pip '%s'\n", nameOf(n.first),
+ nameOfWire(w.first), nameOfWire(getPipDstWire(w.second.pip)), nameOfPip(w.second.pip));
+ if (ni != getBoundPipNet(w.second.pip))
+ CHECK_FAIL("net '%s' not bound to pip '%s' in wires map\n", nameOf(n.first),
+ nameOfPip(w.second.pip));
+ }
+ }
+ if (ni->driver.cell != nullptr) {
+ if (!ni->driver.cell->ports.count(ni->driver.port)) {
+ CHECK_FAIL("net '%s' driver port '%s' missing on cell '%s'\n", nameOf(n.first), nameOf(ni->driver.port),
+ nameOf(ni->driver.cell));
+ } else {
+ const NetInfo *p_net = ni->driver.cell->ports.at(ni->driver.port).net;
+ if (p_net != ni)
+ CHECK_FAIL("net '%s' driver port '%s.%s' connected to incorrect net '%s'\n", nameOf(n.first),
+ nameOf(ni->driver.cell), nameOf(ni->driver.port), p_net ? nameOf(p_net) : "<nullptr>");
+ }
+ }
+ for (auto user : ni->users) {
+ if (!user.cell->ports.count(user.port)) {
+ CHECK_FAIL("net '%s' user port '%s' missing on cell '%s'\n", nameOf(n.first), nameOf(user.port),
+ nameOf(user.cell));
+ } else {
+ const NetInfo *p_net = user.cell->ports.at(user.port).net;
+ if (p_net != ni)
+ CHECK_FAIL("net '%s' user port '%s.%s' connected to incorrect net '%s'\n", nameOf(n.first),
+ nameOf(user.cell), nameOf(user.port), p_net ? nameOf(p_net) : "<nullptr>");
+ }
+ }
+ }
+#ifdef CHECK_WIRES
+ for (auto w : getWires()) {
+ auto ni = getBoundWireNet(w);
+ if (ni != nullptr) {
+ if (!ni->wires.count(w))
+ CHECK_FAIL("wire '%s' missing in wires map of bound net '%s'\n", nameOfWire(w), nameOf(ni));
+ }
+ }
+#endif
+ for (auto &c : cells) {
+ auto ci = c.second.get();
+ if (c.first != ci->name)
+ CHECK_FAIL("cell key '%s' not equal to name '%s'\n", nameOf(c.first), nameOf(ci->name));
+ if (ci->bel != BelId()) {
+ if (getBoundBelCell(c.second->bel) != ci)
+ CHECK_FAIL("cell '%s' not bound to bel '%s' in bel field\n", nameOf(c.first), nameOfBel(ci->bel));
+ }
+ for (auto &port : c.second->ports) {
+ NetInfo *net = port.second.net;
+ if (net != nullptr) {
+ if (nets.find(net->name) == nets.end()) {
+ CHECK_FAIL("cell port '%s.%s' connected to non-existent net '%s'\n", nameOf(c.first),
+ nameOf(port.first), nameOf(net->name));
+ } else if (port.second.type == PORT_OUT) {
+ if (net->driver.cell != c.second.get() || net->driver.port != port.first) {
+ CHECK_FAIL("output cell port '%s.%s' not in driver field of net '%s'\n", nameOf(c.first),
+ nameOf(port.first), nameOf(net));
+ }
+ } else if (port.second.type == PORT_IN) {
+ int usr_count = std::count_if(net->users.begin(), net->users.end(), [&](const PortRef &pr) {
+ return pr.cell == c.second.get() && pr.port == port.first;
+ });
+ if (usr_count != 1)
+ CHECK_FAIL("input cell port '%s.%s' appears %d rather than expected 1 times in users vector of "
+ "net '%s'\n",
+ nameOf(c.first), nameOf(port.first), usr_count, nameOf(net));
+ }
+ }
+ }
+ }
+
+#undef CHECK_FAIL
+
+ if (check_failed)
+ log_error("INTERNAL CHECK FAILED: please report this error with the design and full log output. Failure "
+ "details are above this message.\n");
+}
+
+namespace {
+struct FixupHierarchyWorker
+{
+ FixupHierarchyWorker(Context *ctx) : ctx(ctx){};
+ Context *ctx;
+ void run()
+ {
+ trim_hierarchy(ctx->top_module);
+ rebuild_hierarchy();
+ };
+ // Remove cells and nets that no longer exist in the netlist
+ std::vector<IdString> todelete_cells, todelete_nets;
+ void trim_hierarchy(IdString path)
+ {
+ auto &h = ctx->hierarchy.at(path);
+ todelete_cells.clear();
+ todelete_nets.clear();
+ for (auto &lc : h.leaf_cells) {
+ if (!ctx->cells.count(lc.second))
+ todelete_cells.push_back(lc.first);
+ }
+ for (auto &n : h.nets)
+ if (!ctx->nets.count(n.second))
+ todelete_nets.push_back(n.first);
+ for (auto tdc : todelete_cells) {
+ h.leaf_cells_by_gname.erase(h.leaf_cells.at(tdc));
+ h.leaf_cells.erase(tdc);
+ }
+ for (auto tdn : todelete_nets) {
+ h.nets_by_gname.erase(h.nets.at(tdn));
+ h.nets.erase(tdn);
+ }
+ for (auto &sc : h.hier_cells)
+ trim_hierarchy(sc.second);
+ }
+
+ IdString construct_local_name(HierarchicalCell &hc, IdString global_name, bool is_cell)
+ {
+ std::string gn = global_name.str(ctx);
+ auto dp = gn.find_last_of('.');
+ if (dp != std::string::npos)
+ gn = gn.substr(dp + 1);
+ IdString name = ctx->id(gn);
+ // Make sure name is unique
+ int adder = 0;
+ while (is_cell ? hc.leaf_cells.count(name) : hc.nets.count(name)) {
+ ++adder;
+ name = ctx->id(gn + "$" + std::to_string(adder));
+ }
+ return name;
+ }
+
+ // Update hierarchy structure for nets and cells that have hiercell set
+ void rebuild_hierarchy()
+ {
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (ci->hierpath == IdString())
+ ci->hierpath = ctx->top_module;
+ auto &hc = ctx->hierarchy.at(ci->hierpath);
+ if (hc.leaf_cells_by_gname.count(ci->name))
+ continue; // already known
+ IdString local_name = construct_local_name(hc, ci->name, true);
+ hc.leaf_cells_by_gname[ci->name] = local_name;
+ hc.leaf_cells[local_name] = ci->name;
+ }
+ }
+};
+} // namespace
+
+void Context::fixupHierarchy() { FixupHierarchyWorker(this).run(); }
+
+NEXTPNR_NAMESPACE_END
diff --git a/common/context.h b/common/context.h
new file mode 100644
index 00000000..a5553422
--- /dev/null
+++ b/common/context.h
@@ -0,0 +1,103 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef CONTEXT_H
+#define CONTEXT_H
+
+#include <boost/lexical_cast.hpp>
+
+#include "arch.h"
+#include "deterministic_rng.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+struct Context : Arch, DeterministicRNG
+{
+ bool verbose = false;
+ bool debug = false;
+ bool force = false;
+
+ // Should we disable printing of the location of nets in the critical path?
+ bool disable_critical_path_source_print = false;
+
+ Context(ArchArgs args) : Arch(args) { BaseCtx::as_ctx = this; }
+
+ // --------------------------------------------------------------
+
+ WireId getNetinfoSourceWire(const NetInfo *net_info) const;
+ SSOArray<WireId, 2> getNetinfoSinkWires(const NetInfo *net_info, const PortRef &sink) const;
+ size_t getNetinfoSinkWireCount(const NetInfo *net_info, const PortRef &sink) const;
+ WireId getNetinfoSinkWire(const NetInfo *net_info, const PortRef &sink, size_t phys_idx) const;
+ delay_t getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &sink) const;
+
+ // provided by router1.cc
+ bool checkRoutedDesign() const;
+ bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay = nullptr,
+ std::unordered_map<WireId, PipId> *route = nullptr, bool useEstimate = true);
+
+ // --------------------------------------------------------------
+ // call after changing hierpath or adding/removing nets and cells
+ void fixupHierarchy();
+
+ // --------------------------------------------------------------
+
+ // provided by sdf.cc
+ void writeSDF(std::ostream &out, bool cvc_mode = false) const;
+
+ // --------------------------------------------------------------
+
+ // provided by svg.cc
+ void writeSVG(const std::string &filename, const std::string &flags = "") const;
+
+ // --------------------------------------------------------------
+
+ uint32_t checksum() const;
+
+ void check() const;
+ void archcheck() const;
+
+ template <typename T> T setting(const char *name, T defaultValue)
+ {
+ IdString new_id = id(name);
+ auto found = settings.find(new_id);
+ if (found != settings.end())
+ return boost::lexical_cast<T>(found->second.is_string ? found->second.as_string()
+ : std::to_string(found->second.as_int64()));
+ else
+ settings[id(name)] = std::to_string(defaultValue);
+
+ return defaultValue;
+ }
+
+ template <typename T> T setting(const char *name) const
+ {
+ IdString new_id = id(name);
+ auto found = settings.find(new_id);
+ if (found != settings.end())
+ return boost::lexical_cast<T>(found->second.is_string ? found->second.as_string()
+ : std::to_string(found->second.as_int64()));
+ else
+ throw std::runtime_error("settings does not exists");
+ }
+};
+
+NEXTPNR_NAMESPACE_END
+
+#endif /* CONTEXT_H */
diff --git a/common/deterministic_rng.h b/common/deterministic_rng.h
new file mode 100644
index 00000000..8dc22601
--- /dev/null
+++ b/common/deterministic_rng.h
@@ -0,0 +1,103 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef DETERMINISTIC_RNG_H
+#define DETERMINISTIC_RNG_H
+
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <vector>
+
+#include "nextpnr_namespaces.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+struct DeterministicRNG
+{
+ uint64_t rngstate;
+
+ DeterministicRNG() : rngstate(0x3141592653589793) {}
+
+ uint64_t rng64()
+ {
+ // xorshift64star
+ // https://arxiv.org/abs/1402.6246
+
+ uint64_t retval = rngstate * 0x2545F4914F6CDD1D;
+
+ rngstate ^= rngstate >> 12;
+ rngstate ^= rngstate << 25;
+ rngstate ^= rngstate >> 27;
+
+ return retval;
+ }
+
+ int rng() { return rng64() & 0x3fffffff; }
+
+ int rng(int n)
+ {
+ assert(n > 0);
+
+ // round up to power of 2
+ int m = n - 1;
+ m |= (m >> 1);
+ m |= (m >> 2);
+ m |= (m >> 4);
+ m |= (m >> 8);
+ m |= (m >> 16);
+ m += 1;
+
+ while (1) {
+ int x = rng64() & (m - 1);
+ if (x < n)
+ return x;
+ }
+ }
+
+ void rngseed(uint64_t seed)
+ {
+ rngstate = seed ? seed : 0x3141592653589793;
+ for (int i = 0; i < 5; i++)
+ rng64();
+ }
+
+ template <typename Iter> void shuffle(const Iter &begin, const Iter &end)
+ {
+ std::size_t size = end - begin;
+ for (std::size_t i = 0; i != size; i++) {
+ std::size_t j = i + rng(size - i);
+ if (j > i)
+ std::swap(*(begin + i), *(begin + j));
+ }
+ }
+
+ template <typename T> void shuffle(std::vector<T> &a) { shuffle(a.begin(), a.end()); }
+
+ template <typename T> void sorted_shuffle(std::vector<T> &a)
+ {
+ std::sort(a.begin(), a.end());
+ shuffle(a);
+ }
+};
+
+NEXTPNR_NAMESPACE_END
+
+#endif
diff --git a/common/exclusive_state_groups.h b/common/exclusive_state_groups.h
index f2dcb858..cd3f0bfa 100644
--- a/common/exclusive_state_groups.h
+++ b/common/exclusive_state_groups.h
@@ -20,11 +20,17 @@
#ifndef EXCLUSIVE_STATE_GROUPS_H
#define EXCLUSIVE_STATE_GROUPS_H
-#ifndef NEXTPNR_H
-#error Include after "nextpnr.h" only.
-#endif
+#include <array>
+#include <bitset>
+#include <cstdint>
+#include <limits>
+#include <vector>
+#include "archdefs.h"
#include "bits.h"
+#include "idstring.h"
+#include "nextpnr_assertions.h"
+#include "nextpnr_namespaces.h"
NEXTPNR_NAMESPACE_BEGIN
diff --git a/common/exclusive_state_groups.impl.h b/common/exclusive_state_groups.impl.h
index 9946e9a6..53e4e83c 100644
--- a/common/exclusive_state_groups.impl.h
+++ b/common/exclusive_state_groups.impl.h
@@ -19,14 +19,8 @@
#pragma once
-#include "nextpnr.h"
-
-// This header must be included after "nextpnr.h", otherwise circular header
-// import insanity occurs.
-#ifndef NEXTPNR_H_COMPLETE
-#error This header cannot be used until after "nextpnr.h" is included
-#endif
-
+#include "context.h"
+#include "exclusive_state_groups.h"
#include "log.h"
NEXTPNR_NAMESPACE_BEGIN
diff --git a/common/idstring.cc b/common/idstring.cc
new file mode 100644
index 00000000..69af73de
--- /dev/null
+++ b/common/idstring.cc
@@ -0,0 +1,51 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "idstring.h"
+
+#include "basectx.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+void IdString::set(const BaseCtx *ctx, const std::string &s)
+{
+ auto it = ctx->idstring_str_to_idx->find(s);
+ if (it == ctx->idstring_str_to_idx->end()) {
+ index = ctx->idstring_idx_to_str->size();
+ auto insert_rc = ctx->idstring_str_to_idx->insert({s, index});
+ ctx->idstring_idx_to_str->push_back(&insert_rc.first->first);
+ } else {
+ index = it->second;
+ }
+}
+
+const std::string &IdString::str(const BaseCtx *ctx) const { return *ctx->idstring_idx_to_str->at(index); }
+
+const char *IdString::c_str(const BaseCtx *ctx) const { return str(ctx).c_str(); }
+
+void IdString::initialize_add(const BaseCtx *ctx, const char *s, int idx)
+{
+ NPNR_ASSERT(ctx->idstring_str_to_idx->count(s) == 0);
+ NPNR_ASSERT(int(ctx->idstring_idx_to_str->size()) == idx);
+ auto insert_rc = ctx->idstring_str_to_idx->insert({s, idx});
+ ctx->idstring_idx_to_str->push_back(&insert_rc.first->first);
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/common/idstring.h b/common/idstring.h
new file mode 100644
index 00000000..c3ccbc6b
--- /dev/null
+++ b/common/idstring.h
@@ -0,0 +1,73 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef IDSTRING_H
+#define IDSTRING_H
+
+#include <string>
+#include "nextpnr_namespaces.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+struct BaseCtx;
+
+struct IdString
+{
+ int index;
+
+ static void initialize_arch(const BaseCtx *ctx);
+
+ static void initialize_add(const BaseCtx *ctx, const char *s, int idx);
+
+ constexpr IdString() : index(0) {}
+ explicit constexpr IdString(int index) : index(index) {}
+
+ void set(const BaseCtx *ctx, const std::string &s);
+
+ IdString(const BaseCtx *ctx, const std::string &s) { set(ctx, s); }
+
+ IdString(const BaseCtx *ctx, const char *s) { set(ctx, s); }
+
+ const std::string &str(const BaseCtx *ctx) const;
+
+ const char *c_str(const BaseCtx *ctx) const;
+
+ bool operator<(const IdString &other) const { return index < other.index; }
+
+ bool operator==(const IdString &other) const { return index == other.index; }
+
+ bool operator!=(const IdString &other) const { return index != other.index; }
+
+ bool empty() const { return index == 0; }
+};
+
+NEXTPNR_NAMESPACE_END
+
+namespace std {
+template <> struct hash<NEXTPNR_NAMESPACE_PREFIX IdString>
+{
+ std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX IdString &obj) const noexcept
+ {
+ return std::hash<int>()(obj.index);
+ }
+};
+} // namespace std
+
+#endif /* IDSTRING_H */
diff --git a/common/idstringlist.cc b/common/idstringlist.cc
new file mode 100644
index 00000000..9900f92a
--- /dev/null
+++ b/common/idstringlist.cc
@@ -0,0 +1,61 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "idstringlist.h"
+#include "context.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+IdStringList IdStringList::parse(Context *ctx, const std::string &str)
+{
+ char delim = ctx->getNameDelimiter();
+ size_t id_count = std::count(str.begin(), str.end(), delim) + 1;
+ IdStringList list(id_count);
+ size_t start = 0;
+ for (size_t i = 0; i < id_count; i++) {
+ size_t end = str.find(delim, start);
+ NPNR_ASSERT((i == (id_count - 1)) || (end != std::string::npos));
+ list.ids[i] = ctx->id(str.substr(start, end - start));
+ start = end + 1;
+ }
+ return list;
+}
+
+void IdStringList::build_str(const Context *ctx, std::string &str) const
+{
+ char delim = ctx->getNameDelimiter();
+ bool first = true;
+ str.clear();
+ for (auto entry : ids) {
+ if (!first)
+ str += delim;
+ str += entry.str(ctx);
+ first = false;
+ }
+}
+
+std::string IdStringList::str(const Context *ctx) const
+{
+ std::string s;
+ build_str(ctx, s);
+ return s;
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/common/idstringlist.h b/common/idstringlist.h
new file mode 100644
index 00000000..6a6a554d
--- /dev/null
+++ b/common/idstringlist.h
@@ -0,0 +1,85 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef IDSTRING_LIST_H
+#define IDSTRING_LIST_H
+
+#include <boost/functional/hash.hpp>
+#include "idstring.h"
+#include "nextpnr_namespaces.h"
+#include "sso_array.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+struct Context;
+
+struct IdStringList
+{
+ SSOArray<IdString, 4> ids;
+
+ IdStringList(){};
+ explicit IdStringList(size_t n) : ids(n, IdString()){};
+ explicit IdStringList(IdString id) : ids(1, id){};
+ template <typename Tlist> explicit IdStringList(const Tlist &list) : ids(list){};
+
+ static IdStringList parse(Context *ctx, const std::string &str);
+ void build_str(const Context *ctx, std::string &str) const;
+ std::string str(const Context *ctx) const;
+
+ size_t size() const { return ids.size(); }
+ const IdString *begin() const { return ids.begin(); }
+ const IdString *end() const { return ids.end(); }
+ const IdString &operator[](size_t idx) const { return ids[idx]; }
+ bool operator==(const IdStringList &other) const { return ids == other.ids; }
+ bool operator!=(const IdStringList &other) const { return ids != other.ids; }
+ bool operator<(const IdStringList &other) const
+ {
+ if (size() > other.size())
+ return false;
+ if (size() < other.size())
+ return true;
+ for (size_t i = 0; i < size(); i++) {
+ IdString a = ids[i], b = other[i];
+ if (a.index < b.index)
+ return true;
+ if (a.index > b.index)
+ return false;
+ }
+ return false;
+ }
+};
+
+NEXTPNR_NAMESPACE_END
+
+namespace std {
+template <> struct hash<NEXTPNR_NAMESPACE_PREFIX IdStringList>
+{
+ std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX IdStringList &obj) const noexcept
+ {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, hash<size_t>()(obj.size()));
+ for (auto &id : obj)
+ boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(id));
+ return seed;
+ }
+};
+} // namespace std
+
+#endif /* IDSTRING_LIST_H */
diff --git a/common/log.h b/common/log.h
index 5745df0d..7dfdf165 100644
--- a/common/log.h
+++ b/common/log.h
@@ -28,7 +28,7 @@
#include <string>
#include <unordered_map>
#include <vector>
-#include "nextpnr.h"
+#include "nextpnr_namespaces.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -62,7 +62,6 @@ extern std::unordered_map<LogLevel, int> message_count_by_level;
std::string stringf(const char *fmt, ...);
std::string vstringf(const char *fmt, va_list ap);
-extern std::ostream clog;
void log(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2));
void log_always(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2));
void log_info(const char *format, ...) NPNR_ATTRIBUTE(format(printf, 1, 2));
diff --git a/common/nextpnr.cc b/common/nextpnr.cc
index f05f4a55..1229a7df 100644
--- a/common/nextpnr.cc
+++ b/common/nextpnr.cc
@@ -17,12 +17,6 @@
*
*/
-#include "nextpnr.h"
-#include <boost/algorithm/string.hpp>
-#include "design_utils.h"
-#include "log.h"
-#include "util.h"
-
#if defined(__wasm)
extern "C" {
// FIXME: WASI does not currently support exceptions.
@@ -35,863 +29,3 @@ namespace boost {
void throw_exception(std::exception const &e) { NEXTPNR_NAMESPACE::log_error("boost::exception(): %s\n", e.what()); }
} // namespace boost
#endif
-
-NEXTPNR_NAMESPACE_BEGIN
-
-assertion_failure::assertion_failure(std::string msg, std::string expr_str, std::string filename, int line)
- : runtime_error("Assertion failure: " + msg + " (" + filename + ":" + std::to_string(line) + ")"), msg(msg),
- expr_str(expr_str), filename(filename), line(line)
-{
- log_flush();
-}
-
-void IdString::set(const BaseCtx *ctx, const std::string &s)
-{
- auto it = ctx->idstring_str_to_idx->find(s);
- if (it == ctx->idstring_str_to_idx->end()) {
- index = ctx->idstring_idx_to_str->size();
- auto insert_rc = ctx->idstring_str_to_idx->insert({s, index});
- ctx->idstring_idx_to_str->push_back(&insert_rc.first->first);
- } else {
- index = it->second;
- }
-}
-
-const std::string &IdString::str(const BaseCtx *ctx) const { return *ctx->idstring_idx_to_str->at(index); }
-
-const char *IdString::c_str(const BaseCtx *ctx) const { return str(ctx).c_str(); }
-
-void IdString::initialize_add(const BaseCtx *ctx, const char *s, int idx)
-{
- NPNR_ASSERT(ctx->idstring_str_to_idx->count(s) == 0);
- NPNR_ASSERT(int(ctx->idstring_idx_to_str->size()) == idx);
- auto insert_rc = ctx->idstring_str_to_idx->insert({s, idx});
- ctx->idstring_idx_to_str->push_back(&insert_rc.first->first);
-}
-
-IdStringList IdStringList::parse(Context *ctx, const std::string &str)
-{
- char delim = ctx->getNameDelimiter();
- size_t id_count = std::count(str.begin(), str.end(), delim) + 1;
- IdStringList list(id_count);
- size_t start = 0;
- for (size_t i = 0; i < id_count; i++) {
- size_t end = str.find(delim, start);
- NPNR_ASSERT((i == (id_count - 1)) || (end != std::string::npos));
- list.ids[i] = ctx->id(str.substr(start, end - start));
- start = end + 1;
- }
- return list;
-}
-
-void IdStringList::build_str(const Context *ctx, std::string &str) const
-{
- char delim = ctx->getNameDelimiter();
- bool first = true;
- str.clear();
- for (auto entry : ids) {
- if (!first)
- str += delim;
- str += entry.str(ctx);
- first = false;
- }
-}
-
-std::string IdStringList::str(const Context *ctx) const
-{
- std::string s;
- build_str(ctx, s);
- return s;
-}
-
-std::string &StrRingBuffer::next()
-{
- std::string &s = buffer.at(index++);
- if (index >= N)
- index = 0;
- return s;
-}
-
-Property::Property() : is_string(false), str(""), intval(0) {}
-
-Property::Property(int64_t intval, int width) : is_string(false), intval(intval)
-{
- str.reserve(width);
- for (int i = 0; i < width; i++)
- str.push_back((intval & (1ULL << i)) ? S1 : S0);
-}
-
-Property::Property(const std::string &strval) : is_string(true), str(strval), intval(0xDEADBEEF) {}
-
-Property::Property(State bit) : is_string(false), str(std::string("") + char(bit)), intval(bit == S1) {}
-
-void CellInfo::addInput(IdString name)
-{
- ports[name].name = name;
- ports[name].type = PORT_IN;
-}
-void CellInfo::addOutput(IdString name)
-{
- ports[name].name = name;
- ports[name].type = PORT_OUT;
-}
-void CellInfo::addInout(IdString name)
-{
- ports[name].name = name;
- ports[name].type = PORT_INOUT;
-}
-
-void CellInfo::setParam(IdString name, Property value) { params[name] = value; }
-void CellInfo::unsetParam(IdString name) { params.erase(name); }
-void CellInfo::setAttr(IdString name, Property value) { attrs[name] = value; }
-void CellInfo::unsetAttr(IdString name) { attrs.erase(name); }
-
-bool CellInfo::isConstrained(bool include_abs_z_constr) const
-{
- return constr_parent != nullptr || !constr_children.empty() || (include_abs_z_constr && constr_abs_z);
-}
-
-bool CellInfo::testRegion(BelId bel) const
-{
- return region == nullptr || !region->constr_bels || region->bels.count(bel);
-}
-Loc CellInfo::getConstrainedLoc(Loc parent_loc) const
-{
- NPNR_ASSERT(constr_parent != nullptr);
- Loc cloc = parent_loc;
- if (constr_x != UNCONSTR)
- cloc.x += constr_x;
- if (constr_y != UNCONSTR)
- cloc.y += constr_y;
- if (constr_z != UNCONSTR)
- cloc.z = constr_abs_z ? constr_z : (parent_loc.z + constr_z);
- return cloc;
-}
-
-std::string Property::to_string() const
-{
- if (is_string) {
- std::string result = str;
- int state = 0;
- for (char c : str) {
- if (state == 0) {
- if (c == '0' || c == '1' || c == 'x' || c == 'z')
- state = 0;
- else if (c == ' ')
- state = 1;
- else
- state = 2;
- } else if (state == 1 && c != ' ')
- state = 2;
- }
- if (state < 2)
- result += " ";
- return result;
- } else {
- return std::string(str.rbegin(), str.rend());
- }
-}
-
-Property Property::from_string(const std::string &s)
-{
- Property p;
-
- size_t cursor = s.find_first_not_of("01xz");
- if (cursor == std::string::npos) {
- p.str = std::string(s.rbegin(), s.rend());
- p.is_string = false;
- p.update_intval();
- } else if (s.find_first_not_of(' ', cursor) == std::string::npos) {
- p = Property(s.substr(0, s.size() - 1));
- } else {
- p = Property(s);
- }
- return p;
-}
-
-const char *BaseCtx::nameOfBel(BelId bel) const
-{
- const Context *ctx = getCtx();
- std::string &s = ctx->log_strs.next();
- ctx->getBelName(bel).build_str(ctx, s);
- return s.c_str();
-}
-
-const char *BaseCtx::nameOfWire(WireId wire) const
-{
- const Context *ctx = getCtx();
- std::string &s = ctx->log_strs.next();
- ctx->getWireName(wire).build_str(ctx, s);
- return s.c_str();
-}
-
-const char *BaseCtx::nameOfPip(PipId pip) const
-{
- const Context *ctx = getCtx();
- std::string &s = ctx->log_strs.next();
- ctx->getPipName(pip).build_str(ctx, s);
- return s.c_str();
-}
-
-const char *BaseCtx::nameOfGroup(GroupId group) const
-{
- const Context *ctx = getCtx();
- std::string &s = ctx->log_strs.next();
- ctx->getGroupName(group).build_str(ctx, s);
- return s.c_str();
-}
-
-BelId BaseCtx::getBelByNameStr(const std::string &str)
-{
- Context *ctx = getCtx();
- return ctx->getBelByName(IdStringList::parse(ctx, str));
-}
-
-WireId BaseCtx::getWireByNameStr(const std::string &str)
-{
- Context *ctx = getCtx();
- return ctx->getWireByName(IdStringList::parse(ctx, str));
-}
-
-PipId BaseCtx::getPipByNameStr(const std::string &str)
-{
- Context *ctx = getCtx();
- return ctx->getPipByName(IdStringList::parse(ctx, str));
-}
-
-GroupId BaseCtx::getGroupByNameStr(const std::string &str)
-{
- Context *ctx = getCtx();
- return ctx->getGroupByName(IdStringList::parse(ctx, str));
-}
-
-WireId Context::getNetinfoSourceWire(const NetInfo *net_info) const
-{
- if (net_info->driver.cell == nullptr)
- return WireId();
-
- auto src_bel = net_info->driver.cell->bel;
-
- if (src_bel == BelId())
- return WireId();
-
- auto bel_pins = getBelPinsForCellPin(net_info->driver.cell, net_info->driver.port);
- auto iter = bel_pins.begin();
- if (iter == bel_pins.end())
- return WireId();
- WireId driver = getBelPinWire(src_bel, *iter);
- ++iter;
- NPNR_ASSERT(iter == bel_pins.end()); // assert there is only one driver bel pin;
- return driver;
-}
-
-SSOArray<WireId, 2> Context::getNetinfoSinkWires(const NetInfo *net_info, const PortRef &user_info) const
-{
- auto dst_bel = user_info.cell->bel;
- if (dst_bel == BelId())
- return SSOArray<WireId, 2>(0, WireId());
- size_t bel_pin_count = 0;
- // We use an SSOArray here because it avoids any heap allocation for the 99.9% case of 1 or 2 sink wires
- // but as SSOArray doesn't (currently) support resizing to keep things simple it does mean we have to do
- // two loops
- for (auto s : getBelPinsForCellPin(user_info.cell, user_info.port)) {
- (void)s; // unused
- ++bel_pin_count;
- }
- SSOArray<WireId, 2> result(bel_pin_count, WireId());
- bel_pin_count = 0;
- for (auto pin : getBelPinsForCellPin(user_info.cell, user_info.port)) {
- result[bel_pin_count++] = getBelPinWire(dst_bel, pin);
- }
- return result;
-}
-
-size_t Context::getNetinfoSinkWireCount(const NetInfo *net_info, const PortRef &sink) const
-{
- size_t count = 0;
- for (auto s : getNetinfoSinkWires(net_info, sink)) {
- (void)s; // unused
- ++count;
- }
- return count;
-}
-
-WireId Context::getNetinfoSinkWire(const NetInfo *net_info, const PortRef &sink, size_t phys_idx) const
-{
- size_t count = 0;
- for (auto s : getNetinfoSinkWires(net_info, sink)) {
- if (count == phys_idx)
- return s;
- ++count;
- }
- /* TODO: This should be an assertion failure, but for the zero-wire case of unplaced sinks; legacy code currently
- assumes WireId Remove once the refactoring process is complete.
- */
- return WireId();
-}
-
-delay_t Context::getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &user_info) const
-{
-#ifdef ARCH_ECP5
- if (net_info->is_global)
- return 0;
-#endif
-
- if (net_info->wires.empty())
- return predictDelay(net_info, user_info);
-
- WireId src_wire = getNetinfoSourceWire(net_info);
- if (src_wire == WireId())
- return 0;
-
- delay_t max_delay = 0;
-
- for (auto dst_wire : getNetinfoSinkWires(net_info, user_info)) {
- WireId cursor = dst_wire;
- delay_t delay = 0;
-
- while (cursor != WireId() && cursor != src_wire) {
- auto it = net_info->wires.find(cursor);
-
- if (it == net_info->wires.end())
- break;
-
- PipId pip = it->second.pip;
- if (pip == PipId())
- break;
-
- delay += getPipDelay(pip).maxDelay();
- delay += getWireDelay(cursor).maxDelay();
- cursor = getPipSrcWire(pip);
- }
-
- if (cursor == src_wire)
- max_delay = std::max(max_delay, delay + getWireDelay(src_wire).maxDelay()); // routed
- else
- max_delay = std::max(max_delay, predictDelay(net_info, user_info)); // unrouted
- }
- return max_delay;
-}
-
-static uint32_t xorshift32(uint32_t x)
-{
- x ^= x << 13;
- x ^= x >> 17;
- x ^= x << 5;
- return x;
-}
-
-uint32_t Context::checksum() const
-{
- uint32_t cksum = xorshift32(123456789);
-
- uint32_t cksum_nets_sum = 0;
- for (auto &it : nets) {
- auto &ni = *it.second;
- uint32_t x = 123456789;
- x = xorshift32(x + xorshift32(it.first.index));
- x = xorshift32(x + xorshift32(ni.name.index));
- if (ni.driver.cell)
- x = xorshift32(x + xorshift32(ni.driver.cell->name.index));
- x = xorshift32(x + xorshift32(ni.driver.port.index));
- x = xorshift32(x + xorshift32(getDelayChecksum(ni.driver.budget)));
-
- for (auto &u : ni.users) {
- if (u.cell)
- x = xorshift32(x + xorshift32(u.cell->name.index));
- x = xorshift32(x + xorshift32(u.port.index));
- x = xorshift32(x + xorshift32(getDelayChecksum(u.budget)));
- }
-
- uint32_t attr_x_sum = 0;
- for (auto &a : ni.attrs) {
- uint32_t attr_x = 123456789;
- attr_x = xorshift32(attr_x + xorshift32(a.first.index));
- for (char ch : a.second.str)
- attr_x = xorshift32(attr_x + xorshift32((int)ch));
- attr_x_sum += attr_x;
- }
- x = xorshift32(x + xorshift32(attr_x_sum));
-
- uint32_t wire_x_sum = 0;
- for (auto &w : ni.wires) {
- uint32_t wire_x = 123456789;
- wire_x = xorshift32(wire_x + xorshift32(getWireChecksum(w.first)));
- wire_x = xorshift32(wire_x + xorshift32(getPipChecksum(w.second.pip)));
- wire_x = xorshift32(wire_x + xorshift32(int(w.second.strength)));
- wire_x_sum += wire_x;
- }
- x = xorshift32(x + xorshift32(wire_x_sum));
-
- cksum_nets_sum += x;
- }
- cksum = xorshift32(cksum + xorshift32(cksum_nets_sum));
-
- uint32_t cksum_cells_sum = 0;
- for (auto &it : cells) {
- auto &ci = *it.second;
- uint32_t x = 123456789;
- x = xorshift32(x + xorshift32(it.first.index));
- x = xorshift32(x + xorshift32(ci.name.index));
- x = xorshift32(x + xorshift32(ci.type.index));
-
- uint32_t port_x_sum = 0;
- for (auto &p : ci.ports) {
- uint32_t port_x = 123456789;
- port_x = xorshift32(port_x + xorshift32(p.first.index));
- port_x = xorshift32(port_x + xorshift32(p.second.name.index));
- if (p.second.net)
- port_x = xorshift32(port_x + xorshift32(p.second.net->name.index));
- port_x = xorshift32(port_x + xorshift32(p.second.type));
- port_x_sum += port_x;
- }
- x = xorshift32(x + xorshift32(port_x_sum));
-
- uint32_t attr_x_sum = 0;
- for (auto &a : ci.attrs) {
- uint32_t attr_x = 123456789;
- attr_x = xorshift32(attr_x + xorshift32(a.first.index));
- for (char ch : a.second.str)
- attr_x = xorshift32(attr_x + xorshift32((int)ch));
- attr_x_sum += attr_x;
- }
- x = xorshift32(x + xorshift32(attr_x_sum));
-
- uint32_t param_x_sum = 0;
- for (auto &p : ci.params) {
- uint32_t param_x = 123456789;
- param_x = xorshift32(param_x + xorshift32(p.first.index));
- for (char ch : p.second.str)
- param_x = xorshift32(param_x + xorshift32((int)ch));
- param_x_sum += param_x;
- }
- x = xorshift32(x + xorshift32(param_x_sum));
-
- x = xorshift32(x + xorshift32(getBelChecksum(ci.bel)));
- x = xorshift32(x + xorshift32(ci.belStrength));
-
- cksum_cells_sum += x;
- }
- cksum = xorshift32(cksum + xorshift32(cksum_cells_sum));
-
- return cksum;
-}
-
-void Context::check() const
-{
- bool check_failed = false;
-
-#define CHECK_FAIL(...) \
- do { \
- log_nonfatal_error(__VA_ARGS__); \
- check_failed = true; \
- } while (false)
-
- for (auto &n : nets) {
- auto ni = n.second.get();
- if (n.first != ni->name)
- CHECK_FAIL("net key '%s' not equal to name '%s'\n", nameOf(n.first), nameOf(ni->name));
- for (auto &w : ni->wires) {
- if (ni != getBoundWireNet(w.first))
- CHECK_FAIL("net '%s' not bound to wire '%s' in wires map\n", nameOf(n.first), nameOfWire(w.first));
- if (w.second.pip != PipId()) {
- if (w.first != getPipDstWire(w.second.pip))
- CHECK_FAIL("net '%s' has dest mismatch '%s' vs '%s' in for pip '%s'\n", nameOf(n.first),
- nameOfWire(w.first), nameOfWire(getPipDstWire(w.second.pip)), nameOfPip(w.second.pip));
- if (ni != getBoundPipNet(w.second.pip))
- CHECK_FAIL("net '%s' not bound to pip '%s' in wires map\n", nameOf(n.first),
- nameOfPip(w.second.pip));
- }
- }
- if (ni->driver.cell != nullptr) {
- if (!ni->driver.cell->ports.count(ni->driver.port)) {
- CHECK_FAIL("net '%s' driver port '%s' missing on cell '%s'\n", nameOf(n.first), nameOf(ni->driver.port),
- nameOf(ni->driver.cell));
- } else {
- const NetInfo *p_net = ni->driver.cell->ports.at(ni->driver.port).net;
- if (p_net != ni)
- CHECK_FAIL("net '%s' driver port '%s.%s' connected to incorrect net '%s'\n", nameOf(n.first),
- nameOf(ni->driver.cell), nameOf(ni->driver.port), p_net ? nameOf(p_net) : "<nullptr>");
- }
- }
- for (auto user : ni->users) {
- if (!user.cell->ports.count(user.port)) {
- CHECK_FAIL("net '%s' user port '%s' missing on cell '%s'\n", nameOf(n.first), nameOf(user.port),
- nameOf(user.cell));
- } else {
- const NetInfo *p_net = user.cell->ports.at(user.port).net;
- if (p_net != ni)
- CHECK_FAIL("net '%s' user port '%s.%s' connected to incorrect net '%s'\n", nameOf(n.first),
- nameOf(user.cell), nameOf(user.port), p_net ? nameOf(p_net) : "<nullptr>");
- }
- }
- }
-#ifdef CHECK_WIRES
- for (auto w : getWires()) {
- auto ni = getBoundWireNet(w);
- if (ni != nullptr) {
- if (!ni->wires.count(w))
- CHECK_FAIL("wire '%s' missing in wires map of bound net '%s'\n", nameOfWire(w), nameOf(ni));
- }
- }
-#endif
- for (auto &c : cells) {
- auto ci = c.second.get();
- if (c.first != ci->name)
- CHECK_FAIL("cell key '%s' not equal to name '%s'\n", nameOf(c.first), nameOf(ci->name));
- if (ci->bel != BelId()) {
- if (getBoundBelCell(c.second->bel) != ci)
- CHECK_FAIL("cell '%s' not bound to bel '%s' in bel field\n", nameOf(c.first), nameOfBel(ci->bel));
- }
- for (auto &port : c.second->ports) {
- NetInfo *net = port.second.net;
- if (net != nullptr) {
- if (nets.find(net->name) == nets.end()) {
- CHECK_FAIL("cell port '%s.%s' connected to non-existent net '%s'\n", nameOf(c.first),
- nameOf(port.first), nameOf(net->name));
- } else if (port.second.type == PORT_OUT) {
- if (net->driver.cell != c.second.get() || net->driver.port != port.first) {
- CHECK_FAIL("output cell port '%s.%s' not in driver field of net '%s'\n", nameOf(c.first),
- nameOf(port.first), nameOf(net));
- }
- } else if (port.second.type == PORT_IN) {
- int usr_count = std::count_if(net->users.begin(), net->users.end(), [&](const PortRef &pr) {
- return pr.cell == c.second.get() && pr.port == port.first;
- });
- if (usr_count != 1)
- CHECK_FAIL("input cell port '%s.%s' appears %d rather than expected 1 times in users vector of "
- "net '%s'\n",
- nameOf(c.first), nameOf(port.first), usr_count, nameOf(net));
- }
- }
- }
- }
-
-#undef CHECK_FAIL
-
- if (check_failed)
- log_error("INTERNAL CHECK FAILED: please report this error with the design and full log output. Failure "
- "details are above this message.\n");
-}
-
-void BaseCtx::addClock(IdString net, float freq)
-{
- std::unique_ptr<ClockConstraint> cc(new ClockConstraint());
- cc->period = DelayPair(getCtx()->getDelayFromNS(1000 / freq));
- cc->high = DelayPair(getCtx()->getDelayFromNS(500 / freq));
- cc->low = DelayPair(getCtx()->getDelayFromNS(500 / freq));
- if (!net_aliases.count(net)) {
- log_warning("net '%s' does not exist in design, ignoring clock constraint\n", net.c_str(this));
- } else {
- getNetByAlias(net)->clkconstr = std::move(cc);
- log_info("constraining clock net '%s' to %.02f MHz\n", net.c_str(this), freq);
- }
-}
-
-void BaseCtx::createRectangularRegion(IdString name, int x0, int y0, int x1, int y1)
-{
- std::unique_ptr<Region> new_region(new Region());
- new_region->name = name;
- new_region->constr_bels = true;
- new_region->constr_pips = false;
- new_region->constr_wires = false;
- for (int x = x0; x <= x1; x++) {
- for (int y = y0; y <= y1; y++) {
- for (auto bel : getCtx()->getBelsByTile(x, y))
- new_region->bels.insert(bel);
- }
- }
- region[name] = std::move(new_region);
-}
-void BaseCtx::addBelToRegion(IdString name, BelId bel) { region[name]->bels.insert(bel); }
-void BaseCtx::constrainCellToRegion(IdString cell, IdString region_name)
-{
- // Support hierarchical cells as well as leaf ones
- bool matched = false;
- if (hierarchy.count(cell)) {
- auto &hc = hierarchy.at(cell);
- for (auto &lc : hc.leaf_cells)
- constrainCellToRegion(lc.second, region_name);
- for (auto &hsc : hc.hier_cells)
- constrainCellToRegion(hsc.second, region_name);
- matched = true;
- }
- if (cells.count(cell)) {
- cells.at(cell)->region = region[region_name].get();
- matched = true;
- }
- if (!matched)
- log_warning("No cell matched '%s' when constraining to region '%s'\n", nameOf(cell), nameOf(region_name));
-}
-DecalXY BaseCtx::constructDecalXY(DecalId decal, float x, float y)
-{
- DecalXY dxy;
- dxy.decal = decal;
- dxy.x = x;
- dxy.y = y;
- return dxy;
-}
-
-void BaseCtx::archInfoToAttributes()
-{
- for (auto &cell : cells) {
- auto ci = cell.second.get();
- if (ci->bel != BelId()) {
- if (ci->attrs.find(id("BEL")) != ci->attrs.end()) {
- ci->attrs.erase(ci->attrs.find(id("BEL")));
- }
- ci->attrs[id("NEXTPNR_BEL")] = getCtx()->getBelName(ci->bel).str(getCtx());
- ci->attrs[id("BEL_STRENGTH")] = (int)ci->belStrength;
- }
- if (ci->constr_x != ci->UNCONSTR)
- ci->attrs[id("CONSTR_X")] = ci->constr_x;
- if (ci->constr_y != ci->UNCONSTR)
- ci->attrs[id("CONSTR_Y")] = ci->constr_y;
- if (ci->constr_z != ci->UNCONSTR) {
- ci->attrs[id("CONSTR_Z")] = ci->constr_z;
- ci->attrs[id("CONSTR_ABS_Z")] = ci->constr_abs_z ? 1 : 0;
- }
- if (ci->constr_parent != nullptr)
- ci->attrs[id("CONSTR_PARENT")] = ci->constr_parent->name.str(this);
- if (!ci->constr_children.empty()) {
- std::string constr = "";
- for (auto &item : ci->constr_children) {
- if (!constr.empty())
- constr += std::string(";");
- constr += item->name.c_str(this);
- }
- ci->attrs[id("CONSTR_CHILDREN")] = constr;
- }
- }
- for (auto &net : getCtx()->nets) {
- auto ni = net.second.get();
- std::string routing;
- bool first = true;
- for (auto &item : ni->wires) {
- if (!first)
- routing += ";";
- routing += getCtx()->getWireName(item.first).str(getCtx());
- routing += ";";
- if (item.second.pip != PipId())
- routing += getCtx()->getPipName(item.second.pip).str(getCtx());
- routing += ";" + std::to_string(item.second.strength);
- first = false;
- }
- ni->attrs[id("ROUTING")] = routing;
- }
-}
-
-void BaseCtx::attributesToArchInfo()
-{
- for (auto &cell : cells) {
- auto ci = cell.second.get();
- auto val = ci->attrs.find(id("NEXTPNR_BEL"));
- if (val != ci->attrs.end()) {
- auto str = ci->attrs.find(id("BEL_STRENGTH"));
- PlaceStrength strength = PlaceStrength::STRENGTH_USER;
- if (str != ci->attrs.end())
- strength = (PlaceStrength)str->second.as_int64();
-
- BelId b = getCtx()->getBelByNameStr(val->second.as_string());
- getCtx()->bindBel(b, ci, strength);
- }
-
- val = ci->attrs.find(id("CONSTR_PARENT"));
- if (val != ci->attrs.end()) {
- auto parent = cells.find(id(val->second.str));
- if (parent != cells.end())
- ci->constr_parent = parent->second.get();
- else
- continue;
- }
-
- val = ci->attrs.find(id("CONSTR_X"));
- if (val != ci->attrs.end())
- ci->constr_x = val->second.as_int64();
-
- val = ci->attrs.find(id("CONSTR_Y"));
- if (val != ci->attrs.end())
- ci->constr_y = val->second.as_int64();
-
- val = ci->attrs.find(id("CONSTR_Z"));
- if (val != ci->attrs.end())
- ci->constr_z = val->second.as_int64();
-
- val = ci->attrs.find(id("CONSTR_ABS_Z"));
- if (val != ci->attrs.end())
- ci->constr_abs_z = val->second.as_int64() == 1;
-
- val = ci->attrs.find(id("CONSTR_PARENT"));
- if (val != ci->attrs.end()) {
- auto parent = cells.find(id(val->second.as_string()));
- if (parent != cells.end())
- ci->constr_parent = parent->second.get();
- }
- val = ci->attrs.find(id("CONSTR_CHILDREN"));
- if (val != ci->attrs.end()) {
- std::vector<std::string> strs;
- auto children = val->second.as_string();
- boost::split(strs, children, boost::is_any_of(";"));
- for (auto val : strs) {
- if (cells.count(id(val.c_str())))
- ci->constr_children.push_back(cells.find(id(val.c_str()))->second.get());
- }
- }
- }
- for (auto &net : getCtx()->nets) {
- auto ni = net.second.get();
- auto val = ni->attrs.find(id("ROUTING"));
- if (val != ni->attrs.end()) {
- std::vector<std::string> strs;
- auto routing = val->second.as_string();
- boost::split(strs, routing, boost::is_any_of(";"));
- for (size_t i = 0; i < strs.size() / 3; i++) {
- std::string wire = strs[i * 3];
- std::string pip = strs[i * 3 + 1];
- PlaceStrength strength = (PlaceStrength)std::stoi(strs[i * 3 + 2]);
- if (pip.empty())
- getCtx()->bindWire(getCtx()->getWireByName(IdStringList::parse(getCtx(), wire)), ni, strength);
- else
- getCtx()->bindPip(getCtx()->getPipByName(IdStringList::parse(getCtx(), pip)), ni, strength);
- }
- }
- }
- getCtx()->assignArchInfo();
-}
-
-NetInfo *BaseCtx::createNet(IdString name)
-{
- NPNR_ASSERT(!nets.count(name));
- NPNR_ASSERT(!net_aliases.count(name));
- std::unique_ptr<NetInfo> net{new NetInfo};
- net->name = name;
- net_aliases[name] = name;
- NetInfo *ptr = net.get();
- nets[name] = std::move(net);
- refreshUi();
- return ptr;
-}
-
-void BaseCtx::connectPort(IdString net, IdString cell, IdString port)
-{
- NetInfo *net_info = getNetByAlias(net);
- CellInfo *cell_info = cells.at(cell).get();
- connect_port(getCtx(), net_info, cell_info, port);
-}
-
-void BaseCtx::disconnectPort(IdString cell, IdString port)
-{
- CellInfo *cell_info = cells.at(cell).get();
- disconnect_port(getCtx(), cell_info, port);
-}
-
-void BaseCtx::ripupNet(IdString name)
-{
- NetInfo *net_info = getNetByAlias(name);
- std::vector<WireId> to_unbind;
- for (auto &wire : net_info->wires)
- to_unbind.push_back(wire.first);
- for (auto &unbind : to_unbind)
- getCtx()->unbindWire(unbind);
-}
-void BaseCtx::lockNetRouting(IdString name)
-{
- NetInfo *net_info = getNetByAlias(name);
- for (auto &wire : net_info->wires)
- wire.second.strength = STRENGTH_USER;
-}
-
-CellInfo *BaseCtx::createCell(IdString name, IdString type)
-{
- NPNR_ASSERT(!cells.count(name));
- std::unique_ptr<CellInfo> cell{new CellInfo};
- cell->name = name;
- cell->type = type;
- CellInfo *ptr = cell.get();
- cells[name] = std::move(cell);
- refreshUi();
- return ptr;
-}
-
-void BaseCtx::copyBelPorts(IdString cell, BelId bel)
-{
- CellInfo *cell_info = cells.at(cell).get();
- for (auto pin : getCtx()->getBelPins(bel)) {
- cell_info->ports[pin].name = pin;
- cell_info->ports[pin].type = getCtx()->getBelPinType(bel, pin);
- }
-}
-
-namespace {
-struct FixupHierarchyWorker
-{
- FixupHierarchyWorker(Context *ctx) : ctx(ctx){};
- Context *ctx;
- void run()
- {
- trim_hierarchy(ctx->top_module);
- rebuild_hierarchy();
- };
- // Remove cells and nets that no longer exist in the netlist
- std::vector<IdString> todelete_cells, todelete_nets;
- void trim_hierarchy(IdString path)
- {
- auto &h = ctx->hierarchy.at(path);
- todelete_cells.clear();
- todelete_nets.clear();
- for (auto &lc : h.leaf_cells) {
- if (!ctx->cells.count(lc.second))
- todelete_cells.push_back(lc.first);
- }
- for (auto &n : h.nets)
- if (!ctx->nets.count(n.second))
- todelete_nets.push_back(n.first);
- for (auto tdc : todelete_cells) {
- h.leaf_cells_by_gname.erase(h.leaf_cells.at(tdc));
- h.leaf_cells.erase(tdc);
- }
- for (auto tdn : todelete_nets) {
- h.nets_by_gname.erase(h.nets.at(tdn));
- h.nets.erase(tdn);
- }
- for (auto &sc : h.hier_cells)
- trim_hierarchy(sc.second);
- }
-
- IdString construct_local_name(HierarchicalCell &hc, IdString global_name, bool is_cell)
- {
- std::string gn = global_name.str(ctx);
- auto dp = gn.find_last_of('.');
- if (dp != std::string::npos)
- gn = gn.substr(dp + 1);
- IdString name = ctx->id(gn);
- // Make sure name is unique
- int adder = 0;
- while (is_cell ? hc.leaf_cells.count(name) : hc.nets.count(name)) {
- ++adder;
- name = ctx->id(gn + "$" + std::to_string(adder));
- }
- return name;
- }
-
- // Update hierarchy structure for nets and cells that have hiercell set
- void rebuild_hierarchy()
- {
- for (auto cell : sorted(ctx->cells)) {
- CellInfo *ci = cell.second;
- if (ci->hierpath == IdString())
- ci->hierpath = ctx->top_module;
- auto &hc = ctx->hierarchy.at(ci->hierpath);
- if (hc.leaf_cells_by_gname.count(ci->name))
- continue; // already known
- IdString local_name = construct_local_name(hc, ci->name, true);
- hc.leaf_cells_by_gname[ci->name] = local_name;
- hc.leaf_cells[local_name] = ci->name;
- }
- }
-};
-} // namespace
-
-void Context::fixupHierarchy() { FixupHierarchyWorker(this).run(); }
-
-NEXTPNR_NAMESPACE_END
diff --git a/common/nextpnr.h b/common/nextpnr.h
index 90c3ed6d..5cd6d4b5 100644
--- a/common/nextpnr.h
+++ b/common/nextpnr.h
@@ -17,1516 +17,13 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
-
-#include <algorithm>
-#include <assert.h>
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-#include <stdexcept>
-#include <stdint.h>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-#include <boost/functional/hash.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/range/adaptor/reversed.hpp>
-#ifndef NPNR_DISABLE_THREADS
-#include <boost/thread.hpp>
-#endif
-
#ifndef NEXTPNR_H
#define NEXTPNR_H
-#ifdef NEXTPNR_NAMESPACE
-#define NEXTPNR_NAMESPACE_PREFIX NEXTPNR_NAMESPACE::
-#define NEXTPNR_NAMESPACE_BEGIN namespace NEXTPNR_NAMESPACE {
-#define NEXTPNR_NAMESPACE_END }
-#define USING_NEXTPNR_NAMESPACE using namespace NEXTPNR_NAMESPACE;
-#else
-#define NEXTPNR_NAMESPACE_PREFIX
-#define NEXTPNR_NAMESPACE_BEGIN
-#define NEXTPNR_NAMESPACE_END
-#define USING_NEXTPNR_NAMESPACE
-#endif
-
-#if defined(__GNUC__) || defined(__clang__)
-#define NPNR_ATTRIBUTE(...) __attribute__((__VA_ARGS__))
-#define NPNR_NORETURN __attribute__((noreturn))
-#define NPNR_DEPRECATED __attribute__((deprecated))
-#define NPNR_PACKED_STRUCT(...) __VA_ARGS__ __attribute__((packed))
-#elif defined(_MSC_VER)
-#define NPNR_ATTRIBUTE(...)
-#define NPNR_NORETURN __declspec(noreturn)
-#define NPNR_DEPRECATED __declspec(deprecated)
-#define NPNR_PACKED_STRUCT(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop))
-#else
-#define NPNR_ATTRIBUTE(...)
-#define NPNR_NORETURN
-#define NPNR_DEPRECATED
-#define NPNR_PACKED_STRUCT(...) __VA_ARGS__
-#endif
-
-NEXTPNR_NAMESPACE_BEGIN
-
-class assertion_failure : public std::runtime_error
-{
- public:
- assertion_failure(std::string msg, std::string expr_str, std::string filename, int line);
-
- std::string msg;
- std::string expr_str;
- std::string filename;
- int line;
-};
-
-NPNR_NORETURN
-inline void assert_fail_impl(const char *message, const char *expr_str, const char *filename, int line)
-{
- throw assertion_failure(message, expr_str, filename, line);
-}
-
-NPNR_NORETURN
-inline void assert_fail_impl_str(std::string message, const char *expr_str, const char *filename, int line)
-{
- throw assertion_failure(message, expr_str, filename, line);
-}
-
-#define NPNR_ASSERT(cond) (!(cond) ? assert_fail_impl(#cond, #cond, __FILE__, __LINE__) : (void)true)
-#define NPNR_ASSERT_MSG(cond, msg) (!(cond) ? assert_fail_impl(msg, #cond, __FILE__, __LINE__) : (void)true)
-#define NPNR_ASSERT_FALSE(msg) (assert_fail_impl(msg, "false", __FILE__, __LINE__))
-#define NPNR_ASSERT_FALSE_STR(msg) (assert_fail_impl_str(msg, "false", __FILE__, __LINE__))
-
-#define NPNR_STRINGIFY(x) #x
-
-struct BaseCtx;
-struct Context;
-
-struct IdString
-{
- int index;
-
- static void initialize_arch(const BaseCtx *ctx);
-
- static void initialize_add(const BaseCtx *ctx, const char *s, int idx);
-
- constexpr IdString() : index(0) {}
- explicit constexpr IdString(int index) : index(index) {}
-
- void set(const BaseCtx *ctx, const std::string &s);
-
- IdString(const BaseCtx *ctx, const std::string &s) { set(ctx, s); }
-
- IdString(const BaseCtx *ctx, const char *s) { set(ctx, s); }
-
- const std::string &str(const BaseCtx *ctx) const;
-
- const char *c_str(const BaseCtx *ctx) const;
-
- bool operator<(const IdString &other) const { return index < other.index; }
-
- bool operator==(const IdString &other) const { return index == other.index; }
-
- bool operator!=(const IdString &other) const { return index != other.index; }
-
- bool empty() const { return index == 0; }
-};
-
-NEXTPNR_NAMESPACE_END
-
-namespace std {
-template <> struct hash<NEXTPNR_NAMESPACE_PREFIX IdString>
-{
- std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX IdString &obj) const noexcept
- {
- return std::hash<int>()(obj.index);
- }
-};
-} // namespace std
-
-NEXTPNR_NAMESPACE_BEGIN
-
-// An small size optimised array that is statically allocated when the size is N or less; heap allocated otherwise
-template <typename T, size_t N> class SSOArray
-{
- private:
- union
- {
- T data_static[N];
- T *data_heap;
- };
- size_t m_size;
- inline bool is_heap() const { return (m_size > N); }
- void alloc()
- {
- if (is_heap()) {
- data_heap = new T[m_size];
- }
- }
-
- public:
- T *data() { return is_heap() ? data_heap : data_static; }
- const T *data() const { return is_heap() ? data_heap : data_static; }
- size_t size() const { return m_size; }
-
- T *begin() { return data(); }
- T *end() { return data() + m_size; }
- const T *begin() const { return data(); }
- const T *end() const { return data() + m_size; }
-
- SSOArray() : m_size(0){};
-
- SSOArray(size_t size, const T &init = T()) : m_size(size)
- {
- alloc();
- std::fill(begin(), end(), init);
- }
-
- SSOArray(const SSOArray &other) : m_size(other.size())
- {
- alloc();
- std::copy(other.begin(), other.end(), begin());
- }
-
- template <typename Tother> SSOArray(const Tother &other) : m_size(other.size())
- {
- alloc();
- std::copy(other.begin(), other.end(), begin());
- }
-
- ~SSOArray()
- {
- if (is_heap()) {
- delete[] data_heap;
- }
- }
-
- bool operator==(const SSOArray &other) const
- {
- if (size() != other.size())
- return false;
- return std::equal(begin(), end(), other.begin());
- }
- bool operator!=(const SSOArray &other) const
- {
- if (size() != other.size())
- return true;
- return !std::equal(begin(), end(), other.begin());
- }
- T &operator[](size_t idx)
- {
- NPNR_ASSERT(idx < m_size);
- return data()[idx];
- }
- const T &operator[](size_t idx) const
- {
- NPNR_ASSERT(idx < m_size);
- return data()[idx];
- }
-};
-
-struct IdStringList
-{
- SSOArray<IdString, 4> ids;
-
- IdStringList(){};
- explicit IdStringList(size_t n) : ids(n, IdString()){};
- explicit IdStringList(IdString id) : ids(1, id){};
- template <typename Tlist> explicit IdStringList(const Tlist &list) : ids(list){};
-
- static IdStringList parse(Context *ctx, const std::string &str);
- void build_str(const Context *ctx, std::string &str) const;
- std::string str(const Context *ctx) const;
-
- size_t size() const { return ids.size(); }
- const IdString *begin() const { return ids.begin(); }
- const IdString *end() const { return ids.end(); }
- const IdString &operator[](size_t idx) const { return ids[idx]; }
- bool operator==(const IdStringList &other) const { return ids == other.ids; }
- bool operator!=(const IdStringList &other) const { return ids != other.ids; }
- bool operator<(const IdStringList &other) const
- {
- if (size() > other.size())
- return false;
- if (size() < other.size())
- return true;
- for (size_t i = 0; i < size(); i++) {
- IdString a = ids[i], b = other[i];
- if (a.index < b.index)
- return true;
- if (a.index > b.index)
- return false;
- }
- return false;
- }
-};
-
-NEXTPNR_NAMESPACE_END
-
-namespace std {
-template <> struct hash<NEXTPNR_NAMESPACE_PREFIX IdStringList>
-{
- std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX IdStringList &obj) const noexcept
- {
- std::size_t seed = 0;
- boost::hash_combine(seed, hash<size_t>()(obj.size()));
- for (auto &id : obj)
- boost::hash_combine(seed, hash<NEXTPNR_NAMESPACE_PREFIX IdString>()(id));
- return seed;
- }
-};
-} // namespace std
-
-NEXTPNR_NAMESPACE_BEGIN
-
-// A ring buffer of strings, so we can return a simple const char * pointer for %s formatting - inspired by how logging
-// in Yosys works Let's just hope noone tries to log more than 100 things in one call....
-class StrRingBuffer
-{
- private:
- static const size_t N = 100;
- std::array<std::string, N> buffer;
- size_t index = 0;
-
- public:
- std::string &next();
-};
-
-struct GraphicElement
-{
- enum type_t
- {
- TYPE_NONE,
- TYPE_LINE,
- TYPE_ARROW,
- TYPE_BOX,
- TYPE_CIRCLE,
- TYPE_LABEL,
-
- TYPE_MAX
- } type = TYPE_NONE;
-
- enum style_t
- {
- STYLE_GRID,
- STYLE_FRAME, // Static "frame". Contrast between STYLE_INACTIVE and STYLE_ACTIVE
- STYLE_HIDDEN, // Only display when object is selected or highlighted
- STYLE_INACTIVE, // Render using low-contrast color
- STYLE_ACTIVE, // Render using high-contast color
-
- // UI highlight groups
- STYLE_HIGHLIGHTED0,
- STYLE_HIGHLIGHTED1,
- STYLE_HIGHLIGHTED2,
- STYLE_HIGHLIGHTED3,
- STYLE_HIGHLIGHTED4,
- STYLE_HIGHLIGHTED5,
- STYLE_HIGHLIGHTED6,
- STYLE_HIGHLIGHTED7,
-
- STYLE_SELECTED,
- STYLE_HOVER,
-
- STYLE_MAX
- } style = STYLE_FRAME;
-
- float x1 = 0, y1 = 0, x2 = 0, y2 = 0, z = 0;
- std::string text;
- GraphicElement(){};
- GraphicElement(type_t type, style_t style, float x1, float y1, float x2, float y2, float z)
- : type(type), style(style), x1(x1), y1(y1), x2(x2), y2(y2), z(z){};
-};
-
-struct Loc
-{
- int x = -1, y = -1, z = -1;
-
- Loc() {}
- Loc(int x, int y, int z) : x(x), y(y), z(z) {}
-
- bool operator==(const Loc &other) const { return (x == other.x) && (y == other.y) && (z == other.z); }
- bool operator!=(const Loc &other) const { return (x != other.x) || (y != other.y) || (z != other.z); }
-};
-
-struct ArcBounds
-{
- int x0 = -1, y0 = -1, x1 = -1, y1 = -1;
-
- ArcBounds() {}
- ArcBounds(int x0, int y0, int x1, int y1) : x0(x0), y0(y0), x1(x1), y1(y1){};
-
- int distance(Loc loc) const
- {
- int dist = 0;
- if (loc.x < x0)
- dist += x0 - loc.x;
- if (loc.x > x1)
- dist += loc.x - x1;
- if (loc.y < y0)
- dist += y0 - loc.y;
- if (loc.y > y1)
- dist += loc.y - y1;
- return dist;
- };
-
- bool contains(int x, int y) const { return x >= x0 && y >= y0 && x <= x1 && y <= y1; }
-};
-
-NEXTPNR_NAMESPACE_END
-
-namespace std {
-template <> struct hash<NEXTPNR_NAMESPACE_PREFIX Loc>
-{
- std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX Loc &obj) const noexcept
- {
- std::size_t seed = 0;
- boost::hash_combine(seed, hash<int>()(obj.x));
- boost::hash_combine(seed, hash<int>()(obj.y));
- boost::hash_combine(seed, hash<int>()(obj.z));
- return seed;
- }
-};
-} // namespace std
-
-#include "archdefs.h"
-
-NEXTPNR_NAMESPACE_BEGIN
-
-struct DecalXY
-{
- DecalId decal;
- float x = 0, y = 0;
-
- bool operator==(const DecalXY &other) const { return (decal == other.decal && x == other.x && y == other.y); }
-};
-
-struct BelPin
-{
- BelId bel;
- IdString pin;
-};
-
-struct CellInfo;
-
-struct Region
-{
- IdString name;
-
- bool constr_bels = false;
- bool constr_wires = false;
- bool constr_pips = false;
-
- std::unordered_set<BelId> bels;
- std::unordered_set<WireId> wires;
- std::unordered_set<Loc> piplocs;
-};
-
-enum PlaceStrength
-{
- STRENGTH_NONE = 0,
- STRENGTH_WEAK = 1,
- STRENGTH_STRONG = 2,
- STRENGTH_PLACER = 3,
- STRENGTH_FIXED = 4,
- STRENGTH_LOCKED = 5,
- STRENGTH_USER = 6
-};
-
-struct PortRef
-{
- CellInfo *cell = nullptr;
- IdString port;
- delay_t budget = 0;
-};
-
-struct PipMap
-{
- PipId pip = PipId();
- PlaceStrength strength = STRENGTH_NONE;
-};
-
-struct Property
-{
- enum State : char
- {
- S0 = '0',
- S1 = '1',
- Sx = 'x',
- Sz = 'z'
- };
-
- Property();
- Property(int64_t intval, int width = 32);
- Property(const std::string &strval);
- Property(State bit);
- Property &operator=(const Property &other) = default;
-
- bool is_string;
-
- // The string literal (for string values), or a string of [01xz] (for numeric values)
- std::string str;
- // The lower 64 bits (for numeric values), unused for string values
- int64_t intval;
-
- void update_intval()
- {
- intval = 0;
- for (int i = 0; i < int(str.size()); i++) {
- NPNR_ASSERT(str[i] == S0 || str[i] == S1 || str[i] == Sx || str[i] == Sz);
- if ((str[i] == S1) && i < 64)
- intval |= (1ULL << i);
- }
- }
-
- int64_t as_int64() const
- {
- NPNR_ASSERT(!is_string);
- return intval;
- }
- std::vector<bool> as_bits() const
- {
- std::vector<bool> result;
- result.reserve(str.size());
- NPNR_ASSERT(!is_string);
- for (auto c : str)
- result.push_back(c == S1);
- return result;
- }
- std::string as_string() const
- {
- NPNR_ASSERT(is_string);
- return str;
- }
- const char *c_str() const
- {
- NPNR_ASSERT(is_string);
- return str.c_str();
- }
- size_t size() const { return is_string ? 8 * str.size() : str.size(); }
- double as_double() const
- {
- NPNR_ASSERT(is_string);
- return std::stod(str);
- }
- bool as_bool() const
- {
- if (int(str.size()) <= 64)
- return intval != 0;
- else
- return std::any_of(str.begin(), str.end(), [](char c) { return c == S1; });
- }
- bool is_fully_def() const
- {
- return !is_string && std::all_of(str.begin(), str.end(), [](char c) { return c == S0 || c == S1; });
- }
- Property extract(int offset, int len, State padding = State::S0) const
- {
- Property ret;
- ret.is_string = false;
- ret.str.reserve(len);
- for (int i = offset; i < offset + len; i++)
- ret.str.push_back(i < int(str.size()) ? str[i] : char(padding));
- ret.update_intval();
- return ret;
- }
- // Convert to a string representation, escaping literal strings matching /^[01xz]* *$/ by adding a space at the end,
- // to disambiguate from binary strings
- std::string to_string() const;
- // Convert a string of four-value binary [01xz], or a literal string escaped according to the above rule
- // to a Property
- static Property from_string(const std::string &s);
-};
-
-inline bool operator==(const Property &a, const Property &b) { return a.is_string == b.is_string && a.str == b.str; }
-inline bool operator!=(const Property &a, const Property &b) { return a.is_string != b.is_string || a.str != b.str; }
-
-// minimum and maximum delay
-struct DelayPair
-{
- DelayPair(){};
- explicit DelayPair(delay_t delay) : min_delay(delay), max_delay(delay){};
- DelayPair(delay_t min_delay, delay_t max_delay) : min_delay(min_delay), max_delay(max_delay){};
- delay_t minDelay() const { return min_delay; };
- delay_t maxDelay() const { return max_delay; };
- delay_t min_delay, max_delay;
- DelayPair operator+(const DelayPair &other) const
- {
- return {min_delay + other.min_delay, max_delay + other.max_delay};
- }
- DelayPair operator-(const DelayPair &other) const
- {
- return {min_delay - other.min_delay, max_delay - other.max_delay};
- }
-};
-
-// four-quadrant, min and max rise and fall delay
-struct DelayQuad
-{
- DelayPair rise, fall;
- DelayQuad(){};
- explicit DelayQuad(delay_t delay) : rise(delay), fall(delay){};
- DelayQuad(delay_t min_delay, delay_t max_delay) : rise(min_delay, max_delay), fall(min_delay, max_delay){};
- DelayQuad(DelayPair rise, DelayPair fall) : rise(rise), fall(fall){};
- DelayQuad(delay_t min_rise, delay_t max_rise, delay_t min_fall, delay_t max_fall)
- : rise(min_rise, max_rise), fall(min_fall, max_fall){};
-
- delay_t minRiseDelay() const { return rise.minDelay(); };
- delay_t maxRiseDelay() const { return rise.maxDelay(); };
- delay_t minFallDelay() const { return fall.minDelay(); };
- delay_t maxFallDelay() const { return fall.maxDelay(); };
- delay_t minDelay() const { return std::min<delay_t>(rise.minDelay(), fall.minDelay()); };
- delay_t maxDelay() const { return std::max<delay_t>(rise.maxDelay(), fall.maxDelay()); };
-
- DelayPair delayPair() const { return DelayPair(minDelay(), maxDelay()); };
-
- DelayQuad operator+(const DelayQuad &other) const { return {rise + other.rise, fall + other.fall}; }
- DelayQuad operator-(const DelayQuad &other) const { return {rise - other.rise, fall - other.fall}; }
-};
-
-struct ClockConstraint;
-
-struct NetInfo : ArchNetInfo
-{
- IdString name, hierpath;
- int32_t udata = 0;
-
- PortRef driver;
- std::vector<PortRef> users;
- std::unordered_map<IdString, Property> attrs;
-
- // wire -> uphill_pip
- std::unordered_map<WireId, PipMap> wires;
-
- std::vector<IdString> aliases; // entries in net_aliases that point to this net
-
- std::unique_ptr<ClockConstraint> clkconstr;
-
- Region *region = nullptr;
-};
-
-enum PortType
-{
- PORT_IN = 0,
- PORT_OUT = 1,
- PORT_INOUT = 2
-};
-
-struct PortInfo
-{
- IdString name;
- NetInfo *net;
- PortType type;
-};
-
-struct CellInfo : ArchCellInfo
-{
- IdString name, type, hierpath;
- int32_t udata;
-
- std::unordered_map<IdString, PortInfo> ports;
- std::unordered_map<IdString, Property> attrs, params;
-
- BelId bel;
- PlaceStrength belStrength = STRENGTH_NONE;
-
- // placement constraints
- CellInfo *constr_parent = nullptr;
- std::vector<CellInfo *> constr_children;
- const int UNCONSTR = INT_MIN;
- int constr_x = UNCONSTR; // this.x - parent.x
- int constr_y = UNCONSTR; // this.y - parent.y
- int constr_z = UNCONSTR; // this.z - parent.z
- bool constr_abs_z = false; // parent.z := 0
- // parent.[xyz] := 0 when (constr_parent == nullptr)
-
- Region *region = nullptr;
-
- void addInput(IdString name);
- void addOutput(IdString name);
- void addInout(IdString name);
-
- void setParam(IdString name, Property value);
- void unsetParam(IdString name);
- void setAttr(IdString name, Property value);
- void unsetAttr(IdString name);
-
- // return true if the cell has placement constraints (optionally excluding the case where the only case is an
- // absolute z constraint)
- bool isConstrained(bool include_abs_z_constr = true) const;
- // check whether a bel complies with the cell's region constraint
- bool testRegion(BelId bel) const;
- // get the constrained location for this cell given a provisional location for its parent
- Loc getConstrainedLoc(Loc parent_loc) const;
-};
-
-enum TimingPortClass
-{
- TMG_CLOCK_INPUT, // Clock input to a sequential cell
- TMG_GEN_CLOCK, // Generated clock output (PLL, DCC, etc)
- TMG_REGISTER_INPUT, // Input to a register, with an associated clock (may also have comb. fanout too)
- TMG_REGISTER_OUTPUT, // Output from a register
- TMG_COMB_INPUT, // Combinational input, no paths end here
- TMG_COMB_OUTPUT, // Combinational output, no paths start here
- TMG_STARTPOINT, // Unclocked primary startpoint, such as an IO cell output
- TMG_ENDPOINT, // Unclocked primary endpoint, such as an IO cell input
- TMG_IGNORE, // Asynchronous to all clocks, "don't care", and should be ignored (false path) for analysis
-};
-
-enum ClockEdge
-{
- RISING_EDGE,
- FALLING_EDGE
-};
-
-struct TimingClockingInfo
-{
- IdString clock_port; // Port name of clock domain
- ClockEdge edge;
- DelayPair setup, hold; // Input timing checks
- DelayQuad clockToQ; // Output clock-to-Q time
-};
-
-struct ClockConstraint
-{
- DelayPair high;
- DelayPair low;
- DelayPair period;
-};
-
-// Represents the contents of a non-leaf cell in a design
-// with hierarchy
-
-struct HierarchicalPort
-{
- IdString name;
- PortType dir;
- std::vector<IdString> nets;
- int offset;
- bool upto;
-};
-
-struct HierarchicalCell
-{
- IdString name, type, parent, fullpath;
- // Name inside cell instance -> global name
- std::unordered_map<IdString, IdString> leaf_cells, nets;
- // Global name -> name inside cell instance
- std::unordered_map<IdString, IdString> leaf_cells_by_gname, nets_by_gname;
- // Cell port to net
- std::unordered_map<IdString, HierarchicalPort> ports;
- // Name inside cell instance -> global name
- std::unordered_map<IdString, IdString> hier_cells;
-};
-
-struct DeterministicRNG
-{
- uint64_t rngstate;
-
- DeterministicRNG() : rngstate(0x3141592653589793) {}
-
- uint64_t rng64()
- {
- // xorshift64star
- // https://arxiv.org/abs/1402.6246
-
- uint64_t retval = rngstate * 0x2545F4914F6CDD1D;
-
- rngstate ^= rngstate >> 12;
- rngstate ^= rngstate << 25;
- rngstate ^= rngstate >> 27;
-
- return retval;
- }
-
- int rng() { return rng64() & 0x3fffffff; }
-
- int rng(int n)
- {
- assert(n > 0);
-
- // round up to power of 2
- int m = n - 1;
- m |= (m >> 1);
- m |= (m >> 2);
- m |= (m >> 4);
- m |= (m >> 8);
- m |= (m >> 16);
- m += 1;
-
- while (1) {
- int x = rng64() & (m - 1);
- if (x < n)
- return x;
- }
- }
-
- void rngseed(uint64_t seed)
- {
- rngstate = seed ? seed : 0x3141592653589793;
- for (int i = 0; i < 5; i++)
- rng64();
- }
-
- template <typename Iter> void shuffle(const Iter &begin, const Iter &end)
- {
- size_t size = end - begin;
- for (size_t i = 0; i != size; i++) {
- size_t j = i + rng(size - i);
- if (j > i)
- std::swap(*(begin + i), *(begin + j));
- }
- }
-
- template <typename T> void shuffle(std::vector<T> &a) { shuffle(a.begin(), a.end()); }
-
- template <typename T> void sorted_shuffle(std::vector<T> &a)
- {
- std::sort(a.begin(), a.end());
- shuffle(a);
- }
-};
-
-struct BaseCtx
-{
-#ifndef NPNR_DISABLE_THREADS
- // Lock to perform mutating actions on the Context.
- std::mutex mutex;
- boost::thread::id mutex_owner;
-
- // Lock to be taken by UI when wanting to access context - the yield()
- // method will lock/unlock it when its' released the main mutex to make
- // sure the UI is not starved.
- std::mutex ui_mutex;
-#endif
-
- // ID String database.
- mutable std::unordered_map<std::string, int> *idstring_str_to_idx;
- mutable std::vector<const std::string *> *idstring_idx_to_str;
-
- // Temporary string backing store for logging
- mutable StrRingBuffer log_strs;
-
- // Project settings and config switches
- std::unordered_map<IdString, Property> settings;
-
- // Placed nets and cells.
- std::unordered_map<IdString, std::unique_ptr<NetInfo>> nets;
- std::unordered_map<IdString, std::unique_ptr<CellInfo>> cells;
-
- // Hierarchical (non-leaf) cells by full path
- std::unordered_map<IdString, HierarchicalCell> hierarchy;
- // This is the root of the above structure
- IdString top_module;
-
- // Aliases for nets, which may have more than one name due to assignments and hierarchy
- std::unordered_map<IdString, IdString> net_aliases;
-
- // Top-level ports
- std::unordered_map<IdString, PortInfo> ports;
- std::unordered_map<IdString, CellInfo *> port_cells;
-
- // Floorplanning regions
- std::unordered_map<IdString, std::unique_ptr<Region>> region;
-
- // Context meta data
- std::unordered_map<IdString, Property> attrs;
-
- Context *as_ctx = nullptr;
-
- // Has the frontend loaded a design?
- bool design_loaded;
-
- BaseCtx()
- {
- idstring_str_to_idx = new std::unordered_map<std::string, int>;
- idstring_idx_to_str = new std::vector<const std::string *>;
- IdString::initialize_add(this, "", 0);
- IdString::initialize_arch(this);
-
- design_loaded = false;
- }
-
- virtual ~BaseCtx()
- {
- delete idstring_str_to_idx;
- delete idstring_idx_to_str;
- }
-
- // Must be called before performing any mutating changes on the Ctx/Arch.
- void lock(void)
- {
-#ifndef NPNR_DISABLE_THREADS
- mutex.lock();
- mutex_owner = boost::this_thread::get_id();
-#endif
- }
-
- void unlock(void)
- {
-#ifndef NPNR_DISABLE_THREADS
- NPNR_ASSERT(boost::this_thread::get_id() == mutex_owner);
- mutex.unlock();
-#endif
- }
-
- // Must be called by the UI before rendering data. This lock will be
- // prioritized when processing code calls yield().
- void lock_ui(void)
- {
-#ifndef NPNR_DISABLE_THREADS
- ui_mutex.lock();
- mutex.lock();
-#endif
- }
-
- void unlock_ui(void)
- {
-#ifndef NPNR_DISABLE_THREADS
- mutex.unlock();
- ui_mutex.unlock();
-#endif
- }
-
- // Yield to UI by unlocking the main mutex, flashing the UI mutex and
- // relocking the main mutex. Call this when you're performing a
- // long-standing action while holding a lock to let the UI show
- // visualization updates.
- // Must be called with the main lock taken.
- void yield(void)
- {
-#ifndef NPNR_DISABLE_THREADS
- unlock();
- ui_mutex.lock();
- ui_mutex.unlock();
- lock();
-#endif
- }
-
- IdString id(const std::string &s) const { return IdString(this, s); }
-
- IdString id(const char *s) const { return IdString(this, s); }
-
- Context *getCtx() { return as_ctx; }
-
- const Context *getCtx() const { return as_ctx; }
-
- const char *nameOf(IdString name) const { return name.c_str(this); }
-
- template <typename T> const char *nameOf(const T *obj) const
- {
- if (obj == nullptr)
- return "";
- return obj->name.c_str(this);
- }
-
- const char *nameOfBel(BelId bel) const;
- const char *nameOfWire(WireId wire) const;
- const char *nameOfPip(PipId pip) const;
- const char *nameOfGroup(GroupId group) const;
-
- // Wrappers of arch functions that take a string and handle IdStringList parsing
- BelId getBelByNameStr(const std::string &str);
- WireId getWireByNameStr(const std::string &str);
- PipId getPipByNameStr(const std::string &str);
- GroupId getGroupByNameStr(const std::string &str);
-
- // --------------------------------------------------------------
-
- bool allUiReload = true;
- bool frameUiReload = false;
- std::unordered_set<BelId> belUiReload;
- std::unordered_set<WireId> wireUiReload;
- std::unordered_set<PipId> pipUiReload;
- std::unordered_set<GroupId> groupUiReload;
-
- void refreshUi() { allUiReload = true; }
-
- void refreshUiFrame() { frameUiReload = true; }
-
- void refreshUiBel(BelId bel) { belUiReload.insert(bel); }
-
- void refreshUiWire(WireId wire) { wireUiReload.insert(wire); }
-
- void refreshUiPip(PipId pip) { pipUiReload.insert(pip); }
-
- void refreshUiGroup(GroupId group) { groupUiReload.insert(group); }
-
- // --------------------------------------------------------------
-
- NetInfo *getNetByAlias(IdString alias) const
- {
- return nets.count(alias) ? nets.at(alias).get() : nets.at(net_aliases.at(alias)).get();
- }
-
- // Intended to simplify Python API
- void addClock(IdString net, float freq);
- void createRectangularRegion(IdString name, int x0, int y0, int x1, int y1);
- void addBelToRegion(IdString name, BelId bel);
- void constrainCellToRegion(IdString cell, IdString region_name);
-
- // Helper functions for Python bindings
- NetInfo *createNet(IdString name);
- void connectPort(IdString net, IdString cell, IdString port);
- void disconnectPort(IdString cell, IdString port);
- void ripupNet(IdString name);
- void lockNetRouting(IdString name);
-
- CellInfo *createCell(IdString name, IdString type);
- void copyBelPorts(IdString cell, BelId bel);
-
- // Workaround for lack of wrappable constructors
- DecalXY constructDecalXY(DecalId decal, float x, float y);
-
- void archInfoToAttributes();
- void attributesToArchInfo();
-};
-
-namespace {
-// For several functions; such as bel/wire/pip attributes; the trivial implementation is to return an empty vector
-// But an arch might want to do something fancy with a custom range type that doesn't provide a constructor
-// So some cursed C++ is needed to return an empty object if possible; or error out if not; is needed
-template <typename Tc> typename std::enable_if<std::is_constructible<Tc>::value, Tc>::type empty_if_possible()
-{
- return Tc();
-}
-template <typename Tc> typename std::enable_if<!std::is_constructible<Tc>::value, Tc>::type empty_if_possible()
-{
- NPNR_ASSERT_FALSE("attempting to use default implementation of range-returning function with range type lacking "
- "default constructor!");
-}
-
-// Provide a default implementation of bel bucket name if typedef'd to IdString
-template <typename Tbbid>
-typename std::enable_if<std::is_same<Tbbid, IdString>::value, IdString>::type bbid_to_name(Tbbid id)
-{
- return id;
-}
-template <typename Tbbid>
-typename std::enable_if<!std::is_same<Tbbid, IdString>::value, IdString>::type bbid_to_name(Tbbid id)
-{
- NPNR_ASSERT_FALSE("getBelBucketName must be implemented when BelBucketId is a type other than IdString!");
-}
-template <typename Tbbid>
-typename std::enable_if<std::is_same<Tbbid, IdString>::value, BelBucketId>::type bbid_from_name(IdString name)
-{
- return name;
-}
-template <typename Tbbid>
-typename std::enable_if<!std::is_same<Tbbid, IdString>::value, BelBucketId>::type bbid_from_name(IdString name)
-{
- NPNR_ASSERT_FALSE("getBelBucketByName must be implemented when BelBucketId is a type other than IdString!");
-}
-
-// For the cell type and bel type ranges; we want to return our stored vectors only if the type matches
-template <typename Tret, typename Tc>
-typename std::enable_if<std::is_same<Tret, Tc>::value, Tret>::type return_if_match(Tret r)
-{
- return r;
-}
-
-template <typename Tret, typename Tc>
-typename std::enable_if<!std::is_same<Tret, Tc>::value, Tret>::type return_if_match(Tret r)
-{
- NPNR_ASSERT_FALSE("default implementations of cell type and bel bucket range functions only available when the "
- "respective range types are 'const std::vector&'");
-}
-
-} // namespace
-
-// The specification of the Arch API (pure virtual)
-template <typename R> struct ArchAPI : BaseCtx
-{
- // Basic config
- virtual IdString archId() const = 0;
- virtual std::string getChipName() const = 0;
- virtual typename R::ArchArgsT archArgs() const = 0;
- virtual IdString archArgsToId(typename R::ArchArgsT args) const = 0;
- virtual int getGridDimX() const = 0;
- virtual int getGridDimY() const = 0;
- virtual int getTileBelDimZ(int x, int y) const = 0;
- virtual int getTilePipDimZ(int x, int y) const = 0;
- virtual char getNameDelimiter() const = 0;
- // Bel methods
- virtual typename R::AllBelsRangeT getBels() const = 0;
- virtual IdStringList getBelName(BelId bel) const = 0;
- virtual BelId getBelByName(IdStringList name) const = 0;
- virtual uint32_t getBelChecksum(BelId bel) const = 0;
- virtual void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) = 0;
- virtual void unbindBel(BelId bel) = 0;
- virtual Loc getBelLocation(BelId bel) const = 0;
- virtual BelId getBelByLocation(Loc loc) const = 0;
- virtual typename R::TileBelsRangeT getBelsByTile(int x, int y) const = 0;
- virtual bool getBelGlobalBuf(BelId bel) const = 0;
- virtual bool checkBelAvail(BelId bel) const = 0;
- virtual CellInfo *getBoundBelCell(BelId bel) const = 0;
- virtual CellInfo *getConflictingBelCell(BelId bel) const = 0;
- virtual IdString getBelType(BelId bel) const = 0;
- virtual bool getBelHidden(BelId bel) const = 0;
- virtual typename R::BelAttrsRangeT getBelAttrs(BelId bel) const = 0;
- virtual WireId getBelPinWire(BelId bel, IdString pin) const = 0;
- virtual PortType getBelPinType(BelId bel, IdString pin) const = 0;
- virtual typename R::BelPinsRangeT getBelPins(BelId bel) const = 0;
- virtual typename R::CellBelPinRangeT getBelPinsForCellPin(const CellInfo *cell_info, IdString pin) const = 0;
- // Wire methods
- virtual typename R::AllWiresRangeT getWires() const = 0;
- virtual WireId getWireByName(IdStringList name) const = 0;
- virtual IdStringList getWireName(WireId wire) const = 0;
- virtual IdString getWireType(WireId wire) const = 0;
- virtual typename R::WireAttrsRangeT getWireAttrs(WireId) const = 0;
- virtual typename R::DownhillPipRangeT getPipsDownhill(WireId wire) const = 0;
- virtual typename R::UphillPipRangeT getPipsUphill(WireId wire) const = 0;
- virtual typename R::WireBelPinRangeT getWireBelPins(WireId wire) const = 0;
- virtual uint32_t getWireChecksum(WireId wire) const = 0;
- virtual void bindWire(WireId wire, NetInfo *net, PlaceStrength strength) = 0;
- virtual void unbindWire(WireId wire) = 0;
- virtual bool checkWireAvail(WireId wire) const = 0;
- virtual NetInfo *getBoundWireNet(WireId wire) const = 0;
- virtual WireId getConflictingWireWire(WireId wire) const = 0;
- virtual NetInfo *getConflictingWireNet(WireId wire) const = 0;
- virtual DelayQuad getWireDelay(WireId wire) const = 0;
- // Pip methods
- virtual typename R::AllPipsRangeT getPips() const = 0;
- virtual PipId getPipByName(IdStringList name) const = 0;
- virtual IdStringList getPipName(PipId pip) const = 0;
- virtual IdString getPipType(PipId pip) const = 0;
- virtual typename R::PipAttrsRangeT getPipAttrs(PipId) const = 0;
- virtual uint32_t getPipChecksum(PipId pip) const = 0;
- virtual void bindPip(PipId pip, NetInfo *net, PlaceStrength strength) = 0;
- virtual void unbindPip(PipId pip) = 0;
- virtual bool checkPipAvail(PipId pip) const = 0;
- virtual NetInfo *getBoundPipNet(PipId pip) const = 0;
- virtual WireId getConflictingPipWire(PipId pip) const = 0;
- virtual NetInfo *getConflictingPipNet(PipId pip) const = 0;
- virtual WireId getPipSrcWire(PipId pip) const = 0;
- virtual WireId getPipDstWire(PipId pip) const = 0;
- virtual DelayQuad getPipDelay(PipId pip) const = 0;
- virtual Loc getPipLocation(PipId pip) const = 0;
- // Group methods
- virtual GroupId getGroupByName(IdStringList name) const = 0;
- virtual IdStringList getGroupName(GroupId group) const = 0;
- virtual typename R::AllGroupsRangeT getGroups() const = 0;
- virtual typename R::GroupBelsRangeT getGroupBels(GroupId group) const = 0;
- virtual typename R::GroupWiresRangeT getGroupWires(GroupId group) const = 0;
- virtual typename R::GroupPipsRangeT getGroupPips(GroupId group) const = 0;
- virtual typename R::GroupGroupsRangeT getGroupGroups(GroupId group) const = 0;
- // Delay Methods
- virtual delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const = 0;
- virtual delay_t getDelayEpsilon() const = 0;
- virtual delay_t getRipupDelayPenalty() const = 0;
- virtual float getDelayNS(delay_t v) const = 0;
- virtual delay_t getDelayFromNS(float ns) const = 0;
- virtual uint32_t getDelayChecksum(delay_t v) const = 0;
- virtual bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const = 0;
- virtual delay_t estimateDelay(WireId src, WireId dst) const = 0;
- virtual ArcBounds getRouteBoundingBox(WireId src, WireId dst) const = 0;
- // Decal methods
- virtual typename R::DecalGfxRangeT getDecalGraphics(DecalId decal) const = 0;
- virtual DecalXY getBelDecal(BelId bel) const = 0;
- virtual DecalXY getWireDecal(WireId wire) const = 0;
- virtual DecalXY getPipDecal(PipId pip) const = 0;
- virtual DecalXY getGroupDecal(GroupId group) const = 0;
- // Cell timing methods
- virtual bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const = 0;
- virtual TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const = 0;
- virtual TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const = 0;
- // Placement validity checks
- virtual bool isValidBelForCellType(IdString cell_type, BelId bel) const = 0;
- virtual IdString getBelBucketName(BelBucketId bucket) const = 0;
- virtual BelBucketId getBelBucketByName(IdString name) const = 0;
- virtual BelBucketId getBelBucketForBel(BelId bel) const = 0;
- virtual BelBucketId getBelBucketForCellType(IdString cell_type) const = 0;
- virtual bool isBelLocationValid(BelId bel) const = 0;
- virtual typename R::CellTypeRangeT getCellTypes() const = 0;
- virtual typename R::BelBucketRangeT getBelBuckets() const = 0;
- virtual typename R::BucketBelRangeT getBelsInBucket(BelBucketId bucket) const = 0;
- // Flow methods
- virtual bool pack() = 0;
- virtual bool place() = 0;
- virtual bool route() = 0;
- virtual void assignArchInfo() = 0;
-};
-
-// This contains the relevant range types for the default implementations of Arch functions
-struct BaseArchRanges
-{
- // Bels
- using CellBelPinRangeT = std::array<IdString, 1>;
- // Attributes
- using BelAttrsRangeT = std::vector<std::pair<IdString, std::string>>;
- using WireAttrsRangeT = std::vector<std::pair<IdString, std::string>>;
- using PipAttrsRangeT = std::vector<std::pair<IdString, std::string>>;
- // Groups
- using AllGroupsRangeT = std::vector<GroupId>;
- using GroupBelsRangeT = std::vector<BelId>;
- using GroupWiresRangeT = std::vector<WireId>;
- using GroupPipsRangeT = std::vector<PipId>;
- using GroupGroupsRangeT = std::vector<GroupId>;
- // Decals
- using DecalGfxRangeT = std::vector<GraphicElement>;
- // Placement validity
- using CellTypeRangeT = const std::vector<IdString> &;
- using BelBucketRangeT = const std::vector<BelBucketId> &;
- using BucketBelRangeT = const std::vector<BelId> &;
-};
-
-template <typename R> struct BaseArch : ArchAPI<R>
-{
- // --------------------------------------------------------------
- // Default, trivial, implementations of Arch API functions for arches that don't need complex behaviours
-
- // Basic config
- virtual IdString archId() const override { return this->id(NPNR_STRINGIFY(ARCHNAME)); }
- virtual IdString archArgsToId(typename R::ArchArgsT args) const override { return IdString(); }
- virtual int getTilePipDimZ(int x, int y) const override { return 1; }
- virtual char getNameDelimiter() const override { return ' '; }
-
- // Bel methods
- virtual uint32_t getBelChecksum(BelId bel) const override { return uint32_t(std::hash<BelId>()(bel)); }
- virtual void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) override
- {
- NPNR_ASSERT(bel != BelId());
- auto &entry = base_bel2cell[bel];
- NPNR_ASSERT(entry == nullptr);
- cell->bel = bel;
- cell->belStrength = strength;
- entry = cell;
- this->refreshUiBel(bel);
- }
- virtual void unbindBel(BelId bel) override
- {
- NPNR_ASSERT(bel != BelId());
- auto &entry = base_bel2cell[bel];
- NPNR_ASSERT(entry != nullptr);
- entry->bel = BelId();
- entry->belStrength = STRENGTH_NONE;
- entry = nullptr;
- this->refreshUiBel(bel);
- }
-
- virtual bool getBelHidden(BelId bel) const override { return false; }
-
- virtual bool getBelGlobalBuf(BelId bel) const override { return false; }
- virtual bool checkBelAvail(BelId bel) const override { return getBoundBelCell(bel) == nullptr; };
- virtual CellInfo *getBoundBelCell(BelId bel) const override
- {
- auto fnd = base_bel2cell.find(bel);
- return fnd == base_bel2cell.end() ? nullptr : fnd->second;
- }
- virtual CellInfo *getConflictingBelCell(BelId bel) const override { return getBoundBelCell(bel); }
- virtual typename R::BelAttrsRangeT getBelAttrs(BelId bel) const override
- {
- return empty_if_possible<typename R::BelAttrsRangeT>();
- }
-
- virtual typename R::CellBelPinRangeT getBelPinsForCellPin(const CellInfo *cell_info, IdString pin) const override
- {
- return return_if_match<std::array<IdString, 1>, typename R::CellBelPinRangeT>({pin});
- }
-
- // Wire methods
- virtual IdString getWireType(WireId wire) const override { return IdString(); }
- virtual typename R::WireAttrsRangeT getWireAttrs(WireId) const override
- {
- return empty_if_possible<typename R::WireAttrsRangeT>();
- }
- virtual uint32_t getWireChecksum(WireId wire) const override { return uint32_t(std::hash<WireId>()(wire)); }
-
- virtual void bindWire(WireId wire, NetInfo *net, PlaceStrength strength) override
- {
- NPNR_ASSERT(wire != WireId());
- auto &w2n_entry = base_wire2net[wire];
- NPNR_ASSERT(w2n_entry == nullptr);
- net->wires[wire].pip = PipId();
- net->wires[wire].strength = strength;
- w2n_entry = net;
- this->refreshUiWire(wire);
- }
- virtual void unbindWire(WireId wire) override
- {
- NPNR_ASSERT(wire != WireId());
- auto &w2n_entry = base_wire2net[wire];
- NPNR_ASSERT(w2n_entry != nullptr);
-
- auto &net_wires = w2n_entry->wires;
- auto it = net_wires.find(wire);
- NPNR_ASSERT(it != net_wires.end());
-
- auto pip = it->second.pip;
- if (pip != PipId()) {
- base_pip2net[pip] = nullptr;
- }
-
- net_wires.erase(it);
- base_wire2net[wire] = nullptr;
-
- w2n_entry = nullptr;
- this->refreshUiWire(wire);
- }
- virtual bool checkWireAvail(WireId wire) const override { return getBoundWireNet(wire) == nullptr; }
- virtual NetInfo *getBoundWireNet(WireId wire) const override
- {
- auto fnd = base_wire2net.find(wire);
- return fnd == base_wire2net.end() ? nullptr : fnd->second;
- }
- virtual WireId getConflictingWireWire(WireId wire) const override { return wire; };
- virtual NetInfo *getConflictingWireNet(WireId wire) const override { return getBoundWireNet(wire); }
-
- // Pip methods
- virtual IdString getPipType(PipId pip) const override { return IdString(); }
- virtual typename R::PipAttrsRangeT getPipAttrs(PipId) const override
- {
- return empty_if_possible<typename R::PipAttrsRangeT>();
- }
- virtual uint32_t getPipChecksum(PipId pip) const override { return uint32_t(std::hash<PipId>()(pip)); }
- virtual void bindPip(PipId pip, NetInfo *net, PlaceStrength strength) override
- {
- NPNR_ASSERT(pip != PipId());
- auto &p2n_entry = base_pip2net[pip];
- NPNR_ASSERT(p2n_entry == nullptr);
- p2n_entry = net;
-
- WireId dst = this->getPipDstWire(pip);
- auto &w2n_entry = base_wire2net[dst];
- NPNR_ASSERT(w2n_entry == nullptr);
- w2n_entry = net;
- net->wires[dst].pip = pip;
- net->wires[dst].strength = strength;
- }
- virtual void unbindPip(PipId pip) override
- {
- NPNR_ASSERT(pip != PipId());
- auto &p2n_entry = base_pip2net[pip];
- NPNR_ASSERT(p2n_entry != nullptr);
- WireId dst = this->getPipDstWire(pip);
-
- auto &w2n_entry = base_wire2net[dst];
- NPNR_ASSERT(w2n_entry != nullptr);
- w2n_entry = nullptr;
-
- p2n_entry->wires.erase(dst);
- p2n_entry = nullptr;
- }
- virtual bool checkPipAvail(PipId pip) const override { return getBoundPipNet(pip) == nullptr; }
- virtual NetInfo *getBoundPipNet(PipId pip) const override
- {
- auto fnd = base_pip2net.find(pip);
- return fnd == base_pip2net.end() ? nullptr : fnd->second;
- }
- virtual WireId getConflictingPipWire(PipId pip) const override { return WireId(); }
- virtual NetInfo *getConflictingPipNet(PipId pip) const override { return getBoundPipNet(pip); }
-
- // Group methods
- virtual GroupId getGroupByName(IdStringList name) const override { return GroupId(); };
- virtual IdStringList getGroupName(GroupId group) const override { return IdStringList(); };
- virtual typename R::AllGroupsRangeT getGroups() const override
- {
- return empty_if_possible<typename R::AllGroupsRangeT>();
- }
- // Default implementation of these assumes no groups so never called
- virtual typename R::GroupBelsRangeT getGroupBels(GroupId group) const override
- {
- NPNR_ASSERT_FALSE("unreachable");
- };
- virtual typename R::GroupWiresRangeT getGroupWires(GroupId group) const override
- {
- NPNR_ASSERT_FALSE("unreachable");
- };
- virtual typename R::GroupPipsRangeT getGroupPips(GroupId group) const override
- {
- NPNR_ASSERT_FALSE("unreachable");
- };
- virtual typename R::GroupGroupsRangeT getGroupGroups(GroupId group) const override
- {
- NPNR_ASSERT_FALSE("unreachable");
- };
-
- // Delay methods
- virtual bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const override
- {
- return false;
- }
-
- // Decal methods
- virtual typename R::DecalGfxRangeT getDecalGraphics(DecalId decal) const override
- {
- return empty_if_possible<typename R::DecalGfxRangeT>();
- };
- virtual DecalXY getBelDecal(BelId bel) const override { return DecalXY(); }
- virtual DecalXY getWireDecal(WireId wire) const override { return DecalXY(); }
- virtual DecalXY getPipDecal(PipId pip) const override { return DecalXY(); }
- virtual DecalXY getGroupDecal(GroupId group) const override { return DecalXY(); }
-
- // Cell timing methods
- virtual bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayQuad &delay) const override
- {
- return false;
- }
- virtual TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const override
- {
- return TMG_IGNORE;
- }
- virtual TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const override
- {
- NPNR_ASSERT_FALSE("unreachable");
- }
-
- // Placement validity checks
- virtual bool isValidBelForCellType(IdString cell_type, BelId bel) const override
- {
- return cell_type == this->getBelType(bel);
- }
- virtual IdString getBelBucketName(BelBucketId bucket) const override { return bbid_to_name<BelBucketId>(bucket); }
- virtual BelBucketId getBelBucketByName(IdString name) const override { return bbid_from_name<BelBucketId>(name); }
- virtual BelBucketId getBelBucketForBel(BelId bel) const override
- {
- return getBelBucketForCellType(this->getBelType(bel));
- };
- virtual BelBucketId getBelBucketForCellType(IdString cell_type) const override
- {
- return getBelBucketByName(cell_type);
- };
- virtual bool isBelLocationValid(BelId bel) const override { return true; }
- virtual typename R::CellTypeRangeT getCellTypes() const override
- {
- NPNR_ASSERT(cell_types_initialised);
- return return_if_match<const std::vector<IdString> &, typename R::CellTypeRangeT>(cell_types);
- }
- virtual typename R::BelBucketRangeT getBelBuckets() const override
- {
- NPNR_ASSERT(bel_buckets_initialised);
- return return_if_match<const std::vector<BelBucketId> &, typename R::BelBucketRangeT>(bel_buckets);
- }
- virtual typename R::BucketBelRangeT getBelsInBucket(BelBucketId bucket) const override
- {
- NPNR_ASSERT(bel_buckets_initialised);
- return return_if_match<const std::vector<BelId> &, typename R::BucketBelRangeT>(bucket_bels.at(bucket));
- }
-
- // Flow methods
- virtual void assignArchInfo() override{};
-
- // --------------------------------------------------------------
- // These structures are used to provide default implementations of bel/wire/pip binding. Arches might want to
- // replace them with their own, for example to use faster access structures than unordered_map. Arches might also
- // want to add extra checks around these functions
- std::unordered_map<BelId, CellInfo *> base_bel2cell;
- std::unordered_map<WireId, NetInfo *> base_wire2net;
- std::unordered_map<PipId, NetInfo *> base_pip2net;
-
- // For the default cell/bel bucket implementations
- std::vector<IdString> cell_types;
- std::vector<BelBucketId> bel_buckets;
- std::unordered_map<BelBucketId, std::vector<BelId>> bucket_bels;
-
- // Arches that want to use the default cell types and bel buckets *must* call these functions in their constructor
- bool cell_types_initialised = false;
- bool bel_buckets_initialised = false;
- void init_cell_types()
- {
- std::unordered_set<IdString> bel_types;
- for (auto bel : this->getBels())
- bel_types.insert(this->getBelType(bel));
- std::copy(bel_types.begin(), bel_types.end(), std::back_inserter(cell_types));
- std::sort(cell_types.begin(), cell_types.end());
- cell_types_initialised = true;
- }
- void init_bel_buckets()
- {
- for (auto cell_type : this->getCellTypes()) {
- auto bucket = this->getBelBucketForCellType(cell_type);
- bucket_bels[bucket]; // create empty bucket
- }
- for (auto bel : this->getBels()) {
- auto bucket = this->getBelBucketForBel(bel);
- bucket_bels[bucket].push_back(bel);
- }
- for (auto &b : bucket_bels)
- bel_buckets.push_back(b.first);
- std::sort(bel_buckets.begin(), bel_buckets.end());
- bel_buckets_initialised = true;
- }
-};
-
-NEXTPNR_NAMESPACE_END
-
-#include "arch.h"
-
-NEXTPNR_NAMESPACE_BEGIN
-
-struct Context : Arch, DeterministicRNG
-{
- bool verbose = false;
- bool debug = false;
- bool force = false;
-
- // Should we disable printing of the location of nets in the critical path?
- bool disable_critical_path_source_print = false;
-
- Context(ArchArgs args) : Arch(args) { BaseCtx::as_ctx = this; }
-
- // --------------------------------------------------------------
-
- WireId getNetinfoSourceWire(const NetInfo *net_info) const;
- SSOArray<WireId, 2> getNetinfoSinkWires(const NetInfo *net_info, const PortRef &sink) const;
- size_t getNetinfoSinkWireCount(const NetInfo *net_info, const PortRef &sink) const;
- WireId getNetinfoSinkWire(const NetInfo *net_info, const PortRef &sink, size_t phys_idx) const;
- delay_t getNetinfoRouteDelay(const NetInfo *net_info, const PortRef &sink) const;
-
- // provided by router1.cc
- bool checkRoutedDesign() const;
- bool getActualRouteDelay(WireId src_wire, WireId dst_wire, delay_t *delay = nullptr,
- std::unordered_map<WireId, PipId> *route = nullptr, bool useEstimate = true);
-
- // --------------------------------------------------------------
- // call after changing hierpath or adding/removing nets and cells
- void fixupHierarchy();
-
- // --------------------------------------------------------------
-
- // provided by sdf.cc
- void writeSDF(std::ostream &out, bool cvc_mode = false) const;
-
- // --------------------------------------------------------------
-
- // provided by svg.cc
- void writeSVG(const std::string &filename, const std::string &flags = "") const;
-
- // --------------------------------------------------------------
-
- uint32_t checksum() const;
-
- void check() const;
- void archcheck() const;
-
- template <typename T> T setting(const char *name, T defaultValue)
- {
- IdString new_id = id(name);
- auto found = settings.find(new_id);
- if (found != settings.end())
- return boost::lexical_cast<T>(found->second.is_string ? found->second.as_string()
- : std::to_string(found->second.as_int64()));
- else
- settings[id(name)] = std::to_string(defaultValue);
-
- return defaultValue;
- }
-
- template <typename T> T setting(const char *name) const
- {
- IdString new_id = id(name);
- auto found = settings.find(new_id);
- if (found != settings.end())
- return boost::lexical_cast<T>(found->second.is_string ? found->second.as_string()
- : std::to_string(found->second.as_int64()));
- else
- throw std::runtime_error("settings does not exists");
- }
-};
-
-NEXTPNR_NAMESPACE_END
-
-#define NEXTPNR_H_COMPLETE
+#include "base_arch.h"
+#include "context.h"
+#include "nextpnr_assertions.h"
+#include "nextpnr_namespaces.h"
+#include "nextpnr_types.h"
#endif
diff --git a/common/nextpnr_assertions.cc b/common/nextpnr_assertions.cc
new file mode 100644
index 00000000..922bb1dc
--- /dev/null
+++ b/common/nextpnr_assertions.cc
@@ -0,0 +1,33 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "nextpnr_assertions.h"
+#include "log.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+assertion_failure::assertion_failure(std::string msg, std::string expr_str, std::string filename, int line)
+ : runtime_error("Assertion failure: " + msg + " (" + filename + ":" + std::to_string(line) + ")"), msg(msg),
+ expr_str(expr_str), filename(filename), line(line)
+{
+ log_flush();
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/common/nextpnr_assertions.h b/common/nextpnr_assertions.h
new file mode 100644
index 00000000..85a724c6
--- /dev/null
+++ b/common/nextpnr_assertions.h
@@ -0,0 +1,62 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef NEXTPNR_ASSERTIONS_H
+#define NEXTPNR_ASSERTIONS_H
+
+#include <stdexcept>
+
+#include "nextpnr_namespaces.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+class assertion_failure : public std::runtime_error
+{
+ public:
+ assertion_failure(std::string msg, std::string expr_str, std::string filename, int line);
+
+ std::string msg;
+ std::string expr_str;
+ std::string filename;
+ int line;
+};
+
+NPNR_NORETURN
+inline void assert_fail_impl(const char *message, const char *expr_str, const char *filename, int line)
+{
+ throw assertion_failure(message, expr_str, filename, line);
+}
+
+NPNR_NORETURN
+inline void assert_fail_impl_str(std::string message, const char *expr_str, const char *filename, int line)
+{
+ throw assertion_failure(message, expr_str, filename, line);
+}
+
+#define NPNR_ASSERT(cond) (!(cond) ? assert_fail_impl(#cond, #cond, __FILE__, __LINE__) : (void)true)
+#define NPNR_ASSERT_MSG(cond, msg) (!(cond) ? assert_fail_impl(msg, #cond, __FILE__, __LINE__) : (void)true)
+#define NPNR_ASSERT_FALSE(msg) (assert_fail_impl(msg, "false", __FILE__, __LINE__))
+#define NPNR_ASSERT_FALSE_STR(msg) (assert_fail_impl_str(msg, "false", __FILE__, __LINE__))
+
+#define NPNR_STRINGIFY(x) #x
+
+NEXTPNR_NAMESPACE_END
+
+#endif /* NEXTPNR_ASSERTIONS_H */
diff --git a/common/nextpnr_base_types.h b/common/nextpnr_base_types.h
new file mode 100644
index 00000000..1a15bfcb
--- /dev/null
+++ b/common/nextpnr_base_types.h
@@ -0,0 +1,146 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+// Theses are the nextpnr types that do **not** depend on user defined types,
+// like BelId, etc.
+//
+// If a common type is required that depends on one of the user defined types,
+// add it to nextpnr_types.h, which includes "archdefs.h", or make a new
+// header that includes "archdefs.h"
+#ifndef NEXTPNR_BASE_TYPES_H
+#define NEXTPNR_BASE_TYPES_H
+
+#include <boost/functional/hash.hpp>
+#include <string>
+
+#include "idstring.h"
+#include "nextpnr_namespaces.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+struct GraphicElement
+{
+ enum type_t
+ {
+ TYPE_NONE,
+ TYPE_LINE,
+ TYPE_ARROW,
+ TYPE_BOX,
+ TYPE_CIRCLE,
+ TYPE_LABEL,
+
+ TYPE_MAX
+ } type = TYPE_NONE;
+
+ enum style_t
+ {
+ STYLE_GRID,
+ STYLE_FRAME, // Static "frame". Contrast between STYLE_INACTIVE and STYLE_ACTIVE
+ STYLE_HIDDEN, // Only display when object is selected or highlighted
+ STYLE_INACTIVE, // Render using low-contrast color
+ STYLE_ACTIVE, // Render using high-contast color
+
+ // UI highlight groups
+ STYLE_HIGHLIGHTED0,
+ STYLE_HIGHLIGHTED1,
+ STYLE_HIGHLIGHTED2,
+ STYLE_HIGHLIGHTED3,
+ STYLE_HIGHLIGHTED4,
+ STYLE_HIGHLIGHTED5,
+ STYLE_HIGHLIGHTED6,
+ STYLE_HIGHLIGHTED7,
+
+ STYLE_SELECTED,
+ STYLE_HOVER,
+
+ STYLE_MAX
+ } style = STYLE_FRAME;
+
+ float x1 = 0, y1 = 0, x2 = 0, y2 = 0, z = 0;
+ std::string text;
+ GraphicElement(){};
+ GraphicElement(type_t type, style_t style, float x1, float y1, float x2, float y2, float z)
+ : type(type), style(style), x1(x1), y1(y1), x2(x2), y2(y2), z(z){};
+};
+
+struct Loc
+{
+ int x = -1, y = -1, z = -1;
+
+ Loc() {}
+ Loc(int x, int y, int z) : x(x), y(y), z(z) {}
+
+ bool operator==(const Loc &other) const { return (x == other.x) && (y == other.y) && (z == other.z); }
+ bool operator!=(const Loc &other) const { return (x != other.x) || (y != other.y) || (z != other.z); }
+};
+
+struct ArcBounds
+{
+ int x0 = -1, y0 = -1, x1 = -1, y1 = -1;
+
+ ArcBounds() {}
+ ArcBounds(int x0, int y0, int x1, int y1) : x0(x0), y0(y0), x1(x1), y1(y1){};
+
+ int distance(Loc loc) const
+ {
+ int dist = 0;
+ if (loc.x < x0)
+ dist += x0 - loc.x;
+ if (loc.x > x1)
+ dist += loc.x - x1;
+ if (loc.y < y0)
+ dist += y0 - loc.y;
+ if (loc.y > y1)
+ dist += loc.y - y1;
+ return dist;
+ };
+
+ bool contains(int x, int y) const { return x >= x0 && y >= y0 && x <= x1 && y <= y1; }
+};
+
+enum PlaceStrength
+{
+ STRENGTH_NONE = 0,
+ STRENGTH_WEAK = 1,
+ STRENGTH_STRONG = 2,
+ STRENGTH_PLACER = 3,
+ STRENGTH_FIXED = 4,
+ STRENGTH_LOCKED = 5,
+ STRENGTH_USER = 6
+};
+
+NEXTPNR_NAMESPACE_END
+
+namespace std {
+template <> struct hash<NEXTPNR_NAMESPACE_PREFIX Loc>
+{
+ std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX Loc &obj) const noexcept
+ {
+ std::size_t seed = 0;
+ boost::hash_combine(seed, hash<int>()(obj.x));
+ boost::hash_combine(seed, hash<int>()(obj.y));
+ boost::hash_combine(seed, hash<int>()(obj.z));
+ return seed;
+ }
+};
+
+} // namespace std
+
+#endif /* NEXTPNR_BASE_TYPES_H */
diff --git a/common/nextpnr_namespaces.cc b/common/nextpnr_namespaces.cc
new file mode 100644
index 00000000..07a96b53
--- /dev/null
+++ b/common/nextpnr_namespaces.cc
@@ -0,0 +1,23 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+// This cc file exists to ensure that "nextpnr_namespaces.h" can be compiled
+// on its own.
+#include "nextpnr_namespaces.h"
diff --git a/common/nextpnr_namespaces.h b/common/nextpnr_namespaces.h
new file mode 100644
index 00000000..8242376c
--- /dev/null
+++ b/common/nextpnr_namespaces.h
@@ -0,0 +1,53 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef NEXTPNR_NAMESPACES_H
+#define NEXTPNR_NAMESPACES_H
+
+#ifdef NEXTPNR_NAMESPACE
+#define NEXTPNR_NAMESPACE_PREFIX NEXTPNR_NAMESPACE::
+#define NEXTPNR_NAMESPACE_BEGIN namespace NEXTPNR_NAMESPACE {
+#define NEXTPNR_NAMESPACE_END }
+#define USING_NEXTPNR_NAMESPACE using namespace NEXTPNR_NAMESPACE;
+#else
+#define NEXTPNR_NAMESPACE_PREFIX
+#define NEXTPNR_NAMESPACE_BEGIN
+#define NEXTPNR_NAMESPACE_END
+#define USING_NEXTPNR_NAMESPACE
+#endif
+
+#if defined(__GNUC__) || defined(__clang__)
+#define NPNR_ATTRIBUTE(...) __attribute__((__VA_ARGS__))
+#define NPNR_NORETURN __attribute__((noreturn))
+#define NPNR_DEPRECATED __attribute__((deprecated))
+#define NPNR_PACKED_STRUCT(...) __VA_ARGS__ __attribute__((packed))
+#elif defined(_MSC_VER)
+#define NPNR_ATTRIBUTE(...)
+#define NPNR_NORETURN __declspec(noreturn)
+#define NPNR_DEPRECATED __declspec(deprecated)
+#define NPNR_PACKED_STRUCT(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop))
+#else
+#define NPNR_ATTRIBUTE(...)
+#define NPNR_NORETURN
+#define NPNR_DEPRECATED
+#define NPNR_PACKED_STRUCT(...) __VA_ARGS__
+#endif
+
+#endif /* NEXTPNR_NAMESPACES_H */
diff --git a/common/nextpnr_types.cc b/common/nextpnr_types.cc
new file mode 100644
index 00000000..a76576de
--- /dev/null
+++ b/common/nextpnr_types.cc
@@ -0,0 +1,69 @@
+/*
+ * 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 "nextpnr_types.h"
+
+#include "nextpnr_namespaces.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+void CellInfo::addInput(IdString name)
+{
+ ports[name].name = name;
+ ports[name].type = PORT_IN;
+}
+void CellInfo::addOutput(IdString name)
+{
+ ports[name].name = name;
+ ports[name].type = PORT_OUT;
+}
+void CellInfo::addInout(IdString name)
+{
+ ports[name].name = name;
+ ports[name].type = PORT_INOUT;
+}
+
+void CellInfo::setParam(IdString name, Property value) { params[name] = value; }
+void CellInfo::unsetParam(IdString name) { params.erase(name); }
+void CellInfo::setAttr(IdString name, Property value) { attrs[name] = value; }
+void CellInfo::unsetAttr(IdString name) { attrs.erase(name); }
+
+bool CellInfo::isConstrained(bool include_abs_z_constr) const
+{
+ return constr_parent != nullptr || !constr_children.empty() || (include_abs_z_constr && constr_abs_z);
+}
+
+bool CellInfo::testRegion(BelId bel) const
+{
+ return region == nullptr || !region->constr_bels || region->bels.count(bel);
+}
+Loc CellInfo::getConstrainedLoc(Loc parent_loc) const
+{
+ NPNR_ASSERT(constr_parent != nullptr);
+ Loc cloc = parent_loc;
+ if (constr_x != UNCONSTR)
+ cloc.x += constr_x;
+ if (constr_y != UNCONSTR)
+ cloc.y += constr_y;
+ if (constr_z != UNCONSTR)
+ cloc.z = constr_abs_z ? constr_z : (parent_loc.z + constr_z);
+ return cloc;
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/common/nextpnr_types.h b/common/nextpnr_types.h
new file mode 100644
index 00000000..8b450297
--- /dev/null
+++ b/common/nextpnr_types.h
@@ -0,0 +1,259 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+// Types defined in this header use one or more user defined types (e.g. BelId).
+// If a new common type is desired that doesn't depend on a user defined type,
+// either put it in it's own header, or in nextpnr_base_types.h.
+#ifndef NEXTPNR_TYPES_H
+#define NEXTPNR_TYPES_H
+
+#include <unordered_map>
+#include <unordered_set>
+
+#include "archdefs.h"
+#include "nextpnr_base_types.h"
+#include "nextpnr_namespaces.h"
+#include "property.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+struct DecalXY
+{
+ DecalId decal;
+ float x = 0, y = 0;
+
+ bool operator==(const DecalXY &other) const { return (decal == other.decal && x == other.x && y == other.y); }
+};
+
+struct BelPin
+{
+ BelId bel;
+ IdString pin;
+};
+
+struct Region
+{
+ IdString name;
+
+ bool constr_bels = false;
+ bool constr_wires = false;
+ bool constr_pips = false;
+
+ std::unordered_set<BelId> bels;
+ std::unordered_set<WireId> wires;
+ std::unordered_set<Loc> piplocs;
+};
+
+struct PipMap
+{
+ PipId pip = PipId();
+ PlaceStrength strength = STRENGTH_NONE;
+};
+
+struct CellInfo;
+
+struct PortRef
+{
+ CellInfo *cell = nullptr;
+ IdString port;
+ delay_t budget = 0;
+};
+
+// minimum and maximum delay
+struct DelayPair
+{
+ DelayPair(){};
+ explicit DelayPair(delay_t delay) : min_delay(delay), max_delay(delay){};
+ DelayPair(delay_t min_delay, delay_t max_delay) : min_delay(min_delay), max_delay(max_delay){};
+ delay_t minDelay() const { return min_delay; };
+ delay_t maxDelay() const { return max_delay; };
+ delay_t min_delay, max_delay;
+ DelayPair operator+(const DelayPair &other) const
+ {
+ return {min_delay + other.min_delay, max_delay + other.max_delay};
+ }
+ DelayPair operator-(const DelayPair &other) const
+ {
+ return {min_delay - other.min_delay, max_delay - other.max_delay};
+ }
+};
+
+// four-quadrant, min and max rise and fall delay
+struct DelayQuad
+{
+ DelayPair rise, fall;
+ DelayQuad(){};
+ explicit DelayQuad(delay_t delay) : rise(delay), fall(delay){};
+ DelayQuad(delay_t min_delay, delay_t max_delay) : rise(min_delay, max_delay), fall(min_delay, max_delay){};
+ DelayQuad(DelayPair rise, DelayPair fall) : rise(rise), fall(fall){};
+ DelayQuad(delay_t min_rise, delay_t max_rise, delay_t min_fall, delay_t max_fall)
+ : rise(min_rise, max_rise), fall(min_fall, max_fall){};
+
+ delay_t minRiseDelay() const { return rise.minDelay(); };
+ delay_t maxRiseDelay() const { return rise.maxDelay(); };
+ delay_t minFallDelay() const { return fall.minDelay(); };
+ delay_t maxFallDelay() const { return fall.maxDelay(); };
+ delay_t minDelay() const { return std::min<delay_t>(rise.minDelay(), fall.minDelay()); };
+ delay_t maxDelay() const { return std::max<delay_t>(rise.maxDelay(), fall.maxDelay()); };
+
+ DelayPair delayPair() const { return DelayPair(minDelay(), maxDelay()); };
+
+ DelayQuad operator+(const DelayQuad &other) const { return {rise + other.rise, fall + other.fall}; }
+ DelayQuad operator-(const DelayQuad &other) const { return {rise - other.rise, fall - other.fall}; }
+};
+
+struct ClockConstraint;
+
+struct NetInfo : ArchNetInfo
+{
+ IdString name, hierpath;
+ int32_t udata = 0;
+
+ PortRef driver;
+ std::vector<PortRef> users;
+ std::unordered_map<IdString, Property> attrs;
+
+ // wire -> uphill_pip
+ std::unordered_map<WireId, PipMap> wires;
+
+ std::vector<IdString> aliases; // entries in net_aliases that point to this net
+
+ std::unique_ptr<ClockConstraint> clkconstr;
+
+ Region *region = nullptr;
+};
+
+enum PortType
+{
+ PORT_IN = 0,
+ PORT_OUT = 1,
+ PORT_INOUT = 2
+};
+
+struct PortInfo
+{
+ IdString name;
+ NetInfo *net;
+ PortType type;
+};
+
+struct CellInfo : ArchCellInfo
+{
+ IdString name, type, hierpath;
+ int32_t udata;
+
+ std::unordered_map<IdString, PortInfo> ports;
+ std::unordered_map<IdString, Property> attrs, params;
+
+ BelId bel;
+ PlaceStrength belStrength = STRENGTH_NONE;
+
+ // placement constraints
+ CellInfo *constr_parent = nullptr;
+ std::vector<CellInfo *> constr_children;
+ const int UNCONSTR = INT_MIN;
+ int constr_x = UNCONSTR; // this.x - parent.x
+ int constr_y = UNCONSTR; // this.y - parent.y
+ int constr_z = UNCONSTR; // this.z - parent.z
+ bool constr_abs_z = false; // parent.z := 0
+ // parent.[xyz] := 0 when (constr_parent == nullptr)
+
+ Region *region = nullptr;
+
+ void addInput(IdString name);
+ void addOutput(IdString name);
+ void addInout(IdString name);
+
+ void setParam(IdString name, Property value);
+ void unsetParam(IdString name);
+ void setAttr(IdString name, Property value);
+ void unsetAttr(IdString name);
+
+ // return true if the cell has placement constraints (optionally excluding the case where the only case is an
+ // absolute z constraint)
+ bool isConstrained(bool include_abs_z_constr = true) const;
+ // check whether a bel complies with the cell's region constraint
+ bool testRegion(BelId bel) const;
+ // get the constrained location for this cell given a provisional location for its parent
+ Loc getConstrainedLoc(Loc parent_loc) const;
+};
+
+enum TimingPortClass
+{
+ TMG_CLOCK_INPUT, // Clock input to a sequential cell
+ TMG_GEN_CLOCK, // Generated clock output (PLL, DCC, etc)
+ TMG_REGISTER_INPUT, // Input to a register, with an associated clock (may also have comb. fanout too)
+ TMG_REGISTER_OUTPUT, // Output from a register
+ TMG_COMB_INPUT, // Combinational input, no paths end here
+ TMG_COMB_OUTPUT, // Combinational output, no paths start here
+ TMG_STARTPOINT, // Unclocked primary startpoint, such as an IO cell output
+ TMG_ENDPOINT, // Unclocked primary endpoint, such as an IO cell input
+ TMG_IGNORE, // Asynchronous to all clocks, "don't care", and should be ignored (false path) for analysis
+};
+
+enum ClockEdge
+{
+ RISING_EDGE,
+ FALLING_EDGE
+};
+
+struct TimingClockingInfo
+{
+ IdString clock_port; // Port name of clock domain
+ ClockEdge edge;
+ DelayPair setup, hold; // Input timing checks
+ DelayQuad clockToQ; // Output clock-to-Q time
+};
+
+struct ClockConstraint
+{
+ DelayPair high;
+ DelayPair low;
+ DelayPair period;
+};
+
+// Represents the contents of a non-leaf cell in a design
+// with hierarchy
+
+struct HierarchicalPort
+{
+ IdString name;
+ PortType dir;
+ std::vector<IdString> nets;
+ int offset;
+ bool upto;
+};
+
+struct HierarchicalCell
+{
+ IdString name, type, parent, fullpath;
+ // Name inside cell instance -> global name
+ std::unordered_map<IdString, IdString> leaf_cells, nets;
+ // Global name -> name inside cell instance
+ std::unordered_map<IdString, IdString> leaf_cells_by_gname, nets_by_gname;
+ // Cell port to net
+ std::unordered_map<IdString, HierarchicalPort> ports;
+ // Name inside cell instance -> global name
+ std::unordered_map<IdString, IdString> hier_cells;
+};
+
+NEXTPNR_NAMESPACE_END
+
+#endif /* NEXTPNR_TYPES_H */
diff --git a/common/property.cc b/common/property.cc
new file mode 100644
index 00000000..945462f8
--- /dev/null
+++ b/common/property.cc
@@ -0,0 +1,81 @@
+/*
+ * 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 "property.h"
+
+#include "nextpnr_namespaces.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+Property::Property() : is_string(false), str(""), intval(0) {}
+
+Property::Property(int64_t intval, int width) : is_string(false), intval(intval)
+{
+ str.reserve(width);
+ for (int i = 0; i < width; i++)
+ str.push_back((intval & (1ULL << i)) ? S1 : S0);
+}
+
+Property::Property(const std::string &strval) : is_string(true), str(strval), intval(0xDEADBEEF) {}
+
+Property::Property(State bit) : is_string(false), str(std::string("") + char(bit)), intval(bit == S1) {}
+
+
+std::string Property::to_string() const
+{
+ if (is_string) {
+ std::string result = str;
+ int state = 0;
+ for (char c : str) {
+ if (state == 0) {
+ if (c == '0' || c == '1' || c == 'x' || c == 'z')
+ state = 0;
+ else if (c == ' ')
+ state = 1;
+ else
+ state = 2;
+ } else if (state == 1 && c != ' ')
+ state = 2;
+ }
+ if (state < 2)
+ result += " ";
+ return result;
+ } else {
+ return std::string(str.rbegin(), str.rend());
+ }
+}
+
+Property Property::from_string(const std::string &s)
+{
+ Property p;
+
+ size_t cursor = s.find_first_not_of("01xz");
+ if (cursor == std::string::npos) {
+ p.str = std::string(s.rbegin(), s.rend());
+ p.is_string = false;
+ p.update_intval();
+ } else if (s.find_first_not_of(' ', cursor) == std::string::npos) {
+ p = Property(s.substr(0, s.size() - 1));
+ } else {
+ p = Property(s);
+ }
+ return p;
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/common/property.h b/common/property.h
new file mode 100644
index 00000000..79fbf881
--- /dev/null
+++ b/common/property.h
@@ -0,0 +1,131 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef PROPERTY_H
+#define PROPERTY_H
+
+#include <algorithm>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "nextpnr_assertions.h"
+#include "nextpnr_namespaces.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+struct Property
+{
+ enum State : char
+ {
+ S0 = '0',
+ S1 = '1',
+ Sx = 'x',
+ Sz = 'z'
+ };
+
+ Property();
+ Property(int64_t intval, int width = 32);
+ Property(const std::string &strval);
+ Property(State bit);
+ Property &operator=(const Property &other) = default;
+
+ bool is_string;
+
+ // The string literal (for string values), or a string of [01xz] (for numeric values)
+ std::string str;
+ // The lower 64 bits (for numeric values), unused for string values
+ int64_t intval;
+
+ void update_intval()
+ {
+ intval = 0;
+ for (int i = 0; i < int(str.size()); i++) {
+ NPNR_ASSERT(str[i] == S0 || str[i] == S1 || str[i] == Sx || str[i] == Sz);
+ if ((str[i] == S1) && i < 64)
+ intval |= (1ULL << i);
+ }
+ }
+
+ int64_t as_int64() const
+ {
+ NPNR_ASSERT(!is_string);
+ return intval;
+ }
+ std::vector<bool> as_bits() const
+ {
+ std::vector<bool> result;
+ result.reserve(str.size());
+ NPNR_ASSERT(!is_string);
+ for (auto c : str)
+ result.push_back(c == S1);
+ return result;
+ }
+ std::string as_string() const
+ {
+ NPNR_ASSERT(is_string);
+ return str;
+ }
+ const char *c_str() const
+ {
+ NPNR_ASSERT(is_string);
+ return str.c_str();
+ }
+ size_t size() const { return is_string ? 8 * str.size() : str.size(); }
+ double as_double() const
+ {
+ NPNR_ASSERT(is_string);
+ return std::stod(str);
+ }
+ bool as_bool() const
+ {
+ if (int(str.size()) <= 64)
+ return intval != 0;
+ else
+ return std::any_of(str.begin(), str.end(), [](char c) { return c == S1; });
+ }
+ bool is_fully_def() const
+ {
+ return !is_string && std::all_of(str.begin(), str.end(), [](char c) { return c == S0 || c == S1; });
+ }
+ Property extract(int offset, int len, State padding = State::S0) const
+ {
+ Property ret;
+ ret.is_string = false;
+ ret.str.reserve(len);
+ for (int i = offset; i < offset + len; i++)
+ ret.str.push_back(i < int(str.size()) ? str[i] : char(padding));
+ ret.update_intval();
+ return ret;
+ }
+ // Convert to a string representation, escaping literal strings matching /^[01xz]* *$/ by adding a space at the end,
+ // to disambiguate from binary strings
+ std::string to_string() const;
+ // Convert a string of four-value binary [01xz], or a literal string escaped according to the above rule
+ // to a Property
+ static Property from_string(const std::string &s);
+};
+
+inline bool operator==(const Property &a, const Property &b) { return a.is_string == b.is_string && a.str == b.str; }
+inline bool operator!=(const Property &a, const Property &b) { return a.is_string != b.is_string || a.str != b.str; }
+
+NEXTPNR_NAMESPACE_END
+
+#endif /* PROPERTY_H */
diff --git a/common/relptr.h b/common/relptr.h
index 76850bc9..935af4ac 100644
--- a/common/relptr.h
+++ b/common/relptr.h
@@ -1,4 +1,31 @@
-// This is intended to be included inside arch.h only.
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef RELPTR_H
+#define RELPTR_H
+
+#include <cstdint>
+
+#include "nextpnr_assertions.h"
+#include "nextpnr_namespaces.h"
+
+NEXTPNR_NAMESPACE_BEGIN
template <typename T> struct RelPtr
{
@@ -6,7 +33,7 @@ template <typename T> struct RelPtr
const T *get() const { return reinterpret_cast<const T *>(reinterpret_cast<const char *>(this) + offset); }
- const T &operator[](size_t index) const { return get()[index]; }
+ const T &operator[](std::size_t index) const { return get()[index]; }
const T &operator*() const { return *(get()); }
@@ -22,7 +49,7 @@ NPNR_PACKED_STRUCT(template <typename T> struct RelSlice {
const T *get() const { return reinterpret_cast<const T *>(reinterpret_cast<const char *>(this) + offset); }
- const T &operator[](size_t index) const
+ const T &operator[](std::size_t index) const
{
NPNR_ASSERT(index < length);
return get()[index];
@@ -41,3 +68,7 @@ NPNR_PACKED_STRUCT(template <typename T> struct RelSlice {
RelSlice(const RelSlice &) = delete;
RelSlice &operator=(const RelSlice &) = delete;
});
+
+NEXTPNR_NAMESPACE_END
+
+#endif /* RELPTR_H */
diff --git a/common/sso_array.h b/common/sso_array.h
new file mode 100644
index 00000000..0fe9632b
--- /dev/null
+++ b/common/sso_array.h
@@ -0,0 +1,112 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef SSO_ARRAY_H
+#define SSO_ARRAY_H
+
+#include <cstdint>
+
+#include "nextpnr_assertions.h"
+#include "nextpnr_namespaces.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+// An small size optimised array that is statically allocated when the size is N or less; heap allocated otherwise
+template <typename T, std::size_t N> class SSOArray
+{
+ private:
+ union
+ {
+ T data_static[N];
+ T *data_heap;
+ };
+ std::size_t m_size;
+ inline bool is_heap() const { return (m_size > N); }
+ void alloc()
+ {
+ if (is_heap()) {
+ data_heap = new T[m_size];
+ }
+ }
+
+ public:
+ T *data() { return is_heap() ? data_heap : data_static; }
+ const T *data() const { return is_heap() ? data_heap : data_static; }
+ std::size_t size() const { return m_size; }
+
+ T *begin() { return data(); }
+ T *end() { return data() + m_size; }
+ const T *begin() const { return data(); }
+ const T *end() const { return data() + m_size; }
+
+ SSOArray() : m_size(0){};
+
+ SSOArray(std::size_t size, const T &init = T()) : m_size(size)
+ {
+ alloc();
+ std::fill(begin(), end(), init);
+ }
+
+ SSOArray(const SSOArray &other) : m_size(other.size())
+ {
+ alloc();
+ std::copy(other.begin(), other.end(), begin());
+ }
+
+ template <typename Tother> SSOArray(const Tother &other) : m_size(other.size())
+ {
+ alloc();
+ std::copy(other.begin(), other.end(), begin());
+ }
+
+ ~SSOArray()
+ {
+ if (is_heap()) {
+ delete[] data_heap;
+ }
+ }
+
+ bool operator==(const SSOArray &other) const
+ {
+ if (size() != other.size())
+ return false;
+ return std::equal(begin(), end(), other.begin());
+ }
+ bool operator!=(const SSOArray &other) const
+ {
+ if (size() != other.size())
+ return true;
+ return !std::equal(begin(), end(), other.begin());
+ }
+ T &operator[](std::size_t idx)
+ {
+ NPNR_ASSERT(idx < m_size);
+ return data()[idx];
+ }
+ const T &operator[](std::size_t idx) const
+ {
+ NPNR_ASSERT(idx < m_size);
+ return data()[idx];
+ }
+};
+
+NEXTPNR_NAMESPACE_END
+
+#endif
diff --git a/common/str_ring_buffer.cc b/common/str_ring_buffer.cc
new file mode 100644
index 00000000..f5534296
--- /dev/null
+++ b/common/str_ring_buffer.cc
@@ -0,0 +1,34 @@
+/*
+ * 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 "str_ring_buffer.h"
+
+#include "nextpnr_namespaces.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+std::string &StrRingBuffer::next()
+{
+ std::string &s = buffer.at(index++);
+ if (index >= N)
+ index = 0;
+ return s;
+}
+
+NEXTPNR_NAMESPACE_END
diff --git a/common/str_ring_buffer.h b/common/str_ring_buffer.h
new file mode 100644
index 00000000..9a16fe14
--- /dev/null
+++ b/common/str_ring_buffer.h
@@ -0,0 +1,45 @@
+/*
+ * nextpnr -- Next Generation Place and Route
+ *
+ * Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
+ * Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+#ifndef STR_RING_BUFFER_H
+#define STR_RING_BUFFER_H
+
+#include <array>
+#include <string>
+
+#include "nextpnr_namespaces.h"
+
+NEXTPNR_NAMESPACE_BEGIN
+
+// A ring buffer of strings, so we can return a simple const char * pointer for %s formatting - inspired by how logging
+// in Yosys works Let's just hope noone tries to log more than 100 things in one call....
+class StrRingBuffer
+{
+ private:
+ static const size_t N = 100;
+ std::array<std::string, N> buffer;
+ size_t index = 0;
+
+ public:
+ std::string &next();
+};
+
+NEXTPNR_NAMESPACE_END
+
+#endif /* STR_RING_BUFFER_H */
diff --git a/ecp5/arch.h b/ecp5/arch.h
index f1da870a..063a3df6 100644
--- a/ecp5/arch.h
+++ b/ecp5/arch.h
@@ -18,19 +18,20 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "arch.h" via "nextpnr.h" only.
-#endif
+#ifndef ECP5_ARCH_H
+#define ECP5_ARCH_H
#include <set>
#include <sstream>
+#include "base_arch.h"
+#include "nextpnr_types.h"
+#include "relptr.h"
+
NEXTPNR_NAMESPACE_BEGIN
/**** Everything in this section must be kept in sync with chipdb.py ****/
-#include "relptr.h"
-
NPNR_PACKED_STRUCT(struct BelWirePOD {
LocationPOD rel_wire_loc;
int32_t wire_index;
@@ -928,3 +929,5 @@ struct Arch : BaseArch<ArchRanges>
};
NEXTPNR_NAMESPACE_END
+
+#endif /* ECP5_ARCH_H */
diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h
index cf6902d3..6a149264 100644
--- a/ecp5/archdefs.h
+++ b/ecp5/archdefs.h
@@ -18,12 +18,14 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "archdefs.h" via "nextpnr.h" only.
-#endif
+#ifndef ECP5_ARCHDEFS_H
+#define ECP5_ARCHDEFS_H
#include <boost/functional/hash.hpp>
+#include "idstring.h"
+#include "nextpnr_namespaces.h"
+
NEXTPNR_NAMESPACE_BEGIN
typedef int delay_t;
@@ -165,12 +167,12 @@ struct ArchCellInfo
bool is_output_b_registered;
// Which timing information to use for a DP16KD. Depends on registering
// configuration.
- nextpnr_ecp5::IdString regmode_timing_id;
+ IdString regmode_timing_id;
} ramInfo;
struct
{
bool is_clocked;
- nextpnr_ecp5::IdString timing_id;
+ IdString timing_id;
} multInfo;
};
@@ -242,3 +244,5 @@ template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
};
} // namespace std
+
+#endif /* ECP5_ARCHDEFS_H */
diff --git a/ecp5/lpf.cc b/ecp5/lpf.cc
index 22859783..6d134e8d 100644
--- a/ecp5/lpf.cc
+++ b/ecp5/lpf.cc
@@ -19,7 +19,11 @@
#include <boost/algorithm/string.hpp>
#include <sstream>
+#include <unordered_set>
+
+#include "arch.h"
#include "log.h"
+#include "nextpnr_namespaces.h"
NEXTPNR_NAMESPACE_BEGIN
diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h
index 16c79e8a..a171ba55 100644
--- a/fpga_interchange/arch.h
+++ b/fpga_interchange/arch.h
@@ -20,16 +20,18 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "arch.h" via "nextpnr.h" only.
-#endif
+#ifndef FPGA_INTERCHANGE_ARCH_H
+#define FPGA_INTERCHANGE_ARCH_H
#include <boost/iostreams/device/mapped_file.hpp>
-
#include <iostream>
-
#include <regex>
+
+#include "arch_api.h"
#include "constraints.h"
+#include "nextpnr_types.h"
+#include "relptr.h"
+
#include "dedicated_interconnect.h"
#include "site_router.h"
@@ -37,8 +39,6 @@ NEXTPNR_NAMESPACE_BEGIN
/**** Everything in this section must be kept in sync with chipdb.py ****/
-#include "relptr.h"
-
// Flattened site indexing.
//
// To enable flat BelId.z spaces, every tile and sites within that tile are
@@ -1742,3 +1742,5 @@ struct Arch : ArchAPI<ArchRanges>
};
NEXTPNR_NAMESPACE_END
+
+#endif /* FPGA_INTERCHANGE_ARCH_H */
diff --git a/fpga_interchange/archdefs.h b/fpga_interchange/archdefs.h
index e355a6c4..c9c0bc4f 100644
--- a/fpga_interchange/archdefs.h
+++ b/fpga_interchange/archdefs.h
@@ -18,16 +18,17 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "archdefs.h" via "nextpnr.h" only.
-#endif
+#ifndef FPGA_INTERCHANGE_ARCHDEFS_H
+#define FPGA_INTERCHANGE_ARCHDEFS_H
+
+#include <boost/functional/hash.hpp>
+#include <cstdint>
#include "luts.h"
+#include "nextpnr_namespaces.h"
NEXTPNR_NAMESPACE_BEGIN
-#include <cstdint>
-
typedef int delay_t;
// -----------------------------------------------------------------------
@@ -177,3 +178,5 @@ template <> struct hash<NEXTPNR_NAMESPACE_PREFIX BelBucketId>
};
} // namespace std
+
+#endif /* FPGA_INTERCHANGE_ARCHDEFS_H */
diff --git a/fpga_interchange/dedicated_interconnect.h b/fpga_interchange/dedicated_interconnect.h
index 66e1d41b..41adea15 100644
--- a/fpga_interchange/dedicated_interconnect.h
+++ b/fpga_interchange/dedicated_interconnect.h
@@ -18,9 +18,16 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "dedicated_interconnect.h" via "nextpnr.h" only.
-#endif
+#ifndef DEDICATED_INTERCONNECT_H
+#define DEDICATED_INTERCONNECT_H
+
+#include <boost/functional/hash.hpp>
+#include <cstdint>
+#include <unordered_map>
+
+#include "archdefs.h"
+#include "idstring.h"
+#include "nextpnr_namespaces.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -138,3 +145,5 @@ struct DedicatedInterconnect
};
NEXTPNR_NAMESPACE_END
+
+#endif /* DEDICATED_INTERCONNECT_H */
diff --git a/fpga_interchange/luts.h b/fpga_interchange/luts.h
index a5d3b1d0..333df36e 100644
--- a/fpga_interchange/luts.h
+++ b/fpga_interchange/luts.h
@@ -17,15 +17,17 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "luts.h" via "nextpnr.h" only.
-#endif
-
-#include "dynamic_bitarray.h"
-
#ifndef LUTS_H
#define LUTS_H
+#include <unordered_map>
+#include <unordered_set>
+
+#include "idstring.h"
+#include "nextpnr_namespaces.h"
+
+#include "dynamic_bitarray.h"
+
NEXTPNR_NAMESPACE_BEGIN
struct CellInfo;
diff --git a/fpga_interchange/site_router.h b/fpga_interchange/site_router.h
index 6af32747..c2590728 100644
--- a/fpga_interchange/site_router.h
+++ b/fpga_interchange/site_router.h
@@ -18,13 +18,14 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "site_router.h" via "nextpnr.h" only.
-#endif
-
#ifndef SITE_ROUTER_H
#define SITE_ROUTER_H
+#include <cstdint>
+
+#include "nextpnr_namespaces.h"
+#include "nextpnr_types.h"
+
NEXTPNR_NAMESPACE_BEGIN
struct Context;
diff --git a/generic/arch.h b/generic/arch.h
index cc8de484..1d37b2fd 100644
--- a/generic/arch.h
+++ b/generic/arch.h
@@ -17,9 +17,16 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "arch.h" via "nextpnr.h" only.
-#endif
+#ifndef GENERIC_ARCH_H
+#define GENERIC_ARCH_H
+
+#include <map>
+
+#include "arch_api.h"
+#include "idstring.h"
+#include "idstringlist.h"
+#include "nextpnr_namespaces.h"
+#include "nextpnr_types.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -366,3 +373,5 @@ struct Arch : ArchAPI<ArchRanges>
};
NEXTPNR_NAMESPACE_END
+
+#endif /* GENERIC_ARCH_H */
diff --git a/generic/archdefs.h b/generic/archdefs.h
index a5108e9e..bdd97dde 100644
--- a/generic/archdefs.h
+++ b/generic/archdefs.h
@@ -17,9 +17,12 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "archdefs.h" via "nextpnr.h" only.
-#endif
+#ifndef GENERIC_ARCHDEFS_H
+#define GENERIC_ARCHDEFS_H
+
+#include <unordered_map>
+
+#include "idstringlist.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -52,3 +55,5 @@ struct ArchCellInfo
};
NEXTPNR_NAMESPACE_END
+
+#endif /* GENERIC_ARCHDEFS_H */
diff --git a/gowin/arch.h b/gowin/arch.h
index e0686d1c..0f975f77 100644
--- a/gowin/arch.h
+++ b/gowin/arch.h
@@ -18,9 +18,18 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "arch.h" via "nextpnr.h" only.
-#endif
+#ifndef GOWIN_ARCH_H
+#define GOWIN_ARCH_H
+
+#include <cstdint>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base_arch.h"
+#include "idstring.h"
+#include "nextpnr_namespaces.h"
+#include "nextpnr_types.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -40,7 +49,7 @@ template <typename T> struct RelPtr
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[](std::size_t index) const { return get()[index]; }
const T &operator*() const { return *(get()); }
@@ -453,3 +462,5 @@ struct Arch : BaseArch<ArchRanges>
};
NEXTPNR_NAMESPACE_END
+
+#endif /* GOWIN_ARCH_H */
diff --git a/gowin/archdefs.h b/gowin/archdefs.h
index 963660c6..ef297d41 100644
--- a/gowin/archdefs.h
+++ b/gowin/archdefs.h
@@ -18,9 +18,11 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "archdefs.h" via "nextpnr.h" only.
-#endif
+#ifndef GOWIN_ARCHDEFS_H
+#define GOWIN_ARCHDEFS_H
+
+#include "idstring.h"
+#include "nextpnr_namespaces.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -68,3 +70,5 @@ struct ArchCellInfo
};
NEXTPNR_NAMESPACE_END
+
+#endif /* GOWIN_ARCHDEFS_H */
diff --git a/ice40/arch.h b/ice40/arch.h
index 4bac3d7a..29396f49 100644
--- a/ice40/arch.h
+++ b/ice40/arch.h
@@ -17,16 +17,20 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "arch.h" via "nextpnr.h" only.
-#endif
+#ifndef ICE40_ARCH_H
+#define ICE40_ARCH_H
+
+#include <cstdint>
+
+#include "base_arch.h"
+#include "nextpnr_namespaces.h"
+#include "nextpnr_types.h"
+#include "relptr.h"
NEXTPNR_NAMESPACE_BEGIN
/**** Everything in this section must be kept in sync with chipdb.py ****/
-#include "relptr.h"
-
NPNR_PACKED_STRUCT(struct BelWirePOD {
int32_t port;
int32_t type;
@@ -866,3 +870,5 @@ struct Arch : BaseArch<ArchRanges>
void ice40DelayFuzzerMain(Context *ctx);
NEXTPNR_NAMESPACE_END
+
+#endif /* ICE40_ARCH_H */
diff --git a/ice40/archdefs.h b/ice40/archdefs.h
index 5caa420a..038a3131 100644
--- a/ice40/archdefs.h
+++ b/ice40/archdefs.h
@@ -17,9 +17,13 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "archdefs.h" via "nextpnr.h" only.
-#endif
+#ifndef ICE40_ARCHDEFS_H
+#define ICE40_ARCHDEFS_H
+
+#include <boost/functional/hash.hpp>
+
+#include "idstring.h"
+#include "nextpnr_namespaces.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -196,3 +200,5 @@ template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
};
} // namespace std
+
+#endif /* ICE40_ARCHDEFS_H */
diff --git a/machxo2/arch.h b/machxo2/arch.h
index 7a22dd91..15535fe1 100644
--- a/machxo2/arch.h
+++ b/machxo2/arch.h
@@ -18,9 +18,14 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "arch.h" via "nextpnr.h" only.
-#endif
+#ifndef MACHXO2_ARCH_H
+#define MACHXO2_ARCH_H
+
+#include <cstdint>
+
+#include "base_arch.h"
+#include "nextpnr_namespaces.h"
+#include "nextpnr_types.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -37,7 +42,7 @@ template <typename T> struct RelPtr
const T *get() const { return reinterpret_cast<const T *>(reinterpret_cast<const char *>(this) + offset); }
- const T &operator[](size_t index) const { return get()[index]; }
+ const T &operator[](std::size_t index) const { return get()[index]; }
const T &operator*() const { return *(get()); }
@@ -684,3 +689,5 @@ struct Arch : BaseArch<ArchRanges>
};
NEXTPNR_NAMESPACE_END
+
+#endif /* MACHXO2_ARCH_H */
diff --git a/machxo2/archdefs.h b/machxo2/archdefs.h
index 433b1b6b..31575487 100644
--- a/machxo2/archdefs.h
+++ b/machxo2/archdefs.h
@@ -18,9 +18,11 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "archdefs.h" via "nextpnr.h" only.
-#endif
+#ifndef MACHXO2_ARCHDEFS_H
+#define MACHXO2_ARCHDEFS_H
+
+#include "idstring.h"
+#include "nextpnr_namespaces.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -153,3 +155,5 @@ template <> struct hash<NEXTPNR_NAMESPACE_PREFIX PipId>
};
} // namespace std
+
+#endif /* MACHXO2_ARCHDEFS_H */
diff --git a/nexus/arch.h b/nexus/arch.h
index 9dfc4551..55e9becd 100644
--- a/nexus/arch.h
+++ b/nexus/arch.h
@@ -18,18 +18,19 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "arch.h" via "nextpnr.h" only.
-#endif
+#ifndef NEXUS_ARCH_H
+#define NEXUS_ARCH_H
#include <boost/iostreams/device/mapped_file.hpp>
-
#include <iostream>
-NEXTPNR_NAMESPACE_BEGIN
-
+#include "base_arch.h"
+#include "nextpnr_namespaces.h"
+#include "nextpnr_types.h"
#include "relptr.h"
+NEXTPNR_NAMESPACE_BEGIN
+
/*
Fully deduplicated database
@@ -1394,3 +1395,5 @@ struct Arch : BaseArch<ArchRanges>
};
NEXTPNR_NAMESPACE_END
+
+#endif /* NEXUS_ARCH_H */
diff --git a/nexus/archdefs.h b/nexus/archdefs.h
index e6c3edde..b9ac3c77 100644
--- a/nexus/archdefs.h
+++ b/nexus/archdefs.h
@@ -17,9 +17,14 @@
*
*/
-#ifndef NEXTPNR_H
-#error Include "archdefs.h" via "nextpnr.h" only.
-#endif
+#ifndef NEXUS_ARCHDEFS_H
+#define NEXUS_ARCHDEFS_H
+
+#include <boost/functional/hash.hpp>
+#include <unordered_map>
+
+#include "idstring.h"
+#include "nextpnr_namespaces.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -233,3 +238,5 @@ template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
};
} // namespace std
+
+#endif /* NEXUS_ARCHDEFS_H */
diff --git a/nexus/fasm.cc b/nexus/fasm.cc
index 4394aebc..d802712d 100644
--- a/nexus/fasm.cc
+++ b/nexus/fasm.cc
@@ -22,6 +22,7 @@
#include "nextpnr.h"
#include "util.h"
+#include <boost/range/adaptor/reversed.hpp>
#include <queue>
NEXTPNR_NAMESPACE_BEGIN