/***************************************************************************
 *
 * Copyright 2015-2019 BES.
 * All rights reserved. All unpublished rights reserved.
 *
 * No part of this work may be used or reproduced in any form or by any
 * means, or stored in a database or retrieval system, without prior written
 * permission of BES.
 *
 * Use of this work is governed by a license granted by BES.
 * This work contains confidential and proprietary information of
 * BES. which is protected by copyright, trade secret,
 * trademark and other intellectual property rights.
 *
 ****************************************************************************/
#if !(defined(DEBUG) || defined(REL_TRACE_ENABLE))
// Implement a local copy of dummy trace functions for library linking (which might be built with DEBUG enabled)
#define TRACE_FUNC_SPEC
#endif
#include "hal_trace.h"
#include "cmsis_nvic.h"
#ifdef RTOS
#include "cmsis_os.h"
#endif
#include "hal_cmu.h"
#include "hal_chipid.h"
#include "hal_codec.h"
#include "hal_dma.h"
#include "hal_iomux.h"
#include "hal_location.h"
#include "hal_memsc.h"
#include "hal_sysfreq.h"
#include "hal_timer.h"
#include "hal_uart.h"
#include "stdarg.h"
#include "stdio.h"
#include "string.h"
#include "hal_bootmode.h"

#ifdef CORE_DUMP
#include "CrashCatcherApi.h"
#endif

extern const char sys_build_info[];
extern void nv_record_flash_flush(void);

#ifdef FAULT_DUMP
void hal_trace_fault_dump(const uint32_t *regs, const uint32_t *extra, uint32_t extra_len);
#ifndef __ARM_ARCH_ISA_ARM
static void hal_trace_fault_handler(void);
#endif
#endif

#if !(defined(ROM_BUILD) || defined(PROGRAMMER))
#define ASSERT_MUTE_CODEC
#define CRASH_DUMP_ENABLE
#if !(defined(NO_TRACE_TIME_STAMP) || defined(AUDIO_DEBUG_V0_1_0))
#define TRACE_TIME_STAMP
#endif
#if (defined(DUMP_LOG_ENABLE) || defined(DUMP_CRASH_ENABLE))
#define TRACE_TO_APP
#endif
#ifdef CHIP_HAS_CP
#define CP_TRACE_ENABLE
#endif
#endif

#define TRACE_IDLE_OUTPUT               0

#ifndef TRACE_BAUD_RATE
#define TRACE_BAUD_RATE                  (921600)
#endif

#ifndef TRACE_BUF_SIZE
#ifdef AUDIO_DEBUG
#define TRACE_BUF_SIZE                  (6 * 1024)
#else
#define TRACE_BUF_SIZE                  (4 * 1024)
#endif
#endif

#define CRASH_BUF_SIZE                  100
#define CRASH_BUF_ATTR                  ALIGNED(4) USED

#ifndef TRACE_STACK_DUMP_PREV_WORD
#define TRACE_STACK_DUMP_PREV_WORD      16
#endif
#ifndef TRACE_STACK_DUMP_WORD
#define TRACE_STACK_DUMP_WORD           32
#endif
#ifndef TRACE_BACKTRACE_NUM
#define TRACE_BACKTRACE_NUM             20
#endif
#ifndef TRACE_BACKTRACE_SEARCH_WORD
#define TRACE_BACKTRACE_SEARCH_WORD     1024
#endif

#define STACK_DUMP_CNT_PER_LEN          4
#define STACK_DUMP_CNT_PREV             ((TRACE_STACK_DUMP_PREV_WORD + STACK_DUMP_CNT_PER_LEN - 1) / STACK_DUMP_CNT_PER_LEN * STACK_DUMP_CNT_PER_LEN)
#define STACK_DUMP_CNT                  ((TRACE_STACK_DUMP_WORD + STACK_DUMP_CNT_PER_LEN - 1) / STACK_DUMP_CNT_PER_LEN * STACK_DUMP_CNT_PER_LEN)

#define TRACE_FLUSH_TIMEOUT             MS_TO_TICKS(2000)

#define TRACE_NEAR_FULL_THRESH          200

#define TRACE_CRLF 

#ifdef TRACE_CRLF
#define NEW_LINE_STR                    "\r\n"
#else
#define NEW_LINE_STR                    "\n"
#endif

#define HAL_TRACE_ASSERT_ID             0xBE57AAAA
#define HAL_TRACE_EXCEPTION_ID          0xBE57EEEE

#define HAL_MEMSC_ID_TRACE              HAL_MEMSC_ID_0

#define TRACE_BUF_LOC                   SYNC_FLAGS_LOC

struct ASSERT_INFO_T {
    uint32_t ID;
    uint32_t CPU_ID;
    const char *FILE;
    const char *FUNC;
    uint32_t LINE;
    const char *FMT;
    uint32_t R[15];
#ifndef __ARM_ARCH_ISA_ARM
    uint32_t MSP;
    uint32_t PSP;
    uint32_t CONTROL;
#ifdef __ARM_ARCH_8M_MAIN__
    uint32_t MSPLIM;
    uint32_t PSPLIM;
#endif
#endif
};

struct EXCEPTION_INFO_T {
    uint32_t ID;
    uint32_t CPU_ID;
    const uint32_t *REGS;
#ifdef __ARM_ARCH_ISA_ARM
    const uint32_t *extra;
    uint32_t extra_len;
#else
    uint32_t MSP;
    uint32_t PSP;
    uint8_t PRIMASK;
    uint8_t FAULTMASK;
    uint8_t BASEPRI;
    uint8_t CONTROL;
    uint32_t ICSR;
    uint32_t AIRCR;
    uint32_t SCR;
    uint32_t CCR;
    uint32_t SHCSR;
    uint32_t CFSR;
    uint32_t HFSR;
    uint32_t AFSR;
    uint32_t MMFAR;
    uint32_t BFAR;
#ifdef __ARM_ARCH_8M_MAIN__
    uint32_t MSPLIM;
    uint32_t PSPLIM;
#endif
#endif
};

static CRASH_BUF_ATTR char crash_buf[CRASH_BUF_SIZE];

STATIC_ASSERT(sizeof(crash_buf) >= sizeof(((struct ASSERT_INFO_T *)0)->R), "crash_buf too small to hold assert registers");

#if (defined(DEBUG) || defined(REL_TRACE_ENABLE))

struct HAL_TRACE_BUF_T {
    unsigned char buf[TRACE_BUF_SIZE];
    unsigned short wptr;
    unsigned short rptr;
#if (TRACE_IDLE_OUTPUT == 0)
    unsigned short sends[2];
#endif
    unsigned short discards;
    bool sending;
    bool in_trace;
    bool wrapped;
};

STATIC_ASSERT(TRACE_BUF_SIZE < (1 << (8 * sizeof(((struct HAL_TRACE_BUF_T *)0)->wptr))), "TRACE_BUF_SIZE is too large to fit in wptr/rptr variable");

static const struct HAL_UART_CFG_T uart_cfg = {
    .parity = HAL_UART_PARITY_NONE,
    .stop = HAL_UART_STOP_BITS_1,
    .data = HAL_UART_DATA_BITS_8,
    .flow = HAL_UART_FLOW_CONTROL_NONE,//HAL_UART_FLOW_CONTROL_RTSCTS,
    .tx_level = HAL_UART_FIFO_LEVEL_1_2,
    .rx_level = HAL_UART_FIFO_LEVEL_1_2,
    .baud = TRACE_BAUD_RATE,
#ifdef HAL_TRACE_RX_ENABLE
    .dma_rx = true,
#else
    .dma_rx = false,
#endif
#if (TRACE_IDLE_OUTPUT == 0)
    .dma_tx = true,
#else
    .dma_tx = false,
#endif
    .dma_rx_stop_on_err = false,
};

#if (TRACE_IDLE_OUTPUT == 0)
static const enum HAL_DMA_PERIPH_T uart_periph[] = {
    HAL_GPDMA_UART0_TX,
#if (CHIP_HAS_UART > 1)
    HAL_GPDMA_UART1_TX,
#endif
#if (CHIP_HAS_UART > 2)
    HAL_GPDMA_UART2_TX,
#endif
};

static const struct HAL_UART_CFG_T uart_rx_enable_cfg = {
    .parity = HAL_UART_PARITY_NONE,
    .stop = HAL_UART_STOP_BITS_1,
    .data = HAL_UART_DATA_BITS_8,
    .flow = HAL_UART_FLOW_CONTROL_NONE,//HAL_UART_FLOW_CONTROL_RTSCTS,
    .tx_level = HAL_UART_FIFO_LEVEL_1_2,
    .rx_level = HAL_UART_FIFO_LEVEL_1_2,
    .baud = TRACE_BAUD_RATE,
    .dma_rx = true,

#if (TRACE_IDLE_OUTPUT == 0)
    .dma_tx = true,
#else
    .dma_tx = false,
#endif
    .dma_rx_stop_on_err = false,
};

static struct HAL_DMA_CH_CFG_T dma_cfg;
TRACE_BUF_LOC static struct HAL_DMA_DESC_T dma_desc[2];
#endif

static enum HAL_TRACE_TRANSPORT_T trace_transport = HAL_TRACE_TRANSPORT_QTY;
static enum HAL_UART_ID_T trace_uart;

TRACE_BUF_LOC
static struct HAL_TRACE_BUF_T trace;

POSSIBLY_UNUSED
static const char newline[] = NEW_LINE_STR;

static const char discards_prefix[] = NEW_LINE_STR "LOST ";
static const uint32_t max_discards = 99999;
// 5 digits + "\r\n" = 7 chars
static char discards_buf[sizeof(discards_prefix) - 1 + 7];
static const unsigned char discards_digit_start = sizeof(discards_prefix) - 1;

static bool crash_dump_onprocess = false;

#ifdef CRASH_DUMP_ENABLE
static HAL_TRACE_CRASH_DUMP_CB_T crash_dump_cb_list[HAL_TRACE_CRASH_DUMP_MODULE_END];
static bool crash_handling;
#ifdef TRACE_TO_APP
static HAL_TRACE_APP_NOTIFY_T app_notify_cb = NULL;
static HAL_TRACE_APP_OUTPUT_T app_output_cb = NULL;
static HAL_TRACE_APP_OUTPUT_T app_crash_custom_cb = NULL;
static bool app_output_enabled =
#if defined(DUMP_LOG_ENABLE)
    true;
#else
    false;
#endif
#endif // TRACE_TO_APP
#endif // CRASH_DUMP_ENABLE
#ifdef CP_TRACE_ENABLE
static HAL_TRACE_APP_NOTIFY_T cp_notify_cb = NULL;
static HAL_TRACE_BUF_CTRL_T cp_buffer_cb = NULL;
#endif

#ifdef AUDIO_DEBUG_V0_1_0
static const char trace_head_buf[] = "[trace]";
#endif

static enum LOG_LEVEL_T trace_max_level;
static uint32_t trace_mod_map[(LOG_MODULE_QTY + 31) / 32];

static bool hal_trace_is_uart_transport(enum HAL_TRACE_TRANSPORT_T transport)
{
    if (transport == HAL_TRACE_TRANSPORT_UART0
#if (CHIP_HAS_UART > 1)
            || transport == HAL_TRACE_TRANSPORT_UART1
#endif
#if (CHIP_HAS_UART > 2)
            || transport == HAL_TRACE_TRANSPORT_UART2
#endif
            ) {
        return true;
    }
    return false;
}

#if (TRACE_IDLE_OUTPUT == 0)

