simulavr  1.1.0
hwusi.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 - 2016 Klaus Rudolph & other
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  ****************************************************************************
22  *
23  * $Id$
24  */
25 
26 #include "hwusi.h"
27 #include "avrdevice.h"
28 #include "avrerror.h"
29 #include "hwtimer/hwtimer.h"
30 #include "systemclock.h"
31 
32 void HWUSI::SetUSIDR(unsigned char val) {
33  shift_data = val;
34  setDout();
35 }
36 
37 unsigned char HWUSI::GetUSISR(void) {
38  unsigned char val = counter_data & 0xf;
39  if(irqactive_start) val |= 0x80;
40  if(irqactive_ovr) val |= 0x40;
41  if(flag_stop) val |= 0x20;
42  if(flag_dcol) val |= 0x10;
43  return val;
44 }
45 
46 void HWUSI::SetUSISR(unsigned char val) {
47  /* store counter */
48  counter_data = val & 0xf;
49  /* reset irq/flags */
50  if((val & 0x80) == 0x80) { /* reset USISIF */
52  irqactive_start = false;
53  scl_hold = false;
54  setSCK_TWI(false, sck_ddr, sck_port); // release SCL hold
55  }
56  if((val & 0x40) == 0x40) { /* reset USIOIF */
58  irqactive_ovr = false;
59  scl_hold = false;
60  setSCK_TWI(false, sck_ddr, sck_port); // release SCL hold
61  }
62  /* reset stop flag */
63  if((val & 0x20) == 0x20) /* reset USIPF */
64  flag_stop = false;
65 }
66 
67 void HWUSI::SetUSICR(unsigned char val) {
68  /* store wire mode */
69  WMtype old_wm = wire_mode;
70  wire_mode = (WMtype)((val >> 4) & 0x3);
71  if(old_wm != wire_mode) {
72  /* wire mode changed */
73  switch(wire_mode) {
74  case WM_OFF:
75  controlDO(false);
76  controlTWI(false);
77  break;
78 
79  case WM_3WIRE:
80  controlDO(true);
81  controlTWI(false);
82  setDout();
83  break;
84 
85  case WM_2WIRE:
86  case WM_2WIRE_OVR:
87  if(old_wm != WM_2WIRE && old_wm != WM_2WIRE_OVR) {
88  controlDO(false);
89  controlTWI(true);
90  setDI((shift_data & 0x80) == 0x80, di_ddr, di_port);
91  setSCK_TWI(false, sck_ddr, sck_port);
92  }
93  default:
94  break;
95  }
96  }
97 
98  /* store irq enable flags */
99  irqen_start = (val & 0x80) == 0x80;
100  irqen_ovr = (val & 0x40) == 0x40;
101 
102  /* store clock mode */
103  bool do_count = false;
104  clock_mode = (val >> 1) & 0x7;
105  if(clock_mode < 4) { /* USICS1 = 0 */
106  if(clock_mode == 1) /* software clock strobe with USICLK = 1 */
107  do_count = true;
108  clock_mode &= 0x2; /* reset USICLK */
109  } else if ((clock_mode & 1) == 1) { /* software clock strobe with USITC */
110  if((val & 1) == 1) /* USITC = 1 */
111  do_count = true;
112  }
113  if(do_count) {
114  doCount();
115  if((val & 1) == 1) /* USITC = 1 */
116  toggleSCK();
117  }
118 
119  /* store control data value, bit 0,1 read a 0 in every case */
120  control_data = val & 0xfc;
121 }
122 
123 void HWUSI::doCount(void) {
124  if(clock_mode > 0) {
125  counter_data = (counter_data + 1) & 0xf;
126  if(counter_data == 0) {
127  /* overflow event */
128  irqactive_ovr = true;
130  if(wire_mode == WM_2WIRE_OVR) {
131  scl_hold = true; // SCL hold
132  is_DI_change = false;
133  SystemClock::Instance().Add(this);
134  }
135  if(irqen_ovr) {
136  irq->SetIrqFlag(this, irq_ovr);
137  }
138  }
139  }
140 }
141 
142 void HWUSI::doShift(void) {
143  unsigned char di = (bool)DI ? 1 : 0;
144  shift_data = ((shift_data << 1) | di) & 0xff;
145 }
146 
147 void HWUSI::setDout(void) {
148  if((shift_data & 0x80) == 0x80) {
149  if((wire_mode == WM_OFF) || (wire_mode == WM_3WIRE))
150  setDO(true);
151  else
152  setDI(true, di_ddr, di_port);
153  } else {
154  if((wire_mode == WM_OFF) || (wire_mode == WM_3WIRE))
155  setDO(false);
156  else
157  setDI(false, di_ddr, di_port);
158  }
159 }
160 
161 void HWUSI::toggleSCK(void) {
162  if(SCK.GetPort())
163  SCK.SetPort(false);
164  else
165  SCK.SetPort(true);
166 }
167 
170  DI.GetPin().RegisterCallback(cb);
171 }
172 
173 void HWUSI::setDO(bool state) {
174  DO.SetAlternatePort(state);
175 }
176 
177 void HWUSI::setDI(bool state, bool ddr, bool port) {
178  // set DDOV value for port pin
179  DI.SetAlternateDdr(ddr && (!port || !state));
180 }
181 
182 void HWUSI::setSCK_TWI(bool hold, bool ddr, bool port) {
183  // set DDOV value for port pin
184  SCK.SetAlternateDdr(ddr && (!port || hold));
185 }
186 
187 void HWUSI::controlDO(bool state) {
189 }
190 
191 void HWUSI::controlTWI(bool state) {
192  DI.SetAlternatePullup(false);
193  DI.SetAlternatePort(false);
194  DI.SetUseAlternatePullup(state);
195  DI.SetUseAlternateDdr(state);
197  SCK.SetAlternatePullup(false);
198  SCK.SetAlternatePort(false);
199  SCK.SetUseAlternatePullup(state);
200  SCK.SetUseAlternateDdr(state);
202 }
203 
205  HWIrqSystem *_irq,
206  PinAtPort din,
207  PinAtPort dout,
208  PinAtPort sck,
209  unsigned int ivec_start,
210  unsigned int ivec_ovr):
211  Hardware(_c), TraceValueRegister(_c, "USI"),
212  irq(_irq),
213  DI(din), DO(dout), SCK(sck),
214  irq_start(ivec_start), irq_ovr(ivec_ovr),
215  usidr_reg(this, "USIDR", this, &HWUSI::GetUSIDR, &HWUSI::SetUSIDR),
216  usisr_reg(this, "USISR", this, &HWUSI::GetUSISR, &HWUSI::SetUSISR),
217  usicr_reg(this, "USICR", this, &HWUSI::GetUSICR, &HWUSI::SetUSICR)
218 {
219  irq->DebugVerifyInterruptVector(ivec_start, this);
220  irq->DebugVerifyInterruptVector(ivec_ovr, this);
221 
222  registerDIandSCK(this);
223 
224  trace_direct(this, "ShiftRegister", &shift_data);
225  trace_direct(this, "Counter", &counter_data);
226  Reset();
227 }
228 
229 void HWUSI::Reset() {
230  shift_data = 0;
231  control_data = 0;
232  irqen_start = false;
233  irqen_ovr = false;
234  irqactive_start = false;
235  irqactive_ovr = false;
236  flag_stop = false;
237  flag_dcol = false;
238  wire_mode = WM_OFF;
239  clock_mode = 0;
240  counter_data = 0;
241  sck_state = true; /* initial port state is tristate, read as 1 */
242  di_state = true;
243  sck_ddr = false; /* initial ddr state is 0 */
244  di_ddr = false;
245  sck_port = false; /* initial port state is 0 */
246  di_port = false;
247  is_DI_change = false; /* just to initialize, will be set on demand! */
248  scl_hold = false;
249 
250  /* reset port pin states */
251  controlDO(false);
252  controlTWI(false);
253 }
254 
255 int HWUSI::Step(bool &untilCoreStepFinished, SystemClockOffset *nextStepIn_ns) {
256  /* change SDA or SCK output, if necessary. This can't be made in PiStateHasChanged,
257  no pin change inside this method or you'll get a infinite loop ... */
258  if(is_DI_change)
259  setDI((shift_data & 0x80) == 0x80, di_ddr, di_port);
260  else
262  if(nextStepIn_ns != NULL)
263  *nextStepIn_ns = -1;
264  return 0;
265 }
266 
268  if(&DI.GetPin() == p) {
269  // pin change on DI = SDA
270  bool tmp_state = (bool)DI, tmp_ddr = DI.GetDdr(), tmp_port = DI.GetPort();
271 
272  // check start condition in 2wire mode
273  if((wire_mode == WM_2WIRE) || (wire_mode == WM_2WIRE_OVR)) {
274  // is DI alternate mode changed?
275  if(tmp_ddr != di_ddr || tmp_port != di_port) {
276  is_DI_change = true;
277  SystemClock::Instance().Add(this);
278  }
279  // check edge
280  if(!tmp_state && di_state && sck_state) { // SDA edge to low while SCL high
281  irqactive_start = true;
282  if(irqen_start) {
283  irq->SetIrqFlag(this, irq_start);
284  }
285  }
286  }
287 
288  /* save di states */
289  di_state = tmp_state;
290  di_ddr = tmp_ddr;
291  di_port = tmp_port;
292  return;
293  }
294 
295  // pin change on SCK: save ddr and port value
296  bool current = (bool)SCK, tmp_ddr = SCK.GetDdr(), tmp_port = SCK.GetPort();
297  if((wire_mode == WM_2WIRE) || (wire_mode == WM_2WIRE_OVR)) {
298  if(tmp_ddr != sck_ddr || tmp_port != sck_port) {
299  is_DI_change = false;
300  SystemClock::Instance().Add(this);
301  }
302  }
303  if(current == sck_state)
304  return; /* no action */
305 
306  if((wire_mode == WM_2WIRE) || (wire_mode == WM_2WIRE_OVR)) {
307  if(!current && sck_state && irqactive_start && !scl_hold) {
308  // set scl hold after start condition found and scl is going low
309  scl_hold = true;
310  is_DI_change = false;
311  SystemClock::Instance().Add(this);
312  }
313  }
314 
315  /* save sck states */
316  sck_state = current;
317  sck_ddr = tmp_ddr;
318  sck_port = tmp_port;
319 
320  /* if not in 2wire mode, every clock change will set USISIF */
321  if((wire_mode == WM_OFF) || (wire_mode == WM_3WIRE)) {
322  irqactive_start = true;
323  if(irqen_start)
324  irq->SetIrqFlag(this, irq_start);
325  }
326 
327  /* clock mode: get clock from external source? */
328  if(clock_mode < 4)
329  return; /* no action */
330 
331  /* counter control */
332  if((clock_mode & 1) == 0)
333  doCount();
334 
335  /* SCK edge action */
336  bool neg_edge = (clock_mode & 2) == 2;
337  if(sck_state) {
338  /* pos. edge: L -> H */
339  if(neg_edge)
340  setDout();
341  else
342  doShift();
343  } else {
344  /* neg. edge: H -> L */
345  if(neg_edge)
346  doShift();
347  else
348  setDout();
349  }
350 }
351 
352 void HWUSI::fireEvent(int event) {
353  if((event == BasicTimerUnit::EVT_COMPARE_1) && (clock_mode == 2)) {
354  /* timer 0 compare match with OCR0A register */
355  doShift();
356  doCount();
357  setDout();
358  }
359 }
360 
362  HWIrqSystem *_irq,
363  PinAtPort din,
364  PinAtPort dout,
365  PinAtPort sck,
366  unsigned int ivec_start,
367  unsigned int ivec_ovr):
368  HWUSI(_c, _irq, din, dout, sck, ivec_start, ivec_ovr),
369  usibr_reg(this, "USIBR", this, &HWUSI_BR::GetUSIBR, &HWUSI_BR::SetUSIBR)
370 {
371  Reset();
372 }
373 
374 void HWUSI_BR::SetUSIBR(unsigned char val) {
375  avr_warning("register USIBR is read only (try to write value 0x%02x)", val);
376 }
377 
379  buffer_data = 0;
380  HWUSI::Reset();
381 }
382 
383 void HWUSI_BR::setDataBuffer(unsigned char data) {
384  buffer_data = data;
385 }
386 
387 /* EOF */
void Add(SimulationMember *dev)
Add a simulation member (normally a device)
void PinStateHasChanged(Pin *)
Definition: hwusi.cpp:267
Pin & GetPin()
Definition: pinatport.cpp:45
Basic AVR device, contains the core functionality.
Definition: avrdevice.h:66
bool irqen_start
Definition: hwusi.h:85
bool sck_state
Definition: hwusi.h:68
unsigned int irq_ovr
Definition: hwusi.h:89
compare[0] value reached for one count cycle
Definition: hwtimer.h:64
Pin class, handles input and output to external parts.
Definition: pin.h:98
IOReg< HWUSI > usicr_reg
Definition: hwusi.h:168
void SetAlternateDdr(bool val)
Definition: pinatport.cpp:75
bool GetDdr()
Definition: pinatport.cpp:104
bool scl_hold
Definition: hwusi.h:80
bool irqactive_ovr
Definition: hwusi.h:93
virtual void setDataBuffer(unsigned char data)
Definition: hwusi.h:121
HWUSI_BR(AvrDevice *core, HWIrqSystem *, PinAtPort din, PinAtPort dout, PinAtPort sck, unsigned int irq_start, unsigned int irq_ovr)
Definition: hwusi.cpp:361
Definition: hwusi.h:41
virtual void toggleSCK(void)
Definition: hwusi.cpp:161
bool irqen_ovr
Definition: hwusi.h:91
bool flag_dcol
Definition: hwusi.h:97
virtual void controlTWI(bool state)
Definition: hwusi.cpp:191
virtual void setDO(bool state)
Definition: hwusi.cpp:173
virtual void setDataBuffer(unsigned char data)
Definition: hwusi.cpp:383
bool irqactive_start
Definition: hwusi.h:87
void SetUseAlternatePullup(bool val)
Definition: pinatport.cpp:70
unsigned char GetUSISR(void)
Definition: hwusi.cpp:37
unsigned char buffer_data
Definition: hwusi.h:177
void SetUSIBR(unsigned char val)
Definition: hwusi.cpp:374
void SetUseAlternatePortIfDdrSet(bool val)
Definition: pinatport.cpp:95
void doCount(void)
Definition: hwusi.cpp:123
HWUSI(AvrDevice *core, HWIrqSystem *, PinAtPort din, PinAtPort dout, PinAtPort sck, unsigned int irq_start, unsigned int irq_ovr)
Definition: hwusi.cpp:204
void SetAlternatePort(bool val)
Definition: pinatport.cpp:85
Build a register for TraceValue&#39;s.
Definition: traceval.h:442
unsigned int irq_start
Definition: hwusi.h:83
static SystemClock & Instance()
Returns the central SystemClock instance for the application.
void SetIrqFlag(Hardware *, unsigned int vector_index)
Definition: irqsystem.cpp:243
virtual void setDI(bool state, bool ddr, bool port)
Definition: hwusi.cpp:177
PinAtPort SCK
Definition: hwusi.h:66
bool sck_port
Definition: hwusi.h:70
WMtype wire_mode
Definition: hwusi.h:100
void SetPort(bool val)
Definition: pinatport.cpp:49
unsigned char shift_data
Definition: hwusi.h:57
unsigned char GetUSICR(void)
Definition: hwusi.h:165
void doShift(void)
Definition: hwusi.cpp:142
PinAtPort DO
Definition: hwusi.h:64
long long SystemClockOffset
virtual void fireEvent(int event)
Definition: hwusi.cpp:352
virtual void setSCK_TWI(bool hold, bool ddr, bool port)
Definition: hwusi.cpp:182
unsigned char counter_data
Definition: hwusi.h:107
void SetAlternatePullup(bool val)
Definition: pinatport.cpp:65
bool GetPort()
Definition: pinatport.cpp:100
bool di_port
Definition: hwusi.h:76
void SetUSICR(unsigned char val)
Definition: hwusi.cpp:67
void SetUseAlternateDdr(bool val)
Definition: pinatport.cpp:80
void RegisterCallback(HasPinNotifyFunction *)
Definition: pin.cpp:60
#define avr_warning(...)
Definition: avrerror.h:133
virtual void registerDIandSCK(HWUSI *cb)
Definition: hwusi.cpp:168
IOReg< HWUSI > usisr_reg
Definition: hwusi.h:168
IOReg< HWUSI > usidr_reg
Definition: hwusi.h:168
unsigned char GetUSIDR(void)
Definition: hwusi.h:163
WMtype
Definition: hwusi.h:45
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
virtual void controlDO(bool state)
Definition: hwusi.cpp:187
HWIrqSystem * irq
Definition: hwusi.h:54
bool sck_ddr
Definition: hwusi.h:72
bool flag_stop
Definition: hwusi.h:95
void SetUSISR(unsigned char val)
Definition: hwusi.cpp:46
virtual void Reset()
Definition: hwusi.cpp:229
bool di_state
Definition: hwusi.h:74
void setDout(void)
Definition: hwusi.cpp:147
bool di_ddr
Definition: hwusi.h:78
void ClearIrqFlag(unsigned int vector_index)
Definition: irqsystem.cpp:258
unsigned char control_data
Definition: hwusi.h:59
void SetUSIDR(unsigned char val)
Definition: hwusi.cpp:32
int Step(bool &untilCoreStepFinished, SystemClockOffset *nextStepIn_ns=0)
Return nonzero if a breakpoint was hit.
Definition: hwusi.cpp:255
virtual void Reset()
Definition: hwusi.cpp:378
PinAtPort DI
Definition: hwusi.h:62
bool is_DI_change
Definition: hwusi.h:117
TraceValue * trace_direct(TraceValueRegister *t, const std::string &name, const bool *val)
Register a directly traced bool value.
Definition: traceval.cpp:788
unsigned char clock_mode
Definition: hwusi.h:102