- Main Page
- Related Pages
- Files

# 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.5 on 7 Nov 2008.