Forum: FPGA, VHDL & Co. Spartan 3E - Sinusgenerator - Amplitude verändern


von Jörn (Gast)


Lesenswert?

Hallo,
Ich habe für einen Spartan 3E einen Sinus Generator implementiert.
Über USB kann die Sinuskurve im Block RAM des FPGA abgelegt werden.

Über einen DAC wird der Sinus dann Ausgegeben. Die Abtastfrequenz ist 
einstellbar.

Nun möchte ich nicht immer bei einer Amplitudenänderung die Sinuskurve 
neu über den PC berechnen und in den Block RAM laden.

Hierzu möchte ich einen normierten Sinus im Block RAM ablegen und über 
den PC nur den Multiplikator einstellen.

Um Rundungsfehler zu vermeiden Multipliziere ich erst mit dem 
Multiplikator um anschließend per Division auf den Richtigen Wert zu 
skalieren. Es wird also auf eine feste Divison durch 1000 oder ähnliches 
hinauslaufen.

Der Multiplikator lässt sich leicht mit dem Core Generator von Xilinix 
erstellen. Dieser Multipliziert zwei 16 Bit Werte in einem Takt.

Mein Problem ist jetzt das die Division 30 Takte benötigt. Für meine 
Anwendung ist das viel zu langsam.

Gibt es eine andere möglichkeit die Divison durchzuführen. Oder gibt es 
vielleicht noch einen besseren Ansatz das ganze umzusetzen?

Gruß Jörn

von Duke Scarring (Gast)


Lesenswert?

Jörn schrieb:
> Es wird also auf eine feste Divison durch 1000 oder ähnliches
> hinauslaufen.
>
> Der Multiplikator lässt sich leicht mit dem Core Generator von Xilinix
> erstellen. Dieser Multipliziert zwei 16 Bit Werte in einem Takt.
>
> Mein Problem ist jetzt das die Division 30 Takte benötigt. Für meine
> Anwendung ist das viel zu langsam.
>
> Gibt es eine andere möglichkeit die Divison durchzuführen. Oder gibt es
> vielleicht noch einen besseren Ansatz das ganze umzusetzen?
Jepp. Mach eine Division mit 1024. Die braucht gar keinen Takt ;-)

Duke

von Der E. (rogie)


Lesenswert?

Also eine Verschiebung um 10 Bits nach rechts. ;-)

von Jörn (Gast)


Lesenswert?

Ähm, also irgendwie kann ich gerade nicht folgen....

Angenommen ich möchte 5000 auf 5 skalieren...

5000 / 1000 = 5

Binär wäre das dann

5000: 000100111 0001000
5   : 000000000 0000101

Wie soll ich das durch eine Bitverschiebung erreichen?

von Hans-Georg L. (h-g-l)


Lesenswert?

Deshalb sollst du auch 1024 (Binärzahl)  und nicht 1000 (Dezimalzahl) 
als Divisor nehmen ;)

von Jörn (Gast)


Lesenswert?

Vielleicht stell ich mich gerade auch einfach nur ziemlich dumm an,
aber ich kann mir gerade absolut nicht vorstellen wie ich das umsetzen 
soll.

Der Mit dem Core Generator erstellte Dividirer braucht ja egal mit 
welchem Dividenden seine 30 Takte..

Ein kurzer VHDL schnipsel würde mir bestimmt schon auf die Sprünge 
helfen.

von Duke Scarring (Gast)


Lesenswert?

Überleg Dir das Ganze mal an einer Division mit 2, 4, 8 ...
1
signal s          : unsigned(23 downto 0);
2
signal s_div_1024 : unsigned(13 downto 0);
3
4
begin
5
6
  ...
7
  s_div_1024 <= s(23 downto 10);
8
  ...

Duke

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Duke Scarring schrieb:
> Überleg Dir das Ganze mal an einer Division mit 2, 4, 8 ...
Aber der Mensch hat nun mal 10 Finger...  ;-)

von Jörn (Gast)


Lesenswert?

:-D Na toll, dann hab ich das ja doch richtig verstanden, genau den 
Ansatz habe ich auch schon ausprobiert

Aber angenommen meine Zahl ist 8765 und möchte diese durch 100 
teilen....
dann funktioniert das ganze nicht mehr.... oder?

von Hans-Georg L. (h-g-l)


Lesenswert?

Lothar Miller schrieb:
> Aber der Mensch hat nun mal 10 Finger...  ;-)

Aber 2 Füße und deshalb kann er auch binär ;)

schau hier : http://www.youtube.com/user/AlgoRythmics

HG

von Hagen R. (hagen)


Lesenswert?

