spi.c
Go to the documentation of this file.
1 /*
2  * $Id: spi.c,v 1.5 2004/03/13 19:55:34 troth Exp $
3  *
4  ****************************************************************************
5  *
6  * simulavr - A simulator for the Atmel AVR family of microcontrollers.
7  * Copyright (C) 2003, 2004 Keith Gudger
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22  *
23  ****************************************************************************
24  */
25 
26 /**
27  * \file spi.c
28  * \brief Module to simulate the AVR's SPI module.
29  *
30  */
31 
32 #include <config.h>
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 #include "avrerror.h"
39 #include "avrmalloc.h"
40 #include "avrclass.h"
41 #include "utils.h"
42 #include "callback.h"
43 #include "op_names.h"
44 
45 #include "storage.h"
46 #include "flash.h"
47 
48 #include "vdevs.h"
49 #include "memory.h"
50 #include "stack.h"
51 #include "register.h"
52 #include "sram.h"
53 #include "eeprom.h"
54 #include "timers.h"
55 #include "ports.h"
56 #include "spi.h"
57 
58 #include "avrcore.h"
59 
60 #include "intvects.h"
61 
62 /****************************************************************************\
63  *
64  * SPI Interrupts
65  *
66 \****************************************************************************/
67 
68 static void spii_add_addr (VDevice *vdev, int addr, char *name, int rel_addr,
69  void *data);
70 static uint8_t spi_intr_read (VDevice *dev, int addr);
71 static void spi_intr_write (VDevice *dev, int addr, uint8_t val);
72 static void spi_intr_reset (VDevice *dev);
73 static int spi_intr_cb (uint64_t time, AvrClass *data);
74 
75 /** \brief Allocate a new SPI interrupt */
76 
77 VDevice *
78 spii_create (int addr, char *name, int rel_addr, void *data)
79 {
80  return (VDevice *)spi_intr_new (addr, name);
81 }
82 
83 SPIIntr_T *
84 spi_intr_new (int addr, char *name)
85 {
86  SPIIntr_T *spi;
87 
88  spi = avr_new (SPIIntr_T, 1);
89  spi_intr_construct (spi, addr, name);
90  class_overload_destroy ((AvrClass *)spi, spi_intr_destroy);
91 
92  return spi;
93 }
94 
95 /** \brief Constructor for spi interrupt object. */
96 
97 void
98 spi_intr_construct (SPIIntr_T *spi, int addr, char *name)
99 {
100  if (spi == NULL)
101  avr_error ("passed null ptr");
102 
103  vdev_construct ((VDevice *)spi, spi_intr_read, spi_intr_write,
104  spi_intr_reset, spii_add_addr);
105 
106  spii_add_addr ((VDevice *)spi, addr, name, 0, NULL);
107  spi_intr_reset ((VDevice *)spi);
108 }
109 
110 static void
111 spii_add_addr (VDevice *vdev, int addr, char *name, int rel_addr, void *data)
112 {
113  SPIIntr_T *spi = (SPIIntr_T *)vdev;
114 
115  if (strncmp ("SPCR", name, 4) == 0)
116  {
117  spi->spcr_addr = addr;
118  }
119 
120  else if (strncmp ("SPSR", name, 4) == 0)
121  {
122  spi->spsr_addr = addr;
123  }
124 
125  else
126  {
127  avr_error ("invalid ADC register name: '%s' @ 0x%04x", name, addr);
128  }
129 }
130 
131 /** \brief Destructor for spi interrupt object. */
132 
133 void
134 spi_intr_destroy (void *spi)
135 {
136  if (spi == NULL)
137  return;
138 
139  vdev_destroy (spi);
140 }
141 
142 static uint8_t
143 spi_intr_read (VDevice *dev, int addr)
144 {
145  SPIIntr_T *spi = (SPIIntr_T *)dev;
146 
147  if (addr == spi->spcr_addr)
148  {
149  return (spi->spcr);
150  }
151 
152  else if (addr == spi->spsr_addr)
153  {
154  if (spi->spsr & mask_SPIF)
155  spi->spsr_read |= mask_SPIF;
156  if (spi->spsr & mask_WCOL)
157  spi->spsr_read |= mask_WCOL;
158  return (spi->spsr);
159  }
160 
161  else
162  {
163  avr_error ("Bad address: 0x%04x", addr);
164  }
165 
166  return 0; /* will never get here */
167 }
168 
169 static void
170 spi_intr_write (VDevice *dev, int addr, uint8_t val)
171 {
172  SPIIntr_T *spi = (SPIIntr_T *)dev;
173  CallBack *cb;
174 
175  if (addr == spi->spcr_addr)
176  {
177  spi->spcr = val;
178  if (spi->spcr & mask_SPE)
179  {
180  /* we need to install the intr_cb function */
181  cb = callback_new (spi_intr_cb, (AvrClass *)spi);
182  spi->intr_cb = cb;
183  avr_core_async_cb_add ((AvrCore *)vdev_get_core (dev), cb);
184  }
185  else
186  {
187  spi->intr_cb = NULL; /* no interrupt are enabled, remove the
188  callback */
189  }
190  }
191 
192  else
193  {
194  avr_error ("Bad address: 0x%04x", addr);
195  }
196 }
197 
198 static void
199 spi_intr_reset (VDevice *dev)
200 {
201  SPIIntr_T *spi = (SPIIntr_T *)dev;
202 
203  spi->intr_cb = NULL;
204 
205  spi->spcr = 0;
206  spi->spsr = 0;
207  spi->spsr_read = 0;
208 }
209 
210 static int
211 spi_intr_cb (uint64_t time, AvrClass *data)
212 {
213  SPIIntr_T *spi = (SPIIntr_T *)data;
214 
215  if (spi->intr_cb == NULL)
216  return CB_RET_REMOVE;
217 
218  if ((spi->spcr & mask_SPE) && (spi->spcr & mask_SPIE)
219  && (spi->spsr & mask_SPIF))
220  {
221  /* an enabled interrupt occured */
222  AvrCore *core = (AvrCore *)vdev_get_core ((VDevice *)spi);
223  avr_core_irq_raise (core, irq_vect_table_index (SPI_STC));
224  spi->spsr &= ~mask_SPIF;
225  spi->spsr = 0;
226  }
227 
228  return CB_RET_RETAIN;
229 }
230 
231 /****************************************************************************\
232  *
233  * SPI
234  *
235 \****************************************************************************/
236 
237 static void spi_add_addr (VDevice *vdev, int addr, char *name, int rel_addr,
238  void *data);
239 static uint8_t spi_read (VDevice *dev, int addr);
240 static void spi_write (VDevice *dev, int addr, uint8_t val);
241 static void spi_reset (VDevice *dev);
242 static int spi_clk_incr_cb (uint64_t ck, AvrClass *data);
243 
244 /** \brief Allocate a new SPI structure. */
245 
246 VDevice *
247 spi_create (int addr, char *name, int rel_addr, void *data)
248 {
249  return (VDevice *)spi_new (addr, name, rel_addr);
250 }
251 
252 SPI_T *
253 spi_new (int addr, char *name, int rel_addr)
254 {
255  SPI_T *spi;
256 
257  spi = avr_new (SPI_T, 1);
258  spi_construct (spi, addr, name, rel_addr);
259  class_overload_destroy ((AvrClass *)spi, spi_destroy);
260 
261  return spi;
262 }
263 
264 /** \brief Constructor for SPI object. */
265 
266 void
267 spi_construct (SPI_T *spi, int addr, char *name, int rel_addr)
268 {
269  if (spi == NULL)
270  avr_error ("passed null ptr");
271 
272  vdev_construct ((VDevice *)spi, spi_read, spi_write, spi_reset,
273  spi_add_addr);
274 
275  spi_add_addr ((VDevice *)spi, addr, name, 0, NULL);
276  if (rel_addr)
277  spi->rel_addr = rel_addr;
278  spi_reset ((VDevice *)spi);
279 }
280 
281 static void
282 spi_add_addr (VDevice *vdev, int addr, char *name, int ref_addr, void *data)
283 {
284  SPI_T *spi = (SPI_T *)vdev;
285 
286  if (strncmp ("SPDR", name, 4) == 0)
287  {
288  spi->spdr_addr = addr;
289  }
290 
291  else
292  {
293  avr_error ("invalid SPI register name: '%s' @ 0x%04x", name, addr);
294  }
295 }
296 
297 /** \brief Destructor for SPI object. */
298 
299 void
300 spi_destroy (void *spi)
301 {
302  if (spi == NULL)
303  return;
304 
305  vdev_destroy (spi);
306 }
307 
308 static uint8_t
309 spi_read (VDevice *dev, int addr)
310 {
311  SPI_T *spi = (SPI_T *)dev;
312  SPIIntr_T *spi_ti;
313 
314  spi_ti =
315  (SPIIntr_T *)avr_core_get_vdev_by_addr ((AvrCore *)
316  vdev_get_core ((VDevice *)
317  spi),
318  spi->rel_addr);
319 
320  if (addr == spi->spdr_addr)
321  {
322  if (spi_ti->spsr_read)
323  {
324  spi_ti->spsr &= ~spi_ti->spsr_read;
325  spi_ti->spsr_read = 0;
326  }
327  return spi->spdr;
328 
329  }
330 
331  else
332  {
333  avr_error ("Bad address: 0x%04x", addr);
334  }
335 
336  return 0; /* will never get here */
337 }
338 
339 static void
340 spi_write (VDevice *dev, int addr, uint8_t val)
341 {
342  SPI_T *spi = (SPI_T *)dev;
343  CallBack *cb;
344  SPIIntr_T *spi_ti;
345 
346  spi_ti =
347  (SPIIntr_T *)avr_core_get_vdev_by_addr ((AvrCore *)
348  vdev_get_core ((VDevice *)
349  spi),
350  spi->rel_addr);
351 
352  if (addr == spi->spdr_addr)
353  {
354  if (spi_ti->spsr_read)
355  {
356  spi_ti->spsr &= ~spi_ti->spsr_read;
357  spi_ti->spsr_read = 0;
358  }
359 
360  if (spi->tcnt != 0)
361  {
362  spi_ti->spsr |= mask_WCOL;
363  }
364 
365  spi->spdr = val;
366 
367  /* When the user writes to SPDR, a callback is installed for either
368  clock generated increments or externally generated increments. The
369  two incrememtor callback are mutally exclusive, only one or the
370  other can be installed at any given instant. */
371 
372  switch ((spi_ti->spcr) & (mask_SPR0 | mask_SPR1))
373  {
374  case SPI_CK_4:
375  spi->divisor = 4;
376  break;
377  case SPI_CK_16:
378  spi->divisor = 16;
379  break;
380  case SPI_CK_64:
381  spi->divisor = 64;
382  break;
383  case SPI_CK_128:
384  spi->divisor = 128;
385  break;
386  default:
387  avr_error ("The impossible happened!");
388  }
389 
390  /* install the clock incrementor callback (with flair!) */
391  if (spi->clk_cb == NULL)
392  {
393  cb = callback_new (spi_clk_incr_cb, (AvrClass *)spi);
394  spi->clk_cb = cb;
395  avr_core_clk_cb_add ((AvrCore *)vdev_get_core ((VDevice *)spi),
396  cb);
397  }
398  spi->tcnt = 8; /* set up timer for 8 clocks */
399  spi->spdr_in = spi_port_rd (addr);
400  }
401  else
402  {
403  avr_error ("Bad address: 0x%04x", addr);
404  }
405 }
406 
407 static void
408 spi_reset (VDevice *dev)
409 {
410  SPI_T *spi = (SPI_T *)dev;
411 
412  spi->clk_cb = NULL;
413 
414  spi->spdr = 0;
415  spi->tcnt = 0;
416 
417  spi->divisor = 0;
418 }
419 
420 static int
421 spi_clk_incr_cb (uint64_t ck, AvrClass *data)
422 {
423  SPI_T *spi = (SPI_T *)data;
424  uint8_t last = spi->tcnt;
425  SPIIntr_T *spi_ti;
426 
427  spi_ti =
428  (SPIIntr_T *)avr_core_get_vdev_by_addr ((AvrCore *)
429  vdev_get_core ((VDevice *)
430  spi),
431  spi->rel_addr);
432 
433  if (spi->clk_cb == NULL)
434  return CB_RET_REMOVE;
435 
436  if (spi->divisor <= 0)
437  avr_error ("Bad divisor value: %d", spi->divisor);
438 
439  /* Decrement clock if ck is a mutliple of divisor. Since divisor is always
440  a power of 2, it's much faster to do the bitwise AND instead of using
441  the integer modulus operator (%). */
442  spi->tcnt -= ((ck & (spi->divisor - 1)) == 0);
443 
444  if (spi->tcnt != last) /* we've changed the counter */
445  {
446  if (spi->tcnt == 0)
447  {
448  spi_ti->spsr |= mask_SPIF; /* spdr is not guaranteed until
449  operation complete */
450  spi_port_wr (spi->spdr); /* tell what we wrote */
451  spi->spdr = spi->spdr_in; /* update spdr to what we read */
452 
453  spi->clk_cb = NULL;
454  return CB_RET_REMOVE;
455  }
456  }
457 
458  return CB_RET_RETAIN;
459 }
460 
461 /* FIXME: TRoth/2003-11-28: These will eventually need to be plugged into an
462  external connection interface. */
463 
464 uint8_t
465 spi_port_rd (int addr)
466 {
467  int data;
468  char line[80];
469 
470  while (1)
471  {
472  fprintf (stderr,
473  "\nEnter a byte of hex data to read into the SPI at"
474  " address 0x%04x: ", addr);
475 
476  /* try to read in a line of input */
477  if (fgets (line, sizeof (line), stdin) == NULL)
478  continue;
479 
480  /* try to parse the line for a byte of data */
481  if (sscanf (line, "%x\n", &data) != 1)
482  continue;
483 
484  break;
485  }
486 
487  return (uint8_t) (data & 0xff);
488 }
489 
490 void
491 spi_port_wr (uint8_t val)
492 {
493  fprintf (stderr, "wrote 0x%02x to SPI\n", val);
494 }

Automatically generated by Doxygen 1.8.2 on Tue Mar 12 2013.