Forum: FPGA, VHDL & Co. Sinustabelle generieren vhdl generate


von VHDL-Starter (Gast)


Lesenswert?

Hallo Zusammen,

ich habe eine Sinustabelle mit 12-Werten à 12Bit in einem Blockram 
liegen welche dann auf einen DAC geht. Nun benötige ich aber 
unterschiedliche Amplituden, dieses Sinus-Signals, und bei 200MHZ ist 
mir die Rechnerei zu komplex, zumal ich genügend BRAM übrig habe.
Die Idee ist also mehrere Sinustabellen hintereinander ins BRAM zu 
legen. Beispielsweise:
Adresse 0-11 -> Amplitude 100%,
Adresse 12-23 -> Amplitude 95%,
Adresse 24-35 -> Amplitude 90%

Leider scheitere ich an der passenden Formulierung in VHDL.
Die ursprüngliche Sinustabelle erzeuge ich so:
1
...
2
3
type Rom12x12 is array (0 to 11) of signed (11 downto 0); 
4
    signal Sinus_Rom : Rom12x12;
5
6
begin
7
8
table: for i in 0 to 11 generate
9
        Sinus_Rom(i) <= to_signed(integer( sin(2.0*MATH_PI*(real(i)+0.5)/52.0) *2047.5),12);
10
   end generate;
11
12
process(clk)
13
begin
14
...

Und hier der fehlerhafte Versuch zwei Tabellen hintereinander generieren 
zu lassen:
1
...
2
3
type Rom24x12 is array (0 to 11) of signed (11 downto 0); 
4
    signal Sinus_Rom : Rom24x12;
5
6
begin
7
table: for i in 0 to 23 generate
8
        
9
        if i < 12 then
10
          Sinus_Rom(i) <= to_signed(integer( sin(2.0*MATH_PI*(real(i)+0.5)/52.0) *2047.5),12); -- 100%
11
        else 
12
          Sinus_Rom(i) <= to_signed(integer( sin(2.0*MATH_PI*(real(i)+0.5)/52.0) *2027),12); -- 99%
13
        end if;
14
        
15
    end generate;
16
  
17
18
process(clk)
19
begin
20
...


Quartus gefällt das If-Statement wohl nicht...
Klar könnte ich jetzt zwei getrennte BRAMs machen, aber eig. wäre mir 
zur einfacheren Adressierung ein einzelnes lieber.
Kann mir jemand auf die Sprünge helfen?

Besten Dank und viele Grüße,
Fabian

von Duke Scarring (Gast)


Lesenswert?

Probiere es doch mal so:
1
table: for i in 0 to 11 generate
2
3
        Sinus_Rom(i) <= to_signed(integer( sin(2.0*MATH_PI*(real(i)+0.5)/52.0) *2047.5),12);
4
        Sinus_Rom(i+12) <= to_signed(integer( sin(2.0*MATH_PI*(real(i)+0.5)/52.0) *2047.5*0.95),12);
5
        Sinus_Rom(i+24) <= to_signed(integer( sin(2.0*MATH_PI*(real(i)+0.5)/52.0) *2047.5*0.90),12);
6
   end generate;
Alternativ könntest Du versuchen zwischen ROM-Tabelle und DAC noch einen 
Multiplizierer unterzubringen. Bei 200 MHz braucht der vermutlich eine 
Register-Pipeline.

Duke

von Achim S. (Gast)


Lesenswert?

VHDL-Starter schrieb im Beitrag #6663175:
> Quartus gefällt das If-Statement wohl nicht...

Der Vorschlag von Duke wäre auch mein erster Gedanke gewesen. Falls das 
nicht funktioniert: mit "geschachtelten" generate statements könnte es 
vielleicht gehen. Siehe S. 6 von 
http://web.engr.oregonstate.edu/~traylor/ece474/vhdl_lectures/essential_vhdl_pdfs/essential_vhdl61-76.pdf

VHDL-Starter schrieb im Beitrag #6663175:
> Nun benötige ich aber
> unterschiedliche Amplituden, dieses Sinus-Signals, und bei 200MHZ ist
> mir die Rechnerei zu komplex

Hat dein FPGA dsp-Cores? Bei meinen Anwendungen würde ich typischerweise 
nur ein Sinus-RAM verwenden und einen dsp-Core mit Multiplikation zur 
Amplitudenanpassung dahinter setzen. Gibt vielleicht ein paar Takte 
Latenz, aber ich musste mir dabei noch in keinem Fall Sorgen machen, ob 
der dsp-core die 200MHz schafft.

von VHDL-Starter (Gast)


Lesenswert?

oh man... ja das sieht brauchbar aus. Da hätte man auch selber 
draufkommen können. Besten Dank!

von VHDL-Starter (Gast)


Lesenswert?

@Achim

