OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [or1ksim/] [cpu/] [or32/] [execute.c] - Rev 226

Go to most recent revision | Compare with Previous | Blame | View Log

/* execute.c -- OR1K architecture dependent simulation
 
   Copyright (C) 1999 Damjan Lampret, lampret@opencores.org
   Copyright (C) 2005 György `nog' Jeney, nog@sdf.lonestar.org
   Copyright (C) 2008 Embecosm Limited
 
   Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>
 
   This file is part of OpenRISC 1000 Architectural Simulator.
 
   This program 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 3 of the License, or (at your option)
   any later version.
 
   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>. */
 
/* This program is commented throughout in a fashion suitable for processing
   with Doxygen. */
 
 
/* Most of the OR1K simulation is done in here.
 
   When SIMPLE_EXECUTION is defined below a file insnset.c is included!
*/
 
/* Autoconf and/or portability configuration */
#include "config.h"
#include "port.h"
 
/* System includes */
#include <stdlib.h>
 
/* Package includes */
#include "execute.h"
#include "toplevel-support.h"
#include "except.h"
#include "labels.h"
#include "gdbcomm.h"
#include "sched.h"
#include "stats.h"
#include "opcode/or32.h"
#include "dmmu.h"
#include "immu.h"
#include "sim-cmd.h"
#include "vapi.h"
#include "debug-unit.h"
#include "branch-predict.h"
#include "sprs.h"
#include "rsp-server.h"
#include <fenv.h> // Floating point environment for FPU instructions
#include "execute-fp.h"
 
/* Includes and macros for simple execution */
#if SIMPLE_EXECUTION
 
#define SET_PARAM0(val) set_operand(0, val, current->insn_index, current->insn)
 
#define PARAM0 eval_operand(0, current->insn_index, current->insn)
#define PARAM1 eval_operand(1, current->insn_index, current->insn)
#define PARAM2 eval_operand(2, current->insn_index, current->insn)
 
#define INSTRUCTION(name) void name (struct iqueue_entry *current)
 
#endif	/* SIMPLE_EXECUTION */
 
 
/*! Current cpu state. Globally available. */
struct cpu_state  cpu_state;
 
/*! Temporary program counter. Globally available */
oraddr_t  pcnext;
 
/*! Num cycles waiting for stores to complete. Globally available */
int  sbuf_wait_cyc = 0;
 
/*! Number of total store cycles. Globally available */
int  sbuf_total_cyc = 0;
 
/*! Whether we are doing statistical analysis. Globally available */
int  do_stats = 0;
 
/*! History of execution. Globally available */
struct hist_exec *hist_exec_tail = NULL;
 
/* Benchmark multi issue execution. This file only */
static int  multissue[20];
static int  issued_per_cycle = 4;
 
/* Store buffer analysis - stores are accumulated and commited when IO is
   idle. This file only */
static int  sbuf_head              = 0;
static int  sbuf_tail              = 0;
static int  sbuf_count             = 0;
#if !(DYNAMIC_EXECUTION)
static int  sbuf_buf[MAX_SBUF_LEN] = { 0 };
#endif
 
static int sbuf_prev_cycles = 0;
 
/* Variables used throughout this file to share information */
static int  breakpoint;
static int  next_delay_insn;
 
/* Functions to configure the host machine's FPU before doing simulated OR1k
   FP ops, handle flags and restore things when it's done. One for before and
   one for afterwards.
*/
void fp_set_or1k_rm(void);
void fp_set_flags_restore_host_rm(void);
 
/* A place to store the host's FPU rounding mode while OR1k's is used */
int host_fp_rm;
 
/* Forward declaration of static functions */
#if !(DYNAMIC_EXECUTION)
static void decode_execute (struct iqueue_entry *current);
#endif
 
/*---------------------------------------------------------------------------*/
/*!Get an actual value of a specific register
 
   Implementation specific. Abort if we are given a duff register. Only used
   externally to support simprintf(), which is now obsolete.
 
   @param[in] regno  The register of interest
 
   @return  The value of the register                                        */
/*---------------------------------------------------------------------------*/
uorreg_t
evalsim_reg (unsigned int  regno)
{
  if (regno < MAX_GPRS)
    {
#if RAW_RANGE_STATS
      int delta = (runtime.sim.cycles - raw_stats.reg[regno]);
 
      if ((unsigned long) delta < (unsigned long) RAW_RANGE)
	{
	  raw_stats.range[delta]++;
	}
#endif /* RAW_RANGE */
 
      return cpu_state.reg[regno];
    }
  else
    {
      PRINTF ("\nABORT: read out of registers\n");
      sim_done ();
      return 0;
    }
}	/* evalsim_reg() */
 
 
/*---------------------------------------------------------------------------*/
/*!Set a specific register with value
 
   Implementation specific. Abort if we are given a duff register.
 
   @param[in] regno  The register of interest
   @param[in] value  The value to be set                                     */
