Main Page

- Main Page
- Related Pages
- Files

- File List
- Globals

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 Mon Aug 18 2014.