Forum: Compiler & IDEs Problem mit ltoa


von Joachim (Gast)


Lesenswert?

Hallo,

ich habe ein Problem mit der Funktion ltoa.
In folgendem Progammcode wir die Zahl "-234567" auf dem LCD
rückwärts ausgegeben.
Die LCD-Ausgabe mit lcd_puts funktioniert. Offensichtlich wird der 
String von der Funktion ltoa in der falschen Reihenfolge erstellt.
1
  char text [12];            // String
2
  
3
  lcd_init(LCD_DISP_ON);     // initialize display, cursor off
4
  lcd_clrscr();              // clear display and home cursor
5
  lcd_puts("V 0.1b");        // put string to display (line 1)
6
  lcd_gotoxy(0,1);           // move cursor to position 1 on line 2
7
  ltoa (-234567, text, 10);
8
  lcd_puts(text);            // put string to display
Die Ausgabe auf den Display:
V 0.1b
765432-
Was mache ich falsch?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Vielleicht ein Bug in der verwendeten ltoa-Funktion?
Welche Library/Toolchain benutzt du in welcher Version für welchen µC?

von Reiner (Gast)


Lesenswert?

Ziemlich sicher ein Bug.  In der originalen Version von ltoa wird der
string tatsächlich umgekehrt in ein array geschrieben.  Am Schluss der
Funktion wird der String dann umgedreht:

...
conv_finished:
  st  Z, _zero_reg_  ; terminate string
  pop  a_val_hlo  ; restore &string as return value
  pop  a_val_hhi
  pop  r28    ; restore r28
  XJMP  _U(strrev)  ; reverse string
  .endfunc
...

von Joachim (Gast)


Lesenswert?

Ich habe mit WinAVR-20080110 angefangen und dann ein Update auf 
WinAVR-20100110 durchgeführt. Bei beiden Versionen hatte ich das gleiche 
Ergebnis.
Der Prozessor ist ein ATtiny26.
Ich habe mir inzwischen damit geholfen, den String rückwärts auszugeben.

Gruß
Joachim

von Karl H. (kbuchegg)


Lesenswert?

Joachim schrieb:

> Ich habe mir inzwischen damit geholfen, den String rückwärts auszugeben.

Das ist eine unbefriedigende Lösung, denn sie bedeutet, dass das 
eigentliche Problem nicht behoben ist.

Bei derartigen Basisfunktionen kannst du davon ausgehen, dass sie 
fehlerfrei in der Library sind. Wenn da etwas aussergewöhnliches 
passiert, dann deutet das auf einen Fehler irgendwo in deinem Programm 
hin, der unentdeckt geblieben ist.

von Joachim (Gast)


Lesenswert?

Das ist richtig, das Problem habe ich weder gefunden noch behoben.
Der betroffene Quelltext ist recht einfach:
1
ltoa (-234567, text, 10);
2
lcd_puts(text);            // put string to display
Die Zahl wurde vom Compiler vermutlich richtig als HEX-Wert in das 
Listing übernommen (alle Ziffern tauchen anschließend wieder im Ergebnis 
auf).
Die Funktion lcd_puts() stammt aus einer viel benutzten LCD-Bibliothek. 
Ich habe die Funktion überprüft, indem ich die Zeichen des Strings in 
einer Schleife Byte für Byte übertragen und jede Stelle separat 
adressiert habe.
Der erzeugte Assemblercode enthält offensichtlich eine Routine zum 
reversieren.
Beim Übersetzen für andere Controller (ATmega8 und ATmega32) hat sich 
die Länge des Codes geändert. Das ist aber auf andere Interruptvektoren 
und komfortablere Assemblerbefehle zurückzuführen. Die Funktion habe ich 
nicht identisch wiedergefunden.
Evtl. hat ja wirklich noch niemand diese Funktion beim ATtiny26 
probiert?
U. U. habe ich auch irgendwelche Flags falsch gesetzt?
Echte Compilerfehler sind selten, meistens sitzt das Problem vor dem 
Computer. Ich bin zumindest nicht auf ein Problem gelaufen, dass außer 
mir bereits alle kennen.

von Karl H. (kbuchegg)


Lesenswert?

Joachim schrieb:
> Das ist richtig, das Problem habe ich weder gefunden noch behoben.
> Der betroffene Quelltext ist recht einfach:

