/* frv memory model. Copyright (C) 1999, 2000, 2001 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 "sim-main.h" #include "cgen-mem.h" #include "bfd.h" /* Check for alignment and access restrictions. Return the corrected address. */ static SI fr400_check_data_read_address (SIM_CPU *current_cpu, SI address, int align_mask) { /* Check access restrictions for double word loads only. */ if (align_mask == 7) { if ((USI)address >= 0xfe800000 && (USI)address <= 0xfeffffff) frv_queue_data_access_error_interrupt (current_cpu, address); } return address; } static SI fr500_check_data_read_address (SIM_CPU *current_cpu, SI address, int align_mask) { if (address & align_mask) { frv_queue_mem_address_not_aligned_interrupt (current_cpu, address); address &= ~align_mask; } if ((USI)address >= 0xfeff0600 && (USI)address <= 0xfeff7fff || (USI)address >= 0xfe800000 && (USI)address <= 0xfefeffff) frv_queue_data_access_error_interrupt (current_cpu, address); return address; } static SI check_data_read_address (SIM_CPU *current_cpu, SI address, int align_mask) { SIM_DESC sd = CPU_STATE (current_cpu); switch (STATE_ARCHITECTURE (sd)->mach) { case bfd_mach_fr400: address = fr400_check_data_read_address (current_cpu, address, align_mask); break; case bfd_mach_frvtomcat: case bfd_mach_fr500: case bfd_mach_frv: address = fr500_check_data_read_address (current_cpu, address, align_mask); break; default: break; } return address; } static SI fr400_check_readwrite_address (SIM_CPU *current_cpu, SI address, int align_mask) { if (address & align_mask) { /* Make sure that this exception is not masked. */ USI isr = GET_ISR (); if (! GET_ISR_EMAM (isr)) { /* Bad alignment causes a data_access_error on fr400. */ frv_queue_data_access_error_interrupt (current_cpu, address); } address &= ~align_mask; } /* Nothing to check. */ return address; } static SI fr500_check_readwrite_address (SIM_CPU *current_cpu, SI address, int align_mask) { if ((USI)address >= 0xfe000000 && (USI)address <= 0xfe003fff || (USI)address >= 0xfe004000 && (USI)address <= 0xfe3fffff || (USI)address >= 0xfe400000 && (USI)address <= 0xfe403fff || (USI)address >= 0xfe404000 && (USI)address <= 0xfe7fffff) frv_queue_data_access_exception_interrupt (current_cpu); return address; } static SI check_readwrite_address (SIM_CPU *current_cpu, SI address, int align_mask) { SIM_DESC sd = CPU_STATE (current_cpu); switch (STATE_ARCHITECTURE (sd)->mach) { case bfd_mach_fr400: address = fr400_check_readwrite_address (current_cpu, address, align_mask); break; case bfd_mach_frvtomcat: case bfd_mach_fr500: case bfd_mach_frv: address = fr500_check_readwrite_address (current_cpu, address, align_mask); break; default: break; } return address; } static PCADDR fr400_check_insn_read_address (SIM_CPU *current_cpu, PCADDR address, int align_mask) { if (address & align_mask) { frv_queue_instruction_access_error_interrupt (current_cpu); address &= ~align_mask; } else if ((USI)address >= 0xfe800000 && (USI)address <= 0xfeffffff) frv_queue_instruction_access_error_interrupt (current_cpu); return address; } static PCADDR fr500_check_insn_read_address (SIM_CPU *current_cpu, PCADDR address, int align_mask) { if (address & align_mask) { frv_queue_mem_address_not_aligned_interrupt (current_cpu, address); address &= ~align_mask; } if ((USI)address >= 0xfeff0600 && (USI)address <= 0xfeff7fff || (USI)address >= 0xfe800000 && (USI)address <= 0xfefeffff) frv_queue_instruction_access_error_interrupt (current_cpu); else if ((USI)address >= 0xfe004000 && (USI)address <= 0xfe3fffff || (USI)address >= 0xfe400000 && (USI)address <= 0xfe403fff || (USI)address >= 0xfe404000 && (USI)address <= 0xfe7fffff) frv_queue_instruction_access_exception_interrupt (current_cpu); else { USI hsr0 = GET_HSR0 (); if (! GET_HSR0_RME (hsr0) && (USI)address >= 0xfe000000 && (USI)address <= 0xfe003fff) frv_queue_instruction_access_exception_interrupt (current_cpu); } return address; } static PCADDR check_insn_read_address (SIM_CPU *current_cpu, PCADDR address, int align_mask) { SIM_DESC sd = CPU_STATE (current_cpu); switch (STATE_ARCHITECTURE (sd)->mach) { case bfd_mach_fr400: address = fr400_check_insn_read_address (current_cpu, address, align_mask); break; case bfd_mach_frvtomcat: case bfd_mach_fr500: case bfd_mach_frv: address = fr500_check_insn_read_address (current_cpu, address, align_mask); break; default: break; } return address; } /* Memory reads. */ QI frvbf_read_mem_QI (SIM_CPU *current_cpu, IADDR pc, SI address) { USI hsr0 = GET_HSR0 (); FRV_CACHE *cache = CPU_DATA_CACHE (current_cpu); /* Check for access exceptions. */ address = check_data_read_address (current_cpu, address, 0); address = check_readwrite_address (current_cpu, address, 0); /* If we need to count cycles, then the cache operation will be initiated from the model profiling functions. See frvbf_model_.... */ if (model_insn) { CPU_LOAD_ADDRESS (current_cpu) = address; CPU_LOAD_LENGTH (current_cpu) = 1; CPU_LOAD_SIGNED (current_cpu) = 1; return 0xb7; /* any random value */ } if (GET_HSR0_DCE (hsr0)) { int cycles; cycles = frv_cache_read (cache, 0, address); if (cycles != 0) return CACHE_RETURN_DATA (cache, 0, address, QI, 1); } return GETMEMQI (current_cpu, pc, address); } UQI frvbf_read_mem_UQI (SIM_CPU *current_cpu, IADDR pc, SI address) { USI hsr0 = GET_HSR0 (); FRV_CACHE *cache = CPU_DATA_CACHE (current_cpu); /* Check for access exceptions. */ address = check_data_read_address (current_cpu, address, 0); address = check_readwrite_address (current_cpu, address, 0); /* If we need to count cycles, then the cache operation will be initiated from the model profiling functions. See frvbf_model_.... */ if (model_insn) { CPU_LOAD_ADDRESS (current_cpu) = address; CPU_LOAD_LENGTH (current_cpu) = 1; CPU_LOAD_SIGNED (current_cpu) = 0; return 0xb7; /* any random value */ } if (GET_HSR0_DCE (hsr0)) { int cycles; cycles = frv_cache_read (cache, 0, address); if (cycles != 0) return CACHE_RETURN_DATA (cache, 0, address, UQI, 1); } return GETMEMUQI (current_cpu, pc, address); } HI frvbf_read_mem_HI (SIM_CPU *current_cpu, IADDR pc, SI address) { USI hsr0; FRV_CACHE *cache; /* Check for access exceptions. */ address = check_data_read_address (current_cpu, address, 1); address = check_readwrite_address (current_cpu, address, 1); /* If we need to count cycles, then the cache operation will be initiated from the model profiling functions. See frvbf_model_.... */ hsr0 = GET_HSR0 (); cache = CPU_DATA_CACHE (current_cpu); if (model_insn) { CPU_LOAD_ADDRESS (current_cpu) = address; CPU_LOAD_LENGTH (current_cpu) = 2; CPU_LOAD_SIGNED (current_cpu) = 1; return 0xb711; /* any random value */ } if (GET_HSR0_DCE (hsr0)) { int cycles; cycles = frv_cache_read (cache, 0, address); if (cycles != 0) return CACHE_RETURN_DATA (cache, 0, address, HI, 2); } return GETMEMHI (current_cpu, pc, address); } UHI frvbf_read_mem_UHI (SIM_CPU *current_cpu, IADDR pc, SI address) { USI hsr0; FRV_CACHE *cache; /* Check for access exceptions. */ address = check_data_read_address (current_cpu, address, 1); address = check_readwrite_address (current_cpu, address, 1); /* If we need to count cycles, then the cache operation will be initiated from the model profiling functions. See frvbf_model_.... */ hsr0 = GET_HSR0 (); cache = CPU_DATA_CACHE (current_cpu); if (model_insn) { CPU_LOAD_ADDRESS (current_cpu) = address; CPU_LOAD_LENGTH (current_cpu) = 2; CPU_LOAD_SIGNED (current_cpu) = 0; return 0xb711; /* any random value */ } if (GET_HSR0_DCE (hsr0)) { int cycles; cycles = frv_cache_read (cache, 0, address); if (cycles != 0) return CACHE_RETURN_DATA (cache, 0, address, UHI, 2); } return GETMEMUHI (current_cpu, pc, address); } SI frvbf_read_mem_SI (SIM_CPU *current_cpu, IADDR pc, SI address) { FRV_CACHE *cache; USI hsr0; /* Check for access exceptions. */ address = check_data_read_address (current_cpu, address, 3); address = check_readwrite_address (current_cpu, address, 3); hsr0 = GET_HSR0 (); cache = CPU_DATA_CACHE (current_cpu); /* If we need to count cycles, then the cache operation will be initiated from the model profiling functions. See frvbf_model_.... */ if (model_insn) { CPU_LOAD_ADDRESS (current_cpu) = address; CPU_LOAD_LENGTH (current_cpu) = 4; return 0x37111319; /* any random value */ } if (GET_HSR0_DCE (hsr0)) { int cycles; cycles = frv_cache_read (cache, 0, address); if (cycles != 0) return CACHE_RETURN_DATA (cache, 0, address, SI, 4); } return GETMEMSI (current_cpu, pc, address); } SI frvbf_read_mem_WI (SIM_CPU *current_cpu, IADDR pc, SI address) { return frvbf_read_mem_SI (current_cpu, pc, address); } DI frvbf_read_mem_DI (SIM_CPU *current_cpu, IADDR pc, SI address) { USI hsr0; FRV_CACHE *cache; /* Check for access exceptions. */ address = check_data_read_address (current_cpu, address, 7); address = check_readwrite_address (current_cpu, address, 7); /* If we need to count cycles, then the cache operation will be initiated from the model profiling functions. See frvbf_model_.... */ hsr0 = GET_HSR0 (); cache = CPU_DATA_CACHE (current_cpu); if (model_insn) { CPU_LOAD_ADDRESS (current_cpu) = address; CPU_LOAD_LENGTH (current_cpu) = 8; return 0x37111319; /* any random value */ } if (GET_HSR0_DCE (hsr0)) { int cycles; cycles = frv_cache_read (cache, 0, address); if (cycles != 0) return CACHE_RETURN_DATA (cache, 0, address, DI, 8); } return GETMEMDI (current_cpu, pc, address); } DF frvbf_read_mem_DF (SIM_CPU *current_cpu, IADDR pc, SI address) { USI hsr0; FRV_CACHE *cache; /* Check for access exceptions. */ address = check_data_read_address (current_cpu, address, 7); address = check_readwrite_address (current_cpu, address, 7); /* If we need to count cycles, then the cache operation will be initiated from the model profiling functions. See frvbf_model_.... */ hsr0 = GET_HSR0 (); cache = CPU_DATA_CACHE (current_cpu); if (model_insn) { CPU_LOAD_ADDRESS (current_cpu) = address; CPU_LOAD_LENGTH (current_cpu) = 8; return 0x37111319; /* any random value */ } if (GET_HSR0_DCE (hsr0)) { int cycles; cycles = frv_cache_read (cache, 0, address); if (cycles != 0) return CACHE_RETURN_DATA (cache, 0, address, DF, 8); } return GETMEMDF (current_cpu, pc, address); } USI frvbf_read_imem_USI (SIM_CPU *current_cpu, PCADDR vpc) { USI hsr0; vpc = check_insn_read_address (current_cpu, vpc, 3); hsr0 = GET_HSR0 (); if (GET_HSR0_ICE (hsr0)) { FRV_CACHE *cache; USI value; /* We don't want this to show up in the cache statistics. That read is done in frvbf_simulate_insn_prefetch. So read the cache or memory passively here. */ cache = CPU_INSN_CACHE (current_cpu); if (frv_cache_read_passive_SI (cache, vpc, &value)) return value; } return sim_core_read_unaligned_4 (current_cpu, vpc, read_map, vpc); } static SI fr400_check_write_address (SIM_CPU *current_cpu, SI address, int align_mask) { if (address & align_mask) { /* On the fr400, this causes a data_access_error. */ /* Make sure that this exception is not masked. */ USI isr = GET_ISR (); if (! GET_ISR_EMAM (isr)) { /* Bad alignment causes a data_access_error on fr400. */ frv_queue_data_access_error_interrupt (current_cpu, address); } address &= ~align_mask; } if (align_mask == 7 && address >= 0xfe800000 && address <= 0xfeffffff) frv_queue_program_interrupt (current_cpu, FRV_DATA_STORE_ERROR); return address; } static SI fr500_check_write_address (SIM_CPU *current_cpu, SI address, int align_mask) { if (address & align_mask) { struct frv_interrupt_queue_element *item = frv_queue_mem_address_not_aligned_interrupt (current_cpu, address); /* Record the correct vliw slot with the interrupt. */ if (item != NULL) item->slot = frv_interrupt_state.slot; address &= ~align_mask; } if (address >= 0xfeff0600 && address <= 0xfeff7fff || address >= 0xfe800000 && address <= 0xfefeffff) frv_queue_program_interrupt (current_cpu, FRV_DATA_STORE_ERROR); return address; } static SI check_write_address (SIM_CPU *current_cpu, SI address, int align_mask) { SIM_DESC sd = CPU_STATE (current_cpu); switch (STATE_ARCHITECTURE (sd)->mach) { case bfd_mach_fr400: address = fr400_check_write_address (current_cpu, address, align_mask); break; case bfd_mach_frvtomcat: case bfd_mach_fr500: case bfd_mach_frv: address = fr500_check_write_address (current_cpu, address, align_mask); break; default: break; } return address; } void frvbf_write_mem_QI (SIM_CPU *current_cpu, IADDR pc, SI address, QI value) { USI hsr0; hsr0 = GET_HSR0 (); if (GET_HSR0_DCE (hsr0)) sim_queue_fn_mem_qi_write (current_cpu, frvbf_mem_set_QI, address, value); else sim_queue_mem_qi_write (current_cpu, address, value); frv_set_write_queue_slot (current_cpu); } void frvbf_write_mem_UQI (SIM_CPU *current_cpu, IADDR pc, SI address, UQI value) { frvbf_write_mem_QI (current_cpu, pc, address, value); } void frvbf_write_mem_HI (SIM_CPU *current_cpu, IADDR pc, SI address, HI value) { USI hsr0; hsr0 = GET_HSR0 (); if (GET_HSR0_DCE (hsr0)) sim_queue_fn_mem_hi_write (current_cpu, frvbf_mem_set_HI, address, value); else sim_queue_mem_hi_write (current_cpu, address, value); frv_set_write_queue_slot (current_cpu); } void frvbf_write_mem_UHI (SIM_CPU *current_cpu, IADDR pc, SI address, UHI value) { frvbf_write_mem_HI (current_cpu, pc, address, value); } void frvbf_write_mem_SI (SIM_CPU *current_cpu, IADDR pc, SI address, SI value) { USI hsr0; hsr0 = GET_HSR0 (); if (GET_HSR0_DCE (hsr0)) sim_queue_fn_mem_si_write (current_cpu, frvbf_mem_set_SI, address, value); else sim_queue_mem_si_write (current_cpu, address, value); frv_set_write_queue_slot (current_cpu); } void frvbf_write_mem_WI (SIM_CPU *current_cpu, IADDR pc, SI address, SI value) { frvbf_write_mem_SI (current_cpu, pc, address, value); } void frvbf_write_mem_DI (SIM_CPU *current_cpu, IADDR pc, SI address, DI value) { USI hsr0; hsr0 = GET_HSR0 (); if (GET_HSR0_DCE (hsr0)) sim_queue_fn_mem_di_write (current_cpu, frvbf_mem_set_DI, address, value); else sim_queue_mem_di_write (current_cpu, address, value); frv_set_write_queue_slot (current_cpu); } void frvbf_write_mem_DF (SIM_CPU *current_cpu, IADDR pc, SI address, DF value) { USI hsr0; hsr0 = GET_HSR0 (); if (GET_HSR0_DCE (hsr0)) sim_queue_fn_mem_df_write (current_cpu, frvbf_mem_set_DF, address, value); else sim_queue_mem_df_write (current_cpu, address, value); frv_set_write_queue_slot (current_cpu); } /* Memory writes. These do the actual writing through the cache. */ void frvbf_mem_set_QI (SIM_CPU *current_cpu, IADDR pc, SI address, QI value) { FRV_CACHE *cache = CPU_DATA_CACHE (current_cpu); /* Check for access errors. */ address = check_write_address (current_cpu, address, 0); address = check_readwrite_address (current_cpu, address, 0); /* If we need to count cycles, then submit the write request to the cache and let it prioritize the request. Otherwise perform the write now. */ if (model_insn) { int slot = UNIT_I0; frv_cache_request_store (cache, address, slot, (char *)&value, sizeof (value)); } else frv_cache_write (cache, address, (char *)&value, sizeof (value)); } void frvbf_mem_set_HI (SIM_CPU *current_cpu, IADDR pc, SI address, HI value) { FRV_CACHE *cache; /* Check for access errors. */ address = check_write_address (current_cpu, address, 1); address = check_readwrite_address (current_cpu, address, 1); /* If we need to count cycles, then submit the write request to the cache and let it prioritize the request. Otherwise perform the write now. */ value = H2T_2 (value); cache = CPU_DATA_CACHE (current_cpu); if (model_insn) { int slot = UNIT_I0; frv_cache_request_store (cache, address, slot, (char *)&value, sizeof (value)); } else frv_cache_write (cache, address, (char *)&value, sizeof (value)); } void frvbf_mem_set_SI (SIM_CPU *current_cpu, IADDR pc, SI address, SI value) { FRV_CACHE *cache; /* Check for access errors. */ address = check_write_address (current_cpu, address, 3); address = check_readwrite_address (current_cpu, address, 3); /* If we need to count cycles, then submit the write request to the cache and let it prioritize the request. Otherwise perform the write now. */ cache = CPU_DATA_CACHE (current_cpu); value = H2T_4 (value); if (model_insn) { int slot = UNIT_I0; frv_cache_request_store (cache, address, slot, (char *)&value, sizeof (value)); } else frv_cache_write (cache, address, (char *)&value, sizeof (value)); } void frvbf_mem_set_DI (SIM_CPU *current_cpu, IADDR pc, SI address, DI value) { FRV_CACHE *cache; /* Check for access errors. */ address = check_write_address (current_cpu, address, 7); address = check_readwrite_address (current_cpu, address, 7); /* If we need to count cycles, then submit the write request to the cache and let it prioritize the request. Otherwise perform the write now. */ value = H2T_8 (value); cache = CPU_DATA_CACHE (current_cpu); if (model_insn) { int slot = UNIT_I0; frv_cache_request_store (cache, address, slot, (char *)&value, sizeof (value)); } else frv_cache_write (cache, address, (char *)&value, sizeof (value)); } void frvbf_mem_set_DF (SIM_CPU *current_cpu, IADDR pc, SI address, DF value) { FRV_CACHE *cache; /* Check for access errors. */ address = check_write_address (current_cpu, address, 7); address = check_readwrite_address (current_cpu, address, 7); /* If we need to count cycles, then submit the write request to the cache and let it prioritize the request. Otherwise perform the write now. */ value = H2T_8 (value); cache = CPU_DATA_CACHE (current_cpu); if (model_insn) { int slot = UNIT_I0; frv_cache_request_store (cache, address, slot, (char *)&value, sizeof (value)); } else frv_cache_write (cache, address, (char *)&value, sizeof (value)); } void frvbf_mem_set_XI (SIM_CPU *current_cpu, IADDR pc, SI address, SI *value) { int i; FRV_CACHE *cache; /* Check for access errors. */ address = check_write_address (current_cpu, address, 0xf); address = check_readwrite_address (current_cpu, address, 0xf); /* TODO -- reverse word order as well? */ for (i = 0; i < 4; ++i) value[i] = H2T_4 (value[i]); /* If we need to count cycles, then submit the write request to the cache and let it prioritize the request. Otherwise perform the write now. */ cache = CPU_DATA_CACHE (current_cpu); if (model_insn) { int slot = UNIT_I0; frv_cache_request_store (cache, address, slot, (char*)value, 16); } else frv_cache_write (cache, address, (char*)value, 16); } /* Record the current VLIW slot on the element at the top of the write queue. */ void frv_set_write_queue_slot (SIM_CPU *current_cpu) { FRV_VLIW *vliw = CPU_VLIW (current_cpu); int slot = vliw->next_slot - 1; CGEN_WRITE_QUEUE *q = CPU_WRITE_QUEUE (current_cpu); int ix = CGEN_WRITE_QUEUE_INDEX (q) - 1; CGEN_WRITE_QUEUE_ELEMENT *item = CGEN_WRITE_QUEUE_ELEMENT (q, ix); CGEN_WRITE_QUEUE_ELEMENT_PIPE (item) = (*vliw->current_vliw)[slot]; }