aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorNathan Bailey <nathan.bailey@arm.com>2024-01-26 14:19:52 +0000
committerNathan Bailey <nathan.bailey@arm.com>2024-02-23 15:23:06 +0000
commite506c7bd0453cb204ec7a59267fe3982492aaed6 (patch)
tree225b70ede952e6dfe0b33fcc3813106bb8828e33 /tests
parentd10b53a358d7fddc2e5a818d146b71bc5bb5e0ed (diff)
downloadmlia-e506c7bd0453cb204ec7a59267fe3982492aaed6.tar.gz
refactor: Migrate from Vela's internal code to CSV summary
Removes vela defines from vela compiler.py and performance.py Replaces calls to vela code with data from vela summary csv Resolves: MLIA-1024 Signed-off-by: Nathan Bailey <nathan.bailey@arm.com> Change-Id: I569878f2936767f70c0255919ca40d1969275529
Diffstat (limited to 'tests')
-rw-r--r--tests/conftest.py41
-rw-r--r--tests/test_backend_vela_compiler.py656
-rw-r--r--tests/test_backend_vela_performance.py27
-rw-r--r--tests/test_cli_commands.py2
-rw-r--r--tests/test_core_context.py7
-rw-r--r--tests/test_target_ethos_u_data_analysis.py12
-rw-r--r--tests/test_target_ethos_u_data_collection.py2
-rw-r--r--tests/test_target_ethos_u_performance.py16
-rw-r--r--tests/test_target_ethos_u_reporters.py40
9 files changed, 606 insertions, 197 deletions
diff --git a/tests/conftest.py b/tests/conftest.py
index 9dc1d16..1092979 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -11,7 +11,7 @@ import numpy as np
import pytest
import tensorflow as tf
-from mlia.backend.vela.compiler import optimize_model
+from mlia.backend.vela.compiler import compile_model
from mlia.core.context import ExecutionContext
from mlia.nn.rewrite.core.utils.numpy_tfrecord import NumpyTFWriter
from mlia.nn.tensorflow.tflite_convert import convert_to_tflite
@@ -51,7 +51,7 @@ def invalid_input_model_file(test_tflite_invalid_model: Path) -> Path:
@pytest.fixture(scope="session", name="empty_test_csv_file")
-def fixture_empty_test_csv_file( # pylint: disable=too-many-locals
+def fixture_empty_test_csv_file(
test_csv_path: Path,
) -> Path:
"""Return empty test csv file path."""
@@ -59,7 +59,7 @@ def fixture_empty_test_csv_file( # pylint: disable=too-many-locals
@pytest.fixture(scope="session", name="test_csv_file")
-def fixture_test_csv_file( # pylint: disable=too-many-locals
+def fixture_test_csv_file(
test_csv_path: Path,
) -> Path:
"""Return test csv file path."""
@@ -67,7 +67,7 @@ def fixture_test_csv_file( # pylint: disable=too-many-locals
@pytest.fixture(scope="session", name="test_csv_path")
-def fixture_test_csv_path( # pylint: disable=too-many-locals
+def fixture_test_csv_path(
tmp_path_factory: pytest.TempPathFactory,
) -> Generator[Path, None, None]:
"""Return test csv file path."""
@@ -76,6 +76,32 @@ def fixture_test_csv_path( # pylint: disable=too-many-locals
shutil.rmtree(tmp_path)
+@pytest.fixture(scope="session", name="test_vela_path")
+def fixture_test_vela_path(
+ tmp_path_factory: pytest.TempPathFactory,
+) -> Generator[Path, None, None]:
+ """Return test vela file path."""
+ tmp_path = tmp_path_factory.mktemp("vela_file")
+ yield tmp_path
+ shutil.rmtree(tmp_path)
+
+
+@pytest.fixture(scope="session", name="empty_vela_ini_file")
+def fixture_empty_vela_ini_file(
+ test_vela_path: Path,
+) -> Path:
+ """Return empty test vela file path."""
+ return test_vela_path / "empty_vela.ini"
+
+
+@pytest.fixture(scope="session", name="vela_ini_file")
+def fixture_vela_ini_file(
+ test_vela_path: Path,
+) -> Path:
+ """Return empty test vela file path."""
+ return test_vela_path / "vela.ini"
+
+
def get_test_keras_model() -> tf.keras.Model:
"""Return test Keras model."""
model = tf.keras.Sequential(
@@ -130,13 +156,8 @@ def fixture_test_models_path(
convert_to_tflite(keras_model, quantized=True, output_path=tflite_model_path)
# Vela-optimized TensorFlow Lite model (int8)
- tflite_vela_model = tmp_path / TEST_MODEL_TFLITE_VELA_FILE
target_config = EthosUConfiguration.load_profile("ethos-u55-256")
- optimize_model(
- tflite_model_path,
- target_config.compiler_options,
- tflite_vela_model,
- )
+ compile_model(tflite_model_path, target_config.compiler_options)
tf.saved_model.save(keras_model, str(tmp_path / TEST_MODEL_TF_SAVED_MODEL_FILE))
diff --git a/tests/test_backend_vela_compiler.py b/tests/test_backend_vela_compiler.py
index 5554efb..d5dc5cc 100644
--- a/tests/test_backend_vela_compiler.py
+++ b/tests/test_backend_vela_compiler.py
@@ -3,16 +3,22 @@
"""Tests for module vela/compiler."""
from pathlib import Path
from typing import Any
+from unittest.mock import MagicMock
import pytest
-from ethosu.vela.compiler_driver import TensorAllocator
-from ethosu.vela.scheduler import OptimizationStrategy
+from ethosu.vela.vela import main
-from mlia.backend.vela.compiler import optimize_model
-from mlia.backend.vela.compiler import OptimizedModel
+from mlia.backend.vela.compiler import compile_model
+from mlia.backend.vela.compiler import parse_summary_csv_file
+from mlia.backend.vela.compiler import parse_vela_initialisation_file
+from mlia.backend.vela.compiler import resolve_compiler_config
from mlia.backend.vela.compiler import VelaCompiler
from mlia.backend.vela.compiler import VelaCompilerOptions
+from mlia.backend.vela.compiler import VelaInitData
+from mlia.backend.vela.compiler import VelaInitMemoryData
+from mlia.backend.vela.compiler import VelaSummary
from mlia.target.ethos_u.config import EthosUConfiguration
+from mlia.utils.filesystem import recreate_directory
def test_default_vela_compiler() -> None:
@@ -26,52 +32,15 @@ def test_default_vela_compiler() -> None:
assert default_compiler.accelerator_config == "ethos-u55-256"
assert default_compiler.max_block_dependency == 3
assert default_compiler.arena_cache_size is None
- assert default_compiler.tensor_allocator == TensorAllocator.HillClimb
+ assert default_compiler.tensor_allocator == "HillClimb"
assert default_compiler.cpu_tensor_alignment == 16
- assert default_compiler.optimization_strategy == OptimizationStrategy.Performance
+ assert default_compiler.optimization_strategy == "Performance"
assert default_compiler.output_dir == Path("output")
- assert default_compiler.get_config() == {
- "accelerator_config": "ethos-u55-256",
- "system_config": "internal-default",
- "core_clock": 500000000.0,
- "axi0_port": "Sram",
- "axi1_port": "OffChipFlash",
- "memory_mode": "internal-default",
- "const_mem_area": "Axi1",
- "arena_mem_area": "Axi0",
- "cache_mem_area": "Axi0",
- "arena_cache_size": 4294967296,
- "permanent_storage_mem_area": "OffChipFlash",
- "feature_map_storage_mem_area": "Sram",
- "fast_storage_mem_area": "Sram",
- "memory_area": {
- "Sram": {
- "clock_scales": 1.0,
- "burst_length": 32,
- "read_latency": 32,
- "write_latency": 32,
- },
- "Dram": {
- "clock_scales": 1.0,
- "burst_length": 1,
- "read_latency": 0,
- "write_latency": 0,
- },
- "OnChipFlash": {
- "clock_scales": 1.0,
- "burst_length": 1,
- "read_latency": 0,
- "write_latency": 0,
- },
- "OffChipFlash": {
- "clock_scales": 0.125,
- "burst_length": 128,
- "read_latency": 64,
- "write_latency": 64,
- },
- },
- }
+ with pytest.raises(
+ ValueError, match="System Config: internal-default not present in vela.ini file"
+ ):
+ resolve_compiler_config(vela_compiler_options=default_compiler_options)
def test_vela_compiler_with_parameters(test_resources_path: Path) -> None:
@@ -98,52 +67,120 @@ def test_vela_compiler_with_parameters(test_resources_path: Path) -> None:
assert compiler.accelerator_config == "ethos-u65-256"
assert compiler.max_block_dependency == 1
assert compiler.arena_cache_size == 10
- assert compiler.tensor_allocator == TensorAllocator.Greedy
+ assert compiler.tensor_allocator == "Greedy"
assert compiler.cpu_tensor_alignment == 4
- assert compiler.optimization_strategy == OptimizationStrategy.Size
+ assert compiler.optimization_strategy == "Size"
assert compiler.output_dir == Path("custom_output")
- assert compiler.get_config() == {
- "accelerator_config": "ethos-u65-256",
- "system_config": "Ethos_U65_High_End",
- "core_clock": 1000000000.0,
- "axi0_port": "Sram",
- "axi1_port": "Dram",
- "memory_mode": "Shared_Sram",
- "const_mem_area": "Axi1",
- "arena_mem_area": "Axi0",
- "cache_mem_area": "Axi0",
- "arena_cache_size": 10,
- "permanent_storage_mem_area": "Dram",
- "feature_map_storage_mem_area": "Sram",
- "fast_storage_mem_area": "Sram",
- "memory_area": {
- "Sram": {
- "clock_scales": 1.0,
- "burst_length": 32,
- "read_latency": 32,
- "write_latency": 32,
- },
- "Dram": {
- "clock_scales": 0.234375,
- "burst_length": 128,
- "read_latency": 500,
- "write_latency": 250,
- },
- "OnChipFlash": {
- "clock_scales": 1.0,
- "burst_length": 1,
- "read_latency": 0,
- "write_latency": 0,
- },
- "OffChipFlash": {
- "clock_scales": 1.0,
- "burst_length": 1,
- "read_latency": 0,
- "write_latency": 0,
- },
- },
- }
+ assert resolve_compiler_config(
+ vela_compiler_options=compiler_options
+ ) == VelaInitData(
+ system_config="Ethos_U65_High_End",
+ core_clock=1000000000.0,
+ axi0_port="Sram",
+ axi1_port="Dram",
+ memory_mode="Shared_Sram",
+ const_mem_area="Axi1",
+ arena_mem_area="Axi0",
+ cache_mem_area="Axi0",
+ arena_cache_size=None,
+ sram_memory_data=VelaInitMemoryData(
+ clock_scale=1.0,
+ burst_length=32,
+ read_latency=32,
+ write_latency=32,
+ ),
+ dram_memory_data=VelaInitMemoryData(
+ clock_scale=0.234375,
+ burst_length=128,
+ read_latency=500,
+ write_latency=250,
+ ),
+ on_chip_flash_memory_data=VelaInitMemoryData(
+ clock_scale=None,
+ burst_length=None,
+ read_latency=None,
+ write_latency=None,
+ ),
+ off_chip_flash_memory_data=VelaInitMemoryData(
+ clock_scale=None,
+ burst_length=None,
+ read_latency=None,
+ write_latency=None,
+ ),
+ )
+
+
+def test_vela_compiler_with_parameters_inherit_memory_mode(
+ test_resources_path: Path,
+) -> None:
+ """Test creation of Vela compiler instance with non-default params
+ that inherits a memory mode.
+ """
+ vela_ini_path = str(test_resources_path / "vela/sample_vela.ini")
+
+ compiler_options = VelaCompilerOptions(
+ config_files=vela_ini_path,
+ system_config="Ethos_U65_High_End",
+ memory_mode="Dedicated_Sram_512KB",
+ accelerator_config="ethos-u65-256",
+ max_block_dependency=1,
+ arena_cache_size=10,
+ tensor_allocator="Greedy",
+ cpu_tensor_alignment=4,
+ optimization_strategy="Size",
+ output_dir=Path("custom_output"),
+ )
+ compiler = VelaCompiler(compiler_options)
+
+ assert compiler.config_files == vela_ini_path
+ assert compiler.system_config == "Ethos_U65_High_End"
+ assert compiler.memory_mode == "Dedicated_Sram_512KB"
+ assert compiler.accelerator_config == "ethos-u65-256"
+ assert compiler.max_block_dependency == 1
+ assert compiler.arena_cache_size == 10
+ assert compiler.tensor_allocator == "Greedy"
+ assert compiler.cpu_tensor_alignment == 4
+ assert compiler.optimization_strategy == "Size"
+ assert compiler.output_dir == Path("custom_output")
+
+ assert resolve_compiler_config(
+ vela_compiler_options=compiler_options
+ ) == VelaInitData(
+ system_config="Ethos_U65_High_End",
+ core_clock=1000000000.0,
+ axi0_port="Sram",
+ axi1_port="Dram",
+ memory_mode="Dedicated_Sram_512KB",
+ const_mem_area="Axi1",
+ arena_mem_area="Axi1",
+ cache_mem_area="Axi0",
+ arena_cache_size=524288,
+ sram_memory_data=VelaInitMemoryData(
+ clock_scale=1.0,
+ burst_length=32,
+ read_latency=32,
+ write_latency=32,
+ ),
+ dram_memory_data=VelaInitMemoryData(
+ clock_scale=0.234375,
+ burst_length=128,
+ read_latency=500,
+ write_latency=250,
+ ),
+ on_chip_flash_memory_data=VelaInitMemoryData(
+ clock_scale=None,
+ burst_length=None,
+ read_latency=None,
+ write_latency=None,
+ ),
+ off_chip_flash_memory_data=VelaInitMemoryData(
+ clock_scale=None,
+ burst_length=None,
+ read_latency=None,
+ write_latency=None,
+ ),
+ )
def test_compile_model(test_tflite_model: Path) -> None:
@@ -152,8 +189,17 @@ def test_compile_model(test_tflite_model: Path) -> None:
EthosUConfiguration.load_profile("ethos-u55-256").compiler_options
)
- optimized_model = compiler.compile_model(test_tflite_model)
- assert isinstance(optimized_model, OptimizedModel)
+ expected_model_path = Path(
+ compiler.output_dir.as_posix()
+ + "/"
+ + test_tflite_model.stem
+ + "_vela"
+ + test_tflite_model.suffix
+ )
+ vela_summary_data, optimized_model_path = compiler.compile_model(test_tflite_model)
+ assert isinstance(vela_summary_data, VelaSummary)
+ assert isinstance(optimized_model_path, Path)
+ assert expected_model_path == optimized_model_path
def test_csv_file_created(test_tflite_model: Path) -> None:
@@ -172,7 +218,7 @@ def test_verbose_flag_passed() -> None:
compiler = VelaCompiler(
EthosUConfiguration.load_profile("ethos-u55-256").compiler_options
)
- assert compiler.return_compiler_options().verbose_performance
+ assert compiler.verbose_performance
def test_compile_model_fail_sram_exceeded(
@@ -186,7 +232,7 @@ def test_compile_model_fail_sram_exceeded(
def fake_compiler(*_: Any) -> None:
print("Warning: SRAM target for arena memory area exceeded.")
- monkeypatch.setattr("mlia.backend.vela.compiler.compiler_driver", fake_compiler)
+ monkeypatch.setattr("mlia.backend.vela.compiler.main", fake_compiler)
with pytest.raises(Exception) as exc_info:
compiler.compile_model(test_tflite_model)
@@ -195,12 +241,424 @@ def test_compile_model_fail_sram_exceeded(
def test_optimize_model(tmp_path: Path, test_tflite_model: Path) -> None:
"""Test model optimization and saving into file."""
- tmp_file = tmp_path / "temp.tflite"
-
+ tmp_file = tmp_path / "test_model_int8_vela.tflite"
target_config = EthosUConfiguration.load_profile("ethos-u55-256")
- optimize_model(
- test_tflite_model, target_config.compiler_options, tmp_file.absolute()
- )
+ target_config.compiler_options.output_dir = tmp_path
+ compile_model(test_tflite_model, target_config.compiler_options)
assert tmp_file.is_file()
assert tmp_file.stat().st_size > 0
+
+
+SUMMARY_TMP_DATA = """
+experiment,network,accelerator_configuration,system_config,memory_mode,core_clock,arena_cache_size,sram_bandwidth,dram_bandwidth,on_chip_flash_bandwidth,off_chip_flash_bandwidth,weights_storage_area,feature_map_storage_area,inferences_per_second,batch_size,inference_time,passes_before_fusing,passes_after_fusing,sram_memory_used,dram_memory_used,on_chip_flash_memory_used,off_chip_flash_memory_used,total_original_weights,total_npu_encoded_weights,sram_feature_map_read_bytes,sram_feature_map_write_bytes,sram_weight_read_bytes,sram_weight_write_bytes,sram_total_bytes,dram_feature_map_read_bytes,dram_feature_map_write_bytes,dram_weight_read_bytes,dram_weight_write_bytes,dram_total_bytes,on_chip_flash_feature_map_read_bytes,on_chip_flash_feature_map_write_bytes,on_chip_flash_weight_read_bytes,on_chip_flash_weight_write_bytes,on_chip_flash_total_bytes,off_chip_flash_feature_map_read_bytes,off_chip_flash_feature_map_write_bytes,off_chip_flash_weight_read_bytes,off_chip_flash_weight_write_bytes,off_chip_flash_total_bytes,nn_macs,nn_tops,cycles_npu,cycles_sram_access,cycles_dram_access,cycles_on_chip_flash_access,cycles_off_chip_flash_access,cycles_total
+default,test_model_fp32,Ethos_U55_256,Ethos_U55_High_End_Embedded,Shared_Sram,0.0,0.9,4.0,4.0,4.0,0.5,Off-chip Flash,SRAM,0.0,1,12.1e-05,7,2.0,1.5,0.0,0.0,1.4,7,8,6.0,5.0,7552.0,5.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,4.0,0.0,1.0,2,0.1,23297.0,1.5,0.0,0.0,1.0,2
+""".strip()
+
+SUMMARY_TMP_DATA_MISSING_HEADER = """
+experiment,network,accelerator_configuration,system_config,memory_mode,core_clock,arena_cache_size,sram_bandwidth,dram_bandwidth,on_chip_flash_bandwidth,off_chip_flash_bandwidth,weights_storage_area,feature_map_storage_area,inferences_per_second,batch_size,inference_time,passes_before_fusing,passes_after_fusing,sram_memory_used,dram_memory_used,on_chip_flash_memory_used,off_chip_flash_memory_used,total_original_weights,total_npu_encoded_weights,sram_feature_map_read_bytes,sram_feature_map_write_bytes,sram_weight_read_bytes,sram_weight_write_bytes,sram_total_bytes,dram_feature_map_read_bytes,dram_feature_map_write_bytes,dram_weight_read_bytes,dram_weight_write_bytes,dram_total_bytes,on_chip_flash_feature_map_read_bytes,on_chip_flash_feature_map_write_bytes,on_chip_flash_weight_read_bytes,on_chip_flash_weight_write_bytes,on_chip_flash_total_bytes,off_chip_flash_feature_map_read_bytes,off_chip_flash_feature_map_write_bytes,off_chip_flash_weight_read_bytes,off_chip_flash_weight_write_bytes,off_chip_flash_total_bytes,nn_macs,nn_tops,cycles_npu,cycles_sram_access,cycles_dram_access,cycles_on_chip_flash_access,cycles_off_chip_flash_access
+default,test_model_fp32,Ethos_U55_256,Ethos_U55_High_End_Embedded,Shared_Sram,0.0,0.9,4.0,4.0,4.0,0.5,Off-chip Flash,SRAM,0.0,1,12.1e-05,7,2.0,1.5,0.0,0.0,1.4,7,8,6.0,5.0,7552.0,5.0,1.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,4.0,0.0,1.0,2,0.1,23297.0,1.5,0.0,0.0,1.0
+""".strip()
+
+TMP_DATA_EXPECTED_STRING = "\
+cycles_total: 2.0, \
+cycles_npu: 23297.0, \
+cycles_sram_access: 1.5, \
+cycles_dram_access: 0.0, \
+cycles_on_chip_flash_access: 0.0, \
+cycles_off_chip_flash_access: 1.0, \
+core_clock: 0.0, \
+dram_memory_used: 0.0, \
+sram_memory_used: 1.5, \
+on_chip_flash_memory_used: 0.0, \
+off_chip_flash_memory_used: 1.4, \
+batch_size: 1, \
+memory_mode: Shared_Sram, \
+system_config: Ethos_U55_High_End_Embedded, \
+accelerator_configuration: Ethos_U55_256, \
+arena_cache_size: 0.9, \
+"
+
+
+def test_backend_compiler_parse_summary_csv_file(test_csv_file: Path) -> None:
+ """Test that parsing a csv file produces a LayerwisePerfInfo object."""
+ with open(test_csv_file, "w", encoding="utf8") as csv_file:
+ csv_file.write(SUMMARY_TMP_DATA)
+ summary_object = parse_summary_csv_file(test_csv_file)
+ strings_to_check = repr(summary_object)
+ assert isinstance(summary_object, VelaSummary)
+ assert TMP_DATA_EXPECTED_STRING == strings_to_check
+
+
+def test_backend_compiler_summary_csv_parsed_empty(empty_test_csv_file: Path) -> None:
+ """Test that ensures when we have an empty
+ CSV file we get None as backend data.
+ """
+ empty_test_csv_file.touch()
+ with pytest.raises(RuntimeError, match="Generated Vela Summary CSV is empty"):
+ parse_summary_csv_file(empty_test_csv_file)
+
+
+def test_backend_compiler_summary_csv_parsed_missing_headers(
+ test_csv_file: Path,
+) -> None:
+ """Test that ensures a KeyError
+ is raised when a csv with missing
+ expected headers is parsed.
+ """
+ with open(test_csv_file, "w", encoding="utf8") as csv_file:
+ csv_file.write(SUMMARY_TMP_DATA_MISSING_HEADER)
+ with pytest.raises(
+ KeyError,
+ match="Generated Vela Summary CSV missing expected header: cycles_total.", # pylint: disable=line-too-long
+ ):
+ parse_summary_csv_file(test_csv_file)
+
+
+def test_backend_compiler_summary_csv_parsed_missing_file() -> None:
+ """Test that ensures a FileNotFoundError
+ is raised when a non-existent csv file is parsed.
+ """
+ with pytest.raises(
+ FileNotFoundError, match="CSV File not found at missing_file.csv"
+ ):
+ parse_summary_csv_file(Path("missing_file.csv"))
+
+
+def test_backend_compiler_parsing_vela_ini_file_missing_init_file() -> None:
+ """Test that ensures a FileNotFoundError
+ is raised when a non-existent ini file is parsed.
+ """
+ with pytest.raises(
+ FileNotFoundError,
+ match="Vela Initialisation File not found at missing_init_file.ini",
+ ):
+ parse_vela_initialisation_file(
+ Path("missing_init_file.ini"), "internal-default", "internal-default"
+ )
+
+
+def test_backend_compiler_parsing_vela_ini_file_empty_init_file(
+ empty_vela_ini_file: Path,
+) -> None:
+ """Test that ensures a OSError
+ is raised when an empty vela.ini file is parsed.
+ """
+ empty_vela_ini_file.touch()
+ with pytest.raises(OSError, match="vela.ini File Is Empty"):
+ parse_vela_initialisation_file(
+ empty_vela_ini_file, "internal-default", "internal-default"
+ )
+
+
+@pytest.mark.parametrize(
+ "input_str",
+ [
+ """
+; SPDX-FileCopyrightText: Copyright 2022, Arm Limited and/or its affiliates.
+; SPDX-License-Identifier: Apache-2.0
+; Ethos-U55 High-End Embedded: SRAM (4 GB/s) and Flash (0.5 GB/s)
+[System_Config.Ethos_U55_High_End_Embedded]
+core_clock=500e6
+axi0_port=Sram
+axi1_port=OffChipFlash
+Sram_clock_scale=1.0
+Sram_burst_length=32
+Sram_read_latency=32
+Sram_write_latency=32
+OffChipFlash_clock_scale=0.125
+OffChipFlash_burst_length=128
+OffChipFlash_read_latency=64
+OffChipFlash_write_latency=64
+
+; Ethos-U65 High-End: SRAM (16 GB/s) and DRAM (3.75 GB/s)
+[System_Config.Ethos_U65_High_End]
+core_clock=1e9
+axi0_port=Sram
+axi1_port=Dram
+Sram_clock_scale=1.0
+Sram_burst_length=32
+Sram_read_latency=32
+Sram_write_latency=32
+Dram_clock_scale=0.234375
+Dram_burst_length=128
+Dram_read_latency=500
+Dram_write_latency=250
+"""
+ ],
+)
+def test_backend_compiler_parsing_vela_ini_file_missing_memory_modes(
+ vela_ini_file: Path,
+ input_str: str,
+) -> None:
+ """Test that ensures a IndexError
+ is raised when a vela.ini file with no memory modes
+ is parsed.
+ """
+ with open(vela_ini_file, "w", encoding="utf8") as vela_file:
+ vela_file.write(input_str)
+ with pytest.raises(
+ IndexError, match="No memory modes are present in vela.ini file."
+ ):
+ parse_vela_initialisation_file(
+ vela_ini_file, "Ethos_U65_High_End", "Shared_Sram"
+ )
+
+
+@pytest.mark.parametrize(
+ "input_str",
+ [
+ """
+; -----------------------------------------------------------------------------
+; Memory Mode
+
+; Shared SRAM: the SRAM is shared between the Ethos-U and the Cortex-M software
+; The non-SRAM memory is assumed to be read-only
+[Memory_Mode.Shared_Sram]
+const_mem_area=Axi1
+arena_mem_area=Axi0
+cache_mem_area=Axi0
+
+; The SRAM (384KB) is only for use by the Ethos-U
+; The non-SRAM memory is assumed to be read-writeable
+[Memory_Mode.Dedicated_Sram]
+const_mem_area=Axi1
+arena_mem_area=Axi1
+cache_mem_area=Axi0
+arena_cache_size=393216
+
+"""
+ ],
+)
+def test_backend_compiler_parsing_vela_ini_file_missing_system_configs(
+ vela_ini_file: Path,
+ input_str: str,
+) -> None:
+ """Test that ensures a IndexError
+ is raised when a vela.ini file with no system configs
+ is parsed.
+ """
+ with open(vela_ini_file, "w", encoding="utf8") as vela_file:
+ vela_file.write(input_str)
+ with pytest.raises(
+ IndexError, match="No system configs are present in vela.ini file."
+ ):
+ parse_vela_initialisation_file(
+ vela_ini_file, "Ethos_U65_High_End", "Shared_Sram"
+ )
+
+
+@pytest.mark.parametrize(
+ "input_str",
+ [
+ """
+; SPDX-FileCopyrightText: Copyright 2022, Arm Limited and/or its affiliates.
+; SPDX-License-Identifier: Apache-2.0
+; Ethos-U55 High-End Embedded: SRAM (4 GB/s) and Flash (0.5 GB/s)
+[System_Config.Ethos_U55_High_End_Embedded]
+core_clock=500e6
+axi0_port=Sram
+axi1_port=OffChipFlash
+Sram_clock_scale=1.0
+Sram_burst_length=32
+Sram_read_latency=32
+Sram_write_latency=32
+OffChipFlash_clock_scale=0.125
+OffChipFlash_burst_length=128
+OffChipFlash_read_latency=64
+OffChipFlash_write_latency=64
+
+; Ethos-U65 High-End: SRAM (16 GB/s) and DRAM (3.75 GB/s)
+[System_Config.Ethos_U65_High_End]
+core_clock=1e9
+axi0_port=Sram
+axi1_port=Dram
+Sram_clock_scale=1.0
+Sram_burst_length=32
+Sram_read_latency=32
+Sram_write_latency=32
+Dram_clock_scale=0.234375
+Dram_burst_length=128
+Dram_read_latency=500
+Dram_write_latency=250
+
+; -----------------------------------------------------------------------------
+; Memory Mode
+
+; Shared SRAM: the SRAM is shared between the Ethos-U and the Cortex-M software
+; The non-SRAM memory is assumed to be read-only
+[Memory_Mode.Shared_Sram]
+const_mem_area=Axi1
+arena_mem_area=Axi0
+cache_mem_area=Axi0
+
+"""
+ ],
+)
+def test_backend_compiler_parsing_vela_ini_file_missing_specific_memory_mode(
+ vela_ini_file: Path,
+ input_str: str,
+) -> None:
+ """Test that ensures a ValueError
+ is raised when a vela.ini file with specific missing memory mode
+ is parsed.
+ """
+ with open(vela_ini_file, "w", encoding="utf8") as vela_file:
+ vela_file.write(input_str)
+ with pytest.raises(
+ ValueError, match="Memory Mode: Dedicated_Sram not present in vela.ini file."
+ ):
+ parse_vela_initialisation_file(
+ vela_ini_file, "Ethos_U65_High_End", "Dedicated_Sram"
+ )
+
+
+@pytest.mark.parametrize(
+ "input_str",
+ [
+ """
+; SPDX-FileCopyrightText: Copyright 2022, Arm Limited and/or its affiliates.
+; SPDX-License-Identifier: Apache-2.0
+; Ethos-U55 High-End Embedded: SRAM (4 GB/s) and Flash (0.5 GB/s)
+[System_Config.Ethos_U55_High_End_Embedded]
+core_clock=500e6
+axi0_port=Sram
+axi1_port=OffChipFlash
+Sram_clock_scale=1.0
+Sram_burst_length=32
+Sram_read_latency=32
+Sram_write_latency=32
+OffChipFlash_clock_scale=0.125
+OffChipFlash_burst_length=128
+OffChipFlash_read_latency=64
+OffChipFlash_write_latency=64
+
+; -----------------------------------------------------------------------------
+; Memory Mode
+
+; Shared SRAM: the SRAM is shared between the Ethos-U and the Cortex-M software
+; The non-SRAM memory is assumed to be read-only
+[Memory_Mode.Shared_Sram]
+const_mem_area=Axi1
+arena_mem_area=Axi0
+cache_mem_area=Axi0
+
+; The SRAM (384KB) is only for use by the Ethos-U
+; The non-SRAM memory is assumed to be read-writeable
+[Memory_Mode.Dedicated_Sram]
+const_mem_area=Axi1
+arena_mem_area=Axi1
+cache_mem_area=Axi0
+arena_cache_size=393216
+
+"""
+ ],
+)
+def test_backend_compiler_parsing_vela_ini_file_missing_specific_system_config(
+ vela_ini_file: Path,
+ input_str: str,
+) -> None:
+ """Test that ensures a ValueError
+ is raised when a vela.ini file with specific missing system config
+ is parsed.
+ """
+ with open(vela_ini_file, "w", encoding="utf8") as vela_file:
+ vela_file.write(input_str)
+ with pytest.raises(
+ ValueError,
+ match="System Config: Ethos_U65_High_End not present in vela.ini file.",
+ ):
+ parse_vela_initialisation_file(
+ vela_ini_file, "Ethos_U65_High_End", "Shared_Sram"
+ )
+
+
+@pytest.mark.parametrize(
+ "input_str",
+ [
+ """
+; SPDX-FileCopyrightText: Copyright 2022, Arm Limited and/or its affiliates.
+; SPDX-License-Identifier: Apache-2.0
+; Ethos-U55 High-End Embedded: SRAM (4 GB/s) and Flash (0.5 GB/s)
+[System_Config.Ethos_U55_High_End_Embedded]
+axi0_port=Sram
+axi1_port=OffChipFlash
+Sram_clock_scale=1.0
+Sram_burst_length=32
+Sram_read_latency=32
+Sram_write_latency=32
+OffChipFlash_clock_scale=0.125
+OffChipFlash_burst_length=128
+OffChipFlash_read_latency=64
+OffChipFlash_write_latency=64
+
+; -----------------------------------------------------------------------------
+; Memory Mode
+
+; Shared SRAM: the SRAM is shared between the Ethos-U and the Cortex-M software
+; The non-SRAM memory is assumed to be read-only
+[Memory_Mode.Shared_Sram]
+const_mem_area=Axi1
+arena_mem_area=Axi0
+cache_mem_area=Axi0
+
+; The SRAM (384KB) is only for use by the Ethos-U
+; The non-SRAM memory is assumed to be read-writeable
+[Memory_Mode.Dedicated_Sram]
+const_mem_area=Axi1
+arena_mem_area=Axi1
+cache_mem_area=Axi0
+arena_cache_size=393216
+
+"""
+ ],
+)
+def test_backend_compiler_parsing_vela_ini_file_missing_header(
+ vela_ini_file: Path,
+ input_str: str,
+) -> None:
+ """Test that ensures a KeyError
+ is raised when a vela.ini file with a missing header
+ is parsed.
+ """
+ with open(vela_ini_file, "w", encoding="utf8") as vela_file:
+ vela_file.write(input_str)
+ with pytest.raises(
+ KeyError, match="Vela.ini file missing expected header: core_clock"
+ ):
+ parse_vela_initialisation_file(
+ vela_ini_file, "Ethos_U55_High_End_Embedded", "Shared_Sram"
+ )
+
+
+def test_backend_compiler_model_already_compiled(
+ test_tflite_model: Path, monkeypatch: pytest.MonkeyPatch
+) -> None:
+ """Test that if we try compile a model twice,
+ the correct flag is passed and that main is called only once.
+ """
+ target_config = EthosUConfiguration.load_profile("ethos-u55-256")
+ recreate_directory(Path(target_config.compiler_options.output_dir))
+
+ main_mock = MagicMock(side_effect=main)
+ monkeypatch.setattr("mlia.backend.vela.compiler.main", main_mock)
+ compile_model(test_tflite_model, target_config.compiler_options)
+
+ def vela_compiler_compile_model_mock(
+ model_path: Path, *_: Any
+ ) -> tuple[None, Path]:
+ return None, Path(
+ Path(target_config.compiler_options.output_dir).as_posix()
+ + "/"
+ + model_path.stem
+ + "_vela"
+ + model_path.suffix
+ )
+
+ compiler_mock = MagicMock(side_effect=vela_compiler_compile_model_mock)
+ monkeypatch.setattr(
+ "mlia.backend.vela.compiler.VelaCompiler.compile_model", compiler_mock
+ )
+ compile_model(test_tflite_model, target_config.compiler_options)
+ main_mock.assert_called_once()
+ compiler_mock.assert_called_once_with(test_tflite_model, True)
diff --git a/tests/test_backend_vela_performance.py b/tests/test_backend_vela_performance.py
index 5800630..b4f8d4c 100644
--- a/tests/test_backend_vela_performance.py
+++ b/tests/test_backend_vela_performance.py
@@ -6,13 +6,14 @@ from unittest.mock import MagicMock
import pytest
-from mlia.backend.vela.compiler import optimize_model
+from mlia.backend.vela.compiler import compile_model
from mlia.backend.vela.performance import estimate_performance
from mlia.backend.vela.performance import layer_metrics
from mlia.backend.vela.performance import LayerwisePerfInfo
from mlia.backend.vela.performance import parse_layerwise_perf_csv
from mlia.backend.vela.performance import PerformanceMetrics
from mlia.target.ethos_u.config import EthosUConfiguration
+from mlia.utils.filesystem import recreate_directory
def test_estimate_performance(test_tflite_model: Path) -> None:
@@ -142,24 +143,6 @@ def test_estimate_performance_parse_layerwise_empty_csv_file(
assert len(layerwise_object.layerwise_info) == 0
-def test_estimate_performance_already_optimized(
- tmp_path: Path, test_tflite_model: Path
-) -> None:
- """Test that performance estimation should fail for already optimized model."""
- target_config = EthosUConfiguration.load_profile("ethos-u55-256")
-
- optimized_model_path = tmp_path / "optimized_model.tflite"
-
- optimize_model(
- test_tflite_model, target_config.compiler_options, optimized_model_path
- )
-
- with pytest.raises(
- Exception, match="Unable to estimate performance for the given optimized model"
- ):
- estimate_performance(optimized_model_path, target_config.compiler_options)
-
-
def test_read_invalid_model(test_tflite_invalid_model: Path) -> None:
"""Test that reading invalid model should fail with exception."""
with pytest.raises(
@@ -173,16 +156,18 @@ def test_compile_invalid_model(
test_tflite_model: Path, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
"""Test that if model could not be compiled then correct exception raised."""
+
mock_compiler = MagicMock()
mock_compiler.side_effect = Exception("Bad model!")
- monkeypatch.setattr("mlia.backend.vela.compiler.compiler_driver", mock_compiler)
+ monkeypatch.setattr("mlia.backend.vela.compiler.main", mock_compiler)
model_path = tmp_path / "optimized_model.tflite"
with pytest.raises(
Exception, match="Model could not be optimized with Vela compiler"
):
target_config = EthosUConfiguration.load_profile("ethos-u55-256")
- optimize_model(test_tflite_model, target_config.compiler_options, model_path)
+ recreate_directory(Path(target_config.compiler_options.output_dir))
+ compile_model(test_tflite_model, target_config.compiler_options)
assert not model_path.exists()
diff --git a/tests/test_cli_commands.py b/tests/test_cli_commands.py
index 480e642..1ce793f 100644
--- a/tests/test_cli_commands.py
+++ b/tests/test_cli_commands.py
@@ -207,7 +207,7 @@ def mock_performance_estimation(monkeypatch: pytest.MonkeyPatch) -> None:
metrics = PerformanceMetrics(
EthosUConfiguration.load_profile("ethos-u55-256"),
NPUCycles(1, 2, 3, 4, 5, 6),
- MemoryUsage(1, 2, 3, 4, 5),
+ MemoryUsage(1, 2, 3, 4),
LayerwisePerfInfo(layerwise_info=[]),
)
monkeypatch.setattr(
diff --git a/tests/test_core_context.py b/tests/test_core_context.py
index 0810ad0..9eb3d63 100644
--- a/tests/test_core_context.py
+++ b/tests/test_core_context.py
@@ -1,4 +1,4 @@
-# SPDX-FileCopyrightText: Copyright 2022-2023, Arm Limited and/or its affiliates.
+# SPDX-FileCopyrightText: Copyright 2022-2024, Arm Limited and/or its affiliates.
# SPDX-License-Identifier: Apache-2.0
"""Tests for the module context."""
from __future__ import annotations
@@ -59,7 +59,6 @@ def test_execution_context(tmp_path: Path) -> None:
event_publisher=publisher,
verbose=True,
logs_dir="logs_directory",
- models_dir="models_directory",
output_format="json",
)
@@ -74,7 +73,7 @@ def test_execution_context(tmp_path: Path) -> None:
assert context.event_handlers == []
assert context.event_publisher == publisher
assert context.logs_path == output_dir / "logs_directory"
- expected_model_path = output_dir / "models_directory/sample.model"
+ expected_model_path = output_dir / "sample.model"
assert context.get_model_path("sample.model") == expected_model_path
assert context.verbose is True
assert context.output_format == "json"
@@ -107,7 +106,7 @@ def test_execution_context_with_default_params(tmp_path: Path) -> None:
assert context_with_default_params.logs_path == output_dir / "logs"
default_model_path = context_with_default_params.get_model_path("sample.model")
- expected_default_model_path = output_dir / "models/sample.model"
+ expected_default_model_path = output_dir / "sample.model"
assert default_model_path == expected_default_model_path
assert context_with_default_params.output_format == "plain_text"
diff --git a/tests/test_target_ethos_u_data_analysis.py b/tests/test_target_ethos_u_data_analysis.py
index 3cddf10..0add7c2 100644
--- a/tests/test_target_ethos_u_data_analysis.py
+++ b/tests/test_target_ethos_u_data_analysis.py
@@ -98,7 +98,7 @@ def test_perf_metrics_diff() -> None:
cast(EthosUConfiguration, profile("ethos-u55-256")),
NPUCycles(1, 2, 3, 4, 5, 6),
# memory metrics are in kilobytes
- MemoryUsage(*[i * 1024 for i in range(1, 6)]), # type: ignore
+ MemoryUsage(*list(range(1, 5))), # type: ignore
LayerwisePerfInfo(layerwise_info=[]),
),
[
@@ -110,9 +110,7 @@ def test_perf_metrics_diff() -> None:
cast(EthosUConfiguration, profile("ethos-u55-256")),
NPUCycles(1, 2, 3, 4, 5, 6),
# memory metrics are in kilobytes
- MemoryUsage(
- *[i * 1024 for i in range(1, 6)] # type: ignore
- ),
+ MemoryUsage(*list(range(1, 5))), # type: ignore
LayerwisePerfInfo(layerwise_info=[]),
),
],
@@ -128,8 +126,8 @@ def test_perf_metrics_diff() -> None:
opt_diffs={
"sram": PerfMetricDiff(1.0, 1.0),
"dram": PerfMetricDiff(2.0, 2.0),
- "on_chip_flash": PerfMetricDiff(4.0, 4.0),
- "off_chip_flash": PerfMetricDiff(5.0, 5.0),
+ "on_chip_flash": PerfMetricDiff(3.0, 3.0),
+ "off_chip_flash": PerfMetricDiff(4.0, 4.0),
"npu_total_cycles": PerfMetricDiff(3, 3),
},
)
@@ -143,7 +141,7 @@ def test_perf_metrics_diff() -> None:
cast(EthosUConfiguration, profile("ethos-u55-256")),
NPUCycles(1, 2, 3, 4, 5, 6),
# memory metrics are in kilobytes
- MemoryUsage(*[i * 1024 for i in range(1, 6)]), # type: ignore
+ MemoryUsage(*list(range(1, 5))), # type: ignore
LayerwisePerfInfo(layerwise_info=[]),
),
[],
diff --git a/tests/test_target_ethos_u_data_collection.py b/tests/test_target_ethos_u_data_collection.py
index 3868b95..e034884 100644
--- a/tests/test_target_ethos_u_data_collection.py
+++ b/tests/test_target_ethos_u_data_collection.py
@@ -162,7 +162,7 @@ def mock_performance_estimation(
metrics = PerformanceMetrics(
target,
NPUCycles(1, 2, 3, 4, 5, 6),
- MemoryUsage(1, 2, 3, 4, 5),
+ MemoryUsage(1, 2, 3, 4),
LayerwisePerfInfo(layerwise_info=[]),
)
monkeypatch.setattr(
diff --git a/tests/test_target_ethos_u_performance.py b/tests/test_target_ethos_u_performance.py
index 76860b5..3042265 100644
--- a/tests/test_target_ethos_u_performance.py
+++ b/tests/test_target_ethos_u_performance.py
@@ -1,24 +1,10 @@
-# SPDX-FileCopyrightText: Copyright 2022, Arm Limited and/or its affiliates.
+# SPDX-FileCopyrightText: Copyright 2022-2024, Arm Limited and/or its affiliates.
# SPDX-License-Identifier: Apache-2.0
"""Performance estimation tests."""
from unittest.mock import MagicMock
import pytest
-from mlia.target.ethos_u.performance import MemorySizeType
-from mlia.target.ethos_u.performance import MemoryUsage
-
-
-def test_memory_usage_conversion() -> None:
- """Test MemoryUsage objects conversion."""
- memory_usage_in_kb = MemoryUsage(1, 2, 3, 4, 5, MemorySizeType.KILOBYTES)
- assert memory_usage_in_kb.in_kilobytes() == memory_usage_in_kb
-
- memory_usage_in_bytes = MemoryUsage(
- 1 * 1024, 2 * 1024, 3 * 1024, 4 * 1024, 5 * 1024
- )
- assert memory_usage_in_bytes.in_kilobytes() == memory_usage_in_kb
-
def mock_performance_estimation(monkeypatch: pytest.MonkeyPatch) -> None:
"""Mock performance estimation."""
diff --git a/tests/test_target_ethos_u_reporters.py b/tests/test_target_ethos_u_reporters.py
index 6dff6e1..cfee86d 100644
--- a/tests/test_target_ethos_u_reporters.py
+++ b/tests/test_target_ethos_u_reporters.py
@@ -41,7 +41,6 @@ from mlia.utils.console import remove_ascii_codes
memory_usage=MemoryUsage(
sram_memory_area_size=10,
dram_memory_area_size=0,
- unknown_memory_area_size=0,
on_chip_flash_memory_area_size=0,
off_chip_flash_memory_area_size=20,
memory_size_type=MemorySizeType.KILOBYTES,
@@ -140,7 +139,6 @@ Layer-Wise Metrics:
memory_usage=MemoryUsage(
sram_memory_area_size=10,
dram_memory_area_size=0,
- unknown_memory_area_size=0,
on_chip_flash_memory_area_size=0,
off_chip_flash_memory_area_size=20,
memory_size_type=MemorySizeType.KILOBYTES,
@@ -362,7 +360,6 @@ def test_report_operators(
Const mem area Axi1
Arena mem area Axi0
Cache mem area Axi0
- Arena cache size 2,096,768 bytes
System config Ethos_U55_High_End_Embedded
Accelerator clock 500,000,000 Hz
@@ -376,28 +373,11 @@ def test_report_operators(
Read latency 32 cycles
Write latency 32 cycles
- Dram:
- Clock scales 1.0
- Burst length 1 byte
- Read latency 0 cycles
- Write latency 0 cycles
-
- OnChipFlash:
- Clock scales 1.0
- Burst length 1 byte
- Read latency 0 cycles
- Write latency 0 cycles
-
OffChipFlash:
Clock scales 0.125
Burst length 128 bytes
Read latency 64 cycles
- Write latency 64 cycles
-
- Architecture settings:
- Permanent storage mem area OffChipFlash
- Feature map storage mem area Sram
- Fast storage mem area Sram""",
+ Write latency 64 cycles""",
{
"target": {
"target": "ethos-u55",
@@ -406,7 +386,6 @@ def test_report_operators(
"const_mem_area": "Axi1",
"arena_mem_area": "Axi0",
"cache_mem_area": "Axi0",
- "arena_cache_size": {"value": 2096768, "unit": "bytes"},
},
"system_config": {
"accelerator_clock": {"value": 500000000.0, "unit": "Hz"},
@@ -419,18 +398,6 @@ def test_report_operators(
"read_latency": {"value": 32, "unit": "cycles"},
"write_latency": {"value": 32, "unit": "cycles"},
},
- "Dram": {
- "clock_scales": 1.0,
- "burst_length": {"value": 1, "unit": "byte"},
- "read_latency": {"value": 0, "unit": "cycles"},
- "write_latency": {"value": 0, "unit": "cycles"},
- },
- "OnChipFlash": {
- "clock_scales": 1.0,
- "burst_length": {"value": 1, "unit": "byte"},
- "read_latency": {"value": 0, "unit": "cycles"},
- "write_latency": {"value": 0, "unit": "cycles"},
- },
"OffChipFlash": {
"clock_scales": 0.125,
"burst_length": {"value": 128, "unit": "bytes"},
@@ -439,11 +406,6 @@ def test_report_operators(
},
},
},
- "arch_settings": {
- "permanent_storage_mem_area": "OffChipFlash",
- "feature_map_storage_mem_area": "Sram",
- "fast_storage_mem_area": "Sram",
- },
}
},
],