diff packages/hal/frv/arch/current/src/frv_stub.c @ 208:e0c0827131d1 ecos

Merge from eCos master repository on 2002-05-20-20:11:54-BST
author jlarmour
date Mon, 20 May 2002 22:19:26 +0000
parents
children d2c90368aeef
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/hal/frv/arch/current/src/frv_stub.c
@@ -0,0 +1,382 @@
+//========================================================================
+//
+//      frv_stub.c
+//
+//      Helper functions for stub, generic to all FUJITSU processors
+//
+//========================================================================
+//####ECOSGPLCOPYRIGHTBEGIN####
+// -------------------------------------------
+// This file is part of eCos, the Embedded Configurable Operating System.
+// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+//
+// eCos is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 2 or (at your option) any later version.
+//
+// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with eCos; if not, write to the Free Software Foundation, Inc.,
+// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+//
+// As a special exception, if other files instantiate templates or use macros
+// or inline functions from this file, or you compile this file and link it
+// with other works to produce a work based on this file, this file does not
+// by itself cause the resulting work to be covered by the GNU General Public
+// License. However the source code for this file must still be made available
+// in accordance with section (3) of the GNU General Public License.
+//
+// This exception does not invalidate any other reasons why a work based on
+// this file might be covered by the GNU General Public License.
+//
+// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
+// at http://sources.redhat.com/ecos/ecos-license
+// -------------------------------------------
+//####ECOSGPLCOPYRIGHTEND####
+//========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):     Red Hat, gthomas
+// Contributors:  Red Hat, gthomas, jskov, msalter
+// Date:          2001-09-16
+// Purpose:       
+// Description:   Helper functions for stub, generic to all FUJITSU processors
+// Usage:         
+//
+//####DESCRIPTIONEND####
+//
+//========================================================================
+
+#include <pkgconf/hal.h>
+
+#ifdef CYGPKG_REDBOOT
+#include <pkgconf/redboot.h>
+#endif
+
+#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
+
+#include <cyg/hal/hal_stub.h>
+#include <cyg/hal/hal_arch.h>
+#include <cyg/hal/hal_intr.h>
+#include <cyg/hal/hal_cache.h>
+
+#ifndef FALSE
+#define FALSE 0
+#define TRUE  1
+#endif
+
+#ifdef CYGDBG_HAL_DEBUG_GDB_THREAD_SUPPORT
+#include <cyg/hal/dbg-threads-api.h>    // dbg_currthread_id
+#endif
+
+#if defined(CYGNUM_HAL_BREAKPOINT_LIST_SIZE) && (CYGNUM_HAL_BREAKPOINT_LIST_SIZE > 0)
+cyg_uint32 __frv_breakinst = HAL_BREAKINST;
+#endif
+
+// Sadly, this doesn't seem to work on the FRV400 either
+//#define USE_HW_STEP  
+
+#ifdef CYGSEM_HAL_FRV_HW_DEBUG
+static inline unsigned __get_dcr(void)
+{
+    unsigned retval;
+
+    asm volatile (
+        "movsg   dcr,%0\n"
+        : "=r" (retval)
+        : /* no inputs */  );
+
+    return retval;
+}
+
+static inline void __set_dcr(unsigned val)
+{
+    asm volatile (
+        "movgs   %0,dcr\n"
+        : /* no outputs */
+        : "r" (val) );
+}
+
+#endif
+
+/* Given a trap value TRAP, return the corresponding signal. */
+
+int __computeSignal (unsigned int trap_number)
+{
+    // should also catch CYGNUM_HAL_VECTOR_UNDEF_INSTRUCTION here but we
+    // can't tell the different between a real one and a breakpoint :-(
+    switch (trap_number) {
+      // Interrupts
+    case CYGNUM_HAL_VECTOR_EXTERNAL_INTERRUPT_LEVEL_1 ... CYGNUM_HAL_VECTOR_EXTERNAL_INTERRUPT_LEVEL_15:
+        return SIGINT;
+    case CYGNUM_HAL_VECTOR_INSTR_ACCESS_MMU_MISS:
+    case CYGNUM_HAL_VECTOR_INSTR_ACCESS_ERROR:
+    case CYGNUM_HAL_VECTOR_INSTR_ACCESS_EXCEPTION:
+    case CYGNUM_HAL_VECTOR_MEMORY_ADDRESS_NOT_ALIGNED:
+    case CYGNUM_HAL_VECTOR_DATA_ACCESS_ERROR:
+    case CYGNUM_HAL_VECTOR_DATA_ACCESS_MMU_MISS:
+    case CYGNUM_HAL_VECTOR_DATA_ACCESS_EXCEPTION:
+    case CYGNUM_HAL_VECTOR_DATA_STORE_ERROR:
+        return SIGBUS;
+    case CYGNUM_HAL_VECTOR_PRIVELEDGED_INSTRUCTION:
+    case CYGNUM_HAL_VECTOR_ILLEGAL_INSTRUCTION:
+    case CYGNUM_HAL_VECTOR_REGISTER_EXCEPTION:
+    case CYGNUM_HAL_VECTOR_FP_DISABLED:
+    case CYGNUM_HAL_VECTOR_MP_DISABLED:
+    case CYGNUM_HAL_VECTOR_FP_EXCEPTION:
+    case CYGNUM_HAL_VECTOR_MP_EXCEPTION:
+    case CYGNUM_HAL_VECTOR_DIVISION_EXCEPTION:
+    case CYGNUM_HAL_VECTOR_COMMIT_EXCEPTION:
+    case CYGNUM_HAL_VECTOR_COMPOUND_EXCEPTION:
+      return SIGILL;
+    default:
+        return SIGTRAP;
+    }
+}
+
+
+/* Return the trap number corresponding to the last-taken trap. */
+int __get_trap_number (void)
+{
+    // The vector is not not part of the GDB register set so get it
+    // directly from the save context.
+    return _hal_registers->vector;
+}
+
+
+#if defined(CYGSEM_REDBOOT_BSP_SYSCALLS)
+int __is_bsp_syscall(void) 
+{
+  // Might want to be more specific here
+  return (_hal_registers->vector == CYGNUM_HAL_VECTOR_SYSCALL);
+}
+#endif // defined(CYGSEM_REDBOOT_BSP_SYSCALLS)
+
+/* Set the currently-saved pc register value to PC. */
+
+void set_pc (target_register_t pc)
+{
+    put_register (PC, pc);
+}
+
+
+/*----------------------------------------------------------------------
+ * Single-step support
+ */
+
+/* Set things up so that the next user resume will execute one instruction.
+   This may be done by setting breakpoints or setting a single step flag
+   in the saved user registers, for example. */
+
+#ifndef CYGSEM_HAL_FRV_HW_DEBUG
+#if CYGINT_HAL_FRV_ARCH_FR400 == 1
+#define VLIW_DEPTH 2
+#endif
+#if CYGINT_HAL_FRV_ARCH_FR500 == 1
+#define VLIW_DEPTH 4
+#endif
+/*
+ * Structure to hold opcodes hoisted when breakpoints are
+ * set for single-stepping or async interruption.
+ */
+struct _bp_save {
+    unsigned long  *addr;
+    unsigned long   opcode;
+};
+
+/*
+ * We single-step by setting breakpoints.
+ *
+ * This is where we save the original instructions.
+ */
+static struct _bp_save step_bp[VLIW_DEPTH+1];
+
+//**************************************************************
+//************ CAUTION!! ***************************************
+//**************************************************************
+//
+// Attempt to analyze the current instruction.  This code is not
+// perfect in the case of VLIW sequences, although it's close.
+// Consider these sequences:
+//
+//      ldi.p   @(gr5,0),gr4
+//      jmpl    @(gr4,gr0)
+// and 
+//
+//      ldi.p   @(gr5,0),gr4
+//      add.p   gr6,gr7,gr8
+//      jmpl    @(gr4,gr0)
+//
+// In these cases, the only way to effectively calculate the 
+// target address (of the jump) would be to simulate the actions
+// of the pipelined instructions which come beforehand.
+//
+// Of course, this only affects single stepping through a VLIW
+// sequence which contains such pipelined effects and a branch.
+// Hopefully this is rare.
+//
+// Note: testing of the above sequence on the FR400 yielded an
+// illegal instruction (invalid VLIW sequence), so this may not
+// turn out to be a problem in practice, just theory.
+//
+//**************************************************************
+//**************************************************************
+
+static int
+_analyze_instr(unsigned long pc, unsigned long *targ, 
+               unsigned long *next, int *is_vliw)
+{
+    unsigned long opcode;
+    int n, is_branch = 0;
+
+    opcode = *(unsigned long *)pc;
+    switch ((opcode >> 18) & 0x7f) {
+    case 6:
+    case 7:
+        /* bcc, fbcc */
+        is_branch = 1;
+        n = (int)(opcode << 16);
+        n >>= 16;
+        *targ = pc + n*4;
+        pc += 4;
+        break;
+    case 12:
+        /* jmpl */
+        n = (int)(get_register((opcode>>12)&63));
+        n += (int)(get_register(opcode&63));
+        pc = n;
+        break;
+    case 13:
+        /* jmpil */
+        n = (int)(get_register((opcode>>12)&63));
+        n += (((int)(opcode << 20)) >> 20);
+        pc = n;
+        break;
+    case 15:
+        /* call */
+        n = (opcode >> 25) << 18;
+        n |= (opcode & 0x3ffff);
+        n <<= 8;
+        n >>= 8;
+        pc += n*4;
+        break;
+    case 14:
+        /* ret */
+        is_branch = 1;
+        *targ = get_register(LR);
+        pc += 4;
+        break;
+    default:
+        pc += 4;
+        break;
+    }
+    *next = pc;
+    *is_vliw = (opcode & 0x80000000) == 0;
+    return is_branch;
+}
+#endif
+
+void __single_step (void)
+{
+#ifdef CYGSEM_HAL_FRV_HW_DEBUG
+    __set_dcr(__get_dcr() | _DCR_SE);
+    diag_printf("Setting single step - DCR: %x\n", __get_dcr());
+#else
+    unsigned long pc, targ, next_pc;
+    int i, is_branch = 0;
+    int is_vliw;
+
+    for (i = 0;  i < VLIW_DEPTH+1;  i++) {
+        step_bp[i].addr = NULL;
+    }
+
+    pc = get_register(PC);
+    i = 1;
+    while (i < (VLIW_DEPTH+1)) {
+        is_branch = _analyze_instr(pc, &targ, &next_pc, &is_vliw);
+        if (is_branch && next_pc != targ) {
+            step_bp[i].addr = (unsigned long *)targ;
+            step_bp[i].opcode = *(unsigned long *)targ;
+            *(unsigned long *)targ = HAL_BREAKINST;
+            HAL_DCACHE_STORE(targ, 4);
+            HAL_ICACHE_INVALIDATE(targ, 4);
+        }
+        if (is_vliw) {
+            pc += 4;
+            i++;
+        } else {
+            break;
+        }
+    }
+    step_bp[0].addr = (unsigned long *)next_pc;
+    step_bp[0].opcode = *(unsigned long *)next_pc;
+    *(unsigned long *)next_pc = HAL_BREAKINST;
+    HAL_DCACHE_STORE(next_pc, 4);
+    HAL_ICACHE_INVALIDATE(next_pc, 4);
+#endif
+}
+
+/* Clear the single-step state. */
+
+void __clear_single_step (void)
+{
+#ifdef CYGSEM_HAL_FRV_HW_DEBUG
+    __set_dcr(__get_dcr() & ~_DCR_SE);
+#else
+    struct _bp_save *p;
+    int i;
+
+    for (i = 0;  i < VLIW_DEPTH+1;  i++) {
+        p = &step_bp[i];
+        if (p->addr) {
+            *(p->addr) = p->opcode;
+            HAL_DCACHE_STORE((cyg_uint32)p->addr, 4);
+            HAL_ICACHE_INVALIDATE((cyg_uint32)p->addr, 4);
+            p->addr = NULL;
+        }
+    }
+#endif
+}
+
+void __install_breakpoints (void)
+{
+#if defined(CYGNUM_HAL_BREAKPOINT_LIST_SIZE) && (CYGNUM_HAL_BREAKPOINT_LIST_SIZE > 0)
+    /* Install the breakpoints in the breakpoint list */
+    __install_breakpoint_list();
+#endif
+}
+
+void __clear_breakpoints (void)
+{
+#if defined(CYGNUM_HAL_BREAKPOINT_LIST_SIZE) && (CYGNUM_HAL_BREAKPOINT_LIST_SIZE > 0)
+    __clear_breakpoint_list();
+#endif
+}
+
+/* If the breakpoint we hit is in the breakpoint() instruction, return a
+   non-zero value. */
+
+int
+__is_breakpoint_function ()
+{
+    return get_register (PC) == (target_register_t)&_breakinst;
+}
+
+
+/* Skip the current instruction.  Since this is only called by the
+   stub when the PC points to a breakpoint or trap instruction,
+   we can safely just skip 4. */
+
+void __skipinst (void)
+{
+    unsigned long pc = get_register(PC);
+
+    pc += 4;
+    put_register(PC, pc);
+}
+
+#endif // CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS