Forum: FPGA, VHDL & Co. [VHDL] Daten von ADC in BRAM speichern


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Steffen H. (avrsteffen)



Lesenswert?

Hallo Leute

Wie der Beitragsnahme schon verrät möchte ich einen ADC mit einem FPGA 
auslesen und die Daten des ADC im BRAM speichern.

Die Daten des ADC liegen bei der steigenden Flanke des sys_CLK's am 
Eingang des FPGA bereit. Gesteuert soll der FPGA von einem XMega werden.

Der Start der Aufzeichnung der ADC-Daten soll durch eine steigende 
Flanke von "RUN" erfolgen. Die Aufzeichnugsgeschwindigkeit richtet sich 
nach dem variabelen Clock "var_CLK".
In einer State Maschine sollen die zum Schreiben der Daten in den BRAM 
erforderlichen Control-Signale wie die Schreib-Adresse, Write-EN und der 
Write-CLK erzeugt werden.
Ich hab das in eine FSM gepackt, weil ich die Daten nicht einfach 
nacheinander in das BRAM schreiben will wie man es bei einem FIFO macht. 
Deswegen hab ich auch eine PreTrigger-Adresse. Soll heißen: Bis zur 
PreTrigger-Adresse sollen die Daten wie in einem Ringspeicher 
aufgezeichnet werden und wenn das Trigger-Signal kommt wird die 
momentane (also die letzte Adresse im Ringspeicher vor dem 
Trigger-Signal) in "s_trig_addr" kopiert.
Siehe:
1
elsif NEXT_STATE_WR = STS_ADR_RD then
2
  s_wr_clk <= '1';
3
  s_wr_en <= '1';
4
  s_trig_addr <= s_wr_addr;
5
  s_wr_addr <= to_integer(unsigned(PRE_TRIGGER)) +1;
6
  s_trigger <= '1';
Ab da sollen die neuen Daten des Triggerzeitpunktes ab der 
PreTrigger-Adresse bis zur End-Adresse ("trace_length") in den BRAM 
gespeichert werden.
1
elsif NEXT_STATE_WR = WR_ADR_POST then
2
3
    if s_wr_addr = trace_length then
4
  s_wr_rdy <= '1';
5
  s_trig_en <= '0';
6
    else  
7
  s_wr_addr <= s_wr_addr +1;
8
  s_wr_clk <= '1';
9
  s_wr_en <= '1';
10
    end if;
11
end if;

Kann man das so wie ich das hier versucht habe zu lösen machen, oder 
gibt es da elegantere Lösungen?

Ich frage desshalb, da es nicht richtig stabil läuft..

Wäre nett, wenn mal jemand über den VHDL Code schauen könnte.

Grüße
Steffen

von Christian R. (supachris)


Lesenswert?

Hm, das macht man nicht so. Leg den Takt (konstant und mit wenig Jitter 
direkt vom Quarzoszillator) an den ADC und an einen CLK Eingang des 
FPGA. Dann intern den CLK dauerhaft an den BRAM. Gesteuert wird das 
ganze dann mit dem WE Signal und einem Adresszähler. Und variable 
Abtastrate macht man, indem man nicht alle Samples in den RAM schreibt, 
sondern jedes 2. jedes 4. usw. denn schnelle ADCs mögen es nicht, wenn 
sie (viel) langsamer als ausgelegt getaktet werden. Und ein variabler 
Takt für einen ADC bringt immer Jitter mit sich, was sich in digitalem 
Rauschen auswirkt. Übrigens kann man einen Pretrigger auch ganz einfach 
mit einem FIFO machen. Einfach reinschreiben, und wenn Pretrigger-Anzahl 
erreicht, mit jedem Schreiben ein Wort auslesen, dann hast du immer die 
letzten x Samples drin. Wenn ein Trigger kommt, das Auslesen abstellen. 
Aber so wie du es machen willst, gehts auch.

von Steffen H. (avrsteffen)


Lesenswert?

Hallo Christian

Also der ADC läuft kontinuierlich mit 50Mhz. Der bekommt keinen variblen 
Takt ab.
Ich habe nur einen 25Mhz Oszillator am FPGA. Mit der PLL takte ich im 
FPGA den Takt auf 50Mhz und gebe den auch über einen Pin des FPGA an den 
ADC aus.

Der variable Takt ist ja von dem internen systemTakt abgeleitet. Dieser 
soll ja die Speichergeschwindigkeit bestimmen. Also generier ich in 
Abhängigkeit dieses variablen Taktes die Adresse sowie den WrClockEN des 
BRAM's.
Wie man im Timing des BRAM's sehen kann man doch mit WrClockEN das 
schreiben steuern, oder täusch ich mich da? An den WrClock des BRAM's 
geht ja der SystemClock dran und das sind ja 50Mhz.

Was mir jetzt gerade auffällt ist der "WE" !

Warum ist der im BlockSchaltBild und nicht im Timingdiagramm wieder zu 
finden? Ich dachte ja eigentlich auch dass man damit lesen/schreiben 
steuert. Aber ich hab ja jeweils einen WrClockEN und einen RdClockEN. 
???

Jetzt bin ich schon etwas durcheinander.

Wozu soll der "WE" denn sein?

Grüße Steffen

von Duke Scarring (Gast)


Lesenswert?

Steffen H. schrieb:
> Also der ADC läuft kontinuierlich mit 50Mhz. Der bekommt keinen variblen
> Takt ab.
> Ich habe nur einen 25Mhz Oszillator am FPGA
Damit hast Du zwei Taktquellen und zwei verschiedene Taktdomänen.
Stichwort: Clock Domain Crossing

