Forum: Mikrocontroller und Digitale Elektronik 16-Bit Multiplikation/Division auf 8-Bit MCU testen


von Ralf (Gast)


Lesenswert?

Hallo,

wie kann man effektiv 16-Bit Multiplikations-/Divisionsroutinen 
(Assembler) testen?
Ich könnte jeden möglichen Eingabewert in einer Schleife durchlaufen 
lassen, jeweils inkrementieren und das Ergebnis ausgeben lassen, aber a) 
sitz ich da ewig und b) sitze ich noch länger für's Prüfen aller 
Ergebnisse ;)

Einfach ein paar zufällig gewählte Werte reinwerfen und Ergebnis prüfen 
geht schon, doch mich interessiert, wie man die jeweilige 
Implementierung effektiv prüft.

Gibt's da spezielle Vorgehensweisen, Richtlinien, o.ä.?

Ralf

von Werner (Gast)


Lesenswert?

Ralf schrieb:
> Gibt's da spezielle Vorgehensweisen, Richtlinien, o.ä.?

Rückgriff auf bewährte Routinen, die schon lange stabil laufen?

von c-hater (Gast)


Lesenswert?

Ralf schrieb:

> wie kann man effektiv 16-Bit Multiplikations-/Divisionsroutinen
> (Assembler) testen?

Man schaut sich die Implementierung an und überlegt, was alles schief 
gehen kann. In aller Regel gibt es Sonderfälle oder bestimmte Grenzen, 
ab denen etwas anders laufen muß, damit das Ergebnis stimmt.

Bezogen auf da konkrete Problem sind das natürlich die Fälle, wenn die 
Operanden größer als 8 Bit werden sind bzw. das Ergebnis größer als 8 
Bit wird. Bei signed oder gemischten signed/unsigned Operationen kommt 
dazu dann noch der Einfluß des Vorzeichens, da sind also ein paar Fälle 
mehr zu testen.

Wie auch immer, wenn man die paar Grenzfälle erfolgreich durchgetestet 
hat, kann man ziemlich sicher sein, daß die Implementierung insgesamt 
auch korrekt ist.

von Frank K. (fchk)


Lesenswert?

Ralf schrieb:

> Gibt's da spezielle Vorgehensweisen, Richtlinien, o.ä.?

1. Nachweis der Korrektheit der verwendeten mathematischen Algorithmen

2. Nachweis der Korrektheit der Implementation
- Nachweisen, dass der vorhandene Code zu 100% getestet wird, sprich
  JEDE Codezeile muss mindestens einmal durchlaufen werden
  JEDER Vergleich muss mindestens einmal falsch und einmal wahr getestet 
worden sein

Wenn da steht JEDER, dann musst Du beweisen können, dass wirklich 
ausnahmslos jede Instruktion und jeder bedingte Sprung in den Test 
einbezogen wird, und bei letzterem für (Bedingung wahr) und (Bedingung 
falsch). Das nennt man Testabdeckung, und die muss 100% sein.

Deine Eingangswerte musst Du so wählen, dass Du Deine 100% erreichst.

3. Optional: Nachweis des Zeitverhaltens
heißt: wie viele Takte braucht der Code minimal, wie viel maximal, und 
wovon hängt das ab. Manchmal ist es wichtig, dass eine Routine immer 
exakt die gleiche Anzahl an Zyklen und an Energie braucht, unabhängig 
von den Parametern. Beispiel Kryptographie.

Das jetzt mal so als Anregung ohne Anspruch auf Vollständigkeit.

fchk

von Anja (Gast)


Lesenswert?

Ralf schrieb:
> Einfach ein paar zufällig gewählte Werte reinwerfen und Ergebnis prüfen
> geht schon, doch mich interessiert, wie man die jeweilige
> Implementierung effektiv prüft.

Am besten automatisch, die Ergebnisse dann per RS232 auf den PC 
übertragen und dort auswerten.

Ich würde bis 8*8 Bit vollständig testen und danach mit Schrittweite 127 
für A und 129 für B die übrigen Werte. sind ca 5 * 65536 Ergebnisse a 4 
Byte also bei 9600 Baud ist der Test in einer halben Stunde durch.
Aber hoffentlich findest du keinen Pentium-Bug.

Gruß Anja

