Skip to content

Commit

Permalink
Update section-1 and section-3 examples (Xilinx#1179)
Browse files Browse the repository at this point in the history
  • Loading branch information
jackl-xilinx authored and fifield committed Apr 9, 2024
1 parent 2ed7544 commit 7237e62
Show file tree
Hide file tree
Showing 10 changed files with 770 additions and 22 deletions.
17 changes: 9 additions & 8 deletions programming_guide/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,23 @@
MLIR-AIE is an MLIR-based representation for AI Engine design. It provides a foundation from which complex and performant AI Engine designs can be defined and is supported by simulation and hardware impelemenation infrastructure. To better understand how AI Engine designs are defined at the MLIR level, it is recommended that you spend some time going through the [MLIR tutorial](../tutorials/) material. However, this programming guide is intended to lead you through a higher level abstraction (python) of the underlying MLIR-AIE framework and provide design examples and programming tips to allow users to build designs directly. Keep in mind also that MLIR-AIE is a foundational layer in a AI Engine software development framework and while this guide provides a programmer's view for using AI Engines, it also serves as a lower layer for higher abstraction MLIR layers such as [MLIR-AIR](https://github.com/Xilinx/mlir-air).

## Outline
<details><summary><a href="./section-1">Section 1 - Basic AI Engine building blocks (tiles and buffers)</a></summary>
<details><summary><a href="./section-1">Section 1 - Basic AI Engine building blocks</a></summary>

* Introduce AI Engine building blocks with references to Tutorial material
* Give example of python binded MLIR source for defining tiles and buffers
* Give example of python binded MLIR source for defining tiles
</details>
<details><summary><a href="./section-2">Section 2- My First Program</a></summary>

* Introduce example of first simple program (Bias Add)
* Illustrate how built-in simulation of single core design
</details>
<details><summary><a href="./section-3">Section 3 - Data Movement (Object FIFOs)</a></summary>
<details><summary><a href="./section-2">Section 2 - Data Movement (Object FIFOs)</a></summary>

* Introduce topic of objectfifos and how they abstract connections between objects in the AIE array
* Point to more detailed objectfifo material in Tutorial
* Introduce key objectfifo connection patterns (link/ broadcast, join/ distribute)
</details>
<details><summary><a href="./section-3">Section 3 - My First Program</a></summary>

* Introduce example of first simple program (Bias Add)
* Illustrate how built-in simulation of single core design
* Illustrate how to run designs on Ryzen AI enabled hardware
</details>
<details><summary><a href="./section-4">Section 4 - Vector programming & Peformance Measurement</a></summary>

* Discuss topic of vector programming at the kernel level
Expand Down
28 changes: 28 additions & 0 deletions programming_guide/quick_reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!---//===- README.md --------------------------*- Markdown -*-===//
//
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// Copyright (C) 2022, Advanced Micro Devices, Inc.
//
//===----------------------------------------------------------------------===//-->

# <ins>Quick Reference</ins>

## Python Bindings

| Syntax | Definition | Example | Notes |
|--------|------------|---------|-------|
| \<name\> = tile(column, row) | Declare AI Engine tile | ComputeTile = tile(1,3) | The actual tile coordinates run on the device may deviate from the ones declared here. In Ryzen AI, for example, these coordinates tend to be relative corodinates as the runtime scheduler may assign it to a different available column. |

## Object FIFO Bindings


## Python helper functions
| Function | Description |
|----------|-------------|
| print(ctx.module) | Converts our ctx wrapped structural code to mlir and prints to stdout|
| print(ctx.module.operation.verify()) | Runs additional structural verficiation on the python binded source code and prints to stdout |


18 changes: 18 additions & 0 deletions programming_guide/section-1/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
##===- Makefile -----------------------------------------------------------===##
#
# This file licensed under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
##===----------------------------------------------------------------------===##

include ../../tutorials/makefile-common

build/aie.mlir: aie2.py
mkdir -p ${@D}
python3 $< > $@

all: build/aie.mlir

clean:
rm -rf build
42 changes: 28 additions & 14 deletions programming_guide/section-1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,54 @@
//
//===----------------------------------------------------------------------===//-->

# <ins>Section 1 - Basic AI Engine building blocks (tiles and buffers)</ins>
# <ins>Section 1 - Basic AI Engine building blocks</ins>

When we program for AI Engines, our MLIR-AIE framework serves as the entry point to declare and configure the structural building blocks of the entire AI Engine array. Details for these building blocks, along with the general architecture of AI Engines are breifly described in the top page of [MLIR tutorials](../tutorials) materials. Read through that synopsis first before continuing here.
When we program for AI Engines, our MLIR-AIE framework serves as the entry point to declare and configure the structural building blocks that make up an array of AI Engines. Details for these building blocks, along with the general architecture of AI Engines are described in the [MLIR tutorials](../tutorials). Read through the synopsis first before continuing here.

In this programming guide, we will be utilizing the python bindings of MLIR-AIE components to describe our system and the tile level. Later on, when we focus in on kernel programming, we will programming in C/C++. Let's first look at a basic python source file for a MLIR-AIE design.
In this programming guide, we will be utilizing the python bindings for MLIR-AIE components to describe our design at the tile level of granularity. Later on, when we focus on kernel programming, we will explore vector programming in C/C++. But let's first look at a basic python source file (named [aie2.py](./aie2.py)) for an MLIR-AIE design.

At the top of this python source, we include modules that define the mlir-aie dialect and the mlir ctx wrapper which encapsulates the definition of our AI Engine enabled device (e.g. xcvc1902) and its associated structural building blocks.

```
from aie.dialects.aie import * # primary mlir-aie dialect definitions
from aie.extras.context import mlir_mod_ctx # mlir ctx wrapper
# My program definition
```
Then we declare a structural design function that expands into mlir code when called because it contains the ctx wrapper. This wrapper, defined in the `mlir_mod_ctx()` module, contains custom python bindings definitions that leverage python to simplify some of the more detailed mlir block definitions.
```
# AI Engine structural design function
def mlir_aie_design():
# ctx wrapper - needed to convert python to mlir
# ctx wrapper - to convert python to mlir
with mlir_mod_ctx() as ctx:
```
Within our ctx wrapper, we finally get down to declaring our AI Engine device via `@device(AIEDevice.xcvc1902)` and the blocks within the device. Inside the `def device_body():` , we instantiate our AI Engine blocks, which in this first example is simply the AI Engine tiles. The arguments for the tile delcaration are the tile coordinates (column, row) and we assign it a variable tile name in our python program.

> **NOTE:** The actual tile coordinates run on the device may deviate from the ones declared here. In Ryzen AI, for example, these coordinates tend to be relative corodinates as the runtime scheduler may assign it to a different available column.
# device declaration - here using aie2 device xcvc1902
```
# Dvice declaration - here using aie2 device xcvc1902
@device(AIEDevice.xcvc1902)
def device_body():
# Tile declarations
ComputeTile = tile(1, 4)
# Buffer declarations
ComputeBuffer = buffer(ComputeTile, (8,), T.i32(), name = "a14")
ComputeTile = tile(1, 3)
ComputeTile = tile(2, 3)
ComputeTile = tile(2, 4)
```
Once we are done declaring our blocks (and connections), we print the ctx wrapped design python defined design is converted to mlir and printed to stdout. Then we finish our python code by calling the strucgtural design function.
```
# print the mlir conversion
print(ctx.module)
# Call my program
mlir_aie_design()
```

* Introduce AI Engine building blocks with references to Tutorial material
* Give example of python binded MLIR source for defining tiles and buffers
## <u>Exercises</u>
1. To run our python program, we simply call `python aie2.py` which converts our python structural design into mlir source code. This works from the command line if our design environment already contains the mlir-aie python binded dialect module. We included this in the [Makefile](./Makefile) so go ahead and run `make`. Then take a look at the generated mlir source under `build/aie.mlir`.

2. Run `make clean` to remove the generated files. Then add a type-o to the python source such as misspelling `tile` to `tilex` and then run `make` again. What messages do you see? <img src="../../tutorials/images/answer1.jpg" title="There is python error because tilex is not recognized." height=25>

3. Run `make clean` again. Now change the error by renaming `tilex` back to `tile` but change the coordinates to (-1,3) which is an inavlid location. Run `make` again. What messages do you see now? <img src="../../tutorials/images/answer1.jpg" title="No error is generated." height=25>

4. No error is generated but our code is invalid. Take a look at the generated mlir code under `build/aie.mlir`. You'll notice that the generaed mlir syntax is incorrect and running our mlir-aie tools on this mlir source will generate an error. We do, however, have some additional python structural syntax checks that can be enabled if change the `print(ctx.module)` to `print(ctx.module.operation.verify())`. Make this change and run `make` again. What message do you see now? <img src="../../tutorials/images/answer1.jpg" title="It now says column value fails to satisfy the constraint because the minimum value is 0" height=25>

31 changes: 31 additions & 0 deletions programming_guide/section-1/aie2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

#
# This file is licensed under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# (c) Copyright 2023 AMD Inc.

from aie.dialects.aie import * # primary mlir-aie dialect definitions
from aie.extras.context import mlir_mod_ctx # mlir ctx wrapper

# AI Engine structural design function
def mlir_aie_design():
# ctx wrapper - to convert python to mlir
with mlir_mod_ctx() as ctx:

# Dvice declaration - aie2 device xcvc1902
@device(AIEDevice.xcvc1902)
def device_body():

# Tile(s) declarations
ComputeTile1 = tile(1, 3)
ComputeTile2 = tile(2, 3)
ComputeTile3 = tile(2, 4)

# Print the mlir conversion
print(ctx.module)

# Call design function to generate mlir code to stdout
mlir_aie_design()

69 changes: 69 additions & 0 deletions programming_guide/section-3/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# This file is licensed under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# (c) Copyright 2023 Advanced Micro Devices, Inc.

# parameters
# -DBOOST_ROOT: Path to Boost install
# -DXRT_INC_DIR: Full path to src/runtime_src/core/include in XRT cloned repo
# -DXRT_LIB_DIR: Path to xrt_coreutil.lib
# -DTARGET_NAME: Target name to be built

# cmake needs this line
cmake_minimum_required(VERSION 3.1)

find_program(WSL NAMES powershell.exe)

if (NOT WSL)
set(BOOST_ROOT /usr/include/boost CACHE STRING "Path to Boost install")
set(XRT_INC_DIR /opt/xilinx/xrt/include CACHE STRING "Path to XRT cloned repo")
set(XRT_LIB_DIR /opt/xilinx/xrt/lib CACHE STRING "Path to xrt_coreutil.lib")
else()
set(BOOST_ROOT C:/Technical/thirdParty/boost_1_83_0 CACHE STRING "Path to Boost install")
set(XRT_INC_DIR C:/Technical/XRT/src/runtime_src/core/include CACHE STRING "Path to XRT cloned repo")
set(XRT_LIB_DIR C:/Technical/xrtIPUfromDLL CACHE STRING "Path to xrt_coreutil.lib")
endif()

set(TARGET_NAME test CACHE STRING "Target to be built")

SET (ProjectName ${TARGET_NAME})
SET (currentTarget ${TARGET_NAME})

if ( WSL )
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR})
endif ()

