primitive 0.1.37__py3-none-any.whl → 0.1.39__py3-none-any.whl
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.
- primitive/__about__.py +1 -1
- primitive/agent/actions.py +3 -19
- primitive/lint/actions.py +24 -12
- primitive/utils/cache.py +41 -0
- primitive/utils/verible.py +8 -6
- {primitive-0.1.37.dist-info → primitive-0.1.39.dist-info}/METADATA +1 -1
- {primitive-0.1.37.dist-info → primitive-0.1.39.dist-info}/RECORD +10 -10
- primitive/sim/vcd.py +0 -761
- {primitive-0.1.37.dist-info → primitive-0.1.39.dist-info}/WHEEL +0 -0
- {primitive-0.1.37.dist-info → primitive-0.1.39.dist-info}/entry_points.txt +0 -0
- {primitive-0.1.37.dist-info → primitive-0.1.39.dist-info}/licenses/LICENSE.txt +0 -0
primitive/__about__.py
CHANGED
primitive/agent/actions.py
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
import platform
|
2
1
|
import sys
|
3
2
|
import shutil
|
4
3
|
from pathlib import Path
|
@@ -8,6 +7,7 @@ from loguru import logger
|
|
8
7
|
from primitive.__about__ import __version__
|
9
8
|
import yaml
|
10
9
|
from ..utils.yaml import generate_script_from_yaml
|
10
|
+
from ..utils.cache import get_sources_cache
|
11
11
|
|
12
12
|
try:
|
13
13
|
from yaml import CLoader as Loader
|
@@ -16,22 +16,6 @@ except ImportError:
|
|
16
16
|
|
17
17
|
|
18
18
|
class Agent(BaseAction):
|
19
|
-
def set_cache_dir(self):
|
20
|
-
os_family = platform.system()
|
21
|
-
|
22
|
-
if os_family == "Darwin":
|
23
|
-
self.cache_dir = Path(
|
24
|
-
Path.home() / "Library" / "Caches" / "tech.primitive.agent"
|
25
|
-
)
|
26
|
-
elif os_family == "Linux":
|
27
|
-
self.cache_dir = Path(Path.home() / ".cache" / "primitive")
|
28
|
-
elif os_family == "Windows":
|
29
|
-
raise NotImplementedError("Windows is not currently supported.")
|
30
|
-
self.cache_dir = None
|
31
|
-
|
32
|
-
if not self.cache_dir.exists():
|
33
|
-
self.cache_dir.mkdir(parents=True, exist_ok=True)
|
34
|
-
|
35
19
|
def execute(
|
36
20
|
self,
|
37
21
|
):
|
@@ -40,7 +24,7 @@ class Agent(BaseAction):
|
|
40
24
|
logger.info(f" [*] Version: {__version__}")
|
41
25
|
|
42
26
|
# Create cache dir if it doesnt exist
|
43
|
-
|
27
|
+
cache_dir = get_sources_cache()
|
44
28
|
|
45
29
|
# self.primitive.hardware.update_hardware_system_info()
|
46
30
|
try:
|
@@ -127,7 +111,7 @@ class Agent(BaseAction):
|
|
127
111
|
git_repo_full_name=git_repo_full_name,
|
128
112
|
git_ref=git_ref,
|
129
113
|
github_access_token=github_access_token,
|
130
|
-
destination=
|
114
|
+
destination=cache_dir,
|
131
115
|
)
|
132
116
|
)
|
133
117
|
|
primitive/lint/actions.py
CHANGED
@@ -5,6 +5,7 @@ from typing import Tuple
|
|
5
5
|
from loguru import logger
|
6
6
|
from ..utils.files import find_files_for_extension
|
7
7
|
from ..utils.verible import install_verible
|
8
|
+
from ..utils.cache import get_deps_cache
|
8
9
|
|
9
10
|
|
10
11
|
class Lint(BaseAction):
|
@@ -17,20 +18,31 @@ class Lint(BaseAction):
|
|
17
18
|
return False, message
|
18
19
|
|
19
20
|
logger.debug("Checking if verible is installed")
|
21
|
+
verible_path = Path("verible-verilog-lint")
|
20
22
|
try:
|
21
|
-
subprocess.run([
|
23
|
+
subprocess.run([str(verible_path), "--version"], capture_output=True)
|
22
24
|
except FileNotFoundError:
|
23
|
-
logger.debug("
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
25
|
+
logger.debug("Verible not found in $PATH. Looking in deps cache...")
|
26
|
+
cache_dir = get_deps_cache()
|
27
|
+
|
28
|
+
possible_dirs = cache_dir.glob("verible*")
|
29
|
+
verible_dir = next(possible_dirs, None)
|
30
|
+
|
31
|
+
if verible_dir is not None:
|
32
|
+
verible_path = verible_dir / "bin" / verible_path
|
33
|
+
else:
|
34
|
+
logger.debug("Did not find verible. Installing...")
|
35
|
+
try:
|
36
|
+
system_info = self.primitive.hardware.get_system_info()
|
37
|
+
verible_bin = install_verible(system_info=system_info)
|
38
|
+
verible_path = verible_bin / verible_path
|
39
|
+
except Exception as exception:
|
40
|
+
message = f"Failed to install verible. {exception}"
|
41
|
+
logger.error(message)
|
42
|
+
return False, message
|
31
43
|
|
32
44
|
try:
|
33
|
-
subprocess.run([
|
45
|
+
subprocess.run([str(verible_path), "--version"], capture_output=True)
|
34
46
|
except FileNotFoundError:
|
35
47
|
message = "Verible is not installed. Please install it to run lint."
|
36
48
|
logger.error(message)
|
@@ -40,7 +52,7 @@ class Lint(BaseAction):
|
|
40
52
|
# run is great for now! we will need to switch to Popen if we want to stream the output
|
41
53
|
logger.debug("Running linter...")
|
42
54
|
result = subprocess.run(
|
43
|
-
[
|
55
|
+
[str(verible_path), *files],
|
44
56
|
capture_output=True,
|
45
57
|
text=True,
|
46
58
|
)
|
@@ -56,7 +68,7 @@ class Lint(BaseAction):
|
|
56
68
|
|
57
69
|
if result.returncode != 0:
|
58
70
|
if not self.primitive.DEBUG:
|
59
|
-
message = result.stderr
|
71
|
+
message = result.stdout + result.stderr
|
60
72
|
return (False, message)
|
61
73
|
else:
|
62
74
|
message = "Linting successful."
|
primitive/utils/cache.py
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
import platform
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
|
5
|
+
def get_cache_dir() -> Path:
|
6
|
+
os_family = platform.system()
|
7
|
+
|
8
|
+
cache_dir = None
|
9
|
+
if os_family == "Darwin":
|
10
|
+
cache_dir = Path(Path.home() / "Library" / "Caches" / "tech.primitive.agent")
|
11
|
+
elif os_family == "Linux":
|
12
|
+
cache_dir = Path(Path.home() / ".cache" / "primitive")
|
13
|
+
elif os_family == "Windows":
|
14
|
+
raise NotImplementedError("Windows is not currently supported.")
|
15
|
+
|
16
|
+
if not cache_dir.exists():
|
17
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
18
|
+
|
19
|
+
return cache_dir
|
20
|
+
|
21
|
+
|
22
|
+
def get_sources_cache() -> Path:
|
23
|
+
cache_dir = get_cache_dir()
|
24
|
+
|
25
|
+
sources_dir = cache_dir / "sources"
|
26
|
+
|
27
|
+
if not sources_dir.exists():
|
28
|
+
sources_dir.mkdir(parents=True, exist_ok=True)
|
29
|
+
|
30
|
+
return sources_dir
|
31
|
+
|
32
|
+
|
33
|
+
def get_deps_cache() -> Path:
|
34
|
+
cache_dir = get_cache_dir()
|
35
|
+
|
36
|
+
deps_dir = cache_dir / "deps"
|
37
|
+
|
38
|
+
if not deps_dir.exists():
|
39
|
+
deps_dir.mkdir(parents=True, exist_ok=True)
|
40
|
+
|
41
|
+
return deps_dir
|
primitive/utils/verible.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import tarfile
|
2
2
|
import requests
|
3
3
|
from .shell import add_path_to_shell
|
4
|
-
from
|
4
|
+
from .cache import get_deps_cache
|
5
5
|
|
6
6
|
from loguru import logger
|
7
7
|
|
@@ -12,7 +12,7 @@ VERIBLE_LINUX_X86_64_OS_LINK = "https://github.com/chipsalliance/verible/release
|
|
12
12
|
VERIBLE_LINUX_ARM64_LINK = "https://github.com/chipsalliance/verible/releases/download/v0.0-3752-g8b64887e/verible-v0.0-3752-g8b64887e-linux-static-arm64.tar.gz"
|
13
13
|
|
14
14
|
|
15
|
-
def install_verible(system_info: dict) ->
|
15
|
+
def install_verible(system_info: dict) -> str:
|
16
16
|
url = None
|
17
17
|
if system_info.get("os_family") == "Darwin":
|
18
18
|
url = VERIBLE_MAC_OS_LINK
|
@@ -23,8 +23,10 @@ def install_verible(system_info: dict) -> bool:
|
|
23
23
|
elif system_info.get("processor") == "arm":
|
24
24
|
url = VERIBLE_LINUX_X86_64_OS_LINK
|
25
25
|
|
26
|
+
deps_cache = get_deps_cache()
|
27
|
+
|
26
28
|
verible_dir_name = url.split("/")[-1].split(".tar.gz")[0]
|
27
|
-
file_download_path =
|
29
|
+
file_download_path = deps_cache / f"{verible_dir_name}.tar.gz"
|
28
30
|
|
29
31
|
logger.debug("Downloading verible")
|
30
32
|
response = requests.get(url, stream=True)
|
@@ -38,7 +40,7 @@ def install_verible(system_info: dict) -> bool:
|
|
38
40
|
|
39
41
|
logger.debug("Untaring verible")
|
40
42
|
with tarfile.open(file_download_path) as tar:
|
41
|
-
tar.extractall(
|
43
|
+
tar.extractall(deps_cache)
|
42
44
|
|
43
45
|
logger.debug("Deleting tar.gz artifact")
|
44
46
|
file_download_path.unlink()
|
@@ -47,9 +49,9 @@ def install_verible(system_info: dict) -> bool:
|
|
47
49
|
if "linux" in unpacked_verible_dir_name:
|
48
50
|
unpacked_verible_dir_name = unpacked_verible_dir_name.split("-linux")[0]
|
49
51
|
|
50
|
-
verible_bin =
|
52
|
+
verible_bin = deps_cache.joinpath(unpacked_verible_dir_name).joinpath("bin")
|
51
53
|
|
52
54
|
logger.debug("Adding verible to PATH")
|
53
55
|
add_path_to_shell(verible_bin)
|
54
56
|
|
55
|
-
return
|
57
|
+
return verible_bin
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: primitive
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.39
|
4
4
|
Project-URL: Documentation, https://github.com//primitivecorp/primitive-cli#readme
|
5
5
|
Project-URL: Issues, https://github.com//primitivecorp/primitive-cli/issues
|
6
6
|
Project-URL: Source, https://github.com//primitivecorp/primitive-cli
|
@@ -1,8 +1,8 @@
|
|
1
|
-
primitive/__about__.py,sha256=
|
1
|
+
primitive/__about__.py,sha256=_BMdl-E4C928TL5DMNPwXGZXZshwzVn9FoCKsCYkjoM,130
|
2
2
|
primitive/__init__.py,sha256=bwKdgggKNVssJFVPfKSxqFMz4IxSr54WWbmiZqTMPNI,106
|
3
3
|
primitive/cli.py,sha256=VQPSewC6ouGdEG9W1gllawGJTydpOY0Lzg7LURXcqQg,2374
|
4
4
|
primitive/client.py,sha256=SFPG4H2wJao8euGdnYp-l7dk_fDpWeVn2aT2WNJUAqo,2370
|
5
|
-
primitive/agent/actions.py,sha256=
|
5
|
+
primitive/agent/actions.py,sha256=cxavCfRG76KJ6M-FVwg3htMbJLz16gUjy30h-vsJ7A0,8468
|
6
6
|
primitive/agent/commands.py,sha256=-dVDilELfkGfbZB7qfEPs77Dm1oT62qJj4tsIk4KoxI,254
|
7
7
|
primitive/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
primitive/auth/actions.py,sha256=N2bGcwXNsB89pzs66gF9A5_WzUScY5fhfOyWixqo2y8,1054
|
@@ -22,7 +22,7 @@ primitive/hardware/actions.py,sha256=JJXEeW35QzVGcLN4ym5gYZwY71hxLzM1GYPXWaObEts
|
|
22
22
|
primitive/hardware/commands.py,sha256=QE7LLeFdfOqlvz3JwdwJJRZAY3fHI1zB9kYmmDajpq0,1477
|
23
23
|
primitive/jobs/actions.py,sha256=LpUJN-GOiDSehQANfV_F86eRRKPZ2ew9skKdnlalXU4,7734
|
24
24
|
primitive/jobs/commands.py,sha256=MxPCkBEYW_eLNqgCRYeyj7ZcLOFAWfpVZlqDR2Y_S0o,830
|
25
|
-
primitive/lint/actions.py,sha256=
|
25
|
+
primitive/lint/actions.py,sha256=tWsrht1dowGprcZjEUtjCJzozEQmh9sv2_C2__YHIOI,2825
|
26
26
|
primitive/lint/commands.py,sha256=3CZvkOEMpJspJWmaQzA5bpPKx0_VCijQIXA9l-eTnZE,487
|
27
27
|
primitive/organizations/actions.py,sha256=e0V4E1UK1IcBJsWWH6alHYUmArhzPrBqZ8WkHPIcLq0,2268
|
28
28
|
primitive/organizations/commands.py,sha256=_dwgVEJCqMa5VgB_7P1wLPFc0AuT1p9dtyR9JRr4kpw,487
|
@@ -32,18 +32,18 @@ primitive/projects/commands.py,sha256=Fqqgpi4cm6zOgkHK--0F0hiiIj32BmgZ-h1MydmWwd
|
|
32
32
|
primitive/sim/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
33
33
|
primitive/sim/actions.py,sha256=MkhiAH5QvF2jbnpgWL29HeSrTYiSW0UJpql2wZ4q18w,4900
|
34
34
|
primitive/sim/commands.py,sha256=8PaOfL1MO6qxTn7mNVRnBU1X2wa3gk_mlbAhBW6MnI0,591
|
35
|
-
primitive/sim/vcd.py,sha256=mAbGnKWM0qzIUMkuSmO0p3sU25kOqbl31mvCsDSrXeM,22221
|
36
35
|
primitive/utils/actions.py,sha256=HOFrmM3-0A_A3NS84MqrZ6JmQEiiPSoDqEeuu6b_qfQ,196
|
36
|
+
primitive/utils/cache.py,sha256=rzMhqDnP8QIzRidF7iyYA5rBGRLEu0tjX0POX0E1xFw,975
|
37
37
|
primitive/utils/config.py,sha256=DlFM5Nglo22WPtbpZSVtH7NX-PTMaKYlcrUE7GPRG4c,1058
|
38
38
|
primitive/utils/files.py,sha256=Yv__bQes3YIlzhOT9kVxtYhoA5CmUjPSvphl9PZ41k4,867
|
39
39
|
primitive/utils/git.py,sha256=1qNOu8X-33CavmrD580BmrFhD_WVO9PGWHUUboXJR_g,663
|
40
40
|
primitive/utils/memory_size.py,sha256=4xfha21kW82nFvOTtDFx9Jk2ZQoEhkfXii-PGNTpIUk,3058
|
41
41
|
primitive/utils/printer.py,sha256=f1XUpqi5dkTL3GWvYRUGlSwtj2IxU1q745T4Fxo7Tn4,370
|
42
42
|
primitive/utils/shell.py,sha256=-7UjQaBqSGHzEEyX8pNjeYFFP0P3lVnDV0OkgPz1qHU,1050
|
43
|
-
primitive/utils/verible.py,sha256=
|
43
|
+
primitive/utils/verible.py,sha256=r7c_hfqvL0UicMmIzK3Cy_BfZI1ZpcfBeLqKEWFWqJo,2252
|
44
44
|
primitive/utils/yaml.py,sha256=4UP_9MXHoNb9_SCeUDm9xqYg9sHltqpVhNgsY6GNfb8,527
|
45
|
-
primitive-0.1.
|
46
|
-
primitive-0.1.
|
47
|
-
primitive-0.1.
|
48
|
-
primitive-0.1.
|
49
|
-
primitive-0.1.
|
45
|
+
primitive-0.1.39.dist-info/METADATA,sha256=kANNdscAcTCkcY2ZAAnLS-ovI3wQwUhAEIvUZu9gYtc,3782
|
46
|
+
primitive-0.1.39.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
47
|
+
primitive-0.1.39.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
|
48
|
+
primitive-0.1.39.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
|
49
|
+
primitive-0.1.39.dist-info/RECORD,,
|
primitive/sim/vcd.py
DELETED
@@ -1,761 +0,0 @@
|
|
1
|
-
"""Read Value Change Dump (VCD) files.
|
2
|
-
|
3
|
-
Adapted from Western Digital's PyVCD: https://github.com/westerndigitalcorporation/pyvcd
|
4
|
-
|
5
|
-
The primary interface is the :func:`tokenize()` generator function,
|
6
|
-
parses a binary VCD stream, yielding tokens as they are encountered.
|
7
|
-
|
8
|
-
.. code::
|
9
|
-
|
10
|
-
>>> import io
|
11
|
-
>>> from vcd.reader import TokenKind, tokenize
|
12
|
-
>>> vcd = b"$date today $end $timescale 1 ns $end"
|
13
|
-
>>> tokens = tokenize(io.BytesIO(vcd))
|
14
|
-
>>> token = next(tokens)
|
15
|
-
>>> assert token.kind is TokenKind.DATE
|
16
|
-
>>> assert token.date == 'today'
|
17
|
-
>>> token = next(tokens)
|
18
|
-
>>> assert token.kind is TokenKind.TIMESCALE
|
19
|
-
>>> assert token.timescale.magnitude.value == 1
|
20
|
-
>>> assert token.timescale.unit.value == 'ns'
|
21
|
-
|
22
|
-
"""
|
23
|
-
|
24
|
-
import io
|
25
|
-
from dataclasses import dataclass
|
26
|
-
from enum import Enum
|
27
|
-
from typing import Iterator, NamedTuple, Optional, Tuple, Union
|
28
|
-
|
29
|
-
|
30
|
-
class ScopeType(Enum):
|
31
|
-
"""Valid VCD scope types."""
|
32
|
-
|
33
|
-
begin = "begin"
|
34
|
-
fork = "fork"
|
35
|
-
function = "function"
|
36
|
-
module = "module"
|
37
|
-
task = "task"
|
38
|
-
|
39
|
-
|
40
|
-
class VarType(Enum):
|
41
|
-
"""Valid VCD variable types."""
|
42
|
-
|
43
|
-
event = "event"
|
44
|
-
integer = "integer"
|
45
|
-
parameter = "parameter"
|
46
|
-
real = "real"
|
47
|
-
realtime = "realtime"
|
48
|
-
reg = "reg"
|
49
|
-
supply0 = "supply0"
|
50
|
-
supply1 = "supply1"
|
51
|
-
time = "time"
|
52
|
-
tri = "tri"
|
53
|
-
triand = "triand"
|
54
|
-
trior = "trior"
|
55
|
-
trireg = "trireg"
|
56
|
-
tri0 = "tri0"
|
57
|
-
tri1 = "tri1"
|
58
|
-
wand = "wand"
|
59
|
-
wire = "wire"
|
60
|
-
wor = "wor"
|
61
|
-
string = "string"
|
62
|
-
|
63
|
-
def __str__(self) -> str:
|
64
|
-
return self.value
|
65
|
-
|
66
|
-
|
67
|
-
class TimescaleMagnitude(Enum):
|
68
|
-
"""Valid timescale magnitudes."""
|
69
|
-
|
70
|
-
one = 1
|
71
|
-
ten = 10
|
72
|
-
hundred = 100
|
73
|
-
|
74
|
-
|
75
|
-
class TimescaleUnit(Enum):
|
76
|
-
"""Valid timescale units."""
|
77
|
-
|
78
|
-
second = "s"
|
79
|
-
millisecond = "ms"
|
80
|
-
microsecond = "us"
|
81
|
-
nanosecond = "ns"
|
82
|
-
picosecond = "ps"
|
83
|
-
femtosecond = "fs"
|
84
|
-
|
85
|
-
|
86
|
-
class Timescale(NamedTuple):
|
87
|
-
"""Timescale magnitude and unit."""
|
88
|
-
|
89
|
-
magnitude: TimescaleMagnitude
|
90
|
-
unit: TimescaleUnit
|
91
|
-
|
92
|
-
@classmethod
|
93
|
-
def from_str(cls, s: str) -> "Timescale":
|
94
|
-
for unit in TimescaleUnit:
|
95
|
-
if s == unit.value:
|
96
|
-
mag = TimescaleMagnitude(1)
|
97
|
-
break
|
98
|
-
else:
|
99
|
-
for mag in reversed(TimescaleMagnitude):
|
100
|
-
mag_str = str(mag.value)
|
101
|
-
if s.startswith(mag_str):
|
102
|
-
unit_str = s[len(mag_str) :].lstrip(" ")
|
103
|
-
unit = TimescaleUnit(unit_str)
|
104
|
-
break
|
105
|
-
else:
|
106
|
-
raise ValueError(f"Invalid timescale magnitude {s!r}")
|
107
|
-
return Timescale(mag, unit)
|
108
|
-
|
109
|
-
def __str__(self) -> str:
|
110
|
-
return f"{self.magnitude.value} {self.unit.value}"
|
111
|
-
|
112
|
-
|
113
|
-
class TokenKind(Enum):
|
114
|
-
"""Kinds of VCD tokens."""
|
115
|
-
|
116
|
-
COMMENT = 1
|
117
|
-
DATE = 2
|
118
|
-
ENDDEFINITIONS = 3
|
119
|
-
SCOPE = 4
|
120
|
-
TIMESCALE = 5
|
121
|
-
UPSCOPE = 6
|
122
|
-
VAR = 7
|
123
|
-
VERSION = 8
|
124
|
-
DUMPALL = 9
|
125
|
-
DUMPOFF = 10
|
126
|
-
DUMPON = 11
|
127
|
-
DUMPVARS = 12
|
128
|
-
END = 13
|
129
|
-
CHANGE_TIME = 14
|
130
|
-
CHANGE_SCALAR = 15
|
131
|
-
CHANGE_VECTOR = 16
|
132
|
-
CHANGE_REAL = 17
|
133
|
-
CHANGE_STRING = 18
|
134
|
-
|
135
|
-
|
136
|
-
class VarDecl(NamedTuple):
|
137
|
-
"""VCD variable declaration.
|
138
|
-
|
139
|
-
Examples::
|
140
|
-
|
141
|
-
$var wire 4 !@# foobar [ 3 : 1 ] $end
|
142
|
-
$var real 1 aaa foobar $end
|
143
|
-
$var integer 32 > foobar[8] $end
|
144
|
-
|
145
|
-
"""
|
146
|
-
|
147
|
-
type_: VarType #: Type of variable
|
148
|
-
size: int #: Size, in bits, of variable
|
149
|
-
id_code: str
|
150
|
-
"""Identifer code of variable.
|
151
|
-
|
152
|
-
This code is used in subsequent value change descriptors
|
153
|
-
to map-back to this variable declaration."""
|
154
|
-
|
155
|
-
reference: str
|
156
|
-
"""Reference name of variable.
|
157
|
-
|
158
|
-
This human-readable name typically corresponds to the name of a
|
159
|
-
variable in the model that output the VCD."""
|
160
|
-
|
161
|
-
bit_index: Union[None, int, Tuple[int, int]]
|
162
|
-
"""Optional range of bits to select from the variable.
|
163
|
-
|
164
|
-
May select a single bit index, e.g. ``ref [ 3 ]``. Or a range of
|
165
|
-
bits, e.g. from ``ref [ 7 : 3 ]`` (MSB index then LSB index)."""
|
166
|
-
|
167
|
-
@property
|
168
|
-
def ref_str(self) -> str:
|
169
|
-
if self.bit_index is None:
|
170
|
-
return self.reference
|
171
|
-
elif isinstance(self.bit_index, int):
|
172
|
-
return f"{self.reference}[{self.bit_index}]"
|
173
|
-
else:
|
174
|
-
return f"{self.reference}[{self.bit_index[0]}:{self.bit_index[1]}]"
|
175
|
-
|
176
|
-
|
177
|
-
class ScopeDecl(NamedTuple):
|
178
|
-
"""VCD scope declaration.
|
179
|
-
|
180
|
-
Examples::
|
181
|
-
|
182
|
-
$scope module Foo $end
|
183
|
-
$scope
|
184
|
-
fork alpha_beta
|
185
|
-
$end
|
186
|
-
|
187
|
-
"""
|
188
|
-
|
189
|
-
type_: ScopeType #: Type of scope
|
190
|
-
ident: str #: Scope name
|
191
|
-
|
192
|
-
|
193
|
-
class VectorChange(NamedTuple):
|
194
|
-
"""Vector value change descriptor.
|
195
|
-
|
196
|
-
A vector value consists of multiple 4-state values, where the four
|
197
|
-
states are 0, 1, X, and Z. When a vector value consists entirely
|
198
|
-
of 0 and 1 states, :attr:`value` will be an int. Otherwise
|
199
|
-
:attr:`value` will be a str.
|
200
|
-
|
201
|
-
"""
|
202
|
-
|
203
|
-
id_code: str #: Identifier code of associated variable.
|
204
|
-
value: Union[int, str] #: New value of associated vector variable.
|
205
|
-
|
206
|
-
|
207
|
-
class RealChange(NamedTuple):
|
208
|
-
"""Real value (floating point) change descriptor."""
|
209
|
-
|
210
|
-
id_code: str #: Identifier code of associated variable.
|
211
|
-
value: float #: New value of associated real variable.
|
212
|
-
|
213
|
-
|
214
|
-
class ScalarChange(NamedTuple):
|
215
|
-
"""Scalar value change descriptor.
|
216
|
-
|
217
|
-
A scalar is a single 4-state value. The value is one of '0', '1',
|
218
|
-
'X', or 'Z'.
|
219
|
-
|
220
|
-
"""
|
221
|
-
|
222
|
-
id_code: str #: Identifier code of associated variable.
|
223
|
-
value: str #: New value of associated scalar variable.
|
224
|
-
|
225
|
-
|
226
|
-
class StringChange(NamedTuple):
|
227
|
-
"""String value change descriptor.
|
228
|
-
|
229
|
-
Strings are VCD extension supported by GTKWave.
|
230
|
-
|
231
|
-
"""
|
232
|
-
|
233
|
-
id_code: str #: Identifier code of associated variable.
|
234
|
-
value: str #: New value of associated string variable.
|
235
|
-
|
236
|
-
|
237
|
-
class Location(NamedTuple):
|
238
|
-
"""Describe location within VCD stream/file."""
|
239
|
-
|
240
|
-
line: int #: Line number
|
241
|
-
column: int #: Column number
|
242
|
-
|
243
|
-
|
244
|
-
class Span(NamedTuple):
|
245
|
-
"""Describe location span within VCD stream/file."""
|
246
|
-
|
247
|
-
start: Location #: Start of span
|
248
|
-
end: Location #: End of span
|
249
|
-
|
250
|
-
|
251
|
-
class Token(NamedTuple):
|
252
|
-
"""VCD token yielded from :func:`tokenize()`.
|
253
|
-
|
254
|
-
These are relatively high-level tokens insofar as each token fully
|
255
|
-
captures an entire VCD declaration, command, or change descriptor.
|
256
|
-
|
257
|
-
The :attr:`kind` attribute determines the :attr:`data` type. Various
|
258
|
-
kind-specific properties provide runtime type-checked access to the
|
259
|
-
kind-specific data.
|
260
|
-
|
261
|
-
.. Note::
|
262
|
-
|
263
|
-
The :attr:`data` attribute may be accessed directly to avoid
|
264
|
-
runtime type checks and thus achieve better runtime performance
|
265
|
-
versus accessing kind-specific properties such as
|
266
|
-
:attr:`scalar_change`.
|
267
|
-
|
268
|
-
"""
|
269
|
-
|
270
|
-
kind: TokenKind
|
271
|
-
"The kind of token."
|
272
|
-
|
273
|
-
span: Span
|
274
|
-
"The start and end location of the token within the file/stream."
|
275
|
-
|
276
|
-
data: Union[
|
277
|
-
None, # $enddefinitions $upscope $dump* $end
|
278
|
-
int, # time change
|
279
|
-
str, # $comment, $date, $version
|
280
|
-
ScopeDecl, # $scope
|
281
|
-
Timescale, # $timescale
|
282
|
-
VarDecl, # $var
|
283
|
-
ScalarChange,
|
284
|
-
VectorChange,
|
285
|
-
RealChange,
|
286
|
-
StringChange,
|
287
|
-
]
|
288
|
-
"Data associated with the token. The data type depends on :attr:`kind`."
|
289
|
-
|
290
|
-
@property
|
291
|
-
def comment(self) -> str:
|
292
|
-
"""Unstructured text from a ``$comment`` declaration."""
|
293
|
-
assert self.kind is TokenKind.COMMENT
|
294
|
-
assert isinstance(self.data, str)
|
295
|
-
return self.data
|
296
|
-
|
297
|
-
@property
|
298
|
-
def date(self) -> str:
|
299
|
-
"""Unstructured text from a ``$date`` declaration."""
|
300
|
-
assert self.kind is TokenKind.DATE
|
301
|
-
assert isinstance(self.data, str)
|
302
|
-
return self.data
|
303
|
-
|
304
|
-
@property
|
305
|
-
def scope(self) -> ScopeDecl:
|
306
|
-
"""Scope type and identifier from ``$scope`` declaration."""
|
307
|
-
assert self.kind is TokenKind.SCOPE
|
308
|
-
assert isinstance(self.data, ScopeDecl)
|
309
|
-
return self.data
|
310
|
-
|
311
|
-
@property
|
312
|
-
def timescale(self) -> Timescale:
|
313
|
-
"""Magnitude and unit from ``$timescale`` declaration."""
|
314
|
-
assert self.kind is TokenKind.TIMESCALE
|
315
|
-
assert isinstance(self.data, Timescale)
|
316
|
-
return self.data
|
317
|
-
|
318
|
-
@property
|
319
|
-
def var(self) -> VarDecl:
|
320
|
-
"""Details from a ``$var`` declaration."""
|
321
|
-
assert self.kind is TokenKind.VAR
|
322
|
-
assert isinstance(self.data, VarDecl)
|
323
|
-
return self.data
|
324
|
-
|
325
|
-
@property
|
326
|
-
def version(self) -> str:
|
327
|
-
"""Unstructured text from a ``$version`` declaration."""
|
328
|
-
assert self.kind is TokenKind.VERSION
|
329
|
-
assert isinstance(self.data, str)
|
330
|
-
return self.data
|
331
|
-
|
332
|
-
@property
|
333
|
-
def time_change(self) -> int:
|
334
|
-
"""Simulation time change."""
|
335
|
-
assert self.kind is TokenKind.CHANGE_TIME
|
336
|
-
assert isinstance(self.data, int)
|
337
|
-
return self.data
|
338
|
-
|
339
|
-
@property
|
340
|
-
def scalar_change(self) -> ScalarChange:
|
341
|
-
"""Scalar value change descriptor."""
|
342
|
-
assert self.kind is TokenKind.CHANGE_SCALAR
|
343
|
-
assert isinstance(self.data, ScalarChange)
|
344
|
-
return self.data
|
345
|
-
|
346
|
-
@property
|
347
|
-
def vector_change(self) -> VectorChange:
|
348
|
-
"""Vector value change descriptor."""
|
349
|
-
assert self.kind is TokenKind.CHANGE_VECTOR
|
350
|
-
assert isinstance(self.data, VectorChange)
|
351
|
-
return self.data
|
352
|
-
|
353
|
-
@property
|
354
|
-
def real_change(self) -> RealChange:
|
355
|
-
"""Real (float) value change descriptor."""
|
356
|
-
assert self.kind is TokenKind.CHANGE_REAL
|
357
|
-
assert isinstance(self.data, RealChange)
|
358
|
-
return self.data
|
359
|
-
|
360
|
-
@property
|
361
|
-
def string_change(self) -> StringChange:
|
362
|
-
"String value change descriptor."
|
363
|
-
assert self.kind is TokenKind.CHANGE_STRING
|
364
|
-
assert isinstance(self.data, StringChange)
|
365
|
-
return self.data
|
366
|
-
|
367
|
-
|
368
|
-
class VCDParseError(Exception):
|
369
|
-
"""Catch-all error for any VCD parsing errors."""
|
370
|
-
|
371
|
-
def __init__(self, loc: Location, msg: str) -> None:
|
372
|
-
super().__init__(f"{loc.line}:{loc.column}: {msg}")
|
373
|
-
self.loc = loc
|
374
|
-
"Location within VCD file where error was detected."
|
375
|
-
|
376
|
-
|
377
|
-
HasReadinto = Union[io.BufferedIOBase, io.RawIOBase]
|
378
|
-
|
379
|
-
|
380
|
-
def tokenize(stream: HasReadinto, buf_size: Optional[int] = None) -> Iterator[Token]:
|
381
|
-
"""Parse VCD stream into tokens.
|
382
|
-
|
383
|
-
The input stream must be opened in binary mode. E.g. with ``open(path, 'rb')``.
|
384
|
-
|
385
|
-
"""
|
386
|
-
if buf_size is None:
|
387
|
-
buf_size = io.DEFAULT_BUFFER_SIZE
|
388
|
-
|
389
|
-
s = _TokenizerState(stream, bytearray(buf_size))
|
390
|
-
|
391
|
-
try:
|
392
|
-
while True:
|
393
|
-
s.advance()
|
394
|
-
yield _parse_token(s)
|
395
|
-
except StopIteration:
|
396
|
-
return
|
397
|
-
|
398
|
-
|
399
|
-
@dataclass
|
400
|
-
class _TokenizerState:
|
401
|
-
stream: HasReadinto
|
402
|
-
buf: bytearray
|
403
|
-
pos: int = 0
|
404
|
-
end: int = 0
|
405
|
-
lineno: int = 1
|
406
|
-
column: int = 0
|
407
|
-
|
408
|
-
@property
|
409
|
-
def loc(self) -> Location:
|
410
|
-
return Location(self.lineno, self.column)
|
411
|
-
|
412
|
-
def span(self, start: Location) -> Span:
|
413
|
-
return Span(start, self.loc)
|
414
|
-
|
415
|
-
def advance(self, raise_on_eof: bool = True) -> int:
|
416
|
-
if self.pos < self.end:
|
417
|
-
self.pos += 1
|
418
|
-
else:
|
419
|
-
n = self.stream.readinto(self.buf)
|
420
|
-
if n:
|
421
|
-
self.end = n - 1
|
422
|
-
self.pos = 0
|
423
|
-
elif raise_on_eof:
|
424
|
-
raise StopIteration()
|
425
|
-
else:
|
426
|
-
return 0
|
427
|
-
c = self.buf[self.pos]
|
428
|
-
if c == 10:
|
429
|
-
self.lineno += 1
|
430
|
-
self.column = 1
|
431
|
-
else:
|
432
|
-
self.column += 1
|
433
|
-
return self.buf[self.pos]
|
434
|
-
|
435
|
-
def skip_ws(self) -> int:
|
436
|
-
c = self.buf[self.pos]
|
437
|
-
while c == 32 or 9 <= c <= 13:
|
438
|
-
c = self.advance()
|
439
|
-
return c
|
440
|
-
|
441
|
-
def take_ws_after_kw(self, kw: str) -> None:
|
442
|
-
if _is_ws(self.buf[self.pos]):
|
443
|
-
self.advance()
|
444
|
-
else:
|
445
|
-
raise VCDParseError(self.loc, f"Expected whitespace after identifier ${kw}")
|
446
|
-
|
447
|
-
def take_decimal(self) -> int:
|
448
|
-
digits = []
|
449
|
-
c = self.buf[self.pos]
|
450
|
-
while 48 <= c <= 57: # '0' <= c <= '9'
|
451
|
-
digits.append(c)
|
452
|
-
c = self.advance(raise_on_eof=False)
|
453
|
-
if digits:
|
454
|
-
return int(bytes(digits))
|
455
|
-
else:
|
456
|
-
raise VCDParseError(self.loc, "Expected decimal value")
|
457
|
-
|
458
|
-
def take_id_code(self) -> str:
|
459
|
-
printables = []
|
460
|
-
c = self.buf[self.pos]
|
461
|
-
while 33 <= c <= 126: # printable character
|
462
|
-
printables.append(c)
|
463
|
-
c = self.advance(raise_on_eof=False)
|
464
|
-
if printables:
|
465
|
-
return bytes(printables).decode("ascii")
|
466
|
-
else:
|
467
|
-
raise VCDParseError(self.loc, "Expected id code")
|
468
|
-
|
469
|
-
def take_identifier(self) -> str:
|
470
|
-
identifier = []
|
471
|
-
c = self.buf[self.pos]
|
472
|
-
|
473
|
-
# Identifiers must start with letter or underscore
|
474
|
-
if (
|
475
|
-
65 <= c <= 90 # 'A' <= c <= 'Z'
|
476
|
-
or 97 <= c <= 122 # 'a' - 'z'
|
477
|
-
or c == 95 # '_'
|
478
|
-
):
|
479
|
-
identifier.append(c)
|
480
|
-
c = self.advance()
|
481
|
-
elif c == 92:
|
482
|
-
# If we encounter an escaped identifier,
|
483
|
-
# just remove the escape character
|
484
|
-
# NOTE: may need to revisit this
|
485
|
-
c = self.advance()
|
486
|
-
else:
|
487
|
-
raise VCDParseError(self.loc, "Identifier must start with a-zA-Z_")
|
488
|
-
|
489
|
-
while (
|
490
|
-
48 <= c <= 57 # '0' - '9'
|
491
|
-
or 65 <= c <= 90 # 'A' - 'Z'
|
492
|
-
or 97 <= c <= 122 # 'a' - 'z'
|
493
|
-
or c == 95 # '_'
|
494
|
-
or c == 36 # '$'
|
495
|
-
or c == 46 # '.' not in spec, but seen in the wild
|
496
|
-
or c == 40 # '(' - produced by cva6 core
|
497
|
-
or c == 41 # ')' - produced by cva6 core
|
498
|
-
or c == 91 # '[' - produced by dumping packed arrays
|
499
|
-
or c == 93 # ']' - produced by dumping packed arrays
|
500
|
-
):
|
501
|
-
identifier.append(c)
|
502
|
-
c = self.advance(raise_on_eof=False)
|
503
|
-
|
504
|
-
return bytes(identifier).decode("ascii")
|
505
|
-
|
506
|
-
def take_bit_index(self) -> Union[int, Tuple[int, int]]:
|
507
|
-
self.skip_ws()
|
508
|
-
index0 = self.take_decimal()
|
509
|
-
index1: Optional[int]
|
510
|
-
|
511
|
-
c = self.skip_ws()
|
512
|
-
if c == 58: # ':'
|
513
|
-
self.advance()
|
514
|
-
self.skip_ws()
|
515
|
-
index1 = self.take_decimal()
|
516
|
-
else:
|
517
|
-
index1 = None
|
518
|
-
|
519
|
-
c = self.skip_ws()
|
520
|
-
if c == 93: # ']'
|
521
|
-
self.advance(raise_on_eof=False)
|
522
|
-
if index1 is None:
|
523
|
-
return index0
|
524
|
-
else:
|
525
|
-
return (index0, index1)
|
526
|
-
else:
|
527
|
-
raise VCDParseError(self.loc, 'Expected bit index to terminate with "]"')
|
528
|
-
|
529
|
-
def take_to_end(self) -> str:
|
530
|
-
chars = [
|
531
|
-
self.buf[self.pos], # $
|
532
|
-
self.advance(), # --> e
|
533
|
-
self.advance(), # --> n
|
534
|
-
self.advance(), # --> d
|
535
|
-
]
|
536
|
-
while not ( # Check for 'd' 'n' 'e' '$'
|
537
|
-
chars[-1] == 100
|
538
|
-
and chars[-2] == 110
|
539
|
-
and chars[-3] == 101
|
540
|
-
and chars[-4] == 36
|
541
|
-
):
|
542
|
-
chars.append(self.advance())
|
543
|
-
|
544
|
-
if len(chars) > 4 and not _is_ws(chars[-5]):
|
545
|
-
loc = Location(self.lineno, self.column - min(len(chars), 5))
|
546
|
-
raise VCDParseError(loc, "Expected whitespace before $end")
|
547
|
-
|
548
|
-
return bytes(chars[:-5]).decode("ascii")
|
549
|
-
|
550
|
-
def take_end(self) -> None:
|
551
|
-
if (
|
552
|
-
self.skip_ws() != 36 # '$'
|
553
|
-
or self.advance() != 101 # 'e'
|
554
|
-
or self.advance() != 110 # 'n'
|
555
|
-
or self.advance() != 100 # 'd'
|
556
|
-
):
|
557
|
-
raise VCDParseError(self.loc, "Expected $end")
|
558
|
-
|
559
|
-
|
560
|
-
def _is_ws(c: int) -> bool:
|
561
|
-
return c == 32 or 9 <= c <= 13
|
562
|
-
|
563
|
-
|
564
|
-
def _parse_token(s: _TokenizerState) -> Token:
|
565
|
-
c = s.skip_ws()
|
566
|
-
start = s.loc
|
567
|
-
if c == 35: # '#'
|
568
|
-
# Parse time change
|
569
|
-
s.advance()
|
570
|
-
time = s.take_decimal()
|
571
|
-
return Token(TokenKind.CHANGE_TIME, s.span(start), time)
|
572
|
-
elif c == 48 or c == 49 or c == 122 or c == 90 or c == 120 or c == 88:
|
573
|
-
# c in '01zZxX'
|
574
|
-
# Parse scalar change
|
575
|
-
scalar_value = chr(c)
|
576
|
-
s.advance()
|
577
|
-
id_code = s.take_id_code()
|
578
|
-
return Token(
|
579
|
-
TokenKind.CHANGE_SCALAR, s.span(start), ScalarChange(id_code, scalar_value)
|
580
|
-
)
|
581
|
-
elif c == 66 or c == 98: # 'B' or 'b'
|
582
|
-
# Parse vector change
|
583
|
-
vector = []
|
584
|
-
c = s.advance()
|
585
|
-
while c == 48 or c == 49: # '0' or '1'
|
586
|
-
vector.append(c)
|
587
|
-
c = s.advance()
|
588
|
-
vector_value: Union[int, str]
|
589
|
-
|
590
|
-
if c == 122 or c == 90 or c == 120 or c == 88: # c in 'zZxX'
|
591
|
-
vector.append(c)
|
592
|
-
c = s.advance()
|
593
|
-
while (
|
594
|
-
c == 48 or c == 49 or c == 122 or c == 90 or c == 120 or c == 88
|
595
|
-
): # c in '01zZxX'
|
596
|
-
vector.append(c)
|
597
|
-
c = s.advance()
|
598
|
-
vector_value = bytes(vector).decode("ascii")
|
599
|
-
else:
|
600
|
-
vector_value = int(bytes(vector), 2)
|
601
|
-
|
602
|
-
if not _is_ws(c):
|
603
|
-
raise VCDParseError(s.loc, "Expected whitespace after vector value")
|
604
|
-
|
605
|
-
s.skip_ws()
|
606
|
-
|
607
|
-
id_code = s.take_id_code()
|
608
|
-
|
609
|
-
return Token(
|
610
|
-
TokenKind.CHANGE_VECTOR, s.span(start), VectorChange(id_code, vector_value)
|
611
|
-
)
|
612
|
-
elif c == 82 or c == 114: # 'R' or 'r'
|
613
|
-
# Parse real change
|
614
|
-
real_digits = []
|
615
|
-
c = s.advance()
|
616
|
-
|
617
|
-
while not _is_ws(c):
|
618
|
-
real_digits.append(c)
|
619
|
-
c = s.advance()
|
620
|
-
|
621
|
-
try:
|
622
|
-
real = float(bytes(real_digits))
|
623
|
-
except ValueError:
|
624
|
-
real_str = bytes(real_digits).decode("ascii")
|
625
|
-
raise VCDParseError(start, f"Expected real value, got: {real_str}")
|
626
|
-
|
627
|
-
s.skip_ws()
|
628
|
-
|
629
|
-
id_code = s.take_id_code()
|
630
|
-
|
631
|
-
return Token(TokenKind.CHANGE_REAL, s.span(start), RealChange(id_code, real))
|
632
|
-
elif c == 83 or c == 115: # 'S' or 's'
|
633
|
-
chars = []
|
634
|
-
c = s.advance()
|
635
|
-
while not _is_ws(c):
|
636
|
-
chars.append(c)
|
637
|
-
c = s.advance()
|
638
|
-
s.skip_ws()
|
639
|
-
id_code = s.take_id_code()
|
640
|
-
string_value = bytes(chars).decode("ascii")
|
641
|
-
return Token(
|
642
|
-
TokenKind.CHANGE_STRING, s.span(start), StringChange(id_code, string_value)
|
643
|
-
)
|
644
|
-
elif c == 36: # '$'
|
645
|
-
s.advance()
|
646
|
-
kw = s.take_identifier()
|
647
|
-
|
648
|
-
if kw == "comment":
|
649
|
-
s.take_ws_after_kw(kw)
|
650
|
-
comment = s.take_to_end()
|
651
|
-
return Token(TokenKind.COMMENT, s.span(start), comment)
|
652
|
-
elif kw == "date":
|
653
|
-
s.take_ws_after_kw(kw)
|
654
|
-
date_str = s.take_to_end()
|
655
|
-
return Token(TokenKind.DATE, s.span(start), date_str)
|
656
|
-
elif kw == "enddefinitions":
|
657
|
-
s.take_ws_after_kw(kw)
|
658
|
-
s.take_end()
|
659
|
-
return Token(TokenKind.ENDDEFINITIONS, s.span(start), None)
|
660
|
-
elif kw == "scope":
|
661
|
-
s.take_ws_after_kw(kw)
|
662
|
-
s.skip_ws()
|
663
|
-
identifier = s.take_identifier()
|
664
|
-
try:
|
665
|
-
scope_type = ScopeType(identifier)
|
666
|
-
except ValueError:
|
667
|
-
raise VCDParseError(s.loc, f"Invalid $scope type: {identifier}")
|
668
|
-
|
669
|
-
s.skip_ws()
|
670
|
-
|
671
|
-
scope_ident = s.take_identifier()
|
672
|
-
|
673
|
-
s.take_end()
|
674
|
-
|
675
|
-
scope_decl = ScopeDecl(scope_type, scope_ident)
|
676
|
-
|
677
|
-
return Token(TokenKind.SCOPE, s.span(start), scope_decl)
|
678
|
-
elif kw == "timescale":
|
679
|
-
s.take_ws_after_kw(kw)
|
680
|
-
s.skip_ws()
|
681
|
-
mag_int = s.take_decimal()
|
682
|
-
|
683
|
-
try:
|
684
|
-
magnitude = TimescaleMagnitude(mag_int)
|
685
|
-
except ValueError:
|
686
|
-
valid_magnitudes = ", ".join(str(m.value) for m in TimescaleMagnitude)
|
687
|
-
raise VCDParseError(
|
688
|
-
s.loc,
|
689
|
-
f"Invalid $timescale magnitude: {mag_int}. "
|
690
|
-
f"Must be one of: {valid_magnitudes}.",
|
691
|
-
)
|
692
|
-
|
693
|
-
s.skip_ws()
|
694
|
-
unit_str = s.take_identifier()
|
695
|
-
try:
|
696
|
-
unit = TimescaleUnit(unit_str)
|
697
|
-
except ValueError:
|
698
|
-
valid_units = ", ".join(u.value for u in TimescaleUnit)
|
699
|
-
raise VCDParseError(
|
700
|
-
s.loc,
|
701
|
-
f"Invalid $timescale unit: {unit_str}. "
|
702
|
-
f"Must be one of: {valid_units}.",
|
703
|
-
)
|
704
|
-
|
705
|
-
s.take_end()
|
706
|
-
|
707
|
-
timescale = Timescale(magnitude, unit)
|
708
|
-
return Token(TokenKind.TIMESCALE, s.span(start), timescale)
|
709
|
-
elif kw == "upscope":
|
710
|
-
s.take_ws_after_kw(kw)
|
711
|
-
s.take_end()
|
712
|
-
return Token(TokenKind.UPSCOPE, s.span(start), None)
|
713
|
-
elif kw == "var":
|
714
|
-
s.take_ws_after_kw(kw)
|
715
|
-
s.skip_ws()
|
716
|
-
type_str = s.take_identifier()
|
717
|
-
try:
|
718
|
-
type_ = VarType(type_str)
|
719
|
-
except ValueError:
|
720
|
-
valid_types = ", ".join(t.value for t in VarType)
|
721
|
-
raise VCDParseError(
|
722
|
-
s.loc,
|
723
|
-
f"Invalid $var type: {type_str}. Must be one of: {valid_types}",
|
724
|
-
)
|
725
|
-
|
726
|
-
s.skip_ws()
|
727
|
-
size = s.take_decimal()
|
728
|
-
s.skip_ws()
|
729
|
-
id_code = s.take_id_code()
|
730
|
-
s.skip_ws()
|
731
|
-
ident = s.take_identifier()
|
732
|
-
|
733
|
-
bit_index: Union[None, int, Tuple[int, int]]
|
734
|
-
c = s.skip_ws()
|
735
|
-
if c == 91: # '['
|
736
|
-
s.advance()
|
737
|
-
bit_index = s.take_bit_index()
|
738
|
-
else:
|
739
|
-
bit_index = None
|
740
|
-
|
741
|
-
s.take_end()
|
742
|
-
var_decl = VarDecl(type_, size, id_code, ident, bit_index)
|
743
|
-
return Token(TokenKind.VAR, s.span(start), var_decl)
|
744
|
-
elif kw == "version":
|
745
|
-
s.take_ws_after_kw(kw)
|
746
|
-
version = s.take_to_end()
|
747
|
-
return Token(TokenKind.VERSION, s.span(start), version)
|
748
|
-
elif kw == "dumpall":
|
749
|
-
return Token(TokenKind.DUMPALL, s.span(start), None)
|
750
|
-
elif kw == "dumpoff":
|
751
|
-
return Token(TokenKind.DUMPOFF, s.span(start), None)
|
752
|
-
elif kw == "dumpon":
|
753
|
-
return Token(TokenKind.DUMPON, s.span(start), None)
|
754
|
-
elif kw == "dumpvars":
|
755
|
-
return Token(TokenKind.DUMPVARS, s.span(start), None)
|
756
|
-
elif kw == "end":
|
757
|
-
return Token(TokenKind.END, s.span(start), None)
|
758
|
-
else:
|
759
|
-
raise VCDParseError(s.loc, f"invalid keyword ${kw}")
|
760
|
-
else:
|
761
|
-
raise VCDParseError(s.loc, f"confused: {chr(c)}")
|
File without changes
|
File without changes
|
File without changes
|