1 /* crt0.S -- startup file for OpenRISC 1000.
3 * Copyright (c) 2011, 2014 Authors
5 * Contributor Julius Baxter <juliusbaxter@gmail.com>
6 * Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de>
8 * The authors hereby grant permission to use, copy, modify, distribute,
9 * and license this software and its documentation for any purpose, provided
10 * that existing copyright notices are retained in all copies and that this
11 * notice is included verbatim in any distributions. No written agreement,
12 * license, or royalty fee is required for any of the authorized uses.
13 * Modifications to this software may be copyrighted by their authors
14 * and need not follow the licensing terms described here, provided that
15 * the new terms are clearly indicated on the first page of each file where
19 /* -------------------------------------------------------------------------- */
21 Assembly is hard to read per se, so please follow the following coding
22 conventions to keep it consistent and ease reading:
23 * internal jump labels start with L, no identation
24 * assemble lines have one tab identation
25 * attributes (.section, .global, ..) are indented with one tab
26 * code is structured using tabs, i.e., use 'l.sw\t0(r1),r1' with a single
27 tab. libgloss assumes 8 space tab width, so that might look unstructured
28 with tab widths below 6. Nevertheless don't use spaces or two tabs.
29 * no space after comma
30 * use the defined macros if possible as they reduce errors
31 * use OR1K_INST with OR1K_DELAYED(_NOP)
32 * OR1K_DELAYED is multiline for better readability, the inner parts are
33 indented with another tab.
34 * COMMENT! Try to accompy every line with a meaningful comment. If possible
35 use pseudo code to describe the code. Also mention intentions and not only
36 the obvious things.. */
37 /* -------------------------------------------------------------------------- */
40 #include "include/or1k-asm.h"
41 #include "include/or1k-sprs.h"
43 /* -------------------------------------------------------------------------- */
45 /* -------------------------------------------------------------------------- */
49 // +--------------------+ <- board_mem_base+board_mem_size/exception_stack_top
50 // | exception stack(s) |
51 // +--------------------+ <- stack_top
53 // +--------------------+ <- stack_bottom
55 // +--------------------+
56 // | text, data, bss.. |
57 // +--------------------+
59 // Reserved stack size
60 #define STACK_SIZE 8192
62 // Reserved stack size for exceptions (can usually be smaller than normal stack)
63 #define EXCEPTION_STACK_SIZE 8192
65 // Size of space required to store state
66 // This value must match that in the support library or1k_exception_handler
68 #define EXCEPTION_STACK_FRAME 136
72 .extern _or1k_stack_top /* points to the next address after the stack */
73 .extern _or1k_stack_bottom /* points to the last address in the stack */
74 .extern _or1k_exception_stack_top
75 .extern _or1k_exception_stack_bottom
76 .extern _or1k_exception_level /* Nesting level of exceptions */
79 .global _or1k_stack_size /* reserved stack size */
80 .global _or1k_exception_stack_size
81 .global _or1k_exception_level
83 _or1k_stack_size: .word STACK_SIZE
84 _or1k_exception_stack_size: .word EXCEPTION_STACK_SIZE
86 #ifdef __OR1K_MULTICORE__
87 .extern _or1k_stack_core
88 .extern _or1k_exception_stack_core
91 #define SHADOW_REG(x) (OR1K_SPR_SYS_GPR_BASE + 32 + x)
93 /* -------------------------------------------------------------------------- */
94 /*!Macro to handle exceptions.
96 Load NPC into r3, EPCR into r4
98 /* -------------------------------------------------------------------------- */
100 #ifdef _HAVE_INITFINI_ARRAY
101 #define _init __libc_init_array
102 #define _fini __libc_fini_array
105 #define GPR_BUF_OFFSET(x) (x << 2)
107 #ifndef __OR1K_MULTICORE__
108 #define CALL_EXCEPTION_HANDLER(id) \
109 /* Store current stack pointer to address 4 */ \
111 /* Load address of exception nesting level */ \
112 l.movhi r1,hi(_or1k_exception_level); \
113 l.ori r1,r1,lo(_or1k_exception_level); \
114 /* Load the current nesting level */ \
116 /* Set flag if this is the outer (first) exception */ \
118 /* Branch to the code for nested exceptions */ \
120 OR1K_INST(l.bnf .Lnested_##id) \
122 /* Load top of the exception stack */ \
123 l.movhi r1,hi(_or1k_exception_stack_top); \
124 l.ori r1,r1,lo(_or1k_exception_stack_top); \
126 /* Load value from array to stack pointer */ \
127 OR1K_INST(l.lwz r1,0(r1)), \
128 /* and jump over the nested code */ \
129 OR1K_INST(l.j .Lnesting_done_##id) \
132 /* Load back the stack pointer */ \
134 /* Add redzone, nesting needs this */ \
135 l.addi r1,r1,-REDZONE; \
136 .Lnesting_done_##id: \
137 /* Reserve red zone and context space */ \
138 l.addi r1,r1,-EXCEPTION_STACK_FRAME; \
139 /* Store GPR3 in context */ \
140 l.sw GPR_BUF_OFFSET(3)(r1),r3; \
141 /* Load back software's stack pointer */ \
143 /* Store this in the context */ \
144 l.sw GPR_BUF_OFFSET(1)(r1),r3; \
145 /* Store GPR4 in the context */ \
146 l.sw GPR_BUF_OFFSET(4)(r1),r4; \
147 /* Load address of the exception level */ \
148 l.movhi r3,hi(_or1k_exception_level); \
149 l.ori r3,r3,lo(_or1k_exception_level); \
150 /* Load current value */ \
152 /* Increment level */ \
156 /* Copy the current program counter as first */ \
157 /* argument for the exception handler. This */ \
158 /* is then used to determine the exception. */ \
159 l.mfspr r3,r0,OR1K_SPR_SYS_NPC_ADDR; \
161 /* Copy program counter of exception as */ \
162 /* second argument to the exception handler */ \
163 OR1K_INST(l.mfspr r4,r0,OR1K_SPR_SYS_EPCR_BASE),\
164 /* Jump to exception handler. This will rfe */ \
165 OR1K_INST(l.j _or1k_exception_handler) \
168 #define CALL_EXCEPTION_HANDLER(id) \
169 /* Store current stack pointer to shadow reg */ \
170 l.mtspr r0,r1,SHADOW_REG(1); \
171 /* Store current GPR3 for temporary use */ \
172 l.mtspr r0,r3,SHADOW_REG(2); \
173 /* Store current GPR2 for the level pointer */ \
174 l.mtspr r0,r4,SHADOW_REG(3); \
175 /* Load nesting level of exceptions */ \
176 l.movhi r4,hi(_or1k_exception_level); \
177 l.ori r4,r4,lo(_or1k_exception_level); \
178 /* Load array pointer */ \
181 l.mfspr r3,r0,OR1K_SPR_SYS_COREID_ADDR; \
182 /* Generate offset */ \
184 /* Generate core nesting level address */ \
186 /* Load nesting level */ \
188 /* Increment nesting level */ \
190 /* Write back nesting level */ \
192 /* Set flag if this is the outer (first) exception */ \
194 /* Branch to the code for nested exceptions */ \
196 OR1K_INST(l.bnf .Lnested_##id) \
198 /* Load pointer to exception stack array */ \
199 l.movhi r1,hi(_or1k_exception_stack_core); \
200 l.ori r1,r1,lo(_or1k_exception_stack_core); \
203 l.mfspr r3,r0,OR1K_SPR_SYS_COREID_ADDR; \
204 /* Calculate offset in array */ \
208 /* Load value from array to stack pointer */ \
209 OR1K_INST(l.lwz r1,0(r1)), \
210 /* and jump over nested exception pointer */ \
211 OR1K_INST(l.j .Lnesting_done_##id) \
214 /* The stack pointer is still active */ \
215 /* Add redzone, nesting needs this */ \
216 l.addi r1,r1,-REDZONE; \
217 .Lnesting_done_##id: \
218 /* Reserve context space */ \
219 l.addi r1,r1,-EXCEPTION_STACK_FRAME; \
220 /* Load back software's stack pointer */ \
221 l.mfspr r3,r0,SHADOW_REG(1); \
222 /* Store this in the context */ \
223 l.sw GPR_BUF_OFFSET(1)(r1),r3; \
224 /* Load back GPR3 */ \
225 l.mfspr r3,r0,SHADOW_REG(2); \
226 /* Store this in the context */ \
227 l.sw GPR_BUF_OFFSET(3)(r1),r3; \
228 /* Load back GPR4 */ \
229 l.mfspr r4,r0,SHADOW_REG(3); \
230 /* Store GPR4 in the context */ \
231 l.sw GPR_BUF_OFFSET(4)(r1),r4; \
232 /* Copy the current program counter as first */ \
233 /* argument for the exception handler. This */ \
234 /* is then used to determine the exception. */ \
235 l.mfspr r3,r0,OR1K_SPR_SYS_NPC_ADDR; \
237 /* Copy program counter of exception as */ \
238 /* second argument to the exception handler */ \
239 OR1K_INST(l.mfspr r4,r0,OR1K_SPR_SYS_EPCR_BASE),\
240 /* Jump to exception handler. This will rfe */ \
241 OR1K_INST(l.j _or1k_exception_handler) \
245 /* -------------------------------------------------------------------------- */
246 /*!Exception vectors */
247 /* -------------------------------------------------------------------------- */
248 .section .vectors,"ax"
250 /* 0x100: RESET exception */
254 #ifdef __OR1K_MULTICORE__
255 // This is a hack that relies on the fact, that all cores start at the
256 // same time and they are similarily fast
258 // Similarly, we use address 8 to signal how many cores have exit'ed
293 /* Clear status register, set supervisor mode */
294 l.ori r1,r0,OR1K_SPR_SYS_SR_SM_MASK
295 l.mtspr r0,r1,OR1K_SPR_SYS_SR_ADDR
296 /* Clear timer mode register*/
297 l.mtspr r0,r0,OR1K_SPR_TICK_TTMR_ADDR
298 /* Jump to program initialisation code */
299 LOAD_SYMBOL_2_GPR(r4, _or1k_start)
300 OR1K_DELAYED_NOP(OR1K_INST(l.jr r4))
303 CALL_EXCEPTION_HANDLER(2)
305 /* 0x300: Data Page Fault exception */
307 CALL_EXCEPTION_HANDLER(3)
309 /* 0x400: Insn Page Fault exception */
311 CALL_EXCEPTION_HANDLER(4)
313 /* 0x500: Timer exception */
315 CALL_EXCEPTION_HANDLER(5)
317 /* 0x600: Aligment exception */
319 CALL_EXCEPTION_HANDLER(6)
321 /* 0x700: Illegal insn exception */
323 CALL_EXCEPTION_HANDLER(7)
325 /* 0x800: External interrupt exception */
327 CALL_EXCEPTION_HANDLER(8)
329 /* 0x900: DTLB miss exception */
331 CALL_EXCEPTION_HANDLER(9)
333 /* 0xa00: ITLB miss exception */
335 CALL_EXCEPTION_HANDLER(10)
337 /* 0xb00: Range exception */
339 CALL_EXCEPTION_HANDLER(11)
341 /* 0xc00: Syscall exception */
343 CALL_EXCEPTION_HANDLER(12)
345 /* 0xd00: Floating point exception */
347 CALL_EXCEPTION_HANDLER(13)
349 /* 0xe00: Trap exception */
351 CALL_EXCEPTION_HANDLER(14)
353 /* 0xf00: Reserved exceptions */
355 CALL_EXCEPTION_HANDLER(15)
358 CALL_EXCEPTION_HANDLER(16)
361 CALL_EXCEPTION_HANDLER(17)
364 CALL_EXCEPTION_HANDLER(18)
367 CALL_EXCEPTION_HANDLER(19)
370 CALL_EXCEPTION_HANDLER(20)
373 CALL_EXCEPTION_HANDLER(21)
376 CALL_EXCEPTION_HANDLER(22)
379 CALL_EXCEPTION_HANDLER(23)
382 CALL_EXCEPTION_HANDLER(24)
385 CALL_EXCEPTION_HANDLER(25)
388 CALL_EXCEPTION_HANDLER(26)
391 CALL_EXCEPTION_HANDLER(27)
394 CALL_EXCEPTION_HANDLER(28)
397 CALL_EXCEPTION_HANDLER(29)
400 CALL_EXCEPTION_HANDLER(30)
403 CALL_EXCEPTION_HANDLER(31)
409 /* -------------------------------------------------------------------------- */
412 This is the initialization code of the library. It performs these steps:
414 * Call early board initialization:
415 Before anything happened, the board support may do some very early
416 initialization. This is at maximum some very basic stuff that would
417 otherwise prevent the following code from functioning. Other initialization
418 of peripherals etc. is done later (before calling main).
419 See the description below and README.board for details.
421 * Initialize the stacks:
422 Two stacks are configured: The system stack is used by the software and
423 the exception stack is used when an exception occurs. We added this as
424 this should be flexible with respect to the usage of virtual memory.
426 * Activate the caches:
427 If available the caches are initiliazed and activated.
430 The BSS are essentially the uninitialized C variables. They are set to 0
431 by default. This is performed by this function.
433 * Initialize the impure data structure:
434 Similarly, we need two library contexts, one for the normal software and
435 one that is used during exceptions. The impure data structure holds
436 the context information of the library. The called C function will setup
437 both data structures. There is furthermore a pointer to the currently
438 active impure data structure, which is initially set to the normal one.
440 * Initialize or1k support library reentrant data structures
442 * Initialize constructors:
443 Call the static and global constructors
445 * Set up destructors to call from exit
446 The library will call the function set via atexit() during exit(). We set
447 it to call the _fini function which performs destruction.
449 * Call board initialization:
450 The board initialization can perform board specific initializations such as
451 configuring peripherals etc.
454 Call main with argc = 0 and *argv[] = 0
456 * Call exit after main returns
463 /* -------------------------------------------------------------------------- */
466 /* Following externs from board-specific object passed at link time */
467 .extern _or1k_board_mem_base
468 .extern _or1k_board_mem_size
469 .extern _or1k_board_uart_base
471 /* The early board initialization may for example read the memory size and
472 set the mem_base and mem_size or do some preliminary board
473 initialization. As we do not have a stack at this time, the function may
474 not use the stack (and therefore be a or call a C function. But it can
475 safely use all registers.
477 We define a default implementation, which allows board files in C. As
478 described above, this can only be used in assembly (board_*.S) as at
479 the early stage not stack is available. A board that needs early
480 initialization can overwrite the function with .global _board_init_early.
482 Recommendation: Only use when you really need it! */
483 .weak _or1k_board_init_early
484 _or1k_board_init_early:
485 OR1K_DELAYED_NOP(OR1K_INST(l.jr r9))
487 /* The board initialization is then called after the C library and UART
488 are initialized. It can then be used to configure UART or other
489 devices before the actual main function is called. */
490 .extern _or1k_board_init
493 .type _or1k_start,@function
495 /* It is good to initialize and enable the caches before we do anything,
496 otherwise the cores will continuously access the bus during the wait
497 time for the boot barrier (0x4).
498 Fortunately or1k_cache_init does not need a stack */
499 OR1K_DELAYED_NOP(OR1K_INST(l.jal _or1k_cache_init))
501 #ifdef __OR1K_MULTICORE__
502 // All but core 0 have to wait
503 l.mfspr r1, r0, OR1K_SPR_SYS_COREID_ADDR
505 OR1K_DELAYED_NOP(OR1K_INST(l.bf .Lcore0))
507 /* r1 will be used by the other cores to check for the boot variable
508 Check if r1 is still zero, core 0 will set it to 1 once it booted
509 As the cache is already turned on, this will not create traffic on
510 the bus, but the change is snooped by cache coherency then */
513 OR1K_DELAYED_NOP(OR1K_INST(l.bf .Lspin))
515 /* Initialize core i stack */
516 // _or1k_stack_core is the array of stack pointers
517 LOAD_SYMBOL_2_GPR(r2,_or1k_stack_core)
518 // Load the base address
520 // Generate offset in array
521 l.mfspr r1,r0,OR1K_SPR_SYS_COREID_ADDR
525 // Load pointer to the stack top and set frame pointer
529 // The slave cores are done, jump to main part
530 OR1K_DELAYED_NOP(OR1K_INST(l.j .Linit_done));
532 /* Only core 0 executes the initialization code */
535 /* Call early board initialization */
536 OR1K_DELAYED_NOP(OR1K_INST(l.jal _or1k_board_init_early))
540 LOAD_SYMBOL_2_GPR(r3,__bss_start)
541 LOAD_SYMBOL_2_GPR(r4,end)
547 OR1K_INST(l.addi r3,r3,4),
548 OR1K_INST(l.bf .Lclear_bss_loop)
551 /* Initialise stack and frame pointer (set to same value) */
552 LOAD_SYMBOL_2_GPR(r1,_or1k_board_mem_base)
554 LOAD_SYMBOL_2_GPR(r2,_or1k_board_mem_size)
558 /* Store exception stack top address */
559 LOAD_SYMBOL_2_GPR(r3,_or1k_exception_stack_top)
562 /* Store exception stack bottom address */
563 // calculate bottom address
564 // r3 = *exception stack size
565 LOAD_SYMBOL_2_GPR(r3,_or1k_exception_stack_size)
566 // r3 = exception stack size
568 #ifdef __OR1K_MULTICORE__
569 l.mfspr r4,r0,OR1K_SPR_SYS_NUMCORES_ADDR
572 // r4 = exception stack top - exception stack size = exception stack bottom
574 // r5 = *exception stack bottom
575 LOAD_SYMBOL_2_GPR(r5,_or1k_exception_stack_bottom)
579 // Move stack pointer accordingly
583 /* Store stack top address */
584 LOAD_SYMBOL_2_GPR(r3,_or1k_stack_top)
587 /* Store stack bottom address */
588 // calculate bottom address
590 LOAD_SYMBOL_2_GPR(r3,_or1k_stack_size)
592 #ifdef __OR1K_MULTICORE__
593 l.mfspr r4, r0, OR1K_SPR_SYS_NUMCORES_ADDR
596 // r4 = stack top - stack size = stack bottom
599 // r5 = *exception stack bottom
600 LOAD_SYMBOL_2_GPR(r5,_or1k_stack_bottom)
604 /* Reinitialize the or1k support library */
605 OR1K_DELAYED_NOP(OR1K_INST(l.jal _or1k_init))
607 /* Reinitialize the reentrancy structure */
608 OR1K_DELAYED_NOP(OR1K_INST(l.jal _or1k_libc_impure_init))
610 /* Call global and static constructors */
611 OR1K_DELAYED_NOP(OR1K_INST(l.jal _init))
613 /* Set up destructors to be called from exit if main ever returns */
616 OR1K_INST(l.ori r3,r3,lo(_fini)),
617 OR1K_INST(l.jal atexit)
620 /* Check if UART is to be initialised */
621 LOAD_SYMBOL_2_GPR(r4,_or1k_board_uart_base)
623 /* Is base set? If not, no UART */
627 OR1K_DELAYED_NOP(OR1K_INST(l.jal _or1k_uart_init))
630 /* Board initialization */
631 OR1K_DELAYED_NOP(OR1K_INST(l.jal _or1k_board_init))
633 #ifdef __OR1K_MULTICORE__
640 /* Jump to main program entry point (argc = argv = envp = 0) */
644 OR1K_INST(l.or r5,r0,r0),
645 OR1K_INST(l.jal main)
648 #ifdef __OR1K_MULTICORE__
650 /* Atomically increment number of finished cores */
654 OR1K_DELAYED_NOP(OR1K_INST(l.bnf .incrementexit));
655 /* Compare to number of cores in this cluster */
656 l.mfspr r4,r0, OR1K_SPR_SYS_NUMCORES_ADDR
657 /* Compare to number of finished tasks */
659 /* Last core needs to desctruct library etc. */
660 OR1K_DELAYED_NOP(OR1K_INST(l.bf .exitcorelast));
662 OR1K_INST(l.addi r3,r11,0),
663 OR1K_INST(l.jal _exit)
667 /* If program exits, call exit routine */
669 OR1K_INST(l.addi r3,r11,0),
670 OR1K_INST(l.jal exit)
675 OR1K_DELAYED_NOP(OR1K_INST(l.j .Lloop_forever))
677 .size _or1k_start,.-_or1k_start