Forum: Compiler & IDEs Takte zählen mit avrtest?


von saggi (Gast)


Lesenswert?

Abend zusammen,
Ich habe eine Frage zum zugegebenermasen eher exotischen Tool avrtest.
Ich gehöre wohl nicht wirklich zu der Zielgruppe dieses Tools, aber 
vielleicht löst es mein Problem.
Ich arbeite unter Linux und würde oft gern spontan die Takte einer 
Funktion zählen.  Zb um optimieren zu können.

Einen Windowsrechner mit Avr Studio würd ich nur ungern benutzen 
auserdem müsst ich dann jedesmal meinen Quellcode erst irgendwie ins 
Studio hringen.

Also kurze Frage: kann avrtest das was ich will?
Laut winavr.cvs.sourceforge.net/viewvc/winavr/avrtest/README?view=markup 
kann es ja irgendwie Takte zählen...

Vielleicht verirrt sich ja einer der avr-gcc Developer hierher und kann 
mir diese Frage beantworten...

Schönen Abend noch!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Am einfachsten wäre es, die entsprechende Funktionalität in avrtest 
direkt einzubauen, die Quellen hast du ja.  Wobei CVS nicht mehr aktuell 
ist, inzwischen ist SVN:

http://sourceforge.net/p/winavr/code/HEAD/tree/trunk/avrtest/

avrtest ist einfach genug, sich darin zurechtzufinden.  Implementiert 
werden könnte das über eine spezielle Adresse im SFR-Bereich analog wie 
z.B. ABORT_PORT:

16-Bit Wert (oder größer) schreiben --> Ticks setzen
16-Bit Wert (oder größer) lesen     --> Ticks lesen

Die entsprechende Variable ist program_cycles.

von saggi (Gast)


Lesenswert?

Danke dir,  Johann L.!

Ich hab mit dem Tool bisl rumgespielt und testweise darin rumgebastelt 
und es sieht schonmal ganz brauchbar aus.

Mein Testcode:
1
int main(void)
2
{
3
    uint8_t i;
4
5
    CYCLE_PORT = 0xFF;
6
7
    asm("nop");
8
    asm("nop");
9
    asm("nop");
10
11
    i = CYCLE_PORT;
12
}
produziert mir die Ausgabe:
1
Cycles: 4
2
 exit status: EXIT
3
      reason: infinite loop detected (normal exit)
4
     program: test/test.elf
5
exit address: 0000b6
6
total cycles: 29

Das Ganze gehört natürlich noch bisl sauber gemacht und getestet, aber 
für einen ersten Schuß bin ich zufrieden.
Könnte genau das sein was ich mir immer gewünscht habe. Und man kann das 
Tool wunderbar in eine Make-Umgebung einbauen, etwa wie in "make 
cycles".

Wie verhält sich avrtest denn wenn es zu bedingten Sprungbefehlen kommt? 
Kann/muss man dazu einen Testharness oder so bedaten um den Kontrollfluß 
zu lenken? Geht das überhaupt?

Grüße!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

saggi schrieb:

> Wie verhält sich avrtest denn wenn es zu bedingten Sprungbefehlen kommt?

avrtest folgt dem Pfad, der der Bedingung entspricht.

> Kann/muss man dazu einen Testharness oder so bedaten um den Kontrollfluß
> zu lenken? Geht das überhaupt?

Ich verstehe die Frage nicht.

Prinzipbedingt liefert ein Laufzeittest für eine bestimmte Eingabe 
(Daten, Programm, Daten- und Programmablage, etc.) immer eine zu diesen 
gehörende Zeit bzw. Anzahl von Ticks und damit nur eine untere 
Abschätzung für die WCET (Worst Code Execution Time).  Egal wieviele 
Tests du durchlaufen lässt, du bekommst keine Abschätzung der WCET nach 
oben -- es sei denn du kannst nachweisen, dass dein Programm für alle 
Eingaben und für alle möglichen Codeablagen und sonstigen möglichen 
Einstellungen durchgelaufen ist.