von Ralf (Gast)


Lesenswert?

@Werner:
>> Gibt's da spezielle Vorgehensweisen, Richtlinien, o.ä.?
> Rückgriff auf bewährte Routinen, die schon lange stabil laufen?
Naja, wie man's nimmt... ich hab mir halt für den Anfang eine Routine 
aus'm Web rausgesucht und passend implementiert.
Was ist für dich bewährt? Das was man aus nem Open-Source-Compiler aus 
dem Quellcode rauskopieren kann? Das was die NASA verwendet (die ja 
bekanntlich auch schon Abstürze im wahrsten Sinne des Wortes wegen 
Fehlern im Code hatte)?
Zeig mir bitte die Seite mit den bewährten Sachen ;) Ich verstehe was 
du meinst, aber ich kenne keine "bewährte" Quelle.

@c-hater:
> Man schaut sich die Implementierung an und überlegt, was alles schief
> gehen kann. In aller Regel gibt es Sonderfälle oder bestimmte Grenzen,
> ab denen etwas anders laufen muß, damit das Ergebnis stimmt.
Das erste was ich bei der Divisionsroutine gemacht habe war die 
Erweiterung auf Division durch Null, entsprechend gekennzeichnet durch 
den Rückgabewert ;)

> Bei signed oder gemischten signed/unsigned Operationen kommt dazu dann
> noch der Einfluß des Vorzeichens, da sind also ein paar Fälle mehr zu
> testen.
Ich brauche momentan nur unsigned, aber du hast recht, an die 
'signed'-Fälle hab ich noch gar nicht gedacht...

> Wie auch immer, wenn man die paar Grenzfälle erfolgreich durchgetestet
> hat, kann man ziemlich sicher sein, daß die Implementierung insgesamt
> auch korrekt ist.
Okay, das passt zum o.g. 'reinwerfen von ein paar ausgesuchten Werten'.

@fchk:
> 1. Nachweis der Korrektheit der verwendeten mathematischen Algorithmen
Den Nachweis wollte ich eigentlich durch das Testen erreichen ;)
Aber du meinst vermutlich, dass schon die Basis für die Umsetzung 
nachweislich korrekt sein muss.

> 2. Nachweis der Korrektheit der Implementation
> ...
> Das nennt man Testabdeckung, und die muss 100% sein.
> Deine Eingangswerte musst Du so wählen, dass Du Deine 100% erreichst.
Das aber jeder Sprung bzw. jedes 'IF' abgearbeitet wird bedeutet aber 
doch nicht, dass am Ende immer das korrekte Ergebnis rauskommt?

> 3. Optional: Nachweis des Zeitverhaltens
So schnell wie möglich ist natürlich immer gut, aber 16-Bit-Berechnungen 
auf nem 8-Bitter schließt das irgendwie aus ;)
In meinem Fall wäre es zwar schön, wenn's zügig läuft, aber es ist 
(gottseidank) kein absolutes Kriterium gesetzt.

Aber deine Liste gefällt mir gut.

@Anja:
> Am besten automatisch, die Ergebnisse dann per RS232 auf den PC
> übertragen und dort auswerten.
Ja, das hatte ich ursprünglich vor, aber ich hab das mal grob 
überschlagen, wenn ich für die Eingangswerte die vollen 16 Bit verwende 
und jeweils um eins inkrementiere, da komme ich auf 2^32 Ergebnisse, 
welche jeweils vier Bytes lang sind, (Division, jeweils 16-Bit Ganzzahl 
& Rest). Das wären ohne(!) das Mitsenden der Eingangsdaten 8GB(!). Macht 
bei 115200 Baud, 10 Bit Übertragung und der (irrigen) Annahme, dass 
keine Pausen zwischen den Zeichen auftreten etwa 8,6 Tage ;)

> Ich würde bis 8*8 Bit vollständig testen und danach mit Schrittweite 127
> für A und 129 für B die übrigen Werte. sind ca 5 * 65536 Ergebnisse a 4
> Byte also bei 9600 Baud ist der Test in einer halben Stunde durch.
Mmmmh, okay, das gefällt mir, das könnte ich mal ausprobieren.

> Aber hoffentlich findest du keinen Pentium-Bug.
Es ist ne Intel-Architektur, aber damals waren Cores noch bugfrei ;)

