
This involves very few changes, all typical to WASM ports: * WASM doesn't currently support threads or atomics so those are disabled. * WASM doesn't currently support exceptions so the exception machinery is stubbed out. * WASM doesn't (and can't) have mmap(), so an emulation library is used. That library currently doesn't support MAP_SHARED flags, so MAP_PRIVATE is used instead. There is also an update to bring ECP5 bbasm CMake rules to parity with iCE40 ones, since although it is possible to embed chipdb into nextpnr on WASM, a 200 MB WASM file has very few practical uses. The README is not updated and there is no included toolchain file because at the moment it's not possible to build nextpnr with upstream boost and wasi-libc. Boost requires a patch (merged, will be available in boost 1.74.0), wasi-libc requires a few unmerged patches.
926 lines
25 KiB
C++
926 lines
25 KiB
C++
/*
|
|
* nextpnr -- Next Generation Place and Route
|
|
*
|
|
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
|
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
#include <algorithm>
|
|
#include <assert.h>
|
|
#include <condition_variable>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <stdexcept>
|
|
#include <stdint.h>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
#include <boost/functional/hash.hpp>
|
|
#include <boost/lexical_cast.hpp>
|
|
#include <boost/range/adaptor/reversed.hpp>
|
|
#ifndef NPNR_DISABLE_THREADS
|
|
#include <boost/thread.hpp>
|
|
#endif
|
|
|
|
#ifndef NEXTPNR_H
|
|
#define NEXTPNR_H
|
|
|
|
#ifdef NEXTPNR_NAMESPACE
|
|
#define NEXTPNR_NAMESPACE_PREFIX NEXTPNR_NAMESPACE::
|
|
#define NEXTPNR_NAMESPACE_BEGIN namespace NEXTPNR_NAMESPACE {
|
|
#define NEXTPNR_NAMESPACE_END }
|
|
#define USING_NEXTPNR_NAMESPACE using namespace NEXTPNR_NAMESPACE;
|
|
#else
|
|
#define NEXTPNR_NAMESPACE_PREFIX
|
|
#define NEXTPNR_NAMESPACE_BEGIN
|
|
#define NEXTPNR_NAMESPACE_END
|
|
#define USING_NEXTPNR_NAMESPACE
|
|
#endif
|
|
|
|
#if defined(__GNUC__) || defined(__clang__)
|
|
#define NPNR_ATTRIBUTE(...) __attribute__((__VA_ARGS__))
|
|
#define NPNR_NORETURN __attribute__((noreturn))
|
|
#define NPNR_DEPRECATED __attribute__((deprecated))
|
|
#define NPNR_PACKED_STRUCT(...) __VA_ARGS__ __attribute__((packed))
|
|
#elif defined(_MSC_VER)
|
|
#define NPNR_ATTRIBUTE(...)
|
|
#define NPNR_NORETURN __declspec(noreturn)
|
|
#define NPNR_DEPRECATED __declspec(deprecated)
|
|
#define NPNR_PACKED_STRUCT(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop))
|
|
#else
|
|
#define NPNR_ATTRIBUTE(...)
|
|
#define NPNR_NORETURN
|
|
#define NPNR_DEPRECATED
|
|
#define NPNR_PACKED_STRUCT(...) __VA_ARGS__
|
|
#endif
|
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
class assertion_failure : public std::runtime_error
|
|
{
|
|
public:
|
|
assertion_failure(std::string msg, std::string expr_str, std::string filename, int line);
|
|
|
|
std::string msg;
|
|
std::string expr_str;
|
|
std::string filename;
|
|
int line;
|
|
};
|
|
|
|
NPNR_NORETURN
|
|
inline void assert_fail_impl(const char *message, const char *expr_str, const char *filename, int line)
|
|
{
|
|
throw assertion_failure(message, expr_str, filename, line);
|
|
}
|
|
|
|
NPNR_NORETURN
|
|
inline void assert_fail_impl_str(std::string message, const char *expr_str, const char *filename, int line)
|
|
{
|
|
throw assertion_failure(message, expr_str, filename, line);
|
|
}
|
|
|
|
#define NPNR_ASSERT(cond) (!(cond) ? assert_fail_impl(#cond, #cond, __FILE__, __LINE__) : (void)true)
|
|
#define NPNR_ASSERT_MSG(cond, msg) (!(cond) ? assert_fail_impl(msg, #cond, __FILE__, __LINE__) : (void)true)
|
|
#define NPNR_ASSERT_FALSE(msg) (assert_fail_impl(msg, "false", __FILE__, __LINE__))
|
|
#define NPNR_ASSERT_FALSE_STR(msg) (assert_fail_impl_str(msg, "false", __FILE__, __LINE__))
|
|
|
|
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<NEXTPNR_NAMESPACE_PREFIX IdString>
|
|
{
|
|
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX IdString &obj) const noexcept
|
|
{
|
|
return std::hash<int>()(obj.index);
|
|
}
|
|
};
|
|
} // namespace std
|
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
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;
|
|
};
|
|
};
|
|
|
|
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<NEXTPNR_NAMESPACE_PREFIX Loc>
|
|
{
|
|
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX Loc &obj) const noexcept
|
|
{
|
|
std::size_t seed = 0;
|
|
boost::hash_combine(seed, hash<int>()(obj.x));
|
|
boost::hash_combine(seed, hash<int>()(obj.y));
|
|
boost::hash_combine(seed, hash<int>()(obj.z));
|
|
return seed;
|
|
}
|
|
};
|
|
|
|
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX TimingConstrObjectId>
|
|
{
|
|
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX TimingConstrObjectId &obj) const noexcept
|
|
{
|
|
return hash<int>()(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<BelId> bels;
|
|
std::unordered_set<WireId> wires;
|
|
std::unordered_set<Loc> 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 Property
|
|
{
|
|
enum State : char
|
|
{
|
|
S0 = '0',
|
|
S1 = '1',
|
|
Sx = 'x',
|
|
Sz = 'z'
|
|
};
|
|
|
|
Property();
|
|
Property(int64_t intval, int width = 32);
|
|
Property(const std::string &strval);
|
|
Property(State bit);
|
|
Property &operator=(const Property &other) = default;
|
|
|
|
bool is_string;
|
|
|
|
// The string literal (for string values), or a string of [01xz] (for numeric values)
|
|
std::string str;
|
|
// The lower 64 bits (for numeric values), unused for string values
|
|
int64_t intval;
|
|
|
|
void update_intval()
|
|
{
|
|
intval = 0;
|
|
for (int i = 0; i < int(str.size()); i++) {
|
|
NPNR_ASSERT(str[i] == S0 || str[i] == S1 || str[i] == Sx || str[i] == Sz);
|
|
if ((str[i] == S1) && i < 64)
|
|
intval |= (1ULL << i);
|
|
}
|
|
}
|
|
|
|
int64_t as_int64() const
|
|
{
|
|
NPNR_ASSERT(!is_string);
|
|
return intval;
|
|
}
|
|
std::vector<bool> as_bits() const
|
|
{
|
|
std::vector<bool> result;
|
|
result.reserve(str.size());
|
|
NPNR_ASSERT(!is_string);
|
|
for (auto c : str)
|
|
result.push_back(c == S1);
|
|
return result;
|
|
}
|
|
std::string as_string() const
|
|
{
|
|
NPNR_ASSERT(is_string);
|
|
return str;
|
|
}
|
|
const char *c_str() const
|
|
{
|
|
NPNR_ASSERT(is_string);
|
|
return str.c_str();
|
|
}
|
|
size_t size() const { return is_string ? 8 * str.size() : str.size(); }
|
|
double as_double() const
|
|
{
|
|
NPNR_ASSERT(is_string);
|
|
return std::stod(str);
|
|
}
|
|
bool as_bool() const
|
|
{
|
|
if (int(str.size()) <= 64)
|
|
return intval != 0;
|
|
else
|
|
return std::any_of(str.begin(), str.end(), [](char c) { return c == S1; });
|
|
}
|
|
bool is_fully_def() const
|
|
{
|
|
return !is_string && std::all_of(str.begin(), str.end(), [](char c) { return c == S0 || c == S1; });
|
|
}
|
|
Property extract(int offset, int len, State padding = State::S0) const
|
|
{
|
|
Property ret;
|
|
ret.is_string = false;
|
|
ret.str.reserve(len);
|
|
for (int i = offset; i < offset + len; i++)
|
|
ret.str.push_back(i < int(str.size()) ? str[i] : 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; }
|
|
|
|
struct ClockConstraint;
|
|
|
|
struct NetInfo : ArchNetInfo
|
|
{
|
|
IdString name, hierpath;
|
|
int32_t udata = 0;
|
|
|
|
PortRef driver;
|
|
std::vector<PortRef> users;
|
|
std::unordered_map<IdString, Property> attrs;
|
|
|
|
// wire -> uphill_pip
|
|
std::unordered_map<WireId, PipMap> wires;
|
|
|
|
std::vector<IdString> aliases; // entries in net_aliases that point to this net
|
|
|
|
std::unique_ptr<ClockConstraint> clkconstr;
|
|
|
|
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, hierpath;
|
|
int32_t udata;
|
|
|
|
std::unordered_map<IdString, PortInfo> ports;
|
|
std::unordered_map<IdString, Property> attrs, params;
|
|
|
|
BelId bel;
|
|
PlaceStrength belStrength = STRENGTH_NONE;
|
|
|
|
// cell_port -> bel_pin
|
|
std::unordered_map<IdString, IdString> pins;
|
|
|
|
// placement constraints
|
|
CellInfo *constr_parent = nullptr;
|
|
std::vector<CellInfo *> constr_children;
|
|
const int UNCONSTR = INT_MIN;
|
|
int constr_x = UNCONSTR; // this.x - parent.x
|
|
int constr_y = UNCONSTR; // this.y - parent.y
|
|
int constr_z = UNCONSTR; // this.z - parent.z
|
|
bool constr_abs_z = false; // parent.z := 0
|
|
// parent.[xyz] := 0 when (constr_parent == nullptr)
|
|
|
|
Region *region = nullptr;
|
|
TimingConstrObjectId tmg_id;
|
|
|
|
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);
|
|
};
|
|
|
|
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<TimingConstrObjectId> from;
|
|
std::unordered_set<TimingConstrObjectId> to;
|
|
};
|
|
|
|
// Represents the contents of a non-leaf cell in a design
|
|
// with hierarchy
|
|
|
|
struct HierarchicalPort
|
|
{
|
|
IdString name;
|
|
PortType dir;
|
|
std::vector<IdString> nets;
|
|
int offset;
|
|
bool upto;
|
|
};
|
|
|
|
struct HierarchicalCell
|
|
{
|
|
IdString name, type, parent, fullpath;
|
|
// Name inside cell instance -> global name
|
|
std::unordered_map<IdString, IdString> leaf_cells, nets;
|
|
// Global name -> name inside cell instance
|
|
std::unordered_map<IdString, IdString> leaf_cells_by_gname, nets_by_gname;
|
|
// Cell port to net
|
|
std::unordered_map<IdString, HierarchicalPort> ports;
|
|
// Name inside cell instance -> global name
|
|
std::unordered_map<IdString, IdString> hier_cells;
|
|
};
|
|
|
|
inline bool operator==(const std::pair<const TimingConstrObjectId, TimingConstraint *> &a,
|
|
const std::pair<TimingConstrObjectId, TimingConstraint *> &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 <typename T> void shuffle(std::vector<T> &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 <typename T> void sorted_shuffle(std::vector<T> &a)
|
|
{
|
|
std::sort(a.begin(), a.end());
|
|
shuffle(a);
|
|
}
|
|
};
|
|
|
|
struct BaseCtx
|
|
{
|
|
#ifndef NPNR_DISABLE_THREADS
|
|
// Lock to perform mutating actions on the Context.
|
|
std::mutex mutex;
|
|
boost::thread::id mutex_owner;
|
|
|
|
// Lock to be taken by UI when wanting to access context - the yield()
|
|
// method will lock/unlock it when its' released the main mutex to make
|
|
// sure the UI is not starved.
|
|
std::mutex ui_mutex;
|
|
#endif
|
|
|
|
// ID String database.
|
|
mutable std::unordered_map<std::string, int> *idstring_str_to_idx;
|
|
mutable std::vector<const std::string *> *idstring_idx_to_str;
|
|
|
|
// Project settings and config switches
|
|
std::unordered_map<IdString, Property> settings;
|
|
|
|
// Placed nets and cells.
|
|
std::unordered_map<IdString, std::unique_ptr<NetInfo>> nets;
|
|
std::unordered_map<IdString, std::unique_ptr<CellInfo>> cells;
|
|
|
|
// Hierarchical (non-leaf) cells by full path
|
|
std::unordered_map<IdString, HierarchicalCell> hierarchy;
|
|
// This is the root of the above structure
|
|
IdString top_module;
|
|
|
|
// Aliases for nets, which may have more than one name due to assignments and hierarchy
|
|
std::unordered_map<IdString, IdString> net_aliases;
|
|
|
|
// Top-level ports
|
|
std::unordered_map<IdString, PortInfo> ports;
|
|
|
|
// Floorplanning regions
|
|
std::unordered_map<IdString, std::unique_ptr<Region>> region;
|
|
|
|
// Context meta data
|
|
std::unordered_map<IdString, Property> attrs;
|
|
|
|
BaseCtx()
|
|
{
|
|
idstring_str_to_idx = new std::unordered_map<std::string, int>;
|
|
idstring_idx_to_str = new std::vector<const std::string *>;
|
|
IdString::initialize_add(this, "", 0);
|
|
IdString::initialize_arch(this);
|
|
|
|
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)
|
|
{
|
|
#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 reinterpret_cast<Context *>(this); }
|
|
|
|
const Context *getCtx() const { return reinterpret_cast<const Context *>(this); }
|
|
|
|
const char *nameOf(IdString name) const { return name.c_str(this); }
|
|
|
|
template <typename T> const char *nameOf(const T *obj) const
|
|
{
|
|
if (obj == nullptr)
|
|
return "";
|
|
return obj->name.c_str(this);
|
|
}
|
|
|
|
const char *nameOfBel(BelId bel) const;
|
|
const char *nameOfWire(WireId wire) const;
|
|
const char *nameOfPip(PipId pip) const;
|
|
const char *nameOfGroup(GroupId group) const;
|
|
|
|
// --------------------------------------------------------------
|
|
|
|
bool allUiReload = true;
|
|
bool frameUiReload = false;
|
|
std::unordered_set<BelId> belUiReload;
|
|
std::unordered_set<WireId> wireUiReload;
|
|
std::unordered_set<PipId> pipUiReload;
|
|
std::unordered_set<GroupId> groupUiReload;
|
|
|
|
void refreshUi() { allUiReload = true; }
|
|
|
|
void refreshUiFrame() { frameUiReload = true; }
|
|
|
|
void refreshUiBel(BelId bel) { belUiReload.insert(bel); }
|
|
|
|
void refreshUiWire(WireId wire) { wireUiReload.insert(wire); }
|
|
|
|
void refreshUiPip(PipId pip) { pipUiReload.insert(pip); }
|
|
|
|
void refreshUiGroup(GroupId group) { groupUiReload.insert(group); }
|
|
|
|
// --------------------------------------------------------------
|
|
|
|
// Timing Constraint API
|
|
|
|
// constraint name -> constraint
|
|
std::unordered_map<IdString, std::unique_ptr<TimingConstraint>> constraints;
|
|
// object ID -> object
|
|
std::vector<TimingConstraintObject> constraintObjects;
|
|
// object ID -> constraint
|
|
std::unordered_multimap<TimingConstrObjectId, TimingConstraint *> constrsFrom;
|
|
std::unordered_multimap<TimingConstrObjectId, TimingConstraint *> constrsTo;
|
|
|
|
TimingConstrObjectId timingWildcardObject();
|
|
TimingConstrObjectId timingClockDomainObject(NetInfo *clockDomain);
|
|
TimingConstrObjectId timingNetObject(NetInfo *net);
|
|
TimingConstrObjectId timingCellObject(CellInfo *cell);
|
|
TimingConstrObjectId timingPortObject(CellInfo *cell, IdString port);
|
|
|
|
NetInfo *getNetByAlias(IdString alias) const
|
|
{
|
|
return nets.count(alias) ? nets.at(alias).get() : nets.at(net_aliases.at(alias)).get();
|
|
}
|
|
|
|
void addConstraint(std::unique_ptr<TimingConstraint> constr);
|
|
void removeConstraint(IdString constrName);
|
|
|
|
// 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
|
|
|
|
#include "arch.h"
|
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
struct Context : Arch, DeterministicRNG
|
|
{
|
|
bool verbose = false;
|
|
bool debug = false;
|
|
bool force = false;
|
|
|
|
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<WireId, PipId> *route = nullptr, bool useEstimate = true);
|
|
|
|
// --------------------------------------------------------------
|
|
// call after changing hierpath or adding/removing nets and cells
|
|
void fixupHierarchy();
|
|
|
|
// --------------------------------------------------------------
|
|
|
|
// provided by sdf.cc
|
|
void writeSDF(std::ostream &out, bool cvc_mode = false) const;
|
|
|
|
// --------------------------------------------------------------
|
|
|
|
// provided by svg.cc
|
|
void writeSVG(const std::string &filename, const std::string &flags = "") const;
|
|
|
|
// --------------------------------------------------------------
|
|
|
|
uint32_t checksum() const;
|
|
|
|
void check() const;
|
|
void archcheck() const;
|
|
|
|
template <typename T> T setting(const char *name, T defaultValue)
|
|
{
|
|
IdString new_id = id(name);
|
|
auto found = settings.find(new_id);
|
|
if (found != settings.end())
|
|
return boost::lexical_cast<T>(found->second.is_string ? found->second.as_string()
|
|
: std::to_string(found->second.as_int64()));
|
|
else
|
|
settings[id(name)] = std::to_string(defaultValue);
|
|
|
|
return defaultValue;
|
|
}
|
|
|
|
template <typename T> T setting(const char *name) const
|
|
{
|
|
IdString new_id = id(name);
|
|
auto found = settings.find(new_id);
|
|
if (found != settings.end())
|
|
return boost::lexical_cast<T>(found->second.is_string ? found->second.as_string()
|
|
: std::to_string(found->second.as_int64()));
|
|
else
|
|
throw std::runtime_error("settings does not exists");
|
|
}
|
|
};
|
|
|
|
NEXTPNR_NAMESPACE_END
|
|
|
|
#endif
|