blob: 90687927449f580ba100ae4ab86bbf5e59f98a30 [file] [log] [blame]
// crt1-sim.S
// For the Xtensa simulator target, this code sets up the C calling context
// and calls main() (via __clibrary_start).
// Control arrives here at _start from the reset vector or from crt0-app.S.
// Copyright (c) 1998-2017 Cadence Design Systems, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <xtensa/simboard.h>
#include <xtensa/simcall.h>
#include <xtensa/coreasm.h>
#include "xtos-internal.h"
// Exports
.global _start
// Imports
// __clibrary_init from C library (eg. newlib or uclibc)
// exit from C library
// main from user application
// __stack from linker script (see LSP Ref Manual)
.type __clibrary_init, @function
.type main, @function
.type exit, @function
// Macros to abstract away ABI differences
#if __XTENSA_CALL0_ABI__
# define CALL call0
# define CALLX callx0
# define ARG1 a2 /* 1st outgoing call argument */
# define ARG2 a3 /* 2nd outgoing call argument */
# define ARG3 a4 /* 3rd outgoing call argument */
# define ARG4 a5 /* 4th outgoing call argument */
# define ARG5 a6 /* 5th outgoing call argument */
#else
# define CALL call4
# define CALLX callx4
# define ARG1 a6 /* 1st outgoing call argument */
# define ARG2 a7 /* 2nd outgoing call argument */
# define ARG3 a8 /* 3rd outgoing call argument */
# define ARG4 a9 /* 4th outgoing call argument */
# define ARG5 a10 /* 5th outgoing call argument */
#endif
.data
.weak _start_envp // allow overriding
.align 4
_start_envp: .word 0 // empty environ
.text
.align 4
_start:
// _start is typically NOT at the beginning of the text segment --
// it is always called from either the reset vector or other code
// that does equivalent initialization (such as crt0-app.S).
//
// Assumptions on entry to _start:
// - low (level-one) and medium priority interrupts are disabled
// via PS.INTLEVEL and/or INTENABLE (PS.INTLEVEL is expected to
// be zeroed, to potentially enable them, before calling main)
// - C calling context not initialized:
// - PS not initialized
// - SP not initialized
// - the following are initialized:
// - LITBASE, cache attributes, WindowBase, WindowStart,
// CPENABLE, FP's FCR and FSR, EXCSAVE[n]
// Keep a0 zero. It is used to initialize a few things.
// It is also the return address, where zero indicates
// that the frame used by _start is the bottommost frame.
//
movi a0, 0 // keep this register zero.
#if XTOS_RESET_UNNEEDED
#include "reset-unneeded.S"
#endif
// Initialize the stack pointer.
// See the "ABI and Software Conventions" chapter in the
// Xtensa ISA Reference manual for details.
// NOTE: Because the _start routine does not use any memory in its
// stack frame, and because all of its CALL instructions use a
// window size of 4, the stack frame for _start can be empty.
movi sp, __stack
// reserve stack space for
// - argv array
// - argument strings
movi a2, SYS_iss_argv_size
simcall // returns size of argv[] + its strings in a2
#if XCHAL_HAVE_PIF
// The stack only needs 16-byte alignment.
// However, here we round up the argv size further to 128 byte multiples
// so that in most cases, variations in argv[0]'s path do not result in
// different stack allocation. Otherwise, such variations can impact
// execution timing (eg. due to cache effects etc) for the same code and data.
// If we have a PIF, it's more likely the extra required space is okay.
addi a2, a2, 127
srli a2, a2, 7
slli a2, a2, 7
#else
// Keep stack 16-byte aligned.
addi a2, a2, 15
srli a2, a2, 4
slli a2, a2, 4
#endif
// No need to use MOVSP because we have no caller (we're the
// base caller); in fact it's better not to use MOVSP in this
// context, to avoid unnecessary ALLOCA exceptions and copying
// from undefined memory:
// sub a3, sp, a2
// movsp sp, a3
sub sp, sp, a2
/*
* Now that sp (a1) is set, we can set PS as per the application
* (user vector mode, enable interrupts, enable window exceptions if applicable).
*/
#if XCHAL_HAVE_EXCEPTIONS
movi a3, PS_UM|PS_WOE_ABI // PS.WOE = 0|1, PS.UM = 1, PS.EXCM = 0, PS.INTLEVEL = 0
wsr.ps a3
rsync
#endif
/*
* Do any initialization that affects the memory map, such as
* setting up TLB entries, that needs to be done before we can
* successfully clear BSS (e.g. if some BSS segments are in
* remapped areas).
*
* NOTE: This hook works where the reset vector does not unpack
* segments (see "ROM packing" in the LSP manual), or where
* unpacking of segments is not affected by memory remapping.
* If ROM unpacking is affected, TLB setup must be done in
* assembler from the reset vector.
*
* The __memmap_init() routine can be a C function, however it
* does not have BSS initialized! In particular, __memmap_init()
* cannot set BSS variables, i.e. uninitialized global variables
* (they'll be wiped out by the following BSS clear), nor can it
* assume they are yet initialized to zero.
*
* The __memmap_init() function is optional. It is marked as a
* weak symbol, so that it gets valued zero if not defined.
*/
.weak __memmap_init
movi a4, __memmap_init
beqz a4, 1f
CALLX a4
1:
#if !XCHAL_HAVE_BOOTLOADER /* boot loader takes care of zeroing BSS */
/* If a system-specific BSS init routine is defined, call it.
* Such a routine must be named __bss_init(). It can be a C
* function, however it must be written to be able to work
* with BSS not yet initialized. This function is optional.
* It is marked as a weak symbol, so that it gets value zero
* if not defined.
*/
.weak __bss_init
movi a4, __bss_init
beqz a4, 2f
movi ARG1, _bss_table_start
movi ARG2, _bss_table_end
CALLX a4
j .Lnobss // skip default BSS init code
2:
/* The new ISS simcall only appeared after RB-2007.2: */
#if (XCHAL_HW_MAX_VERSION > XTENSA_HWVERSION_RB_2007_2)
/*
* Clear the BSS (uninitialized data) segments.
* This code supports multiple zeroed sections (*.bss).
* For speed, we clear memory using an ISS simcall
* (see crt1-boards.S for more generic BSS clearing code).
*/
movi a6, _bss_table_start
movi a7, _bss_table_end
bgeu a6, a7, .Lnobss
.Lbssloop:
movi a2, SYS_memset
l32i a3, a6, 0 // arg1 = fill start address
movi a4, 0 // arg2 = fill pattern
l32i a5, a6, 4 // get end address
addi a6, a6, 8 // next bss table entry
sub a5, a5, a3 // arg3 = fill size in bytes
simcall // memset(a3,a4,a5)
bltu a6, a7, .Lbssloop // loop until end of bss table
#endif /* XCHAL_HW_MAX_VERSION */
.Lnobss:
#endif /* XCHAL_HAVE_BOOTLOADER */
/*
* Call __clibrary_init to initialize the C library:
*
* void __clibrary_init(int argc, char ** argv, char ** environ,
* void(*init_func)(void), void(*fini_func)(void));
*/
// Get argv with the arguments from the ISS
mov a3, sp // tell simcall where to write argv[]
movi a2, SYS_iss_set_argv
simcall // write argv[] array at a3
movi a2, SYS_iss_argc
simcall // put argc in a2
// Alternative smaller code for Xtensa TX.
// Many starting with simulation assume a full C env, so NOT DONE FOR NOW.
//
//#if XCHAL_HAVE_HALT
//
// // Assume minimalist environment for memory-constrained TX cores.
// // No C library or board initialization, and no call to exit().
// // However, in the interest of software regressions, for now we
// // still pass parameters to main (but not the rarely used envp).
//
// //mov ARG1, a2 // argc already in a2.
// mov ARG2, sp // argv
// CALL main
// halt
//
//#else /* !HALT */
// ...
#if __XTENSA_CALL0_ABI__
mov a12, a2 // save argc (a2 is ARG1)
#else
mov ARG1, a2 // argc
#endif
mov ARG2, sp // argv
movi ARG3, _start_envp // envp
movi ARG4, _init // _init
movi ARG5, _fini // _fini
CALL __clibrary_init
// Call: int main(int argc, char ** argv, char ** environ);
#if __XTENSA_CALL0_ABI__
mov ARG1, a12 // argc
#else
mov ARG1, a2 // argc
#endif
mov ARG2, sp // argv
movi ARG3, _start_envp // envp = [0]
CALL main
// The return value is the same register as the first outgoing argument.
CALL exit // exit with main's return value
// Does not return here.
.size _start, . - _start
// Local Variables:
// mode:fundamental
// comment-start: "// "
// comment-start-skip: "// *"
// End: