blob: 486df612488df44f2c162e7f0ba1432780c1069d [file] [log] [blame]
/**************************************************************************//**
* @file retarget.c
* @version V3.00
* @brief Debug Port and Semihost Setting Source File
*
* @note
* Copyright (C) 2016 Nuvoton Technology Corp. All rights reserved.
*
******************************************************************************/
#include <stdio.h>
#include "NuMicro.h"
#if defined (__ICCARM__)
# pragma diag_suppress=Pm150
#endif
#if defined ( __CC_ARM )
#if (__ARMCC_VERSION < 400000)
#else
/* Insist on keeping widthprec, to avoid X propagation by benign code in C-lib */
#pragma import _printf_widthprec
#endif
#endif
/*---------------------------------------------------------------------------------------------------------*/
/* Global variables */
/*---------------------------------------------------------------------------------------------------------*/
#if !(defined(__ICCARM__) && (__VER__ >= 6010000))
# if (__ARMCC_VERSION < 6040000)
struct __FILE
{
int handle; /* Add whatever you need here */
};
# endif
#elif(__VER__ >= 8000000)
struct __FILE
{
int handle; /* Add whatever you need here */
};
#endif
FILE __stdout;
FILE __stdin;
#if (defined(__ARMCC_VERSION) || defined(__ICCARM__))
extern int32_t SH_DoCommand(int32_t n32In_R0, int32_t n32In_R1, int32_t *pn32Out_R0);
#if defined( __ICCARM__ )
__WEAK
#else
__attribute__((weak))
#endif
uint32_t ProcessHardFault(uint32_t lr, uint32_t msp, uint32_t psp);
#endif
int kbhit(void);
int IsDebugFifoEmpty(void);
void _ttywrch(int ch);
int fputc(int ch, FILE *stream);
#if (defined(__ARMCC_VERSION) || defined(__ICCARM__))
int fgetc(FILE *stream);
int ferror(FILE *stream);
#endif
char GetChar(void);
void SendChar_ToUART(int ch);
void SendChar(int ch);
#if defined(DEBUG_ENABLE_SEMIHOST)
#if (defined(__ARMCC_VERSION) || defined(__ICCARM__))
/* The static buffer is used to speed up the semihost */
static char g_buf[16];
static char g_buf_len = 0;
static volatile int32_t g_ICE_Conneced = 1;
/**
* @brief This function is called by Hardfault handler.
* @param None
* @returns None
* @details This function is called by Hardfault handler and check if it is caused by __BKPT or not.
*
*/
uint32_t ProcessHardFault(uint32_t lr, uint32_t msp, uint32_t psp)
{
uint32_t *sp;
uint32_t inst;
/* Check the used stack */
if(lr & 0x40)
{
/* Secure stack used */
if(lr & 4)
sp = (uint32_t *)psp;
else
sp = (uint32_t *)msp;
}
#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
else
{
/* Non-secure stack used */
if(lr & 4)
sp = (uint32_t *)__TZ_get_PSP_NS();
else
sp = (uint32_t *)__TZ_get_MSP_NS();
}
#endif
/* Get the instruction caused the hardfault */
inst = M16(sp[6]);
if(inst == 0xBEAB)
{
/*
If the instruction is 0xBEAB, it means it is caused by BKPT without ICE connected.
We still return for output/input message to UART.
*/
g_ICE_Conneced = 0; // Set a flag for ICE offline
sp[6] += 2; // return to next instruction
return lr; // Keep lr in R0
}
/* It is casued by hardfault (Not semihost). Just process the hard fault here. */
/* TODO: Implement your hardfault handle code here */
/*
printf(" HardFault!\n\n");
printf("r0 = 0x%x\n", sp[0]);
printf("r1 = 0x%x\n", sp[1]);
printf("r2 = 0x%x\n", sp[2]);
printf("r3 = 0x%x\n", sp[3]);
printf("r12 = 0x%x\n", sp[4]);
printf("lr = 0x%x\n", sp[5]);
printf("pc = 0x%x\n", sp[6]);
printf("psr = 0x%x\n", sp[7]);
*/
while(1){}
}
/**
*
* @brief The function to process semihosted command
* @param[in] n32In_R0 : semihost register 0
* @param[in] n32In_R1 : semihost register 1
* @param[out] pn32Out_R0: semihost register 0
* @retval 0: No ICE debug
* @retval 1: ICE debug
*
*/
int32_t SH_Return(int32_t n32In_R0, int32_t n32In_R1, int32_t *pn32Out_R0)
{
if(g_ICE_Conneced)
{
if(pn32Out_R0)
*pn32Out_R0 = n32In_R0;
return 1;
}
return 0;
}
#endif
#else // defined(DEBUG_ENABLE_SEMIHOST)
int32_t SH_Return(int32_t n32In_R0, int32_t n32In_R1, int32_t *pn32Out_R0);
#if defined( __ICCARM__ )
__WEAK
#else
__attribute__((weak))
#endif
uint32_t ProcessHardFault(uint32_t lr, uint32_t msp, uint32_t psp)
{
uint32_t *sp;
/* It is casued by hardfault. Just process the hard fault */
/* TODO: Implement your hardfault handle code here */
/* Check the used stack */
if(lr & 0x40UL)
{
/* Secure stack used */
if(lr & 4UL)
{
sp = (uint32_t *)psp;
}
else
{
sp = (uint32_t *)msp;
}
}
#if defined (__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3)
else
{
/* Non-secure stack used */
if(lr & 4)
sp = (uint32_t *)__TZ_get_PSP_NS();
else
sp = (uint32_t *)__TZ_get_MSP_NS();
}
#endif
/*
printf(" HardFault!\n\n");
printf("r0 = 0x%x\n", sp[0]);
printf("r1 = 0x%x\n", sp[1]);
printf("r2 = 0x%x\n", sp[2]);
printf("r3 = 0x%x\n", sp[3]);
printf("r12 = 0x%x\n", sp[4]);
printf("lr = 0x%x\n", sp[5]);
printf("pc = 0x%x\n", sp[6]);
printf("psr = 0x%x\n", sp[7]);
*/
/* Or *sp to remove compiler warning */
while(1U|*sp){}
return lr;
}
int32_t SH_Return(int32_t n32In_R0, int32_t n32In_R1, int32_t *pn32Out_R0)
{
return 0;
}
#endif /* defined(DEBUG_ENABLE_SEMIHOST) */
/**
* @brief Routine to send a char
*
* @param[in] ch A character data writes to debug port
*
* @returns Send value from UART debug port
*
* @details Send a target char to UART debug port .
*/
#ifndef NONBLOCK_PRINTF
void SendChar_ToUART(int ch)
{
while(DEBUG_PORT->FIFOSTS & UART_FIFOSTS_TXFULL_Msk){}
DEBUG_PORT->DAT = (uint32_t)ch;
if((char)ch == '\n')
{
while(DEBUG_PORT->FIFOSTS & UART_FIFOSTS_TXFULL_Msk){}
DEBUG_PORT->DAT = '\r';
}
}
#else
/* Non-block implement of send char */
# define BUF_SIZE 512
void SendChar_ToUART(int ch)
{
static uint8_t u8Buf[BUF_SIZE] = {0};
static int32_t i32Head = 0;
static int32_t i32Tail = 0;
int32_t i32Tmp;
/* Only flush the data in buffer to UART when ch == 0 */
if(ch)
{
// Push char
i32Tmp = i32Head+1;
if(i32Tmp > BUF_SIZE) i32Tmp = 0;
if(i32Tmp != i32Tail)
{
u8Buf[i32Head] = ch;
i32Head = i32Tmp;
}
if(ch == '\n')
{
i32Tmp = i32Head+1;
if(i32Tmp > BUF_SIZE) i32Tmp = 0;
if(i32Tmp != i32Tail)
{
u8Buf[i32Head] = '\r';
i32Head = i32Tmp;
}
}
}
else
{
if(i32Tail == i32Head)
return;
}
// pop char
do
{
i32Tmp = i32Tail + 1;
if(i32Tmp > BUF_SIZE) i32Tmp = 0;
if((DEBUG_PORT->FSR & UART_FSR_TX_FULL_Msk) == 0)
{
DEBUG_PORT->DATA = u8Buf[i32Tail];
i32Tail = i32Tmp;
}
else
break; // FIFO full
}while(i32Tail != i32Head);
}
#endif
/**
* @brief Routine to send a char
*
* @param[in] ch A character data writes to debug port
*
* @returns Send value from UART debug port or semihost
*
* @details Send a target char to UART debug port or semihost.
*/
void SendChar(int ch)
{
#if defined(DEBUG_ENABLE_SEMIHOST)
g_buf[g_buf_len++] = ch;
g_buf[g_buf_len] = '\0';
if(g_buf_len + 1 >= sizeof(g_buf) || ch == '\n' || ch == '\0')
{
/* Send the char */
if(g_ICE_Conneced)
{
if(SH_DoCommand(0x04, (int)g_buf, NULL) != 0)
{
g_buf_len = 0;
return;
}
}
else
{
# if (DEBUG_ENABLE_SEMIHOST == 2) // Re-direct to UART Debug Port only when DEBUG_ENABLE_SEMIHOST=2
int i;
for(i = 0; i < g_buf_len; i++)
SendChar_ToUART(g_buf[i]);
g_buf_len = 0;
# endif
}
}
#else
SendChar_ToUART(ch);
#endif
}
/**
* @brief Routine to get a char
*
* @param None
*
* @returns Get value from UART debug port or semihost
*
* @details Wait UART debug port or semihost to input a char.
*/
char GetChar(void)
{
#ifdef DEBUG_ENABLE_SEMIHOST
# if defined (__ICCARM__)
int nRet;
while(SH_DoCommand(0x7, 0, &nRet) != 0)
{
if(nRet != 0)
return (char)nRet;
}
# else
int nRet;
while(SH_DoCommand(0x101, 0, &nRet) != 0)
{
if(nRet != 0)
{
SH_DoCommand(0x07, 0, &nRet);
return (char)nRet;
}
}
# if (DEBUG_ENABLE_SEMIHOST == 2) // Re-direct to UART Debug Port only when DEBUG_ENABLE_SEMIHOST=2
/* Use debug port when ICE is not connected at semihost mode */
while(!g_ICE_Conneced)
{
if((DEBUG_PORT->FIFOSTS & UART_FIFOSTS_RXEMPTY_Msk) == 0)
{
return (DEBUG_PORT->DAT);
}
}
# endif
# endif
return (0);
#else
while(1)
{
if((DEBUG_PORT->FIFOSTS & UART_FIFOSTS_RXEMPTY_Msk) == 0U)
{
return ((char)DEBUG_PORT->DAT);
}
}
#endif
}
/**
* @brief Check any char input from UART
*
* @param None
*
* @retval 1: No any char input
* @retval 0: Have some char input
*
* @details Check UART RSR RX EMPTY or not to determine if any char input from UART
*/
int kbhit(void)
{
return !((DEBUG_PORT->FIFOSTS & UART_FIFOSTS_RXEMPTY_Msk) == 0U);
}
/**
* @brief Check if debug message finished
*
* @param None
*
* @retval 1: Message is finished
* @retval 0: Message is transmitting.
*
* @details Check if message finished (FIFO empty of debug port)
*/
int IsDebugFifoEmpty(void)
{
return ((DEBUG_PORT->FIFOSTS & UART_FIFOSTS_TXEMPTYF_Msk) != 0U);
}
/**
* @brief C library retargetting
*
* @param[in] ch Write a character data
*
* @returns None
*
* @details Check if message finished (FIFO empty of debug port)
*/
void _ttywrch(int ch)
{
SendChar(ch);
return;
}
/**
* @brief Write character to stream
*
* @param[in] ch Character to be written. The character is passed as its int promotion.
* @param[in] stream Pointer to a FILE object that identifies the stream where the character is to be written.
*
* @returns If there are no errors, the same character that has been written is returned.
* If an error occurs, EOF is returned and the error indicator is set (see ferror).
*
* @details Writes a character to the stream and advances the position indicator.\n
* The character is written at the current position of the stream as indicated \n
* by the internal position indicator, which is then advanced one character.
*
* @note The above descriptions are copied from http://www.cplusplus.com/reference/clibrary/cstdio/fputc/.
*
*
*/
int fputc(int ch, FILE *stream)
{
SendChar(ch);
return ch;
}
#if defined ( __GNUC__ )
#if !defined(OS_USE_SEMIHOSTING)
int _write (int fd, char *ptr, int len)
{
int i = len;
while(i--) {
while(DEBUG_PORT->FIFOSTS & UART_FIFOSTS_TXFULL_Msk);
DEBUG_PORT->DAT = *ptr++;
if(*ptr == '\n') {
while(DEBUG_PORT->FIFOSTS & UART_FIFOSTS_TXFULL_Msk);
DEBUG_PORT->DAT = '\r';
}
}
return len;
}
int _read (int fd, char *ptr, int len)
{
while((DEBUG_PORT->FIFOSTS & UART_FIFOSTS_RXEMPTY_Msk) != 0);
*ptr = DEBUG_PORT->DAT;
return 1;
}
#endif
#else
/**
* @brief Get character from UART debug port or semihosting input
*
* @param[in] stream Pointer to a FILE object that identifies the stream on which the operation is to be performed.
*
* @returns The character read from UART debug port or semihosting
*
* @details For get message from debug port or semihosting.
*
*/
int fgetc(FILE *stream)
{
return ((int)GetChar());
}
/**
* @brief Check error indicator
*
* @param[in] stream Pointer to a FILE object that identifies the stream.
*
* @returns If the error indicator associated with the stream was set, the function returns a nonzero value.
* Otherwise, it returns a zero value.
*
* @details Checks if the error indicator associated with stream is set, returning a value different
* from zero if it is. This indicator is generally set by a previous operation on the stream that failed.
*
* @note The above descriptions are copied from http://www.cplusplus.com/reference/clibrary/cstdio/ferror/.
*
*/
int ferror(FILE *stream)
{
return EOF;
}
#endif
#ifdef DEBUG_ENABLE_SEMIHOST
# ifdef __ICCARM__
void __exit(int return_code)
{
/* Check if link with ICE */
if(SH_DoCommand(0x18, 0x20026, NULL) == 0)
{
/* Make sure all message is print out */
while(IsDebugFifoEmpty() == 0);
}
label:
goto label; /* endless loop */
}
# else
void _sys_exit(int return_code)
{
/* Check if link with ICE */
if(SH_DoCommand(0x18, 0x20026, NULL) == 0)
{
/* Make sure all message is print out */
while(IsDebugFifoEmpty() == 0);
}
label:
goto label; /* endless loop */
}
# endif
#endif
/*** (C) COPYRIGHT 2016 Nuvoton Technology Corp. ***/