static void hal_trace_uart_send(void)
{
    uint32_t wptr, rptr;
    uint32_t sends[2];
    uint32_t lock;

    lock = int_lock();

    wptr = trace.wptr;
    rptr = trace.rptr;

    // There is a race condition if we do not check s/w flag, but only check the h/w status.
    // [e.g., hal_gpdma_chan_busy(dma_cfg.ch)]
    // When the DMA is done, but DMA IRQ handler is still pending due to interrupt lock
    // or higher priority IRQ, it will have a chance to send the same content twice.
    if (!trace.sending && wptr != rptr) {
        trace.sending = true;

        sends[1] = 0;
        if (wptr > rptr) {
            sends[0] = wptr - rptr;
        } else {
            sends[0] = TRACE_BUF_SIZE - rptr;
            if (sends[0] <= HAL_DMA_MAX_DESC_XFER_SIZE) {
                sends[1] = wptr;
            }
        }
        if (sends[0] > HAL_DMA_MAX_DESC_XFER_SIZE) {
            sends[1] = sends[0] - HAL_DMA_MAX_DESC_XFER_SIZE;
            sends[0] = HAL_DMA_MAX_DESC_XFER_SIZE;
        }
        if (sends[1] > HAL_DMA_MAX_DESC_XFER_SIZE) {
            sends[1] = HAL_DMA_MAX_DESC_XFER_SIZE;
        }

        dma_cfg.src = (uint32_t)&trace.buf[rptr];
        if (sends[1] == 0) {
            dma_cfg.src_tsize = sends[0];
            hal_gpdma_init_desc(&dma_desc[0], &dma_cfg, NULL, 1);
        } else {
            dma_cfg.src_tsize = sends[0];
            hal_gpdma_init_desc(&dma_desc[0], &dma_cfg, &dma_desc[1], 0);

            if (rptr + sends[0] < TRACE_BUF_SIZE) {
                dma_cfg.src = (uint32_t)&trace.buf[rptr + sends[0]];
            } else {
                dma_cfg.src = (uint32_t)&trace.buf[0];
            }
            dma_cfg.src_tsize = sends[1];
            hal_gpdma_init_desc(&dma_desc[1], &dma_cfg, NULL, 1);
        }
        trace.sends[0] = sends[0];
        trace.sends[1] = sends[1];

        hal_gpdma_sg_start(&dma_desc[0], &dma_cfg);
    }

    int_unlock(lock);
}

static void hal_trace_uart_xfer_done(uint8_t chan, uint32_t remain_tsize, uint32_t error, struct HAL_DMA_DESC_T *lli)
{
    uint32_t sends[2];
    uint32_t lock;

    lock = int_lock();

    sends[0] = trace.sends[0];
    sends[1] = trace.sends[1];

    if (error) {
        if (lli || sends[1] == 0) {
            if (sends[0] > remain_tsize) {
                sends[0] -= remain_tsize;
            } else {
                sends[0] = 0;
            }
            sends[1] = 0;
        } else {
            if (sends[1] > remain_tsize) {
                sends[1] -= remain_tsize;
            } else {
                sends[1] = 0;
            }
        }
    }

    trace.rptr += sends[0] + sends[1];
    if (trace.rptr >= TRACE_BUF_SIZE) {
        trace.rptr -= TRACE_BUF_SIZE;
    }
    trace.sends[0] = 0;
    trace.sends[1] = 0;
    trace.sending = false;

    hal_trace_uart_send();

    int_unlock(lock);
}

static void hal_trace_send(void)
{
#ifdef CP_TRACE_ENABLE
    if (get_cpu_id()) {
        return;
    }
#endif

    if (hal_trace_is_uart_transport(trace_transport)) {
        hal_trace_uart_send();
    }
}

#else // TRACE_IDLE_OUTPUT

static void hal_trace_uart_idle_send(void)
{
    int i;
    uint32_t lock;
    unsigned short wptr, rptr;

    lock = int_lock();
    wptr = trace.wptr;
    rptr = trace.rptr;
    int_unlock(lock);

    if (wptr == rptr) {
        return;
    }

    if (wptr < rptr) {
        for (i = rptr; i < TRACE_BUF_SIZE; i++) {
            hal_uart_blocked_putc(trace_uart, trace.buf[i]);
        }
        rptr = 0;
    }

    for (i = rptr; i < wptr; i++) {
        hal_uart_blocked_putc(trace_uart, trace.buf[i]);
    }

    trace.rptr = wptr;
    if (trace.rptr >= TRACE_BUF_SIZE) {
        trace.rptr -= TRACE_BUF_SIZE;
    }
}

void hal_trace_idle_send(void)
{
    if (hal_trace_is_uart_transport(trace_transport)) {
        hal_trace_uart_idle_send();
    }
}

#endif // TRACE_IDLE_OUTPUT

int hal_trace_open(enum HAL_TRACE_TRANSPORT_T transport)
{
    int ret;

    crash_dump_onprocess = false;

#if (CHIP_HAS_UART > 1)
#ifdef FORCE_TRACE_UART1
    transport = HAL_TRACE_TRANSPORT_UART1;
#endif
#endif

#if (CHIP_HAS_UART > 2)
#ifdef FORCE_TRACE_UART2
    transport = HAL_TRACE_TRANSPORT_UART2;
#endif
#endif

    if (transport >= HAL_TRACE_TRANSPORT_QTY) {
        return 1;
    }
#ifdef CHIP_HAS_USB
    if (transport == HAL_TRACE_TRANSPORT_USB) {
        return 1;
    }
#endif

    if (trace_transport != HAL_TRACE_TRANSPORT_QTY) {
        return hal_trace_switch(transport);
    }

    trace_max_level = LOG_LEVEL_INFO;
    for (int i = 0; i < ARRAY_SIZE(trace_mod_map); i++) {
        trace_mod_map[i] = ~0;
    }

    memcpy(discards_buf, discards_prefix, discards_digit_start);

    trace.wptr = 0;
    trace.rptr = 0;
    trace.discards = 0;
    trace.sending = false;
    trace.in_trace = false;
    trace.wrapped = false;

    if (hal_trace_is_uart_transport(transport)) {
        trace_uart = HAL_UART_ID_0 + (transport - HAL_TRACE_TRANSPORT_UART0);
        ret = hal_uart_open(trace_uart, &uart_cfg);
        if (ret) {
            return ret;
        }

#if (TRACE_IDLE_OUTPUT == 0)
        trace.sends[0] = 0;
        trace.sends[1] = 0;

        memset(&dma_cfg, 0, sizeof(dma_cfg));
        dma_cfg.dst = 0; // useless
        dma_cfg.dst_bsize = HAL_DMA_BSIZE_8;
        dma_cfg.dst_periph = uart_periph[trace_uart - HAL_UART_ID_0];
        dma_cfg.dst_width = HAL_DMA_WIDTH_BYTE;
        dma_cfg.handler = hal_trace_uart_xfer_done;
        dma_cfg.src_bsize = HAL_DMA_BSIZE_32;
        dma_cfg.src_periph = 0; // useless
        dma_cfg.src_width = HAL_DMA_WIDTH_BYTE;
        dma_cfg.type = HAL_DMA_FLOW_M2P_DMA;
        dma_cfg.try_burst = 0;
        dma_cfg.ch = hal_gpdma_get_chan(dma_cfg.dst_periph, HAL_DMA_HIGH_PRIO);

        ASSERT(dma_cfg.ch != HAL_DMA_CHAN_NONE, "Failed to get DMA channel");
#endif
    }

#ifdef FAULT_DUMP
#ifdef __ARM_ARCH_ISA_ARM
    GIC_SetFaultDumpHandler(hal_trace_fault_dump);
#else
    NVIC_SetDefaultFaultHandler(hal_trace_fault_handler);
#endif
#endif

    trace_transport = transport;

#ifdef HAL_TRACE_RX_ENABLE
    hal_trace_rx_open();
#endif

    // Show build info
    static const char dbl_new_line[] = NEW_LINE_STR NEW_LINE_STR;
    hal_trace_output((unsigned char *)dbl_new_line, sizeof(dbl_new_line));
    hal_trace_output((unsigned char *)sys_build_info, strlen(sys_build_info)+1);

    char buf[50];
    int len;
    len = snprintf(buf, sizeof(buf),
        NEW_LINE_STR NEW_LINE_STR "------" NEW_LINE_STR "METAL_ID: %d" NEW_LINE_STR "------" NEW_LINE_STR NEW_LINE_STR,
        hal_get_chip_metal_id());
    hal_trace_output((unsigned char *)buf, len+1);

    return 0;
}

int hal_trace_switch(enum HAL_TRACE_TRANSPORT_T transport)
{
    uint32_t POSSIBLY_UNUSED lock;
    int ret;

#if (CHIP_HAS_UART > 1)
#ifdef FORCE_TRACE_UART1
    transport = HAL_TRACE_TRANSPORT_UART1;
#endif
#endif

#if (CHIP_HAS_UART > 2)
#ifdef FORCE_TRACE_UART2
    transport = HAL_TRACE_TRANSPORT_UART2;
#endif
#endif

#ifdef CHIP_HAS_USB
    if (transport == HAL_TRACE_TRANSPORT_USB) {
        return 1;
    }
#endif
    if (transport >= HAL_TRACE_TRANSPORT_QTY) {
        return 1;
    }
    if (trace_transport >= HAL_TRACE_TRANSPORT_QTY) {
        return 1;
    }
    if (trace_transport == transport) {
        return 0;
    }

    ret = 0;

#if (CHIP_HAS_UART > 1)

    lock = int_lock();

    if (hal_trace_is_uart_transport(trace_transport)) {
#if (TRACE_IDLE_OUTPUT == 0)
        if (dma_cfg.ch != HAL_DMA_CHAN_NONE) {
            hal_gpdma_cancel(dma_cfg.ch);
        }
#endif
        hal_uart_close(trace_uart);
    }

    if (hal_trace_is_uart_transport(transport)) {
        trace_uart = HAL_UART_ID_0 + (transport - HAL_TRACE_TRANSPORT_UART0);
#if (TRACE_IDLE_OUTPUT == 0)
        dma_cfg.dst_periph = uart_periph[trace_uart - HAL_UART_ID_0];
        trace.sends[0] = 0;
        trace.sends[1] = 0;
#endif
        ret = hal_uart_open(trace_uart, &uart_cfg);
        if (ret) {
#if (TRACE_IDLE_OUTPUT == 0)
            hal_gpdma_free_chan(dma_cfg.ch);
            dma_cfg.ch = HAL_DMA_CHAN_NONE;
#endif
            trace_transport = HAL_TRACE_TRANSPORT_QTY;
            goto _exit;
        }
    }

    trace.sending = false;

    trace_transport = transport;

_exit:
    int_unlock(lock);

#endif // CHIP_HAS_UART > 1

    return ret;
}

int hal_trace_close(void)
{
    if (trace_transport >= HAL_TRACE_TRANSPORT_QTY) {
        goto _exit;
    }
#ifdef CHIP_HAS_USB
    if (trace_transport == HAL_TRACE_TRANSPORT_USB) {
        goto _exit;
    }
#endif

    if (hal_trace_is_uart_transport(trace_transport)) {
#if (TRACE_IDLE_OUTPUT == 0)
        if (dma_cfg.ch != HAL_DMA_CHAN_NONE) {
            hal_gpdma_cancel(dma_cfg.ch);
            hal_gpdma_free_chan(dma_cfg.ch);
            dma_cfg.ch = HAL_DMA_CHAN_NONE;
        }
#endif
        hal_uart_close(trace_uart);
    }

_exit:
    trace_transport = HAL_TRACE_TRANSPORT_QTY;

    return 0;
}

int hal_trace_enable_log_module(enum LOG_MODULE_T module)
{
    if (module >= LOG_MODULE_QTY) {
        return 1;
    }

    trace_mod_map[module >> 5] |= (1 << (module & 0x1F));
    return 0;
}

int hal_trace_disable_log_module(enum LOG_MODULE_T module)
{
    if (module >= LOG_MODULE_QTY) {
        return 1;
    }

    trace_mod_map[module >> 5] &= ~(1 << (module & 0x1F));
    return 0;
}

