Skip to content

Commit e399423

Browse files
Additional parallel programming example updates (#2481)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 4ddf987 commit e399423

34 files changed

Lines changed: 1563 additions & 1348 deletions

aie_kernels/aie2/swiglu.cc

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
//===- swiglu.cc --------------------------------------------*- C++
2+
//-*-===//
3+
//
4+
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
// Copyright (C) 2025, Advanced Micro Devices, Inc.
9+
//
10+
//===-------------------------------------------------- --------===//
11+
12+
#include "../aie_kernel_utils.h"
13+
#include <aie_api/aie.hpp>
14+
#include <lut_based_ops.h>
15+
#include <stdint.h>
16+
17+
using namespace aie;
18+
19+
void swiglu_tanh_approx_bf16(bfloat16 *restrict input_vector,
20+
bfloat16 *restrict weight_vector_1,
21+
bfloat16 *restrict weight_vector_2,
22+
bfloat16 *restrict output_vector,
23+
const int32_t vector_size) {
24+
event0();
25+
26+
int num_elems = vector_size;
27+
auto it_in = aie::begin_restrict_vector<16>((bfloat16 *)input_vector);
28+
auto it_wt_1 = aie::begin_restrict_vector<16>((bfloat16 *)weight_vector_1);
29+
auto it_wt_2 = aie::begin_restrict_vector<16>((bfloat16 *)weight_vector_2);
30+
auto it_out = aie::begin_restrict_vector<16>((bfloat16 *)output_vector);
31+
32+
aie::vector<bfloat16, 16> input;
33+
aie::vector<bfloat16, 16> weight_1;
34+
aie::vector<bfloat16, 16> weight_2;
35+
aie::vector<bfloat16, 16> output;
36+
aie::vector<bfloat16, 16> register_0_5 = aie::broadcast<bfloat16, 16>(0.5f);
37+
aie::vector<bfloat16, 16> register_1 = aie::broadcast<bfloat16, 16>(1.0f);
38+
AIE_PREPARE_FOR_PIPELINING
39+
AIE_LOOP_MIN_ITERATION_COUNT(16)
40+
for (int i = 0; i < num_elems; i += 16) {
41+
// Load input vector
42+
input = *it_in++;
43+
// Load weights
44+
weight_1 = *it_wt_1++;
45+
weight_2 = *it_wt_2++;
46+
47+
// Compute the first part of the SWiGLU: x * w1
48+
aie::vector<bfloat16, 16> mul_input_weight_1 = aie::mul(input, weight_1);
49+
// Compute the second part of the SWiGLU: x * w2
50+
aie::vector<bfloat16, 16> mul_input_weight_2 = aie::mul(input, weight_2);
51+
52+
// Compute tanh approximation
53+
aie::vector<bfloat16, 16> half_x =
54+
aie::mul(mul_input_weight_2, register_0_5);
55+
aie::vector<bfloat16, 16> tanh_half_x = getTanhBf16(half_x);
56+
auto tanh_half_x_approx = aie::add(tanh_half_x, register_1);
57+
aie::vector<bfloat16, 16> sigmoid_approx =
58+
aie::mul(tanh_half_x_approx, register_0_5);
59+
// Compute output: x * tanh_approx
60+
aie::vector<bfloat16, 16> silu_output =
61+
aie::mul(mul_input_weight_2, sigmoid_approx);
62+
63+
// Multiply the first part with the second part
64+
auto mul_output = aie::mul(mul_input_weight_1, silu_output);
65+
66+
// Store output vector
67+
*it_out++ = mul_output.to_vector<bfloat16>();
68+
}
69+
70+
event1();
71+
72+
return;
73+
}
74+
75+
extern "C" {
76+
77+
void swiglu_bf16(bfloat16 *restrict input, bfloat16 *restrict weights_1,
78+
bfloat16 *restrict weights_2, bfloat16 *restrict output) {
79+
int32_t input_size = 1024; // Assuming input size is a multiple of 16
80+
swiglu_tanh_approx_bf16(input, weights_1, weights_2, output, input_size);
81+
}
82+
83+
} // extern "C"

aie_kernels/aie2p/swiglu.cc

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//===- swiglu.cc --------------------------------------------*- C++
2+
//-*-===//
3+
//
4+
// This file is licensed under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
// Copyright (C) 2025, Advanced Micro Devices, Inc.
9+
//
10+
//===-------------------------------------------------- --------===//
11+
12+
#include "../aie_kernel_utils.h"
13+
#include <aie_api/aie.hpp>
14+
#include <stdint.h>
15+
16+
using namespace aie;
17+
18+
void swiglu_tanh_approx_bf16(bfloat16 *restrict input_vector,
19+
bfloat16 *restrict weight_vector_1,
20+
bfloat16 *restrict weight_vector_2,
21+
bfloat16 *restrict output_vector,
22+
const int32_t vector_size) {
23+
event0();
24+
25+
int num_elems = vector_size;
26+
auto it_in = aie::begin_restrict_vector<16>((bfloat16 *)input_vector);
27+
auto it_wt_1 = aie::begin_restrict_vector<16>((bfloat16 *)weight_vector_1);
28+
auto it_wt_2 = aie::begin_restrict_vector<16>((bfloat16 *)weight_vector_2);
29+
auto it_out = aie::begin_restrict_vector<16>((bfloat16 *)output_vector);
30+
31+
aie::vector<bfloat16, 16> input;
32+
aie::vector<bfloat16, 16> weight_1;
33+
aie::vector<bfloat16, 16> weight_2;
34+
aie::vector<bfloat16, 16> output;
35+
aie::vector<bfloat16, 16> register_0_5 = aie::broadcast<bfloat16, 16>(0.5f);
36+
aie::vector<bfloat16, 16> register_1 = aie::broadcast<bfloat16, 16>(1.0f);
37+
AIE_PREPARE_FOR_PIPELINING
38+
AIE_LOOP_MIN_ITERATION_COUNT(16)
39+
for (int i = 0; i < num_elems; i += 16) {
40+
// Load input vector
41+
input = *it_in++;
42+
// Load weights
43+
weight_1 = *it_wt_1++;
44+
weight_2 = *it_wt_2++;
45+
46+
// Compute the first part of the SWiGLU: x * w1
47+
aie::vector<bfloat16, 16> mul_input_weight_1 = aie::mul(input, weight_1);
48+
// Compute the second part of the SWiGLU: x * w2
49+
aie::vector<bfloat16, 16> mul_input_weight_2 = aie::mul(input, weight_2);
50+
51+
// Compute tanh approximation
52+
auto half_x = aie::mul(mul_input_weight_2, register_0_5);
53+
auto tanh_half_x = aie::tanh<bfloat16>(half_x.to_vector<float>());
54+
auto tanh_half_x_approx = aie::add(tanh_half_x, register_1);
55+
aie::vector<bfloat16, 16> sigmoid_approx =
56+
aie::mul(tanh_half_x_approx, register_0_5);
57+
// Compute output: x * tanh_approx
58+
aie::vector<bfloat16, 16> silu_output =
59+
aie::mul(mul_input_weight_2, sigmoid_approx);
60+
61+
// Multiply the first part with the second part
62+
auto mul_output = aie::mul(mul_input_weight_1, silu_output);
63+
64+
// Store output vector
65+
*it_out++ = mul_output.to_vector<bfloat16>();
66+
}
67+
68+
event1();
69+
70+
return;
71+
}
72+
73+
extern "C" {
74+
75+
void swiglu_bf16(bfloat16 *restrict input, bfloat16 *restrict weights_1,
76+
bfloat16 *restrict weights_2, bfloat16 *restrict output) {
77+
int32_t input_size = 1024; // Assuming input size is a multiple of 16
78+
swiglu_tanh_approx_bf16(input, weights_1, weights_2, output, input_size);
79+
}
80+
81+
} // extern "C"

programming_examples/ml/eltwise_add/CMakeLists.txt

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,6 @@ else()
2727
set(XRT_LIB_DIR C:/Technical/xrtNPUfromDLL CACHE STRING "Path to xrt_coreutil.lib")
2828
endif()
2929

30-
set(IN1_SIZE 131072 CACHE STRING "in1 buffer size")
31-
set(IN2_SIZE 131072 CACHE STRING "in2 buffer size")
32-
set(OUT_SIZE 131072 CACHE STRING "out buffer size")
3330
set(TARGET_NAME test CACHE STRING "Target to be built")
3431

3532
SET (ProjectName ${TARGET_NAME})
@@ -42,18 +39,12 @@ endif ()
4239

4340
project(${ProjectName})
4441

45-
4642
add_executable(${currentTarget}
4743
${CMAKE_CURRENT_SOURCE_DIR}/../../../runtime_lib/test_lib/test_utils.cpp
4844
test.cpp
4945
)
5046

51-
target_compile_definitions(${currentTarget} PUBLIC
52-
IN1_SIZE=${IN1_SIZE}
53-
IN2_SIZE=${IN2_SIZE}
54-
OUT_SIZE=${OUT_SIZE}
55-
DISABLE_ABI_CHECK=1
56-
)
47+
target_compile_definitions(${currentTarget} PUBLIC DISABLE_ABI_CHECK=1)
5748

5849
target_include_directories (${currentTarget} PUBLIC
5950
${XRT_INC_DIR}

programming_examples/ml/eltwise_add/Makefile

Lines changed: 29 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -12,82 +12,65 @@ srcdir := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
1212

1313
include ${srcdir}/../../makefile-common
1414

15-
VPATH := ${srcdir}/../../../aie_kernels/aie2
15+
all: build/final.xclbin
1616

1717
devicename ?= $(if $(filter 1,$(NPU2)),npu2,npu)
1818
targetname = eltwise_add
19-
in1_size = 131072 # in bytes
20-
in2_size = 131072 # in bytes, should always be euqal to in1_size
21-
out_size = 131072 # in bytes, should always be equal to in1_size
2219
trace_size = 8192
23-
CHESS ?= false
2420

25-
data_size = in1_size
2621
aie_py_src=${targetname}.py
27-
use_placed?=0
28-
29-
ifeq (${use_placed}, 1)
30-
aie_py_src=${targetname}_placed.py
31-
endif
3222

33-
all: build/final_${data_size}.xclbin build/insts_${data_size}.bin
23+
VPATH := ${srcdir}/../../../aie_kernels/aie2
3424

3525
build/%.o: %.cc
3626
mkdir -p ${@D}
3727
ifeq ($(devicename),npu)
38-
cd ${@D} && ${PEANO_INSTALL_DIR}/bin/clang++ ${PEANOWRAP2_FLAGS} -c $< -o ${@F};
28+
cd ${@D} && ${PEANO_INSTALL_DIR}/bin/clang++ ${PEANOWRAP2_FLAGS} -c $< -o ${@F}
3929
else ifeq ($(devicename),npu2)
40-
cd ${@D} && ${PEANO_INSTALL_DIR}/bin/clang++ ${PEANOWRAP2P_FLAGS} -c $< -o ${@F};
30+
cd ${@D} && ${PEANO_INSTALL_DIR}/bin/clang++ ${PEANOWRAP2P_FLAGS} -c $< -o ${@F}
4131
else
4232
echo "Device type not supported"
4333
endif
4434

45-
build/aie_${data_size}.mlir: ${srcdir}/${aie_py_src}
35+
build/aie.mlir: ${srcdir}/${aie_py_src}
4636
mkdir -p ${@D}
47-
python3 $< -d ${devicename} -i1s ${in1_size} -i2s ${in2_size} -os ${out_size} > $@
37+
python3 $< ${devicename} 0 > $@
4838

49-
build/aie_trace_${data_size}.mlir: ${srcdir}/${aie_py_src}
39+
build/aie_trace.mlir: ${srcdir}/${aie_py_src}
5040
mkdir -p ${@D}
51-
python3 $< -d ${devicename} -i1s ${in1_size} -i2s ${in2_size} -os ${out_size} -t ${trace_size} > $@
41+
python3 $< ${devicename} ${trace_size} > $@
5242

53-
build/final_${data_size}.xclbin: build/aie_${data_size}.mlir build/add.o
43+
build/final.xclbin: build/aie.mlir build/add.o
5444
mkdir -p ${@D}
55-
cd ${@D} && aiecc.py --aie-generate-xclbin --no-compile-host --xclbin-name=${@F} \
56-
--no-xchesscc --no-xbridge \
57-
--aie-generate-npu-insts --npu-insts-name=insts_${data_size}.bin $(<:%=../%)
45+
cd ${@D} && aiecc.py --aie-generate-xclbin --aie-generate-npu-insts --no-compile-host \
46+
--no-xchesscc --no-xbridge \
47+
--xclbin-name=${@F} --npu-insts-name=insts.bin ${<F}
5848

59-
build/final_trace_${data_size}.xclbin: build/aie_trace_${data_size}.mlir build/add.o
49+
build/final_trace.xclbin: build/aie_trace.mlir build/add.o
6050
mkdir -p ${@D}
61-
cd ${@D} && aiecc.py --aie-generate-xclbin --no-compile-host --xclbin-name=${@F} \
62-
--no-xchesscc --no-xbridge \
63-
--aie-generate-npu-insts --npu-insts-name=insts_${data_size}.bin $(<:%=../%)
51+
cd ${@D} && aiecc.py --aie-generate-xclbin --aie-generate-npu-insts --no-compile-host \
52+
--no-xchesscc --no-xbridge \
53+
--xclbin-name=${@F} --npu-insts-name=insts.bin ${<F}
6454

65-
${targetname}_${data_size}.exe: ${srcdir}/test.cpp
55+
56+
${targetname}.exe: ${srcdir}/test.cpp
6657
rm -rf _build
6758
mkdir -p _build
68-
cd _build && ${powershell} cmake `${getwslpath} ${srcdir}` -DTARGET_NAME=${targetname}_${data_size} -DIN1_SIZE=${in1_size} -DIN2_SIZE=${in2_size} -DOUT_SIZE=${out_size}
59+
cd _build && ${powershell} cmake `${getwslpath} ${srcdir}` -DTARGET_NAME=${targetname}
6960
cd _build && ${powershell} cmake --build . --config Release
7061
ifeq "${powershell}" "powershell.exe"
71-
cp _build/${targetname}_${data_size}.exe $@
62+
cp _build/${targetname}.exe $@
7263
else
73-
cp _build/${targetname}_${data_size} $@
64+
cp _build/${targetname} $@
7465
endif
7566

76-
run: ${targetname}_${data_size}.exe build/final_${data_size}.xclbin build/insts_${data_size}.bin
77-
${powershell} ./$< -x build/final_${data_size}.xclbin -i build/insts_${data_size}.bin -k MLIR_AIE
78-
79-
trace: ${targetname}_${data_size}.exe build/final_trace_${data_size}.xclbin build/insts_${data_size}.bin
80-
${powershell} ./$< -x build/final_trace_${data_size}.xclbin -i build/insts_${data_size}.bin -k MLIR_AIE -t ${trace_size}
81-
${srcdir}/../../utils/parse_trace.py --input trace.txt --mlir build/aie_trace_${data_size}.mlir --output trace_${targetname}.json
82-
${srcdir}/../../utils/get_trace_summary.py --input trace_${targetname}.json
83-
84-
trace_py: build/final_trace_${data_size}.xclbin build/insts_${data_size}.bin
85-
${powershell} python3 ${srcdir}/test.py -x build/final_trace_${data_size}.xclbin -i build/insts_${data_size}.bin -k MLIR_AIE -t ${trace_size} -i1s ${in1_size} -i2s ${in2_size} -os ${out_size}
86-
${srcdir}/../../utils/parse_trace.py --input trace.txt --mlir build/aie_trace_${data_size}.mlir --output trace_${targetname}.json
87-
${srcdir}/../../utils/get_trace_summary.py --input trace_${targetname}.json
67+
run: ${targetname}.exe build/final.xclbin
68+
${powershell} ./$< -x build/final.xclbin -i build/insts.bin -k MLIR_AIE
8869

89-
clean_trace:
90-
rm -rf tmpTrace trace.txt parse*json trace*json
70+
trace: ${targetname}.exe build/final_trace.xclbin
71+
${powershell} ./$< -x build/final_trace.xclbin -i build/insts.bin -k MLIR_AIE -t ${trace_size}
72+
${srcdir}/../../utils/parse_trace.py --input trace.txt --mlir build/aie_trace.mlir --output trace_eltwise_add.json
73+
${srcdir}/../../utils/get_trace_summary.py --input trace_eltwise_add.json
9174

92-
clean: clean_trace
93-
rm -rf build _build ${targetname}*.exe
75+
clean:
76+
rm -rf build _build ${targetname}.exe

programming_examples/ml/eltwise_add/README.md

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,16 @@
88
//
99
//===----------------------------------------------------------------------===//-->
1010

11-
# Eltwise Add
11+
# Eltwise Addition
1212

13-
This design implements a `bfloat16` based element wise addition between two vectors, performed in parallel on two cores in a single column. Element-wise addition usually ends up being I/O bound due to the low compute intensity. In a practical ML implementation, it is an example of the type of kernel that is likely best fused onto another more compute-dense kernel (e.g., a convolution or GEMM). Please refer to [bottleneck](../bottleneck/) design on fusing element-wise addition with convolution for the skip addition.
13+
This design implements a `bfloat16` based element-wise addtiplication between two vectors, performed in parallel on two cores in a single column. Element-wise addtiplication usually ends up being I/O bound due to the low compute intensity. In a practical ML implementation, it is an example of the type of kernel that is likely best fused onto another more compute-dense kernel (e.g., a convolution or GEMM).
1414

1515

1616
## Source Files Overview
1717

1818
1. `eltwise_add.py`: A Python script that defines the AIE array structural design using MLIR-AIE operations. This generates MLIR that is then compiled using `aiecc.py` to produce design binaries (ie. XCLBIN and inst.bin for the NPU in Ryzen™ AI).
1919

20-
1. `eltwise_add_placed.py`: An alternative version of the design in `eltwise_add.py`, that is expressed in a lower-level version of IRON.
21-
22-
1. `add.cc`: A C++ implementation of a vectorized vector addition operation for AIE cores. The code uses the AIE API, which is a C++ header-only library providing types and operations that get translated into efficient low-level intrinsics, and whose documentation can be found [here](https://www.xilinx.com/htmldocs/xilinx2023_2/aiengine_api/aie_api/doc/index.html). The source can be found [here](../../../aie_kernels/aie2/add.cc).
20+
1. `add.cc`: A C++ implementation of a vectorized vector addtiplication operation for AIE cores. The code uses the AIE API, which is a C++ header-only library providing types and operations that get translated into efficient low-level intrinsics, and whose documentation can be found [here](https://www.xilinx.com/htmldocs/xilinx2023_2/aiengine_api/aie_api/doc/index.html). The source can be found [here](../../../aie_kernels/aie2/add.cc).
2321

2422
1. `test.cpp`: This C++ code is a testbench for the design example. The code is responsible for loading the compiled XCLBIN file, configuring the AIE module, providing input data, and executing the AIE design on the NPU. After executing, the script verifies the memcpy results and optionally outputs trace data.
2523

@@ -33,18 +31,7 @@ To compile the design and C++ testbench:
3331
make
3432
```
3533

36-
To compile for the placed design:
37-
38-
```shell
39-
env use_placed=1 make
40-
```
41-
4234
To run the design:
4335
```shell
4436
make run
4537
```
46-
47-
To generate a [trace file](../../../programming_guide/section-4/section-4b/README.md):
48-
```shell
49-
env use_placed=1 make trace
50-
```

0 commit comments

Comments
 (0)