RKNPU2 backend is implemented

This commit is contained in:
Invisi
2025-10-17 15:10:09 +07:00
parent 650bf14eb9
commit 1929598052
12 changed files with 2540 additions and 0 deletions
+28
View File
@@ -115,6 +115,7 @@ option(LLAMA_TESTS_INSTALL "llama: install tests" ON)
# 3rd party libs
option(LLAMA_OPENSSL "llama: use openssl to support HTTPS" ON)
option(LLAMA_LLGUIDANCE "llama-common: include LLGuidance library for structured output in common utils" OFF)
option(LLAMA_RKNPU2 "llama: use Rockchip NPU" OFF)
# Required for relocatable CMake package
@@ -169,6 +170,33 @@ llama_option_depr(WARNING LLAMA_CURL)
include("cmake/license.cmake")
license_add_file("llama.cpp" "LICENSE")
if (LLAMA_RKNPU2)
set(GGML_RKNPU2 ON)
endif()
if (NOT MSVC)
if (LLAMA_SANITIZE_THREAD)
message(STATUS "Using -fsanitize=thread")
add_compile_options(-fsanitize=thread)
link_libraries (-fsanitize=thread)
endif()
if (LLAMA_SANITIZE_ADDRESS)
message(STATUS "Using -fsanitize=address")
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
link_libraries (-fsanitize=address)
endif()
if (LLAMA_SANITIZE_UNDEFINED)
message(STATUS "Using -fsanitize=undefined")
add_compile_options(-fsanitize=undefined)
link_libraries (-fsanitize=undefined)
endif()
endif()
#
# 3rd-party
#
+1
View File
@@ -259,6 +259,7 @@ set (GGML_OPENCL_TARGET_VERSION "300" CACHE STRING
"ggml: OpenCL API version to target")
option(GGML_HEXAGON "ggml: enable Hexagon backend" OFF)
option(GGML_RKNPU2 "ggml: use RKNPU2" OFF)
set(GGML_HEXAGON_FP32_QUANTIZE_GROUP_SIZE 128 CACHE STRING "ggml: quantize group size (32, 64, or 128)")
# toolchain for vulkan-shaders-gen
+1
View File
@@ -461,6 +461,7 @@ ggml_add_backend(OpenCL)
ggml_add_backend(Hexagon)
ggml_add_backend(ZenDNN)
ggml_add_backend(OPENVINO)
ggml_add_backend(RKNPU2)
foreach (target ggml-base ggml)
target_include_directories(${target} PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include> $<INSTALL_INTERFACE:include>)
+16
View File
@@ -86,6 +86,19 @@
#include "ggml-openvino.h"
#endif
#ifdef GGML_USE_RKNPU2
#include "ggml-rknpu2.h"
#endif
// disable C++17 deprecation warning for std::codecvt_utf8
#if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
namespace fs = std::filesystem;
static std::string path_str(const fs::path & path) {
@@ -163,6 +176,9 @@ struct ggml_backend_registry {
#endif
#ifdef GGML_USE_CPU
register_backend(ggml_backend_cpu_reg());
#endif
#ifdef GGML_USE_RKNPU2
register_backend(ggml_backend_rknpu2_reg());
#endif
}
+27
View File
@@ -0,0 +1,27 @@
# Finding OpenMP
find_package(OpenMP REQUIRED)
# Adding backend files
ggml_add_backend_library(ggml-rknpu2
ggml-rknpu2.cpp
dma-alloc.cpp
)
# Linking rknpu2 header files
target_include_directories(ggml-rknpu2
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/libs/include
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
# Linking rknpu2 library
target_link_libraries(ggml-rknpu2 PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/libs/librknnrt.so
)
# Adding OpenMP in build
if(OpenMP_FOUND)
add_definitions(-DHAVE_OPENMP=1)
list(APPEND CMAKE_CXX_FLAGS "${OpenMP_CXX_FLAGS}")
endif()
+54
View File
@@ -0,0 +1,54 @@
#include "dma-alloc.h"
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <cerrno>
#include <cstdio>
DmaBuffer dma_alloc(size_t size) {
DmaBuffer buffer;
buffer.size = size;
const char* path = "/dev/dma_heap/system";
int dma_heap_fd = open(path, O_RDWR);
if (dma_heap_fd < 0) {
fprintf(stderr, "DMA_ALLOC: Failed to open %s: %s\n", path, strerror(errno));
return buffer;
}
dma_heap_allocation_data buf_data;
memset(&buf_data, 0, sizeof(buf_data));
buf_data.len = size;
buf_data.fd_flags = O_CLOEXEC | O_RDWR;
if (ioctl(dma_heap_fd, DMA_HEAP_IOCTL_ALLOC, &buf_data) < 0) {
fprintf(stderr, "DMA_ALLOC: ioctl DMA_HEAP_IOCTL_ALLOC failed: %s\n", strerror(errno));
close(dma_heap_fd);
return buffer;
}
close(dma_heap_fd);
buffer.fd = buf_data.fd;
buffer.virt_addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, buffer.fd, 0);
if (buffer.virt_addr == MAP_FAILED) {
fprintf(stderr, "DMA_ALLOC: mmap failed: %s\n", strerror(errno));
close(buffer.fd);
buffer.fd = -1;
buffer.virt_addr = nullptr;
}
return buffer;
}
void dma_free(const DmaBuffer& buffer) {
if (buffer.virt_addr) {
munmap(buffer.virt_addr, buffer.size);
}
if (buffer.fd >= 0) {
close(buffer.fd);
}
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include <cstddef>
#include <cstdint>
// --- Structs ---
struct dma_heap_allocation_data {
uint64_t len;
uint32_t fd;
uint32_t fd_flags;
uint64_t heap_flags;
};
// --- IOCTL-commands ---
#define DMA_HEAP_IOC_MAGIC 'H'
#define DMA_HEAP_IOCTL_ALLOC _IOWR(DMA_HEAP_IOC_MAGIC, 0x0, struct dma_heap_allocation_data)
// Information of allocated block
struct DmaBuffer {
int fd = -1;
void* virt_addr = nullptr;
size_t size = 0;
};
// --- Functions ---
DmaBuffer dma_alloc(size_t size);
void dma_free(const DmaBuffer& buffer);
+878
View File
@@ -0,0 +1,878 @@
#include "ggml-rknpu2.h"
#include "ggml-backend-impl.h"
#include "ggml-impl.h"
#include "ggml-quants.h"
#include "dma-alloc.h"
#include <rknn_api.h>
#include <rknn_matmul_api.h>
#include <arm_neon.h>
#include <omp.h>
#include <chrono>
#include <cassert>
#include <cstring>
#include <mutex>
#include <string>
#include <vector>
#include <map>
#include <tuple>
#include <algorithm>
#include <memory>
#include <unordered_map>
#define UNUSED(x) (void)(x)
// Performance logging flag
#define RKNPU_PERF_LOG 0
// Backend logging flag
#define RKNPU_DEBUG 0
// RAII-struct for performance logging
#if RKNPU_PERF_LOG
struct RknpuPerfLogger {
std::string name;
std::chrono::high_resolution_clock::time_point start;
RknpuPerfLogger(std::string name) : name(std::move(name)), start(std::chrono::high_resolution_clock::now()) {}
~RknpuPerfLogger() {
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
fprintf(stderr, "[PERF] %-40s: %8lld us\n", name.c_str(), (long long)duration);
}
};
#endif
#if RKNPU_DEBUG
#define RKNPU_LOG_INFO(...) fprintf(stderr, __VA_ARGS__)
#else
#define RKNPU_LOG_INFO(...)
#endif
#define RKNPU_LOG_ERROR(...) fprintf(stderr, __VA_ARGS__)
// Macro for RKNN API calls
#define RKNN_CHECK(stmt, msg) \
do { \
int ret = (stmt); \
if (ret < 0) { \
RKNPU_LOG_ERROR("RKNN error %d at %s:%d: %s\n", ret, \
__FILE__, __LINE__, msg); \
assert(false); \
} \
} while (0)
// --- Helper functions ---
// Packing KxN FP16 (row-major: idx [k,n] -> k*N + n) into native RKNN: (N/16, K/32, 16, 32)
static void pack_B_segment_fp16_native_from_KN(
uint16_t * dst, const uint16_t * src,
int K, int N_total, int n_offset, int n_segment) {
GGML_ASSERT(K % 32 == 0 && N_total > 0 && K > 0);
GGML_ASSERT(n_offset % 16 == 0 && n_segment % 16 == 0 && n_offset + n_segment <= N_total);
const size_t s0 = (size_t)(K / 32) * 16 * 32;
const size_t s1 = 16 * 32;
const size_t s2 = 32;
for (int i = 0; i < n_segment / 16; ++i) {
for (int j = 0; j < K / 32; ++j) {
const size_t dst_block = (size_t) i * s0 + (size_t) j * s1;
for (int ii = 0; ii < 16; ++ii) {
const size_t n_global = (size_t)n_offset + (size_t)i * 16 + (size_t)ii;
const uint16_t * src_ptr = src + n_global * K + j * 32;
uint16_t * dst_ptr = dst + dst_block + ii * s2;
uint16x8_t d0 = vld1q_u16(src_ptr + 0);
uint16x8_t d1 = vld1q_u16(src_ptr + 8);
uint16x8_t d2 = vld1q_u16(src_ptr + 16);
uint16x8_t d3 = vld1q_u16(src_ptr + 24);
vst1q_u16(dst_ptr + 0, d0);
vst1q_u16(dst_ptr + 8, d1);
vst1q_u16(dst_ptr + 16, d2);
vst1q_u16(dst_ptr + 24, d3);
}
}
}
}
// --- Hashers ---
// Function for hash combinations
template <class T>
inline void hash_combine(std::size_t& seed, const T& v) {
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
// Hasher for std::pair
struct PairHasher {
template <class T1, class T2>
std::size_t operator()(const std::pair<T1, T2>& p) const {
std::size_t seed = 0;
hash_combine(seed, p.first);
hash_combine(seed, p.second);
return seed;
}
};
// Hasher for std::tuple
struct TupleHasher {
template <typename... Ts>
std::size_t operator()(const std::tuple<Ts...>& t) const {
std::size_t seed = 0;
std::apply([&](const auto&... args) {
(hash_combine(seed, args), ...);
}, t);
return seed;
}
};
// --- Segmenters ---
// Matrix segment information
struct MatrixSegment {
int offset_n; // Segment offset
int size_n; // Segment size
int core_id; // Segment core ID
};
// Split B-matrix into segments
static std::vector<MatrixSegment> compute_matrix_segments(int N, int num_cores = 3) {
std::vector<MatrixSegment> segments;
// N divisible by 16 for F16
const int alignment = 16;
// Basic segment size
int base_segment_size = (N / num_cores / alignment) * alignment;
int remaining = N - (base_segment_size * num_cores);
int offset = 0;
for (int i = 0; i < num_cores; i++) {
MatrixSegment seg;
seg.offset_n = offset;
seg.size_n = base_segment_size;
seg.core_id = i;
if (i < remaining / alignment) {
seg.size_n += alignment;
}
offset += seg.size_n;
segments.push_back(seg);
}
return segments;
}
// --- Structs ---
// RKNN buffer context
struct ggml_backend_rknpu_buffer_context {
rknn_tensor_mem * rknn_mem;
DmaBuffer dma_buf;
std::string name;
};
// RKNN matmul operation context
struct rknpu_matmul_context {
rknn_matmul_info info;
rknn_matmul_io_attr io_attr;
rknn_matmul_ctx ctx = 0;
rknpu_matmul_context(int M, int K, int N, rknn_matmul_type type) {
memset(&info, 0, sizeof(info));
info.M = M;
info.K = K;
info.N = N;
info.type = type;
info.B_layout = RKNN_MM_LAYOUT_NATIVE;
info.AC_layout = RKNN_MM_LAYOUT_NORM;
int ret = rknn_matmul_create(&ctx, &info, &io_attr);
if (ret < 0) {
RKNPU_LOG_ERROR("rknn_matmul_create failed for %dx%dx%d\n", M, K, N);
ctx = 0;
}
}
~rknpu_matmul_context() {
if (ctx != 0) {
rknn_matmul_destroy(ctx);
}
}
};
// Backend main context
struct ggml_backend_rknpu_context {
std::string name;
std::mutex mutex;
// RKNN matmul contexts cache
std::unordered_map<std::tuple<int, int, int, int>, std::shared_ptr<rknpu_matmul_context>, TupleHasher> matmul_ctx_cache;
// B-matrices cache
std::unordered_map<std::tuple<const ggml_tensor*, int>, std::shared_ptr<rknn_tensor_mem>, TupleHasher> b_npu_buffer_cache;
// A- and C-matrices cache
std::unordered_map<std::tuple<int, int, int>, std::shared_ptr<rknn_tensor_mem>, TupleHasher> a_buffer_cache;
std::unordered_map<std::tuple<int, int, int>, std::shared_ptr<rknn_tensor_mem>, TupleHasher> c_buffer_cache;
std::shared_ptr<rknpu_matmul_context> get_matmul_ctx(int M, int K, int N, int core_id) {
std::lock_guard<std::mutex> lock(mutex);
auto key = std::make_tuple(M, K, N, core_id);
auto it = matmul_ctx_cache.find(key);
if (it != matmul_ctx_cache.end()) {
return it->second;
}
auto ctx = std::make_shared<rknpu_matmul_context>(M, K, N, RKNN_FLOAT16_MM_FLOAT16_TO_FLOAT32);
if (ctx->ctx == 0) {
return nullptr;
}
rknn_core_mask core_mask;
switch(core_id) {
case 0: core_mask = RKNN_NPU_CORE_0; break;
case 1: core_mask = RKNN_NPU_CORE_1; break;
case 2: core_mask = RKNN_NPU_CORE_2; break;
default: core_mask = RKNN_NPU_CORE_AUTO; break;
}
int ret = rknn_matmul_set_core_mask(ctx->ctx, core_mask);
if (ret != RKNN_SUCC) {
RKNPU_LOG_ERROR("Failed to set core mask %d for core %d\n", core_mask, core_id);
}
matmul_ctx_cache[key] = ctx;
return ctx;
}
};
// RKNN memory global context
struct rknpu_memory_context {
rknn_matmul_ctx mem_ctx = 0;
std::mutex mutex;
rknpu_memory_context() {
rknn_matmul_info dummy_info;
memset(&dummy_info, 0, sizeof(dummy_info));
dummy_info.M = 32;
dummy_info.K = 32;
dummy_info.N = 32;
dummy_info.type = RKNN_FLOAT16_MM_FLOAT16_TO_FLOAT32;
rknn_matmul_io_attr dummy_io_attr;
int ret = rknn_matmul_create(&mem_ctx, &dummy_info, &dummy_io_attr);
if (ret < 0) {
RKNPU_LOG_ERROR("Failed to create dummy matmul context for memory operations: %d\n", ret);
mem_ctx = 0;
}
}
~rknpu_memory_context() {
if (mem_ctx != 0) {
rknn_matmul_destroy(mem_ctx);
}
}
rknn_matmul_ctx get_ctx() {
std::lock_guard<std::mutex> lock(mutex);
return mem_ctx;
}
};
static rknpu_memory_context & get_rknpu_memory_context() {
static rknpu_memory_context g_mem_ctx;
return g_mem_ctx;
}
//
// Backend
//
static const char * ggml_backend_rknpu_name(ggml_backend_t backend) {
UNUSED(backend);
return "RKNPU";
}
static void ggml_backend_rknpu_free(ggml_backend_t backend) {
ggml_backend_rknpu_context * ctx = (ggml_backend_rknpu_context *)backend->context;
delete ctx;
delete backend;
}
// Function for getting buffer from cache or creating new one
static std::shared_ptr<rknn_tensor_mem> get_or_create_npu_buffer(
ggml_backend_rknpu_context* backend_ctx,
rknn_matmul_ctx matmul_ctx,
size_t size,
const std::tuple<int, int, int>& key,
std::unordered_map<std::tuple<int, int, int>, std::shared_ptr<rknn_tensor_mem>, TupleHasher>& cache
) {
std::lock_guard<std::mutex> lock(backend_ctx->mutex);
auto it = cache.find(key);
if (it != cache.end()) {
return it->second;
}
rknn_tensor_mem* mem = rknn_create_mem(matmul_ctx, size);
if (!mem) { return nullptr; }
auto mem_ctx_for_deleter = get_rknpu_memory_context().get_ctx();
auto deleter = [mem_ctx_for_deleter](rknn_tensor_mem* m) {
if (m && mem_ctx_for_deleter != 0) {
rknn_destroy_mem(mem_ctx_for_deleter, m);
}
};
std::shared_ptr<rknn_tensor_mem> mem_shared(mem, deleter);
cache[key] = mem_shared;
return mem_shared;
}
static enum ggml_status ggml_backend_rknpu_graph_compute(ggml_backend_t backend, struct ggml_cgraph* cgraph) {
auto* backend_ctx = (ggml_backend_rknpu_context*)backend->context;
const int NUM_CORES = 3;
for (int i = 0; i < cgraph->n_nodes; i++) {
struct ggml_tensor* node = cgraph->nodes[i];
if (node->op != GGML_OP_MUL_MAT) continue;
#if RKNPU_PERF_LOG
std::string node_name = std::string(node->name) + " (" + std::to_string(i) + ")";
RknpuPerfLogger perf_total(node_name + " - TOTAL");
#endif
const struct ggml_tensor* src0 = node->src[0]; // Weights : (K x N)
const struct ggml_tensor* src1 = node->src[1]; // Activations : (M x K)
struct ggml_tensor* dst = node;
const int M = (int)src1->ne[1];
const int K = (int)src0->ne[0];
const int N = (int)src0->ne[1];
auto segments = compute_matrix_segments(N, NUM_CORES);
std::vector<std::shared_ptr<rknpu_matmul_context>> matmul_ctxs(NUM_CORES);
std::vector<std::shared_ptr<rknn_tensor_mem>> mem_B_segments(NUM_CORES);
std::shared_ptr<rknn_tensor_mem> mem_A_shared;
std::vector<std::shared_ptr<rknn_tensor_mem>> mem_C_segments(NUM_CORES);
// ===== 1. Preraring contexts =====
{
#if RKNPU_PERF_LOG
RknpuPerfLogger perf_ctx(node_name + " - Context preparation");
#endif
for (int core_id = 0; core_id < NUM_CORES; core_id++) {
int N_segment = segments[core_id].size_n;
if (N_segment == 0) continue;
matmul_ctxs[core_id] = backend_ctx->get_matmul_ctx(M, K, N_segment, core_id);
if (!matmul_ctxs[core_id] || matmul_ctxs[core_id]->ctx == 0) {
RKNPU_LOG_ERROR("Failed to create matmul context for core %d\n", core_id);
return GGML_STATUS_FAILED;
}
}
}
// ===== 2. Preparing B-matrix =====
{
#if RKNPU_PERF_LOG
RknpuPerfLogger perf_b(node_name + " - B matrix preparation");
#endif
std::vector<bool> need_upload(NUM_CORES, false);
std::vector<std::vector<uint16_t>> packed_segments(NUM_CORES);
// Creating or getting memory from cache (sequentially)
for (int core_id = 0; core_id < NUM_CORES; core_id++) {
if (segments[core_id].size_n == 0) continue;
std::lock_guard<std::mutex> lock(backend_ctx->mutex);
auto cache_key = std::make_tuple(src0, core_id);
auto it = backend_ctx->b_npu_buffer_cache.find(cache_key);
if (it != backend_ctx->b_npu_buffer_cache.end()) {
mem_B_segments[core_id] = it->second;
} else {
need_upload[core_id] = true;
auto& matmul_ctx = matmul_ctxs[core_id];
rknn_tensor_mem* mem = rknn_create_mem(matmul_ctx->ctx, matmul_ctx->io_attr.B.size);
if (!mem) return GGML_STATUS_FAILED;
auto mem_ctx_for_deleter = get_rknpu_memory_context().get_ctx();
auto deleter = [mem_ctx_for_deleter](rknn_tensor_mem* m) { if (m) rknn_destroy_mem(mem_ctx_for_deleter, m); };
mem_B_segments[core_id] = std::shared_ptr<rknn_tensor_mem>(mem, deleter);
backend_ctx->b_npu_buffer_cache[cache_key] = mem_B_segments[core_id];
}
}
// Packing segments into native format (parallel)
#pragma omp parallel for num_threads(NUM_CORES)
for (int core_id = 0; core_id < NUM_CORES; core_id++) {
if (need_upload[core_id]) {
auto& matmul_ctx = matmul_ctxs[core_id];
packed_segments[core_id].resize(matmul_ctx->io_attr.B.size / sizeof(uint16_t));
pack_B_segment_fp16_native_from_KN(
packed_segments[core_id].data(),
(const uint16_t*)src0->data,
K, N, segments[core_id].offset_n, segments[core_id].size_n);
}
}
// Setting and syncronizing memory in NPU (sequentially)
for (int core_id = 0; core_id < NUM_CORES; core_id++) {
if (segments[core_id].size_n == 0) continue;
if (need_upload[core_id]) {
memcpy(mem_B_segments[core_id]->virt_addr, packed_segments[core_id].data(), packed_segments[core_id].size() * sizeof(uint16_t));
RKNN_CHECK(rknn_mem_sync(matmul_ctxs[core_id]->ctx, mem_B_segments[core_id].get(), RKNN_MEMORY_SYNC_TO_DEVICE), "sync B segment");
}
RKNN_CHECK(rknn_matmul_set_io_mem(matmul_ctxs[core_id]->ctx, mem_B_segments[core_id].get(), &matmul_ctxs[core_id]->io_attr.B), "set_io_mem B segment");
}
}
// ===== 3. Preparing A-matrix =====
{
#if RKNPU_PERF_LOG
RknpuPerfLogger perf_a(node_name + " - A matrix preparation");
#endif
auto cache_key = std::make_tuple(M, K, -1);
auto& matmul_ctx_0 = matmul_ctxs[0];
mem_A_shared = get_or_create_npu_buffer(backend_ctx, matmul_ctx_0->ctx, matmul_ctx_0->io_attr.A.size, cache_key, backend_ctx->a_buffer_cache);
if (!mem_A_shared) return GGML_STATUS_FAILED;
const float* x = (const float*)src1->data;
const int row_stride = (int)(src1->nb[1] / sizeof(float));
uint16_t* dst_base = (uint16_t*)mem_A_shared->virt_addr;
#pragma omp parallel for
for (int m = 0; m < M; ++m) {
const float* src_row = x + (size_t)m * row_stride;
uint16_t* dst_row = dst_base + (size_t)m * K;
int k = 0;
for (; k <= K - 8; k += 8) {
float32x4_t f32_vec_0 = vld1q_f32(src_row + k);
float32x4_t f32_vec_1 = vld1q_f32(src_row + k + 4);
float16x8_t f16_vec = vcombine_f16(vcvt_f16_f32(f32_vec_0), vcvt_f16_f32(f32_vec_1));
vst1q_u16(dst_row + k, (uint16x8_t)f16_vec);
}
for (; k < K; ++k) {
dst_row[k] = GGML_FP32_TO_FP16(src_row[k]);
}
}
RKNN_CHECK(rknn_mem_sync(matmul_ctx_0->ctx, mem_A_shared.get(), RKNN_MEMORY_SYNC_TO_DEVICE), "sync A TO_DEVICE");
for (int core_id = 0; core_id < NUM_CORES; core_id++) {
if (segments[core_id].size_n == 0) continue;
RKNN_CHECK(rknn_matmul_set_io_mem(matmul_ctxs[core_id]->ctx, mem_A_shared.get(), &matmul_ctxs[core_id]->io_attr.A), "set_io_mem A for core");
}
}
// ===== 4. Preparing C-matrix =====
{
#if RKNPU_PERF_LOG
RknpuPerfLogger perf_c(node_name + " - C matrix preparation");
#endif
for (int core_id = 0; core_id < NUM_CORES; core_id++) {
if (segments[core_id].size_n == 0) continue;
auto& matmul_ctx = matmul_ctxs[core_id];
auto cache_key = std::make_tuple(M, segments[core_id].size_n, core_id);
mem_C_segments[core_id] = get_or_create_npu_buffer(backend_ctx, matmul_ctx->ctx, matmul_ctx->io_attr.C.size, cache_key, backend_ctx->c_buffer_cache);
if (!mem_C_segments[core_id]) return GGML_STATUS_FAILED;
RKNN_CHECK(rknn_matmul_set_io_mem(matmul_ctx->ctx, mem_C_segments[core_id].get(), &matmul_ctx->io_attr.C), "set_io_mem C");
}
}
// ===== 5. Running operation =====
{
#if RKNPU_PERF_LOG
RknpuPerfLogger perf_run(node_name + " - NPU parallel execution");
#endif
#pragma omp parallel for num_threads(NUM_CORES)
for (int core_id = 0; core_id < NUM_CORES; core_id++) {
if (segments[core_id].size_n > 0) {
int ret = rknn_matmul_run(matmul_ctxs[core_id]->ctx);
if (ret != RKNN_SUCC) {
RKNPU_LOG_ERROR("rknn_matmul_run failed for core %d with error %d\n", core_id, ret);
}
}
}
}
// ===== 6. Collecting results =====
{
#if RKNPU_PERF_LOG
RknpuPerfLogger perf_gather(node_name + " - Result gathering");
#endif
float* dst_data = (float*)dst->data;
for (int core_id = 0; core_id < NUM_CORES; core_id++) {
if (segments[core_id].size_n == 0) continue;
RKNN_CHECK(rknn_mem_sync(matmul_ctxs[core_id]->ctx, mem_C_segments[core_id].get(), RKNN_MEMORY_SYNC_FROM_DEVICE), "sync C FROM_DEVICE");
}
#pragma omp parallel for
for (int m = 0; m < M; m++) {
for (int core_id = 0; core_id < NUM_CORES; core_id++) {
if (segments[core_id].size_n == 0) continue;
int N_offset = segments[core_id].offset_n;
int N_segment = segments[core_id].size_n;
float* src_segment_base = (float*)mem_C_segments[core_id]->virt_addr;
memcpy(dst_data + (size_t)m * N + N_offset,
src_segment_base + (size_t)m * N_segment,
N_segment * sizeof(float));
}
}
}
}
return GGML_STATUS_SUCCESS;
}
//
// Buffer
//
static void ggml_backend_rknpu_buffer_free_buffer(ggml_backend_buffer_t buffer) {
ggml_backend_rknpu_buffer_context * ctx = (ggml_backend_rknpu_buffer_context *)buffer->context;
rknn_matmul_ctx mem_ctx = get_rknpu_memory_context().get_ctx();
if (mem_ctx != 0 && ctx->rknn_mem != nullptr) {
RKNN_CHECK(rknn_destroy_mem(mem_ctx, ctx->rknn_mem), "rknn_destroy_mem");
}
dma_free(ctx->dma_buf);
delete ctx;
}
static void * ggml_backend_rknpu_buffer_get_base(ggml_backend_buffer_t buffer) {
ggml_backend_rknpu_buffer_context * ctx = (ggml_backend_rknpu_buffer_context *)buffer->context;
return ctx->rknn_mem->virt_addr;
}
static enum ggml_status ggml_backend_rknpu_buffer_init_tensor(ggml_backend_buffer_t buffer, struct ggml_tensor * tensor) {
UNUSED(buffer);
UNUSED(tensor);
return GGML_STATUS_SUCCESS;
}
static void ggml_backend_rknpu_buffer_set_tensor(ggml_backend_buffer_t buffer, struct ggml_tensor * tensor, const void * data, size_t offset, size_t size) {
auto * ctx = (ggml_backend_rknpu_buffer_context *) buffer->context;
uint8_t * base = (uint8_t *) ctx->rknn_mem->virt_addr;
uint8_t * dst = (uint8_t *) tensor->data + offset;
GGML_ASSERT(dst >= base && dst + size <= base + ctx->rknn_mem->size);
memcpy(dst, data, size);
auto mem_ctx = get_rknpu_memory_context().get_ctx();
RKNN_CHECK(rknn_mem_sync(mem_ctx, ctx->rknn_mem, RKNN_MEMORY_SYNC_TO_DEVICE), "mem_sync write");
}
static void ggml_backend_rknpu_buffer_get_tensor(ggml_backend_buffer_t buffer, const struct ggml_tensor * tensor, void * data, size_t offset, size_t size) {
ggml_backend_rknpu_buffer_context * ctx = (ggml_backend_rknpu_buffer_context *)buffer->context;
rknn_matmul_ctx mem_ctx = get_rknpu_memory_context().get_ctx();
RKNN_CHECK(rknn_mem_sync(mem_ctx, ctx->rknn_mem, RKNN_MEMORY_SYNC_FROM_DEVICE), "rknn_mem_sync from device");
memcpy(data, (const char *)tensor->data + offset, size);
}
static void ggml_backend_rknpu_buffer_clear(ggml_backend_buffer_t buffer, uint8_t value) {
ggml_backend_rknpu_buffer_context * ctx = (ggml_backend_rknpu_buffer_context *)buffer->context;
memset(ctx->rknn_mem->virt_addr, value, ctx->rknn_mem->size);
rknn_matmul_ctx mem_ctx = get_rknpu_memory_context().get_ctx();
RKNN_CHECK(rknn_mem_sync(mem_ctx, ctx->rknn_mem, RKNN_MEMORY_SYNC_TO_DEVICE), "rknn_mem_sync to device after clear");
}
//
// Buffer Type
//
static const char * ggml_backend_rknpu_buffer_type_get_name(ggml_backend_buffer_type_t buft) {
UNUSED(buft);
return "RKNPU";
}
static ggml_backend_buffer_t ggml_backend_rknpu_buffer_type_alloc_buffer(ggml_backend_buffer_type_t buft, size_t size) {
UNUSED(buft);
rknn_matmul_ctx mem_ctx = get_rknpu_memory_context().get_ctx();
if (mem_ctx == 0) {
RKNPU_LOG_ERROR("RKNPU memory context not initialized, cannot allocate buffer.\n");
return NULL;
}
DmaBuffer dma_buf = dma_alloc(size);
if (dma_buf.fd < 0) {
RKNPU_LOG_ERROR("dma_alloc failed to allocate %zu bytes\n", size);
return NULL;
}
rknn_tensor_mem * rknn_mem = rknn_create_mem_from_fd(mem_ctx, dma_buf.fd, dma_buf.virt_addr, size, 0);
if (rknn_mem == NULL) {
RKNPU_LOG_ERROR("rknn_create_mem_from_fd failed for size %zu\n", size);
dma_free(dma_buf);
return NULL;
}
ggml_backend_rknpu_buffer_context * ctx = new ggml_backend_rknpu_buffer_context{rknn_mem, dma_buf, "rknpu_dma_buffer"};
static const ggml_backend_buffer_i rknpu_buffer_interface = {
/* .free_buffer = */ ggml_backend_rknpu_buffer_free_buffer,
/* .get_base = */ ggml_backend_rknpu_buffer_get_base,
/* .init_tensor = */ ggml_backend_rknpu_buffer_init_tensor,
/* .memset_tensor = */ NULL,
/* .set_tensor = */ ggml_backend_rknpu_buffer_set_tensor,
/* .get_tensor = */ ggml_backend_rknpu_buffer_get_tensor,
/* .cpy_tensor = */ NULL,
/* .clear = */ ggml_backend_rknpu_buffer_clear,
/* .reset = */ NULL,
};
return ggml_backend_buffer_init(buft, rknpu_buffer_interface, ctx, size);
}
static size_t ggml_backend_rknpu_buffer_type_get_alignment(ggml_backend_buffer_type_t buft) {
UNUSED(buft);
return 64;
}
static size_t ggml_backend_rknpu_buffer_type_get_alloc_size(ggml_backend_buffer_type_t buft, const struct ggml_tensor * tensor) {
UNUSED(buft);
return ggml_nbytes(tensor);
}
//
// Device
//
static const char * ggml_backend_rknpu_device_get_name(ggml_backend_dev_t dev) {
UNUSED(dev);
return "RKNPU";
}
static const char * ggml_backend_rknpu_device_get_description(ggml_backend_dev_t dev) {
UNUSED(dev);
return "Rockchip NPU";
}
static void ggml_backend_rknpu_device_get_memory(ggml_backend_dev_t dev, size_t * free, size_t * total) {
UNUSED(dev);
*free = 0;
*total = 0;
}
static enum ggml_backend_dev_type ggml_backend_rknpu_device_get_type(ggml_backend_dev_t dev) {
UNUSED(dev);
return GGML_BACKEND_DEVICE_TYPE_ACCEL;
}
static void ggml_backend_rknpu_device_get_props(ggml_backend_dev_t dev, struct ggml_backend_dev_props * props) {
props->name = ggml_backend_rknpu_device_get_name(dev);
props->description = ggml_backend_rknpu_device_get_description(dev);
props->type = ggml_backend_rknpu_device_get_type(dev);
ggml_backend_rknpu_device_get_memory(dev, &props->memory_free, &props->memory_total);
props->device_id = NULL;
props->caps.async = false;
props->caps.host_buffer = false;
props->caps.buffer_from_host_ptr = false;
props->caps.events = false;
}
static bool ggml_backend_rknpu_device_supports_op(ggml_backend_dev_t dev, const struct ggml_tensor * op) {
UNUSED(dev);
RKNPU_LOG_INFO("[RKNPU_SUPPORT] Checking support for op: %s, name: %s\n", ggml_op_name(op->op), op->name);
switch (op->op) {
case GGML_OP_NONE:
RKNPU_LOG_INFO(" - ACCEPT: Op NONE is supported for RKNPU (leaf/weights).\n");
return true;
case GGML_OP_MUL_MAT: {
const struct ggml_tensor * src0 = op->src[0]; // Weights
const struct ggml_tensor * src1 = op->src[1]; // Activations
RKNPU_LOG_INFO(" - src0: type=%s, dims=[%lld, %lld, %lld, %lld]\n", ggml_type_name(src0->type), src0->ne[0], src0->ne[1], src0->ne[2], src0->ne[3]);
RKNPU_LOG_INFO(" - src1: type=%s, dims=[%lld, %lld, %lld, %lld]\n", ggml_type_name(src1->type), src1->ne[0], src1->ne[1], src1->ne[2], src1->ne[3]);
// Checking for weights in F16 and activations in F32
if (src0->type != GGML_TYPE_F16 || src1->type != GGML_TYPE_F32) {
RKNPU_LOG_INFO(" - REJECT: Unsupported type combination. Want src0=F16, src1=F32.\n");
return false;
}
// Checking for K divisible 32, N divisible 16
if (src0->ne[0] % 32 != 0 || src0->ne[1] % 16 != 0) {
RKNPU_LOG_INFO(" - REJECT: K (%lld) must be a multiple of 32 and N (%lld) a multiple of 16.\n", src0->ne[0], src0->ne[1]);
return false;
}
// Checking for exact dimentions
if (src1->ne[0] != src0->ne[0]) {
RKNPU_LOG_INFO(" - REJECT: K dimensions do not match (%lld vs %lld).\n", src1->ne[0], src0->ne[0]);
return false;
}
// Checking contiguous memory
if (!ggml_is_contiguous(src0) || !ggml_is_contiguous(src1)) {
RKNPU_LOG_INFO(" - REJECT: Tensors are not contiguous.\n");
return false;
}
RKNPU_LOG_INFO(" - ACCEPT: Op %s can be offloaded to RKNPU.\n", op->name);
return true;
}
default:
RKNPU_LOG_INFO(" - REJECT: Op %s is not supported by RKNPU backend.\n", ggml_op_name(op->op));
return false;
}
}
static ggml_backend_t ggml_backend_rknpu_device_init_backend(ggml_backend_dev_t dev, const char * params) {
UNUSED(dev);
UNUSED(params);
ggml_backend_rknpu_context * ctx = new ggml_backend_rknpu_context{"RKNPU Backend"};
static const struct ggml_backend_i rknpu_backend_interface = {
/* .get_name = */ ggml_backend_rknpu_name,
/* .free = */ ggml_backend_rknpu_free,
/* .set_tensor_async = */ NULL,
/* .get_tensor_async = */ NULL,
/* .cpy_tensor_async = */ NULL,
/* .synchronize = */ NULL,
/* .graph_plan_create = */ NULL,
/* .graph_plan_free = */ NULL,
/* .graph_plan_update = */ NULL,
/* .graph_plan_compute = */ NULL,
/* .graph_compute = */ ggml_backend_rknpu_graph_compute,
/* .event_record = */ NULL,
/* .event_wait = */ NULL,
/* .graph_optimize = */ NULL,
};
return new ggml_backend{
/* .guid = */ {0},
/* .iface = */ rknpu_backend_interface,
/* .device = */ dev,
/* .context = */ ctx,
};
}
//
// Registry
//
static const char * ggml_backend_rknpu_reg_get_name(ggml_backend_reg_t reg) {
UNUSED(reg);
return "RKNPU";
}
static size_t ggml_backend_rknpu_reg_get_device_count(ggml_backend_reg_t reg) {
UNUSED(reg);
if (get_rknpu_memory_context().get_ctx() != 0) {
return 1;
}
return 0;
}
static ggml_backend_dev_t ggml_backend_rknpu_reg_get_device(ggml_backend_reg_t reg, size_t index) {
if (index != 0) {
return NULL;
}
static const struct ggml_backend_buffer_type_i rknpu_buffer_type_interface = {
/* .get_name = */ ggml_backend_rknpu_buffer_type_get_name,
/* .alloc_buffer = */ ggml_backend_rknpu_buffer_type_alloc_buffer,
/* .get_alignment = */ ggml_backend_rknpu_buffer_type_get_alignment,
/* .get_max_size = */ NULL,
/* .get_alloc_size = */ ggml_backend_rknpu_buffer_type_get_alloc_size,
/* .is_host = */ NULL,
};
static struct ggml_backend_buffer_type rknpu_buffer_type = {
/* .iface = */ rknpu_buffer_type_interface,
/* .device = */ NULL,
/* .context = */ NULL,
};
static const struct ggml_backend_device_i rknpu_device_interface = {
/* .get_name = */ ggml_backend_rknpu_device_get_name,
/* .get_description = */ ggml_backend_rknpu_device_get_description,
/* .get_memory = */ ggml_backend_rknpu_device_get_memory,
/* .get_type = */ ggml_backend_rknpu_device_get_type,
/* .get_props = */ ggml_backend_rknpu_device_get_props,
/* .init_backend = */ ggml_backend_rknpu_device_init_backend,
/* .get_buffer_type = */ [](ggml_backend_dev_t dev) { UNUSED(dev); return &rknpu_buffer_type; },
/* .get_host_buffer_type = */ NULL,
/* .buffer_from_host_ptr = */ NULL,
/* .supports_op = */ ggml_backend_rknpu_device_supports_op,
/* .supports_buft = */ [](ggml_backend_dev_t dev, ggml_backend_buffer_type_t buft) { UNUSED(dev); return buft == &rknpu_buffer_type; },
/* .offload_op = */ NULL,
/* .event_new = */ NULL,
/* .event_free = */ NULL,
/* .event_synchronize = */ NULL,
};
static struct ggml_backend_device rknpu_device = {
/* .iface = */ rknpu_device_interface,
/* .reg = */ reg,
/* .context = */ NULL,
};
if (rknpu_buffer_type.device == NULL) {
rknpu_buffer_type.device = &rknpu_device;
}
return &rknpu_device;
}
//
// Public API
//
GGML_API ggml_backend_reg_t ggml_backend_rknpu2_reg(void) {
static const struct ggml_backend_reg_i rknpu_reg_interface = {
/* .get_name = */ ggml_backend_rknpu_reg_get_name,
/* .get_device_count = */ ggml_backend_rknpu_reg_get_device_count,
/* .get_device = */ ggml_backend_rknpu_reg_get_device,
/* .get_proc_address = */ NULL,
};
static struct ggml_backend_reg rknpu_backend_reg = {
/* .api_version = */ GGML_BACKEND_API_VERSION,
/* .iface = */ rknpu_reg_interface,
/* .context = */ NULL,
};
return &rknpu_backend_reg;
}
#ifdef GGML_BACKEND_DL
GGML_BACKEND_DL_IMPL(ggml_backend_rknpu2_reg)
#endif
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include "ggml-backend.h"
#ifdef __cplusplus
extern "C" {
#endif
GGML_API ggml_backend_reg_t ggml_backend_rknpu2_reg(void);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,804 @@
/****************************************************************************
*
* Copyright (c) 2017 - 2022 by Rockchip Corp. All rights reserved.
*
* The material in this file is confidential and contains trade secrets
* of Rockchip Corporation. This is proprietary information owned by
* Rockchip Corporation. No part of this work may be disclosed,
* reproduced, copied, transmitted, or used in any way for any purpose,
* without the express written permission of Rockchip Corporation.
*
*****************************************************************************/
#ifndef _RKNN_API_H
#define _RKNN_API_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/*
Definition of extended flag for rknn_init.
*/
/* set high priority context. */
#define RKNN_FLAG_PRIOR_HIGH 0x00000000
/* set medium priority context */
#define RKNN_FLAG_PRIOR_MEDIUM 0x00000001
/* set low priority context. */
#define RKNN_FLAG_PRIOR_LOW 0x00000002
/* asynchronous mode.
when enable, rknn_outputs_get will not block for too long because it directly retrieves the result of
the previous frame which can increase the frame rate on single-threaded mode, but at the cost of
rknn_outputs_get not retrieves the result of the current frame.
in multi-threaded mode you do not need to turn this mode on. */
#define RKNN_FLAG_ASYNC_MASK 0x00000004
/* collect performance mode.
when enable, you can get detailed performance reports via rknn_query(ctx, RKNN_QUERY_PERF_DETAIL, ...),
but it will reduce the frame rate. */
#define RKNN_FLAG_COLLECT_PERF_MASK 0x00000008
/* allocate all memory in outside, includes weight/internal/inputs/outputs */
#define RKNN_FLAG_MEM_ALLOC_OUTSIDE 0x00000010
/* weight sharing with the same network structure */
#define RKNN_FLAG_SHARE_WEIGHT_MEM 0x00000020
/* send fence fd from outside */
#define RKNN_FLAG_FENCE_IN_OUTSIDE 0x00000040
/* get fence fd from inside */
#define RKNN_FLAG_FENCE_OUT_OUTSIDE 0x00000080
/* dummy init flag: could only get total_weight_size and total_internal_size by rknn_query*/
#define RKNN_FLAG_COLLECT_MODEL_INFO_ONLY 0x00000100
/* allocate internal memory in outside */
#define RKNN_FLAG_INTERNAL_ALLOC_OUTSIDE 0x00000200
/* set GPU as the preferred execution backend When the operator is not supported by the NPU */
#define RKNN_FLAG_EXECUTE_FALLBACK_PRIOR_DEVICE_GPU 0x00000400
/* enable allocate sram type buffers */
#define RKNN_FLAG_ENABLE_SRAM 0x00000800
/* sram type buffers are shared among different contexts */
#define RKNN_FLAG_SHARE_SRAM 0x00001000
/* default nice -19, this flag can disable default priority */
#define RKNN_FLAG_DISABLE_PROC_HIGH_PRIORITY 0x00002000
/* don't flush input buffer cache, the user must ensure that the input tensor has flushed the cache before calling rknn_run.
!!! Don't use this flags when you call rknn_inputs_set() to set input data. */
#define RKNN_FLAG_DISABLE_FLUSH_INPUT_MEM_CACHE 0x00004000
/* Don't invalid output buffer cache.
Users cannot directly access output_mem->virt_addr,
which will cause cache consistency problems.
If you want to use output_mem->virt_addr,
you must use rknn_mem_sync (ctx, mem, RKNN_MEMORY_SYNC_FROM_DEVICE) to flush the cache.
This flags is generally used when the output data of the NPU is not accessed by the CPU,
but is accessed by the GPU or RGA to reduce the time required to flush the cache.
!!! Don't use this flags when you call rknn_outputs_get() to get output data.*/
#define RKNN_FLAG_DISABLE_FLUSH_OUTPUT_MEM_CACHE 0x00008000
/* This flag is used when the model data buffer is allocated by NPU, and can be accessed by NPU directly. */
#define RKNN_FLAG_MODEL_BUFFER_ZERO_COPY 0x00010000
/* This flag is a memory allocation flag, which is used in rknn_create_mem2() when no context is available. */
#define RKNN_MEM_FLAG_ALLOC_NO_CONTEXT 0x00020000
/*
Error code returned by the RKNN API.
*/
#define RKNN_SUCC 0 /* execute succeed. */
#define RKNN_ERR_FAIL -1 /* execute failed. */
#define RKNN_ERR_TIMEOUT -2 /* execute timeout. */
#define RKNN_ERR_DEVICE_UNAVAILABLE -3 /* device is unavailable. */
#define RKNN_ERR_MALLOC_FAIL -4 /* memory malloc fail. */
#define RKNN_ERR_PARAM_INVALID -5 /* parameter is invalid. */
#define RKNN_ERR_MODEL_INVALID -6 /* model is invalid. */
#define RKNN_ERR_CTX_INVALID -7 /* context is invalid. */
#define RKNN_ERR_INPUT_INVALID -8 /* input is invalid. */
#define RKNN_ERR_OUTPUT_INVALID -9 /* output is invalid. */
#define RKNN_ERR_DEVICE_UNMATCH -10 /* the device is unmatch, please update rknn sdk
and npu driver/firmware. */
#define RKNN_ERR_INCOMPATILE_PRE_COMPILE_MODEL -11 /* This RKNN model use pre_compile mode, but not compatible with current driver. */
#define RKNN_ERR_INCOMPATILE_OPTIMIZATION_LEVEL_VERSION -12 /* This RKNN model set optimization level, but not compatible with current driver. */
#define RKNN_ERR_TARGET_PLATFORM_UNMATCH -13 /* This RKNN model set target platform, but not compatible with current platform. */
/*
Definition for tensor
*/
#define RKNN_MAX_DIMS 16 /* maximum dimension of tensor. */
#define RKNN_MAX_NUM_CHANNEL 15 /* maximum channel number of input tensor. */
#define RKNN_MAX_NAME_LEN 256 /* maximum name lenth of tensor. */
#define RKNN_MAX_DYNAMIC_SHAPE_NUM 512 /* maximum number of dynamic shape for each input. */
#ifdef __arm__
typedef uint32_t rknn_context;
#else
typedef uint64_t rknn_context;
#endif
/*
The query command for rknn_query
*/
typedef enum _rknn_query_cmd {
RKNN_QUERY_IN_OUT_NUM = 0, /* query the number of input & output tensor. */
RKNN_QUERY_INPUT_ATTR = 1, /* query the attribute of input tensor. */
RKNN_QUERY_OUTPUT_ATTR = 2, /* query the attribute of output tensor. */
RKNN_QUERY_PERF_DETAIL = 3, /* query the detail performance, need set
RKNN_FLAG_COLLECT_PERF_MASK when call rknn_init,
this query needs to be valid after rknn_outputs_get. */
RKNN_QUERY_PERF_RUN = 4, /* query the time of run,
this query needs to be valid after rknn_outputs_get. */
RKNN_QUERY_SDK_VERSION = 5, /* query the sdk & driver version */
RKNN_QUERY_MEM_SIZE = 6, /* query the weight & internal memory size */
RKNN_QUERY_CUSTOM_STRING = 7, /* query the custom string */
RKNN_QUERY_NATIVE_INPUT_ATTR = 8, /* query the attribute of native input tensor. */
RKNN_QUERY_NATIVE_OUTPUT_ATTR = 9, /* query the attribute of native output tensor. */
RKNN_QUERY_NATIVE_NC1HWC2_INPUT_ATTR = 8, /* query the attribute of native input tensor. */
RKNN_QUERY_NATIVE_NC1HWC2_OUTPUT_ATTR = 9, /* query the attribute of native output tensor. */
RKNN_QUERY_NATIVE_NHWC_INPUT_ATTR = 10, /* query the attribute of native input tensor. */
RKNN_QUERY_NATIVE_NHWC_OUTPUT_ATTR = 11, /* query the attribute of native output tensor. */
RKNN_QUERY_DEVICE_MEM_INFO = 12, /* query the attribute of rknn memory information. */
RKNN_QUERY_INPUT_DYNAMIC_RANGE = 13, /* query the dynamic shape range of rknn input tensor. */
RKNN_QUERY_CURRENT_INPUT_ATTR = 14, /* query the current shape of rknn input tensor, only valid for dynamic rknn model*/
RKNN_QUERY_CURRENT_OUTPUT_ATTR = 15, /* query the current shape of rknn output tensor, only valid for dynamic rknn model*/
RKNN_QUERY_CURRENT_NATIVE_INPUT_ATTR = 16, /* query the current native shape of rknn input tensor, only valid for dynamic rknn model*/
RKNN_QUERY_CURRENT_NATIVE_OUTPUT_ATTR = 17, /* query the current native shape of rknn output tensor, only valid for dynamic rknn model*/
RKNN_QUERY_CMD_MAX
} rknn_query_cmd;
/*
the tensor data type.
*/
typedef enum _rknn_tensor_type {
RKNN_TENSOR_FLOAT32 = 0, /* data type is float32. */
RKNN_TENSOR_FLOAT16, /* data type is float16. */
RKNN_TENSOR_INT8, /* data type is int8. */
RKNN_TENSOR_UINT8, /* data type is uint8. */
RKNN_TENSOR_INT16, /* data type is int16. */
RKNN_TENSOR_UINT16, /* data type is uint16. */
RKNN_TENSOR_INT32, /* data type is int32. */
RKNN_TENSOR_UINT32, /* data type is uint32. */
RKNN_TENSOR_INT64, /* data type is int64. */
RKNN_TENSOR_BOOL,
RKNN_TENSOR_INT4,
RKNN_TENSOR_BFLOAT16,
RKNN_TENSOR_TYPE_MAX
} rknn_tensor_type;
inline static const char* get_type_string(rknn_tensor_type type)
{
switch(type) {
case RKNN_TENSOR_FLOAT32: return "FP32";
case RKNN_TENSOR_FLOAT16: return "FP16";
case RKNN_TENSOR_INT8: return "INT8";
case RKNN_TENSOR_UINT8: return "UINT8";
case RKNN_TENSOR_INT16: return "INT16";
case RKNN_TENSOR_UINT16: return "UINT16";
case RKNN_TENSOR_INT32: return "INT32";
case RKNN_TENSOR_UINT32: return "UINT32";
case RKNN_TENSOR_INT64: return "INT64";
case RKNN_TENSOR_BOOL: return "BOOL";
case RKNN_TENSOR_INT4: return "INT4";
case RKNN_TENSOR_BFLOAT16: return "BF16";
default: return "UNKNOW";
}
}
/*
the quantitative type.
*/
typedef enum _rknn_tensor_qnt_type {
RKNN_TENSOR_QNT_NONE = 0, /* none. */
RKNN_TENSOR_QNT_DFP, /* dynamic fixed point. */
RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC, /* asymmetric affine. */
RKNN_TENSOR_QNT_MAX
} rknn_tensor_qnt_type;
inline static const char* get_qnt_type_string(rknn_tensor_qnt_type type)
{
switch(type) {
case RKNN_TENSOR_QNT_NONE: return "NONE";
case RKNN_TENSOR_QNT_DFP: return "DFP";
case RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC: return "AFFINE";
default: return "UNKNOW";
}
}
/*
the tensor data format.
*/
typedef enum _rknn_tensor_format {
RKNN_TENSOR_NCHW = 0, /* data format is NCHW. */
RKNN_TENSOR_NHWC, /* data format is NHWC. */
RKNN_TENSOR_NC1HWC2, /* data format is NC1HWC2. */
RKNN_TENSOR_UNDEFINED,
RKNN_TENSOR_FORMAT_MAX
} rknn_tensor_format;
/*
the mode of running on target NPU core.
*/
typedef enum _rknn_core_mask {
RKNN_NPU_CORE_AUTO = 0, /* default, run on NPU core randomly. */
RKNN_NPU_CORE_0 = 1, /* run on NPU core 0. */
RKNN_NPU_CORE_1 = 2, /* run on NPU core 1. */
RKNN_NPU_CORE_2 = 4, /* run on NPU core 2. */
RKNN_NPU_CORE_0_1 = RKNN_NPU_CORE_0 | RKNN_NPU_CORE_1, /* run on NPU core 0 and core 1. */
RKNN_NPU_CORE_0_1_2 = RKNN_NPU_CORE_0_1 | RKNN_NPU_CORE_2, /* run on NPU core 0 and core 1 and core 2. */
RKNN_NPU_CORE_ALL = 0xffff, /* auto choice, run on NPU cores depending on platform */
RKNN_NPU_CORE_UNDEFINED,
} rknn_core_mask;
inline static const char* get_format_string(rknn_tensor_format fmt)
{
switch(fmt) {
case RKNN_TENSOR_NCHW: return "NCHW";
case RKNN_TENSOR_NHWC: return "NHWC";
case RKNN_TENSOR_NC1HWC2: return "NC1HWC2";
case RKNN_TENSOR_UNDEFINED: return "UNDEFINED";
default: return "UNKNOW";
}
}
/*
the information for RKNN_QUERY_IN_OUT_NUM.
*/
typedef struct _rknn_input_output_num {
uint32_t n_input; /* the number of input. */
uint32_t n_output; /* the number of output. */
} rknn_input_output_num;
/*
the information for RKNN_QUERY_INPUT_ATTR / RKNN_QUERY_OUTPUT_ATTR.
*/
typedef struct _rknn_tensor_attr {
uint32_t index; /* input parameter, the index of input/output tensor,
need set before call rknn_query. */
uint32_t n_dims; /* the number of dimensions. */
uint32_t dims[RKNN_MAX_DIMS]; /* the dimensions array. */
char name[RKNN_MAX_NAME_LEN]; /* the name of tensor. */
uint32_t n_elems; /* the number of elements. */
uint32_t size; /* the bytes size of tensor. */
rknn_tensor_format fmt; /* the data format of tensor. */
rknn_tensor_type type; /* the data type of tensor. */
rknn_tensor_qnt_type qnt_type; /* the quantitative type of tensor. */
int8_t fl; /* fractional length for RKNN_TENSOR_QNT_DFP. */
int32_t zp; /* zero point for RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC. */
float scale; /* scale for RKNN_TENSOR_QNT_AFFINE_ASYMMETRIC. */
uint32_t w_stride; /* the stride of tensor along the width dimention of input,
Note: it is read-only, 0 means equal to width. */
uint32_t size_with_stride; /* the bytes size of tensor with stride. */
uint8_t pass_through; /* pass through mode, for rknn_set_io_mem interface.
if TRUE, the buf data is passed directly to the input node of the rknn model
without any conversion. the following variables do not need to be set.
if FALSE, the buf data is converted into an input consistent with the model
according to the following type and fmt. so the following variables
need to be set.*/
uint32_t h_stride; /* the stride along the height dimention of input,
Note: it is write-only, if it was set to 0, h_stride = height. */
} rknn_tensor_attr;
typedef struct _rknn_input_range {
uint32_t index; /* input parameter, the index of input/output tensor,
need set before call rknn_query. */
uint32_t shape_number; /* the number of shape. */
rknn_tensor_format fmt; /* the data format of tensor. */
char name[RKNN_MAX_NAME_LEN]; /* the name of tensor. */
uint32_t dyn_range[RKNN_MAX_DYNAMIC_SHAPE_NUM][RKNN_MAX_DIMS]; /* the dynamic input dimensions range. */
uint32_t n_dims; /* the number of dimensions. */
} rknn_input_range;
/*
the information for RKNN_QUERY_PERF_DETAIL.
*/
typedef struct _rknn_perf_detail {
char* perf_data; /* the string pointer of perf detail. don't need free it by user. */
uint64_t data_len; /* the string length. */
} rknn_perf_detail;
/*
the information for RKNN_QUERY_PERF_RUN.
*/
typedef struct _rknn_perf_run {
int64_t run_duration; /* real inference time (us) */
} rknn_perf_run;
/*
the information for RKNN_QUERY_SDK_VERSION.
*/
typedef struct _rknn_sdk_version {
char api_version[256]; /* the version of rknn api. */
char drv_version[256]; /* the version of rknn driver. */
} rknn_sdk_version;
/*
the information for RKNN_QUERY_MEM_SIZE.
*/
typedef struct _rknn_mem_size {
uint32_t total_weight_size; /* the weight memory size */
uint32_t total_internal_size; /* the internal memory size, exclude inputs/outputs */
uint64_t total_dma_allocated_size; /* total dma memory allocated size */
uint32_t total_sram_size; /* total system sram size reserved for rknn */
uint32_t free_sram_size; /* free system sram size reserved for rknn */
uint32_t reserved[10]; /* reserved */
} rknn_mem_size;
/*
the information for RKNN_QUERY_CUSTOM_STRING.
*/
typedef struct _rknn_custom_string {
char string[1024]; /* the string of custom, lengths max to 1024 bytes */
} rknn_custom_string;
/*
The flags of rknn_tensor_mem.
*/
typedef enum _rknn_tensor_mem_flags {
RKNN_TENSOR_MEMORY_FLAGS_ALLOC_INSIDE = 1, /*Used to mark in rknn_destroy_mem() whether it is necessary to release the "mem" pointer itself.
If the flag RKNN_TENSOR_MEMORY_FLAGS_ALLOC_INSIDE is set, rknn_destroy_mem() will call free(mem).*/
RKNN_TENSOR_MEMORY_FLAGS_FROM_FD = 2, /*Used to mark in rknn_create_mem_from_fd() whether it is necessary to release the "mem" pointer itself.
If the flag RKNN_TENSOR_MEMORY_FLAGS_FROM_FD is set, rknn_destroy_mem() will call free(mem).*/
RKNN_TENSOR_MEMORY_FLAGS_FROM_PHYS = 3, /*Used to mark in rknn_create_mem_from_phys() whether it is necessary to release the "mem" pointer itself.
If the flag RKNN_TENSOR_MEMORY_FLAGS_FROM_PHYS is set, rknn_destroy_mem() will call free(mem).*/
RKNN_TENSOR_MEMORY_FLAGS_UNKNOWN
} rknn_tensor_mem_flags;
/*
The mode to allocate rknn memory.
*/
typedef enum _rknn_mem_alloc_flags {
RKNN_FLAG_MEMORY_FLAGS_DEFAULT = 0 << 0, /* Same with RKNN_FLAG_MEMORY_CACHEABLE */
RKNN_FLAG_MEMORY_CACHEABLE = 1 << 0, /* Create Cacheable memory. */
RKNN_FLAG_MEMORY_NON_CACHEABLE = 1 << 1, /* Create NON-Cacheable memory. */
RKNN_FLAG_MEMORY_TRY_ALLOC_SRAM = 1 << 2, /* Try to allocate memory in SRAM if possible. if SRAM is not enough, allocate rest memory in DRAM. */
} rknn_mem_alloc_flags;
/*
The mode to sync cacheable rknn memory.
*/
typedef enum _rknn_mem_sync_mode {
RKNN_MEMORY_SYNC_TO_DEVICE = 0x1, /* the mode used for consistency of device access after CPU accesses data. */
RKNN_MEMORY_SYNC_FROM_DEVICE = 0x2, /* the mode used for consistency of CPU access after device accesses data. */
RKNN_MEMORY_SYNC_BIDIRECTIONAL = RKNN_MEMORY_SYNC_TO_DEVICE | RKNN_MEMORY_SYNC_FROM_DEVICE, /* the mode used for consistency of data access
between device and CPU in both directions. */
} rknn_mem_sync_mode;
/*
the memory information of tensor.
*/
typedef struct _rknn_tensor_memory {
void* virt_addr; /* the virtual address of tensor buffer. */
uint64_t phys_addr; /* the physical address of tensor buffer. */
int32_t fd; /* the fd of tensor buffer. */
int32_t offset; /* indicates the offset of the memory. */
uint32_t size; /* the size of tensor buffer. */
uint32_t flags; /* the flags of tensor buffer, reserved */
void * priv_data; /* the private data of tensor buffer. */
} rknn_tensor_mem;
/*
the input information for rknn_input_set.
*/
typedef struct _rknn_input {
uint32_t index; /* the input index. */
void* buf; /* the input buf for index. */
uint32_t size; /* the size of input buf. */
uint8_t pass_through; /* pass through mode.
if TRUE, the buf data is passed directly to the input node of the rknn model
without any conversion. the following variables do not need to be set.
if FALSE, the buf data is converted into an input consistent with the model
according to the following type and fmt. so the following variables
need to be set.*/
rknn_tensor_type type; /* the data type of input buf. */
rknn_tensor_format fmt; /* the data format of input buf.
currently the internal input format of NPU is NCHW by default.
so entering NCHW data can avoid the format conversion in the driver. */
} rknn_input;
/*
the output information for rknn_outputs_get.
*/
typedef struct _rknn_output {
uint8_t want_float; /* want transfer output data to float */
uint8_t is_prealloc; /* whether buf is pre-allocated.
if TRUE, the following variables need to be set.
if FALSE, the following variables do not need to be set. */
uint32_t index; /* the output index. */
void* buf; /* the output buf for index.
when is_prealloc = FALSE and rknn_outputs_release called,
this buf pointer will be free and don't use it anymore. */
uint32_t size; /* the size of output buf. */
} rknn_output;
/*
the extend information for rknn_init.
*/
typedef struct _rknn_init_extend {
rknn_context ctx; /* rknn context */
int32_t real_model_offset; /* real rknn model file offset, only valid when init context with rknn file path and zero-copy model model */
uint32_t real_model_size; /* real rknn model file size, only valid when init context with rknn file path and zero-copy model model */
int32_t model_buffer_fd; /* the fd of model buffer. */
uint32_t model_buffer_flags; /* the flags of model_buffer */
uint8_t reserved[112]; /* reserved */
} rknn_init_extend;
/*
the extend information for rknn_run.
*/
typedef struct _rknn_run_extend {
uint64_t frame_id; /* output parameter, indicate current frame id of run. */
int32_t non_block; /* block flag of run, 0 is block else 1 is non block */
int32_t timeout_ms; /* timeout for block mode, in milliseconds */
int32_t fence_fd; /* fence fd from other unit */
} rknn_run_extend;
/*
the extend information for rknn_outputs_get.
*/
typedef struct _rknn_output_extend {
uint64_t frame_id; /* output parameter, indicate the frame id of outputs, corresponds to
struct rknn_run_extend.frame_id.*/
} rknn_output_extend;
/* rknn_init
initial the context and load the rknn model.
input:
rknn_context* context the pointer of context handle.
void* model if size > 0, pointer to the rknn model, if size = 0, filepath to the rknn model.
uint32_t size the size of rknn model.
uint32_t flag extend flag, see the define of RKNN_FLAG_XXX_XXX.
rknn_init_extend* extend the extend information of init.
return:
int error code.
*/
int rknn_init(rknn_context* context, void* model, uint32_t size, uint32_t flag, rknn_init_extend* extend);
/* rknn_dup_context
initial the context and load the rknn model.
input:
rknn_context* context_in the pointer of context in handle.
rknn_context* context_out the pointer of context out handle.
return:
int error code.
*/
int rknn_dup_context(rknn_context* context_in, rknn_context* context_out);
/* rknn_destroy
unload the rknn model and destroy the context.
input:
rknn_context context the handle of context.
return:
int error code.
*/
int rknn_destroy(rknn_context context);
/* rknn_query
query the information about model or others. see rknn_query_cmd.
input:
rknn_context context the handle of context.
rknn_query_cmd cmd the command of query.
void* info the buffer point of information.
uint32_t size the size of information.
return:
int error code.
*/
int rknn_query(rknn_context context, rknn_query_cmd cmd, void* info, uint32_t size);
/* rknn_inputs_set
set inputs information by input index of rknn model.
inputs information see rknn_input.
input:
rknn_context context the handle of context.
uint32_t n_inputs the number of inputs.
rknn_input inputs[] the arrays of inputs information, see rknn_input.
return:
int error code
*/
int rknn_inputs_set(rknn_context context, uint32_t n_inputs, rknn_input inputs[]);
/*
rknn_set_batch_core_num
set rknn batch core_num.
input:
rknn_context context the handle of context.
int core_num the core number.
return:
int error code.
*/
int rknn_set_batch_core_num(rknn_context context, int core_num);
/* rknn_set_core_mask
set the core mask for the model.(only supported on multi-core NPU platform)
RKNN_NPU_CORE_AUTO: auto mode, default value
RKNN_NPU_CORE_0: core 0 mode
RKNN_NPU_CORE_1: core 1 mode
RKNN_NPU_CORE_2: core 2 mode
RKNN_NPU_CORE_0_1: combine core 0/1 mode
RKNN_NPU_CORE_0_1_2: combine core 0/1/2 mode
RKNN_NPU_CORE_ALL: auto mode, select multiple npu cores to run depending on platform
input:
rknn_context context the handle of context.
rknn_core_mask core_mask the core mask.
return:
int error code.
*/
int rknn_set_core_mask(rknn_context context, rknn_core_mask core_mask);
/* rknn_run
run the model to execute inference.
input:
rknn_context context the handle of context.
rknn_run_extend* extend the extend information of run.
return:
int error code.
*/
int rknn_run(rknn_context context, rknn_run_extend* extend);
/* rknn_wait
wait the model after execute inference.
input:
rknn_context context the handle of context.
rknn_run_extend* extend the extend information of run.
return:
int error code.
*/
int rknn_wait(rknn_context context, rknn_run_extend* extend);
/* rknn_outputs_get
wait the inference to finish and get the outputs.
this function will block until inference finish.
the results will set to outputs[].
input:
rknn_context context the handle of context.
uint32_t n_outputs the number of outputs.
rknn_output outputs[] the arrays of output, see rknn_output.
rknn_output_extend* the extend information of output.
return:
int error code.
*/
int rknn_outputs_get(rknn_context context, uint32_t n_outputs, rknn_output outputs[], rknn_output_extend* extend);
/* rknn_outputs_release
release the outputs that get by rknn_outputs_get.
after called, the rknn_output[x].buf get from rknn_outputs_get will
also be free when rknn_output[x].is_prealloc = FALSE.
input:
rknn_context context the handle of context.
uint32_t n_ouputs the number of outputs.
rknn_output outputs[] the arrays of output.
return:
int error code
*/
int rknn_outputs_release(rknn_context context, uint32_t n_ouputs, rknn_output outputs[]);
/* new api for zero copy */
/* rknn_create_mem_from_phys (memory allocated outside)
initialize tensor memory from physical address.
input:
rknn_context ctx the handle of context.
uint64_t phys_addr physical address.
void *virt_addr virtual address.
uint32_t size the size of tensor buffer.
return:
rknn_tensor_mem the pointer of tensor memory information.
*/
rknn_tensor_mem* rknn_create_mem_from_phys(rknn_context ctx, uint64_t phys_addr, void *virt_addr, uint32_t size);
/* rknn_create_mem_from_fd (memory allocated outside)
initialize tensor memory from file description.
input:
rknn_context ctx the handle of context.
int32_t fd file description.
void *virt_addr virtual address.
uint32_t size the size of tensor buffer.
int32_t offset indicates the offset of the memory (virt_addr without offset).
return:
rknn_tensor_mem the pointer of tensor memory information.
*/
rknn_tensor_mem* rknn_create_mem_from_fd(rknn_context ctx, int32_t fd, void *virt_addr, uint32_t size, int32_t offset);
/* rknn_create_mem_from_mb_blk (memory allocated outside)
create tensor memory from mb_blk.
input:
rknn_context ctx the handle of context.
void *mb_blk mb_blk allocate from system api.
int32_t offset indicates the offset of the memory.
return:
rknn_tensor_mem the pointer of tensor memory information.
*/
rknn_tensor_mem* rknn_create_mem_from_mb_blk(rknn_context ctx, void *mb_blk, int32_t offset);
/* rknn_create_mem (memory allocated inside)
create tensor memory.
input:
rknn_context ctx the handle of context.
uint32_t size the size of tensor buffer.
return:
rknn_tensor_mem the pointer of tensor memory information.
*/
rknn_tensor_mem* rknn_create_mem(rknn_context ctx, uint32_t size);
/* rknn_create_mem2 (memory allocated inside)
create tensor memory.
input:
rknn_context ctx the handle of context.
uint64_t size the size of tensor buffer.
uint64_t alloc_flags memory allocation flags.
return:
rknn_tensor_mem the pointer of tensor memory information.
*/
rknn_tensor_mem* rknn_create_mem2(rknn_context ctx, uint64_t size, uint64_t alloc_flags);
/* rknn_destroy_mem (support allocate inside and outside)
destroy tensor memory.
input:
rknn_context ctx the handle of context.
rknn_tensor_mem *mem the pointer of tensor memory information.
return:
int error code
*/
int rknn_destroy_mem(rknn_context ctx, rknn_tensor_mem *mem);
/* rknn_set_weight_mem
set the weight memory.
input:
rknn_context ctx the handle of context.
rknn_tensor_mem *mem the array of tensor memory information
return:
int error code.
*/
int rknn_set_weight_mem(rknn_context ctx, rknn_tensor_mem *mem);
/* rknn_set_internal_mem
set the internal memory.
input:
rknn_context ctx the handle of context.
rknn_tensor_mem *mem the array of tensor memory information
return:
int error code.
*/
int rknn_set_internal_mem(rknn_context ctx, rknn_tensor_mem *mem);
/* rknn_set_io_mem
set the input and output tensors buffer.
input:
rknn_context ctx the handle of context.
rknn_tensor_mem *mem the array of tensor memory information.
rknn_tensor_attr *attr the attribute of input or output tensor buffer.
return:
int error code.
*/
int rknn_set_io_mem(rknn_context ctx, rknn_tensor_mem *mem, rknn_tensor_attr *attr);
/* rknn_set_input_shape(deprecated)
set the input tensor shape (only valid for dynamic shape rknn model).
input:
rknn_context ctx the handle of context.
rknn_tensor_attr *attr the attribute of input or output tensor buffer.
return:
int error code.
*/
int rknn_set_input_shape(rknn_context ctx, rknn_tensor_attr* attr);
/* rknn_set_input_shapes
set all the input tensor shapes. graph will run under current set of input shapes after rknn_set_input_shapes.(only valid for dynamic shape rknn model).
input:
rknn_context ctx the handle of context.
uint32_t n_inputs the number of inputs.
rknn_tensor_attr attr[] the attribute array of all input tensors.
return:
int error code.
*/
int rknn_set_input_shapes(rknn_context ctx, uint32_t n_inputs, rknn_tensor_attr attr[]);
/* rknn_mem_sync
sync cacheable rknn memory when both cpu and device access data.
input:
rknn_context context the handle of context.
rknn_tensor_mem *mem the pointer of tensor memory information.
rknn_mem_sync_mode mode the mode of sync cache.
return:
int error code.
*/
int rknn_mem_sync(rknn_context context, rknn_tensor_mem* mem, rknn_mem_sync_mode mode);
#ifdef __cplusplus
} //extern "C"
#endif
#endif //_RKNN_API_H
@@ -0,0 +1,144 @@
/****************************************************************************
*
* Copyright (c) 2017 - 2023 by Rockchip Corp. All rights reserved.
*
* The material in this file is confidential and contains trade secrets
* of Rockchip Corporation. This is proprietary information owned by
* Rockchip Corporation. No part of this work may be disclosed,
* reproduced, copied, transmitted, or used in any way for any purpose,
* without the express written permission of Rockchip Corporation.
*
*****************************************************************************/
#ifndef _RKNN_CUSTOM_OP_H
#define _RKNN_CUSTOM_OP_H
#ifdef __cplusplus
extern "C" {
#endif
#include "rknn_api.h"
#include <stdint.h>
/*
Error code returned by the RKNN Custom Operator API.
*/
#define RKNN_WARNING_SKIP_CUSTOM_OP_COMPUTE -14 /* if custom op init callback funtion return this code and op type is supported by RKNN, it will use RKNN implementation. */
#define RKNN_CUSTOM_OP_MAX_STR_LEN 64
#define RKNN_CUSTOM_OP_MAX_VALUE_LEN 32
#define RKNN_CUSTOM_OP_EXPORT __attribute__((visibility("default")))
#ifdef __arm__
typedef uint32_t rknn_custom_op_interal_context;
#else
typedef uint64_t rknn_custom_op_interal_context;
#endif
/*
the backend execution device of custom operator.
*/
typedef enum _rknn_target_type
{
RKNN_TARGET_TYPE_CPU = 1, /* backend device is cpu */
RKNN_TARGET_TYPE_GPU = 2, /* backend device is gpu */
RKNN_TARGET_TYPE_MAX
} rknn_target_type;
typedef struct _rknn_gpu_op_context
{
void* cl_context;
void* cl_command_queue;
void* cl_kernel;
} rknn_gpu_op_context;
typedef struct _rknn_custom_op_context
{
rknn_target_type target; /* custom op backend target */
rknn_custom_op_interal_context internal_ctx; /* the context of custom op*/
rknn_gpu_op_context gpu_ctx; /* the gpu context of custom op */
void* priv_data; /* the private data managed by user */
} rknn_custom_op_context;
typedef struct _rknn_custom_op_tensor
{
rknn_tensor_attr attr; /* the attribute of tensor buffer. */
rknn_tensor_mem mem; /* the memory information of tensor. */
} rknn_custom_op_tensor;
typedef struct _rknn_custom_op_attr
{
char name[RKNN_MAX_NAME_LEN]; /* the name of operator atrributes. */
rknn_tensor_type dtype; /* the data type of operator attributes, indicate the 'array' type. */
uint32_t n_elems; /* the number of 'array'. */
void* data; /* the array pointer of operator attributes, the data type of each element is determined by type. */
} rknn_custom_op_attr;
/*
the information of custom operator to add to the rknn_context.
*/
typedef struct _rknn_custom_op
{
uint32_t version; /* custom op version */
rknn_target_type target; /* custom op backend target */
char op_type[RKNN_MAX_NAME_LEN]; /* custom op type */
char cl_kernel_name[RKNN_MAX_NAME_LEN]; /* the opencl kernel name used by custom op */
char* cl_kernel_source; /* if cl_source_size > 0, pointer to the cl kernel source string, if cl_source_size = 0,
filepath to the cl kernel file. */
uint64_t cl_source_size; /* the size of cl_kernel_source */
char cl_build_options[RKNN_MAX_NAME_LEN]; /* the options for opencl to build clProgram used by custom op */
/**
* The callback function sets that the users need to code
*/
int (*init)(rknn_custom_op_context* op_ctx, rknn_custom_op_tensor* inputs, uint32_t n_inputs,
rknn_custom_op_tensor* outputs, uint32_t n_outputs); /* [optional] custom op kernel init falllback function*/
int (*prepare)(rknn_custom_op_context* op_ctx, rknn_custom_op_tensor* inputs, uint32_t n_inputs,
rknn_custom_op_tensor* outputs, uint32_t n_outputs); /* [optional] custom op kernel prepare falllback function*/
int (*compute)(rknn_custom_op_context* op_ctx, rknn_custom_op_tensor* inputs, uint32_t n_inputs,
rknn_custom_op_tensor* outputs, uint32_t n_outputs); /* [required] custom op kernel compute falllback function */
int (*compute_native)(rknn_custom_op_context* op_ctx, rknn_custom_op_tensor* inputs, uint32_t n_inputs,
rknn_custom_op_tensor* outputs, uint32_t n_outputs); /* [optional] custom op kernel compute with native attribute falllback function */
int (*destroy)(rknn_custom_op_context* op_ctx); /* [optional] custom op kernel compute falllback function */
} rknn_custom_op;
/**
* dlopen custom op with so required this function
*/
typedef rknn_custom_op* (*get_custom_op_func)();
/* rknn_register_custom_ops
Register custom operators to rknn_context.
Steps to use a custom op:
1. Create a rknn_custom_op structure array and fill in it.
2. Setup prepare/compute/compute_native/destroy callback function and add them to the
rknn_custom_op.(compute is required and other function is optional, compute_native is not supported now, set it
to nullptr)
3. Call rknn_register_custom_ops to register the op type after rknn_init.
input:
rknn_context ctx the handle of context.
rknn_custom_op* op the custom operator array, each of which contains operator information and calllback function.
uint32_t custom_op_num the length of rknn_custom_op array.
return:
int error code.
*/
int rknn_register_custom_ops(rknn_context ctx, rknn_custom_op* op, uint32_t custom_op_num);
/* rknn_custom_op_get_op_attr
input:
rknn_custom_op_context* op_ctx the handle of custom op context.
const char* attr_name the attribute name of operator.
rknn_custom_op_attr* op_attr the data and information of operator attributes.
*/
void rknn_custom_op_get_op_attr(rknn_custom_op_context* op_ctx, const char* attr_name, rknn_custom_op_attr* op_attr);
#ifdef __cplusplus
} // extern "C"
#endif
#endif //_RKNN_CUSTOM_OP_H
@@ -0,0 +1,544 @@
/****************************************************************************
*
* Copyright (c) 2017 - 2018 by Rockchip Corp. All rights reserved.
*
* The material in this file is confidential and contains trade secrets
* of Rockchip Corporation. This is proprietary information owned by
* Rockchip Corporation. No part of this work may be disclosed,
* reproduced, copied, transmitted, or used in any way for any purpose,
* without the express written permission of Rockchip Corporation.
*
*****************************************************************************/
#ifndef _RKNN_MATMUL_API_H
#define _RKNN_MATMUL_API_H
#ifdef __cplusplus
extern "C" {
#endif
#include "rknn_api.h"
typedef rknn_context rknn_matmul_ctx;
typedef enum _rknn_matmul_quant_type
{
RKNN_QUANT_TYPE_PER_LAYER_SYM = 0,
RKNN_QUANT_TYPE_PER_LAYER_ASYM = 1,
RKNN_QUANT_TYPE_PER_CHANNEL_SYM = 2,
RKNN_QUANT_TYPE_PER_CHANNEL_ASYM = 3,
RKNN_QUANT_TYPE_PER_GROUP_SYM = 4,
RKNN_QUANT_TYPE_PER_GROUP_ASYM = 5,
} rknn_matmul_quant_type;
typedef struct _rknn_quant_params
{
char name[RKNN_MAX_NAME_LEN];
// matmul tensor scale
float* scale;
int32_t scale_len;
// matmul tensor zero point
int32_t* zp;
int32_t zp_len;
} rknn_quant_params;
typedef enum _rknn_matmul_type
{
RKNN_FLOAT16_MM_FLOAT16_TO_FLOAT32 = 1,
RKNN_INT8_MM_INT8_TO_INT32 = 2,
RKNN_INT8_MM_INT8_TO_INT8 = 3,
RKNN_FLOAT16_MM_FLOAT16_TO_FLOAT16 = 4,
RKNN_FLOAT16_MM_INT8_TO_FLOAT32 = 5,
RKNN_FLOAT16_MM_INT8_TO_FLOAT16 = 6,
RKNN_FLOAT16_MM_INT4_TO_FLOAT32 = 7,
RKNN_FLOAT16_MM_INT4_TO_FLOAT16 = 8,
RKNN_INT8_MM_INT8_TO_FLOAT32 = 9,
RKNN_INT4_MM_INT4_TO_INT16 = 10,
RKNN_INT8_MM_INT4_TO_INT32 = 11,
RKNN_FLOAT16_MM_INT4_TO_BFLOAT16 = 12,
RKNN_INT8_MM_INT4_TO_FLOAT16 = 15,
} rknn_matmul_type;
inline static const char* get_matmul_type_string(rknn_matmul_type type)
{
switch (type) {
case RKNN_FLOAT16_MM_FLOAT16_TO_FLOAT32:
return "RKNN_FLOAT16_MM_FLOAT16_TO_FLOAT32";
case RKNN_INT8_MM_INT8_TO_INT32:
return "RKNN_INT8_MM_INT8_TO_INT32";
case RKNN_INT8_MM_INT8_TO_INT8:
return "RKNN_INT8_MM_INT8_TO_INT8";
case RKNN_FLOAT16_MM_FLOAT16_TO_FLOAT16:
return "RKNN_FLOAT16_MM_FLOAT16_TO_FLOAT16";
case RKNN_FLOAT16_MM_INT8_TO_FLOAT32:
return "RKNN_FLOAT16_MM_INT8_TO_FLOAT32";
case RKNN_FLOAT16_MM_INT8_TO_FLOAT16:
return "RKNN_FLOAT16_MM_INT8_TO_FLOAT16";
case RKNN_INT4_MM_INT4_TO_INT16:
return "RKNN_INT4_MM_INT4_TO_INT16";
case RKNN_FLOAT16_MM_INT4_TO_FLOAT32:
return "RKNN_FLOAT16_MM_INT4_TO_FLOAT32";
case RKNN_FLOAT16_MM_INT4_TO_FLOAT16:
return "RKNN_FLOAT16_MM_INT4_TO_FLOAT16";
case RKNN_INT8_MM_INT4_TO_INT32:
return "RKNN_INT8_MM_INT4_TO_INT32";
case RKNN_INT8_MM_INT8_TO_FLOAT32:
return "RKNN_INT8_MM_INT8_TO_FLOAT32";
case RKNN_FLOAT16_MM_INT4_TO_BFLOAT16:
return "RKNN_FLOAT16_MM_INT4_TO_BFLOAT16";
default:
return "UNKNOW";
}
}
typedef struct _rknn_matmul_tensor_attr
{
char name[RKNN_MAX_NAME_LEN];
// indicate A(M, K) or B(K, N) or C(M, N)
uint32_t n_dims;
uint32_t dims[RKNN_MAX_DIMS];
// matmul tensor size
uint32_t size;
// matmul tensor data type
// int8 : A, B
// int32: C
rknn_tensor_type type;
} rknn_matmul_tensor_attr;
typedef struct _rknn_matmul_io_attr
{
// indicate A(M, K) or B(K, N) or C(M, N)
rknn_matmul_tensor_attr A;
rknn_matmul_tensor_attr B;
rknn_matmul_tensor_attr C;
} rknn_matmul_io_attr;
/*
matmul dynamic shape struct
*/
typedef struct _rknn_matmul_shape
{
int32_t M;
int32_t K;
int32_t N;
} rknn_matmul_shape;
/*
the layout of matmul input/output tensor.
*/
typedef enum
{
RKNN_MM_LAYOUT_NORM = 0,
RKNN_MM_LAYOUT_NATIVE = 1,
RKNN_MM_LAYOUT_TP_NORM = 2,
} rknn_matmul_layout;
/*
matmul information struct
*/
typedef struct rknn_matmul_info_t
{
int32_t M;
int32_t K; // limit: RK3566/3568: int8 type must be aligned with 32byte, float16 type must be aligned with 16byte;
// RK3562: int8 type must be aligned with 32byte, float16 type must be aligned with 32byte;
// RK3588/3576: int8 type must be aligned with 32byte, float16 type must be aligned with 32byte,
// int4 type must be aligned with 32byte;
int32_t N; // limit: RK3566/3568: int8 type must be aligned with 16byte, float16 type must be aligned with 8byte;
// RK3562: int8 type must be aligned with 16byte, float16 type must be aligned with 8byte;
// RK3588/3576: int8 type must be aligned with 32byte, float16 type must be aligned with 16byte,
// int4 type must be aligned with 64byte;
// matmul data type
// int4: int4(A) x int4(B) -> int16(C)
// int8: int8(A) x int8(B) -> int32(C)
// float16: float16(A) x float16(B) -> float32(C)
rknn_matmul_type type;
// matmul native layout for B
// 0: normal layout
// 1: native layout
int16_t B_layout;
// matmul quant type for B
// A and C only support per layer
// 0: per layer
// 1: per channel
// 2: per group
int16_t B_quant_type;
// matmul native layout for A and C
// 0: normal layout
// 1: native layout
int16_t AC_layout;
// matmul quant type for A and C, only support 0
int16_t AC_quant_type;
// iommu domain id, each domain has 4GB of space
int32_t iommu_domain_id;
// B_quant_type set 2, group size is enable
int16_t group_size;
// reserved field
int8_t reserved[34];
} rknn_matmul_info;
/* rknn_matmul_create
params:
rknn_matmul_ctx *ctx the handle of context.
rknn_matmul_info *info the matmal information.
rknn_matmul_io_attr *io_attr inputs/output attribute
return:
int error code
*/
int rknn_matmul_create(rknn_matmul_ctx* ctx, rknn_matmul_info* info, rknn_matmul_io_attr* io_attr);
/* rknn_matmul_create_dynamic_shape
params:
rknn_matmul_ctx *ctx the handle of context.
rknn_matmul_info *info the matmal information.
int shape_num the supported shape number of matmul.
rknn_matmul_shape dynamic_shapes[] the supported M,K,N shape struct array.
rknn_matmul_io_attr *io_attr the array of inputs and output attribute
return:
int error code
*/
/*
info.M, K, N无效
*/
int rknn_matmul_create_dynamic_shape(rknn_matmul_ctx* ctx, rknn_matmul_info* info, int shape_num,
rknn_matmul_shape dynamic_shapes[], rknn_matmul_io_attr io_attrs[]);
/* rknn_matmul_set_io_mem
params:
rknn_matmul_ctx ctx the handle of context.
rknn_tensor_mem *mem the pointer of tensor memory information.
rknn_matmul_tensor_attr *attr the attribute of input or output tensor buffer.
return:
int error code.
formula:
C = A * B,
limit:
K max: k <= 10240
K limit: RK3566/3568: int8 type must be aligned with 32byte, float16 type must be aligned with 16byte;
RK3562: int8 type must be aligned with 32byte, float16 type must be aligned with 32byte;
RK3588/3576: int8 type must be aligned with 32byte, float16 type must be aligned with 32byte,
int4 type must be aligned with 32byte;
N limit: RK3566/3568: int8 type must be aligned with 16byte, float16 type must be aligned with 8byte;
RK3562: int8 type must be aligned with 16byte, float16 type must be aligned with 8byte;
RK3588/3576: int8 type must be aligned with 32byte, float16 type must be aligned with 16byte,
int4 type must be aligned with 64byte;
A shape: M x K
normal layout: (M, K)
[M1K1, M1K2, ..., M1Kk,
M2K1, M2K2, ..., M2Kk,
...
MmK1, MmK2, ..., MmKk]
for RK3566/3568
int8:
native layout: (K / 8, M, 8)
[K1M1, K2M1, ..., K8M1,
K9M2, K10M2, ..., K16M2,
...
K(k-7)Mm, K(k-6)Mm, ..., KkMm]
float16:
native layout: (K / 4, M, 4)
[K1M1, K2M1, ..., K4M1,
K9M2, K10M2, ..., K8M2,
...
K(k-3)Mm, K(k-2)Mm, ..., KkMm]
for RK3562
int8:
native layout: (K / 16, M, 16)
[K1M1, K2M1, ..., K16M1,
K17M2, K18M2, ..., K32M2,
...
K(k-15)Mm, K(k-14)Mm, ..., KkMm]
float16:
native layout: (K / 8, M, 8)
[K1M1, K2M1, ..., K8M1,
K9M2, K10M2, ..., K16M2,
...
K(k-7)Mm, K(k-6)Mm, ..., KkMm]
for RK3588/3576
int4:
native layout: (K / 32, M, 32)
[K1M1, K2M1, ..., K32M1,
K33M2, K10M2, ..., K64M2,
...
K(k-31)Mm, K(k-30)Mm, ..., KkMm]
int8:
native layout: (K / 16, M, 16)
[K1M1, K2M1, ..., K16M1,
K17M2, K18M2, ..., K32M2,
...
K(k-15)Mm, K(k-14)Mm, ..., KkMm]
float16:
native layout: (K / 8, M, 8)
[K1M1, K2M1, ..., K8M1,
K9M2, K10M2, ..., K16M2,
...
K(k-7)Mm, K(k-6)Mm, ..., KkMm]
B shape: K x N
normal layout: (K, N)
[K1N1, K1N2, ..., K1Nn,
K2N1, K2N2, ..., K2Nn,
...
KkN1, KkN2, ..., KkNn]
for RK3566/3568
int8:
native layout: (N / 16, K / 32, 16, 32)
[K1N1, K2N1, ..., K32N1,
K1N2, K2N2, ..., K32N2,
...
K1N16, K2N16, ..., K32N16,
K33N1, K34N1, ..., K64N1,
K33N2, K34N2, ..., K64N2,
...
K(k-31)N16, K(k-30)N16, ..., KkN16,
K1N17, K2N17, ..., K32N17,
K1N18, K2N18, ..., K32N18,
...
K(k-31)Nn, K(k-30)Nn, ..., KkNn]
float16:
native layout: (N / 8, K / 16, 8, 16)
[K1N1, K2N1, ..., K16N1,
K1N2, K2N2, ..., K16N2,
...
K1N8, K2N8, ..., K16N8,
K17N1, K18N1, ..., K32N1,
K17N2, K18N2, ..., K32N2,
...
K(k-15)N8, K(k-30)N8, ..., KkN8,
K1N9, K2N9, ..., K16N9,
K1N10, K2N10, ..., K16N10,
...
K(k-15)Nn, K(k-14)Nn, ..., KkNn]
for RK3562
int8:
native layout: (N / 16, K / 32, 16, 32)
[K1N1, K2N1, ..., K32N1,
K1N2, K2N2, ..., K32N2,
...
K1N16, K2N16, ..., K32N16,
K33N1, K34N1, ..., K64N1,
K33N2, K34N2, ..., K64N2,
...
K(k-31)N16, K(k-30)N16, ..., KkN16,
K1N17, K2N17, ..., K32N17,
K1N18, K2N18, ..., K32N18,
...
K(k-31)Nn, K(k-30)Nn, ..., KkNn]
float16:
native layout: (N / 8, K / 32, 8, 32)
[K1N1, K2N1, ..., K32N1,
K1N2, K2N2, ..., K32N2,
...
K1N8, K2N8, ..., K32N8,
K33N1, K34N1, ..., K64N1,
K33N2, K34N2, ..., K64N2,
...
K(k-31)N8, K(k-30)N8, ..., KkN8,
K1N9, K2N9, ..., K16N9,
K1N10, K2N10, ..., K16N10,
...
K(k-31)Nn, K(k-30)Nn, ..., KkNn]
for RK3588
when K > 8192, the B data will be split into T segments.
int T = std::ceil(K / 8192);
For example: normal layout -> native layout
K = 20488, N = 4096, T = 3, the data will be split into 3 segments.
subN = rknn_matmul_io_attr.B.dims[2];
subK = rknn_matmul_io_attr.B.dims[3];
(8196, 4096) (4096 / subN, 8196 / subK, subN, subK)
(K, N) = (20488, 4096) -> (8196, 4096) -> (4096 / subN, 8196 / subK, subN, subK)
normal layout (4096, 4096) (4096 / subN, 4096 / subK, subN, subK)
T normal layout T native layout
It is recommended to use the rknn_B_normal_layout_to_native_layout interface for direct data conversion.
for RK3576
when K > 4096, the B data will be split into T segments.
int T = std::ceil(K / 4096);
For example: normal layout -> native layout
K = 10240, N = 2048, T = 3, the data will be split into 3 segments.
subN = rknn_matmul_io_attr.B.dims[2];
subK = rknn_matmul_io_attr.B.dims[3];
(4096, 2048) (2048 / subN, 4096 / subK, subN, subK)
(K, N) = (10240, 2048) -> (4096, 2048) -> (2048 / subN, 4096 / subK, subN, subK)
normal layout (2048, 2048) (2048 / subN, 2048 / subK, subN, subK)
T normal layout T native layout
It is recommended to use the rknn_B_normal_layout_to_native_layout interface for direct data conversion.
for RK3588/3576
int4:
native layout: (N / 64, K / 32, 64, 32)
[K1N1, K2N1, ..., K32N1,
K1N2, K2N2, ..., K32N2,
...
K1N64, K2N64, ..., K32N64,
K33N1, K34N1, ..., K64N1,
K33N2, K34N2, ..., K64N2,
...
K(k-31)N64, K(k-30)N64, ..., KkN64,
K1N65, K2N65, ..., K32N65,
K1N66, K2N66, ..., K32N66,
...
K(k-31)Nn, K(k-30)Nn, ..., KkNn]
int8:
native layout: (N / 32, K / 32, 32, 32)
[K1N1, K2N1, ..., K32N1,
K1N2, K2N2, ..., K32N2,
...
K1N32, K2N32, ..., K32N32,
K33N1, K34N1, ..., K64N1,
K33N2, K34N2, ..., K64N2,
...
K(k-31)N32, K(k-30)N32, ..., KkN32,
K1N33, K2N33, ..., K32N33,
K1N34, K2N34, ..., K32N34,
...
K(k-31)Nn, K(k-30)Nn, ..., KkNn]
float16:
native layout: (N / 16, K / 32, 16, 32)
[K1N1, K2N1, ..., K32N1,
K1N2, K2N2, ..., K32N2,
...
K1N16, K2N16, ..., K32N16,
K33N1, K34N1, ..., K64N1,
K33N2, K34N2, ..., K64N2,
...
K(k-31)N16, K(k-30)N16, ..., KkN16,
K1N17, K2N17, ..., K32N17,
K1N18, K2N18, ..., K32N18,
...
K(k-31)Nn, K(k-30)Nn, ..., KkNn]
C shape: M x N
normal layout: (M, N)
[M1N1, M1N2, ..., M1Nn,
M2N1, M2N2, ..., M2Nn,
...
MmN1, MmN2, ..., MmNn]
native layout: (N / 4, M, 4)
[N1M1, N2M1, ..., N4M1,
N5M2, N6M2, ..., N8M2,
...
N(n-3)Mm, N(n-2)Mm, ..., NnMm]
for RK3588
int4:
native layout: (N / 8, M, 8)
[N1M1, N2M1, ..., N8M1,
N9M2, N10M2, ..., N16M2,
...
N(n-7)Mm, N(n-6)Mm, ..., NnMm]
*/
int rknn_matmul_set_io_mem(rknn_matmul_ctx ctx, rknn_tensor_mem* mem, rknn_matmul_tensor_attr* attr);
/* rknn_matmul_set_core_mask
set rknn core mask.(only support RK3588 in current)
RKNN_NPU_CORE_AUTO: auto mode, default value
RKNN_NPU_CORE_0: core 0 mode
RKNN_NPU_CORE_1: core 1 mode
RKNN_NPU_CORE_2: core 2 mode
RKNN_NPU_CORE_0_1: combine core 0/1 mode
RKNN_NPU_CORE_0_1_2: combine core 0/1/2 mode
input:
rknn_matmul_ctx context the handle of context.
rknn_core_mask core_mask the core mask.
return:
int error code.
*/
int rknn_matmul_set_core_mask(rknn_matmul_ctx context, rknn_core_mask core_mask);
/* rknn_matmul_set_quant_params
set quant params.(only support matmul type RKNN_INT8_MM_INT8_TO_INT8, RKNN_INT8_MM_INT8_TO_INT32)
input:
rknn_matmul_ctx context the handle of context.
rknn_quant_params params quant params.
return:
int error code.
*/
int rknn_matmul_set_quant_params(rknn_matmul_ctx context, rknn_quant_params* params);
/* rknn_matmul_get_quant_params
get per channel quant params.(only support matmul type RKNN_INT8_MM_INT8_TO_INT32)
input:
rknn_matmul_ctx context the handle of context.
rknn_quant_params params quant params.
float scale get scale for user.
return:
int error code.
*/
int rknn_matmul_get_quant_params(rknn_matmul_ctx ctx, rknn_quant_params* params, float* scale);
/* rknn_matmul_set_dynamic_shape
set the matmul input/output shape. matmul will run under current input shape after rknn_matmul_set_dynamic_shape,
only support M dynamicly now.
input:
rknn_matmul_ctx ctx the handle of context.
rknn_matmul_shape* shape the M,K,N shape of matmul currently
return:
int error code.
*/
int rknn_matmul_set_dynamic_shape(rknn_matmul_ctx ctx, rknn_matmul_shape* shape);
/* rknn_matmul_run
run the matmul in blocking mode
params:
rknn_matmul_ctx ctx the handle of context.
return:
int error code.
*/
int rknn_matmul_run(rknn_matmul_ctx ctx);
/* rknn_matmul_destroy
destroy the matmul context
params:
rknn_matmul_ctx ctx the handle of context.
return:
int error code.
*/
int rknn_matmul_destroy(rknn_matmul_ctx ctx);
/* rknn_B_normal_layout_to_native_layout
change the B normal layout buffer to native layout buffer
params:
void* B_input B normal layout buffer.
void* B_output B native layout buffer.
int K K
int N N
rknn_matmul_info info matmul info
return:
int error code.
*/
int rknn_B_normal_layout_to_native_layout(void* B_input, void* B_output, int K, int N, rknn_matmul_info* info);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // _RKNN_MATMUL_API_H