/*---------------------------------------------------------------------------*/
void
setsim_reg (unsigned int  regno,
	    uorreg_t      value)
{
  if (regno == 0)		/* gpr0 is always zero */
    {
      value = 0;
    }
 
  if (regno < MAX_GPRS)
    {
      cpu_state.reg[regno] = value;
    }
  else
    {
      PRINTF ("\nABORT: write out of registers\n");
      sim_done ();
    }
 
#if RAW_RANGE_STATS
  raw_stats.reg[regno] = runtime.sim.cycles;
#endif /* RAW_RANGE */
 
}	/* setsim_reg() */
 
 
/*---------------------------------------------------------------------------*/
/*!Evaluates source operand operand
 
   Implementation specific. Declared global, although this is only actually
   required for DYNAMIC_EXECUTION,
 
   @param[in] insn  The instruction
   @param[in] opd   The operand
 
   @return  The value of the source operand                                  */
/*---------------------------------------------------------------------------*/
uorreg_t
eval_operand_val (uint32_t               insn,
		  struct insn_op_struct *opd)
{
  unsigned long  operand = 0;
  unsigned long  sbit;
  unsigned int   nbits = 0;
 
  while (1)
    {
      operand |=
	((insn >> (opd->type & OPTYPE_SHR)) & ((1 << opd->data) - 1)) <<
	nbits;
      nbits += opd->data;
 
      if (opd->type & OPTYPE_OP)
	{
	  break;
	}
 
      opd++;
    }
 
  if (opd->type & OPTYPE_SIG)
    {
      sbit = (opd->type & OPTYPE_SBIT) >> OPTYPE_SBIT_SHR;
 
      if (operand & (1 << sbit))
	{
	  operand |= ~REG_C (0) << sbit;
	}
    }
 
  return operand;
 
}	/* eval_operand_val() */
 
 
/*---------------------------------------------------------------------------*/
/*!Does source operand depend on computation of dest operand?
 
      Cycle t                 Cycle t+1
  dst: irrelevant         src: immediate                  always 0
  dst: reg1 direct        src: reg2 direct                0 if reg1 != reg2
  dst: reg1 disp          src: reg2 direct                always 0
  dst: reg1 direct        src: reg2 disp                  0 if reg1 != reg2
  dst: reg1 disp          src: reg2 disp                  always 1 (store must
                                                          finish before load)
  dst: flag               src: flag                       always 1
 
  @param[in] prev  Previous instruction
  @param[in] next  Next instruction
 
  @return  Non-zero if yes.                                                  */
/*---------------------------------------------------------------------------*/
static int
check_depend (struct iqueue_entry *prev,
	      struct iqueue_entry *next)
{
  /* Find destination type. */
  unsigned long          type = 0;
  int                    prev_dis;
  int                    next_dis;
  orreg_t                prev_reg_val = 0;
  struct insn_op_struct *opd;
 
  if (or32_opcodes[prev->insn_index].flags & OR32_W_FLAG
      && or32_opcodes[next->insn_index].flags & OR32_R_FLAG)
    {
      return  1;
    }
 
  opd      = op_start[prev->insn_index];
  prev_dis = 0;
 
  while (1)
    {
      if (opd->type & OPTYPE_DIS)
	{
	  prev_dis = 1;
	}
 
      if (opd->type & OPTYPE_DST)
	{
	  type = opd->type;
 
	  if (prev_dis)
	    {
	      type |= OPTYPE_DIS;
	    }
 
	  /* Destination is always a register */
	  prev_reg_val = eval_operand_val (prev->insn, opd);
	  break;
	}
 
      if (opd->type & OPTYPE_LAST)
	{
	  return 0;		/* Doesn't have a destination operand */
	}
 
      if (opd->type & OPTYPE_OP)
	{
	  prev_dis = 0;
	}
 
      opd++;
    }
 
  /* We search all source operands - if we find confict => return 1 */
  opd      = op_start[next->insn_index];
  next_dis = 0;
 
  while (1)
    {
      if (opd->type & OPTYPE_DIS)
	{
	  next_dis = 1;
	}
 
      /* This instruction sequence also depends on order of execution:
           l.lw r1, k(r1)
           l.sw k(r1), r4
         Here r1 is a destination in l.sw */
 
      /* FIXME: This situation is not handeld here when r1 == r2:
           l.sw k(r1), r4
           l.lw r3, k(r2) */
      if (!(opd->type & OPTYPE_DST) || (next_dis && (opd->type & OPTYPE_DST)))
	{
	  if (opd->type & OPTYPE_REG)
	    {
	      if (eval_operand_val (next->insn, opd) == prev_reg_val)
		{
		  return 1;
		}
	    }
	}
 
      if (opd->type & OPTYPE_LAST)
	{
	  break;
	}
 
      opd++;
    }
 
  return  0;
 
}	/* check_depend() */
 
 
/*---------------------------------------------------------------------------*/
/*!Should instruction NOT be executed?
 
   Modified by CZ 26/05/01 for new mode execution.
 
   @return  Nonzero if instruction should NOT be executed                    */
