From fe4608386eb163c70a75ed84beb07516af378b36 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Fri, 12 Mar 2021 13:09:44 -0800 Subject: Split nextpnr.h to allow for linear inclusion. "nextpnr.h" is no longer the god header. Important improvements: - Functions in log.h can be used without including BaseCtx/Arch/Context. This means that log_X functions can be called without included "nextpnr.h" - NPNR_ASSERT can be used without including "nextpnr.h" by including "nextpnr_assertions.h". This allows NPNR_ASSERT to be used safely in any header file. - Types defined in "archdefs.h" are now available without including BaseCtx/Arch/Context. This means that utility classes that will be used inside of BaseCtx/Arch/Context can be defined safely in a self-contained header. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- common/arch_api.h | 150 +++ common/base_arch.h | 388 ++++++++ common/basectx.cc | 334 +++++++ common/basectx.h | 238 +++++ common/constraints.h | 15 +- common/context.cc | 409 ++++++++ common/context.h | 103 ++ common/deterministic_rng.h | 103 ++ common/exclusive_state_groups.h | 12 +- common/exclusive_state_groups.impl.h | 10 +- common/idstring.cc | 51 + common/idstring.h | 73 ++ common/idstringlist.cc | 61 ++ common/idstringlist.h | 85 ++ common/log.h | 3 +- common/nextpnr.cc | 866 ----------------- common/nextpnr.h | 1513 +---------------------------- common/nextpnr_assertions.cc | 33 + common/nextpnr_assertions.h | 62 ++ common/nextpnr_base_types.h | 146 +++ common/nextpnr_namespaces.cc | 23 + common/nextpnr_namespaces.h | 53 + common/nextpnr_types.cc | 69 ++ common/nextpnr_types.h | 259 +++++ common/property.cc | 81 ++ common/property.h | 131 +++ common/relptr.h | 37 +- common/sso_array.h | 112 +++ common/str_ring_buffer.cc | 34 + common/str_ring_buffer.h | 45 + ecp5/arch.h | 13 +- ecp5/archdefs.h | 14 +- ecp5/lpf.cc | 4 + fpga_interchange/arch.h | 16 +- fpga_interchange/archdefs.h | 13 +- fpga_interchange/dedicated_interconnect.h | 15 +- fpga_interchange/luts.h | 14 +- fpga_interchange/site_router.h | 9 +- generic/arch.h | 15 +- generic/archdefs.h | 11 +- gowin/arch.h | 19 +- gowin/archdefs.h | 10 +- ice40/arch.h | 16 +- ice40/archdefs.h | 12 +- machxo2/arch.h | 15 +- machxo2/archdefs.h | 10 +- nexus/arch.h | 15 +- nexus/archdefs.h | 13 +- nexus/fasm.cc | 1 + 49 files changed, 3267 insertions(+), 2467 deletions(-) create mode 100644 common/arch_api.h create mode 100644 common/base_arch.h create mode 100644 common/basectx.cc create mode 100644 common/basectx.h create mode 100644 common/context.cc create mode 100644 common/context.h create mode 100644 common/deterministic_rng.h create mode 100644 common/idstring.cc create mode 100644 common/idstring.h create mode 100644 common/idstringlist.cc create mode 100644 common/idstringlist.h create mode 100644 common/nextpnr_assertions.cc create mode 100644 common/nextpnr_assertions.h create mode 100644 common/nextpnr_base_types.h create mode 100644 common/nextpnr_namespaces.cc create mode 100644 common/nextpnr_namespaces.h create mode 100644 common/nextpnr_types.cc create mode 100644 common/nextpnr_types.h create mode 100644 common/property.cc create mode 100644 common/property.h create mode 100644 common/sso_array.h create mode 100644 common/str_ring_buffer.cc create mode 100644 common/str_ring_buffer.h 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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 + +#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 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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 +#include + +#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 std::enable_if::value, Tc>::type empty_if_possible() +{ + return Tc(); +} +template typename std::enable_if::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 std::enable_if::value, IdString>::type bbid_to_name(Tbbid id) +{ + return id; +} +template +typename std::enable_if::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 std::enable_if::value, BelBucketId>::type bbid_from_name(IdString name) +{ + return name; +} +template +typename std::enable_if::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 std::enable_if::value, Tret>::type return_if_match(Tret r) +{ + return r; +} + +template +typename std::enable_if::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; + // Attributes + using BelAttrsRangeT = std::vector>; + using WireAttrsRangeT = std::vector>; + using PipAttrsRangeT = std::vector>; + // Groups + using AllGroupsRangeT = std::vector; + using GroupBelsRangeT = std::vector; + using GroupWiresRangeT = std::vector; + using GroupPipsRangeT = std::vector; + using GroupGroupsRangeT = std::vector; + // Decals + using DecalGfxRangeT = std::vector; + // Placement validity + using CellTypeRangeT = const std::vector &; + using BelBucketRangeT = const std::vector &; + using BucketBelRangeT = const std::vector &; +}; + +template struct BaseArch : ArchAPI +{ + // -------------------------------------------------------------- + // 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()(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(); + } + + virtual typename R::CellBelPinRangeT getBelPinsForCellPin(const CellInfo *cell_info, IdString pin) const override + { + return return_if_match, 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(); + } + virtual uint32_t getWireChecksum(WireId wire) const override { return uint32_t(std::hash()(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(); + } + virtual uint32_t getPipChecksum(PipId pip) const override { return uint32_t(std::hash()(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(); + } + // 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(); + }; + 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(bucket); } + virtual BelBucketId getBelBucketByName(IdString name) const override { return bbid_from_name(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 &, typename R::CellTypeRangeT>(cell_types); + } + virtual typename R::BelBucketRangeT getBelBuckets() const override + { + NPNR_ASSERT(bel_buckets_initialised); + return return_if_match &, typename R::BelBucketRangeT>(bel_buckets); + } + virtual typename R::BucketBelRangeT getBelsInBucket(BelBucketId bucket) const override + { + NPNR_ASSERT(bel_buckets_initialised); + return return_if_match &, 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 base_bel2cell; + std::unordered_map base_wire2net; + std::unordered_map base_pip2net; + + // For the default cell/bel bucket implementations + std::vector cell_types; + std::vector bel_buckets; + std::unordered_map> 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 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 + * + * 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 + +#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 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 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 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 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 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 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 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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 +#include +#include +#ifndef NPNR_DISABLE_THREADS +#include +#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 *idstring_str_to_idx; + mutable std::vector *idstring_idx_to_str; + + // Temporary string backing store for logging + mutable StrRingBuffer log_strs; + + // Project settings and config switches + std::unordered_map settings; + + // Placed nets and cells. + std::unordered_map> nets; + std::unordered_map> cells; + + // Hierarchical (non-leaf) cells by full path + std::unordered_map 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 net_aliases; + + // Top-level ports + std::unordered_map ports; + std::unordered_map port_cells; + + // Floorplanning regions + std::unordered_map> region; + + // Context meta data + std::unordered_map attrs; + + Context *as_ctx = nullptr; + + // Has the frontend loaded a design? + bool design_loaded; + + BaseCtx() + { + idstring_str_to_idx = new std::unordered_map; + idstring_idx_to_str = new std::vector; + 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 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 belUiReload; + std::unordered_set wireUiReload; + std::unordered_set pipUiReload; + std::unordered_set 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 +#include +#include +#include "archdefs.h" #include "exclusive_state_groups.h" +#include "idstring.h" +#include "nextpnr_namespaces.h" NEXTPNR_NAMESPACE_BEGIN -template struct Constraints +struct Context; + +template struct Constraints { using ConstraintStateType = StateType; using ConstraintCountType = CountType; @@ -41,7 +46,7 @@ template 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 + * + * 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 Context::getNetinfoSinkWires(const NetInfo *net_info, const PortRef &user_info) const +{ + auto dst_bel = user_info.cell->bel; + if (dst_bel == BelId()) + return SSOArray(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 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) : ""); + } + } + 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) : ""); + } + } + } +#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 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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 + +#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 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 *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 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(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 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(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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 +#include +#include +#include + +#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 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 void shuffle(std::vector &a) { shuffle(a.begin(), a.end()); } + + template void sorted_shuffle(std::vector &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 +#include +#include +#include +#include +#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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 +#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 +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX IdString &obj) const noexcept + { + return std::hash()(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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 +#include "idstring.h" +#include "nextpnr_namespaces.h" +#include "sso_array.h" + +NEXTPNR_NAMESPACE_BEGIN + +struct Context; + +struct IdStringList +{ + SSOArray ids; + + IdStringList(){}; + explicit IdStringList(size_t n) : ids(n, IdString()){}; + explicit IdStringList(IdString id) : ids(1, id){}; + template 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 +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX IdStringList &obj) const noexcept + { + std::size_t seed = 0; + boost::hash_combine(seed, hash()(obj.size())); + for (auto &id : obj) + boost::hash_combine(seed, hash()(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 #include #include -#include "nextpnr.h" +#include "nextpnr_namespaces.h" NEXTPNR_NAMESPACE_BEGIN @@ -62,7 +62,6 @@ extern std::unordered_map 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 -#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 Context::getNetinfoSinkWires(const NetInfo *net_info, const PortRef &user_info) const -{ - auto dst_bel = user_info.cell->bel; - if (dst_bel == BelId()) - return SSOArray(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 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) : ""); - } - } - 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) : ""); - } - } - } -#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 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 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 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 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 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 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 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 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#ifndef NPNR_DISABLE_THREADS -#include -#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 -{ - std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX IdString &obj) const noexcept - { - return std::hash()(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 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 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 ids; - - IdStringList(){}; - explicit IdStringList(size_t n) : ids(n, IdString()){}; - explicit IdStringList(IdString id) : ids(1, id){}; - template 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 -{ - std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX IdStringList &obj) const noexcept - { - std::size_t seed = 0; - boost::hash_combine(seed, hash()(obj.size())); - for (auto &id : obj) - boost::hash_combine(seed, hash()(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 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 -{ - std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX Loc &obj) const noexcept - { - std::size_t seed = 0; - boost::hash_combine(seed, hash()(obj.x)); - boost::hash_combine(seed, hash()(obj.y)); - boost::hash_combine(seed, hash()(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 bels; - std::unordered_set wires; - std::unordered_set 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 as_bits() const - { - std::vector 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(rise.minDelay(), fall.minDelay()); }; - delay_t maxDelay() const { return std::max(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 users; - std::unordered_map attrs; - - // wire -> uphill_pip - std::unordered_map wires; - - std::vector aliases; // entries in net_aliases that point to this net - - std::unique_ptr 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 ports; - std::unordered_map attrs, params; - - BelId bel; - PlaceStrength belStrength = STRENGTH_NONE; - - // placement constraints - CellInfo *constr_parent = nullptr; - std::vector 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 nets; - int offset; - bool upto; -}; - -struct HierarchicalCell -{ - IdString name, type, parent, fullpath; - // Name inside cell instance -> global name - std::unordered_map leaf_cells, nets; - // Global name -> name inside cell instance - std::unordered_map leaf_cells_by_gname, nets_by_gname; - // Cell port to net - std::unordered_map ports; - // Name inside cell instance -> global name - std::unordered_map 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 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 void shuffle(std::vector &a) { shuffle(a.begin(), a.end()); } - - template void sorted_shuffle(std::vector &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 *idstring_str_to_idx; - mutable std::vector *idstring_idx_to_str; - - // Temporary string backing store for logging - mutable StrRingBuffer log_strs; - - // Project settings and config switches - std::unordered_map settings; - - // Placed nets and cells. - std::unordered_map> nets; - std::unordered_map> cells; - - // Hierarchical (non-leaf) cells by full path - std::unordered_map 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 net_aliases; - - // Top-level ports - std::unordered_map ports; - std::unordered_map port_cells; - - // Floorplanning regions - std::unordered_map> region; - - // Context meta data - std::unordered_map attrs; - - Context *as_ctx = nullptr; - - // Has the frontend loaded a design? - bool design_loaded; - - BaseCtx() - { - idstring_str_to_idx = new std::unordered_map; - idstring_idx_to_str = new std::vector; - 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 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 belUiReload; - std::unordered_set wireUiReload; - std::unordered_set pipUiReload; - std::unordered_set 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 std::enable_if::value, Tc>::type empty_if_possible() -{ - return Tc(); -} -template typename std::enable_if::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 std::enable_if::value, IdString>::type bbid_to_name(Tbbid id) -{ - return id; -} -template -typename std::enable_if::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 std::enable_if::value, BelBucketId>::type bbid_from_name(IdString name) -{ - return name; -} -template -typename std::enable_if::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 std::enable_if::value, Tret>::type return_if_match(Tret r) -{ - return r; -} - -template -typename std::enable_if::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 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; - // Attributes - using BelAttrsRangeT = std::vector>; - using WireAttrsRangeT = std::vector>; - using PipAttrsRangeT = std::vector>; - // Groups - using AllGroupsRangeT = std::vector; - using GroupBelsRangeT = std::vector; - using GroupWiresRangeT = std::vector; - using GroupPipsRangeT = std::vector; - using GroupGroupsRangeT = std::vector; - // Decals - using DecalGfxRangeT = std::vector; - // Placement validity - using CellTypeRangeT = const std::vector &; - using BelBucketRangeT = const std::vector &; - using BucketBelRangeT = const std::vector &; -}; - -template struct BaseArch : ArchAPI -{ - // -------------------------------------------------------------- - // 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()(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(); - } - - virtual typename R::CellBelPinRangeT getBelPinsForCellPin(const CellInfo *cell_info, IdString pin) const override - { - return return_if_match, 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(); - } - virtual uint32_t getWireChecksum(WireId wire) const override { return uint32_t(std::hash()(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(); - } - virtual uint32_t getPipChecksum(PipId pip) const override { return uint32_t(std::hash()(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(); - } - // 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(); - }; - 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(bucket); } - virtual BelBucketId getBelBucketByName(IdString name) const override { return bbid_from_name(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 &, typename R::CellTypeRangeT>(cell_types); - } - virtual typename R::BelBucketRangeT getBelBuckets() const override - { - NPNR_ASSERT(bel_buckets_initialised); - return return_if_match &, typename R::BelBucketRangeT>(bel_buckets); - } - virtual typename R::BucketBelRangeT getBelsInBucket(BelBucketId bucket) const override - { - NPNR_ASSERT(bel_buckets_initialised); - return return_if_match &, 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 base_bel2cell; - std::unordered_map base_wire2net; - std::unordered_map base_pip2net; - - // For the default cell/bel bucket implementations - std::vector cell_types; - std::vector bel_buckets; - std::unordered_map> 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 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 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 *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 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(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 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(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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 + +#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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 +#include + +#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 +{ + std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX Loc &obj) const noexcept + { + std::size_t seed = 0; + boost::hash_combine(seed, hash()(obj.x)); + boost::hash_combine(seed, hash()(obj.y)); + boost::hash_combine(seed, hash()(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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 + * + * 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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 +#include + +#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 bels; + std::unordered_set wires; + std::unordered_set 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(rise.minDelay(), fall.minDelay()); }; + delay_t maxDelay() const { return std::max(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 users; + std::unordered_map attrs; + + // wire -> uphill_pip + std::unordered_map wires; + + std::vector aliases; // entries in net_aliases that point to this net + + std::unique_ptr 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 ports; + std::unordered_map attrs, params; + + BelId bel; + PlaceStrength belStrength = STRENGTH_NONE; + + // placement constraints + CellInfo *constr_parent = nullptr; + std::vector 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 nets; + int offset; + bool upto; +}; + +struct HierarchicalCell +{ + IdString name, type, parent, fullpath; + // Name inside cell instance -> global name + std::unordered_map leaf_cells, nets; + // Global name -> name inside cell instance + std::unordered_map leaf_cells_by_gname, nets_by_gname; + // Cell port to net + std::unordered_map ports; + // Name inside cell instance -> global name + std::unordered_map 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 + * + * 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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 +#include +#include +#include + +#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 as_bits() const + { + std::vector 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 + * + * 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 + +#include "nextpnr_assertions.h" +#include "nextpnr_namespaces.h" + +NEXTPNR_NAMESPACE_BEGIN template struct RelPtr { @@ -6,7 +33,7 @@ template struct RelPtr const T *get() const { return reinterpret_cast(reinterpret_cast(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 struct RelSlice { const T *get() const { return reinterpret_cast(reinterpret_cast(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 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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 + +#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 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 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 + * + * 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 + * Copyright (C) 2018 Serge Bazanski + * + * 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 +#include + +#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 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 #include +#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 }; 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 +#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 }; } // 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 #include +#include + +#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 - #include - #include + +#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 }; 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 +#include #include "luts.h" +#include "nextpnr_namespaces.h" NEXTPNR_NAMESPACE_BEGIN -#include - typedef int delay_t; // ----------------------------------------------------------------------- @@ -177,3 +178,5 @@ template <> struct hash }; } // 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 +#include +#include + +#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 +#include + +#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 + +#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 + +#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 }; 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 + +#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 +#include +#include +#include + +#include "base_arch.h" +#include "idstring.h" +#include "nextpnr_namespaces.h" +#include "nextpnr_types.h" NEXTPNR_NAMESPACE_BEGIN @@ -40,7 +49,7 @@ template struct RelPtr return const_cast(reinterpret_cast(reinterpret_cast(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 }; 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 + +#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 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 + +#include "idstring.h" +#include "nextpnr_namespaces.h" NEXTPNR_NAMESPACE_BEGIN @@ -196,3 +200,5 @@ template <> struct hash }; } // 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 + +#include "base_arch.h" +#include "nextpnr_namespaces.h" +#include "nextpnr_types.h" NEXTPNR_NAMESPACE_BEGIN @@ -37,7 +42,7 @@ template struct RelPtr const T *get() const { return reinterpret_cast(reinterpret_cast(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 }; 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 }; } // 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 - #include -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 }; 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 +#include + +#include "idstring.h" +#include "nextpnr_namespaces.h" NEXTPNR_NAMESPACE_BEGIN @@ -233,3 +238,5 @@ template <> struct hash }; } // 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 #include NEXTPNR_NAMESPACE_BEGIN -- cgit v1.2.3