| /* frv simulator machine independent profiling code. | |
| Copyright (C) 1998-2023 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, bool 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, bool 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 */ |