Ich werd' noch mal blöde mit dem Xilinx:
Gerade auf die Schnelle versucht, eine Sinustabell mit dem DDS-Compiler
zu erzeugen, die mit uc-programmierten Frequenzen läuft und
festgestellt, dass die nur bis 16 bit Phasenauflösung geht. Gibt es das?
Markus W. schrieb:> Gerade auf die Schnelle versucht, eine Sinustabell mit dem DDS-Compiler> zu erzeugen, die mit uc-programmierten Frequenzen läuft und> festgestellt, dass die nur bis 16 bit Phasenauflösung geht. Gibt es das?
Du meinst der Phasenakkumulator schafft nur 16 Bit Auflösung? Das fände
ich sehr seltsam.
Oder meinst du, dass die Sinustabelle "nur" 2^16 Einträge schafft? Das
könnte schon sein, schließlich ist der Speicherplatz in deinem FPGA
begrenzt.
Welches FPGA nutzt du denn, wieviel Speicherressourcen hat das?
Genannt ist nur die Phase. Kann sein es ist die LUT-Adress-Breite, keine
Ahnung. Chip ist Kintex, Platz sollte rechnerisch für 20 solche Tabellen
sein.
Markus W. schrieb:> Genannt ist nur die Phase. Kann sein es ist die LUT-Adress-Breite, keine> Ahnung.
Ja, es ist die LUT-Adressbreite. Wenn du oben statt "LUT-Only" eine
andere Option wählst siehst du, dass der Phasenakkumulator wesentlich
breiter sein könnte. Und tatsächlich definiert ja zunächst mal der
Phasenakkumulator die Phasenauflösung der DDS, nicht die Größe der
Sinustabelle.
Bei der Sinustabelle wird tatsächlich auf eine Adressbreite von 16 Bit
begrenzt. Das hat vielleicht auch was mit der Spannungsauflösung zu tun
(in deinem Bild der "Output Width"). Der DDS-Compiler will vielleicht
nicht beliebig viel Speicherplatz für die Sinustabelle verwenden, wenn
in der Tabelle in benachbarten Stellen dann ohnehin jeweils lauter
identische Werte stehen würden.
Findet sich denn im Datenblatt des DDS-Compilers nichts dazu?
Markus W. schrieb:> Gerade auf die Schnelle versucht, ...Markus W. schrieb:> Genannt ist nur die Phase. Kann sein es ist die LUT-Adress-Breite, keine> Ahnung.
Dann solltest du vielleicht erstmal letzteres Ändern.
Nimm dir etwas Zeit und arbeite dich etwas in die Funktionsweise eines
DDS ein, statt blind auf irgendwelchen Tools rumzuklicken.
Achim S. schrieb:> Bei der Sinustabelle wird tatsächlich auf eine Adressbreite von 16 Bit> begrenzt. Das hat vielleicht auch was mit der Spannungsauflösung zu tun> (in deinem Bild der "Output Width").
Leider nein, auch wenn ich auf 20 bit und mehr aufziehe, ergibt sich
keine andere Konfigurationsoption. Die "Rasterung", was immer das sein
soll, schränkt sogar auf 4 Bits ein. Auch andere Optionen ändern das
nicht.
> Der DDS-Compiler will vielleicht> nicht beliebig viel Speicherplatz für die Sinustabelle verwenden,
Es steht genug RAM zur Verfügung.
> wenn in der Tabelle in benachbarten Stellen dann ohnehin jeweils lauter> identische Werte stehen würden.
Das wäre nicht der Fall, da die 24 Bit diese Auflösung hergeben. Lässt
sich ja ausprobieren. Die Werte ändern sich von Phasenwert zu Phasenwert
Achim S. schrieb:> Und tatsächlich definiert ja zunächst mal der> Phasenakkumulator die Phasenauflösung der DDS, nicht die Größe der> Sinustabelle.
Dann würde auch eine 10 Bit DDS dasselbe ausgeben, wie eine 20 Bit DDS?
Hauptsache der Phasenvektor ist gross genug?
my2ct schrieb:> Dann solltest du vielleicht erstmal letzteres Ändern.
Kann ich nicht. Ich benötige die Auflösung.
> Nimm dir etwas Zeit und arbeite dich etwas in die Funktionsweise eines> DDS ein
Ich kenne DDS sehr genau und habe auch den Core immer mal wieder
verwendet.
Ich sehe nur, dass er scheinbar nur noch AXI kann, statt auch das
frühere Interface. Da gab es diese Einschränkung nicht. Hängt es gfs
damit zusammen, dass die AXI-Phase nut 16 Bit kann?
Markus W. schrieb:> Ich sehe nur, dass er scheinbar nur noch AXI kann, statt auch das> frühere Interface. Da gab es diese Einschränkung nicht.
habe es eben mit der ISE 14.7 ausprobiert. Da kann die Phasenbreite zwar
bis 26 Bit eingestellt werden (was beim aktuellen DDS-Compiler
wahrscheinlich immer noch geht), aber die Adressbreite für die
Sinustabelle geht auch bei dieser ziemlich uralten ISE nur maximal bis
16 Bit. War also auch früher schon genau so.
Dafür werden im Datenblatt dann verschiedene Methoden genannt, wie man
die daraus evtl. entstehenden Signalverfälschungen reduzieren kann
(Phase Dither, ...)
Markus W. schrieb:> my2ct schrieb:>> Dann solltest du vielleicht erstmal letzteres Ändern.> Kann ich nicht. Ich benötige die Auflösung.
Na ja.
Es geht dir also einfach darum, einen möglichst großen Speicher im FPGA
mit einer Sinustabelle vorzubelegen? Wenn dir der DDS-Compiler dabei
nicht weit genug geht, dann kannst du ja selbst einen entsprechend
großen Block-RAM instanziieren und die Sinuswerte über ein
Koeffizientenfile vorbelegen. Um Sinus-Werte zu berechnen brauchst du ja
nicht zwingend den DDS-Compiler, das kannst du auch unabhängig von
Xilinx z.B. per Excel erledigen.
Wenn der Router es dann noch schafft, alle Ram-Blöcke wie von dir
gewünscht zu verdrahten, hast du die benötigte Auflösung deines
Problems.
Markus W. schrieb:> my2ct schrieb:>> Dann solltest du vielleicht erstmal letzteres Ändern.> Kann ich nicht. Ich benötige die Auflösung.
Für die Phase des DDS oder für den Analogwert bei Ausgabe der
Sinuskurve?
Achim S. schrieb:> einen großen Block-RAM instanziieren und die Sinuswerte über ein> Koeffizientenfile vorbelegen. Um Sinus-Werte zu berechnen brauchst du ja> nicht zwingend den DDS-Compiler, das kannst du auch unabhängig von> Xilinx z.B. per Excel erledigen.
Exakt. Mache ich schon immer so. Der DDS-Compiler scheint auch so seine
Eigenheiten zu haben, bzw. die Firma Xilinx hat ein eigenes Verständnis,
wie Sinuswerte auszusehen haben, damit man sie sequenziell ausgeben
kann. Ich habe vor etwa 10 Jahren dazu schon einen Artikel gepostet, in
welchem ich auf die Unterschiede hingewiesen habe. Es scheint sich um
Rundungsfehler zu handeln, bzw. ich runde anders, als Xilinx es tut.
Mein Phasenoffset ist auch ein anderer und orientiert sich an dem
Prinzip, dass ein Phasenwert X immer die Mitte eines Phasenbereiches
abdeckt, also die 0 repräsentiert den Bereich von 0 bis 1, die 1 den
von 1 bis 2. Damit ist der richtige Sinuswert Y der des Wertes X in der
Mitte, also z.B. 0.5, 1.5 etc. Der erledigt das Problem eines impliziten
Offsets und einer möglichen Änderung des Verhaltens wenn der Vektor mal
aufgebohrt wird. Auch sind die Sinuswellen unterschiedlicher Auflösungen
so immer auf derselben Phasenlage. Siehe auch meine Ausführungen im
Artikel
Digitale Sinusfunktion.
Der Nachteil einer selbst erzeugten Tabelle ist leider die extreme
Synthesezeit bei langen Tabellen, wenn man es nicht im BRAM, sondern
unter z.B. Mitbenutzung von LUTRAM bauen will. Ich erlebe das auch
gerade wieder bei meinen fest verbauten Wellenformen. Die haben z.B. 20
Bit-Phasen und synthetisieren ewig. Ich will die aber in LUT haben,
damit sie klein bleiben. Typisch lassen sich die Umfänge ja stark
reduzieren, wenn die Logik optimiert wird.
Wenn es nur ein Sinus sein soll, dann tut gfs auch der CORDIC. Der hat
dann eben wieder Latenzen und den Nachteil, dass man nur binäre
Auflösungen erzeugen kann.
Ich habe in 2010 eine Sinustabelle in reinem VHDL nach
< https://opencores.org/projects/sincos >
hochgeladen. Die hat keine künstlichen Beschränkungen.
Wenn du das RAM /ROM hast, dann bekommt er es voll.
Das Testbett enthält als Beispiel einen DDS.
Reines portables VHDL, kein ise/vivado/matlab, was auch immer.
Es werden alle möglichen Symmetrien ausgenutzt und man
bekommt Sinus & Cosinus gleichzeitig ohne Mehraufwand.
(Na ja, ein paar Pipelineregister).
Die Anzahl der Pipelinestufen kann man einstellen von 0
(rein kombinatorisch) bis 10, WIMRE. Die werden halbwegs
sinnvoll an den richtigen Stellen eingefügt, weil der
Register-balancer zumindest damals bei der ISE nicht
viel getaugt hat.
Bei der Compilation wird gecheckt, dass alle Werte
korrekt bis zum letzten Bit gerundet sind; das heisst
der Fehler ist max. ein halbes Bit.
Die einzigen Beschwerden die bisher gekommen sind,
waren dass Leute es nicht geschafft haben ihren
Modelsim auf ps einzustellen.
Wenn man z.B. wirklich niedriges Phasenrauschen
braucht, dann muss man eben den Speicher opfern.
Wenn man Phasen/Frequenzmodulation/sweeps machen will,
dann bekommt man beim Cordic Latenz-Probleme.
Die Sinustabelle ist dieses Jahr bisher 139 mal runter-
geladen worden. Meist in Clustern. Ich vermute,
immer dann, wenn passende Hausaufgaben verteilt werden. :-)
Gruß, Gerhard
Klingt nicht schlecht, die hatte ich auch schon gefunden. Nur hat mich
das "Development status:Beta" abgeschreckt. Auch ist OC nicht die
präferrierte Quelle für Sourcen wegen der Rechtesituation dort. Wir
hatten schon echten Ärger mit Kunden und Kundeskunden, weil OC-SW
verwendet wurde. Wird in Aufträgen auch regelmäßig ausgeschlossen.
BSD ist doch letztlich "Mach damit was du willst, aber lass mich in
Ruhe und behaupte nicht, es wäre von dir."
Entgegenkommender kann man kaum sein.
Ja, ich wollte eigentlich noch erweitern auf BCD, damit man aus einer
100 MHz clock auch EXAKT und phasenstarr 0.999 MHz machen kann, aber
wenn nie eine Rückmeldung kommt, ausser wenn jemand Probleme mit
seinen Werkzeugen hat, dann verschwindet der Antrieb recht schnell.
Gruß, Gerhard
(BCD gestrichen, auf stable gesetzt)
Habe es mal versucht, zu analysieren und zu implementieren. Scheitere
aber an diese float_fixed_lib. Ist die aus dem Xilinx_Lib pool zu
ziehen?
Mit float möchte ich eigentlich nicht arbeiten.
Ich finde mich auch ehrlich gesagt nicht zurecht. Sind erstaunlich viele
files. Welche brauche ich davon denn?
Ich erkenne auch nicht, wo ich die Bitbreiten einstellen soll. Ich hätte
jetzt sowas wie generic erwartet.
?
Ah, Sonntag morgen um 00:45....
Float ist ein ganz normaler VHDL-Datentyp, und der wird nur
zur Compilationszeit auf deinem PC benutzt. Im FPGA landet
keinerlei Gleitkomma-Arithmetik. Die Funktion init_sintabxxxx()
macht zwar intern jede Menge Gleitkomma-Turnerei, aber was
sie letztendlich abliefert ist nur ein ROM-Image, mit dem man
ein konstantes Dingens vom Typ sintab initialisieren kann.
Und sie ist beim Compilieren geschwätzig.
VHDL ist eine Simulationssprache, von der ein Teil
synthetisierbar ist. VHDL kann alles was C oder Pascal
können. Die libraries sind halt beschissen (textio etc)
und es macht eher weniger Spaß. Das kotzt einen spätestens
an, wenn man aus Vorschriftswut kein wirkliches Äquivalent
zu printf() mit variabler Parameterzahl bekommt.
Die Vektor-Breiten ergeben sich automatisch daraus, was du
"von oberhalb" anschliesst. Da ist nix zu tun. Du musst es
nur im Context compilieren.
Wenn Du einen Bus mit 20 Bit Breite an den Ausgang anschliesst,
dann liefert die Tabelle eben 20 Bit. Du brauchst nicht sagen,
übrigens, wir brauchen 20 Bit. Das kann man schon an den
Attributen des Ausgangsbusses sehen. Es wird einfach das 'length
oder 'high-Attribut der angeschlossenen Busse benutzt.
Das passt dann von selbst.
Aaargh, ich hatte > 8 Jahre keinen Grund mehr, mir das anzuschauen.
Ich bemühe mich, mehr in analoge Raumfahrtelektronik abzudriften.
Aber auch da poppt schon mal ein Steinzeit-Virtex hoch, das
nach einer triple module redundancy library fragt.
Gute Nacht für heute!
Gerhard
Danke für die Ausführungen. Ist plausibel. Allerdings kriege ich es
nicht ins Laufen:
Alles importiert, ein Untermodul wurde nicht gefunden.
Verweise intern von work (obsolet) auf xil_defaultlib umgestellt (was
normal klappt), nichts geht mehr:
Braucht es das wirklich so genau? Mit einer Tabelle, die 16 Bit Phase
hat und sowohl SIN als auch COS enthält, kann man durch direktes
rechnerisches Interpolieren deutlich erhöhte Auflösungen erzielen, als
es scheint, in man einfach 2 Punkte ausliest und eine Kurve darüberlegt
(bis zu 4 Randbedingungen). Heutige Microprozessoren sind rechenstark
genug, um das zu leisten. Selbst im FPGA mache ich keine Sinustabellen
mehr, die größer sind. Im Gegenteil: Ich habe schon mit WaveTables
gearbeitet, die nur 12 Bit Phase hatten und die haben ja noch Oberwellen
drin.
Ein einfacher Sinus mit 11Bit Auflösung für die volle 4/4-Phase und nur
9 Bit Amplitude, unter Aufspaltung in 5 Phasen- und 6 Restbits bringt
selbst bei linearer Interpolation nur 0,6% Abweichung, wenn man es
richtig macht.
(effektiv eine Altera-freundliche Tabelle mit (16 9 Bit-Werten,
inklusive Offsettrickserei 8 Stützwerte!). Unter Ausnutzung eines
kompletten Block-RAMs mit voller Symmetrienutzung (11 Bit für die
1/4-Phase und 6 Bit für den Steigungskoeffizienten) sind es es nochmal
einen Faktor 10 besser.
Erhöht man die Amplitudenauflösung auf quasi artefaktfrei, ab z.B. 4
Bits mehr, reduziert sich der Fehler auf etwa die Hälfte, was aber nicht
wirklich besser ist, weil dann je nach erzeugter Frequenz die Werte in
den Tälern rhythmisch getroffen werden und sich der Fehler spektral
schlechter verteilt.
Noch besser wird es mit quadratischer Interpolation über 3 Punkte,
besser aber gleich 4 mit der doppelten RAM-Größe. Das ist im ersten
Schritt sogar effektiver, als die doppelte RAM-Größe in die Amplituden-
oder Phasenauflösung zu investieren.
Erst, wenn man das hat, lohnen mehr Bits in den Tabellen. Das scheint
vielen nicht so recht klar zu sein. Hatte schon mehrere erstaunte
Kunden, die trotz gut aufgelöster Sinus-Tabellen in ihren Generatoren
sehr mäßige Ergebnisse hatten im Vergleich zu meinen spezialisierten
MINI-Cores.
Wenn ich mir den Resourcenbedarf so ansehe, den der XI-Core da
erfordert, steht das in keinem Verhältnis zum Ergebnis.
Ist aber heute Designstrategie: Hauptsache schnell fertig, rein damit
und zufrieden sein. Chipfläche kostet ja nichts.
Gerhard H. schrieb:> Sunderland's Zerlegung
Danke für den Hinweis! Ich konnte über go...le einen Ausschnitt dieses
Buches finden und stieß dort auf eine Tabelle, wie man Sunderland
erzeugen können soll. Leider ist mir nicht so ganz klar, wie das gemeint
ist, denn der Text ist äußerst knapp. Ich finde es aber interessant und
habe einen thread dazu aufgemacht. Ich poste es im DSP-Forum, weil es
sich wohl am Besten für Software-Lösungen eigenen dürfte:
Beitrag "Bildung und Nutzung von Sunderland Tabellen für Sinus"
Ich habe auch heute nochmal gesucht und finde im Grunde immer wieder
dieselbe Tabelle auf die alle Bezug nehmen.
Die Kernfrage wäre, ob sich das skalieren ließe, als z.B. von 12 auf 24
Bit Eingangsbreite und ob (die wichtige Frage) sich damit die Auflösung
auch verbessert - nicht dass der Fehler relativ gleich bleibt.
Gerhard H. schrieb:> Sunderland's Zerlegung des ROMs
Die Methode sollte sich IMHO in der Gesamtschau bei der Qualität
heutiger Synthesewerkzeuge und Hardware erledigt haben. Das gilt
insbesondere für Tabellen in FPGAs, die in LUTs abgebildet sind.
Aufgrund der geringen BIT-Zahl kommt man da nicht mehr so gut bei raus,
sobald die erforderliche Bit-Breite mehr, als 2 LUT-Breiten sind. Eine
Abbildung im Block-RAM ist zielführender, weil schneller addressierbar
und am Ende kleiner und stromsparender, weil der Adress-MUX schon hart
implementiert ist. Und wenn man ein BRAM hat, sind 10kb-20kb das minimal
verfügbare.
Beitrag "Re: Bildung und Nutzung von Sunderland Tabellen für Sinus"
Für etwas rechenschwache Systeme wie NIOS oder MicroBlaze, die in sehr
kleinen FPGAs gebaut werden sollen, welche kaum BRAM haben, aber dennoch
hohe Durchsätze produzieren sollen, könnte das noch ein Thema sein.
Gerhard H. schrieb:> bis zum Dithering, um die entstehenden> spurs glattzurubbeln.
Mit dithering ist diesbezüglich aber nicht viel zu wollen. Das taugt
mehr, um die Auflösung in Y und X zu tunneln und so richtig wirkt das
dann, wenn derselbe Y-Wert oder Werte mit Bezug zueinender infolge
starrer Frequenz mehrfach ausgelesen werden. Das ist aber nur bei tiefen
Frequenzen der Fall / wirklich hilfreich.
Dithering behebt auch keine Interpolationsfehler und die sind bei der
Diskussion hier der Hauptfokus, denke ich. Um bei dem Thema DDS mit
groben Interpolationen klarzukommen, gibt es 3 Hauptstrategien:
1) Anpassung der Tabellenlänge, weg von binären Längen hin zu einer
Länge, welche entweder perfekt zur Zielfrequenz passt oder insofern dazu
passt, dass bei hohen Frequenzen nur gut filterbare Frequenzen entstehen
und nicht etwas tieffrequente Schwebungen.
2) Einteilung der Vektorunterteilung so, dass die Knicke so liegen, dass
ganzzahlige Vielfache der Grundwelle entstehen, also anhand der Phase
und nicht anhand der Amplitude. Bei meinem abschnittsweise definierten
Sinus verfahre ich so. Das produziert harmonische Oberwellen, die
passen.
3) Postfilterung unter Nutzung einer TAP-Zahl die zur Tabellenlänge
komplementär ist, also die Artefakte, die durch das Springen des
Phasenvektors entstehen, optimal filtert und nicht etwa interferiert,
was ich auch schon gesehen habe. Gerade schnell und damit einfache
FIR-Filter haben die unangenehme Eigenschaft, Oberwellen durchzulassen,
die nicht gut in die Filterlänge passen. Da sowohl Filterlänge, als auch
Tabellenlänge oft Binärwerte sind, haben beide genau an derselben
Stelle, also für die selben Frequenzen Stärken und für andere Schwächen.