project(${ProjectName})

# Find packages
find_package(Boost REQUIRED)

add_executable(${currentTarget}
test.cpp
)

target_compile_definitions(${currentTarget} PUBLIC DISABLE_ABI_CHECK=1)

target_include_directories (${currentTarget} PUBLIC
${XRT_INC_DIR}
${Boost_INCLUDE_DIRS}
../../reference_designs/utils
)

target_link_directories(${currentTarget} PUBLIC
${XRT_LIB_DIR}
${Boost_LIBRARY_DIRS}
)

if (NOT WSL)
target_link_libraries(${currentTarget} PUBLIC
xrt_coreutil
boost_program_options
boost_filesystem
)
else()
target_link_libraries(${currentTarget} PUBLIC
xrt_coreutil
)
endif()
40 changes: 40 additions & 0 deletions programming_guide/section-3/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
##===- Makefile -----------------------------------------------------------===##
#
# This file licensed under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
##===----------------------------------------------------------------------===##

include ../../reference_designs/ipu-xrt/makefile-common

all: build/final.xclbin

targetname = myFirstProgram

build/aie.mlir: aie2.py
mkdir -p ${@D}
python3 $< > $@

build/final.xclbin: build/aie.mlir
mkdir -p ${@D}
cd ${@D} && aiecc.py --aie-generate-cdo --aie-generate-ipu --no-compile-host \
--xclbin-name=${@F} --ipu-insts-name=insts.txt ${<F}