Selbst wenn Du aus den 25 MHz im FPGA 50 MHz machst, sind die anders 
(Frequenz, Phasenlage) als die 50 MHz des ADCs.
Für ein synchrones Design würde ich den Takt, den der ADC bekommt auch 
im FPGA verwenden (einfache Lösung). Andernfalls muß da ein asynchrones 
FIFO dazwischen und Du mußt damit rechen, daß der ADC seine Daten 
vielleicht mit 50,00001 MHz liefert, die der FPGA nur mit 49,99995 MHz 
ausliest und damit der FIFO irgendwann voll ist (komplexere Lösung).

Duke

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


Lesenswert?

Duke Scarring schrieb:
> Damit hast Du zwei Taktquellen und zwei verschiedene Taktdomänen.
> Stichwort: Clock Domain Crossing
>
> Selbst wenn Du aus den 25 MHz im FPGA 50 MHz machst, sind die anders
> (Frequenz, Phasenlage) als die 50 MHz des ADCs.
Wenn die 50MHz aus den 25MHz via DCM erzeugt werden, kann das recht gut 
unter Kontrolle gehalten und definiert werden, weil das FPGA seinen 
Taktmanager ja persönlich kennt. Da kann das Taktdomänenproblem 
eigentlich nicht auftreten.
Aber auch ich würde hier eher das gesamte Design auf 50MHz laufen 
lassen. Niemals wird ein Takt für eine Komponente (BRAM) aus der 
Kombinatorik erzeugt:
>>>  s_wr_clk <= '1';  <<<
Der Takt liegt einfach dauernd an, das ClockEnable am DPRAM dient 
eigentlich nur dem Stromsparen und bleibt auch dauernd an. Das Schreiben 
wird nur durch das WE gesteuert.

> Du mußt damit rechen, daß der ADC seine Daten vielleicht mit
> 50,00001 MHz liefert, die der FPGA nur mit 49,99995 MHz ausliest
Sicher?
Da würde die DLL aber sehr schlecht einrasten...

von Duke Scarring (Gast)


Lesenswert?

Lothar Miller schrieb:
>> 50,00001 MHz liefert, die der FPGA nur mit 49,99995 MHz ausliest
> Sicher?
> Da würde die DLL aber sehr schlecht einrasten...
Das war ein Bespiel. Je näher die beiden Takte aneinanderliegen, desto 
seltener treten ja die Probleme auf. Und umso schwerer sind sie zu 
detektieren und zu debuggen... (BTDT)

Duke

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


Lesenswert?

Duke Scarring schrieb:
> Je näher die beiden Takte aneinanderliegen, desto
> seltener treten ja die Probleme auf.
Hast du da einen DCM zum verdoppeln verwendet?

Mit 2 beliebigen Takten taucht der Fehler natürlich immer 1 Tag vor der 
Auslieferung auf...  :-/
(BZW: der Fehler ist natürlich schon vorher aufgetaucht, wurde aber 
ignoriert...)

von Steffen H. (avrsteffen)


Lesenswert?

Hallo Duke

>> Also der ADC läuft kontinuierlich mit 50Mhz. Der bekommt keinen variblen
>> Takt ab.
>> Ich habe nur einen 25Mhz Oszillator am FPGA
>Damit hast Du zwei Taktquellen und zwei verschiedene Taktdomänen.
>Stichwort: Clock Domain Crossing
Warum hab ich da zwei Taktquellen? Wie Lothar es richtig erkannt hat 
wird der Haupttakt = sys_CLK nur aus den externen 25MHz gewonnen und per 
PLL im FPGA verdoppelt. Dies ist doch eigentlich mein Hauptakt im 
gesamten FPGA, also mein System-Takt.

>Für ein synchrones Design würde ich den Takt, den der ADC bekommt auch
>im FPGA verwenden (einfache Lösung).
Der ADC bekommt doch den selben Takt ab wie er im FPGA verwendet wird. 
Also folgendes:
ext.25MHz->in_FPGA->FPGA_PLL->50MHz(sys_CLK)->Systen-Clock in gesamten 
Design
                            ->50MHz(sys_CLK)->FPGA_out->ADC_CLK

>Selbst wenn Du aus den 25 MHz im FPGA 50 MHz machst, sind die anders
>(Frequenz, Phasenlage) als die 50 MHz des ADCs.
Warum ist das so? Hat der interne Takt(sys_CLK) wenn ich den über einen 
Port nach aussen führe eine Verzögerung?

Grüße Steffen

von Steffen H. (avrsteffen)


Lesenswert?

Lothar Miller schrieb:
>Aber auch ich würde hier eher das gesamte Design auf 50MHz laufen
>lassen. Niemals wird ein Takt für eine Komponente (BRAM) aus der
>Kombinatorik erzeugt:
>>>  s_wr_clk <= '1';  <<<
>Der Takt liegt einfach dauernd an, das ClockEnable am DPRAM dient
>eigentlich nur dem Stromsparen und bleibt auch dauernd an. Das Schreiben
>wird nur durch das WE gesteuert.
Also sollte ich lieber den BRAM folgendermaßen mit der Logic verbinden?
>>(LOGIC) sysCLK   -> WrClock   (BRAM)
>>(LOGIC) '1'      -> WrClockEN (BRAM)
>>(LOGIC) WR_en    -> WE        (BRAM)
Das muss ich doch unbedingt mal ausprobieren.

von Steffen H. (avrsteffen)


Angehängte Dateien:

Lesenswert?

Hallo

Hab jetzt mal 3 Varianten ausprobiert.