Das Problem könnte genausogut auch irgendwo ganz anders stecken.
Du siehst nur die Symptome. Das bedeutet aber nicht, dass die Ursache 
ebenfalls hier zu finden ist.

von Joachim (Gast)


Angehängte Dateien:

Lesenswert?

Das ltoa-Problem besteht in der fehlenden Stringumkehrung beim ATtiny26.
Im Web ist dazu nichts zu finden.
Wenn nun die Funktion ltoa nicht funktioniert, dann bestehen gute 
Aussichten, dass auch die Funktion itoa nicht richtig arbeitet.
Mit einem kurzen Test konnte das gezeigt werden.

Da die Funktion itoa häufiger gebraucht wird, sind auch schon mehrere 
Anwender darüber gestolpert. (Ich bin nun nicht mehr allein mit dem 
Problem).

Hier nennt man das "strange behavior":
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=printview&t=68073&start=0
Das Problem beim ATtiny26 wurde einfach durch Einsatz eines ATmega128 
behoben.

Auch hier ist das Problem mit itoa aufgetreten. 1234 wird als 4321 
ausgegeben:
http://www.elektroda.pl/rtvforum/topic1511704.html

Hier schiebt es der Programmierer auf seine mangelnden Kenntnisse:
Beitrag "lcd_putint falsche Ausgabe"

und 2003 hat man das Problem auch schon beobachtet:
Beitrag "bei verwendung von itoa alles verkehrtherum"

Der Compiler erzeugt für einen ATmega16 den gleichen Code, wie für einen 
ATtiny26. Der Code funktioniert auf dem ATmega16, nicht jedoch auf dem 
ATtiny26. Daraus könnte man ableiten, dass der Code in Ordnung ist und 
der Prozessor nicht richtig arbeitet.

Dazu wurde der Code der Stringumkehrung extrahiert und als separates 
Assemblerprogramm eingebunden. Die Kommentare zeigen was passiert. 
(siehe Anlage)

Hier der problematische Teil des Codes:
1
  cp  r26, r30  ; Stelle fest, ob Z > X, 
2
  cpc  r27, r31  
3
  brcs  schleife2  ; Wenn Z > X, Bytes austauschen
4
ret
Z und X sind Registerpaare, mit denen indirekt RAM adressiert wird. Da 
der ATtiny nur 128 Byte RAM hat, kann kein Seitenüberlauf auftreten. Es 
reicht deshalb aus, r26 und r30 zu vergleichen. Der Befehl cpc ist 
unnütz. Insoweit könnte man von einem Compilerfehler sprechen.
Nach der Auskommentierung des cpc-Befehls arbeitet die Stringumkehrung 
auf dem ATtiny26 einwandfrei.

Der cpc-Befehl scheint aber nach meinen ersten Versuchen nicht 
ordentlich zu arbeiten und das Carry-Bit nicht in den Vergleich zu 
übernehmen, aber das ist eine andere Baustelle.

Gruß
Joachim

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Die Frage ist eher, wieso sind r27, r31 nicht richtig initialisiert.

Also was übergibt der Aufrufer an strrev().

Und gibt es vielleicht eine Unterscheidung/Optimierung innerhalb AVR-GCC 
die bei kleinen AVRs von 16-Bit Pointern auf 8-Bit-Pointer 
runterschaltet.

Das Problem kann ja ohne weiteres bei weiteren Funktionen zu schlagen.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Das könnte von der SRAM Größe (<= 256 Bytes) auch betreffen:

Attinys
ATtiny28L
ATtiny10
ATtiny4
ATtiny5
ATtiny9
ATtiny13A
ATtiny13
ATtiny2313
ATtiny25
ATtiny26
ATtiny24
ATtiny261
ATtiny261A
ATtiny2313A
ATtiny24A
ATtiny20
ATtiny40
ATtiny44A
ATtiny43U
ATtiny4313
ATtiny45
ATtiny461A
ATtiny461
ATtiny48
ATtiny44

Atmegas
ATmega48
ATmega48A
ATmega48P
ATmega48PA

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Joachim schrieb:
> Das ltoa-Problem besteht in der fehlenden Stringumkehrung beim ATtiny26.
> [...]
> Der Compiler erzeugt für einen ATmega16 den gleichen Code, wie für einen
> ATtiny26. Der Code funktioniert auf dem ATmega16, nicht jedoch auf dem
> ATtiny26. Daraus könnte man ableiten, dass der Code in Ordnung ist und
> der Prozessor nicht richtig arbeitet.