/*---------------------------------------------------------------------------*/
static int
fetch ()
{
  static int break_just_hit = 0;
 
  if (NULL != breakpoints)
    {
      /* MM: Check for breakpoint.  This has to be done in fetch cycle,
         because of peripheria.  
         MM1709: if we cannot access the memory entry, we could not set the
         breakpoint earlier, so just check the breakpoint list.  */
      if (has_breakpoint (peek_into_itlb (cpu_state.pc)) && !break_just_hit)
	{
	  break_just_hit = 1;
	  return 1;		/* Breakpoint set. */
	}
      break_just_hit = 0;
    }
 
  breakpoint                 = 0;
  cpu_state.iqueue.insn_addr = cpu_state.pc;
  cpu_state.iqueue.insn      = eval_insn (cpu_state.pc, &breakpoint);
 
  /* Fetch instruction. */
  if (!except_pending)
    {
      runtime.cpu.instructions++;
    }
 
  /* update_pc will be called after execution */
  return 0;
 
}	/* fetch() */
 
 
/*---------------------------------------------------------------------------*/
/*!This code actually updates the PC value                                   */
/*---------------------------------------------------------------------------*/
static void
update_pc ()
{
  cpu_state.delay_insn    = next_delay_insn;
  cpu_state.sprs[SPR_PPC] = cpu_state.pc;	/* Store value for later */
  cpu_state.pc            = pcnext;
  pcnext                  = cpu_state.delay_insn ? cpu_state.pc_delay :
                                                   pcnext + 4;
}	/* update_pc() */
 
 
/*---------------------------------------------------------------------------*/
/*!Perform analysis of the instruction being executed
 
   This could be static for SIMPLE_EXECUTION, but made global for general use.
 
   @param[in] current  The instruction being executed                        */
/*---------------------------------------------------------------------------*/
void
analysis (struct iqueue_entry *current)
{
  if (config.cpu.dependstats)
    {
      /* Dynamic, dependency stats. */
      adddstats (cpu_state.icomplet.insn_index, current->insn_index, 1,
		 check_depend (&cpu_state.icomplet, current));
 
      /* Dynamic, functional units stats. */
      addfstats (or32_opcodes[cpu_state.icomplet.insn_index].func_unit,
		 or32_opcodes[current->insn_index].func_unit, 1,
		 check_depend (&cpu_state.icomplet, current));
 
      /* Dynamic, single stats. */
      addsstats (current->insn_index, 1);
    }
 
  if (config.cpu.superscalar)
    {
      if ((or32_opcodes[current->insn_index].func_unit == it_branch) ||
	  (or32_opcodes[current->insn_index].func_unit == it_jump))
	runtime.sim.storecycles += 0;
 
      if (or32_opcodes[current->insn_index].func_unit == it_store)
	runtime.sim.storecycles += 1;
 
      if (or32_opcodes[current->insn_index].func_unit == it_load)
	runtime.sim.loadcycles += 1;
 
      /* Pseudo multiple issue benchmark */
      if ((multissue[or32_opcodes[current->insn_index].func_unit] < 1) ||
	  (check_depend (&cpu_state.icomplet, current))
	  || (issued_per_cycle < 1))
	{
	  int i;
	  for (i = 0; i < 20; i++)
	    multissue[i] = 2;
	  issued_per_cycle = 2;
	  runtime.cpu.supercycles++;
	  if (check_depend (&cpu_state.icomplet, current))
	    runtime.cpu.hazardwait++;
	  multissue[it_unknown] = 2;
	  multissue[it_shift] = 2;
	  multissue[it_compare] = 1;
	  multissue[it_branch] = 1;
	  multissue[it_jump] = 1;
	  multissue[it_extend] = 2;
	  multissue[it_nop] = 2;
	  multissue[it_move] = 2;
	  multissue[it_movimm] = 2;
	  multissue[it_arith] = 2;
	  multissue[it_store] = 2;
	  multissue[it_load] = 2;
	}
      multissue[or32_opcodes[current->insn_index].func_unit]--;
      issued_per_cycle--;
    }
 
  if (config.cpu.dependstats)
    /* Instruction waits in completition buffer until retired. */
    memcpy (&cpu_state.icomplet, current, sizeof (struct iqueue_entry));
 
  if (config.sim.history)
    {
      /* History of execution */
      hist_exec_tail = hist_exec_tail->next;
      hist_exec_tail->addr = cpu_state.icomplet.insn_addr;
    }
 
  if (config.sim.exe_log)
    dump_exe_log ();
 
  if (config.sim.exe_bin_insn_log)
    dump_exe_bin_insn_log (current);
 
}	/* analysis() */
 
 
#if !(DYNAMIC_EXECUTION)
 
/*---------------------------------------------------------------------------*/
/*!Store buffer analysis for store instructions
 
   Stores are accumulated and commited when IO is idle
 
   @param[in] cyc  Number of cycles being analysed                           */
