From b34f6357d032f4b39f9c7adb1995956d04339461 Mon Sep 17 00:00:00 2001 From: Dave Brolley Date: Fri, 29 Aug 2003 16:35:47 +0000 Subject: New simulator for Fujitsu frv contributed by Red Hat. --- sim/frv/mloop.in | 514 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 514 insertions(+) create mode 100644 sim/frv/mloop.in (limited to 'sim/frv/mloop.in') diff --git a/sim/frv/mloop.in b/sim/frv/mloop.in new file mode 100644 index 0000000..2d1726d --- /dev/null +++ b/sim/frv/mloop.in @@ -0,0 +1,514 @@ +# Simulator main loop for frv. -*- C -*- +# Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc. +# Contributed by Red Hat. +# +# This file is part of the GNU Simulators. +# +# 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, 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., +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# Syntax: +# /bin/sh mainloop.in command +# +# Command is one of: +# +# init +# support +# extract-{simple,scache,pbb} +# {full,fast}-exec-{simple,scache,pbb} +# +# A target need only provide a "full" version of one of simple,scache,pbb. +# If the target wants it can also provide a fast version of same. +# It can't provide more than this. + +# ??? After a few more ports are done, revisit. +# Will eventually need to machine generate a lot of this. + +case "x$1" in + +xsupport) + +cat <argbuf.semantic.sem_fast) (current_cpu, sc); + } + else + { + ARGBUF *abuf = &sc->argbuf; + const IDESC *idesc = abuf->idesc; +#if WITH_SCACHE_PBB + int virtual_p = CGEN_ATTR_VALUE (NULL, idesc->attrs, CGEN_INSN_VIRTUAL); +#else + int virtual_p = 0; +#endif + + if (! virtual_p) + { + /* FIXME: call x-before */ + if (ARGBUF_PROFILE_P (abuf)) + PROFILE_COUNT_INSN (current_cpu, abuf->addr, idesc->num); + /* FIXME: Later make cover macros: PROFILE_INSN_{INIT,FINI}. */ + if (FRV_COUNT_CYCLES (current_cpu, ARGBUF_PROFILE_P (abuf))) + { + @cpu@_model_insn_before (current_cpu, sc->first_insn_p); + model_insn = FRV_INSN_MODEL_PASS_1; + if (idesc->timing->model_fn != NULL) + (*idesc->timing->model_fn) (current_cpu, sc); + } + else + model_insn = FRV_INSN_NO_MODELING; + TRACE_INSN_INIT (current_cpu, abuf, 1); + TRACE_INSN (current_cpu, idesc->idata, + (const struct argbuf *) abuf, abuf->addr); + } +#if WITH_SCACHE + vpc = (*sc->argbuf.semantic.sem_full) (current_cpu, sc); +#else + vpc = (*sc->argbuf.semantic.sem_full) (current_cpu, abuf); +#endif + if (! virtual_p) + { + /* FIXME: call x-after */ + if (FRV_COUNT_CYCLES (current_cpu, ARGBUF_PROFILE_P (abuf))) + { + int cycles; + if (idesc->timing->model_fn != NULL) + { + model_insn = FRV_INSN_MODEL_PASS_2; + cycles = (*idesc->timing->model_fn) (current_cpu, sc); + } + else + cycles = 1; + @cpu@_model_insn_after (current_cpu, sc->last_insn_p, cycles); + } + TRACE_INSN_FINI (current_cpu, abuf, 1); + } + } + + return vpc; +} + +static void +@cpu@_parallel_write_init (SIM_CPU *current_cpu) +{ + CGEN_WRITE_QUEUE *q = CPU_WRITE_QUEUE (current_cpu); + CGEN_WRITE_QUEUE_CLEAR (q); + previous_vliw_pc = CPU_PC_GET(current_cpu); + frv_interrupt_state.f_ne_flags[0] = 0; + frv_interrupt_state.f_ne_flags[1] = 0; + frv_interrupt_state.imprecise_interrupt = NULL; +} + +static void +@cpu@_parallel_write_queued (SIM_CPU *current_cpu) +{ + int i; + + FRV_VLIW *vliw = CPU_VLIW (current_cpu); + CGEN_WRITE_QUEUE *q = CPU_WRITE_QUEUE (current_cpu); + + /* Loop over the queued writes, executing them. Set the pc to the address + of the insn which queued each write for the proper context in case an + interrupt is caused. Restore the proper pc after the writes are + completed. */ + IADDR save_pc = CPU_PC_GET (current_cpu); + IADDR new_pc = save_pc; + int branch_taken = 0; + int limit = CGEN_WRITE_QUEUE_INDEX (q); + frv_interrupt_state.data_written.length = 0; + + for (i = 0; i < limit; ++i) + { + CGEN_WRITE_QUEUE_ELEMENT *item = CGEN_WRITE_QUEUE_ELEMENT (q, i); + + /* If an imprecise interrupt was generated, then, check whether the + result should still be written. */ + if (frv_interrupt_state.imprecise_interrupt != NULL) + { + /* Only check writes by the insn causing the exception. */ + if (CGEN_WRITE_QUEUE_ELEMENT_IADDR (item) + == frv_interrupt_state.imprecise_interrupt->vpc) + { + /* Execute writes of floating point operations resulting in + overflow, underflow or inexact. */ + if (frv_interrupt_state.imprecise_interrupt->kind + == FRV_FP_EXCEPTION) + { + if ((frv_interrupt_state.imprecise_interrupt + ->u.fp_info.fsr_mask + & ~(FSR_INEXACT | FSR_OVERFLOW | FSR_UNDERFLOW))) + continue; /* Don't execute */ + } + /* Execute writes marked as 'forced'. */ + else if (! (CGEN_WRITE_QUEUE_ELEMENT_FLAGS (item) + & FRV_WRITE_QUEUE_FORCE_WRITE)) + continue; /* Don't execute */ + } + } + + /* Only execute the first branch on the queue. */ + if (CGEN_WRITE_QUEUE_ELEMENT_KIND (item) == CGEN_PC_WRITE + || CGEN_WRITE_QUEUE_ELEMENT_KIND (item) == CGEN_FN_PC_WRITE) + { + if (branch_taken) + continue; + branch_taken = 1; + if (CGEN_WRITE_QUEUE_ELEMENT_KIND (item) == CGEN_PC_WRITE) + new_pc = item->kinds.pc_write.value; + else + new_pc = item->kinds.fn_pc_write.value; + } + + CPU_PC_SET (current_cpu, CGEN_WRITE_QUEUE_ELEMENT_IADDR (item)); + frv_save_data_written_for_interrupts (current_cpu, item); + cgen_write_queue_element_execute (current_cpu, item); + } + + /* Update the LR with the address of the next insn if the flag is set. + This flag gets set in frvbf_set_write_next_vliw_to_LR by the JMPL, + JMPIL and CALL insns. */ + if (frvbf_write_next_vliw_addr_to_LR) + { + frvbf_h_spr_set_handler (current_cpu, H_SPR_LR, save_pc); + frvbf_write_next_vliw_addr_to_LR = 0; + } + + CPU_PC_SET (current_cpu, new_pc); + CGEN_WRITE_QUEUE_CLEAR (q); +} + +void +@cpu@_perform_writeback (SIM_CPU *current_cpu) +{ + @cpu@_parallel_write_queued (current_cpu); +} + +static unsigned cache_reqno = 0x80000000; /* Start value is for debugging. */ + +#if 0 /* experimental */ +/* FR400 has single prefetch. */ +static void +fr400_simulate_insn_prefetch (SIM_CPU *current_cpu, IADDR vpc) +{ + int cur_ix; + FRV_CACHE *cache; + +/* The cpu receives 8 bytes worth of insn data for each fetch aligned + on 8 byte boundary. */ +#define FR400_FETCH_SIZE 8 + + cur_ix = LS; + vpc &= ~(FR400_FETCH_SIZE - 1); + cache = CPU_INSN_CACHE (current_cpu); + + /* Request a load of the current address buffer, if necessary. */ + if (frv_insn_fetch_buffer[cur_ix].address != vpc) + { + frv_insn_fetch_buffer[cur_ix].address = vpc; + frv_insn_fetch_buffer[cur_ix].reqno = cache_reqno++; + if (FRV_COUNT_CYCLES (current_cpu, 1)) + frv_cache_request_load (cache, frv_insn_fetch_buffer[cur_ix].reqno, + frv_insn_fetch_buffer[cur_ix].address, + UNIT_I0 + cur_ix); + } + + /* Wait for the current address buffer to be loaded, if necessary. */ + if (FRV_COUNT_CYCLES (current_cpu, 1)) + { + FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (current_cpu); + int wait; + + /* Account for any branch penalty. */ + if (ps->branch_penalty > 0 && ! ps->past_first_p) + { + frv_model_advance_cycles (current_cpu, ps->branch_penalty); + frv_model_trace_wait_cycles (current_cpu, ps->branch_penalty, + "Branch penalty:"); + ps->branch_penalty = 0; + } + + /* Account for insn fetch latency. */ + wait = 0; + while (frv_insn_fetch_buffer[cur_ix].reqno != NO_REQNO) + { + frv_model_advance_cycles (current_cpu, 1); + ++wait; + } + frv_model_trace_wait_cycles (current_cpu, wait, "Insn fetch:"); + return; + } + + /* Otherwise just load the insns directly from the cache. + */ + if (frv_insn_fetch_buffer[cur_ix].reqno != NO_REQNO) + { + frv_cache_read (cache, cur_ix, vpc); + frv_insn_fetch_buffer[cur_ix].reqno = NO_REQNO; + } +} +#endif /* experimental */ + +/* FR500 has dual prefetch. */ +static void +simulate_dual_insn_prefetch (SIM_CPU *current_cpu, IADDR vpc, int fetch_size) +{ + int i; + int cur_ix, pre_ix; + SI pre_address; + FRV_CACHE *cache; + + /* See if the pc is within the addresses specified by either of the + fetch buffers. If so, that will be the current buffer. Otherwise, + arbitrarily select the LD buffer as the current one since it gets + priority in the case of interfering load requests. */ + cur_ix = LD; + vpc &= ~(fetch_size - 1); + for (i = LS; i < FRV_CACHE_PIPELINES; ++i) + { + if (frv_insn_fetch_buffer[i].address == vpc) + { + cur_ix = i; + break; + } + } + cache = CPU_INSN_CACHE (current_cpu); + + /* Request a load of the current address buffer, if necessary. */ + if (frv_insn_fetch_buffer[cur_ix].address != vpc) + { + frv_insn_fetch_buffer[cur_ix].address = vpc; + frv_insn_fetch_buffer[cur_ix].reqno = cache_reqno++; + if (FRV_COUNT_CYCLES (current_cpu, 1)) + frv_cache_request_load (cache, frv_insn_fetch_buffer[cur_ix].reqno, + frv_insn_fetch_buffer[cur_ix].address, + UNIT_I0 + cur_ix); + } + + /* If the prefetch buffer does not represent the next sequential address, then + request a load of the next sequential address. */ + pre_ix = (cur_ix + 1) % FRV_CACHE_PIPELINES; + pre_address = vpc + fetch_size; + if (frv_insn_fetch_buffer[pre_ix].address != pre_address) + { + frv_insn_fetch_buffer[pre_ix].address = pre_address; + frv_insn_fetch_buffer[pre_ix].reqno = cache_reqno++; + if (FRV_COUNT_CYCLES (current_cpu, 1)) + frv_cache_request_load (cache, frv_insn_fetch_buffer[pre_ix].reqno, + frv_insn_fetch_buffer[pre_ix].address, + UNIT_I0 + pre_ix); + } + + /* If counting cycles, account for any branch penalty and/or insn fetch + latency here. */ + if (FRV_COUNT_CYCLES (current_cpu, 1)) + { + FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (current_cpu); + int wait; + + /* Account for any branch penalty. */ + if (ps->branch_penalty > 0 && ! ps->past_first_p) + { + frv_model_advance_cycles (current_cpu, ps->branch_penalty); + frv_model_trace_wait_cycles (current_cpu, ps->branch_penalty, + "Branch penalty:"); + ps->branch_penalty = 0; + } + + /* Account for insn fetch latency. */ + wait = 0; + while (frv_insn_fetch_buffer[cur_ix].reqno != NO_REQNO) + { + frv_model_advance_cycles (current_cpu, 1); + ++wait; + } + frv_model_trace_wait_cycles (current_cpu, wait, "Insn fetch:"); + return; + } + + /* Otherwise just load the insns directly from the cache. + */ + if (frv_insn_fetch_buffer[cur_ix].reqno != NO_REQNO) + { + frv_cache_read (cache, cur_ix, vpc); + frv_insn_fetch_buffer[cur_ix].reqno = NO_REQNO; + } + if (frv_insn_fetch_buffer[pre_ix].reqno != NO_REQNO) + { + frv_cache_read (cache, pre_ix, pre_address); + frv_insn_fetch_buffer[pre_ix].reqno = NO_REQNO; + } +} + +static void +@cpu@_simulate_insn_prefetch (SIM_CPU *current_cpu, IADDR vpc) +{ + SI hsr0; + SIM_DESC sd; + + /* Nothing to do if not counting cycles and the cache is not enabled. */ + hsr0 = GET_HSR0 (); + if (! GET_HSR0_ICE (hsr0) && ! FRV_COUNT_CYCLES (current_cpu, 1)) + return; + + /* Different machines handle prefetch defferently. */ + sd = CPU_STATE (current_cpu); + switch (STATE_ARCHITECTURE (sd)->mach) + { + case bfd_mach_fr400: + simulate_dual_insn_prefetch (current_cpu, vpc, 8); + break; + case bfd_mach_frvtomcat: + case bfd_mach_fr500: + case bfd_mach_frv: + simulate_dual_insn_prefetch (current_cpu, vpc, 16); + break; + default: + break; + } +} + +int frv_save_profile_model_p; +EOF + +;; + +xinit) + +cat <mach, + CPU_ELF_FLAGS (current_cpu)); + + for (ninsns = 0; ! last_insn_p && ninsns < FRV_VLIW_SIZE; ++ninsns) + { + SCACHE *sc; + const CGEN_INSN *insn; + int error; + /* Go through the motions of finding the insns in the cache. */ + @cpu@_simulate_insn_prefetch (current_cpu, vpc); + + sc = @cpu@_scache_lookup (current_cpu, vpc, scache, hash_mask, FAST_P); + sc->first_insn_p = first_insn_p; + last_insn_p = sc->last_insn_p; + + /* Add the insn to the vliw and set up the interrupt state. */ + insn = sc->argbuf.idesc->idata; + error = frv_vliw_add_insn (vliw, insn); + if (! error) + frv_vliw_setup_insn (current_cpu, insn); + frv_detect_insn_access_interrupts (current_cpu, sc); + + vpc = execute (current_cpu, sc, FAST_P); + + SET_H_PC (vpc); /* needed for interrupt handling */ + first_insn_p = 0; + } + + /* If the timer is enabled, and model profiling was not originally enabled, + then turn it off again. This is the only place we can currently gain + control to do this. */ + if (frv_interrupt_state.timer.enabled && ! frv_save_profile_model_p) + sim_profile_set_option (current_state, "-model", PROFILE_MODEL_IDX, "0"); + + /* Check for interrupts. Also handles writeback if necessary. */ + frv_process_interrupts (current_cpu); + + CPU_INSN_COUNT (current_cpu) += ninsns; +} +EOF + +;; + +*) + echo "Invalid argument to mainloop.in: $1" >&2 + exit 1 + ;; + +esac -- cgit v1.1