In der neuesten Version der avr-libc ist strrev in asm implementiert:

http://cvs.savannah.gnu.org/viewvc/avr-libc/libc/string/strrev.S?revision=1.8&root=avr-libc&view=markup

> Dazu wurde der Code der Stringumkehrung extrahiert und als separates
> Assemblerprogramm eingebunden. Die Kommentare zeigen was passiert.
> (siehe Anlage)
>
> Hier der problematische Teil des Codes:
>
1
>   cp  r26, r30  ; Stelle fest, ob Z > X,
2
>   cpc  r27, r31
3
>   brcs  schleife2  ; Wenn Z > X, Bytes austauschen
4
> ret
5
>
> Z und X sind Registerpaare, mit denen indirekt RAM adressiert wird. Da
> der ATtiny nur 128 Byte RAM hat, kann kein Seitenüberlauf auftreten. Es
> reicht deshalb aus, r26 und r30 zu vergleichen. Der Befehl cpc ist
> unnütz. Insoweit könnte man von einem Compilerfehler sprechen.

Nein, offenbar nicht. Bestenfalls eine "missed Optimization in der 
avr-libc".

> Nach der Auskommentierung des cpc-Befehls arbeitet die Stringumkehrung
> auf dem ATtiny26 einwandfrei.
>
> Der cpc-Befehl scheint aber nach meinen ersten Versuchen nicht
> ordentlich zu arbeiten und das Carry-Bit nicht in den Vergleich zu
> übernehmen, aber das ist eine andere Baustelle.

NACK. Wenn es Versionen des ATtiny26 gibt, die einen Hardware-Bug haben, 
dann darf CPC nicht so verwendet werden. Die Register werden als 
16-Bit-Register initialisiert, so wie die ABI es für Zeiger vorsieht: 
diese sind 16 Bit groß, einen Sonderfall für 8-Bit-Pointer kennt avr-gcc 
nicht.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Mit meiner jetzigen Konfiguration kann ich den Fehler nicht 
nachvollziehen:

WinAVR-20081205 (der Version aus der Arduino IDE 0022)
_AVR_LIBC_VERSION_STRING_ "1.6.4"

von Stefan B. (stefan) Benutzerseite


Angehängte Dateien:

Lesenswert?

Auch in der folgenden Konfiguration gibt es keine Auffälligkeiten:

WinAVR-20100110 (die Letzte von Sourceforge)
_AVR_LIBC_VERSION_STRING_ "1.6.7"
Projektoptionen: Default (-Os) von AVR Studio 4.16 (build 628)

@ Joachim

Kannst du eine komplette Minimalsource geben, die den Fehler noch zeigt?

von Joachim (Gast)


Lesenswert?

Hallo,

Stefan B. schrieb:
> Die Frage ist eher, wieso sind r27, r31 nicht richtig initialisiert.

In meinem Text habe ich nur drei Zeilen des Anhangs wiedergegeben.
Gleich zu Anfang der Routine werden r26:r27 und r30:r31 eindeutig mit 
dem übergebenen Parameter geladen.

Stefan B. schrieb:
> Also was übergibt der Aufrufer an strrev().

An strrev wird die Adresse des Strings übergegeben, der umgekehrt werden 
soll.

Johann L. schrieb:
> In der neuesten Version der avr-libc ist strrev in asm implementiert:

Der Code entspricht m. E. dem, der aktuell für den ATtiny26 erzeugt 
wird.
Ich habe das Assemblerprogramm aus dem .lss -Listing herauskopiert.

Die Wurzel des Übels scheint mir im cpc-Befehl zu liegen. Dieser "reicht 
das Carry-Bit nicht durch".
Nachdem ich cpc r27, r31 auskommentiert hatte, habe ich den cp r26, r30 
in cpc r26, r30 umgestellt und einmal ein CLC und dann ein SEC 
vorangestellt.
Beides funktionierte.
Der Befehl cpc müsste auf dem ATtiny26 noch einmal intensiver untersucht 
werden.

Stefan B. schrieb:
> Mit meiner jetzigen Konfiguration kann ich den Fehler nicht
> nachvollziehen:
>
> WinAVR-20081205 (der Version aus der Arduino IDE 0022)
> AVR_LIBC_VERSION_STRING "1.6.4"

