diff --git a/libgloss/ChangeLog b/libgloss/ChangeLog index b0f5cd8a6..063757061 100644 --- a/libgloss/ChangeLog +++ b/libgloss/ChangeLog @@ -1,3 +1,26 @@ +2014-12-15 Stefan Wallentowitz + + * 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 * README: Add details about or1k. diff --git a/libgloss/or1k/Makefile.in b/libgloss/or1k/Makefile.in index cf6197ae1..59d385c4b 100644 --- a/libgloss/or1k/Makefile.in +++ b/libgloss/or1k/Makefile.in @@ -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: diff --git a/libgloss/or1k/README b/libgloss/or1k/README new file mode 100644 index 000000000..e9b0a2048 --- /dev/null +++ b/libgloss/or1k/README @@ -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. + diff --git a/libgloss/or1k/caches-asm.S b/libgloss/or1k/caches-asm.S new file mode 100644 index 000000000..f2ac09660 --- /dev/null +++ b/libgloss/or1k/caches-asm.S @@ -0,0 +1,233 @@ +/* caches-asm.S -- cache manipulation for OpenRISC 1000. + * + * Copyright (c) 2011, 2014 Authors + * + * Contributor Julius Baxter + * Contributor Stefan Wallentowitz + * + * 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) + ) diff --git a/libgloss/or1k/exceptions-asm.S b/libgloss/or1k/exceptions-asm.S new file mode 100644 index 000000000..7248683f5 --- /dev/null +++ b/libgloss/or1k/exceptions-asm.S @@ -0,0 +1,201 @@ +/* exceptions-asm.S -- exception handling for OpenRISC 1000. + * + * Copyright (c) 2011, 2014 Authors + * + * Contributor Julius Baxter + * Contributor Stefan Wallentowitz + * + * 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) + ) diff --git a/libgloss/or1k/exceptions.c b/libgloss/or1k/exceptions.c new file mode 100644 index 000000000..8240d09d2 --- /dev/null +++ b/libgloss/or1k/exceptions.c @@ -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 +} diff --git a/libgloss/or1k/impure.c b/libgloss/or1k/impure.c new file mode 100644 index 000000000..f4eb4ad19 --- /dev/null +++ b/libgloss/or1k/impure.c @@ -0,0 +1,120 @@ +/* impure.c. Handling of re-entrancy data structure for OpenRISC 1000. + + Copyright (C) 2014, Authors + + Contributor Stefan Wallentowitz + + * 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-internals.h" + +#include + +/* 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 +} diff --git a/libgloss/or1k/include/or1k-nop.h b/libgloss/or1k/include/or1k-nop.h new file mode 100644 index 000000000..0d432dad4 --- /dev/null +++ b/libgloss/or1k/include/or1k-nop.h @@ -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 diff --git a/libgloss/or1k/include/or1k-support.h b/libgloss/or1k/include/or1k-support.h new file mode 100644 index 000000000..c29b064be --- /dev/null +++ b/libgloss/or1k/include/or1k-support.h @@ -0,0 +1,665 @@ +/* Copyright (c) 2014 Authors + * + * Contributor Julius Baxter + * Contributor Stefan Wallentowitz + * + * 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 + +#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__ */ diff --git a/libgloss/or1k/interrupts-asm.S b/libgloss/or1k/interrupts-asm.S new file mode 100644 index 000000000..560035285 --- /dev/null +++ b/libgloss/or1k/interrupts-asm.S @@ -0,0 +1,166 @@ +/* interrupts-asm.S -- interrupt handling for OpenRISC 1000. + * + * Copyright (c) 2011, 2012, 2014 Authors + * + * Contributor Julius Baxter + * Contributor Stefan Kristiansson + * Contributor Stefan Wallentowitz + * + * 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) + ) diff --git a/libgloss/or1k/interrupts.c b/libgloss/or1k/interrupts.c new file mode 100644 index 000000000..6badc497c --- /dev/null +++ b/libgloss/or1k/interrupts.c @@ -0,0 +1,69 @@ +/* interrupts.c -- interrupt handling for OpenRISC 1000. + * + * Copyright (c) 2014 Authors + * + * Contributor Stefan Wallentowitz + * + * 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 + +#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); +} diff --git a/libgloss/or1k/mmu-asm.S b/libgloss/or1k/mmu-asm.S new file mode 100644 index 000000000..535824ccb --- /dev/null +++ b/libgloss/or1k/mmu-asm.S @@ -0,0 +1,67 @@ +/* mmu-asm.S -- MMU handling for OpenRISC 1000. + * + * Copyright (c) 2011, 2014 Authors + * + * Contributor Julius Baxter + * Contributor Stefan Wallentowitz + * + * 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)) diff --git a/libgloss/or1k/or1k-internals.h b/libgloss/or1k/or1k-internals.h new file mode 100644 index 000000000..b24ad136b --- /dev/null +++ b/libgloss/or1k/or1k-internals.h @@ -0,0 +1,67 @@ +#ifndef __OR1K_INTERNAL_H__ +#define __OR1K_INTERNAL_H__ + +#include +#include + +#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 diff --git a/libgloss/or1k/or1k_uart.c b/libgloss/or1k/or1k_uart.c new file mode 100644 index 000000000..3d79f4516 --- /dev/null +++ b/libgloss/or1k/or1k_uart.c @@ -0,0 +1,82 @@ +/* or1k_uart.c -- UART implementation for OpenRISC 1000. + * + *Copyright (c) 2014 Authors + * + * Contributor Stefan Wallentowitz + * + * 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 + +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); +} diff --git a/libgloss/or1k/or1k_uart.h b/libgloss/or1k/or1k_uart.h new file mode 100644 index 000000000..dc8a3dbce --- /dev/null +++ b/libgloss/or1k/or1k_uart.h @@ -0,0 +1,83 @@ +/* or1k_uart.h -- UART definitions for OpenRISC 1000. + * + * Copyright (c) 2014 Authors + * + * Contributor Stefan Wallentowitz + * + * 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 + +#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 diff --git a/libgloss/or1k/outbyte.S b/libgloss/or1k/outbyte.S new file mode 100644 index 000000000..2cd514855 --- /dev/null +++ b/libgloss/or1k/outbyte.S @@ -0,0 +1,34 @@ +/* outbyte.S -- Write one byte for OpenRISC 1000. + * + * Copyright (c) 2014 Authors + * + * Contributor Stefan Wallentowitz + * + * 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) + ) diff --git a/libgloss/or1k/sbrk.c b/libgloss/or1k/sbrk.c new file mode 100644 index 000000000..de80663ee --- /dev/null +++ b/libgloss/or1k/sbrk.c @@ -0,0 +1,52 @@ +/* sbrk.c -- allocate space on heap on OpenRISC 1000. + * + * Copyright (c) 2014 Authors + * + * Contributor Stefan Wallentowitz + * + * 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 "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; +} diff --git a/libgloss/or1k/sync-asm.S b/libgloss/or1k/sync-asm.S new file mode 100644 index 000000000..911e2552f --- /dev/null +++ b/libgloss/or1k/sync-asm.S @@ -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) + ) diff --git a/libgloss/or1k/syscalls.c b/libgloss/or1k/syscalls.c new file mode 100644 index 000000000..690d21ad1 --- /dev/null +++ b/libgloss/or1k/syscalls.c @@ -0,0 +1,160 @@ +/* syscalls.c -- reentrant syscalls for OpenRISC 1000. + * + * Copyright (c) 2011, 2014 Authors + * + * Contributor Julius Baxter + * Contributor Stefan Wallentowitz + * + * 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 +#include +#include +#include +#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); +} + diff --git a/libgloss/or1k/timer.c b/libgloss/or1k/timer.c new file mode 100644 index 000000000..27a24a1c1 --- /dev/null +++ b/libgloss/or1k/timer.c @@ -0,0 +1,186 @@ +/* timer.c -- tick timer functions for OpenRISC 1000. + * + * Copyright (c) 2011, 2014 Authors + * + * Contributor Julius Baxter + * Contributor Stefan Wallentowitz + * + * 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; +} diff --git a/libgloss/or1k/util.c b/libgloss/or1k/util.c new file mode 100644 index 000000000..74071719e --- /dev/null +++ b/libgloss/or1k/util.c @@ -0,0 +1,83 @@ +/* util.c -- Utility functions for OpenRISC 1000. + * + * Copyright (c) 2014 Authors + * + * Contributor Julius Baxter + * Contributor Stefan Wallentowitz + * + * 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 +#include + +#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); +} +