Skip to content

Commit 66314e4

Browse files
authored
Arm backend: Add TOSA VGF encapsulated compilation target. (#10476)
Add TOSA VGF encapsulated compilation target. This change enables support for "vgf" files which wrap TOSA output and include memory planning for target devices which can JIT TOSA to the target ISA on-device. - Add a VgfQuantizer (same as TOSAQuantizer) - Add a VgfBackend and VgfPartitioner to produce TOSA wrapped in a VGF - Requires yet to be released converter_backend ### Test plan As this is a new encapsulation with a tool that's not yet released, integration with unit tests will come in a subsequent commit. Signed-off-by: Rob Elliott <[email protected]>
1 parent 510c2bd commit 66314e4

10 files changed

+254
-71
lines changed

backends/arm/TARGETS

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ python_library(
77
"ethosu_partitioner.py",
88
"tosa_backend.py",
99
"tosa_partitioner.py",
10+
"vgf_backend.py",
11+
"vgf_partitioner.py",
1012
],
1113
deps = [
1214
":arm_backend",

backends/arm/arm_backend.py

+60-21
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,37 @@ def __init__(self):
2525
self.output_format = None
2626
self.path_for_intermediates = None
2727
self.tosa_spec = None
28-
self.input_order = None
28+
29+
def vgf_compile_spec(
30+
self,
31+
compiler_flags: Optional[str] = "",
32+
) -> "ArmCompileSpecBuilder":
33+
"""
34+
Generate compile spec for VGF compatible targets
35+
36+
Args:
37+
compiler_flags: Extra compiler flags for converter_backend
38+
"""
39+
self.output_format = "vgf"
40+
self.compiler_flags = [
41+
compiler_flags,
42+
]
43+
self.tosa_spec = TosaSpecification.create_from_string("TOSA-0.80+MI")
44+
return self
2945

3046
def ethosu_compile_spec(
3147
self,
32-
config: str,
33-
system_config: str,
34-
memory_mode: str,
48+
target: str,
49+
system_config: Optional[str] = None,
50+
memory_mode: Optional[str] = None,
3551
extra_flags: Optional[str] = None,
3652
config_ini: Optional[str] = "Arm/vela.ini",
3753
) -> "ArmCompileSpecBuilder":
3854
"""
3955
Generate compile spec for Ethos-U NPU
4056
4157
Args:
42-
config: Ethos-U accelerator configuration, e.g. ethos-u55-128
58+
target: Ethos-U accelerator configuration, e.g. ethos-u55-128
4359
system_config: System configuration to select from the Vel
4460
configuration file
4561
memory_mode: Memory mode to select from the Vela configuration file
@@ -52,18 +68,38 @@ def ethosu_compile_spec(
5268
), f"Output format already set to f{self.output_format}"
5369
self.output_format = "vela"
5470
self.compiler_flags = [
55-
f"--accelerator-config={config}",
71+
f"--accelerator-config={target}",
5672
f"--config={config_ini}",
5773
]
74+
75+
# default system config and memory mode
76+
if "ethos-u55" in target:
77+
if system_config is None:
78+
system_config = "Ethos_U55_High_End_Embedded"
79+
if memory_mode is None:
80+
memory_mode = "Shared_Sram"
81+
elif "ethos-u85" in target:
82+
if system_config is None:
83+
system_config = "Ethos_U85_SYS_DRAM_Mid"
84+
if memory_mode is None:
85+
memory_mode = "Sram_Only"
86+
else:
87+
raise RuntimeError(f"Unknown ethos target: {target}")
88+
5889
if system_config is not None:
5990
self.compiler_flags.append(f"--system-config={system_config}")
6091
if memory_mode is not None:
6192
self.compiler_flags.append(f"--memory-mode={memory_mode}")
6293
if extra_flags is not None:
6394
self.compiler_flags.append(extra_flags)
6495

96+
# We require raw output and regor, so add these flags if absent. This
97+
# overrides any other output setting.
98+
self.compiler_flags.append("--output-format=raw")
99+
self.compiler_flags.append("--debug-force-regor")
100+
65101
base_tosa_version = "TOSA-0.80+BI"
66-
if "u55" in config:
102+
if "u55" in target:
67103
# Add the Ethos-U55 extension marker
68104
base_tosa_version += "+u55"
69105
self.tosa_spec = TosaSpecification.create_from_string(base_tosa_version)
@@ -106,26 +142,22 @@ def build(self) -> List[CompileSpec]:
106142
# Always supply a TOSA version
107143
self.compile_spec = [CompileSpec("tosa_spec", str(self.tosa_spec).encode())]
108144

109-
if self.output_format == "vela":
110-
self.compile_spec += [
111-
CompileSpec("output_format", "vela".encode()),
112-
CompileSpec("compile_flags", " ".join(self.compiler_flags).encode()),
113-
]
114-
elif self.output_format == "tosa":
115-
self.compile_spec.append(CompileSpec("output_format", "tosa".encode()))
145+
# Add compile flags, these are backend specific, refer to the backend
146+
# documentation.
147+
self.compile_spec += [
148+
CompileSpec("compile_flags", " ".join(self.compiler_flags).encode()),
149+
]
150+
151+
# encode output format
152+
self.compile_spec.append(
153+
CompileSpec("output_format", self.output_format.encode())
154+
)
116155

117156
if self.path_for_intermediates is not None:
118157
self.compile_spec.append(
119158
CompileSpec("debug_artifact_path", self.path_for_intermediates.encode())
120159
)
121160

122-
if self.input_order:
123-
self.compile_spec.append(
124-
CompileSpec(
125-
"input_order", " ".join(map(str, self.input_order)).encode()
126-
)
127-
)
128-
129161
return self.compile_spec
130162

131163

@@ -148,6 +180,13 @@ def is_ethosu(compile_spec: List[CompileSpec]) -> bool:
148180
return False
149181

150182

183+
def is_vgf(compile_spec: List[CompileSpec]) -> bool:
184+
for spec in compile_spec:
185+
if spec.key == "output_format":
186+
return spec.value.decode() == "vgf"
187+
return False
188+
189+
151190
def get_tosa_spec(compile_spec: List[CompileSpec]) -> TosaSpecification:
152191
for spec in compile_spec:
153192
if spec.key == "tosa_spec":

backends/arm/arm_vela.py

+4-7
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,11 @@
2323

2424
# Pack either input or output tensor block, compose the related arrays into
2525
# per-io structs to simplify runtime use.
26-
def vela_bin_pack_io(prefix, data, shape_order=None):
26+
def vela_bin_pack_io(prefix, data):
2727
vela_input_shapes = data[prefix + "_shape"]
2828

29-
order = shape_order if shape_order else range(len(vela_input_shapes))
3029
ios = struct.pack("<i", len(vela_input_shapes))
31-
for i in order:
30+
for i in range(len(vela_input_shapes)):
3231
io_shape = vela_input_shapes[i]
3332
io_elem_size = data[prefix + "_elem_size"][i]
3433
io_offset = data[prefix + "_offset"][i]
@@ -45,9 +44,7 @@ def vela_bin_pack_io(prefix, data, shape_order=None):
4544
# Output via Vela to binary stream for ArmBackendEthosU
4645
# WARNING: Do not change this without changing VelaBinStream.cpp as that
4746
# function consumes this format and the two need to align.
48-
def vela_compile(
49-
tosa_flatbuffer: bytes, args: List[str], shape_order=None, verbose: bool = False
50-
):
47+
def vela_compile(tosa_flatbuffer: bytes, args: List[str], verbose: bool = False):
5148
"""
5249
Compile a TOSA graph to a binary stream for ArmBackendEthosU using Vela.
5350
"""
@@ -98,7 +95,7 @@ def vela_compile(
9895
bin_blocks["scratch_data"] = b"\x00" * block_length
9996

10097
# Capture inputs and outputs
101-
bin_blocks["inputs"] = vela_bin_pack_io("input", data, shape_order)
98+
bin_blocks["inputs"] = vela_bin_pack_io("input", data)
10299
bin_blocks["outputs"] = vela_bin_pack_io("output", data)
103100

104101
bin_blocks["vela_end_stream"] = b""

backends/arm/ethosu_backend.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,16 @@ class EthosUBackend(BackendDetails):
3535

3636
@staticmethod
3737
def _compile_tosa_flatbuffer(
38-
tosa_flatbuffer: bytes, compile_spec: list[CompileSpec]
38+
tosa_flatbuffer: bytes, compile_spec: List[CompileSpec]
3939
) -> bytes:
4040
"""
4141
Static helper method to do the compilation of the TOSA flatbuffer
4242
representation to a target specific binary stream.
4343
"""
4444
compile_flags = []
45-
input_order = []
4645
for spec in compile_spec:
4746
if spec.key == "compile_flags":
4847
compile_flags.append(spec.value.decode())
49-
if spec.key == "input_order":
50-
input_order = list(map(int, spec.value.decode().split(",")))
5148

5249
if len(compile_flags) == 0:
5350
# Not testing for compile_flags correctness here, just that they are
@@ -60,7 +57,6 @@ def _compile_tosa_flatbuffer(
6057
binary = vela_compile(
6158
tosa_flatbuffer,
6259
compile_flags,
63-
input_order,
6460
verbose=logger.getEffectiveLevel() == logging.INFO,
6561
)
6662
return binary

backends/arm/quantizer/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
EthosUQuantizer,
1010
get_symmetric_quantization_config,
1111
TOSAQuantizer,
12+
VgfQuantizer,
1213
)
1314

1415
# Used in tests

backends/arm/quantizer/arm_quantizer.py

+11
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from executorch.backends.arm.arm_backend import (
2828
get_tosa_spec,
2929
is_ethosu,
30+
is_vgf,
3031
) # usort: skip
3132
from executorch.exir.backend.compile_spec_schema import CompileSpec
3233
from torch.ao.quantization.fake_quantize import (
@@ -52,6 +53,7 @@
5253
__all__ = [
5354
"TOSAQuantizer",
5455
"EthosUQuantizer",
56+
"VgfQuantizer",
5557
"get_symmetric_quantization_config",
5658
]
5759

@@ -358,3 +360,12 @@ def __init__(self, compile_spec: list[CompileSpec]) -> None:
358360

359361
tosa_spec = get_tosa_spec(compile_spec)
360362
super().__init__(tosa_spec)
363+
364+
365+
class VgfQuantizer(TOSAQuantizer):
366+
def __init__(self, compile_spec: list[CompileSpec]) -> None:
367+
if not is_vgf(compile_spec):
368+
raise RuntimeError("compile spec is not targeting VGF")
369+
370+
tosa_spec = get_tosa_spec(compile_spec)
371+
super().__init__(tosa_spec)

backends/arm/tosa_backend.py

+4-13
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@
3535
logger = logging.getLogger(__name__)
3636

3737

38-
def _get_first_delegation_tag(graph_module) -> str | None:
39-
"""Get the first delegation tag from the graph_module or return None."""
38+
def arm_get_first_delegation_tag(graph_module) -> str:
39+
"""Get the first delegation tag from the graph_module or return empty string."""
4040
for node in graph_module.graph.nodes:
4141
tag = node.meta.get("delegation_tag")
4242
if tag:
4343
return tag
4444

4545
logger.debug("No delegation tag found in partition.")
46-
return None
46+
return ""
4747

4848

4949
@final
@@ -63,16 +63,13 @@ def preprocess( # noqa: C901
6363
artifact_path = None
6464
output_format = ""
6565
compile_flags = []
66-
input_order = []
6766
for spec in compile_spec:
6867
if spec.key == "debug_artifact_path":
6968
artifact_path = spec.value.decode()
7069
if spec.key == "output_format":
7170
output_format = spec.value.decode()
7271
if spec.key == "compile_flags":
7372
compile_flags.append(spec.value.decode())
74-
if spec.key == "input_order":
75-
input_order = list(map(int, spec.value.decode().split(",")))
7673

7774
# Check that the output format is set correctly in the compile spec
7875
if output_format != "tosa":
@@ -129,14 +126,8 @@ def preprocess( # noqa: C901
129126
dbg_fail(node, graph_module, tosa_graph, artifact_path)
130127
raise
131128

132-
if len(input_order) > 0:
133-
if input_count != len(input_order):
134-
raise RuntimeError(
135-
"The rank of the input order is not equal to amount of input tensors"
136-
)
137-
138129
if artifact_path:
139-
tag = _get_first_delegation_tag(graph_module)
130+
tag = arm_get_first_delegation_tag(graph_module)
140131
dbg_tosa_dump(
141132
tosa_graph,
142133
artifact_path,

0 commit comments

Comments
 (0)