int hal_trace_set_log_module(const uint32_t *map, uint32_t word_cnt)
{
    if (map == NULL || word_cnt == 0) {
        return 1;
    }

    if (word_cnt > ARRAY_SIZE(trace_mod_map)) {
        word_cnt = ARRAY_SIZE(trace_mod_map);
    }
    for (int i = 0; i < word_cnt; i++) {
        trace_mod_map[i] = map[i];
    }
    return 0;
}

int hal_trace_set_log_level(enum LOG_LEVEL_T level)
{
    if (level >= LOG_LEVEL_QTY) {
        return 1;
    }

    trace_max_level = level;
    return 0;
}

void hal_trace_get_history_buffer(const unsigned char **buf1, unsigned int *len1, const unsigned char **buf2, unsigned int *len2)
{
    uint32_t lock;
    uint8_t *b1, *b2;
    uint32_t l1, l2;

    b1 = b2 = NULL;
    l1 = l2 = 0;

    lock = int_lock();

    if (TRACE_BUF_SIZE > trace.wptr) {
        if (trace.wrapped) {
            b1 = &trace.buf[trace.wptr];
            l1 = TRACE_BUF_SIZE - trace.wptr;
            b2 = &trace.buf[0];
            l2 = trace.wptr;
        } else {
            b1 = &trace.buf[0];
            l1 = trace.wptr;
            b2 = NULL;
            l2 = 0;
        }
    }

    int_unlock(lock);

    if (buf1) {
        *buf1 = b1;
    }
    if (len1) {
        *len1 = l1;
    }
    if (buf2) {
        *buf2 = b2;
    }
    if (len2) {
        *len2 = l2;
    }
}

static void hal_trace_print_discards(uint32_t discards)
{
    static const uint8_t base = 10;
    char digit[5], *d, *out;
    uint16_t len;
    uint16_t size;

    if (discards > max_discards) {
        discards = max_discards;
    }

    d = &digit[0];
    do {
        *d++ = (discards % base) + '0';
    } while (discards /= base);

    out = &discards_buf[discards_digit_start];
    do {
        *out++ = *--d;
    } while (d > &digit[0]);
#ifdef TRACE_CRLF
    *out++ = '\r';
#endif
    *out++ = '\n';
    len = out - &discards_buf[0];

    size = TRACE_BUF_SIZE - trace.wptr;
    if (size >= len) {
        size = len;
    }
    memcpy(&trace.buf[trace.wptr], &discards_buf[0], size);
    if (size < len) {
        memcpy(&trace.buf[0], &discards_buf[size], len - size);
    }
    trace.wptr += len;
    if (trace.wptr >= TRACE_BUF_SIZE) {
        trace.wptr -= TRACE_BUF_SIZE;
    }
}

#ifdef AUDIO_DEBUG_V0_1_0
static void hal_trace_print_head(void)
{
    uint16_t len;
    uint16_t size;

    len = sizeof(trace_head_buf) - 1;

    size = TRACE_BUF_SIZE - trace.wptr;
    if (size >= len) {
        size = len;
    }
    memcpy(&trace.buf[trace.wptr], &trace_head_buf[0], size);
    if (size < len) {
        memcpy(&trace.buf[0], &trace_head_buf[size], len - size);
    }
    trace.wptr += len;
    if (trace.wptr >= TRACE_BUF_SIZE) {
        trace.wptr -= TRACE_BUF_SIZE;
    }
}
#endif

int hal_trace_output(const unsigned char *buf, unsigned int buf_len)
{
    int ret;
    uint32_t lock;
    uint32_t avail;
    uint32_t out_len;
    uint16_t size;

    ret = 0;

    lock = int_lock();
#ifdef CP_TRACE_ENABLE
    while (hal_memsc_lock(HAL_MEMSC_ID_TRACE) == 0);
#endif

    // Avoid troubles when NMI occurs during trace
    if (!trace.in_trace) {
        trace.in_trace = true;

        if (trace.wptr >= trace.rptr) {
            avail = TRACE_BUF_SIZE - (trace.wptr - trace.rptr) - 1;
        } else {
            avail = (trace.rptr - trace.wptr) - 1;
        }

        out_len = buf_len;
#ifdef AUDIO_DEBUG_V0_1_0
        out_len += sizeof(trace_head_buf) - 1;
#endif
        if (trace.discards) {
            out_len += sizeof(discards_buf);
        }

        if (avail < out_len) {
            ret = 1;
            if (trace.discards < (1 << (sizeof(trace.discards) * 8)) - 1) {
                trace.discards++;
            }
#ifdef CP_TRACE_ENABLE
#if (TRACE_IDLE_OUTPUT == 0)
            hal_trace_send();
#endif
#endif
        } else {
#ifdef AUDIO_DEBUG_V0_1_0
            hal_trace_print_head();
#endif

            if (trace.discards) {
                hal_trace_print_discards(trace.discards);
                trace.discards = 0;
            }

            size = TRACE_BUF_SIZE - trace.wptr;
            if (size >= buf_len) {
                size = buf_len;
            }
            memcpy(&trace.buf[trace.wptr], &buf[0], size);
            if (size < buf_len) {
                memcpy(&trace.buf[0], &buf[size], buf_len - size);
            }
            trace.wptr += buf_len;
            if (trace.wptr >= TRACE_BUF_SIZE) {
                trace.wptr -= TRACE_BUF_SIZE;
                trace.wrapped = true;
            }
#if (TRACE_IDLE_OUTPUT == 0)
            hal_trace_send();
#endif
        }

#ifdef CP_TRACE_ENABLE
        if (get_cpu_id()) {
            if (cp_buffer_cb) {
                if (avail < out_len) {
                    cp_buffer_cb(HAL_TRACE_BUF_STATE_FULL);
                } else if (avail - out_len < TRACE_NEAR_FULL_THRESH) {
                    cp_buffer_cb(HAL_TRACE_BUF_STATE_NEAR_FULL);
                }
            }
        }
#endif

        trace.in_trace = false;

#ifdef CRASH_DUMP_ENABLE
#ifdef TRACE_TO_APP
        if (app_output_cb && app_output_enabled) {
            bool saved_output_state;

            saved_output_state = app_output_enabled;
            app_output_enabled = false;

            app_output_cb(buf, buf_len);

            app_output_enabled = saved_output_state;
        }
#endif
#endif
    }

#ifdef CP_TRACE_ENABLE
    hal_memsc_unlock(HAL_MEMSC_ID_TRACE);
#endif
    int_unlock(lock);

    return ret ? 0 : buf_len;
}
#ifdef USE_TRACE_ID
//define USE_CRC_CHECK
//#define LITE_VERSION

typedef struct {
    uint32_t crc:6;
    uint32_t count:4;
    uint32_t tskid:5;
    uint32_t addr:17; //127 KB trace space support
}trace_info_t;

typedef struct {
    uint32_t crc:8;
    uint32_t timestamp:24; // 4 hours support
}trace_head_t;

typedef struct {
#ifndef LITE_VERSION
    trace_head_t trace_head;
#endif
    trace_info_t trace_info;
}__attribute__((packed)) LOG_DATA_T;

extern const char *unkonw_str;

extern uint32_t __trc_str_start__[];
extern uint32_t __trc_str_end__[];
uint8_t crc8(uint8_t *data, uint32_t length)
{
    uint8_t i;
    uint8_t crc = 0;        // Initial value
    while(length--)
    {
        crc ^= *data++;        // crc ^= *data; data++;
        for ( i = 0; i < 8; i++ )
        {
            if ( crc & 0x80 )
                crc = (crc << 1) ^ 0x07;
            else
                crc <<= 1;
        }
    }
    return crc;
}

uint8_t crc6(uint8_t *data, uint32_t length)
{
    uint8_t i;
    uint8_t crc = 0;         // Initial value
    while(length--)
    {
        crc ^= *data++;        // crc ^= *data; data++;
        for (i = 0; i < 8; ++i)
        {
            if (crc & 1)
                crc = (crc >> 1) ^ 0x30;// 0x30 = (reverse 0x03)>>(8-6)
            else
                crc = (crc >> 1);
        }
    }
    return crc;
}


static int hal_trace_format_id(uint32_t attr, char *buf, uint32_t size, const char *fmt, va_list ap)
{
    uint8_t num;
    unsigned int value[10];
    LOG_DATA_T trace;

    if (size < sizeof(trace) + sizeof(value)) {
        return -1;
    }

    num = GET_BITFIELD(attr, LOG_ATTR_ARG_NUM);
    if (num > 10) {
        num = 10;
    }
    for (int i = 0; i < num; i++) {
        value[i] = va_arg(ap, unsigned long);
    }

    //memset(buf, 0, size);

    trace.trace_info.count = num;
    trace.trace_info.addr = (uint32_t)fmt-(uint32_t)0xFFFC0000;//(uint32_t)fmt-(uint32_t)__trc_str_start__;
    trace.trace_info.tskid = osGetThreadIntId();
    trace.trace_info.crc = 0x2A;
#ifndef LITE_VERSION
    trace.trace_head.timestamp = TICKS_TO_MS(hal_sys_timer_get());
#ifdef USE_CRC_CHECK
    trace.trace_head.crc = crc8(((uint8_t *)&trace)+1,7);
#else
    trace.trace_head.crc = 0xBE;
#endif
#else
    trace.trace_info.crc = crc6(((uint8_t *)&trace)+1,3);
#endif
    memcpy(buf, &trace, sizeof(trace));
    if (num != 0) {
        memcpy(buf + sizeof(trace), value, 4*num);
    }

    return sizeof(trace) + 4*num;
}
#endif

static int hal_trace_print_time(enum LOG_LEVEL_T level, enum LOG_MODULE_T module, char *buf, unsigned int size)
{
#ifdef TRACE_TIME_STAMP
    static const char level_ch[] = { 'C', 'E', 'W', 'N', 'I', 'D', 'V', '-', };
    char ctx[10];
    int len;
    int i;
    const char *mod_name;

#ifdef CRASH_DUMP_ENABLE
    if (crash_handling) {
        return 0;
    }
#endif

    if (0) {
#ifdef CP_TRACE_ENABLE
    } else if (get_cpu_id()) {
        ctx[0] = ' ';
        ctx[1] = 'C';
        ctx[2] = 'P';
        ctx[3] = '\0';
#endif
    } else if (in_isr()) {
        len = snprintf(ctx, sizeof(ctx), "%2d", (int8_t)NVIC_GetCurrentActiveIRQ());
        if (len + 1 < ARRAY_SIZE(ctx)) {
            ctx[len] = 'E';
            ctx[len + 1] = '\0';
        } else {
            ctx[ARRAY_SIZE(ctx) - 2] = '.';
            ctx[ARRAY_SIZE(ctx) - 1] = '\0';
        }
    } else {
#ifdef RTOS
#ifdef KERNEL_RTX5
        /* const char *thread_name = osGetThreadName(); */
        /* snprintf(ctx, sizeof(ctx), "%.9s", thread_name ? (char *)thread_name : "NULL"); */
        ctx[0] = ' ';
        ctx[1] = ' ';
        ctx[2] = 't';
        ctx[3] = '\0';
#else
        snprintf(ctx, sizeof(ctx), "%3d", osGetThreadIntId());
#endif
#else
        ctx[0] = ' ';
        ctx[1] = ' ';
        ctx[2] = '0';
        ctx[3] = '\0';
#endif
    }
    ctx[ARRAY_SIZE(ctx) - 1] = '\0';
    len = 0;
    len += snprintf(&buf[len], size - len, "%9u/", (unsigned)TICKS_TO_MS(hal_sys_timer_get()));
    if (size > len + 2) {
        buf[len++] = level_ch[level];
        buf[len++] = '/';
    }
    if (size > len + 7) {
        mod_name = hal_trace_get_log_module_desc(module);
        for (i = 0; i < 6; i++) {
            if (mod_name[i] == '\0') {
                break;
            }
            buf[len++] = mod_name[i];
        }
        for (; i < 6; i++) {
            buf[len++] = ' ';
        }
        buf[len++] = '/';
    }
    len += snprintf(&buf[len], size - len, "%s | ", ctx);
    return len;
#else // !TRACE_TIME_STAMP
    return 0;
#endif // !TRACE_TIME_STAMP
}