/*---------------------------------------------------------------------------*/
static void
sbuf_store (int cyc)
{
  int delta = runtime.sim.cycles - sbuf_prev_cycles;
 
  sbuf_total_cyc   += cyc;
  sbuf_prev_cycles  = runtime.sim.cycles;
 
  /* Take stores from buffer, that occured meanwhile */
  while (sbuf_count && delta >= sbuf_buf[sbuf_tail])
    {
      delta     -= sbuf_buf[sbuf_tail];
      sbuf_tail  = (sbuf_tail + 1) % MAX_SBUF_LEN;
      sbuf_count--;
    }
 
  if (sbuf_count)
    {
      sbuf_buf[sbuf_tail] -= delta;
    }
 
  /* Store buffer is full, take one out */
  if (sbuf_count >= config.cpu.sbuf_len)
    {
      sbuf_wait_cyc          += sbuf_buf[sbuf_tail];
      runtime.sim.mem_cycles += sbuf_buf[sbuf_tail];
      sbuf_prev_cycles       += sbuf_buf[sbuf_tail];
      sbuf_tail               = (sbuf_tail + 1) % MAX_SBUF_LEN;
      sbuf_count--;
    }
 
  /* Put newest store in the buffer */
  sbuf_buf[sbuf_head] = cyc;
  sbuf_head           = (sbuf_head + 1) % MAX_SBUF_LEN;
  sbuf_count++;
 
}	/* sbuf_store() */
 
 
/*---------------------------------------------------------------------------*/
/*!Store buffer analysis for load instructions
 
   Previous stores should commit, before any load                            */
/*---------------------------------------------------------------------------*/
static void
sbuf_load ()
{
  int delta = runtime.sim.cycles - sbuf_prev_cycles;
  sbuf_prev_cycles = runtime.sim.cycles;
 
  /* Take stores from buffer, that occured meanwhile */
  while (sbuf_count && delta >= sbuf_buf[sbuf_tail])
    {
      delta     -= sbuf_buf[sbuf_tail];
      sbuf_tail  = (sbuf_tail + 1) % MAX_SBUF_LEN;
      sbuf_count--;
    }
 
  if (sbuf_count)
    {
      sbuf_buf[sbuf_tail] -= delta;
    }
 
  /* Wait for all stores to complete */
  while (sbuf_count > 0)
    {
      sbuf_wait_cyc          += sbuf_buf[sbuf_tail];
      runtime.sim.mem_cycles += sbuf_buf[sbuf_tail];
      sbuf_prev_cycles       += sbuf_buf[sbuf_tail];
      sbuf_tail               = (sbuf_tail + 1) % MAX_SBUF_LEN;
      sbuf_count--;
    }
}	/* sbuf_load() */
 
