Forum: Mikrocontroller und Digitale Elektronik ISR-Rücksprungadresse feststellen


von Uhu U. (uhu)


Lesenswert?

Ich möchte in der Watchdog-ISR die Interrupt-Rücksprung-Adresse 
speichern, um nach dem Reset feststellen zu können, wo der Watchdog 
angesprochen hat.

Folgender Code für einen 328P:
1
// Speicherbereich, in den der Stack kopiert werden soll. (Die section
2
// .noinit wird nach dem Reset nicht überschrieben!)
3
struct {
4
    uint16_t    sp;
5
    uint8_t     stack[64];
6
} SaveArea __attribute__ ((section (".noinit")));
7
8
ISR(WDT_vect) {
9
    memcpy(&SaveArea.stack, (uint8_t *) SP, 0x900 - (uint16_t) SP);
10
    SaveArea.sp = SP;
11
12
    while (1) ;          // Warten, bis uns der Teufel holt…
13
}
14
15
int main(void) {
16
    wdt_enable(WDTO_2S);
17
    WDTCSR |= (1 << WDIE);       // Enable WDT interrupt
18
19
    // Der Watchdog ist jetzt im Reset+Interrupt Mode
20
21
    while (1) ;                  // Watchdog-Timeout provozieren
22
}

Eigentlich habe ich erwartet, dass ich nach dem Watchdog-Reset in der 
Variablen SaveArea.sp den Inhalt des Stacks der ISR incl. 
Rückkehradresse finde.

Dem ist aber nicht so: SaveArea.sp enthält zwar einen plausiblen Wert 
für SP, in SaveArea.stack finde ich aber kein Bytemuster, das eine 
plausible ISR-Rückkehr-Adresse ergibt.

Wo steckt der Fehler?

von c-hater (Gast)


Lesenswert?

Uhu U. schrieb:

> Wo steckt der Fehler?

Wahrscheinlich in deiner Interpretation der Daten.

Zeige den Assemblerquelltext deines Programms und einen Dump der 
aufgezeichneten Stackdaten. Dann werden wir die Rücksprungadresse schon 
finden...

von (prx) A. K. (prx)


Lesenswert?

Den Unterschied zwischen Byte- und Wortadressierung hast du auf der 
Suche nach der Adresse berücksichtigt?

von Uhu U. (uhu)


Lesenswert?

Meine Daten sehen so aus:
1
SaveArea: E9 08 45 00 92 02 78 60 00 01 00 FF 00 00 0F 02 0A 00 0D
2
          13 1B 08 FF 01 6C 00 00 00 00 00 00 00 00 00 00 00 00 00

Die ersten 2 Byte sind der SP: 08E9 - das sieht gut aus.

Der ASM-Code im Hauptprogramm sieht so aus:
1
    1a14:  87 e7         ldi     r24, 0x77       ; 119
2
    1a16:  90 e0         ldi     r25, 0x00       ; 0
3
    1a18:  0e 94 16 0b   call    0x162c          ; 0x162c <uart_puts_P>
4
    1a1c:  d3 cf         rjmp    .-90            ; 0x19c4 <main+0x12c>
5
6
    1a1e:  87 e8         ldi     r24, 0x87       ; 135
7
    1a20:  90 e0         ldi     r25, 0x00       ; 0
8
    1a22:  0e 94 16 0b   call    0x162c          ; 0x162c <uart_puts_P>
9
    1a26:  ff cf         rjmp    .-2             ; 0x1a26 <main+0x18e>

Die ISR-Rückkehradresse müsste also 0x1a26 sein, denn auf diesem Befehl 
schlägt der Watchdog in main zu. Die ISR kopiert dann die Daten in die 
SaveArea und wartet, bis der WD ein zweites mal zuschlägt und den Reset 
auslöst.

Logischerweise müssten danach in der SaveArea die Bytes 26 1a vorkommen 
- tun sie aber nicht.