static inline int hal_trace_format_va(uint32_t attr, char *buf, unsigned int size, const char *fmt, va_list ap)
{
    int len;

    len = vsnprintf(&buf[0], size, fmt, ap);
    if ((attr & LOG_ATTR_NO_LF) == 0) {
#ifdef TRACE_CRLF
        if (len + 2 < size) {
            buf[len++] = '\r';
        }
#endif
        if (len + 1 < size) {
            buf[len++] = '\n';
        }
    }
    //if (len < size) buf[len] = 0;

    return len;
}

static int hal_trace_printf_internal(uint32_t attr, const char *fmt, va_list ap)
{
#ifdef USE_TRACE_ID
    char buf[60];
#else
    char buf[120];
#endif
    int len = 0;
    enum LOG_LEVEL_T level;
    enum LOG_MODULE_T module;

    level = GET_BITFIELD(attr, LOG_ATTR_LEVEL);
    module = GET_BITFIELD(attr, LOG_ATTR_MOD);

#ifdef CRASH_DUMP_ENABLE
    if (!crash_handling)
#endif
    {
        if (level > trace_max_level) {
            return 0;
        }
        if (level > LOG_LEVEL_CRITICAL && (trace_mod_map[module >> 5] & (1 << (module & 0x1F))) == 0) {
            return 0;
        }
    }

#ifdef USE_TRACE_ID
    if ((attr & LOG_ATTR_NO_ID) || (len = hal_trace_format_id(attr, buf, sizeof(buf), fmt, ap)) < 0)
#endif
    {
        len = 0;
        if ((attr & LOG_ATTR_NO_TS) == 0) {
            len += hal_trace_print_time(level, module, &buf[len], sizeof(buf) - len);
        }
        len += hal_trace_format_va(attr, &buf[len], sizeof(buf) - len, fmt, ap);
    }

    return hal_trace_output((unsigned char *)buf, len);
}

int hal_trace_printf(uint32_t attr, const char *fmt, ...)
{
    int ret;
    va_list ap;

    if (attr & LOG_ATTR_IMM) {
        hal_trace_flush_buffer();
    }

    va_start(ap, fmt);
    ret = hal_trace_printf_internal(attr, fmt, ap);
    va_end(ap);

    if (attr & LOG_ATTR_IMM) {
        hal_trace_flush_buffer();
    }

    return ret;
}

int hal_trace_dump(const char *fmt, unsigned int size,  unsigned int count, const void *buffer)
{
    char buf[255]={0};
    int len=0, n=0, i=0;

    switch( size )
    {
        case sizeof(uint32_t):
            while(i<count && len<sizeof(buf))
            {
                len += snprintf(&buf[len], sizeof(buf) - len, fmt, *(uint32_t *)((uint32_t *)buffer+i));
                i++;
            }
            break;
        case sizeof(uint16_t):
                while(i<count && len<sizeof(buf))
                {
                    len += snprintf(&buf[len], sizeof(buf) - len, fmt, *(int16_t *)((int16_t *)buffer+i));
                    i++;
                }
                break;
        case sizeof(uint8_t):
        default:
            while(i<count && len<sizeof(buf))
            {
                len += snprintf(&buf[len], sizeof(buf) - len, fmt, *(uint8_t *)((uint8_t *)buffer+i));
                i++;
            }
            break;
    }

#ifdef TRACE_CRLF
    if (len + 2 < sizeof(buf)) {
        buf[len++] = '\r';
    }
#endif
    if (len + 1 < sizeof(buf)) {
        buf[len++] = '\n';
    }

    n = hal_trace_output((unsigned char *)buf, len+1);

    return n;
}

int hal_trace_busy(void)
{
    union HAL_UART_FLAG_T flag;

    if (hal_trace_is_uart_transport(trace_transport)) {
        if (hal_uart_opened(trace_uart)) {
            flag = hal_uart_get_flag(trace_uart);
            return flag.BUSY;
        }
    }
    return 0;
}

int hal_trace_pause(void)
{
    if (hal_trace_is_uart_transport(trace_transport)) {
        return hal_uart_pause(trace_uart);
    }
    return 1;
}

int hal_trace_continue(void)
{
    if (hal_trace_is_uart_transport(trace_transport)) {
        return hal_uart_continue(trace_uart);
    }
    return 1;
}

int hal_trace_flush_buffer(void)
{
    uint32_t lock;
    uint32_t time;
    int ret;
    enum HAL_DMA_RET_T dma_ret;

    if (!hal_trace_is_uart_transport(trace_transport)) {
        return -1;
    }

#ifdef CP_TRACE_ENABLE
    if (get_cpu_id()) {
        if (cp_buffer_cb) {
            cp_buffer_cb(HAL_TRACE_BUF_STATE_FLUSH);
        }
        return 0;
    }
#endif

    hal_uart_continue(trace_uart);

    lock = int_lock();

    time = hal_sys_timer_get();
    while (trace.wptr != trace.rptr &&
            hal_sys_timer_get() - time < TRACE_FLUSH_TIMEOUT) {
#if (TRACE_IDLE_OUTPUT == 0)
        while (hal_gpdma_chan_busy(dma_cfg.ch));
        dma_ret = hal_gpdma_irq_run_chan(dma_cfg.ch);
        if (dma_ret != HAL_DMA_OK) {
            hal_trace_send();
        }
#else
        hal_trace_idle_send();
#endif
    }

    ret = (trace.wptr == trace.rptr) ? 0 : 1;

    int_unlock(lock);

    return ret;
}

uint32_t hal_trace_get_backtrace_addr(uint32_t addr)
{
    if (!hal_trace_address_executable(addr)) {
        return 0;
    }

#ifndef __ARM_ARCH_ISA_ARM
#if defined(__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__)
    // BL Instruction
    // OFFSET: 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
    // VALUE :  1  1  1  1  0  -  -  -  -  -  -  -  -  -  -  -  1  1  -  1  -  -  -  -  -  -  -  -  -  -  -  -

    // BLX Instruction
    // OFFSET: 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
    // VALUE :  0  1  0  0  0  1  1  1  1  -  -  -  -  -  -  -

    uint16_t val;
    uint32_t new_addr;

    new_addr = (addr & ~1) - 2;

    val = *(uint16_t *)new_addr;
    if ((val & 0xFF80) == 0x4780) {
        // BLX
        return new_addr;
    } else if ((val & 0xD000) == 0xD000) {
        new_addr -= 2;
        val = *(uint16_t *)new_addr;
        if ((val & 0xF800) == 0xF000) {
            // BL
            return new_addr;
        }
    }
#else
#error "Only ARMv7-M/ARMv8-M function can be checked for BL/BLX instructions"
#endif
#endif

    return 0;
}

#ifndef __ARM_ARCH_ISA_ARM
void hal_trace_print_special_stack_registers(void)
{
    int len;

    hal_trace_output((const unsigned char *)newline, sizeof(newline) - 1);

    len = snprintf(crash_buf, sizeof(crash_buf), "MSP=%08X, PSP=%08X" NEW_LINE_STR, (unsigned)__get_MSP(), (unsigned)__get_PSP());
    hal_trace_output((unsigned char *)crash_buf, len);

#ifdef __ARM_ARCH_8M_MAIN__
    len = snprintf(crash_buf, sizeof(crash_buf), "MSPLIM=%08X, PSPLIM=%08X" NEW_LINE_STR, (unsigned)__get_MSPLIM(), (unsigned)__get_PSPLIM());
    hal_trace_output((unsigned char *)crash_buf, len);
#endif
}
#endif

void hal_trace_print_common_registers(const uint32_t *regs)
{
    int len;
    int i;
    int index;

    hal_trace_output((const unsigned char *)newline, sizeof(newline) - 1);

    for (i = 0; i < 3; i++) {
        index = i * 4;
        len = snprintf(crash_buf, sizeof(crash_buf), "R%-2d=%08X, R%-2d=%08X, R%-2d=%08X, R%-2d=%08X" NEW_LINE_STR,
            index, (unsigned)regs[index], index + 1, (unsigned)regs[index + 1],
            index + 2, (unsigned)regs[index + 2], index + 3, (unsigned)regs[index + 3]);
        hal_trace_output((unsigned char *)crash_buf, len);
    }
    len = snprintf(crash_buf, sizeof(crash_buf), "R12=%08X, SP =%08X, LR =%08X" NEW_LINE_STR,
        (unsigned)regs[12], (unsigned)regs[13], (unsigned)regs[14]);
    hal_trace_output((unsigned char *)crash_buf, len);
}

void hal_trace_print_stack(uint32_t addr)
{
    static const char stack_title[] = "Stack:" NEW_LINE_STR;
    int len = 0;
    int i;
    int pos;
    uint32_t *stack;

    addr &= ~3;
    if (!hal_trace_address_writable(addr)) {
        return;
    }

    hal_trace_output((const unsigned char *)newline, sizeof(newline) - 1);
    hal_trace_output((const unsigned char *)stack_title, sizeof(stack_title) - 1);

    stack = (uint32_t *)addr - STACK_DUMP_CNT_PREV;
    for (i = 0; i < (STACK_DUMP_CNT_PREV + STACK_DUMP_CNT); i++) {
        if (!hal_trace_address_writable((uint32_t)&stack[i])) {
            break;
        }
        pos = (i % STACK_DUMP_CNT_PER_LEN);
        if (pos == 0) {
            len = snprintf(crash_buf, sizeof(crash_buf), "%c %08X: %08X",
                (i == STACK_DUMP_CNT_PREV) ? '*' : ' ', (unsigned)&stack[i], (unsigned)stack[i]);
        } else {
            len += snprintf(crash_buf + len, sizeof(crash_buf) - len, " %08X", (unsigned)stack[i]);
            if (pos == (STACK_DUMP_CNT_PER_LEN - 1)) {
#ifdef TRACE_CRLF
                if (len + 2 < sizeof(crash_buf)) {
                    crash_buf[len++] = '\r';
                }
#endif
                if (len + 1 < sizeof(crash_buf)) {
                    crash_buf[len++] = '\n';
                }
                hal_trace_output((unsigned char *)crash_buf, len);
                hal_trace_flush_buffer();
            }
        }
    }
}

void hal_trace_print_backtrace(uint32_t addr, uint32_t search_cnt, uint32_t print_cnt)
{
    static const char bt_title[] = "Possible Backtrace:" NEW_LINE_STR;
    int i, j;
    int len;
    uint32_t *stack;
    uint32_t call_addr;

    addr &= ~3;
    if (!hal_trace_address_writable(addr)) {
        return;
    }

    hal_trace_output((const unsigned char *)newline, sizeof(newline));
    hal_trace_output((const unsigned char *)bt_title, sizeof(bt_title));

    stack = (uint32_t *)addr;
    for (i = 0, j = 0; i < search_cnt && j < print_cnt; i++) {
        if (!hal_trace_address_writable((uint32_t)&stack[i])) {
            break;
        }
        call_addr = hal_trace_get_backtrace_addr(stack[i]);
        if (call_addr) {
            len = snprintf(crash_buf, sizeof(crash_buf), "%8X" NEW_LINE_STR, (unsigned)call_addr);
            hal_trace_output((unsigned char *)crash_buf, len+1);
            j++;
        }
    }
}

uint32_t hal_trace_get_baudrate(void)
{
    return uart_cfg.baud;
}