Variante 1: (ursprüngliche Version)
>>(LOGIC) sysCLK   -> WrClock   (BRAM)
>>(LOGIC) wr_clk   -> WrClockEN (BRAM)
>>(LOGIC) WR_en    -> WE        (BRAM)

>>> sysCLK: permanent 50Mhz
>>> wr_clk: WR-Impuls von einer Periode der 50Mhz zur Datenübernahme,
            Pausendauer zwischen den Impulsen variert je nach Auf-
            zeichnungsgeschwindigkeit
>>> WR_en:  geht am Beginn der Aufzeichnung auf '1' und erst am Ende der
            Aufzeichnung wieder auf '0'

Ergebnis:
>> Daten landen nicht immer richtig im BRAM


Variante 2: (WE permanent auf 'High')
>>(LOGIC) sysCLK   -> WrClock   (BRAM)
>>(LOGIC) wr_clk   -> WrClockEN (BRAM)
>>(LOGIC) '1'      -> WE        (BRAM)

>>> sysCLK: permanent 50Mhz
>>> wr_clk: WR-Impuls von einer Periode der 50Mhz zur Datenübernahme,
            Pausendauer zwischen den Impulsen variert je nach Auf-
            zeichnungsgeschwindigkeit
>>> WE:     permanent auf 'High' Pegel

Ergebnis:
>> Daten landen richtig im BRAM


Variante 2: (WrClockEN permanent auf 'High')
>>(LOGIC) sysCLK   -> WrClock   (BRAM)
>>(LOGIC) '1'      -> WrClockEN (BRAM)
>>(LOGIC) WR_en    -> WE        (BRAM)

>>> sysCLK: permanent 50Mhz
>>> wr_clk: permanent auf 'High' Pegel
>>> WR_en:  WR-Impuls von einer Periode der 50Mhz zur Datenübernahme,
            Pausendauer zwischen den Impulsen variert je nach Auf-
            zeichnungsgeschwindigkeit

Ergebnis:
>> Daten landen richtig im BRAM (gleiches Ergebnis wie Variante2)

Jetzt frag ich mich wirklich ernsthaft wozu "WE" am generierten
pseudo-DP-RAM gut ist!?
Wie schon gesagt, ich habe getrennte WrClockEN und RdClockEN :/

Ich häng nochmal das Timing und das Blockbild dran.


Steffen

von Duke Scarring (Gast)


Lesenswert?

Steffen H. schrieb:
> Hab jetzt mal 3 Varianten ausprobiert.
Wo? Im Simulator, oder auf dem Board?

Duke

von Steffen H. (avrsteffen)


Lesenswert?

Hallo Duke

Ich hab es live mit dem Board probiert. Und nach mehrmaligen 
durchprobieren der einzelnen Versionen gab es nur eine leichte 
Verbesserung der Version2 und Version3 zur Version1. Es sind aber immer 
noch aussetzer drin! Woher die auch immer kommen :/
1
  s_trig_addr <= s_wr_addr;
2
  s_wr_addr <= to_integer(unsigned(PRE_TRIGGER)) +1;
Kann man die Schreib-Adresse, so wie ich es gemacht habe, einfach in 
"s_trig_addr" zwischenspeichern und dann die selbige neu initialisieren? 
Oder gibt es da eventuell Probleme?


Doch wie gesagt, ich versteh den Sinn von WE am pseudo-Dual-Port-RAM 
nicht! Der WE ist ja auch im Timing-Diagramm gar nicht mit aufgenommen. 
Allerdings muss WE zum schreiben einen "High"-Pegel haben. Das hab ich 
getestet.

Hab mir auch mal die Variante von Christin durch den Kopf gehen lassen 
mit dem FIFO.
>>Christian schrieb:
>Übrigens kann man einen Pretrigger auch ganz einfach
>mit einem FIFO machen. Einfach reinschreiben, und wenn Pretrigger-Anzahl
>erreicht, mit jedem Schreiben ein Wort auslesen, dann hast du immer die
>letzten x Samples drin. Wenn ein Trigger kommt, das Auslesen abstellen.
Ich denke mir mal, das ab einer bestimmten Adresse(X) durch das 
gleichzeitige Lesen des FIFO's ja nicht alle Adressen automatisch 
nachrutschen. Oder wie soll das dann ablaufen?

Steffen

von Steffen H. (avrsteffen)


Lesenswert?

Hab ich da tatsächlich Christin geschrieben?! Ich meint natürlich 
"Christian" :)

von Christian R. (supachris)


Lesenswert?

Klar geht das. Du schreibst meinetwegen 2000 Worte rein, ohne 
auszulesen. Ab da liest du bei jedem Schreiben auch ein Wort aus. Dann 
wird das beim 2001. Wort das 1. unten rausgeworfen und das 2001. rutscht 
auf die Stelle des 2000. usw. der ganze FIFO kreiselt dann, und hält 
immer die letzten 2000 Worte. Natürlich gibts da unzähliche andere Wege, 
bei 2^n Worten Pretrigger gehts immer sehr gut mit Bitmasken auf dem 
Adresszähler usw.
Ich hab gerne die FIFO Lösung genommen, die hat nur den Nachteil, dass 
man während dem Pretrigger den Lesetakt mit einem BUFGMUX beispielsweise 
umschalten muss. Was natürlich auch geht: Die einstellbaren FIFO-Flags 
benutzen. Dann sparst du dir das synchronisieren. Du stellst die 
FIFO-Flasg-Schwelle auf die Pretrigger größe, und wenn die Überschritten 
wird, öffnest du RE und der FIFO kreiselt genauso. Da muss man dann nur 
aufpassen, die FLags haben durch die Gray-Zähler +-1 Takt Latenz. Oder 
aber du machst das selber mit dem Ausgangsvektor, der den Füllstand auf 
der Leseseite anzeigt....

