vunit-yavs 0.10.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,89 @@
1
+ Metadata-Version: 2.4
2
+ Name: vunit-yavs
3
+ Version: 0.10.0
4
+ Summary: The yavs VHDL simulator for VUnit, as a VUnit package
5
+ License-Expression: MPL-2.0
6
+ Project-URL: Source, https://github.com/ru551n/yavs/tree/main/python/vunit-yavs
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: vunit_hdl>=5.0.0.dev16
10
+
11
+ # vunit-yavs
12
+
13
+ The yavs simulator for VUnit, as a VUnit package. Add it in the run script before anything else,
14
+ then select yavs with `VUNIT_SIMULATOR=yavs`:
15
+
16
+ ```python
17
+ from vunit import VUnit
18
+
19
+ vu = VUnit.from_argv()
20
+ vu.add_package("vunit-yavs", allow_setup=True) # first: it provides the simulator
21
+ vu.add_vhdl_builtins()
22
+ vu.add_library("lib").add_source_files("*.vhd")
23
+ vu.main()
24
+ ```
25
+
26
+ ```sh
27
+ pip install vunit-yavs
28
+ VUNIT_SIMULATOR=yavs python run.py
29
+ ```
30
+
31
+ `yavs` is found on `PATH`, or in the directory named by `VUNIT_YAVS_PATH`. It needs VUnit 5.0.0.dev16
32
+ or later (`pip install vunit-yavs` installs it, a pre-release) and yavs 0.10.0 or later.
33
+
34
+ ## Options
35
+
36
+ | Sim or compile option | Passed to |
37
+ |---|---|
38
+ | `yavs.global_flags` | `yavs analyze`, `run` and `elab` |
39
+ | `yavs.a_flags` | `yavs analyze` |
40
+ | `yavs.elab_flags` | `yavs run` and `elab` |
41
+ | `yavs.sim_flags` | `yavs run` |
42
+ | `yavs.max_threads` (integer, at least 1) | `yavs run --threads=N`, the most threads of one simulation |
43
+
44
+ | Command line | What it does |
45
+ |---|---|
46
+ | `-g/--gui` | Opens the run in the graphical debugger (`yavs run --gui`) |
47
+ | `--yavs-debug[=PORT]` | Runs the selected test under `yavs run --dap=PORT` (default 4711, `0` picks a free port) and prints where to attach; write the port as `--yavs-debug=PORT` |
48
+ | `--yavs-max-threads N` | `yavs.max_threads` for every test that does not set it |
49
+ | `--yavs-jobserver` | Lends the CPU slots of `-p N` that no test uses to the running simulations (below) |
50
+
51
+ Without `yavs.max_threads` or `--yavs-max-threads` no thread count is passed, and yavs runs with
52
+ its default. An explicit `--threads` in `yavs.sim_flags` comes after the cap and wins.
53
+
54
+ ## Debugging one test
55
+
56
+ ```sh
57
+ VUNIT_SIMULATOR=yavs python run.py lib.tb.test_name --yavs-debug
58
+ ```
59
+
60
+ yavs waits for a Debug Adapter Protocol client on `127.0.0.1:4711` before the simulation starts;
61
+ attach the editor (the yavs extension's "Attach to yavs" configuration uses that port). When the
62
+ session ends, the test is reported as usual.
63
+
64
+ `--yavs-debug` runs one simulation. A second simulation of the run (a second test, or the same
65
+ test in another configuration) is an error before yavs starts, naming both tests. `--yavs-debug`
66
+ together with `--gui`, `--elaborate` or `-p` above 1 is an error at the start. A bare
67
+ `--yavs-debug` reads a following test name as its port: put test names first, or write
68
+ `--yavs-debug=4711`.
69
+
70
+ ## Jobserver
71
+
72
+ With `-p N --yavs-jobserver` (POSIX only; on Windows it is an error) every running test gets a
73
+ private GNU make jobserver, a named pipe in `MAKEFLAGS=-jN --jobserver-auth=fifo:PATH`, and a
74
+ thread of the run script lends tokens into the pipes: one CPU slot per running test is free, the
75
+ other `N - k` slots (for `k` running tests) are lent, breadth first (every test gets one extra
76
+ before any gets a second) and newest test first within a round, capped per test by
77
+ `yavs.max_threads - 1`. Tokens a simulation leaves unread are taken back and lent again every
78
+ 50 ms, and a test that leaves tokens unread gets at most one more than it uses. A test that
79
+ starts while every token is in use runs at once, so the total is `N` in the steady state, not at
80
+ every instant. A simulation that exits holding tokens, or a run that ends with tokens out, is
81
+ reported with a warning.
82
+
83
+ `MAKEFLAGS` from an outer `make -j` is replaced, not passed on: joining an outer jobserver is not
84
+ supported. The pipes live in a temporary directory of their own, since `MAKEFLAGS` cannot hold a
85
+ path with spaces.
86
+
87
+ ## Licence
88
+
89
+ Mozilla Public License 2.0 (https://mozilla.org/MPL/2.0/), see the header of each file.
@@ -0,0 +1,79 @@
1
+ # vunit-yavs
2
+
3
+ The yavs simulator for VUnit, as a VUnit package. Add it in the run script before anything else,
4
+ then select yavs with `VUNIT_SIMULATOR=yavs`:
5
+
6
+ ```python
7
+ from vunit import VUnit
8
+
9
+ vu = VUnit.from_argv()
10
+ vu.add_package("vunit-yavs", allow_setup=True) # first: it provides the simulator
11
+ vu.add_vhdl_builtins()
12
+ vu.add_library("lib").add_source_files("*.vhd")
13
+ vu.main()
14
+ ```
15
+
16
+ ```sh
17
+ pip install vunit-yavs
18
+ VUNIT_SIMULATOR=yavs python run.py
19
+ ```
20
+
21
+ `yavs` is found on `PATH`, or in the directory named by `VUNIT_YAVS_PATH`. It needs VUnit 5.0.0.dev16
22
+ or later (`pip install vunit-yavs` installs it, a pre-release) and yavs 0.10.0 or later.
23
+
24
+ ## Options
25
+
26
+ | Sim or compile option | Passed to |
27
+ |---|---|
28
+ | `yavs.global_flags` | `yavs analyze`, `run` and `elab` |
29
+ | `yavs.a_flags` | `yavs analyze` |
30
+ | `yavs.elab_flags` | `yavs run` and `elab` |
31
+ | `yavs.sim_flags` | `yavs run` |
32
+ | `yavs.max_threads` (integer, at least 1) | `yavs run --threads=N`, the most threads of one simulation |
33
+
34
+ | Command line | What it does |
35
+ |---|---|
36
+ | `-g/--gui` | Opens the run in the graphical debugger (`yavs run --gui`) |
37
+ | `--yavs-debug[=PORT]` | Runs the selected test under `yavs run --dap=PORT` (default 4711, `0` picks a free port) and prints where to attach; write the port as `--yavs-debug=PORT` |
38
+ | `--yavs-max-threads N` | `yavs.max_threads` for every test that does not set it |
39
+ | `--yavs-jobserver` | Lends the CPU slots of `-p N` that no test uses to the running simulations (below) |
40
+
41
+ Without `yavs.max_threads` or `--yavs-max-threads` no thread count is passed, and yavs runs with
42
+ its default. An explicit `--threads` in `yavs.sim_flags` comes after the cap and wins.
43
+
44
+ ## Debugging one test
45
+
46
+ ```sh
47
+ VUNIT_SIMULATOR=yavs python run.py lib.tb.test_name --yavs-debug
48
+ ```
49
+
50
+ yavs waits for a Debug Adapter Protocol client on `127.0.0.1:4711` before the simulation starts;
51
+ attach the editor (the yavs extension's "Attach to yavs" configuration uses that port). When the
52
+ session ends, the test is reported as usual.
53
+
54
+ `--yavs-debug` runs one simulation. A second simulation of the run (a second test, or the same
55
+ test in another configuration) is an error before yavs starts, naming both tests. `--yavs-debug`
56
+ together with `--gui`, `--elaborate` or `-p` above 1 is an error at the start. A bare
57
+ `--yavs-debug` reads a following test name as its port: put test names first, or write
58
+ `--yavs-debug=4711`.
59
+
60
+ ## Jobserver
61
+
62
+ With `-p N --yavs-jobserver` (POSIX only; on Windows it is an error) every running test gets a
63
+ private GNU make jobserver, a named pipe in `MAKEFLAGS=-jN --jobserver-auth=fifo:PATH`, and a
64
+ thread of the run script lends tokens into the pipes: one CPU slot per running test is free, the
65
+ other `N - k` slots (for `k` running tests) are lent, breadth first (every test gets one extra
66
+ before any gets a second) and newest test first within a round, capped per test by
67
+ `yavs.max_threads - 1`. Tokens a simulation leaves unread are taken back and lent again every
68
+ 50 ms, and a test that leaves tokens unread gets at most one more than it uses. A test that
69
+ starts while every token is in use runs at once, so the total is `N` in the steady state, not at
70
+ every instant. A simulation that exits holding tokens, or a run that ends with tokens out, is
71
+ reported with a warning.
72
+
73
+ `MAKEFLAGS` from an outer `make -j` is replaced, not passed on: joining an outer jobserver is not
74
+ supported. The pipes live in a temporary directory of their own, since `MAKEFLAGS` cannot hold a
75
+ path with spaces.
76
+
77
+ ## Licence
78
+
79
+ Mozilla Public License 2.0 (https://mozilla.org/MPL/2.0/), see the header of each file.
@@ -0,0 +1,25 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "vunit-yavs"
7
+ version = "0.10.0"
8
+ description = "The yavs VHDL simulator for VUnit, as a VUnit package"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MPL-2.0"
12
+ # The first VUnit release with simulator packages (the vunit_pkg.toml key 'simulator')
13
+ dependencies = ["vunit_hdl>=5.0.0.dev16"]
14
+
15
+ [project.urls]
16
+ Source = "https://github.com/ru551n/yavs/tree/main/python/vunit-yavs"
17
+
18
+ [tool.setuptools]
19
+ packages = ["vunit_yavs"]
20
+
21
+ [tool.setuptools.package-data]
22
+ vunit_yavs = ["vunit_pkg.toml"]
23
+
24
+ [tool.black]
25
+ line-length = 120
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,159 @@
1
+ # This Source Code Form is subject to the terms of the Mozilla Public
2
+ # License, v. 2.0. If a copy of the MPL was not distributed with this file,
3
+ # You can obtain one at http://mozilla.org/MPL/2.0/.
4
+ #
5
+ # Copyright (c) 2014-2026, Lars Asplund lars.anders.asplund@gmail.com
6
+
7
+ """
8
+ End to end: the project's run.py adds the package and runs against the fake yavs
9
+ """
10
+
11
+ import json
12
+ import os
13
+ import subprocess
14
+ import sys
15
+ import tempfile
16
+ import unittest
17
+ from pathlib import Path
18
+ from xml.etree import ElementTree
19
+
20
+ HERE = Path(__file__).parent
21
+ PASS = "lib.tb_yavs.test_pass"
22
+ COMMA = "lib.tb_yavs.test with, comma: and colon"
23
+ FAIL = "lib.tb_yavs.test_fail"
24
+
25
+
26
+ def report(xml_file):
27
+ """Return {test name: status} of a JUnit report"""
28
+ result = {}
29
+ for case in ElementTree.parse(xml_file).iter("testcase"):
30
+ status = (
31
+ "failed"
32
+ if case.find("failure") is not None
33
+ else "skipped" if case.find("skipped") is not None else "passed"
34
+ )
35
+ result[f"{case.get('classname')}.{case.get('name')}"] = status
36
+ return result
37
+
38
+
39
+ @unittest.skipIf(sys.platform == "win32", "The fake yavs is a Python script without an .exe wrapper")
40
+ class TestFakeYavs(unittest.TestCase):
41
+ """
42
+ Run a real run.py with VUNIT_SIMULATOR=yavs against the fake yavs
43
+ """
44
+
45
+ def _run(self, *args, fail="", version="0.10.0"):
46
+ """Run run.py and return the exit code, the output, the JUnit results and the logged yavs calls"""
47
+ with tempfile.TemporaryDirectory() as tempdir:
48
+ tempdir = Path(tempdir)
49
+ log = tempdir / "yavs.log"
50
+ env = dict(
51
+ os.environ,
52
+ VUNIT_SIMULATOR="yavs",
53
+ VUNIT_YAVS_PATH=str(HERE / "fake_bin"),
54
+ FAKE_YAVS_LOG=str(log),
55
+ FAKE_YAVS_FAIL=fail,
56
+ FAKE_YAVS_VERSION=version,
57
+ )
58
+ xml = tempdir / "x.xml"
59
+ proc = subprocess.run(
60
+ [sys.executable, str(HERE / "project" / "run.py"), "--clean", "-o", str(tempdir / "out with space")]
61
+ + ["-x", str(xml)]
62
+ + list(args),
63
+ env=env,
64
+ stdout=subprocess.PIPE,
65
+ stderr=subprocess.STDOUT,
66
+ text=True,
67
+ timeout=120,
68
+ check=False,
69
+ )
70
+ calls = [json.loads(line) for line in log.read_text(encoding="utf-8").splitlines()] if log.exists() else []
71
+ results = report(xml) if xml.exists() else {}
72
+ return proc.returncode, proc.stdout, results, calls
73
+
74
+ @staticmethod
75
+ def _runs(calls):
76
+ return [cmd for cmd in calls if cmd[0] == "run"]
77
+
78
+ def test_pass(self):
79
+ exit_code, _, results, calls = self._run(PASS, COMMA)
80
+ self.assertEqual(exit_code, 0)
81
+ self.assertEqual(results, {PASS: "passed", COMMA: "passed"})
82
+ self.assertTrue(any(cmd[0] == "analyze" and cmd[-1].endswith("tb_yavs.vhd") for cmd in calls))
83
+ runs = self._runs(calls)
84
+ self.assertEqual(len(runs), 2)
85
+ self.assertIn("tb_yavs.tb", runs[0])
86
+ self.assertIn("--exit-severity=error", runs[0])
87
+ self.assertFalse(any(arg.startswith(("--threads", "--dap")) for arg in runs[0]))
88
+ for cmd in calls:
89
+ if cmd[0] in ("analyze", "elab", "run"):
90
+ flags = [arg for arg in cmd if arg.startswith("--std-cache=")]
91
+ self.assertEqual(len(flags), 1, cmd)
92
+ self.assertRegex(flags[0], r"out with space[/\\]yavs[/\\]std_cache$")
93
+
94
+ def test_fail(self):
95
+ exit_code, _, results, _ = self._run(fail="test_fail")
96
+ self.assertEqual(exit_code, 1)
97
+ self.assertEqual(results, {PASS: "passed", COMMA: "passed", FAIL: "failed"})
98
+
99
+ def test_elaborate_only(self):
100
+ exit_code, _, _, calls = self._run("--elaborate", PASS)
101
+ self.assertEqual(exit_code, 0)
102
+ self.assertTrue(any(cmd[0] == "elab" for cmd in calls))
103
+ self.assertFalse(self._runs(calls))
104
+
105
+ def test_too_old_yavs_fails_before_compiling(self):
106
+ exit_code, output, _, calls = self._run(PASS, version="0.8.2")
107
+ self.assertNotEqual(exit_code, 0)
108
+ self.assertIn("yavs 0.8.2 at ", output)
109
+ self.assertIn("which needs yavs 0.10.0 or later", output)
110
+ self.assertFalse([call for call in calls if call[0] == "analyze"])
111
+
112
+ def test_max_threads(self):
113
+ exit_code, _, _, calls = self._run("--yavs-max-threads=3", PASS)
114
+ self.assertEqual(exit_code, 0)
115
+ self.assertIn("--threads=3", self._runs(calls)[0])
116
+
117
+ def test_jobserver(self):
118
+ exit_code, output, results, calls = self._run("-p", "2", "--yavs-jobserver", "--yavs-max-threads=2")
119
+ self.assertEqual(exit_code, 0, output)
120
+ self.assertEqual(set(results.values()), {"passed"})
121
+ uses = [call for call in calls if call[0] == "jobserver"]
122
+ self.assertEqual(len(uses), 3)
123
+ for _, flags, _ in uses:
124
+ self.assertRegex(flags, r"^-j2 --jobserver-auth=fifo:\S+/fifo\d+$")
125
+ # A test running alone gets the one free slot of -p 2
126
+ self.assertIn("+", [token for _, _, token in uses])
127
+ self.assertNotIn("token(s) without returning them", output)
128
+
129
+ def test_no_jobserver_without_the_flag(self):
130
+ _, _, _, calls = self._run("-p", "2", PASS, COMMA)
131
+ self.assertFalse([call for call in calls if call[0] == "jobserver"])
132
+
133
+ def test_debug_one_test(self):
134
+ exit_code, output, results, calls = self._run("--yavs-debug=5000", "--yavs-max-threads=3", PASS)
135
+ self.assertEqual(exit_code, 0, output)
136
+ self.assertEqual(results, {PASS: "passed"})
137
+ (run,) = self._runs(calls)
138
+ self.assertEqual(run[1], "--dap=5000")
139
+ self.assertFalse(any(arg.startswith("--threads") for arg in run))
140
+ self.assertIn(f"Debugging {PASS}: attach a DAP client to 127.0.0.1:5000", output)
141
+
142
+ def test_debug_refuses_the_second_test(self):
143
+ exit_code, output, results, calls = self._run("--yavs-debug=5000", PASS, COMMA)
144
+ self.assertEqual(exit_code, 1)
145
+ self.assertEqual(len(self._runs(calls)), 1)
146
+ self.assertEqual(sorted(results.values()), ["failed", "passed"])
147
+ self.assertIn("--yavs-debug runs one simulation", output)
148
+
149
+ def test_debug_refuses_parallel_simulations(self):
150
+ exit_code, output, _, calls = self._run("--yavs-debug", "-p", "2", PASS)
151
+ self.assertNotEqual(exit_code, 0)
152
+ self.assertIn("-p 2 asks for parallel simulations", output)
153
+ self.assertFalse(self._runs(calls))
154
+
155
+ def test_debug_refuses_elaborate(self):
156
+ exit_code, output, _, calls = self._run(PASS, "--elaborate", "--yavs-debug")
157
+ self.assertNotEqual(exit_code, 0)
158
+ self.assertIn("--yavs-debug and --elaborate cannot be combined", output)
159
+ self.assertFalse(any(cmd[0] == "elab" for cmd in calls))