tt-flash 3.4.1__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.
- tt_flash/__init__.py +59 -0
- tt_flash/blackhole.py +70 -0
- tt_flash/boot_fs.py +148 -0
- tt_flash/chip.py +376 -0
- tt_flash/data/blackhole/fw_defines.yaml +5 -0
- tt_flash/data/grayskull/fw_defines.yaml +6 -0
- tt_flash/data/wormhole/fw_defines.yaml +7 -0
- tt_flash/error.py +8 -0
- tt_flash/flash.py +809 -0
- tt_flash/main.py +273 -0
- tt_flash/utility.py +180 -0
- tt_flash-3.4.1.dist-info/METADATA +425 -0
- tt_flash-3.4.1.dist-info/RECORD +18 -0
- tt_flash-3.4.1.dist-info/WHEEL +5 -0
- tt_flash-3.4.1.dist-info/entry_points.txt +2 -0
- tt_flash-3.4.1.dist-info/licenses/LICENSE +206 -0
- tt_flash-3.4.1.dist-info/licenses/LICENSE_understanding.txt +3 -0
- tt_flash-3.4.1.dist-info/top_level.txt +1 -0
tt_flash/__init__.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: © 2024 Tenstorrent AI ULC
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
# Adapted from https://github.com/python-poetry/poetry/issues/273#issuecomment-1877789967
|
|
5
|
+
# This will get the semantic version from the current pyproject package definition.
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
import importlib.metadata as importlib_metadata
|
|
11
|
+
except ModuleNotFoundError:
|
|
12
|
+
import importlib_metadata
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
__package_version = "unknown"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def __get_package_version() -> str:
|
|
19
|
+
"""Find the version of this package."""
|
|
20
|
+
global __package_version
|
|
21
|
+
|
|
22
|
+
if __package_version != "unknown":
|
|
23
|
+
# We already set it at some point in the past,
|
|
24
|
+
# so return that previous value without any
|
|
25
|
+
# extra work.
|
|
26
|
+
return __package_version
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
# Try to get the version of the current package if
|
|
30
|
+
# it is running from a distribution.
|
|
31
|
+
__package_version = importlib_metadata.version("tt-flash")
|
|
32
|
+
except importlib.metadata.PackageNotFoundError:
|
|
33
|
+
# Fall back on getting it from a local pyproject.toml.
|
|
34
|
+
# This works in a development environment where the
|
|
35
|
+
# package has not been installed from a distribution.
|
|
36
|
+
try:
|
|
37
|
+
# This gets added to the standard library as tomllib in Python3.11
|
|
38
|
+
# therefore we expect to hit a ModuleNotFoundError.
|
|
39
|
+
import tomli as toml
|
|
40
|
+
except ModuleNotFoundError:
|
|
41
|
+
import tomllib as toml
|
|
42
|
+
|
|
43
|
+
pyproject_toml_file = Path(__file__).parent.parent / "pyproject.toml"
|
|
44
|
+
if pyproject_toml_file.exists() and pyproject_toml_file.is_file():
|
|
45
|
+
__package_version = toml.loads(pyproject_toml_file.read_text())["project"][
|
|
46
|
+
"version"
|
|
47
|
+
]
|
|
48
|
+
# Indicate it might be locally modified or unreleased.
|
|
49
|
+
__package_version = __package_version + "+"
|
|
50
|
+
|
|
51
|
+
return __package_version
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def __getattr__(name: str) -> Any:
|
|
55
|
+
"""Get package attributes."""
|
|
56
|
+
if name == "__version__":
|
|
57
|
+
return __get_package_version()
|
|
58
|
+
else:
|
|
59
|
+
raise AttributeError(f"No attribute {name} in module {__name__}.")
|
tt_flash/blackhole.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: © 2024 Tenstorrent AI ULC
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import ctypes
|
|
5
|
+
|
|
6
|
+
from tt_flash.boot_fs import tt_boot_fs_fd
|
|
7
|
+
from tt_flash.error import TTError
|
|
8
|
+
from . import boot_fs
|
|
9
|
+
|
|
10
|
+
from tt_flash.chip import BhChip
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def writeback_boardcfg(chip: BhChip, write: bytearray) -> bytearray:
|
|
14
|
+
# Find boardcfg on chip
|
|
15
|
+
fd_in_spi = boot_fs.read_tag(
|
|
16
|
+
lambda addr, size: chip.spi_read(addr, size), "boardcfg"
|
|
17
|
+
)
|
|
18
|
+
if fd_in_spi is None:
|
|
19
|
+
raise TTError("Couldn't find boardcfg on chip")
|
|
20
|
+
|
|
21
|
+
# Find boardcfg in current fd
|
|
22
|
+
fd_to_flash = boot_fs.read_tag(
|
|
23
|
+
lambda addr, size: write[addr : addr + size], "boardcfg"
|
|
24
|
+
)
|
|
25
|
+
if fd_to_flash is None:
|
|
26
|
+
raise TTError("Couldn't find boardcfg in flash package")
|
|
27
|
+
fd_as_data = bytes(fd_in_spi[1])
|
|
28
|
+
write[fd_to_flash[0] : fd_to_flash[0] + len(fd_as_data)] = fd_as_data
|
|
29
|
+
|
|
30
|
+
flashed_fd = boot_fs.read_tag(
|
|
31
|
+
lambda addr, size: write[addr : addr + size], "boardcfg"
|
|
32
|
+
)
|
|
33
|
+
assert flashed_fd[1] == fd_in_spi[1], f"{flashed_fd[1]} != {fd_in_spi[1]}"
|
|
34
|
+
|
|
35
|
+
return write
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
TAG_HANDLERS = {"write-boardcfg": writeback_boardcfg}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def boot_fs_write(
|
|
42
|
+
chip: BhChip, boardname_to_display: str, mask: dict, write: bytearray
|
|
43
|
+
) -> bytearray:
|
|
44
|
+
param_handlers = []
|
|
45
|
+
for v in mask:
|
|
46
|
+
tag = v.get("tag", None)
|
|
47
|
+
|
|
48
|
+
if tag is None or not isinstance(tag, str):
|
|
49
|
+
raise TTError(
|
|
50
|
+
f"Invalid mask format for {boardname_to_display}; expected to see a list of dicts with keys 'tag'"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if tag in TAG_HANDLERS:
|
|
54
|
+
param_handlers.append(TAG_HANDLERS[tag])
|
|
55
|
+
else:
|
|
56
|
+
if len(TAG_HANDLERS) > 0:
|
|
57
|
+
pretty_tags = [f"'{x}'" for x in TAG_HANDLERS.keys()]
|
|
58
|
+
pretty_tags[-1] = f"or {pretty_tags[-1]}"
|
|
59
|
+
raise TTError(
|
|
60
|
+
f"Invalid tag {tag} for {boardname_to_display}; expected to see one of {pretty_tags}"
|
|
61
|
+
)
|
|
62
|
+
else:
|
|
63
|
+
raise TTError(
|
|
64
|
+
f"Invalid tag {tag} for {boardname_to_display}; there aren't any tags defined!"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
for handler in param_handlers:
|
|
68
|
+
write = handler(chip, write)
|
|
69
|
+
|
|
70
|
+
return write
|
tt_flash/boot_fs.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: © 2024 Tenstorrent AI ULC
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
from typing import Callable, Optional, Tuple
|
|
5
|
+
import ctypes
|
|
6
|
+
|
|
7
|
+
# Define constants
|
|
8
|
+
TT_BOOT_FS_FD_HEAD_ADDR = 0x0
|
|
9
|
+
TT_BOOT_FS_SECURITY_BINARY_FD_ADDR = 0x3FE0
|
|
10
|
+
TT_BOOT_FS_FAILOVER_HEAD_ADDR = 0x4000
|
|
11
|
+
IMAGE_TAG_SIZE = 8
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ExtendedStructure(ctypes.Structure):
|
|
15
|
+
def __eq__(self, other):
|
|
16
|
+
if not isinstance(other, self.__class__):
|
|
17
|
+
return False
|
|
18
|
+
for field in self._fields_:
|
|
19
|
+
field_name = field[0]
|
|
20
|
+
|
|
21
|
+
self_value = getattr(self, field_name)
|
|
22
|
+
other_value = getattr(other, field_name)
|
|
23
|
+
|
|
24
|
+
# Handle comparison for ctypes.Array fields
|
|
25
|
+
if isinstance(self_value, ctypes.Array):
|
|
26
|
+
if len(self_value) != len(other_value):
|
|
27
|
+
return False
|
|
28
|
+
for i in range(len(self_value)):
|
|
29
|
+
if self_value[i] != other_value[i]:
|
|
30
|
+
return False
|
|
31
|
+
else:
|
|
32
|
+
if self_value != other_value:
|
|
33
|
+
return False
|
|
34
|
+
return True
|
|
35
|
+
|
|
36
|
+
def __ne__(self, other):
|
|
37
|
+
return not self.__eq__(other)
|
|
38
|
+
|
|
39
|
+
def __repr__(self):
|
|
40
|
+
field_strings = []
|
|
41
|
+
for field in self._fields_:
|
|
42
|
+
field_name = field[0]
|
|
43
|
+
|
|
44
|
+
field_value = getattr(self, field_name)
|
|
45
|
+
|
|
46
|
+
# Handle string representation for ctypes.Array fields
|
|
47
|
+
if isinstance(field_value, ctypes.Array):
|
|
48
|
+
array_str = ", ".join(str(x) for x in field_value)
|
|
49
|
+
field_strings.append(f"{field_name}=[{array_str}]")
|
|
50
|
+
else:
|
|
51
|
+
field_strings.append(f"{field_name}={field_value}")
|
|
52
|
+
|
|
53
|
+
fields_repr = ", ".join(field_strings)
|
|
54
|
+
return f"{self.__class__.__name__}({fields_repr})"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ExtendedUnion(ctypes.Union):
|
|
58
|
+
def __eq__(self, other):
|
|
59
|
+
for fld in self._fields_:
|
|
60
|
+
if getattr(self, fld[0]) != getattr(other, fld[0]):
|
|
61
|
+
return False
|
|
62
|
+
return True
|
|
63
|
+
|
|
64
|
+
def __ne__(self, other):
|
|
65
|
+
for fld in self._fields_:
|
|
66
|
+
if getattr(self, fld[0]) != getattr(other, fld[0]):
|
|
67
|
+
return True
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
def __repr__(self):
|
|
71
|
+
field_strings = []
|
|
72
|
+
for field in self._fields_:
|
|
73
|
+
field_name = field[0]
|
|
74
|
+
|
|
75
|
+
field_value = getattr(self, field_name)
|
|
76
|
+
field_strings.append(f"{field_name}={field_value}")
|
|
77
|
+
fields_repr = ", ".join(field_strings)
|
|
78
|
+
return f"{self.__class__.__name__}({fields_repr})"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# Define fd_flags structure
|
|
82
|
+
class fd_flags(ExtendedStructure):
|
|
83
|
+
_fields_ = [
|
|
84
|
+
("image_size", ctypes.c_uint32, 24),
|
|
85
|
+
("invalid", ctypes.c_uint32, 1),
|
|
86
|
+
("executable", ctypes.c_uint32, 1),
|
|
87
|
+
("fd_flags_rsvd", ctypes.c_uint32, 6),
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# Define fd_flags union
|
|
92
|
+
class fd_flags_u(ExtendedUnion):
|
|
93
|
+
_fields_ = [("val", ctypes.c_uint32), ("f", fd_flags)]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# Define security_fd_flags structure
|
|
97
|
+
class security_fd_flags(ExtendedStructure):
|
|
98
|
+
_fields_ = [
|
|
99
|
+
("signature_size", ctypes.c_uint32, 12),
|
|
100
|
+
("sb_phase", ctypes.c_uint32, 8), # 0 - Phase0A, 1 - Phase0B
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# Define security_fd_flags union
|
|
105
|
+
class security_fd_flags_u(ExtendedUnion):
|
|
106
|
+
_fields_ = [("val", ctypes.c_uint32), ("f", security_fd_flags)]
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# Define tt_boot_fs_fd structure (File descriptor)
|
|
110
|
+
class tt_boot_fs_fd(ExtendedStructure):
|
|
111
|
+
_fields_ = [
|
|
112
|
+
("spi_addr", ctypes.c_uint32),
|
|
113
|
+
("copy_dest", ctypes.c_uint32),
|
|
114
|
+
("flags", fd_flags_u),
|
|
115
|
+
("data_crc", ctypes.c_uint32),
|
|
116
|
+
("security_flags", security_fd_flags_u),
|
|
117
|
+
("image_tag", ctypes.c_uint8 * IMAGE_TAG_SIZE),
|
|
118
|
+
("fd_crc", ctypes.c_uint32),
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
def image_tag_str(self):
|
|
122
|
+
output = ""
|
|
123
|
+
for c in self.image_tag:
|
|
124
|
+
if c == "\0":
|
|
125
|
+
break
|
|
126
|
+
output += chr(c)
|
|
127
|
+
return output
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def read_fd(reader, addr: int) -> tt_boot_fs_fd:
|
|
131
|
+
fd = reader(addr, ctypes.sizeof(tt_boot_fs_fd))
|
|
132
|
+
return tt_boot_fs_fd.from_buffer_copy(fd)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def read_tag(
|
|
136
|
+
reader: Callable[[int, int], bytes], tag: str
|
|
137
|
+
) -> Optional[Tuple[int, tt_boot_fs_fd]]:
|
|
138
|
+
curr_addr = 0
|
|
139
|
+
while True:
|
|
140
|
+
fd = read_fd(reader, curr_addr)
|
|
141
|
+
|
|
142
|
+
if fd.flags.f.invalid != 0:
|
|
143
|
+
return None
|
|
144
|
+
|
|
145
|
+
if fd.image_tag_str() == tag:
|
|
146
|
+
return curr_addr, fd
|
|
147
|
+
|
|
148
|
+
curr_addr += ctypes.sizeof(tt_boot_fs_fd)
|
tt_flash/chip.py
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: © 2024 Tenstorrent AI ULC
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
import time
|
|
9
|
+
from typing import Union, Optional
|
|
10
|
+
import sys
|
|
11
|
+
import yaml
|
|
12
|
+
|
|
13
|
+
from pyluwen import PciChip, Telemetry
|
|
14
|
+
from pyluwen import detect_chips as luwen_detect_chips
|
|
15
|
+
from pyluwen import detect_chips_fallible as luwen_detect_chips_fallible
|
|
16
|
+
|
|
17
|
+
from tt_flash import utility
|
|
18
|
+
from tt_flash.error import TTError
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class FwVersion:
|
|
23
|
+
allow_exception: bool
|
|
24
|
+
exception: Optional[Exception]
|
|
25
|
+
running: Optional[tuple[int, int, int, int]]
|
|
26
|
+
spi: Optional[tuple[int, int, int, int]]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_bundle_version_v1(chip: TTChip) -> FwVersion:
|
|
30
|
+
"""
|
|
31
|
+
Get the currently running bundle version for gs and wh, using a legacy method
|
|
32
|
+
|
|
33
|
+
@param chip
|
|
34
|
+
|
|
35
|
+
@return the detected fw bundle version.
|
|
36
|
+
"""
|
|
37
|
+
running_bundle_version = None
|
|
38
|
+
spi_bundle_version = None
|
|
39
|
+
exception = None
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
fw_version = chip.arc_msg(
|
|
43
|
+
chip.fw_defines["MSG_TYPE_FW_VERSION"], wait_for_done=True, arg0=0, arg1=0
|
|
44
|
+
)[0]
|
|
45
|
+
|
|
46
|
+
# Pre fw version 5 we don't have bundle support
|
|
47
|
+
# this version of tt-flash only works with bundled fw
|
|
48
|
+
# so it's safe to assume that we need to update
|
|
49
|
+
if fw_version >= chip.min_fw_version():
|
|
50
|
+
temp = chip.arc_msg(
|
|
51
|
+
chip.fw_defines["MSG_TYPE_FW_VERSION"],
|
|
52
|
+
wait_for_done=True,
|
|
53
|
+
arg0=1,
|
|
54
|
+
arg1=0,
|
|
55
|
+
)[0]
|
|
56
|
+
|
|
57
|
+
if temp not in [0xFFFFFFFF, 0xDEAD]:
|
|
58
|
+
patch = temp & 0xFF
|
|
59
|
+
minor = (temp >> 8) & 0xFF
|
|
60
|
+
major = (temp >> 16) & 0xFF
|
|
61
|
+
component = (temp >> 24) & 0xFF
|
|
62
|
+
running_bundle_version = (component, major, minor, patch)
|
|
63
|
+
|
|
64
|
+
# There is a version of the firmware that doesn't correctly return an error when setting arg0 to an unknown option.
|
|
65
|
+
# The running_bundle_version and fw_version can never be the same (as mandated by the version formatting) so I can safely check to see if they are the same when checking for this older FW.
|
|
66
|
+
if (
|
|
67
|
+
running_bundle_version != 0xDEAD
|
|
68
|
+
and fw_version != running_bundle_version
|
|
69
|
+
):
|
|
70
|
+
temp = chip.arc_msg(
|
|
71
|
+
chip.fw_defines["MSG_TYPE_FW_VERSION"],
|
|
72
|
+
wait_for_done=True,
|
|
73
|
+
arg0=2,
|
|
74
|
+
arg1=0,
|
|
75
|
+
)[0]
|
|
76
|
+
|
|
77
|
+
if temp not in [0xFFFFFFFF, 0xDEAD]:
|
|
78
|
+
patch = temp & 0xFF
|
|
79
|
+
minor = (temp >> 8) & 0xFF
|
|
80
|
+
major = (temp >> 16) & 0xFF
|
|
81
|
+
component = (temp >> 24) & 0xFF
|
|
82
|
+
spi_bundle_version = (component, major, minor, patch)
|
|
83
|
+
except Exception as e:
|
|
84
|
+
exception = e
|
|
85
|
+
|
|
86
|
+
return FwVersion(
|
|
87
|
+
allow_exception=True,
|
|
88
|
+
exception=exception,
|
|
89
|
+
running=running_bundle_version,
|
|
90
|
+
spi=spi_bundle_version,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def get_chip_data(chip, file, internal: bool):
|
|
95
|
+
with utility.package_root_path() as path:
|
|
96
|
+
if isinstance(chip, WhChip):
|
|
97
|
+
prefix = "wormhole"
|
|
98
|
+
elif isinstance(chip, GsChip):
|
|
99
|
+
prefix = "grayskull"
|
|
100
|
+
elif isinstance(chip, BhChip):
|
|
101
|
+
prefix = "blackhole"
|
|
102
|
+
else:
|
|
103
|
+
raise TTError("Only support flashing Wh or GS chips")
|
|
104
|
+
if internal:
|
|
105
|
+
prefix = f".ignored/{prefix}"
|
|
106
|
+
else:
|
|
107
|
+
prefix = f"data/{prefix}"
|
|
108
|
+
return open(str(path.joinpath(f"{prefix}/{file}")))
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def init_fw_defines(chip):
|
|
112
|
+
return yaml.safe_load(get_chip_data(chip, "fw_defines.yaml", False))
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class TTChip:
|
|
116
|
+
def __init__(self, chip: PciChip):
|
|
117
|
+
self.luwen_chip = chip
|
|
118
|
+
self.interface_id = chip.pci_interface_id()
|
|
119
|
+
|
|
120
|
+
self.fw_defines = init_fw_defines(self)
|
|
121
|
+
|
|
122
|
+
self.telmetry_cache = None
|
|
123
|
+
|
|
124
|
+
def reinit(self, callback=None):
|
|
125
|
+
self.luwen_chip = PciChip(self.interface_id)
|
|
126
|
+
self.telmetry_cache = None
|
|
127
|
+
|
|
128
|
+
chip_count = 0
|
|
129
|
+
block_count = 0
|
|
130
|
+
last_draw = time.time()
|
|
131
|
+
|
|
132
|
+
def chip_detect_callback(status):
|
|
133
|
+
nonlocal chip_count, last_draw, block_count
|
|
134
|
+
|
|
135
|
+
if status.new_chip():
|
|
136
|
+
chip_count += 1
|
|
137
|
+
elif status.correct_down():
|
|
138
|
+
chip_count -= 1
|
|
139
|
+
chip_count = max(chip_count, 0)
|
|
140
|
+
|
|
141
|
+
if sys.stdout.isatty():
|
|
142
|
+
current_time = time.time()
|
|
143
|
+
if current_time - last_draw > 0.1:
|
|
144
|
+
last_draw = current_time
|
|
145
|
+
|
|
146
|
+
if block_count > 0:
|
|
147
|
+
print(f"\033[{block_count}A", end="", flush=True)
|
|
148
|
+
print(f"\033[J", end="", flush=True)
|
|
149
|
+
|
|
150
|
+
print(f"\rDetected Chips: {chip_count}\n", end="", flush=True)
|
|
151
|
+
block_count = 1
|
|
152
|
+
|
|
153
|
+
status_string = status.status_string()
|
|
154
|
+
if status_string is not None:
|
|
155
|
+
for line in status_string.splitlines():
|
|
156
|
+
block_count += 1
|
|
157
|
+
print(f"\r{line}", flush=True)
|
|
158
|
+
else:
|
|
159
|
+
time.sleep(0.01)
|
|
160
|
+
|
|
161
|
+
self.luwen_chip.init(
|
|
162
|
+
callback=chip_detect_callback if callback is None else callback
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
def get_telemetry(self) -> Telemetry:
|
|
166
|
+
self.telmetry_cache = self.luwen_chip.get_telemetry()
|
|
167
|
+
return self.telmetry_cache
|
|
168
|
+
|
|
169
|
+
def get_telemetry_unchanged(self) -> Telemetry:
|
|
170
|
+
if self.telmetry_cache is None:
|
|
171
|
+
self.telmetry_cache = self.luwen_chip.get_telemetry()
|
|
172
|
+
|
|
173
|
+
return self.telmetry_cache
|
|
174
|
+
|
|
175
|
+
def __vnum_to_version(self, version: int) -> tuple[int, int, int, int]:
|
|
176
|
+
return (
|
|
177
|
+
(version >> 24) & 0xFF,
|
|
178
|
+
(version >> 16) & 0xFF,
|
|
179
|
+
(version >> 8) & 0xFF,
|
|
180
|
+
version & 0xFF,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
def m3_fw_app_version(self):
|
|
184
|
+
telem = self.get_telemetry_unchanged()
|
|
185
|
+
return self.__vnum_to_version(telem.m3_app_fw_version)
|
|
186
|
+
|
|
187
|
+
def smbus_fw_version(self):
|
|
188
|
+
telem = self.get_telemetry_unchanged()
|
|
189
|
+
return self.__vnum_to_version(telem.arc1_fw_version)
|
|
190
|
+
|
|
191
|
+
def arc_l2_fw_version(self):
|
|
192
|
+
telem = self.get_telemetry_unchanged()
|
|
193
|
+
return self.__vnum_to_version(telem.arc0_fw_version)
|
|
194
|
+
|
|
195
|
+
def board_type(self):
|
|
196
|
+
return self.luwen_chip.pci_board_type()
|
|
197
|
+
|
|
198
|
+
def axi_write32(self, addr: int, value: int):
|
|
199
|
+
self.luwen_chip.axi_write32(addr, value)
|
|
200
|
+
|
|
201
|
+
def axi_write(self, addr: int, data: bytes):
|
|
202
|
+
self.luwen_chip.axi_write(addr, data)
|
|
203
|
+
|
|
204
|
+
def axi_read32(self, addr: int) -> int:
|
|
205
|
+
return self.luwen_chip.axi_read32(addr)
|
|
206
|
+
|
|
207
|
+
def axi_read(self, addr: int, size: int) -> bytes:
|
|
208
|
+
data = bytearray(size)
|
|
209
|
+
self.luwen_chip.axi_read(addr, data)
|
|
210
|
+
|
|
211
|
+
return bytes(data)
|
|
212
|
+
|
|
213
|
+
def spi_write(self, addr: int, data: bytes):
|
|
214
|
+
self.luwen_chip.spi_write(addr, data)
|
|
215
|
+
|
|
216
|
+
def spi_read(self, addr: int, size: int) -> bytes:
|
|
217
|
+
data = bytearray(size)
|
|
218
|
+
self.luwen_chip.spi_read(addr, data)
|
|
219
|
+
|
|
220
|
+
return bytes(data)
|
|
221
|
+
|
|
222
|
+
def arc_msg(self, *args, **kwargs):
|
|
223
|
+
return self.luwen_chip.arc_msg(*args, **kwargs)
|
|
224
|
+
|
|
225
|
+
@abstractmethod
|
|
226
|
+
def min_fw_version(self):
|
|
227
|
+
pass
|
|
228
|
+
|
|
229
|
+
@abstractmethod
|
|
230
|
+
def get_bundle_version(self) -> FwVersion:
|
|
231
|
+
pass
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class BhChip(TTChip):
|
|
235
|
+
def min_fw_version(self):
|
|
236
|
+
return 0x0
|
|
237
|
+
|
|
238
|
+
def __repr__(self):
|
|
239
|
+
return f"Blackhole[{self.interface_id}]"
|
|
240
|
+
|
|
241
|
+
def get_bundle_version(self) -> FwVersion:
|
|
242
|
+
running = None
|
|
243
|
+
spi = None
|
|
244
|
+
exception = None
|
|
245
|
+
try:
|
|
246
|
+
# Read running FW bundle version from telemetry
|
|
247
|
+
telem = self.get_telemetry_unchanged()
|
|
248
|
+
temp = telem.fw_bundle_version
|
|
249
|
+
patch = temp & 0xFF
|
|
250
|
+
minor = (temp >> 8) & 0xFF
|
|
251
|
+
major = (temp >> 16) & 0xFF
|
|
252
|
+
component = (temp >> 24) & 0xFF
|
|
253
|
+
running = (component, major, minor, patch)
|
|
254
|
+
|
|
255
|
+
# Read SPI FW bundle version
|
|
256
|
+
cmfwcfg = self.luwen_chip.decode_boot_fs_table("cmfwcfg")
|
|
257
|
+
temp = cmfwcfg["fw_bundle_version"]
|
|
258
|
+
patch = temp & 0xFF
|
|
259
|
+
minor = (temp >> 8) & 0xFF
|
|
260
|
+
major = (temp >> 16) & 0xFF
|
|
261
|
+
component = (temp >> 24) & 0xFF
|
|
262
|
+
spi = (component, major, minor, patch)
|
|
263
|
+
except Exception as e:
|
|
264
|
+
exception = e
|
|
265
|
+
|
|
266
|
+
return FwVersion(
|
|
267
|
+
allow_exception=True, exception=exception, running=running, spi=spi
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
class WhChip(TTChip):
|
|
272
|
+
def min_fw_version(self):
|
|
273
|
+
return 0x2170000
|
|
274
|
+
|
|
275
|
+
def __repr__(self):
|
|
276
|
+
return f"Wormhole[{self.interface_id}]"
|
|
277
|
+
|
|
278
|
+
def get_bundle_version(self) -> FwVersion:
|
|
279
|
+
return get_bundle_version_v1(self)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class GsChip(TTChip):
|
|
283
|
+
def min_fw_version(self):
|
|
284
|
+
return 0x1050000
|
|
285
|
+
|
|
286
|
+
def __repr__(self):
|
|
287
|
+
return f"Grayskull[{self.interface_id}]"
|
|
288
|
+
|
|
289
|
+
def get_bundle_version(self) -> FwVersion:
|
|
290
|
+
return get_bundle_version_v1(self)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def detect_local_chips(
|
|
294
|
+
ignore_ethernet: bool = False,
|
|
295
|
+
) -> list[Union[GsChip, WhChip, BhChip]]:
|
|
296
|
+
"""
|
|
297
|
+
This will create a chip which only gaurentees that you have communication with the chip.
|
|
298
|
+
"""
|
|
299
|
+
|
|
300
|
+
chip_count = 0
|
|
301
|
+
block_count = 0
|
|
302
|
+
last_draw = time.time()
|
|
303
|
+
did_draw = False
|
|
304
|
+
|
|
305
|
+
def chip_detect_callback(status):
|
|
306
|
+
nonlocal chip_count, last_draw, block_count, did_draw
|
|
307
|
+
|
|
308
|
+
if status.new_chip():
|
|
309
|
+
chip_count += 1
|
|
310
|
+
elif status.correct_down():
|
|
311
|
+
chip_count -= 1
|
|
312
|
+
chip_count = max(chip_count, 0)
|
|
313
|
+
|
|
314
|
+
if sys.stdout.isatty():
|
|
315
|
+
did_draw = True
|
|
316
|
+
current_time = time.time()
|
|
317
|
+
if current_time - last_draw > 0.1:
|
|
318
|
+
last_draw = current_time
|
|
319
|
+
|
|
320
|
+
if block_count > 0:
|
|
321
|
+
print(f"\033[{block_count}A", end="", flush=True)
|
|
322
|
+
print(f"\033[J", end="", flush=True)
|
|
323
|
+
|
|
324
|
+
print(f"\rDetected Chips: {chip_count}\n", end="", flush=True)
|
|
325
|
+
block_count = 1
|
|
326
|
+
|
|
327
|
+
status_string = status.status_string()
|
|
328
|
+
if status_string is not None:
|
|
329
|
+
for line in status_string.splitlines():
|
|
330
|
+
block_count += 1
|
|
331
|
+
print(f"\r{line}", flush=True)
|
|
332
|
+
else:
|
|
333
|
+
time.sleep(0.01)
|
|
334
|
+
|
|
335
|
+
output = []
|
|
336
|
+
for device in luwen_detect_chips_fallible(
|
|
337
|
+
local_only=True,
|
|
338
|
+
continue_on_failure=False,
|
|
339
|
+
callback=chip_detect_callback,
|
|
340
|
+
noc_safe=ignore_ethernet,
|
|
341
|
+
):
|
|
342
|
+
if not device.have_comms():
|
|
343
|
+
raise Exception(
|
|
344
|
+
f"Do not have communication with {device}, you should reset or remove this device from your system before continuing."
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
device = device.force_upgrade()
|
|
348
|
+
|
|
349
|
+
if device.as_gs() is not None:
|
|
350
|
+
output.append(GsChip(device.as_gs()))
|
|
351
|
+
elif device.as_wh() is not None:
|
|
352
|
+
output.append(WhChip(device.as_wh()))
|
|
353
|
+
elif device.as_bh() is not None:
|
|
354
|
+
output.append(BhChip(device.as_bh()))
|
|
355
|
+
else:
|
|
356
|
+
raise ValueError("Did not recognize board")
|
|
357
|
+
|
|
358
|
+
if not did_draw:
|
|
359
|
+
print(f"\tDetected Chips: {chip_count}")
|
|
360
|
+
|
|
361
|
+
return output
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def detect_chips(local_only: bool = False) -> list[Union[GsChip, WhChip, BhChip]]:
|
|
365
|
+
output = []
|
|
366
|
+
for device in luwen_detect_chips(local_only=local_only):
|
|
367
|
+
if device.as_gs() is not None:
|
|
368
|
+
output.append(GsChip(device.as_gs()))
|
|
369
|
+
elif device.as_wh() is not None:
|
|
370
|
+
output.append(WhChip(device.as_wh()))
|
|
371
|
+
elif device.as_bh() is not None:
|
|
372
|
+
output.append(BhChip(device.as_bh()))
|
|
373
|
+
else:
|
|
374
|
+
raise ValueError("Did not recognize board")
|
|
375
|
+
|
|
376
|
+
return output
|