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/>.
20 #include "Registers.h"
22 #include "ImplementationException.h"
28 CS0
= 1 << 0, /* timer/counter clock select bit 0 */
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
)
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 */
59 mask_WGMa
= (WGM1
|WGM0
),
60 mask_COMB
= (COMB1
|COMB0
),
61 mask_COMA
= (COMA1
|COMA0
)
78 MODE_FASTPWM_ICR
= 14,
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
) {
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
) {
118 //outputComparePin.toggle();
122 //outputComparePin.low();
126 //outputComparePin.high();
134 Timer16::Timer16(Bus
& bus
, unsigned char tovmask
, int compunits
,
135 unsigned char ocfmask
[])
136 : Hardware(bus
), tovmask(tovmask
), direction(1) {
137 blockCompareMatch
= false;
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 )
149 else if( strcmp(name
, "tcnth") == 0 )
151 else if( strcmp(name
, "tccra") == 0 )
153 else if( strcmp(name
, "tccrb") == 0 )
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 ) {
169 reg
->registerHW(this);
173 bool Timer16::finishBuild() {
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
) {
189 unsigned char val
= tccra
->get();
191 unsigned char timerModeNew
= getTimerMode();
192 if( timerMode
!= timerModeNew
)
194 timerMode
= timerModeNew
;
196 units
[0].setCompareMode( getCOMa(val
) );
197 units
[0].setCompareMode( getCOMb(val
) );
200 units
[0].forceOutputCompare(tifr
, tcnt
);
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
)
210 timerMode
= timerModeNew
;
213 } else if( reg
== tcntl
) {
214 tcnt
|= (tcnth
->get()<<8) | tcntl
->get();
215 blockCompareMatch
= true;
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
;
229 bus
.clearBreak(this);
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;
268 word compareA
= units
[0].value();
270 word tcntSave
= tcnt
;
273 switch( timerMode
) {
275 if( tcnt
== TOP
[timerMode
] ) {
284 if( (tcnt
== TOP
[timerMode
]) && (direction
== +1) ) {
287 } else if( (tcnt
== 0) && (direction
== -1) ) {
294 if( tcnt
== compareA
) {
296 } else if( tcnt
== MAX
) {
303 case MODE_FASTPWM_10
:
304 if( tcnt
== TOP
[timerMode
] ) {
311 case MODE_PWM_PNF_ICR
:
312 if( (tcnt
== compareI
) && (direction
== +1) ) {
314 } else if( (tcnt
== 0) && (direction
== -1) ) {
321 case MODE_PWM_PNF_OCR
:
322 if( (tcnt
== compareA
) && (direction
== +1) ) {
324 } else if( (tcnt
== 0) && (direction
== -1) ) {
332 if( (tcnt
== compareI
) && (direction
== +1) ) {
335 } else if( (tcnt
== 0) && (direction
== -1) ) {
342 if( (tcnt
== compareI
) && (direction
== +1) ) {
345 } else if( (tcnt
== 0) && (direction
== -1) ) {
352 if( tcnt
== compareI
) {
354 } else if( tcnt
== MAX
) {
359 case MODE_FASTPWM_ICR
:
360 if( tcnt
== compareI
) {
367 case MODE_FASTPWM_OCR
:
368 if( tcnt
== compareI
) {
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
++ )