${targetname}.exe: test.cpp
rm -rf _build
mkdir -p _build
# cd _build && ${powershell} cmake .. -DTARGET_NAME=${targetname}
cd _build && ${powershell} cmake -E env CXXFLAGS="-std=c++23 -ggdb" cmake .. -D CMAKE_C_COMPILER=gcc-13 -D CMAKE_CXX_COMPILER=g++-13 -DTARGET_NAME=${targetname} -Dsubdir=${subdir}
cd _build && ${powershell} cmake --build . --config Release
ifeq "${powershell}" "powershell.exe"
cp _build/${targetname}.exe $@
else
cp _build/${targetname} $@
endif

run: ${targetname}.exe build/final.xclbin build/insts.txt
${powershell} ./$< -x build/final.xclbin -i build/insts.txt -k MLIR_AIE

clean:
rm -rf build _build ${targetname}.exe
69 changes: 69 additions & 0 deletions programming_guide/section-3/aie2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@

#
# This file is licensed under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# (c) Copyright 2023 AMD Inc.

from aie.dialects.aie import * # primary mlir-aie dialect definitions
from aie.extras.context import mlir_mod_ctx # mlir ctx wrapper

from aie.dialects.aiex import * # extended mlir-aie dialect definitions
from aie.dialects.scf import * # scf (strcutred control flow) dialect
from aie.extras.dialects.ext import memref, arith # memref and arithmatic dialects

