Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
2acc058
Fix GP-SDK SYSEMU launcher behavior
FCLC Mar 30, 2026
aec5b82
Add initial BLAS kernel scaffold
FCLC Mar 30, 2026
0000403
Restructure BLAS layout by implementation class
FCLC Mar 30, 2026
080674d
Trim BLAS documentation to policy files
FCLC Mar 30, 2026
39f8d8c
Add BLAS Level 1 reference kernels and verifier
FCLC Mar 30, 2026
922ff63
Add BLAS Level 2 reference kernels and verifiers
FCLC Mar 31, 2026
48a4d3d
Complete BLAS Level 2 reference kernels
FCLC Mar 31, 2026
784d657
Add BLAS Level 3 reference kernels and verifier
FCLC Mar 31, 2026
d83b77f
Complete verification harness before optimized kernels
FCLC Apr 1, 2026
3d69483
Fix BLAS reference verifier synchronization
FCLC Apr 2, 2026
5967e7c
Expand full verifier default sweep ranges
FCLC Apr 2, 2026
b3987c6
Add optimized BLAS proof-of-life kernels
FCLC Apr 3, 2026
fd591d2
Add ET-SIMD optimized SDOT variants
FCLC Apr 3, 2026
8cb3859
Add ET-SIMD optimized SGEMV vector path
FCLC Apr 3, 2026
7fc04cc
Stabilize reference BLAS verification harness
FCLC Apr 6, 2026
cbde67f
Split optimized SGEMM paths and verifier controls
FCLC Apr 6, 2026
e3736fa
Scale BLAS verifier timeouts by workload size
FCLC Apr 8, 2026
c435556
gp-sdk: add single-neighborhood-per-shire launch shim
FCLC Apr 13, 2026
46cc4b2
gp-sdk: add scratchpad star-cluster launch mode
FCLC Apr 13, 2026
92c8d64
gp-sdk: prove star scratchpad access on silicon
FCLC Apr 15, 2026
28f89a9
gp-sdk: add reusable star scratchpad pool
FCLC Apr 15, 2026
5654a60
gp-sdk: add surround scratchpad block mode
FCLC Apr 15, 2026
e536f9a
WIP gp-sdk: prototype nested scratchpad star topology
FCLC Apr 15, 2026
d104bea
gp-sdk: infer per-device nested scratchpad topology
FCLC Apr 15, 2026
ac19a48
gp-sdk: add erbium sim scratchpad fencing
FCLC Apr 15, 2026
84e2af0
gp-sdk: guard erbium sim tensor and global atomic ops
FCLC Apr 15, 2026
8711a25
gp-sdk: add erbium sim validation demos
FCLC Apr 16, 2026
ed93b31
gp-sdk: add nested-star silicon suite runner
FCLC Apr 16, 2026
e3fbbf0
gp-sdk: export scratchpad maps and guard shard boundaries
FCLC Apr 22, 2026
3bc460a
gp-sdk: document erbium sim porting risks
FCLC Apr 22, 2026
636c7d0
proof: probe SCP code launch rejection
FCLC Apr 22, 2026
05a3559
gp-sdk: prove fixed LPDDR kernel execution
FCLC Apr 28, 2026
19a88d9
gp-sdk: add LPDDR Erbium silicon suite
FCLC Apr 28, 2026
ad5b975
Merge branch 'aifoundry-org:master' into feature/lpddr-erbium-exec-probe
FCLC May 1, 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
22 changes: 22 additions & 0 deletions blas/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#------------------------------------------------------------------------------
# Copyright (c) 2026 Ainekko, Co.
# SPDX-License-Identifier: Apache-2.0
#------------------------------------------------------------------------------

function(add_etsoc_blas_kernel TARGET_NAME)
set(options)
set(oneValueArgs INSTALL_DESTINATION)
set(multiValueArgs)
cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

if(NOT DEFINED ARGS_INSTALL_DESTINATION)
set(ARGS_INSTALL_DESTINATION kernels/blas)
endif()

add_etsoc_riscv_executable(${TARGET_NAME} ${ARGS_UNPARSED_ARGUMENTS})
target_link_libraries(${TARGET_NAME} etsoc_crt0)
install(TARGETS ${TARGET_NAME} ${TARGET_NAME}_dbg DESTINATION ${ARGS_INSTALL_DESTINATION})
endfunction()

add_subdirectory(reference)
add_subdirectory(optimized)
34 changes: 34 additions & 0 deletions blas/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# BLAS Kernels