ja das mit den DSP-Blöcken hatte ich auch schon überlegt. Ich versuche 
aber noch, die Designs möglichst einfach zu halten, da mir die Erfahrung 
einfach noch fehlt. Latenz wäre kein Problem... ich werde das mal 
probieren, wenn die andere Version soweit funktioniert -> schöne 
Lernaufgabe

von mama (Gast)


Lesenswert?

VHDL-Starter schrieb im Beitrag #6663175:
> Und hier der fehlerhafte Versuch zwei Tabellen hintereinander generieren
> zu lassen:

Das sollte so auch funktionieren. Nur hast du vergessen, deinen ROM Type 
zu erweitern (immer noch als 0 to 11 definiert).

von Kommentator (Gast)


Lesenswert?

VHDL-Starter schrieb im Beitrag #6663175:
> und bei 200MHZ ist
> mir die Rechnerei zu komplex, zumal ich genügend BRAM übrig habe.

Das ist nicht wirklich dein Ersnt?
Statt eine Multiplikation eine Masse an Speicher zu verwenden.
Das ist aber typisch Jungingenieur. Darf ich raten: Duale Hochschule / 
Bachelor?

Wenn ein FPGA schnell genug ist, Daten aus einem RAM zu ziehen, was 
meistenst Register kostet, um bis zum Ausgang zu kommen, dann packt er 
auch einen Multiplikator, zumal die heute fest in den FPGA verbaut sind.

von Duke Scarring (Gast)


Lesenswert?

Kommentator schrieb:
> Das ist aber typisch Jungingenieur. Darf ich raten: Duale Hochschule /
> Bachelor?
Du glaubst nicht, was ich schon alles für Lösungen bei Altingenieuren 
gesehen habe.

Es gibt einige die sich nicht (mehr) mit neuen Technologien 
auseinandersetzen wollen oder können. Da wird dann ein TTL-Gattergrab 
zusammengewürfelt, welches sich auch gut in einem CPLD abbilden lässt.
Vermutlich hat die Generation vorher ähnlich verächtlich auf die 
IC-Nutzer geschaut und lieber diskrete Transistoren eingesetzt. Und 
davor kamen die Röhren und vordem wurde Logik mechanisch realisiert.

Ist ja auch robuster, aber heute will keiner mehr die Feile in die Hand 
nehmen...

Duke

von Achim S. (Gast)


Lesenswert?

Kommentator schrieb:
> Wenn ein FPGA schnell genug ist, Daten aus einem RAM zu ziehen, was
> meistenst Register kostet, um bis zum Ausgang zu kommen

Der TO nutzt nach eigenem Bekunden bereits ein Block-RAM für die 
einfache Sinus-Tabelle. Für die einfache Sinustabelle belegt er darin 
nur 12 Werte, für mehrere Sinustabellen mit unterschiedlichen Amplituden 
wird einfach nur der ohnehin belegte RAM-Block besser ausgenutzt. Sein 
Ansatz "kostet" ggf. weniger als die nachträgliche Multiplikation.

Wenn ein dsp-Multiplikator im FPGA vorhanden ist, hätte ich persönlich 
auch den zur Amplitudenanpassung verwendet. Aber nur, weil ich das 
offensichtlicher, intuitiver und feiner abstufbar finde als mehrere 
Sinus-Tabellen ins BRAM zu stecken. Dass er als selbsterklärter 
VHDL-Starter beide Lösungen ausprobieren will finde ich eher lobenswert 
als dass man sich darüber mokieren müsste.

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

VHDL-Starter schrieb im Beitrag #6663175:
> Adresse 0-11 -> Amplitude 100%,
> Adresse 12-23 -> Amplitude 95%,
> Adresse 24-35 -> Amplitude 90%

Wenn es keine Platzprobleme im BRAM gibt, bietet es sich eventuell an, 
die Adressen der Sinustabellen an Sechzehnergrenzen auszurichten. Dann 
müssen nämlich die Adressbits nicht miteinander verrechnet werden, 
sondern bleiben separat. Das spart dann eventuell eine LUT-Laufzeit.

von Kommentator (Gast)


Lesenswert?

Achim S. schrieb:
> Der TO nutzt nach eigenem Bekunden bereits ein Block-RAM für die
> einfache Sinus-Tabelle.

Was auch schon ein Fehler ist und auch kaum persistent, denn der 
Synthesizer wird erkennen, dass er für eine 12-PUNKTE-Funktion besser 
ein LUTRAM nehmen sollte, statt eines Block-RAMs und soforn das nicht 
kommandiert ist, wird er dies auch tun :-)

Es sei denn, der TO hat die HW ausdrücklich instanziiert, statt es zu 
inferieren und damit eben jenes altertümlich HW-Basteln appliziert, das 
hier krtisiert wird:

