Improving the Python bindings, particularly the map/pair wrappers

Signed-off-by: David Shah <davey1576@gmail.com>
This commit is contained in:
David Shah 2018-06-08 15:53:24 +02:00
parent 5fa79cd2b5
commit 57cd67dbc1
3 changed files with 176 additions and 21 deletions

View File

@ -21,10 +21,13 @@
#include "chip.h"
#include "design.h"
#include "emb.h"
#include "jsonparse.h"
// include after design.h/chip.h
#include "pybindings.h"
#include <fstream>
// Required to determine concatenated module name (which differs for different
// archs)
#define PASTER(x, y) x##_##y
@ -43,6 +46,24 @@ bool operator==(const PortRef &a, const PortRef &b)
return (a.cell == b.cell) && (a.port == b.port);
}
// Load a JSON file into a design
void parse_json_shim(std::string filename, Design &d)
{
std::ifstream inf(filename);
if (!inf)
throw std::runtime_error("failed to open file " + filename);
std::istream *ifp = &inf;
parse_json_file(ifp, filename, &d);
}
// Create a new Chip and load design from json file
Design load_design_shim(std::string filename, ChipArgs args)
{
Design d(args);
parse_json_shim(filename, d);
return d;
}
BOOST_PYTHON_MODULE(MODULE_NAME)
{
class_<GraphicElement>("GraphicElement")
@ -101,6 +122,9 @@ BOOST_PYTHON_MODULE(MODULE_NAME)
WRAP_MAP(decltype(Design::nets), "IdNetMap");
WRAP_MAP(decltype(Design::cells), "IdCellMap");
def("parse_json", parse_json_shim);
def("load_design", load_design_shim);
arch_wrap_python();
}

View File