Jörn schrieb:
> Aber angenommen meine Zahl ist 8765 und möchte diese durch 100
> teilen....
> dann funktioniert das ganze nicht mehr.... oder?


Doch:

s=Sinusample
g=Gain
r=Resultat


r=s*g / 2^x

also s und g sind als Binärzahlen gespeichert in deinem FPGA. Zb. sind 
sie beide 16 Bit breit, dann ergibt sich

t=s*g

t = 32Bit breit, da s,g jeweils 16 Bit muß t minimal 32Bit breiter 
Vektor sein.

r = t / 2^16, da t = 32Bit und r wieder 16 Bit breite haben soll.

Fertig. Umgeschrieben sähe es so aus:

r = s * (g / 2^16)

Dein Gain g kannst du also als eine zahl zwischen 1 bis 1/2^16 
betrachten. Ist g also 0xFFFF dann hast du die höchste Verstärkung, bei 
0 kommt hinten auch immer 0 raus.

Möchtest du also 0.5 Gain haben steht in g = 2^8-1 drinnen da 0.5 = 
2^8/2^16-1 ist.

Ergo: wenn du dein Gain Register befüllst dann weist du das dieses 
Register einen Wertebereich von (0 bis 2^x-1)/2^x hat. Möchtest du Gain 
= 0.1 setzen dann gilt g = 0.1 * 2^16, x = 16 Bits, so einfach ist das.

Mit anderen Worten: alles in deinem FPGA sind Binärzahlen zur basis 2. 
Du möchtest mit Zahlen zur Basis 10 einstellen können. Um keine 
umständlichen Deziemalen Berechnungen durchführen zu müssen entscheidest 
du dich alle Berechnungen zur Basis 2 durchzuführen. Das bedeutet du 
musst deine Dezimalen Konfigurationen wie den Gain vorher von Dezimal 
nach Binär umrechnen. Das ist alles.

Arbeite dich in Zahlendarstellung=Potenzschreibweise, in 5. Klasse 
gelernt ?, und Festpunktarithmetik ein.

>8765 und möchte diese durch 100

87.65 = 8765 / 100 = 8765 * (0.01 * 2^x) / 2^x

(0.01 * 2^x) ist das Gain Register im FPGA und ausrechnen tust du 0.01 * 
2^x nicht im FPGA sondern extern in deiner Steuersoftware.

Schaue dir die Formel genau an und du wirst sehen das sich die beiden 
2^x streichen lassen. Aber für deine Berechnungen bedeutet diese 
Expansion der dezimalen Formel das du nun keine Berechnugen wie 
Divisionen mit dezimalen Zahlen durchführst sondern mit binären Zahlen 
und so auf langwierige Divisonen verzichten kannst weil dein Divisor 
eine zahl zur Basis 2 ist.

Gruß Hagen

von Hagen R. (hagen)


Lesenswert?

auf einem FPGA mit 2^x zu dividieren bedeutet nichts anders als die x 
untersten Bits eines Vektors zu ignorieren bzw. bestensfalls noch eine 
Rundung mit Hilfe dieser x untersten Bits.

von Hagen R. (hagen)


Lesenswert?

Der nächste Tipp ist die Einführung eines Offsets um alle Berechnung 
ganzzahlig ohen Vorzeichen machen zu können.

Also deine Sinus tabelle enthält normalerweise zb. Werte zwischen 
-+2^(x-1), sie sind also mit Vorzeichen gespeichert. Sagen wir mal 8Bit 
pro Sinuswert ergibt Wertebereich -128 bis +127. Wie fürhen einen Offset 
von +128 ein. Unsere Sinustabelle enthält nun Werte zwischen 0 bis 255 
ohne Vorzeichen.

Alle Berechungen werden nun ohne Vorzeihen und als ganzzahlen 
durchgeführt. Erst ganzs am Ende wird der DAC Wert wieder mit -Offset 
korregiert falls das überhaupt notwendig ist.

So, finally hast du

1.) alles Wichtige auf binäre vorzeichenlose Berechnungen umgestellt
2.) dadurch die Möglichkeit eröffnet mit Festpunkzahlen zu arbeiten 
(hier 0.xyz)
3.) alle Divsionen durch Multiplikationen mit dem Reziproken 
ausgetauscht, statt also x / y rechnest du x * (1/y) wobei 1/y offline 
extern berechnet werden kann.
4.) alle verbleibenden Divisonen so umgewandelt das der Divisior immer 
eine Potenz zur Basis 2 ist
5.) dadurch diese Divisonen als Performancebremse komplett entfernt


Gruß hagen

PS: den Offset von +128 kannst du so sehen wie im Analogbereich der 
virtuelle Nullpunkt der Schaltung.

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.