Wichtig ist der Prozessor. Der Fehler wurde bisher nur auf dem ATtiny26 
beobachtet. Die WinAVR Version ist unerheblich. Ich habe den Fehler bei
WinAVR-20080110 und WinAVR-20100110 beobachtet. 2003 war der Fehler 
offensicht bereits vorhanden. Ich halte es deshalb für sehr 
wahrscheinlich, dass alle WinAVR-Versionen von 2003 bis heute betroffen 
sind.
Falls Du wirklich einen ATtiny26 hast, der den Fehler nicht zeigt, wäre 
der Date-Code des ICs interessant.

Gruß
Joachim

von Joachim (Gast)


Lesenswert?

@ Stefan B.

Ich vermute, dass Du eine Simulation benutzt hast.
Die wird keinen Fehler zeigen.
Wenn ich das fehlerhafte Programm erst für einen ATtiny26 und dann für 
einen ATmega16 übersetze und anschließend beide .lss-Dateien mit 
Microsoft Word vergleiche, dann stelle ich fest, dass der entsprechende 
Programmteil identisch ist!
Ich will das noch mal auf einem ATmega16 praktisch testen, um 
nachzuweisen, dass sich die Controller bei identischem Code verschieden 
verhalten.

Eine Minimalversion stelle ich noch zusammen.

Gruß
Joachim

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Ja, ich habe nur die Simulation.

Wenn die Toolchain den Pointer richtig 16-Bit an die itoa() und die den 
richtig 16-Bit an strrev() übergibt und nur bei paar Attiny26 auf der 
Welt (bisher) ein Fehler bei CPC auftritt... liegt es vielleicht an 
dem einen Attiny26.

Es ist also richtig und wichtig, dass du nach anderen Berichten und nach 
dem Datecode des Attiny26 forschst. Vielleicht ist es ein 
Produktionsfehler. Steht etwas in dem Errata-Abschnitt des Datenblatts? 
Welcher Datecode hat dein Attiny26?

Vielleicht können die Leute aus 
Beitrag "Bleiakku-Lader 12/24V" noch etwas beitragen. Dort 
wurde ein Attiny26 benutzt.

von Joachim (Gast)


Lesenswert?

Stefan B. schrieb:
> ... und nur bei paar Attiny26 auf der
> Welt (bisher) ein Fehler bei CPC auftritt...

Ich würde sagen: "... beobachtet wurde".

Ich besitze zwei ATtiny26 mit der Aufschrift:
ATTINY26-16PU 0743

Im Datenblatt ist ein Codefehler festgehalten. Und zwar in 
REV1477D-05/03 Punkt 14 und REV1477D-10/03 Punkt 9.
Beides hat aber nichts mit dem hier betrachteten Fall zu tun.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Hast du denn inzwischen ein Minimalbeispiel? Etwa strrev() direkt 
aufrufen und anhand des Ergebnisses ne LED an/aus, so daß Fehler 
anderswo (LCD, out of RAM, ...) ausgeschlossen werden können.

Zumindest für einige Versionen des ATtiny26 funktioniert kein LPM Rd, 
Z+, ditto für LPM Rd, Z; siehe zB in den Errata oder auch EW15952 in 
http://supp.iar.com/FilesPublic/UPDINFO/004793/ew/doc/infocenter/iccavr.htm

Damit wird womöglich im Startup-Code die .data-Section nicht richtig 
initialisiert.

AFAIK gibt es für dieses Erratum keinen Workaround in den GNU-Tools bzw. 
der avr-libc

von Markus Bücker (Gast)


Lesenswert?

Hallo Joachim,

ich habe noch nie mit der Funktion Itoa() gearbeitet und diesen Bitrag 
nur überflogen, aber wenn ich das richtig verstehe macht sie aus einem 
Integer einen String. Jetzt setzt du einen zahlenwert -234567 ein. Ein 
Uint hat einen Maximalwert von 65536, ein signed int geht von -32767 bis 
32768. Also ist deine Zahl zu groß, Itoa() kann nur was falsches 
rausgeben. Schon mal mit "-23456" versucht, um Itoa() zu prüfen?



MFG Markus

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Nee, ltoa (long integer to ascii) packt die -234567 schon. itoa 
(integer to ascii) hätte die Probleme.

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Ich wollts ja erst nicht glauben, aber das ist definitiv ein Hardwarebug 
im ATtiny26.

