diff --git a/common/nextpnr.cc b/common/nextpnr.cc index a197eaff..3861e5fe 100644 --- a/common/nextpnr.cc +++ b/common/nextpnr.cc @@ -27,8 +27,6 @@ assertion_failure::assertion_failure(std::string msg, std::string expr_str, std: { } -std::unordered_set IdString::global_ctx; - void IdString::set(const BaseCtx *ctx, const std::string &s) { auto it = ctx->idstring_str_to_idx->find(s); diff --git a/common/nextpnr.h b/common/nextpnr.h index 6a45875b..ec0c2f9f 100644 --- a/common/nextpnr.h +++ b/common/nextpnr.h @@ -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 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; idstring_idx_to_str = new std::vector; 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; } diff --git a/common/pybindings.cc b/common/pybindings.cc index e9ceca51..9bca307c 100644 --- a/common/pybindings.cc +++ b/common/pybindings.cc @@ -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(&translate_assertfail); + + using namespace PythonConversion; + class_("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").def_readwrite("cell", &PortRef::cell).def_readwrite("port", &PortRef::port); - - class_("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_>("PortRefVector").def(vector_indexing_suite>()); - enum_("PortType") .value("PORT_IN", PORT_IN) .value("PORT_OUT", PORT_OUT) .value("PORT_INOUT", PORT_INOUT) .export_values(); - class_("PortInfo") - .def_readwrite("name", &PortInfo::name) - .def_readwrite("net", &PortInfo::net) - .def_readwrite("type", &PortInfo::type); + typedef std::unordered_map AttrMap; + typedef std::unordered_map PortMap; + typedef std::unordered_map PinMap; - class_("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", no_init); - WRAP_MAP(decltype(CellInfo::ports), "IdPortMap"); - // WRAP_MAP(decltype(CellInfo::pins), "IdIdMap"); + auto ci_cls = class_>("CellInfo", no_init); + readwrite_wrapper, + conv_from_str>::def_wrap(ci_cls, "name"); + readwrite_wrapper, + conv_from_str>::def_wrap(ci_cls, "type"); + readonly_wrapper>::def_wrap( + ci_cls, "attrs"); + readonly_wrapper>::def_wrap( + ci_cls, "params"); + readonly_wrapper>::def_wrap( + ci_cls, "ports"); + readwrite_wrapper, + conv_from_str>::def_wrap(ci_cls, "bel"); + readwrite_wrapper, + pass_through>::def_wrap(ci_cls, "belStrength"); + readonly_wrapper>::def_wrap(ci_cls, + "pins"); - class_("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_>("PortInfo", no_init); + readwrite_wrapper, + conv_from_str>::def_wrap(pi_cls, "name"); + readonly_wrapper>::def_wrap(pi_cls, + "net"); + readwrite_wrapper, + pass_through>::def_wrap(pi_cls, "type"); - WRAP_MAP_UPTR(decltype(Context::nets), "IdNetMap"); - WRAP_MAP_UPTR(decltype(Context::cells), "IdCellMap"); + typedef std::vector PortVector; + typedef std::unordered_map WireMap; + + auto ni_cls = class_>("NetInfo", no_init); + readwrite_wrapper, + conv_from_str>::def_wrap(ni_cls, "name"); + readwrite_wrapper, + unwrap_context>::def_wrap(ni_cls, "driver"); + readonly_wrapper>::def_wrap( + ni_cls, "users"); + readonly_wrapper>::def_wrap(ni_cls, + "wires"); + + auto pr_cls = class_>("PortRef", no_init); + readonly_wrapper>::def_wrap(pr_cls, + "cell"); + readwrite_wrapper, + conv_from_str>::def_wrap(pr_cls, "port"); + readwrite_wrapper, + pass_through>::def_wrap(pr_cls, "budget"); def("parse_json", parse_json_shim); def("load_design", load_design_shim, return_value_policy()); - class_("IdString") - .def("__str__", &IdString::global_str, return_value_policy()) - .def(self < self) - .def(self == self); - arch_wrap_python(); + WRAP_MAP(AttrMap, pass_through, "AttrMap"); + WRAP_MAP(PortMap, wrap_context, "PortMap"); + WRAP_MAP(PinMap, conv_to_str, "PinMap"); - class_, boost::noncopyable>("Context", no_init).def("checksum", &Context::checksum); + arch_wrap_python(); } static wchar_t *program; diff --git a/common/pybindings.h b/common/pybindings.h index 1565a138..c4d84442 100644 --- a/common/pybindings.h +++ b/common/pybindings.h @@ -36,50 +36,6 @@ NEXTPNR_NAMESPACE_BEGIN using namespace boost::python; -/* -A wrapper to enable custom type/ID to/from string conversions - */ -template struct string_wrapper -{ - template struct from_pystring_converter - { - from_pystring_converter() - { - converter::registry::push_back(&convertible, &construct, boost::python::type_id()); - }; - - 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 *)data)->storage.bytes; - new (storage) T(fn(std::string(value_ws.begin(), value_ws.end()))); - data->convertible = storage; - } - - static F fn; - }; - - template struct to_str_wrapper - { - static F fn; - - std::string str(T &x) { return fn(x); } - }; - - template static void wrap(const char *type_name, F1 to_str_fn, F2 from_str_fn) - { - from_pystring_converter::fn = from_str_fn; - from_pystring_converter(); - to_str_wrapper::fn = to_str_fn; - class_(type_name, no_init).def("__str__", to_str_wrapper::str); - }; -}; - std::string parse_python_exception(); template 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 +{ + 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 +{ + 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 */ diff --git a/common/pycontainers.h b/common/pycontainers.h index 917f49e9..f4251558 100644 --- a/common/pycontainers.h +++ b/common/pycontainers.h @@ -24,10 +24,12 @@ #include #include #include +#include #include #include #include #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 containing (current, end) +pair containing (current, end), wrapped in a ContextualWrapper + */ -template struct iterator_wrapper +template > struct iterator_wrapper { typedef decltype(*(std::declval())) value_t; - static value_t next(std::pair &iter) + typedef PythonConversion::ContextualWrapper> 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 struct iterator_wrapper static void wrap(const char *python_name) { - class_>(python_name, no_init).def("__next__", next, P()); + class_(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 > struct range_wrapper +template , + typename value_conv = PythonConversion::pass_through> +struct range_wrapper { typedef decltype(std::declval().begin()) iterator_t; + typedef decltype(*(std::declval())) value_t; + typedef typename PythonConversion::ContextualWrapper wrapped_range; + typedef typename PythonConversion::ContextualWrapper> 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 iter(T &range) { return std::make_pair(range.begin(), range.end()); } + static std::string repr(wrapped_range &range) + { + PythonConversion::string_converter 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_(range_name, no_init).def("__iter__", iter); - iterator_wrapper().wrap(iter_name); + class_(range_name, no_init).def("__iter__", iter).def("__repr__", repr); + iterator_wrapper().wrap(iter_name); } - typedef iterator_wrapper iter_wrap; + typedef iterator_wrapper iter_wrap; }; -#define WRAP_RANGE(t) range_wrapper().wrap(#t "Range", #t "Iterator") +#define WRAP_RANGE(t, conv) \ + range_wrapper, conv>().wrap(#t "Range", #t "Iterator") /* Wrapper for a pair, allows accessing either using C++-style members (.first and @@ -158,20 +188,23 @@ template struct pair_wrapper /* Special case of above for map key/values */ -template struct map_pair_wrapper +template struct map_pair_wrapper { typedef std::pair T; + typedef PythonConversion::ContextualWrapper wrapped_pair; + typedef typename T::second_type V; struct pair_iterator_wrapper { - static object next(std::pair &iter) + static object next(std::pair &iter) { if (iter.second == 0) { iter.second++; - return object(iter.first.first); + return object(PythonConversion::string_converter().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 struct map_pair_wrapper static void wrap(const char *python_name) { - class_>(python_name, no_init).def("__next__", next); + class_>(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 iter(T &x) { return std::make_pair(boost::ref(x), 0); }; + static std::pair iter(wrapped_pair &x) { return std::make_pair(boost::ref(x), 0); }; + + static std::string first_getter(wrapped_pair &t) + { + return PythonConversion::string_converter().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_(pair_name, no_init) + class_(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 struct map_pair_wrapper Wrapper for a map, either an unordered_map, regular map or dict */ -template struct map_wrapper +template struct map_wrapper { typedef typename std::remove_cv::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 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().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().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().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 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::wrap(kv_name, kv_iter_name); - typedef range_wrapper> rw; + map_pair_wrapper::wrap(kv_name, kv_iter_name); + typedef range_wrapper, PythonConversion::wrap_context> rw; typename rw::iter_wrap().wrap(iter_name); - class_(map_name, no_init) + class_(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 struct map_pair_wrapper_uptr { typedef std::pair T; + typedef PythonConversion::ContextualWrapper wrapped_pair; typedef typename T::second_type::element_type V; + struct pair_iterator_wrapper { - static object next(std::pair &iter) + static object next(std::pair &iter) { if (iter.second == 0) { iter.second++; - return object(iter.first.first); + return object(PythonConversion::string_converter().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(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 struct map_pair_wrapper_uptr static void wrap(const char *python_name) { - class_>(python_name, no_init).def("__next__", next); + class_>(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(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 iter(T &x) { return std::make_pair(boost::ref(x), 0); }; + static std::pair 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().to_str(t.ctx, t.base.first); + } + + static PythonConversion::ContextualWrapper second_getter(wrapped_pair &t) + { + return PythonConversion::ContextualWrapper(t.ctx, *t.base.second.get()); + } static void wrap(const char *pair_name, const char *iter_name) { pair_iterator_wrapper::wrap(iter_name); - class_(pair_name, no_init) + class_(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 struct map_wrapper_uptr { typedef typename std::remove_cv::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 wrapped_map; - static V get(T &x, K const &i) + static PythonConversion::ContextualWrapper get(wrapped_map &x, std::string const &i) { - if (x.find(i) != x.end()) - return x.at(i).get(); + K k = PythonConversion::string_converter().from_str(x.ctx, i); + if (x.base.find(k) != x.base.end()) + return PythonConversion::ContextualWrapper(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().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().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 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::wrap(kv_name, kv_iter_name); - typedef range_wrapper> rw; + typedef range_wrapper, PythonConversion::wrap_context> rw; typename rw::iter_wrap().wrap(iter_name); - class_(map_name, no_init) + class_(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().wrap(#name, #name "KeyValue", #name "KeyValueIter", #name "Iterator") +#define WRAP_MAP(t, conv, name) \ + map_wrapper().wrap(#name, #name "KeyValue", #name "KeyValueIter", #name "Iterator") #define WRAP_MAP_UPTR(t, name) \ map_wrapper_uptr().wrap(#name, #name "KeyValue", #name "KeyValueIter", #name "Iterator") diff --git a/common/pywrappers.h b/common/pywrappers.h index 2c91f26f..725caca8 100644 --- a/common/pywrappers.h +++ b/common/pywrappers.h @@ -41,65 +41,297 @@ template 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 struct WrapIfNotContext +{ + typedef ContextualWrapper maybe_wrapped_t; +}; + +template <> struct WrapIfNotContext +{ + typedef Context maybe_wrapped_t; +}; + +template inline Context *get_ctx(typename WrapIfNotContext::maybe_wrapped_t &wrp_ctx) +{ + return wrp_ctx.ctx; +} + +template <> inline Context *get_ctx(WrapIfNotContext::maybe_wrapped_t &unwrp_ctx) +{ + return &unwrp_ctx; +} + +template inline T &get_base(typename WrapIfNotContext::maybe_wrapped_t &wrp_ctx) +{ + return wrp_ctx.base; +} + +template <> inline Context &get_base(WrapIfNotContext::maybe_wrapped_t &unwrp_ctx) +{ + return unwrp_ctx; +} + template ContextualWrapper wrap_ctx(Context *ctx, T x) { return ContextualWrapper(ctx, x); } // Dummy class, to be implemented by users -template class string_converter; +template struct string_converter; + +class bad_wrap +{ +}; // Action options -template class do_nothing +template 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 class wrap_context +template struct wrap_context { - ContextualWrapper operator()(Context *ctx, T x) { return ContextualWrapper(ctx, x); } + inline ContextualWrapper operator()(Context *ctx, T x) { return ContextualWrapper(ctx, x); } + using arg_type = T; using ret_type = ContextualWrapper; }; -template class unwrap_context +template struct unwrap_context { - T operator()(Context *ctx, ContextualWrapper x) { return x.base; } + inline T operator()(Context *ctx, ContextualWrapper x) { return x.base; } + using ret_type = T; using arg_type = ContextualWrapper; }; -template class conv_from_string +template struct conv_from_str { - T operator()(Context *ctx, std::string x) { return string_converter().from_str(ctx, x); } + inline T operator()(Context *ctx, std::string x) { return string_converter().from_str(ctx, x); } + using ret_type = T; using arg_type = std::string; }; -template class conv_to_string +template struct conv_to_str { - std::string operator()(Context *ctx, T x) { return string_converter().to_str(ctx, x); } + inline std::string operator()(Context *ctx, T x) { return string_converter().to_str(ctx, x); } + using ret_type = std::string; using arg_type = T; }; -// Function wrapper -// Example: one parameter, one return -template struct function_wrapper +template struct deref_and_wrap { - using class_type = ContextualWrapper; + inline ContextualWrapper operator()(Context *ctx, T *x) + { + if (x == nullptr) + throw bad_wrap(); + return ContextualWrapper(ctx, *x); + } + + using arg_type = T *; + using ret_type = ContextualWrapper; +}; + +// Function wrapper +// Zero parameters, one return +template struct fn_wrapper_0a +{ + using class_type = typename WrapIfNotContext::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(cls); + Class &base = get_base(cls); + return rv_conv()(ctx, (base.*fn)()); + } + + template static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); } +}; + +// One parameter, one return +template struct fn_wrapper_1a +{ + using class_type = typename WrapIfNotContext::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(cls); + Class &base = get_base(cls); + return rv_conv()(ctx, (base.*fn)(arg1_conv()(ctx, arg1))); + } + + template static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); } +}; + +// Two parameters, one return +template +struct fn_wrapper_2a +{ + using class_type = typename WrapIfNotContext::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(cls); + Class &base = get_base(cls); + return rv_conv()(ctx, (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2))); + } + + template static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); } +}; + +// Three parameters, one return +template +struct fn_wrapper_3a +{ + using class_type = typename WrapIfNotContext::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(cls); + Class &base = get_base(cls); + return rv_conv()(ctx, (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2), arg3_conv()(ctx, arg3))); + } + + template static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); } +}; + +// Zero parameters void +template struct fn_wrapper_0a_v +{ + using class_type = typename WrapIfNotContext::maybe_wrapped_t; + + static void wrapped_fn(class_type &cls) + { + Class &base = get_base(cls); + return (base.*fn)(); + } + + template static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); } +}; + +// One parameter, void +template struct fn_wrapper_1a_v +{ + using class_type = typename WrapIfNotContext::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(cls); + Class &base = get_base(cls); + return (base.*fn)(arg1_conv()(ctx, arg1)); + } + + template static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); } +}; + +// Two parameters, one return +template struct fn_wrapper_2a_v +{ + using class_type = typename WrapIfNotContext::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(cls); + Class &base = get_base(cls); + return (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2)); + } + + template static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); } +}; + +// Three parameters, one return +template +struct fn_wrapper_3a_v +{ + using class_type = typename WrapIfNotContext::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(cls); + Class &base = get_base(cls); + return (base.*fn)(arg1_conv()(ctx, arg1), arg2_conv()(ctx, arg2), arg3_conv()(ctx, arg3)); + } + + template static void def_wrap(WrapCls cls_, const char *name) { cls_.def(name, wrapped_fn); } +}; + +// Wrapped getter +template struct readonly_wrapper +{ + using class_type = typename WrapIfNotContext::maybe_wrapped_t; + using conv_val_type = typename v_conv::ret_type; + + static object wrapped_getter(class_type &cls) + { + Context *ctx = get_ctx(cls); + Class &base = get_base(cls); + try { + return object(v_conv()(ctx, (base.*mem))); + } catch (bad_wrap &) { + return object(); + } + } + + template static void def_wrap(WrapCls cls_, const char *name) + { + cls_.add_property(name, wrapped_getter); + } +}; + +// Wrapped getter/setter +template struct readwrite_wrapper +{ + using class_type = typename WrapIfNotContext::maybe_wrapped_t; + using conv_val_type = typename get_conv::ret_type; + + static object wrapped_getter(class_type &cls) + { + Context *ctx = get_ctx(cls); + Class &base = get_base(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(cls); + Class &base = get_base(cls); + (base.*mem) = set_conv()(ctx, val); + } + + template static void def_wrap(WrapCls cls_, const char *name) + { + cls_.add_property(name, wrapped_getter, wrapped_setter); } }; diff --git a/generic/pybindings.cc b/generic/arch_pybindings.cc similarity index 100% rename from generic/pybindings.cc rename to generic/arch_pybindings.cc index a2997456..a99723f2 100644 --- a/generic/pybindings.cc +++ b/generic/arch_pybindings.cc @@ -20,8 +20,8 @@ #ifndef NO_PYTHON -#include "pybindings.h" #include "nextpnr.h" +#include "pybindings.h" NEXTPNR_NAMESPACE_BEGIN diff --git a/generic/arch_pybindings.h b/generic/arch_pybindings.h new file mode 100644 index 00000000..f7f07529 --- /dev/null +++ b/generic/arch_pybindings.h @@ -0,0 +1,31 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 David Shah + * + * 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 diff --git a/ice40/arch_pybindings.cc b/ice40/arch_pybindings.cc new file mode 100644 index 00000000..4cbfc8af --- /dev/null +++ b/ice40/arch_pybindings.cc @@ -0,0 +1,162 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 David Shah + * + * 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").def_readwrite("type", &ArchArgs::type); + + enum_().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").def_readwrite("index", &BelId::index); + + class_("WireId").def_readwrite("index", &WireId::index); + + class_("PipId").def_readwrite("index", &PipId::index); + + class_("BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin); + + enum_("PortPin") +#define X(t) .value("PIN_" #t, PIN_##t) + +#include "portpins.inc" + ; +#undef X + + auto arch_cls = class_, boost::noncopyable>("Arch", init()); + auto ctx_cls = class_, boost::noncopyable>("Context", no_init) + .def("checksum", &Context::checksum); + + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBelType"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "checkBelAvail"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBelChecksum"); + fn_wrapper_3a_v, + conv_from_str, pass_through>::def_wrap(ctx_cls, "bindBel"); + fn_wrapper_1a_v>::def_wrap( + ctx_cls, "unbindBel"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBoundBelCell"); + fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getConflictingBelCell"); + fn_wrapper_0a>::def_wrap(ctx_cls, + "getBels"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBelsAtSameTile"); + + fn_wrapper_2a, + conv_from_str, conv_from_str>::def_wrap(ctx_cls, "getWireBelPin"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBelPinUphill"); + fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getBelPinsDownhill"); + + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getWireChecksum"); + fn_wrapper_3a_v, + conv_from_str, pass_through>::def_wrap(ctx_cls, "bindWire"); + fn_wrapper_1a_v>::def_wrap( + ctx_cls, "unbindWire"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "checkWireAvail"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBoundWireNet"); + fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getConflictingWireNet"); + + fn_wrapper_0a>::def_wrap( + ctx_cls, "getWires"); + + fn_wrapper_0a>::def_wrap( + ctx_cls, "getPips"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipChecksum"); + fn_wrapper_3a_v, + conv_from_str, pass_through>::def_wrap(ctx_cls, "bindPip"); + fn_wrapper_1a_v>::def_wrap( + ctx_cls, "unbindPip"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "checkPipAvail"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBoundPipNet"); + fn_wrapper_1a, conv_from_str>::def_wrap(ctx_cls, "getConflictingPipNet"); + + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipsDownhill"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipsUphill"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getWireAliases"); + + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipSrcWire"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipDstWire"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getPipDelay"); + + fn_wrapper_1a, + pass_through>::def_wrap(ctx_cls, "getPackagePinBel"); + fn_wrapper_1a, + conv_from_str>::def_wrap(ctx_cls, "getBelPackagePin"); + + fn_wrapper_0a>::def_wrap( + ctx_cls, "getChipName"); + fn_wrapper_0a>::def_wrap(ctx_cls, + "archId"); + + typedef std::unordered_map> CellMap; + typedef std::unordered_map> NetMap; + + readonly_wrapper>::def_wrap(ctx_cls, + "cells"); + readonly_wrapper>::def_wrap(ctx_cls, + "nets"); + WRAP_RANGE(Bel, conv_to_str); + WRAP_RANGE(Wire, conv_to_str); + WRAP_RANGE(AllPip, conv_to_str); + WRAP_RANGE(Pip, conv_to_str); + + WRAP_MAP_UPTR(CellMap, "IdCellMap"); + WRAP_MAP_UPTR(NetMap, "IdNetMap"); +} + +NEXTPNR_NAMESPACE_END + +#endif // NO_PYTHON diff --git a/ice40/arch_pybindings.h b/ice40/arch_pybindings.h new file mode 100644 index 00000000..e502905f --- /dev/null +++ b/ice40/arch_pybindings.h @@ -0,0 +1,76 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2018 Clifford Wolf + * Copyright (C) 2018 David Shah + * + * 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 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 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 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 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 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 diff --git a/ice40/pybindings.cc b/ice40/pybindings.cc deleted file mode 100644 index 3c3e2394..00000000 --- a/ice40/pybindings.cc +++ /dev/null @@ -1,88 +0,0 @@ -/* - * nextpnr -- Next Generation Place and Route - * - * Copyright (C) 2018 Clifford Wolf - * Copyright (C) 2018 David Shah - * - * 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").def_readwrite("type", &ArchArgs::type); - - enum_().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").def_readwrite("index", &BelId::index); - - class_("WireId").def_readwrite("index", &WireId::index); - - class_("PipId").def_readwrite("index", &PipId::index); - - class_("BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin); - - enum_("PortPin") -#define X(t) .value("PIN_" #t, PIN_##t) -#include "portpins.inc" - ; -#undef X - - class_, boost::noncopyable>("Arch", init()) - .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 diff --git a/python/dump_design.py b/python/dump_design.py index 37309f6f..419c7304 100644 --- a/python/dump_design.py +++ b/python/dump_design.py @@ -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()