1. 前言
GD32VW553是一款集成RISC-V处理器、2.4 GHz Wi-Fi、BLE、片上Flash、SRAM以及多种外设接口的无线微控制器。该芯片最高运行频率为160 MHz,并支持浮点和DSP扩展指令,为轻量级神经网络、无线通信与实时控制功能的融合提供了较好的硬件基础。基于这一特点,本项目以GD32VW553为核心主控,尝试在单芯片资源条件下完成从底层驱动、FreeRTOS任务调度、Wi-Fi网络接入,到HTTP交互、NMSIS-NN模型推理、ADC温度采集及MQTT状态发布的完整部署流程。
本评测报告将重点评估GD32VW553在资源受限环境下同时承载无线网络、Web服务、物联网通信和轻量级神经网络推理任务的可行性。
2. 环境配置
2.1 工具链
从文档上看,GD32VW553使用了nuclei N307内核,核心配置为:
CPU 架构:RISC-V RV32
主频:160 MHz
ABI:ilp32f
FPU:单精度浮点
DSP:Packed-SIMD / Xxldsp
指令扩展:
rv32imafc
zba / zbb / zbc / zbs
xxldsp
Debug: JTAG/cJTAG因此可以直接下载uclei工具链
编译参数为:
-march=rv32imafc_zba_zbb_zbc_zbs_xxldsp
-mabi=ilp32f其中,NMSIS-NN 主要利用定点运算、SIMD 指令和优化卷积内核提升神经网络推理效率。
2.2 OpenOCD调试器
GDBuilder使用OpenOCD,实现代码的烧录以及调试。仿照其cfg,我们可以使用OpenOCD,直接将其移植到VSCode中
#
# GigaDevice GD32VW55x target
#
adapter driver cmsis-dap
adapter speed 4000
reset_config trst_only
adapter srst pulse_width 100
transport select jtag
set _CHIPNAME riscv
jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10307a6d
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME riscv -chain-position $_TARGETNAME
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size 20480 -work-area-backup 0
# Work-area is a space in RAM used for flash programming
if { [info exists WORKAREASIZE] } {
set _WORKAREASIZE $WORKAREASIZE
} else {
set _WORKAREASIZE 0x5000
}
# Allow overriding the Flash bank size
if { [info exists FLASH_SIZE] } {
set _FLASH_SIZE $FLASH_SIZE
} else {
# autodetect size
set _FLASH_SIZE 0
}
# flash size will be probed
set _FLASHNAME $_CHIPNAME.flash
flash bank $_FLASHNAME gd32vw55x 0x08000000 0x400000 0 0 $_TARGETNAME
riscv set_reset_timeout_sec 1
init
halt{
"version": "2.0.0",
"tasks": [
{
"label": "flash",
"type": "shell",
"command": "D:/Projects/gd32/workspace/gd32vm553/riscv_toolchains/openocd_1.0.2.202606101542/bin/openocd.exe",
"args": [
"-f", "openocd_gdlink.cfg",
"-c", "halt; flash write_image erase build/gcc-release/edge_ai_web.elf; verify_image build/gcc-release/edge_ai_web.elf; reset run; exit"
],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
}
]
}将JTAG连接到调试器,测试一下代码的下载与调试
硬件连线