Ich hab dann nen ATtiny261 reingesteckt und der läuft einwandfrei.
Ich hab nichtmal neu compiliert, also das gleiche Hexfile vom ATtiny26 
reingebrannt.

Anbei mein Testprogramm.


Peter

von Peter D. (peda)


Lesenswert?

Joachim schrieb:
> Der Prozessor ist ein ATtiny26.
> Ich habe mir inzwischen damit geholfen, den String rückwärts auszugeben.

Ich würd dann doch eher zu nem ATtiny261 raten, so teuer sind die ja 
nicht.
Ein "falsches" Programm zu schreiben, ist keine gute Lösung.

Vielleicht bist Du auch schon knapp mit Speicher und könntest eh nen 
Attiny461 oder 861 gut gebrauchen.


Peter

von Loonix (Gast)


Lesenswert?

@Joachim und Peter

Meine Hochachtung und vielen Dank für diesen Einsatz. Derlei HW-Bugs 
sind unheimlich schwer zu finden und zu beweisen. Mit solchen Threads 
steigt der Wert dieses Forums für uns Entwickler ganz enorm an.

von Joachim (Gast)


Angehängte Dateien:

Lesenswert?

Erst einmal vielen Dank Peter!
Ich hatte vor, den Code auf einem ATmega16 zu überprüfen. Dazu hätte ich 
das ganze auf einer anderen Hardware laufen lassen müssen.
Dein Ansatz ist um einiges eleganter. Mit dem Verwenden zweier 
pinkompatibler Controller ist der fehlerhafte Controller leichter zu 
bestimmen und das Risiko einer abweichenden Hardware sicher 
auszuschalten.
Da meine bevorzugten Elektronikversender nur den Attiny26 im Programm 
haben, hatte ich keinen neueren 261, 461 etc. zur Hand.

Meine Vermutung, dass der cpc-Befehl fehlerhaft ist, hat sich als falsch 
erwiesen. Der cpc-Befehl arbeitet so wie es sein soll.
Der Befehl ld r23, Z+ weist einen gravierenden Fehler auf.
Z wird um 1 erhöht und das HOB von Z wird überraschend auf 0 gesetzt!

Ein Testprogramm dazu im Anhang.
Um den Fehler sehen zu können muss das Programm auf einem ATtiny26 
laufen. Eine Simulation reicht sicher nicht aus.

Damit ist der ursächliche Fehler gefunden.

Das Auskommentieren des cpc-Befehls in der strrev-Funktion der 
Bibliothek ist sicher eine brauchbare Umgehung für den ATtiny26.
Evtl. finden die Compilerbauer ja auch eine andere Lösung.

Gruß
Joachim

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Joachim schrieb:

> Z wird um 1 erhöht und das HOB von Z wird überraschend auf 0 gesetzt!

Möglicherweise hat ein übereifriger Designcompiler hier festgestellt,
dass das höhere Adressbyte von Z ja nie benutzt werden kann, sodass
es egal ist, auf welchen Wert man dieses Register setzt.

> Das Auskommentieren des cpc-Befehls in der strrev-Funktion der
> Bibliothek ist sicher eine brauchbare Umgehung für den ATtiny26.

Leider wird die avr-libc nicht pro einzelnem Controller compiliert,
sondern die Controller werden zu relativ groben Familien zusammen-
gefasst.  Daher ist es nicht ohne weiteres möglich, nur beim ATtiny26
(oder etwa nur bei Controllern mit <= 128 Byte SRAM) diesen Befehl
wegzulassen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:

> Leider wird die avr-libc nicht pro einzelnem Controller compiliert,
> sondern die Controller werden zu relativ groben Familien zusammen-
> gefasst.  Daher ist es nicht ohne weiteres möglich, nur beim ATtiny26
> (oder etwa nur bei Controllern mit <= 128 Byte SRAM) diesen Befehl
> wegzulassen.

Damit ist der Attiny26 aber immer noch kaputt, d.h. falls irgendwo 
Arithmetik/Vergleiche > 1 Byte gemacht wird, geht's sehr wahrscheinlich 
in die Hose -- und das sind ja nicht nur Zeiger. Je nach Ausgeberoutine 
werden damit auch falsche Werte angezeigt; zudem ist nicht mal klar, ob 
alle Versionen des ATtiny26 betroffen sind.

Vorstellbar als Hack in iotn26.h ist ein
1
asm (".macro cpc a b");
2
asm (".warning ...");
3
asm (".endm");