Duke Scarring schrieb:
> Da wird dann ein TTL-Gattergrab
> zusammengewürfelt, welches sich auch gut in einem CPLD abbilden lässt.

Es ist allemal zweckmäßiger, die Funktion der Schaltung zu beschreiben, 
statt die Struktur, abgesehen von sehr eindeutigen, alternativlosen 
Realisationen, die der Synthesizer erst einbauen muss und damit mehr 
Zeit benötigt.

Hier wird versucht, HW zu basteln, die sich komplett als Funktion 
schreiben lässt, d.h. es bräuchte keine Verwendung eines BRAMs, sondern 
nur die Beschreibung der Zuordnung ADR<->DAT an sich. Der Vorteil der 
Verwendung der Beschreibung in Form einer Zuweisung ist zudem, dass der 
sich ergebende Multiplexer und seine Kombinatorik mit den Ergebnissen 
der LUTs und der davor liegenden Schaltung zusammengefasst werden kann 
und damit es bessere Nieschenlösungen für AREA und SPEED gibt, d.h die 
so zu bauende Schaltung ist sowohl schneller, als auch potenziell 
kleiner.

Ferner ergibt sich bei größeren Schaltungen, die letztlich in der Tat im 
RAM landen werden, die Möglichkeit, dass der Synthesizer die 
RAM-Funktion mit anderen Speicheranforderungen mischt und in das dann 
richtige RAM packt, nachdem die ADR-Zuordnung optimiert worden ist.

Wenn man seine Schaltung mit SUB-Modulen zusammenbaut, die selber alle 
schon ihr Block-RAM mitbringen, weil es ausdrücklich eingebaut wurde, 
besteht diese Chance kaum noch, es sei denn man stellt selber sicher, 
dass ADR-Aufrufe, die vereinfacht werden können, so aufgezogen wurden, 
dass sie zeitlich passen.

Gerade bei vielen kleinen und änhlichen Funktionen (und den Fall haben 
wir ja hier) wird es darüber hinaus dennoch so sein, dass er zuerst die 
zu erzeugenden BITs aus den Adressen heraus optimiert und dann ein 
Netzwerk bekommt, dass deutlich kleiner ist, als die rechnerische Summe 
der n Einzelnetzwerke, weil etliche LUTs / Kombinationen mehrfach 
genutzt werden können. Auch die Sinus-typische Doppelfaltung um 2 Achsen 
findet der Synthesizer und reduziert den RAM-Bedarf mal gleich deutlich.

Achim S. schrieb:
> Dass er als selbsterklärter
> VHDL-Starter beide Lösungen ausprobieren will finde ich eher lobenswert
Sicher ... hoffen wir, dass der VHDL-Starter alle denkbaren Optionen 
ausprobiert, sie katalogisert, analysiert und dokumentiert - so wie es 
richtige INGs tun und damit dann die wirklich optimiale Lösung auch 
gefunden wird. :-)

Eventuell wäre eine kleine Publikation der Ergebnisse möglich. Dann 
haben wir es schwarz auf weiss.

von J. S. (engineer) Benutzerseite


Lesenswert?

Was hier synthetisiert werden soll, braucht eigentlich weder Block-RAM 
noch LUT-Ram. Als Beispiel der einfache LFO-WAVEFORM-GENERATOR aus 
meinem ersten Synth:

Bei der üblichen Technik mit Viertel-Bogen und Anwendung einer voll 
symmetrischen Vorschrift wie hier Digitale Sinusfunktion braucht es 
in einem ARTIX gerade einmal 18 LUTs und 28 FFs bei 8 Bit Input und 11 
Bit signed output. Die Tabelle hat 64 Einträge und 10-Bit Auflösung. Man 
braucht das Signum, die Spiegelung der lokalen Adresse und bekommt somit 
schon alle 4 Signalformen Sinus, Dreieck, Rechteck und Sägezahn heraus. 
Effizienter geht es eigentlich nicht.

Bei der Aufgabenstellung des TE reichen sicher ein noch weniger LUTs.

Will man einen solch groben Sinus aber ernsthaft verwenden, sollte man 
interpolieren.

von J. S. (engineer) Benutzerseite


Angehängte Dateien:

Lesenswert?

Das hier wäre eine Simulation dazu. Man kann das auch noch verbessern , 
indem man den Sinus mit Parabeln interpoliert, bzw. ihn gleich aus 
solchigen baut. Dann geht es auch mit hohen Auflösungen sehr genau. 
Konkret einer bis zur 27. Oberwelle mit 28 Multiplieren, 14+1 
pipeline-Stufen und etwas LUT-RAM (aber ohne BRAM) mit 20 Bit Ausgang - 
effektiv 20 Bit Präzision bei 1kHz.

von Gerhard H. (ghf)


Lesenswert?

