Running NoC Partition Mode Simulations
In this section, we provide a step-by-step tutorial on how to partition a SoC with a ring NoC containing four tiles into 3 FPGAs connected as a ring. We will run this on the local Xilinx Alveo U250 FPGAs. Note that this feature is highly experimental and has not been tested on EC2 F1. This assumes that you have read the FireAxe running fast-mode simulations.
Some caveats of this mode:
You have to disable TracerV by mixing in the
WithNoTraceIOconfiguration fragment to theTARGET_CONFIG.Works only for mesh or ring based NoC topologies where each FPGA is connected to exactly 2 other FPGAs.
1. Building Partitioned Sims: Setting up FireAxe Target configs
Similarily to the fast-mode, we need to setup the FireAxe
target configurations. One change to note is that we are now using the WithQSFP
configuration fragment. With this information, FireAxe will generate an Aurora IP in the
FPGA shell to exchange data between multiple FPGAs using the QSFP interconnects. Also,
note that we added the WithFireAxeNoCPart. This tells the compiler to perform the
NoC-partition pass by grouping router nodes and modules connected to those router nodes.
The indices in WithPartitionGlobalInfo now indicates the router node indices.
//////////////////////////////////////////////////////////////////////////////
// - Xilinx U250 partition a ring NoC onto 3 FPGA connected as a ring
//
// FPGA 0 ------------------ FPGA 1
// - router 0 & tile 0 - router 2 & tile 2
// - router 1 & tile 1 - router 3 & tile 3
// \ /
// \ /
// \ /
// \ /
// FPGA 2
// - router nodes 4 ~ 10
// - SoC subsystem
//////////////////////////////////////////////////////////////////////////////
class QuadTileRingNoCTopoQSFPXilinxAlveoConfig
extends Config(
new WithQSFP ++ // FireSim will instantiate Aurora IPs
new WithFireAxeNoCPart ++ // Compiler will detect NoC router nodes to partition
new WithPartitionGlobalInfo(
Seq(
Seq("0", "1"),
Seq("2", "3"),
// base group has to be put last
(4 until 10).map(i => s"${i}"),
)
) ++
new BaseXilinxAlveoU250Config
)
class QuadTileRingNoCU250Base
extends Config(
new WithPartitionBase ++
new QuadTileRingNoCTopoQSFPXilinxAlveoConfig
)
class QuadTileRingNoCU250Partition0
extends Config(
new WithPartitionIndex(0) ++
new QuadTileRingNoCTopoQSFPXilinxAlveoConfig
)
class QuadTileRingNoCU250Partition1
extends Config(
new WithPartitionIndex(1) ++
new QuadTileRingNoCTopoQSFPXilinxAlveoConfig
)
2. Building Partitioned Sims: config_build_recipes.yaml
We can specify the config_build_recipes.yaml at this point.
##############################################################################
# Using the NoC-partition-mode to partition the design across 3 FPGAs
# connected as a ring.
##############################################################################
# xilinx_u250_quad_rocket_ring_base:
# ...
# PLATFORM: xilinx_alveo_u250
# TARGET_CONFIG: FireSimQuadRocketSbusRingNoCConfig
# PLATFORM_CONFIG: QuadTileRingNoCBase
# bit_builder_recipe: bit-builder-recipes/xilinx_alveo_u250.yaml
# ...
#
# xilinx_u250_quad_rocket_ring_0:
# ...
# PLATFORM: xilinx_alveo_u250
# TARGET_CONFIG: FireSimQuadRocketSbusRingNoCConfig
# PLATFORM_CONFIG: QuadTileRingNoC0
# bit_builder_recipe: bit-builder-recipes/xilinx_alveo_u250.yaml
# ...
#
# xilinx_u250_quad_rocket_ring_1:
# ...
# PLATFORM: xilinx_alveo_u250
# TARGET_CONFIG: FireSimQuadRocketSbusRingNoCConfig
# PLATFORM_CONFIG: QuadTileRingNoC1
# bit_builder_recipe: bit-builder-recipes/xilinx_alveo_u250.yaml
# ...
3. Running Partitioned Simulations: user_topology.py
Once again, we need to specify the FireAxe topology to run simulations.
def fireaxe_ring_noc_config(self) -> None:
hwdb_entries = {
0: "xilinx_u250_quad_rocket_ring_0",
1: "xilinx_u250_quad_rocket_ring_1",
2: "xilinx_u250_quad_rocket_ring_base",
}
slotid_to_pidx = [0, 1, 2]
# DOC include start: fireaxe_ring_noc_config edges
edges = [
FireAxeEdge(FireAxeNodeBridgePair(0, 0), FireAxeNodeBridgePair(2, 1)),
FireAxeEdge(FireAxeNodeBridgePair(2, 0), FireAxeNodeBridgePair(1, 1)),
FireAxeEdge(FireAxeNodeBridgePair(1, 0), FireAxeNodeBridgePair(0, 1)),
]
# DOC include end: fireaxe_ring_noc_config edges
mode = PartitionMode.NOC_MODE
self.fireaxe_topology_config(hwdb_entries, edges, slotid_to_pidx, mode)
Lets look at how the FireAxe topology is set in this example. We can see that bridge 0
of partition N is always connected to bridge 1 of partition (N + 1) % NFPGAs and
bridge 1 of partition N is always connected to bridge 0 of partition (N + NFPGAs -
1) % NFPGAs.
We can specify the above topology as below:
edges = [
FireAxeEdge(FireAxeNodeBridgePair(0, 0), FireAxeNodeBridgePair(2, 1)),
FireAxeEdge(FireAxeNodeBridgePair(2, 0), FireAxeNodeBridgePair(1, 1)),
FireAxeEdge(FireAxeNodeBridgePair(1, 0), FireAxeNodeBridgePair(0, 1)),
]
4. Running Partitioned Simulations: config_runtime.yaml
Now we can update config_runtime.yaml to run FireAxe simulations.
target_config:
topology: fireaxe_rocket_ring_noc_config