URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [tags/] [nog_patch_68/] [or1ksim/] [cpu/] [or32/] [op_support.c] - Rev 1780
Go to most recent revision | Compare with Previous | Blame | View Log
/* op_support.c -- Support routines for micro operations Copyright (C) 2005 György `nog' Jeney, nog@sdf.lonestar.org 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 2 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, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <stdlib.h> #include "config.h" #ifdef HAVE_INTTYPES_H #include <inttypes.h> #endif #include "port.h" #include "arch.h" #include "opcode/or32.h" #include "sim-config.h" #include "spr_defs.h" #include "except.h" #include "immu.h" #include "abstract.h" #include "execute.h" #include "sched.h" #include "i386_regs.h" #include "dyn_rec.h" #include "op_support.h" #include "rec_i386.h" /* Stuff that is really a `micro' operation but is rather big (or for some other * reason (like calling exit()) */ void upd_reg_from_t(oraddr_t pc) { int reg; reg = cpu_state.curr_page->ts[(pc & (PAGE_SIZE - 1)) / 2]; if(reg & 0x1f) cpu_state.reg[reg & 0x1f] = cpu_state.t0; if((reg >> 5) & 0x1f) cpu_state.reg[(reg >> 5) & 0x1f] = cpu_state.t1; if((reg >> 10) & 0x1f) cpu_state.reg[(reg >> 10) & 0x1f] = cpu_state.t2; } void op_support_nop_exit(void) { upd_reg_from_t(get_pc()); PRINTF("exit(%"PRIdREG")\n", cpu_state.reg[3]); fprintf(stderr, "@reset : cycles %lld, insn #%lld\n", runtime.sim.reset_cycles, runtime.cpu.reset_instructions); fprintf(stderr, "@exit : cycles %lld, insn #%lld\n", runtime.sim.cycles, runtime.cpu.instructions); fprintf(stderr, " diff : cycles %lld, insn #%lld\n", runtime.sim.cycles - runtime.sim.reset_cycles, runtime.cpu.instructions - runtime.cpu.reset_instructions); /* FIXME: Implement emulation of a stalled cpu if (config.debug.gdb_enabled) set_stall_state (1); else runtime.sim.cont_run = 0; */ exit(0); } void op_support_nop_reset(void) { PRINTF("****************** counters reset ******************\n"); PRINTF("cycles %lld, insn #%lld\n", runtime.sim.cycles, runtime.cpu.instructions); PRINTF("****************** counters reset ******************\n"); runtime.sim.reset_cycles = runtime.sim.cycles; runtime.cpu.reset_instructions = runtime.cpu.instructions; } void op_support_nop_printf(void) { upd_reg_from_t(get_pc()); simprintf(cpu_state.reg[4], cpu_state.reg[3]); } void op_support_nop_report(void) { upd_reg_from_t(get_pc()); PRINTF("report(0x%"PRIxREG");\n", cpu_state.reg[3]); } void op_support_nop_report_imm(int imm) { upd_reg_from_t(get_pc()); PRINTF("report %i (0x%"PRIxREG");\n", imm, cpu_state.reg[3]); } /* Handles a jump */ /* addr is a VIRTUAL address */ /* NOTE: We can't use env since this code is compiled like the rest of the * simulator (most likely without -fomit-frame-pointer) and thus env will point * to some bogus value. */ void do_jump(oraddr_t addr) { struct dyn_page *target_dp; struct x_ref *xref; oraddr_t phys_page; /* The pc is set to the location of the jump in op_set_pc_preemt(_check) and * then it is incermented by 4 when the scheduler is run. If a scheduled job * so happens to raise an exception cpu_state.delay_insn will still be set and * so except_handle will do its pc adjusting magic (ie. -4 from it) and every- * thing ends up just working right, except when a scheduled job does not * raise an exeception. In that case we set the pc here explicitly */ set_pc(addr); /* immu_translate must be called after set_pc. If it would be called before * it and it issued an ITLB miss then it would appear that the instruction * that faulted was the instruction in the delay slot which is incorrect */ phys_page = immu_translate(addr); /* do_jump is called from the delay slot, which is the jump instruction * address + 4. */ /* printf("Recompiled code jumping out to %"PRIxADDR" from %"PRIxADDR"\n", phys_page, cpu_state.sprs[SPR_PPC] - 4); */ /* immu_translate() adds the hit delay to runtime.sim.mem_cycles but we add it * to the cycles when the instruction is executed so if we don't reset it now * it will produce wrong results */ runtime.sim.mem_cycles = 0; target_dp = find_dynd_page(phys_page); if(!target_dp) target_dp = new_dp(phys_page); /* Since writes to the 0x0-0xff range do not dirtyfy a page recompile the 0x0 * page if the jump is to that location */ if(phys_page < 0x100) target_dp->dirty = 1; /* Check if this location is cross-referenced */ if(!(xref = find_host_x_ref(target_dp->xrefs, phys_page))) { target_dp->dirty = 1; xref = add_to_xrefs(target_dp, phys_page); if(cpu_state.curr_page) add_to_held_xrefs(cpu_state.curr_page, xref); } else { /* Only increment reference count if this page didn't already */ if(cpu_state.curr_page && !find_held_x_ref(cpu_state.curr_page->held_xrefs, phys_page)) { xref->ref++; add_to_held_xrefs(cpu_state.curr_page, xref); } } if(target_dp->dirty) recompile_page(target_dp); cpu_state.curr_page = target_dp; /* FIXME: If the page is backed by more than one type of memory, this will * produce wrong results */ if(cpu_state.sprs[SPR_SR] & SPR_SR_IME) /* Add the mmu hit delay to the cycle counter */ upd_cycles_dec(target_dp->delayr - config.immu.hitdelay); else upd_cycles_dec(target_dp->delayr); cpu_state.ts_current = 0; /* Initially this (and do_rfe/handle_except) returned the address that we * should jump to and then the recompiled code performed the jump. This was * no problem if the jump was trully an interpage jump or if the location * didn't need recompileation. If the jump is page local and the page needs * recompileation there is a very high probability that the page will move in * memory and then the return address that is on the stack will point to * memory that has already been freed, sometimes leading to crashes */ /* This looks like it could really be simpler, but no it can't. The only * issue here is the stack: it has to be unwound. This function is called * from except_handle, which generally ends up quite high on the stack... */ or_longjmp(xref->dyn_addr); } /* l.rfe is a hard instruction to emulate. One could just call * do_jump(cpu_state.sprs[SPR_EPCR_BASE]), but then the location that we jump to * will get cross referenced and because the page that contains the exception * handlers is very rearly marked as dirty it will accumulate alot of held * cross references over time. */ void do_rfe(void) { struct dyn_page *target_dp; struct x_ref *xref; oraddr_t phys_page; int already_held = 0; set_pc(cpu_state.sprs[SPR_EPCR_BASE]); phys_page = immu_translate(cpu_state.sprs[SPR_EPCR_BASE]); /* Same reason as in do_jump() */ runtime.sim.mem_cycles = 0; /* op_do_sched has run by the time this is run, which makes the pc point to * the instruction after l.rfe. */ printf("Returning from exception to %"PRIxADDR" from %"PRIxADDR"\n", phys_page, cpu_state.sprs[SPR_PPC]); target_dp = find_dynd_page(phys_page); if(!target_dp) target_dp = new_dp(phys_page); /* Since writes to the 0x0-0xff range do not dirtyfy a page recompile the 0x0 * page if the jump is to that location */ if(phys_page < 0x100) target_dp->dirty = 1; /* Check if this location is cross-referenced */ if(!(xref = find_host_x_ref(target_dp->xrefs, phys_page))) { xref = add_to_xrefs(target_dp, phys_page); /* Calling dirtyfy_page is real tempting but if we get to the situation were * the l.rfe instruction and the location to which it returns to are on the * same page then all the exception cross references will get removed and * this will result in excessive recompileations of this page */ target_dp->dirty = 1; /* There is alot of code (especially in linux) that do loops like this: * int a; * // Stuff such that b gets on another page than a * int b; * for(i = 0; i < (some big value); i++) { * a = b; * // Some more stuff * } * Here a DTLB miss will happen on every acess to a and b and l.rfe will * always return to the same locations but since the previous l.rfe to this * page was to a different location the page will get recompiled each time a * or b is acessed. This is why the last NUM_RFE_HELD returns are `cached'. */ if(++cpu_state.rfe_held_xref_pos == NUM_RFE_HELD) cpu_state.rfe_held_xref_pos = 0; if(cpu_state.rfe_held_xrefs[cpu_state.rfe_held_xref_pos]) cpu_state.rfe_held_xrefs[cpu_state.rfe_held_xref_pos]->ref--; cpu_state.rfe_held_xrefs[cpu_state.rfe_held_xref_pos] = xref; } else { /* Make sure we increase this cross reference's reference count, since it is * decremented below. */ xref->ref++; already_held = 1; } if(target_dp->dirty) recompile_page(target_dp); if(already_held) xref->ref--; cpu_state.curr_page = target_dp; /* FIXME: If the page is backed by more than one type of memory, this will * produce wrong results */ if(cpu_state.sprs[SPR_SR] & SPR_SR_IME) /* Add the mmu hit delay to the cycle counter */ upd_cycles_dec(target_dp->delayr - config.immu.hitdelay); else upd_cycles_dec(target_dp->delayr); cpu_state.ts_current = 0; /* See the comment at the end of do_jump */ or_longjmp(xref->dyn_addr); } /* Handles an exception. */ void handle_except(oraddr_t except) { struct dyn_page *target_dp; struct x_ref *xref; /* NOTE: It is known when this code will be run. It is therefore not * necessary to have to plough through cpu_state.curr_page->ts to store the * temporaries. On the other hand, except_handle is also called from the * scheduler, therefore we don't know when it is called and we can't move the * temporaries to their permanent storeage in the recompiled code. */ /* op_do_sched has run by the time we run this, which makes the pc point to * the next instruction. */ printf("Exception %"PRIxADDR" (%s) from %"PRIxADDR"\n", except, except_name(except), get_pc() - 4); set_pc(except); target_dp = find_dynd_page(except); if(!target_dp) target_dp = new_dp(except); /* Check if this location is cross-referenced */ if(!(xref = find_host_x_ref(target_dp->xrefs, except))) { /* See the comment in do_rfe for why dirtyfy page is not called */ target_dp->dirty = 1; xref = add_to_xrefs(target_dp, except); } else { /* If this cross reference is scheduled for removal increment its reference * count */ if(!xref->ref) xref->ref++; } if(target_dp->dirty) recompile_page(target_dp); cpu_state.curr_page = target_dp; /* FIXME: If the page is backed by more than one type of memory, this will * produce wrong results */ /* Address translation is disabled above (no need to add hitdelay) */ upd_cycles_dec(target_dp->delayr); cpu_state.ts_current = 0; /* See the comment at the end of do_jump */ or_longjmp(xref->dyn_addr); }
Go to most recent revision | Compare with Previous | Blame | View Log