memory.c

Go to the documentation of this file.
00001 /*
00002  * $Id: memory.c,v 1.24 2009/02/24 21:10:12 joerg_wunsch Exp $
00003  *
00004  ****************************************************************************
00005  *
00006  * simulavr - A simulator for the Atmel AVR family of microcontrollers.
00007  * Copyright (C) 2001, 2002, 2003, 2004  Theodore A. Roth
00008  *
00009  * This program is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00022  *
00023  ****************************************************************************
00024  */
00025 
00026 /**
00027  * \file memory.c
00028  * \brief Memory access functions.
00029  *
00030  * This module provides functions for reading and writing to simulated memory.
00031  * The Memory class is a subclass of AvrClass.
00032  */
00033 
00034 #include <config.h>
00035 
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038 #include <string.h>
00039 
00040 #include "avrerror.h"
00041 #include "avrmalloc.h"
00042 #include "avrclass.h"
00043 #include "utils.h"
00044 #include "callback.h"
00045 #include "op_names.h"
00046 
00047 #include "storage.h"
00048 #include "flash.h"
00049 
00050 #include "vdevs.h"
00051 #include "memory.h"
00052 #include "stack.h"
00053 #include "register.h"
00054 #include "sram.h"
00055 #include "eeprom.h"
00056 #include "timers.h"
00057 #include "ports.h"
00058 
00059 #include "avrcore.h"
00060 
00061 #include "display.h"
00062 
00063 /** \brief Allocates memory for a new memory object. */
00064 
00065 Memory *
00066 mem_new (int gpwr_end, int io_reg_end, int sram_end, int xram_end)
00067 {
00068     Memory *mem;
00069 
00070     mem = avr_new0 (Memory, 1);
00071     mem_construct (mem, gpwr_end, io_reg_end, sram_end, xram_end);
00072     class_overload_destroy ((AvrClass *)mem, mem_destroy);
00073 
00074     return mem;
00075 }
00076 
00077 /** \brief Constructor for the memory object. */
00078 
00079 void
00080 mem_construct (Memory *mem, int gpwr_end, int io_reg_end, int sram_end,
00081                int xram_end)
00082 {
00083     if (mem == NULL)
00084         avr_error ("passed null ptr");
00085 
00086     mem->gpwr_end = gpwr_end;
00087     mem->io_reg_end = io_reg_end;
00088     mem->sram_end = sram_end;
00089     mem->xram_end = xram_end;
00090 
00091     mem->cell = avr_new0 (MemoryCell, xram_end + 1);
00092 
00093     class_construct ((AvrClass *)mem);
00094 }
00095 
00096 /** \brief Descructor for the memory object. */
00097 
00098 void
00099 mem_destroy (void *mem)
00100 {
00101     int i;
00102 
00103     Memory *this = (Memory *)mem;
00104 
00105     if (mem == NULL)
00106         return;
00107 
00108     for (i = 0; i < this->xram_end; i++)
00109     {
00110         if (this->cell[i].vdev)
00111         {
00112             class_unref ((AvrClass *)this->cell[i].vdev);
00113         }
00114     }
00115 
00116     avr_free (this->cell);
00117 
00118     class_destroy (mem);
00119 }
00120 
00121 /** \brief Attach a device to the device list.
00122  
00123   Devices that are accessed more often should be attached
00124   last so that they will be at the front of the list.
00125  
00126   A default virtual device can be overridden by attaching
00127   a new device ahead of it in the list.  */
00128 
00129 void
00130 mem_attach (Memory *mem, int addr, char *name, VDevice *vdev, int flags,
00131             uint8_t reset_value, uint8_t rd_mask, uint8_t wr_mask)
00132 {
00133     MemoryCell *cell;
00134 
00135     if (mem == NULL)
00136         avr_error ("passed null ptr");
00137 
00138     if (vdev == NULL)
00139         avr_error ("attempt to attach null device");
00140 
00141     if ((addr < 0) || (addr > mem->xram_end))
00142         avr_error ("address out of range");
00143 
00144     cell = &mem->cell[addr];
00145 
00146     cell->name = name;
00147     cell->flags = flags;
00148     cell->reset_value = reset_value;
00149     cell->rd_mask = rd_mask;
00150     cell->wr_mask = wr_mask;
00151 
00152     class_ref ((AvrClass *)vdev);
00153     cell->vdev = vdev;
00154 }
00155 
00156 /** \brief Find the VDevice associated with the given address. */
00157 
00158 VDevice *
00159 mem_get_vdevice_by_addr (Memory *mem, int addr)
00160 {
00161     return mem->cell[addr].vdev;
00162 }
00163 
00164 /** \brief Find the VDevice associated with the given name. 
00165 
00166     \deprecated */
00167 
00168 VDevice *
00169 mem_get_vdevice_by_name (Memory *mem, char *name)
00170 {
00171 #if 0
00172     return (VDevice *)dlist_lookup (mem->dev, (AvrClass *)name,
00173                                     vdev_name_cmp);
00174 #else
00175     avr_error ("use of deprecated interface");
00176     return NULL;
00177 #endif
00178 }
00179 
00180 static MemoryCell *
00181 mem_get_cell (Memory *mem, int addr)
00182 {
00183     /*
00184      * Just return *any* possible cell for illegal addresses,
00185      * to avoid accessing cells that have not been allocated.
00186      * Later on, the illegal address will be caught anyway.
00187      */
00188     if ((addr < 0) || (addr > mem->xram_end))
00189         addr = mem->xram_end - 1;
00190 
00191     return mem->cell + addr;
00192 }
00193 
00194 static int
00195 mem_is_io_reg (Memory *mem, int addr)
00196 {
00197     return ((addr > mem->gpwr_end) && (addr <= mem->io_reg_end));
00198 }
00199 
00200 static char *
00201 mem_get_name (Memory *mem, int addr)
00202 {
00203     return mem->cell[addr].name;
00204 }
00205 
00206 void
00207 mem_set_addr_name (Memory *mem, int addr, char *name)
00208 {
00209     mem->cell[addr].name = name;
00210 }
00211 
00212 /** \brief Reads byte from memory and sanity-checks for valid address. 
00213  * 
00214  * \param mem A pointer to the memory object
00215  * \param addr The address to be read 
00216  * \return The byte found at that address addr
00217  */
00218 
00219 uint8_t
00220 mem_read (Memory *mem, int addr)
00221 {
00222     MemoryCell *cell = mem_get_cell (mem, addr);
00223 
00224     if (cell->vdev == NULL)
00225     {
00226         char *name = mem_get_name (mem, addr);
00227 
00228         if (name)
00229         {
00230             avr_warning ("**** Attempt to read invalid %s: %s at 0x%04x\n",
00231                          mem_is_io_reg (mem, addr) ? "io reg" : "mem addr",
00232                          name, addr);
00233         }
00234         else
00235         {
00236             avr_warning ("**** Attempt to read invalid %s: 0x%04x\n",
00237                          mem_is_io_reg (mem, addr) ? "io reg" : "mem addr",
00238                          addr);
00239         }
00240 
00241         return 0;
00242     }
00243 
00244     return (vdev_read (cell->vdev, addr) & cell->rd_mask);
00245 }
00246 
00247 /** \brief Writes byte to memory and updates display for io registers. 
00248  * 
00249  * \param mem A pointer to a memory object
00250  * \param addr The address to be written to
00251  * \param val The value to be written there
00252  */
00253 
00254 void
00255 mem_write (Memory *mem, int addr, uint8_t val)
00256 {
00257     MemoryCell *cell = mem_get_cell (mem, addr);
00258 
00259     if (cell->vdev == NULL)
00260     {
00261         char *name = mem_get_name (mem, addr);
00262 
00263         if (name)
00264         {
00265             avr_warning ("**** Attempt to write invalid %s: %s at 0x%04x\n",
00266                          mem_is_io_reg (mem, addr) ? "io reg" : "mem addr",
00267                          name, addr);
00268         }
00269         else
00270         {
00271             avr_warning ("**** Attempt to write invalid %s: 0x%04x\n",
00272                          mem_is_io_reg (mem, addr) ? "io reg" : "mem addr",
00273                          addr);
00274         }
00275 
00276         return;
00277     }
00278 
00279     /* update the display for io registers here */
00280 
00281     if (mem_is_io_reg (mem, addr))
00282         display_io_reg (addr - (mem->gpwr_end + 1), val & cell->wr_mask);
00283 
00284     vdev_write (cell->vdev, addr, val & cell->wr_mask);
00285 }
00286 
00287 /** \brief Resets every device in the memory object.
00288  * \param mem A pointer to the memory object.
00289  */
00290 
00291 void
00292 mem_reset (Memory *mem)
00293 {
00294     int i;
00295 
00296     for (i = 0; i < mem->xram_end; i++)
00297     {
00298         MemoryCell *cell = mem_get_cell (mem, i);
00299 
00300         if (cell->vdev)
00301             vdev_reset (cell->vdev);
00302     }
00303 }
00304 
00305 static void
00306 mem_reg_dump_core (Memory *mem, FILE * f_core)
00307 {
00308     int i, j;
00309 
00310     fprintf (f_core, "General Purpose Register Dump:\n");
00311     for (i = 0; i < 32; i += 8)
00312     {
00313         for (j = i; j < (i + 8); j++)
00314             fprintf (f_core, "r%02d=%02x  ", j, mem_read (mem, j));
00315         fprintf (f_core, "\n");
00316     }
00317     fprintf (f_core, "\n");
00318 }
00319 
00320 /** \brief Fetch the name and value of the io register (addr). 
00321  *
00322  * \param mem A pointer to the memory object.
00323  * \param addr The address to fetch from.
00324  * \param val A pointer where the value of the register is to be copied.
00325  * \param buf A pointer to where the name of the register should be copied.
00326  * \param bufsiz The maximum size of the the buf string.
00327  */
00328 
00329 void
00330 mem_io_fetch (Memory *mem, int addr, uint8_t * val, char *buf, int bufsiz)
00331 {
00332     MemoryCell *cell;
00333 
00334     if (mem_is_io_reg (mem, addr))
00335     {
00336         cell = mem_get_cell (mem, addr);
00337 
00338         if (cell->name == NULL)
00339         {
00340             strncpy (buf, "Reserved", bufsiz);
00341             *val = 0;
00342         }
00343         else
00344         {
00345             strncpy (buf, cell->name, bufsiz);
00346 
00347             if (cell->vdev)
00348             {
00349                 /* FIXME: Add vdev_read_no_ext () interface to avoid calling
00350                    the external functions during a read. This will require a
00351                    reworking of how the vdev invokes the external read
00352                    method. */
00353 
00354                 *val = (vdev_read (cell->vdev, addr) & cell->rd_mask);
00355             }
00356             else
00357             {
00358                 *val = 0;
00359             }
00360         }
00361     }
00362     else
00363     {
00364         *val = 0;
00365         strncpy (buf, "NOT AN IO REG", bufsiz);
00366     }
00367 }
00368 
00369 static void
00370 mem_io_reg_dump_core (Memory *mem, FILE * f_core)
00371 {
00372     unsigned int i, j;
00373     char name[80];
00374     uint8_t val;
00375 
00376     unsigned int begin = (unsigned)mem->gpwr_end + 1;
00377     unsigned int end = (unsigned)mem->io_reg_end;
00378     unsigned int half = (end - begin + 1) / 2;
00379     unsigned int mid = begin + half;
00380 
00381     fprintf (f_core, "IO Register Dump:\n");
00382     for (i = begin; i < mid; i++)
00383     {
00384         for (j = i; j <= end; j += half)
00385         {
00386             memset (name, '\0', sizeof (name));
00387             mem_io_fetch (mem, j, &val, name, sizeof (name) - 1);
00388 
00389             fprintf (f_core, "%02x : %-10s : 0x%02x               ", j,
00390                      name, val);
00391         }
00392         fprintf (f_core, "\n");
00393     }
00394     fprintf (f_core, "\n");
00395 }
00396 
00397 static void
00398 mem_sram_display (Memory *mem, FILE * f_core, int base, int size)
00399 {
00400     int i;
00401     int dup = 0;
00402     int ndat = 16;
00403     char line[80];
00404     char last_line[80];
00405     char buf[80];
00406     line[0] = last_line[0] = '\0';
00407 
00408     for (i = base; i < (base + size); i++)
00409     {
00410         if (((i % ndat) == 0) && strlen (line))
00411         {
00412             if (strncmp (line, last_line, 80) == 0)
00413             {
00414                 dup++;
00415             }
00416             else
00417             {
00418                 if (dup > 0)
00419                     fprintf (f_core, "  -- last line repeats --\n");
00420                 fprintf (f_core, "%04x : %s\n", i - ndat, line);
00421                 dup = 0;
00422             }
00423             strncpy (last_line, line, 80);
00424             line[0] = '\0';
00425         }
00426         snprintf (buf, 80, "%02x ", mem_read (mem, i));
00427         strncat (line, buf, 80);
00428     }
00429     if (dup > 0)
00430     {
00431         fprintf (f_core, "  -- last line repeats --\n");
00432         fprintf (f_core, "%04x : %s\n", i - ndat, line);
00433     }
00434     fprintf (f_core, "\n");
00435 }
00436 
00437 static void
00438 mem_sram_dump_core (Memory *mem, FILE * f_core)
00439 {
00440     int size, base;
00441 
00442     /*
00443      * Dump the internal sram
00444      */
00445 
00446     if (mem->io_reg_end == mem->sram_end)
00447         return;                 /* device has no sram */
00448 
00449     fprintf (f_core, "Internal SRAM Memory Dump:\n");
00450     base = mem->io_reg_end + 1;
00451     size = mem->sram_end - base + 1;
00452     mem_sram_display (mem, f_core, base, size);
00453 
00454     /*
00455      * If external sram present, dump it too.
00456      */
00457 
00458     if (mem->sram_end == mem->xram_end)
00459         return;                 /* device has no xram */
00460 
00461     fprintf (f_core, "External SRAM Memory Dump:\n");
00462     base = mem->sram_end + 1;
00463     size = mem->xram_end - base + 1;
00464     mem_sram_display (mem, f_core, base, size);
00465 
00466 }
00467 
00468 #if 0
00469 
00470 /* FIXME: Still need to figure out a sane way to look up a specific type of
00471    vdev by generic name. */
00472 
00473 static void
00474 mem_eeprom_dump_core (Memory *mem, FILE * f_core)
00475 {
00476     VDevice *dev = mem_get_vdevice_by_name (mem, "EEProm");
00477 
00478     if (dev != NULL)
00479         eeprom_dump_core ((EEProm *)dev, f_core);
00480 }
00481 #endif
00482 
00483 /** \brief Dump all the various memory locations to a file descriptor 
00484  *         in text format.
00485  *
00486  *  \param mem A memory object.
00487  *  \param f_core An open file descriptor.
00488  */
00489 
00490 void
00491 mem_dump_core (Memory *mem, FILE * f_core)
00492 {
00493     mem_reg_dump_core (mem, f_core);
00494     mem_io_reg_dump_core (mem, f_core);
00495     mem_sram_dump_core (mem, f_core);
00496 /*     mem_eeprom_dump_core (mem, f_core); */
00497 }