von Steffen H. (avrsteffen)


Lesenswert?

Danke für deine Hilfe Christian.

Dann werd ich das demnächst mal ausprobieren.

Noch ein paar Fragen dazu:
>während dem Pretrigger den Lesetakt mit einem BUFGMUX umschalten
Was macht man mit dem BUFGMUX? Weil ich arbeite mit eimnem Lattice. 
Meinst du damit zufällig das man den Lesetakt dann zwischen zwei 
verschiedenen Takten umschalten muss?

>Die einstellbaren FIFO-Flags benutzen. Du stellst die FIFO-Flasg-Schwelle
>auf die Pretrigger-Größe
Das wäre optimal wenn das so einfach gehen sollte. Zweifel dabei hab ich 
allerdings dass dies funktioniert wenn ich einen 
variabelen/einstellbaren PreTrigger habe. Denn die Flag-Schwellen werden 
doch im IP-Core-Generator vorher bestimmt. Die Dokumentation ist leider 
sehr schlecht bei Lattice. Leider..


Grüße Steffen

von Christian R. (supachris)


Lesenswert?

Ahso, ich arbeite mit Xilinx, da braucht man zum Takte schalten einen 
BUFGMUX. Sowas gibts bei Lattice bestimmt auch. Ist halt dann eine 
herstellerspezifische Primitive. Bei den Xilinx FIFO Cores kann man 
einstellen, dass man die Schwellen während der Laufzeit einfach ändern 
kann, da it ja nur ein Komparator drin. Ebenso erlauben die FIFOs 
Füllstandsanzeigen, sogar für die Write und die Read Domain getrennt. 
Sowas geht sicher bei Lattice auch, kann mir nicht vorstellen, dass die 
da in viel nachstehen.

von Steffen H. (avrsteffen)


Lesenswert?

Okay,

BUFGMUX = DCS bei Lattice. Sowas gibt´s da also auch. Aber ob ich die 
Schwellen des FIFO's zur Laufzeit ändern kann, muss ich noch versuchen 
rauszubekommen. Auf alle Fälle haben die FIFO's "empty", 
"allmost_empty", full" und "allmost_full" Flags.

von Duke Scarring (Gast)


Lesenswert?

Steffen H. schrieb:
> Ich hab es live mit dem Board probiert. Und nach mehrmaligen
> durchprobieren der einzelnen Versionen gab es nur eine leichte
> Verbesserung der Version2 und Version3 zur Version1. Es sind aber immer
> noch aussetzer drin! Woher die auch immer kommen :/
Ah. Unqualifiziertes rumprobieren. Ich nutze für sowas einen Simulator 
und eine Testbench. Da kann ich mehr sehen, als nur geht/geht nicht...

Duke

von Steffen H. (avrsteffen)


Lesenswert?

Hallo

Duke schrieb:
>Ah. Unqualifiziertes rumprobieren. Ich nutze für sowas einen Simulator
>und eine Testbench. Da kann ich mehr sehen, als nur geht/geht nicht...
Da hast du schon irgendwie Recht Duke. Dann werd ich mal am WE eine 
Testbench bauen. Der nachteil einer Testbench ist nur die, dass die 
Verzögerungen/Delays nicht mit einbezogen werden. Die müsste man dann 
alle manuell im code setzen.
Wie Beispielsweise:
>>> s_wr_clk <= '1' after 6 ns; <<<

Aber was solls, wenn man den Fehler nicht mehr so einfach im Code selber 
finden kann dann bleibt mir ja garnix anderes mehr übrig.

@Christian
Mit dem FIFO komm ich gerade auch nicht weiter. Was ich schon 
rausbekommen hab ist: Es gibt bei Lattice ebenfalls die Möglichkeit die 
Adressen für die Flags "dynamisch" im laufenden Desingn zu setzen. Heut 
Nachmittag hörte sich das alles noch so einfach an. Und nun seh ich 
schon wieder den Wald vor lauter Bäumen nicht.. Naja, eins nach dem 
anderen.
Ich kann mir übrigens auch beim FIFO die Addressen nach außen führen 
lassen. Mal sehen, ob das auch noch eine Option für mich ist.

Also danke schonmal für Euere Hilfe bis hierhin.

Steffen

von Christian R. (supachris)


Lesenswert?

In der Testbench musst du sinnvollerweise die Delays der externen 
Komponenten setzen, beispielsweise dein ADC. Die internen Delays weiß 
die Lattice Toolchain nach dem Routen. Und dann kannst du wenn gar nix 
hilft, eine Timing-Simulation machen. Grundlage sind dann die VHD 
Beschreibung der fertig gerouteten Netzliste und die SDF Delay Daten. 
Modelsim kann das dann mit dem echten Timing im FPGA simulieren und 
spueckt auch Fehler aus, wenn Setup- und/oder Hold-zeiten der internen 
Komponenten verletzt werden. Natürlich nur, wenn Lattice das gescheit 
einprogrammiert hat. Bei Xilinx klappt das jedenfalls, bei Lattice ganz 
sicher auch.
So eine Timing-Simulation ist aber nur das letzte Mittel, zunächst mal 
muss man natürlich eine Verhaltenssimulation machen. Wenn man ohne Simu 
FPGA Designs entwirft, tappt man ziemlich oft im Dunkeln und muss nur 
raten, wo der Fehler liegen könnte.