Aber am wichtigsten ist, daß der Fehler nachgewiesen ist. Wenn in der 
Zufunft wieder jemand Huddel mit einem ATtiny26 hat, klingeln bestimmt 
bei irgendjemand hier zumindest die Glocken und es muss nicht erst ewig 
gesucht werden.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Johann L. schrieb:
> Aber am wichtigsten ist, daß der Fehler nachgewiesen ist. Wenn in der
> Zufunft wieder jemand Huddel mit einem ATtiny26 hat, klingeln bestimmt
> bei irgendjemand hier zumindest die Glocken und es muss nicht erst ewig
> gesucht werden.

Man (also: jemand, der davon betroffen ist) sollte zumindest mal einen
Bugreport bei Atmel öffnen.

von Joachim (Gast)


Lesenswert?

Langsam, langsam ...
Mit so einem Fehler ist das nicht so einfach.
Anfangs habe ich festgestellt, dass die Begrenzung der Pointerberechnung 
auf 8 Bit den Fehler behebt. (durch das auskommentieren von cpc ...) Das 
ist aber nicht unbedingt ein Compilerfehler.

Im nächsten Schritt habe ich festgestellt, dass der Pointer zur 
indirekten Adressierung Z, bei dem Postincrement-Befehl Z+ überraschend 
im HOB auf 0 gesetzt wird. Das ist überraschend und nicht dokumentiert. 
Es ist aber nicht zwingend ein Controller-Fehler. Der ATtiny 26 verfügt 
über einen SRAM-Bereich, der von 0x00 bis 0x5f adressiert wird. Register 
zur indirekten Adressierung sollten deshalb sinnvollerweise auch nur auf 
diesen erlaubten Bereich zugreifen. Wenn das alle tun, fällt niemandem 
auf, wenn das HOB des Pointers <> 0 ist.
Es sollte deshalb niemand ein Problem mit dem Verhalten des Z+ Befehls 
haben.

Die Frage ist nun, weshalb der Maschinencode den String nicht mit 0x005f 
adressiert. Das HOB der Stringadresse wird im Controller dynamisch 
ermittelt.
Und das geht so:
Zuerst wird der Stackpointer auf die letzte Speicherstelle gesetzt. Beim 
Attiny26 wird der 8Bit-Stackpointer korrekt auf 0x5f gesetzt. Das 
"HOB-Register des Stackpointers" ist nicht definiert, weil es nicht 
benötigt wird und 8 Bit gut ausreichen.
Das Hauptprogramm liest dann den 16-Bit-Stackpointer!, zieht die Länge 
des benötigten Speichers ab und setzt den Stackpointer unter den 
benötigten Speicher. Die Adresse des definierten Strings liegt nun über 
dem Stackpointer und seine Adresse setzt sich aus dem berechneten LOB 
und dem HOB aus dem nicht definierten Inhalt des 
HOB-Stackpointer-Registers zusammen.

Der Befehl Z+ funktioniert immer dann einwandfrei, wenn die 
Speicherseite 0 angesprochen wird. Da das HOB-Stackpointer-Register beim 
ATtiny26 beim Lesen nicht definierte Werte liefert, wird eine 
Speicherseite <> 0 angesprochen (d.h. HOB <>0), was offensichtlich sogar 
gelegentlich funktioniert. Beim Lesen eines nichtdefinierten Registers 
besteht aber kein Anspruch auf die Ausgabe eines bestimmten Wertes.

Zusammenfassend:
1. CPC ist in Ordnung. Das Auskommentieren ist nur eine Umgehung.
2. Z+ verhält sich ungewohnt und undokumentiert, ist aber logisch 
konsequent.
3. Der vom Compiler generierte Code liest einen 16 Bit Stackpointer aus 
einem 8 Bit Stackpointerregister und einem nicht definierten Register 
(das auf größeren Controllern als HOB des Stackpointers definiert ist)

So wird der Stackpointer gesetzt (korrekt):
ldi  r28, 0xDF  ; 223
out  0x3d, r28  ; 61

So wird er am Anfang von main ausgelesen:
in  r28, 0x3d  ; 61  ; ok
in  r29, 0x3e  ; 62  ; reserviertes ATtiny26 Register

Nach dieser Erkenntnis würde ich nun doch für einen Compilerfehler 
plädieren.

