URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [or1ksim/] [cpu/] [or32/] [execute.c] - Rev 227
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