mirror of
https://github.com/YosysHQ/yosys
synced 2026-02-26 18:15:39 +00:00
Merge pull request #5370 from donn/pyosys_pybind11
pyosys: rewrite using pybind11
This commit is contained in:
commit
ba1a347d59
38 changed files with 2382 additions and 2722 deletions
46
tests/pyosys/run_tests.py
Normal file
46
tests/pyosys/run_tests.py
Normal 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
BIN
tests/pyosys/spm.cut.v.gz
Normal file
Binary file not shown.
45
tests/pyosys/test_data_read.py
Normal file
45
tests/pyosys/test_data_read.py
Normal 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
44
tests/pyosys/test_dict.py
Normal 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
|
||||
31
tests/pyosys/test_idict.py
Normal file
31
tests/pyosys/test_idict.py
Normal 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"
|
||||
20
tests/pyosys/test_import.py
Normal file
20
tests/pyosys/test_import.py
Normal 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)
|
||||
8
tests/pyosys/test_logs.py
Normal file
8
tests/pyosys/test_logs.py
Normal 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")
|
||||
22
tests/pyosys/test_monitor.py
Normal file
22
tests/pyosys/test_monitor.py
Normal 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
34
tests/pyosys/test_pass.py
Normal 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)
|
||||
21
tests/pyosys/test_script.py
Normal file
21
tests/pyosys/test_script.py
Normal 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
42
tests/pyosys/test_set.py
Normal 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.")
|
||||
Loading…
Add table
Add a link
Reference in a new issue