aboutsummaryrefslogtreecommitdiffstats
path: root/misc
diff options
context:
space:
mode:
authorBenedikt Tutzer <benedikt.tutzer@tuwien.ac.at>2019-10-09 13:59:35 +0200
committerBenedikt Tutzer <benedikt.tutzer@tuwien.ac.at>2019-10-09 13:59:35 +0200
commit9c59a56aa495a9d73979c76b92440ad256ca0f13 (patch)
tree8a3263e052116c82358773a3e4af6c0a0d8d07ff /misc
parent3fb604c75d3e8ee45d35fac8b787cb95a8adcf84 (diff)
downloadyosys-9c59a56aa495a9d73979c76b92440ad256ca0f13.tar.gz
yosys-9c59a56aa495a9d73979c76b92440ad256ca0f13.tar.bz2
yosys-9c59a56aa495a9d73979c76b92440ad256ca0f13.zip
Expose global variables and allow logging to python streams
Global variables are now accessible via the Yosys class. To capture Yosys output, once can now register an output stream in Pyosys.
Diffstat (limited to 'misc')
-rw-r--r--misc/py_wrap_generator.py292
1 files changed, 286 insertions, 6 deletions
diff --git a/misc/py_wrap_generator.py b/misc/py_wrap_generator.py
index 4ce8e947e..d8649e9ce 100644
--- a/misc/py_wrap_generator.py
+++ b/misc/py_wrap_generator.py
@@ -253,6 +253,8 @@ class WContainer:
candidate = WType.from_string(arg.strip(), containing_file, line_number)
if candidate == None:
return None
+ if candidate.name == "void":
+ return None
cont.args.append(candidate)
return cont
@@ -880,11 +882,8 @@ class WClass:
text += fun.gen_def_virtual()
return text
- def gen_boost_py(self):
- text = "\n\t\tclass_<" + self.name
- if self.link_type == link_types.derive:
- text += "Wrap, boost::noncopyable"
- text += ">(\"" + self.name + "\""
+ def gen_boost_py_body(self):
+ text = ""
if self.printable_constrs() == 0 or not self.contains_default_constr():
text += ", no_init"
text += ")"
@@ -907,6 +906,21 @@ class WClass:
text += "\n\t\t\t;\n"
return text
+ def gen_boost_py(self):
+ body = self.gen_boost_py_body()
+ if self.link_type == link_types.derive:
+ text = "\n\t\tclass_<" + self.name + ">(\"" + self.name + "\""
+ text += body
+ text += "\n\t\tclass_<" + self.name
+ text += "Wrap, boost::noncopyable"
+ text += ">(\"" + self.name + "\""
+ text += body
+ else:
+ text = "\n\t\tclass_<" + self.name + ">(\"Cpp" + self.name + "\""
+ text += body
+ return text
+
+
def contains_default_constr(self):
for c in self.found_constrs:
if len(c.args) == 0:
@@ -974,6 +988,7 @@ blacklist_methods = ["YOSYS_NAMESPACE::Pass::run_register", "YOSYS_NAMESPACE::Mo
enum_names = ["State","SyncType","ConstFlags"]
enums = [] #Do not edit
+glbls = []
unowned_functions = []
@@ -1723,6 +1738,159 @@ class WMember:
text += ")"
return text
+class WGlobal:
+ orig_text = None
+ wtype = attr_types.default
+ name = None
+ containing_file = None
+ namespace = ""
+ is_const = False
+
+ def from_string(str_def, containing_file, line_number, namespace):
+ glbl = WGlobal()
+ glbl.orig_text = str_def
+ glbl.wtype = None
+ glbl.name = ""
+ glbl.containing_file = containing_file
+ glbl.namespace = namespace
+ glbl.is_const = False
+
+ if not str.startswith(str_def, "extern"):
+ return None
+ str_def = str_def[7:]
+
+ if str.startswith(str_def, "const "):
+ glbl.is_const = True
+ str_def = str_def[6:]
+
+ if str_def.count(" ") == 0:
+ return None
+
+ parts = split_list(str_def.strip(), " ")
+
+ prefix = ""
+ i = 0
+ for part in parts:
+ if part in ["unsigned", "long", "short"]:
+ prefix += part + " "
+ i += 1
+ else:
+ break
+ parts = parts[i:]
+
+ if len(parts) <= 1:
+ return None
+
+ glbl.wtype = WType.from_string(prefix + parts[0], containing_file, line_number)
+
+ if glbl.wtype == None:
+ return None
+
+ str_def = parts[1]
+ for part in parts[2:]:
+ str_def = str_def + " " + part
+
+ if str_def.find("(") != -1 or str_def.find(")") != -1 or str_def.find("{") != -1 or str_def.find("}") != -1:
+ return None
+
+ found = str_def.find(";")
+ if found == -1:
+ return None
+
+ found_eq = str_def.find("=")
+ if found_eq != -1:
+ found = found_eq
+
+ glbl.name = str_def[:found]
+ str_def = str_def[found+1:]
+ if glbl.name.find("*") == 0:
+ glbl.name = glbl.name.replace("*", "")
+ glbl.wtype.attr_type = attr_types.star
+ if glbl.name.find("&&") == 0:
+ glbl.name = glbl.name.replace("&&", "")
+ glbl.wtype.attr_type = attr_types.ampamp
+ if glbl.name.find("&") == 0:
+ glbl.name = glbl.name.replace("&", "")
+ glbl.wtype.attr_type = attr_types.amp
+
+ if(len(str_def.strip()) != 0):
+ return None
+
+ if len(glbl.name.split(",")) > 1:
+ glbl_list = []
+ for name in glbl.name.split(","):
+ name = name.strip();
+ glbl_list.append(WGlobal())
+ glbl_list[-1].orig_text = glbl.orig_text
+ glbl_list[-1].wtype = glbl.wtype
+ glbl_list[-1].name = name
+ glbl_list[-1].containing_file = glbl.containing_file
+ glbl_list[-1].namespace = glbl.namespace
+ glbl_list[-1].is_const = glbl.is_const
+ return glbl_list
+
+ return glbl
+
+ def gen_def(self):
+ text = "\n\t"
+ if self.is_const:
+ text += "const "
+ text += self.wtype.gen_text() + " get_var_py_" + self.name + "()"
+ text += "\n\t{\n\t\t"
+ if self.wtype.attr_type == attr_types.star:
+ text += "if(" + self.namespace + "::" + self.name + " == NULL)\n\t\t\t"
+ text += "throw std::runtime_error(\"" + self.namespace + "::" + self.name + " is NULL\");\n\t\t"
+ if self.wtype.name in known_containers:
+ text += self.wtype.gen_text_cpp()
+ else:
+ if self.is_const:
+ text += "const "
+ text += self.wtype.gen_text()
+
+ if self.wtype.name in classnames or (self.wtype.name in known_containers and self.wtype.attr_type == attr_types.star):
+ text += "*"
+ text += " ret_ = "
+ if self.wtype.name in classnames:
+ text += self.wtype.name + "::get_py_obj("
+ if self.wtype.attr_type != attr_types.star:
+ text += "&"
+ text += self.namespace + "::" + self.name
+ if self.wtype.name in classnames:
+ text += ")"
+ text += ";"
+
+ if self.wtype.name in classnames:
+ text += "\n\t\treturn *ret_;"
+ elif self.wtype.name in known_containers:
+ text += known_containers[self.wtype.name].translate_cpp("ret_", self.wtype.cont.args, "\n\t\t", self.wtype.attr_type == attr_types.star)
+ text += "\n\t\treturn ret____tmp;"
+ else:
+ text += "\n\t\treturn ret_;"
+ text += "\n\t}\n"
+
+ if self.is_const:
+ return text
+
+ ret = Attribute(self.wtype, "rhs");
+
+ if self.wtype.name in classnames:
+ text += "\n\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " *rhs)"
+ else:
+ text += "\n\tvoid set_var_py_" + self.name + "(" + self.wtype.gen_text() + " rhs)"
+ text += "\n\t{"
+ text += ret.gen_translation()
+ text += "\n\t\t" + self.namespace + "::" + self.name + " = " + ret.gen_call() + ";"
+ text += "\n\t}\n"
+
+ return text;
+
+ def gen_boost_py(self):
+ text = "\n\t\t\t.add_static_property(\"" + self.name + "\", &" + "YOSYS_PYTHON::get_var_py_" + self.name
+ if not self.is_const:
+ text += ", &YOSYS_PYTHON::set_var_py_" + self.name
+ text += ")"
+ return text
+
def concat_namespace(tuple_list):
if len(tuple_list) == 0:
return ""
@@ -1859,6 +2027,16 @@ def parse_header(source):
else:
debug("\t\tFound member \"" + candidate.name + "\" of class \"" + class_[0].name + "\" of type \"" + candidate.wtype.name + "\"", 2)
class_[0].found_vars.append(candidate)
+ if candidate == None and class_ == None:
+ candidate = WGlobal.from_string(ugly_line, source.name, i, concat_namespace(namespaces))
+ if candidate != None:
+ if type(candidate) == list:
+ for c in candidate:
+ glbls.append(c)
+ debug("\tFound global \"" + c.name + "\" in namespace " + concat_namespace(namespaces), 2)
+ else:
+ glbls.append(candidate)
+ debug("\tFound global \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces), 2)
j = i
line = unpretty_string(line)
@@ -1888,6 +2066,17 @@ def parse_header(source):
debug("\t\tFound constructor of class \"" + class_[0].name + "\" in namespace " + concat_namespace(namespaces),2)
class_[0].found_constrs.append(candidate)
continue
+ if class_ == None:
+ candidate = WGlobal.from_string(line, source.name, i, concat_namespace(namespaces))
+ if candidate != None:
+ if type(candidate) == list:
+ for c in candidate:
+ glbls.append(c)
+ debug("\tFound global \"" + c.name + "\" in namespace " + concat_namespace(namespaces), 2)
+ else:
+ glbls.append(candidate)
+ debug("\tFound global \"" + candidate.name + "\" in namespace " + concat_namespace(namespaces), 2)
+ continue
if candidate != None:
while i < j:
i += 1
@@ -1990,6 +2179,7 @@ def gen_wrappers(filename, debug_level_ = 0):
if len(class_.found_constrs) == 0:
class_.found_constrs.append(WConstructor(source.name, class_))
debug(str(len(unowned_functions)) + " functions are unowned", 1)
+ debug(str(len(unowned_functions)) + " global variables", 1)
for enum in enums:
debug("Enum " + assure_length(enum.name, len(max(enum_names, key=len)), True) + " contains " + assure_length(str(len(enum.values)), 2, False) + " values", 1)
debug("-"*col, 1)
@@ -2025,10 +2215,15 @@ def gen_wrappers(filename, debug_level_ = 0):
#include <boost/python/wrapper.hpp>
#include <boost/python/call.hpp>
#include <boost/python.hpp>
-
+#include <iosfwd> // std::streamsize
+#include <iostream>
+#include <boost/iostreams/concepts.hpp> // boost::iostreams::sink
+#include <boost/iostreams/stream.hpp>
USING_YOSYS_NAMESPACE
namespace YOSYS_PYTHON {
+
+ struct YosysStatics{};
""")
for source in sources:
@@ -2050,6 +2245,9 @@ namespace YOSYS_PYTHON {
for fun in unowned_functions:
wrapper_file.write(fun.gen_def())
+ for glbl in glbls:
+ wrapper_file.write(glbl.gen_def())
+
wrapper_file.write(""" struct Initializer
{
Initializer() {
@@ -2068,12 +2266,89 @@ namespace YOSYS_PYTHON {
}
};
+
+ /// source: https://stackoverflow.com/questions/26033781/converting-python-io-object-to-stdostream-when-using-boostpython?noredirect=1&lq=1
+ /// @brief Type that implements the Boost.IOStream's Sink and Flushable
+ /// concept for writing data to Python object that support:
+ /// n = object.write(str) # n = None or bytes written
+ /// object.flush() # if flush exists, then it is callable
+ class PythonOutputDevice
+ {
+ public:
+
+ // This class models both the Sink and Flushable concepts.
+ struct category
+ : boost::iostreams::sink_tag,
+ boost::iostreams::flushable_tag
+ {};
+
+ explicit
+ PythonOutputDevice(boost::python::object object)
+ : object_(object)
+ {}
+
+ // Sink concept.
+ public:
+
+ typedef char char_type;
+
+ std::streamsize write(const char* buffer, std::streamsize buffer_size)
+ {
+ namespace python = boost::python;
+ // Copy the buffer to a python string.
+ python::str data(buffer, buffer_size);
+
+ // Invoke write on the python object, passing in the data. The following
+ // is equivalent to:
+ // n = object_.write(data)
+ python::extract<std::streamsize> bytes_written(
+ object_.attr("write")(data));
+
+ // Per the Sink concept, return the number of bytes written. If the
+ // Python return value provides a numeric result, then use it. Otherwise,
+ // such as the case of a File object, use the buffer_size.
+ return bytes_written.check()
+ ? bytes_written
+ : buffer_size;
+ }
+
+ // Flushable concept.
+ public:
+
+ bool flush()
+ {
+ // If flush exists, then call it.
+ boost::python::object flush = object_.attr("flush");
+ if (!flush.is_none())
+ {
+ flush();
+ }
+
+ // Always return true. If an error occurs, an exception should be thrown.
+ return true;
+ }
+
+ private:
+ boost::python::object object_;
+ };
+
+ /// @brief Use an auxiliary function to adapt the legacy function.
+ void log_to_stream(boost::python::object object)
+ {
+ // Create an ostream that delegates to the python object.
+ boost::iostreams::stream<PythonOutputDevice>* output = new boost::iostreams::stream<PythonOutputDevice>(object);
+ Yosys::log_streams.insert(Yosys::log_streams.begin(), output);
+ };
+
+
BOOST_PYTHON_MODULE(libyosys)
{
using namespace boost::python;
class_<Initializer>("Initializer");
scope().attr("_hidden") = new Initializer();
+
+ def("log_to_stream", &log_to_stream);
""")
for enum in enums:
@@ -2086,6 +2361,11 @@ namespace YOSYS_PYTHON {
for fun in unowned_functions:
wrapper_file.write(fun.gen_boost_py())
+ wrapper_file.write("\n\n\t\tclass_<YosysStatics>(\"Yosys\")\n")
+ for glbl in glbls:
+ wrapper_file.write(glbl.gen_boost_py())
+ wrapper_file.write("\t\t;\n")
+
wrapper_file.write("\n\t}\n}\n#endif")
def print_includes():