PS: > D:/Projects/gd32/workspace/gd32vm553/riscv_toolchains/openocd_1.0.2.202606101542/bin/openocd.exe -f openocd_gdlink.cfg -c 'halt; flash write_image erase build/gcc-release/edge_ai_web.elf; verify_image build/gcc-release/edge_ai_web.elf; reset run; exit'
xPack Open On-Chip Debugger 0.12.0+dev1.14.1-01850-geb6f2745b-dirty (2026-06-09-13:04)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : CMSIS-DAP: SWD supported
Info : CMSIS-DAP: JTAG supported
Info : CMSIS-DAP: Atomic commands supported
Info : CMSIS-DAP: FW Version = 0254
Info : CMSIS-DAP: Serial# = 110136024420312043525138313030313932303697969903
Info : CMSIS-DAP: Interface Initialised (JTAG)
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 1 TDO = 0 nTRST = 0 nRESET = 1
Info : CMSIS-DAP: Interface ready
Info : clock speed 4000 kHz
Info : cmsis-dap JTAG TLR_RESET
Info : cmsis-dap JTAG TLR_RESET
Info : JTAG tap: riscv.cpu tap/device found: 0x10307a6d (mfg: 0x536 (Nuclei System Technology Co Ltd), part: 0x0307, ver: 0x1)
Info : JTAG tap: auto0.tap tap/device found: 0x790007a3 (mfg: 0x3d1 (GigaDevice Semiconductor (Beijing)), part: 0x9000, ver: 0x7)
Warn : AUTO auto0.tap - use "jtag newtap auto0 tap -irlen 5 -expected-id 0x790007a3"
Info : datacount=4 progbufsize=2
Info : Examined RISC-V core; found 1 harts
Info : hart 0: XLEN=32, misa=0x4010912f
Info : [riscv.cpu] Examination succeed
Info : [riscv.cpu] starting gdb server on 3333
Info : Listening on port 3333 for gdb connections
Info : device id = 0x23030418
Warn : gd32vw55x flash size failed, probe inaccurate - assuming 4096k flash
Info : ignoring flash probed value, using configured bank size
Info : flash size = 4096kbytes
Info : GD32: Flash erase ... sector erase(0 to 121)
Info : fmc_obstat_reg = 100
Info : erase ok
Info : GD32: Flash write ... not words to write, padding with 0xff
Info : GD32: Flash write ... words to be prgrammed = 0x0001e801
Info : cmsis-dap JTAG TLR_RESET
Info : cmsis-dap JTAG TLR_RESET
Info : JTAG tap: riscv.cpu tap/device found: 0x10307a6d (mfg: 0x536 (Nuclei System Technology Co Ltd), part: 0x0307, ver: 0x1)
Info : JTAG tap: auto0.tap tap/device found: 0x790007a3 (mfg: 0x3d1 (GigaDevice Semiconductor (Beijing)), part: 0x9000, ver: 0x7)3. 代码实现
3.1 最小系统实现
由于官方 Wi-Fi/BLE SDK 包含大量示例和完整应用框架,对于边缘 AI 场景存在代码冗余。因此项目先对 SDK 进行裁剪,仅保留FreeRTOS,BSP,WiFI/LwIP层,移除BLE协议栈,最终的主应用实现如下:
#include <stdint.h>
#include "gd32vw55x.h"
#include "gd32vw55x_platform.h"
#include "wifi_init.h"
#include "wrapper_os.h"
#include "board.h"
#include "wifi_http_app.h"
#include "FreeRTOS.h"
#include "task.h"
static void led_task(void *argument)
{
(void)argument;
for (;;) {
board_led_toggle();
vTaskDelay(pdMS_TO_TICKS(500U));
}
}
static void monitor_task(void *argument)
{
(void)argument;
for (;;) {
board_uart_write("[rtos] uptime_ms=");
board_uart_write_u32(
(uint32_t)(xTaskGetTickCount() * portTICK_PERIOD_MS));
board_uart_write(" free_heap=");
board_uart_write_u32((uint32_t)xPortGetFreeHeapSize());
board_uart_write(" min_heap=");
board_uart_write_u32((uint32_t)xPortGetMinimumEverFreeHeapSize());
board_uart_write("\r\n");
vTaskDelay(pdMS_TO_TICKS(5000U));
}
}
static void fail_with_code(const char *stage, int code)
{
board_uart_write("[FATAL] ");
board_uart_write(stage);
board_uart_write(" failed, code=");
board_uart_write_i32((int32_t)code);
board_uart_write("\r\n");
edge_assert_failed(__FILE__, __LINE__, stage);
}
int main(void)
{
int ret;
/* Keep the same ECLIC grouping used by the vendor Wi-Fi image. */
eclic_priority_group_set(ECLIC_PRIGROUP_LEVEL4_PRIO0);
/* UART1 and the project LED are available for early diagnostics. */
board_init();
board_uart_write("\r\nedge_ai_web\r\n");
board_uart_write("SystemCoreClock=");
board_uart_write_u32(SystemCoreClock);
board_uart_write(" Hz\r\n");
sys_os_init();
board_uart_write("[platform] init...\r\n");
platform_init();
board_uart_write("[platform] ready\r\n");
board_uart_write("[wifi] init...\r\n");
ret = wifi_init();
if (ret != 0) {
fail_with_code("wifi_init", ret);
}
board_uart_write("[wifi] init accepted, free_heap=");
board_uart_write_u32((uint32_t)xPortGetFreeHeapSize());
board_uart_write("\r\n");
if (wifi_http_app_start() != 0) {
fail_with_code("wifi_http_app_start", -1);
}
if (xTaskCreate(led_task,
"led",
256U,
NULL,
1U,
NULL) != pdPASS) {
fail_with_code("xTaskCreate(led)", -1);
}
if (xTaskCreate(monitor_task,
"monitor",
384U,
NULL,
1U,
NULL) != pdPASS) {
fail_with_code("xTaskCreate(monitor)", -1);
}
board_uart_write("[rtos] scheduler start\r\n");
sys_os_start();
edge_assert_failed(__FILE__, __LINE__, "sys_os_start returned");
return 0;
}
3.1 Wi-Fi 局域网通信
参考SDK,我们将WiFi设置为STA模式,使其可以连接到无线局域网络
#include <stdint.h>
#include "FreeRTOS.h"
#include "task.h"
#include "board.h"
#include "http_server.h"
#include "mqtt_status.h"
#include "wifi_http_app.h"
#include "wifi_station_config.h"
#include "wifi_management.h"
#include "wifi_net_ip.h"
#include "wifi_init.h"
#include "wrapper_os_config.h"
#define EDGE_HTTP_STACK_WORDS 1536U
#define EDGE_HTTP_TASK_PRIO OS_TASK_PRIORITY(0)
#define EDGE_WIFI_VIF_DEFAULT 0
static char s_sta_ssid[] = EDGE_WIFI_STA_SSID;
static char s_sta_password[] = EDGE_WIFI_STA_PASSWORD;
static void log_result(const char *operation, int result)
{
board_uart_write(operation);
board_uart_write(" result=");
board_uart_write_i32((int32_t)result);
board_uart_write("\r\n");
}
static void log_ipv4(uint32_t address)
{
board_uart_write_u32(address & 0xFFU);
board_uart_write(".");
board_uart_write_u32((address >> 8) & 0xFFU);
board_uart_write(".");
board_uart_write_u32((address >> 16) & 0xFFU);
board_uart_write(".");
board_uart_write_u32((address >> 24) & 0xFFU);
}
static int connect_station(void)
{
struct wifi_ip_addr_cfg ip_config;
int result;
for (;;) {
board_uart_write("[wifi] connecting STA SSID: ");
board_uart_write(s_sta_ssid);
board_uart_write("\r\n");
result = wifi_management_connect(
s_sta_ssid,
(s_sta_password[0] != '\0') ? s_sta_password : NULL,
1U);
if (result == 0) {
result = wifi_get_vif_ip(EDGE_WIFI_VIF_DEFAULT, &ip_config);
if ((result == 0) && (ip_config.ipv4.addr != 0U)) {
board_uart_write("[wifi] STA connected\r\n");
board_uart_write("[wifi] IPv4: ");
log_ipv4(ip_config.ipv4.addr);
board_uart_write("\r\n[wifi] gateway: ");
log_ipv4(ip_config.ipv4.gw);
board_uart_write("\r\n[http] URL: http://");
log_ipv4(ip_config.ipv4.addr);
board_uart_write("/\r\n");
return 0;
}
log_result("[wifi] wifi_get_vif_ip", result);
} else {
log_result("[wifi] station connect", result);
}
board_uart_write("[wifi] reconnect in ms=");
board_uart_write_u32(EDGE_WIFI_STA_RECONNECT_PERIOD_MS);
board_uart_write("\r\n");
vTaskDelay(pdMS_TO_TICKS(EDGE_WIFI_STA_RECONNECT_PERIOD_MS));
}
}
static void wifi_http_task(void *argument)
{
int result;
(void)argument;
board_uart_write("[wifi] waiting for core tasks\r\n");
result = wifi_wait_ready();
if (result != 0) {
log_result("[wifi] wifi_wait_ready", result);
vTaskDelete(NULL);
return;
}
/* wifi_wait_ready() also exposes the 32 KiB shared SRAM heap region. */
board_uart_write("[wifi] core ready, free_heap=");
board_uart_write_u32((uint32_t)xPortGetFreeHeapSize());
board_uart_write("\r\n");
result = connect_station();
if (result != 0) {
log_result("[wifi] connect_station", result);
vTaskDelete(NULL);
return;
}
/* MQTT and HTTP start only after DHCP has supplied a usable LAN address. */
result = mqtt_status_start();
if (result != 0) {
log_result("[mqtt] mqtt_status_start", result);
}
result = http_server_run();
log_result("[http] server stopped", result);
(void)wifi_management_disconnect();
vTaskDelete(NULL);
}
int wifi_http_app_start(void)
{
BaseType_t result;
result = xTaskCreate(
wifi_http_task,
"wifi-http",
EDGE_HTTP_STACK_WORDS,
NULL,
EDGE_HTTP_TASK_PRIO,
NULL);
if (result != pdPASS) {
board_uart_write("[wifi] failed to create wifi-http task\r\n");
return -1;
}
return 0;
}
3.2 HTTP 可视化交互
设备内部使用静态网页实现一个简单的http server

