Skip to content

Commit 393dd9d

Browse files
committed
Add PyTest suite for Siracusa and Siracusa Tiled
1 parent 1929253 commit 393dd9d

File tree

7 files changed

+536
-22
lines changed

7 files changed

+536
-22
lines changed

Deeploy/TilingExtension/TilerExtension.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class Tiler():
5252
_MINIMALLOC_OUTPUT_FILENAME = "output_minimalloc"
5353

5454
# Initialize with the list of TemplateTCFbinding
55-
def __init__(self, memoryHierarchy: MemoryHierarchy):
55+
def __init__(self, memoryHierarchy: MemoryHierarchy, testName: Optional[str] = None, workDir: Optional[str] = None):
5656

5757
self.memoryHierarchy = memoryHierarchy
5858
self.tilerModel: Optional[TilerModel] = None
@@ -66,6 +66,23 @@ def __init__(self, memoryHierarchy: MemoryHierarchy):
6666
self.memoryAllocStrategy: Literal["TetrisRandom", "TetrisCo-Opt", "MiniMalloc"] = "TetrisRandom"
6767
self.searchStrategy: Literal["min", "max", "random-max"] = "random-max"
6868

69+
if workDir is not None:
70+
os.makedirs(workDir, exist_ok = True)
71+
minimalloc_base = os.path.join(workDir, self._MINIMALLOC_INPUT_FILENAME)
72+
minimalloc_output_base = os.path.join(workDir, self._MINIMALLOC_OUTPUT_FILENAME)
73+
else:
74+
minimalloc_base = self._MINIMALLOC_INPUT_FILENAME
75+
minimalloc_output_base = self._MINIMALLOC_OUTPUT_FILENAME
76+
77+
if testName is not None:
78+
# VJUNG: Sanitize path
79+
safe_test_name = testName.replace("/", "_").replace("\\", "_")
80+
self._minimalloc_input = f"{minimalloc_base}_{safe_test_name}"
81+
self._minimalloc_output = f"{minimalloc_output_base}_{safe_test_name}"
82+
else:
83+
self._minimalloc_input = minimalloc_base
84+
self._minimalloc_output = minimalloc_output_base
85+
6986
@property
7087
def worstCaseBufferSize(self):
7188
return self._worstCaseBufferSize
@@ -238,7 +255,7 @@ def _convertCtxtToStaticSchedule(self, ctxt: NetworkContext,
238255

239256
def minimalloc(self, memoryMap, ctxt, nodeMemoryConstraint, capacity: int, memoryLevel: str):
240257

241-
with open(f"{self._MINIMALLOC_INPUT_FILENAME}.csv", mode = "w", newline = "") as file:
258+
with open(f"{self._minimalloc_input}.csv", mode = "w", newline = "") as file:
242259
writer = csv.writer(file, lineterminator = "\n")
243260
writer.writerow(["id", "lower", "upper", "size"])
244261
for memoryBlock in memoryMap:
@@ -273,7 +290,7 @@ def minimalloc(self, memoryMap, ctxt, nodeMemoryConstraint, capacity: int, memor
273290

274291
minimallocOutput = subprocess.run([
275292
f"{minimallocInstallDir}/minimalloc", f"--capacity={capacity}",
276-
f"--input={self._MINIMALLOC_INPUT_FILENAME}.csv", f"--output={self._MINIMALLOC_OUTPUT_FILENAME}.csv"
293+
f"--input={self._minimalloc_input}.csv", f"--output={self._minimalloc_output}.csv"
277294
],
278295
capture_output = True,
279296
text = True)
@@ -284,7 +301,7 @@ def minimalloc(self, memoryMap, ctxt, nodeMemoryConstraint, capacity: int, memor
284301
)
285302
raise subprocess.CalledProcessError(minimallocOutput.returncode, " ".join(minimallocOutput.args))
286303

287-
with open(f"{self._MINIMALLOC_OUTPUT_FILENAME}.csv", mode = "r", newline = "") as file:
304+
with open(f"{self._minimalloc_output}.csv", mode = "r", newline = "") as file:
288305
reader = csv.reader(file)
289306
header = next(reader)
290307
for row in reader:
@@ -944,11 +961,11 @@ def testMemoryMapCorrectness(self, memoryMap: Dict[str, List[List[MemoryBlock]]]
944961

945962
class TilerDeployerWrapper(NetworkDeployerWrapper):
946963

947-
def __init__(self, deployer: Union[MemoryLevelAwareDeployer, MemoryDeployerWrapper], tilerCls: Type[Tiler] = Tiler):
964+
def __init__(self, deployer: Union[MemoryLevelAwareDeployer, MemoryDeployerWrapper], tilerCls: Type[Tiler] = Tiler, testName: Optional[str] = None, workDir: Optional[str] = None):
948965
super().__init__(deployer)
949966
assert isinstance(self.Platform, (MemoryPlatform, MemoryPlatformWrapper)), \
950967
f"Platform should be a MemoryPlatform or MemoryPlatformWrapper! Got {type(self.Platform).__name__}"
951-
self.tiler = tilerCls(self.Platform.memoryHierarchy)
968+
self.tiler = tilerCls(self.Platform.memoryHierarchy, testName = testName, workDir = workDir)
952969

953970
@property
954971
def worstCaseBufferSize(self):

DeeployTest/conftest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,14 @@ def pytest_configure(config: pytest.Config) -> None:
5151
# Register custom markers
5252
config.addinivalue_line("markers", "generic: mark test as a Generic platform test")
5353
config.addinivalue_line("markers", "cortexm: mark test as a Cortex-M (QEMU-ARM) platform test")
54+
config.addinivalue_line("markers", "siracusa: mark test as a Siracusa platform test (untiled)")
55+
config.addinivalue_line("markers", "siracusa_tiled: mark test as a Siracusa platform test (tiled)")
5456
config.addinivalue_line("markers", "kernels: mark test as a kernel test (individual operators)")
5557
config.addinivalue_line("markers", "models: mark test as a model test (full networks)")
58+
config.addinivalue_line("markers", "singlebuffer: mark test as single-buffer configuration")
59+
config.addinivalue_line("markers", "doublebuffer: mark test as double-buffer configuration")
60+
config.addinivalue_line("markers", "l2: mark test as L2 default memory level")
61+
config.addinivalue_line("markers", "l3: mark test as L3 default memory level")
5662
config.addinivalue_line("markers", "slow: mark test as slow running")
5763

5864
# Configure logging based on verbosity

DeeployTest/testMVP.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import argparse
66
import os
77
import sys
8+
import hashlib
89
from collections import OrderedDict
910
from typing import List, Tuple
1011

@@ -115,14 +116,18 @@ def setupDeployer(graph: gs.Graph, memoryHierarchy: MemoryHierarchy, defaultTarg
115116
deployer = MemoryDeployerWrapper(deployer, memoryLevelAnnotationPasses)
116117

117118
# Make the deployer tiler aware
119+
# VJUNG: Create unique ID for the IO files of minimalloc and prevent conflict in case of parallel execution
120+
unique_params = f"{args.dumpdir}_L1{args.l1}_L2{args.l2}_{args.defaultMemLevel}_DB{args.doublebuffer}"
121+
testIdentifier = hashlib.md5(unique_params.encode()).hexdigest()[:16]
122+
118123
if args.doublebuffer:
119124
assert args.defaultMemLevel in ["L3", "L2"]
120125
if args.defaultMemLevel == "L3":
121-
deployer = TilerDeployerWrapper(deployer, DBOnlyL3Tiler)
126+
deployer = TilerDeployerWrapper(deployer, DBOnlyL3Tiler, testName = testIdentifier, workDir = args.dumpdir)
122127
else:
123-
deployer = TilerDeployerWrapper(deployer, DBTiler)
128+
deployer = TilerDeployerWrapper(deployer, DBTiler, testName = testIdentifier, workDir = args.dumpdir)
124129
else:
125-
deployer = TilerDeployerWrapper(deployer, SBTiler)
130+
deployer = TilerDeployerWrapper(deployer, SBTiler, testName = testIdentifier, workDir = args.dumpdir)
126131

127132
deployer.tiler.visualizeMemoryAlloc = args.plotMemAlloc
128133
deployer.tiler.memoryAllocStrategy = args.memAllocStrategy

DeeployTest/testUtils/pytestRunner.py

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import re
77
import shutil
88
import subprocess
9+
import sys
910
from dataclasses import dataclass
1011
from pathlib import Path
1112
from typing import List, Literal, Optional, Tuple
@@ -132,10 +133,10 @@ def generate_network(config: DeeployTestConfig, skip: bool = False) -> None:
132133

133134
log.debug(f"[pytestRunner] Generation command: {' '.join(cmd)}")
134135

135-
result = subprocess.run(cmd, capture_output = True, text = True)
136+
result = subprocess.run(cmd, check = False)
136137

137138
if result.returncode != 0:
138-
log.error(f"Network generation failed:\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}")
139+
log.error(f"Network generation failed with return code {result.returncode}")
139140
raise RuntimeError(f"Network generation failed for {config.test_name}")
140141

141142

@@ -192,10 +193,10 @@ def configure_cmake(config: DeeployTestConfig) -> None:
192193

193194
log.debug(f"[pytestRunner] CMake command: {' '.join(cmd)}")
194195

195-
result = subprocess.run(cmd, capture_output = True, text = True, env = env)
196+
result = subprocess.run(cmd, check = False, env = env)
196197

197198
if result.returncode != 0:
198-
log.error(f"CMake configuration failed:\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}")
199+
log.error(f"CMake configuration failed with return code {result.returncode}")
199200
raise RuntimeError(f"CMake configuration failed for {config.test_name}")
200201

201202

@@ -223,10 +224,10 @@ def build_binary(config: DeeployTestConfig) -> None:
223224

224225
log.debug(f"[pytestRunner] Build command: {' '.join(cmd)}")
225226

226-
result = subprocess.run(cmd, capture_output = True, text = True, env = env)
227+
result = subprocess.run(cmd, check = False, env = env)
227228

228229
if result.returncode != 0:
229-
log.error(f"Build failed:\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}")
230+
log.error(f"Build failed with return code {result.returncode}")
230231
raise RuntimeError(f"Build failed for {config.test_name}")
231232

232233

@@ -281,6 +282,12 @@ def run_simulation(config: DeeployTestConfig, skip: bool = False) -> TestResult:
281282

282283
result = subprocess.run(cmd, capture_output = True, text = True, env = env)
283284

285+
# Print captured output so it's visible when running with pytest -s
286+
if result.stdout:
287+
print(result.stdout, end = '')
288+
if result.stderr:
289+
print(result.stderr, end = '', file = sys.stderr)
290+
284291
# Parse output for error count
285292
output = result.stdout + result.stderr
286293

@@ -362,19 +369,39 @@ def create_test_config(
362369
toolchain_dir: Optional[str],
363370
cmake_args: List[str],
364371
tiling: bool = False,
372+
cores: Optional[int] = None,
373+
l1: Optional[int] = None,
374+
l2: int = 1024000,
375+
default_mem_level: str = "L2",
376+
double_buffer: bool = False,
377+
mem_alloc_strategy: str = "MiniMalloc",
378+
search_strategy: str = "random-max",
379+
profile_tiling: bool = False,
380+
plot_mem_alloc: bool = False,
381+
randomized_mem_scheduler: bool = False,
365382
) -> DeeployTestConfig:
366383
"""
367384
Create DeeployTestConfig for a specific test and platform.
368385
369386
Args:
370387
test_name: Name of the test
371-
platform: Target platform (e.g., "Generic", "QEMU-ARM")
388+
platform: Target platform (e.g., "Generic", "QEMU-ARM", "Siracusa")
372389
simulator: Simulator to use
373390
deeploy_test_dir: Base DeeployTest directory
374391
toolchain: Toolchain to use - LLVM/GCC
375392
toolchain_dir: Path to toolchain installation
376393
cmake_args: Additional CMake arguments
377394
tiling: Whether to use tiling
395+
cores: Number of cores (for Siracusa platforms)
396+
l1: L1 memory size in bytes (for tiled platforms)
397+
l2: L2 memory size in bytes (default: 1024000)
398+
default_mem_level: Default memory level ("L2" or "L3")
399+
double_buffer: Enable double buffering
400+
mem_alloc_strategy: Memory allocation strategy
401+
search_strategy: CP solver search strategy
402+
profile_tiling: Enable tiling profiling
403+
plot_mem_alloc: Enable memory allocation plotting
404+
randomized_mem_scheduler: Enable randomized memory scheduler
378405
379406
Returns:
380407
DeeployTestConfig instance
@@ -384,7 +411,39 @@ def create_test_config(
384411
gen_dir, test_dir_abs, test_name_clean = get_test_paths(test_dir, platform, base_dir = deeploy_test_dir)
385412

386413
worker_id = get_worker_id()
387-
build_dir = str(Path(deeploy_test_dir) / f"TEST_{platform.upper()}" / f"build_{worker_id}")
414+
415+
# VJUNG: Build dir has to be unique for each worker to prevent conflict
416+
build_suffix = Path(gen_dir).name
417+
build_dir = str(Path(deeploy_test_dir) / f"TEST_{platform.upper()}" / f"build_{worker_id}_{build_suffix}")
418+
419+
cmake_args_list = list(cmake_args) if cmake_args else []
420+
if cores is not None:
421+
cmake_args_list.append(f"NUM_CORES={cores}")
422+
423+
gen_args_list = []
424+
425+
if cores is not None and platform in ["Siracusa", "Siracusa_w_neureka"]:
426+
gen_args_list.append(f"--cores={cores}")
427+
428+
if tiling:
429+
if l1 is not None:
430+
gen_args_list.append(f"--l1={l1}")
431+
if l2 != 1024000:
432+
gen_args_list.append(f"--l2={l2}")
433+
if default_mem_level != "L2":
434+
gen_args_list.append(f"--defaultMemLevel={default_mem_level}")
435+
if double_buffer:
436+
gen_args_list.append("--doublebuffer")
437+
if mem_alloc_strategy != "MiniMalloc":
438+
gen_args_list.append(f"--memAllocStrategy={mem_alloc_strategy}")
439+
if search_strategy != "random-max":
440+
gen_args_list.append(f"--searchStrategy={search_strategy}")
441+
if profile_tiling:
442+
gen_args_list.append("--profileTiling")
443+
if plot_mem_alloc:
444+
gen_args_list.append("--plotMemAlloc")
445+
if randomized_mem_scheduler:
446+
gen_args_list.append("--randomizedMemoryScheduler")
388447

389448
config = DeeployTestConfig(
390449
test_name = test_name_clean,
@@ -396,7 +455,8 @@ def create_test_config(
396455
build_dir = build_dir,
397456
toolchain = toolchain,
398457
toolchain_install_dir = toolchain_dir,
399-
cmake_args = cmake_args,
458+
cmake_args = cmake_args_list,
459+
gen_args = gen_args_list,
400460
)
401461

402462
return config

0 commit comments

Comments
 (0)