Skip to content

Commit f2852e9

Browse files
committed
Added ApplyPartitioning tests, Aurora packaging tests success, fixed various other tests, removed unnecessary test file
1 parent c9df260 commit f2852e9

11 files changed

Lines changed: 301 additions & 213 deletions

File tree

src/finn/builder/build_dataflow_config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ class PartitioningConfiguration:
232232

233233
# The number of ports per device - this might change in meaning,
234234
# depending on the communication kernel used
235+
# TODO: Should be moved into platforms.py
235236
ports_per_device: int = 2
236237

237238
# What strategy to use to partition the dataflow graph

src/finn/interface/manage_tests.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,10 @@ def run_test(variant: str, num_workers: str, args: str = "", no_cache_clear: boo
6161
"given (please additionally pass --args "
6262
"<test-name> in pytest syntax)"
6363
)
64-
subprocess.run(shlex.split(f"{sys.executable} -m pytest {args}", posix=IS_POSIX))
64+
workers_arg = f"-n {num_workers}" if num_workers not in ["1", ""] else ""
65+
subprocess.run(
66+
shlex.split(f"{sys.executable} -m pytest {workers_arg} {args}", posix=IS_POSIX)
67+
)
6568
case "doctest":
6669
if args == "":
6770
status(

src/finn/transformation/fpgadataflow/multifpga/aurora/prepare_aurora.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def __init__(
3636
self.make_args = " ".join(
3737
f"{k}={v}" for k, v in partitioning_configuration.communication_kernel_arguments.items()
3838
)
39+
self.ports = partitioning_configuration.ports_per_device
3940
self.aurora_storage = Path(make_build_dir("aurora_storage_")).absolute()
4041
self.aurora_path = get_settings().finn_deps / "AuroraFlow"
4142
if not self.aurora_path.exists():
@@ -65,6 +66,12 @@ def package_single(self, args: str, device: int, index: int) -> Path:
6566
>>> xo.parent.parent.name
6667
'auroraflow_build_dev0_ind0'
6768
"""
69+
if index >= self.ports:
70+
raise FINNMultiFPGAConfigError(
71+
f"Cannot create aurora kernel number {index+1} "
72+
f"(index {index}), since the configuration/device "
73+
f"has only {self.ports} networking ports."
74+
)
6875
if self.verbosity == MFVerbosity.HIGH:
6976
log.info(f"Packaging AuroraFlow core (Device: {device}, Index: {index})")
7077

src/finn/util/resources.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1+
from numbers import Real
12
from qonnx.core.modelwrapper import ModelWrapper
3+
from typing import cast
24

35
from finn.analysis.fpgadataflow.hls_synth_res_estimation import hls_synth_res_estimation
46
from finn.analysis.fpgadataflow.res_estimation import res_estimation
57
from finn.util.exception import FINNUserError
68
from finn.util.logging import log
79
from finn.util.platforms import Platform
810

9-
ResourceEstimates = dict[str, dict[str, int | float]]
11+
ResourceEstimates = dict[str, dict[str, Real]]
1012
"""Short alias for a resource dict."""
1113

12-
ResourceEstimatesByIndex = dict[int, dict[str, int | float]]
14+
ResourceEstimatesByIndex = dict[int, dict[str, Real]]
1315
"""Short alias for a resource dict."""
1416

1517

@@ -56,25 +58,44 @@ def _merge_resource_estimations(
5658
return result
5759

5860

59-
def get_estimated_model_resources(
60-
model: ModelWrapper, fpga_part: str, considered_resources: list[str]
61+
def get_estimated_model_resources( # noqa
62+
model: ModelWrapper,
63+
fpga_part: str,
64+
considered_resources: list[str],
65+
add_missing_resources: bool,
6166
) -> ResourceEstimatesByIndex:
6267
"""Gather resources of all layers based on estimates both from FINNs HWCustomOp implementation,
6368
as well as the HLS reports. These are then merged to produce
6469
the worst case resource estimates and returned.
6570
66-
If there are layers that don't have an estimate
67-
for all considered resources, an error is raised.
71+
Arguments:
72+
---------
73+
`model`: The model to estimate.
74+
`fpga_part`: FPGA Part identifier.
75+
`considered_resources`: A list of resource types to consider.
76+
`add_missing_resources`: Determines behaviour in case a resource type
77+
from `considered_resources` is not found: If `True`, then missing
78+
resource types are set to 0. If, for example, a layer has no `FF` estimate
79+
in either FINN or HLS estimation, FF: 0 is entered for these layers. If set
80+
to `False`, an error is raised instead.
6881
"""
6982
estimates: ResourceEstimates = res_estimation(model, fpga_part)
7083
hls_estimates: ResourceEstimates = hls_synth_res_estimation(model)
7184
result: ResourceEstimates = _merge_resource_estimations(estimates, hls_estimates)
7285
resource_missing = False
86+
87+
# Check if resource types are missing (and add them)
7388
for layer in result:
7489
for restype in considered_resources:
7590
if restype not in result[layer]:
76-
resource_missing = True
77-
log.error(f"Node {layer} has no resource estimation for resource {restype}!")
91+
if add_missing_resources:
92+
result[layer][restype] = cast("Real", 0)
93+
log.info(f"Added missing resource estimation on layer {layer} ({restype}: 0)")
94+
else:
95+
resource_missing = True
96+
log.error(f"Node {layer} has no resource estimation for resource {restype}!")
97+
98+
# Check that estimates for all layers are available
7899
layer_missing = False
79100
est_by_index = {}
80101
for i, node in enumerate(model.graph.node):
@@ -84,7 +105,9 @@ def get_estimated_model_resources(
84105
log.error(f"No resource estimations were found for node {node.name}!")
85106
est_by_index[i] = result[node.name]
86107
if layer_missing or resource_missing:
87-
raise FINNUserError("At least one node is missing one or more resource estimation numbers.")
108+
raise FINNUserError(
109+
"At least one node is missing one or more resource estimation numbers.\n" + str(result)
110+
)
88111
return est_by_index
89112

90113

tests/fpgadataflow/multifpga/test_multifpga_aurora.py

Lines changed: 42 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -11,44 +11,38 @@
1111
from fpgadataflow.multifpga.utils import get_model
1212
from pathlib import Path
1313
from test_multifpga_sdp_creation import create_sdp_ready_model_no_branches
14-
from typing import TYPE_CHECKING, Any, Literal
1514

1615
from finn.builder.build_dataflow_config import (
1716
DataflowBuildConfig,
1817
MFCommunicationKernel,
1918
MFTopology,
2019
MFVerbosity,
21-
MIPSolver,
2220
PartitioningConfiguration,
2321
PartitioningStrategy,
2422
ShellFlowType,
2523
)
26-
from finn.transformation.fpgadataflow.multifpga.assign_metadata import AssignNetworkMetadata
27-
from finn.transformation.fpgadataflow.multifpga.aurora_metadata import AuroraNetworkMetadata
24+
from finn.transformation.fpgadataflow.multifpga.aurora.metadata import AuroraNetworkMetadata
25+
from finn.transformation.fpgadataflow.multifpga.aurora.partitioner import AuroraPartitioner
2826
from finn.transformation.fpgadataflow.multifpga.communication_kernels import PrepareAuroraFlow
2927
from finn.transformation.fpgadataflow.multifpga.create_multi_sdp import (
3028
CreateMultiFPGAStreamingDataflowPartition,
3129
)
32-
from finn.transformation.fpgadataflow.multifpga.partitioner import (
33-
AuroraPartitioner,
34-
PartitionForMultiFPGA,
35-
)
36-
from finn.transformation.fpgadataflow.multifpga.utils import available_resources, get_device_id
30+
from finn.transformation.fpgadataflow.multifpga.create_network_metadata import CreateNetworkMetadata
31+
from finn.transformation.fpgadataflow.multifpga.partition_model import PartitionForMultiFPGA
3732
from finn.util.basic import make_build_dir
3833
from finn.util.exception import (
3934
FINNError,
4035
FINNMultiFPGAConfigError,
4136
FINNMultiFPGANoPartitionerSolutionError,
4237
)
38+
from finn.util.fpgadataflow import get_device_id
4339
from finn.util.platforms import platforms
44-
45-
if TYPE_CHECKING:
46-
from qonnx.core.modelwrapper import ModelWrapper
40+
from finn.util.resources import available_resources
4741

4842

4943
@pytest.mark.auroraflow
5044
@pytest.mark.multifpga
51-
@pytest.mark.parametrize("board", ["U280", "U55C", "Pynq-Z1"])
45+
@pytest.mark.parametrize("board", ["U280", "U55C"])
5246
@pytest.mark.parametrize("topology", [MFTopology.CHAIN])
5347
@pytest.mark.parametrize(
5448
"communication_kernel_args",
@@ -78,13 +72,8 @@ def test_aurora_packaging_integrated(
7872
"""
7973
devices, nodes = device_node_combinations
8074

81-
# Check which creator we use
82-
assignment_order = {MFTopology.CHAIN: "linear"}[topology]
83-
8475
# Create an SDP ready branchless model
85-
model = create_sdp_ready_model_no_branches(
86-
nodes, devices, assignment_type, assignment_order, shuffle_devices
87-
)
76+
model = create_sdp_ready_model_no_branches(nodes, devices, assignment_type, shuffle_devices)
8877

8978
# Create a config based on the test parameters
9079
cfg = DataflowBuildConfig(
@@ -108,12 +97,19 @@ def test_aurora_packaging_integrated(
10897
)
10998
)
11099
model = model.transform(
111-
AssignNetworkMetadata(
112-
communication_kernel=cfg.partitioning_configuration.communication_kernel,
113-
topology=topology,
114-
verbosity=MFVerbosity.NONE,
100+
CreateNetworkMetadata(
101+
cfg.partitioning_configuration.communication_kernel, MFVerbosity.NONE
115102
)
116103
)
104+
105+
# No kernels packaged yet
106+
meta = AuroraNetworkMetadata.from_model(model)
107+
unprepared_aurora_kernels = len(meta.get_unprepared_aurora_kernels())
108+
if devices > 1:
109+
assert unprepared_aurora_kernels == (devices - 1) * 2
110+
else:
111+
assert unprepared_aurora_kernels == 0
112+
117113
model = model.transform(
118114
PrepareAuroraFlow(
119115
cfg._resolve_vitis_platform(), # noqa
@@ -124,6 +120,8 @@ def test_aurora_packaging_integrated(
124120

125121
# Try and load the previously generated metadata from the models metadata prop
126122
meta = AuroraNetworkMetadata.from_model(model)
123+
unprepared_aurora_kernels = len(meta.get_unprepared_aurora_kernels())
124+
assert unprepared_aurora_kernels == 0
127125

128126
# Check that the AuroraFlow storage directory got saved in the model metadata
129127
aurora_storage = model.get_metadata_prop("aurora_storage")
@@ -134,6 +132,10 @@ def test_aurora_packaging_integrated(
134132
assert aurora_storage.exists()
135133

136134
# Check if each device had its respective kernels packaged
135+
if devices == 1:
136+
assert len(meta.data.keys()) == 0
137+
else:
138+
assert len(meta.data.keys()) == devices
137139
for kerneldata in meta.data.values():
138140
for aurora in kerneldata:
139141
assert aurora.aurora_xo is not None
@@ -142,7 +144,7 @@ def test_aurora_packaging_integrated(
142144
@pytest.mark.multifpga
143145
@pytest.mark.slow
144146
def test_aurora_package_single(
145-
self, communication_kernel_args: dict[str, str], board: str
147+
self, communication_kernel_args: dict[str, str], board: str, topology: MFTopology
146148
) -> None:
147149
"""Test Aurora packaging. In detail:
148150
- Check that the names of the XO files produced by AuroraFlow didn't change.
@@ -154,6 +156,7 @@ def test_aurora_package_single(
154156
board=board,
155157
partitioning_configuration=PartitioningConfiguration(
156158
num_fpgas=2,
159+
topology=topology,
157160
communication_kernel=MFCommunicationKernel.AURORA,
158161
communication_kernel_arguments=communication_kernel_args,
159162
),
@@ -169,10 +172,14 @@ def test_aurora_package_single(
169172
build_dir = prep.aurora_storage / "auroraflow_build_dev0_ind0"
170173
assert build_dir.exists()
171174
assert res.exists()
172-
res = prep.package_single("", 1, 2)
173-
build_dir = prep.aurora_storage / "auroraflow_build_dev1_ind2"
174-
assert build_dir.exists()
175-
assert res.exists()
175+
176+
# On devices like the U280, there are only 2 QSFPs, so an index 2 (third kernel) cannot
177+
# be produced, hence the kernel should not be there.
178+
with pytest.raises(FINNError):
179+
res = prep.package_single("", 1, 2)
180+
build_dir = prep.aurora_storage / "auroraflow_build_dev1_ind2"
181+
assert build_dir.exists()
182+
assert res.exists()
176183

177184

178185
@pytest.mark.auroraflow
@@ -441,33 +448,15 @@ def test_objective_regression(
441448
TODO: Save recent CI run data in proper infrastructure as soon as we have set it
442449
up, instead of hardcoding the values.
443450
"""
444-
flow_type = self.get_shell_flow_type(board)
445-
model, _ = self.make_model(
446-
model_type, f"test_regression_model_{'_'.join(map(str, (model_type)))}"
447-
)
448-
_, _, part = self.prepare_and_partition_model(
449-
model,
450-
board,
451-
flow_type,
452-
2,
453-
topology,
454-
strategy,
455-
True,
456-
model_type[0],
457-
max_util=0.85,
458-
ideal_util=0.75,
459-
ports_per_device=2,
460-
separate_iodmas=True,
461-
target_fps=10,
462-
mvau_wwidth_max=1024,
463-
synth_clk_ns=5.0,
464-
solver=None,
465-
)
466-
assert part.partitioner is not None
467-
assert part.partitioner.model.objective.x is not None
468-
assert part.partitioner.model.objective.x < 0
451+
# flow_type = self.get_shell_flow_type(board)
452+
453+
# TODO
469454
raise NotImplementedError()
470455

456+
# assert part.partitioner is not None
457+
# assert part.partitioner.model.objective.x is not None
458+
# assert part.partitioner.model.objective.x < 0
459+
471460
@pytest.mark.parametrize(
472461
"distribution",
473462
["equal-LUT", "equal-FF"],
Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +0,0 @@
1-
import pytest
2-
3-
from test_multifpga_sdp_creation import create_sdp_ready_model_no_branches
4-
5-
from finn.builder.build_dataflow_config import (
6-
DataflowBuildConfig,
7-
MFCommunicationKernel,
8-
MFTopology,
9-
PartitioningConfiguration,
10-
)
11-
from finn.util.basic import make_build_dir
12-
13-
14-
@pytest.mark.multifpga
15-
def test_multifpga_end2end_mobilenet() -> None:
16-
raise NotImplementedError()
17-
18-
19-
@pytest.mark.multifpga
20-
@pytest.mark.parametrize(
21-
"device_node_combinations", [(2, 2), (5, 10), (40, 100), (50, 50), (1, 10)]
22-
)
23-
@pytest.mark.parametrize("assignment_type", ["random", "equal"])
24-
@pytest.mark.parametrize("topology", [MFTopology.CHAIN])
25-
@pytest.mark.parametrize("communication_kernel", [MFCommunicationKernel.AURORA])
26-
@pytest.mark.parametrize("shuffle_devices", [True, False])
27-
def test_multifpga_end2end_artifical_network(
28-
device_node_combinations: tuple[int, int],
29-
assignment_type: str,
30-
topology: MFTopology,
31-
communication_kernel: MFCommunicationKernel,
32-
shuffle_devices: bool,
33-
) -> None:
34-
devices, nodes = device_node_combinations
35-
assignment_order = None
36-
if topology == MFTopology.CHAIN:
37-
assignment_order = "linear"
38-
else:
39-
raise NotImplementedError()
40-
41-
# TODO: Make this model entirely from scratch
42-
# TODO: Collapse all params and only pass a partitioning config
43-
temps = make_build_dir("test_end2end_artificial_outputs")
44-
_ = DataflowBuildConfig(
45-
output_dir=temps,
46-
synth_clk_period_ns=5.0,
47-
partitioning_configuration=PartitioningConfiguration(
48-
num_fpgas=devices, topology=topology, communication_kernel=communication_kernel
49-
),
50-
)
51-
model = create_sdp_ready_model_no_branches(
52-
nodes, devices, assignment_type, assignment_order, shuffle_devices
53-
)
54-
model.set_metadata_prop("is_multifpga", "True")
55-
# model = step_create_multifpga_sdp(model, cfg)
56-
# model = step_prepare_network_infrastructure(model, cfg)
57-
raise NotImplementedError("Assertions.")

tests/fpgadataflow/multifpga/test_multifpga_integration.py

Whitespace-only changes.

0 commit comments

Comments
 (0)