Karl heinz Buchegger schrieb:
> Das Problem könnte genausogut auch irgendwo ganz anders stecken.
> Du siehst nur die Symptome. Das bedeutet aber nicht, dass die Ursache
> ebenfalls hier zu finden ist.

Karl Heinz hat in weiser Voraussicht recht gehabt.

Gruß
Joachim

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Joachim schrieb:

> So wird er am Anfang von main ausgelesen:
> in  r28, 0x3d  ; 61  ; ok
> in  r29, 0x3e  ; 62  ; reserviertes ATtiny26 Register
>
> Nach dieser Erkenntnis würde ich nun doch für einen Compilerfehler
> plädieren.

"Das haben wir aber schon immer so gemacht." ;-)  Auf jedem anderen
AVR funktioniert das auch (auch auf den kleinen mit 128 oder weniger
Byte SRAM).

Eigentlich sollte das egal sein, was im oberen Byte des Zeigers drin
steht, denn da dieses Byte für die Adressierung des SRAMs nicht
benötigt wird, sollte es einfach ignoriert werden.  Damit ist es
wiederum auch wurscht, ob das Auslesen des nicht vorhandenen SPH nun
0, 0xff oder 42 ergibt.

Wenn der ATtiny26 das nicht ignoriert und "auf nichts" zugreift,
wenn das obere Byte != 0 ist, dann würde ich das zumindest unter
"surprisingly unexpected behaviour" einsortieren.

von Peter D. (peda)


Lesenswert?

Das paßt dann sehr gut mit dem Datenblatt zusammen:

Datasheet Revision History:
9. Removed LPM Rd, Z+ from “Instruction Set Summary” on page 171. This
instruction is not supported in ATtiny26.

Also auch beim LPM-Befehl wird Z+ fehlerhaft berechnet und daher hat man 
diesen Befehl einfach wieder "gestrichen".

Statt:
Z = Z + 1;
wird also ausgeführt:
Z = (Z + 1) & 0x00FF;

Atmel hätte besser den Fehler korrigieren sollen, anstatt Befehle zu 
entfernen.


Peter

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:

> Atmel hätte besser den Fehler korrigieren sollen, anstatt Befehle zu
> entfernen.

Eine neue Chiprevision kostet halt deutlich mehr als eine neue
Datenblattrevision. ;-)  Da es außerdem den ATtiny261 als de-facto-
Nachfolger gibt, wird keiner mehr die Summen für einen neuen
Maskensatz für den alten IC in die Hand nehmen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Joachim schrieb:
> Zusammenfassend:
> 1. CPC ist in Ordnung. Das Auskommentieren ist nur eine Umgehung.

Nein. CPC ist nicht in Ordnung, zumindest nach obigen Ergebnissen.

Wer sagt dir, daß das Byte, das per CPC verglichen wird, zu einer 
Adresse gehört? Es kann ebenso zur 16-Bit Arithmetik eines Skalars 
gehören (Fixed-Point, Integer mit 16, 24, 32, ... Bits).

Ein richtiger Workaround für diesen Silicon-Bug hätte im Assembler 
avr-as zu geschehen, z.B.
1
mov R0, Ra
2
sbc R0, Rb

anstatt
1
cpc Ra, Rb

und einem Fatal Error falls Rb = R0.
Ein WA im Compiler ist aufwändiger und fängt weniger Stellen, z.B. keine 
in (Inline-)Assembler, und damit auch keine wie im strrev der avr-libc.

Da strrev per ltoa -- also nicht unwahrscheinlich -- benutzt wird, würde 
ich vorschlagen, diesen WA für mcu=avr2 direkt in die avr-libc 
einzubauen, oder was auch immer grep cpc zu Tage fördert. Ich weiß ja 
nicht, wie langsam die Mühlen bei den avr-binutils mahlen... aber wenn 
sie so fix sind wie die von avr-gcc, dann gibt es nie einen WA für 
diesen Bug im Prozessorkern -- um so übler, da er nicht bei den Errata 
auftaucht.

Die Initialisierung von SP geschieht im Startup-Code, und dort könnte 
SPH explizit mit Null geladen werden (section .init2 in crttn26.[oS]).

von Joachim (Gast)


Lesenswert?

