spi.c

Go to the documentation of this file.
00001 /*
00002  * $Id: spi.c,v 1.5 2004/03/13 19:55:34 troth Exp $
00003  *
00004  ****************************************************************************
00005  *
00006  * simulavr - A simulator for the Atmel AVR family of microcontrollers.
00007  * Copyright (C) 2003, 2004  Keith Gudger
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 spi.c
00028  * \brief Module to simulate the AVR's SPI module.
00029  *
00030  */
00031 
00032 #include <config.h>
00033 
00034 #include <stdio.h>
00035 #include <stdlib.h>
00036 #include <string.h>
00037 
00038 #include "avrerror.h"
00039 #include "avrmalloc.h"
00040 #include "avrclass.h"
00041 #include "utils.h"
00042 #include "callback.h"
00043 #include "op_names.h"
00044 
00045 #include "storage.h"
00046 #include "flash.h"
00047 
00048 #include "vdevs.h"
00049 #include "memory.h"
00050 #include "stack.h"
00051 #include "register.h"
00052 #include "sram.h"
00053 #include "eeprom.h"
00054 #include "timers.h"
00055 #include "ports.h"
00056 #include "spi.h"
00057 
00058 #include "avrcore.h"
00059 
00060 #include "intvects.h"
00061 
00062 /****************************************************************************\
00063  *
00064  * SPI Interrupts 
00065  *
00066 \****************************************************************************/
00067 
00068 static void spii_add_addr (VDevice *vdev, int addr, char *name, int rel_addr,
00069                            void *data);
00070 static uint8_t spi_intr_read (VDevice *dev, int addr);
00071 static void spi_intr_write (VDevice *dev, int addr, uint8_t val);
00072 static void spi_intr_reset (VDevice *dev);
00073 static int spi_intr_cb (uint64_t time, AvrClass *data);
00074 
00075 /** \brief Allocate a new SPI interrupt */
00076 
00077 VDevice *
00078 spii_create (int addr, char *name, int rel_addr, void *data)
00079 {
00080     return (VDevice *)spi_intr_new (addr, name);
00081 }
00082 
00083 SPIIntr_T *
00084 spi_intr_new (int addr, char *name)
00085 {
00086     SPIIntr_T *spi;
00087 
00088     spi = avr_new (SPIIntr_T, 1);
00089     spi_intr_construct (spi, addr, name);
00090     class_overload_destroy ((AvrClass *)spi, spi_intr_destroy);
00091 
00092     return spi;
00093 }
00094 
00095 /** \brief Constructor for spi interrupt object. */
00096 
00097 void
00098 spi_intr_construct (SPIIntr_T *spi, int addr, char *name)
00099 {
00100     if (spi == NULL)
00101         avr_error ("passed null ptr");
00102 
00103     vdev_construct ((VDevice *)spi, spi_intr_read, spi_intr_write,
00104                     spi_intr_reset, spii_add_addr);
00105 
00106     spii_add_addr ((VDevice *)spi, addr, name, 0, NULL);
00107     spi_intr_reset ((VDevice *)spi);
00108 }
00109 
00110 static void
00111 spii_add_addr (VDevice *vdev, int addr, char *name, int rel_addr, void *data)
00112 {
00113     SPIIntr_T *spi = (SPIIntr_T *)vdev;
00114 
00115     if (strncmp ("SPCR", name, 4) == 0)
00116     {
00117         spi->spcr_addr = addr;
00118     }
00119 
00120     else if (strncmp ("SPSR", name, 4) == 0)
00121     {
00122         spi->spsr_addr = addr;
00123     }
00124 
00125     else
00126     {
00127         avr_error ("invalid ADC register name: '%s' @ 0x%04x", name, addr);
00128     }
00129 }
00130 
00131 /** \brief Destructor for spi interrupt object. */
00132 
00133 void
00134 spi_intr_destroy (void *spi)
00135 {
00136     if (spi == NULL)
00137         return;
00138 
00139     vdev_destroy (spi);
00140 }
00141 
00142 static uint8_t
00143 spi_intr_read (VDevice *dev, int addr)
00144 {
00145     SPIIntr_T *spi = (SPIIntr_T *)dev;
00146 
00147     if (addr == spi->spcr_addr)
00148     {
00149         return (spi->spcr);
00150     }
00151 
00152     else if (addr == spi->spsr_addr)
00153     {
00154         if (spi->spsr & mask_SPIF)
00155             spi->spsr_read |= mask_SPIF;
00156         if (spi->spsr & mask_WCOL)
00157             spi->spsr_read |= mask_WCOL;
00158         return (spi->spsr);
00159     }
00160 
00161     else
00162     {
00163         avr_error ("Bad address: 0x%04x", addr);
00164     }
00165 
00166     return 0;                   /* will never get here */
00167 }
00168 
00169 static void
00170 spi_intr_write (VDevice *dev, int addr, uint8_t val)
00171 {
00172     SPIIntr_T *spi = (SPIIntr_T *)dev;
00173     CallBack *cb;
00174 
00175     if (addr == spi->spcr_addr)
00176     {
00177         spi->spcr = val;
00178         if (spi->spcr & mask_SPE)
00179         {
00180             /* we need to install the intr_cb function */
00181             cb = callback_new (spi_intr_cb, (AvrClass *)spi);
00182             spi->intr_cb = cb;
00183             avr_core_async_cb_add ((AvrCore *)vdev_get_core (dev), cb);
00184         }
00185         else
00186         {
00187             spi->intr_cb = NULL; /* no interrupt are enabled, remove the
00188                                     callback */
00189         }
00190     }
00191 
00192     else
00193     {
00194         avr_error ("Bad address: 0x%04x", addr);
00195     }
00196 }
00197 
00198 static void
00199 spi_intr_reset (VDevice *dev)
00200 {
00201     SPIIntr_T *spi = (SPIIntr_T *)dev;
00202 
00203     spi->intr_cb = NULL;
00204 
00205     spi->spcr = 0;
00206     spi->spsr = 0;
00207     spi->spsr_read = 0;
00208 }
00209 
00210 static int
00211 spi_intr_cb (uint64_t time, AvrClass *data)
00212 {
00213     SPIIntr_T *spi = (SPIIntr_T *)data;
00214 
00215     if (spi->intr_cb == NULL)
00216         return CB_RET_REMOVE;
00217 
00218     if ((spi->spcr & mask_SPE) && (spi->spcr & mask_SPIE)
00219         && (spi->spsr & mask_SPIF))
00220     {
00221         /* an enabled interrupt occured */
00222         AvrCore *core = (AvrCore *)vdev_get_core ((VDevice *)spi);
00223         avr_core_irq_raise (core, irq_vect_table_index (SPI_STC));
00224         spi->spsr &= ~mask_SPIF;
00225         spi->spsr = 0;
00226     }
00227 
00228     return CB_RET_RETAIN;
00229 }
00230 
00231 /****************************************************************************\
00232  *
00233  * SPI  
00234  *
00235 \****************************************************************************/
00236 
00237 static void spi_add_addr (VDevice *vdev, int addr, char *name, int rel_addr,
00238                           void *data);
00239 static uint8_t spi_read (VDevice *dev, int addr);
00240 static void spi_write (VDevice *dev, int addr, uint8_t val);
00241 static void spi_reset (VDevice *dev);
00242 static int spi_clk_incr_cb (uint64_t ck, AvrClass *data);
00243 
00244 /** \brief Allocate a new SPI structure. */
00245 
00246 VDevice *
00247 spi_create (int addr, char *name, int rel_addr, void *data)
00248 {
00249     return (VDevice *)spi_new (addr, name, rel_addr);
00250 }
00251 
00252 SPI_T *
00253 spi_new (int addr, char *name, int rel_addr)
00254 {
00255     SPI_T *spi;
00256 
00257     spi = avr_new (SPI_T, 1);
00258     spi_construct (spi, addr, name, rel_addr);
00259     class_overload_destroy ((AvrClass *)spi, spi_destroy);
00260 
00261     return spi;
00262 }
00263 
00264 /** \brief Constructor for SPI object. */
00265 
00266 void
00267 spi_construct (SPI_T *spi, int addr, char *name, int rel_addr)
00268 {
00269     if (spi == NULL)
00270         avr_error ("passed null ptr");
00271 
00272     vdev_construct ((VDevice *)spi, spi_read, spi_write, spi_reset,
00273                     spi_add_addr);
00274 
00275     spi_add_addr ((VDevice *)spi, addr, name, 0, NULL);
00276     if (rel_addr)
00277         spi->rel_addr = rel_addr;
00278     spi_reset ((VDevice *)spi);
00279 }
00280 
00281 static void
00282 spi_add_addr (VDevice *vdev, int addr, char *name, int ref_addr, void *data)
00283 {
00284     SPI_T *spi = (SPI_T *)vdev;
00285 
00286     if (strncmp ("SPDR", name, 4) == 0)
00287     {
00288         spi->spdr_addr = addr;
00289     }
00290 
00291     else
00292     {
00293         avr_error ("invalid SPI register name: '%s' @ 0x%04x", name, addr);
00294     }
00295 }
00296 
00297 /** \brief Destructor for SPI object. */
00298 
00299 void
00300 spi_destroy (void *spi)
00301 {
00302     if (spi == NULL)
00303         return;
00304 
00305     vdev_destroy (spi);
00306 }
00307 
00308 static uint8_t
00309 spi_read (VDevice *dev, int addr)
00310 {
00311     SPI_T *spi = (SPI_T *)dev;
00312     SPIIntr_T *spi_ti;
00313 
00314     spi_ti =
00315         (SPIIntr_T *)avr_core_get_vdev_by_addr ((AvrCore *)
00316                                                 vdev_get_core ((VDevice *)
00317                                                                spi),
00318                                                 spi->rel_addr);
00319 
00320     if (addr == spi->spdr_addr)
00321     {
00322         if (spi_ti->spsr_read)
00323         {
00324             spi_ti->spsr &= ~spi_ti->spsr_read;
00325             spi_ti->spsr_read = 0;
00326         }
00327         return spi->spdr;
00328 
00329     }
00330 
00331     else
00332     {
00333         avr_error ("Bad address: 0x%04x", addr);
00334     }
00335 
00336     return 0;                   /* will never get here */
00337 }
00338 
00339 static void
00340 spi_write (VDevice *dev, int addr, uint8_t val)
00341 {
00342     SPI_T *spi = (SPI_T *)dev;
00343     CallBack *cb;
00344     SPIIntr_T *spi_ti;
00345 
00346     spi_ti =
00347         (SPIIntr_T *)avr_core_get_vdev_by_addr ((AvrCore *)
00348                                                 vdev_get_core ((VDevice *)
00349                                                                spi),
00350                                                 spi->rel_addr);
00351 
00352     if (addr == spi->spdr_addr)
00353     {
00354         if (spi_ti->spsr_read)
00355         {
00356             spi_ti->spsr &= ~spi_ti->spsr_read;
00357             spi_ti->spsr_read = 0;
00358         }
00359 
00360         if (spi->tcnt != 0)
00361         {
00362             spi_ti->spsr |= mask_WCOL;
00363         }
00364 
00365         spi->spdr = val;
00366 
00367         /* When the user writes to SPDR, a callback is installed for either
00368            clock generated increments or externally generated increments. The
00369            two incrememtor callback are mutally exclusive, only one or the
00370            other can be installed at any given instant. */
00371 
00372         switch ((spi_ti->spcr) & (mask_SPR0 | mask_SPR1))
00373         {
00374             case SPI_CK_4:
00375                 spi->divisor = 4;
00376                 break;
00377             case SPI_CK_16:
00378                 spi->divisor = 16;
00379                 break;
00380             case SPI_CK_64:
00381                 spi->divisor = 64;
00382                 break;
00383             case SPI_CK_128:
00384                 spi->divisor = 128;
00385                 break;
00386             default:
00387                 avr_error ("The impossible happened!");
00388         }
00389 
00390         /* install the clock incrementor callback (with flair!) */
00391         if (spi->clk_cb == NULL)
00392         {
00393             cb = callback_new (spi_clk_incr_cb, (AvrClass *)spi);
00394             spi->clk_cb = cb;
00395             avr_core_clk_cb_add ((AvrCore *)vdev_get_core ((VDevice *)spi),
00396                                  cb);
00397         }
00398         spi->tcnt = 8;          /* set up timer for 8 clocks */
00399         spi->spdr_in = spi_port_rd (addr);
00400     }
00401     else
00402     {
00403         avr_error ("Bad address: 0x%04x", addr);
00404     }
00405 }
00406 
00407 static void
00408 spi_reset (VDevice *dev)
00409 {
00410     SPI_T *spi = (SPI_T *)dev;
00411 
00412     spi->clk_cb = NULL;
00413 
00414     spi->spdr = 0;
00415     spi->tcnt = 0;
00416 
00417     spi->divisor = 0;
00418 }
00419 
00420 static int
00421 spi_clk_incr_cb (uint64_t ck, AvrClass *data)
00422 {
00423     SPI_T *spi = (SPI_T *)data;
00424     uint8_t last = spi->tcnt;
00425     SPIIntr_T *spi_ti;
00426 
00427     spi_ti =
00428         (SPIIntr_T *)avr_core_get_vdev_by_addr ((AvrCore *)
00429                                                 vdev_get_core ((VDevice *)
00430                                                                spi),
00431                                                 spi->rel_addr);
00432 
00433     if (spi->clk_cb == NULL)
00434         return CB_RET_REMOVE;
00435 
00436     if (spi->divisor <= 0)
00437         avr_error ("Bad divisor value: %d", spi->divisor);
00438 
00439     /* Decrement clock if ck is a mutliple of divisor. Since divisor is always
00440        a power of 2, it's much faster to do the bitwise AND instead of using
00441        the integer modulus operator (%). */
00442     spi->tcnt -= ((ck & (spi->divisor - 1)) == 0);
00443 
00444     if (spi->tcnt != last)      /* we've changed the counter */
00445     {
00446         if (spi->tcnt == 0)
00447         {
00448             spi_ti->spsr |= mask_SPIF; /* spdr is not guaranteed until
00449                                           operation complete */
00450             spi_port_wr (spi->spdr); /* tell what we wrote */
00451             spi->spdr = spi->spdr_in; /* update spdr to what we read */
00452 
00453             spi->clk_cb = NULL;
00454             return CB_RET_REMOVE;
00455         }
00456     }
00457 
00458     return CB_RET_RETAIN;
00459 }
00460 
00461 /* FIXME: TRoth/2003-11-28: These will eventually need to be plugged into an
00462    external connection interface. */
00463 
00464 uint8_t
00465 spi_port_rd (int addr)
00466 {
00467     int data;
00468     char line[80];
00469 
00470     while (1)
00471     {
00472         fprintf (stderr,
00473                  "\nEnter a byte of hex data to read into the SPI at"
00474                  " address 0x%04x: ", addr);
00475 
00476         /* try to read in a line of input */
00477         if (fgets (line, sizeof (line), stdin) == NULL)
00478             continue;
00479 
00480         /* try to parse the line for a byte of data */
00481         if (sscanf (line, "%x\n", &data) != 1)
00482             continue;
00483 
00484         break;
00485     }
00486 
00487     return (uint8_t) (data & 0xff);
00488 }
00489 
00490 void
00491 spi_port_wr (uint8_t val)
00492 {
00493     fprintf (stderr, "wrote 0x%02x to SPI\n", val);
00494 }

Automatically generated by Doxygen 1.5.2 on 13 Feb 2008.