Forum: Digitale Signalverarbeitung / DSP / Machine Learning DDS performance code Ausführung


von Florian A. (florian_a39)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,

Ich arbeite momentan mit dem TMS320C6713 und möchte gerne mittels EMDA 
über die Serielle Schnittstelle einen, bzw. mehrere überlagerte Sinus 
Wellen ausgeben. Zur Erzeugung der Sinuswelle benutze ich eine 
abgewandelte Version der DDS mit Lanczos-Interpolation zwischen den 
Stützwerten meiner LUT. Die Funktion funktioniert soweit einwandfrei. 
Lediglich die Ausführungszeit lässt deutlich zu wünschen übrig!
So benötige ich ohne Optimierung durchschnittlich 15ms und mit höchstem 
Optimierungsgrad 9ms für eine Welle. Die Zeit liegt jedoch deutlich über 
der Zeit die ich benötige um meinen Ping/Pong Puffer auszugeben!

Kann vlt. jemand mal über den Code schauen und ggf. 
Laufzeitoptimierungs- Vorschläge geben? Ehrlich gesagt, kann ich mir 
nicht vorstellen, dass ich für (momentan) 960 Werte a 4 Lanczos Splines 
über 15ms benötige!!!

Mit freundlichen Grüßen,
Florian

von Fritz (Gast)


Lesenswert?

Habe vor Jahren diese CPU in einem Projekt verwendet und dabei fällt mir 
zu deinen listings folgendes ein:

Ich habe damals für meine DSP-Berechnungen nur float (nicht double) 
verwendet. Das ist glaube ich schneller als double und verbraucht auch 
weniger RAM. Du solltest einmal überlegen ob du wirklich diese hohe 
Genauigkeit überall brauchst.

Eigene code-optimierung ist zwar möglich über diverse Optionen, aber 
wirklich nicht einfach. So richtig ging erst die Post ab mit den von TI 
hand-optimierten Routinen aus den DSP-Libraries.

If's in Schleifen sind (so viel ich mich erinnere) für die 
Geschwindigkeit tödlich!

Überlege dir ob du nicht statt deiner Interpolation eine 
Samplerateconversion mit Filter verwenden kannst und die entsprechenden 
DSP-Libs verwenden kannst. Mag beim ersten Blick langsamer ausschauen, 
muß es aber nicht sein.

von Helmut S. (helmuts)


Lesenswert?

DDS ist doch prädestiniert dafür mit table-lookup zu arbeiten. Ich 
verstehe gar nicht warum du außer add+accumulate viel rechnen musst. Der 
TMS6713 hat jede Menge internes RAM um eine große Sinus-Tabelle 
abzulegen.

von Florian A. (florian_a39)


Lesenswert?

@Fritz: Danke für den Hinweis mit float, ich werde es die Tage mal 
ausprobieren.
Bezüglich der Schleifen, habe ich eine neue Funktion geschrieben die 
komplett ohne schleifen aber dafür mit einigen if's mehr auskommt. Die 
Ausführungszeit verringert sich um ca. 5ms! Aber für die Eigentliche 
Aufgabe ist es halt immer noch deutlich zu langsam.

@Helmut: Ja da haben Sie durch aus recht, jedoch sollte hier extra die 
LUT klein gehalten und die Zwischenwerte interpoliert werden.

Ich habe die Aufgabe bereits mittels IIR-Filter gelöst, dafür rufe ich 
aber einmal den cos für den Koeffizienten und den sin für die 
Amplitudenkorrektur. Und benötige lediglich 500us für 1000Werte.

Nach meinem Verständnis müsste die DDS Methode, selbst mit 
Interpolation, schneller sein als der IIR-Filter. Ist aber >22mal 
langsamer.

Gründe sind für mich als Anfänger jedoch nicht ersichtlich.

von Fritz (Gast)


Lesenswert?

Florian A. schrieb:
> Ich habe die Aufgabe bereits mittels IIR-Filter gelöst, dafür rufe ich
> aber einmal den cos für den Koeffizienten und den sin für die
> Amplitudenkorrektur. Und benötige lediglich 500us für 1000Werte.

Habe ich leider nicht verstanden wie das funktioniert.

Florian A. schrieb:
> Nach meinem Verständnis müsste die DDS Methode, selbst mit
> Interpolation, schneller sein als der IIR-Filter. Ist aber >22mal
> langsamer.
>
> Gründe sind für mich als Anfänger jedoch nicht ersichtlich.