Ich habe mal vor geraumer Zeit eine Sin/cos-Tabelle nach opencores 
hochgeladen. Sie ist unter Arithmetic... und liefert sin & cos
gleichzeitig ohne viel Mehraufwand weil zu jedem Zeitpunkt sin und cos 
nie das gleiche Blockram ansprechen. Das Ding ist ziemlich 
parametrisierbar, incl. pipeline delay und Auflösung.

Gruß, Gerhard

(der sich zur Zeit eher mit analogen 10 GHz-Sinusen beschäftigt.)

von Duke Scarring (Gast)


Lesenswert?

Gerhard H. schrieb:
> (der sich zur Zeit eher mit analogen 10 GHz-Sinusen beschäftigt.)
Da kommen die FPGAs auch noch hin...




...irgendwann :-)

von Dr. Wang (Gast)


Lesenswert?

Gerhard H. schrieb:
> weil zu jedem Zeitpunkt sin und cos
> nie das gleiche Blockram ansprechen

Sicher? Wenn nur eine Viertelwelle hinterlegt wird, laufen beide 
Auslese-Pointer auch gegeneinander und überkreuzen sich. Innerhalb der 
ersten 90 Grad gilt COS (x) = SIN (90-x).

von Duke Scarring (Gast)


Lesenswert?

Dr. Wang schrieb:
> Sicher?
Selbst wenn nicht, die meisten FPGAs, die ich kenne, können 
Dual-port-RAM. Da kann jeder Generator den RAM lesen, wo er möchte. Man 
könnte auch zwei unterschiedliche Frequenzen ausgeben.
Oder man taktet den RAM doppelt so schnell und multiplext die Zugriffe.

Duke

von J. S. (engineer) Benutzerseite


Lesenswert?

Für eine bekannte Wellenform würde ich kein BRAM nehmen, wenn es die 
Größe nicht zwingend erfordert. Die Synthese kann das wirklich richtig 
klein zusammenbacken und mehrere Zugriffe sind dann auch kein Problem 
mehr. Wo es ein BRAM braucht, ist bei unbekannten Wellenformen, die der 
Anwender laden möchte. Den zweiten Anschluss der BRAMs nutzt man dann 
z.B. zur Auslese des nächsten Steps, um an Daten zu gelangen, die zur 
Interpolation des Restvektors benötigt werden, der über bleibt, wenn man 
aus der hochaufgelösten Phase die Adresse gewinnt. Die Steilheit (und 
das wäre ja der COS des SIN) hat man dann auch gleich.

von Gerhard H. (ghf)


Lesenswert?

Wenn man einen DDS z.B. in einer PLL benutzt, dann hat man keine Zeit,
sich von einer neuen Phase zu einem genauen Sinus hin zu iterieren.
Totzeit verdient ihren Namen. Das ist der Tod jedes vernünftigen 
Reglers.
Die BRAMs sind nun mal da. Wenn man sie ignoriert, dann sind sie
trotzdem noch da. Der Chip wird dadurch nicht billiger. Einen 
optimierten
und gehärteten Block durch eine Poesie mit n Pipelinestufen zu ersetzen
ist nicht gerade der Weg zur Abstraktion.

Gerhard

von J. S. (engineer) Benutzerseite


Lesenswert?

Wobei die BRAMs meist an einer bestimmten Stelle im FPGA sitzen und man 
von da schnell in Richtung Ausgang möchte, wird oft einen weiteren 
Registertakt benötigt, bis der Wert dort ist, wo man ihn braucht. Der 
Zugriff auf einen Sinus als Tabelle über LUTs, erfolgt hingegen in einem 
Takt, zumindest wenn die Größe in den o.a. Bereichen bleibt. Auch der 
Zugriff auf weitere 3-4 Adressen ist so möglich. Da ist man im Vergleich 
sogar eventuell schneller, vor allem dann, wenn mit dem Wert etwas 
veranstaltet werden soll, er also in weitere Kombinatorik einfließt, die 
teilweise mit der Decodierung des Wertes zusammengefasst werden kann.

Aber auch größere Tabellen laufen noch einigermaßen schnell. Mal konkret 
nachgeschaut:

Mein 24-Bit signed INT Sinus aus 12-Bit Adressen synthetisiert mit 
64x768kHz = ~200 MHz und hat noch 1.3ns budget. Er hat die Adresse im 3. 
Takt parat. Bedarf:

-- LUT  407  63400  0.64195585
-- FF  71  126800  0.05599369
-- IO  37  210  17.619047
-- BUFG  1  32  3.125

Mit Interpolation der 6 Restadressbits sind es 4 Takte. Mehrbedarf: 1 
MUL.

Und gerade ausprobiert: 250 MHz constraint -> immer noch 0.6ns budget - 
quer durchs FPGA geroutet. Register und LUTs lassen sich herrlich gut 
verteilen.

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.