Watchdog trouble

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I got the watchdog working on an XMega32E5 based project this evening. The reason I'm posting is that wdt_enable() didn't work, but doing it by hand with _PROTECTED_WRITE and the correct contents of WDT.CTRL and WDT.WCTRL did work.

 

wdt_enable() has worked in the past on tiny and mega controllers. I see there's code in avr/wdt.h for the XMega... but it doesn't seem to work...?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

nsayer wrote:
I see there's code in avr/wdt.h for the XMega... but it doesn't seem to work...?

Why not post the [nearly trivial?] test program invoking the WD in your environment and for your target.  Posting the build sequence will help to determine version and similar.  Posting the generated code, along with your hand-build sequence, will allow us tyros to compare and discuss.

 

000000c4 <main>:
#include <avr/io.h>
#include <avr/wdt.h>

int main(void)
{
	wdt_enable(123);
  c4:	9b e7       	ldi	r25, 0x7B	; 123
  c6:	88 ed       	ldi	r24, 0xD8	; 216
  c8:	08 b6       	in	r0, 0x38	; 56
  ca:	18 be       	out	0x38, r1	; 56
  cc:	84 bf       	out	0x34, r24	; 52
  ce:	90 93 80 00 	sts	0x0080, r25	; 0x800080 <__TEXT_REGION_LENGTH__+0x700080>
  d2:	80 91 82 00 	lds	r24, 0x0082	; 0x800082 <__TEXT_REGION_LENGTH__+0x700082>
  d6:	81 fd       	sbrc	r24, 1
  d8:	fc cf       	rjmp	.-8      	; 0xd2 <main+0xe>
  da:	08 be       	out	0x38, r0	; 56
  dc:	ff cf       	rjmp	.-2      	; 0xdc <main+0x18>

000000de <_exit>:
  de:	f8 94       	cli

000000e0 <__stop_program>:
  e0:	ff cf       	rjmp	.-2      	; 0xe0 <__stop_program>

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Wed. Jun 28, 2017 - 03:10 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wdt.h has:

152 	#define wdt_enable(timeout) \
153 	do { \
154 	uint8_t temp; \
155 	__asm__ __volatile__ (         \
156 	    "in __tmp_reg__, %[rampd]"              "\n\t" \
157 	    "out %[rampd], __zero_reg__"            "\n\t" \
158 	    "out %[ccp_reg], %[ioreg_cen_mask]"     "\n\t" \
159 	    "sts %[wdt_reg], %[wdt_enable_timeout]" "\n\t" \
160 	    "1:lds %[tmp], %[wdt_status_reg]"       "\n\t" \
161 	    "sbrc %[tmp], %[wdt_syncbusy_bit]"      "\n\t" \
162 	    "rjmp 1b"                               "\n\t" \
163 	    "out %[rampd], __tmp_reg__"             "\n\t" \
164 	    : [tmp]                "=r" (temp) \
165 	    : [rampd]              "I" (_SFR_IO_ADDR(RAMPD)),      \
166 	      [ccp_reg]            "I" (_SFR_IO_ADDR(CCP)),        \
167 	      [ioreg_cen_mask]     "r" ((uint8_t)CCP_IOREG_gc),     \
168 	      [wdt_reg]            "n" (_SFR_MEM_ADDR(WDT_CTRL)),   \
169 	      [wdt_enable_timeout] "r" ((uint8_t)(WDT_CEN_bm | WDT_ENABLE_bm | timeout)), \
170 	      [wdt_status_reg]     "n" (_SFR_MEM_ADDR(WDT_STATUS)), \
171 	      [wdt_syncbusy_bit]   "I" (WDT_SYNCBUSY_bm)            \
172 	    : "r0" \
173 	); \
174 	} while(0)

So I guess one might annotate what Lee saw as:

  c8:	08 b6       	in	r0, 0x38	;   in __tmp_reg__, %[rampd]
  ca:	18 be       	out	0x38, r1	;   out %[rampd], __zero_reg__
  cc:	84 bf       	out	0x34, r24	;   out %[ccp_reg], %[ioreg_cen_mask]
  ce:	90 93 80 00 	sts	0x0080, r25	;   sts %[wdt_reg], %[wdt_enable_timeout]
  d2:	80 91 82 00 	lds	r24, 0x0082	; 1:lds %[tmp], %[wdt_status_reg]
  d6:	81 fd           sbrc r24, 1             ;   sbrc %[tmp], %[wdt_syncbusy_bit]
  d8:	fc cf       	rjmp	.-8             ;   rjmp 1b
  da:	08 be       	out	0x38, r0	;   out %[rampd], __tmp_reg__

Where the symbols there are:

[rampd]              _SFR_IO_ADDR(RAMPD)
[ccp_reg]            _SFR_IO_ADDR(CCP)
[ioreg_cen_mask]     (uint8_t)CCP_IOREG_gc
[wdt_reg]            _SFR_MEM_ADDR(WDT_CTRL)
[wdt_enable_timeout] (uint8_t)(WDT_CEN_bm | WDT_ENABLE_bm | timeout)
[wdt_status_reg]     _SFR_MEM_ADDR(WDT_STATUS)
[wdt_syncbusy_bit]   WDT_SYNCBUSY_bm    

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have no idea what a proper/successful WD setup sequence is for an Xmega.  Hmmm--let me fire up the CodeVision Wizard and see what it does.  [I'll ass-u-me that the CV sequence works...]

 

// I/O Registers definitions
#include <io.h>

// Watchdog Timer initialization
void watchdog_init(void)
{
unsigned char s,n;

// Optimize for speed
#pragma optsize- 
// Save interrupts enabled/disabled state
s=SREG;
// Disable interrupts
#asm("cli")

// Watchdog Timer: On
// Watchdog timeout period: 500 ms
n=(WDT.CTRL & (~WDT_PER_gm)) | WDT_PER_500CLK_gc | WDT_ENABLE_bm | WDT_CEN_bm;
CCP=CCP_IOREG_gc;
WDT.CTRL=n;
// Watchdog window mode: Off
n=(WDT.WINCTRL & (~WDT_WEN_bm)) | WDT_WCEN_bm;
CCP=CCP_IOREG_gc;
WDT.WINCTRL=n;

// Restore interrupts enabled/disabled state
SREG=s;
// Restore optimization for size if needed
#pragma optsize_default
}
                 _watchdog_init:
                 ;.FSTART _watchdog_init
                 ;0004 0010 unsigned char s,n;
                 ;0004 0011 
                 ;0004 0012 // Optimize for speed
                 ;0004 0013 #pragma optsize-
                 ;0004 0014 // Save interrupts enabled/disabled state
                 ;0004 0015 s=SREG;
00014c 931a      	ST   -Y,R17
00014d 930a      	ST   -Y,R16
                 ;	s -> R17
                 ;	n -> R16
00014e b71f      	IN   R17,63
                 ;0004 0016 // Disable interrupts
                 ;0004 0017 #asm("cli")
00014f 94f8      	CLI
                 ;0004 0018 
                 ;0004 0019 // Watchdog Timer: On
                 ;0004 001A // Watchdog timeout period: 500 ms
                 ;0004 001B n=(WDT.CTRL & (~WDT_PER_gm)) | WDT_PER_500CLK_gc | WDT_ENABLE_bm | WDT_CEN_bm;
000150 91e0 0080 	LDS  R30,128
000152 7ce3      	ANDI R30,LOW(0xC3)
000153 61eb      	ORI  R30,LOW(0x1B)
000154 2f0e      	MOV  R16,R30
                 ;0004 001C CCP=CCP_IOREG_gc;
000155 ede8      	LDI  R30,LOW(216)
000156 bfe4      	OUT  0x34,R30
                 ;0004 001D WDT.CTRL=n;
000157 9300 0080 	STS  128,R16
                 ;0004 001E // Watchdog window mode: Off
                 ;0004 001F n=(WDT.WINCTRL & (~WDT_WEN_bm)) | WDT_WCEN_bm;
000159 91e0 0081 	LDS  R30,129
00015b 7fed      	ANDI R30,0xFD
00015c 60e1      	ORI  R30,1
00015d 2f0e      	MOV  R16,R30
                 ;0004 0020 CCP=CCP_IOREG_gc;
00015e ede8      	LDI  R30,LOW(216)
00015f bfe4      	OUT  0x34,R30
                 ;0004 0021 WDT.WINCTRL=n;
000160 9300 0081 	STS  129,R16
                 ;0004 0022 
                 ;0004 0023 // Restore interrupts enabled/disabled state
                 ;0004 0024 SREG=s;
000162 bf1f      	OUT  0x3F,R17
                 ;0004 0025 // Restore optimization for size if needed
                 ;0004 0026 #pragma optsize_default
                 ;0004 0027 }
                 _0x2060001:
000163 9109      	LD   R16,Y+
000164 9119      	LD   R17,Y+
000165 9508      	RET
                 ;.FEND
                 ;

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The code for this project is on github: https://github.com/nsayer/GPS_clock/blob/master/GPS_Clock_v4.c

The code that's checked in uses _PROTECTED_WRITE and direct register values and works. The commented out line right above that does wdt_enable() seems to result in the watchdog triggering constantly, as if the time setting was wrong (or a long closed window being set, which causes my code to reset during a closed time, triggering it).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I compiled the code both ways down to assembly, and I think the issue is that the constants for the watchdog period result in values from 1 to 6. For the XMega, the WDT.CTRLA register period bits are bits 2 through 5. Nothing in the code shifts the period values over two bits. So wdt_enable() is just plain broken for XMega so far as I can tell. It's not a huge surprise, given that it doesn't have support for the closed window configuration anyway.

 

The differences in just using _PROTECTED_WRITE are that I'm not waiting for the syncbusy flag (but I don't think I care), and I'm not disabling interrupts around the change (but interrupts are not yet enabled at this point in the code).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

nsayer wrote:
So wdt_enable() is just plain broken for XMega so far as I can tell.
And yet someone actually wrote:

138 	#if defined(__AVR_XMEGA__)

so at some stage Xmega support was added and was presumably tested/working.

 

HOWEVER note:

 

 

 

So even the HEAD of AVR-LibC has not seen an update to that file in 14 months which surely predates the widespread availability of XE5 ? What's more the last history I can see on wdt.h that mentions Xmega are actually:

 

 

So the best part of 3 years ago by one of the engineers who works for Atmel. That definitely predates XE5. The E5 is quite, quite different to the "family" Xmega chips. In fact it really deserves to be called some other category of AVR.

Of course the great thing about AVR-LibC is that it's an open project and anyone is free to contribute. So if you know what is required for XE5 support then why not push it back to the repo for the benefit of others?

Last Edited: Wed. Jun 28, 2017 - 05:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, a cursory check of all 5 of the XMega series manuals shows that the watchdog register layout is consistent across all of them, so I'm seriously hard pressed to see how it ever worked.