This directory holds custom GP-SDK device kernels for the ET-SOC1 BLAS work.

It is intended to be consumed through the existing `gp-sdk/device` custom-kernel
hook by setting:

- `CUSTOM_KERNELS_SRC_DIR=<repo>/blas`
- `CUSTOM_KERNELS_BIN_DIR=<build-dir>/blas`

Layout conventions:

- `blas/reference/` holds numerically conservative scalar baselines
- `blas/optimized/` holds vector and tensor implementations
- datatype directories sit below those roots
- BLAS level and operation directories sit below the datatype

Datatype rollout order:

- `fp32` first
- `fp16`
- `bf16`
- `int16`
- `int8`

Implementation policy:

- every optimized kernel must have a matching reference kernel
- reference and optimized kernels must keep the same external argument contract
- numerical comparisons are always made against the reference implementation

The intent is to always keep a trustworthy reference kernel beside each
optimized family so we can measure error growth as operation ordering and
reduction strategies change.
1 change: 1 addition & 0 deletions blas/optimized/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_subdirectory(fp32)
59 changes: 59 additions & 0 deletions blas/optimized/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Optimized Kernels

Optimized kernels hold vector and tensor implementations that are validated
against the matching reference kernels.

Allowed here:

- vector and tensor implementations
- tiling, blocking, fusion, and staging for performance
- controlled operation reordering when justified by performance

Required here:

- keep the same external kernel contract as the matching reference kernel
- document any intentional numerical tradeoff in code comments or commit history
- validate against the matching reference implementation

Not allowed here:

- silently changing kernel semantics
- changing datatype interpretation relative to the reference path
- landing an optimized kernel before a reference kernel exists

Current kernels:

- `fp32/level1/axpy`: scalar inner loop unrolled by 4 with the same minion
partitioning and external contract as the reference kernel
- `fp32/level1/dot`: ET packed-SIMD loads plus packed multiply/add in the main
loop, with a masked packed-SIMD tail in the default artifact and a scalar-tail
comparison artifact kept alongside it
- `fp32/level2/gemv`: ET packed-SIMD vectorization across contiguous output-row
blocks for the non-transpose, unit-stride path, with scalar fallback for
transpose and non-unit-stride cases
- `fp32/level3/gemm`:
- `blas_gemm_optimized_fp32_vector`: single-minion 4x4 blocked `NN`
micro-kernel with K strip-mining for lower-latency execution
- `blas_gemm_optimized_fp32_tensor`: tensor-engine tiled `NN` path for
higher-throughput execution, with scalar fallback for unsupported tails and
non-`NN` cases
- current tensor bring-up keeps the tensor engine on the bulk of each tile
and applies a post-store scalar correction for the dropped final inner term
observed on the current hardware path

Current validation policy:

- prefer `*_dbg` kernel artifacts when validating optimized kernels
- treat release-ELF bring-up as a separate runtime/toolchain issue
- do not treat a release-ELF failure by itself as evidence that the optimized
kernel math is wrong when the matching `_dbg` artifact passes

Current ET-SIMD bring-up notes:

- `fp32/level1/dot` now uses ET packed-SIMD loads and packed multiply/add in
the main loop
- the default optimized `dot` artifact uses a masked packed-SIMD tail to avoid
switching back into scalar FP arithmetic while packed state is still live in
the overlaid `f` register file
- `blas_dot_optimized_fp32_scalar_tail.elf{,_dbg}` is retained as a comparison
artifact for validating the scalar-tail alternative
3 changes: 3 additions & 0 deletions blas/optimized/fp32/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
add_subdirectory(level1)
add_subdirectory(level2)
add_subdirectory(level3)
2 changes: 2 additions & 0 deletions blas/optimized/fp32/level1/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
add_subdirectory(axpy)
add_subdirectory(dot)
6 changes: 6 additions & 0 deletions blas/optimized/fp32/level1/axpy/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
add_etsoc_blas_kernel(
blas_axpy_optimized_fp32.elf
axpy.cpp
INSTALL_DESTINATION kernels/blas/optimized/fp32/level1/axpy
)
target_include_directories(blas_axpy_optimized_fp32.elf PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
51 changes: 51 additions & 0 deletions blas/optimized/fp32/level1/axpy/axpy.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*-------------------------------------------------------------------------
* Copyright (c) 2026 Ainekko, Co.
* SPDX-License-Identifier: Apache-2.0
*-------------------------------------------------------------------------
*/

#include <algorithm>
#include <cstddef>

#include <etsoc/common/utils.h>

#include "axpy_kernel_arguments.h"
#include "entryPoint.h"

int entryPoint_0(KernelArguments* args);
DECLARE_KERNEL_ENTRY_POINTS(entryPoint_0, nullptr);

int entryPoint_0(KernelArguments* args) {
const auto minionId = get_relative_thread_id();
const size_t numWorkers = SOC_MINIONS_PER_SHIRE;

if (args->numElements == 0) {
return 0;
}

size_t elemsPerWorker = (args->numElements + numWorkers - 1) / numWorkers;
if (elemsPerWorker % 16) {
elemsPerWorker += 16 - (elemsPerWorker % 16);
}

const size_t begin = elemsPerWorker * minionId;
const size_t end = std::min(elemsPerWorker * (minionId + 1), static_cast<size_t>(args->numElements));
if (begin > (args->numElements - 1)) {
return 0;
}

size_t i = begin;
const size_t loopEnd = begin + ((end - begin) / 4) * 4;
for (; i < loopEnd; i += 4) {
args->y[i + 0] = args->alpha * args->x[i + 0] + args->y[i + 0];
args->y[i + 1] = args->alpha * args->x[i + 1] + args->y[i + 1];
args->y[i + 2] = args->alpha * args->x[i + 2] + args->y[i + 2];
args->y[i + 3] = args->alpha * args->x[i + 3] + args->y[i + 3];
}

for (; i < end; ++i) {
args->y[i] = args->alpha * args->x[i] + args->y[i];
}

return 0;
}
13 changes: 13 additions & 0 deletions blas/optimized/fp32/level1/axpy/axpy_kernel_arguments.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifndef ET_BLAS_OPTIMIZED_FP32_AXPY_KERNEL_ARGUMENTS_H
#define ET_BLAS_OPTIMIZED_FP32_AXPY_KERNEL_ARGUMENTS_H

#include <cstdint>

struct KernelArguments {
uint64_t numElements;
const float* x;
float* y;
float alpha;
} __attribute__((packed));

#endif
12 changes: 12 additions & 0 deletions blas/optimized/fp32/level1/dot/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
function(add_dot_variant target_name tail_define)
add_etsoc_blas_kernel(
${target_name}
dot.cpp
INSTALL_DESTINATION kernels/blas/optimized/fp32/level1/dot
)
target_include_directories(${target_name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(${target_name} PRIVATE ${tail_define}=1)
endfunction()

add_dot_variant(blas_dot_optimized_fp32.elf ET_BLAS_DOT_TAIL_MASKED)
add_dot_variant(blas_dot_optimized_fp32_scalar_tail.elf ET_BLAS_DOT_TAIL_SCALAR)
184 changes: 184 additions & 0 deletions blas/optimized/fp32/level1/dot/dot.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*-------------------------------------------------------------------------
* Copyright (c) 2026 Ainekko, Co.
* SPDX-License-Identifier: Apache-2.0
*-------------------------------------------------------------------------
*/

#include <algorithm>
#include <cstddef>
#include <cstdint>

#include <etsoc/common/utils.h>
#include <etsoc/isa/hart.h>
#include <etsoc/isa/tensors.h>

#include "CommonCode.h"
#include "dot_kernel_arguments.h"
#include "entryPoint.h"
#include "sync.h"

#if !defined(ET_BLAS_DOT_TAIL_SCALAR) && !defined(ET_BLAS_DOT_TAIL_MASKED)
#define ET_BLAS_DOT_TAIL_MASKED 1
#endif

namespace {

constexpr size_t kVectorLanes = 8;

inline __attribute__((always_inline)) void setMaskForLaneCount(size_t activeLanes) {
switch (activeLanes) {
case 0:
mask_set(0, 0x00);
break;
case 1:
mask_set(0, 0x01);
break;
case 2:
mask_set(0, 0x03);
break;
case 3:
mask_set(0, 0x07);
break;
case 4:
mask_set(0, 0x0f);
break;
case 5:
mask_set(0, 0x1f);
break;
case 6:
mask_set(0, 0x3f);
break;
case 7:
mask_set(0, 0x7f);
break;
default:
mask_set(0, 0xff);
break;
}
}

inline __attribute__((always_inline)) float packedHorizontalSum(float packedValue) {
alignas(32) float lanes[kVectorLanes];
__asm__ __volatile__("fsw.ps %[packedValue], 0(%[dst])\n"
:
: [dst] "r"(lanes), [packedValue] "f"(packedValue)
: "memory");

float result = 0.0f;
for (size_t lane = 0; lane < kVectorLanes; ++lane) {
result += lanes[lane];
}
return result;
}

inline __attribute__((always_inline)) float sdotVectorCore(
const float* x, const float* y, size_t begin, size_t end) {
float packedAcc;
uint32_t zeroWord = 0;
__asm__ __volatile__("fbcx.ps %[packedAcc], %[zeroWord]\n"
: [packedAcc] "=&f"(packedAcc)
: [zeroWord] "r"(zeroWord));

size_t i = begin;
const size_t loopEnd = begin + ((end - begin) / kVectorLanes) * kVectorLanes;
for (; i < loopEnd; i += kVectorLanes) {
float xVec;
float yVec;
float prodVec;
const float* xPtr = x + i;
const float* yPtr = y + i;

__asm__ __volatile__("flw.ps %[xVec], 0(%[xPtr])\n"
"flw.ps %[yVec], 0(%[yPtr])\n"
: [xVec] "=&f"(xVec), [yVec] "=&f"(yVec)
: [xPtr] "r"(xPtr), [yPtr] "r"(yPtr)
: "memory");

__asm__ __volatile__("fmul.ps %[prodVec], %[xVec], %[yVec]\n"
"fadd.ps %[packedAcc], %[packedAcc], %[prodVec]\n"
: [packedAcc] "+&f"(packedAcc), [prodVec] "=&f"(prodVec)
: [xVec] "f"(xVec), [yVec] "f"(yVec));
}

#if defined(ET_BLAS_DOT_TAIL_MASKED)
const size_t remaining = end - i;
if (remaining > 0) {
alignas(32) float tailX[kVectorLanes] = {};
alignas(32) float tailY[kVectorLanes] = {};
for (size_t lane = 0; lane < remaining; ++lane) {
tailX[lane] = x[i + lane];
tailY[lane] = y[i + lane];
}

setMaskForLaneCount(remaining);

float xVec;
float yVec;
float prodVec;
__asm__ __volatile__("flw.ps %[xVec], 0(%[xPtr])\n"
"flw.ps %[yVec], 0(%[yPtr])\n"
: [xVec] "=&f"(xVec), [yVec] "=&f"(yVec)
: [xPtr] "r"(tailX), [yPtr] "r"(tailY)
: "memory");

__asm__ __volatile__("fmul.ps %[prodVec], %[xVec], %[yVec]\n"
"fadd.ps %[packedAcc], %[packedAcc], %[prodVec]\n"
: [packedAcc] "+&f"(packedAcc), [prodVec] "=&f"(prodVec)
: [xVec] "f"(xVec), [yVec] "f"(yVec));

mask_set(0, 0xff);
}
#endif

float localSum = packedHorizontalSum(packedAcc);

#if defined(ET_BLAS_DOT_TAIL_SCALAR)
for (; i < end; ++i) {
localSum += x[i] * y[i];
}
#endif

return localSum;
}

} // namespace

int entryPoint_0(KernelArguments* args);
DECLARE_KERNEL_ENTRY_POINTS(entryPoint_0, nullptr);

int entryPoint_0(KernelArguments* args) {
const auto minionId = get_relative_thread_id();
const size_t numWorkers = SOC_MINIONS_PER_SHIRE;

if (args->numElements == 0) {
if (minionId == 0) {
*(args->res) = 0.0f;
}
return 0;
}

size_t elemsPerWorker = (args->numElements + numWorkers - 1) / numWorkers;
if (elemsPerWorker % 16) {
elemsPerWorker += 16 - (elemsPerWorker % 16);
}

const size_t begin = elemsPerWorker * minionId;
const size_t end = std::min(elemsPerWorker * (minionId + 1), static_cast<size_t>(args->numElements));

if (begin <= (args->numElements - 1)) {
const float localSum = sdotVectorCore(args->x, args->y, begin, end);
args->partials[begin] = localSum;
evictCacheLine(0x1ULL, reinterpret_cast<uint8_t*>(&args->partials[begin]));
}
hart::barrier();

if (minionId == 0) {
float result = 0.0f;
for (size_t i = 0; i < args->numElements; i += elemsPerWorker) {
result += args->partials[i];
}
*(args->res) = result;
}

return 0;
}
Loading