Danke an alle für den Input!

Ralf

von Viktor N. (Gast)


Lesenswert?

mit einem Simulator ?

von Ralf (Gast)


Lesenswert?

@Viktor:
> mit einem Simulator ?
Ich bezweifle dass es einen 8051 Simulator gibt, der die Ausgabe der 
seriellen Schnittstelle in eine Datei umleiten kann, aber ich such mal, 
danke für den Tipp.

Ralf

von Frank K. (fchk)


Lesenswert?

Ralf schrieb:

> @fchk:
>> 1. Nachweis der Korrektheit der verwendeten mathematischen Algorithmen
> Den Nachweis wollte ich eigentlich durch das Testen erreichen ;)

Damit prüfst Du Deine Implementation. Die Mathematik dahinter könnte 
trotzdem falsch sein.

> Aber du meinst vermutlich, dass schon die Basis für die Umsetzung
> nachweislich korrekt sein muss.

Genau.

>> 2. Nachweis der Korrektheit der Implementation
>> ...
>> Das nennt man Testabdeckung, und die muss 100% sein.
>> Deine Eingangswerte musst Du so wählen, dass Du Deine 100% erreichst.
> Das aber jeder Sprung bzw. jedes 'IF' abgearbeitet wird bedeutet aber
> doch nicht, dass am Ende immer das korrekte Ergebnis rauskommt?

Automatisch nicht.

Du weißt dann aber, dass jeder Vergleich korrekt codiert ist. Wenn Du in 
einer Hochsprache arbeitest und mehrere Vergleich logisch miteinander 
verknüpfst, musst Du das natürlich für jeden Einzelvergleich machen.

Und Du gehst sicher, dass JEDE Codezeile tatsächlich durchlaufen und 
damit getestet wird und Du damit keine ungetesteten Codeblöcke hast, und 
auch keinen toten Code, der nie durchlaufen werden kann, weil Du z.B. 
ein unsigned auf <0 oder ein char auf >300 testest oder ein if (a=0) 
statt if (a==0) stehen hast (vorsichtige Menschen schreiben daher if 
(0==a), weil ein versehentliches if (0=a) einen error wirft).

Die Kunst ist, mit möglichst wenigen Testvektoren auszukommen und 
trotzdem eine vollständige Codeabdeckung zu haben. Das ist dann aber 
schon etwas für echte Spezialisten in diesem Bereich. Ich gehöre nicht 
dazu.

>> 3. Optional: Nachweis des Zeitverhaltens
> So schnell wie möglich ist natürlich immer gut,
Eben gerade nicht! Das hatte ich ja geschrieben. Manchmal ist es 
wichtiger, eine konstante Durchlaufzeit zu haben als in jedem Fall eine 
möglichst kurze.

Bei einer Multiplikation kannst Du Spezialfälle (0, 1, 2^n) nutzen, wenn 
es nicht auf konstante Durchlaufzeit ankommt.

Und Du kannst schauen, ob beispielsweise 193*13 mehr Takte braucht als 
13*193, sprich, ob es vorteilhaft ist, wenn der größere oder der 
kleinere Faktor zuerst kommt. Das Beispiel habe ich mit Bedacht gewählt 
- beide Zahlen sind prim und haben drei 1-Bits in der Binärdarstellung.

fchk

von Anfänger (Gast)


Lesenswert?

Hallo
Fortlaufend, chipintern, Multiplikation und deren Umkehrung rechnen. 
Kommt es zu einer Abweichung wird das im EEprom niedergelegt. Da darf 
der Chip gerne 8-Tage vor sich hinrechnen, Kontrollarbeit fällt keine 
an, der Chip rechnet und übernimmt zugleich die Kontrollarbeit.

von Bronco (Gast)


Lesenswert?

Ralf schrieb:
>> Am besten automatisch, die Ergebnisse dann per RS232 auf den PC
>> übertragen und dort auswerten.
>
> Ja, das hatte ich ursprünglich vor, aber ich hab das mal grob
> überschlagen, wenn ich für die Eingangswerte die vollen 16 Bit verwende
> und jeweils um eins inkrementiere, da komme ich auf 2^32 Ergebnisse,
> welche jeweils vier Bytes lang sind, (Division, jeweils 16-Bit Ganzzahl
> & Rest). Das wären ohne(!) das Mitsenden der Eingangsdaten 8GB(!). Macht
> bei 115200 Baud, 10 Bit Übertragung und der (irrigen) Annahme, dass
> keine Pausen zwischen den Zeichen auftreten etwa 8,6 Tage ;)