von Steffen H. (avrsteffen)


Lesenswert?

So wie ich :))

von Christian R. (supachris)


Lesenswert?

Na dann ist der erste Schritt jetzt eine gescheite Testbench zu 
erstellen. Und dann die Simulation anwerfen und schauen, was passiert. 
Sonst kommst du eh kaum weiter. Ohne Simulation kann man vielleicht ein 
kleines CPLD Design machen, aber ein komplexes FPGA? Naja...

von Steffen H. (avrsteffen)


Lesenswert?

Hallo

Hat jemand eine Idee wie ich mir in einer Testbench 8bit Daten in einer 
Sinusform generieren könnte???

Steffen

von Christian R. (supachris)


Lesenswert?

Mit Excel generieren, in einer Textdatei abspeichern und dann über 
textio in der VHDL Testbench auslesen.

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


Lesenswert?

Steffen H. schrieb:
> Hat jemand eine Idee wie ich mir in einer Testbench 8bit Daten in einer
> Sinusform generieren könnte???
Einfach mit der sin() Funktion aus der math_real?
Ein Ausschnitt aus http://www.lothar-miller.de/s9y/categories/31-DDFS
Hier wird ein ROM vorbelegt:
1
  use ieee.math_real.all;  
2
:
3
  type Rom64x8 is array (0 to 63) of signed (7 downto 0); 
4
  -- Sinus von 0° bis 90° (0 bis PI/2)
5
  signal Sinus_Rom : Rom64x8;
6
:
7
   table: for i in 0 to 63 generate
8
       Sinus_Rom(i) <= to_signed(integer( ( sin(2.0*MATH_PI*real(i)/255.0) +1.0)*127),8);
9
   end generate;
In einer Testbench hast du ja die Simulationszeit now. Und diese 
Simulationszeit kannst du durch eine andere Zeit teilen und dann als 
Argument an die sin() Funktion übergeben:
1
sin(2.0 * math_pi * (NOW/period))

Dann noch ein paar Konvertierungen und Casts:
1
  std_logic_vector(to_signed(integer((sin(2.0*MATH_PI*(NOW/10 ms)/255.0))+1.0)*127),8);

Und fertig ist die Laube... ;-)

von Christian R. (supachris)


Lesenswert?

Hehe, so gehts natürlich auch. Ich mach es immer über den Text-Dateien 
Weg, weil ich fast immer reale Signale einspeise um die Funktion meiner 
Signalverarbeitungskette zu simulieren. Für rein mathematische Signale 
ist Lothars Weg natürlich etwas einfacher.

von Steffen H. (avrsteffen)


Lesenswert?

Danke euch beiden.

@Christian
Das mit der textio muss ich mir erst mal noch genauer durchlesen. Gibt's 
ja hier genug Beiträge im Forum.

Die Methode von Lothar klingt auch sehr gut.


@Lothar
>In einer Testbench hast du ja die Simulationszeit now.
Wie groß ist den die Simulationszeit? Die konnte man doch irgendwo 
einstellen, bzw. in irgendeiner Datei hab ich das schon mal gesehen. Ist 
die so um die 1ps?

Ich hab jetzt erst mal so auf die Schnelle versucht ein Rechteck-Signal 
mit Rise-und Falltime zu generieren.
1
signal iCLK: std_logic ;
2
        :
3
begin
4
        :
5
6
p_iCLK: process begin
7
  iCLK <= '0'; wait for 1 ns;
8
  iCLK <= '1'; wait for 1 ns;
9
end process;
10
11
p_DATA_IN: process begin
12
13
for t in 1 to 1000 loop
14
--   one square wave
15
  for r in 1 to 10 loop
16
  wait until iCLK'event and iCLK = '1';
