1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

cpu: mips: Add support for mips32r2

cpu/mips32r2_common adds base architecture support for mips32r2 cores it can be
 built in it own right as a 'CPU', but is dependant on a bootloader (like
 u-boot) to have bootstrapped the system, this has been tested on a 'malta'
 FPGA system (BOARD=mips-malta) with various mips32r2 compliant cores
 (interAptiv, P5600, etc).
This commit is contained in:
Neil Jones 2017-02-09 12:18:44 +00:00
parent eb9985119b
commit c74ec1c253
10 changed files with 1355 additions and 0 deletions

77
cpu/mips32r2_common/cpu.c Normal file
View File

@ -0,0 +1,77 @@
/*
* Copyright 2016, Imagination Technologies Limited and/or its
* affiliated group companies.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#include <mips/m32c0.h>
#include <mips/regdef.h>
#include <mips/asm.h>
#include <string.h>
#include <assert.h>
#include "periph/uart.h"
#include "periph/timer.h"
#include "arch/panic_arch.h"
#include "kernel_init.h"
#include "cpu.h"
#include "board.h"
void mips_start(void);
extern void _fini(void);
extern void atexit(void (*)(void));
extern void _init(void);
extern void exit(int);
#ifdef FLASH_XIP
extern char _rom_data_copy __attribute__((section("data")));
extern char _fdata __attribute__((section("data")));
extern char _edata __attribute__((section("data")));
#endif
/*
* Note the mips toolchain crt expects to jump to main but RIOT wants the user
* code to start at main for some perverse reason, but thankfully the crt
* does provide this hook function which gets called fairly close to the jump
* to main, thus if we finish off the job of the crt here and never returns
* we can support this madness.
*/
void software_init_hook(void)
{
#ifdef FLASH_XIP
/* copy initialised data from its LMA to VMA */
memcpy(&_fdata, &_rom_data_copy, (int)&_edata - (int)&_fdata);
#endif
atexit(_fini);
_init();
mips_start();
exit(-1);
}
void mips_start(void)
{
board_init();
#if MODULE_NEWLIB
#error "This Port is designed to work with the (newlib) C library provided with the mips sdk toolchain"
#endif
/* kernel_init */
kernel_init();
}
void panic_arch(void)
{
printf("\nPANIC!\n");
assert(0);
while (1) {
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2016, Imagination Technologies Limited and/or its
* affiliated group companies.
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup cpu_mips32r2_commom MIPS32R2 Common
* @ingroup cpu
* @brief Common implementations and headers for mips32r2 compliant devices
* @{
*
* @file
* @brief Common implementations and headers for mips32r2 compliant devices
*
* @author Neil Jones <neil.jones@imgtec.com>
*/
#ifndef CPU_H_
#define CPU_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <inttypes.h>
#include "irq.h"
/**
* @brief Print the last instruction's address
*
* @todo: Not supported
*/
static inline void cpu_print_last_instruction(void)
{
/* This function must exist else RIOT won't compile */
}
#ifdef __cplusplus
}
#endif
#endif
/** @} */

View File

@ -0,0 +1,70 @@
/*
* Copyright 2016, Imagination Technologies Limited and/or its
* affiliated group companies.
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#ifndef _CPU_CONF_H_
#define _CPU_CONF_H_
/**
* @defgroup cpu_mips32r2_commom MIPS32R2 Common
* @ingroup cpu
* @{
*
* @file
* @brief Common CPU definitions for mip32r2 compatable devices.
*
* @author Neil Jones <neil.jones@imgtec.com>
*/
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Configuration of default stack sizes
*
* printf takes a pretty tortured route through the C lib
* then via UHI syscall exception to end up at the UART
* driver.
*
* When debugging timer code we get printfs on the idle threads
* stack which can easily blow its limits.
*
* Note code must be compiled at -Os with these values, using -O0
* you'll overflow these stacks.
*
* NO ISR stack is in use yet, interrupt use the current running stack
* hence the big-ish default stack size.
* @{
*/
#ifndef THREAD_EXTRA_STACKSIZE_PRINTF
#define THREAD_EXTRA_STACKSIZE_PRINTF (1024)
#endif
#ifndef THREAD_STACKSIZE_DEFAULT
#define THREAD_STACKSIZE_DEFAULT (2048)
#endif
#ifndef THREAD_STACKSIZE_IDLE
#ifdef NDEBUG
#define THREAD_STACKSIZE_IDLE (512)
#else
#define THREAD_STACKSIZE_IDLE (512 + THREAD_EXTRA_STACKSIZE_PRINTF)
#endif
#endif
#define ISR_STACKSIZE (0)
/** @} */
#ifdef __cplusplus
}
#endif
#endif
/** @} */

View File

@ -0,0 +1,25 @@
/*
* Copyright 2016, Imagination Technologies Limited and/or its
* affiliated group companies.
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/* This file must exist to get timer code to build */
/* No peripherals I/O via JTAG or Bootloader using UHI */
/*
* Note mips32r2_common can be selected in its own right as a CPU
* for testing on PFGA systems (like BOARD=mips-malta) with limited
* or no peripherals
*/
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,42 @@
/*
* Copyright 2016, Imagination Technologies Limited and/or its
* affiliated group companies.
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#include <mips/m32c0.h>
#include "arch/irq_arch.h"
unsigned int irq_arch_enable(void)
{
unsigned int status;
__asm__ volatile ("ei %0" : "=r" (status));
return status;
}
unsigned int irq_arch_disable(void)
{
unsigned int status;
__asm__ volatile ("di %0" : "=r" (status));
return status;
}
void irq_arch_restore(unsigned int state)
{
if (state & SR_IE) {
mips32_bs_c0(C0_STATUS, SR_IE);
}
else {
mips32_bc_c0(C0_STATUS, SR_IE);
}
}
int irq_arch_in(void)
{
return (mips32_get_c0(C0_STATUS) & SR_EXL) != 0;
}

View File

@ -0,0 +1,151 @@
/*
* Copyright 2016, Imagination Technologies Limited and/or its
* affiliated group companies.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 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.
*/
/* ************ PLEASE READ ME !!!! ****************
This file is missing from $MIPS_ELF_ROOT/share/mips/hal
Future toolchain versions will have this file included and this local copy will
be no longer needed.
Note the above copyright/license is 3 Clause BSD and as such is compatible with LGPLv2.1
as such we grant licensing this file under LGPLv2.1 (See the file LICENSE in the top level
directory for more details) as well.
Thanks for reading.
*/
.set nomips16
#include <mips/asm.h>
#include <mips/regdef.h>
#include <mips/m32c0.h>
#include <mips/hal.h>
#include <mips/endian.h>
#
# FUNCTION: _dsp_save
#
# DESCRIPTION: save the DSP context.
#
# RETURNS: int
#
# 0: No context saved
# CTX_*: Type of conext stored
#
LEAF(_dsp_save)
PTR_S zero, LINKCTX_NEXT(a0)
move v0, zero
# Test for DSP support
mfc0 t0, C0_CONFIG3
ext t0, t0, CFG3_DSPP_SHIFT, 1
beqz t0, 1f
# Test for DSP enabled
mfc0 t0, C0_STATUS
ext t0, t0, SR_MX_SHIFT, 1
beqz t0, 1f
lui v0, %hi(LINKCTX_TYPE_DSP)
addiu v0, v0, %lo(LINKCTX_TYPE_DSP)
.set push
.set dsp
rddsp t1
mfhi t2, $ac1
mflo t3, $ac1
mfhi t4, $ac2
mflo t5, $ac2
mfhi t6, $ac3
mflo t7, $ac3
.set pop
sw t1, DSPCTX_DSPC(a0)
sw t2, DSPCTX_HI1(a0)
sw t3, DSPCTX_LO1(a0)
sw t4, DSPCTX_HI2(a0)
sw t5, DSPCTX_LO2(a0)
sw t6, DSPCTX_HI3(a0)
sw t7, DSPCTX_LO3(a0)
REG_S v0, LINKCTX_ID(a0)
1:
jr ra
END(_dsp_save)
#
# FUNCTION: _dsp_load
#
# DESCRIPTION: load the DSP context.
#
# RETURNS: int
#
# 0: Unrecognised context
# CTX_*: Type of context restored
#
LEAF(_dsp_load)
REG_L v0, LINKCTX_ID(a0)
lui v1, %hi(LINKCTX_TYPE_DSP)
addiu v1, v1, %lo(LINKCTX_TYPE_DSP)
bne v0,v1,1f
# Test for DSP support
mfc0 t0, C0_CONFIG3
ext t0, t0, CFG3_DSPP_SHIFT, 1
beqz t0, 1f
# Force on DSP
di t3
ehb
or t3, t3, SR_MX
mtc0 t3, C0_STATUS
ehb
lw t1, DSPCTX_DSPC(a0)
lw t2, DSPCTX_HI1(a0)
lw t3, DSPCTX_LO1(a0)
lw t4, DSPCTX_HI2(a0)
lw t5, DSPCTX_LO2(a0)
lw t6, DSPCTX_HI3(a0)
lw t7, DSPCTX_LO3(a0)
.set push
.set dsp
wrdsp t1
mthi t2, $ac1
mtlo t3, $ac1
mthi t4, $ac2
mtlo t5, $ac2
mthi t6, $ac3
mtlo t7, $ac3
.set pop
jr ra
1:
# Don't recognise this context, fail
move v0, zero
jr ra
END(_dsp_load)

View File

@ -0,0 +1,288 @@
/*
* Copyright 2014-2015, Imagination Technologies Limited and/or its
* affiliated group companies.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* 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.
*/
/* ************ PLEASE READ ME !!!! ****************
This file is a copy of the mips_excpt_entry.S from $MIPS_ELF_ROOT/share/mips/hal
(from the 2016.05-03 version) with a single modification, we add a global flag
'__exception_restore' so we can jump to the restore code sequence from C.
Future toolchain versions will have this change included and this file will
be no longer needed.
Note the above copyright/license is 3 Clause BSD and as such is compatible with LGPLv2.1
as such we grant licensing this file under LGPLv2.1 (See the file LICENSE in the top level
directory for more details) as well.
Thanks for reading.
*/
# Keep each function in a separate named section
#define _FUNCTION_SECTIONS_
.set nomips16
#include <mips/asm.h>
#include <mips/regdef.h>
#include <mips/cpu.h>
#include <mips/hal.h>
# Context size, adjusted for ABI parameter area
#define ADJ (NARGSAVE * SZARG)
# Round up to 16-byte boundary (maximum stack alignment required for any
# supported ABI)
#define CTX_SIZEROUND ((CTX_SIZE + ALSZ) & ALMASK)
#define CTX_SIZEADJ (CTX_SIZEROUND + ADJ)
#define e_ISR s1
#define e_CR s3
#define e_BADV s4
#define e_SR s5
#define e_EPC s6
#define e_RA s7
# DESCRIPTION: Exception entry point. This is small because it must go at
# EBASE+0x180. It saves enough context to chain onwards to
# __exception_save.
#
LEAF(__exception_entry)
.set push
.set noat
.weak _mips_tlb_refill
_mips_tlb_refill = __exception_save
__tlb_refill_loop:
# Support an alternative entry point at the start of the exception
# vector. Since the exception vector is normally placed first
# in the link map this allows a user to start execution from the
# same address that an executable is loaded to.
LA k1, __first_boot
lw k1, 0(k1)
beqz k1, 1f
# The start code is responsible for clearing __first_boot prior
# to installing the exception handlers.
j _start
1:
LA k1, _mips_tlb_refill
beqz k1, __tlb_refill_loop
jr k1
.org 0x80
.weak _mips_xtlb_refill
_mips_xtlb_refill = __exception_save
__xtlb_refill_loop:
LA k1, _mips_xtlb_refill
beqz k1, __xtlb_refill_loop
jr k1
.org 0x100
.weak _mips_cache_error
__cache_error_loop:
LA k1, _mips_cache_error
beqz k1, __cache_error_loop
jr k1
.org 0x180
# Free up k1, defering sp adjustment until later
REG_S k1, (-CTX_SIZEROUND + CTX_K1)(sp)
# Use k1 to invoke __exception_save
LA k1, _mips_general_exception
jr k1
.set pop
END(__exception_entry)
#
# FUNCTION: __exception_save
#
# DESCRIPTION: Exception context save. Save the context, then fake up a call
# frame.
#
ANESTED(__exception_save, _mips_general_exception, CTX_SIZEADJ, zero)
.globl __exception_save;
.globl __exception_restore;
.set push
.set noat
# k1 is already saved, so use it to save the users sp
move k1, sp
# Finally adjust sp
PTR_ADDU sp, sp, -CTX_SIZEADJ # This should be picked up by the backtracer
# Save context
REG_S $1, CTX_REG(1) + ADJ(sp)
REG_S $2, CTX_REG(2) + ADJ(sp)
REG_S $3, CTX_REG(3) + ADJ(sp)
REG_S $4, CTX_REG(4) + ADJ(sp)
REG_S $5, CTX_REG(5) + ADJ(sp)
REG_S $6, CTX_REG(6) + ADJ(sp)
REG_S $7, CTX_REG(7) + ADJ(sp)
REG_S $8, CTX_REG(8) + ADJ(sp)
REG_S $9, CTX_REG(9) + ADJ(sp)
REG_S $10, CTX_REG(10) + ADJ(sp)
REG_S $11, CTX_REG(11) + ADJ(sp)
REG_S $12, CTX_REG(12) + ADJ(sp)
REG_S $13, CTX_REG(13) + ADJ(sp)
REG_S $14, CTX_REG(14) + ADJ(sp)
REG_S $15, CTX_REG(15) + ADJ(sp)
REG_S $16, CTX_REG(16) + ADJ(sp)
REG_S $17, CTX_REG(17) + ADJ(sp)
REG_S $18, CTX_REG(18) + ADJ(sp)
REG_S $19, CTX_REG(19) + ADJ(sp)
REG_S $20, CTX_REG(20) + ADJ(sp)
REG_S $21, CTX_REG(21) + ADJ(sp)
REG_S $22, CTX_REG(22) + ADJ(sp)
REG_S $23, CTX_REG(23) + ADJ(sp)
REG_S $24, CTX_REG(24) + ADJ(sp)
REG_S $25, CTX_REG(25) + ADJ(sp)
REG_S $26, CTX_REG(26) + ADJ(sp)
# k1/$27 has already been saved
REG_S $28, CTX_REG(28) + ADJ(sp)
REG_S k1, CTX_REG(29) + ADJ(sp) # Use saved sp from earlier
REG_S $30, CTX_REG(30) + ADJ(sp)
REG_S $31, CTX_REG(31) + ADJ(sp)
PTR_S $0, CTX_LINK + ADJ(sp) # Clear the link field
#if (__mips_isa_rev < 6)
mfhi $9
mflo $10
REG_S $9, CTX_HI0 + ADJ(sp)
REG_S $10, CTX_LO0 + ADJ(sp)
#endif
# Trick the backtracer into stepping back to the point where the exception
# occurred.
PTR_MFC0 ra, C0_EPC
mfc0 e_CR, C0_CR
REG_S ra, CTX_EPC + ADJ(sp)
# Finish storing the rest of the CP0 registers
PTR_MFC0 $9, C0_BADVADDR
REG_S $9, CTX_BADVADDR + ADJ(sp)
sw e_CR, CTX_CAUSE + ADJ(sp)
move $11, $0
move $12, $0
mfc0 $9, C0_CONFIG3
ext $10, $9, CFG3_BP_SHIFT, 1
beqz $10, 1f
mfc0 $11, C0_BADPINSTR
1:
ext $9, $9, CFG3_BI_SHIFT, 1
beqz $9, 1f
mfc0 $12, C0_BADINSTR
1:
sw $11, CTX_BADPINSTR + ADJ(sp)
sw $12, CTX_BADINSTR + ADJ(sp)
# Start computing the address of the context for a0
move a0, sp
# Clear EXL. Exceptions can now nest.
mfc0 e_SR, C0_SR
sw e_SR, CTX_STATUS + ADJ(sp)
lui $9, %hi(~SR_EXL)
addiu $9, $9, %lo(~SR_EXL)
and e_SR, e_SR, $9
mtc0 e_SR, C0_SR
# Manually set up the return address to restore the context below
LA ra, __exception_restore
# Extract the cause code
and a1, e_CR, CR_XMASK
# Finish computing the address of the context for a0
addiu a0, a0, ADJ
# Shift exception number down into expected range
srl a1, a1, 2
# Call the handler, indirect through t9 albeit not for any specific
# reason
LA t9, _mips_handle_exception
jr t9
# Return point from handler
# Load context
# now a function on its own, note save code just falls through.
__exception_restore:
#if (__mips_isa_rev < 6)
REG_L $9, CTX_HI0 + ADJ(sp)
REG_L $10, CTX_LO0 + ADJ(sp)
mthi $9
mtlo $10
#endif
REG_L $1, CTX_REG(1) + ADJ(sp)
REG_L $2, CTX_REG(2) + ADJ(sp)
REG_L $3, CTX_REG(3) + ADJ(sp)
REG_L $4, CTX_REG(4) + ADJ(sp)
REG_L $5, CTX_REG(5) + ADJ(sp)
REG_L $6, CTX_REG(6) + ADJ(sp)
REG_L $7, CTX_REG(7) + ADJ(sp)
REG_L $8, CTX_REG(8) + ADJ(sp)
REG_L $9, CTX_REG(9) + ADJ(sp)
REG_L $10, CTX_REG(10) + ADJ(sp)
REG_L $11, CTX_REG(11) + ADJ(sp)
REG_L $12, CTX_REG(12) + ADJ(sp)
REG_L $13, CTX_REG(13) + ADJ(sp)
REG_L $14, CTX_REG(14) + ADJ(sp)
REG_L $15, CTX_REG(15) + ADJ(sp)
REG_L $16, CTX_REG(16) + ADJ(sp)
REG_L $17, CTX_REG(17) + ADJ(sp)
REG_L $18, CTX_REG(18) + ADJ(sp)
REG_L $19, CTX_REG(19) + ADJ(sp)
REG_L $20, CTX_REG(20) + ADJ(sp)
REG_L $21, CTX_REG(21) + ADJ(sp)
REG_L $22, CTX_REG(22) + ADJ(sp)
REG_L $23, CTX_REG(23) + ADJ(sp)
REG_L $24, CTX_REG(24) + ADJ(sp)
REG_L $25, CTX_REG(25) + ADJ(sp)
# $26/K0 and $27/K1 are restored with interrupts disabled
REG_L $28, CTX_REG(28) + ADJ(sp)
# $29/SP is restored last
REG_L $30, CTX_REG(30) + ADJ(sp)
REG_L $31, CTX_REG(31) + ADJ(sp)
di
lw k0, CTX_STATUS + ADJ(sp)
REG_L k1, CTX_EPC + ADJ(sp)
mtc0 k0, C0_SR
PTR_MTC0 k1, C0_EPC
ehb
REG_L k0, CTX_K0 + ADJ(sp)
REG_L k1, CTX_K1 + ADJ(sp)
REG_L sp, CTX_SP + ADJ(sp)
# Return from exception
eret
.set pop
END(__exception_save)

View File

@ -0,0 +1,37 @@
/*
* Copyright 2016, Imagination Technologies Limited and/or its
* affiliated group companies.
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_mips32r2_common
* @{
*
* @file
* @brief common periph/pm functions
*
* @author Neil Jones <neil.jones@imgtec.com>
*
* @}
*/
#include <mips/m32c0.h>
#include "periph/pm.h"
#ifndef FEATURES_PERIPH_PM
void pm_set_lowest(void)
{
/* Dont wait if interrupts are not enabled - we would never return!*/
if (mips32_get_c0(C0_STATUS) & SR_IE) {
__asm volatile ("wait");
}
}
#endif
void pm_off(void)
{
/* No Generic Power off Mechanism */
}

View File

@ -0,0 +1,217 @@
/*
* Copyright 2016, Imagination Technologies Limited and/or its
* affiliated group companies.
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#include <mips/cpu.h>
#include <mips/m32c0.h>
#include <mips/regdef.h>
#include <mips/asm.h>
#include <string.h>
#include <periph/timer.h>
#include "cpu_conf.h"
#include <stdio.h>
#include "sched.h"
#include "thread.h"
#include "board.h"
#include "irq.h"
#include "timex.h"
#include "div.h"
#include <sys/time.h>
/*
* setting TIMER_ACCURACY_SHIFT lower will improve accuracy
* at the cost of more regular interrupts (hence less power efficient).
* */
#define TIMER_ACCURACY_SHIFT (10)
#define TIMER_ACCURACY (1 << TIMER_ACCURACY_SHIFT)
#define CHANNELS (3)
/* TICKS_PER_US must be set in the board file */
#ifndef TICKS_PER_US
#error "Please set TICK_PER_US in your board file"
#endif
/*
* The base MIPS count / compare timer is fixed frequency at core clock / 2
* and is pretty basic This timer is currently only supported in Vectored
* Interrupt Mode (VI), EIC mode is not supported yet.
*
* RIOT's xtimer expects the timer to operate at 1MHZ or any 2^n multiple or
* factor of this, thus we maintain a software timer which counts at 1MHz.
* This is not particularly power efficient and may add latency too.
*
* If other SoC specific timers are available which are more flexible then
* it is advised to use them, this timer is a fallback version
* that should work on all MIPS implementations.
*
*/
static timer_isr_ctx_t timer_isr_ctx;
volatile unsigned int counter;
volatile unsigned int compares[CHANNELS];
static volatile int spurious_int;
/*
* The mips toolchain C library does not implement gettimeofday()
*
* implement it here using the timer.
*
*/
int gettimeofday(struct timeval *__restrict __p, void *__restrict __tz)
{
uint64_t now = counter * US_PER_MS;
__p->tv_sec = div_u64_by_1000000(now);
__p->tv_usec = now - (__p->tv_sec * US_PER_SEC);
return 0;
}
int timer_init(tim_t dev, unsigned long freq, timer_cb_t cb, void *arg)
{
assert(dev == 0);
(void)freq; /*Cannot adjust Frequency */
timer_isr_ctx.cb = cb;
timer_isr_ctx.arg = arg;
/* Clear down soft counters */
memset((void *)compares, 0, sizeof(compares));
counter = (1 << TIMER_ACCURACY_SHIFT);
/* Set compare up */
mips_setcompare(mips_getcount() + TICKS_PER_US * TIMER_ACCURACY);
/* Start the timer if stopped */
mips32_bc_c0(C0_CAUSE, CR_DC);
/* Enable Timer Interrupts */
mips32_bs_c0(C0_STATUS, SR_HINT5);
return 0;
}
int timer_set(tim_t dev, int channel, unsigned int timeout)
{
assert(dev == 0);
assert(channel < CHANNELS);
timeout >>= TIMER_ACCURACY_SHIFT;
timeout <<= TIMER_ACCURACY_SHIFT;
uint32_t status = irq_arch_disable();
compares[channel] = counter + timeout;
irq_arch_restore(status);
return channel;
}
int timer_set_absolute(tim_t dev, int channel, unsigned int value)
{
assert(dev == 0);
assert(channel < CHANNELS);
value >>= TIMER_ACCURACY_SHIFT;
value <<= TIMER_ACCURACY_SHIFT;
uint32_t status = irq_arch_disable();
compares[channel] = value;
irq_arch_restore(status);
return channel;
}
int timer_clear(tim_t dev, int channel)
{
assert(dev == 0);
assert(channel < CHANNELS);
uint32_t status = irq_arch_disable();
compares[channel] = 0;
irq_arch_restore(status);
return channel;
}
unsigned int timer_read(tim_t dev)
{
assert(dev == 0);
return counter;
}
void timer_start(tim_t dev)
{
mips32_bc_c0(C0_CAUSE, CR_DC);
}
void timer_stop(tim_t dev)
{
mips32_bs_c0(C0_CAUSE, CR_DC);
}
void timer_irq_enable(tim_t dev)
{
mips32_bs_c0(C0_STATUS, SR_HINT5);
}
void timer_irq_disable(tim_t dev)
{
mips32_bc_c0(C0_STATUS, SR_HINT5);
}
void __attribute__ ((interrupt("vector=hw5"))) _mips_isr_hw5(void)
{
register int cr = mips_getcr();
if (cr & CR_TI) {
uint32_t status = irq_arch_disable();
counter += TIMER_ACCURACY;
irq_arch_restore(status);
if (counter == compares[0]) {
/*
* The Xtimer code expects the ISR to take some time
* but our counter is a fake software one, so bump it a
* bit to give the impression some time elapsed in the ISR.
* Without this the callback ( _shoot(timer) on xtimer_core.c )
* never fires.
*/
counter += TIMER_ACCURACY;
timer_isr_ctx.cb(timer_isr_ctx.arg, 0);
if (sched_context_switch_request) {
thread_yield();
}
}
if (counter == compares[1]) {
timer_isr_ctx.cb(timer_isr_ctx.arg, 1);
if (sched_context_switch_request) {
thread_yield();
}
}
if (counter == compares[2]) {
timer_isr_ctx.cb(timer_isr_ctx.arg, 2);
if (sched_context_switch_request) {
thread_yield();
}
}
mips_setcompare(mips_getcount() + TICKS_PER_US * TIMER_ACCURACY);
}
else {
spurious_int++;
}
}

View File

@ -0,0 +1,400 @@
/*
* Copyright 2016, Imagination Technologies Limited and/or its
* affiliated group companies.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#include <mips/cpu.h>
#include <mips/hal.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include "thread.h"
#include "cpu.h"
#include "irq.h"
#include "cpu_conf.h"
#include "periph_conf.h" /* for debug uart number */
#include "periph/uart.h"
#include "malloc.h"
#define STACK_END_PAINT (0xdeadc0de)
#define C0_STATUS_EXL (2)
#define PADDING (16)
#define MICROMIPS_ISA_MODE (1)
#define M32_SYSCALL (0xC)
#define M32_SYSCALL_MASK (0xfc00003f)
/*
* note the major 16bits of a 32bit MicroMIPS opcode appear first in the
* instruction stream
*/
#define MM_SYSCALL (0x8B7C0000)
#define MM_SYSCALL_MASK (0xfffffc00)
#ifdef MIPS_HARD_FLOAT
/* pointer to the current and old fpu context for lazy context switching */
static struct fp64ctx *currentfpctx; /* fpu context of current running task */
static struct fp64ctx *oldfpctx; /* fpu context of last task that executed fpu */
#endif
/*
* Stack Layout, note struct gpctx is defined in
* $MIPS_ELF_ROOT/mips-mti-elf/include/mips/hal.h
*
* Top Of Stack
* ---------------
* | |
* | User stack |
* | |
* --------------- <--- gpctx->sp
* | |
* | gpctx |
* | |
* ---------------
* | 16 byte pad |
* --------------- <--- sched_active_thread->sp
*/
char *thread_arch_stack_init(thread_task_func_t task_func, void *arg,
void *stack_start, int stack_size)
{
/* make sure it is aligned to 8 bytes this is a requirement of the O32 ABI */
uintptr_t *p = (uintptr_t *)(((long)(stack_start) + stack_size) & ~7);
uintptr_t *fp;
/* paint */
p--;
*p-- = STACK_END_PAINT;
/* prepare stack for __exception_restore() */
fp = p;
p -= sizeof(struct gpctx) / sizeof(unsigned int);
struct gpctx *initial_ctx = (struct gpctx *)p;
initial_ctx->a[0] = (reg_t)arg;
initial_ctx->status = mips32_get_c0(C0_STATUS) | SR_IE; /* Enable interrupts */
__asm volatile ("sw $gp, 0(%0)" : : "r" (&initial_ctx->gp));
initial_ctx->epc = (reg_t)task_func;
initial_ctx->ra = (reg_t)sched_task_exit;
initial_ctx->sp = (reg_t)fp;
initial_ctx->link = (struct linkctx *)NULL;
#ifdef MIPS_MICROMIPS
initial_ctx->epc |= MICROMIPS_ISA_MODE;
initial_ctx->ra |= MICROMIPS_ISA_MODE;
#endif
#ifdef MIPS_HARD_FLOAT
/*
* Disable FPU so we get an exception on first use to allow
* Lazy FPU context save and restore
*/
initial_ctx->status &= ~SR_CU1;
initial_ctx->status |= SR_FR; /*use double width FPU */
#endif
/*
* note the -4 (-16 bytes) as the toolchain exception handling code
* adjusts the sp for alignment
*/
p -= PADDING/sizeof(unsigned int);
return (void *)p;
}
void thread_arch_stack_print(void)
{
uintptr_t *sp = (void *)sched_active_thread->sp;
printf("Stack trace:\n");
while (*sp != STACK_END_PAINT) {
printf(" 0x%p: 0x%08lx\n", sp, *sp);
sp++;
}
}
/* This function calculates the ISR_usage */
int thread_arch_isr_stack_usage(void)
{
/* TODO */
return -1;
}
void *thread_arch_isr_stack_pointer(void)
{
/* TODO */
return (void *)-1;
}
void *thread_arch_isr_stack_start(void)
{
/* TODO */
return (void *)-1;
}
extern void __exception_restore(void);
void thread_arch_start_threading(void)
{
unsigned int status = mips32_get_c0(C0_STATUS);
/*
* Set Exception level if we are not already running at it
* the EXL mode depends on the bootloader.
*/
if ((status & C0_STATUS_EXL) == 0) {
mips32_set_c0(C0_STATUS, status | C0_STATUS_EXL);
}
sched_run();
__asm volatile ("lw $sp, 0(%0)" : : "r" (&sched_active_thread->sp));
__exception_restore();
UNREACHABLE();
}
void thread_arch_yield(void)
{
/*
* throw a syscall exception to get into exception level
* we context switch at exception level.
*
* Note syscall 1 is reserved for UHI see:
* http://wiki.prplfoundation.org/w/images/4/42/UHI_Reference_Manual.pdf
*/
__asm volatile ("syscall 2");
}
struct linkctx* exctx_find(reg_t id, struct gpctx *gp)
{
struct linkctx **ctx = (struct linkctx **)&gp->link;
while (*ctx) {
if ((*ctx)->id == id) {
return *ctx;
}
ctx = &(*ctx)->next;
}
return NULL;
}
/* unaligned access helper */
static inline uint32_t __attribute__((optimize("-O3")))
mem_rw(const void *vaddr)
{
uint32_t v;
memcpy(&v, vaddr, sizeof(v));
return v;
}
#ifdef MIPS_DSP
extern int _dsp_save(struct dspctx *ctx);
extern int _dsp_load(struct dspctx *ctx);
#endif
/*
* The nomips16 attribute should not really be needed, it works around a toolchain
* issue in 2016.05-03.
*/
void __attribute__((nomips16))
_mips_handle_exception(struct gpctx *ctx, int exception)
{
unsigned int syscall_num = 0;
#ifdef MIPS_DSP
struct dspctx dsp_ctx; /* intentionally allocated on current stack */
#endif
switch (exception) {
case EXC_SYS:
#ifdef MIPS_MICROMIPS
/* note major 16bits of opcode is first in instruction stream */
syscall_num =
mem_rw((const void *)(ctx->epc & ~MICROMIPS_ISA_MODE))
& 0x3FF;
#else
syscall_num = (mem_rw((const void *)ctx->epc) >> 6) & 0xFFFF;
#endif
#ifdef DEBUG_VIA_UART
#include <mips/uhi_syscalls.h>
/*
* intercept UHI write syscalls (printf) which would normally
* get routed to debug probe or bootloader handler and output
* via a UART
*/
if (syscall_num == __MIPS_UHI_SYSCALL_NUM) {
if (ctx->t2[1] == __MIPS_UHI_WRITE &&
(ctx->a[0] == STDOUT_FILENO || ctx->a[0] == STDERR_FILENO)) {
uint32_t status = irq_arch_disable();
uart_write(DEBUG_VIA_UART, (uint8_t *)ctx->a[1], ctx->a[2]);
ctx->v[0] = ctx->a[2];
ctx->epc += 4; /* move PC past the syscall */
irq_arch_restore(status);
return;
}
else if (ctx->t2[1] == __MIPS_UHI_FSTAT &&
(ctx->a[0] == STDOUT_FILENO || ctx->a[0] == STDERR_FILENO)) {
/*
* Printf fstat's the stdout/stderr file so
* fill out a minimal struct stat.
*/
struct stat *sbuf = (struct stat *)ctx->a[1];
memset(sbuf, 0, sizeof(struct stat));
sbuf->st_mode = S_IRUSR | S_IWUSR | S_IWGRP;
sbuf->st_blksize = BUFSIZ;
/* return 0 */
ctx->v[0] = 0;
ctx->epc += 4; /* move PC past the syscall */
return;
}
}
else
#endif
if (syscall_num == 2) {
unsigned int return_instruction = 0;
struct gpctx *new_ctx;
#ifdef MIPS_DSP
struct dspctx *new_dspctx;
#endif
/*
* Syscall 1 is reserved for UHI.
*/
/*
* save the stack pointer in the thread info
* note we want the saved value to include the
* saved off context and the 16 bytes padding.
* Note we cannot use the current sp value as
* the prologue of this function has adjusted it
*/
sched_active_thread->sp = (char *)(ctx->sp
- sizeof(struct gpctx) - PADDING);
#ifdef MIPS_DSP
_dsp_save(&dsp_ctx);
_linkctx_append(ctx,&(dsp_ctx.link));
#endif
#ifdef MIPS_HARD_FLOAT
if(currentfpctx) {
_linkctx_append(ctx,&(currentfpctx->fp.link));
}
#endif
sched_run();
new_ctx = (struct gpctx *)((unsigned int)sched_active_thread->sp + PADDING);
#ifdef MIPS_HARD_FLOAT
currentfpctx = (struct fp64ctx *)exctx_find(LINKCTX_TYPE_FP64, new_ctx);
if(!currentfpctx) {
/* check for half-width FPU ctx in-case hardware doesn't support double. */
currentfpctx = (struct fp64ctx *)exctx_find(LINKCTX_TYPE_FP32, new_ctx);
}
#endif
#ifdef MIPS_DSP
new_dspctx = (struct dspctx *)exctx_find(LINKCTX_TYPE_DSP, new_ctx);
if (new_dspctx)
_dsp_load(new_dspctx);
#endif
#ifdef MIPS_MICROMIPS
return_instruction =
mem_rw((const void *)(new_ctx->epc & ~MICROMIPS_ISA_MODE));
if ((return_instruction & MM_SYSCALL_MASK) == MM_SYSCALL) { /* syscall */
new_ctx->epc += 4; /* move PC past the syscall */
}
#else
return_instruction = mem_rw((const void *)new_ctx->epc);
if ((return_instruction & M32_SYSCALL_MASK) == M32_SYSCALL) { /* syscall */
new_ctx->epc += 4; /* move PC past the syscall */
}
#endif
/*
* The toolchain Exception restore code just wholesale copies the
* status register from the context back to the register loosing
* any changes that may have occured, 'status' is really global state
* You dont enable interrupts on one thread and not another...
* So we just copy the current status value into the saved value
* so nothing changes on the restore
*/
new_ctx->status = mips32_get_c0(C0_STATUS);
#ifdef MIPS_HARD_FLOAT
/*
* Disable FPU so we get an exception on first use to allow
* Lazy FPU context save and restore
*/
new_ctx->status &= ~SR_CU1;
#endif
__asm volatile ("lw $sp, 0(%0)" : : "r" (&sched_active_thread->sp));
/*
* Jump straight to the exception restore code
* if we return this functions prologue messes up
* the stack pointer
*/
__exception_restore();
UNREACHABLE();
}
break;
#ifdef MIPS_HARD_FLOAT
case EXC_CPU:
{
int newly_allocd = false;
mips_bissr(SR_CU1);
ctx->status |= SR_CU1;
if (!currentfpctx) {
currentfpctx = malloc(sizeof(struct fp64ctx));
assert(currentfpctx);
memset(currentfpctx,0,sizeof(struct fp64ctx));
currentfpctx->fp.link.id = LINKCTX_TYPE_FP64;
newly_allocd = true;
}
/* this means no one exec'd fpu since we last run */
if (oldfpctx == currentfpctx) {
return;
}
if (oldfpctx) {
_fpctx_save(&oldfpctx->fp);
}
if (!newly_allocd) {
_fpctx_load(&currentfpctx->fp);
}
/*
* next fpu exception must save our context as it's not necessarily
* the next context switch will cause fpu exception and it's very
* hard for any future task to determine which was the last one
* that performed fpu operations. so by saving this pointer now we
* give this knowledge to that future task
*/
oldfpctx = currentfpctx;
return;
}
#endif
/* default: */
}
/* Pass all other exceptions through to the toolchain handler */
__exception_handle(ctx, exception);
}