3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-11-04 21:39:14 +00:00
This commit is contained in:
Mohamed Gaber 2025-11-03 10:04:14 +01:00 committed by GitHub
commit a3db034aca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 135 additions and 34 deletions

View file

@ -3088,6 +3088,14 @@ RTLIL::Cell *RTLIL::Module::addCell(RTLIL::IdString name, const RTLIL::Cell *oth
return cell; 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 *RTLIL::Module::addMemory(RTLIL::IdString name, const RTLIL::Memory *other)
{ {
RTLIL::Memory *mem = new RTLIL::Memory; RTLIL::Memory *mem = new RTLIL::Memory;

View file

@ -1827,6 +1827,7 @@ public:
RTLIL::Cell *addCell(RTLIL::IdString name, RTLIL::IdString type); RTLIL::Cell *addCell(RTLIL::IdString name, RTLIL::IdString type);
RTLIL::Cell *addCell(RTLIL::IdString name, const RTLIL::Cell *other); 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::Memory *addMemory(RTLIL::IdString name, const RTLIL::Memory *other);
RTLIL::Process *addProcess(RTLIL::IdString name); RTLIL::Process *addProcess(RTLIL::IdString name);

View file

@ -56,6 +56,7 @@ from cxxheaderparser.types import (
Variable, Variable,
Array, Array,
FundamentalSpecifier, FundamentalSpecifier,
FunctionType,
) )
__file_dir__ = Path(__file__).absolute().parent __file_dir__ = Path(__file__).absolute().parent
@ -177,11 +178,11 @@ pyosys_headers = [
denylist=frozenset({"bits", "bitvectorize"}), denylist=frozenset({"bits", "bitvectorize"}),
), ),
PyosysClass("AttrObject", denylist=frozenset({"get_blackbox_attribute"})), PyosysClass("AttrObject", denylist=frozenset({"get_blackbox_attribute"})),
PyosysClass("NamedObject", denylist=frozenset({"get_blackbox_attribute"})), PyosysClass("NamedObject"),
PyosysClass("Selection"), PyosysClass("Selection"),
# PyosysClass("Monitor"), # Virtual methods, manually bridged # PyosysClass("Monitor"), # Virtual methods, manually bridged
PyosysClass("CaseRule", denylist=frozenset({"get_blackbox_attribute"})), PyosysClass("CaseRule"),
PyosysClass("SwitchRule", denylist=frozenset({"get_blackbox_attribute"})), PyosysClass("SwitchRule"),
PyosysClass("SyncRule"), PyosysClass("SyncRule"),
PyosysClass( PyosysClass(
"Process", "Process",
@ -219,7 +220,7 @@ pyosys_headers = [
), ),
PyosysClass( PyosysClass(
"Design", "Design",
string_expr="s.hashidx_", string_expr="std::to_string(s.hashidx_)",
hash_expr="s", hash_expr="s",
denylist=frozenset({"selected_whole_modules"}), # deprecated denylist=frozenset({"selected_whole_modules"}), # deprecated
), ),
@ -241,13 +242,17 @@ class PyosysType:
@classmethod @classmethod
def from_type(Self, type_obj, drop_const=False) -> "PyosysType": 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): if isinstance(type_obj, Pointer):
ptr_to = Self.from_type(type_obj.ptr_to) ptr_to = Self.from_type(type_obj.ptr_to)
return Self("ptr", (ptr_to,), const) return Self("ptr", (ptr_to,), const)
elif isinstance(type_obj, Reference): elif isinstance(type_obj, Reference):
ref_to = Self.from_type(type_obj.ref_to) ref_to = Self.from_type(type_obj.ref_to)
return Self("ref", (ref_to,), const) 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( assert isinstance(
type_obj, Type type_obj, Type
), f"unexpected c++ type object of type {type(type_obj)}" ), f"unexpected c++ type object of type {type(type_obj)}"
@ -270,6 +275,16 @@ class PyosysType:
if title == "Dict": if title == "Dict":
key, value = self.specialization key, value = self.specialization
return f"{key.generate_identifier()}To{value.generate_identifier()}{title}" 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 ( return (
"".join(spec.generate_identifier() for spec in self.specialization) + title "".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()} *" return const_prefix + f"{self.specialization[0].generate_cpp_name()} *"
elif self.base == "ref": elif self.base == "ref":
return const_prefix + f"{self.specialization[0].generate_cpp_name()} &" 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: else:
return ( return (
const_prefix const_prefix
@ -301,7 +319,7 @@ class PyosysWrapperGenerator(object):
self.f = wrapper_stream self.f = wrapper_stream
self.f_inc = header_stream self.f_inc = header_stream
self.found_containers: Dict[PyosysType, Any] = {} self.found_containers: Dict[PyosysType, Any] = {}
self.class_registry: Dict[str, ClassScope] = {} self.class_registry: Dict[str, Tuple[ClassScope, PyosysClass]] = {}
# entry point # entry point
def generate(self): def generate(self):
@ -380,7 +398,7 @@ class PyosysWrapperGenerator(object):
if isinstance(type_info, Reference): if isinstance(type_info, Reference):
return PyosysWrapperGenerator.find_containers(containers, type_info.ref_to) return PyosysWrapperGenerator.find_containers(containers, type_info.ref_to)
if not isinstance(type_info, Type): if not isinstance(type_info, Type):
return () return {}
segments = type_info.typename.segments segments = type_info.typename.segments
containers_found = {} containers_found = {}
for segment in segments: for segment in segments:
@ -411,19 +429,23 @@ class PyosysWrapperGenerator(object):
def get_parameter_types(function: Function) -> str: def get_parameter_types(function: Function) -> str:
return ", ".join(p.type.format() for p in function.parameters) 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") supported = ("dict", "idict", "pool", "set", "vector")
found = False
if isinstance(target, Function): if isinstance(target, Function):
self.found_containers.update( return_type_containers = self.find_containers(supported, target.return_type)
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: for parameter in target.parameters:
self.found_containers.update( parameter_containers = self.find_containers(supported, parameter.type)
self.find_containers(supported, parameter.type) found = found or len(parameter_containers)
) self.found_containers.update(parameter_containers)
else: 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 # processors
def get_overload_cast( def get_overload_cast(
@ -470,9 +492,9 @@ class PyosysWrapperGenerator(object):
def_args = [f'"{python_function_basename}"'] def_args = [f'"{python_function_basename}"']
def_args.append(self.get_overload_cast(function, class_basename)) def_args.append(self.get_overload_cast(function, class_basename))
for parameter in function.parameters: for i, parameter in enumerate(function.parameters):
# ASSUMPTION: there are no unnamed parameters in the yosys codebase name = parameter.name or f"arg{i}"
parameter_arg = f'py::arg("{parameter.name}")' parameter_arg = f'py::arg("{name}")'
if parameter.default is not None: if parameter.default is not None:
parameter_arg += f" = {parameter.default.format()}" parameter_arg += f" = {parameter.default.format()}"
def_args.append(parameter_arg) def_args.append(parameter_arg)
@ -525,8 +547,12 @@ class PyosysWrapperGenerator(object):
if function.static: if function.static:
definition_fn = "def_static" definition_fn = "def_static"
definition_args = self.get_definition_args(
function, metadata.name, python_name_override
)
print( 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, file=self.f,
) )
@ -565,7 +591,7 @@ class PyosysWrapperGenerator(object):
# care # care
return return
self.register_containers(field) has_containers = self.register_containers(field)
definition_fn = f"def_{'readonly' if field.type.const else 'readwrite'}" definition_fn = f"def_{'readonly' if field.type.const else 'readwrite'}"
if field.static: if field.static:
@ -573,8 +599,13 @@ class PyosysWrapperGenerator(object):
field_python_basename = keyword_aliases.get(field.name, field.name) 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( 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, file=self.f,
) )
@ -603,16 +634,20 @@ class PyosysWrapperGenerator(object):
) )
def process_class_members( 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: 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 continue
self.process_method(metadata, method) self.process_method(metadata, method)
visited_anonymous_unions = set() visited_anonymous_unions = set()
for field_ in cls.fields: for field_ in cls.fields:
if field_.name in metadata.denylist: if field_.name in base_metadata.denylist:
continue continue
self.process_field(metadata, field_) self.process_field(metadata, field_)
@ -627,6 +662,16 @@ class PyosysWrapperGenerator(object):
for subfield in subclass.fields: for subfield in subclass.fields:
self.process_field(metadata, subfield) 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( def process_class(
self, self,
metadata: PyosysClass, metadata: PyosysClass,
@ -638,7 +683,7 @@ class PyosysWrapperGenerator(object):
segment.format() for segment in pqname.segments segment.format() for segment in pqname.segments
] ]
basename = full_path.pop() basename = full_path.pop()
self.class_registry[basename] = cls self.class_registry[basename] = (cls, metadata)
declaration_namespace = "::".join(full_path) declaration_namespace = "::".join(full_path)
tpl_args = [basename] tpl_args = [basename]
@ -649,19 +694,17 @@ class PyosysWrapperGenerator(object):
file=self.f, file=self.f,
) )
self.process_class_members(metadata, cls, basename) self.process_class_members(metadata, 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)
if expr := metadata.string_expr: if expr := metadata.string_expr:
print( print(
f'\t\t.def("__str__", [](const {basename} &s) {{ return {expr}; }})', f'\t\t.def("__str__", [](const {basename} &s) {{ return {expr}; }})',
file=self.f, 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: if expr := metadata.hash_expr:
print( print(

View file

@ -21,6 +21,12 @@
// <!-- generated includes --> // <!-- generated includes -->
#include <pybind11/pybind11.h> #include <pybind11/pybind11.h>
#include <pybind11/native_enum.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" #include "pyosys/hashlib.h"
namespace py = pybind11; namespace py = pybind11;
@ -28,7 +34,7 @@ namespace py = pybind11;
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
using std::set; using std::set;
using std::regex; using std::function;
using std::ostream; using std::ostream;
using namespace RTLIL; using namespace RTLIL;

View 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)

View 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

View file

@ -14,7 +14,7 @@ class Monitor(ys.Monitor):
self.mods.append(mod.name.str()) self.mods.append(mod.name.str())
m = Monitor() 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(f"read_verilog {__file_dir__ / 'spm.cut.v.gz'}", d)
ys.run_pass("hierarchy -top spm", d) ys.run_pass("hierarchy -top spm", d)