#endif	/* !DYNAMIC_EXECUTION */
 
 
/*---------------------------------------------------------------------------*/
/*!Outputs dissasembled instruction                                          */
/*---------------------------------------------------------------------------*/
void
dump_exe_log ()
{
  oraddr_t      insn_addr = cpu_state.iqueue.insn_addr;
  unsigned int  i;
  unsigned int  j;
  uorreg_t      operand;
 
  if (insn_addr == 0xffffffff)
    {
      return;
    }
 
  if ((config.sim.exe_log_start <= runtime.cpu.instructions) &&
      ((config.sim.exe_log_end <= 0) ||
       (runtime.cpu.instructions <= config.sim.exe_log_end)))
    {
      struct label_entry *entry;
 
      if (config.sim.exe_log_marker &&
	  !(runtime.cpu.instructions % config.sim.exe_log_marker))
	{
	  fprintf (runtime.sim.fexe_log,
		   "--------------------- %8lli instruction "
		   "---------------------\n",
		   runtime.cpu.instructions);
	}
 
      switch (config.sim.exe_log_type)
	{
	case EXE_LOG_HARDWARE:
	  fprintf (runtime.sim.fexe_log,
		   "\nEXECUTED(%11llu): %" PRIxADDR ":  ",
		   runtime.cpu.instructions, insn_addr);
	  fprintf (runtime.sim.fexe_log, "%.2x%.2x",
		   eval_direct8 (insn_addr, 0, 0),
		   eval_direct8 (insn_addr + 1, 0, 0));
	  fprintf (runtime.sim.fexe_log, "%.2x%.2x",
		   eval_direct8 (insn_addr + 2, 0, 0),
		   eval_direct8 (insn_addr + 3, 0, 0));
 
	  for (i = 0; i < MAX_GPRS; i++)
	    {
	      if (i % 4 == 0)
		{
		  fprintf (runtime.sim.fexe_log, "\n");
		}
 
	      fprintf (runtime.sim.fexe_log, "GPR%2u: %" PRIxREG "  ", i,
		       cpu_state.reg[i]);
	    }
 
	  fprintf (runtime.sim.fexe_log, "\n");
	  fprintf (runtime.sim.fexe_log, "SR   : %.8" PRIx32 "  ",
		   cpu_state.sprs[SPR_SR]);
	  fprintf (runtime.sim.fexe_log, "EPCR0: %" PRIxADDR "  ",
		   cpu_state.sprs[SPR_EPCR_BASE]);
	  fprintf (runtime.sim.fexe_log, "EEAR0: %" PRIxADDR "  ",
		   cpu_state.sprs[SPR_EEAR_BASE]);
	  fprintf (runtime.sim.fexe_log, "ESR0 : %.8" PRIx32 "\n",
		   cpu_state.sprs[SPR_ESR_BASE]);
	  break;
 
	case EXE_LOG_SIMPLE:
	case EXE_LOG_SOFTWARE:
	  disassemble_index (cpu_state.iqueue.insn,
			     cpu_state.iqueue.insn_index);
 
	  entry = get_label (insn_addr);
	  if (entry)
	    {
	      fprintf (runtime.sim.fexe_log, "%s:\n", entry->name);
	    }
 
	  if (config.sim.exe_log_type == EXE_LOG_SOFTWARE)
	    {
	      struct insn_op_struct *opd =
		op_start[cpu_state.iqueue.insn_index];
 
	      j = 0;
	      while (1)
		{
		  operand = eval_operand_val (cpu_state.iqueue.insn, opd);
		  while (!(opd->type & OPTYPE_OP))
		    {
		      opd++;
		    }
		  if (opd->type & OPTYPE_DIS)
		    {
		      fprintf (runtime.sim.fexe_log,
			       "EA =%" PRIxADDR " PA =%" PRIxADDR " ",
			       cpu_state.insn_ea,
			       peek_into_dtlb (cpu_state.insn_ea, 0, 0));
		      opd++;	/* Skip of register operand */
		      j++;
		    }
		  else if ((opd->type & OPTYPE_REG) && operand)
		    {
		      fprintf (runtime.sim.fexe_log, "r%-2i=%" PRIxREG " ",
			       (int) operand, evalsim_reg (operand));
		    }
		  else
		    {
		      fprintf (runtime.sim.fexe_log, "             ");
		    }
		  j++;
		  if (opd->type & OPTYPE_LAST)
		    {
		      break;
		    }
		  opd++;
		}
	      if (or32_opcodes[cpu_state.iqueue.insn_index].flags & OR32_R_FLAG)
		{
		  fprintf (runtime.sim.fexe_log, "SR =%" PRIxREG " ",
			   cpu_state.sprs[SPR_SR]);
		  j++;
		}
	      while (j < 3)
		{
		  fprintf (runtime.sim.fexe_log, "             ");
		  j++;
		}
	    }
	  fprintf (runtime.sim.fexe_log, "%" PRIxADDR " ", insn_addr);
	  fprintf (runtime.sim.fexe_log, "%s\n", disassembled);
	}
    }
}	/* dump_exe_log() */
 
 
 
/*---------------------------------------------------------------------------*/
/*!Outputs binary copy of instruction to a file                              */
/*---------------------------------------------------------------------------*/
void
dump_exe_bin_insn_log (struct iqueue_entry *current)
{
  // Do endian swap before spitting out (will be kept in LE on a LE machine)
  // but more useful to see it in big endian format.
  // Should probably host htonl().
  uint32_t insn = (((current->insn & 0xff)<<24) | 
		   ((current->insn & 0xff00)<<8) | 
		   ((current->insn & 0xff0000)>>8) | 
		   ((current->insn & 0xff000000)>>24));
 
  //for(i=0;i<4;i++) tmp_insn[i] = eval_direct8 (insn_addr+i, 0, 0);
 
  // Dump it into binary log file
  fwrite((void*)&insn, 4, 1, runtime.sim.fexe_bin_insn_log);
 
 
}	/* dump_exe_bin_insn_log() */
 
 
/*---------------------------------------------------------------------------*/
/*!Dump registers
 
   Supports the CLI 'r' and 't' commands                                     */
/*---------------------------------------------------------------------------*/
void
dumpreg ()
{
  int       i;
  oraddr_t  physical_pc;
 
  if ((physical_pc = peek_into_itlb (cpu_state.iqueue.insn_addr)))
    {
      disassemble_memory (physical_pc, physical_pc + 4, 0);
    }
  else
    {
      PRINTF ("INTERNAL SIMULATOR ERROR:\n");
      PRINTF ("no translation for currently executed instruction\n");
    }
 
  // generate_time_pretty (temp, runtime.sim.cycles * config.sim.clkcycle_ps);
  PRINTF (" (executed) [cycle %lld, #%lld]\n", runtime.sim.cycles,
	  runtime.cpu.instructions);
  if (config.cpu.superscalar)
    {
      PRINTF ("Superscalar CYCLES: %u", runtime.cpu.supercycles);
    }
  if (config.cpu.hazards)
    {
      PRINTF ("  HAZARDWAIT: %u\n", runtime.cpu.hazardwait);
    }
  else if (config.cpu.superscalar)
    {
      PRINTF ("\n");
    }
 
  if ((physical_pc = peek_into_itlb (cpu_state.pc)))
    {
      disassemble_memory (physical_pc, physical_pc + 4, 0);
    }
  else
    {
      PRINTF ("%" PRIxADDR ": : xxxxxxxx  ITLB miss follows", cpu_state.pc);
    }
 
  PRINTF (" (next insn) %s", (cpu_state.delay_insn ? "(delay insn)" : ""));
 
  for (i = 0; i < MAX_GPRS; i++)
    {
      if (i % 4 == 0)
	{
	  PRINTF ("\n");
	}
 
      PRINTF ("GPR%.2u: %" PRIxREG "  ", i, evalsim_reg (i));
    }
 
  PRINTF ("flag: %u\n", cpu_state.sprs[SPR_SR] & SPR_SR_F ? 1 : 0);
 
}	/* dumpreg() */
 
 
/*---------------------------------------------------------------------------*/
/*!Wrapper around real decode_execute function
 
   Some statistics here only
 
   @param[in] current  Instruction being executed                            */
