Update copyright notices to add year 2010.
[deliverable/binutils-gdb.git] / sim / frv / memory.c
index 603f7bbff8abbbe00ed961411bce9db6539ca870..8a7bec48e4dade3633e4a0602443b035cd0b9b6b 100644 (file)
@@ -1,22 +1,22 @@
 /* frv memory model.
-   Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
-   Contributed by Red Hat.
+   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 2, or (at your option)
-any later version.
+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, write to the Free Software Foundation, Inc.,
-59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+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
@@ -55,6 +55,17 @@ fr500_check_data_read_address (SIM_CPU *current_cpu, SI address, int align_mask)
   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)
 {
@@ -62,6 +73,7 @@ check_data_read_address (SIM_CPU *current_cpu, SI address, int align_mask)
   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;
@@ -71,6 +83,10 @@ check_data_read_address (SIM_CPU *current_cpu, SI address, int align_mask)
       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;
     }
@@ -108,6 +124,25 @@ fr500_check_readwrite_address (SIM_CPU *current_cpu, SI address, int align_mask)
   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)
 {
@@ -115,6 +150,7 @@ check_readwrite_address (SIM_CPU *current_cpu, SI address, int align_mask)
   switch (STATE_ARCHITECTURE (sd)->mach)
     {
     case bfd_mach_fr400:
+    case bfd_mach_fr450:
       address = fr400_check_readwrite_address (current_cpu, address,
                                                    align_mask);
       break;
@@ -124,6 +160,10 @@ check_readwrite_address (SIM_CPU *current_cpu, SI address, int align_mask)
       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;
     }
@@ -174,6 +214,27 @@ fr500_check_insn_read_address (SIM_CPU *current_cpu, PCADDR address,
   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)
 {
@@ -181,6 +242,7 @@ check_insn_read_address (SIM_CPU *current_cpu, PCADDR address, int align_mask)
   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;
@@ -190,6 +252,10 @@ check_insn_read_address (SIM_CPU *current_cpu, PCADDR address, int align_mask)
       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;
     }
@@ -262,6 +328,16 @@ frvbf_read_mem_UQI (SIM_CPU *current_cpu, IADDR pc, SI address)
   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)
 {
@@ -288,6 +364,13 @@ frvbf_read_mem_HI (SIM_CPU *current_cpu, IADDR pc, SI address)
   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);
@@ -322,6 +405,13 @@ frvbf_read_mem_UHI (SIM_CPU *current_cpu, IADDR pc, SI address)
   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);
@@ -330,6 +420,44 @@ frvbf_read_mem_UHI (SIM_CPU *current_cpu, IADDR pc, SI address)
   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)
 {
@@ -355,6 +483,13 @@ frvbf_read_mem_SI (SIM_CPU *current_cpu, IADDR pc, SI address)
   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);
@@ -369,6 +504,79 @@ 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)
 {
@@ -394,6 +602,13 @@ frvbf_read_mem_DI (SIM_CPU *current_cpu, IADDR pc, SI address)
   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);
@@ -427,6 +642,13 @@ frvbf_read_mem_DF (SIM_CPU *current_cpu, IADDR pc, SI address)
   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);
@@ -460,18 +682,6 @@ frvbf_read_imem_USI (SIM_CPU *current_cpu, PCADDR 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);
@@ -498,6 +708,17 @@ fr500_check_write_address (SIM_CPU *current_cpu, SI address, int align_mask)
   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)
 {
@@ -505,6 +726,7 @@ check_write_address (SIM_CPU *current_cpu, SI address, int align_mask)
   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:
@@ -512,6 +734,9 @@ check_write_address (SIM_CPU *current_cpu, SI address, int align_mask)
     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;
     }
@@ -618,6 +843,16 @@ frvbf_mem_set_QI (SIM_CPU *current_cpu, IADDR pc, SI address, QI value)
     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)
 {
@@ -638,7 +873,30 @@ frvbf_mem_set_HI (SIM_CPU *current_cpu, IADDR pc, SI address, HI value)
                               (char *)&value, sizeof (value));
     }
   else
-    frv_cache_write (cache, address, (char *)&value, sizeof (value));
+    {
+      /* 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
@@ -661,7 +919,30 @@ frvbf_mem_set_SI (SIM_CPU *current_cpu, IADDR pc, SI address, SI value)
                               (char *)&value, sizeof (value));
     }
   else
-    frv_cache_write (cache, address, (char *)&value, sizeof (value));
+    {
+      /* 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
@@ -684,7 +965,19 @@ frvbf_mem_set_DI (SIM_CPU *current_cpu, IADDR pc, SI address, DI value)
                               (char *)&value, sizeof (value));
     }
   else
-    frv_cache_write (cache, address, (char *)&value, sizeof (value));
+    {
+      /* 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
@@ -707,7 +1000,19 @@ frvbf_mem_set_DF (SIM_CPU *current_cpu, IADDR pc, SI address, DF value)
                               (char *)&value, sizeof (value));
     }
   else
-    frv_cache_write (cache, address, (char *)&value, sizeof (value));
+    {
+      /* 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
This page took 0.030104 seconds and 4 git commands to generate.