bool hal_trace_crash_dump_onprocess(void)
{
    return crash_dump_onprocess;
}

int hal_trace_crash_dump_register(enum HAL_TRACE_CRASH_DUMP_MODULE_T module, HAL_TRACE_CRASH_DUMP_CB_T cb)
{
#ifdef CRASH_DUMP_ENABLE
    ASSERT(module < HAL_TRACE_CRASH_DUMP_MODULE_END, "%s module %d", __func__, module);
    crash_dump_cb_list[module] = cb;
#endif
    return 0;
}

#ifdef CRASH_DUMP_ENABLE
static void hal_trace_crash_dump_callback(void)
{
    int i;

    crash_handling = true;

    for (i = 0; i < ARRAY_SIZE(crash_dump_cb_list); i++) {
        if (crash_dump_cb_list[i]) {
            crash_dump_cb_list[i]();
        }
    }
}
#endif

void hal_trace_app_register(HAL_TRACE_APP_NOTIFY_T notify_cb, HAL_TRACE_APP_OUTPUT_T output_cb)
{
#ifdef TRACE_TO_APP
    app_notify_cb = notify_cb;
    app_output_cb = output_cb;
#endif
}

void hal_trace_app_custom_register(HAL_TRACE_APP_NOTIFY_T notify_cb, HAL_TRACE_APP_OUTPUT_T output_cb, HAL_TRACE_APP_OUTPUT_T crash_custom_cb)
{
#ifdef TRACE_TO_APP
    hal_trace_app_register(notify_cb, output_cb);
    app_crash_custom_cb = crash_custom_cb;
#endif
}

void hal_trace_cp_register(HAL_TRACE_APP_NOTIFY_T notify_cb, HAL_TRACE_BUF_CTRL_T buf_cb)
{
#ifdef CP_TRACE_ENABLE
    cp_notify_cb = notify_cb;
    cp_buffer_cb = buf_cb;
#endif
}

#else // !(DEBUG || REL_TRACE_ENABLE)

int hal_trace_open(enum HAL_TRACE_TRANSPORT_T transport)
{
#ifdef FAULT_DUMP
#ifdef __ARM_ARCH_ISA_ARM
    GIC_SetFaultDumpHandler(hal_trace_fault_dump);
#else
    NVIC_SetDefaultFaultHandler(hal_trace_fault_handler);
#endif
#endif

    return 0;
}

#endif // !(DEBUG || REL_TRACE_ENABLE)

int hal_trace_open_cp(void)
{
#ifdef CP_TRACE_ENABLE
#ifdef FAULT_DUMP
    NVIC_SetDefaultFaultHandler_cp(hal_trace_fault_handler);
#endif
#endif
    return 0;
}

int hal_trace_address_writable(uint32_t addr)
{
    if (RAM_BASE < addr && addr < RAM_BASE + RAM_SIZE) {
        return 1;
    }
#ifdef PSRAM_BASE
    if (PSRAM_BASE < addr && addr < PSRAM_BASE + PSRAM_SIZE) {
        return 1;
    }
#endif
#ifdef PSRAM_NC_BASE
    if (PSRAM_NC_BASE < addr && addr < PSRAM_NC_BASE + PSRAM_SIZE) {
        return 1;
    }
#endif
#ifdef PSRAMUHS_BASE
    if (PSRAMUHS_BASE < addr && addr < PSRAMUHS_BASE + PSRAMUHS_SIZE) {
        return 1;
    }
#endif
#ifdef PSRAMUHS_NC_BASE
    if (PSRAMUHS_NC_BASE < addr && addr < PSRAMUHS_NC_BASE + PSRAMUHS_SIZE) {
        return 1;
    }
#endif
#ifdef RAMRET_BASE
    if (RAMRET_BASE < addr && addr < RAMRET_BASE + RAMRET_SIZE) {
        return 1;
    }
#endif
#ifdef RAMCP_BASE
    if (RAMCP_BASE < addr && addr < RAMCP_BASE + RAMCP_SIZE) {
        return 1;
    }
#endif
    return 0;
}

int hal_trace_address_executable(uint32_t addr)
{
// Avoid reading out of valid memory region when parsing instruction content
#define X_ADDR_OFFSET                   0x10

    // Check thumb code
    if ((addr & 1) == 0) {
        return 0;
    }
    // Check location
    if (RAMX_BASE + X_ADDR_OFFSET < addr && addr < RAMX_BASE + RAM_SIZE) {
        return 1;
    }
    if (FLASHX_BASE + X_ADDR_OFFSET < addr && addr < FLASHX_BASE + FLASH_SIZE) {
        return 1;
    }
#ifdef PSRAMX_BASE
    if (PSRAMX_BASE + X_ADDR_OFFSET < addr && addr < PSRAMX_BASE + PSRAM_SIZE) {
        return 1;
    }
#endif
#ifdef PSRAMUHSX_BASE
    if (PSRAMUHSX_BASE + X_ADDR_OFFSET < addr && addr < PSRAMUHSX_BASE + PSRAMUHS_SIZE) {
        return 1;
    }
#endif
#ifdef RAMXRET_BASE
    if (RAMXRET_BASE < addr && addr < RAMXRET_BASE + RAMRET_SIZE) {
        return 1;
    }
#endif

//#define CHECK_ROM_CODE
#ifdef CHECK_ROM_CODE
#ifndef USED_ROM_SIZE
#define USED_ROM_SIZE                   ROM_SIZE
#endif
    if (ROM_BASE + (NVIC_USER_IRQ_OFFSET * 4) < addr && addr < ROM_BASE + USED_ROM_SIZE) {
        return 1;
    }
#endif

#if 0
    if (FLASHX_NC_BASE < addr && addr < FLASHX_NC_BASE + FLASH_SIZE) {
        return 1;
    }
#ifdef PSRAMX_NC_BASE
    if (PSRAMX_NC_BASE < addr && addr < PSRAMX_NC_BASE + PSRAM_SIZE) {
        return 1;
    }
#endif
#ifdef PSRAMUHSX_NC_BASE
    if (PSRAMUHSX_NC_BASE < addr && addr < PSRAMUHSX_NC_BASE + PSRAMUHS_SIZE) {
        return 1;
    }
#endif
#endif
    return 0;
}

int hal_trace_address_readable(uint32_t addr)
{
    if (hal_trace_address_writable(addr)) {
        return 1;
    }
    if (hal_trace_address_executable(addr)) {
        return 1;
    }
    if (FLASH_BASE < addr && addr < FLASH_BASE + FLASH_SIZE) {
        return 1;
    }
    if (FLASH_NC_BASE < addr && addr < FLASH_NC_BASE + FLASH_SIZE) {
        return 1;
    }
#ifdef PSRAM_NC_BASE
    if (PSRAM_NC_BASE < addr && addr < PSRAM_NC_BASE + PSRAM_SIZE) {
        return 1;
    }
#endif
#ifdef PSRAMUHS_NC_BASE
    if (PSRAMUHS_NC_BASE < addr && addr < PSRAMUHS_NC_BASE + PSRAMUHS_SIZE) {
        return 1;
    }
#endif
    return 0;
}

static void NORETURN hal_trace_crash_end(void)
{
#if (defined(DEBUG) || defined(REL_TRACE_ENABLE))
    hal_trace_output((const unsigned char *)newline, sizeof(newline));
    hal_trace_flush_buffer();
    hal_sys_timer_delay(MS_TO_TICKS(5));
#endif

    // Tag failure for simulation environment
    hal_cmu_simu_fail();
#ifndef PROGRAMMER
#ifndef __BES_OTA_MODE__
    nv_record_flash_flush();
#endif
#endif
#ifdef CRASH_REBOOT
    
    hal_sw_bootmode_set(HAL_SW_BOOTMODE_REBOOT|HAL_SW_BOOTMODE_REBOOT_FROM_CRASH);
    hal_cmu_sys_reboot();
#else
    hal_iomux_set_analog_i2c();
    hal_iomux_set_jtag();
    hal_cmu_jtag_clock_enable();
#endif

    SAFE_PROGRAM_STOP();
}

__STATIC_FORCEINLINE uint32_t get_cpu_id_tag(void)
{
    uint32_t cpu_id;
#ifdef CP_TRACE_ENABLE
    cpu_id = get_cpu_id();
#elif defined(__ARM_ARCH_ISA_ARM)
    cpu_id = 0xAAAA0000 | (get_cpu_id() & 0xFFFF);
#else
    cpu_id = 0;
#endif
    return cpu_id;
}