Die main sieht folgendermaßen aus:
1
int main(void) {
2
    memcpy(&sa, &SaveArea, sizeof sa);
3
4
    initErrorLED();
5
    initWatchdog(WDTO_2S);
6
7
    initializeUART();
8
9
    set_sleep_mode(SLEEP_MODE_IDLE);
10
   sei();
11
12
   ...
13
   
14
    assert(!memcmp(&SaveArea, &sa, sizeof sa));     // check, if SaveArea has been corrupted
15
16
    while (1) {
17
        char c = uart_getchar();
18
        switch (c) {
19
            case 0x1b:
20
                uart_puts_P(PSTR("\r\nREBOOT\r\n"));
21
                while (1)       // reboot
22
                    ;
23
            case '#':           // fill SaveArea.stack with text
24
                uart_puts_P(PSTR("Text: "));
25
                uart_gets(SaveArea.stack, sizeof SaveArea.stack, '\r', 1);
26
                printHeadline();
27
                break;
28
            case '*':           // clear saveArea
29
                memset(&SaveArea, 0, sizeof SaveArea);
30
            case '?':           // print SaveArea
31
                printHeadline();
32
                break;
33
            case '~':           // print SP
34
                uart_puts_P(PSTR("SP = "));
35
                uart_hex_putchar(SPH);
36
                uart_hex_putchar(SPL);
37
                uart_puts_P(PSTR("\r\n"));
38
                break;
39
            default:
40
                uart_putchar(c);
41
        }
42
    }
43
44
    return 0;
45
}

Zu Beginn von main lege ich eine Sicherheitskopie sa der SaveArea an und 
prüfe mit dem assert(…), ob sich durch die (hier weggelassenen) 
Initialisierungen irgendwas geändert hat - das ist nicht der Fall.

Mit den Befehlen * und # habe ich getestet, ob meine SaveArea einen 
manuell ausgelösten Reset unbeschadet übersteht - das ist der Fall, die 
Daten in der SaveArea sollten also den Stack enthalten, wie er in der 
Watchdog-ISR war.

Leider bin ich nicht schlauer, als vergangene Nacht…

: Bearbeitet durch User
von Christoph db1uq K. (christoph_kessler)


Lesenswert?

Kann man die Adresse auch mit pull/push vom Stack holen? So kenne ich 
das aus alter Zeit.

Einmal habe ich einen 8080-Emulator auf dem 6502 laufen lassen, der in 
der Funkschau veröffentlicht war. Die Rücksprungadresse lag allerdings 
daneben, weil die beiden Prozessoren unterschiedliches ablegten, der 
eine die Folgeadresse und der andere die aktuelle.

von Oliver S. (oliverso)


Lesenswert?

Uhu U. schrieb:
> Der ASM-Code im Hauptprogramm sieht so aus:

Und der der ISR?
Welche Werte finden sich denn im Stack?

Da deine ISR nicht naked ist, werden vermutlich doch ein paar Register 
auf den Stack gepusht. Memcopy kann sicherlich nicht ganz ohne. Damit 
zeigt der Stackpointer dann nicht mehr auf das, was du vermutest.

Oliver

von Stefan E. (sternst)


Lesenswert?

Uhu U. schrieb:
1
SaveArea: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 0D
2
          13 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
Da ist deine Rücksprungadresse.

Uhu U. schrieb:
> Logischerweise müssten danach in der SaveArea die Bytes 26 1a vorkommen

Nein, denn erstens ist das die Byte-Adresse, und zweites stehen die 
Bytes auf dem Stack andersherum, denn er wird ja von Oben nach Unten 
beschrieben.

: Bearbeitet durch User
von Uhu U. (uhu)


Lesenswert?

Christoph db1uq K. schrieb:
> Kann man die Adresse auch mit pull/push vom Stack holen? So kenne ich
> das aus alter Zeit.

Dann müsste man den ganzen Stack abräumen, denn die Rückkehradresse 
liegt ja über allem, was die ISR auf den Stack geschoben hat.

Der springende Punkt kann eigentlich nur dieser Aufruf in der ISR sein:
1
memcpy(&SaveArea.stack, (uint8_t *) SP, 0x900 - (uint16_t) SP);
bzw. dessen 2. Parameter sein. Der cast ändert nichts am Inhalt von SP…

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Uhu U. schrieb:
> Logischerweise müssten danach in der SaveArea die Bytes 26 1a vorkommen

Da steht aber 13 0D, was die genaue Hälfte von 26 1A ist. Da der AVR das 
ROM mit 16-Bit-Zugriffen anspricht, und nicht mit 8-Bit-Zugriffen, 
könnte das die Erklärung sein.

von Einer K. (Gast)


Lesenswert?

Uhu U. schrieb:
> Der ASM-Code im Hauptprogramm sieht so aus:

Und die ISR?

Denn:
Durch abzählen der push Operationen, solltest du den abgelegten PC 
lokalisieren können.