17
  DATA_in <= std_logic_vector(to_unsigned((r*20),DATA_in'length));
18
  end loop;
19
20
  wait for 490 ns;
21
  for r in 1 to 10 loop
22
  wait until iCLK'event and iCLK = '1';
23
  DATA_in <= std_logic_vector(to_unsigned((200-(r*20)),DATA_in'length));
24
  end loop;
25
  wait for 490 ns;
26
end loop;
27
wait;
28
end process;
Sollte erstmal eine Frequenz=1Mhz, Periodendauer=1µs ergeben. Den Sinus 
werde ich auch noch versuchen zu implementieren.

Danke euch
Steffen

von Steffen H. (avrsteffen)


Lesenswert?

Ich glaub ich hab schon den ersten Fehler gefunden. Zumindest die 
Simulation mag es gar nicht mit uninitialisierten Signalen zu arbeiten.
Hier mal noch ein Auszug aus meinem Trigger:
1
signal s_trigger   : std_logic;
2
signal s_trig_hold : std_logic;
3
    :
4
begin
5
    :
6
process begin
7
wait until CLK'event and CLK = '1';
8
   if s_trig_hold = '0' and s_trigger = '1' then s_trig_hold <= '1';
9
   end if;
10
   if s_trig_hold = '1' and var_CLK = '1' then s_trig_hold <= '0';
11
   end if;
12
end process;
Ich versuch es mal kurz zu beschreiben wie sich "s_trig_hold" verhalten 
sollte:
"s_trigger=1" soll "s_trig_hold" setzen, aber nur falls "s_trig_hold=0" 
ist. Ist "s_trig_hold=1" dann tut "s_trigger" nix. Allerdings sollte 
dann der "var_CLK" "s_trig_hold" wieder zurücksetzten.

"s_trig_hold" ist doch als "std_logic" deklariert. Das heißt ja er kann 
13 verschiedene Zustände haben. Und so versteht es auch die Simulation. 
Denn "s_trig_hold" kommt aus dem Zustand "U" gar nicht erst raus.

Würde es gehen, wenn ich es so löse?
1
process begin
2
wait until CLK'event and CLK = '1';
3
   s_trig_hold <= '0';
4
   if s_trig_hold = '0' and s_trigger = '1' then s_trig_hold <= '1';
5
   end if;
6
   if s_trig_hold = '1' and var_CLK = '1' then s_trig_hold <= '0';
7
   end if;
8
end process;
Ich denke mir dabei: s_trig_hold ist ja ein Signal und keine Variable. 
Dann müsste doch wenn s_trig_hold als letzten Zustand eine '1' hatte 
doch auch die Abfragen (if) noch auf den letzten Zustand greifen und 
nicht auf den neu gesetzten vor den if-Abfragen? Oder hab ich da einen 
Denkfehler?

Steffen

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


Lesenswert?

Steffen H. schrieb:
> Zumindest die
> Simulation mag es gar nicht mit uninitialisierten Signalen zu arbeiten.
Doch: sie arbeitet mit 'U'. Und irgendwas irgendwie mit 'U' verknüpft, 
gibt 'U'...

> Dann müsste doch wenn s_trig_hold als letzten Zustand eine '1' hatte
> doch auch die Abfragen (if) noch auf den letzten Zustand greifen und
> nicht auf den neu gesetzten vor den if-Abfragen?
Das schon, aber:
> Oder hab ich da einen Denkfehler?
Ja. Du denkst in Signalen und Variablen und deren Verhalten. Das ist 
Unsinn. Denn letztlich muß deine Beschreibung in FFs und LUTs abgebildet 
werden. Und du mußt es eben so beschreiben, dass das geht...

> Denn "s_trig_hold" kommt aus dem Zustand "U" gar nicht erst raus.
Such dir Stelle, wo das signal definiert wird und schreib da einen 
Initwert hin:
signal s_trig_hold : std_logic := '0';

Steffen H. schrieb:
>>In einer Testbench hast du ja die Simulationszeit now.
> Wie groß ist den die Simulationszeit? Die konnte man doch irgendwo
> einstellen,
Da brauchst du nicht einstellen: die Simulationszeit ist das, was du 
in der Waveform oben in der Zeitskala siehst.
> bzw. in irgendeiner Datei hab ich das schon mal gesehen.
> Ist die so um die 1ps?
Das ist die kleinste zeitliche Auflösung der Simulation.

von Steffen H. (avrsteffen)


Lesenswert?

Hallo Zusammen

Hab gerade mal versucht den Sinus in die Testbench einzubinden:
1
LIBRARY ieee;
2
USE ieee.std_logic_1164.ALL;
3
USE ieee.numeric_std.ALL;
4
use ieee.math_real.all;
5
 :
6
begin
7
 :
8
p_DATA_IN: process begin
9
10
for t in 1 to 1000 loop
11
  DATA_in <= std_logic_vector(to_signed(integer((sin(2.0 * MATH_PI * (NOW/10 ms)/255.0))+1.0) * 127),8);
12
end loop;
13
wait;
14
end process;

>>>Und Modelsim bringt mir spontan eine "nette" Fehlermeldung!


# Model Technology ModelSim PE Student Edition vcom 6.6d Compiler 
2010.11 Nov  2 2010
# -- Loading package standard
# -- Loading package std_logic_1164
# -- Loading package numeric_std
# -- Loading package math_real
# -- Compiling entity testbench
# -- Compiling architecture behavior of testbench
# ** Error: 
I:/FPGA/Versuche/DSO_CORE_8bit/TEST_TRIGGER_RAM/tb_sampler.vhd(79): No 
feasible entries for infix operator "*".
# ** Error: 
I:/FPGA/Versuche/DSO_CORE_8bit/TEST_TRIGGER_RAM/tb_sampler.vhd(79): 
Cannot resolve expression type of infix expression.
# ** Error: 
I:/FPGA/Versuche/DSO_CORE_8bit/TEST_TRIGGER_RAM/tb_sampler.vhd(79): 
Illegal type conversion to std.standard.integer (operand type is not 
known).
# ** Error: 
I:/FPGA/Versuche/DSO_CORE_8bit/TEST_TRIGGER_RAM/tb_sampler.vhd(129): No 
feasible entries for infix operator "*".
# ** Error: 
I:/FPGA/Versuche/DSO_CORE_8bit/TEST_TRIGGER_RAM/tb_sampler.vhd(129): Bad 
expression in left operand of infix expression "/".
# ** Error: 
I:/FPGA/Versuche/DSO_CORE_8bit/TEST_TRIGGER_RAM/tb_sampler.vhd(129): No 
feasible entries for subprogram "sin".
# ** Error: 
I:/FPGA/Versuche/DSO_CORE_8bit/TEST_TRIGGER_RAM/tb_sampler.vhd(129): 
Cannot resolve expression type of infix expression.
# ** Error: 
I:/FPGA/Versuche/DSO_CORE_8bit/TEST_TRIGGER_RAM/tb_sampler.vhd(129): 
Illegal type conversion to std.standard.integer (operand type is not 
known).
# ** Error: 
I:/FPGA/Versuche/DSO_CORE_8bit/TEST_TRIGGER_RAM/tb_sampler.vhd(129): 
Type conversion (to std_logic_vector) can not have aggregate operand.
# ** Error: 
I:/FPGA/Versuche/DSO_CORE_8bit/TEST_TRIGGER_RAM/tb_sampler.vhd(200): 
VHDL Compiler exiting

>>Das versteh ich jetzt mal gar nicht. Zumal Modelsim ja das Package >>"math_real" 
geladen hat!

Jemand eine Idee?

Steffen

von Steffen H. (avrsteffen)


Angehängte Dateien:

Lesenswert?

So, hier mal die Waves der Verhaltens-Simulation. Hier sieht alles 
perfekt aus. Hab den Ausschnitt extrem verkleinert gewählt, damit man 
die Aufzeichnung des PreTriggers besser erkennen kann.

von Steffen H. (avrsteffen)


Angehängte Dateien:

Lesenswert?

Und hier nochmal eine Detail-Ansicht von WRITE und READ. Kann irgendwie 
keinen Fehler entdecken! :/

Steffen

von Christian R. (supachris)


Lesenswert?

Wenn der Fehler nur ab und zu auftritt, ist es ein Timing-Problem. Hast 
du denn die Timing Constraints richtig gesetzt? Also den/die Clocks erst 
mal und dann noch mindestens die Eingänge für die ADC Daten?

von Steffen H. (avrsteffen)


Angehängte Dateien:

Lesenswert?

Ganz ehrlich? Ich kenn mich mit den Timing-Constraints noch gar nicht 
aus. Wo schreibt man die eigentlich rein? Ich hab ein paar in der "lpf" 
und "prf" Datei drin. Ich häng sie mal mit dran.

Ich müsst wahrscheinlich mal noch die Beziehung zwischen den externen 
Signalen zu dem internen Clock setzten. Aber wie das geht hab ich mir 
noch noch nicht erarbeitet.

Bei der Synthese bekomme ich auch noch eine Warnung:
>>> A user-defined clock should be declared on object "p:sysCLK" <<<
Und da hab ich auch immer noch nicht raus bekommen wo und wie ich da 
sysCLK deklariere.

Jetzt hab ich gerade in einer Projektdatei gesehen:
>>  # mapper_options
>>  set_option -frequency 50
>>> set_option -auto_constrain_io 1 <<<
>>  set_option -write_verilog 0
>>  set_option -write_vhdl 0

Ich sollte mir also eine Constraint Datei basteln.
Bitte habt ein wenig Nachsicht mit mir. Hab mir VHDL selber bei gebracht 
und noch am lernen.Hab erst vor 4 Monaten damit angefangen.

Grüße Steffen

von Steffen H. (avrsteffen)


Lesenswert?

Hallo Zusammen

Hab letztens mal versucht den Sinus in die Testbench einzubinden:
1
LIBRARY ieee;
2
USE ieee.std_logic_1164.ALL;
3
USE ieee.numeric_std.ALL;
4
use ieee.math_real.all;
5
 :
6
begin
7
 :
8
p_DATA_IN: process begin
9
10
for t in 1 to 1000 loop
11
  DATA_in <= std_logic_vector(to_signed(integer((sin(2.0 * MATH_PI * (NOW/10 ms)/255.0))+1.0) * 127),8);
12
end loop;
13
wait;
14
end process;

Und Modelsim bringt mir spontan eine "nette" Fehlermeldung!


>># Model Technology ModelSim PE Student Edition vcom 6.6d Compiler
>>2010.11 Nov  2 2010
>># -- Loading package standard
>># -- Loading package std_logic_1164
>># -- Loading package numeric_std
>># -- Loading package math_real
>># -- Compiling entity testbench
>># -- Compiling architecture behavior of testbench
>>># ** Error:
>>I:/FPGA/tb_sampler.vhd(79): No feasible entries for infix operator "*".
>>># ** Error:
>>I:/FPGA/tb_sampler.vhd(79): Cannot resolve expression type of infix expression.
>>># ** Error:
>>I:/FPGA/tb_sampler.vhd(79): Illegal type conversion to std.standard.integer
>>(operand type is not known).
>>># ** Error:
>>I:/FPGA/tb_sampler.vhd(129): No feasible entries for infix operator "*".
>>># ** Error:
>>I:/FPGA/tb_sampler.vhd(129): Bad expression in left operand of infix expression 
"/".
>>># ** Error:
>>I:/FPGA/tb_sampler.vhd(129): No feasible entries for subprogram "sin".
>>># ** Error:
>>I:/FPGA/tb_sampler.vhd(129): Cannot resolve expression type of infix expression.
>>># ** Error:
>>I:/FPGA/tb_sampler.vhd(129): Illegal type conversion to std.standard.integer
>>(operand type is not known).
>>># ** Error:
>>I:/FPGA/tb_sampler.vhd(129): Type conversion (to std_logic_vector) can not
>>have aggregate operand.
>>># ** Error:
>>I:/FPGA/tb_sampler.vhd(200): VHDL Compiler exiting

Das versteh ich jetzt mal gar nicht. Zumal Modelsim ja das Package 
"math_real" geladen hat! Was mache ich da falsch?

Hat dazu jemand eine Idee?

Steffen

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


Lesenswert?

Steffen H. schrieb:
> Hat dazu jemand eine Idee?
Da passen noch ein paar Datentypen nicht ganz zusammen... :-/

Probiers mal so:
1
library ieee;
2
use ieee.STD_LOGIC_1164.all;
3
use ieee.NUMERIC_STD.all;
4
use ieee.MATH_REAL.all;  
5
6
entity Waves is
7
    Generic ( period : time     := 1 ms;
8
              width  : integer  := 8 
9
              );
10
    Port    ( D   : out std_logic_vector(width-1 downto 0);
11
              clk : in  std_logic
12
              );
13
end Waves;
14
architecture Sine of Waves is
15
begin
16
   process (clk)
17
   variable x, s : real;
18
   variable tact, tper : real;
19
   begin
20
     tper := real(period/1 ns);
21
     tact := real(NOW/1 ns);
22
     x := tact/tper;
23
     s := sin(2.0*MATH_PI*X);
24
     D <= std_logic_vector(to_signed(integer(s*(2**real(width)/2.0-1.0)),width));
25
   end process;
26
end Sine;


Oder die knackige Variante:
1
  :
2
  process (clk)
3
  begin
4
    D <= std_logic_vector(to_signed(integer(sin(2.0*MATH_PI*real(NOW/1 ps)/real(period/1 ps))*(2**real(width)/2.0-1.0)),width));
5
  end process;
6
  :

von Steffen H. (avrsteffen)


Lesenswert?

Danke Lothar,

Wird sofort nach Feierabend ausprobiert. Ich hatte dank der Testbench 
noch ein paar Fehler in meinem Design (siehe Anhänge ganz oben) gefunden 
und schon erfolgreich beseitigt.

Tolles Forum hier! Muss ich jetzt ernsthaft mal anmerken.

Grüße Steffen

von Steffen H. (avrsteffen)



Lesenswert?

Hallo Lothar

Ich hab es hin bekommen! Danke nochmal. Im Anhang mal noch ein paar 
Waves von der Testbench. In "Real", also in der Hardware scheint es auch 
alles so zu klappen. Habe jetzt keine Aussetzer und krummen Werte die 
mir das Design bringt.

Der ADC wird momentan allerdings nur mit 50Mhz getaktet. Deswegen ja 
auch nur max. 5Mhz als Eingangssignal.

Werde dann mal später, wenn mal alles soweit auf einer Platine ist 
versuchen hier und da noch zu optimieren um noch ein wenig mehr Speed 
aus dem FPGA zu holen. (Pipelining)

Momentan lässt das Design laut Timing-Report nur max 69.1Mhz zu. Zum 
Optimieren muss ich mich wohl oder übel in die Timing-Constraints 
einarbeiten müssen. Da ist dann bestimmt nochmal EURE HILFE gefragt.

Bis dahin
Gruß Steffen

von Christian R. (supachris)


Lesenswert?

Steffen H. schrieb:
> Momentan lässt das Design laut Timing-Report nur max 69.1Mhz zu. Zum
> Optimieren muss ich mich wohl oder übel in die Timing-Constraints
> einarbeiten müssen. Da ist dann bestimmt nochmal EURE HILFE gefragt.

Naja, der Wert stammt doch bestimmt aus der Synthese, oder? Das kann 
dann nach dem P&R noch einiges mehr oder auch weniger werden.
Und die Timing Constraints optimieren nix, die sagen erst mal nur, wo 
was nicht ausreicht.

von Steffen H. (avrsteffen)


Lesenswert?

Ja, genau das meinte ich. Wenn die Constraints richtig gesetzt sind, 
dann meckert doch bestimmt die Synthese (Synplify) rum. Das schlechteste 
Timing Ergebnis kommt im Modul RAM_CONTROL vor. Und da werd ich dann 
nochmal ansetzen.

Steffen

von Steffen H. (avrsteffen)


Lesenswert?

Diese Meldung finde ich schon komisch:
>>>Found inferred clock PLL_MODUL_50Mhz_1|CLKOS_inferred_clock with period 
>>>10.00ns. A user-defined clock should be declared on object 
>>>"n:System_Clock.CLKOS"
Vor allem da sie in der PLL vorkommt und da ja eigentlich die CLK NETs 
drin stehen. ???

Wie und wo ich die auch immer deklarieren (on object) soll!! Das gilt es 
noch zu erforschen :)

Steffen

von Christian R. (supachris)


Lesenswert?

Timing Constraints werden im Normalfall erst nach der Synthese beachtet. 
Es gibt zwar auch Synthese Constraint Files, aber die haben wenig mit 
Timing zu tun.
Was die Warnung bedeutet, weiß ich nicht, arbeite nahezu ausschließlich 
mit Xilinx.

von Thomas (kosmos)


Lesenswert?

@Steffen H.: Welcher Logic Analyzer ist das?

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


Lesenswert?

Thomas O. schrieb:
> Welcher Logic Analyzer
Das ist eine Waveform aus ModelSim.
Und das ist ein HDL-Simulator (VHDL, Verilog).

von Steffen H. (avrsteffen)


Lesenswert?

Hallo

Um euch mal auf dem Laufenden zu halten: Heute mal die ersten Bilder 
meines Projektes. Scheint alles ganz gut zu funktionieren. DSO in 
TEK-Optik: 2 Kanäle mit einer analogen Bandbreite von min. 20Mhz. 
Samplingrate allerdings est einmal (bloß) bei 50 Mhz. Jetzt geht es 
erstmal an die Funktionen im Display.

LG Steffen

von Steffen H. (avrsteffen)


Angehängte Dateien:

Lesenswert?

Hier nun auch die Bilder..

von Steffen H. (avrsteffen)


Angehängte Dateien:

Lesenswert?

Und hier mal noch ein Bild zum Versuchsaufbau.

1x ATmega8: 4x Encoderauswertung + Aufbereitung
1x XMega128A1: 3x Encoderauswertung + Datenaufbereitung Display über 
SDRAM
1x ATmega8515: Displaycontroller
1x LatticeXP: Datenspeicher + Trigger ADC Daten

Bis jetzt erst 1 analog-Kanal aufgebaut zum Testen!

Alles recht ESD conform.. ;-)

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.