Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 23 additions & 10 deletions src/tigris_kernels_cmsis_nn.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,16 +135,22 @@ static int adapt_conv2d(
int OH = y_shape[1], OW = y_shape[2], OC = y_shape[3];
int KH = op->spatial.kernel_h, KW = op->spatial.kernel_w;
int pad_top = op->spatial.pad_top;
int pad_left = op->spatial.pad_left;

/* Plain-height tile: the loaded input tile holds tile.in_h rows and produces
* tile.out_h output rows, with tile.pad_top at the top. The bottom pad is
* implicit - CMSIS-NN clips input rows at or past IH to the input zero-point,
* so an asymmetric edge-tile pad needs no pre-pad. Width is never tiled here
* (2D width tiles route to s8_ref), so IW/OW/pad_left stay full-tensor. */
/* Height tile: tile.in_h rows produce tile.out_h output rows at tile.pad_top;
* the bottom pad is implicit (CMSIS-NN clips input rows at or past IH to the
* input zero-point), so an asymmetric edge-tile pad needs no pre-pad. A 2D
* (width_tiled) tile additionally packs tile.in_w columns into tile.out_w
* with tile.pad_left; the right pad is implicit the same way. */
if (mem->tile.active) {
IH = mem->tile.in_h;
OH = mem->tile.out_h;
pad_top = mem->tile.pad_top;
if (mem->tile.width_tiled) {
IW = mem->tile.in_w;
OW = mem->tile.out_w;
pad_left = mem->tile.pad_left;
}
}

/* Line-buffer roll: emit the tile.out_h new rows starting at output row
Expand All @@ -165,7 +171,7 @@ static int adapt_conv2d(
.input_offset = in_qp ? -in_qp->zero_point : 0,
.output_offset = out_qp ? out_qp->zero_point : 0,
.stride = { .w = op->spatial.stride_w, .h = op->spatial.stride_h },
.padding = { .w = op->spatial.pad_left, .h = pad_top },
.padding = { .w = pad_left, .h = pad_top },
.dilation = { .w = op->spatial.dilation_w ? op->spatial.dilation_w : 1,
.h = op->spatial.dilation_h ? op->spatial.dilation_h : 1 },
.activation = { .min = op->act_min, .max = op->act_max },
Expand Down Expand Up @@ -227,14 +233,21 @@ static int adapt_depthwise_conv2d(
int OH = y_shape[1], OW = y_shape[2];
int KH = op->spatial.kernel_h, KW = op->spatial.kernel_w;
int pad_top = op->spatial.pad_top;
int pad_left = op->spatial.pad_left;

/* Plain-height tile: same contract as adapt_conv2d - tile.in_h input rows
* produce tile.out_h output rows at tile.pad_top; the bottom pad is implicit
* via IH clipping. Width is never tiled here. */
/* Same contract as adapt_conv2d - tile.in_h input rows produce tile.out_h
* output rows at tile.pad_top (bottom pad implicit via IH clipping); a 2D
* (width_tiled) tile also packs tile.in_w columns into tile.out_w with
* tile.pad_left (right pad implicit via IW clipping). */
if (mem->tile.active) {
IH = mem->tile.in_h;
OH = mem->tile.out_h;
pad_top = mem->tile.pad_top;
if (mem->tile.width_tiled) {
IW = mem->tile.in_w;
OW = mem->tile.out_w;
pad_left = mem->tile.pad_left;
}
}

/* Line-buffer roll folds out_row_start into the output pointer and
Expand All @@ -257,7 +270,7 @@ static int adapt_depthwise_conv2d(
.output_offset = out_qp ? out_qp->zero_point : 0,
.ch_mult = 1,
.stride = { .w = op->spatial.stride_w, .h = op->spatial.stride_h },
.padding = { .w = op->spatial.pad_left, .h = pad_top },
.padding = { .w = pad_left, .h = pad_top },
.dilation = { .w = op->spatial.dilation_w ? op->spatial.dilation_w : 1,
.h = op->spatial.dilation_h ? op->spatial.dilation_h : 1 },
.activation = { .min = op->act_min, .max = op->act_max },
Expand Down
18 changes: 16 additions & 2 deletions src/tigris_kernels_esp_nn.c
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,10 @@ static int adapt_conv2d(
OH = mem->tile.out_h;
pt = mem->tile.pad_top;
pb = mem->tile.pad_bottom;
if (mem->tile.width_tiled) {
IW = mem->tile.in_w;
OW = mem->tile.out_w;
}
}

/* Line-buffer roll: fold out_row_start into the output pointer and
Expand All @@ -449,8 +453,14 @@ static int adapt_conv2d(

int32_t in_offset = in_qp ? -in_qp->zero_point : 0;

/* Handle asymmetric padding */
/* Handle asymmetric padding. A 2D (width_tiled) tile carries its own
* left/right pads; exec_stage_tiled_2d sets all four, so the bounce below
* pads the packed rectangle exactly as kern_conv2d_s8's IW/IH clipping. */
int pl = op->spatial.pad_left, pr = op->spatial.pad_right;
if (mem->tile.active && mem->tile.width_tiled) {
pl = mem->tile.pad_left;
pr = mem->tile.pad_right;
}
int asymmetric = (pt != pb) || (pl != pr);

const int8_t *conv_input = X;
Expand Down Expand Up @@ -543,6 +553,10 @@ static int adapt_depthwise_conv2d(
if (mem->tile.active) {
IH = mem->tile.in_h;
OH = mem->tile.out_h;
if (mem->tile.width_tiled) {
IW = mem->tile.in_w;
OW = mem->tile.out_w;
}
}

/* Line-buffer roll: fold out_row_start into the output pointer and
Expand Down Expand Up @@ -577,7 +591,7 @@ static int adapt_depthwise_conv2d(
.out_offset = out_offset,
.ch_mult = 1,
.stride = { .width = op->spatial.stride_w, .height = op->spatial.stride_h },
.padding = { .width = op->spatial.pad_left, .height = mem->tile.active ? mem->tile.pad_top : op->spatial.pad_top },
.padding = { .width = (mem->tile.active && mem->tile.width_tiled) ? mem->tile.pad_left : op->spatial.pad_left, .height = mem->tile.active ? mem->tile.pad_top : op->spatial.pad_top },
.dilation = { .width = 0, .height = 0 },
.activation = { .min = op->act_min, .max = op->act_max },
};
Expand Down
10 changes: 6 additions & 4 deletions src/tigris_kernels_s8.c
Original file line number Diff line number Diff line change
Expand Up @@ -1482,10 +1482,12 @@ int tigris_accel_try_s8_ref(
return -1;

/* 2D tiles carry mem->tile.width_tiled and partition width via
* pad_left/tile-in_w as well as height (see exec_stage_tiled_2d). No vendor
* adapter honors the packed-width contract yet, so a 2D-tiled op routes to
* s8_ref on every backend. */
if (mem->tile.active && mem->tile.width_tiled) {
* pad_left/tile-in_w as well as height (see exec_stage_tiled_2d). The
* ESP-NN/CMSIS-NN Conv/Depthwise adapters honor the packed-width tile
* natively (in_w/out_w/pad_left, right pad implicit via IW clipping); every
* other op still routes to s8_ref. */
if (mem->tile.active && mem->tile.width_tiled &&
!is_conv_or_depthwise(op)) {
*handled = 1;
return tigris_dispatch_kernel_s8(
plan, op, op_index, mem, user_ctx);
Expand Down
46 changes: 33 additions & 13 deletions test/test_accel_routing.c
Original file line number Diff line number Diff line change
Expand Up @@ -446,12 +446,13 @@ static void test_rolled_tile_routing(void)
}

/* A 2D-tiled op sets mem->tile.width_tiled on tiles that partition width as
* well as height. The ESP-NN/CMSIS-NN Conv/Depthwise adapters do not honor
* width_tiled/pad_left/tile-in_w, so a 2D-tiled op must fall back to s8_ref,
* same reasoning as the line-buffer roll guard above. */
static void test_width_tiled_routes_reference(void)
* well as height. 1.5c: the ESP-NN/CMSIS-NN Conv/Depthwise adapters now honor
* the packed-width tile (in_w/out_w/pad_left, right pad implicit), so a
* width-tiled conv/depthwise stays on the vendor path; every other op still
* routes to s8_ref. */
static void test_width_tiled_routing(void)
{
printf(" test_width_tiled_routes_reference...\n");
printf(" test_width_tiled_routing...\n");
route_fixture_t fx;
build_fixture(&fx, TIGRIS_OP_CONV, 0);
fx.op.spatial.dilation_h = 1;
Expand All @@ -460,20 +461,39 @@ static void test_width_tiled_routes_reference(void)
int handled;
int8_t out[8];

/* width_tiled tile: guard fires for both backends. */
/* width_tiled conv/depthwise now runs on the adapter on both backends. */
memset(out, 0, sizeof(out));
handled = 1;
run_pre_route_width_tiled(&fx, TIGRIS_ACCEL_ESP_NN, out, 1, &handled);
TEST_ASSERT_EQ(handled, 1, "2D tile routes ESP to s8_ref");
TEST_ASSERT_EQ(handled, 0, "2D-tiled conv stays on the ESP adapter");

memset(out, 0, sizeof(out));
handled = 1;
run_pre_route_width_tiled(&fx, TIGRIS_ACCEL_CMSIS_NN, out, 1, &handled);
TEST_ASSERT_EQ(handled, 0, "2D-tiled conv stays on the CMSIS adapter");

build_fixture(&fx, TIGRIS_OP_DEPTHWISE, 1);
fx.op.spatial.dilation_h = 1;
fx.op.spatial.dilation_w = 1;
memset(out, 0, sizeof(out));
handled = 1;
run_pre_route_width_tiled(&fx, TIGRIS_ACCEL_CMSIS_NN, out, 1, &handled);
TEST_ASSERT_EQ(handled, 1, "2D tile routes CMSIS to s8_ref");
TEST_ASSERT_EQ(handled, 0, "2D-tiled depthwise stays on the CMSIS adapter");

/* non-2D tiled conv still uses the adapter on ESP (guard not
* over-firing). */
/* A width-tiled NON-conv/depthwise op still routes to s8_ref. */
build_fixture(&fx, TIGRIS_OP_MAX_POOL, 0);
fx.op.spatial.dilation_h = 1;
fx.op.spatial.dilation_w = 1;
fx.op.spatial.kernel_h = 1;
fx.op.spatial.kernel_w = 1;
fx.op.spatial.pad_top = 0;
fx.op.spatial.pad_bottom = 0;
fx.op.spatial.pad_left = 0;
fx.op.spatial.pad_right = 0;
memset(out, 0, sizeof(out));
run_pre_route_width_tiled(&fx, TIGRIS_ACCEL_ESP_NN, out, 0, &handled);
TEST_ASSERT_EQ(handled, 0, "non-2D tiled conv stays on ESP adapter");
handled = 0;
run_pre_route_width_tiled(&fx, TIGRIS_ACCEL_ESP_NN, out, 1, &handled);
TEST_ASSERT_EQ(handled, 1, "2D-tiled max-pool still routes ESP to s8_ref");
}

/* 1.5a: a plain-height tile (tile.active, no roll offset, not width_tiled) with
Expand Down Expand Up @@ -528,7 +548,7 @@ int main(void)
test_cmsis_plain_height_tile_routes_adapter();
test_unit_dilation_keeps_esp_adapter();
test_rolled_tile_routing();
test_width_tiled_routes_reference();
test_width_tiled_routing();
test_esp_asymmetric_pad_workspace_policy();

printf("\nResults: %d passed, %d failed, %d total\n",
Expand Down
Loading