Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
127174c
feat: Add conv2d production code (SPEC-012)
antmikinka May 29, 2026
2f35dac
ci: Add per-operator CI workflow (operator-ci.yml) for canonical branch
antmikinka May 29, 2026
4f6aecd
ci: fix YAML parse error in operator-ci.yml (python validation heredoc)
antmikinka May 29, 2026
34d5ee3
fix: AIE2P bf16 kernel + harness updates for real NPU runs
antmikinka May 29, 2026
7991f97
fix: L3 staging via .cons().forward() for ins/weights/bias (relieve t…
antmikinka May 29, 2026
6a0326a
fix: implement get_arg_spec + get_callable on AIEConv2d (post-ABC ref…
antmikinka May 29, 2026
9423dae
fix: defensive device query in conv2d op + mark forwards extensive (p…
antmikinka May 29, 2026
f918703
fix: use input_chunk for fifodepth large-tile heuristic (design.py hy…
antmikinka May 29, 2026
d98105e
fix: update conv2d set_up_artifacts to current PythonGeneratedMLIRArt…
antmikinka May 29, 2026
e7e0e39
fix: reduce not-extensive preferred_col to 2 (workaround residual DMA…
antmikinka May 29, 2026
0883284
fix: switch not-extensive matrix to nobias core configs (final minima…
antmikinka May 29, 2026
fc262fa
fix(conv2d): active get_shim_dma_limit + per-ingress channel budgetin…
antmikinka May 29, 2026
81e31e6
fix(conv2d): DMA-legal 1-col design with host-side bias
antmikinka Jul 30, 2026
3411a63
feat(conv2d): Phase A OC tiling for L1 fit (1-col, groups=1)
antmikinka Jul 30, 2026
9bf5c79
feat(conv2d): Phase A depthwise channel tiling for L1 fit
antmikinka Jul 30, 2026
c7d51b0
test(conv2d): Phase A not-extensive multi-tile OC and depthwise coverage
antmikinka Jul 30, 2026
a3ec50d
feat(conv2d): Phase B multi-col OC/channel split (2 DMA, host bias)
antmikinka Jul 30, 2026
2fab796
feat(conv2d): export AIEConv2d from iron.operators (Phase C)
antmikinka Jul 30, 2026
c520363
test(conv2d): Phase C not-extensive multi-col 2c CORE smoke
antmikinka Jul 30, 2026
a932515
docs(conv2d): Phase C MODELING STATUS (export + 2c CI surface)
antmikinka Jul 30, 2026
20066f1
feat(conv2d): Phase D.1 construct-time L1/column constraint hardening
antmikinka Jul 30, 2026
a991fe0
fix(conv2d): float accum in standard conv kernel for groups=2 accuracy
antmikinka Jul 30, 2026
330465e
fix(conv2d): modernize forward() for get_callable/XRTTensor path
antmikinka Jul 30, 2026
c5bc25c
fix(conv2d): skip extensive L1-OOM configs via D.1 ConstraintError
antmikinka Jul 30, 2026
d3980a2
feat(conv2d): Phase D.3 pointwise H-strip spatial L1 tiling
antmikinka Aug 2, 2026
ebe40d7
feat(conv2d): Phase D.3 k>1 host-pad halo H-strip spatial L1 tiling
antmikinka Aug 2, 2026
782c244
feat(conv2d): DMA-parity bottom/right pad for odd-OW k>1 H-strip
antmikinka Aug 2, 2026
f71ca64
fix(conv2d): zero extensive skips via groups>1 H-strip and BD-safe to…
antmikinka Aug 2, 2026
e17d2ba
fix(conv2d): BD u10 toh search for H-strip (eliminate extensive skips)
antmikinka Aug 2, 2026
2ef7752
ci(conv2d): enforce operator-ci exit codes and fix lint for CI green
antmikinka Aug 2, 2026
bd6acd2
ci: fix operator-ci.yml YAML indentation for types-runtime heredoc
antmikinka Aug 2, 2026
862215b
chore: drop fork-only operator-ci and restore upstream ci scripts
antmikinka Aug 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
328 changes: 328 additions & 0 deletions aie_kernels/aie2/conv2d.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,328 @@
// SPDX-FileCopyrightText: Copyright (C) 2026 Advanced Micro Devices, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

// 2D Convolution Kernel for AIE2 (NPU)
// Supports standard conv2d with configurable kernel_size, stride, padding

#define NOCPP

#include "../aie_kernel_utils.h"

#include <aie_api/aie.hpp>
// aie_bf16.hpp not required (bfloat16 support is in aie.hpp for this toolchain)
#include <stdint.h>
#include <stdio.h>
#include <type_traits>

extern "C" {

/**
* 2D Convolution Kernel - AIE2 optimized
* Naive implementation for small kernels (3x3, 5x5)
*
* @param input - Input tensor [in_channels * in_height * in_width]
* @param weight - Weight tensor [out_channels * in_channels * kernel_height * kernel_width]
* @param output - Output tensor [out_channels * out_height * out_width]
* @param bias - Optional bias tensor [out_channels], can be NULL
* @param in_channels - Number of input channels
* @param in_height - Input height
* @param in_width - Input width
* @param out_channels - Number of output channels
* @param out_height - Output height
* @param out_width - Output width
* @param kernel_height - Kernel height
* @param kernel_width - Kernel width
* @param stride_height - Stride in height dimension
* @param stride_width - Stride in width dimension
* @param pad_height - Padding in height dimension
* @param pad_width - Padding in width dimension
*/
void conv2d_bf16_scalar(bfloat16 *input,
bfloat16 *weight,
bfloat16 *output,
bfloat16 *bias,
int in_channels,
int in_height,
int in_width,
int out_channels,
int out_height,
int out_width,
int kernel_height,
int kernel_width,
int stride_height,
int stride_width,
int pad_height,
int pad_width,
int groups,
int apply_bias)
{
int channels_per_group = in_channels / groups;
int out_channels_per_group = out_channels / groups;

for (int oc = 0; oc < out_channels; oc++) {
int group_id = oc / out_channels_per_group;
int oc_in_group = oc % out_channels_per_group;

for (int oh = 0; oh < out_height; oh++) {
for (int ow = 0; ow < out_width; ow++) {
// Calculate input position
int ih_start = oh * stride_height - pad_height;
int iw_start = ow * stride_width - pad_width;

bfloat16 acc = bfloat16(0.0f);

// Sum over input channels in the group
for (int ic = 0; ic < channels_per_group; ic++) {
int ic_global = group_id * channels_per_group + ic;

for (int kh = 0; kh < kernel_height; kh++) {
for (int kw = 0; kw < kernel_width; kw++) {
int ih = ih_start + kh * 1; // dilation = 1 for now
int iw = iw_start + kw * 1;

// Check bounds (handle padding)
if (ih >= 0 && ih < in_height && iw >= 0 && iw < in_width) {
// NCHW flat: (ic_global * H + ih) * W + iw (N=1 layout)
int input_idx = (ic_global * in_height + ih) * in_width + iw;
int weight_idx =
((oc * channels_per_group + ic) * kernel_height + kh) * kernel_width + kw;

acc += input[input_idx] * weight[weight_idx];
}
}
}
}

// Add bias if provided
if (apply_bias) {
acc += bias[oc];
}

int output_idx = (oc * out_height + oh) * out_width + ow;
output[output_idx] = acc;
}
}
}
}

/**
* 2D Convolution Kernel - Vectorized version for AIE2
* Optimized for 3x3 kernels with vector operations
*
* @param input - Input tensor [N, in_channels, in_height, in_width] (flattened)
* @param weight - Weight tensor [out_channels, in_channels, kernel_height, kernel_width]
* @param output - Output tensor [N, out_channels, out_height, out_width] (flattened)
* @param bias - Optional bias tensor [out_channels]
* @param params - Packed parameters for convolution
*/
void conv2d_bf16_vector(bfloat16 *input,
bfloat16 *weight,
bfloat16 *output,
bfloat16 *bias,
int N, // batch size
int in_channels,
int in_height,
int in_width,
int out_channels,
int out_height,
int out_width,
int kernel_h,
int kernel_w,
int stride_h,
int stride_w,
int pad_h,
int pad_w,
int groups,
int apply_bias)
{
constexpr int vec_factor = 8; // Process 8 elements per vector operation
(void)vec_factor;

event0();

int channels_per_group = in_channels / groups;
int out_channels_per_group = out_channels / groups;

// Iterate over batch
for (int n = 0; n < N; n++) {
// Iterate over output channels
for (int oc = 0; oc < out_channels; oc++) {
int group_id = oc / out_channels_per_group;
int ic_start = group_id * channels_per_group;

// Calculate output position for this channel
bfloat16 *output_ptr = output + ((n * out_channels + oc) * out_height * out_width);

// Iterate over output spatial dimensions
for (int oh = 0; oh < out_height; oh++) {
for (int ow = 0; ow < out_width; ow++) {
// Calculate corresponding input position
int ih_start = oh * stride_h - pad_h;
int iw_start = ow * stride_w - pad_w;

// Float accum (matvec_scalar pattern): bf16*bf16 product
// promotes into float acc; cast once on store. Fixes grouped
// k3 cases where pure bf16 MAC chains diverge from torch.
float acc = 0.0f;

for (int ic = 0; ic < channels_per_group; ic++) {
int ic_global = ic_start + ic;

for (int kh = 0; kh < kernel_h; kh++) {
for (int kw = 0; kw < kernel_w; kw++) {
int ih = ih_start + kh;
int iw = iw_start + kw;

// Check bounds (handle padding)
if (ih >= 0 && ih < in_height && iw >= 0 && iw < in_width) {
int input_idx = ((n * in_channels + ic_global) * in_height + ih) * in_width + iw;
int weight_idx = ((oc * channels_per_group + ic) * kernel_h + kh) * kernel_w + kw;
// Promote product into float accumulator (no C-style cast).
acc += input[input_idx] * weight[weight_idx];
}
}
}
}

// Add bias if provided
if (apply_bias) {
acc += bias[oc];
}

// Store output
int out_idx = oh * out_width + ow;
output_ptr[out_idx] = static_cast<bfloat16>(acc);
}
}
}
}

event1();
}

/**
* Depthwise Convolution Kernel - Specialized for depthwise conv
* Each output channel depends only on one input channel
*
* @param input - Input tensor [N, channels, in_height, in_width]
* @param weight - Weight tensor [channels, kernel_h, kernel_w]
* @param output - Output tensor [N, channels, out_height, out_width]
* @param bias - Optional bias tensor [channels]
*/
void depthwise_conv2d_bf16_vector(bfloat16 *input,
bfloat16 *weight,
bfloat16 *output,
bfloat16 *bias,
int N,
int channels,
int in_height,
int in_width,
int out_height,
int out_width,
int kernel_h,
int kernel_w,
int stride_h,
int stride_w,
int pad_h,
int pad_w,
int apply_bias)
{
event0();

for (int n = 0; n < N; n++) {
for (int c = 0; c < channels; c++) {
for (int oh = 0; oh < out_height; oh++) {
for (int ow = 0; ow < out_width; ow++) {
int ih_start = oh * stride_h - pad_h;
int iw_start = ow * stride_w - pad_w;

bfloat16 acc = bfloat16(0.0f);

for (int kh = 0; kh < kernel_h; kh++) {
for (int kw = 0; kw < kernel_w; kw++) {
int ih = ih_start + kh;
int iw = iw_start + kw;

if (ih >= 0 && ih < in_height && iw >= 0 && iw < in_width) {
int input_idx = ((n * channels + c) * in_height + ih) * in_width + iw;
int weight_idx = (c * kernel_h + kh) * kernel_w + kw;

acc += input[input_idx] * weight[weight_idx];
}
}
}

if (apply_bias) {
acc += bias[c];
}

int out_idx = ((n * channels + c) * out_height + oh) * out_width + ow;
output[out_idx] = acc;
}
}
}
}

event1();
}

/**
* Pointwise (1x1) Convolution Kernel - Optimized for 1x1 kernels
* This is essentially a matrix multiplication per spatial location
*
* @param input - Input tensor [N, in_channels, H, W]
* @param weight - Weight tensor [out_channels, in_channels]
* @param output - Output tensor [N, out_channels, H, W]
* @param bias - Optional bias tensor [out_channels]
*/
void pointwise_conv2d_bf16_vector(bfloat16 *input,
bfloat16 *weight,
bfloat16 *output,
bfloat16 *bias,
int N,
int in_channels,
int out_channels,
int height,
int width,
int apply_bias)
{
constexpr int vec_factor = 8;

event0();

int spatial_size = height * width;

for (int n = 0; n < N; n++) {
for (int oc = 0; oc < out_channels; oc++) {
for (int sp = 0; sp < spatial_size; sp++) {
bfloat16 acc = bfloat16(0.0f);

// Vectorized dot product
const int V = in_channels / vec_factor;
for (int v = 0; v < V; v++) {
aie::vector<bfloat16, vec_factor> in_vec, w_vec;
for (int i = 0; i < vec_factor; i++) {
int ic = v * vec_factor + i;
in_vec[i] = input[((n * in_channels + ic) * height * width) + sp];
w_vec[i] = weight[oc * in_channels + ic];
}
acc += aie::mulacc(aie::zeros<bfloat16, vec_factor>(), in_vec, w_vec);
}

// Handle remainder
for (int ic = V * vec_factor; ic < in_channels; ic++) {
acc += input[((n * in_channels + ic) * height * width) + sp] * weight[oc * in_channels + ic];
}

if (apply_bias) {
acc += bias[oc];
}

output[((n * out_channels + oc) * height * width) + sp] = acc;
}
}
}

event1();
}
} // end extern "C" for C-linkage kernels (fix for symbol resolution in aiecc link, matching reduction.cc fix)
Loading
Loading