Merge branch 'new_python' into 'master'
New "contextual" system of Python wrappers See merge request SymbioticEDA/nextpnr!4
This commit is contained in:
commit
6d423bb24a
@ -27,8 +27,6 @@ assertion_failure::assertion_failure(std::string msg, std::string expr_str, std:
|
||||
{
|
||||
}
|
||||
|
||||
std::unordered_set<BaseCtx *> IdString::global_ctx;
|
||||
|
||||
void IdString::set(const BaseCtx *ctx, const std::string &s)
|
||||
{
|
||||
auto it = ctx->idstring_str_to_idx->find(s);
|
||||
|
@ -118,16 +118,6 @@ struct IdString
|
||||
bool operator!=(const IdString &other) const { return index != other.index; }
|
||||
|
||||
bool empty() const { return index == 0; }
|
||||
|
||||
// --- deprecated old API ---
|
||||
|
||||
static std::unordered_set<BaseCtx *> global_ctx;
|
||||
|
||||
NPNR_DEPRECATED const std::string &global_str() const
|
||||
{
|
||||
assert(global_ctx.size() == 1);
|
||||
return str(*global_ctx.begin());
|
||||
}
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
@ -252,8 +242,6 @@ struct BaseCtx
|
||||
|
||||
BaseCtx()
|
||||
{
|
||||
IdString::global_ctx.insert(this);
|
||||
|
||||
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);
|
||||
@ -262,7 +250,6 @@ struct BaseCtx
|
||||
|
||||
~BaseCtx()
|
||||
{
|
||||
IdString::global_ctx.erase(this);
|
||||
delete idstring_str_to_idx;
|
||||
delete idstring_idx_to_str;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
#ifndef NO_PYTHON
|
||||
|
||||
#include "pybindings.h"
|
||||
#include "arch_pybindings.h"
|
||||
#include "jsonparse.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
@ -61,8 +62,18 @@ Context *load_design_shim(std::string filename, ArchArgs args)
|
||||
return d;
|
||||
}
|
||||
|
||||
void translate_assertfail(const assertion_failure &e)
|
||||
{
|
||||
// Use the Python 'C' API to set up an exception object
|
||||
PyErr_SetString(PyExc_AssertionError, e.what());
|
||||
}
|
||||
|
||||
BOOST_PYTHON_MODULE(MODULE_NAME)
|
||||
{
|
||||
register_exception_translator<assertion_failure>(&translate_assertfail);
|
||||
|
||||
using namespace PythonConversion;
|
||||
|
||||
class_<GraphicElement>("GraphicElement")
|
||||
.def_readwrite("type", &GraphicElement::type)
|
||||
.def_readwrite("x1", &GraphicElement::x1)
|
||||
@ -71,59 +82,73 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
|
||||
.def_readwrite("y2", &GraphicElement::y2)
|
||||
.def_readwrite("text", &GraphicElement::text);
|
||||
|
||||
class_<PortRef>("PortRef").def_readwrite("cell", &PortRef::cell).def_readwrite("port", &PortRef::port);
|
||||
|
||||
class_<NetInfo, NetInfo *, boost::noncopyable>("NetInfo")
|
||||
.def_readwrite("name", &NetInfo::name)
|
||||
.def_readwrite("driver", &NetInfo::driver)
|
||||
.def_readwrite("users", &NetInfo::users)
|
||||
.def_readwrite("attrs", &NetInfo::attrs)
|
||||
.def_readwrite("wires", &NetInfo::wires);
|
||||
|
||||
WRAP_MAP(decltype(NetInfo::attrs), "IdStrMap");
|
||||
|
||||
class_<std::vector<PortRef>>("PortRefVector").def(vector_indexing_suite<std::vector<PortRef>>());
|
||||
|
||||
enum_<PortType>("PortType")
|
||||
.value("PORT_IN", PORT_IN)
|
||||
.value("PORT_OUT", PORT_OUT)
|
||||
.value("PORT_INOUT", PORT_INOUT)
|
||||
.export_values();
|
||||
|
||||
class_<PortInfo>("PortInfo")
|
||||
.def_readwrite("name", &PortInfo::name)
|
||||
.def_readwrite("net", &PortInfo::net)
|
||||
.def_readwrite("type", &PortInfo::type);
|
||||
typedef std::unordered_map<IdString, std::string> AttrMap;
|
||||
typedef std::unordered_map<IdString, PortInfo> PortMap;
|
||||
typedef std::unordered_map<IdString, IdString> PinMap;
|
||||
|
||||
class_<CellInfo, CellInfo *>("CellInfo")
|
||||
.def_readwrite("name", &CellInfo::name)
|
||||
.def_readwrite("type", &CellInfo::type)
|
||||
.def_readwrite("ports", &CellInfo::ports)
|
||||
.def_readwrite("attrs", &CellInfo::attrs)
|
||||
.def_readwrite("params", &CellInfo::params)
|
||||
.def_readwrite("bel", &CellInfo::bel)
|
||||
.def_readwrite("pins", &CellInfo::pins);
|
||||
class_<BaseCtx, BaseCtx *, boost::noncopyable>("BaseCtx", no_init);
|
||||
|
||||
WRAP_MAP(decltype(CellInfo::ports), "IdPortMap");
|
||||
// WRAP_MAP(decltype(CellInfo::pins), "IdIdMap");
|
||||
auto ci_cls = class_<ContextualWrapper<CellInfo &>>("CellInfo", no_init);
|
||||
readwrite_wrapper<CellInfo &, typeof(&CellInfo::name), &CellInfo::name, conv_to_str<IdString>,
|
||||
conv_from_str<IdString>>::def_wrap(ci_cls, "name");
|
||||
readwrite_wrapper<CellInfo &, typeof(&CellInfo::type), &CellInfo::type, conv_to_str<IdString>,
|
||||
conv_from_str<IdString>>::def_wrap(ci_cls, "type");
|
||||
readonly_wrapper<CellInfo &, typeof(&CellInfo::attrs), &CellInfo::attrs, wrap_context<AttrMap &>>::def_wrap(
|
||||
ci_cls, "attrs");
|
||||
readonly_wrapper<CellInfo &, typeof(&CellInfo::params), &CellInfo::params, wrap_context<AttrMap &>>::def_wrap(
|
||||
ci_cls, "params");
|
||||
readonly_wrapper<CellInfo &, typeof(&CellInfo::ports), &CellInfo::ports, wrap_context<PortMap &>>::def_wrap(
|
||||
ci_cls, "ports");
|
||||
readwrite_wrapper<CellInfo &, typeof(&CellInfo::bel), &CellInfo::bel, conv_to_str<BelId>,
|
||||
conv_from_str<BelId>>::def_wrap(ci_cls, "bel");
|
||||
readwrite_wrapper<CellInfo &, typeof(&CellInfo::belStrength), &CellInfo::belStrength, pass_through<PlaceStrength>,
|
||||
pass_through<PlaceStrength>>::def_wrap(ci_cls, "belStrength");
|
||||
readonly_wrapper<CellInfo &, typeof(&CellInfo::pins), &CellInfo::pins, wrap_context<PinMap &>>::def_wrap(ci_cls,
|
||||
"pins");
|
||||
|
||||
class_<BaseCtx, BaseCtx *, boost::noncopyable>("BaseCtx", no_init)
|
||||
.add_property("nets", make_getter(&Context::nets, return_internal_reference<>()))
|
||||
.add_property("cells", make_getter(&Context::cells, return_internal_reference<>()));
|
||||
auto pi_cls = class_<ContextualWrapper<PortInfo &>>("PortInfo", no_init);
|
||||
readwrite_wrapper<PortInfo &, typeof(&PortInfo::name), &PortInfo::name, conv_to_str<IdString>,
|
||||
conv_from_str<IdString>>::def_wrap(pi_cls, "name");
|
||||
readonly_wrapper<PortInfo &, typeof(&PortInfo::net), &PortInfo::net, deref_and_wrap<NetInfo>>::def_wrap(pi_cls,
|
||||
"net");
|
||||
readwrite_wrapper<PortInfo &, typeof(&PortInfo::type), &PortInfo::type, pass_through<PortType>,
|
||||
pass_through<PortType>>::def_wrap(pi_cls, "type");
|
||||
|
||||
WRAP_MAP_UPTR(decltype(Context::nets), "IdNetMap");
|
||||
WRAP_MAP_UPTR(decltype(Context::cells), "IdCellMap");
|
||||
typedef std::vector<PortRef> PortVector;
|
||||
typedef std::unordered_map<WireId, PipMap> WireMap;
|
||||
|
||||
auto ni_cls = class_<ContextualWrapper<NetInfo &>>("NetInfo", no_init);
|
||||
readwrite_wrapper<NetInfo &, typeof(&NetInfo::name), &NetInfo::name, conv_to_str<IdString>,
|
||||
conv_from_str<IdString>>::def_wrap(ni_cls, "name");
|
||||
readwrite_wrapper<NetInfo &, typeof(&NetInfo::driver), &NetInfo::driver, wrap_context<PortRef &>,
|
||||
unwrap_context<PortRef &>>::def_wrap(ni_cls, "driver");
|
||||
readonly_wrapper<NetInfo &, typeof(&NetInfo::users), &NetInfo::users, wrap_context<PortVector &>>::def_wrap(
|
||||
ni_cls, "users");
|
||||
readonly_wrapper<NetInfo &, typeof(&NetInfo::wires), &NetInfo::wires, wrap_context<WireMap &>>::def_wrap(ni_cls,
|
||||
"wires");
|
||||
|
||||
auto pr_cls = class_<ContextualWrapper<PortRef &>>("PortRef", no_init);
|
||||
readonly_wrapper<PortRef &, typeof(&PortRef::cell), &PortRef::cell, deref_and_wrap<CellInfo>>::def_wrap(pr_cls,
|
||||
"cell");
|
||||
readwrite_wrapper<PortRef &, typeof(&PortRef::port), &PortRef::port, conv_to_str<IdString>,
|
||||
conv_from_str<IdString>>::def_wrap(pr_cls, "port");
|
||||
readwrite_wrapper<PortRef &, typeof(&PortRef::budget), &PortRef::budget, pass_through<delay_t>,
|
||||
pass_through<delay_t>>::def_wrap(pr_cls, "budget");
|
||||
|
||||
def("parse_json", parse_json_shim);
|
||||
def("load_design", load_design_shim, return_value_policy<manage_new_object>());
|
||||
|
||||
class_<IdString>("IdString")
|
||||
.def("__str__", &IdString::global_str, return_value_policy<copy_const_reference>())
|
||||
.def(self < self)
|
||||
.def(self == self);
|
||||
arch_wrap_python();
|
||||
WRAP_MAP(AttrMap, pass_through<std::string>, "AttrMap");
|
||||
WRAP_MAP(PortMap, wrap_context<PortInfo &>, "PortMap");
|
||||
WRAP_MAP(PinMap, conv_to_str<IdString>, "PinMap");
|
||||
|
||||
class_<Context, Context *, bases<Arch>, boost::noncopyable>("Context", no_init).def("checksum", &Context::checksum);
|
||||
arch_wrap_python();
|
||||
}
|
||||
|
||||
static wchar_t *program;
|
||||
|
@ -36,50 +36,6 @@ NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
using namespace boost::python;
|
||||
|
||||
/*
|
||||
A wrapper to enable custom type/ID to/from string conversions
|
||||
*/
|
||||
template <typename T> struct string_wrapper
|
||||
{
|
||||
template <typename F> struct from_pystring_converter
|
||||
{
|
||||
from_pystring_converter()
|
||||
{
|
||||
converter::registry::push_back(&convertible, &construct, boost::python::type_id<T>());
|
||||
};
|
||||
|
||||
static void *convertible(PyObject *object) { return PyUnicode_Check(object) ? object : 0; }
|
||||
|
||||
static void construct(PyObject *object, converter::rvalue_from_python_stage1_data *data)
|
||||
{
|
||||
const wchar_t *value = PyUnicode_AsUnicode(object);
|
||||
const std::wstring value_ws(value);
|
||||
if (value == 0)
|
||||
throw_error_already_set();
|
||||
void *storage = ((boost::python::converter::rvalue_from_python_storage<T> *)data)->storage.bytes;
|
||||
new (storage) T(fn(std::string(value_ws.begin(), value_ws.end())));
|
||||
data->convertible = storage;
|
||||
}
|
||||
|
||||
static F fn;
|
||||
};
|
||||
|
||||
template <typename F> struct to_str_wrapper
|
||||
{
|
||||
static F fn;
|
||||
|
||||
std::string str(T &x) { return fn(x); }
|
||||
};
|
||||
|
||||
template <typename F1, typename F2> static void wrap(const char *type_name, F1 to_str_fn, F2 from_str_fn)
|
||||
{
|
||||
from_pystring_converter<F2>::fn = from_str_fn;
|
||||
from_pystring_converter<F2>();
|
||||
to_str_wrapper<F1>::fn = to_str_fn;
|
||||
class_<T>(type_name, no_init).def("__str__", to_str_wrapper<F1>::str);
|
||||
};
|
||||
};
|
||||
|
||||
std::string parse_python_exception();
|
||||
|
||||
template <typename Tn> void python_export_global(const char *name, Tn &x)
|
||||
@ -106,6 +62,25 @@ void deinit_python();
|
||||
|
||||
void execute_python_file(const char *python_file);
|
||||
|
||||
// Defauld IdString conversions
|
||||
namespace PythonConversion {
|
||||
|
||||
template <> struct string_converter<IdString>
|
||||
{
|
||||
inline IdString from_str(Context *ctx, std::string name) { return ctx->id(name); }
|
||||
|
||||
inline std::string to_str(Context *ctx, IdString id) { return id.str(ctx); }
|
||||
};
|
||||
|
||||
template <> struct string_converter<const IdString>
|
||||
{
|
||||
inline IdString from_str(Context *ctx, std::string name) { return ctx->id(name); }
|
||||
|
||||
inline std::string to_str(Context *ctx, IdString id) { return id.str(ctx); }
|
||||
};
|
||||
|
||||
} // namespace PythonConversion
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif /* end of include guard: COMMON_PYBINDINGS_HH */
|
||||
|
@ -24,10 +24,12 @@
|
||||
#include <boost/python.hpp>
|
||||
#include <boost/python/suite/indexing/map_indexing_suite.hpp>
|
||||
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "nextpnr.h"
|
||||
#include "pywrappers.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
@ -37,18 +39,22 @@ inline void KeyError() { PyErr_SetString(PyExc_KeyError, "Key not found"); }
|
||||
|
||||
/*
|
||||
A wrapper for a Pythonised nextpnr Iterator. The actual class wrapped is a
|
||||
pair<Iterator, Iterator> containing (current, end)
|
||||
pair<Iterator, Iterator> containing (current, end), wrapped in a ContextualWrapper
|
||||
|
||||
*/
|
||||
|
||||
template <typename T, typename P> struct iterator_wrapper
|
||||
template <typename T, typename P, typename value_conv = PythonConversion::pass_through<T>> struct iterator_wrapper
|
||||
{
|
||||
typedef decltype(*(std::declval<T>())) value_t;
|
||||
|
||||
static value_t next(std::pair<T, T> &iter)
|
||||
typedef PythonConversion::ContextualWrapper<std::pair<T, T>> wrapped_iter_t;
|
||||
using return_t = typename value_conv::ret_type;
|
||||
|
||||
static return_t next(wrapped_iter_t &iter)
|
||||
{
|
||||
if (iter.first != iter.second) {
|
||||
value_t val = *iter.first;
|
||||
++iter.first;
|
||||
if (iter.base.first != iter.base.second) {
|
||||
return_t val = value_conv()(iter.ctx, *iter.base.first);
|
||||
++iter.base.first;
|
||||
return val;
|
||||
} else {
|
||||
PyErr_SetString(PyExc_StopIteration, "End of range reached");
|
||||
@ -61,7 +67,7 @@ template <typename T, typename P> struct iterator_wrapper
|
||||
|
||||
static void wrap(const char *python_name)
|
||||
{
|
||||
class_<std::pair<T, T>>(python_name, no_init).def("__next__", next, P());
|
||||
class_<wrapped_iter_t>(python_name, no_init).def("__next__", next, P());
|
||||
}
|
||||
};
|
||||
|
||||
@ -71,22 +77,46 @@ and end() which return iterator-like objects supporting ++, * and !=
|
||||
Full STL iterator semantics are not required, unlike the standard Boost wrappers
|
||||
*/
|
||||
|
||||
template <typename T, typename P = return_value_policy<return_by_value>> struct range_wrapper
|
||||
template <typename T, typename P = return_value_policy<return_by_value>,
|
||||
typename value_conv = PythonConversion::pass_through<T>>
|
||||
struct range_wrapper
|
||||
{
|
||||
typedef decltype(std::declval<T>().begin()) iterator_t;
|
||||
typedef decltype(*(std::declval<iterator_t>())) value_t;
|
||||
typedef typename PythonConversion::ContextualWrapper<T> wrapped_range;
|
||||
typedef typename PythonConversion::ContextualWrapper<std::pair<iterator_t, iterator_t>> wrapped_pair;
|
||||
static wrapped_pair iter(wrapped_range &range)
|
||||
{
|
||||
return wrapped_pair(range.ctx, std::make_pair(range.base.begin(), range.base.end()));
|
||||
}
|
||||
|
||||
static std::pair<iterator_t, iterator_t> iter(T &range) { return std::make_pair(range.begin(), range.end()); }
|
||||
static std::string repr(wrapped_range &range)
|
||||
{
|
||||
PythonConversion::string_converter<value_t> conv;
|
||||
bool first = true;
|
||||
std::stringstream ss;
|
||||
ss << "[";
|
||||
for (const auto &item : range.base) {
|
||||
if (!first)
|
||||
ss << ", ";
|
||||
ss << "'" << conv.to_str(range.ctx, item) << "'";
|
||||
first = false;
|
||||
}
|
||||
ss << "]";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static void wrap(const char *range_name, const char *iter_name)
|
||||
{
|
||||
class_<T>(range_name, no_init).def("__iter__", iter);
|
||||
iterator_wrapper<iterator_t, P>().wrap(iter_name);
|
||||
class_<wrapped_range>(range_name, no_init).def("__iter__", iter).def("__repr__", repr);
|
||||
iterator_wrapper<iterator_t, P, value_conv>().wrap(iter_name);
|
||||
}
|
||||
|
||||
typedef iterator_wrapper<iterator_t, P> iter_wrap;
|
||||
typedef iterator_wrapper<iterator_t, P, value_conv> iter_wrap;
|
||||
};
|
||||
|
||||
#define WRAP_RANGE(t) range_wrapper<t##Range>().wrap(#t "Range", #t "Iterator")
|
||||
#define WRAP_RANGE(t, conv) \
|
||||
range_wrapper<t##Range, return_value_policy<return_by_value>, conv>().wrap(#t "Range", #t "Iterator")
|
||||
|
||||
/*
|
||||
Wrapper for a pair, allows accessing either using C++-style members (.first and
|
||||
@ -158,20 +188,23 @@ template <typename T1, typename T2> struct pair_wrapper
|
||||
/*
|
||||
Special case of above for map key/values
|
||||
*/
|
||||
template <typename T1, typename T2> struct map_pair_wrapper
|
||||
template <typename T1, typename T2, typename value_conv> struct map_pair_wrapper
|
||||
{
|
||||
typedef std::pair<T1, T2> T;
|
||||
typedef PythonConversion::ContextualWrapper<T &> wrapped_pair;
|
||||
typedef typename T::second_type V;
|
||||
|
||||
struct pair_iterator_wrapper
|
||||
{
|
||||
static object next(std::pair<T &, int> &iter)
|
||||
static object next(std::pair<wrapped_pair &, int> &iter)
|
||||
{
|
||||
if (iter.second == 0) {
|
||||
iter.second++;
|
||||
return object(iter.first.first);
|
||||
return object(PythonConversion::string_converter<typeof(iter.first.base.first)>().to_str(
|
||||
iter.first.ctx, iter.first.base.first));
|
||||
} else if (iter.second == 1) {
|
||||
iter.second++;
|
||||
return object(iter.first.second);
|
||||
return object(value_conv()(iter.first.ctx, iter.first.base.second));
|
||||
} else {
|
||||
PyErr_SetString(PyExc_StopIteration, "End of range reached");
|
||||
boost::python::throw_error_already_set();
|
||||
@ -183,30 +216,37 @@ template <typename T1, typename T2> struct map_pair_wrapper
|
||||
|
||||
static void wrap(const char *python_name)
|
||||
{
|
||||
class_<std::pair<T &, int>>(python_name, no_init).def("__next__", next);
|
||||
class_<std::pair<wrapped_pair &, int>>(python_name, no_init).def("__next__", next);
|
||||
}
|
||||
};
|
||||
|
||||
static object get(T &x, int i)
|
||||
static object get(wrapped_pair &x, int i)
|
||||
{
|
||||
if ((i >= 2) || (i < 0))
|
||||
KeyError();
|
||||
return (i == 1) ? object(x.second) : object(x.first);
|
||||
return (i == 1) ? object(value_conv()(x.ctx, x.base.second)) : object(x.base.first);
|
||||
}
|
||||
|
||||
static int len(T &x) { return 2; }
|
||||
static int len(wrapped_pair &x) { return 2; }
|
||||
|
||||
static std::pair<T &, int> iter(T &x) { return std::make_pair(boost::ref(x), 0); };
|
||||
static std::pair<wrapped_pair &, int> iter(wrapped_pair &x) { return std::make_pair(boost::ref(x), 0); };
|
||||
|
||||
static std::string first_getter(wrapped_pair &t)
|
||||
{
|
||||
return PythonConversion::string_converter<typeof(t.base.first)>().to_str(t.ctx, t.base.first);
|
||||
}
|
||||
|
||||
static typename value_conv::ret_type second_getter(wrapped_pair &t) { return value_conv()(t.ctx, t.base.second); }
|
||||
|
||||
static void wrap(const char *pair_name, const char *iter_name)
|
||||
{
|
||||
pair_iterator_wrapper::wrap(iter_name);
|
||||
class_<T>(pair_name, no_init)
|
||||
class_<wrapped_pair>(pair_name, no_init)
|
||||
.def("__iter__", iter)
|
||||
.def("__len__", len)
|
||||
.def("__getitem__", get)
|
||||
.def_readonly("first", &T::first)
|
||||
.def_readwrite("second", &T::second);
|
||||
.add_property("first", first_getter)
|
||||
.add_property("second", second_getter);
|
||||
}
|
||||
};
|
||||
|
||||
@ -214,26 +254,35 @@ template <typename T1, typename T2> struct map_pair_wrapper
|
||||
Wrapper for a map, either an unordered_map, regular map or dict
|
||||
*/
|
||||
|
||||
template <typename T> struct map_wrapper
|
||||
template <typename T, typename value_conv> struct map_wrapper
|
||||
{
|
||||
typedef typename std::remove_cv<typename std::remove_reference<typename T::key_type>::type>::type K;
|
||||
typedef typename T::mapped_type V;
|
||||
typedef typename value_conv::ret_type wrapped_V;
|
||||
typedef typename T::value_type KV;
|
||||
typedef typename PythonConversion::ContextualWrapper<T &> wrapped_map;
|
||||
|
||||
static V &get(T &x, K const &i)
|
||||
static wrapped_V get(wrapped_map &x, std::string const &i)
|
||||
{
|
||||
if (x.find(i) != x.end())
|
||||
return x.at(i);
|
||||
K k = PythonConversion::string_converter<K>().from_str(x.ctx, i);
|
||||
if (x.base.find(k) != x.base.end())
|
||||
return value_conv()(x.ctx, x.base.at(k));
|
||||
KeyError();
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
static void set(T &x, K const &i, V &v) { x[i] = v; }
|
||||
|
||||
static void del(T const &x, K const &i)
|
||||
static void set(wrapped_map &x, std::string const &i, V const &v)
|
||||
{
|
||||
if (x.find(i) != x.end())
|
||||
x.erase(i);
|
||||
x.base[PythonConversion::string_converter<K>().from_str(x.ctx, i)] = v;
|
||||
}
|
||||
|
||||
static size_t len(wrapped_map &x) { return x.base.size(); }
|
||||
|
||||
static void del(T const &x, std::string const &i)
|
||||
{
|
||||
K k = PythonConversion::string_converter<K>().from_str(x.ctx, i);
|
||||
if (x.base.find(k) != x.base.end())
|
||||
x.base.erase(k);
|
||||
else
|
||||
KeyError();
|
||||
std::terminate();
|
||||
@ -241,13 +290,13 @@ template <typename T> struct map_wrapper
|
||||
|
||||
static void wrap(const char *map_name, const char *kv_name, const char *kv_iter_name, const char *iter_name)
|
||||
{
|
||||
map_pair_wrapper<typename KV::first_type, typename KV::second_type>::wrap(kv_name, kv_iter_name);
|
||||
typedef range_wrapper<T, return_value_policy<copy_non_const_reference>> rw;
|
||||
map_pair_wrapper<typename KV::first_type, typename KV::second_type, value_conv>::wrap(kv_name, kv_iter_name);
|
||||
typedef range_wrapper<T &, return_value_policy<return_by_value>, PythonConversion::wrap_context<KV &>> rw;
|
||||
typename rw::iter_wrap().wrap(iter_name);
|
||||
class_<T>(map_name, no_init)
|
||||
class_<wrapped_map>(map_name, no_init)
|
||||
.def("__iter__", rw::iter)
|
||||
.def("__len__", &T::size)
|
||||
.def("__getitem__", get, return_internal_reference<>())
|
||||
.def("__len__", len)
|
||||
.def("__getitem__", get)
|
||||
.def("__setitem__", set, with_custodian_and_ward<1, 2>());
|
||||
}
|
||||
};
|
||||
@ -258,17 +307,20 @@ Special case of above for map key/values where value is a unique_ptr
|
||||
template <typename T1, typename T2> struct map_pair_wrapper_uptr
|
||||
{
|
||||
typedef std::pair<T1, T2> T;
|
||||
typedef PythonConversion::ContextualWrapper<T &> wrapped_pair;
|
||||
typedef typename T::second_type::element_type V;
|
||||
|
||||
struct pair_iterator_wrapper
|
||||
{
|
||||
static object next(std::pair<T &, int> &iter)
|
||||
static object next(std::pair<wrapped_pair &, int> &iter)
|
||||
{
|
||||
if (iter.second == 0) {
|
||||
iter.second++;
|
||||
return object(iter.first.first);
|
||||
return object(PythonConversion::string_converter<typeof(iter.first.base.first)>().to_str(
|
||||
iter.first.ctx, iter.first.base.first));
|
||||
} else if (iter.second == 1) {
|
||||
iter.second++;
|
||||
return object(iter.first.second.get());
|
||||
return object(PythonConversion::ContextualWrapper<V &>(iter.first.ctx, *iter.first.base.second.get()));
|
||||
} else {
|
||||
PyErr_SetString(PyExc_StopIteration, "End of range reached");
|
||||
boost::python::throw_error_already_set();
|
||||
@ -280,32 +332,41 @@ template <typename T1, typename T2> struct map_pair_wrapper_uptr
|
||||
|
||||
static void wrap(const char *python_name)
|
||||
{
|
||||
class_<std::pair<T &, int>>(python_name, no_init).def("__next__", next);
|
||||
class_<std::pair<wrapped_pair &, int>>(python_name, no_init).def("__next__", next);
|
||||
}
|
||||
};
|
||||
|
||||
static object get(T &x, int i)
|
||||
static object get(wrapped_pair &x, int i)
|
||||
{
|
||||
if ((i >= 2) || (i < 0))
|
||||
KeyError();
|
||||
return (i == 1) ? object(x.second.get()) : object(x.first);
|
||||
return (i == 1) ? object(PythonConversion::ContextualWrapper<V &>(x.ctx, *x.base.second.get()))
|
||||
: object(x.base.first);
|
||||
}
|
||||
|
||||
static int len(T &x) { return 2; }
|
||||
static int len(wrapped_pair &x) { return 2; }
|
||||
|
||||
static std::pair<T &, int> iter(T &x) { return std::make_pair(boost::ref(x), 0); };
|
||||
static std::pair<wrapped_pair &, int> iter(wrapped_pair &x) { return std::make_pair(boost::ref(x), 0); };
|
||||
|
||||
static V &second_getter(T &t) { return *t.second.get(); }
|
||||
static std::string first_getter(wrapped_pair &t)
|
||||
{
|
||||
return PythonConversion::string_converter<typeof(t.base.first)>().to_str(t.ctx, t.base.first);
|
||||
}
|
||||
|
||||
static PythonConversion::ContextualWrapper<V &> second_getter(wrapped_pair &t)
|
||||
{
|
||||
return PythonConversion::ContextualWrapper<V &>(t.ctx, *t.base.second.get());
|
||||
}
|
||||
|
||||
static void wrap(const char *pair_name, const char *iter_name)
|
||||
{
|
||||
pair_iterator_wrapper::wrap(iter_name);
|
||||
class_<T, boost::noncopyable>(pair_name, no_init)
|
||||
class_<wrapped_pair>(pair_name, no_init)
|
||||
.def("__iter__", iter)
|
||||
.def("__len__", len)
|
||||
.def("__getitem__", get)
|
||||
.def_readonly("first", &T::first)
|
||||
.add_property("second", make_function(second_getter, return_internal_reference<>()));
|
||||
.add_property("first", first_getter)
|
||||
.add_property("second", second_getter);
|
||||
}
|
||||
};
|
||||
|
||||
@ -317,22 +378,31 @@ template <typename T> struct map_wrapper_uptr
|
||||
{
|
||||
typedef typename std::remove_cv<typename std::remove_reference<typename T::key_type>::type>::type K;
|
||||
typedef typename T::mapped_type::pointer V;
|
||||
typedef typename T::mapped_type::element_type &Vr;
|
||||
typedef typename T::value_type KV;
|
||||
typedef typename PythonConversion::ContextualWrapper<T &> wrapped_map;
|
||||
|
||||
static V get(T &x, K const &i)
|
||||
static PythonConversion::ContextualWrapper<Vr> get(wrapped_map &x, std::string const &i)
|
||||
{
|
||||
if (x.find(i) != x.end())
|
||||
return x.at(i).get();
|
||||
K k = PythonConversion::string_converter<K>().from_str(x.ctx, i);
|
||||
if (x.base.find(k) != x.base.end())
|
||||
return PythonConversion::ContextualWrapper<Vr>(x.ctx, *x.base.at(k).get());
|
||||
KeyError();
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
static void set(T &x, K const &i, V const &v) { x[i] = typename T::mapped_type(v); }
|
||||
|
||||
static void del(T const &x, K const &i)
|
||||
static void set(wrapped_map &x, std::string const &i, V const &v)
|
||||
{
|
||||
if (x.find(i) != x.end())
|
||||
x.erase(i);
|
||||
x.base[PythonConversion::string_converter<K>().from_str(x.ctx, i)] = typename T::mapped_type(v);
|
||||
}
|
||||
|
||||
static size_t len(wrapped_map &x) { return x.base.size(); }
|
||||
|
||||
static void del(T const &x, std::string const &i)
|
||||
{
|
||||
K k = PythonConversion::string_converter<K>().from_str(x.ctx, i);
|
||||
if (x.base.find(k) != x.base.end())
|
||||
x.base.erase(k);
|
||||
else
|
||||
KeyError();
|
||||
std::terminate();
|
||||
@ -341,17 +411,18 @@ template <typename T> struct map_wrapper_uptr
|
||||
static void wrap(const char *map_name, const char *kv_name, const char *kv_iter_name, const char *iter_name)
|
||||
{
|
||||
map_pair_wrapper_uptr<typename KV::first_type, typename KV::second_type>::wrap(kv_name, kv_iter_name);
|
||||
typedef range_wrapper<T, return_internal_reference<>> rw;
|
||||
typedef range_wrapper<T &, return_value_policy<return_by_value>, PythonConversion::wrap_context<KV &>> rw;
|
||||
typename rw::iter_wrap().wrap(iter_name);
|
||||
class_<T, boost::noncopyable>(map_name, no_init)
|
||||
class_<wrapped_map>(map_name, no_init)
|
||||
.def("__iter__", rw::iter)
|
||||
.def("__len__", &T::size)
|
||||
.def("__getitem__", get, return_internal_reference<>())
|
||||
.def("__len__", len)
|
||||
.def("__getitem__", get)
|
||||
.def("__setitem__", set, with_custodian_and_ward<1, 2>());
|
||||
}
|
||||
};
|
||||
|
||||
#define WRAP_MAP(t, name) map_wrapper<t>().wrap(#name, #name "KeyValue", #name "KeyValueIter", #name "Iterator")
|
||||
#define WRAP_MAP(t, conv, name) \
|
||||
map_wrapper<t, conv>().wrap(#name, #name "KeyValue", #name "KeyValueIter", #name "Iterator")
|
||||
#define WRAP_MAP_UPTR(t, name) \
|
||||
map_wrapper_uptr<t>().wrap(#name, #name "KeyValue", #name "KeyValueIter", #name "Iterator")
|
||||
|
||||
|
@ -41,65 +41,297 @@ template <typename T> struct ContextualWrapper
|
||||
Context *ctx;
|
||||
T base;
|
||||
|
||||
ContextualWrapper(Context *c, T &&x) : ctx(c), base(x){};
|
||||
inline ContextualWrapper(Context *c, T x) : ctx(c), base(x){};
|
||||
|
||||
operator T() { return base; };
|
||||
inline operator T() { return base; };
|
||||
typedef T base_type;
|
||||
};
|
||||
|
||||
template <typename T> struct WrapIfNotContext
|
||||
{
|
||||
typedef ContextualWrapper<T> maybe_wrapped_t;
|
||||
};
|
||||
|
||||
template <> struct WrapIfNotContext<Context>
|
||||
{
|
||||
typedef Context maybe_wrapped_t;
|
||||
};
|
||||
|
||||
template <typename T> inline Context *get_ctx(typename WrapIfNotContext<T>::maybe_wrapped_t &wrp_ctx)
|
||||
{
|
||||
return wrp_ctx.ctx;
|
||||
}
|
||||
|
||||
template <> inline Context *get_ctx<Context>(WrapIfNotContext<Context>::maybe_wrapped_t &unwrp_ctx)
|
||||
{
|
||||
return &unwrp_ctx;
|
||||
}
|
||||
|
||||
template <typename T> inline T &get_base(typename WrapIfNotContext<T>::maybe_wrapped_t &wrp_ctx)
|
||||
{
|
||||
return wrp_ctx.base;
|
||||
}
|
||||
|
||||
template <> inline Context &get_base<Context>(WrapIfNotContext<Context>::maybe_wrapped_t &unwrp_ctx)
|
||||
{
|
||||
return unwrp_ctx;
|
||||
}
|
||||
|
||||
template <typename T> ContextualWrapper<T> wrap_ctx(Context *ctx, T x) { return ContextualWrapper<T>(ctx, x); }
|
||||
|
||||
// Dummy class, to be implemented by users
|
||||
template <typename T> class string_converter;
|
||||
template <typename T> struct string_converter;
|
||||
|
||||
class bad_wrap
|
||||
{
|
||||
};
|
||||
|
||||
// Action options
|
||||
template <typename T> class do_nothing
|
||||
template <typename T> struct pass_through
|
||||
{
|
||||
T operator()(Context *ctx, T x) { return x; }
|
||||
inline T operator()(Context *ctx, T x) { return x; }
|
||||
|
||||
using ret_type = T;
|
||||
using arg_type = T;
|
||||
};
|
||||
|
||||
template <typename T> class wrap_context
|
||||
template <typename T> struct wrap_context
|
||||
{
|
||||
ContextualWrapper<T> operator()(Context *ctx, T x) { return ContextualWrapper<T>(ctx, x); }
|
||||
inline ContextualWrapper<T> operator()(Context *ctx, T x) { return ContextualWrapper<T>(ctx, x); }
|
||||
|
||||
using arg_type = T;
|
||||
using ret_type = ContextualWrapper<T>;
|
||||
};
|
||||
|
||||
template <typename T> class unwrap_context
|
||||
template <typename T> struct unwrap_context
|
||||
{
|
||||
T operator()(Context *ctx, ContextualWrapper<T> x) { return x.base; }
|
||||
inline T operator()(Context *ctx, ContextualWrapper<T> x) { return x.base; }
|
||||
|
||||
using ret_type = T;
|
||||
using arg_type = ContextualWrapper<T>;
|
||||
};
|
||||
|
||||
template <typename T> class conv_from_string
|
||||
template <typename T> struct conv_from_str
|
||||
{
|
||||
T operator()(Context *ctx, std::string x) { return string_converter<T>().from_str(ctx, x); }
|
||||
inline T operator()(Context *ctx, std::string x) { return string_converter<T>().from_str(ctx, x); }
|
||||
|
||||
using ret_type = T;
|
||||
using arg_type = std::string;
|
||||
};
|
||||
|
||||
template <typename T> class conv_to_string
|
||||
template <typename T> struct conv_to_str
|
||||
{
|
||||
std::string operator()(Context *ctx, T x) { return string_converter<T>().to_str(ctx, x); }
|
||||
inline std::string operator()(Context *ctx, T x) { return string_converter<T>().to_str(ctx, x); }
|
||||
|
||||
using ret_type = std::string;
|
||||
using arg_type = T;
|
||||
};
|
||||
|
||||
// Function wrapper
|
||||
// Example: one parameter, one return
|
||||
template <typename Class, typename FuncT, FuncT fn, typename rv_conv, typename arg1_conv> struct function_wrapper
|
||||
template <typename T> struct deref_and_wrap
|
||||
{
|
||||
using class_type = ContextualWrapper<Class>;
|
||||
inline ContextualWrapper<T &> operator()(Context *ctx, T *x)
|
||||
{
|
||||
if (x == nullptr)
|
||||
throw bad_wrap();
|
||||
return ContextualWrapper<T &>(ctx, *x);
|
||||
}
|
||||
|
||||
using arg_type = T *;
|
||||
using ret_type = ContextualWrapper<T &>;
|
||||
};
|
||||
|
||||
// Function wrapper
|
||||
// Zero parameters, one return
|
||||
template <typename Class, typename FuncT, FuncT fn, typename rv_conv> struct fn_wrapper_0a
|
||||
{
|
||||
using class_type = typename WrapIfNotContext<Class>::maybe_wrapped_t;
|
||||
using conv_result_type = typename rv_conv::ret_type;
|
||||
|
||||
static conv_result_type wrapped_fn(class_type &cls)
|
||||
{
|
||||
Context *ctx = get_ctx<Class>(cls);
|
||||
Class &base = get_base<Class>(cls);
|
||||
return rv_conv()(ctx, (base.*fn)());
|
||||
}
|
||||
|
||||
template <typename WrapCls> static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); }
|
||||
};
|
||||
|
||||
// One parameter, one return
|
||||
template <typename Class, typename FuncT, FuncT fn, typename rv_conv, typename arg1_conv> struct fn_wrapper_1a
|
||||
{
|
||||
using class_type = typename WrapIfNotContext<Class>::maybe_wrapped_t;
|
||||
using conv_result_type = typename rv_conv::ret_type;
|
||||
using conv_arg1_type = typename arg1_conv::arg_type;
|
||||
|
||||
static conv_result_type wrapped_fn(class_type &cls, conv_arg1_type arg1)
|
||||
{
|
||||
return rv_conv()(cls.ctx, cls.base.*fn(arg1_conv()(cls.ctx, arg1)));
|
||||
Context *ctx = get_ctx<Class>(cls);
|
||||
Class &base = get_base<Class>(cls);
|
||||
return rv_conv()(ctx, (base.*fn)(arg1_conv()(ctx, arg1)));
|
||||
}
|
||||
|
||||
template <typename WrapCls> static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); }
|
||||
};
|
||||
|
||||
// Two parameters, one return
|
||||
template <typename Class, typename FuncT, FuncT fn, typename rv_conv, typename arg1_conv, typename arg2_conv>
|
||||
struct fn_wrapper_2a
|
||||
{
|
||||
using class_type = typename WrapIfNotContext<Class>::maybe_wrapped_t;
|
||||
using conv_result_type = typename rv_conv::ret_type;
|
||||
using conv_arg1_type = typename arg1_conv::arg_type;
|
||||
using conv_arg2_type = typename arg2_conv::arg_type;
|
||||
|
||||
static conv_result_type wrapped_fn(class_type &cls, conv_arg1_type arg1, conv_arg2_type arg2)
|
||||
{
|
||||
Context *ctx = get_ctx<Class>(cls);
|
||||
Class &base = get_base<Class>(cls);
|
||||
return rv_conv()(ctx, (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2)));
|
||||
}
|
||||
|
||||
template <typename WrapCls> static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); }
|
||||
};
|
||||
|
||||
// Three parameters, one return
|
||||
template <typename Class, typename FuncT, FuncT fn, typename rv_conv, typename arg1_conv, typename arg2_conv,
|
||||
typename arg3_conv>
|
||||
struct fn_wrapper_3a
|
||||
{
|
||||
using class_type = typename WrapIfNotContext<Class>::maybe_wrapped_t;
|
||||
using conv_result_type = typename rv_conv::ret_type;
|
||||
using conv_arg1_type = typename arg1_conv::arg_type;
|
||||
using conv_arg2_type = typename arg2_conv::arg_type;
|
||||
using conv_arg3_type = typename arg3_conv::arg_type;
|
||||
|
||||
static conv_result_type wrapped_fn(class_type &cls, conv_arg1_type arg1, conv_arg2_type arg2, conv_arg3_type arg3)
|
||||
{
|
||||
Context *ctx = get_ctx<Class>(cls);
|
||||
Class &base = get_base<Class>(cls);
|
||||
return rv_conv()(ctx, (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2), arg3_conv()(ctx, arg3)));
|
||||
}
|
||||
|
||||
template <typename WrapCls> static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); }
|
||||
};
|
||||
|
||||
// Zero parameters void
|
||||
template <typename Class, typename FuncT, FuncT fn> struct fn_wrapper_0a_v
|
||||
{
|
||||
using class_type = typename WrapIfNotContext<Class>::maybe_wrapped_t;
|
||||
|
||||
static void wrapped_fn(class_type &cls)
|
||||
{
|
||||
Class &base = get_base<Class>(cls);
|
||||
return (base.*fn)();
|
||||
}
|
||||
|
||||
template <typename WrapCls> static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); }
|
||||
};
|
||||
|
||||
// One parameter, void
|
||||
template <typename Class, typename FuncT, FuncT fn, typename arg1_conv> struct fn_wrapper_1a_v
|
||||
{
|
||||
using class_type = typename WrapIfNotContext<Class>::maybe_wrapped_t;
|
||||
using conv_arg1_type = typename arg1_conv::arg_type;
|
||||
|
||||
static void wrapped_fn(class_type &cls, conv_arg1_type arg1)
|
||||
{
|
||||
Context *ctx = get_ctx<Class>(cls);
|
||||
Class &base = get_base<Class>(cls);
|
||||
return (base.*fn)(arg1_conv()(ctx, arg1));
|
||||
}
|
||||
|
||||
template <typename WrapCls> static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); }
|
||||
};
|
||||
|
||||
// Two parameters, one return
|
||||
template <typename Class, typename FuncT, FuncT fn, typename arg1_conv, typename arg2_conv> struct fn_wrapper_2a_v
|
||||
{
|
||||
using class_type = typename WrapIfNotContext<Class>::maybe_wrapped_t;
|
||||
using conv_arg1_type = typename arg1_conv::arg_type;
|
||||
using conv_arg2_type = typename arg2_conv::arg_type;
|
||||
|
||||
static void wrapped_fn(class_type &cls, conv_arg1_type arg1, conv_arg2_type arg2)
|
||||
{
|
||||
Context *ctx = get_ctx<Class>(cls);
|
||||
Class &base = get_base<Class>(cls);
|
||||
return (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2));
|
||||
}
|
||||
|
||||
template <typename WrapCls> static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); }
|
||||
};
|
||||
|
||||
// Three parameters, one return
|
||||
template <typename Class, typename FuncT, FuncT fn, typename arg1_conv, typename arg2_conv, typename arg3_conv>
|
||||
struct fn_wrapper_3a_v
|
||||
{
|
||||
using class_type = typename WrapIfNotContext<Class>::maybe_wrapped_t;
|
||||
using conv_arg1_type = typename arg1_conv::arg_type;
|
||||
using conv_arg2_type = typename arg2_conv::arg_type;
|
||||
using conv_arg3_type = typename arg3_conv::arg_type;
|
||||
|
||||
static void wrapped_fn(class_type &cls, conv_arg1_type arg1, conv_arg2_type arg2, conv_arg3_type arg3)
|
||||
{
|
||||
Context *ctx = get_ctx<Class>(cls);
|
||||
Class &base = get_base<Class>(cls);
|
||||
return (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2), arg3_conv()(ctx, arg3));
|
||||
}
|
||||
|
||||
template <typename WrapCls> static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); }
|
||||
};
|
||||
|
||||
// Wrapped getter
|
||||
template <typename Class, typename MemT, MemT mem, typename v_conv> struct readonly_wrapper
|
||||
{
|
||||
using class_type = typename WrapIfNotContext<Class>::maybe_wrapped_t;
|
||||
using conv_val_type = typename v_conv::ret_type;
|
||||
|
||||
static object wrapped_getter(class_type &cls)
|
||||
{
|
||||
Context *ctx = get_ctx<Class>(cls);
|
||||
Class &base = get_base<Class>(cls);
|
||||
try {
|
||||
return object(v_conv()(ctx, (base.*mem)));
|
||||
} catch (bad_wrap &) {
|
||||
return object();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename WrapCls> static void def_wrap(WrapCls cls_, const char *name)
|
||||
{
|
||||
cls_.add_property(name, wrapped_getter);
|
||||
}
|
||||
};
|
||||
|
||||
// Wrapped getter/setter
|
||||
template <typename Class, typename MemT, MemT mem, typename get_conv, typename set_conv> struct readwrite_wrapper
|
||||
{
|
||||
using class_type = typename WrapIfNotContext<Class>::maybe_wrapped_t;
|
||||
using conv_val_type = typename get_conv::ret_type;
|
||||
|
||||
static object wrapped_getter(class_type &cls)
|
||||
{
|
||||
Context *ctx = get_ctx<Class>(cls);
|
||||
Class &base = get_base<Class>(cls);
|
||||
try {
|
||||
return object(get_conv()(ctx, (base.*mem)));
|
||||
} catch (bad_wrap &) {
|
||||
return object();
|
||||
}
|
||||
}
|
||||
|
||||
using conv_arg_type = typename set_conv::arg_type;
|
||||
|
||||
static void wrapped_setter(class_type &cls, conv_arg_type val)
|
||||
{
|
||||
Context *ctx = get_ctx<Class>(cls);
|
||||
Class &base = get_base<Class>(cls);
|
||||
(base.*mem) = set_conv()(ctx, val);
|
||||
}
|
||||
|
||||
template <typename WrapCls> static void def_wrap(WrapCls cls_, const char *name)
|
||||
{
|
||||
cls_.add_property(name, wrapped_getter, wrapped_setter);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -20,8 +20,8 @@
|
||||
|
||||
#ifndef NO_PYTHON
|
||||
|
||||
#include "pybindings.h"
|
||||
#include "nextpnr.h"
|
||||
#include "pybindings.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
31
generic/arch_pybindings.h
Normal file
31
generic/arch_pybindings.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef ARCH_PYBINDINGS_H
|
||||
#define ARCH_PYBINDINGS_H
|
||||
#ifndef NO_PYTHON
|
||||
|
||||
#include "nextpnr.h"
|
||||
#include "pybindings.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
#endif
|
||||
#endif
|
162
ice40/arch_pybindings.cc
Normal file
162
ice40/arch_pybindings.cc
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NO_PYTHON
|
||||
|
||||
#include "arch_pybindings.h"
|
||||
#include "nextpnr.h"
|
||||
#include "pybindings.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
void arch_wrap_python()
|
||||
{
|
||||
using namespace PythonConversion;
|
||||
class_<ArchArgs>("ArchArgs").def_readwrite("type", &ArchArgs::type);
|
||||
|
||||
enum_<decltype(std::declval<ArchArgs>().type)>("iCE40Type")
|
||||
.value("NONE", ArchArgs::NONE)
|
||||
.value("LP384", ArchArgs::LP384)
|
||||
.value("LP1K", ArchArgs::LP1K)
|
||||
.value("LP8K", ArchArgs::LP8K)
|
||||
.value("HX1K", ArchArgs::HX1K)
|
||||
.value("HX8K", ArchArgs::HX8K)
|
||||
.value("UP5K", ArchArgs::UP5K)
|
||||
.export_values();
|
||||
|
||||
class_<BelId>("BelId").def_readwrite("index", &BelId::index);
|
||||
|
||||
class_<WireId>("WireId").def_readwrite("index", &WireId::index);
|
||||
|
||||
class_<PipId>("PipId").def_readwrite("index", &PipId::index);
|
||||
|
||||
class_<BelPin>("BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin);
|
||||
|
||||
enum_<PortPin>("PortPin")
|
||||
#define X(t) .value("PIN_" #t, PIN_##t)
|
||||
|
||||
#include "portpins.inc"
|
||||
;
|
||||
#undef X
|
||||
|
||||
auto arch_cls = class_<Arch, Arch *, bases<BaseCtx>, boost::noncopyable>("Arch", init<ArchArgs>());
|
||||
auto ctx_cls = class_<Context, Context *, bases<Arch>, boost::noncopyable>("Context", no_init)
|
||||
.def("checksum", &Context::checksum);
|
||||
|
||||
fn_wrapper_1a<Context, typeof(&Context::getBelType), &Context::getBelType, conv_to_str<BelType>,
|
||||
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelType");
|
||||
fn_wrapper_1a<Context, typeof(&Context::checkBelAvail), &Context::checkBelAvail, pass_through<bool>,
|
||||
conv_from_str<BelId>>::def_wrap(ctx_cls, "checkBelAvail");
|
||||
fn_wrapper_1a<Context, typeof(&Context::getBelChecksum), &Context::getBelChecksum, pass_through<uint32_t>,
|
||||
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelChecksum");
|
||||
fn_wrapper_3a_v<Context, typeof(&Context::bindBel), &Context::bindBel, conv_from_str<BelId>,
|
||||
conv_from_str<IdString>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindBel");
|
||||
fn_wrapper_1a_v<Context, typeof(&Context::unbindBel), &Context::unbindBel, conv_from_str<BelId>>::def_wrap(
|
||||
ctx_cls, "unbindBel");
|
||||
fn_wrapper_1a<Context, typeof(&Context::getBoundBelCell), &Context::getBoundBelCell, conv_to_str<IdString>,
|
||||
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBoundBelCell");
|
||||
fn_wrapper_1a<Context, typeof(&Context::getConflictingBelCell), &Context::getConflictingBelCell,
|
||||
conv_to_str<IdString>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell");
|
||||
fn_wrapper_0a<Context, typeof(&Context::getBels), &Context::getBels, wrap_context<BelRange>>::def_wrap(ctx_cls,
|
||||
"getBels");
|
||||
fn_wrapper_1a<Context, typeof(&Context::getBelsAtSameTile), &Context::getBelsAtSameTile, wrap_context<BelRange>,
|
||||
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelsAtSameTile");
|
||||
|
||||
fn_wrapper_2a<Context, typeof(&Context::getWireBelPin), &Context::getWireBelPin, conv_to_str<WireId>,
|
||||
conv_from_str<BelId>, conv_from_str<PortPin>>::def_wrap(ctx_cls, "getWireBelPin");
|
||||
fn_wrapper_1a<Context, typeof(&Context::getBelPinUphill), &Context::getBelPinUphill, wrap_context<BelPin>,
|
||||
conv_from_str<WireId>>::def_wrap(ctx_cls, "getBelPinUphill");
|
||||
fn_wrapper_1a<Context, typeof(&Context::getBelPinsDownhill), &Context::getBelPinsDownhill,
|
||||
wrap_context<BelPinRange>, conv_from_str<WireId>>::def_wrap(ctx_cls, "getBelPinsDownhill");
|
||||
|
||||
fn_wrapper_1a<Context, typeof(&Context::getWireChecksum), &Context::getWireChecksum, pass_through<uint32_t>,
|
||||
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireChecksum");
|
||||
fn_wrapper_3a_v<Context, typeof(&Context::bindWire), &Context::bindWire, conv_from_str<WireId>,
|
||||
conv_from_str<IdString>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindWire");
|
||||
fn_wrapper_1a_v<Context, typeof(&Context::unbindWire), &Context::unbindWire, conv_from_str<WireId>>::def_wrap(
|
||||
ctx_cls, "unbindWire");
|
||||
fn_wrapper_1a<Context, typeof(&Context::checkWireAvail), &Context::checkWireAvail, pass_through<bool>,
|
||||
conv_from_str<WireId>>::def_wrap(ctx_cls, "checkWireAvail");
|
||||
fn_wrapper_1a<Context, typeof(&Context::getBoundWireNet), &Context::getBoundWireNet, conv_to_str<IdString>,
|
||||
conv_from_str<WireId>>::def_wrap(ctx_cls, "getBoundWireNet");
|
||||
fn_wrapper_1a<Context, typeof(&Context::getConflictingWireNet), &Context::getConflictingWireNet,
|
||||
conv_to_str<IdString>, conv_from_str<WireId>>::def_wrap(ctx_cls, "getConflictingWireNet");
|
||||
|
||||
fn_wrapper_0a<Context, typeof(&Context::getWires), &Context::getWires, wrap_context<WireRange>>::def_wrap(
|
||||
ctx_cls, "getWires");
|
||||
|
||||
fn_wrapper_0a<Context, typeof(&Context::getPips), &Context::getPips, wrap_context<AllPipRange>>::def_wrap(
|
||||
ctx_cls, "getPips");
|
||||
fn_wrapper_1a<Context, typeof(&Context::getPipChecksum), &Context::getPipChecksum, pass_through<uint32_t>,
|
||||
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipChecksum");
|
||||
fn_wrapper_3a_v<Context, typeof(&Context::bindPip), &Context::bindPip, conv_from_str<PipId>,
|
||||
conv_from_str<IdString>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindPip");
|
||||
fn_wrapper_1a_v<Context, typeof(&Context::unbindPip), &Context::unbindPip, conv_from_str<PipId>>::def_wrap(
|
||||
ctx_cls, "unbindPip");
|
||||
fn_wrapper_1a<Context, typeof(&Context::checkPipAvail), &Context::checkPipAvail, pass_through<bool>,
|
||||
conv_from_str<PipId>>::def_wrap(ctx_cls, "checkPipAvail");
|
||||
fn_wrapper_1a<Context, typeof(&Context::getBoundPipNet), &Context::getBoundPipNet, conv_to_str<IdString>,
|
||||
conv_from_str<PipId>>::def_wrap(ctx_cls, "getBoundPipNet");
|
||||
fn_wrapper_1a<Context, typeof(&Context::getConflictingPipNet), &Context::getConflictingPipNet,
|
||||
conv_to_str<IdString>, conv_from_str<PipId>>::def_wrap(ctx_cls, "getConflictingPipNet");
|
||||
|
||||
fn_wrapper_1a<Context, typeof(&Context::getPipsDownhill), &Context::getPipsDownhill, wrap_context<PipRange>,
|
||||
conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsDownhill");
|
||||
fn_wrapper_1a<Context, typeof(&Context::getPipsUphill), &Context::getPipsUphill, wrap_context<PipRange>,
|
||||
conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsUphill");
|
||||
fn_wrapper_1a<Context, typeof(&Context::getWireAliases), &Context::getWireAliases, wrap_context<PipRange>,
|
||||
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireAliases");
|
||||
|
||||
fn_wrapper_1a<Context, typeof(&Context::getPipSrcWire), &Context::getPipSrcWire, conv_to_str<WireId>,
|
||||
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipSrcWire");
|
||||
fn_wrapper_1a<Context, typeof(&Context::getPipDstWire), &Context::getPipDstWire, conv_to_str<WireId>,
|
||||
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDstWire");
|
||||
fn_wrapper_1a<Context, typeof(&Context::getPipDelay), &Context::getPipDelay, pass_through<DelayInfo>,
|
||||
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDelay");
|
||||
|
||||
fn_wrapper_1a<Context, typeof(&Context::getPackagePinBel), &Context::getPackagePinBel, conv_to_str<BelId>,
|
||||
pass_through<std::string>>::def_wrap(ctx_cls, "getPackagePinBel");
|
||||
fn_wrapper_1a<Context, typeof(&Context::getBelPackagePin), &Context::getBelPackagePin, pass_through<std::string>,
|
||||
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelPackagePin");
|
||||
|
||||
fn_wrapper_0a<Context, typeof(&Context::getChipName), &Context::getChipName, pass_through<std::string>>::def_wrap(
|
||||
ctx_cls, "getChipName");
|
||||
fn_wrapper_0a<Context, typeof(&Context::archId), &Context::archId, conv_to_str<IdString>>::def_wrap(ctx_cls,
|
||||
"archId");
|
||||
|
||||
typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
|
||||
typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
|
||||
|
||||
readonly_wrapper<Context, typeof(&Context::cells), &Context::cells, wrap_context<CellMap &>>::def_wrap(ctx_cls,
|
||||
"cells");
|
||||
readonly_wrapper<Context, typeof(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls,
|
||||
"nets");
|
||||
WRAP_RANGE(Bel, conv_to_str<BelId>);
|
||||
WRAP_RANGE(Wire, conv_to_str<WireId>);
|
||||
WRAP_RANGE(AllPip, conv_to_str<PipId>);
|
||||
WRAP_RANGE(Pip, conv_to_str<PipId>);
|
||||
|
||||
WRAP_MAP_UPTR(CellMap, "IdCellMap");
|
||||
WRAP_MAP_UPTR(NetMap, "IdNetMap");
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif // NO_PYTHON
|
76
ice40/arch_pybindings.h
Normal file
76
ice40/arch_pybindings.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#ifndef ARCH_PYBINDINGS_H
|
||||
#define ARCH_PYBINDINGS_H
|
||||
#ifndef NO_PYTHON
|
||||
|
||||
#include "nextpnr.h"
|
||||
#include "pybindings.h"
|
||||
#include "pywrappers.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
namespace PythonConversion {
|
||||
|
||||
template <> struct string_converter<BelId>
|
||||
{
|
||||
BelId from_str(Context *ctx, std::string name) { return ctx->getBelByName(ctx->id(name)); }
|
||||
|
||||
std::string to_str(Context *ctx, BelId id)
|
||||
{
|
||||
if (id == BelId())
|
||||
throw bad_wrap();
|
||||
return ctx->getBelName(id).str(ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct string_converter<BelType>
|
||||
{
|
||||
BelType from_str(Context *ctx, std::string name) { return ctx->belTypeFromId(ctx->id(name)); }
|
||||
|
||||
std::string to_str(Context *ctx, BelType typ) { return ctx->belTypeToId(typ).str(ctx); }
|
||||
};
|
||||
|
||||
template <> struct string_converter<WireId>
|
||||
{
|
||||
WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); }
|
||||
|
||||
std::string to_str(Context *ctx, WireId id) { return ctx->getWireName(id).str(ctx); }
|
||||
};
|
||||
|
||||
template <> struct string_converter<PipId>
|
||||
{
|
||||
PipId from_str(Context *ctx, std::string name) { return ctx->getPipByName(ctx->id(name)); }
|
||||
|
||||
std::string to_str(Context *ctx, PipId id) { return ctx->getPipName(id).str(ctx); }
|
||||
};
|
||||
|
||||
template <> struct string_converter<PortPin>
|
||||
{
|
||||
PortPin from_str(Context *ctx, std::string name) { return ctx->portPinFromId(ctx->id(name)); }
|
||||
|
||||
std::string to_str(Context *ctx, PortPin id) { return ctx->portPinToId(id).str(ctx); }
|
||||
};
|
||||
|
||||
} // namespace PythonConversion
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
#endif
|
||||
#endif
|
@ -1,88 +0,0 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
|
||||
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef NO_PYTHON
|
||||
|
||||
#include "pybindings.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
void arch_wrap_python()
|
||||
{
|
||||
class_<ArchArgs>("ArchArgs").def_readwrite("type", &ArchArgs::type);
|
||||
|
||||
enum_<decltype(std::declval<ArchArgs>().type)>("iCE40Type")
|
||||
.value("NONE", ArchArgs::NONE)
|
||||
.value("LP384", ArchArgs::LP384)
|
||||
.value("LP1K", ArchArgs::LP1K)
|
||||
.value("LP8K", ArchArgs::LP8K)
|
||||
.value("HX1K", ArchArgs::HX1K)
|
||||
.value("HX8K", ArchArgs::HX8K)
|
||||
.value("UP5K", ArchArgs::UP5K)
|
||||
.export_values();
|
||||
|
||||
class_<BelId>("BelId").def_readwrite("index", &BelId::index);
|
||||
|
||||
class_<WireId>("WireId").def_readwrite("index", &WireId::index);
|
||||
|
||||
class_<PipId>("PipId").def_readwrite("index", &PipId::index);
|
||||
|
||||
class_<BelPin>("BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin);
|
||||
|
||||
enum_<PortPin>("PortPin")
|
||||
#define X(t) .value("PIN_" #t, PIN_##t)
|
||||
#include "portpins.inc"
|
||||
;
|
||||
#undef X
|
||||
|
||||
class_<Arch, Arch *, bases<BaseCtx>, boost::noncopyable>("Arch", init<ArchArgs>())
|
||||
.def("getBelByName", &Arch::getBelByName)
|
||||
.def("getWireByName", &Arch::getWireByName)
|
||||
.def("getBelName", &Arch::getBelName)
|
||||
.def("getWireName", &Arch::getWireName)
|
||||
.def("getBels", &Arch::getBels)
|
||||
.def("getBelType", &Arch::getBelType)
|
||||
.def("getWireBelPin", &Arch::getWireBelPin)
|
||||
.def("getBelPinUphill", &Arch::getBelPinUphill)
|
||||
.def("getBelPinsDownhill", &Arch::getBelPinsDownhill)
|
||||
.def("getWires", &Arch::getWires)
|
||||
.def("getPipByName", &Arch::getPipByName)
|
||||
.def("getPipName", &Arch::getPipName)
|
||||
.def("getPips", &Arch::getPips)
|
||||
.def("getPipSrcWire", &Arch::getPipSrcWire)
|
||||
.def("getPipDstWire", &Arch::getPipDstWire)
|
||||
.def("getPipDelay", &Arch::getPipDelay)
|
||||
.def("getPipsDownhill", &Arch::getPipsDownhill)
|
||||
.def("getPipsUphill", &Arch::getPipsUphill)
|
||||
.def("getWireAliases", &Arch::getWireAliases)
|
||||
.def("estimatePosition", &Arch::estimatePosition)
|
||||
.def("estimateDelay", &Arch::estimateDelay);
|
||||
|
||||
WRAP_RANGE(Bel);
|
||||
WRAP_RANGE(BelPin);
|
||||
WRAP_RANGE(Wire);
|
||||
WRAP_RANGE(AllPip);
|
||||
WRAP_RANGE(Pip);
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif // NO_PYTHON
|
@ -20,6 +20,6 @@ for cell, cinfo in sorted(ctx.cells, key=lambda x: x.first):
|
||||
val = "{}'b{}".format(len(val), val)
|
||||
print("\t\t{}: {}".format(param, val))
|
||||
|
||||
if cinfo.bel.index != -1:
|
||||
print("\tBel: {}".format(ctx.getBelName(cinfo.bel)))
|
||||
if cinfo.bel is not None:
|
||||
print("\tBel: {}".format(cinfo.bel))
|
||||
print()
|
||||
|
Loading…
Reference in New Issue
Block a user