@ Johann L.
Der Fehler ist hier eine Verkettung mehrerer Umstände.
Wenn ich den 16-bit-Vergleich am Ende der strrev-Routine zu einem 
8-bit-Vergleich mache, dann verdecke ich das Problem und das Programm 
funktioniert.
Da der angesprochene Vergleich korrekt ist und auf allen anderen AVR's 
funktioniert, ist entweder, wie von mir Anfangs fälschlicherweise 
festgestellt der CPC-Befehl fehlerhaft oder die Eingangsdaten stimmen 
nicht.
Hier stimmen die Eingangsdaten nicht, weil der Z+ Befehl das HOB des 
Vektors überraschend auf 0 gesetzt hat. Da ein Zeiger auf das RAM im 
ATtiny immer 0 im HOB stehen haben muss, ist es kein wirklicher Fehler, 
wenn bei Z+ das HOB auf 0 gesetzt wird.

Der Fehler liegt an dieser Stelle darin, dass das HOB des Vektors nicht 
bereits vorher auf 0 stand.

Und das ist darauf zurückzuführen, dass die RAM-Adresse gebildet wurde, 
in dem man darauf vertraute dass ein nicht definiertes Register beim 
Lesen 0 ausgibt. Auch wenn die anderen AVR's offensichtlich 0 ausgeben, 
ist es ein Risiko darauf zu vertrauen und das generell vorauszusetzen.

Die Beseitigung des Mangels sollte man an der Quelle durchführen. Dort, 
wo zu Beginn der main-Routine die höchste Speicheradresse festgestellt 
wird, ist das HOB auf 0 zu setzen und nicht aus dem Stackpointer zu 
lesen.
Beim Setzen des Stackpointers wurde das ATtiny26-individuell korrekt 
gemacht. Zu Beginn der main-Routine sollte das auch ATtiny26-individuell 
machbar sein.

Gruß
Joachim

von Joachim (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Wenn der ATtiny26 das nicht ignoriert und "auf nichts" zugreift,
> wenn das obere Byte != 0 ist, dann würde ich das ....

Es ist schon so, dass der ATtiny26 das HOB beim Zugriff auf das SRAM 
ignoriert. Das SRAM wird entsprechend oft gespiegelt.
Das geht solange gut, bis vor einem Vergleich zweier Zeiger der eine 
Zeiger nach einem Z+ Befehl mit & 0x00FF maskiert wurde.
Der Vergleich muss schiefgehen und das Programm liefert eine 
Fehlfunktion.

von Joachim (Gast)


Lesenswert?

Johann L. schrieb:
> Die Initialisierung von SP geschieht im Startup-Code, und dort könnte
> SPH explizit mit Null geladen werden (section .init2 in crttn26.[oS]).

Das Laden eines reservierten Registers kann gut gehen, ist aber 
ebenfalls keine dokumentierte Funktion. Der ATtiny26 hat nur einen 
8-Bit-Stackpointer!

An der Stelle, wo ich einen 16-Bit-Zeiger aus einem 8-Bit-Stackpointer 
lade, muss die Korrektur ansetzen.

von Peter D. (peda)


Lesenswert?

Es scheint wirklich nur der ATtiny26 betroffen zu sein.

Der ATtiny13 und ATtiny2313 wären zwar auch Kandidaten, aber die haben 
den "LPM Rd, Z+" Befehl, also sollten sie funktionieren.


Peter

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Joachim schrieb:
> @ Johann L.
> wie von mir Anfangs fälschlicherweise
> festgestellt der CPC-Befehl fehlerhaft

Ok, hatte ich oben wohl überlesen. Was bedeutet eigentlich HOB? Bei der 
Suche finde ich da bestenfalls "House of Boys" :-)

von Peter (Gast)


Lesenswert?

Das wird "high order byte" sein, also der höherwertige teil der Adresse.

Peter

Frauenhaus ist mir ein Begriff, aber das es sowas auch für Männer gibt
war mir neu.

von Joachim (Gast)


Lesenswert?

Ich verwende seit Jahren
   HOB für high order Byte
und
   LOB für low order Byte.
Offensichtlich hat sich das aber nicht allgemein durchgesetzt. Hat 
jemand andere geeignetere Begriffe dafür?

Zurück zum Thema
Hier noch einmal der fehlerhafte Code:
1
char text [12];            // String
2
ltoa (-234567, text, 10);
3
lcd_puts(text);            // put string to display
und hier eine im ATtiny26 funktionierende Lösung:
1
char text [12];            // String
2
ltoa (-234567, (char*)((int)text & 0xff), 10);
3
lcd_puts(text);            // put string to display
Gruß
Joachim

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.