2014-12-15 Stefan Wallentowitz <stefan.wallentowitz@tum.de>
* or1k/Makefile.in: Add libor1k * or1k/README: New file * or1k/caches-asm.S: New file * or1k/exceptions-asm.S: New file * or1k/exceptions.c: New file * or1k/impure.c: New file * or1k/include/or1k-nop.h: New file * or1k/include/or1k-support.h: New file * or1k/interrupts-asm.S: New file * or1k/interrupts.c: New file * or1k/mmu-asm.S: New file * or1k/or1k-internals.h: New file * or1k/or1k_uart.c: New file * or1k/or1k_uart.h: New file * or1k/outbyte.S: New file * or1k/sbrk.c: New file * or1k/sync-asm.S: New file * or1k/syscalls.c: New file * or1k/timer.c: New file * or1k/util.c: New file
This commit is contained in:
parent
d1219c0e89
commit
68a9101237
|
@ -1,3 +1,26 @@
|
|||
2014-12-15 Stefan Wallentowitz <stefan.wallentowitz@tum.de>
|
||||
|
||||
* or1k/Makefile.in: Add libor1k
|
||||
* or1k/README: New file
|
||||
* or1k/caches-asm.S: New file
|
||||
* or1k/exceptions-asm.S: New file
|
||||
* or1k/exceptions.c: New file
|
||||
* or1k/impure.c: New file
|
||||
* or1k/include/or1k-nop.h: New file
|
||||
* or1k/include/or1k-support.h: New file
|
||||
* or1k/interrupts-asm.S: New file
|
||||
* or1k/interrupts.c: New file
|
||||
* or1k/mmu-asm.S: New file
|
||||
* or1k/or1k-internals.h: New file
|
||||
* or1k/or1k_uart.c: New file
|
||||
* or1k/or1k_uart.h: New file
|
||||
* or1k/outbyte.S: New file
|
||||
* or1k/sbrk.c: New file
|
||||
* or1k/sync-asm.S: New file
|
||||
* or1k/syscalls.c: New file
|
||||
* or1k/timer.c: New file
|
||||
* or1k/util.c: New file
|
||||
|
||||
2014-12-15 Stefan Wallentowitz <stefan.wallentowitz@tum.de>
|
||||
|
||||
* README: Add details about or1k.
|
||||
|
|
|
@ -55,10 +55,29 @@ OBJCOPY = `if [ -f ${objroot}/../binutils/objcopy ] ; \
|
|||
then echo ${objroot}/../binutils/objcopy ; \
|
||||
else t='$(program_transform_name)'; echo objcopy | sed -e $$t ; fi`
|
||||
|
||||
# object files needed
|
||||
COMMON_FILES = syscalls \
|
||||
or1k_uart \
|
||||
outbyte \
|
||||
caches-asm \
|
||||
exceptions \
|
||||
exceptions-asm \
|
||||
interrupts \
|
||||
interrupts-asm \
|
||||
mmu-asm \
|
||||
timer \
|
||||
sbrk \
|
||||
impure \
|
||||
util \
|
||||
sync-asm
|
||||
|
||||
LIBOR1K_FILES = $(COMMON_FILES)
|
||||
LIBOR1K_OBJS = $(addsuffix .o,$(LIBOR1K_FILES))
|
||||
|
||||
GCC_LDFLAGS = `if [ -d ${objroot}/../gcc ] ; \
|
||||
then echo -L${objroot}/../gcc ; fi`
|
||||
|
||||
OUTPUTS = crt0.o
|
||||
OUTPUTS = libor1k.a crt0.o
|
||||
|
||||
# Host specific makefile fragment comes in here.
|
||||
@host_makefile_frag@
|
||||
|
@ -70,6 +89,10 @@ all: ${OUTPUTS}
|
|||
# here's where we build the library for each target
|
||||
#
|
||||
|
||||
libor1k.a: $(LIBOR1K_OBJS)
|
||||
${AR} ${ARFLAGS} $@ $(LIBOR1K_OBJS)
|
||||
${RANLIB} $@
|
||||
|
||||
doc:
|
||||
|
||||
clean mostlyclean:
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
This document describes the internals of the port for OpenRISC
|
||||
1000. The API is documented in or1k-support.h as Doxygen comments.
|
||||
|
||||
# Data Structures
|
||||
|
||||
+----------------+ 0x0
|
||||
| vectors |
|
||||
+----------------+
|
||||
| text,data,.. |
|
||||
+----------------+
|
||||
| bss |
|
||||
+----------------+
|
||||
| heap |
|
||||
| vv |
|
||||
| |
|
||||
| ^^ |
|
||||
| stack(s) |
|
||||
+----------------+ _or1k_board_mem_base +
|
||||
_or1k_board_mem_size
|
||||
|
||||
## Stack and Heap
|
||||
|
||||
The stack is allocated at the end of available physical memory which
|
||||
is defined by each board as _or1k_board_mem_base and
|
||||
_or1k_board_mem_size. The _or1k_stack_top and _or1k_stack_bottom are
|
||||
determined by those variables and _or1k_stack_size (which may be
|
||||
overwritten in _or1k_board_init_early).
|
||||
|
||||
A second stack for exceptions is allocated as we allow exceptions to
|
||||
be arbitrary complex and call C functions etc. It is not an option to
|
||||
re-use the current software stack as we want to be so generic, that
|
||||
this can also be a virtual memory stack at moment of exception. The
|
||||
exception starts below the normal software stack and is
|
||||
_or1k_exception_stack_size large.
|
||||
|
||||
Multicore: For each core a stack and exception stack is allocated and
|
||||
the stack pointer set at boot. That is: sp(core0) = _or1k_stack_top,
|
||||
sp(core1) = _or1k_stack_top - _or1k_stack_size, etc.
|
||||
|
||||
## _or1k_stack_core (multicore only)
|
||||
|
||||
An array of pointers to the software stacks (size:
|
||||
4*or1k_numcores()). It is dynamically allocated from heap in or1k_init
|
||||
by calling sbrk(). The pointers contain the values for stack top
|
||||
pointers as described above. This variable is essentially used on boot
|
||||
of the slave cores to configure the stack register.
|
||||
|
||||
## _or1k_exception_stack_core (multicore only)
|
||||
|
||||
An array of pointers to the exception stacks (size:
|
||||
4*or1k_numcores()). It is allocated identical as the stack_core
|
||||
array. It is loaded whenever an exception occurs to start with a clean
|
||||
stack in the exception.
|
||||
|
||||
## _or1k_exception_handler_table
|
||||
|
||||
A table of function pointers to the handlers of the exceptions. The
|
||||
generic exception handler checks if an exception handler is registered
|
||||
and calls it. There are 30 exceptions defined (0x0 is not an exception
|
||||
vector and 0x100 is reset which is static). This array resides in BSS
|
||||
and is therefore initialized as 0 (no handler registered) after start.
|
||||
|
||||
Multicore: As the number of course is not known at compile time, the
|
||||
variable is a pointer to and array of arrays (cores x 30) which is
|
||||
allocated in or1k_init() on heap (using sbrk).
|
||||
|
||||
## _or1k_interrupt_handler_table and _or1k_interrupt_handler_table_data_ptr
|
||||
|
||||
The interrupt handlers are stored identical to to the exception handler table.
|
||||
|
||||
## _or1k_reent
|
||||
|
||||
The struct _or1k_reent contains formerly global data and allows for
|
||||
reentrancy. In the single core case, this is an allocated object,
|
||||
while it is a pointer to an array of structs in the multicore library.
|
||||
It is allocated in _or1k_reent_init() on the heap.
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
/* caches-asm.S -- cache manipulation for OpenRISC 1000.
|
||||
*
|
||||
* Copyright (c) 2011, 2014 Authors
|
||||
*
|
||||
* Contributor Julius Baxter <juliusbaxter@gmail.com>
|
||||
* Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de>
|
||||
*
|
||||
* The authors hereby grant permission to use, copy, modify, distribute,
|
||||
* and license this software and its documentation for any purpose, provided
|
||||
* that existing copyright notices are retained in all copies and that this
|
||||
* notice is included verbatim in any distributions. No written agreement,
|
||||
* license, or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their authors
|
||||
* and need not follow the licensing terms described here, provided that
|
||||
* the new terms are clearly indicated on the first page of each file where
|
||||
* they apply.
|
||||
*/
|
||||
|
||||
#include "include/or1k-asm.h"
|
||||
#include "include/or1k-sprs.h"
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/*!Function used at reset to clear and enable all caches
|
||||
*/
|
||||
/* -------------------------------------------------------------------------- */
|
||||
.global _or1k_cache_init
|
||||
.type _or1k_cache_init,@function
|
||||
|
||||
_or1k_cache_init:
|
||||
/* Instruction cache enable */
|
||||
/* Check if IC present and skip enabling otherwise */
|
||||
l.mfspr r3,r0,OR1K_SPR_SYS_UPR_ADDR
|
||||
l.andi r4,r3,OR1K_SPR_SYS_UPR_ICP_MASK
|
||||
l.sfeq r4,r0
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.bf .Lnoic))
|
||||
|
||||
/* Disable IC */
|
||||
l.mfspr r6,r0,OR1K_SPR_SYS_SR_ADDR
|
||||
l.addi r5,r0,-1
|
||||
l.xori r5,r5,OR1K_SPR_SYS_SR_ICE_MASK
|
||||
l.and r5,r6,r5
|
||||
l.mtspr r0,r5,OR1K_SPR_SYS_SR_ADDR
|
||||
|
||||
/* Establish cache block size
|
||||
If BS=0, 16;
|
||||
If BS=1, 32;
|
||||
r14 contain block size
|
||||
*/
|
||||
l.mfspr r3,r0,OR1K_SPR_SYS_ICCFGR_ADDR
|
||||
l.andi r4,r3,OR1K_SPR_SYS_ICCFGR_CBS_MASK
|
||||
l.srli r7,r4,7
|
||||
l.ori r8,r0,16
|
||||
l.sll r14,r8,r7
|
||||
|
||||
/* Establish number of cache sets
|
||||
r13 contains number of cache sets
|
||||
r7 contains log(# of cache sets)
|
||||
*/
|
||||
l.andi r4,r3,OR1K_SPR_SYS_ICCFGR_NCS_MASK
|
||||
l.srli r7,r4,3
|
||||
l.ori r8,r0,1
|
||||
l.sll r13,r8,r7
|
||||
|
||||
/* Invalidate IC */
|
||||
l.addi r6,r0,0
|
||||
l.sll r5,r14,r7
|
||||
|
||||
.Linvi: l.mtspr r0,r6,OR1K_SPR_ICACHE_ICBIR_ADDR
|
||||
l.sfne r6,r5
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.add r6,r6,r14),
|
||||
OR1K_INST(l.bf .Linvi)
|
||||
)
|
||||
|
||||
/* Enable IC */
|
||||
l.mfspr r6,r0,OR1K_SPR_SYS_SR_ADDR
|
||||
l.ori r6,r6,OR1K_SPR_SYS_SR_ICE_MASK
|
||||
l.mtspr r0,r6,OR1K_SPR_SYS_SR_ADDR
|
||||
l.nop
|
||||
l.nop
|
||||
l.nop
|
||||
l.nop
|
||||
l.nop
|
||||
l.nop
|
||||
l.nop
|
||||
l.nop
|
||||
|
||||
/* Data cache enable */
|
||||
/* Check if DC present and skip enabling otherwise */
|
||||
.Lnoic: l.mfspr r3,r0,OR1K_SPR_SYS_UPR_ADDR
|
||||
l.andi r4,r3,OR1K_SPR_SYS_UPR_DCP_MASK
|
||||
l.sfeq r4,r0
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.bf .Lnodc))
|
||||
/* Disable DC */
|
||||
l.mfspr r6,r0,OR1K_SPR_SYS_SR_ADDR
|
||||
l.addi r5,r0,-1
|
||||
l.xori r5,r5,OR1K_SPR_SYS_SR_DCE_MASK
|
||||
l.and r5,r6,r5
|
||||
l.mtspr r0,r5,OR1K_SPR_SYS_SR_ADDR
|
||||
/* Establish cache block size
|
||||
If BS=0, 16;
|
||||
If BS=1, 32;
|
||||
r14 contain block size */
|
||||
l.mfspr r3,r0,OR1K_SPR_SYS_DCCFGR_ADDR
|
||||
l.andi r4,r3,OR1K_SPR_SYS_DCCFGR_CBS_MASK
|
||||
l.srli r7,r4,7
|
||||
l.ori r8,r0,16
|
||||
l.sll r14,r8,r7
|
||||
/* Establish number of cache sets
|
||||
r13 contains number of cache sets
|
||||
r7 contains log(# of cache sets) */
|
||||
l.andi r4,r3,OR1K_SPR_SYS_ICCFGR_NCS_MASK
|
||||
l.srli r7,r4,3
|
||||
l.ori r8,r0,1
|
||||
l.sll r13,r8,r7
|
||||
/* Invalidate DC */
|
||||
l.addi r6,r0,0
|
||||
l.sll r5,r14,r7
|
||||
|
||||
.Linvd: l.mtspr r0,r6,OR1K_SPR_DCACHE_DCBIR_ADDR
|
||||
l.sfne r6,r5
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.add r6,r6,r14),
|
||||
OR1K_INST(l.bf .Linvd)
|
||||
)
|
||||
/* Enable DC */
|
||||
l.mfspr r6,r0,OR1K_SPR_SYS_SR_ADDR
|
||||
l.ori r6,r6,OR1K_SPR_SYS_SR_DCE_MASK
|
||||
l.mtspr r0,r6,OR1K_SPR_SYS_SR_ADDR
|
||||
|
||||
.Lnodc:
|
||||
/* Return */
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.jr r9))
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/*!Function to enable instruction cache
|
||||
*/
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
.global or1k_icache_enable
|
||||
.type or1k_icache_enable,@function
|
||||
|
||||
or1k_icache_enable:
|
||||
/* Enable IC */
|
||||
l.mfspr r13,r0,OR1K_SPR_SYS_SR_ADDR
|
||||
l.ori r13,r13,OR1K_SPR_SYS_SR_ICE_MASK
|
||||
l.mtspr r0,r13,OR1K_SPR_SYS_SR_ADDR
|
||||
l.nop
|
||||
l.nop
|
||||
l.nop
|
||||
l.nop
|
||||
l.nop
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.jr r9))
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/*!Function to disable instruction cache
|
||||
*/
|
||||
/* -------------------------------------------------------------------------- */
|
||||
.global or1k_icache_disable
|
||||
.type or1k_icache_disable,@function
|
||||
|
||||
or1k_icache_disable:
|
||||
/* Disable IC */
|
||||
l.mfspr r13,r0,OR1K_SPR_SYS_SR_ADDR
|
||||
l.addi r12,r0,-1
|
||||
l.xori r12,r12,OR1K_SPR_SYS_SR_ICE_MASK
|
||||
l.and r12,r13,r12
|
||||
l.mtspr r0,r12,OR1K_SPR_SYS_SR_ADDR
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.jr r9))
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/*!Function to flush address of instruction cache
|
||||
*/
|
||||
/* -------------------------------------------------------------------------- */
|
||||
.global or1k_icache_flush
|
||||
.type or1k_icache_flush,@function
|
||||
|
||||
or1k_icache_flush:
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.mtspr r0,r3,OR1K_SPR_ICACHE_ICBIR_ADDR),
|
||||
/* Push r3 into IC invalidate reg */
|
||||
OR1K_INST(l.jr r9)
|
||||
)
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/*!Function to enable data cache
|
||||
*/
|
||||
/* -------------------------------------------------------------------------- */
|
||||
.global or1k_dcache_enable
|
||||
.type or1k_dcache_enable,@function
|
||||
|
||||
or1k_dcache_enable:
|
||||
/* Enable DC */
|
||||
l.mfspr r13,r0,OR1K_SPR_SYS_SR_ADDR
|
||||
l.ori r13,r13,OR1K_SPR_SYS_SR_DCE_MASK
|
||||
l.mtspr r0,r13,OR1K_SPR_SYS_SR_ADDR
|
||||
l.nop
|
||||
l.nop
|
||||
l.nop
|
||||
l.nop
|
||||
l.nop
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.jr r9))
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/*!Function to disable data cache
|
||||
*/
|
||||
/* -------------------------------------------------------------------------- */
|
||||
.global or1k_dcache_disable
|
||||
.type or1k_dcache_disable,@function
|
||||
|
||||
or1k_dcache_disable:
|
||||
/* Disable DC */
|
||||
l.mfspr r13,r0,OR1K_SPR_SYS_SR_ADDR
|
||||
l.addi r12,r0,-1
|
||||
l.xori r12,r12,OR1K_SPR_SYS_SR_DCE_MASK
|
||||
l.and r12,r13,r12
|
||||
l.mtspr r0,r12,OR1K_SPR_SYS_SR_ADDR
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.jr r9))
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/*!Function to flush address of data cache
|
||||
*/
|
||||
/* -------------------------------------------------------------------------- */
|
||||
.global or1k_dcache_flush
|
||||
.type or1k_dcache_flush,@function
|
||||
|
||||
or1k_dcache_flush:
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.mtspr r0,r3,OR1K_SPR_DCACHE_DCBIR_ADDR),
|
||||
/* Push r3 into DC invalidate reg */
|
||||
OR1K_INST(l.jr r9)
|
||||
)
|
|
@ -0,0 +1,201 @@
|
|||
/* exceptions-asm.S -- exception handling for OpenRISC 1000.
|
||||
*
|
||||
* Copyright (c) 2011, 2014 Authors
|
||||
*
|
||||
* Contributor Julius Baxter <juliusbaxter@gmail.com>
|
||||
* Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de>
|
||||
*
|
||||
* The authors hereby grant permission to use, copy, modify, distribute,
|
||||
* and license this software and its documentation for any purpose, provided
|
||||
* that existing copyright notices are retained in all copies and that this
|
||||
* notice is included verbatim in any distributions. No written agreement,
|
||||
* license, or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their authors
|
||||
* and need not follow the licensing terms described here, provided that
|
||||
* the new terms are clearly indicated on the first page of each file where
|
||||
* they apply.
|
||||
*/
|
||||
|
||||
#include "include/or1k-asm.h"
|
||||
#include "include/or1k-sprs.h"
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/*!Generic exception handler function
|
||||
*/
|
||||
/* -------------------------------------------------------------------------- */
|
||||
// Warning - this must be the same as specified in crt0.S
|
||||
#define EXCEPTION_STACK_SIZE 128+128
|
||||
|
||||
.extern _or1k_exception_handler_table
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/*!Function to call appropriate exception handler
|
||||
*/
|
||||
/* -------------------------------------------------------------------------- */
|
||||
.section .text
|
||||
.global _or1k_exception_handler
|
||||
.type _or1k_exception_handler,@function
|
||||
|
||||
/*
|
||||
r3 = address of exception vector
|
||||
r4 = address where exception occurred
|
||||
*/
|
||||
|
||||
#define GPR_BUF_OFFSET(x) (x << 2)
|
||||
|
||||
_or1k_exception_handler:
|
||||
/* Store remainder of state (r3,r4 stored in vector entry)*/
|
||||
l.sw GPR_BUF_OFFSET(2)(r1),r2
|
||||
l.sw GPR_BUF_OFFSET(5)(r1),r5
|
||||
l.sw GPR_BUF_OFFSET(6)(r1),r6
|
||||
l.sw GPR_BUF_OFFSET(7)(r1),r7
|
||||
l.sw GPR_BUF_OFFSET(8)(r1),r8
|
||||
l.sw GPR_BUF_OFFSET(9)(r1),r9
|
||||
l.sw GPR_BUF_OFFSET(10)(r1),r10
|
||||
l.sw GPR_BUF_OFFSET(11)(r1),r11
|
||||
l.sw GPR_BUF_OFFSET(12)(r1),r12
|
||||
l.sw GPR_BUF_OFFSET(13)(r1),r13
|
||||
l.sw GPR_BUF_OFFSET(14)(r1),r14
|
||||
l.sw GPR_BUF_OFFSET(15)(r1),r15
|
||||
l.sw GPR_BUF_OFFSET(16)(r1),r16
|
||||
l.sw GPR_BUF_OFFSET(17)(r1),r17
|
||||
l.sw GPR_BUF_OFFSET(18)(r1),r18
|
||||
l.sw GPR_BUF_OFFSET(19)(r1),r19
|
||||
l.sw GPR_BUF_OFFSET(20)(r1),r20
|
||||
l.sw GPR_BUF_OFFSET(21)(r1),r21
|
||||
l.sw GPR_BUF_OFFSET(22)(r1),r22
|
||||
l.sw GPR_BUF_OFFSET(23)(r1),r23
|
||||
l.sw GPR_BUF_OFFSET(24)(r1),r24
|
||||
l.sw GPR_BUF_OFFSET(25)(r1),r25
|
||||
l.sw GPR_BUF_OFFSET(26)(r1),r26
|
||||
l.sw GPR_BUF_OFFSET(27)(r1),r27
|
||||
l.sw GPR_BUF_OFFSET(28)(r1),r28
|
||||
l.sw GPR_BUF_OFFSET(29)(r1),r29
|
||||
l.sw GPR_BUF_OFFSET(30)(r1),r30
|
||||
l.sw GPR_BUF_OFFSET(31)(r1),r31
|
||||
|
||||
/* Replace impure pointer for exception */
|
||||
l.movhi r20,hi(_or1k_exception_impure_ptr)
|
||||
l.ori r20,r20,lo(_or1k_exception_impure_ptr)
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
l.lwz r20,0(r20)
|
||||
l.mfspr r22,r0,OR1K_SPR_SYS_COREID_ADDR
|
||||
l.slli r22,r22,2
|
||||
l.add r20,r20,r22
|
||||
#endif
|
||||
l.lwz r20,0(r20)
|
||||
|
||||
l.movhi r21,hi(_or1k_current_impure_ptr)
|
||||
l.ori r21,r21,lo(_or1k_current_impure_ptr)
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
l.lwz r21,0(r21)
|
||||
l.add r21,r21,r22
|
||||
#endif
|
||||
l.sw 0(r21),r20
|
||||
|
||||
/* Determine offset in table of exception handler using r3*/
|
||||
l.andi r13,r3,0xff00
|
||||
l.srli r13,r13,6
|
||||
/* Substract 2 words, as we have no vector at 0 and no reset handler */
|
||||
l.addi r13,r13,-8
|
||||
/* r13 now contains offset in or1k_exception_handler_table for
|
||||
function
|
||||
*/
|
||||
/* Get or1k_exception_handler_table address */
|
||||
l.movhi r14,hi(_or1k_exception_handler_table)
|
||||
l.ori r14,r14,lo(_or1k_exception_handler_table)
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
/* Read the address of the array of cores */
|
||||
/* r14 = (*or1k_exception_handler_table) */
|
||||
l.lwz r14,0(r14)
|
||||
/* Generate core offset in array (off = coreid*30*4 = coreid*120) */
|
||||
/* r15 = coreid */
|
||||
l.mfspr r15,r0,OR1K_SPR_SYS_COREID_ADDR
|
||||
/* r16 = coreid * 128 */
|
||||
l.slli r16,r15,7
|
||||
/* r15 = coreid * 8 */
|
||||
l.slli r15,r15,3
|
||||
/* r15 = coreid*128 - coreid*8 = coreid*120 = off */
|
||||
l.sub r15,r16,r15
|
||||
/* r14 = (*or1k_exception_handler_table)[coreid] = r14 + off */
|
||||
l.add r14,r14,r15
|
||||
#endif
|
||||
/* r14 now contains base of exception handler table */
|
||||
/* add offset of exception vector */
|
||||
l.add r14,r14,r13
|
||||
/* load handler address from table */
|
||||
l.lwz r13, 0(r14)
|
||||
|
||||
/* Check to see if this handler has been set yet */
|
||||
l.sfne r13,r0
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.bnf exception_exit))
|
||||
|
||||
/* Call exception handler, copy EPCR to r3 */
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.or r3,r4,r4),
|
||||
OR1K_INST(l.jalr r13)
|
||||
)
|
||||
|
||||
/* Restore impure pointer */
|
||||
l.movhi r20,hi(_or1k_impure_ptr)
|
||||
l.ori r20,r20,lo(_or1k_impure_ptr)
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
l.lwz r20,0(r20)
|
||||
l.mfspr r22,r0,OR1K_SPR_SYS_COREID_ADDR
|
||||
l.slli r22,r22,2
|
||||
l.add r20,r20,r22
|
||||
#endif
|
||||
l.lwz r20,0(r20)
|
||||
|
||||
l.movhi r21,hi(_or1k_current_impure_ptr)
|
||||
l.ori r21,r21,lo(_or1k_current_impure_ptr)
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
l.lwz r21,0(r21)
|
||||
l.add r21,r21,r22
|
||||
#endif
|
||||
l.sw 0(r21),r20
|
||||
|
||||
/* Restore state */
|
||||
l.lwz r2,GPR_BUF_OFFSET(2)(r1)
|
||||
l.lwz r3,GPR_BUF_OFFSET(3)(r1)
|
||||
l.lwz r4,GPR_BUF_OFFSET(4)(r1)
|
||||
l.lwz r5,GPR_BUF_OFFSET(5)(r1)
|
||||
l.lwz r6,GPR_BUF_OFFSET(6)(r1)
|
||||
l.lwz r7,GPR_BUF_OFFSET(7)(r1)
|
||||
l.lwz r8,GPR_BUF_OFFSET(8)(r1)
|
||||
l.lwz r9,GPR_BUF_OFFSET(9)(r1)
|
||||
l.lwz r10,GPR_BUF_OFFSET(10)(r1)
|
||||
l.lwz r11,GPR_BUF_OFFSET(11)(r1)
|
||||
l.lwz r12,GPR_BUF_OFFSET(12)(r1)
|
||||
l.lwz r13,GPR_BUF_OFFSET(13)(r1)
|
||||
l.lwz r14,GPR_BUF_OFFSET(14)(r1)
|
||||
l.lwz r15,GPR_BUF_OFFSET(15)(r1)
|
||||
l.lwz r16,GPR_BUF_OFFSET(16)(r1)
|
||||
l.lwz r17,GPR_BUF_OFFSET(17)(r1)
|
||||
l.lwz r18,GPR_BUF_OFFSET(18)(r1)
|
||||
l.lwz r19,GPR_BUF_OFFSET(19)(r1)
|
||||
l.lwz r20,GPR_BUF_OFFSET(20)(r1)
|
||||
l.lwz r21,GPR_BUF_OFFSET(21)(r1)
|
||||
l.lwz r22,GPR_BUF_OFFSET(22)(r1)
|
||||
l.lwz r23,GPR_BUF_OFFSET(23)(r1)
|
||||
l.lwz r24,GPR_BUF_OFFSET(24)(r1)
|
||||
l.lwz r25,GPR_BUF_OFFSET(25)(r1)
|
||||
l.lwz r26,GPR_BUF_OFFSET(26)(r1)
|
||||
l.lwz r27,GPR_BUF_OFFSET(27)(r1)
|
||||
l.lwz r28,GPR_BUF_OFFSET(28)(r1)
|
||||
l.lwz r29,GPR_BUF_OFFSET(29)(r1)
|
||||
l.lwz r30,GPR_BUF_OFFSET(30)(r1)
|
||||
l.lwz r31,GPR_BUF_OFFSET(31)(r1)
|
||||
|
||||
// Restore original stack
|
||||
l.lwz r1,GPR_BUF_OFFSET(1)(r1)
|
||||
|
||||
l.rfe
|
||||
l.nop
|
||||
|
||||
exception_exit:
|
||||
/* Exception handler not set, exit */
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.or r3,r4,r4),
|
||||
OR1K_INST(l.jal exit)
|
||||
)
|
|
@ -0,0 +1,21 @@
|
|||
#include "include/or1k-support.h"
|
||||
|
||||
#include "or1k-internals.h"
|
||||
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
or1k_exception_handler_table_t *_or1k_exception_handler_table;
|
||||
#else
|
||||
or1k_exception_handler_table_t _or1k_exception_handler_table;
|
||||
#endif
|
||||
|
||||
void or1k_exception_handler_add(int id, or1k_exception_handler_fptr handler)
|
||||
{
|
||||
// Subtract 2 as we do not have a vector at 0 and reset is static
|
||||
id = id - 2;
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
_or1k_exception_handler_table[or1k_coreid()][id] = handler;
|
||||
|
||||
#else
|
||||
_or1k_exception_handler_table[id] = handler;
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
/* impure.c. Handling of re-entrancy data structure for OpenRISC 1000.
|
||||
|
||||
Copyright (C) 2014, Authors
|
||||
|
||||
Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de>
|
||||
|
||||
* The authors hereby grant permission to use, copy, modify, distribute,
|
||||
* and license this software and its documentation for any purpose, provided
|
||||
* that existing copyright notices are retained in all copies and that this
|
||||
* notice is included verbatim in any distributions. No written agreement,
|
||||
* license, or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their authors
|
||||
* and need not follow the licensing terms described here, provided that
|
||||
* the new terms are clearly indicated on the first page of each file where
|
||||
* they apply.
|
||||
*/
|
||||
|
||||
#include <reent.h>
|
||||
#include "or1k-internals.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* As an exception handler may also use the library, it is better to use
|
||||
* a different re-entrancy data structure for the exceptions.
|
||||
* This data structure is configured here and as part of the exception
|
||||
* handler (or1k_exception_handler) temporarily replaces the software's
|
||||
* impure data pointer.
|
||||
*
|
||||
* During initialization, the libraries standard _impure_data and the exception
|
||||
* impure data (_exception_impure_data) are initialized. Afterwards,
|
||||
* the current value _current_impure_ptr is set to _impure_ptr.
|
||||
*
|
||||
* At runtime __getreent is called to return the current reentrancy pointer,
|
||||
* which is stored in _current_impure_ptr.
|
||||
*
|
||||
* In the or1k_exception_handler the _current_impure_ptr is set to point to
|
||||
* _exception_impure_ptr. After the exception handler returned, it is set back
|
||||
* to _impure_ptr.
|
||||
*/
|
||||
|
||||
/* Link in the external impure_data structure */
|
||||
extern struct _reent *__ATTRIBUTE_IMPURE_PTR__ _impure_ptr;
|
||||
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
struct _reent **_or1k_impure_ptr;
|
||||
struct _reent **_or1k_exception_impure_ptr;
|
||||
struct _reent **_or1k_current_impure_ptr;
|
||||
#else
|
||||
struct _reent *__ATTRIBUTE_IMPURE_PTR__ _or1k_impure_ptr;
|
||||
|
||||
/* Create exception impure data structure */
|
||||
static struct _reent _or1k_exception_impure_data = _REENT_INIT (_or1k_exception_impure_data);
|
||||
|
||||
/* Link to the exception impure data structure */
|
||||
struct _reent *__ATTRIBUTE_IMPURE_PTR__ _or1k_exception_impure_ptr = &_or1k_exception_impure_data;
|
||||
|
||||
/* Link to the currently used data structure. */
|
||||
struct _reent *__ATTRIBUTE_IMPURE_PTR__ _or1k_current_impure_ptr;
|
||||
#endif
|
||||
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
#define OR1K_LIBC_GETREENT _or1k_current_impure_ptr[or1k_coreid()]
|
||||
#else
|
||||
#define OR1K_LIBC_GETREENT _or1k_current_impure_ptr
|
||||
#endif
|
||||
|
||||
void
|
||||
_or1k_libc_impure_init (void)
|
||||
{
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
uint32_t c;
|
||||
|
||||
_or1k_impure_ptr = _sbrk_r(0, sizeof(struct _reent*) * or1k_numcores());
|
||||
_or1k_exception_impure_ptr = _sbrk_r(0, sizeof(struct _reent*) * or1k_numcores());
|
||||
_or1k_current_impure_ptr = _sbrk_r(0, sizeof(struct _reent*) * or1k_numcores());
|
||||
|
||||
_or1k_impure_ptr[0] = _impure_ptr;
|
||||
_REENT_INIT_PTR(_impure_ptr);
|
||||
for (c = 1; c < or1k_numcores(); c++) {
|
||||
_or1k_impure_ptr[c] = _sbrk_r(0, sizeof(struct _reent));
|
||||
_REENT_INIT_PTR(_or1k_impure_ptr[c]);
|
||||
}
|
||||
|
||||
for (c = 0; c < or1k_numcores(); c++) {
|
||||
_or1k_exception_impure_ptr[c] = _sbrk_r(0, sizeof(struct _reent));
|
||||
_REENT_INIT_PTR(_or1k_exception_impure_ptr[c]);
|
||||
}
|
||||
|
||||
for (c = 0; c < or1k_numcores(); c++) {
|
||||
_or1k_current_impure_ptr[c] = _or1k_impure_ptr[c];
|
||||
}
|
||||
#else
|
||||
// Initialize both impure data structures
|
||||
_REENT_INIT_PTR (_impure_ptr);
|
||||
_REENT_INIT_PTR (_or1k_exception_impure_ptr);
|
||||
|
||||
// Set current to standard impure pointer
|
||||
_or1k_current_impure_ptr = _impure_ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct _reent*
|
||||
_or1k_libc_getreent(void) {
|
||||
return OR1K_LIBC_GETREENT;
|
||||
}
|
||||
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
struct _or1k_reent (*_or1k_reent)[];
|
||||
#else
|
||||
struct _or1k_reent _or1k_reent;
|
||||
#endif
|
||||
|
||||
void
|
||||
_or1k_reent_init(void)
|
||||
{
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
size_t memsize = sizeof(struct _or1k_reent) * or1k_numcores();
|
||||
_or1k_reent = (struct _or1k_reent*) _sbrk_r(0, memsize);
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/* or1k-asm.h -- OR1K assembly helper macros
|
||||
|
||||
Copyright (c) 2014 OpenRISC Project Maintainers
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following condition
|
||||
is met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __OR1K_NOP_H__
|
||||
#define __OR1K_NOP_H__
|
||||
|
||||
#define OR1K_NOP_K_NOP 0x0
|
||||
#define OR1K_NOP_K_EXIT 0x1
|
||||
#define OR1K_NOP_K_PUTC 0x4
|
||||
#define OR1K_NOP_K_EXIT_QUIET 0xc
|
||||
|
||||
#endif
|
|
@ -0,0 +1,665 @@
|
|||
/* Copyright (c) 2014 Authors
|
||||
*
|
||||
* Contributor Julius Baxter <julius.baxter@orsoc.se>
|
||||
* Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de>
|
||||
*
|
||||
* The authors hereby grant permission to use, copy, modify, distribute,
|
||||
* and license this software and its documentation for any purpose, provided
|
||||
* that existing copyright notices are retained in all copies and that this
|
||||
* notice is included verbatim in any distributions. No written agreement,
|
||||
* license, or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their authors
|
||||
* and need not follow the licensing terms described here, provided that
|
||||
* the new terms are clearly indicated on the first page of each file where
|
||||
* they apply.
|
||||
*/
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* This program is commented throughout in a fashion suitable for processing
|
||||
with Doxygen. */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef __OR1K_SUPPORT_H__
|
||||
#define __OR1K_SUPPORT_H__
|
||||
|
||||
/*!
|
||||
* \defgroup or1k_macros OR1K macros
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Access byte-sized memory mapped register
|
||||
*
|
||||
* Used to access a byte-sized memory mapped register. It avoids usage errors
|
||||
* when not defining register addresses volatile and handles casting correctly.
|
||||
*
|
||||
* Example for both read and write:
|
||||
*
|
||||
* \code
|
||||
* uint8_t status = REG8(IPBLOCK_STATUS_REG_ADDR);
|
||||
* REG8(IPBLOCK_ENABLE) = 1;
|
||||
* \endcode
|
||||
*
|
||||
* \param add Register address
|
||||
*/
|
||||
#define REG8(add) *((volatile unsigned char *) (add))
|
||||
|
||||
/*!
|
||||
* Access halfword-sized memory mapped register
|
||||
*
|
||||
* Used to access a 16 byte-sized memory mapped register. It avoids usage errors
|
||||
* when not defining register addresses volatile and handles casting correctly.
|
||||
*
|
||||
* See REG8() for an example.
|
||||
*
|
||||
* \param add Register address
|
||||
*/
|
||||
#define REG16(add) *((volatile unsigned short *) (add))
|
||||
|
||||
/*!
|
||||
* Access word-sized memory mapped register
|
||||
*
|
||||
* Used to access a word-sized memory mapped register. It avoids usage errors
|
||||
* when not defining register addresses volatile and handles casting correctly.
|
||||
*
|
||||
* See REG8() for an example.
|
||||
*
|
||||
* \param add Register address
|
||||
*/
|
||||
#define REG32(add) *((volatile unsigned long *) (add))
|
||||
/*!
|
||||
* @}
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \defgroup or1k_interrupts OR1K interrupt control
|
||||
*
|
||||
* Interrupt control function prototypes
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*! Function pointer to interrupt handler functions */
|
||||
typedef void (*or1k_interrupt_handler_fptr)(void* data);
|
||||
|
||||
/*!
|
||||
* Add interrupt handler for interrupt line
|
||||
*
|
||||
* Registers a callback function for a certain interrupt line.
|
||||
*
|
||||
* \param line Interrupt line/id to register a handler for
|
||||
* \param handler Handler to register
|
||||
* \param data Data value passed to the handler
|
||||
*/
|
||||
void or1k_interrupt_handler_add(uint32_t line,
|
||||
or1k_interrupt_handler_fptr handler,
|
||||
void* data);
|
||||
|
||||
/*!
|
||||
* Enable interrupts from a given line
|
||||
*
|
||||
* Unmask the given interrupt line. It is also important to enable interrupts
|
||||
* in general, e.g., using or1k_interrupts_enable().
|
||||
*
|
||||
* \param line Interrupt line to enable
|
||||
*/
|
||||
void or1k_interrupt_enable(int line);
|
||||
|
||||
/*!
|
||||
* Disable interrupts from a given line
|
||||
*
|
||||
* Mask given interrupt line. It can be unmasked using or1k_interrupt_enable().
|
||||
*
|
||||
* \param line Interrupt line to disable
|
||||
*/
|
||||
void or1k_interrupt_disable(int line);
|
||||
|
||||
/*!
|
||||
* Disable interrupts
|
||||
*
|
||||
* This disables the interrupt exception. This is sufficient to disable all
|
||||
* interrupts. It does not change the mask register (which is modified using
|
||||
* or1k_interrupt_enable() and or1k_interrupt_disable()).
|
||||
*
|
||||
* The interrupt exception can be enabled using or1k_interrupts_enable().
|
||||
*
|
||||
* Finally, the status of the interrupt exception enable flag is returned by
|
||||
* this function. That allows to call this function even if interrupts are
|
||||
* already disabled. To restore the value of the interrupt exception enable
|
||||
* flag, use the or1k_interrupts_restore() function. That way you avoid to
|
||||
* accidentally enable interrupts. Example:
|
||||
*
|
||||
* \code
|
||||
* void f() {
|
||||
* uint32_t interrupt_status = or1k_interrupts_disable();
|
||||
* // do something
|
||||
* or1k_interrupts_restore(status);
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* This code will preserve the original status of the interrupt enable flag.
|
||||
*
|
||||
* \return Interrupt exception enable flag before call
|
||||
*/
|
||||
uint32_t or1k_interrupts_disable(void);
|
||||
|
||||
/*!
|
||||
* Enable interrupt exception
|
||||
*
|
||||
* Enable the interrupt exception. Beside the interrupt exception, it is also
|
||||
* necessary to enable the individual interrupt lines using
|
||||
* or1k_interrupt_enable().
|
||||
*
|
||||
* You should avoid using this function together with or1k_interrupts_disable()
|
||||
* to guard atomic blocks as it unconditionally enables the interrupt
|
||||
* exception (see documentation of or1k_interrupts_disable()).
|
||||
*/
|
||||
void or1k_interrupts_enable(void);
|
||||
|
||||
/*!
|
||||
* Restore interrupt exception enable flag
|
||||
*
|
||||
* This function restores the given status to the processor.
|
||||
* or1k_interrupts_restore(0) is identical to or1k_interrupts_disable() and
|
||||
* or1k_interrupts_restore(SPR_SR_IEE) is identical to or1k_interrupts_enable().
|
||||
*
|
||||
* It is for example used to guard an atomic block and restore the original
|
||||
* status of the interrupt exception enable flag as returned by
|
||||
* or1k_interrupts_disable(). See the documentation of or1k_interrupts_disable()
|
||||
* for a usage example.
|
||||
*
|
||||
* \param status Status of the flag to restore
|
||||
*/
|
||||
void or1k_interrupts_restore(uint32_t status);
|
||||
|
||||
/*!
|
||||
* Disable timer and interrupt exception
|
||||
*
|
||||
* This function disables the timer and interrupt exception to guard critical
|
||||
* sections. It returns the status of the enable bits before the critical
|
||||
* section, that is restored with or1k_critical_end().
|
||||
*
|
||||
* Example:
|
||||
* \code
|
||||
* ...
|
||||
* uint32_t status = or1k_critical_start();
|
||||
* // critical part
|
||||
* or1k_critical_end(status);
|
||||
* ...
|
||||
* \endcode
|
||||
*
|
||||
* \return Status of timer and interrupt exception at time of call
|
||||
*/
|
||||
uint32_t or1k_critical_begin();
|
||||
|
||||
/*!
|
||||
* Enable timer and interrupt exception
|
||||
*
|
||||
* Restore the timer and interrupt exception enable. The restore value is the
|
||||
* return value from or1k_critical_start().
|
||||
*
|
||||
* \param restore Interrupt and timer exception enable restore value
|
||||
*/
|
||||
void or1k_critical_end(uint32_t restore);
|
||||
/*!
|
||||
* @}
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \defgroup or1k_exception Exception handling
|
||||
* @{
|
||||
*/
|
||||
/*! Function pointer to an exception handler function */
|
||||
typedef void (*or1k_exception_handler_fptr)(void);
|
||||
|
||||
/*!
|
||||
* Register exception handler
|
||||
*
|
||||
* Register an exception handler for the given exception id. This handler is
|
||||
* in the following then called when the exception occurs. You can thereby
|
||||
* individually handle those exceptions.
|
||||
*
|
||||
* \param id Exception id
|
||||
* \param handler Handler callback
|
||||
*/
|
||||
void or1k_exception_handler_add(int id, or1k_exception_handler_fptr handler);
|
||||
/*!
|
||||
* @}
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \defgroup or1k_spr SPR access
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Move value to special purpose register
|
||||
*
|
||||
* Move data value to a special purpose register
|
||||
*
|
||||
* \param spr SPR identifier, see spr-defs.h for macros
|
||||
* \param value value to move to SPR
|
||||
*/
|
||||
static inline void or1k_mtspr (uint32_t spr, uint32_t value)
|
||||
{
|
||||
__asm__ __volatile__ ("l.mtspr\t\t%0,%1,0": : "r" (spr), "r" (value));
|
||||
}
|
||||
|
||||
/*!
|
||||
* Copy value from special purpose register
|
||||
*
|
||||
* Copy a data value from the given special purpose register.
|
||||
*
|
||||
* \param spr SPR identifier, see spr-defs.h for macros
|
||||
* \return SPR data value
|
||||
*/
|
||||
static inline uint32_t or1k_mfspr (uint32_t spr) {
|
||||
uint32_t value;
|
||||
__asm__ __volatile__ ("l.mfspr\t\t%0,%1,0" : "=r" (value) : "r" (spr));
|
||||
return value;
|
||||
}
|
||||
/*!
|
||||
* @}
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \defgroup or1k_util Miscellaneous utility functions
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Report value to simulator
|
||||
*
|
||||
* Uses the built-in simulator functionality.
|
||||
*
|
||||
* \param value Value to report
|
||||
*/
|
||||
void or1k_report (unsigned long int value);
|
||||
|
||||
/*!
|
||||
* Get (pseudo) random number
|
||||
*
|
||||
* This should return pseudo-random numbers, based on a Galois LFSR.
|
||||
*
|
||||
* \return (Pseudo) Random number
|
||||
*/
|
||||
unsigned long int or1k_rand(void);
|
||||
|
||||
/*!
|
||||
* Register UART callback
|
||||
*
|
||||
* This function sets a callback function that is called when a character is
|
||||
* received via UART. The callback function has no return and a gets the
|
||||
* character as parameter. When a character is received, the function is called.
|
||||
*
|
||||
* Example (UART echo):
|
||||
* \code
|
||||
* void uart_in(char c) {
|
||||
* printf("%c", c); // Echo
|
||||
* }
|
||||
*
|
||||
* int main() {
|
||||
* or1k_uart_set_read_cb(&uart_in);
|
||||
* or1k_interrupts_enable();
|
||||
*
|
||||
* while (1) {}
|
||||
* }
|
||||
* \endcode
|
||||
*/
|
||||
void or1k_uart_set_read_cb(void (*cb)(char c));
|
||||
/*!
|
||||
* @}
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \defgroup or1k_cache Cache control
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Enable instruction cache
|
||||
*/
|
||||
void or1k_icache_enable(void);
|
||||
|
||||
/*!
|
||||
* Disable instruction cache
|
||||
*/
|
||||
void or1k_icache_disable(void);
|
||||
|
||||
/*!
|
||||
* Flush instruction cache
|
||||
*
|
||||
* Invalidate instruction cache entry
|
||||
*
|
||||
* \param entry Entry to invalidate
|
||||
*/
|
||||
void or1k_icache_flush(uint32_t entry);
|
||||
|
||||
/*!
|
||||
* Enable data cache
|
||||
*/
|
||||
void or1k_dcache_enable(void);
|
||||
|
||||
/*!
|
||||
* Disable data cache
|
||||
*/
|
||||
void or1k_dcache_disable(void);
|
||||
|
||||
/*!
|
||||
* Flush data cache
|
||||
*
|
||||
* Invalidate data cache entry
|
||||
*
|
||||
* \param entry Entry to invalidate
|
||||
*/
|
||||
void or1k_dcache_flush(unsigned long entry);
|
||||
/*!
|
||||
* @}
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \defgroup or1k_mmu MMU control
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Enable instruction MMU
|
||||
*/
|
||||
void or1k_immu_enable(void);
|
||||
|
||||
/*!
|
||||
* Disable instruction MMU
|
||||
*/
|
||||
void or1k_immu_disable(void);
|
||||
|
||||
/*!
|
||||
* Enable data MMU
|
||||
*/
|
||||
void or1k_dmmu_enable(void);
|
||||
|
||||
/*!
|
||||
* Disable data MMU
|
||||
*/
|
||||
void or1k_dmmu_disable(void);
|
||||
/*!
|
||||
* @}
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \defgroup or1k_timer Timer control
|
||||
*
|
||||
* The tick timer can be used for time measurement, operating system scheduling
|
||||
* etc. By default it is initialized to continuously count the ticks of a
|
||||
* certain period after calling or1k_timer_init(). The period can later be
|
||||
* changed using or1k_timer_set_period().
|
||||
*
|
||||
* The timer is controlled using or1k_timer_enable(), or1k_timer_disable(),
|
||||
* or1k_timer_restore(), or1k_timer_pause(). After initialization it is required
|
||||
* to enable the timer the first time using or1k_timer_enable().
|
||||
* or1k_timer_disable() only disables the tick timer interrupts, it does not
|
||||
* disable the timer counting. If you plan to use a pair of or1k_timer_disable()
|
||||
* and or1k_timer_enable() to protect sections of your code against interrupts
|
||||
* you should use or1k_timer_disable() and or1k_timer_restore(), as it may be
|
||||
* possible that the timer interrupt was not enabled before disabling it,
|
||||
* enable would then start it unconditionally. or1k_timer_pause() pauses the
|
||||
* counting.
|
||||
*
|
||||
* In the default mode you can get the tick value using or1k_timer_get_ticks()
|
||||
* and reset this value using or1k_timer_reset_ticks().
|
||||
*
|
||||
* Example for using the default mode:
|
||||
*
|
||||
* \code
|
||||
* int main() {
|
||||
* uint32_t ticks = 0;
|
||||
* uint32_t timerstate;
|
||||
* or1k_timer_init(100);
|
||||
* or1k_timer_enable();
|
||||
* while (1) {
|
||||
* while (ticks == or1k_timer_get_ticks()) { }
|
||||
* timerstate = or1k_timer_disable();
|
||||
* // do something atomar
|
||||
* or1k_timer_restore(timerstate);
|
||||
* if (ticks == 100) {
|
||||
* printf("A second elapsed\n");
|
||||
* or1k_timer_reset_ticks();
|
||||
* ticks = 0;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* It is possible to change the mode of the tick timer using
|
||||
* or1k_timer_set_mode(). Allowed values are the correct bit pattern (including
|
||||
* the bit positions) for the TTMR register, it is recommended to use the macros
|
||||
* defined in spr-defs.h. For example, implementing an operating system with
|
||||
* scheduling decisions of varying duration favors the implementation of single
|
||||
* run tick timer. Here, each quantum is started before leaving the operating
|
||||
* system kernel. The counter can be restarted with or1k_timer_reset().
|
||||
* Example:
|
||||
*
|
||||
* \code
|
||||
* void tick_handler(void) {
|
||||
* // Make schedule decision
|
||||
* // and set new thread
|
||||
* or1k_timer_reset();
|
||||
* // End of exception, new thread will run
|
||||
* }
|
||||
*
|
||||
* int main() {
|
||||
* // Configure operating system and start threads..
|
||||
*
|
||||
* // Configure timer
|
||||
* or1k_timer_init(50);
|
||||
* or1k_timer_set_handler(&tick_handler);
|
||||
* or1k_timer_set_mode(SPR_TTMR_SR);
|
||||
* or1k_timer_enable();
|
||||
*
|
||||
* // Schedule first thread and die..
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Initialize tick timer
|
||||
*
|
||||
* This initializes the tick timer in default mode (see \ref or1k_timer for
|
||||
* details).
|
||||
*
|
||||
* \param hz Initial period of the tick timer
|
||||
* \return 0 if successful, -1 if timer not present
|
||||
*/
|
||||
int or1k_timer_init(unsigned int hz);
|
||||
|
||||
/*!
|
||||
* Set period of timer
|
||||
*
|
||||
* Set the period of the timer to a value in Hz. The frequency from the board
|
||||
* support package is used to determine the match value.
|
||||
*/
|
||||
void or1k_timer_set_period(uint32_t hz);
|
||||
|
||||
/*!
|
||||
* Replace the timer interrupt handler
|
||||
*
|
||||
* By default the tick timer is used to handle timer ticks. The user can replace
|
||||
* this with an own handler for example when implementing an operating system.
|
||||
*
|
||||
* \param handler The callback function pointer to the handler
|
||||
*/
|
||||
void or1k_timer_set_handler(void (*handler)(void));
|
||||
|
||||
/*!
|
||||
* Set timer mode
|
||||
*
|
||||
* The timer has different modes (see architecture manual). The default is to
|
||||
* automatically restart counting (SPR_TTMR_RT), others are single run
|
||||
* (SPR_TTMR_SR) and continuous run (SPR_TTMR_CR).
|
||||
*
|
||||
* \param mode a valid mode (use definitions from spr-defs.h as it is important
|
||||
* that those are also at the correct position in the bit field!)
|
||||
*/
|
||||
void or1k_timer_set_mode(uint32_t mode);
|
||||
|
||||
/*!
|
||||
* Enable timer interrupt
|
||||
*
|
||||
* Enable the timer interrupt exception, independent of the status before.
|
||||
* If you want to enable the timer conditionally, for example to implement a
|
||||
* non-interruptible sequence of code, you should use or1k_timer_restore(). See
|
||||
* the description of or1k_timer_disable() for more details.
|
||||
*
|
||||
* The enable will also restore the mode if the timer was paused previously.
|
||||
*/
|
||||
void or1k_timer_enable(void);
|
||||
|
||||
/*!
|
||||
* Disable timer interrupt
|
||||
*
|
||||
* This disables the timer interrupt exception and returns the state of the
|
||||
* interrupt exception enable flag before the call. This can be used with
|
||||
* or1k_timer_restore() to implement sequences of code that are not allowed to
|
||||
* be interrupted. Using or1k_timer_enable() will unconditionally enable the
|
||||
* interrupt independent of the state before calling or1k_timer_disable().
|
||||
* For an example see \ref or1k_timer.
|
||||
*
|
||||
* \return Status of timer interrupt before call
|
||||
*/
|
||||
uint32_t or1k_timer_disable(void);
|
||||
|
||||
/*!
|
||||
* Restore timer interrupt exception flag
|
||||
*
|
||||
* Restores the timer interrupt exception flag as returned by
|
||||
* or1k_timer_disable(). See the description of or1k_timer_disable() and \ref
|
||||
* or1k_timer for details and an example.
|
||||
*
|
||||
* \param sr_tee Status of timer interrupt
|
||||
*/
|
||||
void or1k_timer_restore(uint32_t sr_tee);
|
||||
|
||||
/*!
|
||||
* Pause timer counter
|
||||
*
|
||||
* Pauses the counter of the tick timer. The counter will hold its current value
|
||||
* and it can be started again with or1k_timer_enable() which will restore the
|
||||
* configured mode.
|
||||
*/
|
||||
void or1k_timer_pause(void);
|
||||
|
||||
/*!
|
||||
* Reset timer counter
|
||||
*/
|
||||
void or1k_timer_reset(void);
|
||||
|
||||
/*!
|
||||
* Get timer ticks
|
||||
*
|
||||
* Get the global ticks of the default configuration. This will increment the
|
||||
* tick counter according to the preconfigured period.
|
||||
*
|
||||
* \return Current value of ticks
|
||||
*/
|
||||
unsigned long or1k_timer_get_ticks(void);
|
||||
|
||||
/*!
|
||||
* Reset timer ticks
|
||||
*
|
||||
* Resets the timer ticks in default configuration to 0.
|
||||
*/
|
||||
void or1k_timer_reset_ticks(void);
|
||||
/*!
|
||||
* @}
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \defgroup or1k_multicore Multicore and Synchronization Support
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Compiled with multicore support
|
||||
*
|
||||
* \return 1 if compiled with multicore support, 0 otherwise
|
||||
*/
|
||||
uint32_t or1k_has_multicore_support(void);
|
||||
|
||||
/*!
|
||||
* Read core identifier
|
||||
*
|
||||
* \return Core identifier
|
||||
*/
|
||||
uint32_t or1k_coreid(void);
|
||||
|
||||
/*!
|
||||
* Read number of cores
|
||||
*
|
||||
* \return Total number of cores
|
||||
*/
|
||||
uint32_t or1k_numcores(void);
|
||||
|
||||
/*!
|
||||
* Load linked
|
||||
*
|
||||
* Load a value from the given address and link it. If the following
|
||||
* or1k_sync_sc() goes to the same address and there was no conflicting access
|
||||
* between loading and storing, the value is written back, else the write fails.
|
||||
*
|
||||
* \param address Address to load value from
|
||||
* \return Value read from the address
|
||||
*/
|
||||
uint32_t or1k_sync_ll(void *address);
|
||||
|
||||
/**
|
||||
* Store conditional
|
||||
*
|
||||
* Conditionally store a value to the address. The address must have been read
|
||||
* before using or1k_sync_ll() and there must be no other load link after that,
|
||||
* otherwise this will always fail. In case there was no other write to the same
|
||||
* address in between the load link and the store conditional, the store is
|
||||
* successful, otherwise it will also fail.
|
||||
*
|
||||
* \param address Address to conditionally store to
|
||||
* \param value Value to write to address
|
||||
* \return 1 if success, 0 if fail
|
||||
*/
|
||||
int or1k_sync_sc(void *address, uint32_t value);
|
||||
|
||||
/*!
|
||||
* Compare and Swap
|
||||
*
|
||||
* Loads a data item from the memory and compares a given value to it. If the
|
||||
* values match, a new value is written to the memory, if they mismatch, the
|
||||
* operation is aborted. The whole operation is atomic, i.e., it is guaranteed
|
||||
* that no other core changes the value between the read and the write.
|
||||
*
|
||||
* \param address Address to operate on
|
||||
* \param compare Compare value
|
||||
* \param swap New value to write
|
||||
* \return The value read from memory (can be used to check for success)
|
||||
*/
|
||||
uint32_t or1k_sync_cas(void *address, uint32_t compare, uint32_t swap);
|
||||
|
||||
/*!
|
||||
* Test and Set Lock
|
||||
*
|
||||
* Check for a lock on an address. This means, if there is 0 at an address it
|
||||
* will overwrite it with 1 and return 0. If the lock was already set (value
|
||||
* 1 read from address), the function returns 1. The operation is atomic.
|
||||
*
|
||||
* \param address Address of the lock
|
||||
* \return 0 if success, 1 if failed
|
||||
*/
|
||||
int or1k_sync_tsl(void *address);
|
||||
/*!
|
||||
* @}
|
||||
*/
|
||||
|
||||
#endif /* __NEWLIB_OR1K_SUPPORT_H__ */
|
|
@ -0,0 +1,166 @@
|
|||
/* interrupts-asm.S -- interrupt handling for OpenRISC 1000.
|
||||
*
|
||||
* Copyright (c) 2011, 2012, 2014 Authors
|
||||
*
|
||||
* Contributor Julius Baxter <juliusbaxter@gmail.com>
|
||||
* Contributor Stefan Kristiansson <stefan.kristiansson@saunalahti.fi>
|
||||
* Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de>
|
||||
*
|
||||
* The authors hereby grant permission to use, copy, modify, distribute,
|
||||
* and license this software and its documentation for any purpose, provided
|
||||
* that existing copyright notices are retained in all copies and that this
|
||||
* notice is included verbatim in any distributions. No written agreement,
|
||||
* license, or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their authors
|
||||
* and need not follow the licensing terms described here, provided that
|
||||
* the new terms are clearly indicated on the first page of each file where
|
||||
* they apply.
|
||||
*/
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/*!Generic interrupt handler function for or1k
|
||||
*/
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#include "include/or1k-asm.h"
|
||||
#include "include/or1k-sprs.h"
|
||||
|
||||
.extern _or1k_interrupt_handler_table
|
||||
.extern _or1k_interrupt_handler_data_ptr_table
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/*!Function to call appropriate interrupt handler
|
||||
*/
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
.section .text
|
||||
.global _or1k_interrupt_handler
|
||||
.type _or1k_interrupt_handler,@function
|
||||
|
||||
_or1k_interrupt_handler:
|
||||
/* Make room on stack, save link register */
|
||||
l.addi r1,r1,-12
|
||||
l.sw 0(r1),r9
|
||||
|
||||
/* Read PICSR */
|
||||
l.mfspr r3,r0,OR1K_SPR_PIC_PICSR_ADDR
|
||||
|
||||
/* Load handler table base address */
|
||||
l.movhi r7,hi(_or1k_interrupt_handler_table)
|
||||
l.ori r7,r7,lo(_or1k_interrupt_handler_table)
|
||||
/* Load data pointer table base address */
|
||||
l.movhi r12,hi(_or1k_interrupt_handler_data_ptr_table)
|
||||
l.ori r12,r12,lo(_or1k_interrupt_handler_data_ptr_table)
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
/* Read the addresses of the arrays of cores */
|
||||
/* r7 = (*or1k_interrupt_handler_table) */
|
||||
l.lwz r7,0(r7)
|
||||
/* r12 = (*or1k_interrupt_handler_data_ptr_table) */
|
||||
l.lwz r12,0(r12)
|
||||
/* Generate offset in arrays */
|
||||
/* r14 = coreid */
|
||||
l.mfspr r14,r0,OR1K_SPR_SYS_COREID_ADDR
|
||||
/* r14 = coreid*32*4 = off */
|
||||
l.slli r14,r14,7
|
||||
/* r7 = (*or1k_exception_handler_table)[coreid] */
|
||||
l.add r7,r7,r14
|
||||
/* r12 = (*or1k_exception_handler_table)[coreid] */
|
||||
l.add r12,r12,r14
|
||||
#endif
|
||||
|
||||
.L0:
|
||||
/* Find first set bit in PICSR */
|
||||
l.ff1 r4,r3
|
||||
/* Any bits set? */
|
||||
l.sfne r4,r0
|
||||
/* If none, finish */
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.bnf .L2))
|
||||
/* What is IRQ function table offset? */
|
||||
l.addi r5,r4,-1
|
||||
l.slli r6,r5,2
|
||||
/* Add this to table bases */
|
||||
l.add r14,r6,r7
|
||||
l.add r13,r6,r12
|
||||
|
||||
/* Fetch handler function address */
|
||||
l.lwz r14,0(r14)
|
||||
|
||||
/* Double check it's valid, compare against INTERRUPT_HANDLER_NOT_SET */
|
||||
l.sfne r14,r0
|
||||
/* Skip if no handler: TODO: Indicate interrupt fired but no handler*/
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.bnf .L1))
|
||||
|
||||
/* Pull out data pointer from table, save r3, we'll write over it */
|
||||
l.sw 4(r1),r3
|
||||
l.lwz r3,0(r13)
|
||||
/* Call handler, save r5 in delay slot */
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.sw 8(r1),r5),
|
||||
OR1K_INST(l.jalr r14)
|
||||
)
|
||||
|
||||
/* Reload r3,r5 */
|
||||
l.lwz r3,4(r1)
|
||||
l.lwz r5,8(r1)
|
||||
.L1:
|
||||
/* Clear bit from PICSR, return to start of checking loop */
|
||||
l.ori r6,r0,1
|
||||
l.sll r6,r6,r5
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.xor r3,r3,r6),
|
||||
OR1K_INST(l.j .L0)
|
||||
)
|
||||
|
||||
.L2:
|
||||
/* Finish up - write PICSR back, restore r9*/
|
||||
l.lwz r9,0(r1)
|
||||
l.mtspr r0,r3,OR1K_SPR_PIC_PICSR_ADDR
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.addi r1,r1,12),
|
||||
OR1K_INST(l.jr r9)
|
||||
)
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/*!Function to enable an interrupt handler in the PICMR
|
||||
*/
|
||||
/* -------------------------------------------------------------------------- */
|
||||
.global or1k_interrupt_enable
|
||||
.type or1k_interrupt_enable,@function
|
||||
|
||||
/* r3 should have IRQ line for peripheral */
|
||||
or1k_interrupt_enable:
|
||||
l.addi r1,r1,-4
|
||||
l.sw 0(r1),r4
|
||||
l.ori r4,r0,0x1
|
||||
l.sll r4,r4,r3
|
||||
l.mfspr r3,r0,OR1K_SPR_PIC_PICMR_ADDR
|
||||
l.or r3,r3,r4
|
||||
l.mtspr r0,r3,OR1K_SPR_PIC_PICMR_ADDR
|
||||
l.lwz r4,0(r1)
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.addi r1,r1,4),
|
||||
OR1K_INST(l.jr r9)
|
||||
)
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/*!Function to disable an interrupt handler in the PICMR
|
||||
*/
|
||||
/* -------------------------------------------------------------------------- */
|
||||
.global or1k_interrupt_disable
|
||||
.type or1k_interrupt_disable,@function
|
||||
|
||||
/* r3 should have IRQ line for peripheral */
|
||||
or1k_interrupt_disable:
|
||||
l.addi r1,r1,-4
|
||||
l.sw 0(r1),r4
|
||||
l.ori r4,r0,0x1
|
||||
l.sll r4,r4,r3
|
||||
l.xori r4,r4,0xffff
|
||||
l.mfspr r3,r0,OR1K_SPR_PIC_PICMR_ADDR
|
||||
l.and r3,r3,r4
|
||||
l.mtspr r0,r3,OR1K_SPR_PIC_PICMR_ADDR
|
||||
l.lwz r4,0(r1)
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.addi r1,r1,4),
|
||||
OR1K_INST(l.jr r9)
|
||||
)
|
|
@ -0,0 +1,69 @@
|
|||
/* interrupts.c -- interrupt handling for OpenRISC 1000.
|
||||
*
|
||||
* Copyright (c) 2014 Authors
|
||||
*
|
||||
* Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de>
|
||||
*
|
||||
* The authors hereby grant permission to use, copy, modify, distribute,
|
||||
* and license this software and its documentation for any purpose, provided
|
||||
* that existing copyright notices are retained in all copies and that this
|
||||
* notice is included verbatim in any distributions. No written agreement,
|
||||
* license, or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their authors
|
||||
* and need not follow the licensing terms described here, provided that
|
||||
* the new terms are clearly indicated on the first page of each file where
|
||||
* they apply.
|
||||
*/
|
||||
|
||||
#include "include/or1k-support.h"
|
||||
#include "include/or1k-sprs.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#include "or1k-internals.h"
|
||||
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
or1k_interrupt_handler_table_t *_or1k_interrupt_handler_table;
|
||||
or1k_interrupt_handler_data_ptr_table_t *_or1k_interrupt_handler_data_ptr_table;
|
||||
#else
|
||||
or1k_interrupt_handler_table_t _or1k_interrupt_handler_table;
|
||||
or1k_interrupt_handler_data_ptr_table_t _or1k_interrupt_handler_data_ptr_table;
|
||||
#endif
|
||||
|
||||
void or1k_interrupt_handler_add(uint32_t id,
|
||||
or1k_interrupt_handler_fptr handler,
|
||||
void *data_ptr)
|
||||
{
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
_or1k_interrupt_handler_table[or1k_coreid()][id] = handler;
|
||||
_or1k_interrupt_handler_data_ptr_table[or1k_coreid()][id] = (uint32_t) data_ptr;
|
||||
#else
|
||||
_or1k_interrupt_handler_table[id] = handler;
|
||||
_or1k_interrupt_handler_data_ptr_table[id] = (uint32_t) data_ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
or1k_interrupts_enable(void)
|
||||
{
|
||||
uint32_t sr = or1k_mfspr(OR1K_SPR_SYS_SR_ADDR);
|
||||
sr = OR1K_SPR_SYS_SR_IEE_SET(sr, 1);
|
||||
or1k_mtspr(OR1K_SPR_SYS_SR_ADDR, sr);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
or1k_interrupts_disable(void)
|
||||
{
|
||||
uint32_t oldsr, newsr;
|
||||
oldsr= or1k_mfspr(OR1K_SPR_SYS_SR_ADDR);
|
||||
newsr = OR1K_SPR_SYS_SR_IEE_SET(oldsr, 0);
|
||||
or1k_mtspr(OR1K_SPR_SYS_SR_ADDR, newsr);
|
||||
return OR1K_SPR_SYS_SR_IEE_GET(oldsr);
|
||||
}
|
||||
|
||||
void
|
||||
or1k_interrupts_restore(uint32_t sr_iee)
|
||||
{
|
||||
uint32_t sr = or1k_mfspr(OR1K_SPR_SYS_SR_ADDR);
|
||||
sr = OR1K_SPR_SYS_SR_IEE_SET(sr, sr_iee);
|
||||
or1k_mtspr(OR1K_SPR_SYS_SR_ADDR, sr);
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/* mmu-asm.S -- MMU handling for OpenRISC 1000.
|
||||
*
|
||||
* Copyright (c) 2011, 2014 Authors
|
||||
*
|
||||
* Contributor Julius Baxter <juliusbaxter@gmail.com>
|
||||
* Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de>
|
||||
*
|
||||
* The authors hereby grant permission to use, copy, modify, distribute,
|
||||
* and license this software and its documentation for any purpose, provided
|
||||
* that existing copyright notices are retained in all copies and that this
|
||||
* notice is included verbatim in any distributions. No written agreement,
|
||||
* license, or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their authors
|
||||
* and need not follow the licensing terms described here, provided that
|
||||
* the new terms are clearly indicated on the first page of each file where
|
||||
* they apply.
|
||||
*/
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/*!Function to control MMU
|
||||
*/
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#include "include/or1k-asm.h"
|
||||
#include "include/or1k-sprs.h"
|
||||
|
||||
/* MMU control functions always switch MMU control with a l.rfe to return
|
||||
from function */
|
||||
.section .text
|
||||
|
||||
.global or1k_dmmu_enable
|
||||
or1k_dmmu_enable:
|
||||
l.mfspr r3,r0,OR1K_SPR_SYS_SR_ADDR
|
||||
l.ori r3,r3,OR1K_SPR_SYS_SR_DME_MASK
|
||||
l.mtspr r0,r3,OR1K_SPR_SYS_ESR_BASE
|
||||
l.mtspr r0,r9,OR1K_SPR_SYS_EPCR_BASE
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.rfe))
|
||||
|
||||
|
||||
.global or1k_dmmu_disable
|
||||
or1k_dmmu_disable:
|
||||
l.ori r3,r0,OR1K_SPR_SYS_SR_DME_MASK
|
||||
l.xori r4,r3,0xffff
|
||||
l.mfspr r3,r0,OR1K_SPR_SYS_SR_ADDR
|
||||
l.and r3,r4,r3
|
||||
l.mtspr r0,r3,OR1K_SPR_SYS_ESR_BASE
|
||||
l.mtspr r0,r9,OR1K_SPR_SYS_EPCR_BASE
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.rfe))
|
||||
|
||||
|
||||
.global or1k_immu_enable
|
||||
or1k_immu_enable:
|
||||
l.mfspr r3,r0,OR1K_SPR_SYS_SR_ADDR
|
||||
l.ori r3,r3,OR1K_SPR_SYS_SR_IME_MASK
|
||||
l.mtspr r0,r3,OR1K_SPR_SYS_ESR_BASE
|
||||
l.mtspr r0,r9,OR1K_SPR_SYS_EPCR_BASE
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.rfe))
|
||||
|
||||
.global or1k_immu_disable
|
||||
or1k_immu_disable:
|
||||
l.ori r3,r0,OR1K_SPR_SYS_SR_IME_MASK
|
||||
l.xori r4,r3,0xffff
|
||||
l.mfspr r3,r0,OR1K_SPR_SYS_SR_ADDR
|
||||
l.and r3,r4,r3
|
||||
l.mtspr r0,r3,OR1K_SPR_SYS_ESR_BASE
|
||||
l.mtspr r0,r9,OR1K_SPR_SYS_EPCR_BASE
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.rfe))
|
|
@ -0,0 +1,67 @@
|
|||
#ifndef __OR1K_INTERNAL_H__
|
||||
#define __OR1K_INTERNAL_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "include/or1k-support.h"
|
||||
|
||||
extern uint32_t* _or1k_stack_top;
|
||||
extern size_t _or1k_stack_size;
|
||||
extern uint32_t* _or1k_stack_bottom;
|
||||
|
||||
extern uint32_t* _or1k_exception_stack_top;
|
||||
extern size_t _or1k_exception_stack_size;
|
||||
extern uint32_t* _or1k_exception_stack_bottom;
|
||||
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
extern uint32_t* *_or1k_stack_core;
|
||||
extern uint32_t* *_or1k_exception_stack_core;
|
||||
#endif
|
||||
|
||||
|
||||
// The first two vectors are not used (address 0 and reset)
|
||||
#define OR1K_NUM_EXCEPTIONS 30
|
||||
|
||||
typedef or1k_exception_handler_fptr or1k_exception_handler_table_t[OR1K_NUM_EXCEPTIONS];
|
||||
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
extern or1k_exception_handler_table_t *_or1k_exception_handler_table;
|
||||
#else
|
||||
extern or1k_exception_handler_table_t _or1k_exception_handler_table;
|
||||
#endif
|
||||
|
||||
typedef or1k_interrupt_handler_fptr or1k_interrupt_handler_table_t[32];
|
||||
typedef void* or1k_interrupt_handler_data_ptr_table_t[32];
|
||||
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
extern or1k_interrupt_handler_table_t *_or1k_interrupt_handler_table;
|
||||
extern or1k_interrupt_handler_data_ptr_table_t *_or1k_interrupt_handler_data_ptr_table;
|
||||
#else
|
||||
extern or1k_interrupt_handler_table_t _or1k_interrupt_handler_table;
|
||||
extern or1k_interrupt_handler_data_ptr_table_t _or1k_interrupt_handler_data_ptr_table;
|
||||
#endif
|
||||
|
||||
extern void _or1k_interrupt_handler(void);
|
||||
|
||||
struct _or1k_reent {
|
||||
/* Tick timer variable */
|
||||
volatile uint32_t or1k_timer_ticks;
|
||||
|
||||
/* Tick rate storage */
|
||||
uint32_t or1k_timer_period;
|
||||
uint32_t or1k_timer_mode;
|
||||
};
|
||||
|
||||
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
extern struct _or1k_reent (*_or1k_reent)[];
|
||||
#define OR1K_REENT (*_or1k_reent)[or1k_coreid()]
|
||||
#else
|
||||
extern struct _or1k_reent _or1k_reent;
|
||||
#define OR1K_REENT _or1k_reent
|
||||
#endif
|
||||
|
||||
extern void _or1k_reent_init();
|
||||
|
||||
#endif
|
|
@ -0,0 +1,82 @@
|
|||
/* or1k_uart.c -- UART implementation for OpenRISC 1000.
|
||||
*
|
||||
*Copyright (c) 2014 Authors
|
||||
*
|
||||
* Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de>
|
||||
*
|
||||
* The authors hereby grant permission to use, copy, modify, distribute,
|
||||
* and license this software and its documentation for any purpose, provided
|
||||
* that existing copyright notices are retained in all copies and that this
|
||||
* notice is included verbatim in any distributions. No written agreement,
|
||||
* license, or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their authors
|
||||
* and need not follow the licensing terms described here, provided that
|
||||
* the new terms are clearly indicated on the first page of each file where
|
||||
* they apply.
|
||||
*/
|
||||
|
||||
#include "include/or1k-support.h"
|
||||
#include "or1k_uart.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void (*_or1k_uart_read_cb)(char c);
|
||||
|
||||
void _or1k_uart_interrupt_handler(uint32_t data)
|
||||
{
|
||||
_or1k_uart_read_cb(REG8(RB));
|
||||
}
|
||||
|
||||
int _or1k_uart_init(void)
|
||||
{
|
||||
uint16_t divisor;
|
||||
|
||||
// Is uart present?
|
||||
if (!_or1k_board_uart_base) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Reset the callback function
|
||||
_or1k_uart_read_cb = 0;
|
||||
|
||||
// Calculate and set divisor
|
||||
divisor = _or1k_board_clk_freq / (_or1k_board_uart_baud * 16);
|
||||
REG8(LCR) = LCR_DLA;
|
||||
REG8(DLB1) = divisor & 0xff;
|
||||
REG8(DLB2) = divisor >> 8;
|
||||
|
||||
// Set line control register:
|
||||
// - 8 bits per character
|
||||
// - 1 stop bit
|
||||
// - No parity
|
||||
// - Break disabled
|
||||
// - Disallow access to divisor latch
|
||||
REG8(LCR) = LCR_BPC_8;
|
||||
|
||||
// Reset FIFOs and set trigger level to 14 bytes
|
||||
REG8(FCR) = FCR_CLRRECV | FCR_CLRTMIT | FCR_TRIG_14;
|
||||
|
||||
// Disable all interrupts
|
||||
REG8(IER) = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void _or1k_uart_write(char c)
|
||||
{
|
||||
while (!REG8(LSR) & LSR_TFE) {}
|
||||
|
||||
REG8(THR) = c;
|
||||
}
|
||||
|
||||
void or1k_uart_set_read_cb(void (*cb)(char c))
|
||||
{
|
||||
_or1k_uart_read_cb = cb;
|
||||
|
||||
// Enable interrupt
|
||||
REG8(IER) = 1 << IER_RDAI;
|
||||
|
||||
or1k_interrupt_handler_add(_or1k_board_uart_IRQ,
|
||||
_or1k_uart_interrupt_handler, 0);
|
||||
or1k_interrupt_enable(_or1k_board_uart_IRQ);
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/* or1k_uart.h -- UART definitions for OpenRISC 1000.
|
||||
*
|
||||
* Copyright (c) 2014 Authors
|
||||
*
|
||||
* Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de>
|
||||
*
|
||||
* The authors hereby grant permission to use, copy, modify, distribute,
|
||||
* and license this software and its documentation for any purpose, provided
|
||||
* that existing copyright notices are retained in all copies and that this
|
||||
* notice is included verbatim in any distributions. No written agreement,
|
||||
* license, or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their authors
|
||||
* and need not follow the licensing terms described here, provided that
|
||||
* the new terms are clearly indicated on the first page of each file where
|
||||
* they apply.
|
||||
*/
|
||||
|
||||
/* This is the generic board support for the OpenCores UART device */
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "board.h"
|
||||
|
||||
extern void (*_or1k_uart_read_cb)(char c);
|
||||
|
||||
void _or1k_uart_interrupt_handler(uint32_t data);
|
||||
|
||||
int _or1k_uart_init(void);
|
||||
void _or1k_uart_write(char c);
|
||||
|
||||
#define RB _or1k_board_uart_base + 0
|
||||
#define THR _or1k_board_uart_base + 0
|
||||
#define IER _or1k_board_uart_base + 1
|
||||
#define IIR _or1k_board_uart_base + 2
|
||||
#define FCR _or1k_board_uart_base + 2
|
||||
#define LCR _or1k_board_uart_base + 3
|
||||
#define MCR _or1k_board_uart_base + 4
|
||||
#define LSR _or1k_board_uart_base + 5
|
||||
#define MSR _or1k_board_uart_base + 6
|
||||
|
||||
#define DLB1 _or1k_board_uart_base + 0
|
||||
#define DLB2 _or1k_board_uart_base + 1
|
||||
|
||||
#define IER_RDAI 0
|
||||
#define IER_TEI 1
|
||||
#define IER_RLSI 2
|
||||
#define IER_MSI 3
|
||||
|
||||
#define IIR_RLS 0xC3
|
||||
#define IIR_RDA 0xC2
|
||||
#define IIR_TO 0xC6
|
||||
#define IIR_THRE 0xC1
|
||||
#define IIT_MS 0xC0
|
||||
|
||||
#define FCR_CLRRECV 0x1
|
||||
#define FCR_CLRTMIT 0x2
|
||||
#define FCR_TRIG_1 0x0
|
||||
#define FCR_TRIG_4 0x40
|
||||
#define FCR_TRIG_8 0x80
|
||||
#define FCR_TRIG_14 0xC0
|
||||
|
||||
#define LCR_BPC_MASK 0x3
|
||||
#define LCR_SB_MASK 0x4
|
||||
|
||||
#define LCR_BPC_5 0x0
|
||||
#define LCR_BPC_6 0x1
|
||||
#define LCR_BPC_7 0x2
|
||||
#define LCR_BPC_8 0x3
|
||||
#define LCR_SB_1 0x0
|
||||
#define LCR_SB_2 0x4
|
||||
#define LCR_PE 0x8
|
||||
#define LCR_EPS 0x10
|
||||
#define LCR_SP 0x20
|
||||
#define LCR_BC 0x40
|
||||
#define LCR_DLA 0x80
|
||||
|
||||
#define LSR_DR 0x0
|
||||
#define LSR_OE 0x2
|
||||
#define LSR_PE 0x4
|
||||
#define LSR_FE 0x8
|
||||
#define LSR_BI 0x10
|
||||
#define LSR_TFE 0x20
|
||||
#define LSR_TEI 0x40
|
|
@ -0,0 +1,34 @@
|
|||
/* outbyte.S -- Write one byte for OpenRISC 1000.
|
||||
*
|
||||
* Copyright (c) 2014 Authors
|
||||
*
|
||||
* Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de>
|
||||
*
|
||||
* The authors hereby grant permission to use, copy, modify, distribute,
|
||||
* and license this software and its documentation for any purpose, provided
|
||||
* that existing copyright notices are retained in all copies and that this
|
||||
* notice is included verbatim in any distributions. No written agreement,
|
||||
* license, or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their authors
|
||||
* and need not follow the licensing terms described here, provided that
|
||||
* the new terms are clearly indicated on the first page of each file where
|
||||
* they apply.
|
||||
*/
|
||||
|
||||
#include "include/or1k-asm.h"
|
||||
|
||||
.global _or1k_outbyte
|
||||
|
||||
.text
|
||||
_or1k_outbyte:
|
||||
LOAD_SYMBOL_2_GPR(r4,_or1k_board_uart_base)
|
||||
l.lwz r4, 0(r4)
|
||||
l.sfeq r4, r0
|
||||
OR1K_DELAYED_NOP(l.bf .Lnouart)
|
||||
.Luart:
|
||||
OR1K_DELAYED_NOP(l.j _or1k_uart_write)
|
||||
.Lnouart:
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.nop 0x4),
|
||||
OR1K_INST(l.jr r9)
|
||||
)
|
|
@ -0,0 +1,52 @@
|
|||
/* sbrk.c -- allocate space on heap on OpenRISC 1000.
|
||||
*
|
||||
* Copyright (c) 2014 Authors
|
||||
*
|
||||
* Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de>
|
||||
*
|
||||
* The authors hereby grant permission to use, copy, modify, distribute,
|
||||
* and license this software and its documentation for any purpose, provided
|
||||
* that existing copyright notices are retained in all copies and that this
|
||||
* notice is included verbatim in any distributions. No written agreement,
|
||||
* license, or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their authors
|
||||
* and need not follow the licensing terms described here, provided that
|
||||
* the new terms are clearly indicated on the first page of each file where
|
||||
* they apply.
|
||||
*/
|
||||
|
||||
#include <reent.h>
|
||||
|
||||
#include "include/or1k-support.h"
|
||||
|
||||
static uint32_t _or1k_heap_end;
|
||||
|
||||
void *
|
||||
_sbrk_r (struct _reent * reent, ptrdiff_t incr)
|
||||
{
|
||||
extern uint32_t end; /* Set by linker. */
|
||||
uint32_t prev_heap_end;
|
||||
|
||||
// This needs to be atomic
|
||||
|
||||
// Disable interrupts on this core
|
||||
uint32_t sr_iee = or1k_interrupts_disable();
|
||||
uint32_t sr_tee = or1k_timer_disable();
|
||||
|
||||
// Initialize heap end to end if not initialized before
|
||||
or1k_sync_cas((void*) &_or1k_heap_end, 0, (uint32_t) &end);
|
||||
|
||||
do {
|
||||
// Read previous heap end
|
||||
prev_heap_end = _or1k_heap_end;
|
||||
// and try to set it to the new value as long as it has changed
|
||||
} while (or1k_sync_cas((void*) &_or1k_heap_end,
|
||||
(uint32_t) prev_heap_end,
|
||||
(uint32_t) (prev_heap_end + incr)) != (uint32_t) prev_heap_end);
|
||||
|
||||
// Restore interrupts on this core
|
||||
or1k_timer_restore(sr_tee);
|
||||
or1k_interrupts_restore(sr_iee);
|
||||
|
||||
return (void*) prev_heap_end;
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
#include "include/or1k-asm.h"
|
||||
#include "include/or1k-sprs.h"
|
||||
|
||||
.section .text
|
||||
|
||||
|
||||
.global or1k_has_multicore_support
|
||||
.type or1k_has_multicore_support,@function
|
||||
or1k_has_multicore_support:
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
// Return 1
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.ori r11,r0,1),
|
||||
OR1K_INST(l.jr r9)
|
||||
)
|
||||
#else
|
||||
// Return 0
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.or r11,r0,r0),
|
||||
OR1K_INST(l.jr r9)
|
||||
)
|
||||
#endif
|
||||
|
||||
.global or1k_coreid
|
||||
.type or1k_coreid,@function
|
||||
or1k_coreid:
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
// Return SPR with core identifier
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.mfspr r11,r0,OR1K_SPR_SYS_COREID_ADDR),
|
||||
OR1K_INST(l.jr r9)
|
||||
)
|
||||
#else
|
||||
// Return 0
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.or r11,r0,r0),
|
||||
OR1K_INST(l.jr r9)
|
||||
)
|
||||
#endif
|
||||
|
||||
.global or1k_numcores
|
||||
.type or1k_numcores,@function
|
||||
or1k_numcores:
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
// Return SPR with number of cores
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.mfspr r11,r0,OR1K_SPR_SYS_NUMCORES_ADDR),
|
||||
OR1K_INST(l.jr r9)
|
||||
)
|
||||
#else
|
||||
// Return 1
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.ori r11,r0,1),
|
||||
OR1K_INST(l.jr r9)
|
||||
)
|
||||
#endif
|
||||
|
||||
.global or1k_sync_ll
|
||||
.type or1k_sync_ll,@function
|
||||
or1k_sync_ll:
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
// Load word atomic
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.lwa r11, 0(r3)),
|
||||
OR1K_INST(l.jr r9)
|
||||
)
|
||||
#else
|
||||
// Simply load word, TODO: throw exception? which?
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.lwz r11, 0(r3)),
|
||||
OR1K_INST(l.jr r9)
|
||||
)
|
||||
#endif
|
||||
|
||||
.global or1k_sync_sc
|
||||
.type or1k_sync_sc,@function
|
||||
or1k_sync_sc:
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
// swa sets the flag if it was succesfull
|
||||
// Store the value to address and set flag
|
||||
l.swa 0(r3),r4
|
||||
OR1K_DELAYED(
|
||||
// Set return to success speculatively (may go to delay slot)
|
||||
OR1K_INST(l.ori r11,r0,1),
|
||||
// If the swa was successfull, jump to end
|
||||
OR1K_INST(l.bf .or1k_sync_sc_done)
|
||||
)
|
||||
// If the swa was not successfull, set
|
||||
l.or r11,r0,r0
|
||||
.or1k_sync_sc_done:
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.jr r9))
|
||||
#else
|
||||
// Simply store word, TODO: throw exception? which?
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.sw 0(r3),r4),
|
||||
OR1K_INST(l.jr r9)
|
||||
)
|
||||
#endif
|
||||
|
||||
|
||||
.global or1k_sync_cas
|
||||
.type or1k_sync_sc,@function
|
||||
or1k_sync_cas:
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
/* Load linked address value to return register */
|
||||
l.lwa r11,0(r3)
|
||||
/* Compare value to parameter */
|
||||
l.sfeq r11,r4
|
||||
/* If not equal: abort and return the read value */
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.bnf .or1k_sync_cas_done))
|
||||
/* If compare was successfull: try writing */
|
||||
l.swa 0(r3),r5
|
||||
/* If writing was not successful: restart */
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.bnf or1k_sync_cas))
|
||||
.or1k_sync_cas_done:
|
||||
/* Return value is the original read value */
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.jr r9))
|
||||
#else
|
||||
// Non-atomic CAS, TODO: throw exception? which?
|
||||
l.lwz r11,0(r3)
|
||||
l.sfeq r11,r4
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.bnf .or1k_sync_cas_done))
|
||||
l.sw 0(r3),r5
|
||||
.or1k_sync_cas_done:
|
||||
OR1K_DELAYED_NOP(OR1K_INST(l.jr r9))
|
||||
#endif
|
||||
|
||||
.global or1k_sync_tsl
|
||||
.type or1k_sync_tsl,@function
|
||||
or1k_sync_tsl:
|
||||
l.or r4,r0,r0
|
||||
OR1K_DELAYED(
|
||||
OR1K_INST(l.addi r5,r0,1),
|
||||
OR1K_INST(l.j or1k_sync_cas)
|
||||
)
|
|
@ -0,0 +1,160 @@
|
|||
/* syscalls.c -- reentrant syscalls for OpenRISC 1000.
|
||||
*
|
||||
* Copyright (c) 2011, 2014 Authors
|
||||
*
|
||||
* Contributor Julius Baxter <juliusbaxter@gmail.com>
|
||||
* Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de>
|
||||
*
|
||||
* The authors hereby grant permission to use, copy, modify, distribute,
|
||||
* and license this software and its documentation for any purpose, provided
|
||||
* that existing copyright notices are retained in all copies and that this
|
||||
* notice is included verbatim in any distributions. No written agreement,
|
||||
* license, or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their authors
|
||||
* and need not follow the licensing terms described here, provided that
|
||||
* the new terms are clearly indicated on the first page of each file where
|
||||
* they apply.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <reent.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include "board.h"
|
||||
|
||||
/* Write is actually the only thing we provide. All other are stubs.. */
|
||||
|
||||
extern void _or1k_outbyte(char c);
|
||||
|
||||
_ssize_t
|
||||
_write_r(struct _reent * reent, int fd, const void *buf, size_t nbytes)
|
||||
{
|
||||
int i;
|
||||
char* b = (char*) buf;
|
||||
|
||||
for (i = 0; i < nbytes; i++) {
|
||||
if (*(b + i) == '\n') {
|
||||
_or1k_outbyte ('\r');
|
||||
}
|
||||
_or1k_outbyte (*(b + i));
|
||||
}
|
||||
return (nbytes);
|
||||
}
|
||||
|
||||
void
|
||||
_exit(int rc)
|
||||
{
|
||||
_or1k_board_exit();
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
int
|
||||
_close_r(struct _reent *reent, int fildes)
|
||||
{
|
||||
reent->_errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *__env[1] = { 0 };
|
||||
char **environ = __env;
|
||||
|
||||
int
|
||||
_execve_r(struct _reent *reent, const char *name, char * const *argv,
|
||||
char * const *env)
|
||||
{
|
||||
reent->_errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
_fork_r(struct _reent *reent)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
_fstat_r(struct _reent *reent, int fildes, struct stat *st)
|
||||
{
|
||||
reent->_errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
_getpid_r(struct _reent *reent)
|
||||
{
|
||||
reent->_errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
_gettimeofday(struct _reent *reent, struct timeval *ptimeval, void *ptimezone)
|
||||
{
|
||||
reent->_errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
_isatty_r(struct _reent *reent, int file)
|
||||
{
|
||||
reent->_errno = ENOSYS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_kill_r(struct _reent *reent, int pid, int sig)
|
||||
{
|
||||
reent->_errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
_link_r(struct _reent *reent, const char *existing, const char *new)
|
||||
{
|
||||
reent->_errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_off_t
|
||||
_lseek_r(struct _reent *reent, int file, _off_t ptr, int dir)
|
||||
{
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
_open(struct _reent *reent, char *file, int flags, int mode)
|
||||
{
|
||||
reent->_errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_ssize_t
|
||||
_read_r(struct _reent *reent, int file, void *ptr, size_t len)
|
||||
{
|
||||
reent->_errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
_readlink_r(struct _reent *reent, const char *path, char *buf, size_t bufsize)
|
||||
{
|
||||
reent->_errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
_stat_r(struct _reent *reent, const char *path, struct stat *buf)
|
||||
{
|
||||
reent->_errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
_unlink_r(struct _reent *reent, const char * path)
|
||||
{
|
||||
reent->_errno = EIO;
|
||||
return (-1);
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
/* timer.c -- tick timer functions for OpenRISC 1000.
|
||||
*
|
||||
* Copyright (c) 2011, 2014 Authors
|
||||
*
|
||||
* Contributor Julius Baxter <juliusbaxter@gmail.com>
|
||||
* Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de>
|
||||
*
|
||||
* The authors hereby grant permission to use, copy, modify, distribute,
|
||||
* and license this software and its documentation for any purpose, provided
|
||||
* that existing copyright notices are retained in all copies and that this
|
||||
* notice is included verbatim in any distributions. No written agreement,
|
||||
* license, or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their authors
|
||||
* and need not follow the licensing terms described here, provided that
|
||||
* the new terms are clearly indicated on the first page of each file where
|
||||
* they apply.
|
||||
*/
|
||||
|
||||
#include "include/or1k-support.h"
|
||||
#include "include/or1k-sprs.h"
|
||||
|
||||
#include "or1k-internals.h"
|
||||
#include "board.h"
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/*!Tick timer interrupt handler
|
||||
|
||||
Increment timer ticks counter, reload TTMR
|
||||
*/
|
||||
/* --------------------------------------------------------------------------*/
|
||||
void
|
||||
_or1k_timer_interrupt_handler(void)
|
||||
{
|
||||
OR1K_REENT.or1k_timer_ticks++;
|
||||
uint32_t ttmr = or1k_mfspr(OR1K_SPR_TICK_TTMR_ADDR);
|
||||
ttmr = OR1K_SPR_TICK_TTMR_IE_SET(ttmr, 1);
|
||||
ttmr = OR1K_SPR_TICK_TTMR_MODE_SET(ttmr, OR1K_SPR_TICK_TTMR_MODE_RESTART);
|
||||
ttmr = OR1K_SPR_TICK_TTMR_IE_SET(ttmr, 1);
|
||||
or1k_mtspr(OR1K_SPR_TICK_TTMR_ADDR, ttmr);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/*!Enable tick timer
|
||||
|
||||
Install handler, calculate TTMR period, reset tick counter
|
||||
|
||||
@param[in] hz Rate at which to trigger timer ticks */
|
||||
/* --------------------------------------------------------------------------*/
|
||||
int
|
||||
or1k_timer_init(unsigned int hz)
|
||||
{
|
||||
uint32_t upr = or1k_mfspr(OR1K_SPR_SYS_UPR_ADDR);
|
||||
if (OR1K_SPR_SYS_UPR_TTP_GET(upr) == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Set this, for easy access when reloading */
|
||||
uint32_t period = (_or1k_board_clk_freq/hz) & OR1K_SPR_TICK_TTMR_TP_MASK;
|
||||
OR1K_REENT.or1k_timer_period = period;
|
||||
or1k_mtspr(OR1K_SPR_TICK_TTMR_ADDR, period);
|
||||
|
||||
/* Reset timer tick counter */
|
||||
OR1K_REENT.or1k_timer_ticks = 0;
|
||||
|
||||
/* Install handler */
|
||||
or1k_exception_handler_add(0x5, _or1k_timer_interrupt_handler);
|
||||
OR1K_REENT.or1k_timer_mode = OR1K_SPR_TICK_TTMR_MODE_RESTART;
|
||||
|
||||
/* Reset counter register */
|
||||
or1k_mtspr(OR1K_SPR_TICK_TTCR_ADDR, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
or1k_timer_set_period(uint32_t hz)
|
||||
{
|
||||
uint32_t period = (_or1k_board_clk_freq/hz) & OR1K_SPR_TICK_TTMR_TP_MASK;
|
||||
uint32_t ttmr = or1k_mfspr(OR1K_SPR_TICK_TTMR_ADDR);
|
||||
ttmr = OR1K_SPR_TICK_TTMR_TP_SET(ttmr, period);
|
||||
or1k_mtspr(OR1K_SPR_TICK_TTMR_ADDR, ttmr);
|
||||
OR1K_REENT.or1k_timer_period = period;
|
||||
}
|
||||
|
||||
void
|
||||
or1k_timer_set_handler(void (*handler)(void))
|
||||
{
|
||||
or1k_exception_handler_add(0x5, handler);
|
||||
}
|
||||
|
||||
void
|
||||
or1k_timer_set_mode(uint32_t mode)
|
||||
{
|
||||
// Store mode in variable
|
||||
OR1K_REENT.or1k_timer_mode = mode;
|
||||
|
||||
uint32_t ttmr = or1k_mfspr(OR1K_SPR_TICK_TTMR_ADDR);
|
||||
// If the timer is currently running, we also change the mode
|
||||
if (OR1K_SPR_TICK_TTMR_MODE_GET(ttmr) != 0) {
|
||||
ttmr = OR1K_SPR_TICK_TTMR_MODE_SET(ttmr, mode);
|
||||
or1k_mtspr(OR1K_SPR_TICK_TTMR_ADDR, ttmr);
|
||||
}
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/*!Enable tick timer
|
||||
|
||||
Enable timer interrupt, install handler, load TTMR
|
||||
*/
|
||||
/* --------------------------------------------------------------------------*/
|
||||
void
|
||||
or1k_timer_enable(void)
|
||||
{
|
||||
uint32_t ttmr = or1k_mfspr(OR1K_SPR_TICK_TTMR_ADDR);
|
||||
ttmr = OR1K_SPR_TICK_TTMR_IE_SET(ttmr, 1);
|
||||
ttmr = OR1K_SPR_TICK_TTMR_MODE_SET(ttmr, OR1K_REENT.or1k_timer_mode);
|
||||
or1k_mtspr(OR1K_SPR_TICK_TTMR_ADDR, ttmr);
|
||||
|
||||
uint32_t sr = or1k_mfspr(OR1K_SPR_SYS_SR_ADDR);
|
||||
sr = OR1K_SPR_SYS_SR_TEE_SET(sr, 1);
|
||||
or1k_mtspr(OR1K_SPR_SYS_SR_ADDR, sr);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/*!Disable tick timer
|
||||
|
||||
Disable timer interrupt in SR
|
||||
*/
|
||||
/* --------------------------------------------------------------------------*/
|
||||
uint32_t
|
||||
or1k_timer_disable(void)
|
||||
{
|
||||
uint32_t oldsr = or1k_mfspr(OR1K_SPR_SYS_SR_ADDR);
|
||||
uint32_t sr = OR1K_SPR_SYS_SR_TEE_SET(oldsr, 0);
|
||||
or1k_mtspr(OR1K_SPR_SYS_SR_ADDR, sr);
|
||||
return OR1K_SPR_SYS_SR_TEE_GET(oldsr);
|
||||
}
|
||||
|
||||
void
|
||||
or1k_timer_restore(uint32_t sr_tee)
|
||||
{
|
||||
uint32_t sr = or1k_mfspr(OR1K_SPR_SYS_SR_ADDR);
|
||||
sr = OR1K_SPR_SYS_SR_TEE_SET(sr, 1);
|
||||
or1k_mtspr(OR1K_SPR_SYS_SR_ADDR, sr);
|
||||
}
|
||||
|
||||
void
|
||||
or1k_timer_pause(void)
|
||||
{
|
||||
uint32_t ttmr = or1k_mfspr(OR1K_SPR_TICK_TTMR_ADDR);
|
||||
ttmr = OR1K_SPR_TICK_TTMR_MODE_SET(ttmr, OR1K_SPR_TICK_TTMR_MODE_DISABLE);
|
||||
or1k_mtspr(OR1K_SPR_TICK_TTMR_ADDR, ttmr);
|
||||
}
|
||||
|
||||
void
|
||||
or1k_timer_reset(void)
|
||||
{
|
||||
uint32_t ttmr = or1k_mfspr(OR1K_SPR_TICK_TTMR_ADDR);
|
||||
ttmr = OR1K_SPR_TICK_TTMR_IP_SET(ttmr, 0);
|
||||
or1k_mtspr(OR1K_SPR_TICK_TTMR_ADDR, ttmr);
|
||||
or1k_mtspr(OR1K_SPR_TICK_TTCR_ADDR, 0);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/*!Get tick timer
|
||||
|
||||
Return value of tick timer
|
||||
*/
|
||||
/* --------------------------------------------------------------------------*/
|
||||
unsigned long
|
||||
or1k_timer_get_ticks(void)
|
||||
{
|
||||
return OR1K_REENT.or1k_timer_ticks;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------*/
|
||||
/*!Reset tick timer
|
||||
|
||||
Clear value of tick timer
|
||||
*/
|
||||
/* --------------------------------------------------------------------------*/
|
||||
void
|
||||
or1k_timer_reset_ticks(void)
|
||||
{
|
||||
OR1K_REENT.or1k_timer_ticks = 0;
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/* util.c -- Utility functions for OpenRISC 1000.
|
||||
*
|
||||
* Copyright (c) 2014 Authors
|
||||
*
|
||||
* Contributor Julius Baxter <julius.baxter@orsoc.se>
|
||||
* Contributor Stefan Wallentowitz <stefan.wallentowitz@tum.de>
|
||||
*
|
||||
* The authors hereby grant permission to use, copy, modify, distribute,
|
||||
* and license this software and its documentation for any purpose, provided
|
||||
* that existing copyright notices are retained in all copies and that this
|
||||
* notice is included verbatim in any distributions. No written agreement,
|
||||
* license, or royalty fee is required for any of the authorized uses.
|
||||
* Modifications to this software may be copyrighted by their authors
|
||||
* and need not follow the licensing terms described here, provided that
|
||||
* the new terms are clearly indicated on the first page of each file where
|
||||
* they apply.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <reent.h>
|
||||
|
||||
#include "or1k-internals.h"
|
||||
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
uint32_t* *_or1k_stack_core;
|
||||
uint32_t* *_or1k_exception_stack_core;
|
||||
#endif
|
||||
|
||||
uint32_t* _or1k_stack_top;
|
||||
uint32_t* _or1k_stack_bottom;
|
||||
|
||||
uint32_t* _or1k_exception_stack_top;
|
||||
uint32_t* _or1k_exception_stack_bottom;
|
||||
|
||||
void _or1k_init() {
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
uint32_t c;
|
||||
|
||||
// Initialize stacks
|
||||
_or1k_stack_core = _sbrk_r(0, sizeof(uint32_t*) * or1k_numcores());
|
||||
_or1k_exception_stack_core = _sbrk_r(0, sizeof(uint32_t*) * or1k_numcores());
|
||||
|
||||
_or1k_stack_core[0] = _or1k_stack_top;
|
||||
_or1k_exception_stack_core[0] = _or1k_exception_stack_top;
|
||||
|
||||
for (c = 1; c < or1k_numcores(); c++) {
|
||||
_or1k_stack_core[c] = _or1k_stack_core[c-1] - _or1k_stack_size;
|
||||
_or1k_exception_stack_core[c] = _or1k_exception_stack_core[c-1] -
|
||||
_or1k_exception_stack_size;
|
||||
}
|
||||
|
||||
size_t exc_size = sizeof(void*) * or1k_numcores() * OR1K_NUM_EXCEPTIONS;
|
||||
_or1k_exception_handler_table = _sbrk_r(0, exc_size);
|
||||
|
||||
size_t int_size = sizeof(void*) * or1k_numcores() * 32;
|
||||
size_t intdata_size = sizeof(void*) * or1k_numcores() * 32;
|
||||
_or1k_interrupt_handler_table = _sbrk_r(0, int_size);
|
||||
_or1k_interrupt_handler_data_ptr_table = _sbrk_r(0, intdata_size);
|
||||
#endif
|
||||
|
||||
_or1k_reent_init();
|
||||
|
||||
#ifdef __OR1K_MULTICORE__
|
||||
for (c = 0; c < or1k_numcores(); c++) {
|
||||
_or1k_exception_handler_table[c][6] = _or1k_interrupt_handler;
|
||||
}
|
||||
#else
|
||||
_or1k_exception_handler_table[6] = _or1k_interrupt_handler;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t or1k_critical_begin() {
|
||||
uint32_t iee = or1k_interrupts_disable();
|
||||
uint32_t tee = or1k_timer_disable();
|
||||
return (iee << 1) | tee;
|
||||
}
|
||||
|
||||
void or1k_critical_end(uint32_t restore) {
|
||||
or1k_timer_restore(restore & 0x1);
|
||||
or1k_interrupts_restore((restore >> 1) & 0x1);
|
||||
}
|
||||
|
Loading…
Reference in New Issue