From 47f2ef0b03e6d89c2e771ce38827030bae361e0e Mon Sep 17 00:00:00 2001 From: Taimuraz Kaitmazov Date: Sun, 19 Jul 2026 19:42:18 +0300 Subject: [PATCH 1/2] aie.hpp: include the ADF headers only when adf.h is available aie_adf.hpp brings in the ADF stream API, which includes . That header is part of the Vitis ADF framework and is not present in bare-metal, IRON, or mlir-aie builds, so including it unconditionally under __AIENGINE__ makes the aie.hpp umbrella fail to compile in those flows. Guard the include with __has_include() so it is pulled in when it is available (the Vitis flow, unchanged) and skipped gracefully otherwise, which lets aie_api be used outside the ADF flow. When __has_include is not available, fall back to the previous behavior. --- include/aie_api/aie.hpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/include/aie_api/aie.hpp b/include/aie_api/aie.hpp index 9b631d9..9198700 100644 --- a/include/aie_api/aie.hpp +++ b/include/aie_api/aie.hpp @@ -8247,7 +8247,12 @@ BINARY_OP_IMPL(Neg) } -#ifdef __AIENGINE__ +// aie_adf.hpp brings in the ADF stream API, which includes . That header +// is part of the Vitis ADF framework and is not present in bare-metal, IRON, or +// mlir-aie builds. Only pull it in when it is actually available, so aie_api can +// be used outside the ADF flow; when __has_include is unavailable, fall back to +// the previous behavior of including it under __AIENGINE__. +#if defined(__AIENGINE__) && (!defined(__has_include) || __has_include()) #include "aie_adf.hpp" #endif #include "operators.hpp" From c1161e801cb699e8c9da071d83a434d37c3df286 Mon Sep 17 00:00:00 2001 From: Taimuraz Kaitmazov Date: Sun, 19 Jul 2026 19:42:31 +0300 Subject: [PATCH 2/2] Add an adf-free accessor for the core-to-core cascade aie_api exposes the AIE core-to-core cascade only through the ADF stream API in aie_api/adf/, which includes . Outside the ADF flow that header is absent, so a kernel cannot issue a cascade put or get through aie_api, even though the hardware datapath is reachable through the stream intrinsics. Add aie_api/cascade.hpp with typed free-function accessors (cascade_out, cascade_in_i32, cascade_in_acc32) that bridge aie_api's vector and accum types onto the compiler's put_mcd/get_scd stream intrinsics, which enable the cascade with en = 1 by default. The acc32 path is the one a fused cross-core K-reduction uses; the accum to native conversion stays inside the public accum API through accum::to_native() and the implicit accum(storage_t) constructor. Availability follows aie_api's capability model. __AIE_API_HAS_CASCADE__ is defined in each detail//config.hpp and is 1 exactly where an implementation is present: today aie2p under Peano. It is 0 on the other architectures and on chess for aie2p, so the header is a no-op wherever the intrinsics are not available and it does not affect other compilers or architectures. examples/cascade.cpp, guarded on __AIE_API_HAS_CASCADE__, shows the int32 and acc32 round-trips. --- examples/Makefile.peano | 2 +- examples/cascade.cpp | 33 +++++++++ include/aie_api/aie.hpp | 1 + include/aie_api/cascade.hpp | 86 ++++++++++++++++++++++++ include/aie_api/detail/aie1/config.hpp | 4 ++ include/aie_api/detail/aie2/config.hpp | 4 ++ include/aie_api/detail/aie2p/config.hpp | 11 +++ include/aie_api/detail/aie2ps/config.hpp | 4 ++ 8 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 examples/cascade.cpp create mode 100644 include/aie_api/cascade.hpp diff --git a/examples/Makefile.peano b/examples/Makefile.peano index 010365f..ccf97e4 100644 --- a/examples/Makefile.peano +++ b/examples/Makefile.peano @@ -6,7 +6,7 @@ CXX = clang++ --target=aie2-none-unknown-elf CXXFLAGS = -std=c++2b -Wno-unknown-attributes CPPFLAGS = -I../include -I$(XILINX_VITIS_AIETOOLS)/include -SOURCES := add.cpp aligned_memcpy.cpp gemm_bf16xbf16.cpp gemm_int8xint8_sparse.cpp \ +SOURCES := add.cpp aligned_memcpy.cpp cascade.cpp gemm_bf16xbf16.cpp gemm_int8xint8_sparse.cpp \ lazy.cpp lookup_table.cpp mmul.cpp operators.cpp TARGETS := $(SOURCES:.cpp=.o) diff --git a/examples/cascade.cpp b/examples/cascade.cpp new file mode 100644 index 0000000..6fc24da --- /dev/null +++ b/examples/cascade.cpp @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +// Copyright (C) 2022 Xilinx, Inc. +// Copyright (C) 2022-2026 Advanced Micro Devices, Inc. + +#include + +// The adf-free cascade accessors are available where __AIE_API_HAS_CASCADE__ is +// set (today: aie2p under Peano; see aie_api/cascade.hpp). Guarding the example +// on the capability macro keeps a default build green on architectures and +// compilers that do not implement it yet, and exercises the accessors where it +// is available, e.g. clang++ --target=aie2p-none-unknown-elf -I../include -c cascade.cpp. +#if __AIE_API_HAS_CASCADE__ + +//![Adf-free cascade round-trip] +// Put a 16-lane int32 vector to the master cascade, then read one back from the +// slave cascade. On real hardware the two ends are on adjacent cores; a fused +// cross-core reduction issues the put on one core and the get on the next. +extern "C" aie::vector cascade_passthrough_i32(aie::vector x) +{ + aie::cascade_out(x); + return aie::cascade_in_i32(); +} + +// The accumulator path, which a fused cross-core K-reduction uses (partial sums +// travel as accumulators). +extern "C" aie::accum cascade_passthrough_acc32(aie::accum a) +{ + aie::cascade_out(a); + return aie::cascade_in_acc32(); +} +//![Adf-free cascade round-trip] + +#endif // __AIE_API_HAS_CASCADE__ diff --git a/include/aie_api/aie.hpp b/include/aie_api/aie.hpp index 9198700..2064cc9 100644 --- a/include/aie_api/aie.hpp +++ b/include/aie_api/aie.hpp @@ -72,6 +72,7 @@ #if AIE_API_ML_VERSION >= 210 #include "block_vector.hpp" #endif +#include "cascade.hpp" #include "concepts.hpp" #include "expr.hpp" #include "fft.hpp" diff --git a/include/aie_api/cascade.hpp b/include/aie_api/cascade.hpp new file mode 100644 index 0000000..434aaf6 --- /dev/null +++ b/include/aie_api/cascade.hpp @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: MIT +// Copyright (C) 2022 Xilinx, Inc. +// Copyright (C) 2022-2026 Advanced Micro Devices, Inc. + +#pragma once + +#ifndef __AIE_API_CASCADE__HPP__ +#define __AIE_API_CASCADE__HPP__ + +#include "accum.hpp" +#include "vector.hpp" + +// Adf-free accessor for the AIE core-to-core cascade. +// +// aie_api otherwise exposes the cascade only through the ADF stream API in +// aie_api/adf/, which includes . That header is part of the Vitis ADF +// framework and is absent in bare-metal, IRON, and mlir-aie builds, so a kernel +// compiled outside the ADF flow cannot issue a cascade put or get through +// aie_api, even though the hardware datapath is reachable through the compiler +// intrinsics directly. +// +// The accessors bridge aie_api's vector and accum types onto the compiler's +// cascade stream intrinsics put_mcd/get_scd (aie2p intrinsic layer), which +// enable the cascade with en = 1 by default. aie_api otherwise reaches those +// intrinsics only through the ADF stream types. +// +// Availability follows aie_api's usual capability model. The cascade datapath +// exists across the AIE generations, but the intrinsics are compiler and +// architecture specific (chess vs Peano; aie2/aie2p/aie2ps). __AIE_API_HAS_CASCADE__ +// (detail//config.hpp) is 1 exactly where an implementation is present: +// today aie2p under Peano. put_mcd/get_scd are the standard stream intrinsic +// names, so extending to another compiler or architecture is expected to be a +// matter of confirming the intrinsics there and setting __AIE_API_HAS_CASCADE__; +// callers that guard on the macro then pick it up with no source change. + +namespace aie { + +#if __AIE_API_HAS_CASCADE__ + +/** + * \brief Write a 16-lane int32 vector to the master cascade datapath. + * + * Adf-free: reaches the cascade without the ADF stream API. + */ +inline void cascade_out(const vector &v) +{ + put_mcd(v.to_native()); +} + +/** + * \brief Read a 16-lane int32 vector from the slave cascade datapath. + */ +inline vector cascade_in_i32() +{ + return get_scd_v16int32(); +} + +/** + * \brief Write a 16-lane acc32 accumulator to the master cascade datapath. + * + * The accumulator path is the one a fused cross-core K-reduction uses, since + * partial sums travel as accumulators. The accum to native conversion stays + * inside the public accum API through accum::to_native(), so no reinterpret_cast + * is needed and the storage representation is not assumed. + */ +inline void cascade_out(const accum &a) +{ + put_mcd(a.to_native()); +} + +/** + * \brief Read a 16-lane acc32 accumulator from the slave cascade datapath. + * + * The native accumulator the intrinsic returns is rebuilt into an accum through + * the implicit accum(storage_t) constructor. + */ +inline accum cascade_in_acc32() +{ + return accum(get_scd_v16acc32()); +} + +#endif // __AIE_API_HAS_CASCADE__ + +} // namespace aie + +#endif // __AIE_API_CASCADE__HPP__ diff --git a/include/aie_api/detail/aie1/config.hpp b/include/aie_api/detail/aie1/config.hpp index 513d532..17d18c9 100644 --- a/include/aie_api/detail/aie1/config.hpp +++ b/include/aie_api/detail/aie1/config.hpp @@ -11,6 +11,10 @@ #define AIE_API_ML_VERSION 100 #define AIE_API_MATH_VERSION 100 +// The adf-free cascade accessor (aie_api/cascade.hpp) is currently implemented +// only for aie2p under Peano; it is not available on this architecture. +#define __AIE_API_HAS_CASCADE__ 0 + // >= 900000 is used to detect the master development branch #define __AIE_API_REGISTER_ATTR_DEFINED__ ((__AIE_MODEL_VERSION__ >= 900000) || \ diff --git a/include/aie_api/detail/aie2/config.hpp b/include/aie_api/detail/aie2/config.hpp index 4ad6399..922640c 100644 --- a/include/aie_api/detail/aie2/config.hpp +++ b/include/aie_api/detail/aie2/config.hpp @@ -19,6 +19,10 @@ #define __AIE_API_FP32_EMULATION__ (__AIE_MODEL_VERSION__ >= 3000) +// The adf-free cascade accessor (aie_api/cascade.hpp) is currently implemented +// only for aie2p under Peano; it is not available on this architecture. +#define __AIE_API_HAS_CASCADE__ 0 + #define __AIE_API_FP32_SUPPORT__ 0 #define __AIE_API_32ELEM_FLOAT_SRS_UPS__ (__AIE_MODEL_VERSION__ >= 10300) diff --git a/include/aie_api/detail/aie2p/config.hpp b/include/aie_api/detail/aie2p/config.hpp index 11e1ba0..501e5a7 100644 --- a/include/aie_api/detail/aie2p/config.hpp +++ b/include/aie_api/detail/aie2p/config.hpp @@ -33,6 +33,17 @@ #define __AIE_API_HAS_COMPLEX_BFLOAT16_FIFO__ (__AIE_MODEL_VERSION__ >= 10500) +// Adf-free core-to-core cascade accessor (aie_api/cascade.hpp). The cascade +// datapath exists on the silicon, but the accessor is currently implemented +// only through the Peano __builtin_aie2p_* intrinsics, so it is available on +// aie2p under Peano. On other compilers (chess) the capability is 0 until the +// corresponding intrinsic path is added, so the accessor is simply not defined. +#ifdef __PEANO__ +#define __AIE_API_HAS_CASCADE__ 1 +#else +#define __AIE_API_HAS_CASCADE__ 0 +#endif + #define __AIE_API_CONSTEXPR_BFLOAT16__ (__AIE_MODEL_VERSION__ >= 10900) #define __AIE_API_COMPLEX_FP32_EMULATION__ 0 diff --git a/include/aie_api/detail/aie2ps/config.hpp b/include/aie_api/detail/aie2ps/config.hpp index fee9156..0ed2278 100644 --- a/include/aie_api/detail/aie2ps/config.hpp +++ b/include/aie_api/detail/aie2ps/config.hpp @@ -21,6 +21,10 @@ #define __AIE_API_FP32_EMULATION__ (__AIE_MODEL_VERSION__ >= 3000) +// The adf-free cascade accessor (aie_api/cascade.hpp) is currently implemented +// only for aie2p under Peano; it is not available on this architecture. +#define __AIE_API_HAS_CASCADE__ 0 + #define __AIE_API_FP32_SUPPORT__ 0 #define __AIE_API_HAS_EXTRACT_V64BFP16__ 0