/* * Minimal RK3588 NPU smoke test for RKNN Runtime 2.3.2. * * The program deliberately uses deterministic synthetic inputs. Its purpose is * to verify model loading, core selection, command submission, and finite * output, not the semantic accuracy of MobileNet. */ #include #include #include #include #include #include #include #include #include #include "rknn_api.h" #define DEFAULT_MODEL "/opt/rknn/current/share/models/rk3588/mobilenet_v1.rknn" #define MAX_TENSORS 64U static void usage(FILE *stream, const char *program) { fprintf(stream, "Usage: %s [--model PATH] [--core auto|0|1|2|all] " "[--iterations N]\n", program); } static int parse_core(const char *value, rknn_core_mask *mask) { if (strcmp(value, "auto") == 0) { *mask = RKNN_NPU_CORE_AUTO; } else if (strcmp(value, "0") == 0) { *mask = RKNN_NPU_CORE_0; } else if (strcmp(value, "1") == 0) { *mask = RKNN_NPU_CORE_1; } else if (strcmp(value, "2") == 0) { *mask = RKNN_NPU_CORE_2; } else if (strcmp(value, "all") == 0) { *mask = RKNN_NPU_CORE_0_1_2; } else { return -1; } return 0; } static int load_file(const char *path, void **buffer, uint32_t *size) { struct stat info; FILE *file = NULL; void *data = NULL; if (stat(path, &info) != 0) { fprintf(stderr, "cannot stat model %s: %s\n", path, strerror(errno)); return -1; } if (info.st_size <= 0 || (uint64_t)info.st_size > UINT32_MAX) { fprintf(stderr, "invalid model size: %jd\n", (intmax_t)info.st_size); return -1; } file = fopen(path, "rb"); if (file == NULL) { fprintf(stderr, "cannot open model %s: %s\n", path, strerror(errno)); return -1; } data = malloc((size_t)info.st_size); if (data == NULL) { fprintf(stderr, "cannot allocate %jd bytes for model\n", (intmax_t)info.st_size); fclose(file); return -1; } if (fread(data, 1, (size_t)info.st_size, file) != (size_t)info.st_size) { fprintf(stderr, "short read from model %s\n", path); free(data); fclose(file); return -1; } fclose(file); *buffer = data; *size = (uint32_t)info.st_size; return 0; } static double elapsed_ms(const struct timespec *start, const struct timespec *end) { double seconds = (double)(end->tv_sec - start->tv_sec) * 1000.0; double nanos = (double)(end->tv_nsec - start->tv_nsec) / 1000000.0; return seconds + nanos; } int main(int argc, char **argv) { const char *model_path = DEFAULT_MODEL; const char *core_name = "auto"; rknn_core_mask core_mask = RKNN_NPU_CORE_AUTO; unsigned long iterations = 1; void *model = NULL; uint32_t model_size = 0; rknn_context context = 0; rknn_sdk_version sdk_version; rknn_input_output_num io_count; rknn_tensor_attr *input_attrs = NULL; rknn_input *inputs = NULL; rknn_output *outputs = NULL; int outputs_acquired = 0; int context_created = 0; int result = EXIT_FAILURE; double total_ms = 0.0; uint32_t last_top_index = 0; float last_top_value = -INFINITY; int ret; uint32_t i; for (i = 1; i < (uint32_t)argc; ++i) { if (strcmp(argv[i], "--model") == 0 && i + 1U < (uint32_t)argc) { model_path = argv[++i]; } else if (strcmp(argv[i], "--core") == 0 && i + 1U < (uint32_t)argc) { core_name = argv[++i]; if (parse_core(core_name, &core_mask) != 0) { fprintf(stderr, "invalid core selector: %s\n", core_name); usage(stderr, argv[0]); return 2; } } else if (strcmp(argv[i], "--iterations") == 0 && i + 1U < (uint32_t)argc) { char *end = NULL; errno = 0; iterations = strtoul(argv[++i], &end, 10); if (errno != 0 || end == argv[i] || *end != '\0' || iterations == 0 || iterations > 1000) { fprintf(stderr, "iterations must be between 1 and 1000\n"); return 2; } } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { usage(stdout, argv[0]); return 0; } else { fprintf(stderr, "unknown or incomplete option: %s\n", argv[i]); usage(stderr, argv[0]); return 2; } } if (load_file(model_path, &model, &model_size) != 0) { goto cleanup; } ret = rknn_init(&context, model, model_size, 0, NULL); if (ret != RKNN_SUCC) { fprintf(stderr, "rknn_init failed: %d\n", ret); goto cleanup; } context_created = 1; ret = rknn_set_core_mask(context, core_mask); if (ret != RKNN_SUCC) { fprintf(stderr, "rknn_set_core_mask(%s) failed: %d\n", core_name, ret); goto cleanup; } memset(&sdk_version, 0, sizeof(sdk_version)); ret = rknn_query(context, RKNN_QUERY_SDK_VERSION, &sdk_version, sizeof(sdk_version)); if (ret != RKNN_SUCC) { fprintf(stderr, "RKNN_QUERY_SDK_VERSION failed: %d\n", ret); goto cleanup; } memset(&io_count, 0, sizeof(io_count)); ret = rknn_query(context, RKNN_QUERY_IN_OUT_NUM, &io_count, sizeof(io_count)); if (ret != RKNN_SUCC || io_count.n_input == 0 || io_count.n_output == 0 || io_count.n_input > MAX_TENSORS || io_count.n_output > MAX_TENSORS) { fprintf(stderr, "invalid RKNN input/output count (%u/%u), ret=%d\n", io_count.n_input, io_count.n_output, ret); goto cleanup; } input_attrs = calloc(io_count.n_input, sizeof(*input_attrs)); inputs = calloc(io_count.n_input, sizeof(*inputs)); outputs = calloc(io_count.n_output, sizeof(*outputs)); if (input_attrs == NULL || inputs == NULL || outputs == NULL) { fprintf(stderr, "cannot allocate tensor metadata\n"); goto cleanup; } for (i = 0; i < io_count.n_input; ++i) { uint32_t byte; input_attrs[i].index = i; ret = rknn_query(context, RKNN_QUERY_INPUT_ATTR, &input_attrs[i], sizeof(input_attrs[i])); if (ret != RKNN_SUCC || input_attrs[i].n_elems == 0) { fprintf(stderr, "query for input %u failed: %d\n", i, ret); goto cleanup; } inputs[i].index = i; inputs[i].size = input_attrs[i].n_elems; inputs[i].type = RKNN_TENSOR_UINT8; inputs[i].fmt = input_attrs[i].fmt == RKNN_TENSOR_UNDEFINED ? RKNN_TENSOR_NHWC : input_attrs[i].fmt; inputs[i].pass_through = 0; inputs[i].buf = malloc(inputs[i].size); if (inputs[i].buf == NULL) { fprintf(stderr, "cannot allocate input %u (%u bytes)\n", i, inputs[i].size); goto cleanup; } for (byte = 0; byte < inputs[i].size; ++byte) { ((uint8_t *)inputs[i].buf)[byte] = (uint8_t)((byte * 17U + i * 23U) & 0xffU); } } ret = rknn_inputs_set(context, io_count.n_input, inputs); if (ret != RKNN_SUCC) { fprintf(stderr, "rknn_inputs_set failed: %d\n", ret); goto cleanup; } for (i = 0; i < io_count.n_output; ++i) { outputs[i].index = i; outputs[i].want_float = 1; outputs[i].is_prealloc = 0; } for (unsigned long iteration = 0; iteration < iterations; ++iteration) { struct timespec start; struct timespec end; if (clock_gettime(CLOCK_MONOTONIC, &start) != 0) { fprintf(stderr, "clock_gettime failed: %s\n", strerror(errno)); goto cleanup; } ret = rknn_run(context, NULL); if (ret != RKNN_SUCC) { fprintf(stderr, "rknn_run failed at iteration %lu: %d\n", iteration, ret); goto cleanup; } ret = rknn_outputs_get(context, io_count.n_output, outputs, NULL); if (ret != RKNN_SUCC) { fprintf(stderr, "rknn_outputs_get failed at iteration %lu: %d\n", iteration, ret); goto cleanup; } outputs_acquired = 1; if (clock_gettime(CLOCK_MONOTONIC, &end) != 0) { fprintf(stderr, "clock_gettime failed: %s\n", strerror(errno)); goto cleanup; } total_ms += elapsed_ms(&start, &end); last_top_value = -INFINITY; last_top_index = 0; for (i = 0; i < io_count.n_output; ++i) { const float *values = outputs[i].buf; uint32_t count = outputs[i].size / (uint32_t)sizeof(float); uint32_t value_index; if (values == NULL || count == 0) { fprintf(stderr, "output %u is empty\n", i); goto cleanup; } for (value_index = 0; value_index < count; ++value_index) { if (!isfinite(values[value_index])) { fprintf(stderr, "output %u contains a non-finite value\n", i); goto cleanup; } if (i == 0 && values[value_index] > last_top_value) { last_top_value = values[value_index]; last_top_index = value_index; } } } ret = rknn_outputs_release(context, io_count.n_output, outputs); outputs_acquired = 0; if (ret != RKNN_SUCC) { fprintf(stderr, "rknn_outputs_release failed: %d\n", ret); goto cleanup; } } printf("RKNN_RESULT status=pass core=%s iterations=%lu avg_ms=%.3f " "top_index=%u top_value=%.7g api=%s driver=%s\n", core_name, iterations, total_ms / (double)iterations, last_top_index, last_top_value, sdk_version.api_version, sdk_version.drv_version); result = EXIT_SUCCESS; cleanup: if (outputs_acquired) { (void)rknn_outputs_release(context, io_count.n_output, outputs); } if (inputs != NULL) { for (i = 0; i < io_count.n_input; ++i) { free(inputs[i].buf); } } free(outputs); free(inputs); free(input_attrs); if (context_created) { (void)rknn_destroy(context); } free(model); return result; }