Wenn du die Geschwindigkeit mehr ausreizen möchtest solltest du deine 
IDE so konfigurieren, dass die Assemblerlistings deines C-Programms 
erhalten bleiben. Dann solltest du ein paar einfache Test schreiben und 
du wirst wahrscheinlich erstaunliches vorfinden. Besonders die 
Paralellisierung von Schleifen ist eindrucksvoll. So kann es vorkommen, 
dass bei entsprechender Optimierung eine Schleife die 1000* durchlaufen 
wird der tatsächliche Code nue 500* oder gar nur 250* mal durchlaufen 
wird, weil die Rechnungen auf die mehrfach vorhandenen ALUs aufgeteilt 
wird. Jedes if in diesen Schleifen erschwert aber die Paralellisierung 
bzw. macht sie unmöglich!

Weiters würde ich ganz simple erst einmal messen wie lang eine 1000er 
Schleife für den sinus alleine benötigt (glaube mich zu erinnern man 
muss dann y = fsin(x) verwenden).
Als nächstes würde ich auch noch ausprobiere um zu testen wie weit die 
Optimierung erfolgreich war:
loop: y[n] = fsin(x)
      y[n + 1] = fsin(x + dx)
      y[n + 2] = fsin(x + 2 * dx)
      y[n + 3] = fsin(x + 3 * dx)
      schleifenwiederholung zu loop

PS: Ich denke, dass das direkte Berechnen der Sinuswerte ohne 
Tabellenlookup und Interpolation schnell genug sein sollte.

von Florian A. (florian_a39)


Lesenswert?

Fritz schrieb:
> Florian A. schrieb:
>> Ich habe die Aufgabe bereits mittels IIR-Filter gelöst, dafür rufe ich
>> aber einmal den cos für den Koeffizienten und den sin für die
>> Amplitudenkorrektur. Und benötige lediglich 500us für 1000Werte.
>
> Habe ich leider nicht verstanden wie das funktioniert.
>

Mit Hilfe eines Grenzstabilen IIR-Filter lässt sich ebenfalls eine Sinus 
Schwingung erzeugen. Der Koeffizient des Filters berechnet sich mithilfe 
eines Sinus. Der Amplitudengang wir mit Hilfe eines Cousins Terms 
korrigiert:
1
void IIR(Int16 *buffer, double Amp, Int16 Freq)
2
{
3
  int ii;
4
  coef = 2* cos(2* PI * Freq / F_CLK);
5
  ampcorrect = sin( PI * Freq / F_CLK);
6
7
  for(ii=0; ii<OUT_SIZE; ii++)
8
  {
9
    *(buffer+ii) = Amp * calculate_sample();
10
  }
11
}
12
double calculate_sample()
13
{
14
   y0 = coef * y1 - y2;
15
   y2 = y1;
16
   y1 = y0;
17
   return ampcorrect * y0 * 32767;
18
}
Hierbei wird eine sin und cos Berechnung durchgeführt. Die möchte man ja 
eigentlich vermeiden. Bei dieser Variante der Signalerzeugung wird sie 
lediglich einmal verwendet.


Wie auch immer, vielen Dank für den Hinweis mit dem float-Werten. Habe 
alle Einzelteile durch gemessen und verblüffende Erkenntnisse gezogen!

Konnte jetzt die Variante ohne Schleifen, lediglich unter Verwendung von 
if-Anweisungen, der Lanczos-Interpolation mit float-Funktionskopf und 
interner Berechnung mit double, die Zeit ohne Optimierung auf 7.896us 
und mit Optimierung auf sagenhafte 449us drücken!

Vielen Dank noch mal :)

PS: Habe auch die Variante mit dem sin() getestet, fsin() konnte ich 
nicht finden. Resultat: ohne Opti: ~10180us; mit opti: ~4360us

von J. S. (engineer) Benutzerseite


Lesenswert?

Du solltest nochmal einen Blick hierauf werfen, wegen Symmetrie zur 
X-Achse und geschickter Spiegelung der Quartile, wenn du bei der Tabelle 
bleibst.

http://www.mikrocontroller.net/articles/Digitale_Sinusfunktion

Ferner kannst Du die Vielfachen einfach mehr Multiplikation errechnen.

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.