In etwa so:
adresse = AktuellerSP+AnzahlPush+FlagByte
Adresse+1 und Adresse+2 sollte dann den PC ergeben

? ? ?

Rufus Τ. F. schrieb:
> Da der AVR das
> ROM mit 16-Bit-Zugriffen anspricht, und nicht mit 8-Bit-Zugriffen,
> könnte das die Erklärung sein.
Das wird so sein, nicht nur könnte.

von Kohan (Gast)


Lesenswert?

Christoph db1uq K. schrieb:

> Kann man die Adresse auch mit pull/push vom Stack holen?

Kann man. Funktioniert ohne Probleme.

von Einer (Gast)


Lesenswert?

Uhu U. schrieb:
> Die ISR-Rückkehradresse müsste also 0x1a26 sein, denn auf diesem Befehl
> schlägt der Watchdog in main zu.

Wie kommst du zu dieser Erkenntnis?

Diese Adresse wird auch auf dem Stack sein.

Die ISR-Rückkehradresse ist davon abhängig wo genau der Watchdog in der 
uart_puts_P zuschlägt.

von Uhu U. (uhu)


Lesenswert?

Oliver S. schrieb:
> Und der der ISR?

Die ganze ISR:
in c:
1
ISR(WDT_vect) {
2
    if ((_SLEEP_CONTROL_REG & (uint8_t)_SLEEP_ENABLE_MASK)) {
3
        // watchdog interrupt in sleep mode
4
PIND |= (1 << 3);
5
        wdt_disable();                                  // prevent reset
6
    } else {
7
        // error condition
8
        WdtTrapAddress = *(uint16_t *) ((uint8_t *) SP + 10);        // Interrupt return address
9
        setErrorLED(1);
10
11
memcpy(&SaveArea.stack, (uint8_t *) SP, 0x900 - (uint16_t) SP);
12
SaveArea.sp = SP;
13
        while (1) ;
14
    }
15
}
(Die Zuweisung WdtTrapAddress = … langt daneben, weil der Offset auf SP 
nicht stimmt - den wollte ich eigentlich mit der SaveArea finden.)

in asm:
1
00001804 <__vector_6>:
2
    1804:  1f 92         push  r1
3
    1806:  0f 92         push  r0
4
    1808:  0f b6         in  r0, 0x3f  ; 63
5
    180a:  0f 92         push  r0
6
    180c:  11 24         eor  r1, r1
7
    180e:  2f 93         push  r18
8
    1810:  3f 93         push  r19
9
    1812:  4f 93         push  r20
10
    1814:  5f 93         push  r21
11
    1816:  6f 93         push  r22
12
    1818:  7f 93         push  r23
13
    181a:  8f 93         push  r24
14
    181c:  9f 93         push  r25
15
    181e:  af 93         push  r26
16
    1820:  bf 93         push  r27
17
    1822:  ef 93         push  r30
18
    1824:  ff 93         push  r31
19
    1826:  03 b6         in  r0, 0x33  ; 51
20
    1828:  00 fe         sbrs  r0, 0
21
    182a:  1a c0         rjmp  .+52       ; 0x1860 <__vector_6+0x5c>
22
    182c:  4b 9a         sbi  0x09, 3  ; 9
23
    182e:  88 e1         ldi  r24, 0x18  ; 24
24
    1830:  0f b6         in  r0, 0x3f  ; 63
25
    1832:  f8 94         cli
26
    1834:  80 93 60 00   sts  0x0060, r24
27
    1838:  10 92 60 00   sts  0x0060, r1
28
    183c:  0f be         out  0x3f, r0  ; 63
29
    183e:  ff 91         pop  r31
30
    1840:  ef 91         pop  r30
31
    1842:  bf 91         pop  r27
32
    1844:  af 91         pop  r26
33
    1846:  9f 91         pop  r25
34
    1848:  8f 91         pop  r24
35
    184a:  7f 91         pop  r23
36
    184c:  6f 91         pop  r22
37
    184e:  5f 91         pop  r21
38
    1850:  4f 91         pop  r20
39
    1852:  3f 91         pop  r19
40
    1854:  2f 91         pop  r18
41
    1856:  0f 90         pop  r0
42
    1858:  0f be         out  0x3f, r0  ; 63
43
    185a:  0f 90         pop  r0
44
    185c:  1f 90         pop  r1
45
    185e:  18 95         reti
