3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2026-02-26 10:05:40 +00:00

Merge pull request #5370 from donn/pyosys_pybind11

pyosys: rewrite using pybind11
This commit is contained in:
Miodrag Milanović 2025-10-08 13:07:59 +02:00 committed by GitHub
commit ba1a347d59
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
38 changed files with 2382 additions and 2722 deletions

46
tests/pyosys/run_tests.py Normal file
View file

@ -0,0 +1,46 @@
import sys
import shutil
import shlex
import subprocess
from pathlib import Path
__file_dir__ = Path(__file__).absolute().parent
if len(sys.argv) > 2 or len(sys.argv) == 2 and sys.argv[1] != 'yosys':
print(f"Usage: {sys.argv[0]} [yosys]")
exit(64)
if len(sys.argv) == 2:
binary = [str(__file_dir__.parents[1] / "yosys"), "-Qy"]
else:
binary = [sys.executable or shutil.which("python3") or "python3"] # sys.executable can actually be None
tests = __file_dir__.glob("test_*.py")
log_dir = __file_dir__ / "logs"
try:
shutil.rmtree(log_dir)
except FileNotFoundError:
pass
fail_logs = set()
for test in tests:
print(f"* {test.name} ", end="")
log_dir.mkdir(parents=True, exist_ok=True)
log = log_dir / (test.stem + ".log")
cmd = [*binary, str(test)]
log_file = open(log, "w", encoding="utf8")
log_file.write(f"$ {shlex.join(cmd)}\n")
log_file.flush()
result = subprocess.run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
if result.returncode == 0:
print("OK!")
else:
print(f"FAILED: {log}")
fail_logs.add(log)
log_file.close()
for log in fail_logs:
print(f">>> {log}")
with open(log, encoding="utf8") as f:
print(f.read())
if len(fail_logs):
exit(1)

BIN
tests/pyosys/spm.cut.v.gz Normal file

Binary file not shown.

View file

@ -0,0 +1,45 @@
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)
name_by_tv_location = []
name_by_au_location = []
# test both dictionary mapping and equiv operators working fine
module = None
print(d.modules_)
for idstr, module_obj in d.modules_.items():
if idstr != ys.IdString("\\spm"):
continue
if idstr.str() != "\\spm":
continue
module = module_obj
break
assert module == d.top_module(), "top module search failed"
for name in module.ports:
wire = module.wires_[name]
name_str = name.str()
if name_str.endswith(".d"): # single reg output, in au
name_by_au_location.append(name_str[1:-2])
elif name_str.endswith(".q"): # single reg input, in tv
name_by_tv_location.append(name_str[1:-2])
else: # port/boundary scan
frm = wire.start_offset + wire.width
to = wire.start_offset
for i in range(frm - 1, to - 1, -1):
bit_name = name_str[1:] + f"\\[{i}\\]"
if wire.port_input:
name_by_tv_location.append(bit_name)
elif wire.port_output:
name_by_au_location.append(bit_name)
assert name_by_tv_location == ['x\\[0\\]', 'a\\[31\\]', 'a\\[30\\]', 'a\\[29\\]', 'a\\[28\\]', 'a\\[27\\]', 'a\\[26\\]', 'a\\[25\\]', 'a\\[24\\]', 'a\\[23\\]', 'a\\[22\\]', 'a\\[21\\]', 'a\\[20\\]', 'a\\[19\\]', 'a\\[18\\]', 'a\\[17\\]', 'a\\[16\\]', 'a\\[15\\]', 'a\\[14\\]', 'a\\[13\\]', 'a\\[12\\]', 'a\\[11\\]', 'a\\[10\\]', 'a\\[9\\]', 'a\\[8\\]', 'a\\[7\\]', 'a\\[6\\]', 'a\\[5\\]', 'a\\[4\\]', 'a\\[3\\]', 'a\\[2\\]', 'a\\[1\\]', 'a\\[0\\]', '_315_', '_314_', '_313_', '_312_', '_311_', '_310_', '_309_', '_308_', '_307_', '_306_', '_305_', '_304_', '_303_', '_302_', '_301_', '_300_', '_299_', '_298_', '_297_', '_296_', '_295_', '_294_', '_293_', '_292_', '_291_', '_290_', '_289_', '_288_', '_287_', '_286_', '_285_', '_284_', '_283_', '_282_', '_281_', '_280_', '_279_', '_278_', '_277_', '_276_', '_275_', '_274_', '_273_', '_272_', '_271_', '_270_', '_269_', '_268_', '_267_', '_266_', '_265_', '_264_', '_263_', '_262_', '_261_', '_260_', '_259_', '_258_', '_257_', '_256_', '_255_', '_254_', '_253_', '_252_'], "failed to extract test vector register locations"
assert name_by_au_location == ['y\\[0\\]', '_315_', '_314_', '_313_', '_312_', '_311_', '_310_', '_309_', '_308_', '_307_', '_306_', '_305_', '_304_', '_303_', '_302_', '_301_', '_300_', '_299_', '_298_', '_297_', '_296_', '_295_', '_294_', '_293_', '_292_', '_291_', '_290_', '_289_', '_288_', '_287_', '_286_', '_285_', '_284_', '_283_', '_282_', '_281_', '_280_', '_279_', '_278_', '_277_', '_276_', '_275_', '_274_', '_273_', '_272_', '_271_', '_270_', '_269_', '_268_', '_267_', '_266_', '_265_', '_264_', '_263_', '_262_', '_261_', '_260_', '_259_', '_258_', '_257_', '_256_', '_255_', '_254_', '_253_', '_252_'], "failed to extract golden output register locations"
print("ok!")

