/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #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__)) 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(int index = 0) : 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 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; }; 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 TimingConstrObjectId { int32_t index = -1; bool operator==(const TimingConstrObjectId &other) const { return index == other.index; } bool operator!=(const TimingConstrObjectId &other) const { return index != other.index; } }; 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; } }; template <> struct hash { std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX TimingConstrObjectId &obj) const noexcept { return hash()(obj.index); } }; } // 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_FIXED = 3, STRENGTH_LOCKED = 4, STRENGTH_USER = 5 }; struct PortRef { CellInfo *cell = nullptr; IdString port; delay_t budget = 0; }; struct PipMap { PipId pip = PipId(); PlaceStrength strength = STRENGTH_NONE; }; struct ClockConstraint; struct NetInfo : ArchNetInfo { IdString name; int32_t udata = 0; PortRef driver; std::vector users; std::unordered_map attrs; // wire -> uphill_pip std::unordered_map wires; std::unique_ptr clkconstr; TimingConstrObjectId tmg_id; Region *region = nullptr; }; enum PortType { PORT_IN = 0, PORT_OUT = 1, PORT_INOUT = 2 }; struct PortInfo { IdString name; NetInfo *net; PortType type; TimingConstrObjectId tmg_id; }; struct CellInfo : ArchCellInfo { IdString name, type; int32_t udata; std::unordered_map ports; std::unordered_map attrs, params; BelId bel; PlaceStrength belStrength = STRENGTH_NONE; // cell_port -> bel_pin std::unordered_map pins; // 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; TimingConstrObjectId tmg_id; }; 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; DelayInfo setup, hold; // Input timing checks DelayInfo clockToQ; // Output clock-to-Q time }; struct ClockConstraint { DelayInfo high; DelayInfo low; DelayInfo period; TimingConstrObjectId domain_tmg_id; }; struct TimingConstraintObject { TimingConstrObjectId id; enum { ANYTHING, CLOCK_DOMAIN, NET, CELL, CELL_PORT } type; IdString entity; // Name of clock net; net or cell IdString port; // Name of port on a cell }; struct TimingConstraint { IdString name; enum { FALSE_PATH, MIN_DELAY, MAX_DELAY, MULTICYCLE, } type; delay_t value; std::unordered_set from; std::unordered_set to; }; inline bool operator==(const std::pair &a, const std::pair &b) { return a.first == b.first && a.second == b.second; } 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(std::vector &a) { for (size_t i = 0; i != a.size(); i++) { size_t j = i + rng(a.size() - i); if (j > i) std::swap(a[i], a[j]); } } template void sorted_shuffle(std::vector &a) { std::sort(a.begin(), a.end()); shuffle(a); } }; struct BaseCtx { // Lock to perform mutating actions on the Context. std::mutex mutex; std::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; // ID String database. mutable std::unordered_map *idstring_str_to_idx; mutable std::vector *idstring_idx_to_str; // Project settings and config switches std::unordered_map settings; // Placed nets and cells. std::unordered_map> nets; std::unordered_map> cells; // Floorplanning regions std::unordered_map> region; 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); TimingConstraintObject wildcard; wildcard.id.index = 0; wildcard.type = TimingConstraintObject::ANYTHING; constraintObjects.push_back(wildcard); } ~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) { mutex.lock(); mutex_owner = std::this_thread::get_id(); } void unlock(void) { NPNR_ASSERT(std::this_thread::get_id() == mutex_owner); mutex.unlock(); } // Must be called by the UI before rendering data. This lock will be // prioritized when processing code calls yield(). void lock_ui(void) { ui_mutex.lock(); mutex.lock(); } void unlock_ui(void) { mutex.unlock(); ui_mutex.unlock(); } // 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) { unlock(); ui_mutex.lock(); ui_mutex.unlock(); lock(); } IdString id(const std::string &s) const { return IdString(this, s); } IdString id(const char *s) const { return IdString(this, s); } Context *getCtx() { return reinterpret_cast(this); } const Context *getCtx() const { return reinterpret_cast(this); } 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; // -------------------------------------------------------------- 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); } // -------------------------------------------------------------- // Timing Constraint API // constraint name -> constraint std::unordered_map> constraints; // object ID -> object std::vector constraintObjects; // object ID -> constraint std::unordered_multimap constrsFrom; std::unordered_multimap constrsTo; TimingConstrObjectId timingWildcardObject(); TimingConstrObjectId timingClockDomainObject(NetInfo *clockDomain); TimingConstrObjectId timingNetObject(NetInfo *net); TimingConstrObjectId timingCellObject(CellInfo *cell); TimingConstrObjectId timingPortObject(CellInfo *cell, IdString port); void addConstraint(std::unique_ptr constr); void removeConstraint(IdString constrName); // Intended to simplify Python API void addClock(IdString net, float freq); }; NEXTPNR_NAMESPACE_END #include "arch.h" NEXTPNR_NAMESPACE_BEGIN struct Context : Arch, DeterministicRNG { bool verbose = false; bool debug = false; bool force = false; bool timing_driven = true; float target_freq = 12e6; bool auto_freq = false; int slack_redist_iter = 0; Context(ArchArgs args) : Arch(args) {} // -------------------------------------------------------------- WireId getNetinfoSourceWire(const NetInfo *net_info) const; WireId getNetinfoSinkWire(const NetInfo *net_info, const PortRef &sink) 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); // -------------------------------------------------------------- uint32_t checksum() const; void check() const; void archcheck() const; }; NEXTPNR_NAMESPACE_END #endif