simulavr  1.1.0
hwspi.cpp
Go to the documentation of this file.
1 /*
2  ****************************************************************************
3  *
4  * simulavr - A simulator for the Atmel AVR family of microcontrollers.
5  * Copyright (C) 2001, 2002, 2003 Klaus Rudolph
6  * Copyright (C) 2009 Onno Kortmann
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  ****************************************************************************
23  *
24  * $Id$
25  */
26 
27 #include <assert.h>
28 #include <stdio.h>
29 #include "hwspi.h"
30 #include "flash.h"
31 #include "avrdevice.h"
32 #include "traceval.h"
33 #include "irqsystem.h"
34 #include "avrerror.h"
35 
36 //configuration
37 #define SPIE 0x80
38 #define SPE 0x40
39 #define DORD 0x20
40 #define MSTR 0x10
41 #define CPOL 0x08
42 #define CPHA 0x04
43 #define SPR1 0x02
44 #define SPR0 0x01
45 
46 //status
47 #define SPIF 0x80
48 #define WCOL 0x40
49 #define SPI2X 0x01 //only on mega devices speed x 2
50 
51 
52 /* SPI verbosity level
53  FIXME: Make this configurable through the command line interface. */
54 #define SPI_VERBOSE 0
55 
56 using namespace std;
58  if (spsr_read) {
59  // if status is read with SPIF == 1
60  //we can remove the SPIF and WCOL flag after reading
61  //data register
62  spsr&=~(SPIF|WCOL);
63  spsr_read=false;
64  }
65 }
66 
67 unsigned char HWSpi::GetSPDR() {
68  spdr_access();
69  return data_read;
70 }
71 
72 unsigned char HWSpi::GetSPSR() {
73  spsr_read=true;
74  return spsr;
75 }
76 
77 unsigned char HWSpi::GetSPCR() {
78  return spcr;
79 }
80 
81 void HWSpi::SetSPDR(unsigned char val) {
82  spdr_access();
83  data_write=val;
84  if (spcr & MSTR) { // mster mode?
85  if (bitcnt<8) {
86  spsr|=WCOL; // not yet ready -> Write Collision
87  } else {
88  bitcnt=0;
89  finished=false;
90  clkcnt=0;
91  }
92  }
93 }
94 
96  int fac2x=(spsr&SPI2X) ? 1 : 2;
97  switch (spcr & (SPR1|SPR0)) {
98  case 0: clkdiv=1; break;
99  case SPR0: clkdiv=4; break;
100  case SPR1: clkdiv=16; break;
101  case SPR1|SPR0: clkdiv=32; break;
102  }
103  clkdiv*=fac2x;
104 }
105 
106 void HWSpi::SetSPSR(unsigned char val) {
107  if (mega_mode) {
108  spsr&=~SPI2X;
109  spsr|=val&SPI2X;
110  updatePrescaler();
111  } else {
112  ((core->trace_on) ?
113  (traceOut) : (cerr))
114  << "spsr is read only! (0x" << hex << core->PC << " = " <<
115  core->Flash->GetSymbolAtAddress(core->PC) << ")" << endl;
116  }
117 }
118 
119 
120 void HWSpi::SetSPCR(unsigned char val) {
121  spcr=val;
122  if ( spcr & SPE) { //SPI is enabled
123  core->AddToCycleList(this);
124  if (spcr & MSTR) { //master
125  MISO.SetUseAlternateDdr(1);
126  MISO.SetAlternateDdr(0); //always input
127  MOSI.SetUseAlternatePortIfDdrSet(1);
128 
129  /* according to the graphics in the atmega8 datasheet, p.132
130  (10/06), MOSI is high when idle. FIXME: check whether
131  this applies to real hardware. */
132  MOSI.SetAlternatePort(1);
133  SCK.SetAlternatePort(spcr & CPOL);
134  SCK.SetUseAlternatePortIfDdrSet(1);
135  assert(SCK.GetPin().outState == ((spcr & CPOL) ? Pin::HIGH : Pin::LOW));
136  assert(SCK.GetPin().outState == ((spcr & CPOL) ? Pin::HIGH : Pin::LOW));
137  } else { //slave
138  MISO.SetUseAlternatePortIfDdrSet(1);
139  MOSI.SetUseAlternateDdr(1);
140  MOSI.SetAlternateDdr(0);
141  SCK.SetUseAlternateDdr(1);
142  SCK.SetAlternateDdr(0);
143  SS.SetUseAlternateDdr(1);
144  SS.SetAlternateDdr(0);
145  }
146  } else { //Spi is off so unset alternate pin functions
147 
148  /* FIXME: Check whether these will be really tied
149  to reset state as long as the SPI is off. Check
150  the switch on/off behaviour of the SPI interface! */
151  bitcnt=8;
152  finished=false;
153  core->RemoveFromCycleList(this);
154  MOSI.SetUseAlternatePortIfDdrSet(0);
155  MISO.SetUseAlternatePortIfDdrSet(0);
156  SCK.SetUseAlternatePortIfDdrSet(0);
157  MOSI.SetUseAlternateDdr(0);
158  MISO.SetUseAlternateDdr(0);
159  SCK.SetUseAlternateDdr(0);
160  SS.SetUseAlternateDdr(0);
161  }
162  updatePrescaler();
163 }
164 
165 
167  HWIrqSystem *_irq,
168  PinAtPort mosi,
169  PinAtPort miso,
170  PinAtPort sck,
171  PinAtPort ss,
172  unsigned int ivec,
173  bool mm) :
174  Hardware(_c), TraceValueRegister(_c, "SPI"),
175  core(_c), irq(_irq),
176  MOSI(mosi), MISO(miso), SCK(sck), SS(ss),
177  irq_vector(ivec), mega_mode(mm),
178  spdr_reg(this, "SPDR", this, &HWSpi::GetSPDR, &HWSpi::SetSPDR),
179  spsr_reg(this, "SPSR", this, &HWSpi::GetSPSR, &HWSpi::SetSPSR),
180  spcr_reg(this, "SPCR", this, &HWSpi::GetSPCR, &HWSpi::SetSPCR)
181 {
182  irq->DebugVerifyInterruptVector(ivec, this);
183  bitcnt=8;
184  finished=false;
185 
186  trace_direct(this, "shift_in", &shift_in);
187  trace_direct(this, "data_read", &data_read);
188  trace_direct(this, "data_write", &data_write);
189  trace_direct(this, "sSPSR", &spsr);
190  trace_direct(this, "sSPCR", &spcr);
191  Reset();
192 }
193 
194 void HWSpi::Reset() {
195  spsr=0;
196  SetSPCR(0);
198 }
199 
200 void HWSpi::ClearIrqFlag(unsigned int vector) {
201  if (vector==irq_vector) {
202  spsr&=~SPIF;
204  } else {
205  cerr << "WARNING: There is HWSPI called to get a irq vector which is not assigned for!?!?!?!?";
206  }
207 }
208 
209 void HWSpi::txbit(const int bitpos) {
210  // set next output bit
211  PinAtPort *out=(spcr & MSTR) ? &MOSI : &MISO;
212  out->SetAlternatePort(data_write&(1<<bitpos));
213 }
214 
215 void HWSpi::rxbit(const int bitpos) {
216  // sample input
217  bool bit=(spcr & MSTR) ? MISO : MOSI;
218  if (bit)
219  shift_in|=(1<<bitpos);
220 }
221 
223  if (finished) {
224  finished=false;
225  if (core->trace_on && SPI_VERBOSE)
226  traceOut << "SPI: READ " << int(shift_in) << endl;
227  /* set also data_write to allow continuous shifting
228  when slave. */
230 
231  spsr|=SPIF;
232  if (spcr&SPIE) {
233  irq->SetIrqFlag(this, irq_vector);
234  }
235  spsr_read=false;
236  }
237 }
238 
239 unsigned int HWSpi::CpuCycle() {
240  if ((spcr & SPE) == 0) // active at all?
241  return 0;
242  int bitpos=(spcr&DORD) ? bitcnt : 7-bitcnt;
243  int bitpos_prec=(spcr&DORD) ? bitcnt-1 : 8-bitcnt;
244 
245  if (core->trace_on && SPI_VERBOSE) {
246  traceOut << "SPI: " << bitcnt << ", " << bitpos << ", " << clkcnt << endl;
247  }
248 
249  if (spcr & MSTR) {
250  /* Check whether we're externally driven into slave mode.
251  FIXME: It is unclear at least from mega8 docs if this behaviour is
252  also right when the SPI is inactive!*/
253  if (! SS.GetDdr() && ! SS) {
254  SetSPCR(spcr & ~MSTR);
255  // request interrupt
256  spsr |= SPIF;
257  if (spcr&SPIE) {
258  irq->SetIrqFlag(this, irq_vector);
259  }
260  bitcnt = 8; // slave and idle
261  finished=false;
262  clkcnt=0;
263  }
264  if ((clkcnt%clkdiv) == 0){ // TRX bits
265  if (bitcnt < 8) {
266  if (bitcnt == 0)
267  shift_in = 0;
268  switch ((clkcnt/clkdiv)&1) {
269  case 0:
270  // set idle clock
272  // late phase (for last bit)?
273  if (spcr&CPHA) {
274  if (bitcnt) {
275  rxbit(bitpos_prec);
276  }
277  } else {
278  txbit(bitpos);
279  }
280  break;
281  case 1:
282  // set valid clock
283  SCK.SetAlternatePort(!(spcr&CPOL));
284  if (spcr&CPHA) {
285  txbit(bitpos);
286  } else {
287  rxbit(bitpos);
288  }
289  bitcnt++;
290  break;
291  }
292  finished = (bitcnt==8);
293  } else if (finished) {
294  if (spcr&CPHA) {
295  rxbit(bitpos_prec);
296  }
297  trxend();
298  // set idle clock
300  // set idle MOSI (high if CPHA==0)
301  if (!(spcr&CPHA))
303  }
304  }
305  } else {
306  // possible slave mode
307  if (SS) {
308  // slave selected lifted-> force end of transmission
309  bitcnt=8;
310  } else {
311  // Slave mode
312  if (bitcnt == 8) {
313  bitcnt = 0;
314  finished = false;
315  shift_in = 0;
316  oldsck = SCK;
317  } else {
318  /* Set initial bit for CPHA==0 */
319  if (!(spcr&CPHA)) {
320  txbit(bitpos);
321  }
322  }
323  if (SCK != oldsck) { // edge detection
324  bool leading = false; // leading edge clock?
325  if (spcr&CPOL) {
326  // leading edge is falling edge
327  leading = ! SCK;
328  } else
329  leading = SCK;
330 
331  // determine whether we should sample or setup
332  bool sample = leading ^ ((spcr&CPHA)!=0);
333 
334  if (sample)
335  rxbit(bitpos);
336  else
337  txbit(bitpos);
338 
339  if (!leading) {
340  bitcnt++;
341  finished = (bitcnt==8);
342  }
343  }
344  trxend();
345  oldsck = SCK;
346  }
347  }
348  clkcnt++;
349  return 0;
350 }
351 
unsigned char data_write
Definition: hwspi.h:47
Basic AVR device, contains the core functionality.
Definition: avrdevice.h:66
unsigned int CpuCycle()
Definition: hwspi.cpp:239
void trxend()
Handle end of transmission if necessary.
Definition: hwspi.cpp:222
bool oldsck
Definition: hwspi.h:72
unsigned char spsr
Definition: hwspi.h:48
void spdr_access()
Called for all SPDR access to clear the WCOL and SPIF flags if needed.
Definition: hwspi.cpp:57
unsigned char GetSPDR()
Definition: hwspi.cpp:67
void ClearIrqFlag(unsigned int)
Definition: hwspi.cpp:200
#define SPI_VERBOSE
Definition: hwspi.cpp:54
PinAtPort MOSI
Definition: hwspi.h:54
int bitcnt
Definition: hwspi.h:75
bool GetDdr()
Definition: pinatport.cpp:104
void SetSPCR(unsigned char val)
Definition: hwspi.cpp:120
unsigned char spcr
Definition: hwspi.h:49
HWSpi(AvrDevice *core, HWIrqSystem *, PinAtPort mosi, PinAtPort miso, PinAtPort sck, PinAtPort ss, unsigned int irq_vec, bool mega_mode=true)
Definition: hwspi.cpp:166
#define traceOut
Definition: avrerror.h:121
void Reset()
Definition: hwspi.cpp:194
STL namespace.
void txbit(const int bitpos)
Send/receive one bit.
Definition: hwspi.cpp:209
PinAtPort SCK
Definition: hwspi.h:56
#define CPOL
"When this bit is written to one, SCK is high when idle."
Definition: hwspi.cpp:41
void updatePrescaler()
Takes info from registers and updates clkdiv.
Definition: hwspi.cpp:95
unsigned char data_read
Definition: hwspi.h:45
#define SPI2X
Definition: hwspi.cpp:49
#define SPE
Definition: hwspi.cpp:38
Definition: pin.h:116
bool finished
finished transmission?
Definition: hwspi.h:86
void SetAlternatePort(bool val)
Definition: pinatport.cpp:85
Build a register for TraceValue&#39;s.
Definition: traceval.h:442
PinAtPort SS
Definition: hwspi.h:57
void SetIrqFlag(Hardware *, unsigned int vector_index)
Definition: irqsystem.cpp:243
#define MSTR
Definition: hwspi.cpp:40
unsigned char GetSPSR()
Definition: hwspi.cpp:72
unsigned char shift_in
Definition: hwspi.h:43
unsigned clkcnt
Definition: hwspi.h:78
void SetSPDR(unsigned char val)
Definition: hwspi.cpp:81
bool spsr_read
Definition: hwspi.h:69
PinAtPort MISO
Definition: hwspi.h:55
unsigned int irq_vector
Definition: hwspi.h:58
Definition: hwspi.h:38
void SetSPSR(unsigned char val)
Definition: hwspi.cpp:106
void rxbit(const int bitpos)
Definition: hwspi.cpp:215
unsigned char GetSPCR()
Definition: hwspi.cpp:77
#define SPIE
Definition: hwspi.cpp:37
HWIrqSystem * irq
Definition: hwspi.h:52
void DebugVerifyInterruptVector(unsigned int vector_index, const Hardware *source)
In datasheets RESET vector is index 1 but we use 0! And not a byte address.
Definition: irqsystem.cpp:297
#define CPHA
When this bit is written to one, output is setup at leading edge and input is sampled trailing edge...
Definition: hwspi.cpp:42
int trace_on
Definition: avrdevice.h:90
#define SPR0
Definition: hwspi.cpp:44
Definition: pin.h:117
#define SPIF
Definition: hwspi.cpp:47
void ClearIrqFlag(unsigned int vector_index)
Definition: irqsystem.cpp:258
#define SPR1
Definition: hwspi.cpp:43
AvrDevice * core
Definition: hwspi.h:51
#define WCOL
Definition: hwspi.cpp:48
#define DORD
"When the DORD bit is written to one, the LSB of the data word is transmitted first."
Definition: hwspi.cpp:39
TraceValue * trace_direct(TraceValueRegister *t, const std::string &name, const bool *val)
Register a directly traced bool value.
Definition: traceval.cpp:788
int clkdiv
Definition: hwspi.h:62