44
tests/pyosys/test_dict.py Normal file
View file

@ -0,0 +1,44 @@
from typing import Mapping
from pyosys import libyosys as ys
StringToStringDict = ys.StringToStringDict
my_dict = StringToStringDict()
assert isinstance(my_dict, Mapping)
my_dict["foo"] = "bar"
my_dict.update([("first", "second")])
my_dict.update({"key": "value"})
for key, value in my_dict.items():
print(key, value)
new_dict = my_dict | {"tomato": "tomato"}
del new_dict["foo"]
assert set(my_dict.keys()) == {"first", "key", "foo"}
assert set(new_dict.keys()) == {"first", "key", "tomato"}
constructor_test_1 = ys.StringToStringDict(new_dict)
constructor_test_2 = ys.StringToStringDict([("tomato", "tomato")])
constructor_test_3 = ys.StringToStringDict({ "im running": "out of string ideas" })
the_great_or = constructor_test_1 | constructor_test_2 | constructor_test_3
assert set(the_great_or) == {"first", "key", "tomato", "im running"}
repr_test = eval(repr(the_great_or))
assert repr_test == the_great_or # compare dicts
assert repr_test == {'tomato': 'tomato', 'first': 'second', 'key': 'value', 'im running': 'out of string ideas', } # compare dict with mapping
before = len(repr_test)
print(repr_test.popitem())
assert before - 1 == len(repr_test)
# test noncomparable
## if ys.CellType ever gets an == operator just disable this section
uncomparable_value = ys.Globals.yosys_celltypes.cell_types[ys.IdString("$not")]
x = ys.IdstringToCelltypeDict({ ys.IdString("\\a"): uncomparable_value})
y = ys.IdstringToCelltypeDict({ ys.IdString("\\a"): uncomparable_value})
assert x != y # not comparable

View file

@ -0,0 +1,31 @@
from pyosys import libyosys as ys
my_idict = ys.IdstringIdict()
print(my_idict(ys.IdString("\\hello"))) # test explicit IdString construction
print(my_idict("\\world"))
print(my_idict.get("\\world"))
try:
print(my_idict.get("\\dummy"))
except IndexError as e:
print(f"{repr(e)}")
print(my_idict[0])
print(my_idict[1])
try:
print(my_idict[2])
except IndexError as e:
print(f"{repr(e)}")
for i in my_idict:
print(i)
current_len = len(my_idict)
assert current_len == 2, "copy"
my_copy = my_idict.copy()
my_copy("\\copy")
assert len(my_idict) == current_len, "copy seemed to have mutate original idict"
assert len(my_copy) == current_len + 1, "copy not behaving as expected"
current_copy_len = len(my_copy)
my_copy |= ("\\the", "\\world") # 1 new element
assert len(my_copy) == current_copy_len + 1, "or operator returned unexpected result"

View file

