mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-15 04:31:49 +08:00
cc67f780ec
The verbose argument has always been an int treated as a bool, so convert it to an explicit bool. Further, update the API docs to match the reality that the verbose value is actually used by some of the internal modules.
2085 lines
54 KiB
C
2085 lines
54 KiB
C
/* 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 */
|