Debugging mit bedingten Befehlen auf ARM-CPUs - EuE24.net
Debugging mit bedingten Befehlen auf ARM-CPUs - EuE24.net
Debugging mit bedingten Befehlen auf ARM-CPUs - EuE24.net
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
E&E PRODUCTS & SOLUTIONS<br />
MIKROCONTROLLER & PROZESSOREN<br />
B.01<br />
gefügt. Das Feld cond bestimmt hierbei, abhängig<br />
von den Flags N, Z, C und V im Statusregister<br />
CPSR, ob der Befehl ausgeführt<br />
wird oder nicht. Für die Bedingungen ergeben<br />
sich da<strong>mit</strong> insgesamt 16 Fälle:<br />
Die Mnemonic-Erweiterung wird hierbei an<br />
den Assembler-Befehl angehängt, wobei für<br />
Befehle ohne Bedingung die Erweiterung al<br />
in der Regel entfällt. Für den Befehl<br />
add r2, r2, #1 ; inkrementiere Register r2<br />
ergibt sich im Falle der Bedingung „Ausführen<br />
bei Gleichheit“ Z=1 der Befehl<br />
addeq r2, r2, #1 ;<br />
inkrementiere Register r2 wenn Z=1.<br />
Im Falle von Z=0 wird r2 nicht inkrementiert.<br />
Im folgenden Abschnitt werden Beispiele<br />
angeführt.<br />
C-Code versus Assembler-Code<br />
Beispiel 1<br />
# “C” Code <strong>ARM</strong> Assembler<br />
Code<br />
1 if (index < counter) ldrh r2, [r5]<br />
2 { ldrh r3, [r4, #00h]<br />
cmp r2, r3<br />
3 buffer1 = index; movcc r7, r2<br />
4 }<br />
5 else<br />
6 {<br />
7 buffer2 = index+1; ldrcs r3, [pc, #1ch]<br />
ldrcsb r6, [r3]<br />
addcs r6, r6, #1h<br />
9 }<br />
Abb. 1:<br />
Den „<strong>bedingten</strong>“<br />
Breakpoint, erkennt<br />
man am Fragezeichen<br />
im Punkt, den un<strong>bedingten</strong><br />
Breakpoint<br />
am Punkt<br />
Hier werden drei Beispiele für bedingte Anweisungen<br />
in C- und Assembler-Code gezeigt.<br />
Der C-Code wurde dabei <strong>mit</strong> einem<br />
<strong>ARM</strong>-GNU-Compiler übersetzt. Die den C-<br />
Anweisungen entsprechenden Assembler-<br />
Instruktionen sind zur Verdeutlichung in<br />
Gruppen zusammengefasst. Ein einfaches<br />
Beispiel für eine „if-then-else“-Bedingung in<br />
C und Assembler zeigt Beispiel 1.<br />
Diese in C absolut übliche Konstruktion<br />
einer Abfrage erzeugt im Assembler-Code<br />
eine lineare Abfolge von <strong>Befehlen</strong> ohne<br />
Sprünge. Abhängig vom Carry-Flag werden<br />
die Befehle ausgeführt oder nicht (ersichtlich<br />
an den Mnemonic-Erweiterungen cc<br />
und cs, siehe vorherige Tabelle). Der Vorteil<br />
ist eine konstante L<strong>auf</strong>zeit des Codes, unabhängig<br />
von der Gültigkeit der Abfrage-<br />
Bedingung in Zeile #1. Es werden immer<br />
sowohl der „if“-Zweig als auch der „else“-<br />
Zweig durchl<strong>auf</strong>en.<br />
Für Prozessoren ohne bedingte Befehle werden<br />
Assembler-Anweisungen generiert, die<br />
die „if-then-else“-Pfade über Sprungbefehle<br />
erreichen. Dazu wird nach der Abfrage der<br />
Bedingung in Zeile #1 ein bedingter Sprung<br />
eingefügt, und im „if“-Zweig der „else“-<br />
Zweig übersprungen und umgekehrt. Dies<br />
ist in Beispiel 2 zu sehen. Hier wird der Programmabl<strong>auf</strong><br />
durch Sprünge gesteuert,<br />
wo<strong>mit</strong> die L<strong>auf</strong>zeit vom durchl<strong>auf</strong>enen Pfad<br />
abhängt. Dies ist an der Anzahl der ausgeführten<br />
Befehle leicht zu erkennen (Vergleich<br />
der Zeilen #3 und #7).<br />
Beispiel 3 zeigt, wie durch eine zusätzliche<br />
Abfragebedingung im Beispiel 1 ein <strong>ARM</strong><br />
Assembler-Code wie bei Beispiel 2 erzeugt<br />
wird. Hier wird der Programmabl<strong>auf</strong> ebenfalls<br />
durch Sprünge gesteuert.<br />
Die Zeilen #7 aller Beispiele erzeugen dieselbe<br />
Folge von Assemblerbefehlen, <strong>mit</strong> dem<br />
Unterschied, dass sie in Beispiel 1 bedingt<br />
ausgeführt werden, während sie in Beispiel 2<br />
und 3 immer (Bedingung „Always“, siehe<br />
vorherige Tabelle) ausgeführt werden. Die<br />
Mnemonic-Erweiterung wird hierbei, wie<br />
bereits erwähnt, nicht angezeigt.<br />
Für den Software-Entwickler ist da<strong>mit</strong> nicht<br />
unbedingt zu erkennen, welcher Programmcode<br />
erzeugt wird und wie das L<strong>auf</strong>zeitverhalten<br />
sein wird. Der generierte <strong>ARM</strong>-Assembler-Code<br />
hängt im Wesentlichen davon<br />
ab, wie komplex die Anweisungen in der<br />
„if“-Bedingung und innerhalb der beiden<br />
Pfade sind und welche Register zur Verfügung<br />
stehen.<br />
Debug-Problematik<br />
Das Debuggen solcher Programmabläufe<br />
<strong>mit</strong> Breakpoints führt zu unterschiedlichen<br />
Resultaten:<br />
Bei einem Breakpoint in Zeile #7 des C-Codes<br />
wird eine Programmunterbrechung erwartet,<br />
falls die Bedingung in Zeile #1 nicht erfüllt ist.<br />
Für die Beispiele 2 und 3 trifft dies zu. In Beispiel<br />
1 jedoch wird die Programmausführung<br />
in jedem Fall in Zeile #7 angehalten, da immer<br />
beide Zweige durchl<strong>auf</strong>en werden. Was<br />
Beispiel 2<br />
# “C” Code <strong>ARM</strong> Assembler<br />
Code<br />
1 if (index < counter) ldrh r2, [r5]<br />
2 { ldrh r3, [r4, #00h]<br />
cmp r2, r3<br />
bcs #else<br />
3 buffer1 = index; mov r7, r2<br />
ble #end<br />
4 }<br />
5 else #else<br />
6 {<br />
7 buffer2 = index+1; ldr r3, [pc, #1ch]<br />
ldrb r6, [r3]<br />
add r6, r6, #1h<br />
9 } #end<br />
Beispiel 3<br />
# “C” Code <strong>ARM</strong> Assembler<br />
Code<br />
1 if ((index < counter) && ldrh r2, [r5]<br />
(buffer1+buffer2 < 25)) ldrh r3, [r4, #00h]<br />
2 { cmp r2, r3<br />
bcs #else<br />
add r3, r7, r6<br />
cmp r3, #18h<br />
movle r7, r2<br />
ble #end<br />
3 buffer1 = index;<br />
4 }<br />
5 else #else<br />
6 {<br />
7 buffer2 = index+1; ldr r3, [pc, #1ch]<br />
ldrb r6, [r3]<br />
add r6, r6, #1h<br />
9 } #end<br />
92<br />
www.<strong>EuE24.net</strong>