Services and Modeling for Embedded Software Development
Embecosm divider strip
Prev  Next

5.2.1.  Exception vector setup

The first requirement is to populate the exception vectors. The OpenRISC 1000  uses memory from 0x0 to 0x1fff for exception vectors, with vectors placed 0x100 bytes apart. Thus a reset exception will jump to 0x100, a bus error exception to 0x200 and so on.

In this simple BSP, the vast majority of exceptions are not supported. If they are received, they print out (using printf) identification of the exception and the address which caused it to the simulator console, and then exit. We provide a macro for that assembly code, since it will be reused many times.

#define UNHANDLED_EXCEPTION(str)                                         \
        l.addi  r1,r1,-20               /* Standard prologue */         ;\
        l.sw    16(r1),r2                                               ;\
        l.addi  r2,r1,20                                                ;\
        l.sw    12(r1),r9                                               ;\
                                                                        ;\
        l.movhi r3,hi(.Lfmt)            /* printf format string */      ;\
        l.ori   r3,r3,lo(.Lfmt)                                         ;\
        l.sw    0(r1),r3                                                ;\
        l.movhi r4,hi(str)              /* Name of exception */         ;\
        l.ori   r4,r4,lo(str)                                           ;\
        l.sw    4(r1),r4                                                ;\
        l.mfspr r5,r0,SPR_EPCR_BASE     /* Source of the interrupt */   ;\
        l.jal   _printf                                                 ;\
        l.sw    8(r1),r5                                                ;\
                                                                        ;\
        l.ori   r3,r0,0xffff            /* Failure RC */                ;\
        l.jal   _exit                                                   ;\
        l.nop                                                           ;\
                                                                        ;\
        l.rfe                           /* Never executed we hope */
	  

The call to printf is expected to use a standard format string (at the label .Lfmt) which requires two other arguments, an identification string (labeled by the parameter st to the macro) and the program counter where the exception occurred (loaded from Special Purpose Register SPR_EPCR_BASE). Return from exception is provided as a formality, although the call to exit means that we should never execute it.

Note that compiled C functions have their names prepended by underscore on the OpenRISC 1000 . It is these names that must be used from the assembler code.

The format and identification strings are read only data.

        .section .rodata
.Lfmt:  .string "Unhandled %s exception at address %08p\n"
.L200:  .string "bus error"
.L300:  .string "data page fault"
.L400:  .string "instruction page fault"
.L500:  .string "timer"
.L600:  .string "alignment"
.L700:  .string "illegal instruction"
.L800:  .string "external interrupt"
.L900:  .string "data TLB"
.La00:  .string "instruction TLB"
.Lb00:  .string "range"
.Lc00:  .string "syscall"
.Ld00:  .string "floating point"
.Le00:  .string "trap"
.Lf00:  .string "undefined 0xf00"
.L1000: .string "undefined 0x1000"
.L1100: .string "undefined 0x1100"
.L1200: .string "undefined 0x1200"
.L1300: .string "undefined 0x1300"
.L1400: .string "undefined 0x1400"
.L1500: .string "undefined 0x1500"
.L1600: .string "undefined 0x1600"
.L1700: .string "undefined 0x1700"
.L1800: .string "undefined 0x1800"
.L1900: .string "undefined 0x1900"
.L1a00: .string "undefined 0x1a00"
.L1b00: .string "undefined 0x1b00"
.L1c00: .string "undefined 0x1c00"
.L1d00: .string "undefined 0x1d00"
.L1e00: .string "undefined 0x1e00"
.L1f00: .string "undefined 0x1f00"
	  

The first executable code is for the exception vectors. These must go first in memory, so are placed in their own section, .vectors. The linker/loader will ensure this this code is placed first in memory (see Section 7.3).

The reset vector just jumps to the start code. The code is too large to sit within the 0x100 bytes of an exception vector entry, and is placed in the main text space, in function _start.

        .section .vectors,"ax"

        /* 0x100: RESET exception */
        .org    0x100   
_reset:
        /* Jump to program initialisation code */
        l.movhi r2,hi(_start)
        l.ori   r2,r2,lo(_start)
        l.jr    r2
        l.nop
	  

The second vector, at address 0x200 is the bus error exception vector. In normal use, like all other exceptions it it causes exit and uses the UNHANDLED_EXCEPTION macro.

However during start up, the code tries deliberately to write out of memory, to determine the end of memory, which will trigger this bus exception. For this a simple exception handler, which just skips the offending instruction is required.

The solution is to place this code first, followed by the unhandled exception code. Once the end of memory has been located, the initial code can be overwritten by l.nop opcodes, so the exception will drop through to the UNHANDLED_EXCEPTOON code.

        .org    0x200
_buserr:
        l.mfspr r24,r0,SPR_EPCR_BASE
        l.addi  r24,r24,4               /* Return one instruction on */
        l.mtspr r0,r24,SPR_EPCR_BASE
        l.rfe

_buserr_std:
        UNHANDLED_EXCEPTION (.L200)
	  

No effort is made to save the register (r24) that is used in the handler. The start up code testing for end of memory must not use this register.

The next exception, data page fault, at location 0x300, like all other exceptions is unhandled.

        .org    0x300
        UNHANDLED_EXCEPTION (.L300)
	  
Embecosm divider strip