#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "lwip/inet.h"
#include "lwip/sockets.h"
#include "board.h"
#include "cifar10_inference.h"
#include "http_server.h"
#include "mqtt_status.h"
extern const char g_index_html[];
extern const size_t g_index_html_size;
#define HTTP_HEADER_BUFFER_SIZE 1024U
#define HTTP_JSON_BUFFER_SIZE 768U
static uint8_t s_rgb_upload[CIFAR10_INPUT_BYTES];
static const char k_header_html[] =
"HTTP/1.0 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
"Cache-Control: no-store\r\n"
"Connection: close\r\n"
"\r\n";
static const char k_header_json[] =
"HTTP/1.0 200 OK\r\n"
"Content-Type: application/json; charset=utf-8\r\n"
"Cache-Control: no-store\r\n"
"Connection: close\r\n"
"\r\n";
static const char k_response_not_found[] =
"HTTP/1.0 404 Not Found\r\n"
"Content-Type: application/json; charset=utf-8\r\n"
"Cache-Control: no-store\r\n"
"Connection: close\r\n"
"\r\n"
"{\"error\":\"not_found\"}\r\n";
static const char k_response_bad_request[] =
"HTTP/1.0 400 Bad Request\r\n"
"Content-Type: application/json; charset=utf-8\r\n"
"Cache-Control: no-store\r\n"
"Connection: close\r\n"
"\r\n"
"{\"error\":\"expected_exactly_3072_bytes_rgb888\"}\r\n";
static const char k_response_server_error[] =
"HTTP/1.0 500 Internal Server Error\r\n"
"Content-Type: application/json; charset=utf-8\r\n"
"Cache-Control: no-store\r\n"
"Connection: close\r\n"
"\r\n"
"{\"error\":\"inference_failed\"}\r\n";
static const char k_model_json[] =
"{"
"\"model\":\"nmsis-cifar10-q7\","
"\"input\":{\"width\":32,\"height\":32,\"channels\":3,"
"\"format\":\"RGB888\",\"bytes\":3072},"
"\"classes\":[\"Plane\",\"Car\",\"Bird\",\"Cat\",\"Deer\","
"\"Dog\",\"Frog\",\"Horse\",\"Ship\",\"Truck\"]"
"}\r\n";
static void log_socket_error(const char *stage)
{
board_uart_write(stage);
board_uart_write(" errno=");
board_uart_write_i32((int32_t)errno);
board_uart_write("\r\n");
}
static int send_all(int socket_fd, const void *data, size_t size)
{
const uint8_t *cursor = (const uint8_t *)data;
while (size > 0U) {
int sent = send(socket_fd, cursor, size, 0);
if (sent <= 0) {
return -1;
}
cursor += (size_t)sent;
size -= (size_t)sent;
}
return 0;
}
static int starts_with(const uint8_t *data,
size_t data_size,
const char *text)
{
size_t text_size = strlen(text);
return (data_size >= text_size) &&
(memcmp(data, text, text_size) == 0);
}
static size_t find_header_end(const uint8_t *data, size_t size)
{
size_t i;
if (size < 4U) {
return 0U;
}
for (i = 0U; i + 3U < size; ++i) {
if ((data[i] == '\r') && (data[i + 1U] == '\n') &&
(data[i + 2U] == '\r') && (data[i + 3U] == '\n')) {
return i + 4U;
}
}
return 0U;
}
static uint8_t ascii_lower(uint8_t value)
{
if ((value >= (uint8_t)'A') && (value <= (uint8_t)'Z')) {
return (uint8_t)(value + ((uint8_t)'a' - (uint8_t)'A'));
}
return value;
}
static int header_name_matches(const uint8_t *line,
size_t line_size,
const char *name)
{
size_t i;
size_t name_size = strlen(name);
if (line_size < name_size) {
return 0;
}
for (i = 0U; i < name_size; ++i) {
if (ascii_lower(line[i]) != ascii_lower((uint8_t)name[i])) {
return 0;
}
}
return 1;
}
static int parse_content_length(const uint8_t *header,
size_t header_size,
size_t *content_length)
{
static const char k_name[] = "content-length:";
size_t line_start = 0U;
while (line_start < header_size) {
size_t line_end = line_start;
size_t cursor;
size_t value = 0U;
int have_digit = 0;
while ((line_end + 1U < header_size) &&
!((header[line_end] == '\r') &&
(header[line_end + 1U] == '\n'))) {
++line_end;
}
if (header_name_matches(header + line_start,
line_end - line_start,
k_name)) {
cursor = line_start + sizeof(k_name) - 1U;
while ((cursor < line_end) &&
((header[cursor] == ' ') || (header[cursor] == '\t'))) {
++cursor;
}
while ((cursor < line_end) &&
(header[cursor] >= '0') && (header[cursor] <= '9')) {
have_digit = 1;
value = (value * 10U) + (size_t)(header[cursor] - '0');
if (value > CIFAR10_INPUT_BYTES) {
return -1;
}
++cursor;
}
if (!have_digit) {
return -1;
}
*content_length = value;
return 0;
}
if (line_end + 2U > header_size) {
break;
}
line_start = line_end + 2U;
}
return -1;
}
static int recv_http_header(int client_fd,
uint8_t *buffer,
size_t capacity,
size_t *received_size,
size_t *header_end)
{
size_t total = 0U;
while (total < capacity) {
int received = recv(client_fd,
buffer + total,
capacity - total,
0);
if (received <= 0) {
return -1;
}
total += (size_t)received;
*header_end = find_header_end(buffer, total);
if (*header_end != 0U) {
*received_size = total;
return 0;
}
}
return -1;
}
static int recv_exact_body(int client_fd,
const uint8_t *initial_body,
size_t initial_size,
uint8_t *destination,
size_t expected_size)
{
size_t copied = initial_size;
if (copied > expected_size) {
copied = expected_size;
}
if (copied > 0U) {
memcpy(destination, initial_body, copied);
}
while (copied < expected_size) {
int received = recv(client_fd,
destination + copied,
expected_size - copied,
0);
if (received <= 0) {
return -1;
}
copied += (size_t)received;
}
return 0;
}
static int append_char(char *buffer,
size_t capacity,
size_t *length,
char value)
{
if (*length + 1U >= capacity) {
return -1;
}
buffer[*length] = value;
++(*length);
buffer[*length] = '\0';
return 0;
}
static int append_text(char *buffer,
size_t capacity,
size_t *length,
const char *text)
{
while (*text != '\0') {
if (append_char(buffer, capacity, length, *text) != 0) {
return -1;
}
++text;
}
return 0;
}
static int append_u32(char *buffer,
size_t capacity,
size_t *length,
uint32_t value)
{
char digits[10];
size_t count = 0U;
do {
digits[count++] = (char)('0' + (value % 10U));
value /= 10U;
} while ((value != 0U) && (count < sizeof(digits)));
while (count > 0U) {
if (append_char(buffer,
capacity,
length,
digits[--count]) != 0) {
return -1;
}
}
return 0;
}
static int append_i32(char *buffer,
size_t capacity,
size_t *length,
int32_t value)
{
uint32_t magnitude;
if (value < 0) {
if (append_char(buffer, capacity, length, '-') != 0) {
return -1;
}
magnitude = (uint32_t)(-(value + 1)) + 1U;
} else {
magnitude = (uint32_t)value;
}
return append_u32(buffer, capacity, length, magnitude);
}
static int build_result_json(char *buffer,
size_t capacity,
const cifar10_result_t *result)
{
size_t length = 0U;
size_t i;
buffer[0] = '\0';
if ((append_text(buffer, capacity, &length,
"{\"model\":\"nmsis-cifar10-q7\",\"top1\":{") != 0) ||
(append_text(buffer, capacity, &length, "\"index\":") != 0) ||
(append_u32(buffer, capacity, &length, result->top1_index) != 0) ||
(append_text(buffer, capacity, &length, ",\"label\":\"") != 0) ||
(append_text(buffer, capacity, &length,
cifar10_label(result->top1_index)) != 0) ||
(append_text(buffer, capacity, &length, "\",\"score_q7\":") != 0) ||
(append_i32(buffer, capacity, &length, result->top1_score_q7) != 0) ||
(append_text(buffer, capacity, &length,
",\"probability_1e4\":") != 0) ||
(append_u32(buffer, capacity, &length,
result->top1_probability_1e4) != 0) ||
(append_text(buffer, capacity, &length,
"},\"latency_ms\":") != 0) ||
(append_u32(buffer, capacity, &length, result->latency_ms) != 0) ||
(append_text(buffer, capacity, &length, ",\"scores_q7\":[") != 0)) {
return -1;
}
for (i = 0U; i < CIFAR10_CLASS_COUNT; ++i) {
if ((i != 0U) &&
(append_char(buffer, capacity, &length, ',') != 0)) {
return -1;
}
if (append_i32(buffer,
capacity,
&length,
result->scores_q7[i]) != 0) {
return -1;
}
}
if (append_text(buffer, capacity, &length, "]}\r\n") != 0) {
return -1;
}
return (int)length;
}
static void serve_inference(int client_fd,
const uint8_t *header_buffer,
size_t received_size,
size_t header_end)
{
cifar10_result_t result;
char json[HTTP_JSON_BUFFER_SIZE];
size_t content_length = 0U;
size_t initial_body_size = received_size - header_end;
int json_size;
int infer_result;
if ((parse_content_length(header_buffer,
header_end,
&content_length) != 0) ||
(content_length != CIFAR10_INPUT_BYTES)) {
(void)send_all(client_fd,
k_response_bad_request,
sizeof(k_response_bad_request) - 1U);
return;
}
if (recv_exact_body(client_fd,
header_buffer + header_end,
initial_body_size,
s_rgb_upload,
CIFAR10_INPUT_BYTES) != 0) {
(void)send_all(client_fd,
k_response_bad_request,
sizeof(k_response_bad_request) - 1U);
return;
}
board_uart_write("[ai] CIFAR-10 inference start\r\n");
infer_result = cifar10_infer_rgb888(s_rgb_upload,
CIFAR10_INPUT_BYTES,
&result);
if (infer_result != 0) {
board_uart_write("[ai] inference failed code=");
board_uart_write_i32(infer_result);
board_uart_write("\r\n");
(void)send_all(client_fd,
k_response_server_error,
sizeof(k_response_server_error) - 1U);
return;
}
board_uart_write("[ai] top1=");
board_uart_write(cifar10_label(result.top1_index));
board_uart_write(" score_q7=");
board_uart_write_i32(result.top1_score_q7);
board_uart_write(" latency_ms=");
board_uart_write_u32(result.latency_ms);
board_uart_write("\r\n");
mqtt_status_record_inference(&result);
json_size = build_result_json(json, sizeof(json), &result);
if (json_size < 0) {
(void)send_all(client_fd,
k_response_server_error,
sizeof(k_response_server_error) - 1U);
return;
}
if (send_all(client_fd,
k_header_json,
sizeof(k_header_json) - 1U) == 0) {
(void)send_all(client_fd, json, (size_t)json_size);
}
}
static void serve_client(int client_fd)
{
uint8_t request[HTTP_HEADER_BUFFER_SIZE];
size_t received_size = 0U;
size_t header_end = 0U;
if (recv_http_header(client_fd,
request,
sizeof(request),
&received_size,
&header_end) != 0) {
return;
}
if (starts_with(request, header_end, "GET / ") ||
starts_with(request, header_end, "GET /index.html ")) {
if (send_all(client_fd,
k_header_html,
sizeof(k_header_html) - 1U) == 0) {
(void)send_all(client_fd, g_index_html, g_index_html_size);
}
return;
}
if (starts_with(request, header_end, "GET /api/model ")) {
if (send_all(client_fd,
k_header_json,
sizeof(k_header_json) - 1U) == 0) {
(void)send_all(client_fd,
k_model_json,
sizeof(k_model_json) - 1U);
}
return;
}
if (starts_with(request, header_end, "POST /api/infer ")) {
serve_inference(client_fd,
request,
received_size,
header_end);
return;
}
(void)send_all(client_fd,
k_response_not_found,
sizeof(k_response_not_found) - 1U);
}
int http_server_run(void)
{
int server_fd;
int reuse = 1;
struct sockaddr_in address;
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
log_socket_error("[http] socket failed");
return -1;
}
if (setsockopt(server_fd,
SOL_SOCKET,
SO_REUSEADDR,
&reuse,
sizeof(reuse)) != 0) {
log_socket_error("[http] SO_REUSEADDR failed");
close(server_fd);
return -2;
}
memset(&address, 0, sizeof(address));
address.sin_family = AF_INET;
address.sin_port = htons(80U);
address.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(server_fd,
(struct sockaddr *)&address,
sizeof(address)) != 0) {
log_socket_error("[http] bind failed");
close(server_fd);
return -3;
}
if (listen(server_fd, 2) != 0) {
log_socket_error("[http] listen failed");
close(server_fd);
return -4;
}
board_uart_write("[http] listening on TCP/80\r\n");
board_uart_write("[ai] model_bytes=");
board_uart_write_u32((uint32_t)cifar10_model_bytes());
board_uart_write(" workspace_bytes=");
board_uart_write_u32((uint32_t)cifar10_workspace_bytes());
board_uart_write("\r\n");
board_uart_write("[ai] POST /api/infer: 3072-byte RGB888\r\n");
for (;;) {
int client_fd = accept(server_fd, NULL, NULL);
if (client_fd < 0) {
log_socket_error("[http] accept failed");
continue;
}
serve_client(client_fd);
(void)shutdown(client_fd, SHUT_RDWR);
close(client_fd);
}
}
3.3 MQTT 状态上报
MQTT根据sdk参考示例改造,实现:内部温度采集;周期上传;远程状态监控。
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
#include "lwip/apps/mqtt.h"
#include "lwip/ip_addr.h"
#include "lwip/tcpip.h"
#include "board.h"
#include "cifar10_inference.h"
#include "mqtt_status.h"
#include "mqtt_status_config.h"
#include "wrapper_os_config.h"
#include "device_temperature.h"
/*
* GigaDevice's lwIP MQTT fork declares this symbol in mqtt.h and expects
* exactly one application-layer definition.
*/
int16_t connect_fail_reason = -1;
#define MQTT_JSON_BUFFER_SIZE 640U
#define MQTT_TOPIC_BUFFER_SIZE 96U
#define MQTT_QOS_TELEMETRY 0U
#define MQTT_RETAIN_EVENT 0U
#define MQTT_RETAIN_STATE 1U
#define MQTT_NOTIFY_INFERENCE (1UL << 0)
#define MQTT_NOTIFY_CONNECTED (1UL << 1)
static mqtt_client_t *s_client;
static TaskHandle_t s_task_handle;
static volatile uint8_t s_connected;
static volatile uint8_t s_connecting;
static volatile uint8_t s_started;
static cifar10_result_t s_latest_inference;
static uint32_t s_inference_sequence;
static uint8_t s_inference_valid;
static char s_client_id[] = EDGE_MQTT_CLIENT_ID;
static char s_username[] = EDGE_MQTT_USERNAME;
static char s_password[] = EDGE_MQTT_PASSWORD;
static char s_will_topic[] = EDGE_MQTT_TOPIC_ROOT "/availability";
static char s_will_message[] = "offline";
static struct mqtt_connect_client_info_t s_client_info;
static void append_char(char *buffer, size_t capacity, size_t *length, char value)
{
if ((*length + 1U) < capacity) {
buffer[*length] = value;
++(*length);
buffer[*length] = '\0';
}
}
static void append_text(char *buffer,
size_t capacity,
size_t *length,
const char *text)
{
if (text == NULL) {
return;
}
while (*text != '\0') {
append_char(buffer, capacity, length, *text);
++text;
}
}
static void append_u32(char *buffer,
size_t capacity,
size_t *length,
uint32_t value)
{
char digits[10];
size_t count = 0U;
do {
digits[count++] = (char)('0' + (value % 10U));
value /= 10U;
} while ((value != 0U) && (count < sizeof(digits)));
while (count > 0U) {
append_char(buffer, capacity, length, digits[--count]);
}
}
static void append_i32(char *buffer,
size_t capacity,
size_t *length,
int32_t value)
{
uint32_t magnitude;
if (value < 0) {
append_char(buffer, capacity, length, '-');
magnitude = (uint32_t)(-(int64_t)value);
} else {
magnitude = (uint32_t)value;
}
append_u32(buffer, capacity, length, magnitude);
}
static int make_topic(char *buffer,
size_t capacity,
const char *suffix)
{
size_t root_length = strlen(EDGE_MQTT_TOPIC_ROOT);
size_t suffix_length = strlen(suffix);
if ((root_length + 1U + suffix_length + 1U) > capacity) {
return -1;
}
memcpy(buffer, EDGE_MQTT_TOPIC_ROOT, root_length);
buffer[root_length] = '/';
memcpy(buffer + root_length + 1U, suffix, suffix_length);
buffer[root_length + 1U + suffix_length] = '\0';
return 0;
}
static err_t publish_payload(const char *topic,
const char *payload,
uint8_t retain)
{
err_t result;
if ((s_client == NULL) || (s_connected == 0U)) {
return ERR_CONN;
}
LOCK_TCPIP_CORE();
result = mqtt_msg_publish(s_client,
topic,
payload,
(u16_t)strlen(payload),
MQTT_QOS_TELEMETRY,
retain,
NULL,
NULL);
UNLOCK_TCPIP_CORE();
return result;
}
static void log_publish_error(const char *name, err_t result)
{
if (result != ERR_OK) {
board_uart_write("[mqtt] publish ");
board_uart_write(name);
board_uart_write(" failed=");
board_uart_write_i32((int32_t)result);
board_uart_write("\r\n");
}
}
static void append_scores(char *buffer,
size_t capacity,
size_t *length,
const int8_t *scores)
{
uint32_t index;
append_char(buffer, capacity, length, '[');
for (index = 0U; index < CIFAR10_CLASS_COUNT; ++index) {
if (index != 0U) {
append_char(buffer, capacity, length, ',');
}
append_i32(buffer, capacity, length, scores[index]);
}
append_char(buffer, capacity, length, ']');
}
static size_t build_inference_json(char *buffer,
size_t capacity,
const cifar10_result_t *result,
uint32_t sequence)
{
size_t length = 0U;
buffer[0] = '\0';
append_text(buffer, capacity, &length, "{\"sequence\":");
append_u32(buffer, capacity, &length, sequence);
append_text(buffer, capacity, &length, ",\"latency_ms\":");
append_u32(buffer, capacity, &length, result->latency_ms);
append_text(buffer, capacity, &length, ",\"index\":");
append_u32(buffer, capacity, &length, result->top1_index);
append_text(buffer, capacity, &length, ",\"label\":\"");
append_text(buffer, capacity, &length, cifar10_label(result->top1_index));
append_text(buffer, capacity, &length, "\",\"score_q7\":");
append_i32(buffer, capacity, &length, result->top1_score_q7);
append_text(buffer, capacity, &length, ",\"probability_1e4\":");
append_u32(buffer, capacity, &length, result->top1_probability_1e4);
append_text(buffer, capacity, &length, ",\"scores_q7\":");
append_scores(buffer, capacity, &length, result->scores_q7);
append_char(buffer, capacity, &length, '}');
return length;
}
static size_t build_temperature_json(char *buffer,
size_t capacity,
const device_temperature_sample_t *sample)
{
size_t length = 0U;
buffer[0] = '\0';
append_text(buffer, capacity, &length, "{\"adc_channel\":9,\"adc_raw\":");
append_u32(buffer, capacity, &length, sample->adc_raw);
append_text(buffer, capacity, &length, ",\"milli_celsius\":");
append_i32(buffer, capacity, &length, sample->milli_celsius);
append_text(buffer, capacity, &length, ",\"vref_channel\":10,\"vref_raw\":");
append_u32(buffer, capacity, &length, sample->vref_raw);
append_text(buffer, capacity, &length, ",\"vref_mv\":");
append_u32(buffer, capacity, &length, sample->vref_mv);
append_text(buffer, capacity, &length, ",\"vdda_mv\":");
append_u32(buffer, capacity, &length, EDGE_TEMPERATURE_VDDA_MV);
append_char(buffer, capacity, &length, '}');
return length;
}
static size_t build_status_json(char *buffer,
size_t capacity,
const device_temperature_sample_t *temperature,
int temperature_valid,
const cifar10_result_t *inference,
uint8_t inference_valid,
uint32_t sequence)
{
size_t length = 0U;
buffer[0] = '\0';
append_text(buffer, capacity, &length, "{\"uptime_ms\":");
append_u32(buffer,
capacity,
&length,
(uint32_t)(xTaskGetTickCount() * portTICK_PERIOD_MS));
append_text(buffer, capacity, &length, ",\"temperature\":");
if (temperature_valid != 0) {
append_text(buffer, capacity, &length, "{\"adc_channel\":9,\"adc_raw\":");
append_u32(buffer, capacity, &length, temperature->adc_raw);
append_text(buffer, capacity, &length, ",\"milli_celsius\":");
append_i32(buffer, capacity, &length, temperature->milli_celsius);
append_text(buffer, capacity, &length, ",\"vref_channel\":10,\"vref_raw\":");
append_u32(buffer, capacity, &length, temperature->vref_raw);
append_text(buffer, capacity, &length, ",\"vref_mv\":");
append_u32(buffer, capacity, &length, temperature->vref_mv);
append_text(buffer, capacity, &length, ",\"vdda_mv\":");
append_u32(buffer, capacity, &length, EDGE_TEMPERATURE_VDDA_MV);
append_char(buffer, capacity, &length, '}');
} else {
append_text(buffer, capacity, &length, "null");
}
append_text(buffer, capacity, &length, ",\"inference\":");
if (inference_valid != 0U) {
append_text(buffer, capacity, &length, "{\"sequence\":");
append_u32(buffer, capacity, &length, sequence);
append_text(buffer, capacity, &length, ",\"latency_ms\":");
append_u32(buffer, capacity, &length, inference->latency_ms);
append_text(buffer, capacity, &length, ",\"index\":");
append_u32(buffer, capacity, &length, inference->top1_index);
append_text(buffer, capacity, &length, ",\"label\":\"");
append_text(buffer, capacity, &length, cifar10_label(inference->top1_index));
append_text(buffer, capacity, &length, "\",\"score_q7\":");
append_i32(buffer, capacity, &length, inference->top1_score_q7);
append_text(buffer, capacity, &length, ",\"probability_1e4\":");
append_u32(buffer, capacity, &length, inference->top1_probability_1e4);
append_text(buffer, capacity, &length, ",\"scores_q7\":");
append_scores(buffer, capacity, &length, inference->scores_q7);
append_char(buffer, capacity, &length, '}');
} else {
append_text(buffer, capacity, &length, "null");
}
append_text(buffer, capacity, &length, ",\"heap_free\":");
append_u32(buffer, capacity, &length, (uint32_t)xPortGetFreeHeapSize());
append_text(buffer, capacity, &length, ",\"heap_min\":");
append_u32(buffer,
capacity,
&length,
(uint32_t)xPortGetMinimumEverFreeHeapSize());
append_char(buffer, capacity, &length, '}');
return length;
}
static void copy_latest_inference(cifar10_result_t *result,
uint8_t *valid,
uint32_t *sequence)
{
taskENTER_CRITICAL();
*result = s_latest_inference;
*valid = s_inference_valid;
*sequence = s_inference_sequence;
taskEXIT_CRITICAL();
}
static void mqtt_connection_callback(mqtt_client_t *client,
void *argument,
mqtt_connection_status_t status)
{
(void)client;
(void)argument;
s_connecting = 0U;
if (status == MQTT_CONNECT_ACCEPTED) {
s_connected = 1U;
} else {
s_connected = 0U;
}
if (s_task_handle != NULL) {
(void)xTaskNotify(s_task_handle,
MQTT_NOTIFY_CONNECTED,
eSetBits);
}
}
static err_t start_connection(void)
{
ip_addr_t broker_address;
err_t result;
if (ipaddr_aton(EDGE_MQTT_BROKER_IPV4, &broker_address) == 0) {
return ERR_ARG;
}
if (s_client == NULL) {
LOCK_TCPIP_CORE();
s_client = mqtt_client_new();
UNLOCK_TCPIP_CORE();
if (s_client == NULL) {
return ERR_MEM;
}
}
memset(&s_client_info, 0, sizeof(s_client_info));
s_client_info.client_id = s_client_id;
s_client_info.client_user =
(s_username[0] != '\0') ? s_username : NULL;
s_client_info.client_pass =
(s_password[0] != '\0') ? s_password : NULL;
s_client_info.keep_alive = EDGE_MQTT_KEEP_ALIVE_SECONDS;
s_client_info.will_topic = s_will_topic;
s_client_info.will_msg = s_will_message;
s_client_info.will_qos = MQTT_QOS_TELEMETRY;
s_client_info.will_retain = 1U;
s_client_info.clean_session_disabled = 0U;
s_connecting = 1U;
LOCK_TCPIP_CORE();
result = mqtt_client_connect(s_client,
&broker_address,
EDGE_MQTT_BROKER_PORT,
NULL,
mqtt_connection_callback,
NULL,
&s_client_info);
UNLOCK_TCPIP_CORE();
if (result != ERR_OK) {
s_connecting = 0U;
}
return result;
}
static void publish_availability(void)
{
char topic[MQTT_TOPIC_BUFFER_SIZE];
err_t result;
if (make_topic(topic, sizeof(topic), "availability") != 0) {
return;
}
result = publish_payload(topic, "online", MQTT_RETAIN_STATE);
log_publish_error("availability", result);
}
static void publish_inference(const cifar10_result_t *inference,
uint32_t sequence)
{
char topic[MQTT_TOPIC_BUFFER_SIZE];
char payload[MQTT_JSON_BUFFER_SIZE];
err_t result;
if (make_topic(topic, sizeof(topic), "inference") != 0) {
return;
}
(void)build_inference_json(payload,
sizeof(payload),
inference,
sequence);
result = publish_payload(topic, payload, MQTT_RETAIN_EVENT);
log_publish_error("inference", result);
}
static void publish_periodic_status(void)
{
char topic[MQTT_TOPIC_BUFFER_SIZE];
char payload[MQTT_JSON_BUFFER_SIZE];
device_temperature_sample_t temperature = {0};
cifar10_result_t inference;
uint32_t sequence;
uint8_t inference_valid;
int temperature_result;
err_t publish_result;
temperature_result = device_temperature_read(&temperature);
if (temperature_result != 0) {
board_uart_write("[temp] ADC9 read failed=");
board_uart_write_i32(temperature_result);
board_uart_write("\r\n");
}
copy_latest_inference(&inference, &inference_valid, &sequence);
if (make_topic(topic, sizeof(topic), "temperature") == 0) {
if (temperature_result == 0) {
(void)build_temperature_json(payload,
sizeof(payload),
&temperature);
publish_result = publish_payload(topic,
payload,
MQTT_RETAIN_STATE);
log_publish_error("temperature", publish_result);
}
}
if (make_topic(topic, sizeof(topic), "status") == 0) {
(void)build_status_json(payload,
sizeof(payload),
&temperature,
temperature_result == 0,
&inference,
inference_valid,
sequence);
publish_result = publish_payload(topic,
payload,
MQTT_RETAIN_STATE);
log_publish_error("status", publish_result);
}
}
static void mqtt_status_task(void *argument)
{
TickType_t next_status_tick;
TickType_t next_connect_tick = 0U;
uint32_t last_published_sequence = 0U;
uint32_t notification_value;
(void)argument;
s_task_handle = xTaskGetCurrentTaskHandle();
next_status_tick = xTaskGetTickCount();
board_uart_write("[mqtt] broker=");
board_uart_write(EDGE_MQTT_BROKER_IPV4);
board_uart_write(":");
board_uart_write_u32(EDGE_MQTT_BROKER_PORT);
board_uart_write("\r\n");
for (;;) {
TickType_t now = xTaskGetTickCount();
if ((s_connected == 0U) &&
(s_connecting == 0U) &&
((int32_t)(now - next_connect_tick) >= 0)) {
err_t result = start_connection();
if (result == ERR_OK) {
board_uart_write("[mqtt] connecting\r\n");
} else {
board_uart_write("[mqtt] connect start failed=");
board_uart_write_i32((int32_t)result);
board_uart_write("\r\n");
}
next_connect_tick =
now + pdMS_TO_TICKS(EDGE_MQTT_RECONNECT_PERIOD_MS);
}
notification_value = 0U;
(void)xTaskNotifyWait(0U,
UINT32_MAX,
¬ification_value,
pdMS_TO_TICKS(250U));
if ((notification_value & MQTT_NOTIFY_CONNECTED) != 0U) {
if (s_connected != 0U) {
board_uart_write("[mqtt] connected\r\n");
publish_availability();
next_status_tick = xTaskGetTickCount();
} else {
board_uart_write("[mqtt] disconnected\r\n");
next_connect_tick =
xTaskGetTickCount() +
pdMS_TO_TICKS(EDGE_MQTT_RECONNECT_PERIOD_MS);
}
}
if (s_connected != 0U) {
cifar10_result_t inference;
uint32_t sequence;
uint8_t valid;
/* The notification only shortens latency. Sequence polling also
* covers an inference recorded before the MQTT task was scheduled
* or while the broker was disconnected. */
copy_latest_inference(&inference, &valid, &sequence);
if ((valid != 0U) && (sequence != last_published_sequence)) {
publish_inference(&inference, sequence);
last_published_sequence = sequence;
}
}
now = xTaskGetTickCount();
if ((s_connected != 0U) &&
((int32_t)(now - next_status_tick) >= 0)) {
publish_periodic_status();
next_status_tick =
now + pdMS_TO_TICKS(EDGE_MQTT_STATUS_PERIOD_MS);
}
}
}
int mqtt_status_start(void)
{
BaseType_t result;
int temperature_result;
taskENTER_CRITICAL();
if (s_started != 0U) {
taskEXIT_CRITICAL();
return 0;
}
s_started = 1U;
taskEXIT_CRITICAL();
temperature_result = device_temperature_init();
if (temperature_result != 0) {
board_uart_write("[temp] ADC9 init failed=");
board_uart_write_i32(temperature_result);
board_uart_write("\r\n");
} else {
board_uart_write("[temp] ADC9 temperature + ADC10 VREFINT ready\r\n");
}
result = xTaskCreate(mqtt_status_task,
"mqtt-status",
EDGE_MQTT_TASK_STACK_WORDS,
NULL,
OS_TASK_PRIORITY(EDGE_MQTT_TASK_PRIORITY_OFFSET),
NULL);
if (result != pdPASS) {
taskENTER_CRITICAL();
s_started = 0U;
taskEXIT_CRITICAL();
board_uart_write("[mqtt] task create failed\r\n");
return -1;
}
return 0;
}
void mqtt_status_record_inference(const cifar10_result_t *result)
{
TaskHandle_t task_handle;
if (result == NULL) {
return;
}
taskENTER_CRITICAL();
s_latest_inference = *result;
++s_inference_sequence;
s_inference_valid = 1U;
task_handle = s_task_handle;
taskEXIT_CRITICAL();
if (task_handle != NULL) {
(void)xTaskNotify(task_handle,
MQTT_NOTIFY_INFERENCE,
eSetBits);
}
}
3.4 NMSIS-NN 神经网络推理
AI 部署采用官方 NMSIS-NN CIFAR10 示例。
#include "cifar10_inference.h"
#include <stddef.h>
#include <stdint.h>
#include "FreeRTOS.h"
#include "task.h"
#include "riscv_math.h"
#include "riscv_nnfunctions.h"
#include "cifar10_parameter.h"
#include "cifar10_weights.h"
/* Keep weights in Flash/rodata. The original standalone example omitted
* const, which would copy roughly 32 KiB of weights into RAM at startup. */
static const q7_t s_conv1_wt[
CONV1_IM_CH * CONV1_KER_DIM * CONV1_KER_DIM * CONV1_OUT_CH] = CONV1_WT;
static const q7_t s_conv1_bias[CONV1_OUT_CH] = CONV1_BIAS;
static const q7_t s_conv2_wt[
CONV2_IM_CH * CONV2_KER_DIM * CONV2_KER_DIM * CONV2_OUT_CH] = CONV2_WT;
static const q7_t s_conv2_bias[CONV2_OUT_CH] = CONV2_BIAS;
static const q7_t s_conv3_wt[
CONV3_IM_CH * CONV3_KER_DIM * CONV3_KER_DIM * CONV3_OUT_CH] = CONV3_WT;
static const q7_t s_conv3_bias[CONV3_OUT_CH] = CONV3_BIAS;
static const q7_t s_ip1_wt[IP1_DIM * IP1_OUT] = IP1_WT;
static const q7_t s_ip1_bias[IP1_OUT] = IP1_BIAS;
/* Exact workspaces used by the upstream example:
* activation scratch: 40 KiB
* im2col/FC scratch: 3.125 KiB
* Total: 43.125 KiB
*
* They are static so inference does not fragment or consume the Wi-Fi heap. */
static q7_t s_col_buffer[2U * 5U * 5U * 32U * 2U]
__attribute__((aligned(8)));
static q7_t s_scratch_buffer[32U * 32U * 10U * 4U]
__attribute__((aligned(8)));
static q7_t s_output_data[IP1_OUT];
static const char *const s_labels[CIFAR10_CLASS_COUNT] = {
"Plane", "Car", "Bird", "Cat", "Deer",
"Dog", "Frog", "Horse", "Ship", "Truck"
};
static int check_status(riscv_status status)
{
return (status == RISCV_MATH_SUCCESS) ? 0 : -1;
}
int cifar10_infer_rgb888(const uint8_t *rgb,
size_t rgb_size,
cifar10_result_t *result)
{
static const int32_t mean_data[3] = INPUT_MEAN_SHIFT;
static const uint32_t scale_data[3] = INPUT_RIGHT_SHIFT;
q7_t *img_buffer1;
q7_t *img_buffer2;
TickType_t tick_start;
TickType_t tick_end;
uint8_t top1_index;
int8_t top1_score;
size_t i;
if ((rgb == NULL) || (result == NULL) ||
(rgb_size != (size_t)CIFAR10_INPUT_BYTES)) {
return -1;
}
img_buffer1 = s_scratch_buffer;
img_buffer2 = img_buffer1 + (32U * 32U * 32U);
tick_start = xTaskGetTickCount();
/* RGB888 -> signed q7 input using the example model's mean/scale. */
for (i = 0U; i < (size_t)CIFAR10_INPUT_BYTES; i += 3U) {
uint32_t channel;
for (channel = 0U; channel < 3U; ++channel) {
int32_t value;
uint32_t shift;
shift = scale_data[channel];
value = ((int32_t)rgb[i + channel] - mean_data[channel]) << 7;
value += (int32_t)(1UL << (shift - 1U));
value >>= shift;
img_buffer2[i + channel] = (q7_t)__SSAT(value, 8U);
}
}
if (check_status(riscv_convolve_HWC_q7_RGB(
img_buffer2,
CONV1_IM_DIM,
CONV1_IM_CH,
s_conv1_wt,
CONV1_OUT_CH,
CONV1_KER_DIM,
CONV1_PADDING,
CONV1_STRIDE,
s_conv1_bias,
CONV1_BIAS_LSHIFT,
CONV1_OUT_RSHIFT,
img_buffer1,
CONV1_OUT_DIM,
(q15_t *)s_col_buffer,
NULL)) != 0) {
return -2;
}
riscv_relu_q7(img_buffer1,
CONV1_OUT_DIM * CONV1_OUT_DIM * CONV1_OUT_CH);
riscv_maxpool_q7_HWC(img_buffer1,
CONV1_OUT_DIM,
CONV1_OUT_CH,
POOL1_KER_DIM,
POOL1_PADDING,
POOL1_STRIDE,
POOL1_OUT_DIM,
NULL,
img_buffer2);
if (check_status(riscv_convolve_HWC_q7_fast(
img_buffer2,
CONV2_IM_DIM,
CONV2_IM_CH,
s_conv2_wt,
CONV2_OUT_CH,
CONV2_KER_DIM,
CONV2_PADDING,
CONV2_STRIDE,
s_conv2_bias,
CONV2_BIAS_LSHIFT,
CONV2_OUT_RSHIFT,
img_buffer1,
CONV2_OUT_DIM,
(q15_t *)s_col_buffer,
NULL)) != 0) {
return -3;
}
riscv_relu_q7(img_buffer1,
CONV2_OUT_DIM * CONV2_OUT_DIM * CONV2_OUT_CH);
riscv_maxpool_q7_HWC(img_buffer1,
CONV2_OUT_DIM,
CONV2_OUT_CH,
POOL2_KER_DIM,
POOL2_PADDING,
POOL2_STRIDE,
POOL2_OUT_DIM,
s_col_buffer,
img_buffer2);
if (check_status(riscv_convolve_HWC_q7_fast(
img_buffer2,
CONV3_IM_DIM,
CONV3_IM_CH,
s_conv3_wt,
CONV3_OUT_CH,
CONV3_KER_DIM,
CONV3_PADDING,
CONV3_STRIDE,
s_conv3_bias,
CONV3_BIAS_LSHIFT,
CONV3_OUT_RSHIFT,
img_buffer1,
CONV3_OUT_DIM,
(q15_t *)s_col_buffer,
NULL)) != 0) {
return -4;
}
riscv_relu_q7(img_buffer1,
CONV3_OUT_DIM * CONV3_OUT_DIM * CONV3_OUT_CH);
riscv_maxpool_q7_HWC(img_buffer1,
CONV3_OUT_DIM,
CONV3_OUT_CH,
POOL3_KER_DIM,
POOL3_PADDING,
POOL3_STRIDE,
POOL3_OUT_DIM,
s_col_buffer,
img_buffer2);
if (check_status(riscv_fully_connected_q7_opt(
img_buffer2,
s_ip1_wt,
IP1_DIM,
IP1_OUT,
IP1_BIAS_LSHIFT,
IP1_OUT_RSHIFT,
s_ip1_bias,
s_output_data,
(q15_t *)img_buffer1)) != 0) {
return -5;
}
riscv_softmax_q7(s_output_data, IP1_OUT, s_output_data);
tick_end = xTaskGetTickCount();
top1_index = 0U;
top1_score = s_output_data[0];
for (i = 0U; i < CIFAR10_CLASS_COUNT; ++i) {
result->scores_q7[i] = s_output_data[i];
if (s_output_data[i] > top1_score) {
top1_score = s_output_data[i];
top1_index = (uint8_t)i;
}
}
result->top1_index = top1_index;
result->top1_score_q7 = top1_score;
result->top1_probability_1e4 =
(uint16_t)(((uint32_t)(uint8_t)top1_score * 10000U + 63U) / 127U);
result->latency_ms =
(uint32_t)((tick_end - tick_start) * portTICK_PERIOD_MS);
return 0;
}
const char *cifar10_label(uint8_t index)
{
if (index >= CIFAR10_CLASS_COUNT) {
return "Unknown";
}
return s_labels[index];
}
size_t cifar10_workspace_bytes(void)
{
return sizeof(s_col_buffer) + sizeof(s_scratch_buffer) +
sizeof(s_output_data);
}
size_t cifar10_model_bytes(void)
{
return sizeof(s_conv1_wt) + sizeof(s_conv1_bias) +
sizeof(s_conv2_wt) + sizeof(s_conv2_bias) +
sizeof(s_conv3_wt) + sizeof(s_conv3_bias) +
sizeof(s_ip1_wt) + sizeof(s_ip1_bias);
}
4. 效果
系统初始化

MQTT EMQX Broker

MQTT消息

网页推理


单张图片推理约670ms左右
5. 小结
本项目在 GD32VW553 单芯片平台上实现了从底层驱动、实时操作系统、无线通信到神经网络推理和物联网遥测的完整系统。评测表明,GD32VW553 能够在资源受限的 MCU 环境中同时承载无线网络、Web 服务、MQTT 遥测和小型神经网络推理。

开源社区 


