KL520_SDK_2.2/mdw/usb_companion/kdp2_usb_companion.c
2025-12-17 15:55:25 +08:00

517 lines
18 KiB
C

// #define ENABLE_DBG_LOG
#include <string.h>
#include "cmsis_os2.h"
#include "kmdw_power_manager.h"
#include "kmdw_console.h"
#include "kdrv_power.h"
#include "kdrv_scu_ext.h"
#include "kdp_system.h"
#include "usbd_hal.h"
#include "buffer_object.h"
#include "kmdw_fifoq_manager.h"
#include "kdp2_usb_companion.h"
#include "kdp2_ipc_cmd.h"
#ifdef ENABLE_DBG_LOG
#define dbg_log(__format__, ...) kmdw_printf("[kp companion]"__format__, ##__VA_ARGS__)
#else
#define dbg_log(__format__, ...)
#endif
#define FLAG_WAIT_USB_CONNECTION 0x1
static osThreadId_t result_thread_id = NULL;
static osThreadId_t image_thread_id = NULL;
static bool _do_reset_queue = false;
static bool _enable_inf_droppable = false;
static bool allocate_memory_for_inference_queue(uint32_t image_count, uint32_t image_size, uint32_t result_count, uint32_t result_size)
{
if (true == kmdw_fifoq_manager_get_fifoq_allocated())
return false; // already inited
uint32_t total_need_size = (image_count * image_size) + (result_count * result_size);
kmdw_printf("allocating memory for fifoq: image %d x %d, result %d x %d, total %u bytes\n", image_count, image_size, result_count, result_size, total_need_size);
uint32_t buf_addr = kmdw_ddr_reserve(total_need_size);
if (buf_addr > 0)
{
dbg_log("allocated fifoq buffers OK\n");
kmdw_fifoq_manager_store_fifoq_config(image_count, image_size, result_count, result_size);
osStatus_t sts;
// queue image and result buffers into queues correspondingly
for (uint32_t i = 0; i < image_count; i++)
{
sts = kmdw_fifoq_manager_image_put_free_buffer(buf_addr, (int)image_size, 0);
if (sts != osOK)
{
dbg_log("kmdw_fifoq_manager_image_put_free_buffer error = %d\n", sts);
}
buf_addr += image_size;
}
for (uint32_t i = 0; i < result_count; i++)
{
sts = kmdw_fifoq_manager_result_put_free_buffer(buf_addr, (int)result_size, 0);
if (sts != osOK)
{
dbg_log("kmdw_fifoq_manager_image_put_free_buffer error = %d\n", sts);
}
buf_addr += result_size;
}
return true;
}
else
{
kmdw_printf("error ! not enough memory for inference queue buffers\n");
return false;
}
}
// usb link status notify
void usb_link_status_callback(usbd_hal_link_status_t link_status)
{
switch (link_status)
{
case USBD_STATUS_DISCONNECTED:
kmdw_printf("USB is disconnected\n");
break;
case USBD_STATUS_CONFIGURED:
kmdw_printf("USB is connected\n");
osThreadFlagsSet(image_thread_id, FLAG_WAIT_USB_CONNECTION);
break;
}
}
// vendor-specific control transfer setup packet notify
static bool usb_user_control_callback(usbd_hal_setup_packet_t *setup)
{
bool ret = false;
dbg_log("control bRequest = 0x%x\n", setup->bRequest);
switch (setup->bRequest)
{
case KDP2_CONTROL_REBOOT:
{
dbg_log("control reboot\n");
kdrv_power_sw_reset();
break;
}
case KDP2_CONTROL_SHUTDOWN:
{
dbg_log("control shutdown\n");
kmdw_power_manager_shutdown();
break;
}
case KDP2_CONTROL_FIFOQ_RESET:
{
dbg_log("control fifoq reset\n");
if (true == kmdw_fifoq_manager_get_fifoq_allocated())
{
_do_reset_queue = true;
// also reset any inf defaults
_enable_inf_droppable = false;
usbd_hal_terminate_all_endpoint();
}
ret = true;
break;
}
case KDP2_CONTROL_FIFOQ_CONFIGURE:
{
if (true == kmdw_fifoq_manager_get_fifoq_allocated())
break; // already inited
uint16_t arg1_image = setup->wValue;
uint16_t arg2_result = setup->wIndex;
uint32_t image_count = (arg1_image & 0x7) + 1; // lower 3 bits for number of image, 1~8
uint32_t image_size = (10 * 1024) * (uint32_t)((arg1_image >> 3) + 1); // higher 13 bits for image buffer size in 10KB, 10KB~80MB
uint32_t result_count = (arg2_result & 0x7) + 1; // lower 3 bits for number of image, 1~8
uint32_t result_size = (10 * 1024) * (uint32_t)((arg2_result >> 3) + 1); // higher 13 bits for image buffer size in 10KB, 10KB~80MB
ret = allocate_memory_for_inference_queue(image_count, image_size, result_count, result_size);
break;
}
case KDP2_CONTROL_FIFOQ_ENABLE_DROPPABLE:
{
_enable_inf_droppable = (setup->wValue == 1);
ret = true;
break;
}
case KDP2_CONTROL_DDR_HEAP_BOUNDARY_ADJUST:
{
uint32_t arg1 = (uint32_t)setup->wValue;
uint32_t arg2 = (uint32_t)setup->wIndex;
uint32_t boundary_addr = (arg1 << 16) | arg2;
ret = (0 == kmdw_ddr_set_ddr_boundary(boundary_addr));
break;
}
case KDP2_CONTROL_REBOOT_SYSTEM:
{
dbg_log("control reboot system\n");
kdrv_power_sw_reset();
break;
}
default:
ret = false;
break;
}
return ret;
}
static void process_plus_command(uint32_t cmd_buf)
{
kp_inference_header_stamp_t *header_stamp = (kp_inference_header_stamp_t *)cmd_buf;
if (header_stamp->magic_type == KDP2_MAGIC_TYPE_COMMAND)
{
dbg_log("handle kdp2 command = 0x%x\n", header_stamp->job_id);
// handle kdp2 commands ...
kdp2_cmd_handle_kp_command(cmd_buf);
}
else if ((header_stamp->magic_type & 0xFFFF) == KDP_MSG_HDR_CMD) // very speical case for old arch. fw update
{
// handle legendary kdp commands, should be as few as possible
dbg_log("handle legendary kdp command = 0x%x\n", header_stamp->job_id);
kdp2_cmd_handle_legend_kdp_command(cmd_buf);
}
else
{
dbg_log("[%s] error ! received un-recognized buffer beginning with incorrect magic_type 0x%x\n", __FUNCTION__, header_stamp->magic_type);
}
}
enum
{
RECV_TYPE_INF_IMAGE = 1,
RECV_TYPE_COMMAND = 2,
RECV_TYPE_UNKNOWN = 3,
};
void kdp2_usb_companion_image_thread(void *arg)
{
dbg_log("[%s] starting ..\n", __FUNCTION__);
uint32_t temp_cmd_buffer = 0;
uint32_t temp_cmd_buffer_size = 0;
kmdw_ddr_get_system_reserve(&temp_cmd_buffer, &temp_cmd_buffer_size);
// this is due to "static thread allocation", refer to task_handler.h
image_thread_id = osThreadGetId();
if (image_thread_id == NULL)
kmdw_printf("%s creation failed !\n", __FUNCTION__);
// wait until usb connection is established
osThreadFlagsWait(FLAG_WAIT_USB_CONNECTION, osFlagsWaitAny, osWaitForever);
// run infinitely
while (1)
{
uint32_t buf_addr; // contains a inference image or a command
int buf_size; // buffer size should bigger than inference image size
// take a free buffer to receive a inf image or a command
if (true == kmdw_fifoq_manager_get_fifoq_allocated()) {
osStatus_t sts = kmdw_fifoq_manager_image_get_free_buffer(&buf_addr, &buf_size, osWaitForever, _enable_inf_droppable);
while (_enable_inf_droppable && osErrorResource == sts)
{
sts = kmdw_fifoq_manager_image_get_free_buffer(&buf_addr, &buf_size, osWaitForever, _enable_inf_droppable);
}
} else {
buf_addr = temp_cmd_buffer;
buf_size = temp_cmd_buffer_size;
dbg_log("[%s] got system reserve buffer: 0x%X size %d\n", __FUNCTION__, buf_addr, buf_size);
}
dbg_log("[%s] free queue --> buf 0x%X size %d\n", __FUNCTION__, buf_addr, buf_size);
uint32_t total_recv_len = 0;
uint32_t total_wanted_len = 0;
int recv_type = RECV_TYPE_UNKNOWN;
uint32_t total_image_count = 0;
uint32_t image_index = 0;
// loop done when receiving size-matched data
while (1)
{
uint32_t txLen = buf_size - total_recv_len;
kdrv_status_t usb_sts = usbd_hal_bulk_receive(KDP2_USB_ENDPOINT_DATA_OUT, (uint32_t *)(buf_addr + total_recv_len), &txLen, osWaitForever);
if (usb_sts != KDRV_STATUS_OK) // KDRV_STATUS_USBD_TRANSFER_TERMINATED or KDRV_STATUS_USBD_TRANSFER_DISCONNECTED
{
dbg_log("[%s] bulk receive is terminated, sts %d\n", __FUNCTION__, usb_sts);
// guess this is good time to clear/reset queue
if (_do_reset_queue)
{
dbg_log("[%s] do reset fifo queue !!!\n", __FUNCTION__);
_do_reset_queue = false;
// abandon all unprocessed data
kmdw_fifoq_manager_clean_queues();
// sending enpoint may still hold a buffer, terminate it
usbd_hal_terminate_all_endpoint();
recv_type = RECV_TYPE_UNKNOWN;
break;
}
}
if (total_recv_len == 0) // buffer should begin with header stamp 'magic_type'
{
kp_inference_header_stamp_t *header_stamp = (kp_inference_header_stamp_t *)buf_addr;
if (header_stamp->magic_type == KDP2_MAGIC_TYPE_INFERENCE)
{
recv_type = RECV_TYPE_INF_IMAGE; // indicate this buffer contains inf image
total_wanted_len = header_stamp->total_size;
total_image_count = header_stamp->total_image;
image_index = header_stamp->image_index;
if ((buf_addr == temp_cmd_buffer) && (true == kmdw_fifoq_manager_get_fifoq_allocated())) {
osStatus_t sts = kmdw_fifoq_manager_image_get_free_buffer(&buf_addr, &buf_size, osWaitForever, _enable_inf_droppable);
while (_enable_inf_droppable && osErrorResource == sts) {
sts = kmdw_fifoq_manager_image_get_free_buffer(&buf_addr, &buf_size, osWaitForever, _enable_inf_droppable);
}
memcpy((void *)buf_addr, (void *)temp_cmd_buffer, txLen);
dbg_log("[%s] swap data from system reserve buffer to fifoq buffer: 0x%X\n", __FUNCTION__, buf_addr);
}
if (header_stamp->total_size > buf_size) {
// FIXME, serious error, make host SW pending (timeout)
// reboot and configure bigger buffer !!!!
kmdw_printf("[%s] error !! inf image size (%d) is bigger than buffer size (%d)\n", __FUNCTION__, total_wanted_len, buf_size);
return; // a way to inform host SW ?
}
}
else
{
recv_type = RECV_TYPE_COMMAND; // indicate this buffer contains a command (if no failuire)
total_wanted_len = txLen; // assume this is a command (or not if failure)
}
}
else
{
// extra check for inference image data
kp_inference_header_stamp_t *header_stamp = (kp_inference_header_stamp_t *)(buf_addr + total_recv_len);
if (header_stamp->magic_type == KDP2_MAGIC_TYPE_COMMAND ||
header_stamp->magic_type == KDP2_MAGIC_TYPE_COMMAND)
{
// this should not happen
kmdw_printf("[%s] error !! receiving inf image corrupted ! start over.\n", __FUNCTION__);
recv_type = RECV_TYPE_UNKNOWN;
}
}
total_recv_len += txLen;
dbg_log("[%s] host -- usb --> buf 0x%x len %d (total %d)\n", __FUNCTION__, (void *)buf_addr, txLen, total_recv_len);
if (total_recv_len == total_wanted_len)
break; // recv done
else if (total_recv_len > total_wanted_len)
{
kmdw_printf("[%s] warning !! actual received size (%d) is bigger than expected size (%d)\n", __FUNCTION__, total_recv_len, total_wanted_len);
break;
}
}
if (recv_type == RECV_TYPE_INF_IMAGE)
{
dbg_log("[%s] buf 0x%x -- > inference queue\n", __FUNCTION__, (void *)buf_addr);
kmdw_fifoq_manager_image_enqueue(total_image_count, image_index, buf_addr, buf_size, osWaitForever, false);
}
else if (recv_type == RECV_TYPE_COMMAND)
{
dbg_log("[%s] buf 0x%x -- > command handler\n", __FUNCTION__, (void *)buf_addr);
process_plus_command(buf_addr);
// as the buffer is not put into inference queue, return it back to free buffer queue
if (temp_cmd_buffer != buf_addr)
kmdw_fifoq_manager_image_put_free_buffer(buf_addr, buf_size, osWaitForever);
}
else
{
// invalid data !! ignore it !
dbg_log("[%s] XXXXXX buf 0x%x -- > free buffer queue\n", __FUNCTION__, (void *)buf_addr);
if (temp_cmd_buffer != buf_addr)
kmdw_fifoq_manager_image_put_free_buffer(buf_addr, buf_size, osWaitForever);
}
}
}
void kdp2_usb_companion_result_thread(void *arg)
{
bool bRunning_dbg = false;
dbg_log("[%s] starting ..\n", __FUNCTION__);
result_thread_id = osThreadGetId();
if (result_thread_id == NULL)
kmdw_printf("%s creation failed !\n", __FUNCTION__);
while (1)
{
uint32_t buf_addr;
int buf_size;
// get result data from queue blocking wait
kmdw_fifoq_manager_result_dequeue(&buf_addr, &buf_size, osWaitForever);
kp_inference_header_stamp_t *header_stamp = (kp_inference_header_stamp_t *)buf_addr;
dbg_log("[%s] buf 0x%x len %d -- usb --> host\n", __FUNCTION__, (void *)buf_addr, header_stamp->total_size);
// kp inference debug code
if(bRunning_dbg && header_stamp->magic_type != KDP2_MAGIC_TYPE_CHECKPOINT_DATA)
{
// send a signal notify the end of dbg process
kp_inference_header_stamp_t dbg_ending;
dbg_ending.magic_type = KDP2_MAGIC_TYPE_CHECKPOINT_DATA;
dbg_ending.total_size = sizeof(kp_inference_header_stamp_t);
dbg_ending.job_id = 0;
dbg_ending.status_code = KP_SUCCESS;
usbd_hal_bulk_send(KDP2_USB_ENDPOINT_DATA_IN, (void *)&dbg_ending, dbg_ending.total_size, osWaitForever);
bRunning_dbg = false;
}
// send result to the host, blocking wait
kdrv_status_t usb_sts = usbd_hal_bulk_send(KDP2_USB_ENDPOINT_DATA_IN, (void *)buf_addr, header_stamp->total_size, osWaitForever);
if (usb_sts != KDRV_STATUS_OK) // KDRV_STATUS_USBD_TRANSFER_TERMINATED or KDRV_STATUS_USBD_TRANSFER_DISCONNECTED
{
dbg_log("[%s] bulk send is terminated, sts %d\n", __FUNCTION__, usb_sts);
}
// kp inference debug code
if(header_stamp->magic_type == KDP2_MAGIC_TYPE_CHECKPOINT_DATA)
{
bRunning_dbg = true;
header_stamp->status_code = 1; // to let ncpu go on
continue;
}
#ifdef AUTOTEST /* CI_PACK_REMOVE_START */
if(header_stamp->magic_type == KDP2_MAGIC_TYPE_JSON)
{
// FIXME: figure out a better way to find offset from kdp2_ipc_json_result_t
uint32_t offset = *(uint32_t *)(buf_addr + sizeof(kp_inference_header_stamp_t) + 8);
buf_addr -= offset;
}
#endif /* CI_PACK_REMOVE_END */
// return free buf back to queue
kmdw_fifoq_manager_result_put_free_buffer(buf_addr, buf_size, osWaitForever);
}
}
////////////////////////////////////////////////////////////
// for usb-boot, flash-boot, jtag-boot, kdp2 loader
#define JTAG_MAGIC_ADDRESS 0x10100000
#define CONTROL_BYPASS_ADDRESS 0x10100004
#define KDP2_BOOT_CONFIG_ADDRESS 0x10100100
#define RECOVERY_MARK_POS (SdRAM_MEM_BASE + SdRAM_MEM_SIZE - 64)
#define JTAG_MAGIC_VALUE 0xFEDCBA01
#define BOOT_FROM_FLASH 0xA
typedef struct
{
uint32_t boot_type; // 0xA = flash-boot, others = usb-boot
uint8_t loader_ver[4]; // fw loader version numbers
uint8_t scpu_fw_ver[4]; // SCPU fw version numbers
uint8_t ncpu_fw_ver[4]; // NCPU fw version numbers
} kdp2_boot_config_t;
////////////////////////////////////////////////////////////
int kdp2_usb_companion_init()
{
// retrieve real serial number here from efuse
// then convert it to hex string format
uint32_t uid = 0;
uid = kdp_sys_get_kn_number();
int32_t sidx = 0;
uint8_t kn_num_string[32] = {0};
for (int i = 7; i >= 0; i--)
{
uint32_t hex = (uid >> i * 4) & 0xF;
kn_num_string[sidx] = (hex < 10) ? '0' + hex : 'A' + (hex - 10);
sidx += 2;
}
// Companion Mode
uint16_t bcdDevice = KP_KDP2_FW_COMPANION_MODE;
if (*((uint32_t *)JTAG_MAGIC_ADDRESS) == JTAG_MAGIC_VALUE)
{
kmdw_printf("FW is running in JTAG mode\n");
bcdDevice |= KP_KDP2_FW_JTAG_TYPE;
}
else
{
kdp2_boot_config_t *bConfig = (kdp2_boot_config_t *)KDP2_BOOT_CONFIG_ADDRESS;
if (bConfig->boot_type == BOOT_FROM_FLASH)
{
kmdw_printf("KDP2 FW is running in flash-boot mode\n");
bcdDevice |= KP_KDP2_FW_FLASH_TYPE;
kmdw_printf("boot ncpu fw from flash\n");
SCU_EXTREG_CM4_NCPU_CTRL_SET_wakeup(1); // run ncpu
}
else
{
kmdw_printf("KDP2 FW is running in usb-boot mode\n");
bcdDevice |= KP_KDP2_FW_USB_TYPE;
}
}
// this is about recovery mode
*(uint32_t *)RECOVERY_MARK_POS = 0;
usbd_hal_initialize(kn_num_string, bcdDevice, usb_link_status_callback, usb_user_control_callback);
usbd_hal_set_enable(true);
// wow ! fifoq can also handle command
kdp2_cmd_handler_initialize();
#ifdef FIFIOQ_LOG_VIA_USB
kdp2_usb_log_initialize();
#endif
return 0;
}