diff options
Diffstat (limited to 'sim/frv/cache.c')
-rw-r--r-- | sim/frv/cache.c | 1669 |
1 files changed, 0 insertions, 1669 deletions
diff --git a/sim/frv/cache.c b/sim/frv/cache.c deleted file mode 100644 index 0605682..0000000 --- a/sim/frv/cache.c +++ /dev/null @@ -1,1669 +0,0 @@ -/* frv cache model. - Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation, Inc. - Contributed by Red Hat. - -This file is part of the GNU simulators. - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - -#define WANT_CPU frvbf -#define WANT_CPU_FRVBF - -#include "libiberty.h" -#include "sim-main.h" -#include "cache.h" -#include "bfd.h" - -void -frv_cache_init (SIM_CPU *cpu, FRV_CACHE *cache) -{ - int elements; - int i, j; - SIM_DESC sd; - - /* Set defaults for fields which are not initialized. */ - sd = CPU_STATE (cpu); - switch (STATE_ARCHITECTURE (sd)->mach) - { - case bfd_mach_fr400: - case bfd_mach_fr450: - if (cache->configured_sets == 0) - cache->configured_sets = 512; - if (cache->configured_ways == 0) - cache->configured_ways = 2; - if (cache->line_size == 0) - cache->line_size = 32; - if (cache->memory_latency == 0) - cache->memory_latency = 20; - break; - case bfd_mach_fr550: - if (cache->configured_sets == 0) - cache->configured_sets = 128; - if (cache->configured_ways == 0) - cache->configured_ways = 4; - if (cache->line_size == 0) - cache->line_size = 64; - if (cache->memory_latency == 0) - cache->memory_latency = 20; - break; - default: - if (cache->configured_sets == 0) - cache->configured_sets = 64; - if (cache->configured_ways == 0) - cache->configured_ways = 4; - if (cache->line_size == 0) - cache->line_size = 64; - if (cache->memory_latency == 0) - cache->memory_latency = 20; - break; - } - - frv_cache_reconfigure (cpu, cache); - - /* First allocate the cache storage based on the given dimensions. */ - elements = cache->sets * cache->ways; - cache->tag_storage = (FRV_CACHE_TAG *) - zalloc (elements * sizeof (*cache->tag_storage)); - cache->data_storage = (char *) xmalloc (elements * cache->line_size); - - /* Initialize the pipelines and status buffers. */ - for (i = LS; i < FRV_CACHE_PIPELINES; ++i) - { - cache->pipeline[i].requests = NULL; - cache->pipeline[i].status.flush.valid = 0; - cache->pipeline[i].status.return_buffer.valid = 0; - cache->pipeline[i].status.return_buffer.data - = (char *) xmalloc (cache->line_size); - for (j = FIRST_STAGE; j < FRV_CACHE_STAGES; ++j) - cache->pipeline[i].stages[j].request = NULL; - } - cache->BARS.valid = 0; - cache->NARS.valid = 0; - - /* Now set the cache state. */ - cache->cpu = cpu; - cache->statistics.accesses = 0; - cache->statistics.hits = 0; -} - -void -frv_cache_term (FRV_CACHE *cache) -{ - /* Free the cache storage. */ - free (cache->tag_storage); - free (cache->data_storage); - free (cache->pipeline[LS].status.return_buffer.data); - free (cache->pipeline[LD].status.return_buffer.data); -} - -/* Reset the cache configuration based on registers in the cpu. */ -void -frv_cache_reconfigure (SIM_CPU *current_cpu, FRV_CACHE *cache) -{ - int ihsr8; - int icdm; - SIM_DESC sd; - - /* Set defaults for fields which are not initialized. */ - sd = CPU_STATE (current_cpu); - switch (STATE_ARCHITECTURE (sd)->mach) - { - case bfd_mach_fr550: - if (cache == CPU_INSN_CACHE (current_cpu)) - { - ihsr8 = GET_IHSR8 (); - icdm = GET_IHSR8_ICDM (ihsr8); - /* If IHSR8.ICDM is set, then the cache becomes a one way cache. */ - if (icdm) - { - cache->sets = cache->sets * cache->ways; - cache->ways = 1; - break; - } - } - /* fall through */ - default: - /* Set the cache to its original settings. */ - cache->sets = cache->configured_sets; - cache->ways = cache->configured_ways; - break; - } -} - -/* Determine whether the given cache is enabled. */ -int -frv_cache_enabled (FRV_CACHE *cache) -{ - SIM_CPU *current_cpu = cache->cpu; - int hsr0 = GET_HSR0 (); - if (GET_HSR0_ICE (hsr0) && cache == CPU_INSN_CACHE (current_cpu)) - return 1; - if (GET_HSR0_DCE (hsr0) && cache == CPU_DATA_CACHE (current_cpu)) - return 1; - return 0; -} - -/* Determine whether the given address is RAM access, assuming that HSR0.RME - is set. */ -static int -ram_access (FRV_CACHE *cache, USI address) -{ - int ihsr8; - int cwe; - USI start, end, way_size; - SIM_CPU *current_cpu = cache->cpu; - SIM_DESC sd = CPU_STATE (current_cpu); - - switch (STATE_ARCHITECTURE (sd)->mach) - { - case bfd_mach_fr550: - /* IHSR8.DCWE or IHSR8.ICWE deternines which ways get RAM access. */ - ihsr8 = GET_IHSR8 (); - if (cache == CPU_INSN_CACHE (current_cpu)) - { - start = 0xfe000000; - end = 0xfe008000; - cwe = GET_IHSR8_ICWE (ihsr8); - } - else - { - start = 0xfe400000; - end = 0xfe408000; - cwe = GET_IHSR8_DCWE (ihsr8); - } - way_size = (end - start) / 4; - end -= way_size * cwe; - return address >= start && address < end; - default: - break; - } - - return 1; /* RAM access */ -} - -/* Determine whether the given address should be accessed without using - the cache. */ -static int -non_cache_access (FRV_CACHE *cache, USI address) -{ - int hsr0; - SIM_DESC sd; - SIM_CPU *current_cpu = cache->cpu; - - sd = CPU_STATE (current_cpu); - switch (STATE_ARCHITECTURE (sd)->mach) - { - case bfd_mach_fr400: - case bfd_mach_fr450: - if (address >= 0xff000000 - || address >= 0xfe000000 && address <= 0xfeffffff) - return 1; /* non-cache access */ - break; - case bfd_mach_fr550: - if (address >= 0xff000000 - || address >= 0xfeff0000 && address <= 0xfeffffff) - return 1; /* non-cache access */ - if (cache == CPU_INSN_CACHE (current_cpu)) - { - if (address >= 0xfe000000 && address <= 0xfe007fff) - return 1; /* non-cache access */ - } - else if (address >= 0xfe400000 && address <= 0xfe407fff) - return 1; /* non-cache access */ - break; - default: - if (address >= 0xff000000 - || address >= 0xfeff0000 && address <= 0xfeffffff) - return 1; /* non-cache access */ - if (cache == CPU_INSN_CACHE (current_cpu)) - { - if (address >= 0xfe000000 && address <= 0xfe003fff) - return 1; /* non-cache access */ - } - else if (address >= 0xfe400000 && address <= 0xfe403fff) - return 1; /* non-cache access */ - break; - } - - hsr0 = GET_HSR0 (); - if (GET_HSR0_RME (hsr0)) - return ram_access (cache, address); - - return 0; /* cache-access */ -} - -/* Find the cache line corresponding to the given address. - If it is found then 'return_tag' is set to point to the tag for that line - and 1 is returned. - If it is not found, 'return_tag' is set to point to the tag for the least - recently used line and 0 is returned. -*/ -static int -get_tag (FRV_CACHE *cache, SI address, FRV_CACHE_TAG **return_tag) -{ - int set; - int way; - int bits; - USI tag; - FRV_CACHE_TAG *found; - FRV_CACHE_TAG *available; - - ++cache->statistics.accesses; - - /* First calculate which set this address will fall into. Do this by - shifting out the bits representing the offset within the line and - then keeping enough bits to index the set. */ - set = address & ~(cache->line_size - 1); - for (bits = cache->line_size - 1; bits != 0; bits >>= 1) - set >>= 1; - set &= (cache->sets - 1); - - /* Now search the set for a valid tag which matches this address. At the - same time make note of the least recently used tag, which we will return - if no match is found. */ - available = NULL; - tag = CACHE_ADDRESS_TAG (cache, address); - for (way = 0; way < cache->ways; ++way) - { - found = CACHE_TAG (cache, set, way); - /* This tag is available as the least recently used if it is the - least recently used seen so far and it is not locked. */ - if (! found->locked && (available == NULL || available->lru > found->lru)) - available = found; - if (found->valid && found->tag == tag) - { - *return_tag = found; - ++cache->statistics.hits; - return 1; /* found it */ - } - } - - *return_tag = available; - return 0; /* not found */ -} - -/* Write the given data out to memory. */ -static void -write_data_to_memory (FRV_CACHE *cache, SI address, char *data, int length) -{ - SIM_CPU *cpu = cache->cpu; - IADDR pc = CPU_PC_GET (cpu); - int write_index = 0; - - switch (length) - { - case 1: - default: - PROFILE_COUNT_WRITE (cpu, address, MODE_QI); - break; - case 2: - PROFILE_COUNT_WRITE (cpu, address, MODE_HI); - break; - case 4: - PROFILE_COUNT_WRITE (cpu, address, MODE_SI); - break; - case 8: - PROFILE_COUNT_WRITE (cpu, address, MODE_DI); - break; - } - - for (write_index = 0; write_index < length; ++write_index) - { - /* TODO: Better way to copy memory than a byte at a time? */ - sim_core_write_unaligned_1 (cpu, pc, write_map, address + write_index, - data[write_index]); - } -} - -/* Write a cache line out to memory. */ -static void -write_line_to_memory (FRV_CACHE *cache, FRV_CACHE_TAG *tag) -{ - SI address = tag->tag; - int set = CACHE_TAG_SET_NUMBER (cache, tag); - int bits; - for (bits = cache->line_size - 1; bits != 0; bits >>= 1) - set <<= 1; - address |= set; - write_data_to_memory (cache, address, tag->line, cache->line_size); -} - -static void -read_data_from_memory (SIM_CPU *current_cpu, SI address, char *buffer, - int length) -{ - PCADDR pc = CPU_PC_GET (current_cpu); - int i; - PROFILE_COUNT_READ (current_cpu, address, MODE_QI); - for (i = 0; i < length; ++i) - { - /* TODO: Better way to copy memory than a byte at a time? */ - buffer[i] = sim_core_read_unaligned_1 (current_cpu, pc, read_map, - address + i); - } -} - -/* Fill the given cache line from memory. */ -static void -fill_line_from_memory (FRV_CACHE *cache, FRV_CACHE_TAG *tag, SI address) -{ - PCADDR pc; - int line_alignment; - SI read_address; - SIM_CPU *current_cpu = cache->cpu; - - /* If this line is already valid and the cache is in copy-back mode, then - write this line to memory before refilling it. - Check the dirty bit first, since it is less likely to be set. */ - if (tag->dirty && tag->valid) - { - int hsr0 = GET_HSR0 (); - if (GET_HSR0_CBM (hsr0)) - write_line_to_memory (cache, tag); - } - else if (tag->line == NULL) - { - int line_index = tag - cache->tag_storage; - tag->line = cache->data_storage + (line_index * cache->line_size); - } - - pc = CPU_PC_GET (current_cpu); - line_alignment = cache->line_size - 1; - read_address = address & ~line_alignment; - read_data_from_memory (current_cpu, read_address, tag->line, - cache->line_size); - tag->tag = CACHE_ADDRESS_TAG (cache, address); - tag->valid = 1; -} - -/* Update the LRU information for the tags in the same set as the given tag. */ -static void -set_most_recently_used (FRV_CACHE *cache, FRV_CACHE_TAG *tag) -{ - /* All tags in the same set are contiguous, so find the beginning of the - set by aligning to the size of a set. */ - FRV_CACHE_TAG *item = cache->tag_storage + CACHE_TAG_SET_START (cache, tag); - FRV_CACHE_TAG *limit = item + cache->ways; - - while (item < limit) - { - if (item->lru > tag->lru) - --item->lru; - ++item; - } - tag->lru = cache->ways; /* Mark as most recently used. */ -} - -/* Update the LRU information for the tags in the same set as the given tag. */ -static void -set_least_recently_used (FRV_CACHE *cache, FRV_CACHE_TAG *tag) -{ - /* All tags in the same set are contiguous, so find the beginning of the - set by aligning to the size of a set. */ - FRV_CACHE_TAG *item = cache->tag_storage + CACHE_TAG_SET_START (cache, tag); - FRV_CACHE_TAG *limit = item + cache->ways; - - while (item < limit) - { - if (item->lru != 0 && item->lru < tag->lru) - ++item->lru; - ++item; - } - tag->lru = 0; /* Mark as least recently used. */ -} - -/* Find the line containing the given address and load it if it is not - already loaded. - Returns the tag of the requested line. */ -static FRV_CACHE_TAG * -find_or_retrieve_cache_line (FRV_CACHE *cache, SI address) -{ - /* See if this data is already in the cache. */ - FRV_CACHE_TAG *tag; - int found = get_tag (cache, address, &tag); - - /* Fill the line from memory, if it is not valid. */ - if (! found) - { - /* The tag could be NULL is all ways in the set were used and locked. */ - if (tag == NULL) - return tag; - - fill_line_from_memory (cache, tag, address); - tag->dirty = 0; - } - - /* Update the LRU information for the tags in this set. */ - set_most_recently_used (cache, tag); - - return tag; -} - -static void -copy_line_to_return_buffer (FRV_CACHE *cache, int pipe, FRV_CACHE_TAG *tag, - SI address) -{ - /* A cache line was available for the data. - Copy the data from the cache line to the output buffer. */ - memcpy (cache->pipeline[pipe].status.return_buffer.data, - tag->line, cache->line_size); - cache->pipeline[pipe].status.return_buffer.address - = address & ~(cache->line_size - 1); - cache->pipeline[pipe].status.return_buffer.valid = 1; -} - -static void -copy_memory_to_return_buffer (FRV_CACHE *cache, int pipe, SI address) -{ - address &= ~(cache->line_size - 1); - read_data_from_memory (cache->cpu, address, - cache->pipeline[pipe].status.return_buffer.data, - cache->line_size); - cache->pipeline[pipe].status.return_buffer.address = address; - cache->pipeline[pipe].status.return_buffer.valid = 1; -} - -static void -set_return_buffer_reqno (FRV_CACHE *cache, int pipe, unsigned reqno) -{ - cache->pipeline[pipe].status.return_buffer.reqno = reqno; -} - -/* Read data from the given cache. - Returns the number of cycles required to obtain the data. */ -int -frv_cache_read (FRV_CACHE *cache, int pipe, SI address) -{ - FRV_CACHE_TAG *tag; - - if (non_cache_access (cache, address)) - { - copy_memory_to_return_buffer (cache, pipe, address); - return 1; - } - - tag = find_or_retrieve_cache_line (cache, address); - - if (tag == NULL) - return 0; /* Indicate non-cache-access. */ - - /* A cache line was available for the data. - Copy the data from the cache line to the output buffer. */ - copy_line_to_return_buffer (cache, pipe, tag, address); - - return 1; /* TODO - number of cycles unknown */ -} - -/* Writes data through the given cache. - The data is assumed to be in target endian order. - Returns the number of cycles required to write the data. */ -int -frv_cache_write (FRV_CACHE *cache, SI address, char *data, unsigned length) -{ - int copy_back; - - /* See if this data is already in the cache. */ - SIM_CPU *current_cpu = cache->cpu; - USI hsr0 = GET_HSR0 (); - FRV_CACHE_TAG *tag; - int found; - - if (non_cache_access (cache, address)) - { - write_data_to_memory (cache, address, data, length); - return 1; - } - - found = get_tag (cache, address, &tag); - - /* Write the data to the cache line if one was available and if it is - either a hit or a miss in copy-back mode. - The tag may be NULL if all ways were in use and locked on a miss. - */ - copy_back = GET_HSR0_CBM (GET_HSR0 ()); - if (tag != NULL && (found || copy_back)) - { - int line_offset; - /* Load the line from memory first, if it was a miss. */ - if (! found) - fill_line_from_memory (cache, tag, address); - line_offset = address & (cache->line_size - 1); - memcpy (tag->line + line_offset, data, length); - tag->dirty = 1; - - /* Update the LRU information for the tags in this set. */ - set_most_recently_used (cache, tag); - } - - /* Write the data to memory if there was no line available or we are in - write-through (not copy-back mode). */ - if (tag == NULL || ! copy_back) - { - write_data_to_memory (cache, address, data, length); - if (tag != NULL) - tag->dirty = 0; - } - - return 1; /* TODO - number of cycles unknown */ -} - -/* Preload the cache line containing the given address. Lock the - data if requested. - Returns the number of cycles required to write the data. */ -int -frv_cache_preload (FRV_CACHE *cache, SI address, USI length, int lock) -{ - int offset; - int lines; - - if (non_cache_access (cache, address)) - return 1; - - /* preload at least 1 line. */ - if (length == 0) - length = 1; - - offset = address & (cache->line_size - 1); - lines = 1 + (offset + length - 1) / cache->line_size; - - /* Careful with this loop -- length is unsigned. */ - for (/**/; lines > 0; --lines) - { - FRV_CACHE_TAG *tag = find_or_retrieve_cache_line (cache, address); - if (lock && tag != NULL) - tag->locked = 1; - address += cache->line_size; - } - - return 1; /* TODO - number of cycles unknown */ -} - -/* Unlock the cache line containing the given address. - Returns the number of cycles required to unlock the line. */ -int -frv_cache_unlock (FRV_CACHE *cache, SI address) -{ - FRV_CACHE_TAG *tag; - int found; - - if (non_cache_access (cache, address)) - return 1; - - found = get_tag (cache, address, &tag); - - if (found) - tag->locked = 0; - - return 1; /* TODO - number of cycles unknown */ -} - -static void -invalidate_return_buffer (FRV_CACHE *cache, SI address) -{ - /* If this address is in one of the return buffers, then invalidate that - return buffer. */ - address &= ~(cache->line_size - 1); - if (address == cache->pipeline[LS].status.return_buffer.address) - cache->pipeline[LS].status.return_buffer.valid = 0; - if (address == cache->pipeline[LD].status.return_buffer.address) - cache->pipeline[LD].status.return_buffer.valid = 0; -} - -/* Invalidate the cache line containing the given address. Flush the - data if requested. - Returns the number of cycles required to write the data. */ -int -frv_cache_invalidate (FRV_CACHE *cache, SI address, int flush) -{ - /* See if this data is already in the cache. */ - FRV_CACHE_TAG *tag; - int found; - - /* Check for non-cache access. This operation is still perfromed even if - the cache is not currently enabled. */ - if (non_cache_access (cache, address)) - return 1; - - /* If the line is found, invalidate it. If a flush is requested, then flush - it if it is dirty. */ - found = get_tag (cache, address, &tag); - if (found) - { - SIM_CPU *cpu; - /* If a flush is requested, then flush it if it is dirty. */ - if (tag->dirty && flush) - write_line_to_memory (cache, tag); - set_least_recently_used (cache, tag); - tag->valid = 0; - tag->locked = 0; - - /* If this is the insn cache, then flush the cpu's scache as well. */ - cpu = cache->cpu; - if (cache == CPU_INSN_CACHE (cpu)) - scache_flush_cpu (cpu); - } - - invalidate_return_buffer (cache, address); - - return 1; /* TODO - number of cycles unknown */ -} - -/* Invalidate the entire cache. Flush the data if requested. */ -int -frv_cache_invalidate_all (FRV_CACHE *cache, int flush) -{ - /* See if this data is already in the cache. */ - int elements = cache->sets * cache->ways; - FRV_CACHE_TAG *tag = cache->tag_storage; - SIM_CPU *cpu; - int i; - - for(i = 0; i < elements; ++i, ++tag) - { - /* If a flush is requested, then flush it if it is dirty. */ - if (tag->valid && tag->dirty && flush) - write_line_to_memory (cache, tag); - tag->valid = 0; - tag->locked = 0; - } - - - /* If this is the insn cache, then flush the cpu's scache as well. */ - cpu = cache->cpu; - if (cache == CPU_INSN_CACHE (cpu)) - scache_flush_cpu (cpu); - - /* Invalidate both return buffers. */ - cache->pipeline[LS].status.return_buffer.valid = 0; - cache->pipeline[LD].status.return_buffer.valid = 0; - - return 1; /* TODO - number of cycles unknown */ -} - -/* --------------------------------------------------------------------------- - Functions for operating the cache in cycle accurate mode. - ------------------------------------------------------------------------- */ -/* Convert a VLIW slot to a cache pipeline index. */ -static int -convert_slot_to_index (int slot) -{ - switch (slot) - { - case UNIT_I0: - case UNIT_C: - return LS; - case UNIT_I1: - return LD; - default: - abort (); - } - return 0; -} - -/* Allocate free chains of cache requests. */ -#define FREE_CHAIN_SIZE 16 -static FRV_CACHE_REQUEST *frv_cache_request_free_chain = NULL; -static FRV_CACHE_REQUEST *frv_store_request_free_chain = NULL; - -static void -allocate_new_cache_requests (void) -{ - int i; - frv_cache_request_free_chain = xmalloc (FREE_CHAIN_SIZE - * sizeof (FRV_CACHE_REQUEST)); - for (i = 0; i < FREE_CHAIN_SIZE - 1; ++i) - { - frv_cache_request_free_chain[i].next - = & frv_cache_request_free_chain[i + 1]; - } - - frv_cache_request_free_chain[FREE_CHAIN_SIZE - 1].next = NULL; -} - -/* Return the next free request in the queue for the given cache pipeline. */ -static FRV_CACHE_REQUEST * -new_cache_request (void) -{ - FRV_CACHE_REQUEST *req; - - /* Allocate new elements for the free chain if necessary. */ - if (frv_cache_request_free_chain == NULL) - allocate_new_cache_requests (); - - req = frv_cache_request_free_chain; - frv_cache_request_free_chain = req->next; - - return req; -} - -/* Return the given cache request to the free chain. */ -static void -free_cache_request (FRV_CACHE_REQUEST *req) -{ - if (req->kind == req_store) - { - req->next = frv_store_request_free_chain; - frv_store_request_free_chain = req; - } - else - { - req->next = frv_cache_request_free_chain; - frv_cache_request_free_chain = req; - } -} - -/* Search the free chain for an existing store request with a buffer that's - large enough. */ -static FRV_CACHE_REQUEST * -new_store_request (int length) -{ - FRV_CACHE_REQUEST *prev = NULL; - FRV_CACHE_REQUEST *req; - for (req = frv_store_request_free_chain; req != NULL; req = req->next) - { - if (req->u.store.length == length) - break; - prev = req; - } - if (req != NULL) - { - if (prev == NULL) - frv_store_request_free_chain = req->next; - else - prev->next = req->next; - return req; - } - - /* No existing request buffer was found, so make a new one. */ - req = new_cache_request (); - req->kind = req_store; - req->u.store.data = xmalloc (length); - req->u.store.length = length; - return req; -} - -/* Remove the given request from the given pipeline. */ -static void -pipeline_remove_request (FRV_CACHE_PIPELINE *p, FRV_CACHE_REQUEST *request) -{ - FRV_CACHE_REQUEST *next = request->next; - FRV_CACHE_REQUEST *prev = request->prev; - - if (prev == NULL) - p->requests = next; - else - prev->next = next; - - if (next != NULL) - next->prev = prev; -} - -/* Add the given request to the given pipeline. */ -static void -pipeline_add_request (FRV_CACHE_PIPELINE *p, FRV_CACHE_REQUEST *request) -{ - FRV_CACHE_REQUEST *prev = NULL; - FRV_CACHE_REQUEST *item; - - /* Add the request in priority order. 0 is the highest priority. */ - for (item = p->requests; item != NULL; item = item->next) - { - if (item->priority > request->priority) - break; - prev = item; - } - - request->next = item; - request->prev = prev; - if (prev == NULL) - p->requests = request; - else - prev->next = request; - if (item != NULL) - item->prev = request; -} - -/* Requeu the given request from the last of the given pipeline. */ -static void -pipeline_requeue_request (FRV_CACHE_PIPELINE *p) -{ - FRV_CACHE_STAGE *stage = & p->stages[LAST_STAGE]; - FRV_CACHE_REQUEST *req = stage->request; - stage->request = NULL; - pipeline_add_request (p, req); -} - -/* Return the priority lower than the lowest one in this cache pipeline. - 0 is the highest priority. */ -static int -next_priority (FRV_CACHE *cache, FRV_CACHE_PIPELINE *pipeline) -{ - int i, j; - int pipe; - int lowest = 0; - FRV_CACHE_REQUEST *req; - - /* Check the priorities of any queued items. */ - for (req = pipeline->requests; req != NULL; req = req->next) - if (req->priority > lowest) - lowest = req->priority; - - /* Check the priorities of items in the pipeline stages. */ - for (i = FIRST_STAGE; i < FRV_CACHE_STAGES; ++i) - { - FRV_CACHE_STAGE *stage = & pipeline->stages[i]; - if (stage->request != NULL && stage->request->priority > lowest) - lowest = stage->request->priority; - } - - /* Check the priorities of load requests waiting in WAR. These are one - higher than the request that spawned them. */ - for (i = 0; i < NUM_WARS; ++i) - { - FRV_CACHE_WAR *war = & pipeline->WAR[i]; - if (war->valid && war->priority > lowest) - lowest = war->priority + 1; - } - - /* Check the priorities of any BARS or NARS associated with this pipeline. - These are one higher than the request that spawned them. */ - pipe = pipeline - cache->pipeline; - if (cache->BARS.valid && cache->BARS.pipe == pipe - && cache->BARS.priority > lowest) - lowest = cache->BARS.priority + 1; - if (cache->NARS.valid && cache->NARS.pipe == pipe - && cache->NARS.priority > lowest) - lowest = cache->NARS.priority + 1; - - /* Return a priority 2 lower than the lowest found. This allows a WAR - request to be generated with a priority greater than this but less than - the next higher priority request. */ - return lowest + 2; -} - -static void -add_WAR_request (FRV_CACHE_PIPELINE* pipeline, FRV_CACHE_WAR *war) -{ - /* Add the load request to the indexed pipeline. */ - FRV_CACHE_REQUEST *req = new_cache_request (); - req->kind = req_WAR; - req->reqno = war->reqno; - req->priority = war->priority; - req->address = war->address; - req->u.WAR.preload = war->preload; - req->u.WAR.lock = war->lock; - pipeline_add_request (pipeline, req); -} - -/* Remove the next request from the given pipeline and return it. */ -static FRV_CACHE_REQUEST * -pipeline_next_request (FRV_CACHE_PIPELINE *p) -{ - FRV_CACHE_REQUEST *first = p->requests; - if (first != NULL) - pipeline_remove_request (p, first); - return first; -} - -/* Return the request which is at the given stage of the given pipeline. */ -static FRV_CACHE_REQUEST * -pipeline_stage_request (FRV_CACHE_PIPELINE *p, int stage) -{ - return p->stages[stage].request; -} - -static void -advance_pipelines (FRV_CACHE *cache) -{ - int stage; - int pipe; - FRV_CACHE_PIPELINE *pipelines = cache->pipeline; - - /* Free the final stage requests. */ - for (pipe = 0; pipe < FRV_CACHE_PIPELINES; ++pipe) - { - FRV_CACHE_REQUEST *req = pipelines[pipe].stages[LAST_STAGE].request; - if (req != NULL) - free_cache_request (req); - } - - /* Shuffle the requests along the pipeline. */ - for (stage = LAST_STAGE; stage > FIRST_STAGE; --stage) - { - for (pipe = 0; pipe < FRV_CACHE_PIPELINES; ++pipe) - pipelines[pipe].stages[stage] = pipelines[pipe].stages[stage - 1]; - } - - /* Add a new request to the pipeline. */ - for (pipe = 0; pipe < FRV_CACHE_PIPELINES; ++pipe) - pipelines[pipe].stages[FIRST_STAGE].request - = pipeline_next_request (& pipelines[pipe]); -} - -/* Handle a request for a load from the given address. */ -void -frv_cache_request_load (FRV_CACHE *cache, unsigned reqno, SI address, int slot) -{ - FRV_CACHE_REQUEST *req; - - /* slot is a UNIT_*. Convert it to a cache pipeline index. */ - int pipe = convert_slot_to_index (slot); - FRV_CACHE_PIPELINE *pipeline = & cache->pipeline[pipe]; - - /* Add the load request to the indexed pipeline. */ - req = new_cache_request (); - req->kind = req_load; - req->reqno = reqno; - req->priority = next_priority (cache, pipeline); - req->address = address; - - pipeline_add_request (pipeline, req); -} - -void -frv_cache_request_store (FRV_CACHE *cache, SI address, - int slot, char *data, unsigned length) -{ - FRV_CACHE_REQUEST *req; - - /* slot is a UNIT_*. Convert it to a cache pipeline index. */ - int pipe = convert_slot_to_index (slot); - FRV_CACHE_PIPELINE *pipeline = & cache->pipeline[pipe]; - - /* Add the load request to the indexed pipeline. */ - req = new_store_request (length); - req->kind = req_store; - req->reqno = NO_REQNO; - req->priority = next_priority (cache, pipeline); - req->address = address; - req->u.store.length = length; - memcpy (req->u.store.data, data, length); - - pipeline_add_request (pipeline, req); - invalidate_return_buffer (cache, address); -} - -/* Handle a request to invalidate the cache line containing the given address. - Flush the data if requested. */ -void -frv_cache_request_invalidate (FRV_CACHE *cache, unsigned reqno, SI address, - int slot, int all, int flush) -{ - FRV_CACHE_REQUEST *req; - - /* slot is a UNIT_*. Convert it to a cache pipeline index. */ - int pipe = convert_slot_to_index (slot); - FRV_CACHE_PIPELINE *pipeline = & cache->pipeline[pipe]; - - /* Add the load request to the indexed pipeline. */ - req = new_cache_request (); - req->kind = req_invalidate; - req->reqno = reqno; - req->priority = next_priority (cache, pipeline); - req->address = address; - req->u.invalidate.all = all; - req->u.invalidate.flush = flush; - - pipeline_add_request (pipeline, req); -} - -/* Handle a request to preload the cache line containing the given address. */ -void -frv_cache_request_preload (FRV_CACHE *cache, SI address, - int slot, int length, int lock) -{ - FRV_CACHE_REQUEST *req; - - /* slot is a UNIT_*. Convert it to a cache pipeline index. */ - int pipe = convert_slot_to_index (slot); - FRV_CACHE_PIPELINE *pipeline = & cache->pipeline[pipe]; - - /* Add the load request to the indexed pipeline. */ - req = new_cache_request (); - req->kind = req_preload; - req->reqno = NO_REQNO; - req->priority = next_priority (cache, pipeline); - req->address = address; - req->u.preload.length = length; - req->u.preload.lock = lock; - - pipeline_add_request (pipeline, req); - invalidate_return_buffer (cache, address); -} - -/* Handle a request to unlock the cache line containing the given address. */ -void -frv_cache_request_unlock (FRV_CACHE *cache, SI address, int slot) -{ - FRV_CACHE_REQUEST *req; - - /* slot is a UNIT_*. Convert it to a cache pipeline index. */ - int pipe = convert_slot_to_index (slot); - FRV_CACHE_PIPELINE *pipeline = & cache->pipeline[pipe]; - - /* Add the load request to the indexed pipeline. */ - req = new_cache_request (); - req->kind = req_unlock; - req->reqno = NO_REQNO; - req->priority = next_priority (cache, pipeline); - req->address = address; - - pipeline_add_request (pipeline, req); -} - -/* Check whether this address interferes with a pending request of - higher priority. */ -static int -address_interference (FRV_CACHE *cache, SI address, FRV_CACHE_REQUEST *req, - int pipe) -{ - int i, j; - int line_mask = ~(cache->line_size - 1); - int other_pipe; - int priority = req->priority; - FRV_CACHE_REQUEST *other_req; - SI other_address; - SI all_address; - - address &= line_mask; - all_address = -1 & line_mask; - - /* Check for collisions in the queue for this pipeline. */ - for (other_req = cache->pipeline[pipe].requests; - other_req != NULL; - other_req = other_req->next) - { - other_address = other_req->address & line_mask; - if ((address == other_address || address == all_address) - && priority > other_req->priority) - return 1; - } - - /* Check for a collision in the the other pipeline. */ - other_pipe = pipe ^ 1; - other_req = cache->pipeline[other_pipe].stages[LAST_STAGE].request; - if (other_req != NULL) - { - other_address = other_req->address & line_mask; - if (address == other_address || address == all_address) - return 1; - } - - /* Check for a collision with load requests waiting in WAR. */ - for (i = LS; i < FRV_CACHE_PIPELINES; ++i) - { - for (j = 0; j < NUM_WARS; ++j) - { - FRV_CACHE_WAR *war = & cache->pipeline[i].WAR[j]; - if (war->valid - && (address == (war->address & line_mask) - || address == all_address) - && priority > war->priority) - return 1; - } - /* If this is not a WAR request, then yield to any WAR requests in - either pipeline or to a higher priority request in the same pipeline. - */ - if (req->kind != req_WAR) - { - for (j = FIRST_STAGE; j < FRV_CACHE_STAGES; ++j) - { - other_req = cache->pipeline[i].stages[j].request; - if (other_req != NULL) - { - if (other_req->kind == req_WAR) - return 1; - if (i == pipe - && (address == (other_req->address & line_mask) - || address == all_address) - && priority > other_req->priority) - return 1; - } - } - } - } - - /* Check for a collision with load requests waiting in ARS. */ - if (cache->BARS.valid - && (address == (cache->BARS.address & line_mask) - || address == all_address) - && priority > cache->BARS.priority) - return 1; - if (cache->NARS.valid - && (address == (cache->NARS.address & line_mask) - || address == all_address) - && priority > cache->NARS.priority) - return 1; - - return 0; -} - -/* Wait for a free WAR register in BARS or NARS. */ -static void -wait_for_WAR (FRV_CACHE* cache, int pipe, FRV_CACHE_REQUEST *req) -{ - FRV_CACHE_WAR war; - FRV_CACHE_PIPELINE *pipeline = & cache->pipeline[pipe]; - - if (! cache->BARS.valid) - { - cache->BARS.pipe = pipe; - cache->BARS.reqno = req->reqno; - cache->BARS.address = req->address; - cache->BARS.priority = req->priority - 1; - switch (req->kind) - { - case req_load: - cache->BARS.preload = 0; - cache->BARS.lock = 0; - break; - case req_store: - cache->BARS.preload = 1; - cache->BARS.lock = 0; - break; - case req_preload: - cache->BARS.preload = 1; - cache->BARS.lock = req->u.preload.lock; - break; - } - cache->BARS.valid = 1; - return; - } - if (! cache->NARS.valid) - { - cache->NARS.pipe = pipe; - cache->NARS.reqno = req->reqno; - cache->NARS.address = req->address; - cache->NARS.priority = req->priority - 1; - switch (req->kind) - { - case req_load: - cache->NARS.preload = 0; - cache->NARS.lock = 0; - break; - case req_store: - cache->NARS.preload = 1; - cache->NARS.lock = 0; - break; - case req_preload: - cache->NARS.preload = 1; - cache->NARS.lock = req->u.preload.lock; - break; - } - cache->NARS.valid = 1; - return; - } - /* All wait registers are busy, so resubmit this request. */ - pipeline_requeue_request (pipeline); -} - -/* Find a free WAR register and wait for memory to fetch the data. */ -static void -wait_in_WAR (FRV_CACHE* cache, int pipe, FRV_CACHE_REQUEST *req) -{ - int war; - FRV_CACHE_PIPELINE *pipeline = & cache->pipeline[pipe]; - - /* Find a valid WAR to hold this request. */ - for (war = 0; war < NUM_WARS; ++war) - if (! pipeline->WAR[war].valid) - break; - if (war >= NUM_WARS) - { - wait_for_WAR (cache, pipe, req); - return; - } - - pipeline->WAR[war].address = req->address; - pipeline->WAR[war].reqno = req->reqno; - pipeline->WAR[war].priority = req->priority - 1; - pipeline->WAR[war].latency = cache->memory_latency + 1; - switch (req->kind) - { - case req_load: - pipeline->WAR[war].preload = 0; - pipeline->WAR[war].lock = 0; - break; - case req_store: - pipeline->WAR[war].preload = 1; - pipeline->WAR[war].lock = 0; - break; - case req_preload: - pipeline->WAR[war].preload = 1; - pipeline->WAR[war].lock = req->u.preload.lock; - break; - } - pipeline->WAR[war].valid = 1; -} - -static void -handle_req_load (FRV_CACHE *cache, int pipe, FRV_CACHE_REQUEST *req) -{ - FRV_CACHE_TAG *tag; - SI address = req->address; - - /* If this address interferes with an existing request, then requeue it. */ - if (address_interference (cache, address, req, pipe)) - { - pipeline_requeue_request (& cache->pipeline[pipe]); - return; - } - - if (frv_cache_enabled (cache) && ! non_cache_access (cache, address)) - { - int found = get_tag (cache, address, &tag); - - /* If the data was found, return it to the caller. */ - if (found) - { - set_most_recently_used (cache, tag); - copy_line_to_return_buffer (cache, pipe, tag, address); - set_return_buffer_reqno (cache, pipe, req->reqno); - return; - } - } - - /* The data is not in the cache or this is a non-cache access. We need to - wait for the memory unit to fetch it. Store this request in the WAR in - the meantime. */ - wait_in_WAR (cache, pipe, req); -} - -static void -handle_req_preload (FRV_CACHE *cache, int pipe, FRV_CACHE_REQUEST *req) -{ - int found; - FRV_CACHE_WAR war; - FRV_CACHE_TAG *tag; - int length; - int lock; - int offset; - int lines; - int line; - SI address = req->address; - SI cur_address; - - if (! frv_cache_enabled (cache) || non_cache_access (cache, address)) - return; - - /* preload at least 1 line. */ - length = req->u.preload.length; - if (length == 0) - length = 1; - - /* Make sure that this request does not interfere with a pending request. */ - offset = address & (cache->line_size - 1); - lines = 1 + (offset + length - 1) / cache->line_size; - cur_address = address & ~(cache->line_size - 1); - for (line = 0; line < lines; ++line) - { - /* If this address interferes with an existing request, - then requeue it. */ - if (address_interference (cache, cur_address, req, pipe)) - { - pipeline_requeue_request (& cache->pipeline[pipe]); - return; - } - cur_address += cache->line_size; - } - - /* Now process each cache line. */ - /* Careful with this loop -- length is unsigned. */ - lock = req->u.preload.lock; - cur_address = address & ~(cache->line_size - 1); - for (line = 0; line < lines; ++line) - { - /* If the data was found, then lock it if requested. */ - found = get_tag (cache, cur_address, &tag); - if (found) - { - if (lock) - tag->locked = 1; - } - else - { - /* The data is not in the cache. We need to wait for the memory - unit to fetch it. Store this request in the WAR in the meantime. - */ - wait_in_WAR (cache, pipe, req); - } - cur_address += cache->line_size; - } -} - -static void -handle_req_store (FRV_CACHE *cache, int pipe, FRV_CACHE_REQUEST *req) -{ - SIM_CPU *current_cpu; - FRV_CACHE_TAG *tag; - int found; - int copy_back; - SI address = req->address; - char *data = req->u.store.data; - int length = req->u.store.length; - - /* If this address interferes with an existing request, then requeue it. */ - if (address_interference (cache, address, req, pipe)) - { - pipeline_requeue_request (& cache->pipeline[pipe]); - return; - } - - /* Non-cache access. Write the data directly to memory. */ - if (! frv_cache_enabled (cache) || non_cache_access (cache, address)) - { - write_data_to_memory (cache, address, data, length); - return; - } - - /* See if the data is in the cache. */ - found = get_tag (cache, address, &tag); - - /* Write the data to the cache line if one was available and if it is - either a hit or a miss in copy-back mode. - The tag may be NULL if all ways were in use and locked on a miss. - */ - current_cpu = cache->cpu; - copy_back = GET_HSR0_CBM (GET_HSR0 ()); - if (tag != NULL && (found || copy_back)) - { - int line_offset; - /* Load the line from memory first, if it was a miss. */ - if (! found) - { - /* We need to wait for the memory unit to fetch the data. - Store this request in the WAR and requeue the store request. */ - wait_in_WAR (cache, pipe, req); - pipeline_requeue_request (& cache->pipeline[pipe]); - /* Decrement the counts of accesses and hits because when the requeued - request is processed again, it will appear to be a new access and - a hit. */ - --cache->statistics.accesses; - --cache->statistics.hits; - return; - } - line_offset = address & (cache->line_size - 1); - memcpy (tag->line + line_offset, data, length); - invalidate_return_buffer (cache, address); - tag->dirty = 1; - - /* Update the LRU information for the tags in this set. */ - set_most_recently_used (cache, tag); - } - - /* Write the data to memory if there was no line available or we are in - write-through (not copy-back mode). */ - if (tag == NULL || ! copy_back) - { - write_data_to_memory (cache, address, data, length); - if (tag != NULL) - tag->dirty = 0; - } -} - -static void -handle_req_invalidate (FRV_CACHE *cache, int pipe, FRV_CACHE_REQUEST *req) -{ - FRV_CACHE_PIPELINE *pipeline = & cache->pipeline[pipe]; - SI address = req->address; - SI interfere_address = req->u.invalidate.all ? -1 : address; - - /* If this address interferes with an existing request, then requeue it. */ - if (address_interference (cache, interfere_address, req, pipe)) - { - pipeline_requeue_request (pipeline); - return; - } - - /* Invalidate the cache line now. This function already checks for - non-cache access. */ - if (req->u.invalidate.all) - frv_cache_invalidate_all (cache, req->u.invalidate.flush); - else - frv_cache_invalidate (cache, address, req->u.invalidate.flush); - if (req->u.invalidate.flush) - { - pipeline->status.flush.reqno = req->reqno; - pipeline->status.flush.address = address; - pipeline->status.flush.valid = 1; - } -} - -static void -handle_req_unlock (FRV_CACHE *cache, int pipe, FRV_CACHE_REQUEST *req) -{ - FRV_CACHE_PIPELINE *pipeline = & cache->pipeline[pipe]; - SI address = req->address; - - /* If this address interferes with an existing request, then requeue it. */ - if (address_interference (cache, address, req, pipe)) - { - pipeline_requeue_request (pipeline); - return; - } - - /* Unlock the cache line. This function checks for non-cache access. */ - frv_cache_unlock (cache, address); -} - -static void -handle_req_WAR (FRV_CACHE *cache, int pipe, FRV_CACHE_REQUEST *req) -{ - char *buffer; - FRV_CACHE_TAG *tag; - SI address = req->address; - - if (frv_cache_enabled (cache) && ! non_cache_access (cache, address)) - { - /* Look for the data in the cache. The statistics of cache hit or - miss have already been recorded, so save and restore the stats before - and after obtaining the cache line. */ - FRV_CACHE_STATISTICS save_stats = cache->statistics; - tag = find_or_retrieve_cache_line (cache, address); - cache->statistics = save_stats; - if (tag != NULL) - { - if (! req->u.WAR.preload) - { - copy_line_to_return_buffer (cache, pipe, tag, address); - set_return_buffer_reqno (cache, pipe, req->reqno); - } - else - { - invalidate_return_buffer (cache, address); - if (req->u.WAR.lock) - tag->locked = 1; - } - return; - } - } - - /* All cache lines in the set were locked, so just copy the data to the - return buffer directly. */ - if (! req->u.WAR.preload) - { - copy_memory_to_return_buffer (cache, pipe, address); - set_return_buffer_reqno (cache, pipe, req->reqno); - } -} - -/* Resolve any conflicts and/or execute the given requests. */ -static void -arbitrate_requests (FRV_CACHE *cache) -{ - int pipe; - /* Simply execute the requests in the final pipeline stages. */ - for (pipe = LS; pipe < FRV_CACHE_PIPELINES; ++pipe) - { - FRV_CACHE_REQUEST *req - = pipeline_stage_request (& cache->pipeline[pipe], LAST_STAGE); - /* Make sure that there is a request to handle. */ - if (req == NULL) - continue; - - /* Handle the request. */ - switch (req->kind) - { - case req_load: - handle_req_load (cache, pipe, req); - break; - case req_store: - handle_req_store (cache, pipe, req); - break; - case req_invalidate: - handle_req_invalidate (cache, pipe, req); - break; - case req_preload: - handle_req_preload (cache, pipe, req); - break; - case req_unlock: - handle_req_unlock (cache, pipe, req); - break; - case req_WAR: - handle_req_WAR (cache, pipe, req); - break; - default: - abort (); - } - } -} - -/* Move a waiting ARS register to a free WAR register. */ -static void -move_ARS_to_WAR (FRV_CACHE *cache, int pipe, FRV_CACHE_WAR *war) -{ - /* If BARS is valid for this pipe, then move it to the given WAR. Move - NARS to BARS if it is valid. */ - if (cache->BARS.valid && cache->BARS.pipe == pipe) - { - war->address = cache->BARS.address; - war->reqno = cache->BARS.reqno; - war->priority = cache->BARS.priority; - war->preload = cache->BARS.preload; - war->lock = cache->BARS.lock; - war->latency = cache->memory_latency + 1; - war->valid = 1; - if (cache->NARS.valid) - { - cache->BARS = cache->NARS; - cache->NARS.valid = 0; - } - else - cache->BARS.valid = 0; - return; - } - /* If NARS is valid for this pipe, then move it to the given WAR. */ - if (cache->NARS.valid && cache->NARS.pipe == pipe) - { - war->address = cache->NARS.address; - war->reqno = cache->NARS.reqno; - war->priority = cache->NARS.priority; - war->preload = cache->NARS.preload; - war->lock = cache->NARS.lock; - war->latency = cache->memory_latency + 1; - war->valid = 1; - cache->NARS.valid = 0; - } -} - -/* Decrease the latencies of the various states in the cache. */ -static void -decrease_latencies (FRV_CACHE *cache) -{ - int pipe, j; - /* Check the WAR registers. */ - for (pipe = LS; pipe < FRV_CACHE_PIPELINES; ++pipe) - { - FRV_CACHE_PIPELINE *pipeline = & cache->pipeline[pipe]; - for (j = 0; j < NUM_WARS; ++j) - { - FRV_CACHE_WAR *war = & pipeline->WAR[j]; - if (war->valid) - { - --war->latency; - /* If the latency has expired, then submit a WAR request to the - pipeline. */ - if (war->latency <= 0) - { - add_WAR_request (pipeline, war); - war->valid = 0; - move_ARS_to_WAR (cache, pipe, war); - } - } - } - } -} - -/* Run the cache for the given number of cycles. */ -void -frv_cache_run (FRV_CACHE *cache, int cycles) -{ - int i; - for (i = 0; i < cycles; ++i) - { - advance_pipelines (cache); - arbitrate_requests (cache); - decrease_latencies (cache); - } -} - -int -frv_cache_read_passive_SI (FRV_CACHE *cache, SI address, SI *value) -{ - SI offset; - FRV_CACHE_TAG *tag; - - if (non_cache_access (cache, address)) - return 0; - - { - FRV_CACHE_STATISTICS save_stats = cache->statistics; - int found = get_tag (cache, address, &tag); - cache->statistics = save_stats; - - if (! found) - return 0; /* Indicate non-cache-access. */ - } - - /* A cache line was available for the data. - Extract the target data from the line. */ - offset = address & (cache->line_size - 1); - *value = T2H_4 (*(SI *)(tag->line + offset)); - return 1; -} - -/* Check the return buffers of the data cache to see if the requested data is - available. */ -int -frv_cache_data_in_buffer (FRV_CACHE* cache, int pipe, SI address, - unsigned reqno) -{ - return cache->pipeline[pipe].status.return_buffer.valid - && cache->pipeline[pipe].status.return_buffer.reqno == reqno - && cache->pipeline[pipe].status.return_buffer.address <= address - && cache->pipeline[pipe].status.return_buffer.address + cache->line_size - > address; -} - -/* Check to see if the requested data has been flushed. */ -int -frv_cache_data_flushed (FRV_CACHE* cache, int pipe, SI address, unsigned reqno) -{ - return cache->pipeline[pipe].status.flush.valid - && cache->pipeline[pipe].status.flush.reqno == reqno - && cache->pipeline[pipe].status.flush.address <= address - && cache->pipeline[pipe].status.flush.address + cache->line_size - > address; -} |