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)