Fixed problem in DeviceSettings::strParam, returned wrong string
[avr-sim.git] / src / Timer16.cpp
blobc8ca352199db41b40ce6bdf00d6321c86c39bdb1
1 /*
2 * avr-sim: An atmel AVR simulator
3 * Copyright (C) 2008 Tom Haber
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "Timer16.h"
20 #include "Registers.h"
21 #include "Bus.h"
22 #include "ImplementationException.h"
24 #include <iostream>
25 #include <cstring>
27 enum {
28 CS0 = 1 << 0, /* timer/counter clock select bit 0 */
29 CS1 = 1 << 1,
30 CS2 = 1 << 2,
31 WGM2 = 1 << 3,
32 WGM3 = 1 << 4,
33 ICES = 1 << 6, /* input capture edge select */
34 ICNC = 1 << 7, /* input capture noise canceler (4 CKs) */
35 mask_CS = (CS0 | CS1 | CS2),
36 mask_WGMb = (WGM2 | WGM3)
39 enum {
40 CS_STOP = 0x00, /* Stop, the Timer/Counter is stopped */
41 CS_CK = 0x01, /* CK */
42 CS_CK_8 = 0x02, /* CK/8 */
43 CS_CK_64 = 0x03, /* CK/64 */
44 CS_CK_256 = 0x04, /* CK/256 */
45 CS_CK_1024 = 0x05, /* CK/1024 */
46 CS_EXT_FALL = 0x06, /* External Pin Tn, falling edge */
47 CS_EXT_RISE = 0x07, /* External Pin Tn, rising edge */
50 enum {
51 WGM0 = (1<<0),
52 WGM1 = (1<<1),
53 FOCB = (1<<2),
54 FOCA = (1<<3),
55 COMB0 = (1<<4),
56 COMB1 = (1<<5),
57 COMA0 = (1<<6),
58 COMA1 = (1<<7),
59 mask_WGMa = (WGM1|WGM0),
60 mask_COMB = (COMB1|COMB0),
61 mask_COMA = (COMA1|COMA0)
64 enum {
65 MODE_NORMAL = 0,
66 MODE_PWM_8 = 1,
67 MODE_PWM_9 = 2,
68 MODE_PWM_10 = 3,
69 MODE_CTC_OCR = 4,
70 MODE_FASTPWM_8 = 5,
71 MODE_FASTPWM_9 = 6,
72 MODE_FASTPWM_10 = 7,
73 MODE_PWM_PNF_ICR = 8,
74 MODE_PWM_PNF_OCR = 9,
75 MODE_PWM_ICR = 10,
76 MODE_PWM_OCR = 11,
77 MODE_CTC_ICR = 12,
78 MODE_FASTPWM_ICR = 14,
79 MODE_FASTPWM_OCR = 15
82 enum {
83 COM_NORMAL = 0,
84 COM_TOGGLE = 1,
85 COM_CLEAR = 2,
86 COM_SET = 3
90 namespace avr {
92 #define overflow() *tifr |= tovmask
93 #define compareMatch() *tifr |= ocfmask
95 void OutputCompareUnit16::flush() {
96 ocrBuf = (ocrh->get() << 8) | ocrl->get();
99 void OutputCompareUnit16::regChanged(IORegister *reg) {
100 if( reg == ocrl )
101 ocrBuf = (ocrh->get() << 8) | ocrl->get();
104 void OutputCompareUnit16::forceOutputCompare(IORegister *tifr, word tcnt) {
105 compareMatchOutput( tifr, tcnt );
108 void OutputCompareUnit16::compareMatchOutput(IORegister *tifr, word tcnt) {
109 // the non-PWM modes are NORMAL and CTC
110 // under NORMAL, there is no pin action for a compare match
111 // under CTC, the action is to clear the pin.
112 if( tcnt == ocrBuf ) {
113 switch( compareMode ) {
114 case COM_NORMAL:
115 break;
117 case COM_TOGGLE:
118 //outputComparePin.toggle();
119 break;
121 case COM_CLEAR:
122 //outputComparePin.low();
123 break;
125 case COM_SET:
126 //outputComparePin.high();
127 break;
130 compareMatch();
134 Timer16::Timer16(Bus & bus, unsigned char tovmask, int compunits,
135 unsigned char ocfmask[])
136 : Hardware(bus), tovmask(tovmask), direction(1) {
137 blockCompareMatch = false;
138 timerMode = 0;
139 period = 0;
141 nUnits = compunits;
142 for(int i = 0; i < nUnits; ++i)
143 units[i].setMask( ocfmask[i] );
146 bool Timer16::attachReg(const char *name, IORegister *reg) {
147 if( strcmp(name, "tcntl") == 0 )
148 tcntl = reg;
149 else if( strcmp(name, "tcnth") == 0 )
150 tcnth = reg;
151 else if( strcmp(name, "tccra") == 0 )
152 tccra = reg;
153 else if( strcmp(name, "tccrb") == 0 )
154 tccrb = reg;
155 else if( strcmp(name, "ocrah") == 0 || strcmp(name, "ocrh") == 0 )
156 units[0].setOcrhRegister(reg);
157 else if( strcmp(name, "ocrbh") == 0 )
158 units[1].setOcrhRegister(reg);
159 else if( strcmp(name, "ocral") == 0 || strcmp(name, "ocrl") == 0 )
160 units[0].setOcrlRegister(reg);
161 else if( strcmp(name, "ocrbl") == 0 )
162 units[1].setOcrlRegister(reg);
163 else if( strcmp(name, "tifr") == 0 ) {
164 tifr = reg;
165 return true;
166 } else
167 return false;
169 reg->registerHW(this);
170 return true;
173 bool Timer16::finishBuild() {
174 bool unitsOk = true;
175 for(int i = 0; i < nUnits; ++i)
176 unitsOk = unitsOk && units[i].ocrSet();
178 return (tcntl != 0) && (tcnth != 0)
179 && (tccra != 0) && (tccrb != 0)
180 && (tifr != 0) && unitsOk;
183 #define getTimerMode() (((tccrb->get() & mask_WGMb) >> 1) | (tccra->get() & mask_WGMa))
184 #define getCOMa(val) (((val) & mask_COMA) >> 6)
185 #define getCOMb(val) (((val) & mask_COMA) >> 4)
187 void Timer16::regChanged( IORegister *reg ) {
188 if( reg == tccra ) {
189 unsigned char val = tccra->get();
191 unsigned char timerModeNew = getTimerMode();
192 if( timerMode != timerModeNew )
193 direction = +1;
194 timerMode = timerModeNew;
196 units[0].setCompareMode( getCOMa(val) );
197 units[0].setCompareMode( getCOMb(val) );
199 if( val & FOCA )
200 units[0].forceOutputCompare(tifr, tcnt);
201 if( val & FOCB )
202 units[1].forceOutputCompare(tifr, tcnt);
204 } else if( reg == tccrb ) {
205 unsigned char val = tccrb->get();
207 unsigned char timerModeNew = getTimerMode();
208 if( timerMode != timerModeNew )
209 direction = +1;
210 timerMode = timerModeNew;
212 setClock(val);
213 } else if( reg == tcntl ) {
214 tcnt |= (tcnth->get()<<8) | tcntl->get();
215 blockCompareMatch = true;
216 } else {
217 if( (timerMode == MODE_NORMAL) || (timerMode == MODE_CTC_OCR)
218 || (timerMode == MODE_CTC_ICR) ) {
219 for(int i = 0; i < nUnits; ++i)
220 units[i].regChanged(reg);
225 void Timer16::setClock(unsigned char tccr) {
226 byte clk = tccr & mask_CS;
227 switch( clk ) {
228 case CS_STOP:
229 bus.clearBreak(this);
230 return;
232 case CS_CK:
233 period = 1;
234 break;
236 case CS_CK_8:
237 period = 8;
238 break;
240 case CS_CK_64:
241 period = 64;
242 break;
244 case CS_CK_256:
245 period = 256;
246 break;
248 case CS_CK_1024:
249 period = 1024;
250 break;
252 case CS_EXT_FALL:
253 case CS_EXT_RISE:
254 throw util::ImplementationException(
255 "external timer/counter sources not implemented" );
258 bus.setBreakDelta(period, this);
261 void Timer16::step() {
262 static const word TOP[] = {
263 0xffff, 0x00ff, 0x01ff, 0x03ff, 0, 0x0ff, 0x01ff, 0x03ff
265 static const word MAX = 0xffff;
267 word compareI = 0;
268 word compareA = units[0].value();
270 word tcntSave = tcnt;
271 bool tov = false;
273 switch( timerMode ) {
274 case MODE_NORMAL:
275 if( tcnt == TOP[timerMode] ) {
276 tov = true;
277 tcnt = 0;
279 break;
281 case MODE_PWM_8:
282 case MODE_PWM_9:
283 case MODE_PWM_10:
284 if( (tcnt == TOP[timerMode]) && (direction == +1) ) {
285 direction = -1;
286 flushOCRn();
287 } else if( (tcnt == 0) && (direction == -1) ) {
288 direction = +1;
289 tov = true;
291 break;
293 case MODE_CTC_OCR:
294 if( tcnt == compareA ) {
295 tcnt = 0;
296 } else if( tcnt == MAX ) {
297 tov = true;
299 break;
301 case MODE_FASTPWM_8:
302 case MODE_FASTPWM_9:
303 case MODE_FASTPWM_10:
304 if( tcnt == TOP[timerMode] ) {
305 tcnt = 0;
306 flushOCRn();
307 tov = true;
309 break;
311 case MODE_PWM_PNF_ICR:
312 if( (tcnt == compareI) && (direction == +1) ) {
313 direction = -1;
314 } else if( (tcnt == 0) && (direction == -1) ) {
315 direction = +1;
316 flushOCRn();
317 tov = true;
319 break;
321 case MODE_PWM_PNF_OCR:
322 if( (tcnt == compareA) && (direction == +1) ) {
323 direction = -1;
324 } else if( (tcnt == 0) && (direction == -1) ) {
325 direction = +1;
326 flushOCRn();
327 tov = true;
329 break;
331 case MODE_PWM_ICR:
332 if( (tcnt == compareI) && (direction == +1) ) {
333 direction = -1;
334 flushOCRn();
335 } else if( (tcnt == 0) && (direction == -1) ) {
336 direction = +1;
337 tov = true;
339 break;
341 case MODE_PWM_OCR:
342 if( (tcnt == compareI) && (direction == +1) ) {
343 direction = -1;
344 flushOCRn();
345 } else if( (tcnt == 0) && (direction == -1) ) {
346 direction = +1;
347 tov = true;
349 break;
351 case MODE_CTC_ICR:
352 if( tcnt == compareI ) {
353 tcnt = 0;
354 } else if( tcnt == MAX ) {
355 tov = true;
357 break;
359 case MODE_FASTPWM_ICR:
360 if( tcnt == compareI ) {
361 tcnt = 0;
362 tov = true;
363 flushOCRn();
365 break;
367 case MODE_FASTPWM_OCR:
368 if( tcnt == compareI ) {
369 tcnt = 0;
370 tov = true;
371 flushOCRn();
373 break;
376 if( tov )
377 overflow();
378 else
379 tcnt += direction;
381 if( ! blockCompareMatch ) {
382 for(int cntr = 0; cntr < nUnits; cntr++ )
383 units[cntr].compareMatchOutput(tifr, tcntSave);
385 blockCompareMatch = false;
387 tcntl->set( tcnt & 0xff );
388 tcnth->set( (tcnt >> 8) & 0xff );
389 bus.setBreakDelta(period, this);
392 void Timer16::flushOCRn() {
393 for(int cntr = 0; cntr < nUnits; cntr++ )
394 units[cntr].flush();