mirror of
https://github.com/YosysHQ/yosys
synced 2025-11-03 13:07:58 +00:00
Merge d6b9158fa3 into 797780eda5
This commit is contained in:
commit
b32d320fc0
7 changed files with 135 additions and 34 deletions
|
|
@ -3088,6 +3088,14 @@ RTLIL::Cell *RTLIL::Module::addCell(RTLIL::IdString name, const RTLIL::Cell *oth
|
|||
return cell;
|
||||
}
|
||||
|
||||
RTLIL::Memory *RTLIL::Module::addMemory(RTLIL::IdString name)
|
||||
{
|
||||
RTLIL::Memory *mem = new RTLIL::Memory;
|
||||
mem->name = std::move(name);
|
||||
memories[mem->name] = mem;
|
||||
return mem;
|
||||
}
|
||||
|
||||
RTLIL::Memory *RTLIL::Module::addMemory(RTLIL::IdString name, const RTLIL::Memory *other)
|
||||
{
|
||||
RTLIL::Memory *mem = new RTLIL::Memory;
|
||||
|
|
|
|||
|
|
@ -1827,6 +1827,7 @@ public:
|
|||
RTLIL::Cell *addCell(RTLIL::IdString name, RTLIL::IdString type);
|
||||
RTLIL::Cell *addCell(RTLIL::IdString name, const RTLIL::Cell *other);
|
||||
|
||||
RTLIL::Memory *addMemory(RTLIL::IdString name);
|
||||
RTLIL::Memory *addMemory(RTLIL::IdString name, const RTLIL::Memory *other);
|
||||
|
||||
RTLIL::Process *addProcess(RTLIL::IdString name);
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ from cxxheaderparser.types import (
|
|||
Variable,
|
||||
Array,
|
||||
FundamentalSpecifier,
|
||||
FunctionType,
|
||||
)
|
||||
|
||||
__file_dir__ = Path(__file__).absolute().parent
|
||||
|
|
@ -177,11 +178,11 @@ pyosys_headers = [
|
|||
denylist=frozenset({"bits", "bitvectorize"}),
|
||||
),
|
||||
PyosysClass("AttrObject", denylist=frozenset({"get_blackbox_attribute"})),
|
||||
PyosysClass("NamedObject", denylist=frozenset({"get_blackbox_attribute"})),
|
||||
PyosysClass("NamedObject"),
|
||||
PyosysClass("Selection"),
|
||||
# PyosysClass("Monitor"), # Virtual methods, manually bridged
|
||||
PyosysClass("CaseRule", denylist=frozenset({"get_blackbox_attribute"})),
|
||||
PyosysClass("SwitchRule", denylist=frozenset({"get_blackbox_attribute"})),
|
||||
PyosysClass("CaseRule"),
|
||||
PyosysClass("SwitchRule"),
|
||||
PyosysClass("SyncRule"),
|
||||
PyosysClass(
|
||||
"Process",
|
||||
|
|
@ -219,7 +220,7 @@ pyosys_headers = [
|
|||
),
|
||||
PyosysClass(
|
||||
"Design",
|
||||
string_expr="s.hashidx_",
|
||||
string_expr="std::to_string(s.hashidx_)",
|
||||
hash_expr="s",
|
||||
denylist=frozenset({"selected_whole_modules"}), # deprecated
|
||||
),
|
||||
|
|
@ -241,13 +242,17 @@ class PyosysType:
|
|||
|
||||
@classmethod
|
||||
def from_type(Self, type_obj, drop_const=False) -> "PyosysType":
|
||||
const = type_obj.const and not drop_const
|
||||
const = hasattr(type_obj, "const") and type_obj.const and not drop_const
|
||||
if isinstance(type_obj, Pointer):
|
||||
ptr_to = Self.from_type(type_obj.ptr_to)
|
||||
return Self("ptr", (ptr_to,), const)
|
||||
elif isinstance(type_obj, Reference):
|
||||
ref_to = Self.from_type(type_obj.ref_to)
|
||||
return Self("ref", (ref_to,), const)
|
||||
elif isinstance(type_obj, FunctionType):
|
||||
ret_type = Self.from_type(type_obj.return_type)
|
||||
param_types = (Self.from_type(p.type) for p in type_obj.parameters)
|
||||
return Self("fn", (ret_type, *param_types), False)
|
||||
assert isinstance(
|
||||
type_obj, Type
|
||||
), f"unexpected c++ type object of type {type(type_obj)}"
|
||||
|
|
@ -270,6 +275,16 @@ class PyosysType:
|
|||
if title == "Dict":
|
||||
key, value = self.specialization
|
||||
return f"{key.generate_identifier()}To{value.generate_identifier()}{title}"
|
||||
elif title == "Fn":
|
||||
identifier = self.specialization[0].generate_identifier()
|
||||
if identifier == "Void":
|
||||
identifier = ""
|
||||
else:
|
||||
identifier += "From"
|
||||
identifier += "And".join(
|
||||
p.generate_identifier() for p in self.specialization[1:]
|
||||
)
|
||||
return identifier
|
||||
|
||||
return (
|
||||
"".join(spec.generate_identifier() for spec in self.specialization) + title
|
||||
|
|
@ -283,6 +298,9 @@ class PyosysType:
|
|||
return const_prefix + f"{self.specialization[0].generate_cpp_name()} *"
|
||||
elif self.base == "ref":
|
||||
return const_prefix + f"{self.specialization[0].generate_cpp_name()} &"
|
||||
elif self.base == "fn":
|
||||
param_cpp_names = (s.generate_cpp_name() for s in self.specialization[1:])
|
||||
return f"{self.specialization[0].generate_cpp_name()}({','.join(param_cpp_names)})"
|
||||
else:
|
||||
return (
|
||||
const_prefix
|
||||
|
|
@ -301,7 +319,7 @@ class PyosysWrapperGenerator(object):
|
|||
self.f = wrapper_stream
|
||||
self.f_inc = header_stream
|
||||
self.found_containers: Dict[PyosysType, Any] = {}
|
||||
self.class_registry: Dict[str, ClassScope] = {}
|
||||
self.class_registry: Dict[str, Tuple[ClassScope, PyosysClass]] = {}
|
||||
|
||||
# entry point
|
||||
def generate(self):
|
||||
|
|
@ -380,7 +398,7 @@ class PyosysWrapperGenerator(object):
|
|||
if isinstance(type_info, Reference):
|
||||
return PyosysWrapperGenerator.find_containers(containers, type_info.ref_to)
|
||||
if not isinstance(type_info, Type):
|
||||
return ()
|
||||
return {}
|
||||
segments = type_info.typename.segments
|
||||
containers_found = {}
|
||||
for segment in segments:
|
||||
|
|
@ -411,19 +429,23 @@ class PyosysWrapperGenerator(object):
|
|||
def get_parameter_types(function: Function) -> str:
|
||||
return ", ".join(p.type.format() for p in function.parameters)
|
||||
|
||||
def register_containers(self, target: Union[Function, Field, Variable]):
|
||||
def register_containers(self, target: Union[Function, Field, Variable]) -> bool:
|
||||
supported = ("dict", "idict", "pool", "set", "vector")
|
||||
found = False
|
||||
if isinstance(target, Function):
|
||||
self.found_containers.update(
|
||||
self.find_containers(supported, target.return_type)
|
||||
)
|
||||
return_type_containers = self.find_containers(supported, target.return_type)
|
||||
found = found or len(return_type_containers)
|
||||
self.found_containers.update(return_type_containers)
|
||||
|
||||
for parameter in target.parameters:
|
||||
self.found_containers.update(
|
||||
self.find_containers(supported, parameter.type)
|
||||
)
|
||||
parameter_containers = self.find_containers(supported, parameter.type)
|
||||
found = found or len(parameter_containers)
|
||||
self.found_containers.update(parameter_containers)
|
||||
else:
|
||||
self.found_containers.update(self.find_containers(supported, target.type))
|
||||
variable_containers = self.find_containers(supported, target.type)
|
||||
found = found or len(variable_containers)
|
||||
self.found_containers.update(variable_containers)
|
||||
return found
|
||||
|
||||
# processors
|
||||
def get_overload_cast(
|
||||
|
|
@ -470,9 +492,9 @@ class PyosysWrapperGenerator(object):
|
|||
|
||||
def_args = [f'"{python_function_basename}"']
|
||||
def_args.append(self.get_overload_cast(function, class_basename))
|
||||
for parameter in function.parameters:
|
||||
# ASSUMPTION: there are no unnamed parameters in the yosys codebase
|
||||
parameter_arg = f'py::arg("{parameter.name}")'
|
||||
for i, parameter in enumerate(function.parameters):
|
||||
name = parameter.name or f"arg{i}"
|
||||
parameter_arg = f'py::arg("{name}")'
|
||||
if parameter.default is not None:
|
||||
parameter_arg += f" = {parameter.default.format()}"
|
||||
def_args.append(parameter_arg)
|
||||
|
|
@ -525,8 +547,12 @@ class PyosysWrapperGenerator(object):
|
|||
if function.static:
|
||||
definition_fn = "def_static"
|
||||
|
||||
definition_args = self.get_definition_args(
|
||||
function, metadata.name, python_name_override
|
||||
)
|
||||
|
||||
print(
|
||||
f"\t\t\t.{definition_fn}({', '.join(self.get_definition_args(function, metadata.name, python_name_override))})",
|
||||
f"\t\t\t.{definition_fn}({', '.join(definition_args)})",
|
||||
file=self.f,
|
||||
)
|
||||
|
||||
|
|
@ -565,7 +591,7 @@ class PyosysWrapperGenerator(object):
|
|||
# care
|
||||
return
|
||||
|
||||
self.register_containers(field)
|
||||
has_containers = self.register_containers(field)
|
||||
|
||||
definition_fn = f"def_{'readonly' if field.type.const else 'readwrite'}"
|
||||
if field.static:
|
||||
|
|
@ -573,8 +599,13 @@ class PyosysWrapperGenerator(object):
|
|||
|
||||
field_python_basename = keyword_aliases.get(field.name, field.name)
|
||||
|
||||
def_args = [
|
||||
f'"{field_python_basename}"',
|
||||
f"&{metadata.name}::{field.name}",
|
||||
]
|
||||
def_args.append("py::return_value_policy::copy")
|
||||
print(
|
||||
f'\t\t\t.{definition_fn}("{field_python_basename}", &{metadata.name}::{field.name})',
|
||||
f"\t\t\t.{definition_fn}({', '.join(def_args)})",
|
||||
file=self.f,
|
||||
)
|
||||
|
||||
|
|
@ -603,16 +634,20 @@ class PyosysWrapperGenerator(object):
|
|||
)
|
||||
|
||||
def process_class_members(
|
||||
self, metadata: PyosysClass, cls: ClassScope, basename: str
|
||||
self,
|
||||
metadata: PyosysClass,
|
||||
base_metadata: PyosysClass,
|
||||
cls: ClassScope,
|
||||
basename: str,
|
||||
):
|
||||
for method in cls.methods:
|
||||
if method.name.segments[-1].name in metadata.denylist:
|
||||
if method.name.segments[-1].name in base_metadata.denylist:
|
||||
continue
|
||||
self.process_method(metadata, method)
|
||||
|
||||
visited_anonymous_unions = set()
|
||||
for field_ in cls.fields:
|
||||
if field_.name in metadata.denylist:
|
||||
if field_.name in base_metadata.denylist:
|
||||
continue
|
||||
self.process_field(metadata, field_)
|
||||
|
||||
|
|
@ -627,6 +662,16 @@ class PyosysWrapperGenerator(object):
|
|||
for subfield in subclass.fields:
|
||||
self.process_field(metadata, subfield)
|
||||
|
||||
for base in cls.class_decl.bases:
|
||||
if base.access != "public":
|
||||
continue
|
||||
name = base.typename.segments[-1].format()
|
||||
if processed := self.class_registry.get(name):
|
||||
base_scope, base_metadata = processed
|
||||
self.process_class_members(
|
||||
metadata, base_metadata, base_scope, basename
|
||||
)
|
||||
|
||||
def process_class(
|
||||
self,
|
||||
metadata: PyosysClass,
|
||||
|
|
@ -638,7 +683,7 @@ class PyosysWrapperGenerator(object):
|
|||
segment.format() for segment in pqname.segments
|
||||
]
|
||||
basename = full_path.pop()
|
||||
self.class_registry[basename] = cls
|
||||
self.class_registry[basename] = (cls, metadata)
|
||||
|
||||
declaration_namespace = "::".join(full_path)
|
||||
tpl_args = [basename]
|
||||
|
|
@ -649,19 +694,17 @@ class PyosysWrapperGenerator(object):
|
|||
file=self.f,
|
||||
)
|
||||
|
||||
self.process_class_members(metadata, cls, basename)
|
||||
for base in cls.class_decl.bases:
|
||||
if base.access != "public":
|
||||
continue
|
||||
name = base.typename.segments[-1].format()
|
||||
if base_scope := self.class_registry.get(name):
|
||||
self.process_class_members(metadata, base_scope, basename)
|
||||
self.process_class_members(metadata, metadata, cls, basename)
|
||||
|
||||
if expr := metadata.string_expr:
|
||||
print(
|
||||
f'\t\t.def("__str__", [](const {basename} &s) {{ return {expr}; }})',
|
||||
file=self.f,
|
||||
)
|
||||
print(
|
||||
f'\t\t.def("__repr__", [](const {basename} &s) {{ std::stringstream ss; ss << "<{basename} " << {expr} << ">"; return ss.str(); }})',
|
||||
file=self.f,
|
||||
)
|
||||
|
||||
if expr := metadata.hash_expr:
|
||||
print(
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@
|
|||
// <!-- generated includes -->
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/native_enum.h>
|
||||
#include <pybind11/functional.h>
|
||||
|
||||
// duplicates for LSPs
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/yosys_common.h"
|
||||
|
||||
#include "pyosys/hashlib.h"
|
||||
|
||||
namespace py = pybind11;
|
||||
|
|
@ -28,7 +34,7 @@ namespace py = pybind11;
|
|||
USING_YOSYS_NAMESPACE
|
||||
|
||||
using std::set;
|
||||
using std::regex;
|
||||
using std::function;
|
||||
using std::ostream;
|
||||
using namespace RTLIL;
|
||||
|
||||
|
|
|
|||
28
tests/pyosys/test_idstring_lifetime.py
Normal file
28
tests/pyosys/test_idstring_lifetime.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
|
||||
from pyosys import libyosys as ys
|
||||
from pathlib import Path
|
||||
|
||||
__file_dir__ = Path(__file__).absolute().parent
|
||||
|
||||
d = ys.Design()
|
||||
ys.run_pass(f"read_verilog {__file_dir__ / 'spm.cut.v.gz'}", d)
|
||||
ys.run_pass("hierarchy -top spm", d)
|
||||
|
||||
external_idstring_holder_0 = None
|
||||
external_idstring_holder_1 = None
|
||||
|
||||
def get_top_module_idstring():
|
||||
global external_idstring_holder_0, external_idstring_holder_1
|
||||
d = ys.Design()
|
||||
ys.run_pass(f"read_verilog {__file_dir__ / 'spm.cut.v.gz'}", d)
|
||||
ys.run_pass("hierarchy -top spm", d)
|
||||
external_idstring_holder_0 = d.top_module().name
|
||||
for cell in d.top_module().cells_:
|
||||
print(f"TARGETED: {cell}", flush=True)
|
||||
external_idstring_holder_1 = cell
|
||||
break
|
||||
# d deallocates
|
||||
|
||||
get_top_module_idstring()
|
||||
print(external_idstring_holder_0, flush=True)
|
||||
print(external_idstring_holder_1, flush=True)
|
||||
15
tests/pyosys/test_indirect_inheritance.py
Normal file
15
tests/pyosys/test_indirect_inheritance.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
from pyosys import libyosys as ys
|
||||
from pathlib import Path
|
||||
|
||||
__file_dir__ = Path(__file__).absolute().parent
|
||||
|
||||
|
||||
d = ys.Design()
|
||||
ys.run_pass(f"read_verilog {__file_dir__ / 'spm.cut.v.gz'}", d)
|
||||
ys.run_pass("hierarchy -top spm", d)
|
||||
|
||||
for idstr, cell in d.top_module().cells_.items():
|
||||
cell.set_bool_attribute("\\set")
|
||||
print(cell.attributes)
|
||||
break
|
||||
|
|
@ -14,7 +14,7 @@ class Monitor(ys.Monitor):
|
|||
self.mods.append(mod.name.str())
|
||||
|
||||
m = Monitor()
|
||||
d.monitors.add(m)
|
||||
d.monitors = [m]
|
||||
|
||||
ys.run_pass(f"read_verilog {__file_dir__ / 'spm.cut.v.gz'}", d)
|
||||
ys.run_pass("hierarchy -top spm", d)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue