From 29132f9d17a275b14554ca57464ec9135bff62b8 Mon Sep 17 00:00:00 2001 From: Jiri Ocenasek Date: Tue, 1 Sep 2026 09:19:16 +0200 Subject: [PATCH 1/2] NXP backend: switch to neutron compiler --- ...manager.py => neutron_compiler_manager.py} | 66 ++++++++++++------- backends/nxp/backend/neutron_target_spec.py | 12 ++-- backends/nxp/nxp_backend.py | 8 +-- ...er.py => test_neutron_compiler_manager.py} | 12 ++-- 4 files changed, 57 insertions(+), 41 deletions(-) rename backends/nxp/backend/{neutron_converter_manager.py => neutron_compiler_manager.py} (71%) rename backends/nxp/tests/generic_tests/{test_neutron_converter_manager.py => test_neutron_compiler_manager.py} (82%) diff --git a/backends/nxp/backend/neutron_converter_manager.py b/backends/nxp/backend/neutron_compiler_manager.py similarity index 71% rename from backends/nxp/backend/neutron_converter_manager.py rename to backends/nxp/backend/neutron_compiler_manager.py index 92b4e25a5de..7c7e5e82e51 100644 --- a/backends/nxp/backend/neutron_converter_manager.py +++ b/backends/nxp/backend/neutron_compiler_manager.py @@ -8,17 +8,27 @@ import os try: - from eiq_neutron_sdk import neutron_converter, neutron_library_utils + from eiq_neutron_sdk import neutron_compiler, neutron_library_utils + + _USING_NEUTRON_COMPILER = True except ImportError: - raise RuntimeError( - "eIQ Neutron SDK not found. To install it, run 'examples/nxp/setup.sh'." - ) + try: + from eiq_neutron_sdk import ( + neutron_converter as neutron_compiler, + neutron_library_utils, + ) + + _USING_NEUTRON_COMPILER = False + except ImportError: + raise RuntimeError( + "eIQ Neutron SDK not found. To install it, run 'examples/nxp/setup.sh'." + ) def _build_compilation_context(compilation_opts): """Build a CompilationContext from a plain dict of options.""" - cctx = neutron_converter.CompilationContext() - cctx.targetOpts = neutron_converter.getNeutronTarget(compilation_opts["target"]) + cctx = neutron_compiler.CompilationContext() + cctx.targetOpts = neutron_compiler.getNeutronTarget(compilation_opts["target"]) cctx.compilationOpts.minNumOpsPerGraph = compilation_opts["minNumOpsPerGraph"] cctx.compilationOpts.excludeGraphPasses = compilation_opts["excludeGraphPasses"] cctx.compilationOpts.fetchConstantsToSRAM = compilation_opts["fetchConstantsToSRAM"] @@ -37,19 +47,22 @@ def _build_compilation_context(compilation_opts): return cctx -def convert_unsafe(tflite_model, compilation_opts, queue): +def compile_unsafe(tflite_model, compilation_opts, queue): """ - Run neutron_converter on given tflite_model with the provided compilation options. + Run neutron_compiler on given tflite_model with the provided compilation options. This routine is supposed to run in a separate process. - If properly finished, the output queue contains the converted model, - otherwise the neutron_converter exits and the output queue is empty. + If properly finished, the output queue contains the compiled model, + otherwise the neutron_compiler exits and the output queue is empty. """ cctx = _build_compilation_context(compilation_opts) - model_converted = neutron_converter.convertModel(list(tflite_model), cctx) - queue.put(model_converted) + if _USING_NEUTRON_COMPILER: + model_compiled = neutron_compiler.compileModel(list(tflite_model), cctx) + else: + model_compiled = neutron_compiler.convertModel(list(tflite_model), cctx) + queue.put(model_compiled) -class NeutronConverterManager: +class NeutronCompilerManager: """ Manager for conversion of TFLite model in flatbuffers format into TFLite model that contains NeutronGraph nodes. @@ -69,8 +82,8 @@ def _rename_partition_kernel_selection_file(delegation_tag): except OSError: logging.error("Failed to rename partition kernel selection file.") - def get_converter(self): - return neutron_converter + def get_compiler(self): + return neutron_compiler def get_library_utils(self): return neutron_library_utils @@ -84,7 +97,7 @@ def verify_target(self, target: str): f"Target `{target}` is not a valid target. Must be one of `{valid_targets}`." ) - def convert( + def compile( self, tflite_model: bytes, target: str, @@ -93,9 +106,9 @@ def convert( use_profiling: bool = False, ) -> bytes: """ - Call Neutron Converter. + Call Neutron Compiler. - :param tflite_model: A generic TFLite model to be converted. + :param tflite_model: A generic TFLite model to be compiled. :param target: The target platform. :param delegation_tag: The delegation tag of model partition. :param fetch_constants_to_sram: Add microcode that fetches weights from external memory. @@ -104,7 +117,7 @@ def convert( :return: TFLite model with Neutron microcode as bytes. """ - # Neutron converter crashes if we provide invalid target -> verify. + # Neutron compiler crashes if we provide invalid target -> verify. self.verify_target(target) compilation_opts = { @@ -124,7 +137,7 @@ def convert( queue = multiprocessing.Manager().Queue() process = multiprocessing.Process( - target=convert_unsafe, + target=compile_unsafe, args=(tflite_model, compilation_opts, queue), ) process.start() @@ -132,20 +145,23 @@ def convert( if queue.empty(): # signals the unsafe task did not run till the end raise RuntimeError( - f"Neutron converter module terminated unexpectedly with exit code {process.exitcode}" + f"Neutron compiler module terminated unexpectedly with exit code {process.exitcode}" ) - model_converted = queue.get() + model_compiled = queue.get() process.close() except (EOFError, OSError, TypeError) as e: # Multiprocessing failed (likely due to environment restrictions) # Fall back to direct execution logging.warning( - f"Multiprocessing not available ({e}), running neutron converter directly" + f"Multiprocessing not available ({e}), running neutron compiler directly" ) cctx = _build_compilation_context(compilation_opts) - model_converted = neutron_converter.convertModel(list(tflite_model), cctx) + if _USING_NEUTRON_COMPILER: + model_compiled = neutron_compiler.compileModel(list(tflite_model), cctx) + else: + model_compiled = neutron_compiler.convertModel(list(tflite_model), cctx) if self.dump_kernel_selection_code: self._rename_partition_kernel_selection_file(delegation_tag) - return bytes(model_converted) + return bytes(model_compiled) diff --git a/backends/nxp/backend/neutron_target_spec.py b/backends/nxp/backend/neutron_target_spec.py index 5a75caf9a75..a51d437f060 100644 --- a/backends/nxp/backend/neutron_target_spec.py +++ b/backends/nxp/backend/neutron_target_spec.py @@ -8,8 +8,8 @@ from enum import Enum import torch -from executorch.backends.nxp.backend.neutron_converter_manager import ( - NeutronConverterManager, +from executorch.backends.nxp.backend.neutron_compiler_manager import ( + NeutronCompilerManager, ) from executorch.exir.dialects._ops import ops as exir_ops from torch.fx import Node @@ -98,10 +98,10 @@ class NeutronTargetSpec: def __init__(self, target: str): - converter_manager = NeutronConverterManager() - converter_manager.verify_target(target) - neutron_converter = converter_manager.get_converter() - self.neutron_target = neutron_converter.getNeutronTarget(target) + compiler_manager = NeutronCompilerManager() + compiler_manager.verify_target(target) + neutron_compiler = compiler_manager.get_compiler() + self.neutron_target = neutron_compiler.getNeutronTarget(target) if self.is_subsystem(): raise ValueError( diff --git a/backends/nxp/nxp_backend.py b/backends/nxp/nxp_backend.py index 2f4bb07316f..b091d9f9246 100644 --- a/backends/nxp/nxp_backend.py +++ b/backends/nxp/nxp_backend.py @@ -25,8 +25,8 @@ EdgeProgramToIRConverter, ) from executorch.backends.nxp.backend.ir.conversion_config import ConversionConfig -from executorch.backends.nxp.backend.neutron_converter_manager import ( - NeutronConverterManager, +from executorch.backends.nxp.backend.neutron_compiler_manager import ( + NeutronCompilerManager, ) from executorch.backends.nxp.backend.neutron_map import NeutronMap @@ -282,9 +282,9 @@ def preprocess( # noqa C901 ) with capture_fd_output() as tmp: - neutron_model = NeutronConverterManager( + neutron_model = NeutronCompilerManager( dump_kernel_selection_code - ).convert( + ).compile( tflite_model, target, delegation_tag, diff --git a/backends/nxp/tests/generic_tests/test_neutron_converter_manager.py b/backends/nxp/tests/generic_tests/test_neutron_compiler_manager.py similarity index 82% rename from backends/nxp/tests/generic_tests/test_neutron_converter_manager.py rename to backends/nxp/tests/generic_tests/test_neutron_compiler_manager.py index 8bd3446da7a..fab2c8afc06 100644 --- a/backends/nxp/tests/generic_tests/test_neutron_converter_manager.py +++ b/backends/nxp/tests/generic_tests/test_neutron_compiler_manager.py @@ -6,8 +6,8 @@ import multiprocessing import pickle -from executorch.backends.nxp.backend.neutron_converter_manager import ( - NeutronConverterManager, +from executorch.backends.nxp.backend.neutron_compiler_manager import ( + NeutronCompilerManager, ) from executorch.backends.nxp.tests.executorch_pipeline import to_quantized_edge_program from executorch.backends.nxp.tests.models import LinearModule @@ -17,23 +17,23 @@ def test_conv2d_neutron_conversion__prefetching(mocker): model = LinearModule(True) input_shape = (1, 1, 32, 32) - converter_spy = mocker.spy(NeutronConverterManager, "convert") + compiler_spy = mocker.spy(NeutronCompilerManager, "compile") _ = to_quantized_edge_program( model, input_shape, fetch_constants_to_sram=True ).exported_program() - neutron_model_prefetch = converter_spy.spy_return + neutron_model_prefetch = compiler_spy.spy_return _ = to_quantized_edge_program( model, input_shape, fetch_constants_to_sram=False ).exported_program() - neutron_model_regular = converter_spy.spy_return + neutron_model_regular = compiler_spy.spy_return assert len(neutron_model_prefetch) != len( neutron_model_regular ), "The weight prefetching flag does not make a difference!" -def test_convert_unsafe_args_are_picklable(mocker): +def test_compile_unsafe_args_are_picklable(mocker): """Verify that all args passed to `multiprocessing.Process` are picklable. The subprocess uses forkserver/spawn in some environments, which requires From f3982fa8a40d388f7a3c0fe68d072de7b18c12b9 Mon Sep 17 00:00:00 2001 From: Jiri Ocenasek Date: Fri, 4 Sep 2026 10:17:54 +0200 Subject: [PATCH 2/2] NXP backend: Updating documentation: switch to neutron compiler --- backends/nxp/README.md | 6 ++--- ...add_batch_size_for_3d_input_pool_2d_ops.py | 2 +- .../ops_converters/mean_dim_converter.py | 2 +- .../ops_converters/permute_copy_converter.py | 2 +- .../upsample_bilinear2d_converter.py | 2 +- .../upsample_nearest2d_converter.py | 2 +- backends/nxp/backend/neutron_map.py | 22 +++++++++---------- backends/nxp/nxp_backend.py | 6 ++--- backends/nxp/runtime/NeutronDriver.h | 6 ++--- .../test_context_sensitive_delegation.py | 4 ++-- .../node_converter/test_cat_converter.py | 2 +- .../backends/nxp/nxp-kernel-selection.md | 8 +++---- docs/source/backends/nxp/nxp-overview.md | 2 +- docs/source/backends/nxp/nxp-partitioner.rst | 6 ++--- docs/source/backends/nxp/nxp-profiling.md | 12 +++++----- docs/source/backends/nxp/nxp-quantization.md | 2 +- .../nxp/tutorials/nxp-basic-tutorial.md | 4 ++-- examples/nxp/aot_neutron_compile.py | 2 +- 18 files changed, 46 insertions(+), 46 deletions(-) diff --git a/backends/nxp/README.md b/backends/nxp/README.md index 4188dd8f810..05204fd7c78 100644 --- a/backends/nxp/README.md +++ b/backends/nxp/README.md @@ -33,9 +33,9 @@ The eIQ Neutron NPU Backend should be considered as prototype quality at this mo improvements. NXP and the ExecuTorch community is actively developing this codebase. ## Neutron Backend implementation and SW architecture -Neutron Backend uses the eIQ Neutron Converter as ML compiler to compile the delegated subgraph to Neutron microcode. -The Neutron Converter accepts the ML model in LiteRT format, for the **eIQ Neutron N3** class therefore the Neutron Backend -uses the LiteRT flatbuffers format as IR between the ExecuTorch and Neutron Converter ML compiler. +Neutron Backend uses the eIQ Neutron Compiler as ML compiler to compile the delegated subgraph to Neutron microcode. +The Neutron Compiler accepts the ML model in LiteRT format, for the **eIQ Neutron N3** class therefore the Neutron Backend +uses the LiteRT flatbuffers format as IR between the ExecuTorch and Neutron Compiler ML compiler. ## Layout * `backend/ir/` - TFLite/LiteRT based IR to represent the Edge Subgraph, taken from onnx2tflite code base and extended to diff --git a/backends/nxp/aten_passes/add_batch_size_for_3d_input_pool_2d_ops.py b/backends/nxp/aten_passes/add_batch_size_for_3d_input_pool_2d_ops.py index 7435b3b6969..cbd000befbb 100644 --- a/backends/nxp/aten_passes/add_batch_size_for_3d_input_pool_2d_ops.py +++ b/backends/nxp/aten_passes/add_batch_size_for_3d_input_pool_2d_ops.py @@ -13,7 +13,7 @@ class AddBatchSizeFor3DInputPool2DOps(PassBase): """Adds batch size dimension for aten.adaptive_avg_pool2d.default, aten.avg_pool2d.default - and aten.max_pool2d.default ops with 3D input, as the Neutron Converter is unable to convert these ops with 3D input. + and aten.max_pool2d.default ops with 3D input, as the Neutron Compiler is unable to compile these ops with 3D input. │ ┌──────▼──────┐ diff --git a/backends/nxp/backend/ir/converter/node_converters/ops_converters/mean_dim_converter.py b/backends/nxp/backend/ir/converter/node_converters/ops_converters/mean_dim_converter.py index 4d03e5e97b7..b9c5a11ca35 100644 --- a/backends/nxp/backend/ir/converter/node_converters/ops_converters/mean_dim_converter.py +++ b/backends/nxp/backend/ir/converter/node_converters/ops_converters/mean_dim_converter.py @@ -41,7 +41,7 @@ def supports_partitioning_result( is_alone_in_partition = cls.is_node_alone_in_partition(node, partition_list) if is_alone_in_partition and keepdim and all(input_shape[d] == 1 for d in dim): - # The operator is a no-op, so the Neutron Converter will skip it. If it's the only node in the + # The operator is a no-op, so the Neutron Compiler will skip it. If it's the only node in the # partition, the graph would end up empty. return False diff --git a/backends/nxp/backend/ir/converter/node_converters/ops_converters/permute_copy_converter.py b/backends/nxp/backend/ir/converter/node_converters/ops_converters/permute_copy_converter.py index 3e4908c2211..6f0eb6ad757 100644 --- a/backends/nxp/backend/ir/converter/node_converters/ops_converters/permute_copy_converter.py +++ b/backends/nxp/backend/ir/converter/node_converters/ops_converters/permute_copy_converter.py @@ -400,7 +400,7 @@ def supports_partitioning_result( is_alone_in_partition = cls.is_node_alone_in_partition(node, partition_list) if has_static_input and is_alone_in_partition: # Transpose with a static input is a no-op on Neutron. If it was the only operator in the partition, - # Neutron Converter would produce and empty graph, so delegation is prohibited. + # Neutron Compiler would produce and empty graph, so delegation is prohibited. return False return True diff --git a/backends/nxp/backend/ir/converter/node_converters/ops_converters/upsample_bilinear2d_converter.py b/backends/nxp/backend/ir/converter/node_converters/ops_converters/upsample_bilinear2d_converter.py index 2f0126e9aae..544638f5b5b 100644 --- a/backends/nxp/backend/ir/converter/node_converters/ops_converters/upsample_bilinear2d_converter.py +++ b/backends/nxp/backend/ir/converter/node_converters/ops_converters/upsample_bilinear2d_converter.py @@ -39,7 +39,7 @@ def supports_partitioning_result( is_alone_in_partition = cls.is_node_alone_in_partition(node, partition_list) if is_alone_in_partition and input_shape == output_shape: - # The operator is a no-op, so the Neutron Converter will skip it. If it's the only node in the + # The operator is a no-op, so the Neutron Compiler will skip it. If it's the only node in the # partition, the graph would end up empty. return False diff --git a/backends/nxp/backend/ir/converter/node_converters/ops_converters/upsample_nearest2d_converter.py b/backends/nxp/backend/ir/converter/node_converters/ops_converters/upsample_nearest2d_converter.py index a3c8db14f51..cdf8ad7d46e 100644 --- a/backends/nxp/backend/ir/converter/node_converters/ops_converters/upsample_nearest2d_converter.py +++ b/backends/nxp/backend/ir/converter/node_converters/ops_converters/upsample_nearest2d_converter.py @@ -41,7 +41,7 @@ def supports_partitioning_result( is_alone_in_partition = cls.is_node_alone_in_partition(node, partition_list) if is_alone_in_partition and h_scale == w_scale == 1: - # The operator is a no-op, so the Neutron Converter will skip it. If it's the only node in the + # The operator is a no-op, so the Neutron Compiler will skip it. If it's the only node in the # partition, the graph would end up empty. return False diff --git a/backends/nxp/backend/neutron_map.py b/backends/nxp/backend/neutron_map.py index da497565726..a5becafc4f0 100644 --- a/backends/nxp/backend/neutron_map.py +++ b/backends/nxp/backend/neutron_map.py @@ -91,15 +91,15 @@ def get_tensors_name(tensors: str) -> list[str]: class NeutronMap: - """Mapping between Neutron, TFLite, and Edge operators based on the Neutron converter log. + """Mapping between Neutron, TFLite, and Edge operators based on the Neutron compiler log. - Parses the Neutron converter log to extract information about TFLite nodes and Neutron subgraphs. + Parses the Neutron compiler log to extract information about TFLite nodes and Neutron subgraphs. Maps TFLite operators to corresponding Neutron operators. Maps Edge operators to Neutron operators via the Edge-to-TFLite mapping. Attributes: - tflite_nodes (list[Node]): TFLite node information extracted from the converter log. - neutron_subgraphs (list[SubgraphInfo]): Neutron subgraph information extracted from the converter log. + tflite_nodes (list[Node]): TFLite node information extracted from the compiler log. + neutron_subgraphs (list[SubgraphInfo]): Neutron subgraph information extracted from the compiler log. neutron_graphs (list[int]): Indices of final Neutron graphs derived from neutron_subgraphs. edge_to_tflite_map (dict[int, tuple[int, ...]]): Mapping from Edge operators to TFLite operators. edge_to_neutron_map (dict[int, tuple[int, ...]]): Mapping from Edge operators to Neutron operators. @@ -118,12 +118,12 @@ class NeutronMap: tflite_to_neutron_map: dict[int, tuple[int, ...]] def __init__( - self, neutron_converter_log: str, edge_to_tflite_map: dict[int, tuple[int, ...]] + self, neutron_compiler_log: str, edge_to_tflite_map: dict[int, tuple[int, ...]] ) -> None: - """Initialize neutron map from neutron converter log. + """Initialize neutron map from neutron compiler log. - :param neutron_converter_log: neutron converter log obtained during model conversion. It should contain - original tflite graph and neutron graph dump. To add these dumps to converter log the dumpAfterImport and + :param neutron_compiler_log: neutron compiler log obtained during model compilation. It should contain + original tflite graph and neutron graph dump. To add these dumps to compiler log the dumpAfterImport and dumpAfterGenerate flags have to be set to "console". """ super().__init__() @@ -134,12 +134,12 @@ def __init__( self.tflite_to_neutron_map = {} self.edge_to_neutron_map = {} self.neutron_kernels_num = 0 - self._split_profiling_log(neutron_converter_log) + self._split_profiling_log(neutron_compiler_log) def _split_profiling_log(self, log: str) -> None: """Process profiling log to split it into original TFLite and converted Neutron nodes. - :param log: Neutron converter log obtained during model conversion, containing the original + :param log: Neutron compiler log obtained during model compilation, containing the original TFLite graph and Neutron graph dump. :return: None. Sets class attributes tflite_nodes and neutron_subgraphs with node information. """ @@ -175,7 +175,7 @@ def _split_profiling_log(self, log: str) -> None: def _get_neutron_subgraphs(self, graph_dump: str) -> list[SubgraphInfo]: """Parse Neutron graph dump and extract subgraph information. - :param graph_dump: String containing the Neutron graph dump from the converter log. + :param graph_dump: String containing the Neutron graph dump from the compiler log. :return: List of SubgraphInfo objects containing subgraph metadata and operator nodes. """ diff --git a/backends/nxp/nxp_backend.py b/backends/nxp/nxp_backend.py index b091d9f9246..16430afada5 100644 --- a/backends/nxp/nxp_backend.py +++ b/backends/nxp/nxp_backend.py @@ -86,10 +86,10 @@ def neutron_compile_spec( :param use_neutron_for_format_conversion: If True, the EdgeProgramToIRConverter will insert `Transpose` ops to ensure that the IO matches the executorch partition, which will be delegated to Neutron. - :param fetch_constants_to_sram: If True, the Neutron Converter will insert microinstructions to prefetch weights + :param fetch_constants_to_sram: If True, the Neutron Compiler will insert microinstructions to prefetch weights from FLASH to SRAM. This should be used when the whole model does not fit into SRAM. - :param dump_kernel_selection_code: Whether Neutron converter dumps kernel selection code. - :param use_profiling: If true Neutron Converter will enable profiling for neutron delegated model + :param dump_kernel_selection_code: Whether Neutron Compiler dumps kernel selection code. + :param use_profiling: If true Neutron Compiler will enable profiling for neutron delegated model :return: self for method chaining """ diff --git a/backends/nxp/runtime/NeutronDriver.h b/backends/nxp/runtime/NeutronDriver.h index 5c47bd74eab..5b879b4442f 100644 --- a/backends/nxp/runtime/NeutronDriver.h +++ b/backends/nxp/runtime/NeutronDriver.h @@ -42,7 +42,7 @@ typedef void* NeutronModelHandle; typedef struct { /// Neutron microcode buffer address. - /// The Neutron microcode is generated by the Neutron converter tool. + /// The Neutron microcode is generated by the Neutron compiler tool. /// The microcode buffer, 16 bytes aligned, is allocated and initialized by /// the application or ML framework. The microcode buffer is passed by /// reference to the Neutron firmware. The microcode buffer is specific for a @@ -50,7 +50,7 @@ typedef struct { const void* microcode; /// Neutron weights buffer address. - /// The Neutron weights is generated by the Neutron converter tool. + /// The Neutron weights is generated by the Neutron compiler tool. /// The weights buffer, 16 bytes aligned, is allocated and initialized by the /// application or ML framework. The weights buffer address is passed by /// reference to the Neutron-firmware. The weights buffer is specific for a @@ -58,7 +58,7 @@ typedef struct { const void* weights; /// Neutron kernels buffer address. - /// The Neutron kernels are generated by the Neutron converter tool. + /// The Neutron kernels are generated by the Neutron compiler tool. /// The kernels buffer, 16 bytes aligned, is allocated and initialized by the /// application or ML framework. The kernels buffer address is passed by /// reference to the Neutron-firmware. The kernels buffer is specific for a diff --git a/backends/nxp/tests/generic_tests/test_context_sensitive_delegation.py b/backends/nxp/tests/generic_tests/test_context_sensitive_delegation.py index 1b1aaed897e..677312f9483 100644 --- a/backends/nxp/tests/generic_tests/test_context_sensitive_delegation.py +++ b/backends/nxp/tests/generic_tests/test_context_sensitive_delegation.py @@ -122,10 +122,10 @@ def test_noop_partitions__concatenate_one_tensor_and_add_zeros(): @pytest.mark.xfail( strict=True, - reason="Neutron Converter currently supports these 2 noops in sequence.", + reason="Neutron Compiler currently supports these 2 noops in sequence.", ) def test_noop_partitions__concatenate_one_tensor_and_add_zeros__forced_delegation(): - # When the noop `Concatenate` and noop `Add` are in sequence, Neutron Converter supports them. This edge case is + # When the noop `Concatenate` and noop `Add` are in sequence, Neutron Compiler supports them. This edge case is # not reflected in our logic. But as this edge case is extremely rare (and even if it ever happened in a real # model, the consequences would be minimal), fixing it is not a priority. diff --git a/backends/nxp/tests/ir/converter/node_converter/test_cat_converter.py b/backends/nxp/tests/ir/converter/node_converter/test_cat_converter.py index b28a431e3ca..fefb70185e3 100644 --- a/backends/nxp/tests/ir/converter/node_converter/test_cat_converter.py +++ b/backends/nxp/tests/ir/converter/node_converter/test_cat_converter.py @@ -136,7 +136,7 @@ def test__different_shapes__channels_first(self, mocker, request, dim, num_input lower_run_compare(model, input_shapes, graph_verifier, request) def test__single_input__alone_in_partition__not_delegated(self): - # The operator is a noop, and there is no other op in the model. The Neutron Converter would produce an empty + # The operator is a noop, and there is no other op in the model. The Neutron Compiler would produce an empty # graph, so the `cat` is not delegated. input_shape = [ModelInputSpec((2, 3, 5))] model = CatModule(1) diff --git a/docs/source/backends/nxp/nxp-kernel-selection.md b/docs/source/backends/nxp/nxp-kernel-selection.md index 307f06d1d02..4cd3bfc33f7 100644 --- a/docs/source/backends/nxp/nxp-kernel-selection.md +++ b/docs/source/backends/nxp/nxp-kernel-selection.md @@ -1,12 +1,12 @@ # NXP eIQ Neutron Kernel Selective Kernel Registration The NXP ExecuTorch backend supports selective Neutron kernel registration for `Neutron-C` targets, which reduces the -size of the Neutron Firmware. During the backend's conversion to the Neutron representation by the Neutron Converter, +size of the Neutron Firmware. During the backend's conversion to the Neutron representation by the Neutron Compiler, microcode for the Neutron accelerator is generated. The microcode consists of kernel calls executed by the Neutron Driver. The code for kernel call functions is distributed in the Neutron Firmware. -The `eiq_neutron_sdk.neutron_converter` optionally generates a `*_kernel_selection.c` file, registering +The `eiq_neutron_sdk.neutron_compiler` optionally generates a `*_kernel_selection.c` file, registering only kernels that are required for a particular model or, in the case of ExecuTorch, a delegated subgraph. This `*_kernel_selection.c`, when used during application linking, takes precedence over the default list of registered kernels in the Neutron Firmware, and allows the linker to include only the necessary Neutron kernels. @@ -21,7 +21,7 @@ final application with unused code. In memory-constrained environments, you can deployed models. This way you can reduce the size of the final application by linking only selected kernels, used in one or more models. -The feature works as follows: The Neutron Converter with the appropriate flag exports a kernel selection file for each +The feature works as follows: The Neutron Compiler with the appropriate flag exports a kernel selection file for each converted subgraph, the kernel selection files are then merged and ready to be included in the MCUXpresso SDK to use for a selection-only build. @@ -52,7 +52,7 @@ python -m eiq_neutron_sdk.neutron_library_utils.merge_kernel_selection_code \ -output-file merged_kernel_selection.c ``` -Each particular model must be converted by the same Neutron converter version, so the `*_kernel_selection.c` files +Each particular model must be compiled by the same Neutron Compiler version, so the `*_kernel_selection.c` files share the same version. ## MCUXpresso SDK build with kernel selection diff --git a/docs/source/backends/nxp/nxp-overview.md b/docs/source/backends/nxp/nxp-overview.md index 581c375d038..72c35cd40c8 100644 --- a/docs/source/backends/nxp/nxp-overview.md +++ b/docs/source/backends/nxp/nxp-overview.md @@ -46,7 +46,7 @@ For a quick overview how to convert a custom PyTorch model, take a look at our [ An example runtime application using the eIQ NSYS (eIQ Neutron Simulator) is available [examples/nxp/executor_runner](https://github.com/pytorch/executorch/blob/main/examples/nxp/executor_runner/), described in the tutorial [Getting started with eIQ Neutron NPU ExecuTorch backend](tutorials/nxp-basic-tutorial.md) -To learn how to run the converted model on the NXP hardware, use one of our example projects on using ExecuTorch runtime from MCUXpresso IDE example projects list. +To learn how to run the compiled model on the NXP hardware, use one of our example projects on using ExecuTorch runtime from MCUXpresso IDE example projects list. For more finegrained tutorial, visit [this manual page](https://mcuxpresso.nxp.com/mcuxsdk/latest/html/middleware/eiq/executorch/docs/nxp/topics/example_applications.html). For guideline how to update the eIQ Neutron Runtime on MCUXpresso SDK, follow the instructions from the eIQ Neutron SDK package `docs/NeutronSDKUserGuide.md` available diff --git a/docs/source/backends/nxp/nxp-partitioner.rst b/docs/source/backends/nxp/nxp-partitioner.rst index 4ddc38fb2db..b24f4dde78e 100644 --- a/docs/source/backends/nxp/nxp-partitioner.rst +++ b/docs/source/backends/nxp/nxp-partitioner.rst @@ -27,9 +27,9 @@ Following fields can be set: * `extra_flags` - Extra flags for the Neutron compiler. * `operators_not_to_delegate` - List of operators that will not be delegated. * `use_neutron_for_format_conversion` - If True, let the eIQ Neutron NPU to handle conversion between channel-first (NCHW) and channel-last (NHWC) data formats. That is the Neutron backend will insert `Transpose` ops to ensure that the IO matches the executorch partition, which will be delegated to Neutron. -* `fetch_constants_to_sram` - If True, the Neutron Converter will insert microinstructions to prefetch weights from FLASH to SRAM. This should be used when the whole model does not fit into SRAM on Neutron-C devices, like i.MX RT700. -* `dump_kernel_selection_code` - Whether Neutron converter dumps kernel selection code, which is used by the selective kernel registration, see :doc:`Neutron Firmware Kernel Selection support `. -* `use_profiling` - If true Neutron Converter will enable profiling for neutron delegated model. +* `fetch_constants_to_sram` - If True, the Neutron Compiler will insert microinstructions to prefetch weights from FLASH to SRAM. This should be used when the whole model does not fit into SRAM on Neutron-C devices, like i.MX RT700. +* `dump_kernel_selection_code` - Whether Neutron Compiler dumps kernel selection code, which is used by the selective kernel registration, see :doc:`Neutron Firmware Kernel Selection support `. +* `use_profiling` - If true Neutron Compiler will enable profiling for neutron delegated model. ------------------------- Custom Delegation Options diff --git a/docs/source/backends/nxp/nxp-profiling.md b/docs/source/backends/nxp/nxp-profiling.md index 17e352e479d..27380a20dde 100644 --- a/docs/source/backends/nxp/nxp-profiling.md +++ b/docs/source/backends/nxp/nxp-profiling.md @@ -7,16 +7,16 @@ to provide visibility into delegated operator execution time. There are three steps required to obtain profiling results for an NXP‑delegated model: -* Convert the model with profiling support enabled. +* Compile the model with profiling support enabled. * Generate the artifacts consumed by the Developer Tools (`ETRecord`, `ETDump`). * Create and run the Inspector class to consume these artifacts and print the results. --- -## Convert a model with the profiling support +## Compile a model with the profiling support Profiling data is generated only for a **profilable** model. -To convert a model with profiling enabled, the `--use-profiling` flag must be set. +To compile a model with profiling enabled, the `--use-profiling` flag must be set. See the `aot_neutron_compile.py` example and its [README](https://github.com/pytorch/executorch/blob/main/examples/nxp/README.md) @@ -89,7 +89,7 @@ A full implementation is available in [aot_neutron_compile.py](https://github.com/pytorch/executorch/blob/main/examples/nxp/aot_neutron_compile.py). The `--use_profiling` flag is used to create a **profilable** model and the corresponding `ETRecord` file -(see [Convert a model with profiling support](#convert-a-model-with-profiling-support) for the full command). +(see [Compile a model with profiling support](#compile-a-model-with-profiling-support) for the full command). --- @@ -102,7 +102,7 @@ The next step is to generate an `ETDump`. An `ETDump` contains runtime data coll To generate an `ETDump`, ensure that the ExecuTorch runtime library is integrated with the Developer Tools and built with the `ET_EVENT_TRACER_ENABLED` flag enabled. -Only models converted with profiling support will produce an `ETDump` containing execution times for all Neutron +Only models compiled with profiling support will produce an `ETDump` containing execution times for all Neutron operators. Otherwise, the dump will include only the final delegate execution time. Neutron software provides a profiling mechanism that logs individual operator execution times to a dedicated runtime @@ -176,7 +176,7 @@ The [Inspector](https://docs.pytorch.org/executorch/1.0/model-inspector.html) AP contents of `ETRecord` and `ETDump`, enabling developers to gain insights into model architecture and performance statistics. -`ETRecord` is an optional argument used to obtain a mapping between the original model and the converted Neutron model. +`ETRecord` is an optional argument used to obtain a mapping between the original model and the compiled Neutron model. An `ETDump` generated on the board contains metadata for each Neutron operator, including its unique identifier. To visualize this metadata in the Inspector results table, set the `include_delegate_debug_data = True` argument. diff --git a/docs/source/backends/nxp/nxp-quantization.md b/docs/source/backends/nxp/nxp-quantization.md index 61cd00632df..ef038e47f44 100644 --- a/docs/source/backends/nxp/nxp-quantization.md +++ b/docs/source/backends/nxp/nxp-quantization.md @@ -248,7 +248,7 @@ Moving from PTQ to QAT check-list: #### Known limitations of QAT In the current ExecuTorch/TorchAO implementation, there is an issue when quantizing biasless convolutions during QAT. -The pipeline produces a non‑quantized empty bias, which causes the Neutron Converter to fail. +The pipeline produces a non‑quantized empty bias, which causes the Neutron Compiler to fail. To mitigate this issue, use the `QuantizeFusedConvBnBiasAtenPass` post‑quantization: ```python diff --git a/docs/source/backends/nxp/tutorials/nxp-basic-tutorial.md b/docs/source/backends/nxp/tutorials/nxp-basic-tutorial.md index b2e07bb7c1d..264a3fc2dfc 100644 --- a/docs/source/backends/nxp/tutorials/nxp-basic-tutorial.md +++ b/docs/source/backends/nxp/tutorials/nxp-basic-tutorial.md @@ -13,14 +13,14 @@ You need to install the ExecuTorch. Please follow the tutorial to install the Ex In addition to this, you will need to install the eIQ Neutron Simulator, called NSYS, -and the Neutron Converter for generating the byte-code for the eIQ Neutron NPU, +and the Neutron Compiler for generating the byte-code for the eIQ Neutron NPU, during the model conversion in ExecuTorch AoT flow. To install the eIQ Neutron dependencies, run: ```bash examples/nxp/setup.sh ``` This will install: -* Neutron Converter, for converting the Neutron IR to Neutron byte-code +* Neutron Compiler, for compiling the Neutron IR to Neutron byte-code * eIQ Neutron SDK, containing the eIQ Neutron runtimes (driver and firmware) for various NXP SoC and simulator * eIQ NSYS, the Neutron behavioral simulator diff --git a/examples/nxp/aot_neutron_compile.py b/examples/nxp/aot_neutron_compile.py index 258b4c87772..876702ac8b3 100644 --- a/examples/nxp/aot_neutron_compile.py +++ b/examples/nxp/aot_neutron_compile.py @@ -229,7 +229,7 @@ def get_model_and_inputs_from_name(model_name: str, use_random_dataset: bool): required=False, default=False, action="store_true", - help="During conversion to Neutron microcode by Neutron Converter, a kernel selection file will be dumped in " + help="During compilation to Neutron microcode by Neutron Compiler, a kernel selection file will be dumped in " "the working directory. This file can be used for reduction of Neutron Firmware size in the built app." "See `docs/source/backends/nxp/nxp-kernel-selection.md` for details.", )