46
47
; hier wirds interessant:
48
    1860:  ed b7         in  r30, 0x3d  ; 61
49
    1862:  fe b7         in  r31, 0x3e  ; 62
50
    1864:  82 85         ldd  r24, Z+10  ; 0x0a
51
    1866:  93 85         ldd  r25, Z+11  ; 0x0b
52
    1868:  90 93 79 02   sts  0x0279, r25
53
    186c:  80 93 78 02   sts  0x0278, r24
54
    1870:  5c 9a         sbi  0x0b, 4  ; 11
55
    1872:  8d b7         in  r24, 0x3d  ; 61
56
    1874:  9e b7         in  r25, 0x3e  ; 62
57
    1876:  6d b7         in  r22, 0x3d  ; 61
58
    1878:  7e b7         in  r23, 0x3e  ; 62
59
    187a:  40 e0         ldi  r20, 0x00  ; 0
60
    187c:  59 e0         ldi  r21, 0x09  ; 9
61
    187e:  48 1b         sub  r20, r24
62
    1880:  59 0b         sbc  r21, r25
63
    1882:  88 e3         ldi  r24, 0x38  ; 56
64
    1884:  92 e0         ldi  r25, 0x02  ; 2
65
    1886:  0e 94 5f 0d   call  0x1abe  ; 0x1abe <memcpy>
66
    188a:  8d b7         in  r24, 0x3d  ; 61
67
    188c:  9e b7         in  r25, 0x3e  ; 62
68
    188e:  90 93 37 02   sts  0x0237, r25
69
    1892:  80 93 36 02   sts  0x0236, r24
70
    1896:  ff cf         rjmp  .-2        ; 0x1896 <__vector_6+0x92>

von Uhu U. (uhu)


Lesenswert?

Stefan E. schrieb:
> SaveArea: .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 0D
>           13 .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..Da ist
> deine Rücksprungadresse.

Sie ist es nicht. Wie kommst du darauf?

von Stefan E. (sternst)


Lesenswert?

Uhu U. schrieb:
> Wie kommst du darauf?

Stefan E. schrieb:
> Uhu U. schrieb:
>> Logischerweise müssten danach in der SaveArea die Bytes 26 1a vorkommen
>
> Nein, denn erstens ist das die Byte-Adresse, und zweites stehen die
> Bytes auf dem Stack andersherum, denn er wird ja von Oben nach Unten
> beschrieben.

von Uhu U. (uhu)


Lesenswert?

Rufus Τ. F. schrieb:
> Uhu U. schrieb:
>> Logischerweise müssten danach in der SaveArea die Bytes 26 1a vorkommen
>
> Da steht aber 13 0D, was die genaue Hälfte von 26 1A ist. Da der AVR das
> ROM mit 16-Bit-Zugriffen anspricht, und nicht mit 8-Bit-Zugriffen,
> könnte das die Erklärung sein.

Interessanter Gedanke:
Die ISR pusht 15 Byte, dann müsste die Rückkehradresse bei SP+16 liegen.

---

Ich habs ausprobiert - das kommt hin mit folgendem Befehl:
1
 WdtTrapAddress = (((*(uint8_t *) ((uint8_t *) SP + 16)) << 8) |
2
                  (*(uint8_t *) ((uint8_t *) SP + 17))) << 1;

Das war eine schwere Geburt… Mit diesem Harvard-Modell werde ich mich 
wohl nie anfreunden.

Vielen Dank für die Hilfe!


PS für die, die den Trick nachmachen wollen:
Der Offset ist nicht fest, sondern hängt davon ab, welche Register in 
der ISR gebraucht und folglich auf dem Stack zwischengespeichert werden 
müssen.

Man muss sich im konkreten Fall immer den asm-Code der ISR ansehen und 
danach den Offset bestimmen.

: Bearbeitet durch User
von Uhu U. (uhu)


Lesenswert?

Der Beitrag "Watchdog speichert Reset-Adresse -- für atmega328p" enthält eine 
ausgefeiltere Version des Watchdog-Paktes, die die Rückkehr-Adresse der 
Watchdog-ISR automatisch findet. Man muss also nicht mehr nach jeder 
Änderung an der Watchdog-ISR den Offset überprüfen.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

1
ISR(WDT_vect, ISR_NAKED)
2
{
3
  WdtTrapAddress = *(uint16_t*)(uint16_t)SP;
4
  while (1);
5
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.