In ATmega328P unter den Stapel gehen

Ich arbeite mit einem AVR-basierten Projekt und habe nur eine Neugier.

Der ATmega328P hat insgesamt 2kB SRAM. Die niedrigste Adresse des Stapels kann 0x8FF sein. Angenommen, der Stapelzeiger befindet sich auf 0x8FF. Was würde passieren, wenn ich an dieser Stelle eine RETI-Instruktion oder eine POP-Instruktion mehrmals gebe?

RETI und POP sollen beide den Wert am Anfang des Stapels nehmen und den Stapelzeiger erhöhen (das Erhöhen des Stapelzeigers würde bedeuten, dass der Stapel heruntergefahren wird).

Ich habe es mit Microchip Studio (früher AVR Studio) versucht.

Dort erhöht es einfach den Stapelzeiger von 0x08FF auf 0x0900 auf 0x0901 und so weiter. Dies könnte tatsächlich möglich sein, da der Stapelzeiger ein 16-Bit-Register ist (2 Register SPL und SPH mit jeweils 8 Bit).

Nehmen wir nun an, ich bringe meinen SP auf 0x0905 und mache einen PUSH und dann einen Pop. Im Simulator wird der richtige Wert (der gepusht wurde) in mein Ausgangsregister eingefügt. In einem echten ATmega328P wäre dies jedoch nicht möglich (da es keinen Platz zum Speichern der folgenden 0x8FF-Adresse gibt.)

Wie würde eine reale Mikroprozessoreinheit auf ein solches Szenario reagieren?

Der Code, den ich verwendet habe, war:

LDI R16, 0x88 ;load a random value
POP R0        ;increase SP to 0x900
POP R1        ;increase SP to 0x901
POP R2        ;increase SP to 0x902
POP R3        ;increase SP to 0x903
PUSH R16      ;decrease SP to 0x902
PUSH R16      ;decrease SP to 0x901
POP R4        ;increase SP to 0x902 and store 0x88 in R4

In Kommentaren habe ich alles geschrieben, was ich im Simulator beobachtet habe, ich möchte wissen, wie ein echter Prozessor reagieren würde.

Der Ram ist nur 2KByte groß. Woher hast du die 23kb?
0x8ff ist das TOP des Stapels. Der Stack wächst nach unten dh gegen 0. Jenseits von 0x8ff ist frische Luft. Sie können drücken, aber was Sie knallen, wird nichts Nützliches sein
Verwenden AVR keine Stacks mit Abwärtszählung oder haben sie nach der Fusion von Microchip auf Aufwärtszählung umgestellt? :) Ich denke, PIC sind die einzigen, die aufwärts zählende Stapel verwenden.
Ja, avr implementiert einen abwärts zählenden Stapel (0x08FF ist das "TOP"), aber wenn er wächst, verringert er den Stapelzeiger (das wollte ich sagen). @Kartman ja, die MCU hat 2 KB SRAM. Ich habe die falsche Nummer geschrieben
Sie können 16 Bit für SP nicht garantieren, das Datenblatt besagt, dass die Anzahl der Bits vom Adressraum abhängt - also 11 Bits für 328p. (Die kleineren MCUs haben nicht einmal SPH.) Was wirklich in der Hardware steckt, obwohl ich keine Ahnung habe. Aber wäre nicht überrascht, wenn es nur auf Null umlaufen würde.

Antworten (1)

Ich weiß nicht speziell über AVR Bescheid, aber im Allgemeinen macht die MCU einfach weiter und schreibt außerhalb der Grenzen. Dies ist ein Stapelüberlauf/-unterlauf. (Ein Stapelunterlauf kann nur in Assembler auftreten, nicht in reinem C.) Normalerweise weiß nur der Linker, wo Ihr Stapel beginnt und endet: weder der Compiler noch die MCU selbst wissen es oder kümmern sich darum.

Im Allgemeinen bieten Low-End-MCUs nur physischen Speicherzugriff, sodass es keine MMU gibt, auf die Sie sich mit den Fingern schlagen können, wenn Sie Speicher berühren, den Sie nicht berühren sollten. Oder wenn es eine MMU gibt, ist sie oft sehr vereinfacht. Höherwertige Teile würden Hardware-Ausnahmen generieren.

Die hartgesottenen Embedded-System-Programmierer achten deshalb darauf, ihren Stack immer so abzubilden, dass er in harmlosen Speicher überläuft. Zum Beispiel Adressen, denen nichts zugewiesen ist, oder Adressen, an denen nichts als unprogrammierter Flash vorhanden ist. Ein Stapelzeiger, der dort landet, führt ziemlich schnell zu Runaway-Code oder ähnlichem, der oft versucht, den Opcode 0xFF auszuführen. Von da an können Sie normalerweise illegale Opcode-Interrupts abfangen. Oder im schlimmsten Fall stoppt hoffentlich der Watchdog das Programm.

Wenn Sie Ihren Stapel nicht so sorgfältig platzieren oder wenn Sie ein vom Hersteller und/oder Affen bereitgestelltes Standardspeicherlayout verwenden, kann es passieren, dass der Stapel in Ihre Hardware-Registerkarte oder ein .data/.bss-RAM-Segment überläuft , was eine Katastrophe ist.

Eine andere etwas grobe Technik, die Sie verwenden können und die oft von High-End-Systemcompilern verwendet wird, ist die Verwendung eines "Stack Canary". Machen Sie eine Berechnung Ihrer erwarteten Stack-Nutzung im ungünstigsten Fall. Setzen Sie dann den gesamten Stack beim Start auf einen bekannten Wert (alle 0x00 oder alle 0xFF). Schreiben Sie dann über Ihr geschätztes Worst-Case-Stack-Szenario hinaus einen anderen Wert (0xAA oder was auch immer) an verschiedene feste Stellen. Sie können diese Stellen dann wiederholt von der Hauptschleife aus überprüfen, um festzustellen, ob sie immer noch den festen Wert enthalten - wenn nicht, dann haben Sie einen Stapelüberlauf. Dies ist nicht 100 % sicher, fängt aber die meisten Stapelüberläufe ab.