/*---------------------------------------------------------------------------*/
static void
decode_execute_wrapper (struct iqueue_entry *current)
{
  breakpoint = 0;
 
#ifndef HAVE_EXECUTION
#error HAVE_EXECUTION has to be defined in order to execute programs.
#endif
 
  /* FIXME: Most of this file is not needed with DYNAMIC_EXECUTION */
#if !(DYNAMIC_EXECUTION)
  decode_execute (current);
#endif
 
  if (breakpoint)
    {
      except_handle (EXCEPT_TRAP, cpu_state.sprs[SPR_EEAR_BASE]);
    }
}	/* decode_execute_wrapper() */
 
/*---------------------------------------------------------------------------*/
/*!Reset the CPU                                                             */
/*---------------------------------------------------------------------------*/
void
cpu_reset ()
{
  int               i;
  struct hist_exec *hist_exec_head = NULL;
  struct hist_exec *hist_exec_new;
 
  runtime.sim.cycles       = 0;
  runtime.sim.loadcycles   = 0;
  runtime.sim.storecycles  = 0;
  runtime.cpu.instructions = 0;
  runtime.cpu.supercycles  = 0;
  runtime.cpu.hazardwait   = 0;
 
  for (i = 0; i < MAX_GPRS; i++)
    {
      setsim_reg (i, 0);
    }
 
  memset (&cpu_state.iqueue,   0, sizeof (cpu_state.iqueue));
  memset (&cpu_state.icomplet, 0, sizeof (cpu_state.icomplet));
 
  sbuf_head        = 0;
  sbuf_tail        = 0;
  sbuf_count       = 0;
  sbuf_prev_cycles = 0;
 
  /* Initialise execution history circular buffer */
  for (i = 0; i < HISTEXEC_LEN; i++)
    {
      hist_exec_new = malloc (sizeof (struct hist_exec));
 
      if (!hist_exec_new)
	{
	  fprintf (stderr, "Out-of-memory\n");
	  exit (1);
	}
 
      if (!hist_exec_head)
	{
	  hist_exec_head = hist_exec_new;
	}
      else
	{
	  hist_exec_tail->next = hist_exec_new;
	}
 
      hist_exec_new->prev = hist_exec_tail;
      hist_exec_tail = hist_exec_new;
    }
 
  /* Make hist_exec_tail->next point to hist_exec_head */
  hist_exec_tail->next = hist_exec_head;
  hist_exec_head->prev = hist_exec_tail;
 
  /* MM1409: All progs should start at reset vector entry! This sorted out by
     setting the cpu_state.pc field below. Not clear this is very good code! */
 
  /* Patches suggested by Shinji Wakatsuki, so that the vector address takes
     notice of the Exception Prefix High bit of the Supervision register */
  pcnext = (cpu_state.sprs[SPR_SR] & SPR_SR_EPH ? 0xf0000000 : 0x00000000);
 
  if (config.sim.verbose)
    {
      PRINTF ("Starting at 0x%" PRIxADDR "\n", pcnext);
    }
 
  cpu_state.pc  = pcnext;
  pcnext       += 4;
 
  /* MM1409: All programs should set their stack pointer!  */
#if !(DYNAMIC_EXECUTION)
  except_handle (EXCEPT_RESET, 0);
  update_pc ();
#endif
 
  except_pending = 0;
  cpu_state.pc   = cpu_state.sprs[SPR_SR] & SPR_SR_EPH ?
    0xf0000000 + EXCEPT_RESET : EXCEPT_RESET;
 
}	/* cpu_reset() */
 
 
/*---------------------------------------------------------------------------*/
/*!Simulates one CPU clock cycle
 
  @return  non-zero if a breakpoint is hit, zero otherwise.                  */
