| /* frv memory model. | |
| Copyright (C) 1999, 2000, 2001, 2003, 2007, 2008, 2009, 2010 | |
| 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/>. */ | |
| #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 | |
| fr550_check_data_read_address (SIM_CPU *current_cpu, SI address, int align_mask) | |
| { | |
| if ((USI)address >= 0xfe800000 && (USI)address <= 0xfefeffff | |
| || (align_mask > 0x3 | |
| && ((USI)address >= 0xfeff0000 && (USI)address <= 0xfeffffff))) | |
| 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: | |
| case bfd_mach_fr450: | |
| 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; | |
| case bfd_mach_fr550: | |
| address = fr550_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 | |
| fr550_check_readwrite_address (SIM_CPU *current_cpu, SI address, int align_mask) | |
| { | |
| /* No alignment restrictions on fr550 */ | |
| if ((USI)address >= 0xfe000000 && (USI)address <= 0xfe3fffff | |
| || (USI)address >= 0xfe408000 && (USI)address <= 0xfe7fffff) | |
| frv_queue_data_access_exception_interrupt (current_cpu); | |
| else | |
| { | |
| USI hsr0 = GET_HSR0 (); | |
| if (! GET_HSR0_RME (hsr0) | |
| && (USI)address >= 0xfe400000 && (USI)address <= 0xfe407fff) | |
| 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: | |
| case bfd_mach_fr450: | |
| 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; | |
| case bfd_mach_fr550: | |
| address = fr550_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 | |
| fr550_check_insn_read_address (SIM_CPU *current_cpu, PCADDR address, | |
| int align_mask) | |
| { | |
| address &= ~align_mask; | |
| if ((USI)address >= 0xfe800000 && (USI)address <= 0xfeffffff) | |
| frv_queue_instruction_access_error_interrupt (current_cpu); | |
| else if ((USI)address >= 0xfe008000 && (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 <= 0xfe007fff) | |
| 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: | |
| case bfd_mach_fr450: | |
| 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; | |
| case bfd_mach_fr550: | |
| address = fr550_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); | |
| } | |
| /* Read a HI which spans two cache lines */ | |
| static HI | |
| read_mem_unaligned_HI (SIM_CPU *current_cpu, IADDR pc, SI address) | |
| { | |
| HI value = frvbf_read_mem_QI (current_cpu, pc, address); | |
| value <<= 8; | |
| value |= frvbf_read_mem_UQI (current_cpu, pc, address + 1); | |
| return T2H_2 (value); | |
| } | |
| 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; | |
| /* Handle access which crosses cache line boundary */ | |
| SIM_DESC sd = CPU_STATE (current_cpu); | |
| if (STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr550) | |
| { | |
| if (DATA_CROSSES_CACHE_LINE (cache, address, 2)) | |
| return read_mem_unaligned_HI (current_cpu, pc, address); | |
| } | |
| 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; | |
| /* Handle access which crosses cache line boundary */ | |
| SIM_DESC sd = CPU_STATE (current_cpu); | |
| if (STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr550) | |
| { | |
| if (DATA_CROSSES_CACHE_LINE (cache, address, 2)) | |
| return read_mem_unaligned_HI (current_cpu, pc, address); | |
| } | |
| 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); | |
| } | |
| /* Read a SI which spans two cache lines */ | |
| static SI | |
| read_mem_unaligned_SI (SIM_CPU *current_cpu, IADDR pc, SI address) | |
| { | |
| FRV_CACHE *cache = CPU_DATA_CACHE (current_cpu); | |
| unsigned hi_len = cache->line_size - (address & (cache->line_size - 1)); | |
| char valarray[4]; | |
| SI SIvalue; | |
| HI HIvalue; | |
| switch (hi_len) | |
| { | |
| case 1: | |
| valarray[0] = frvbf_read_mem_QI (current_cpu, pc, address); | |
| SIvalue = frvbf_read_mem_SI (current_cpu, pc, address + 1); | |
| SIvalue = H2T_4 (SIvalue); | |
| memcpy (valarray + 1, (char*)&SIvalue, 3); | |
| break; | |
| case 2: | |
| HIvalue = frvbf_read_mem_HI (current_cpu, pc, address); | |
| HIvalue = H2T_2 (HIvalue); | |
| memcpy (valarray, (char*)&HIvalue, 2); | |
| HIvalue = frvbf_read_mem_HI (current_cpu, pc, address + 2); | |
| HIvalue = H2T_2 (HIvalue); | |
| memcpy (valarray + 2, (char*)&HIvalue, 2); | |
| break; | |
| case 3: | |
| SIvalue = frvbf_read_mem_SI (current_cpu, pc, address - 1); | |
| SIvalue = H2T_4 (SIvalue); | |
| memcpy (valarray, (char*)&SIvalue, 3); | |
| valarray[3] = frvbf_read_mem_QI (current_cpu, pc, address + 3); | |
| break; | |
| default: | |
| abort (); /* can't happen */ | |
| } | |
| return T2H_4 (*(SI*)valarray); | |
| } | |
| 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; | |
| /* Handle access which crosses cache line boundary */ | |
| SIM_DESC sd = CPU_STATE (current_cpu); | |
| if (STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr550) | |
| { | |
| if (DATA_CROSSES_CACHE_LINE (cache, address, 4)) | |
| return read_mem_unaligned_SI (current_cpu, pc, address); | |
| } | |
| 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); | |
| } | |
| /* Read a SI which spans two cache lines */ | |
| static DI | |
| read_mem_unaligned_DI (SIM_CPU *current_cpu, IADDR pc, SI address) | |
| { | |
| FRV_CACHE *cache = CPU_DATA_CACHE (current_cpu); | |
| unsigned hi_len = cache->line_size - (address & (cache->line_size - 1)); | |
| DI value, value1; | |
| switch (hi_len) | |
| { | |
| case 1: | |
| value = frvbf_read_mem_QI (current_cpu, pc, address); | |
| value <<= 56; | |
| value1 = frvbf_read_mem_DI (current_cpu, pc, address + 1); | |
| value1 = H2T_8 (value1); | |
| value |= value1 & ((DI)0x00ffffff << 32); | |
| value |= value1 & 0xffffffffu; | |
| break; | |
| case 2: | |
| value = frvbf_read_mem_HI (current_cpu, pc, address); | |
| value = H2T_2 (value); | |
| value <<= 48; | |
| value1 = frvbf_read_mem_DI (current_cpu, pc, address + 2); | |
| value1 = H2T_8 (value1); | |
| value |= value1 & ((DI)0x0000ffff << 32); | |
| value |= value1 & 0xffffffffu; | |
| break; | |
| case 3: | |
| value = frvbf_read_mem_SI (current_cpu, pc, address - 1); | |
| value = H2T_4 (value); | |
| value <<= 40; | |
| value1 = frvbf_read_mem_DI (current_cpu, pc, address + 3); | |
| value1 = H2T_8 (value1); | |
| value |= value1 & ((DI)0x000000ff << 32); | |
| value |= value1 & 0xffffffffu; | |
| break; | |
| case 4: | |
| value = frvbf_read_mem_SI (current_cpu, pc, address); | |
| value = H2T_4 (value); | |
| value <<= 32; | |
| value1 = frvbf_read_mem_SI (current_cpu, pc, address + 4); | |
| value1 = H2T_4 (value1); | |
| value |= value1 & 0xffffffffu; | |
| break; | |
| case 5: | |
| value = frvbf_read_mem_DI (current_cpu, pc, address - 3); | |
| value = H2T_8 (value); | |
| value <<= 24; | |
| value1 = frvbf_read_mem_SI (current_cpu, pc, address + 5); | |
| value1 = H2T_4 (value1); | |
| value |= value1 & 0x00ffffff; | |
| break; | |
| case 6: | |
| value = frvbf_read_mem_DI (current_cpu, pc, address - 2); | |
| value = H2T_8 (value); | |
| value <<= 16; | |
| value1 = frvbf_read_mem_HI (current_cpu, pc, address + 6); | |
| value1 = H2T_2 (value1); | |
| value |= value1 & 0x0000ffff; | |
| break; | |
| case 7: | |
| value = frvbf_read_mem_DI (current_cpu, pc, address - 1); | |
| value = H2T_8 (value); | |
| value <<= 8; | |
| value1 = frvbf_read_mem_QI (current_cpu, pc, address + 7); | |
| value |= value1 & 0x000000ff; | |
| break; | |
| default: | |
| abort (); /* can't happen */ | |
| } | |
| return T2H_8 (value); | |
| } | |
| 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; | |
| /* Handle access which crosses cache line boundary */ | |
| SIM_DESC sd = CPU_STATE (current_cpu); | |
| if (STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr550) | |
| { | |
| if (DATA_CROSSES_CACHE_LINE (cache, address, 8)) | |
| return read_mem_unaligned_DI (current_cpu, pc, address); | |
| } | |
| 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; | |
| /* Handle access which crosses cache line boundary */ | |
| SIM_DESC sd = CPU_STATE (current_cpu); | |
| if (STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr550) | |
| { | |
| if (DATA_CROSSES_CACHE_LINE (cache, address, 8)) | |
| return read_mem_unaligned_DI (current_cpu, pc, address); | |
| } | |
| 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 (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 | |
| fr550_check_write_address (SIM_CPU *current_cpu, SI address, int align_mask) | |
| { | |
| if ((USI)address >= 0xfe800000 && (USI)address <= 0xfefeffff | |
| || (align_mask > 0x3 | |
| && ((USI)address >= 0xfeff0000 && (USI)address <= 0xfeffffff))) | |
| 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: | |
| case bfd_mach_fr450: | |
| 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; | |
| case bfd_mach_fr550: | |
| address = fr550_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)); | |
| } | |
| /* Write a HI which spans two cache lines */ | |
| static void | |
| mem_set_unaligned_HI (SIM_CPU *current_cpu, IADDR pc, SI address, HI value) | |
| { | |
| FRV_CACHE *cache = CPU_DATA_CACHE (current_cpu); | |
| /* value is already in target byte order */ | |
| frv_cache_write (cache, address, (char *)&value, 1); | |
| frv_cache_write (cache, address + 1, ((char *)&value + 1), 1); | |
| } | |
| 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 | |
| { | |
| /* Handle access which crosses cache line boundary */ | |
| SIM_DESC sd = CPU_STATE (current_cpu); | |
| if (STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr550) | |
| { | |
| if (DATA_CROSSES_CACHE_LINE (cache, address, 2)) | |
| { | |
| mem_set_unaligned_HI (current_cpu, pc, address, value); | |
| return; | |
| } | |
| } | |
| frv_cache_write (cache, address, (char *)&value, sizeof (value)); | |
| } | |
| } | |
| /* Write a SI which spans two cache lines */ | |
| static void | |
| mem_set_unaligned_SI (SIM_CPU *current_cpu, IADDR pc, SI address, SI value) | |
| { | |
| FRV_CACHE *cache = CPU_DATA_CACHE (current_cpu); | |
| unsigned hi_len = cache->line_size - (address & (cache->line_size - 1)); | |
| /* value is already in target byte order */ | |
| frv_cache_write (cache, address, (char *)&value, hi_len); | |
| frv_cache_write (cache, address + hi_len, (char *)&value + hi_len, 4 - hi_len); | |
| } | |
| 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 | |
| { | |
| /* Handle access which crosses cache line boundary */ | |
| SIM_DESC sd = CPU_STATE (current_cpu); | |
| if (STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr550) | |
| { | |
| if (DATA_CROSSES_CACHE_LINE (cache, address, 4)) | |
| { | |
| mem_set_unaligned_SI (current_cpu, pc, address, value); | |
| return; | |
| } | |
| } | |
| frv_cache_write (cache, address, (char *)&value, sizeof (value)); | |
| } | |
| } | |
| /* Write a DI which spans two cache lines */ | |
| static void | |
| mem_set_unaligned_DI (SIM_CPU *current_cpu, IADDR pc, SI address, DI value) | |
| { | |
| FRV_CACHE *cache = CPU_DATA_CACHE (current_cpu); | |
| unsigned hi_len = cache->line_size - (address & (cache->line_size - 1)); | |
| /* value is already in target byte order */ | |
| frv_cache_write (cache, address, (char *)&value, hi_len); | |
| frv_cache_write (cache, address + hi_len, (char *)&value + hi_len, 8 - hi_len); | |
| } | |
| 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 | |
| { | |
| /* Handle access which crosses cache line boundary */ | |
| SIM_DESC sd = CPU_STATE (current_cpu); | |
| if (STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr550) | |
| { | |
| if (DATA_CROSSES_CACHE_LINE (cache, address, 8)) | |
| { | |
| mem_set_unaligned_DI (current_cpu, pc, address, value); | |
| return; | |
| } | |
| } | |
| 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 | |
| { | |
| /* Handle access which crosses cache line boundary */ | |
| SIM_DESC sd = CPU_STATE (current_cpu); | |
| if (STATE_ARCHITECTURE (sd)->mach == bfd_mach_fr550) | |
| { | |
| if (DATA_CROSSES_CACHE_LINE (cache, address, 8)) | |
| { | |
| mem_set_unaligned_DI (current_cpu, pc, address, value); | |
| return; | |
| } | |
| } | |
| 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]; | |
| } |