123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- # Simulator main loop for frv. -*- C -*-
- # Copyright (C) 1998-2015 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 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/>.
- # 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 <<EOF
- static INLINE const IDESC *
- extract (SIM_CPU *current_cpu, PCADDR pc, CGEN_INSN_INT insn, ARGBUF *abuf,
- int fast_p)
- {
- const IDESC *id = @cpu@_decode (current_cpu, pc, insn, insn, abuf);
- @cpu@_fill_argbuf (current_cpu, abuf, id, pc, fast_p);
- if (! fast_p)
- {
- int trace_p = PC_IN_TRACE_RANGE_P (current_cpu, pc);
- int profile_p = PC_IN_PROFILE_RANGE_P (current_cpu, pc);
- @cpu@_fill_argbuf_tp (current_cpu, abuf, trace_p, profile_p);
- }
- return id;
- }
- static INLINE SEM_PC
- execute (SIM_CPU *current_cpu, SCACHE *sc, int fast_p)
- {
- SEM_PC vpc;
- /* Force gr0 to zero before every insn. */
- @cpu@_h_gr_set (current_cpu, 0, 0);
- if (fast_p)
- {
- vpc = (*sc->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;
- CGEN_TRACE_INSN_INIT (current_cpu, abuf, 1);
- CGEN_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);
- }
- CGEN_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:
- case bfd_mach_fr450:
- simulate_dual_insn_prefetch (current_cpu, vpc, 8);
- break;
- case bfd_mach_frvtomcat:
- case bfd_mach_fr500:
- case bfd_mach_fr550:
- case bfd_mach_frv:
- simulate_dual_insn_prefetch (current_cpu, vpc, 16);
- break;
- default:
- break;
- }
- }
- int frv_save_profile_model_p;
- EOF
- ;;
- xinit)
- cat <<EOF
- /*xxxinit*/
- /* If the timer is enabled, then we will enable model profiling during
- execution. This is because the timer needs accurate cycles counts to
- work properly. Save the original setting of model profiling. */
- if (frv_interrupt_state.timer.enabled)
- frv_save_profile_model_p = PROFILE_MODEL_P (current_cpu);
- EOF
- ;;
- xextract-simple | xextract-scache)
- # Inputs: current_cpu, vpc, sc, FAST_P
- # Outputs: sc filled in
- # SET_LAST_INSN_P(last_p) called to indicate whether insn is last one
- cat <<EOF
- {
- CGEN_INSN_INT insn = frvbf_read_imem_USI (current_cpu, vpc);
- extract (current_cpu, vpc, insn, SEM_ARGBUF (sc), FAST_P);
- SET_LAST_INSN_P ((insn & 0x80000000) != 0);
- }
- EOF
- ;;
- xfull-exec-* | xfast-exec-*)
- # Inputs: current_cpu, vpc, FAST_P
- # Outputs:
- # vpc contains the address of the next insn to execute
- # pc of current_cpu must be up to date (=vpc) upon exit
- # CPU_INSN_COUNT (current_cpu) must be updated by number of insns executed
- #
- # Unlike the non-parallel case, this version is responsible for doing the
- # scache lookup.
- cat <<EOF
- {
- FRV_VLIW *vliw;
- int first_insn_p = 1;
- int last_insn_p = 0;
- int ninsns;
- CGEN_ATTR_VALUE_ENUM_TYPE slot;
- /* If the timer is enabled, then enable model profiling. This is because
- the timer needs accurate cycles counts to work properly. */
- if (frv_interrupt_state.timer.enabled && ! frv_save_profile_model_p)
- sim_profile_set_option (current_state, "-model", PROFILE_MODEL_IDX, "1");
- /* Init parallel-write queue and vliw. */
- @cpu@_parallel_write_init (current_cpu);
- vliw = CPU_VLIW (current_cpu);
- frv_vliw_reset (vliw, STATE_ARCHITECTURE (CPU_STATE (current_cpu))->mach,
- CPU_ELF_FLAGS (current_cpu));
- frv_current_fm_slot = UNIT_NIL;
- 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);
- slot = (*vliw->current_vliw)[vliw->next_slot - 1];
- if (slot >= UNIT_FM0 && slot <= UNIT_FM3)
- frv_current_fm_slot = slot;
- 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
|