| /* frv simulator machine independent profiling code. |
| |
| Copyright (C) 1998-2021 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/>. */ |
| |
| /* This must come before any other includes. */ |
| #include "defs.h" |
| |
| #define WANT_CPU |
| #define WANT_CPU_FRVBF |
| |
| #include "sim-main.h" |
| #include "bfd.h" |
| #include <stdlib.h> |
| |
| #if WITH_PROFILE_MODEL_P |
| |
| #include "profile.h" |
| #include "profile-fr400.h" |
| #include "profile-fr500.h" |
| #include "profile-fr550.h" |
| |
| static void |
| reset_gr_flags (SIM_CPU *cpu, INT gr) |
| { |
| SIM_DESC sd = CPU_STATE (cpu); |
| if (STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr400 |
| || STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr450) |
| fr400_reset_gr_flags (cpu, gr); |
| /* Other machines have no gr flags right now. */ |
| } |
| |
| static void |
| reset_fr_flags (SIM_CPU *cpu, INT fr) |
| { |
| SIM_DESC sd = CPU_STATE (cpu); |
| if (STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr400 |
| || STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr450) |
| fr400_reset_fr_flags (cpu, fr); |
| else if (STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr500) |
| fr500_reset_fr_flags (cpu, fr); |
| } |
| |
| static void |
| reset_acc_flags (SIM_CPU *cpu, INT acc) |
| { |
| SIM_DESC sd = CPU_STATE (cpu); |
| if (STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr400 |
| || STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr450) |
| fr400_reset_acc_flags (cpu, acc); |
| /* Other machines have no acc flags right now. */ |
| } |
| |
| static void |
| reset_cc_flags (SIM_CPU *cpu, INT cc) |
| { |
| SIM_DESC sd = CPU_STATE (cpu); |
| if (STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr500) |
| fr500_reset_cc_flags (cpu, cc); |
| /* Other machines have no cc flags. */ |
| } |
| |
| void |
| set_use_is_gr_complex (SIM_CPU *cpu, INT gr) |
| { |
| if (gr != -1) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| reset_gr_flags (cpu, gr); |
| ps->cur_gr_complex |= (((DI)1) << gr); |
| } |
| } |
| |
| void |
| set_use_not_gr_complex (SIM_CPU *cpu, INT gr) |
| { |
| if (gr != -1) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| ps->cur_gr_complex &= ~(((DI)1) << gr); |
| } |
| } |
| |
| int |
| use_is_gr_complex (SIM_CPU *cpu, INT gr) |
| { |
| if (gr != -1) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| return ps->cur_gr_complex & (((DI)1) << gr); |
| } |
| return 0; |
| } |
| |
| /* Globals flag indicates whether this insn is being modeled. */ |
| enum FRV_INSN_MODELING model_insn = FRV_INSN_NO_MODELING; |
| |
| /* static buffer for the name of the currently most restrictive hazard. */ |
| static char hazard_name[100] = ""; |
| |
| /* Print information about the wait applied to an entire VLIW insn. */ |
| FRV_INSN_FETCH_BUFFER frv_insn_fetch_buffer[] |
| = { |
| {1, NO_REQNO}, {1, NO_REQNO} /* init with impossible address. */ |
| }; |
| |
| enum cache_request |
| { |
| cache_load, |
| cache_invalidate, |
| cache_flush, |
| cache_preload, |
| cache_unlock |
| }; |
| |
| /* A queue of load requests from the data cache. Use to keep track of loads |
| which are still pending. */ |
| /* TODO -- some of these are mutually exclusive and can use a union. */ |
| typedef struct |
| { |
| FRV_CACHE *cache; |
| unsigned reqno; |
| SI address; |
| int length; |
| int is_signed; |
| int regnum; |
| int cycles; |
| int regtype; |
| int lock; |
| int all; |
| int slot; |
| int active; |
| enum cache_request request; |
| } CACHE_QUEUE_ELEMENT; |
| |
| #define CACHE_QUEUE_SIZE 64 /* TODO -- make queue dynamic */ |
| struct |
| { |
| unsigned reqno; |
| int ix; |
| CACHE_QUEUE_ELEMENT q[CACHE_QUEUE_SIZE]; |
| } cache_queue = {0, 0}; |
| |
| /* Queue a request for a load from the cache. The load will be queued as |
| 'inactive' and will be requested after the given number |
| of cycles have passed from the point the load is activated. */ |
| void |
| request_cache_load (SIM_CPU *cpu, INT regnum, int regtype, int cycles) |
| { |
| CACHE_QUEUE_ELEMENT *q; |
| FRV_VLIW *vliw; |
| int slot; |
| |
| /* For a conditional load which was not executed, CPU_LOAD_LENGTH will be |
| zero. */ |
| if (CPU_LOAD_LENGTH (cpu) == 0) |
| return; |
| |
| if (cache_queue.ix >= CACHE_QUEUE_SIZE) |
| abort (); /* TODO: Make the queue dynamic */ |
| |
| q = & cache_queue.q[cache_queue.ix]; |
| ++cache_queue.ix; |
| |
| q->reqno = cache_queue.reqno++; |
| q->request = cache_load; |
| q->cache = CPU_DATA_CACHE (cpu); |
| q->address = CPU_LOAD_ADDRESS (cpu); |
| q->length = CPU_LOAD_LENGTH (cpu); |
| q->is_signed = CPU_LOAD_SIGNED (cpu); |
| q->regnum = regnum; |
| q->regtype = regtype; |
| q->cycles = cycles; |
| q->active = 0; |
| |
| vliw = CPU_VLIW (cpu); |
| slot = vliw->next_slot - 1; |
| q->slot = (*vliw->current_vliw)[slot]; |
| |
| CPU_LOAD_LENGTH (cpu) = 0; |
| } |
| |
| /* Queue a request to flush the cache. The request will be queued as |
| 'inactive' and will be requested after the given number |
| of cycles have passed from the point the request is activated. */ |
| void |
| request_cache_flush (SIM_CPU *cpu, FRV_CACHE *cache, int cycles) |
| { |
| CACHE_QUEUE_ELEMENT *q; |
| FRV_VLIW *vliw; |
| int slot; |
| |
| if (cache_queue.ix >= CACHE_QUEUE_SIZE) |
| abort (); /* TODO: Make the queue dynamic */ |
| |
| q = & cache_queue.q[cache_queue.ix]; |
| ++cache_queue.ix; |
| |
| q->reqno = cache_queue.reqno++; |
| q->request = cache_flush; |
| q->cache = cache; |
| q->address = CPU_LOAD_ADDRESS (cpu); |
| q->all = CPU_PROFILE_STATE (cpu)->all_cache_entries; |
| q->cycles = cycles; |
| q->active = 0; |
| |
| vliw = CPU_VLIW (cpu); |
| slot = vliw->next_slot - 1; |
| q->slot = (*vliw->current_vliw)[slot]; |
| } |
| |
| /* Queue a request to invalidate the cache. The request will be queued as |
| 'inactive' and will be requested after the given number |
| of cycles have passed from the point the request is activated. */ |
| void |
| request_cache_invalidate (SIM_CPU *cpu, FRV_CACHE *cache, int cycles) |
| { |
| CACHE_QUEUE_ELEMENT *q; |
| FRV_VLIW *vliw; |
| int slot; |
| |
| if (cache_queue.ix >= CACHE_QUEUE_SIZE) |
| abort (); /* TODO: Make the queue dynamic */ |
| |
| q = & cache_queue.q[cache_queue.ix]; |
| ++cache_queue.ix; |
| |
| q->reqno = cache_queue.reqno++; |
| q->request = cache_invalidate; |
| q->cache = cache; |
| q->address = CPU_LOAD_ADDRESS (cpu); |
| q->all = CPU_PROFILE_STATE (cpu)->all_cache_entries; |
| q->cycles = cycles; |
| q->active = 0; |
| |
| vliw = CPU_VLIW (cpu); |
| slot = vliw->next_slot - 1; |
| q->slot = (*vliw->current_vliw)[slot]; |
| } |
| |
| /* Queue a request to preload the cache. The request will be queued as |
| 'inactive' and will be requested after the given number |
| of cycles have passed from the point the request is activated. */ |
| void |
| request_cache_preload (SIM_CPU *cpu, FRV_CACHE *cache, int cycles) |
| { |
| CACHE_QUEUE_ELEMENT *q; |
| FRV_VLIW *vliw; |
| int slot; |
| |
| if (cache_queue.ix >= CACHE_QUEUE_SIZE) |
| abort (); /* TODO: Make the queue dynamic */ |
| |
| q = & cache_queue.q[cache_queue.ix]; |
| ++cache_queue.ix; |
| |
| q->reqno = cache_queue.reqno++; |
| q->request = cache_preload; |
| q->cache = cache; |
| q->address = CPU_LOAD_ADDRESS (cpu); |
| q->length = CPU_LOAD_LENGTH (cpu); |
| q->lock = CPU_LOAD_LOCK (cpu); |
| q->cycles = cycles; |
| q->active = 0; |
| |
| vliw = CPU_VLIW (cpu); |
| slot = vliw->next_slot - 1; |
| q->slot = (*vliw->current_vliw)[slot]; |
| |
| CPU_LOAD_LENGTH (cpu) = 0; |
| } |
| |
| /* Queue a request to unlock the cache. The request will be queued as |
| 'inactive' and will be requested after the given number |
| of cycles have passed from the point the request is activated. */ |
| void |
| request_cache_unlock (SIM_CPU *cpu, FRV_CACHE *cache, int cycles) |
| { |
| CACHE_QUEUE_ELEMENT *q; |
| FRV_VLIW *vliw; |
| int slot; |
| |
| if (cache_queue.ix >= CACHE_QUEUE_SIZE) |
| abort (); /* TODO: Make the queue dynamic */ |
| |
| q = & cache_queue.q[cache_queue.ix]; |
| ++cache_queue.ix; |
| |
| q->reqno = cache_queue.reqno++; |
| q->request = cache_unlock; |
| q->cache = cache; |
| q->address = CPU_LOAD_ADDRESS (cpu); |
| q->cycles = cycles; |
| q->active = 0; |
| |
| vliw = CPU_VLIW (cpu); |
| slot = vliw->next_slot - 1; |
| q->slot = (*vliw->current_vliw)[slot]; |
| } |
| |
| static void |
| submit_cache_request (CACHE_QUEUE_ELEMENT *q) |
| { |
| switch (q->request) |
| { |
| case cache_load: |
| frv_cache_request_load (q->cache, q->reqno, q->address, q->slot); |
| break; |
| case cache_flush: |
| frv_cache_request_invalidate (q->cache, q->reqno, q->address, q->slot, |
| q->all, 1/*flush*/); |
| break; |
| case cache_invalidate: |
| frv_cache_request_invalidate (q->cache, q->reqno, q->address, q->slot, |
| q->all, 0/*flush*/); |
| break; |
| case cache_preload: |
| frv_cache_request_preload (q->cache, q->address, q->slot, |
| q->length, q->lock); |
| break; |
| case cache_unlock: |
| frv_cache_request_unlock (q->cache, q->address, q->slot); |
| break; |
| default: |
| abort (); |
| } |
| } |
| |
| /* Activate all inactive load requests. */ |
| static void |
| activate_cache_requests (SIM_CPU *cpu) |
| { |
| int i; |
| for (i = 0; i < cache_queue.ix; ++i) |
| { |
| CACHE_QUEUE_ELEMENT *q = & cache_queue.q[i]; |
| if (! q->active) |
| { |
| q->active = 1; |
| /* Submit the request now if the cycle count is zero. */ |
| if (q->cycles == 0) |
| submit_cache_request (q); |
| } |
| } |
| } |
| |
| /* Check to see if a load is pending which affects the given register(s). |
| */ |
| int |
| load_pending_for_register (SIM_CPU *cpu, int regnum, int words, int regtype) |
| { |
| int i; |
| for (i = 0; i < cache_queue.ix; ++i) |
| { |
| CACHE_QUEUE_ELEMENT *q = & cache_queue.q[i]; |
| |
| /* Must be the same kind of register. */ |
| if (! q->active || q->request != cache_load || q->regtype != regtype) |
| continue; |
| |
| /* If the registers numbers are equal, then we have a match. */ |
| if (q->regnum == regnum) |
| return 1; /* load pending */ |
| |
| /* Check for overlap of a load with a multi-word register. */ |
| if (regnum < q->regnum) |
| { |
| if (regnum + words > q->regnum) |
| return 1; |
| } |
| /* Check for overlap of a multi-word load with the register. */ |
| else |
| { |
| int data_words = (q->length + sizeof (SI) - 1) / sizeof (SI); |
| if (q->regnum + data_words > regnum) |
| return 1; |
| } |
| } |
| |
| return 0; /* no load pending */ |
| } |
| |
| /* Check to see if a cache flush pending which affects the given address. */ |
| static int |
| flush_pending_for_address (SIM_CPU *cpu, SI address) |
| { |
| int line_mask = ~(CPU_DATA_CACHE (cpu)->line_size - 1); |
| int i; |
| for (i = 0; i < cache_queue.ix; ++i) |
| { |
| CACHE_QUEUE_ELEMENT *q = & cache_queue.q[i]; |
| |
| /* Must be the same kind of request and active. */ |
| if (! q->active || q->request != cache_flush) |
| continue; |
| |
| /* If the addresses are equal, then we have a match. */ |
| if ((q->address & line_mask) == (address & line_mask)) |
| return 1; /* flush pending */ |
| } |
| |
| return 0; /* no flush pending */ |
| } |
| |
| static void |
| remove_cache_queue_element (SIM_CPU *cpu, int i) |
| { |
| /* If we are removing the load of a FR register, then remember which one(s). |
| */ |
| CACHE_QUEUE_ELEMENT q = cache_queue.q[i]; |
| |
| for (--cache_queue.ix; i < cache_queue.ix; ++i) |
| cache_queue.q[i] = cache_queue.q[i + 1]; |
| |
| /* If we removed a load of a FR register, check to see if any other loads |
| of that register is still queued. If not, then apply the queued post |
| processing time of that register to its latency. Also apply |
| 1 extra cycle of latency to the register since it was a floating point |
| load. */ |
| if (q.request == cache_load && q.regtype != REGTYPE_NONE) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int data_words = (q.length + sizeof (SI) - 1) / sizeof (SI); |
| int j; |
| for (j = 0; j < data_words; ++j) |
| { |
| int regnum = q.regnum + j; |
| if (! load_pending_for_register (cpu, regnum, 1, q.regtype)) |
| { |
| if (q.regtype == REGTYPE_FR) |
| { |
| int *fr = ps->fr_busy; |
| fr[regnum] += 1 + ps->fr_ptime[regnum]; |
| ps->fr_ptime[regnum] = 0; |
| } |
| } |
| } |
| } |
| } |
| |
| /* Copy data from the cache buffer to the target register(s). */ |
| static void |
| copy_load_data (SIM_CPU *current_cpu, FRV_CACHE *cache, int slot, |
| CACHE_QUEUE_ELEMENT *q) |
| { |
| switch (q->length) |
| { |
| case 1: |
| if (q->regtype == REGTYPE_FR) |
| { |
| if (q->is_signed) |
| { |
| QI value = CACHE_RETURN_DATA (cache, slot, q->address, QI, 1); |
| SET_H_FR (q->regnum, value); |
| } |
| else |
| { |
| UQI value = CACHE_RETURN_DATA (cache, slot, q->address, UQI, 1); |
| SET_H_FR (q->regnum, value); |
| } |
| } |
| else |
| { |
| if (q->is_signed) |
| { |
| QI value = CACHE_RETURN_DATA (cache, slot, q->address, QI, 1); |
| SET_H_GR (q->regnum, value); |
| } |
| else |
| { |
| UQI value = CACHE_RETURN_DATA (cache, slot, q->address, UQI, 1); |
| SET_H_GR (q->regnum, value); |
| } |
| } |
| break; |
| case 2: |
| if (q->regtype == REGTYPE_FR) |
| { |
| if (q->is_signed) |
| { |
| HI value = CACHE_RETURN_DATA (cache, slot, q->address, HI, 2); |
| SET_H_FR (q->regnum, value); |
| } |
| else |
| { |
| UHI value = CACHE_RETURN_DATA (cache, slot, q->address, UHI, 2); |
| SET_H_FR (q->regnum, value); |
| } |
| } |
| else |
| { |
| if (q->is_signed) |
| { |
| HI value = CACHE_RETURN_DATA (cache, slot, q->address, HI, 2); |
| SET_H_GR (q->regnum, value); |
| } |
| else |
| { |
| UHI value = CACHE_RETURN_DATA (cache, slot, q->address, UHI, 2); |
| SET_H_GR (q->regnum, value); |
| } |
| } |
| break; |
| case 4: |
| if (q->regtype == REGTYPE_FR) |
| { |
| SET_H_FR (q->regnum, |
| CACHE_RETURN_DATA (cache, slot, q->address, SF, 4)); |
| } |
| else |
| { |
| SET_H_GR (q->regnum, |
| CACHE_RETURN_DATA (cache, slot, q->address, SI, 4)); |
| } |
| break; |
| case 8: |
| if (q->regtype == REGTYPE_FR) |
| { |
| SET_H_FR_DOUBLE (q->regnum, |
| CACHE_RETURN_DATA (cache, slot, q->address, DF, 8)); |
| } |
| else |
| { |
| SET_H_GR_DOUBLE (q->regnum, |
| CACHE_RETURN_DATA (cache, slot, q->address, DI, 8)); |
| } |
| break; |
| case 16: |
| if (q->regtype == REGTYPE_FR) |
| frvbf_h_fr_quad_set_handler (current_cpu, q->regnum, |
| CACHE_RETURN_DATA_ADDRESS (cache, slot, |
| q->address, |
| 16)); |
| else |
| frvbf_h_gr_quad_set_handler (current_cpu, q->regnum, |
| CACHE_RETURN_DATA_ADDRESS (cache, slot, |
| q->address, |
| 16)); |
| break; |
| default: |
| abort (); |
| } |
| } |
| |
| static int |
| request_complete (SIM_CPU *cpu, CACHE_QUEUE_ELEMENT *q) |
| { |
| FRV_CACHE* cache; |
| if (! q->active || q->cycles > 0) |
| return 0; |
| |
| cache = CPU_DATA_CACHE (cpu); |
| switch (q->request) |
| { |
| case cache_load: |
| /* For loads, we must wait until the data is returned from the cache. */ |
| if (frv_cache_data_in_buffer (cache, 0, q->address, q->reqno)) |
| { |
| copy_load_data (cpu, cache, 0, q); |
| return 1; |
| } |
| if (frv_cache_data_in_buffer (cache, 1, q->address, q->reqno)) |
| { |
| copy_load_data (cpu, cache, 1, q); |
| return 1; |
| } |
| break; |
| |
| case cache_flush: |
| /* We must wait until the data is flushed. */ |
| if (frv_cache_data_flushed (cache, 0, q->address, q->reqno)) |
| return 1; |
| if (frv_cache_data_flushed (cache, 1, q->address, q->reqno)) |
| return 1; |
| break; |
| |
| default: |
| /* All other requests are complete once they've been made. */ |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* Run the insn and data caches through the given number of cycles, taking |
| note of load requests which are fullfilled as a result. */ |
| static void |
| run_caches (SIM_CPU *cpu, int cycles) |
| { |
| FRV_CACHE* data_cache = CPU_DATA_CACHE (cpu); |
| FRV_CACHE* insn_cache = CPU_INSN_CACHE (cpu); |
| int i; |
| /* For each cycle, run the caches, noting which requests have been fullfilled |
| and submitting new requests on their designated cycles. */ |
| for (i = 0; i < cycles; ++i) |
| { |
| int j; |
| /* Run the caches through 1 cycle. */ |
| frv_cache_run (data_cache, 1); |
| frv_cache_run (insn_cache, 1); |
| |
| /* Note whether prefetched insn data has been loaded yet. */ |
| for (j = LS; j < FRV_CACHE_PIPELINES; ++j) |
| { |
| if (frv_insn_fetch_buffer[j].reqno != NO_REQNO |
| && frv_cache_data_in_buffer (insn_cache, j, |
| frv_insn_fetch_buffer[j].address, |
| frv_insn_fetch_buffer[j].reqno)) |
| frv_insn_fetch_buffer[j].reqno = NO_REQNO; |
| } |
| |
| /* Check to see which requests have been satisfied and which should |
| be submitted now. */ |
| for (j = 0; j < cache_queue.ix; ++j) |
| { |
| CACHE_QUEUE_ELEMENT *q = & cache_queue.q[j]; |
| if (! q->active) |
| continue; |
| |
| /* If a load has been satisfied, complete the operation and remove it |
| from the queue. */ |
| if (request_complete (cpu, q)) |
| { |
| remove_cache_queue_element (cpu, j); |
| --j; |
| continue; |
| } |
| |
| /* Decrease the cycle count of each queued request. |
| Submit a request for each queued request whose cycle count has |
| become zero. */ |
| --q->cycles; |
| if (q->cycles == 0) |
| submit_cache_request (q); |
| } |
| } |
| } |
| |
| static void |
| apply_latency_adjustments (SIM_CPU *cpu) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int i; |
| /* update the latencies of the registers. */ |
| int *fr = ps->fr_busy; |
| int *acc = ps->acc_busy; |
| for (i = 0; i < 64; ++i) |
| { |
| if (ps->fr_busy_adjust[i] > 0) |
| *fr -= ps->fr_busy_adjust[i]; /* OK if it goes negative. */ |
| if (ps->acc_busy_adjust[i] > 0) |
| *acc -= ps->acc_busy_adjust[i]; /* OK if it goes negative. */ |
| ++fr; |
| ++acc; |
| } |
| } |
| |
| /* Account for the number of cycles which have just passed in the latency of |
| various system elements. Works for negative cycles too so that latency |
| can be extended in the case of insn fetch latency. |
| If negative or zero, then no adjustment is necessary. */ |
| static void |
| update_latencies (SIM_CPU *cpu, int cycles) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int i; |
| /* update the latencies of the registers. */ |
| int *fdiv; |
| int *fsqrt; |
| int *idiv; |
| int *flt; |
| int *media; |
| int *ccr; |
| int *gr = ps->gr_busy; |
| int *fr = ps->fr_busy; |
| int *acc = ps->acc_busy; |
| int *spr; |
| /* This loop handles GR, FR and ACC registers. */ |
| for (i = 0; i < 64; ++i) |
| { |
| if (*gr <= cycles) |
| { |
| *gr = 0; |
| reset_gr_flags (cpu, i); |
| } |
| else |
| *gr -= cycles; |
| /* If the busy drops to 0, then mark the register as |
| "not in use". */ |
| if (*fr <= cycles) |
| { |
| int *fr_lat = ps->fr_latency + i; |
| *fr = 0; |
| ps->fr_busy_adjust[i] = 0; |
| /* Only clear flags if this register has no target latency. */ |
| if (*fr_lat == 0) |
| reset_fr_flags (cpu, i); |
| } |
| else |
| *fr -= cycles; |
| /* If the busy drops to 0, then mark the register as |
| "not in use". */ |
| if (*acc <= cycles) |
| { |
| int *acc_lat = ps->acc_latency + i; |
| *acc = 0; |
| ps->acc_busy_adjust[i] = 0; |
| /* Only clear flags if this register has no target latency. */ |
| if (*acc_lat == 0) |
| reset_acc_flags (cpu, i); |
| } |
| else |
| *acc -= cycles; |
| ++gr; |
| ++fr; |
| ++acc; |
| } |
| /* This loop handles CCR registers. */ |
| ccr = ps->ccr_busy; |
| for (i = 0; i < 8; ++i) |
| { |
| if (*ccr <= cycles) |
| { |
| *ccr = 0; |
| reset_cc_flags (cpu, i); |
| } |
| else |
| *ccr -= cycles; |
| ++ccr; |
| } |
| /* This loop handles SPR registers. */ |
| spr = ps->spr_busy; |
| for (i = 0; i < 4096; ++i) |
| { |
| if (*spr <= cycles) |
| *spr = 0; |
| else |
| *spr -= cycles; |
| ++spr; |
| } |
| /* This loop handles resources. */ |
| idiv = ps->idiv_busy; |
| fdiv = ps->fdiv_busy; |
| fsqrt = ps->fsqrt_busy; |
| for (i = 0; i < 2; ++i) |
| { |
| *idiv = (*idiv <= cycles) ? 0 : (*idiv - cycles); |
| *fdiv = (*fdiv <= cycles) ? 0 : (*fdiv - cycles); |
| *fsqrt = (*fsqrt <= cycles) ? 0 : (*fsqrt - cycles); |
| ++idiv; |
| ++fdiv; |
| ++fsqrt; |
| } |
| /* Float and media units can occur in 4 slots on some machines. */ |
| flt = ps->float_busy; |
| media = ps->media_busy; |
| for (i = 0; i < 4; ++i) |
| { |
| *flt = (*flt <= cycles) ? 0 : (*flt - cycles); |
| *media = (*media <= cycles) ? 0 : (*media - cycles); |
| ++flt; |
| ++media; |
| } |
| } |
| |
| /* Print information about the wait for the given number of cycles. */ |
| void |
| frv_model_trace_wait_cycles (SIM_CPU *cpu, int cycles, const char *hazard_name) |
| { |
| if (TRACE_INSN_P (cpu) && cycles > 0) |
| { |
| SIM_DESC sd = CPU_STATE (cpu); |
| trace_printf (sd, cpu, "**** %s wait %d cycles ***\n", |
| hazard_name, cycles); |
| } |
| } |
| |
| void |
| trace_vliw_wait_cycles (SIM_CPU *cpu) |
| { |
| if (TRACE_INSN_P (cpu)) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| frv_model_trace_wait_cycles (cpu, ps->vliw_wait, hazard_name); |
| } |
| } |
| |
| /* Wait for the given number of cycles. */ |
| void |
| frv_model_advance_cycles (SIM_CPU *cpu, int cycles) |
| { |
| PROFILE_DATA *p = CPU_PROFILE_DATA (cpu); |
| update_latencies (cpu, cycles); |
| run_caches (cpu, cycles); |
| PROFILE_MODEL_TOTAL_CYCLES (p) += cycles; |
| } |
| |
| void |
| handle_resource_wait (SIM_CPU *cpu) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| if (ps->vliw_wait != 0) |
| frv_model_advance_cycles (cpu, ps->vliw_wait); |
| if (ps->vliw_load_stall > ps->vliw_wait) |
| ps->vliw_load_stall -= ps->vliw_wait; |
| else |
| ps->vliw_load_stall = 0; |
| } |
| |
| /* Account for the number of cycles until these resources will be available |
| again. */ |
| static void |
| update_target_latencies (SIM_CPU *cpu) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int i; |
| /* update the latencies of the registers. */ |
| int *ccr_lat; |
| int *gr_lat = ps->gr_latency; |
| int *fr_lat = ps->fr_latency; |
| int *acc_lat = ps->acc_latency; |
| int *spr_lat; |
| int *ccr; |
| int *gr = ps->gr_busy; |
| int *fr = ps->fr_busy; |
| int *acc = ps->acc_busy; |
| int *spr; |
| /* This loop handles GR, FR and ACC registers. */ |
| for (i = 0; i < 64; ++i) |
| { |
| if (*gr_lat) |
| { |
| *gr = *gr_lat; |
| *gr_lat = 0; |
| } |
| if (*fr_lat) |
| { |
| *fr = *fr_lat; |
| *fr_lat = 0; |
| } |
| if (*acc_lat) |
| { |
| *acc = *acc_lat; |
| *acc_lat = 0; |
| } |
| ++gr; ++gr_lat; |
| ++fr; ++fr_lat; |
| ++acc; ++acc_lat; |
| } |
| /* This loop handles CCR registers. */ |
| ccr = ps->ccr_busy; |
| ccr_lat = ps->ccr_latency; |
| for (i = 0; i < 8; ++i) |
| { |
| if (*ccr_lat) |
| { |
| *ccr = *ccr_lat; |
| *ccr_lat = 0; |
| } |
| ++ccr; ++ccr_lat; |
| } |
| /* This loop handles SPR registers. */ |
| spr = ps->spr_busy; |
| spr_lat = ps->spr_latency; |
| for (i = 0; i < 4096; ++i) |
| { |
| if (*spr_lat) |
| { |
| *spr = *spr_lat; |
| *spr_lat = 0; |
| } |
| ++spr; ++spr_lat; |
| } |
| } |
| |
| /* Run the caches until all pending cache flushes are complete. */ |
| static void |
| wait_for_flush (SIM_CPU *cpu) |
| { |
| SI address = CPU_LOAD_ADDRESS (cpu); |
| int wait = 0; |
| while (flush_pending_for_address (cpu, address)) |
| { |
| frv_model_advance_cycles (cpu, 1); |
| ++wait; |
| } |
| if (TRACE_INSN_P (cpu) && wait) |
| { |
| sprintf (hazard_name, "Data cache flush address %x:", address); |
| frv_model_trace_wait_cycles (cpu, wait, hazard_name); |
| } |
| } |
| |
| /* Initialize cycle counting for an insn. |
| FIRST_P is non-zero if this is the first insn in a set of parallel |
| insns. */ |
| void |
| frvbf_model_insn_before (SIM_CPU *cpu, int first_p) |
| { |
| SIM_DESC sd = CPU_STATE (cpu); |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| |
| ps->vliw_wait = 0; |
| ps->post_wait = 0; |
| memset (ps->fr_busy_adjust, 0, sizeof (ps->fr_busy_adjust)); |
| memset (ps->acc_busy_adjust, 0, sizeof (ps->acc_busy_adjust)); |
| |
| if (first_p) |
| { |
| ps->vliw_insns++; |
| ps->vliw_cycles = 0; |
| ps->vliw_branch_taken = 0; |
| ps->vliw_load_stall = 0; |
| } |
| |
| switch (STATE_ARCHITECTURE (sd)->mach) |
| { |
| case bfd_mach_fr400: |
| case bfd_mach_fr450: |
| fr400_model_insn_before (cpu, first_p); |
| break; |
| case bfd_mach_fr500: |
| fr500_model_insn_before (cpu, first_p); |
| break; |
| case bfd_mach_fr550: |
| fr550_model_insn_before (cpu, first_p); |
| break; |
| default: |
| break; |
| } |
| |
| if (first_p) |
| wait_for_flush (cpu); |
| } |
| |
| /* Record the cycles computed for an insn. |
| LAST_P is non-zero if this is the last insn in a set of parallel insns, |
| and we update the total cycle count. |
| CYCLES is the cycle count of the insn. */ |
| |
| void |
| frvbf_model_insn_after (SIM_CPU *cpu, int last_p, int cycles) |
| { |
| PROFILE_DATA *p = CPU_PROFILE_DATA (cpu); |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| SIM_DESC sd = CPU_STATE (cpu); |
| |
| PROFILE_MODEL_CUR_INSN_CYCLES (p) = cycles; |
| |
| /* The number of cycles for a VLIW insn is the maximum number of cycles |
| used by any individual insn within it. */ |
| if (cycles > ps->vliw_cycles) |
| ps->vliw_cycles = cycles; |
| |
| if (last_p) |
| { |
| /* This is the last insn in a VLIW insn. */ |
| struct frv_interrupt_timer *timer = & frv_interrupt_state.timer; |
| |
| activate_cache_requests (cpu); /* before advancing cycles. */ |
| apply_latency_adjustments (cpu); /* must go first. */ |
| update_target_latencies (cpu); /* must go next. */ |
| frv_model_advance_cycles (cpu, ps->vliw_cycles); |
| |
| PROFILE_MODEL_LOAD_STALL_CYCLES (p) += ps->vliw_load_stall; |
| |
| /* Check the interrupt timer. cycles contains the total cycle count. */ |
| if (timer->enabled) |
| { |
| cycles = PROFILE_MODEL_TOTAL_CYCLES (p); |
| if (timer->current % timer->value |
| + (cycles - timer->current) >= timer->value) |
| frv_queue_external_interrupt (cpu, timer->interrupt); |
| timer->current = cycles; |
| } |
| |
| ps->past_first_p = 0; /* Next one will be the first in a new VLIW. */ |
| ps->branch_address = -1; |
| } |
| else |
| ps->past_first_p = 1; |
| |
| switch (STATE_ARCHITECTURE (sd)->mach) |
| { |
| case bfd_mach_fr400: |
| case bfd_mach_fr450: |
| fr400_model_insn_after (cpu, last_p, cycles); |
| break; |
| case bfd_mach_fr500: |
| fr500_model_insn_after (cpu, last_p, cycles); |
| break; |
| case bfd_mach_fr550: |
| fr550_model_insn_after (cpu, last_p, cycles); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void |
| frvbf_model_branch (SIM_CPU *current_cpu, PCADDR target, int hint) |
| { |
| /* Record the hint and branch address for use in profiling. */ |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (current_cpu); |
| ps->branch_hint = hint; |
| ps->branch_address = target; |
| } |
| |
| /* Top up the latency of the given GR by the given number of cycles. */ |
| void |
| update_GR_latency (SIM_CPU *cpu, INT out_GR, int cycles) |
| { |
| if (out_GR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *gr = ps->gr_latency; |
| if (gr[out_GR] < cycles) |
| gr[out_GR] = cycles; |
| } |
| } |
| |
| void |
| decrease_GR_busy (SIM_CPU *cpu, INT in_GR, int cycles) |
| { |
| if (in_GR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *gr = ps->gr_busy; |
| gr[in_GR] -= cycles; |
| } |
| } |
| |
| /* Top up the latency of the given double GR by the number of cycles. */ |
| void |
| update_GRdouble_latency (SIM_CPU *cpu, INT out_GR, int cycles) |
| { |
| if (out_GR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *gr = ps->gr_latency; |
| if (gr[out_GR] < cycles) |
| gr[out_GR] = cycles; |
| if (out_GR < 63 && gr[out_GR + 1] < cycles) |
| gr[out_GR + 1] = cycles; |
| } |
| } |
| |
| void |
| update_GR_latency_for_load (SIM_CPU *cpu, INT out_GR, int cycles) |
| { |
| if (out_GR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *gr = ps->gr_latency; |
| |
| /* The latency of the GR will be at least the number of cycles used |
| by the insn. */ |
| if (gr[out_GR] < cycles) |
| gr[out_GR] = cycles; |
| |
| /* The latency will also depend on how long it takes to retrieve the |
| data from the cache or memory. Assume that the load is issued |
| after the last cycle of the insn. */ |
| request_cache_load (cpu, out_GR, REGTYPE_NONE, cycles); |
| } |
| } |
| |
| void |
| update_GRdouble_latency_for_load (SIM_CPU *cpu, INT out_GR, int cycles) |
| { |
| if (out_GR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *gr = ps->gr_latency; |
| |
| /* The latency of the GR will be at least the number of cycles used |
| by the insn. */ |
| if (gr[out_GR] < cycles) |
| gr[out_GR] = cycles; |
| if (out_GR < 63 && gr[out_GR + 1] < cycles) |
| gr[out_GR + 1] = cycles; |
| |
| /* The latency will also depend on how long it takes to retrieve the |
| data from the cache or memory. Assume that the load is issued |
| after the last cycle of the insn. */ |
| request_cache_load (cpu, out_GR, REGTYPE_NONE, cycles); |
| } |
| } |
| |
| void |
| update_GR_latency_for_swap (SIM_CPU *cpu, INT out_GR, int cycles) |
| { |
| update_GR_latency_for_load (cpu, out_GR, cycles); |
| } |
| |
| /* Top up the latency of the given FR by the given number of cycles. */ |
| void |
| update_FR_latency (SIM_CPU *cpu, INT out_FR, int cycles) |
| { |
| if (out_FR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *fr = ps->fr_latency; |
| if (fr[out_FR] < cycles) |
| fr[out_FR] = cycles; |
| } |
| } |
| |
| /* Top up the latency of the given double FR by the number of cycles. */ |
| void |
| update_FRdouble_latency (SIM_CPU *cpu, INT out_FR, int cycles) |
| { |
| if (out_FR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *fr = ps->fr_latency; |
| if (fr[out_FR] < cycles) |
| fr[out_FR] = cycles; |
| if (out_FR < 63 && fr[out_FR + 1] < cycles) |
| fr[out_FR + 1] = cycles; |
| } |
| } |
| |
| void |
| update_FR_latency_for_load (SIM_CPU *cpu, INT out_FR, int cycles) |
| { |
| if (out_FR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *fr = ps->fr_latency; |
| |
| /* The latency of the FR will be at least the number of cycles used |
| by the insn. */ |
| if (fr[out_FR] < cycles) |
| fr[out_FR] = cycles; |
| |
| /* The latency will also depend on how long it takes to retrieve the |
| data from the cache or memory. Assume that the load is issued |
| after the last cycle of the insn. */ |
| request_cache_load (cpu, out_FR, REGTYPE_FR, cycles); |
| } |
| } |
| |
| void |
| update_FRdouble_latency_for_load (SIM_CPU *cpu, INT out_FR, int cycles) |
| { |
| if (out_FR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *fr = ps->fr_latency; |
| |
| /* The latency of the FR will be at least the number of cycles used |
| by the insn. */ |
| if (fr[out_FR] < cycles) |
| fr[out_FR] = cycles; |
| if (out_FR < 63 && fr[out_FR + 1] < cycles) |
| fr[out_FR + 1] = cycles; |
| |
| /* The latency will also depend on how long it takes to retrieve the |
| data from the cache or memory. Assume that the load is issued |
| after the last cycle of the insn. */ |
| request_cache_load (cpu, out_FR, REGTYPE_FR, cycles); |
| } |
| } |
| |
| /* Top up the post-processing time of the given FR by the given number of |
| cycles. */ |
| void |
| update_FR_ptime (SIM_CPU *cpu, INT out_FR, int cycles) |
| { |
| if (out_FR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| /* If a load is pending on this register, then add the cycles to |
| the post processing time for this register. Otherwise apply it |
| directly to the latency of the register. */ |
| if (! load_pending_for_register (cpu, out_FR, 1, REGTYPE_FR)) |
| { |
| int *fr = ps->fr_latency; |
| fr[out_FR] += cycles; |
| } |
| else |
| ps->fr_ptime[out_FR] += cycles; |
| } |
| } |
| |
| void |
| update_FRdouble_ptime (SIM_CPU *cpu, INT out_FR, int cycles) |
| { |
| if (out_FR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| /* If a load is pending on this register, then add the cycles to |
| the post processing time for this register. Otherwise apply it |
| directly to the latency of the register. */ |
| if (! load_pending_for_register (cpu, out_FR, 2, REGTYPE_FR)) |
| { |
| int *fr = ps->fr_latency; |
| fr[out_FR] += cycles; |
| if (out_FR < 63) |
| fr[out_FR + 1] += cycles; |
| } |
| else |
| { |
| ps->fr_ptime[out_FR] += cycles; |
| if (out_FR < 63) |
| ps->fr_ptime[out_FR + 1] += cycles; |
| } |
| } |
| } |
| |
| /* Top up the post-processing time of the given ACC by the given number of |
| cycles. */ |
| void |
| update_ACC_ptime (SIM_CPU *cpu, INT out_ACC, int cycles) |
| { |
| if (out_ACC >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| /* No load can be pending on this register. Apply the cycles |
| directly to the latency of the register. */ |
| int *acc = ps->acc_latency; |
| acc[out_ACC] += cycles; |
| } |
| } |
| |
| /* Top up the post-processing time of the given SPR by the given number of |
| cycles. */ |
| void |
| update_SPR_ptime (SIM_CPU *cpu, INT out_SPR, int cycles) |
| { |
| if (out_SPR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| /* No load can be pending on this register. Apply the cycles |
| directly to the latency of the register. */ |
| int *spr = ps->spr_latency; |
| spr[out_SPR] += cycles; |
| } |
| } |
| |
| void |
| decrease_ACC_busy (SIM_CPU *cpu, INT out_ACC, int cycles) |
| { |
| if (out_ACC >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *acc = ps->acc_busy; |
| acc[out_ACC] -= cycles; |
| if (ps->acc_busy_adjust[out_ACC] >= 0 |
| && cycles > ps->acc_busy_adjust[out_ACC]) |
| ps->acc_busy_adjust[out_ACC] = cycles; |
| } |
| } |
| |
| void |
| increase_ACC_busy (SIM_CPU *cpu, INT out_ACC, int cycles) |
| { |
| if (out_ACC >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *acc = ps->acc_busy; |
| acc[out_ACC] += cycles; |
| } |
| } |
| |
| void |
| enforce_full_acc_latency (SIM_CPU *cpu, INT in_ACC) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| ps->acc_busy_adjust [in_ACC] = -1; |
| } |
| |
| void |
| decrease_FR_busy (SIM_CPU *cpu, INT out_FR, int cycles) |
| { |
| if (out_FR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *fr = ps->fr_busy; |
| fr[out_FR] -= cycles; |
| if (ps->fr_busy_adjust[out_FR] >= 0 |
| && cycles > ps->fr_busy_adjust[out_FR]) |
| ps->fr_busy_adjust[out_FR] = cycles; |
| } |
| } |
| |
| void |
| increase_FR_busy (SIM_CPU *cpu, INT out_FR, int cycles) |
| { |
| if (out_FR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *fr = ps->fr_busy; |
| fr[out_FR] += cycles; |
| } |
| } |
| |
| /* Top up the latency of the given ACC by the given number of cycles. */ |
| void |
| update_ACC_latency (SIM_CPU *cpu, INT out_ACC, int cycles) |
| { |
| if (out_ACC >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *acc = ps->acc_latency; |
| if (acc[out_ACC] < cycles) |
| acc[out_ACC] = cycles; |
| } |
| } |
| |
| /* Top up the latency of the given CCR by the given number of cycles. */ |
| void |
| update_CCR_latency (SIM_CPU *cpu, INT out_CCR, int cycles) |
| { |
| if (out_CCR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *ccr = ps->ccr_latency; |
| if (ccr[out_CCR] < cycles) |
| ccr[out_CCR] = cycles; |
| } |
| } |
| |
| /* Top up the latency of the given SPR by the given number of cycles. */ |
| void |
| update_SPR_latency (SIM_CPU *cpu, INT out_SPR, int cycles) |
| { |
| if (out_SPR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *spr = ps->spr_latency; |
| if (spr[out_SPR] < cycles) |
| spr[out_SPR] = cycles; |
| } |
| } |
| |
| /* Top up the latency of the given integer division resource by the given |
| number of cycles. */ |
| void |
| update_idiv_resource_latency (SIM_CPU *cpu, INT in_resource, int cycles) |
| { |
| /* operate directly on the busy cycles since each resource can only |
| be used once in a VLIW insn. */ |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *r = ps->idiv_busy; |
| r[in_resource] = cycles; |
| } |
| |
| /* Set the latency of the given resource to the given number of cycles. */ |
| void |
| update_fdiv_resource_latency (SIM_CPU *cpu, INT in_resource, int cycles) |
| { |
| /* operate directly on the busy cycles since each resource can only |
| be used once in a VLIW insn. */ |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *r = ps->fdiv_busy; |
| r[in_resource] = cycles; |
| } |
| |
| /* Set the latency of the given resource to the given number of cycles. */ |
| void |
| update_fsqrt_resource_latency (SIM_CPU *cpu, INT in_resource, int cycles) |
| { |
| /* operate directly on the busy cycles since each resource can only |
| be used once in a VLIW insn. */ |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *r = ps->fsqrt_busy; |
| r[in_resource] = cycles; |
| } |
| |
| /* Set the latency of the given resource to the given number of cycles. */ |
| void |
| update_float_resource_latency (SIM_CPU *cpu, INT in_resource, int cycles) |
| { |
| /* operate directly on the busy cycles since each resource can only |
| be used once in a VLIW insn. */ |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *r = ps->float_busy; |
| r[in_resource] = cycles; |
| } |
| |
| void |
| update_media_resource_latency (SIM_CPU *cpu, INT in_resource, int cycles) |
| { |
| /* operate directly on the busy cycles since each resource can only |
| be used once in a VLIW insn. */ |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *r = ps->media_busy; |
| r[in_resource] = cycles; |
| } |
| |
| /* Set the branch penalty to the given number of cycles. */ |
| void |
| update_branch_penalty (SIM_CPU *cpu, int cycles) |
| { |
| /* operate directly on the busy cycles since only one branch can occur |
| in a VLIW insn. */ |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| ps->branch_penalty = cycles; |
| } |
| |
| /* Check the availability of the given GR register and update the number |
| of cycles the current VLIW insn must wait until it is available. */ |
| void |
| vliw_wait_for_GR (SIM_CPU *cpu, INT in_GR) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *gr = ps->gr_busy; |
| /* If the latency of the register is greater than the current wait |
| then update the current wait. */ |
| if (in_GR >= 0 && gr[in_GR] > ps->vliw_wait) |
| { |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for gr%d:", in_GR); |
| ps->vliw_wait = gr[in_GR]; |
| } |
| } |
| |
| /* Check the availability of the given GR register and update the number |
| of cycles the current VLIW insn must wait until it is available. */ |
| void |
| vliw_wait_for_GRdouble (SIM_CPU *cpu, INT in_GR) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *gr = ps->gr_busy; |
| /* If the latency of the register is greater than the current wait |
| then update the current wait. */ |
| if (in_GR >= 0) |
| { |
| if (gr[in_GR] > ps->vliw_wait) |
| { |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for gr%d:", in_GR); |
| ps->vliw_wait = gr[in_GR]; |
| } |
| if (in_GR < 63 && gr[in_GR + 1] > ps->vliw_wait) |
| { |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for gr%d:", in_GR + 1); |
| ps->vliw_wait = gr[in_GR + 1]; |
| } |
| } |
| } |
| |
| /* Check the availability of the given FR register and update the number |
| of cycles the current VLIW insn must wait until it is available. */ |
| void |
| vliw_wait_for_FR (SIM_CPU *cpu, INT in_FR) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *fr = ps->fr_busy; |
| /* If the latency of the register is greater than the current wait |
| then update the current wait. */ |
| if (in_FR >= 0 && fr[in_FR] > ps->vliw_wait) |
| { |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for fr%d:", in_FR); |
| ps->vliw_wait = fr[in_FR]; |
| } |
| } |
| |
| /* Check the availability of the given GR register and update the number |
| of cycles the current VLIW insn must wait until it is available. */ |
| void |
| vliw_wait_for_FRdouble (SIM_CPU *cpu, INT in_FR) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *fr = ps->fr_busy; |
| /* If the latency of the register is greater than the current wait |
| then update the current wait. */ |
| if (in_FR >= 0) |
| { |
| if (fr[in_FR] > ps->vliw_wait) |
| { |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for fr%d:", in_FR); |
| ps->vliw_wait = fr[in_FR]; |
| } |
| if (in_FR < 63 && fr[in_FR + 1] > ps->vliw_wait) |
| { |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for fr%d:", in_FR + 1); |
| ps->vliw_wait = fr[in_FR + 1]; |
| } |
| } |
| } |
| |
| /* Check the availability of the given CCR register and update the number |
| of cycles the current VLIW insn must wait until it is available. */ |
| void |
| vliw_wait_for_CCR (SIM_CPU *cpu, INT in_CCR) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *ccr = ps->ccr_busy; |
| /* If the latency of the register is greater than the current wait |
| then update the current wait. */ |
| if (in_CCR >= 0 && ccr[in_CCR] > ps->vliw_wait) |
| { |
| if (TRACE_INSN_P (cpu)) |
| { |
| if (in_CCR > 3) |
| sprintf (hazard_name, "Data hazard for icc%d:", in_CCR-4); |
| else |
| sprintf (hazard_name, "Data hazard for fcc%d:", in_CCR); |
| } |
| ps->vliw_wait = ccr[in_CCR]; |
| } |
| } |
| |
| /* Check the availability of the given ACC register and update the number |
| of cycles the current VLIW insn must wait until it is available. */ |
| void |
| vliw_wait_for_ACC (SIM_CPU *cpu, INT in_ACC) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *acc = ps->acc_busy; |
| /* If the latency of the register is greater than the current wait |
| then update the current wait. */ |
| if (in_ACC >= 0 && acc[in_ACC] > ps->vliw_wait) |
| { |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for acc%d:", in_ACC); |
| ps->vliw_wait = acc[in_ACC]; |
| } |
| } |
| |
| /* Check the availability of the given SPR register and update the number |
| of cycles the current VLIW insn must wait until it is available. */ |
| void |
| vliw_wait_for_SPR (SIM_CPU *cpu, INT in_SPR) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *spr = ps->spr_busy; |
| /* If the latency of the register is greater than the current wait |
| then update the current wait. */ |
| if (in_SPR >= 0 && spr[in_SPR] > ps->vliw_wait) |
| { |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for spr %d:", in_SPR); |
| ps->vliw_wait = spr[in_SPR]; |
| } |
| } |
| |
| /* Check the availability of the given integer division resource and update |
| the number of cycles the current VLIW insn must wait until it is available. |
| */ |
| void |
| vliw_wait_for_idiv_resource (SIM_CPU *cpu, INT in_resource) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *r = ps->idiv_busy; |
| /* If the latency of the resource is greater than the current wait |
| then update the current wait. */ |
| if (r[in_resource] > ps->vliw_wait) |
| { |
| if (TRACE_INSN_P (cpu)) |
| { |
| sprintf (hazard_name, "Resource hazard for integer division in slot I%d:", in_resource); |
| } |
| ps->vliw_wait = r[in_resource]; |
| } |
| } |
| |
| /* Check the availability of the given float division resource and update |
| the number of cycles the current VLIW insn must wait until it is available. |
| */ |
| void |
| vliw_wait_for_fdiv_resource (SIM_CPU *cpu, INT in_resource) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *r = ps->fdiv_busy; |
| /* If the latency of the resource is greater than the current wait |
| then update the current wait. */ |
| if (r[in_resource] > ps->vliw_wait) |
| { |
| if (TRACE_INSN_P (cpu)) |
| { |
| sprintf (hazard_name, "Resource hazard for floating point division in slot F%d:", in_resource); |
| } |
| ps->vliw_wait = r[in_resource]; |
| } |
| } |
| |
| /* Check the availability of the given float square root resource and update |
| the number of cycles the current VLIW insn must wait until it is available. |
| */ |
| void |
| vliw_wait_for_fsqrt_resource (SIM_CPU *cpu, INT in_resource) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *r = ps->fsqrt_busy; |
| /* If the latency of the resource is greater than the current wait |
| then update the current wait. */ |
| if (r[in_resource] > ps->vliw_wait) |
| { |
| if (TRACE_INSN_P (cpu)) |
| { |
| sprintf (hazard_name, "Resource hazard for square root in slot F%d:", in_resource); |
| } |
| ps->vliw_wait = r[in_resource]; |
| } |
| } |
| |
| /* Check the availability of the given float unit resource and update |
| the number of cycles the current VLIW insn must wait until it is available. |
| */ |
| void |
| vliw_wait_for_float_resource (SIM_CPU *cpu, INT in_resource) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *r = ps->float_busy; |
| /* If the latency of the resource is greater than the current wait |
| then update the current wait. */ |
| if (r[in_resource] > ps->vliw_wait) |
| { |
| if (TRACE_INSN_P (cpu)) |
| { |
| sprintf (hazard_name, "Resource hazard for floating point unit in slot F%d:", in_resource); |
| } |
| ps->vliw_wait = r[in_resource]; |
| } |
| } |
| |
| /* Check the availability of the given media unit resource and update |
| the number of cycles the current VLIW insn must wait until it is available. |
| */ |
| void |
| vliw_wait_for_media_resource (SIM_CPU *cpu, INT in_resource) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *r = ps->media_busy; |
| /* If the latency of the resource is greater than the current wait |
| then update the current wait. */ |
| if (r[in_resource] > ps->vliw_wait) |
| { |
| if (TRACE_INSN_P (cpu)) |
| { |
| sprintf (hazard_name, "Resource hazard for media unit in slot M%d:", in_resource); |
| } |
| ps->vliw_wait = r[in_resource]; |
| } |
| } |
| |
| /* Run the caches until all requests for the given register(s) are satisfied. */ |
| void |
| load_wait_for_GR (SIM_CPU *cpu, INT in_GR) |
| { |
| if (in_GR >= 0) |
| { |
| int wait = 0; |
| while (load_pending_for_register (cpu, in_GR, 1/*words*/, REGTYPE_NONE)) |
| { |
| frv_model_advance_cycles (cpu, 1); |
| ++wait; |
| } |
| if (wait) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| ps->vliw_wait += wait; |
| ps->vliw_load_stall += wait; |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for gr%d:", in_GR); |
| } |
| } |
| } |
| |
| void |
| load_wait_for_FR (SIM_CPU *cpu, INT in_FR) |
| { |
| if (in_FR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *fr; |
| int wait = 0; |
| while (load_pending_for_register (cpu, in_FR, 1/*words*/, REGTYPE_FR)) |
| { |
| frv_model_advance_cycles (cpu, 1); |
| ++wait; |
| } |
| /* Post processing time may have been added to the register's |
| latency after the loads were processed. Account for that too. |
| */ |
| fr = ps->fr_busy; |
| if (fr[in_FR]) |
| { |
| wait += fr[in_FR]; |
| frv_model_advance_cycles (cpu, fr[in_FR]); |
| } |
| /* Update the vliw_wait with the number of cycles we waited for the |
| load and any post-processing. */ |
| if (wait) |
| { |
| ps->vliw_wait += wait; |
| ps->vliw_load_stall += wait; |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for fr%d:", in_FR); |
| } |
| } |
| } |
| |
| void |
| load_wait_for_GRdouble (SIM_CPU *cpu, INT in_GR) |
| { |
| if (in_GR >= 0) |
| { |
| int wait = 0; |
| while (load_pending_for_register (cpu, in_GR, 2/*words*/, REGTYPE_NONE)) |
| { |
| frv_model_advance_cycles (cpu, 1); |
| ++wait; |
| } |
| if (wait) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| ps->vliw_wait += wait; |
| ps->vliw_load_stall += wait; |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for gr%d:", in_GR); |
| } |
| } |
| } |
| |
| void |
| load_wait_for_FRdouble (SIM_CPU *cpu, INT in_FR) |
| { |
| if (in_FR >= 0) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *fr; |
| int wait = 0; |
| while (load_pending_for_register (cpu, in_FR, 2/*words*/, REGTYPE_FR)) |
| { |
| frv_model_advance_cycles (cpu, 1); |
| ++wait; |
| } |
| /* Post processing time may have been added to the registers' |
| latencies after the loads were processed. Account for that too. |
| */ |
| fr = ps->fr_busy; |
| if (fr[in_FR]) |
| { |
| wait += fr[in_FR]; |
| frv_model_advance_cycles (cpu, fr[in_FR]); |
| } |
| if (in_FR < 63) |
| { |
| if (fr[in_FR + 1]) |
| { |
| wait += fr[in_FR + 1]; |
| frv_model_advance_cycles (cpu, fr[in_FR + 1]); |
| } |
| } |
| /* Update the vliw_wait with the number of cycles we waited for the |
| load and any post-processing. */ |
| if (wait) |
| { |
| ps->vliw_wait += wait; |
| ps->vliw_load_stall += wait; |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for fr%d:", in_FR); |
| } |
| } |
| } |
| |
| void |
| enforce_full_fr_latency (SIM_CPU *cpu, INT in_FR) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| ps->fr_busy_adjust [in_FR] = -1; |
| } |
| |
| /* Calculate how long the post processing for a floating point insn must |
| wait for resources to become available. */ |
| void |
| post_wait_for_FR (SIM_CPU *cpu, INT in_FR) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *fr = ps->fr_busy; |
| |
| if (in_FR >= 0 && fr[in_FR] > ps->post_wait) |
| { |
| ps->post_wait = fr[in_FR]; |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for fr%d:", in_FR); |
| } |
| } |
| |
| /* Calculate how long the post processing for a floating point insn must |
| wait for resources to become available. */ |
| void |
| post_wait_for_FRdouble (SIM_CPU *cpu, INT in_FR) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *fr = ps->fr_busy; |
| |
| if (in_FR >= 0) |
| { |
| if (fr[in_FR] > ps->post_wait) |
| { |
| ps->post_wait = fr[in_FR]; |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for fr%d:", in_FR); |
| } |
| if (in_FR < 63 && fr[in_FR + 1] > ps->post_wait) |
| { |
| ps->post_wait = fr[in_FR + 1]; |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for fr%d:", in_FR + 1); |
| } |
| } |
| } |
| |
| void |
| post_wait_for_ACC (SIM_CPU *cpu, INT in_ACC) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *acc = ps->acc_busy; |
| |
| if (in_ACC >= 0 && acc[in_ACC] > ps->post_wait) |
| { |
| ps->post_wait = acc[in_ACC]; |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for acc%d:", in_ACC); |
| } |
| } |
| |
| void |
| post_wait_for_CCR (SIM_CPU *cpu, INT in_CCR) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *ccr = ps->ccr_busy; |
| |
| if (in_CCR >= 0 && ccr[in_CCR] > ps->post_wait) |
| { |
| ps->post_wait = ccr[in_CCR]; |
| if (TRACE_INSN_P (cpu)) |
| { |
| if (in_CCR > 3) |
| sprintf (hazard_name, "Data hazard for icc%d:", in_CCR - 4); |
| else |
| sprintf (hazard_name, "Data hazard for fcc%d:", in_CCR); |
| } |
| } |
| } |
| |
| void |
| post_wait_for_SPR (SIM_CPU *cpu, INT in_SPR) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *spr = ps->spr_busy; |
| |
| if (in_SPR >= 0 && spr[in_SPR] > ps->post_wait) |
| { |
| ps->post_wait = spr[in_SPR]; |
| if (TRACE_INSN_P (cpu)) |
| sprintf (hazard_name, "Data hazard for spr[%d]:", in_SPR); |
| } |
| } |
| |
| void |
| post_wait_for_fdiv (SIM_CPU *cpu, INT slot) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *fdiv = ps->fdiv_busy; |
| |
| /* Multiple floating point divisions in the same slot need only wait 1 |
| extra cycle. */ |
| if (fdiv[slot] > 0 && 1 > ps->post_wait) |
| { |
| ps->post_wait = 1; |
| if (TRACE_INSN_P (cpu)) |
| { |
| sprintf (hazard_name, "Resource hazard for floating point division in slot F%d:", slot); |
| } |
| } |
| } |
| |
| void |
| post_wait_for_fsqrt (SIM_CPU *cpu, INT slot) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *fsqrt = ps->fsqrt_busy; |
| |
| /* Multiple floating point square roots in the same slot need only wait 1 |
| extra cycle. */ |
| if (fsqrt[slot] > 0 && 1 > ps->post_wait) |
| { |
| ps->post_wait = 1; |
| if (TRACE_INSN_P (cpu)) |
| { |
| sprintf (hazard_name, "Resource hazard for square root in slot F%d:", slot); |
| } |
| } |
| } |
| |
| void |
| post_wait_for_float (SIM_CPU *cpu, INT slot) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *flt = ps->float_busy; |
| |
| /* Multiple floating point square roots in the same slot need only wait 1 |
| extra cycle. */ |
| if (flt[slot] > ps->post_wait) |
| { |
| ps->post_wait = flt[slot]; |
| if (TRACE_INSN_P (cpu)) |
| { |
| sprintf (hazard_name, "Resource hazard for floating point unit in slot F%d:", slot); |
| } |
| } |
| } |
| |
| void |
| post_wait_for_media (SIM_CPU *cpu, INT slot) |
| { |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| int *media = ps->media_busy; |
| |
| /* Multiple floating point square roots in the same slot need only wait 1 |
| extra cycle. */ |
| if (media[slot] > ps->post_wait) |
| { |
| ps->post_wait = media[slot]; |
| if (TRACE_INSN_P (cpu)) |
| { |
| sprintf (hazard_name, "Resource hazard for media unit in slot M%d:", slot); |
| } |
| } |
| } |
| |
| /* Print cpu-specific profile information. */ |
| #define COMMAS(n) sim_add_commas (comma_buf, sizeof (comma_buf), (n)) |
| |
| static void |
| print_cache (SIM_CPU *cpu, FRV_CACHE *cache, const char *cache_name) |
| { |
| SIM_DESC sd = CPU_STATE (cpu); |
| |
| if (cache != NULL) |
| { |
| char comma_buf[20]; |
| unsigned accesses; |
| |
| sim_io_printf (sd, " %s Cache\n\n", cache_name); |
| accesses = cache->statistics.accesses; |
| sim_io_printf (sd, " Total accesses: %s\n", COMMAS (accesses)); |
| if (accesses != 0) |
| { |
| float rate; |
| unsigned hits = cache->statistics.hits; |
| sim_io_printf (sd, " Hits: %s\n", COMMAS (hits)); |
| rate = (float)hits / accesses; |
| sim_io_printf (sd, " Hit rate: %.2f%%\n", rate * 100); |
| } |
| } |
| else |
| sim_io_printf (sd, " Model %s has no %s cache\n", |
| MODEL_NAME (CPU_MODEL (cpu)), cache_name); |
| |
| sim_io_printf (sd, "\n"); |
| } |
| |
| /* This table must correspond to the UNIT_ATTR table in |
| opcodes/frv-desc.h. Only the units up to UNIT_C need be |
| listed since the others cannot occur after mapping. */ |
| static char * |
| slot_names[] = |
| { |
| "none", |
| "I0", "I1", "I01", "I2", "I3", "IALL", |
| "FM0", "FM1", "FM01", "FM2", "FM3", "FMALL", "FMLOW", |
| "B0", "B1", "B01", |
| "C" |
| }; |
| |
| static void |
| print_parallel (SIM_CPU *cpu, int verbose) |
| { |
| SIM_DESC sd = CPU_STATE (cpu); |
| PROFILE_DATA *p = CPU_PROFILE_DATA (cpu); |
| FRV_PROFILE_STATE *ps = CPU_PROFILE_STATE (cpu); |
| unsigned total, vliw; |
| char comma_buf[20]; |
| float average; |
| |
| sim_io_printf (sd, "Model %s Parallelization\n\n", |
| MODEL_NAME (CPU_MODEL (cpu))); |
| |
| total = PROFILE_TOTAL_INSN_COUNT (p); |
| sim_io_printf (sd, " Total instructions: %s\n", COMMAS (total)); |
| vliw = ps->vliw_insns; |
| sim_io_printf (sd, " VLIW instructions: %s\n", COMMAS (vliw)); |
| average = (float)total / vliw; |
| sim_io_printf (sd, " Average VLIW length: %.2f\n", average); |
| average = (float)PROFILE_MODEL_TOTAL_CYCLES (p) / vliw; |
| sim_io_printf (sd, " Cycles per VLIW instruction: %.2f\n", average); |
| average = (float)total / PROFILE_MODEL_TOTAL_CYCLES (p); |
| sim_io_printf (sd, " Instructions per cycle: %.2f\n", average); |
| |
| if (verbose) |
| { |
| int i; |
| int max_val = 0; |
| int max_name_len = 0; |
| for (i = UNIT_NIL + 1; i < UNIT_NUM_UNITS; ++i) |
| { |
| if (INSNS_IN_SLOT (i)) |
| { |
| int len; |
| if (INSNS_IN_SLOT (i) > max_val) |
| max_val = INSNS_IN_SLOT (i); |
| len = strlen (slot_names[i]); |
| if (len > max_name_len) |
| max_name_len = len; |
| } |
| } |
| if (max_val > 0) |
| { |
| sim_io_printf (sd, "\n"); |
| sim_io_printf (sd, " Instructions per slot:\n"); |
| sim_io_printf (sd, "\n"); |
| for (i = UNIT_NIL + 1; i < UNIT_NUM_UNITS; ++i) |
| { |
| if (INSNS_IN_SLOT (i) != 0) |
| { |
| sim_io_printf (sd, " %*s: %*s: ", |
| max_name_len, slot_names[i], |
| max_val < 10000 ? 5 : 10, |
| COMMAS (INSNS_IN_SLOT (i))); |
| sim_profile_print_bar (sd, cpu, PROFILE_HISTOGRAM_WIDTH, |
| INSNS_IN_SLOT (i), |
| max_val); |
| sim_io_printf (sd, "\n"); |
| } |
| } |
| } /* details to print */ |
| } /* verbose */ |
| |
| sim_io_printf (sd, "\n"); |
| } |
| |
| void |
| frv_profile_info (SIM_CPU *cpu, int verbose) |
| { |
| /* FIXME: Need to add smp support. */ |
| PROFILE_DATA *p = CPU_PROFILE_DATA (cpu); |
| |
| #if WITH_PROFILE_PARALLEL_P |
| if (PROFILE_FLAGS (p) [PROFILE_PARALLEL_IDX]) |
| print_parallel (cpu, verbose); |
| #endif |
| |
| #if WITH_PROFILE_CACHE_P |
| if (PROFILE_FLAGS (p) [PROFILE_CACHE_IDX]) |
| { |
| SIM_DESC sd = CPU_STATE (cpu); |
| sim_io_printf (sd, "Model %s Cache Statistics\n\n", |
| MODEL_NAME (CPU_MODEL (cpu))); |
| print_cache (cpu, CPU_INSN_CACHE (cpu), "Instruction"); |
| print_cache (cpu, CPU_DATA_CACHE (cpu), "Data"); |
| } |
| #endif /* WITH_PROFILE_CACHE_P */ |
| } |
| |
| /* A hack to get registers referenced for profiling. */ |
| SI frv_ref_SI (SI ref) {return ref;} |
| #endif /* WITH_PROFILE_MODEL_P */ |