/******************************************************************************* * Copyright 2019-2021 Microchip FPGA Embedded Systems Solutions. * * SPDX-License-Identifier: MIT * * MPFS HAL Embedded Software * */ /******************************************************************************* * @file newlib_stubs.c * @author Microchip-FPGA Embedded Systems Solutions * @brief Stubs for Newlib system calls. * */ #include #include #include #include #include #include "../mss_hal.h" /*============================================================================== * Redirection of standard output to a SmartFusion2 MSS UART. *------------------------------------------------------------------------------ * A default implementation for the redirection of the output of printf() to a * UART is provided at the bottom of this file. This redirection is enabled by * adding the symbol/define MICROCHIP_STDIO_THRU_MMUART0 or * MICROCHIP_STDIO_THRU_MMUART0 to your project settings and specifying the baud * rate using the MICROCHIP_STDIO_BAUD_RATE define. */ #ifdef MICROCHIP_STDIO_THRU_MMUART0 #ifndef MICROCHIP_STDIO_THRU_UART #define MICROCHIP_STDIO_THRU_UART #endif #endif /* MICROCHIP_STDIO_THRU_MMUART0 */ #ifdef MICROCHIP_STDIO_THRU_MMUART1 #ifndef MICROCHIP_STDIO_THRU_UART #define MICROCHIP_STDIO_THRU_UART #endif #endif /* MICROCHIP_STDIO_THRU_MMUART1 */ /* * Select which MMUART will be used for stdio and what baud rate will be used. * Default to 57600 baud if no baud rate is specified using the * MICROCHIP_STDIO_BAUD_RATE #define. */ #ifdef MICROCHIP_STDIO_THRU_UART #include "drivers/mss/mss_mmuart/mss_uart.h" #ifndef MICROCHIP_STDIO_BAUD_RATE #define MICROCHIP_STDIO_BAUD_RATE MSS_UART_115200_BAUD #endif #ifdef MICROCHIP_STDIO_THRU_MMUART0 static mss_uart_instance_t * const gp_my_uart = &g_mss_uart0; #else static mss_uart_instance_t * const gp_my_uart = &g_mss_uart1; #endif /*------------------------------------------------------------------------------ * Global flag used to indicate if the UART driver needs to be initialized. */ static int g_stdio_uart_init_done = 0; #endif /* MICROCHIP_STDIO_THRU_UART */ /*============================================================================== * Environment variables. * A pointer to a list of environment variables and their values. For a minimal * environment, this empty list is adequate: */ char *__env[1] = { 0 }; char **environ = __env; /*============================================================================== * Close a file. */ int _close(int file); int _close(int file) { (void)file; return -1; } /*============================================================================== * Transfer control to a new process. */ int _execve(char *name, char **argv, char **env); int _execve(char *name, char **argv, char **env) { (void)name; (void)argv; (void)env; errno = ENOMEM; return -1; } /*============================================================================== * Exit a program without cleaning up files. */ void _exit( int code ) { (void)code; /* Should we force a system reset? */ while( 1 ) { ; } } /*============================================================================== * Create a new process. */ int _fork(void); int _fork(void) { errno = EAGAIN; return -1; } /*============================================================================== * Status of an open file. */ int _fstat(int file, struct stat *st); int _fstat(int file, struct stat *st) { (void)file; st->st_mode = S_IFCHR; return (0); } /*============================================================================== * Process-ID */ int _getpid(void); int _getpid(void) { return (1); } /*============================================================================== * Query whether output stream is a terminal. */ int _isatty(int file); int _isatty(int file) { (void)file; return (1); } /*============================================================================== * Send a signal. */ int _kill(int pid, int sig); int _kill(int pid, int sig) { (void)pid; (void)sig; errno = EINVAL; return (-1); } /*============================================================================== * Establish a new name for an existing file. */ int _link(char *old, char *new); int _link(char *old, char *new) { (void)old; (void)new; errno = EMLINK; return (-1); } /*============================================================================== * Set position in a file. */ int _lseek(int file, int ptr, int dir); int _lseek(int file, int ptr, int dir) { (void)file; (void)ptr; (void)dir; return (0); } /*============================================================================== * Open a file. */ int _open(const char *name, int flags, int mode); int _open(const char *name, int flags, int mode) { (void)name; (void)flags; (void)mode; return (-1); } /*============================================================================== * Read from a file. */ int _read(int file, char *ptr, int len); int _read(int file, char *ptr, int len) { (void)file; (void)ptr; (void)len; return (0); } /*============================================================================== * Write to a file. libc subroutines will use this system routine for output to * all files, including stdout so if you need to generate any output, for * example to a serial port for debugging, you should make your minimal write * capable of doing this. */ int _write_r( void * reent, int file, char * ptr, int len ); int _write_r( void * reent, int file, char * ptr, int len ) { (void)reent; (void)file; (void)ptr; (void)len; #ifdef MICROCHIP_STDIO_THRU_UART /*-------------------------------------------------------------------------- * Initialize the UART driver if it is the first time this function is * called. */ if(!g_stdio_uart_init_done) { MSS_UART_init(gp_my_uart, MICROCHIP_STDIO_BAUD_RATE, MSS_UART_DATA_8_BITS | MSS_UART_NO_PARITY); g_stdio_uart_init_done = 1; } /*-------------------------------------------------------------------------- * Output text to the UART. */ MSS_UART_polled_tx(gp_my_uart, (uint8_t *)ptr, len); return len; #else /* MICROCHIP_STDIO_THRU_UART */ return (0); #endif /* MICROCHIP_STDIO_THRU_UART */ } /*============================================================================== * Increase program data space. As malloc and related functions depend on this, * it is useful to have a working implementation. The following suffices for a * standalone system; it exploits the symbol _end automatically defined by the * GNU linker. */ caddr_t _sbrk(int incr); caddr_t _sbrk(int incr) { extern char _end; /* Defined by the linker */ extern char __heap_start; extern char __heap_end; static char *heap_end; char *prev_heap_end; (void)__heap_start; (void)__heap_end; #ifdef DEBUG_HEAP_SIZE char * stack_ptr = NULL; #endif /* * Did we allocated memory for the heap in the linker script? * You need to set HEAP_SIZE to a non-zero value in your linker script if * the following assertion fires. */ ASSERT(&__heap_end > &__heap_start); if (heap_end == NULL) { heap_end = &_end; } prev_heap_end = heap_end; #ifdef DEBUG_HEAP_SIZE /* add this define if you want to debug crash due to overflow of heap */ /* fixme- this test needs to be reworked to take account of multiple harts and TLS */ stack_ptr = read_csr(sp); /* stack_ptr has just been placed on the stack, so its address in currently pointing to the stack end */ if(heap_end < stack_ptr) { /* * Heap is at an address below the stack, growing up toward the stack. * The stack is above the heap, growing down towards the heap. * Make sure the stack and heap do not run into each other. */ if (heap_end + incr > stack_ptr) { _write_r ((void *)0, 1, "Heap and stack collision\n", 25); _exit (1); } } else { /* * If the heap and stack are not growing towards each other then use the * _eheap linker script symbol to figure out if there is room left on * the heap. * Please note that this use case makes sense when the stack is located * in internal eSRAM in the 0x20000000 address range and the heap is * located in the external memory in the 0xA0000000 memory range. * Please note that external memory should not be accessed using the * 0x00000000 memory range to read/write variables/data because of the * SmartFusion2 cache design. */ extern char _heap_end; /* Defined by the linker */ char *top_of_heap; top_of_heap = &_heap_end; if(heap_end + incr > top_of_heap) { _write_r ((void *)0, 1, "Out of heap memory\n", 25); _exit (1); } } #endif heap_end += incr; /* * Did we run out of heap? * You need to increase the heap size in the linker script if the following * assertion fires. * */ ASSERT(heap_end <= &__heap_end); return ((caddr_t) prev_heap_end); } /*============================================================================== * Status of a file (by name). */ int _stat(char *file, struct stat *st); int _stat(char *file, struct stat *st) { (void)file; st->st_mode = S_IFCHR; return 0; } /*============================================================================== * Timing information for current process. */ int _times(struct tms *buf); int _times(struct tms *buf) { (void)buf; return (-1); } /*============================================================================== * Remove a file's directory entry. */ int _unlink(char *name); int _unlink(char *name) { (void)name; errno = ENOENT; return (-1); } /*============================================================================== * Wait for a child process. */ int _wait(int *status); int _wait(int *status) { (void)status; errno = ECHILD; return (-1); }