Skip to content

Commit 0751b74

Browse files
authored
Merge pull request #5441 from donn/pyosys_bugfixes
pyosys: fix a number of regressions from 0.58
2 parents 0f2e470 + d6b9158 commit 0751b74

File tree

7 files changed

+135
-34
lines changed

7 files changed

+135
-34
lines changed

kernel/rtlil.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3088,6 +3088,14 @@ RTLIL::Cell *RTLIL::Module::addCell(RTLIL::IdString name, const RTLIL::Cell *oth
30883088
return cell;
30893089
}
30903090

3091+
RTLIL::Memory *RTLIL::Module::addMemory(RTLIL::IdString name)
3092+
{
3093+
RTLIL::Memory *mem = new RTLIL::Memory;
3094+
mem->name = std::move(name);
3095+
memories[mem->name] = mem;
3096+
return mem;
3097+
}
3098+
30913099
RTLIL::Memory *RTLIL::Module::addMemory(RTLIL::IdString name, const RTLIL::Memory *other)
30923100
{
30933101
RTLIL::Memory *mem = new RTLIL::Memory;

kernel/rtlil.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,6 +1828,7 @@ struct RTLIL::Module : public RTLIL::NamedObject
18281828
RTLIL::Cell *addCell(RTLIL::IdString name, RTLIL::IdString type);
18291829
RTLIL::Cell *addCell(RTLIL::IdString name, const RTLIL::Cell *other);
18301830

1831+
RTLIL::Memory *addMemory(RTLIL::IdString name);
18311832
RTLIL::Memory *addMemory(RTLIL::IdString name, const RTLIL::Memory *other);
18321833

18331834
RTLIL::Process *addProcess(RTLIL::IdString name);

pyosys/generator.py

Lines changed: 75 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
Variable,
5757
Array,
5858
FundamentalSpecifier,
59+
FunctionType,
5960
)
6061

6162
__file_dir__ = Path(__file__).absolute().parent
@@ -177,11 +178,11 @@ def __post_init__(self):
177178
denylist=frozenset({"bits", "bitvectorize"}),
178179
),
179180
PyosysClass("AttrObject", denylist=frozenset({"get_blackbox_attribute"})),
180-
PyosysClass("NamedObject", denylist=frozenset({"get_blackbox_attribute"})),
181+
PyosysClass("NamedObject"),
181182
PyosysClass("Selection"),
182183
# PyosysClass("Monitor"), # Virtual methods, manually bridged
183-
PyosysClass("CaseRule", denylist=frozenset({"get_blackbox_attribute"})),
184-
PyosysClass("SwitchRule", denylist=frozenset({"get_blackbox_attribute"})),
184+
PyosysClass("CaseRule"),
185+
PyosysClass("SwitchRule"),
185186
PyosysClass("SyncRule"),
186187
PyosysClass(
187188
"Process",
@@ -219,7 +220,7 @@ def __post_init__(self):
219220
),
220221
PyosysClass(
221222
"Design",
222-
string_expr="s.hashidx_",
223+
string_expr="std::to_string(s.hashidx_)",
223224
hash_expr="s",
224225
denylist=frozenset({"selected_whole_modules"}), # deprecated
225226
),
@@ -241,13 +242,17 @@ class PyosysType:
241242

242243
@classmethod
243244
def from_type(Self, type_obj, drop_const=False) -> "PyosysType":
244-
const = type_obj.const and not drop_const
245+
const = hasattr(type_obj, "const") and type_obj.const and not drop_const
245246
if isinstance(type_obj, Pointer):
246247
ptr_to = Self.from_type(type_obj.ptr_to)
247248
return Self("ptr", (ptr_to,), const)
248249
elif isinstance(type_obj, Reference):
249250
ref_to = Self.from_type(type_obj.ref_to)
250251
return Self("ref", (ref_to,), const)
252+
elif isinstance(type_obj, FunctionType):
253+
ret_type = Self.from_type(type_obj.return_type)
254+
param_types = (Self.from_type(p.type) for p in type_obj.parameters)
255+
return Self("fn", (ret_type, *param_types), False)
251256
assert isinstance(
252257
type_obj, Type
253258
), f"unexpected c++ type object of type {type(type_obj)}"
@@ -270,6 +275,16 @@ def generate_identifier(self):
270275
if title == "Dict":
271276
key, value = self.specialization
272277
return f"{key.generate_identifier()}To{value.generate_identifier()}{title}"
278+
elif title == "Fn":
279+
identifier = self.specialization[0].generate_identifier()
280+
if identifier == "Void":
281+
identifier = ""
282+
else:
283+
identifier += "From"
284+
identifier += "And".join(
285+
p.generate_identifier() for p in self.specialization[1:]
286+
)
287+
return identifier
273288

