# Assignment 10: Using the whole NPU
This week you will perform the matrix multiplication on the whole NPU.
For this, you will adapt the data-movement code and execute your XDNA tensor kernel on all compute tiles.
**Prerequisites:** Run the following to enter the development environment and install torch.
```bash
nix develop nix-amd-npu#iron-full
iron-fhs
pip install torch # only once
```
## Data Layout and Data Movement
In main memory, the matrices are stored in row-major order (`in0: MK`, `in1: KN`, and `out: MN` with `M=256`, `N=128`, and `K=1024`).
During the data movement from L3 (main memory) to L1 (scratchpad), the matrices are first tiled and then broadcast along the columns or rows.
The dimensions are split as follows:
- `M->axpm` with `a=2`, `x=8`, `p=2`, `m=8`,
- `N->byqn` with `b=2`, `y=4`, `q=2`, `n=8`, and
- `K->crk` with `c=16`, `r=8`, `k=8`.
This yields the views `in0: axpmcrk`, `in1: crkbyqn`, and `out: axpmbyqn`.
During the data movement to the L1 scratchpad memory, the layout must be changed to `in0: prmk`, `in1: rqkn`, and `out: pqmn`.
The dimensions `a`, `b`, and `c` are handled sequentially through loops on the compute tiles.
The dimensions `x` and `y` are handled spatially by distributing them across the compute-tile columns and rows, respectively.
The DMAs move the corresponding tiles from the memory tile to the compute tiles, broadcasting the `in0` tiles along the columns and the `in1` tiles along the rows, so that each column receives a different `in0` tile and each row receives a different `in1` tile.
Before the `c` loop, the output tile is zero-initialized.
When writing the `out` tiles, the four `out` tiles produced by the compute tiles of one column are joined into the intermediate layout `ypqmn`.
The dimension `y` is realized by giving each row's L1L2 FIFO a different write offset into the joined L2 buffer.
Note that the layout is changed to `ypmqn` when reading the data from L2 to the stream.
When writing the output tiles from the memory tile to the main memory, the layout is changed to a matrix layout (`out: MN`).
## Task 1 - Setup of the Whole NPU
**Create** variables for each tile (shim, memory, and compute).
**Adapt** the fused `ab`-loop size and **duplicate** the core function for each compute tile.
**Set** the FIFO queue suffixes inside each core function so that they match its column and row: replace the placeholder suffixes `_0` (for `in0`/`in1`) and `_0_0` (for `out`) with the actual tile coordinates, yielding `in0_
`, `in1_`, and `out__`.
Row indices in the FIFO suffixes are zero-based and counted from the first compute-tile row (so `aie.tile(_, 2)` → row index 0, `aie.tile(_, 3)` → row index 1, etc.). For example, for `aie.tile(7, 3)` the suffixes are `@in0_L2L1_7`, `@in1_L2L1_1`, and `@out_L1L2_7_1`.
## Task 2 - Broadcasting the Inputs
**Extend** the existing `L2L1` input FIFO queues by adding the additional consumer tiles along row 0 (`in0`) or column 0 (`in1`).
**Create** the needed `L2L1` and `L3L2` input FIFO queues for the remaining columns and rows.
Note that only four `in1_L3L2` FIFO queues are needed. Their placement on the shim tiles can be freely chosen.
**Adapt** the input `dma_memcpy_nd` operations to their corresponding FIFO queue.
Note that each shim tile has 16 buffer descriptors; consequently, FIFO queues on *different* shim tiles may reuse the same BD id.
## Task 3 - Writing the Output
**Create** the needed `L1L2` and `L2L3` output FIFO queues and **join** the `L1L2` FIFO queues along a column to the corresponding `L2L3` FIFO queue.
**Adapt** the output `dma_memcpy_nd` operations to their corresponding FIFO queue and **change** their sizes, strides, and offsets.
Note that the `dma_memcpy_nd` operations in each shim tile receive the output tensor tiles in the layout `aby(p*m)(q*n)`, and `x` is handled spatially across the columns and therefore does not appear in this view.
The dimensions `b` and `y` could be fused in this view, but we keep them separate.
## Task 4 - Testing
**Test** your implementation by running `make run_matmul`.
## Task 5 - Selection of Spatial Dimensions (optional)
**Explain** how the data transfer would change if the roles of the dimensions `y` and `x` were switched, i.e., the dimensions were split as follows:
- `M->aypm` with `a=4`, `y=4`, `p=2`, `m=8`,
- `N->bxqn` with `b=1`, `x=8`, `q=2`, `n=8`, and
- `K->crk` with `c=16`, `r=8`, `k=8`.
In which cases should `in0` be broadcast along the rows, and in which cases should `in1` be broadcast along the rows?
When is no difference in performance expected?
## Group Specific Component
At the end of the course, your group will work on a project of your own choosing.
**Prepare** a pitch for at least two possible project ideas: one targeting the XDNA NPU and one targeting GPUs with cuTile.
**Create** at least two slides per project idea, covering
- a brief introduction,
- the problem formulation,
- the proposed solution, and
- the expected results or insights.
You will present both project ideas on 17.06.2026, with about 5 minutes for each idea.
**You do not have to hand in the slides. Hand in a short description (2-3 sentences) about each project idea.**