A target cross-toolchain is located outside the target directory. Under Unix (or Unix-emulation) it is placed in some directory like /opt/BeagleBoneBlack/. There are 4 main tools: C/C++ compiler, assembler, linker and archiver. These tools must be built to be aligned with the XCore. Linker must produce an output, suitable for the target board. Usually it means the linker script must be prepared with the target board memory configuration. Compiler libraries must be compatible with the XCore. I.e. they must use XCore synchronization primitives and memory allocation functions. XCore contains the following C standard library files: assert.cpp, stdlib.cpp, stdio.cpp and time.cpp. They implements a part of the C standard library and expects the C standard include headers be written in a particular way. You may find a correct example in the target Vanilla-X sysroot directory.
This file implements the function __std_assert_failed. Its prototype in the assert.h is
/* __std_assert_failed() */
extern int __std_assert_failed(const char *cond,const char *file_name,int line,const char *func_name);
It is used to implement the macro assert:
/* __std_assert_failed() */ extern int __std_assert_failed(const char *cond,const char *file_name,int line,const char *func_name); /* assert() */ #ifdef NDEBUG #define assert(ignore) ((void)0) #else #define assert(cond) ((void)((cond)?0:__std_assert_failed(#cond,__FILE__,__LINE__,__func__))) #endif
This file implements the functions clock() and time(). These functions are declared in the header time.h as
/* clock() */ #define CLOCKS_PER_SEC ???? typedef unsigned long long clock_t; extern clock_t clock(void); /* time() */ typedef unsigned time_t; extern time_t time(time_t *time_ptr);
The type clock_t must have the same size as Sys::ClockTimeType.
The type time_t must have the same size as Sys::SecTimeType.
The constant CLOCKS_PER_SEC must have the same value as Sys::ClocksPerSec.
This file implements the memory allocation functions with extra functions. They are declared in the header stdlib.h as
/* malloc() */ extern void * malloc(size_t size); extern void * calloc(size_t elem_count,size_t elem_size); extern void * realloc(void *mem,size_t size); extern void free(void *mem); /* malloc_int() */ extern void * malloc_int(size_t size); /* ext function */ extern void free_int(void *mem); /* ext function */
Two extra memory allocation functions malloc_int() and free_int() give access to the interrupt heap. They may be called in any execution context and must be used in C/C++ language support libraries to allocate/free memory for various context data (for example, for exception objects).
This is a large file with an stdio library implementation. You may find a proper header stdio.h in the Vanilla-X sysroot directory.
STD implementation is a particular way to implement some very basic target functionality. It uses an extension to the C standard library. The most of these extended functions are declared in the file __std_init.h. These functions are implemented mostly using the assembler. They are archived with the standard C library functions in the cross-toolchain. This file defines the following entities:
#ifndef XCore___std_init_h #define XCore___std_init_h #ifdef __cplusplus extern "C" { #endif /* types */ typedef unsigned __std_len_t; /* init */ extern void __std_init(void); /* main */ extern void __std_main(void); /* exit */ extern void __std_exit(void); /* constructors/destructors */ typedef void (*__std_init_t)(void); extern __std_init_t * __std_get_init_base(void); extern __std_init_t * __std_get_init_lim(void); /* abort */ extern void __std_abort(const char *zstr) __attribute__((noreturn)) ; extern void __std_abort2(const char *ptr,__std_len_t len) __attribute__((noreturn)) ; /* mem */ /* all MaxAlign-ed */ extern __std_len_t __std_get_heap_int_len(void); extern __std_len_t __std_get_heap_len(void); extern __std_len_t __std_get_syslog_len(void); extern void * __std_alloc(__std_len_t len); /* shared mem */ /* all MaxAlign-ed */ extern void * __std_get_shared_mem(void); extern __std_len_t __std_get_shared_mem_len(void); /* video mem */ /* all MaxAlign-ed */ extern void * __std_get_video_mem(void); extern __std_len_t __std_get_video_mem_len(void); /* int */ typedef void (*__std_handler_t)(void); extern void __std_intsetup(__std_handler_t handler); extern void __std_intcleanup(void); /* context */ /* all MaxAlign-ed */ extern void * __std_context; extern __std_len_t __std_context_len(void); typedef void (*__std_entry_t)(void *arg); extern void __std_context_init(void *context, void *stack, __std_len_t stack_len, __std_entry_t entry, void *arg); extern void __std_switch(void *context); // int disabled #ifdef __cplusplus } // extern "C" #endif #endif
__std_len_t defines the "size type", i.e. it is a substitute for size_t.
__std_init() this function is called by the startup code to perform initialization actions.
__std_main() this function is called by the startup code to run the main functionality.
__std_exit() this function is called by the startup code to perform cleanup actions. These three functions are implemented in the CCore target, in the __std_init.cpp file.
These functions supports the global object's constructors call. There are different ways how the compiler may implement initialization of global objects. One of them is the following: compiler and linker produces a section with the initialization function pointers. These functions must be called in order of appearance. Each constructor register the correspondent destructor to be called using the atexit mechanism. So destructors are called during the atexit processing and not required any additional data to be generated.
__std_init_t is the initialization function type.
__std_get_init_base() returns the base of the initialization function section.
__std_get_init_lim() returns the limit of the initialization function section.
__std_abort() aborts the execution with the diagnostic message. Message is given as the zero-terminated string. It can be send out to a serial port, for example, or stored in a flash memory for further extraction. Abort means the execution termination and may reset the board or stop it.
__std_abort2() another abort function, the message is given as the pointer/length couple.
__std_get_heap_int_len() returns the length of the interrupt heap memory region. This value is aligned. This memory region is used to build the interrupt heap.
__std_get_heap_len() returns the length of the heap memory region. This value is aligned. This memory region is used to build the main heap.
__std_get_syslog_len() returns the length of the region. This value is aligned. This memory region is used to build the system log.
__std_alloc() is used to allocate memory from the available board memory. This function is called once per each memory region to obtain memory for this region.
__std_get_shared_mem() returns the base address of the shared memory. This value is aligned. Shared memory can be used to exchange data between CPU and peripheral devices, this memory region is not cached. This memory region is used to build the shared heap.
__std_get_shared_mem_len() returns the length of the shared memory. This value is aligned.
__std_get_video_mem() returns the base address of the video memory. This value is aligned. If the board has no video capabilities this region can be null. Normally this memory region is not cached.
__std_get_video_mem_len() returns the length of the video memory. This value is aligned.
__std_handler_t is the interrupt function type.
__std_intsetup() initializes the interrupt handle subsystem and installs the primary interrupt handler. All interrupts goes to this function. To determine a particular interrupt source the handler must examine the interrupt controller register values. On systems with the multiple interrupt vectors it can be simulated using the global status variables instead of registers. The primary interupt handler is called in the interrupt context, interrupts are disabled. Interrupts in interrupts are not supported.
__std_intcleanup() cleans up the interrupt handle subsystem. Both functions must be called with interrupts disabled.
__std_context is the context of the current task. The task context is region of memory, attached to each task. It is used to preserve some per-task data while task is suspended. These data includes, in particular, CPU register values.
__std_context_len() is the context task length in bytes.
__std_entry_t is the task entry function type.
__std_context_init() initializes the new task context. To prepare a new task context you must allocate first a memory for the task stack and the task context. These memory blocked must be aligned. The stack length must be aligned too. Then you prepare the task context, providing these blocks and the task entry function pointer with the argument. Once you have the context ready, simply switch to this new task using the __std_switch() function.
__std_switch() performs the task switch. This function must be called with interrupts disabled and only in a task context. To switch the current task in the interrupt context you must simply change the variable __std_context.