Wie Du richtig erkannt hast, macht es wenig Sinn, absolut jeden 
einzelnen Fall zu testen (obwohl dies bei einer geringen Anzahl von 
Fällen am einfachsten ist).
Stattdessen mußt Du Dir überlegen, welche Fälle es wert sind, getestet 
zu werden. Diese Fälle zu identifizieren, ist eine Kunst für sich.
Typischerweise deckt man die Fälle ab, wo es zu Über/Unterläufen bzw. 
Über/Unterträgen kommt.

Stichworte:
http://en.wikipedia.org/wiki/Boundary_case
http://en.wikipedia.org/wiki/Corner_case
http://en.wikipedia.org/wiki/Edge_case

von W.S. (Gast)


Lesenswert?

Ralf schrieb:
> wie kann man effektiv 16-Bit Multiplikations-/Divisionsroutinen
> (Assembler) testen?

Mach's mal ein bissel einfacher als all die schlauen Vorredner.

Ich würde mir nen kleinen Kommandointerpreter auf dem uC schreiben, der 
Kommandos von der seriellen Schnittstelle empfängt, deine Lib aufruft 
und das Ergebnis als Text wieder zurückgibt. Die umgekehrt polnische 
macht sich da am einfachsten. Beispiel:
1234E321*

sollte als 123 Enter, 321 multipliziere mit "geenterter" Zahl und 
liefere Ergebnis (also 123*321) zurück interpretiert werden.

Den Rest macht ein Skript auf'n PC und da würde ich Roulette spielen, 
also mit Zufallszahlen arbeiten. Bei einer Lib, die so etwa 10000 mal 
mit Zufallszahlen richtig rechnet, brauchst du dann bloß noch 
Sonderfälle abzutesten, also Überläufe, Division durch 0 usw.

W.S.

von Wilhelm F. (Gast)


Lesenswert?

Muß man für eine 16bit/8bit-Division wirklich alle Zahlen durch 
simulieren, die überhaupt vor kommen können?

Nonsens.

Es reicht, wenn man begriffen hat, daß der Code anständig arbeitet, und 
wie er arbeitet.

Man kann da bspw. mal zwei passende Operanden nehmen, und den 
Assemblercode mit dem Simulator durch steppen, um zu sehen.

Zu beachten sind da nur Sonderfälle wie z.B. Division durch 0.

von Wilhelm F. (Gast)


Lesenswert?

In einem ollen aber leistungsfähigen 8051-Derivat 80C517A habe ich eine 
MDU. Eine Hardware-Multiplikations-Divisisions-Unit für 32/16 oder 
16*16. Nur 4 Maschinenzyklen für das Ergebnis. Diese bläßt den 8051 auch 
um Welten auf, er rechnet damit gut um den Faktor 100 schneller als 
Software, die Unit ist auch noch auf Floating Point optimiert, und hat 
Shift-Operationen bzw. Normalisierung. Da weiß man allerdings nicht, ob 
die Hardware richtig rechnet. Bugs ERRATA sind mir aber nicht bekannt.

von Ralf (Gast)


Lesenswert?

@fchk:
> Die Kunst ist, mit möglichst wenigen Testvektoren auszukommen und
> trotzdem eine vollständige Codeabdeckung zu haben. Das ist dann aber
> schon etwas für echte Spezialisten in diesem Bereich. Ich gehöre nicht
> dazu.
Ja, ich glaub das sind z.T. mittlerweile sogar eigene Studienkurse (je 
nachdem).

>> So schnell wie möglich ist natürlich immer gut,
> Eben gerade nicht! Das hatte ich ja geschrieben. Manchmal ist es
> wichtiger, eine konstante Durchlaufzeit zu haben als in jedem Fall eine
> möglichst kurze.
Das stimmt schon, aber abgesehen von der von dir erwähnten Kryptographie 
fällt mir keine Anwendung ein, bei der die konstante Laufzeit den Vorzug 
hat. Oder gibt's da noch mehr?