@ -0,0 +1,20 @@
import os
import sys
from pyosys import libyosys as ys
print(ys)
ys.log("Hello, world!\n")
from pyosys.libyosys import log
print(log)
log("Goodbye, world!\n")
import pyosys
if os.path.basename(sys.executable) == "yosys":
# make sure it's not importing the directory
assert "built-in" in repr(pyosys)

View file

@ -0,0 +1,8 @@
from pyosys import libyosys as ys
d = ys.Design(); ys.log_header(d, "foo\n")
ys.log("foo\n")
ys.log_warning("foo\n")
ys.log_warning_noprefix("foo\n")
ys.log_file_info("foo.ys", 1, "foo\n")
ys.log_file_warning("foo.ys", 1, "foo\n")

View file

@ -0,0 +1,22 @@
from pyosys import libyosys as ys
from pathlib import Path
__file_dir__ = Path(__file__).absolute().parent
d = ys.Design()
class Monitor(ys.Monitor):
def __init__(self):
super().__init__()
self.mods = []
def notify_module_add(self, mod):
self.mods.append(mod.name.str())
m = Monitor()
d.monitors.add(m)
ys.run_pass(f"read_verilog {__file_dir__ / 'spm.cut.v.gz'}", d)
ys.run_pass("hierarchy -top spm", d)
assert m.mods == ["\\spm"]

34
tests/pyosys/test_pass.py Normal file
View file

@ -0,0 +1,34 @@
from pyosys import libyosys as ys
import json
from pathlib import Path
__file_dir__ = Path(__file__).absolute().parent
class CellStatsPass(ys.Pass):
def __init__(self):
super().__init__(
"cell_stats",
"dumps cell statistics in JSON format"
)
def execute(self, args, design):
ys.log_header(design, "Dumping cell stats\n")
ys.log_push()
cell_stats = {}
for module in design.all_selected_whole_modules():
for cell in module.selected_cells():
if cell.type.str() in cell_stats:
cell_stats[cell.type.str()] += 1
else:
cell_stats[cell.type.str()] = 1
ys.log(json.dumps(cell_stats))
ys.log_pop()
p = CellStatsPass() # registration
design = ys.Design()
ys.run_pass(f"read_verilog {__file_dir__.parent / 'simple' / 'fiedler-cooley.v'}", design)
ys.run_pass("prep", design)
ys.run_pass("opt -full", design)
ys.run_pass("cell_stats", design)

View file

@ -0,0 +1,21 @@
from pathlib import Path
from pyosys import libyosys as ys
__file_dir__ = Path(__file__).absolute().parent
add_sub = __file_dir__.parent / "arch" / "common" / "add_sub.v"
base = ys.Design()
ys.run_pass(f"read_verilog {add_sub}", base)
ys.run_pass("hierarchy -top top", base)
ys.run_pass("proc", base)
ys.run_pass("equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5", base)
postopt = ys.Design()
ys.run_pass("design -load postopt", postopt)
ys.run_pass("cd top", postopt)
ys.run_pass("select -assert-min 25 t:LUT4", postopt)
ys.run_pass("select -assert-max 26 t:LUT4", postopt)
ys.run_pass("select -assert-count 10 t:PFUMX", postopt)
ys.run_pass("select -assert-count 6 t:L6MUX21", postopt)
ys.run_pass("select -assert-none t:LUT4 t:PFUMX t:L6MUX21 %% t:* %D", postopt)

42
tests/pyosys/test_set.py Normal file
View file

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
from pyosys.libyosys import StringSet, StringPool
for cls in [StringSet, StringPool]:
print(f"Testing {cls.__name__}...")
A = cls()
A.add("a")
B = cls()
B = A | {"b"}
assert A < B
assert A <= B
A.add("b")
assert A == B
assert A <= B
assert not A < B
A.add("c")
assert A > B
A &= B
assert A == B
Ø = A - B
assert len(Ø) == 0
C = cls({"A", "B", "C"})
D = cls()
C |= {"A", "B", "C"}
D |= {"C", "D", "E"}
c_symdiff_d = (C ^ D)
assert c_symdiff_d == {"A", "B", "D", "E"} # compare against iterable
repr_test = eval(repr(c_symdiff_d))
assert c_symdiff_d == repr_test # compare against self
print("Done.")