274289
return (
275290
"".join(spec.generate_identifier() for spec in self.specialization) + title
@@ -283,6 +298,9 @@ def generate_cpp_name(self):
283298
return const_prefix + f"{self.specialization[0].generate_cpp_name()} *"
284299
elif self.base == "ref":
285300
return const_prefix + f"{self.specialization[0].generate_cpp_name()} &"
301+
elif self.base == "fn":
302+
param_cpp_names = (s.generate_cpp_name() for s in self.specialization[1:])
303+
return f"{self.specialization[0].generate_cpp_name()}({','.join(param_cpp_names)})"
286304
else:
287305
return (
288306
const_prefix
@@ -301,7 +319,7 @@ def __init__(
301319
self.f = wrapper_stream
302320
self.f_inc = header_stream
303321
self.found_containers: Dict[PyosysType, Any] = {}
304-
self.class_registry: Dict[str, ClassScope] = {}
322+
self.class_registry: Dict[str, Tuple[ClassScope, PyosysClass]] = {}
305323

306324
# entry point
307325
def generate(self):
@@ -380,7 +398,7 @@ def find_containers(
380398
if isinstance(type_info, Reference):
381399
return PyosysWrapperGenerator.find_containers(containers, type_info.ref_to)
382400
if not isinstance(type_info, Type):
383-
return ()
401+
return {}
384402
segments = type_info.typename.segments
385403
containers_found = {}
386404
for segment in segments:
@@ -411,19 +429,23 @@ def find_anonymous_union(cls: ClassScope):
411429
def get_parameter_types(function: Function) -> str:
412430
return ", ".join(p.type.format() for p in function.parameters)
413431

414-
def register_containers(self, target: Union[Function, Field, Variable]):
432+
def register_containers(self, target: Union[Function, Field, Variable]) -> bool:
415433
supported = ("dict", "idict", "pool", "set", "vector")
434+
found = False
416435
if isinstance(target, Function):
417-
self.found_containers.update(
418-
self.find_containers(supported, target.return_type)
419-
)
436+
return_type_containers = self.find_containers(supported, target.return_type)
437+
found = found or len(return_type_containers)
438+
self.found_containers.update(return_type_containers)
420439

421440
for parameter in target.parameters:
422-
self.found_containers.update(
423-
self.find_containers(supported, parameter.type)
424-
)
441+
parameter_containers = self.find_containers(supported, parameter.type)
442+
found = found or len(parameter_containers)
443+
self.found_containers.update(parameter_containers)
425444
else:
426-
self.found_containers.update(self.find_containers(supported, target.type))
445+
variable_containers = self.find_containers(supported, target.type)
446+
found = found or len(variable_containers)
447+
self.found_containers.update(variable_containers)
448+
return found
427449

428450
# processors
429451
def get_overload_cast(
@@ -470,9 +492,9 @@ def get_definition_args(
470492

471493
def_args = [f'"{python_function_basename}"']
472494
def_args.append(self.get_overload_cast(function, class_basename))
473-
for parameter in function.parameters:
474-
# ASSUMPTION: there are no unnamed parameters in the yosys codebase
475-
parameter_arg = f'py::arg("{parameter.name}")'
495+
for i, parameter in enumerate(function.parameters):
496+
name = parameter.name or f"arg{i}"
497+
parameter_arg = f'py::arg("{name}")'
476498
if parameter.default is not None:
477499
parameter_arg += f" = {parameter.default.format()}"
478500
def_args.append(parameter_arg)
@@ -525,8 +547,12 @@ def process_method(self, metadata: PyosysClass, function: Method):
525547
if function.static:
526548
definition_fn = "def_static"
527549

550+
definition_args = self.get_definition_args(
551+
function, metadata.name, python_name_override
552+
)
553+
528554
print(
529-
f"\t\t\t.{definition_fn}({', '.join(self.get_definition_args(function, metadata.name, python_name_override))})",
555+
f"\t\t\t.{definition_fn}({', '.join(definition_args)})",
530556
file=self.f,
531557
)
532558

@@ -565,16 +591,21 @@ def process_field(self, metadata: PyosysClass, field: Field):
565591
# care
566592
return
567593

568-
self.register_containers(field)
594+
has_containers = self.register_containers(field)
569595

570596
definition_fn = f"def_{'readonly' if field.type.const else 'readwrite'}"
571597
if field.static:
572598
definition_fn += "_static"
573599

574600
field_python_basename = keyword_aliases.get(field.name, field.name)
575601

602+
def_args = [
603+
f'"{field_python_basename}"',
604+
f"&{metadata.name}::{field.name}",
605+
]
606+
def_args.append("py::return_value_policy::copy")
576607
print(
577-
f'\t\t\t.{definition_fn}("{field_python_basename}", &{metadata.name}::{field.name})',
608+
f"\t\t\t.{definition_fn}({', '.join(def_args)})",
578609
file=self.f,
579610
)
580611

@@ -603,16 +634,20 @@ def process_variable(self, variable: Variable):
603634
)
604635

605636
def process_class_members(
606-
self, metadata: PyosysClass, cls: ClassScope, basename: str
637+
self,
638+
metadata: PyosysClass,
639+
base_metadata: PyosysClass,
640+
cls: ClassScope,
641+
basename: str,
607642
):
608643
for method in cls.methods:
609-
if method.name.segments[-1].name in metadata.denylist:
644+
if method.name.segments[-1].name in base_metadata.denylist:
610645
continue
611646
self.process_method(metadata, method)
612647

613648
visited_anonymous_unions = set()
614649
for field_ in cls.fields:
615-
if field_.name in metadata.denylist:
650+
if field_.name in base_metadata.denylist:
616651
continue
617652
self.process_field(metadata, field_)
618653

@@ -627,6 +662,16 @@ def process_class_members(
627662
for subfield in subclass.fields:
628663
self.process_field(metadata, subfield)
629664

665+
for base in cls.class_decl.bases:
666+
if base.access != "public":
667+
continue
668+
name = base.typename.segments[-1].format()
669+
if processed := self.class_registry.get(name):
670+
base_scope, base_metadata = processed
671+
self.process_class_members(
672+
metadata, base_metadata, base_scope, basename
673+
)
674+
630675
def process_class(
631676
self,
632677
metadata: PyosysClass,
@@ -638,7 +683,7 @@ def process_class(
638683
segment.format() for segment in pqname.segments
639684
]
640685
basename = full_path.pop()
641-
self.class_registry[basename] = cls
686+
self.class_registry[basename] = (cls, metadata)
642687

643688
declaration_namespace = "::".join(full_path)
644689
tpl_args = [basename]
@@ -649,19 +694,17 @@ def process_class(
649694
file=self.f,
650695
)
651696

652-
self.process_class_members(metadata, cls, basename)
653-
for base in cls.class_decl.bases:
654-
if base.access != "public":
655-
continue
656-
name = base.typename.segments[-1].format()
657-
if base_scope := self.class_registry.get(name):
658-
self.process_class_members(metadata, base_scope, basename)
697+
self.process_class_members(metadata, metadata, cls, basename)
659698

660699
if expr := metadata.string_expr:
661700
print(
662701
f'\t\t.def("__str__", [](const {basename} &s) {{ return {expr}; }})',
663702
file=self.f,
664703
)
704+
print(
705+
f'\t\t.def("__repr__", [](const {basename} &s) {{ std::stringstream ss; ss << "<{basename} " << {expr} << ">"; return ss.str(); }})',
706+
file=self.f,
707+
)
665708

666709
if expr := metadata.hash_expr:
667710
print(

pyosys/wrappers_tpl.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,20 @@
2121
// <!-- generated includes -->
2222
#include <pybind11/pybind11.h>
2323
#include <pybind11/native_enum.h>
24+
#include <pybind11/functional.h>
25+
26+
// duplicates for LSPs
27+
#include "kernel/register.h"
28+
#include "kernel/yosys_common.h"
29+
2430
#include "pyosys/hashlib.h"
2531

2632
namespace py = pybind11;
2733

2834
USING_YOSYS_NAMESPACE
2935

3036
using std::set;
31-
using std::regex;
37+
using std::function;
3238
using std::ostream;
3339
using namespace RTLIL;
3440

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
from pyosys import libyosys as ys
3+
from pathlib import Path
4+
5+
__file_dir__ = Path(__file__).absolute().parent
6+
7+
d = ys.Design()
8+
ys.run_pass(f"read_verilog {__file_dir__ / 'spm.cut.v.gz'}", d)
9+
ys.run_pass("hierarchy -top spm", d)
10+
11+
external_idstring_holder_0 = None
12+
external_idstring_holder_1 = None
13+
14+
def get_top_module_idstring():
15+
global external_idstring_holder_0, external_idstring_holder_1
16+
d = ys.Design()
17+
ys.run_pass(f"read_verilog {__file_dir__ / 'spm.cut.v.gz'}", d)
18+
ys.run_pass("hierarchy -top spm", d)
19+
external_idstring_holder_0 = d.top_module().name
20+
for cell in d.top_module().cells_:
21+
print(f"TARGETED: {cell}", flush=True)
22+
external_idstring_holder_1 = cell
23+
break
24+
# d deallocates
25+
26+
get_top_module_idstring()
27+
print(external_idstring_holder_0, flush=True)
28+
print(external_idstring_holder_1, flush=True)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
from pyosys import libyosys as ys
3+
from pathlib import Path
4+
5+
__file_dir__ = Path(__file__).absolute().parent
6+
7+
8+
d = ys.Design()
9+
ys.run_pass(f"read_verilog {__file_dir__ / 'spm.cut.v.gz'}", d)
10+
ys.run_pass("hierarchy -top spm", d)
11+
12+
for idstr, cell in d.top_module().cells_.items():
13+
cell.set_bool_attribute("\\set")
14+
print(cell.attributes)
15+
break

tests/pyosys/test_monitor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def notify_module_add(self, mod):
1414
self.mods.append(mod.name.str())
1515

1616
m = Monitor()
17-
d.monitors.add(m)
17+
d.monitors = [m]
1818

1919
ys.run_pass(f"read_verilog {__file_dir__ / 'spm.cut.v.gz'}", d)
2020
ys.run_pass("hierarchy -top spm", d)

0 commit comments

Comments
 (0)