Hallo liebe Forumsgemeinde,
ich habe leider ein Problem bzgl. einer weiteren Funktion meines
Generators.
Ein sehr gut funktionierendes Rechtecksignal ist schon drin (ja auch
eher eine einfache Übung, aber war mein Einstieg). Undzwar habe ich mir
einen 7-bit R2R-DAC gebaut und möchte nun eine Dreiecksfunktion
erstellen.
Jetzt habe ich nur das Problem, dass ich eine laut Simulation
funktionierende VHDL habe, diese (ich vermute durch den Teiler der
Auflösung des DAC's) aber sehr unzuverlässige Ergebnisse für die
Frequenz ausgibt (immer Abweichung von 100Hz ode mehr) . Das bisherige
Konstrukt funktioniert bisher so, dass ich einen Wert für die Frequenz
an den FPGA übergebe (50MHz Takt / Wunschfrequenz = Nötige Takte /
zweifache Auflösung (da aufsteigend und wieder Absteigendes Signal) =
Counterwert), der Counter so lang läuft und dann in einen je nach Status
aufsteigenden (weiterer Zähler der die Bits des DAC's schaltet) oder
absteigenden Zustandsautomaten geht. Also auf 127 rauf zählen und wieder
runter klappt super, nur die Zeit haut nicht hin.
Ich weis leider absolut nicht, wie ich mir einen relativ genauen
Dreieckgenerator bauen könnte bzw was ich an meiner VHDL abändern
müsste.. hat vielleicht jemand einen Tipp oder sowas schonmal umgesetzt?
Grüße Bucki
Z.B. bei einer "eingestellten Frequenz von 4 kHz". Das Problem ist, dass
ich ja keine Gleitkommazahlen an die VHDL übergeben kann und wenn ich
mir die nötige Zahl für den Counter berechne immer die Nachkommastellen
vernachlässige und mir somit einige Counter Ticks/CLK-Ticks fehlen. Bsp:
Gewollte Frequenz ist 4kHz und F_CLK_FPGA ist 50 MHz:
50.000.000Hz/4.000Hz = 12500 CLK Ticks --> 12500 / 256 = 48,828.. -->
convert to int für VHDL --> 48. Das wäre eine Periodendauer von
245,76us. Nun fehlen mir ja aber 0,828.. * 256 = 212 CLK-Ticks
(Periodendauer von 4,24us) die ich irgendwie in das gesamte mit einfügen
müsste.
Ich bräuchte also einen weiteren Eingang für die VHDL wo ich den modulo
Rest an diese weitergebe und irgendwie einen weiteren Counter der mir
asynchron immer mal den normalen Counter verzögert. Also das grobe Wie
ist mir bekannt, ich habe momentan nur noch keine Vorstellung für ein
passendes VHDL Konstrukt, um mir diese Funktion umzusetzen.
Grüße Bucki
Bucki E. schrieb:> ich habe momentan nur noch keine Vorstellung für ein passendes VHDL> Konstrukt, um mir diese Funktion umzusetzen.
Der Ansatz über den Teiler bringt bei höher werdenden Frequenzen immmer
mehr Granularität. Die "letzten" Sprünge wären so gesehen ja 25MHz und
12,5MHz...
Sieh dir mal das Thema DDFS an. Damit werden üblicherweise solche
Kurvenformen erzeugt, und damit hast du das "Teilerproblem" nicht,
sondern du tauscht es gegen einen gewissen Jitter ein:
http://www.lothar-miller.de/s9y/categories/31-DDFS
Genauso leicht wie den Sinus dort kannst du natürlich ein Dreieck ins
ROM ablegen. Oder sonstwas...
Lothar M. schrieb:> Genauso leicht wie den Sinus dort kannst du natürlich ein Dreieck ins> ROM ablegen. Oder sonstwas...
Das macht aber keinen Sinn, bzw bringt keinen Vorteil. Der Wert des
Dreiecks lässt sich aus der Phase eindeutig ermitteln. Den Sinus legt
man - wie andere komplexe Wellenformen auch - nur deshalb als Tabelle
ab, weil man ihn nicht berechnen möchte.
Der Aspekt der genauen Frequenz, der durch die DDS-Methodik gelöst wird,
bleibt davon unberührt. Es braucht nur den hoch aufgelösten Phasen-Akku.
Bleibt die Thematik der Bandbreitenbegrenzung des DAC und des
AA-Filters: Um da ein möglichst sauberes Dreieck rauszubekommen, muss
man Amplitude und Phase frequenzabhängig vorverzerren.
Ansonsten bliebe noch die direkte Erzeugung eines DSD per PDM:
Beitrag "Re: Zweiklang Ton erzeugen - direkte Ausgabe per PWM"
Jürgen S. schrieb:> Der Wert des Dreiecks lässt sich aus der Phase eindeutig ermitteln.
Natürlich kann man das Dreieck auch einfach mit einem Zähler
realisieren. Man muss ja eigentlich nur den Akku etwas breiter machen...
Mir ging es hier vorrangig darum, zu zeigen, dass man "feine" Auflösung
nicht per binären Vorteiler macht.
Der Rest der Signalverarbeitung sollte logischerweise die höheren
Frequenzen, die ein "Nichtsinussignal" hat, verarbeiten können.
> Das macht aber keinen Sinn, bzw bringt keinen Vorteil.
Langfristig nicht. Aber kurzfristig bekommt man nach Schema F das Ding
schnell ans Laufen und kann sich das dann mal in Ruhe anschauen und auf
Optimierungen abklopfen.
Und die nächste Kurvenform, die gefragt wird, ist dann sicher schon der
Sinus... ?
Lothar M. schrieb:> Mir ging es hier vorrangig darum, zu zeigen, dass man "feine" Auflösung> nicht per binären Vorteiler macht.
Huch, da fällt mir eine Webseite ein, auf der eine DDS präsentiert wird,
die einen solchen Vorteiler hat. Muss ich nochmal schauen, wo das war ..
:-)
> Und die nächste Kurvenform, die gefragt wird, ist dann sicher schon der> Sinus... ?
Das ist allerdings wahrscheinlich ja.
Und dann kommt wavetable synthesis, sowie die Frage nach
formantenneutraler Überblendung. Und danach kommt WSOLA.
Danke euch beiden!
Ich werde mir mal was zum Thema DDFS durchlesen! Für den Dreieck werde
ich aber wohl den Ansatz des Counters erstmal weiter verfolgen, da ich
bisher eine max. Frequenz von 40 kHz ausgeben möchte. (Es ist "nur" ein
Projekt für ein Fach im Studium und mit Rechteck wäre die Note
eigentlich schon gegessen, ich möchte aber gern die weiteren
Möglichkeiten eines FPGA's in Hinsicht auf Signalerzeugung erkunden )
Und wie auch richtig erraten, soll Sinus als letzte Form auch noch
implementiert werden. :-D Aber da hatte ich mich schon vorher etwas
erkundigt und bin schon auf das Thema der LUT gestoßen. Demnach könnte
ich ja ( da dieser auch "nur" bis 40kHz) das Dreieckkonstrukt verwenden
und das signal "steps" nicht als Zähler an und für sich implementieren,
sondern als Laufvariable für die LUT um dann den jeweiligen Wert
auszugeben. Wie ihr seht wollte ich das erstmal etwas einfacher halten,
da dieses Projekt auch mein erster Berührungspunkt mit FPGA's und VHDL
ist. Man wächst ja an seinen Aufgaben. :)
Grüße Bucki
Danke Jürgen für die Verlinkungen!
Nach doch mal rantrauen an die DDS und einfach mal durchsimulieren ist
das Ganze gar nicht mal so schwer und sogar recht verständlich.
Als Grundlage dafür habe ich den Code von Lothar Miller zu Rande
gezogen. Etwas sehr spärlich kommentiert, aber nach der Simulation war
soweit alles klar. Nun läuft zumindest in der Simulation das Dreieck
(ohne LUT) und der Sinus (mit LUT) super durch. Bei ungünstigem
Teilerverhältnis habe ich zwar noch bis zu 11 Hz Abweichung, aber das
tut bei eingestellten 37,52kHz nicht mehr viel zur Sache, da das ganze
"nur" ein Projekt für ein Modul im Studium ist.
Jetzt habe ich "nur noch" das Problem, dass beim Einbinden der Sinus LUT
bei Altium Designer mir eine Fehlermeldung ausgespuckt wird, dass das
Modul keine Last treiben würde und dadurch wegoptimiert wird. Hatte
jemand jemals schonmal so eine Meldung bei sich? Laut Synthesetool und
Simulation läuft eigentlich alles durch, nur den Bit File will er mir
damit nicht generieren... kann es sein, dass der BRAM zu ungünstig auf
dem Chip liegt?
Grüße
Bucki
Bucki E. schrieb:> Als Grundlage dafür habe ich den Code von Lothar Miller zu Rande> gezogen. Etwas sehr spärlich kommentiert
Das soll das Mitdenken anregen... ?
> dass beim Einbinden der Sinus LUT bei Altium Designer mir eine> Fehlermeldung ausgespuckt wird, dass das Modul keine Last treiben würde> und dadurch wegoptimiert wird
Kommt nur diese eine Meldung? Oder kommen vorher schon Warnungen, die
sich auf diese Ecke beziehen? Und mit welchem Code genau? Und wie sieht
die "Einbindung" aus?
-- for example at the change of "Half_of_Wave" 0 to 1 Phase Accumulator equals 0111 1111 10...., so the 'high equals the MSB, that minus 1 to MSB - 7
35
-- equals the part "111 1111" and is 127 in decimal --> so the last value of the Triangular
36
-- for better understanding use ModelSim and simulate all values
37
38
Dout<=b"000_0000_0000"&std_logic_vector(Result);-- resize the vector for the 19 downto 2 User Header
39
endBehavioral;
Beim Sinus bekomme ich leider folgende zwei Meldungen:
"ERROR:PhysDesignRules:368 - the signal
<U_sine_wave_generator/ram_255/clk> is incomplete. The signal is not
driven by any source pin in your design."
"ERROR:PhysDesignRules:10 - the network
<U_sine_wave_generator/ram_255/clk> is completly unrouted."
1
entityLUT_Drivenis
2
Port(CLK:instd_logic;
3
EN:instd_logic;
4
RST:instd_logic;
5
Freq_Data:instd_logic_vector(18downto0);
6
Dout:outstd_logic_vector(7downto0)
7
);
8
endLUT_Driven;
9
10
architectureBehavioralofLUT_Drivenis
11
12
signalResult:unsigned(7downto0);-- chosen Value for DAC output
signalAddress:integerrange0to63;-- address of the LUT
15
signalLUTAddr:integerrange0to63;
16
signalHalf_of_Wave:std_logic;-- left side of the halvwave or right side of the half wave
17
signalPos_Neg_Wave:std_logic;-- positive or negative halfwave
18
19
typeLUT64x7isarray(0to63)ofunsigned(7downto0);
20
21
constantLUT_Rom:LUT64x7:=(
22
x"40",x"41",x"43",x"44",x"46",x"47",x"49",x"4B",
23
x"4C",x"4E",x"4F",x"51",x"52",x"54",x"55",x"57",
24
x"58",x"5A",x"5B",x"5C",x"5E",x"5F",x"61",x"62",
25
x"63",x"65",x"66",x"67",x"68",x"6A",x"6B",x"6C",
26
x"6D",x"6E",x"6F",x"70",x"71",x"72",x"73",x"74",
27
x"75",x"76",x"76",x"77",x"78",x"79",x"79",x"7A",
28
x"7B",x"7B",x"7C",x"7C",x"7D",x"7D",x"7D",x"7E",
29
x"7E",x"7E",x"7F",x"7F",x"7F",x"7F",x"7F",x"7F");
30
31
begin
32
33
clocked_data:process(CLK)
34
begin
35
ifrising_edge(CLK)then
36
ifRST<='0'andEN<='1'then
37
LUTAddr<=Address;-- clocked adress for BRAM
38
Phase_Accumulator<=Phase_Accumulator+unsigned(Freq_Data);-- for more information look for "DDS - Direct Digital Synthesis"
39
Pos_Neg_Wave<=Phase_Accumulator(Phase_Accumulator'left);-- look at the MSB of Phase Accumulator, if 1000 0000 ... the halfwave is reached --> sign change
40
else
41
Phase_Accumulator<=(28=>'1',others=>'0');
42
endif;
43
endif;
44
endprocess;
45
46
Result<=unsigned(LUT_Rom(LUTAddr));-- look in the LUT for the actual DAC value
47
Half_of_Wave<=Phase_Accumulator(Phase_Accumulator'left-1);-- look at MSB - 1 --> if 0100 0000 ..... the quarter wave is reached --> calculate direction changes
Oh durchaus.. doof wenn man nicht lesen kann.. :D Aber hier steht auch
Verschiedenstes auf den Seiten. ^^ Nur bei Augenmerk auf die
Antworten-Schaltfläche und ich hätte das Richtige rausgefunden.. haha
Aber nochmal zum Problem: Heute hab ich es wieder versucht aber die
Block RAM Methode funktioniert leider genausowenig wie das Array als
Distributed RAM auszuführen. (Ähnlich dem Beispiel von Lothar Miller)
Leider komme ich erst am Mittwoch wieder an das Board, wenn bis dahin
keiner einen eventuell guten Vorschlag für mich hätte werde ich es mal
mit einer "direkten LUT" probieren. Habe zumindest gerade die VHDL mit
einem "With select"-Statement umgeschrieben und auch dies erfolgreich
Simuliert. Vielleicht mag er es eher als normale LUT eingebunden haben
... nur werde ich sonst aus der Fehlermeldung nicht weiter schlau... :/
Grüße
Bucki
Bucki E. schrieb:> Aber nochmal zum Problem
Das ist mit hoher Wahrscheinlichkeit in dieser Zeile:
1
ifRST<='0'andEN<='1'then
Sind da nicht zwei < zu viel? Was kommt bei einem "kleiner-gleich"
Vergleich mit einem einzelnen std_logic heraus?
Offenbar kommt der Synthesizer zum Schluss, dass immer der else-Zweig
aktiv ist und deshalb die ganze Takterei wegoptimiert werden kann.
Aus der Beschreibung lese ich das aber nicht ab. Es müsste den Fall RST
= 1 und EN = 1 schon geben.
Aber ist das überhaupt noch ein Vergleich? Weist er nicht einfach die
Signale zu und legt RST auf 1, was dann andere Schaltungsteile
wegoptimieren lässt?
Rolf S. schrieb:> Aus der Beschreibung lese ich das aber nicht ab.
Ein Problem ist hier, dass wieder die Zeilen vor "entity" fehlen.
Deshalb kann ich es auch nur vermuten. Aber in allen derartigen Fällen,
die mir bisher untergekommen war das nicht gewollt und der Fehler.
Mir erscheint auch nicht sinnvoll, den neuwertigen std_logic irgendwie
zu vergleichen. Wie soll denn 0 mit H oder X oder Z verglichen werden?
> Weist er nicht einfach die Signale zu
Niemals an dieser Stelle. Wir sind hier nicht bei C, wo man jederzeit
irgendwas zuweisen kann, und deshalb für einen Vergleich einen eigenen
Operator hat.
Bucki E. schrieb:> Phase_Accumulator <= (29 => '1', others => '0');
Du machst das zu komplziert. Z.B. das händische Umklappen des Signums
mit dem Phasenakku ist vollkommen unnötig. Es reicht:
1) Akku = Akku + Frequenz und dann
2) Wert = Akku - Range/2 bzw -Akku + 3* Range/2
mit dem höchsten Akku-bit als Entscheidung / Signum und dem Range/2 als
Offset zur Verschiebung auf 0.
Du produzierst auch kein gutes Dreieck, weil einige Werte doppelt
vorkommen. Der Lauf ist in etwa der 0,1,2 .... 126,127, 127, 126 ....
Besser bei den kleinen Werten ist 0,1, ....126,127 128,127,126 ....
2,1 loop , d.h. der Rücklauf ist um 1 erhöht.
Bei großen Akku/Amplituden-Werten ist das aus Symmetriegründen besser,
allerdings sollte dann der Phasenvektor um 1 erhöht aufgelöst gerechnet
werden, wegen dem "0,5 - Problem". Dann kann man die Phasenwerte auch
direkt auf Oberwellen umrechnen.
Bucki E. schrieb:> Aber nochmal zum Problem: Heute hab ich es wieder versucht aber die> Block RAM Methode funktioniert leider genausowenig wie das Array als> Distributed RAM auszuführen. (Ähnlich dem Beispiel von Lothar Miller)
Was soll daran nicht gehen? Die oberen Bits des Akkus sind die Adresse.
Der Rest ist Phasendrehung und Amplitudendrehung für die 4 Fälle der
90-Grad-Bereiche einer typischen Funktion.
Wenn das Dreieck dann irgendwann mal funzt, ist es an der Zeit, mehrere
davon zu nehmen und zu einem Sinus zusammenzubauen.
Man nehme die Fourieranalyse des Dreiecks und ziehe weitere Dreiecke
mit entsprechender Frequenz so ab, dass sich die Oberwellen eliminieren.
Das Spektrum wäre also d1: 1(f) - (3f)/9 + (5f)/25 - (7f)/49 +
(9f)/81 ... (15f)
und die erste Vielfache d3: + (3f)/9 -
(9f)/81/9 ... (15f) ... (21f)
die folgende zweite V. d5: - (5f)/25
(15f) (25f)
d.h. man kann die nächsten Wellen mit ihren Fourieranteilen skalieren,
um sie abzuziehen, muss aber aufpassen, was sie selber wieder an
Oberwellen erzeugen und die mit den schon vorhandenen Verrechnen.
Mit der 7. Oberwelle bekommt man das Bild oben, das noch Knicke zeigt.
Die rote Kurve ist der um Faktor 10 verstärkte Fehler zum Sinus. Sind 2%
maximal. Mit der 15. Oberwelle erhalte ich einen Sinus besser, als 0,1%.
Man kann das Ganze weiter verbessern, wenn man mit verschobenen
Dreiecken arbeitet. Dann entstehen andere Fourierkoeffizienten, die es
erlauben mit weniger Operationen zu arbeiten, produzieren aber auch
geradzahlige Komponenten. Diese sind musikalisch günstiger, weil sie in
die Oktaven gehen. Daher arbeitete einer meiner ersten Synths mit
parabolisch erzeugen Sinuswellen.
Danke nochmal für eure vielen Hinweise!
Das Problem hat sich aber glücklicherweise mit meiner Lösung
verabschiedet. Anscheinend hat das Board/Altium Designer wirklich das
Problem, dass es keinen freien BRAM mehr hatte (obwohl der Endreport was
anderes sagte, noch 50% frei). Ich habe das Ganze nun mit dem
with-select Statement realisiert wo die Software dies auch direkt als
LUT erkannt hat.
Meine Lösung sieht nun Schlussendlich so aus:
1
entitySin_Wave_Generatoris
2
Port(CLK:instd_logic;
3
EN:instd_logic;
4
RST:instd_logic;
5
Freq_Data:instd_logic_vector(18downto0);
6
Dout:outstd_logic_vector(19downto2)
7
);
8
endSin_Wave_Generator;
9
10
architectureBehavioralofSin_Wave_Generatoris
11
12
signalData:unsigned(7downto0);
13
signalAddress:unsigned(7downto0);
14
signalResult:unsigned(7downto0);-- chosen Value for DAC output
-- for example at the change of "Half_of_Wave" 0 to 1 Phase Accumulator equals 0111 1111 10...., so the 'high equals the MSB, that minus 1 to MSB - 7
64
-- equals the part "111 1111" and is 127 in decimal --> so the last value of the Triangular
65
-- for better understanding use ModelSim and simulate all values
66
Data<=ResultwhenHalf_of_Wave='0'else89-Result;
67
68
Dout<=b"00_0000_0000"&std_logic_vector(Data);-- resize the vector for the 19 downto 2 User Header
69
70
endBehavioral;
Der Sinus sieht um den Nullpunkt nur relativ schlecht aus, da ich beim
Conrad um die Ecke nur an 5% Widerstände ran kam... dient ja eher den
Demonstrationszwecken fürs Modul.
Danke nochmal an alle, von mir aus kann der Thread gern geschlossen
werden.
Grüße,
Bucki
Bucki E. schrieb:> Anscheinend hat das Board/Altium Designer wirklich das> Problem, dass es keinen freien BRAM mehr hatte
Du baust FPGA mit Altium Designer? Seit wann kann das VHDL-Code
übersetzen?
Bucki E. schrieb:> 50.000.000Hz/4.000Hz = 12500 CLK Ticks --> 12500 / 256 = 48,828.
Aussergewöhnliche Frequenz. Warum setzt die halbe Welt eigentlich diesen
krummen Wert ein? Ist das ein neuer Standard?
Jürgen S. schrieb:> Wenn das Dreieck dann irgendwann mal funzt, ist es an der Zeit, mehrere> davon zu nehmen und zu einem Sinus zusammenzubauen.
Mit welcher Begründung soll man eine Sinusfunktion mit Dreiecken
erzeugen wollen? Das bringt nur Fehler, die sich mit eine einfache
Tabelle meiden lassen. Und warum so umständlich? Warum nicht einfach
gerade Abschnitte?
Hallo Victor!
Altium hat mal selbst Eval Boards verkauft, wir nutzen in der Hochschule
das Altium NanoBoard 2. Und zumindest beim Altium Designer 14 und 16
kann man damit auch VHDL Codes synthetisieren und co. Ob das Programm
dazu ein externes Tool nutzt kann ich aber nicht sagen, das läuft im
Hintergrund ab.
Und zur Frequenz, dass ist die vom Board zur Verfügung gestellte. Ich
hätte natürlich auch einen CLK Divider von 5 nehmen können und hätte
super Werte, dabei würde ich aber bei der Auflösung des Sinus etc
eingeschränkt werden.
Grüße
Bucki
Bucki E. schrieb:> Und zumindest beim Altium Designer 14 und 16> kann man damit auch VHDL Codes synthetisieren und co.
Aktium ist der Nachfolger von Protel und da war schon in den 90ern die
Entwicklung von PLDs gut ins Tool integriert, hieß "Protel Advanced
PLD".
Man hat z.B. ein digitales Gatter platziert ohne vorab Rücksicht darauf
zu nehmen, ob es in ein reales 7400er Gehäuse wandert oder in ein PLD.
Ich fand das eigentlich ziemlich gelungen. Wenn Du das heute machen
willst, suchst du bei anderen tools vergeblich. Bei Mentor z.B. kriegst
du design-Fragmente nicht einfach so zwischen HDL-Designer und Board
Architect hin und her geschoben.
Victor schrieb:> Bucki E. schrieb:>> 50.000.000Hz/4.000Hz = 12500 CLK Ticks --> 12500 / 256 = 48,828.> Aussergewöhnliche Frequenz. Warum setzt die halbe Welt eigentlich diesen> krummen Wert ein? Ist das ein neuer Standard?
Ich glaube, dass ist nur eine Vereinfachung. Vielleicht deshalb, weil
viele heute mit der eigentlichen Digitaltechnik nicht mehr viel zu tun
haben und es verlernt haben, ein bisschen trickreich zu denken :-)
Ich habe in wenigstens 10 verschiedenen Foren in den letzten Dekaden
schon meine Lösung gepostet, die da lautet 29/59 und die aus den
25MHz-Quarzen und ihren Freunden fast perfekte S/PDIF Frequenzen
liefert. Ausgehend von den 192000Hz kriegt man alles, was man möchte.
Bei FPGAs die Zwischenoszillatorfrequenzen von 1,5GHz verkraften, kann
man mit 50Meg direkt 384kHz produzieren.
> Mit welcher Begründung soll man eine Sinusfunktion mit Dreiecken> erzeugen wollen?
- Die Einteilung in eine ganzzahlige Anzahl von Abschnitten erzeugt
musikalisch wertvolle Oberwellen auf den Oktaven
- Die Benutzung von Dreiecksfunktionen lässt es zu deren Phase zu
verschieben und wandernde Oberwellenprofile zu generieren.