Für AVRs ist das zwar in der Regel etwas einfacher, da z.B. die Anzahl 
der Ticks pro Instruktion weder von der Ablage im Speicher noch von den 
nachfolgenden oder vorhergehenden Instruktionen abhängig ist (keine 
Caches, kein Pipelining, keine nicht-bekannten Wait-States, ...), aber 
für nicht-triviale Programme / Algorithmen ist es immer noch keineswegs 
einfach, an eine obere Abschätzung für die WCET zu kommen, die zudem 
noch halbwegs brauchbar und nicht total überschätzt ist.

Eine Möglichkeit solcher statischer Codeanalyse ist die "abstrakte 
Interpretation" wie sie in folgender Software:

http://www.absint.com/ait/

Allerdings gibt's die nicht für AVRs soweit ich weiß.

von saggi (Gast)


Lesenswert?

Ok, ich glaube ich verstehe langsam wie avrtest arbeitet.
Das Tool kennt den kompletten RAM und Flashinhalt (und damit auch 
globale Variablen) und alle Funktionen (natürlich auch .c-File 
übergreifend).
Allerdings ist der test rein statisch (ich hoffe das ist das richtige 
Wort in diesem Zusammenhang). Externe Ereignisse können nicht simuliert 
werden. Darunter verstehe ich zum einen Interrupts, zum anderen Werte 
die "von außen" kommen, bspw. der Wert einer ADC-Wandlung, der aus einem 
SFR gelesen wird.

Ohne externe Ereignisse ist der komplette Programmablauf bis zum 
Sankt-Nimmerlein-Tag natürlich fest vorherbestimmt. D.h. es gibt nur 
einen Pfad durch das komplette Programm. Selbst wenn es auf dem Weg zu 
Abzweigungen kommen sollte (if-Konstrukte) kann genau bestimmt werden 
welcher Weg genommen werden muss, da die "Startbedingungen" ja bekannt 
sind.

Ok, folgende Funktion:
1
void foo(void)
2
{
3
  uint8_t someLocalValue;
4
  ...
5
  ...
6
  someLocalValue = Adc_GetResult(CHANNEL_05);
7
  
8
  CYCLE_PORT = 0xFF;
9
  
10
  if (someLocalValue < VOLTAGE_CRITICAL)
11
  {
12
    ...
13
    ...
14
    ...
15
  }
16
  else
17
  {
18
    ...
19
    ...
20
    ...
21
  }  
22
  ...
23
  CYCLE_PORT;
24
}
Ich hab mal meinen CYCLE_PORT aus meinem letzten Beitrag übernommen.
Wenn ich nun die Laufzeit der Funktion (ohne den ADC-Teil) messen will 
ist das so erstmal nicht möglich.
Ich müsste meinen Code ändern, um zu steuern welcher Pfad der 
If-Bedingung durchlaufen wird.
1
uint8_t myControlFlag = 123;
2
void foo(void)
3
{
4
  uint8_t someLocalValue;
5
  ...
6
  ...
7
  someLocalValue = Adc_GetResult(CHANNEL_05);
8
  
9
  CYCLE_PORT = 0xFF;
10
  
11
  if (myControlFlag < VOLTAGE_CRITICAL)
12
  {
13
    ...
14
    ...
15
    ...
16
  }
17
  else
18
  {
19
    ...
20
    ...
21
    ...
22
  }  
23
  ...
24
  CYCLE_PORT;
25
}
In diesem Beispiel habe ich mir eine globale Variable angelegt, die mir 
den Ablauf steuert.
Jetzt kann ich meinen Test einmal im if- und einmal im else-Pfad 
durchlaufen lassen.
Versteh ich das nun richtig?

Dann noch eine Frage zum ersten Beispiel:
Angenommen, irgendwo in den Tiefen von Adc_GetResult() wird aufs 
ADC-Ergebnis-Register lesend zugegriffen (oder UART-Eingangsregister, 
oder PIND, du verstehst was ich meine).
Was wird daraus gelesen? 0x00 vielleicht?

Viele Grüße, und danke für bisher!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

saggi schrieb:

> Angenommen, irgendwo in den Tiefen von Adc_GetResult() wird aufs
> ADC-Ergebnis-Register lesend zugegriffen (oder UART-Eingangsregister,
> oder PIND, du verstehst was ich meine).
> Was wird daraus gelesen? 0x00 vielleicht?

avrtest ist in C geschrieben und RAM als Array im Static Storage 
realisiert; dieses wird also beim Starten von avrtest genullt.  Erste 
Schreibeaktionen werden dann getriggert durch den Startcode des 
geladenen Programms: SP setzen, .data und .bss initialisieren.

Allerdings ist avrtest nicht für Programme gedacht, die I/O verwenden -- 
egal ob intern oder extern -- oder die Interrupts verwenden.  avrtest 
ist ein reiner Code-Simulator für algorithmische Tests, der möglichst 
schnell aufen soll, damit die GCC testsuite in erträglicher Zeit fertig 
wird.  Aus dem README:
1
The main intention of avrtest is to supply a fast, easy-to-use
2
simulator to run the GCC testsuite for avr-gcc. [...]
3
avrtest does not simulate internal peripherals like timers,
4
I/O ports, interrupts, etc.

Momentan ist die einzige Möglichkeit, Eingabe in ein Programm zu 
bekommen, die Verwendung von STDIN_PORT.

Am ehesten kann man mit avrtest "normale" C-Programme simulieren wie
1
#include <stdio.h>
2
3
int main (void)
4
{
5
    printf ("Hallo World!\n");
6
    return 0;
7
}

was ein "Hallo World!" auf die Console ausgibt -- vorausgesetzt, das 
Programm wird gelinkt wie im README erklärt.  Ansonsten kann die obige 
hallo-Quelle unverändert verwendet werden.

Abgesehen vom Testen von avr-gcc eignet sich avrtest zum Auffinden von 
Fehlern in komplexen Algorithmen, die man ansonsten, etwa durch scharfes 
Nachdenken, nicht findet.  Dazu llässt man eine Trace schreiben und 
schaut, wo es hakt.  Verwendet habe ich dies z.B. bei der 
Implementierung der Fixed-Point Erweiterung im avr-gcc; einerseits um 
schnelle Lösungen zu finden und andererseits, um für die Algorithmen 
vernünftige Testabdeckung zu erlangen, wofür es immer ein kleines 
Framework braucht.

von Oliver (Gast)


Lesenswert?

In der Richtung gibt es ja nun noch mehr, z.B. das hier:

http://www.nongnu.org/simulavr/

Oliver

von saggi (Gast)


Lesenswert?

Oliver schrieb:
> In der Richtung gibt es ja nun noch mehr, z.B. das hier:
>
> http://www.nongnu.org/simulavr/
>
> Oliver

Den hab ich mir auch schonmal angeschaut. Wird der überhaupt noch aktiv 
entwickelt? Damals wurde zB mein ATmega1284P nicht unterstützt...
Wenn er noch entwickelt werden würde wäre er super, kann immerhin als 
Backend für gdb dienen.

@ Johann:
Ok, genauso hab ich mir das gedacht.
I/O ist eigentlich nicht nötig. Mir gehts eben hauptsächlich darum, 
Laufzeiten von Programmteile zu messen, z.B. um Algorithmen zu 
verbessern.
Das ist ja möglich, so wies aussieht.

Einzige Einschränkung: Um eine If-Abfrage in eine bestimmte Richtung zu 
lenken muss ich globale Variablen verwenden. Ich werd mal bisl weiter 
damit spielen und dann berichten!

Viele Grüße!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

saggi schrieb:
> Ich habe eine Frage zum zugegebenermasen eher exotischen Tool avrtest.
> [...] Ich arbeite unter Linux und würde oft gern spontan die Takte
> einer Funktion zählen.  Zb um optimieren zu können.
>
> [...]
>
> Also kurze Frage: kann avrtest das was ich will?

Jupp, inzwischen kann avrtest das: heißt TICKS_PORT, ist ein 
Glitch-freier 32-Bit-Zähler und kann per -ticks aktiviert werden.  Das 
Define zu TICKS_PORT befindet sich wie die anderen auch in avrtest.h, 
das man direkt includen kann oder per -include avrtest.h.

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.