static void NORETURN USED hal_trace_assert_dump_internal(ASSERT_DUMP_ARGS)
{
    const uint32_t *p_regs;
    struct ASSERT_INFO_T info;
    int i;

    // MUST SAVE REGISTERS FIRST!
    p_regs = (const uint32_t *)crash_buf;
    for (i = 0; i < ARRAY_SIZE(info.R); i++) {
        info.R[i] = p_regs[i];
    }

    int_lock_global();

    info.ID = HAL_TRACE_ASSERT_ID;
    info.CPU_ID = get_cpu_id_tag();
#if (defined(DEBUG) || defined(REL_TRACE_ENABLE)) && defined(ASSERT_SHOW_FILE_FUNC)
    info.FILE = file;
#elif (defined(DEBUG) || defined(REL_TRACE_ENABLE)) && defined(ASSERT_SHOW_FILE)
    info.FILE = scope;
#else
    info.FILE = NULL;
#endif
#if (defined(DEBUG) || defined(REL_TRACE_ENABLE)) && defined(ASSERT_SHOW_FILE_FUNC)
    info.FUNC = func;
#elif (defined(DEBUG) || defined(REL_TRACE_ENABLE)) && defined(ASSERT_SHOW_FUNC)
    info.FUNC = scope;
#else
    info.FUNC = NULL;
#endif
#if (defined(DEBUG) || defined(REL_TRACE_ENABLE)) && (defined(ASSERT_SHOW_FILE_FUNC) || defined(ASSERT_SHOW_FILE) || defined(ASSERT_SHOW_FUNC))
    info.LINE = line;
#else
    info.LINE = 0;
#endif
    info.FMT = fmt;
#ifndef __ARM_ARCH_ISA_ARM
    info.MSP = __get_MSP();
    info.PSP = __get_PSP();
    info.CONTROL = __get_CONTROL();
#ifdef __ARM_ARCH_8M_MAIN__
    info.MSPLIM = __get_MSPLIM();
    info.PSPLIM = __get_PSPLIM();
#endif
#endif

    *(volatile uint32_t *)RAM_BASE = (uint32_t)&info;

#ifdef ASSERT_MUTE_CODEC
    bool crash_mute = true;
#ifdef CP_TRACE_ENABLE
    if (get_cpu_id()) {
        // Forbid CP to access CODEC
        crash_mute = false;
    }
#endif
    if (crash_mute) {
        hal_codec_crash_mute();
    }
#endif

#if (defined(DEBUG) || defined(REL_TRACE_ENABLE))

    static const char POSSIBLY_UNUSED desc_file[] = "FILE    : ";
    static const char POSSIBLY_UNUSED desc_func[] = "FUNCTION: ";
    static const char POSSIBLY_UNUSED desc_line[] = "LINE    : ";
    int len;
    va_list ap;

#ifdef CRASH_DUMP_ENABLE
    bool full_dump = true;

#ifdef CP_TRACE_ENABLE
    if (get_cpu_id()) {
        full_dump = false;
        if (cp_notify_cb) {
            cp_notify_cb(HAL_TRACE_STATE_CRASH_ASSERT_START);
        }
    }
#endif

    if (full_dump) {
#ifdef TRACE_TO_APP
        if (app_notify_cb) {
            app_notify_cb(HAL_TRACE_STATE_CRASH_ASSERT_START);
        }
        app_output_enabled = true;
#endif

        crash_dump_onprocess = true;
        for (uint8_t i = 0; i < 10; i++){
            REL_TRACE_IMM(0,"                                                                        ");
            REL_TRACE_IMM(0,"                                                                        " NEW_LINE_STR);
            hal_sys_timer_delay(MS_TO_TICKS(50));
        }
    }
#endif

    hal_trace_flush_buffer();

    hal_sysfreq_req(HAL_SYSFREQ_USER_INIT, HAL_CMU_FREQ_52M);

    len = hal_trace_print_time(LOG_LEVEL_CRITICAL, LOG_MODULE_NONE, &crash_buf[0], sizeof(crash_buf));
    if (len > 0) {
        hal_trace_output((const unsigned char *)newline, sizeof(newline));
        hal_trace_output((unsigned char *)crash_buf, len);
    }
    len = snprintf(&crash_buf[0], sizeof(crash_buf), NEW_LINE_STR "### ASSERT @ 0x%08X ###" NEW_LINE_STR, (unsigned)info.R[14]);
    hal_trace_output((unsigned char *)crash_buf, len);

#if defined(ASSERT_SHOW_FILE_FUNC) || defined(ASSERT_SHOW_FILE) || defined(ASSERT_SHOW_FUNC)
    const char separate_line[] = "----------------------------------------" NEW_LINE_STR;
#ifdef ASSERT_SHOW_FILE
    const char *file = scope;
#elif defined(ASSERT_SHOW_FUNC)
    const char *func = scope;
#endif

#if defined(ASSERT_SHOW_FILE_FUNC) || defined(ASSERT_SHOW_FILE)
    hal_trace_output((const unsigned char *)desc_file, sizeof(desc_file));
    hal_trace_output((const unsigned char *)file, strlen(file)+1);
    hal_trace_output((const unsigned char *)newline, sizeof(newline));
#endif

#if defined(ASSERT_SHOW_FILE_FUNC) || defined(ASSERT_SHOW_FUNC)
    hal_trace_output((const unsigned char *)desc_func, sizeof(desc_func));
    hal_trace_output((const unsigned char *)func, strlen(func)+!);
    hal_trace_output((const unsigned char *)newline, sizeof(newline));
#endif

    hal_trace_output((const unsigned char *)desc_line, sizeof(desc_func));
    len = snprintf(crash_buf, sizeof(crash_buf), "%u", line);
    hal_trace_output((const unsigned char *)crash_buf, len);
    hal_trace_output((const unsigned char *)newline, sizeof(newline));

    hal_trace_output((unsigned char *)separate_line, sizeof(separate_line));

    hal_trace_flush_buffer();
#endif

    va_start(ap, fmt);
    len = hal_trace_format_va(0, crash_buf, sizeof(crash_buf), fmt, ap);
    va_end(ap);

    hal_trace_output((unsigned char *)crash_buf, len);

    hal_trace_flush_buffer();

    hal_trace_print_common_registers(info.R);
    hal_trace_print_special_stack_registers();

    hal_trace_print_stack(info.R[13]);

    hal_trace_print_backtrace(info.R[13], TRACE_BACKTRACE_SEARCH_WORD, TRACE_BACKTRACE_NUM);

    hal_trace_output((const unsigned char *)newline, sizeof(newline));

    hal_trace_flush_buffer();

#ifdef CRASH_DUMP_ENABLE
    if (full_dump) {
        hal_trace_crash_dump_callback();
        hal_trace_flush_buffer();
        hal_sys_timer_delay(MS_TO_TICKS(5));

#ifdef CORE_DUMP
        AssertCatcher_Entry();
        hal_sys_timer_delay(MS_TO_TICKS(5));
#endif

#ifdef TRACE_TO_APP
        if (app_notify_cb) {
            app_notify_cb(HAL_TRACE_STATE_CRASH_END);
        }
#endif
    }

#ifdef CP_TRACE_ENABLE
    if (get_cpu_id()) {
        if (cp_notify_cb) {
            cp_notify_cb(HAL_TRACE_STATE_CRASH_END);
        }
        SAFE_PROGRAM_STOP();
    }
#endif
#endif // CRASH_DUMP_ENABLE

#endif // DEBUG || REL_TRACE_ENABLE

    hal_trace_crash_end();
}

void NORETURN NAKED hal_trace_assert_dump(ASSERT_DUMP_ARGS)
{
    asm volatile (
        "subs sp, sp, #4*" TO_STRING(STACK_DUMP_CNT_PREV) ";"
        ".cfi_def_cfa_offset 4*" TO_STRING(STACK_DUMP_CNT_PREV) ";"
        "push {r0, r1};"
        "ldr r0, =crash_buf;"
        "ldr r1, [sp];"
        "str r1, [r0], 4;"
        "ldr r1, [sp, 4];"
        "str r1, [r0], 4;"
        "stmia r0!, {r2-r12};"
        "add r1, sp, #4*(2+" TO_STRING(STACK_DUMP_CNT_PREV) ");"
        "str r1, [r0], 4;"
        "str lr, [r0], 4;"
        "pop {r0, r1};"
        "b.w hal_trace_assert_dump_internal;"
    );
}

#ifdef FAULT_DUMP
static void hal_trace_fill_exception_info(struct EXCEPTION_INFO_T *info, const uint32_t *regs, const uint32_t *extra, uint32_t extra_len)
{
    info->ID = HAL_TRACE_EXCEPTION_ID;
    info->CPU_ID = get_cpu_id_tag();
    info->REGS = regs;
#ifdef __ARM_ARCH_ISA_ARM
    info->extra = extra;
    info->extra_len = extra_len;
#else
    info->MSP = __get_MSP();
    info->PSP = __get_PSP();
    info->PRIMASK = regs[17];
    info->FAULTMASK = __get_FAULTMASK();
    info->BASEPRI = __get_BASEPRI();
    info->CONTROL = __get_CONTROL();
    info->ICSR = SCB->ICSR;
    info->AIRCR = SCB->AIRCR;
    info->SCR = SCB->SCR;
    info->CCR = SCB->CCR;
    info->SHCSR = SCB->SHCSR;
    info->CFSR = SCB->CFSR;
    info->HFSR = SCB->HFSR;
    info->AFSR = SCB->AFSR;
    info->MMFAR = SCB->MMFAR;
    info->BFAR = SCB->BFAR;
#ifdef __ARM_ARCH_8M_MAIN__
    info->MSPLIM = __get_MSPLIM();
    info->PSPLIM = __get_PSPLIM();
#endif
#endif
}

#if (defined(DEBUG) || defined(REL_TRACE_ENABLE))
#ifdef __ARM_ARCH_ISA_ARM
static void hal_trace_print_fault_info_ca(const struct EXCEPTION_INFO_T *info)
{
    const struct FAULT_REGS_T *fregs;
    const uint32_t *extra;
    //uint32_t extra_len;
    enum EXCEPTION_ID_T id;
    int len;
    uint32_t val;
    const char *desc;

    fregs = (const struct FAULT_REGS_T*)info->REGS;
    extra = info->extra;
    //extra_len = info->extra_len;

    id = (enum EXCEPTION_ID_T)extra[0];

    len = snprintf(crash_buf, sizeof(crash_buf), "PC =%08X", (unsigned)fregs->r[15]);
    len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, ", ExceptionNumber=%d" NEW_LINE_STR, id);
    hal_trace_output((unsigned char *)crash_buf, len);

    hal_trace_print_common_registers(fregs->r);

    len = snprintf(crash_buf, sizeof(crash_buf), "SPSR=%08X", (unsigned)fregs->spsr);
    len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, ", APSR=%c%c%c%c%c",
        (fregs->spsr & (1 << 31)) ? 'N' : 'n',
        (fregs->spsr & (1 << 30)) ? 'Z' : 'z',
        (fregs->spsr & (1 << 29)) ? 'C' : 'c',
        (fregs->spsr & (1 << 28)) ? 'V' : 'v',
        (fregs->spsr & (1 << 27)) ? 'Q' : 'q'
        );
    len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, ", XC=%c%c%c%c%c",
        (fregs->spsr & (1 << 9)) ? 'E' : 'e',
        (fregs->spsr & (1 << 8)) ? 'A' : 'a',
        (fregs->spsr & (1 << 7)) ? 'I' : 'i',
        (fregs->spsr & (1 << 6)) ? 'F' : 'f',
        (fregs->spsr & (1 << 5)) ? 'T' : 't'
        );
    val = fregs->spsr & 0x1F;
    if (val == 0x10) {
        desc = "USR";
    } else if (val == 0x11) {
        desc = "FIQ";
    } else if (val == 0x12) {
        desc = "IRQ";
    } else if (val == 0x13) {
        desc = "SVC";
    } else if (val == 0x16) {
        desc = "MON";
    } else if (val == 0x17) {
        desc = "ABT";
    } else if (val == 0x1A) {
        desc = "HYP";
    } else if (val == 0x1B) {
        desc = "UND";
    } else if (val == 0x1F) {
        desc = "SYS";
    } else {
        desc = "UNKNOWN";
    }
    len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, ", MODE=%02X (%s)", (unsigned)val, desc);
    hal_trace_output((unsigned char *)crash_buf, len);
    hal_trace_output((const unsigned char *)newline, sizeof(newline));
    hal_trace_output((const unsigned char *)newline, sizeof(newline));

    hal_trace_flush_buffer();

    if (id == EXCEPTION_UNDEF) {
    } else if (id == EXCEPTION_SVC) {
    } else if (id == EXCEPTION_PABT) {
    } else if (id == EXCEPTION_DABT) {
    } else {
    }
}
#else
static void hal_trace_print_fault_info_cm(const struct EXCEPTION_INFO_T *info)
{
    const uint32_t *regs;
    int len;
    uint32_t val;
    uint32_t primask;

    regs = info->REGS;
    primask = regs[17];

    len = snprintf(crash_buf, sizeof(crash_buf), "PC =%08X", (unsigned)regs[15]);
    val = __get_IPSR();
    if (val == 0) {
        len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, ", ThreadMode" NEW_LINE_STR);
    } else {
        len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, ", ExceptionNumber=%d" NEW_LINE_STR, (int)val - 16);
    }
    hal_trace_output((unsigned char *)crash_buf, len);

    hal_trace_print_common_registers(regs);
    hal_trace_print_special_stack_registers();

    hal_trace_output((const unsigned char *)newline, sizeof(newline));

    len = snprintf(crash_buf, sizeof(crash_buf), "PRIMASK=%02X, FAULTMASK=%02X, BASEPRI=%02X, CONTROL=%02X" NEW_LINE_STR,
        (unsigned)primask, (unsigned)__get_FAULTMASK(), (unsigned)__get_BASEPRI(), (unsigned)__get_CONTROL());
    hal_trace_output((unsigned char *)crash_buf, len);
    len = snprintf(crash_buf, sizeof(crash_buf), "XPSR=%08X", (unsigned)regs[16]);
    len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, ", APSR=%c%c%c%c%c",
        (regs[16] & (1 << 31)) ? 'N' : 'n',
        (regs[16] & (1 << 30)) ? 'Z' : 'z',
        (regs[16] & (1 << 29)) ? 'C' : 'c',
        (regs[16] & (1 << 28)) ? 'V' : 'v',
        (regs[16] & (1 << 27)) ? 'Q' : 'q'
        );
    val = regs[16] & 0xFF;
    len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, ", EPSR=%08X, IPSR=%02X", (unsigned)(regs[16] & 0x0700FC00), (unsigned)val);
    if (val == 0) {
        len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (NoException)");
    }
    hal_trace_output((unsigned char *)crash_buf, len);
    hal_trace_output((const unsigned char *)newline, sizeof(newline));
    hal_trace_output((const unsigned char *)newline, sizeof(newline));

    hal_trace_flush_buffer();

    len = snprintf(crash_buf, sizeof(crash_buf), "ICSR =%08X, AIRCR=%08X, SCR  =%08X, CCR  =%08X" NEW_LINE_STR,
        (unsigned)info->ICSR, (unsigned)info->AIRCR, (unsigned)info->SCR, (unsigned)info->CCR);
    hal_trace_output((unsigned char *)crash_buf, len);

    len = snprintf(crash_buf, sizeof(crash_buf), "SHCSR=%08X, CFSR =%08X, HFSR =%08X, AFSR =%08X" NEW_LINE_STR,
        (unsigned)info->SHCSR, (unsigned)info->CFSR, (unsigned)info->HFSR, (unsigned)info->AFSR);
    hal_trace_output((unsigned char *)crash_buf, len);

    len = snprintf(crash_buf, sizeof(crash_buf), "MMFAR=%08X, BFAR =%08X" NEW_LINE_STR, (unsigned)info->MMFAR, (unsigned)info->BFAR);
    hal_trace_output((unsigned char *)crash_buf, len);

    if (info->HFSR & (1 << 30)) {
        len = snprintf(crash_buf, sizeof(crash_buf), "(Escalation HardFault)" NEW_LINE_STR);
        hal_trace_output((unsigned char *)crash_buf, len);
    }

    len = snprintf(crash_buf, sizeof(crash_buf), "FaultInfo :");
    if ((info->SHCSR & 0x3F) == 0) {
        len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (None)");
    } else {
        if (info->SHCSR & (1 << 0)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (MemFault)");
        }
        if (info->SHCSR & (1 << 1)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (BusFault)");
        }
#ifdef __ARM_ARCH_8M_MAIN__
        if (info->SHCSR & (1 << 2)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (HardFault)");
        }
#endif
        if (info->SHCSR & (1 << 3)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (UsageFault)");
        }
#ifdef __ARM_ARCH_8M_MAIN__
        if (info->SHCSR & (1 << 4)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (SecureFault)");
        }
        if (info->SHCSR & (1 << 5)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (NMI)");
        }
#endif
    }
    hal_trace_output((unsigned char *)crash_buf, len);
    hal_trace_output((const unsigned char *)newline, sizeof(newline));

    len = snprintf(crash_buf, sizeof(crash_buf), "FaultCause:");
    if (info->CFSR == 0) {
        len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (None)");
    } else {
        if (info->CFSR & (1 << 0)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (Instruction access violation)");
        }
        if (info->CFSR & (1 << 1)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (Data access violation)");
        }
        if (info->CFSR & (1 << 3)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (MemFault on unstacking for a return from exception)");
        }
        if (info->CFSR & (1 << 4)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (MemFault on stacking for exception entry)");
        }
        if (info->CFSR & (1 << 5)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (MemFault during floating-point lazy state preservation)");
        }
        if (info->CFSR & (1 << 7)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (MMFAR valid)");
        }
        if (len) {
            hal_trace_output((unsigned char *)crash_buf, len);
            hal_trace_flush_buffer();
            len = 0;
        }
        if (info->CFSR & (1 << 8)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (Instruction bus error)");
        }
        if (info->CFSR & (1 << 9)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (Precise data bus error)");
        }
#ifndef __ARM_ARCH_8M_MAIN__
        if (info->CFSR & (1 << 10)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (Imprecise data bus error)");
        }
#endif
        if (info->CFSR & (1 << 11)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (BusFault on unstacking for a return from exception)");
        }
        if (info->CFSR & (1 << 12)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (BusFault on stacking for exception entry)");
        }
        if (info->CFSR & (1 << 13)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (BusFault during floating-point lazy state preservation)");
        }
        if (info->CFSR & (1 << 15)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (BFAR valid)");
        }
        if (len) {
            hal_trace_output((unsigned char *)crash_buf, len);
            hal_trace_flush_buffer();
            len = 0;
        }
        if (info->CFSR & (1 << 16)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (Undefined instruction UsageFault)");
        }
        if (info->CFSR & (1 << 17)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (Invalid state UsageFault)");
        }
        if (info->CFSR & (1 << 18)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (Invalid PC load by EXC_RETURN UsageFault)");
        }
        if (info->CFSR & (1 << 19)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (No coprocessor UsageFault)");
        }
#ifdef __ARM_ARCH_8M_MAIN__
        if (info->CFSR & (1 << 20)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (Stack overflow UsageFault)");
        }
#endif
        if (info->CFSR & (1 << 24)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (Unaligned access UsageFault)");
        }
        if (info->CFSR & (1 << 25)) {
            len += snprintf(&crash_buf[len], sizeof(crash_buf) - len, " (Divide by zero UsageFault)");
        }
    }
    hal_trace_output((unsigned char *)crash_buf, len);
    hal_trace_output((const unsigned char *)newline, sizeof(newline));
}
#endif