/*---------------------------------------------------------------------------*/
int
cpu_clock ()
{
  except_pending  = 0;
  next_delay_insn = 0;
 
  if (fetch ())
    {
      PRINTF ("Breakpoint hit.\n");
      return  1;
    }
 
  if (except_pending)
    {
      update_pc ();
      except_pending = 0;
      return  0;
    }
 
  if (breakpoint)
    {
      except_handle (EXCEPT_TRAP, cpu_state.sprs[SPR_EEAR_BASE]);
      update_pc ();
      except_pending = 0;
      return  0;
    }
 
  decode_execute_wrapper (&cpu_state.iqueue);
  update_pc ();
  return  0;
 
}	/* cpu_clock() */
 
 
/*---------------------------------------------------------------------------*/
/*!If decoding cannot be found, call this function                           */
/*---------------------------------------------------------------------------*/
#if SIMPLE_EXECUTION
void
l_invalid (struct iqueue_entry *current)
{
#else
void
l_invalid ()
{
#endif
  except_handle (EXCEPT_ILLEGAL, cpu_state.iqueue.insn_addr);
 
}	/* l_invalid() */
 
 
/*---------------------------------------------------------------------------*/
/*!The main execution loop                                                   */
/*---------------------------------------------------------------------------*/
void
exec_main ()
{
  long long time_start;
 
  while (1)
    {
      time_start = runtime.sim.cycles;
      if (config.debug.enabled)
	{
	  while (runtime.cpu.stalled)
	    {
	      if (config.debug.rsp_enabled)
		{
		  handle_rsp ();
		}
	      else if (config.debug.gdb_enabled)
		{
		  block_jtag ();
		  handle_server_socket (FALSE);
		}
	      else
		{
		  fprintf (stderr, "ERROR: CPU stalled and GDB connection not "
			   "enabled: Invoking CLI and terminating.\n");
		  /* Dump the user into interactive mode.  From there he or
		     she can decide what to do. */
		  handle_sim_command ();
		  sim_done ();
		}
	      if (runtime.sim.iprompt)
		handle_sim_command ();
	    }
	}
 
      /* Each cycle has counter of mem_cycles; this value is joined with cycles
         at the end of the cycle; no sim originated memory accesses should be
         performed inbetween. */
      runtime.sim.mem_cycles = 0;
 
      if (!config.pm.enabled ||
	  !(config.pm.enabled &
	    (cpu_state.sprs[SPR_PMR] & (SPR_PMR_DME | SPR_PMR_SME))))
	{
	  if (cpu_clock ())
	    {
	      /* A breakpoint has been hit, drop to interactive mode */
	      handle_sim_command ();
	    }
	}
 
      if (config.vapi.enabled && runtime.vapi.enabled)
	{
	  vapi_check ();
	}
 
      if (config.debug.gdb_enabled)
	{
	  handle_server_socket (FALSE);	/* block & check_stdin = false */
	}
 
      if (config.debug.enabled)
	{
	  if (cpu_state.sprs[SPR_DMR1] & SPR_DMR1_ST)
	    {
	      set_stall_state (1);
 
	      if (config.debug.rsp_enabled)
		{
		  rsp_exception (EXCEPT_TRAP);
		}
	    }
	}
 
      runtime.sim.cycles        += runtime.sim.mem_cycles;
      scheduler.job_queue->time -= runtime.sim.cycles - time_start;
 
      if (scheduler.job_queue->time <= 0)
	{
	  do_scheduler ();
	}
    }
}	/* exec_main() */
 
 
/*---------------------------------------------------------------------------*/
/*!Floating point operation setup function
 
   Save host rounding mode, set it to OR1K's according to FPCSR
   This function should be called before performing any FP instructions to
   ensure the OR1K's rounding mode is installed in the host
                                                                             */
/*---------------------------------------------------------------------------*/
 void fp_set_or1k_rm(void)
 {
   // Set OR1K's RM in host machine
   // First save host RM to restore it later
   host_fp_rm = fegetround();
   // Now map OR1K RM to host RM
   int or1k_rm = 0;
   switch(cpu_state.sprs[SPR_FPCSR] & SPR_FPCSR_RM)
     {
     case FPCSR_RM_RN:
       or1k_rm = FE_TONEAREST;
       break;
     case FPCSR_RM_RZ:
       or1k_rm = FE_TOWARDZERO;
       break;
     case FPCSR_RM_RIP:
       or1k_rm = FE_UPWARD;
       break;
     case FPCSR_RM_RIN:
       or1k_rm = FE_TOWARDZERO;
       break;
     }
   // Now set this RM for the host
   fesetround(or1k_rm); // TODO - check for nonzero return here, if RM not
                        // able to be set, maybe warn user
 }
 
/*---------------------------------------------------------------------------*/
/*!Floating point operation flag set and host restore function
 
  Copy flags from floating point op into OR1K's FPCSR, and restore the host's
  rounding mode.
                                                                             */
/*---------------------------------------------------------------------------*/
void fp_set_flags_restore_host_rm(void)
 {
   // Check FP flags on host, convert to OR1K FPCSR bits
   // First clear all flags in OR1K FPCSR
   cpu_state.sprs[SPR_FPCSR] &= ~SPR_FPCSR_ALLF;
 
   // Test host flags, set appropriate OR1K flags
   if (fetestexcept(FE_DIVBYZERO)) cpu_state.sprs[SPR_FPCSR] |= SPR_FPCSR_DZF;
   if (fetestexcept(FE_INEXACT)) cpu_state.sprs[SPR_FPCSR] |= SPR_FPCSR_IXF;
   if (fetestexcept(FE_INVALID)) cpu_state.sprs[SPR_FPCSR] |= SPR_FPCSR_IVF;
   if (fetestexcept(FE_OVERFLOW)) cpu_state.sprs[SPR_FPCSR] |= SPR_FPCSR_OVF;
   if (fetestexcept(FE_UNDERFLOW)) cpu_state.sprs[SPR_FPCSR] |= SPR_FPCSR_UNF;
 
   // Restore the hosts's rounding mode
   fesetround(host_fp_rm);
 
   // TODO: Call FP exception is FPEE set and any of the flags were set
   /*
     if ((cpu_state.sprs[SPR_FPCSR] & SPR_FPCSR_FPEE) &
     (|(cpu_state.sprs[SPR_FPCSR] & SPR_FPCSR_ALLF)))
     except_handle (EXCEPT_FPE, cpu_state.iqueue.insn_addr);
   */
 }
 
#if COMPLEX_EXECUTION
 
/* Include generated/built in decode_execute function */
#include "execgen.c"
 
#elif SIMPLE_EXECUTION
 
 
/*---------------------------------------------------------------------------*/
/*!Evaluates source operand
 
   Implementation specific.
 
   @param[in] op_no       The operand
   @param[in] insn_index  Address of the instruction
   @param[in] insn        The instruction
 
   @return  The value of the operand                                         */
/*---------------------------------------------------------------------------*/
static uorreg_t
eval_operand (int            op_no,
	      unsigned long  insn_index,
	      uint32_t       insn)
{
  struct insn_op_struct *opd = op_start[insn_index];
  uorreg_t               ret;
 
  while (op_no)
    {
      if (opd->type & OPTYPE_LAST)
	{
	  fprintf (stderr,
		   "Instruction requested more operands than it has\n");
	  exit (1);
	}
 
      if ((opd->type & OPTYPE_OP) && !(opd->type & OPTYPE_DIS))
	{
	  op_no--;
	}
 
      opd++;
    }
 
  if (opd->type & OPTYPE_DIS)
    {
      ret = eval_operand_val (insn, opd);
 
      while (!(opd->type & OPTYPE_OP))
	{
	  opd++;
	}
 
      opd++;
      ret               += evalsim_reg (eval_operand_val (insn, opd));
      cpu_state.insn_ea  = ret;
 
      return  ret;
    }
 
  if (opd->type & OPTYPE_REG)
    {
      return  evalsim_reg (eval_operand_val (insn, opd));
    }
 
  return  eval_operand_val (insn, opd);
 
}	/* eval_operand() */
 
 
/*---------------------------------------------------------------------------*/
/*!Set destination operand (register direct) with value.
 
   Implementation specific.
 
   @param[in] op_no       The operand
   @param[in] value       The value to set
   @param[in] insn_index  Address of the instruction
   @param[in] insn        The instruction                                    */
/*---------------------------------------------------------------------------*/
static void
set_operand (int            op_no,
	     orreg_t        value,
	     unsigned long  insn_index,
	     uint32_t       insn)
{
  struct insn_op_struct *opd = op_start[insn_index];
 
  while (op_no)
    {
      if (opd->type & OPTYPE_LAST)
	{
	  fprintf (stderr,
		   "Instruction requested more operands than it has\n");
	  exit (1);
	}
 
      if ((opd->type & OPTYPE_OP) && !(opd->type & OPTYPE_DIS))
	{
	  op_no--;
	}
 
      opd++;
    }
 
  if (!(opd->type & OPTYPE_REG))
    {
      fprintf (stderr, "Trying to set a non-register operand\n");
      exit (1);
    }
 
  setsim_reg (eval_operand_val (insn, opd), value);
 
}	/* set_operand() */
 
 
/*---------------------------------------------------------------------------*/
/*!Simple and rather slow decoding function
 
   Based on built automata.
 
   @param[in] current  The current instruction to execute                    */
/*---------------------------------------------------------------------------*/
static void
decode_execute (struct iqueue_entry *current)
{
  int insn_index;
 
  current->insn_index = insn_index = insn_decode (current->insn);
 
  if (insn_index < 0)
    {
      l_invalid (current);
    }
  else
    {
      or32_opcodes[insn_index].exec (current);
    }
 
  if (do_stats)
    analysis (&cpu_state.iqueue);
}
 
#include "insnset.c"
 
#elif defined(DYNAMIC_EXECUTION)
 
#else
# error "Must define SIMPLE_EXECUTION, COMPLEX_EXECUTION or DYNAMIC_EXECUTION"
#endif
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.