www.mikrocontroller.net

Forum: Compiler & IDEs gcc-avr: sprintf_P kracht (mit 3 oder mehr Parameter)


Autor: m_bedded (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Leute,

ich brauche eine frische Idee!

uCOS-II läuft auf mega128, WinAVR-20070525. Das Gerät hat 
Command-Line-Interface, da hantiere ich ziemlich viel mit printf und 
ROM-strings rum.

warum soll
sprintf_P(&PrnBuf[0], FMT_TABLEHEADER);
wunderbar laufen

und
sprintf_P(&PrnBuf[0], FMT_NAMEVALUE, value); /* name=%d */
das System abstürzen?

Es gibt ausreichend Stack (sieht man mit JTAG ICE). Es gibt nur 1 Task 
(bei diesem Test) und Rx/Tx UART-Fifos (Interrupt-gesteuert), die 
scheinbar ok sind, immerhin kann ich Tabellen-Köpfe und sonstigen 
multi-Kb ASCII-Art-Kram ganz ok printen.

Gibt es was besonderes über sprintf_P? Nutzt diese Funktion vielleicht 
den HEAP, den ich nicht organiziert habe? Oder einen globalen Bereich 
statt Task-Stack, womit mein uCOS-II nicht mehr klar kommt?

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie sind FMT_TABLEHEADER und FMT_NAMEVALUE und value definiert? Wie 
gross ist PrnBuf?

Autor: m_bedded (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
char PrnBuf[256];
char FMT_TABLEHEADER[] PROGMEM ="*********\r\n";
char FMT_NAMEVALUE  [] PROGMEM =" PARAM1=%d\r\n";
uint8_t value;

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Warum nimmst du sprintf_P() wenn dein format string doch im RAM steht?
Nimm sprintf().  Wenn der Ziel-String im ROM steht, nimm %S statt %s
(und ignoriere GCCs Warnung über einen angeblichen wchar_t *, ich habe
noch nicht rausgefunden, wie man ihm das abgewöhnen kann).

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
p.s.: &prnBuf[0] ist eine umständliche und schwer verständliche
Ausdrucksform für prnBuf (zumindest in diesem Kontext).

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch wrote:
> Warum nimmst du sprintf_P() wenn dein format string doch im RAM steht?

Wie meinst du das?
Der OP schrieb:

char FMT_NAMEVALUE  [] PROGMEM =" PARAM1=%d\r\n";
uint8_t value;

sprintf_P(&PrnBuf[0], FMT_NAMEVALUE, value); /* name=%d */

ist m.M. doch in Ordnung? Oder hab ich grade ein Blackout?

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ahem, sorry, ich habe das `s' in `sprintf' irgendwie überlesen...

Dann ist aber das Problem nicht nachvollziehbar.

Autor: m_bedded (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für eure Antworten!

Nun eine kleine Egänzung: werden die Interrupts vor sprintf_P()
gesperrt, funktioniert es einwandfrei:
OS_ENTER_CRITICAL();
sprintf_P(&PrnBuf[0], FMT_NAMEVALUE, value); /* name=%d */
OS_EXIT_CRITICAL();

Ich weiss, dass es ga-a-anz viel bei einem RTOS passieren kann. Aber wo 
ist es diesmal??

Wenn der Context-Switch alle Register und RAMPZ und dazu noch SPH und 
SPL speichert und wiederherstellt, wenn ich über 256 bytes Reserve im 
Task Stack habe, was passiert dieser Funktion (sodass sie kracht):
sprintf_P(&PrnBuf[0], FMT_NAMEVALUE, value); /* name=%d */
was dieser Funktion NICHT passiert:
sprintf_P(&PrnBuf[0], FMT_TABLEHEADER);
?

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Interessant. Möglicherweise ist da ein Bug in der Codeerzeugung.

Aus diesem C-Code

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <stdio.h>

char PrnBuf[256];
char FMT_TABLEHEADER[] PROGMEM ="*********\r\n";
char FMT_NAMEVALUE  [] PROGMEM =" PARAM1=%d\r\n";
uint8_t value = 42;

int main(void)
{
   sprintf_P(&PrnBuf[0], FMT_NAMEVALUE, value); /* name=%d */
   while(1);
}


Macht WinAVR-20070525 dieses Listing (hier für Atmega32). No Panic, 
nicht alles ist wichtig, aber man beachte die Zeilen 113 bis 120

   1                   .file  "test.c"
   2                   .arch atmega32
   3                 __SREG__ = 0x3f
   4                 __SP_H__ = 0x3e
   5                 __SP_L__ = 0x3d
   6                 __tmp_reg__ = 0
   7                 __zero_reg__ = 1
   8                   .global __do_copy_data
   9                   .global __do_clear_bss
  11                   .text
  12                 .Ltext0:
  91                 .global  main
  93                 main:
   1:test.c        **** #include <avr/io.h>
   2:test.c        **** #include <avr/pgmspace.h>
   3:test.c        **** #include <stdio.h>
   4:test.c        ****
   5:test.c        **** char PrnBuf[256];
   6:test.c        **** char FMT_TABLEHEADER[] PROGMEM ="*********\r\n";
   7:test.c        **** char FMT_NAMEVALUE  [] PROGMEM =" PARAM1=%d\r\n";
   8:test.c        **** uint8_t value = 42;
   9:test.c        ****
  10:test.c        **** int main(void)
  11:test.c        **** {
  94                 abn  68,0,12,.LM1-main
  95                 .LM1:
  96                   lds r24,value
  97                   clr r25
  12:test.c        ****    sprintf_P(&PrnBuf[0], FMT_NAMEVALUE, value); /* name=%d */
  98                 tabn  68,0,12,.LM1-main
  99                 .LM1:
 100 0000 8091 0000     lds r24,value
 101 0004 9927          clr r25
 102 0006 9F93          push r25
 103 0008 8F93          push r24
 104 000a 80E0          ldi r24,lo8(FMT_NAMEVALUE)
 105 000c 90E0          ldi r25,hi8(FMT_NAMEVALUE)
 106 000e 9F93          push r25
 107 0010 8F93          push r24
 108 0012 80E0          ldi r24,lo8(PrnBuf)
 109 0014 90E0          ldi r25,hi8(PrnBuf)
 110 0016 9F93          push r25
 111 0018 8F93          push r24
 112 001a 0E94 0000     call sprintf_P
 113 001e 8DB7          in r24,__SP_L__
 114 0020 9EB7          in r25,__SP_H__
 115 0022 0696          adiw r24,6
 116 0024 0FB6          in __tmp_reg__,__SREG__
 117 0026 F894          cli
 118 0028 9EBF          out __SP_H__,r25
 119 002a 0FBE          out __SREG__,__tmp_reg__
 120 002c 8DBF          out __SP_L__,r24
 121                 .L2:
 122 002e 00C0          rjmp .L2
 123                 /* epilogue: frame size=0 */
 124                 /* epilogue: noreturn */
 125                 /* epilogue end (size=0) */
 126                 /* function main size 24 (24) */
 128                 .Lscope0:
 129                 .global  FMT_TABLEHEADER
 130                   .section  .progmem.data,"a",@progbits
 133                 FMT_TABLEHEADER:
 134 0000 2A2A 2A2A     .string  "*********\r\n"
 134      2A2A 2A2A
 134      2A0D 0A00
 135                 .global  FMT_NAMEVALUE
 138                 FMT_NAMEVALUE:
 139 000c 2050 4152     .string  " PARAM1=%d\r\n"
 139      414D 313D
 139      2564 0D0A
 139      00
 140                 .global  value
 141                   .data
 144                 value:
 145 0000 2A            .byte  42
 146                   .comm PrnBuf,256,1
 151                   .text
 153                 .Letext0:
 154                 /* File "test.c": code   24 = 0x0018 (  24), prologues   0, epilogues   0 */
DEFINED SYMBOLS
                            *ABS*:00000000 test.c
  f:\temp/ccLURB1Z.s:3      *ABS*:0000003f __SREG__
  f:\temp/ccLURB1Z.s:4      *ABS*:0000003e __SP_H__
  f:\temp/ccLURB1Z.s:5      *ABS*:0000003d __SP_L__
  f:\temp/ccLURB1Z.s:6      *ABS*:00000000 __tmp_reg__
  f:\temp/ccLURB1Z.s:7      *ABS*:00000001 __zero_reg__
  f:\temp/ccLURB1Z.s:93     .text:00000000 main
  f:\temp/ccLURB1Z.s:144    .data:00000000 value
  f:\temp/ccLURB1Z.s:138    .progmem.data:0000000c FMT_NAMEVALUE
                            *COM*:00000100 PrnBuf
  f:\temp/ccLURB1Z.s:133    .progmem.data:00000000 FMT_TABLEHEADER

UNDEFINED SYMBOLS
__do_copy_data
__do_clear_bss
sprintf_P

In den Zeilen 113 bis 120 soll der Stack aufgeräumt werden, indem der 
Stackpointer auf den Wert gesstzt wird, den er vor dem Ablegend der 
Argumente von sprintf_P hatte.

Da es sich um einen atomaren Zugriff auf SPH und SPL handeln muss, 
müssen die Interrupts gesperrt werden.

ABER die Interrupts werden IMHO eine Anweisung zu früh freigegeben. 
Das untere Byte des Spackpointers ist noch nicht restauriert.

So sähe es für mich sinnvoller aus
 116 0024 0FB6          in __tmp_reg__,__SREG__
 117 0026 F894          cli
 118 0028 9EBF          out __SP_H__,r25
 119 002c 8DBF          out __SP_L__,r24
 120 002a 0FBE          out __SREG__,__tmp_reg__

Eigentlich ist IMHO schon das Sperren der Interrupts (Zeile 116-117) vor 
dem Auslesen des 16-Bit Stackpointers (Zeile 113-115) sinnvoll, wenn man 
AVR072: Accessing 16-Bit Registers beachtet.

Autor: Stefan B. (stefan) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
ADD: Das Makefile zum vorhergehenden Post

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du suchst an der falchen Ecke. sprintf, und die avr-libc funktionieren 
ohne das RTOS einwandfrei.

Dein Problem der Zeilen 113ff ist keins:
Beitrag "Re: Compilerbug bei AVR-GCC ?: Stack Pointer und Interrupts"

Um das Problem zu lösen, wäre ein anständiger Simulator hilfreich. 
Leider sind die sourcen des RTOS nicht im Netz zu finden, sonst könnte 
man es mal in VMLAB laufen lassen.

Oliver

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du meinst den Abschnitt von Jörg
Nein, das ist kein Problem.  Auch wenn es nirgends dokumentiert ist,
hat Atmel dem Compilerschreiber mal irgendwann bestätigt, dass die
Interruptfreigabe in der Tat und garantiert erst einen Befehl
verspätet erfolgt, d.h. der Befehl direkt nach einer Zuweisung an das
SREG, bei dem das I-Bit wieder gesetzt wird, wird selbst bei einem
anhängigen Interrupt noch ausgeführt.
Beitrag "Re: Compilerbug bei AVR-GCC ?: Stack Pointer und Interrupts"

Und "Why are interrupts re-enabled in the middle of writing the stack 
pointer?" in der FAQ der avr-libc?
http://www.nongnu.org/avr-libc/user-manual/FAQ.htm...

Beim SEI ist das Verhalten bzgl. des unmittelbar folgenden ASM-Befehls 
im Manual "8-Bit AVR Instruction Set" dokumentiert (SEI - Sets the 
Global Interrupt Flag (I) in SREG (Status Register). The instruction 
following SEI will be executed before any pend­ing interrupts.)

Eine entsprechende Doku von Atmel beim OUT SREG,... habe ich nicht 
gesehen und so bleibt meine Grundskepsis ;-)

Es wäre mal interessant, ob ein gepatchtes Binary (out 
_SREG_,__tmp_reg__ zu SEI gepatcht) die gleichen Probleme zeigt. 
Leider habe ich kein Binary, welches die Probleme hat, aber vielleicht 
kann m_bedded den Test machen?

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
BTW. Was passiert eigentlich bei sprintf_P mit nur zwei Parametern?

Bei nur zwei Parametern wird obige Methode zur Stackkorrektur nicht 
verwendet. Stattdessen werden klassische POPs gemacht (Zeile 107-110).
   1                   .file  "test.c"
   2                   .arch atmega32
   3                 __SREG__ = 0x3f
   4                 __SP_H__ = 0x3e
   5                 __SP_L__ = 0x3d
   6                 __tmp_reg__ = 0
   7                 __zero_reg__ = 1
   8                   .global __do_copy_data
   9                   .global __do_clear_bss
  11                   .text
  12                 .Ltext0:
  91                 .global  main
  93                 main:
   1:test.c        **** #include <avr/io.h>
   2:test.c        **** #include <avr/pgmspace.h>
   3:test.c        **** #include <stdio.h>
   4:test.c        ****
   5:test.c        **** char PrnBuf[256];
   6:test.c        **** char FMT_TABLEHEADER[] PROGMEM ="*********\r\n";
   7:test.c        **** char FMT_NAMEVALUE  [] PROGMEM =" PARAM1=%d\r\n";
   8:test.c        **** uint8_t value = 42;
   9:test.c        ****
  10:test.c        **** int main(void)
  11:test.c        **** {
  94                 abn  68,0,12,.LM1-main
  95                 .LM1:
  96                   ldi r24,lo8(FMT_TABLEHEADER)
  97                   ldi r25,hi8(FMT_TABLEHEADER)
  12:test.c        ****    sprintf_P(&PrnBuf[0], FMT_TABLEHEADER); /* name=%d */
  98                 T_TABLEHEADER)
  99                   ldi r25,hi8(FMT_TABLEHEADER)
 100 0000 80E0          push r25
 101 0002 90E0          push r24
 102 0004 9F93          ldi r24,lo8(PrnBuf)
 103 0006 8F93          ldi r25,hi8(PrnBuf)
 104 0008 80E0          push r25
 105 000a 90E0          push r24
 106 000c 9F93          call sprintf_P
 107 000e 8F93          pop __tmp_reg__
 108 0010 0E94 0000     pop __tmp_reg__
 109 0014 0F90          pop __tmp_reg__
 110 0016 0F90          pop __tmp_reg__
 111 0018 0F90        .L2:
 112 001a 0F90          rjmp .L2
 113                 /* epilogue: frame size=0 */
 114 001c 00C0        /* epilogue: noreturn */
 115                 /* epilogue end (size=0) */
 116                 /* function main size 15 (15) */
 118                 .Lscope0:
 119                 .global  FMT_TABLEHEADER
 120                   .section  .progmem.data,"a",@progbits
 123                 FMT_TABLEHEADER:
 124                   .string  "*********\r\n"
 125                 .global  FMT_NAMEVALUE
 128                 FMT_NAMEVALUE:
 129                   .string  " PARAM1=%d\r\n"
 130                 .global  value
 131 000c 2050 4152     .data
 131      414D 313D
 131      2564 0D0A
 131      00
 134                 value:
 135                   .byte  42
 136                   .comm PrnBuf,256,1
 141                   .text
 143                 .Letext0:
 144                 /* File "test.c": code   15 = 0x000f (  15), prologues   0, epilogues   0 */
 145                 ...
DEFINED SYMBOLS
                            *ABS*:00000000 test.c
  f:\temp/ccV2wNZ4.s:3      *ABS*:0000003f __SREG__
  f:\temp/ccV2wNZ4.s:4      *ABS*:0000003e __SP_H__
  f:\temp/ccV2wNZ4.s:5      *ABS*:0000003d __SP_L__
  f:\temp/ccV2wNZ4.s:6      *ABS*:00000000 __tmp_reg__
  f:\temp/ccV2wNZ4.s:7      *ABS*:00000001 __zero_reg__
  f:\temp/ccV2wNZ4.s:93     .text:00000000 main
  f:\temp/ccV2wNZ4.s:125    .progmem.data:00000000 FMT_TABLEHEADER
                            *COM*:00000100 PrnBuf
  f:\temp/ccV2wNZ4.s:130    .progmem.data:0000000c FMT_NAMEVALUE
  f:\temp/ccV2wNZ4.s:136    .data:00000000 value

UNDEFINED SYMBOLS
__do_copy_data
__do_clear_bss
sprintf_P

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan "stefb" B. wrote:
> Eigentlich ist IMHO schon das Sperren der Interrupts (Zeile 116-117) vor
> dem Auslesen des 16-Bit Stackpointers (Zeile 113-115) sinnvoll, wenn man
> AVR072: Accessing 16-Bit Registers beachtet.

Das ist Unfug. Da habe ich nicht ausreichend überlegt ;-)

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oliver wrote:
> Um das Problem zu lösen, wäre ein anständiger Simulator hilfreich.
> Leider sind die sourcen des RTOS nicht im Netz zu finden, sonst könnte
> man es mal in VMLAB laufen lassen.

Grundsätzlich kann man die Sourcen zur Evaluation bekommen und zwar 
FREE. Allerdings muss man sich bei http://www.micrium.com/ registrieren 
und kann dann 45 Tage testen.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Grundsätzlich kann man die Sourcen zur Evaluation bekommen

Hab mich mal registriert, und mir das mal angesehen. Vielleicht brauch 
ich ja auch mal ein uCOS auf eine AVR, wenn ich bisher auch nicht 
wüsste, wofür.

Das sieht allerdings schlecht aus.
Downloadbar sind ist nur die aktuelle V 2.85, und für die gibt es keinen 
avr-gcc port. Die verfügbaren gcc-ports benötigen v2.70, dafür gibt es 
die sourcen nicht mehr zum download.

Das es vom avr-gcc-port eine aktuellere Version auf
https://www.ee.lut.fi/staff/Julius.Luukko/ucos-ii/...
gibt, hast du aber mitbekommen? Die Vorversion hatte da einen bug
https://www.ee.lut.fi/staff/Julius.Luukko/ucos-ii/...

Wie gesagt, wenn du alle sourcen hast, wäre ein Versuch mit VMLAB nicht 
verkehrt. Das gibts im Netz, ist nicht ganz fertig, aber das, was geht, 
ist ganz ok. Und weil die VMLAB-Entwickler die Stelle zu OUT SREG in den 
AVR-DOCS auch nicht gefunden haben, simuliert VMLAB das auch nicht 
richtig. Abhilfe schafft

Beitrag "VMLAB Interrupt zu früh, Stackpointer falsch"

Oliver

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
m_bedded wrote:

> ..., was passiert dieser Funktion (sodass sie kracht):
>
sprintf_P(&PrnBuf[0], FMT_NAMEVALUE, value); /* name=%d */
> was dieser Funktion NICHT passiert:
>
sprintf_P(&PrnBuf[0], FMT_TABLEHEADER);
> ?

Ab da werden die Argumente vom Stack bewertet.  Die ersten beiden
Argumente werden in Registern übergeben.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch wrote:
> Ab da werden die Argumente vom Stack bewertet.  Die ersten beiden
> Argumente werden in Registern übergeben.

Das sehe ich anders, zumindest bei meinen beiden Übersetzungen oben.

In meinen beiden Assemblerlistings für 3 bzw. 2 Argumente werden niemals 
Argumente in den Registern übergeben. Alle Argumente werden per PUSH auf 
den Stack gegeben. Die verwendete Optimierung war -Os.

Bei einer Funktion mit variabler Argumentliste wie es sprintf_P ist, 
macht das IMHO auch Sinn.

Der Unterschied zwischen 3 und 2 Argumenten ist die Art, wie die 
Argumente wieder vom Stack abgereinigt werden - bei 2 Argumenten mit POP 
und bei 3 Argumenten mit einer Stackpointermanipulation inklusive 
Interruptsperrung.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich bezog die zwei Argumente auf die Adresse des Puffers und den
Formatstring.  Die sind noch nicht variabel.

Kann aber auch sein, dass es durch die unterschiedliche Methode
der Übertragung auf den Stack ist.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.