static void hal_trace_print_fault_info(const struct EXCEPTION_INFO_T *info)
{
#ifdef __ARM_ARCH_ISA_ARM
    hal_trace_print_fault_info_ca(info);
#else
    hal_trace_print_fault_info_cm(info);
#endif
}
#endif // DEBUG || REL_TRACE_ENABLE

void hal_trace_fault_dump(const uint32_t *regs, const uint32_t *extra, uint32_t extra_len)
{
    struct EXCEPTION_INFO_T info;

    int_lock_global();

    hal_trace_fill_exception_info(&info, regs, extra, extra_len);

    *(volatile uint32_t *)RAM_BASE = (uint32_t)&info;

#if (defined(DEBUG) || defined(REL_TRACE_ENABLE))

    static const char title[] = NEW_LINE_STR "### EXCEPTION ###" NEW_LINE_STR;
    int len;

#ifdef CRASH_DUMP_ENABLE
    bool full_dump = true;

#ifdef CP_TRACE_ENABLE
    if (get_cpu_id()) {
        full_dump = false;
        if (cp_notify_cb) {
            cp_notify_cb(HAL_TRACE_STATE_CRASH_FAULT_START);
        }
    }
#endif

    if (full_dump) {
#ifdef TRACE_TO_APP
        if (app_notify_cb) {
            app_notify_cb(HAL_TRACE_STATE_CRASH_FAULT_START);
        }
        if (app_crash_custom_cb == NULL) {
            app_output_enabled = true;
        }
#endif

        crash_dump_onprocess = true;
        for (uint8_t i = 0; i < 10; i++) {
            REL_TRACE_IMM(0,"                                                                        ");
            REL_TRACE_IMM(0,"                                                                        " NEW_LINE_STR);
            hal_sys_timer_delay(MS_TO_TICKS(50));
        }
    }
#endif

    hal_trace_flush_buffer();

    hal_sysfreq_req(HAL_SYSFREQ_USER_INIT, HAL_CMU_FREQ_52M);

    len = hal_trace_print_time(LOG_LEVEL_CRITICAL, LOG_MODULE_NONE, &crash_buf[0], sizeof(crash_buf));
    if (len > 0) {
        hal_trace_output((const unsigned char *)newline, sizeof(newline));
        hal_trace_output((unsigned char *)crash_buf, len);
    }
    hal_trace_output((unsigned char *)title, sizeof(title));

    hal_trace_print_fault_info(&info);

    hal_trace_flush_buffer();

    hal_trace_print_stack(regs[13]);

    hal_trace_print_backtrace(regs[13], TRACE_BACKTRACE_SEARCH_WORD, TRACE_BACKTRACE_NUM);

    hal_trace_output((const unsigned char *)newline, sizeof(newline));

    hal_trace_flush_buffer();

#ifdef CRASH_DUMP_ENABLE
    if (full_dump) {
        hal_trace_crash_dump_callback();
        hal_trace_flush_buffer();

#ifndef __ARM_ARCH_ISA_ARM
#ifdef TRACE_TO_APP
        // Crash-Dump "Lite-Version"
        if (app_crash_custom_cb) {
            app_crash_custom_cb((unsigned char *)regs,
                           CRASH_DUMP_REGISTERS_NUM_BYTES);
            stack =  (uint32_t *)(__get_MSP() & ~3);
            app_crash_custom_cb((unsigned char *)stack,
                           ((CRASH_DUMP_STACK_NUM_BYTES)/2));
            stack =  (uint32_t *)(__get_PSP() & ~3);
            app_crash_custom_cb((unsigned char *)stack,
                          ((CRASH_DUMP_STACK_NUM_BYTES)/2));
        }
#endif

#ifdef CORE_DUMP
        {
            static CrashCatcherExceptionRegisters eregs;

            eregs.msp = info.MSP;
            eregs.psp = info.PSP;
            eregs.exceptionPSR = regs[16] & 0x0700FE00;
            eregs.r4 = regs[4];
            eregs.r5 = regs[5];
            eregs.r6 = regs[6];
            eregs.r7 = regs[7];
            eregs.r8 = regs[8];
            eregs.r9 = regs[9];
            eregs.r10 = regs[10];
            eregs.r11 = regs[11];
            eregs.exceptionLR = regs[14];
            CrashCatcher_Entry( &eregs);
        }
#endif
#endif // !__ARM_ARCH_ISA_ARM

#ifdef TRACE_TO_APP
        if (app_notify_cb) {
            app_notify_cb(HAL_TRACE_STATE_CRASH_END);
        }
#endif
    }

#ifdef CP_TRACE_ENABLE
    if (get_cpu_id()) {
        if (cp_notify_cb) {
            cp_notify_cb(HAL_TRACE_STATE_CRASH_END);
        }
        SAFE_PROGRAM_STOP();
    }
#endif
#endif // CRASH_DUMP_ENABLE

#endif // DEBUG || REL_TRACE_ENABLE

    hal_trace_crash_end();
}

#ifndef __ARM_ARCH_ISA_ARM
static void NAKED hal_trace_fault_handler(void)
{
    // TODO: Save FP registers (and check lazy Floating-point context preservation)
    asm volatile (
        // Check EXC_RETURN.SPSEL (bit[2])
        "tst lr, #0x04;"
        "ite eq;"
        // Using MSP
        "mrseq r3, msp;"
        // Using PSP
        "mrsne r3, psp;"
        // Check EXC_RETURN.FType (bit[4])
        "tst lr, #0x10;"
        "ite eq;"
        // FPU context saved
        "moveq r1, #1;"
        // No FPU context
        "movne r1, #0;"
#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
        "mov, r0, #0;"
        // -- Check EXC_RETURN.S (bit[6])
        "tst lr, #0x40;"
        "beq _done_sec_cntx;"
        // -- Check EXC_RETURN.ES (bit[0])
        "tst lr, #0x01;"
        "bne _done_sec_cntx;"
        // -- Check EXC_RETURN.DCRS (bit[5])
        "tst lr, #0x20;"
        "bne _done_sec_cntx;"
        "mov, r0, #1;"
        "push {r4-r11};"
        "add r3, #2*4;"
        "ldm r3!, {r4-r11};"
        "_done_sec_cntx:;"
        "push {r0};"
#endif
        // Make room for r0-r15,psr,primask
        "sub sp, #18*4;"
        ".cfi_def_cfa_offset 18*4;"
        // Save r4-r11
        "add r0, sp, #4*4;"
        "stm r0, {r4-r11};"
        ".cfi_offset r4, -14*4;"
        ".cfi_offset r5, -13*4;"
        ".cfi_offset r6, -12*4;"
        ".cfi_offset r7, -11*4;"
        ".cfi_offset r8, -10*4;"
        ".cfi_offset r9, -9*4;"
        ".cfi_offset r10, -8*4;"
        ".cfi_offset r11, -7*4;"
        // Save r0-r3
        "ldm r3, {r4-r7};"
        "stm sp, {r4-r7};"
        ".cfi_offset r0, -18*4;"
        ".cfi_offset r1, -17*4;"
        ".cfi_offset r2, -16*4;"
        ".cfi_offset r3, -15*4;"
        // Save r12
        "ldr r0, [r3, #4*4];"
        "str r0, [sp, #12*4];"
        ".cfi_offset r12, -6*4;"
        // Save sp
        "teq r1, 0;"
        "itt eq;"
        "addeq r0, r3, #8*4;"
        "beq _done_stack_frame;"
        "add r0, r3, #(8+18)*4;"
#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
        // -- Check EXC_RETURN.S (bit[6])
        "tst lr, #0x40;"
        "beq _done_stack_frame;"
        // -- Check FPCCR_S.TS (bit[26])
        "ldr r4, =0xE000EF34;"
        "ldr r4, [r4];"
        "tst r4, #(1 << 26);"
        "it ne;"
        "addne r3, #16*4;"
#endif
        "_done_stack_frame:;"
        // -- Check RETPSR.SPREALIGN (bit[9])
        "ldr r4, [r3, #7*4];"
        "tst r4, #(1 << 9);"
        "it ne;"
        "addne r0, #4;"
        "str r0, [sp, #13*4];"
        // Save lr
        "ldr r0, [r3, #5*4];"
        "str r0, [sp, #14*4];"
        // Save pc
        "ldr r0, [r3, #6*4];"
        "str r0, [sp, #15*4];"
        // Save PSR
        "ldr r0, [r3, #7*4];"
        "str r0, [sp, #16*4];"
        // Save primask
        "mrs r0, primask;"
        "str r0, [sp, #17*4];"
        // Save current exception lr
        "mov r4, lr;"
        ".cfi_register lr, r4;"
        // Invoke the fault handler
        "mov r0, sp;"
        "ldr r2, =hal_trace_fault_dump;"
        "blx r2;"
        // Restore current exception lr
        "mov lr, r4;"
        // Restore r4-r7
        "add r0, sp, #4*4;"
        "ldm r0, {r4-r7};"
        "mov r0, r3;"
        // Restore sp
        "add sp, #18*4;"
#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
        "pop {r0};"
        "cmp r0, #1;"
        "it eq;"
        "popeq {r4-r11};"
#endif
        "bx lr;"
        ""
        );
}
#endif
#endif