> Bei einer Multiplikation kannst Du Spezialfälle (0, 1, 2^n) nutzen, wenn
> es nicht auf konstante Durchlaufzeit ankommt.
Stimmt, wobei ich das für diesen Fall wahrscheinlich eher nicht brauche, 
aber ich behalt's im Hinterkopf.

> Und Du kannst schauen, ob beispielsweise 193*13 mehr Takte braucht als
> 13*193, sprich, ob es vorteilhaft ist, wenn der größere oder der
> kleinere Faktor zuerst kommt. Das Beispiel habe ich mit Bedacht gewählt
> - beide Zahlen sind prim und haben drei 1-Bits in der Binärdarstellung.
Gute Idee, das probier ich mal.

@Anfänger:
> Fortlaufend, chipintern, Multiplikation und deren Umkehrung rechnen.
> Kommt es zu einer Abweichung wird das im EEprom niedergelegt. Da darf
> der Chip gerne 8-Tage vor sich hinrechnen, Kontrollarbeit fällt keine
> an, der Chip rechnet und übernimmt zugleich die Kontrollarbeit.
Weiss nicht ob die Idee so gut ist. Ich halte es zwar für nahezu 
unwahrscheinlich dass beispielsweise eine Multiplikations- und 
Divisionsroutine "gleichartig falsch" rechnen, aber ich halte einen 
(zusätzlichen) externen Test für sinnvoll.

@Bronco:
Danke für die Stichwörter.

@W.S.:
> Mach's mal ein bissel einfacher als all die schlauen Vorredner.
Hältst du nichts von dem, was sie sagten? Dann darfst du gerne sachlich 
und detailliert argumentieren/kommentieren ;)

> Ich würde...
> Den Rest macht...
So hab ich's jetzt auch gemacht, Eingangsdaten rein, Ergebnis zurück und 
vergleichen.

Danke an alle.

Ralf

von Ralf (Gast)


Lesenswert?

@Wilhelm:
Njaaaaaaaaa, der ist dann physikalisch etwas oversized vom Gehäuse ;)
Und es mag sein dass solche Berechnungen zwar schneller gehen, aber der 
C517 hat glaub ich noch die 12er-Taktteilung. Damit ist er für das was 
ich machen will wiederum zu langsam :/

Ralf

von Wilhelm F. (Gast)


Lesenswert?

Ralf schrieb:

> @Wilhelm:
> Njaaaaaaaaa, der ist dann physikalisch etwas oversized vom Gehäuse ;)
> Und es mag sein dass solche Berechnungen zwar schneller gehen, aber der
> C517 hat glaub ich noch die 12er-Taktteilung. Damit ist er für das was
> ich machen will wiederum zu langsam :/
>
> Ralf

Wegen seiner schnellen Rechnung war dieser µC in der Automation und mit 
Regelungen sehr beliebt. Auch wenn da noch der Flaschenhals 8051 drin 
steckt. Für Miniatur-Platinen in der Streichholzschachtel ist er nichts, 
da hast du Recht. Fürs Hobby reicht er mir immer noch ein Weilchen.

Auf meiner To-Do-Liste steht noch, die Mathe-Routinen aus dem 
SDCC-Compiler darauf anzupassen. SDCC unterstützt ja leider nicht die 
speziellen Features der 8051-Derivate. memcpy() ist unter anderem auch 
noch mal dran, wegen der Multiple Datapointer im Derivat.

Wenn ich mal was brauche, was ich nicht habe, suche ich auch was 
anständiges, z.B. ARM Cortex, keine Frage.

von Alexander S. (esko) Benutzerseite


Lesenswert?

Ralf schrieb:
> Das stimmt schon, aber abgesehen von der von dir erwähnten Kryptographie
> fällt mir keine Anwendung ein, bei der die konstante Laufzeit den Vorzug
> hat.
In welcher Anwendung im Embedded-Bereich hat denn eine möglichst kurze 
durchschnittliche Laufzeit einen Vorteil, wenn die maximale Laufzeit 
gleich bleibt?
Die meisten Sachen haben gewisse Echtzeitanforderungen und da bringt es 
nichts schneller als nötig fertig zu sein. Ganz im Gegenteil ist eine 
konstante Laufzeit beim Test der Anwendung hilfreich.

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.