@ -30,6 +30,8 @@
using namespace boost::python;
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)
@ -82,15 +84,145 @@ struct range_wrapper
class_<T>(range_name, no_init).def("__iter__", iter);
iterator_wrapper<iterator_t, P>().wrap(iter_name);
}
typedef iterator_wrapper<iterator_t, P> iter_wrap;
};
#define WRAP_RANGE(t) range_wrapper<t##Range>().wrap(#t "Range", #t "Iterator")
/*
Wrapper for a pair, allows accessing either using C++-style members (.first and
.second) or as a Python iterable and indexable object
*/
template <typename T1, typename T2> struct pair_wrapper
{
typedef std::pair<T1, T2> T;
struct pair_iterator_wrapper
{
static object next(std::pair<T &, int> &iter)
{
if (iter.second == 0) {
iter.second++;
return object(iter.first.first);
} else if (iter.second == 1) {
iter.second++;
return object(iter.first.second);
} else {
PyErr_SetString(PyExc_StopIteration, "End of range reached");
boost::python::throw_error_already_set();
// Should be unreachable, but prevent control may reach end of
// non-void
throw std::runtime_error("unreachable");
}
}
static void wrap(const char *python_name)
{
class_<std::pair<T &, int>>(python_name, no_init)
.def("__next__", next);
}
};
static object get(T &x, int i)
{
if ((i >= 2) || (i < 0))
KeyError();
return (i == 1) ? object(x.second) : object(x.first);
}
static void set(T &x, int i, object val)
{
if ((i >= 2) || (i < 0))
KeyError();
if (i == 0)
x.first = extract<T1>(val);
if (i == 1)
x.second = extract<T2>(val);
}
static int len(T &x) { return 2; }
static std::pair<T &, int> iter(T &x)
{
return std::make_pair(boost::ref(x), 0);
};
static void wrap(const char *pair_name, const char *iter_name)
{
pair_iterator_wrapper::wrap(iter_name);
class_<T>(pair_name, no_init)
.def("__iter__", iter)
.def("__len__", len)
.def("__getitem__", get)
.def("__setitem__", set, with_custodian_and_ward<1, 2>())
.def_readwrite("first", &T::first)
.def_readwrite("second", &T::second);
}
};
/*
Special case of above for map key/values
*/
template <typename T1, typename T2> struct map_pair_wrapper
{
typedef std::pair<T1, T2> T;
struct pair_iterator_wrapper
{
static object next(std::pair<T &, int> &iter)
{
if (iter.second == 0) {
iter.second++;
return object(iter.first.first);
} else if (iter.second == 1) {
iter.second++;
return object(iter.first.second);
} else {
PyErr_SetString(PyExc_StopIteration, "End of range reached");
boost::python::throw_error_already_set();
// Should be unreachable, but prevent control may reach end of
// non-void
throw std::runtime_error("unreachable");
}
}
static void wrap(const char *python_name)
{
class_<std::pair<T &, int>>(python_name, no_init)
.def("__next__", next);
}
};
static object get(T &x, int i)
{
if ((i >= 2) || (i < 0))
KeyError();
return (i == 1) ? object(x.second) : object(x.first);
}
static int len(T &x) { return 2; }
static std::pair<T &, int> iter(T &x)
{
return std::make_pair(boost::ref(x), 0);
};
static void wrap(const char *pair_name, const char *iter_name)
{
pair_iterator_wrapper::wrap(iter_name);
class_<T>(pair_name, no_init)
.def("__iter__", iter)
.def("__len__", len)
.def("__getitem__", get)
.def_readonly("first", &T::first)
.def_readwrite("second", &T::second);
}
};
/*
Wrapper for a map, either an unordered_map, regular map or dict
*/
inline void KeyError() { PyErr_SetString(PyExc_KeyError, "Key not found"); }
template <typename T> struct map_wrapper
{
@ -119,11 +251,10 @@ template <typename T> struct map_wrapper
}
static void wrap(const char *map_name, const char *kv_name,
const char *iter_name)
const char *kv_iter_name, const char *iter_name)
{
class_<KV>(kv_name)
.def_readonly("first", &KV::first)
.def_readwrite("second", &KV::second);
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;
typename rw::iter_wrap().wrap(iter_name);
@ -136,6 +267,7 @@ template <typename T> struct map_wrapper
};
#define WRAP_MAP(t, name) \
map_wrapper<t>().wrap(#name, #name "KeyValue", #name "Iterator")
map_wrapper<t>().wrap(#name, #name "KeyValue", #name "KeyValueIter", \
#name "Iterator")
#endif

View File

@ -1,26 +1,25 @@
# Run ./nextpnr-ice40 --json ice40/blinky.json --run python/dump_design.py
for cell in sorted(design.cells, key=lambda x: x.first):
print("Cell {} : {}".format(cell.first, cell.second.type))
for cell, cinfo in sorted(design.cells, key=lambda x: x.first):
print("Cell {} : {}".format(cell, cinfo.type))
print("\tPorts:")
for port in sorted(cell.second.ports, key=lambda x: x.first):
dir = (" <-- ", " --> ", " <-> ")[int(port.second.type)]
if port.second.net is not None:
print("\t\t{} {} {}".format(port.first, dir, port.second.net.name))
for port, pinfo in sorted(cinfo.ports, key=lambda x: x.first):
dir = (" <-- ", " --> ", " <-> ")[int(pinfo.type)]
if pinfo.net is not None:
print("\t\t{} {} {}".format(port, dir, pinfo.net.name))
if len(cell.second.attrs) > 0:
if len(cinfo.attrs) > 0:
print("\tAttrs:")
for attr in cell.second.attrs:
print("\t\t{}: {}".format(attr.first, attr.second))
for attr, val in cinfo.attrs:
print("\t\t{}: {}".format(attr, val))
if len(cell.second.params) > 0:
if len(cinfo.params) > 0:
print("\tParams:")
for param in cell.second.params:
val = param.second
for param, val in cinfo.params:
if val.isdigit():
val = bin(int(val))[2:]
val = "{}'b{}".format(len(val), val)
print("\t\t{}: {}".format(param.first, val))
print("\t\t{}: {}".format(param, val))
if not cell.second.bel.nil():
print("\tBel: {}".format(chip.getBelName(cell.second.bel)))
if not cinfo.bel.nil():
print("\tBel: {}".format(chip.getBelName(cinfo.bel)))
print()