# AI Engine structural design function
def my_first_aie_program():
# ctx wrapper - to convert python to mlir
with mlir_mod_ctx() as ctx:

# Dvice declaration - aie2 device IPU (aka Ryzen AI)
@device(AIEDevice.ipu)
def device_body():
# Memref types
memRef_8_ty = T.memref(8, T.i32())
memRef_16_ty = T.memref(16, T.i32())
memRef_32_ty = T.memref(32, T.i32())
memRef_64_ty = T.memref(64, T.i32())

# Tile declarations
ComputeTile = tile(0, 2)
ShimTile = tile(0, 0)

# AIE-array data movement with object fifos
# Input
of_in0 = object_fifo("in0", ShimTile, ComputeTile, 2, memRef_8_ty)

# Output
of_out0 = object_fifo("out0", ComputeTile, ShimTile, 2, memRef_8_ty)

# Compute tile
@core(ComputeTile)
def core_body():
for _ in for_(8):
elem_in = of_in0.acquire(ObjectFifoPort.Consume, 1)
elem_out = of_out0.acquire(ObjectFifoPort.Produce, 1)
for i in for_(8):
v0 = memref.load(elem_in, [i])
v1 = arith.addi(v0, arith.constant(1, T.i32()))
memref.store(v1, elem_out, [i])
yield_([])
of_in0.release(ObjectFifoPort.Consume, 1)
of_out0.release(ObjectFifoPort.Produce, 1)
yield_([])

# To/from AIE-array data movement
@FuncOp.from_py_func(memRef_64_ty, memRef_32_ty, memRef_64_ty)
def sequence(inTensor, notUsed, outTensor):
ipu_dma_memcpy_nd(
metadata="out0", bd_id=0, mem=outTensor, sizes=[1, 1, 1, 64]
)
ipu_dma_memcpy_nd(
metadata="in0", bd_id=1, mem=inTensor, sizes=[1, 1, 1, 64]
)
ipu_sync(column=0, row=0, direction=0, channel=0)

print(ctx.module)

my_first_aie_program()
Loading

0 comments on commit 7237e62

Please sign in to comment.