#define HAL_TRACE_RX_HEAD_SIZE   4
#define HAL_TRACE_RX_NAME_SIZE   20
#define HAL_TRACE_RX_BUF_SIZE    1024
#define HAL_TRACE_RX_ROLE_NUM    6

#define HAL_TRACE_RX_HEAD       '['
#define HAL_TRACE_RX_END        ']'
#define HAL_TRACE_RX_SEPARATOR  ','

//static struct HAL_DMA_CH_CFG_T dma_cfg_rx;
#if (defined(DEBUG) || defined(REL_TRACE_ENABLE))
static struct HAL_DMA_DESC_T dma_desc_rx;
#endif

typedef struct{
    char *name;
    uint32_t len;
    uint8_t  *buf;
}HAL_TRACE_RX_CFG_T;

typedef struct{
    char name[HAL_TRACE_RX_NAME_SIZE];
    HAL_TRACE_RX_CALLBACK_T callback;
}HAL_TRACE_RX_LIST_T;

typedef struct{
    uint32_t list_num;
    HAL_TRACE_RX_LIST_T list[HAL_TRACE_RX_ROLE_NUM];

    uint32_t rx_enable;
    uint32_t pos;
    uint8_t  buf[HAL_TRACE_RX_BUF_SIZE];
}HAL_TRACE_RX_T;

#if defined(_AUTO_TEST_)
extern int auto_test_prase(uint8_t *cmd);
#endif

HAL_TRACE_RX_T hal_trace_rx;

int hal_trace_rx_dump_list(void)
{
    for(int i=0;i<HAL_TRACE_RX_ROLE_NUM;i++)
    {
        TRACE(2,"%d: %s", i, hal_trace_rx.list[i].name);
    }
    return 0;
}

int hal_trace_rx_is_in_list(const char *name)
{
    for(int i=0;i<HAL_TRACE_RX_ROLE_NUM;i++)
    {
        if(!strcmp(hal_trace_rx.list[i].name, name))
        {
            return i;
        }
    }
    hal_trace_rx_dump_list();
    // TRACE(1,"%s", hal_trace_rx.list[0].name);
    // TRACE(1,"%s", name);
    // TRACE(1,"%d", strlen(hal_trace_rx.list[0].name));
    // TRACE(1,"%d", strlen(name));
    // TRACE(1,"%d", strcmp(hal_trace_rx.list[0].name, name));
    return -1;
}

int hal_trace_rx_add_item_to_list(const char *name, HAL_TRACE_RX_CALLBACK_T callback)
{
    for(int i=0;i<HAL_TRACE_RX_ROLE_NUM;i++)
    {
        if(hal_trace_rx.list[i].name[0] == 0)
        {
            memcpy(hal_trace_rx.list[i].name, name, strlen(name));
            hal_trace_rx.list[i].callback = callback;
            hal_trace_rx.list_num++;
            return 0;
        }
    }

    return 1;
}

int hal_trace_rx_del_item_to_list(int id)
{
    memset(hal_trace_rx.list[id].name, 0, sizeof(hal_trace_rx.list[id].name));
    hal_trace_rx.list[id].callback = NULL;
    hal_trace_rx.list_num--;

    return 0;
}

int hal_trace_rx_register(const char *name, HAL_TRACE_RX_CALLBACK_T callback)
{
    TRACE(2,"[%s] Add %s", __func__, name);
    if(hal_trace_rx_is_in_list(name) == -1)
    {
        hal_trace_rx_add_item_to_list(name, callback);
        return 0;
    }
    else
    {
        return 0;
    }
}

int hal_trace_rx_deregister(const char *name)
{
    int id = 0;

    id = hal_trace_rx_is_in_list(name);

    if(id != -1)
    {
        hal_trace_rx_del_item_to_list(id);
        return 0;
    }
    else
    {
        return 1;
    }
}

#if (defined(DEBUG) || defined(REL_TRACE_ENABLE))
static int hal_trace_rx_reset(void)
{

    hal_trace_rx.pos = 0;
    memset(hal_trace_rx.buf, 0, HAL_TRACE_RX_BUF_SIZE);

    return 0;
}
#endif
// [test,12,102.99]
static int hal_trace_rx_parse(int8_t *buf, HAL_TRACE_RX_CFG_T *cfg)
{
    // TRACE(1,"[%s] Start...", __func__);
    int pos = 0;
    int len = 0;

    for(; pos<HAL_TRACE_RX_HEAD_SIZE; pos++)
    {
        if(buf[pos] == HAL_TRACE_RX_HEAD)
        {
            buf[pos] = 0;
            break;
        }
    }

    if(pos == HAL_TRACE_RX_HEAD_SIZE)
    {
        return 3;
    }

    pos++;

    cfg->name = (char *)(buf+pos);
    for(; pos<HAL_TRACE_RX_NAME_SIZE+HAL_TRACE_RX_HEAD_SIZE; pos++)
    {
        if(buf[pos] == HAL_TRACE_RX_SEPARATOR)
        {
            buf[pos] = 0;
            break;
        }
    }

    // TRACE(1,"Step1: %s", cfg->name);
    // TRACE(1,"%d", strlen(cfg->name));

    if(pos == HAL_TRACE_RX_NAME_SIZE)
    {
        return 1;
    }

    pos++;

    len = 0;
    cfg->buf = (uint8_t*)(buf+pos);
    for(; pos<HAL_TRACE_RX_BUF_SIZE; pos++)
    {
        if(buf[pos] == HAL_TRACE_RX_END)
        {
            buf[pos] = 0;
            break;
        }
        len++;
    }
    cfg->len = len;
    if(pos == HAL_TRACE_RX_BUF_SIZE)
    {
        return 2;
    }

    return 0;
}

#if defined(IBRT)
void app_ibrt_peripheral_automate_test(const char* ibrt_cmd, uint32_t cmd_len);
void app_ibrt_peripheral_perform_test(const char* ibrt_cmd);
#endif

int hal_trace_rx_process(uint8_t *buf, uint32_t len)
{
    HAL_TRACE_RX_CFG_T cfg;
    int id = 0;
    int res = 0;

#if defined(IBRT)
    if(buf && strlen((char*)buf) >= 10 &&((strncmp((char*)buf, "auto_test:", 10) == 0)||(strncmp((char*)buf, "ibrt_test:", 10) == 0)))
    {
#ifdef BES_AUTOMATE_TEST
        app_ibrt_peripheral_automate_test((char*)(buf + 10), len - 10);
#else
        app_ibrt_peripheral_perform_test((char*)(buf + 10));
#endif
        return 0;
    }
#endif

    res = hal_trace_rx_parse((int8_t*)buf, &cfg);

    if(res)
    {
        TRACE(1,"ERROR: hal_trace_rx_parse %d", res);
        return 1;
    }
    else
    {
        // TRACE(1,"%s rx OK", cfg.name);
    }

    id = hal_trace_rx_is_in_list(cfg.name);

    if(id == -1)
    {
        TRACE(1,"%s is invalid", cfg.name);
        return -1;
    }

    if(hal_trace_rx.list[id].callback)
    {
        hal_trace_rx.list[id].callback(cfg.buf, cfg.len);
    }
    else
    {
        TRACE(1,"%s has not callback", hal_trace_rx.list[id].name);
    }

    return 0;
}
#if (defined(DEBUG) || defined(REL_TRACE_ENABLE))
void hal_trace_rx_start(void)
{
    uint32_t desc_cnt = 1;
    union HAL_UART_IRQ_T mask;

    mask.reg = 0;
    mask.BE = 0;
    mask.FE = 0;
    mask.OE = 0;
    mask.PE = 0;
    mask.RT = 1;

    hal_uart_dma_recv_mask(trace_uart, hal_trace_rx.buf, HAL_TRACE_RX_BUF_SIZE, &dma_desc_rx, &desc_cnt, &mask);
}

void hal_trace_rx_irq_handler(uint32_t xfer_size, int dma_error, union HAL_UART_IRQ_T status)
{
    int res;
    // TRACE(4,"[%s] %d, %d, %d", __func__, xfer_size, dma_error, status);

    if (xfer_size)
    {
        hal_trace_rx.buf[xfer_size] = 0;
 #if defined(_AUTO_TEST_)
        res = auto_test_prase(hal_trace_rx.buf);
        if(res)
        {
            TRACE(2,"%s:auto_test_prase prase data error, err_code = %d", __func__, res);
        }
 #else
        //TRACE(2,"[%s] RX = %s", __func__, hal_trace_rx.buf);
        res = hal_trace_rx_process(hal_trace_rx.buf, xfer_size);
        if(res)
        {
            TRACE(2,"%s:hal_trace_rx_process prase data error, err_code = %d",__func__, res);
        }
 #endif
        hal_trace_rx_reset();
        hal_trace_rx_start();
    }
}

uint32_t app_test_callback(unsigned char *buf, uint32_t len)
{
    TRACE(2,"[%s] len = %d", __func__, len);

    // Process string
    int num_int = 0;
    int num_float = 0.0;
    TRACE(2,"[%s] %s", __func__, buf);
    hal_trace_rx_parser((char*)buf, "%d,%d", &num_int, &num_float);

    TRACE(3,"[%s] %d:%d", __func__, num_int, num_float);

    return 0;
}

int hal_trace_rx_open()
{
    hal_uart_irq_set_dma_handler(trace_uart, hal_trace_rx_irq_handler, NULL);
    hal_trace_rx_start();

    hal_trace_rx_register("test", (HAL_TRACE_RX_CALLBACK_T)app_test_callback);

    return 0;
}

int hal_trace_rx_reopen()
{
    hal_uart_reopen(trace_uart, &uart_rx_enable_cfg);
    hal_trace_rx_open();
	
	return 0;
}
#endif
