Forum: FPGA, VHDL & Co. Seltsame (Neben-)Effekte in der Simulation


von Anfänger (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

habe mich endlich mit Modelsim arrangiert und jetzt ein kleines Projekt 
(siehe Anhang) simuliert.

Das Spartan 3E Dev-Board hat ja einen LTC1407 drauf und dafür habe ich 
einmal ein Modul für den Spartan und zum anderen ein Modul für den 
Simulator, in dem auch der ADC simuliert wird.

Die Testbench liest die gewünschten ADC-Werte aus einer Datei und gibt 
die entsprechend weiter. Im "Groben" funktioniert es, aber im Detail 
klemmt es. Jetzt weiß ich aber nicht wo - höchstwahrscheinlich fehlt mir 
wieder etwas Verständnis ...

Es gibt ja jetzt einige "gleichzeitige" Prozesse und ich bin mir nicht 
sicher, welche davon ich zu synchronisieren habe und welche - per 
Definition - unsynchronisiert sein sollen.
Um den Zeitversatz auf der Platine (übertrieben) ins Spiel zu bringen, 
arbeitet der gefakte ADC mit der fallenden Taktflanke, das FPGA-Modul 
mit der steigenden.

Wenn also mal jemand von denen, die sich auskennen, drüber schauen und 
mir etwas Nachhilfe geben könnte, würde mir das sehr freuen :)

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


Lesenswert?

> Um den Zeitversatz auf der Platine (übertrieben) ins Spiel zu bringen,
> arbeitet der gefakte ADC mit der fallenden Taktflanke, das FPGA-Modul
> mit der steigenden.
Das macht man aber anders   :-o    Dafür gibt es after
Mit einer funktionalen Simulation findest du sowieso keine 
(relevanten) Timing-Verletzungen. Machs besser straight-forward (mit dem 
selben Takt), damit du Funktionsfehler findest.

Was willst du jetzt eigentlich testen?
Das Modul LTC1407 eigentlich nicht.
Denn dort stehen Sachen drin, die die Synthese ins Grübeln bringen...
(Am Rande: Der Name des Moduls ist ungünstig, LTC1407 ist ein Bauteil 
von LT. Das macht was anderes, als das was in dem vhdl-File steht...)

BTW:
1
      if (cs = '0') then
2
      :
3
      elsif rising_edge(clk50) then
4
      :
Das ist bedenklich, falls der cs (in der Realität) asynchron zum clk50 
kommen kann...

von Anfänger (Gast)


Lesenswert?

Guten Morgen Lothar,

Vielen Dank für Deine Hilfe!

> Das macht man aber anders   :-o    Dafür gibt es after

Danke für den Tip - habe doch gleich interessante Beispiele gefunden!
Was ein guter Suchbegriff nicht ausmachen kann :)

> Machs besser straight-forward (mit dem
> selben Takt), damit du Funktionsfehler findest.

Hm, die seltsamen Effekte hatten mich dazu gebracht, es mit einem 
Zeitversatz zu probieren - mir scheint, dass noch was anderes zuschlägt.

> Was willst du jetzt eigentlich testen?
> Das Modul LTC1407 eigentlich nicht.

Hm, schon - aber da ich das fake-Modul für den ADC ja selbst geschnitzt 
habe, wie auch die Testbench, bin ich mir nicht sicher, wo ich den 
Fehler suchen kann, bzw. wie ich ihm auf die Spur komme.

Sinn der Sache ist doch, dass die Ausgabe, die die Werte für den 
Speicher kontrolliert, genau die gleichen Werte in der gleichen 
Reihenfolge ausspuckt, wie die Testdaten, die ich der Testbench zur 
Verfügung stelle.

Der Punkt ist, dass die Übertragung bei jedem 2. Messwert funktioniert, 
dazwischen wird 0 in den Speicher geschrieben. Jetzt weiß ich aber von 
niemand, der die 0 produziert - habe die Testdaten extra mit != 0 
initialisiert.

> Denn dort stehen Sachen drin, die die Synthese ins Grübeln bringen...

Ich weiß. Die Meldung habe ich nur eingebaut, um zu sehen, was abgeht.

> Das ist bedenklich, falls der cs (in der Realität) asynchron zum clk50
> kommen kann...

Genau aus dem Grunde habe ich es so geschrieben. Ich habe zuerst Deine 
Variante vom Beitrag "Re: Der vhdl-Schnipsel-Anfängerfragen Thread" 
simuliert, dann aber gesehen, dass der Reset nicht in meinem Sinne 
funzt.
Kann durchaus sein, dass ich beim Umschreiben noch was verbockt habe.

von Anfänger (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe es etwas umgestellt, um dem Fehler auf die Spur zu kommen, 
dabei ist es leider noch schlimmer geworden :(

die gesamte Ausgabe der Simulation:
1
# start reading test data from file ...
2
# -- xFE xAB -- 254 171 -- 11111110  10101011
3
# got row #0 of test data as ch-A: 254, ch-B: 171, FRC: 1
4
#                  -- 218  35 -- 11011010  00100011
5
# got row #1 of test data as ch-A: 218, ch-B: 35, FRC: 1
6
#                  --  18 239 -- 00010010  11101111
7
# got row #2 of test data as ch-A: 18, ch-B: 239, FRC: 1
8
#                  -- 105  63 -- 01101001  00111111
9
# got row #3 of test data as ch-A: 105, ch-B: 63, FRC: 1
10
#                    --  65  23 -- 01000001  00010111
11
# got row #4 of test data as ch-A: 41, ch-B: 17, FRC: 1
12
# DONE reading test data!
13
# activated test data of row #0
14
# test data on lines - A:UUUUUUUUUUUUUUUU, B:UUUUUUUUUUUUUUUU
15
# testdata to transmit: 000UUUUUUUUUUUU0000UUUUUUUUUUUU000
16
# activated test data of row #1
17
# test data should be - A:0000000011011010, B:0000000000100011
18
# test data on lines - A:UUUUUUUUUUUUUUUU, B:UUUUUUUUUUUUUUUU
19
#             7654321-7654321-7654321-76543210
20
# current sr: UU000UUUUUUUUUUUU0000UUUUUUUUUUU
21
# ** Warning: NUMERIC_STD.TO_INTEGER: metavalue detected, returning 0
22
#    Time: 930 ns  Iteration: 2  Instance: /testbench
23
# ** Warning: NUMERIC_STD.TO_INTEGER: metavalue detected, returning 0
24
#    Time: 930 ns  Iteration: 2  Instance: /testbench
25
#      >>> wrote channel-A with: 0, channel-B with: 0, to address: 0

Es geht um diese Passage, genauer um die Signale "iChA" und "iChB":
1
      iChA <= std_logic_vector(to_unsigned(myTestData(row).chn_A, 16));
2
      iChB <= std_logic_vector(to_unsigned(myTestData(row).chn_B, 16));
3
      if (myTestData(row).FRC = 1) then
4
         ADC_frc <= '1';
5
      else
6
         ADC_frc <= '0';
7
      end if;
8
      write(scratch, STRING'("activated test data of row #")); write(scratch, row);
9
      writeline(output, scratch);
10
11
      write(scratch, STRING'("test data should be - A:")); write(scratch, std_logic_vector(to_unsigned(myTestData(row).chn_A, 16)));
12
      write(scratch, STRING'(", B:")); write(scratch, std_logic_vector(to_unsigned(myTestData(row).chn_B, 16)));
13
      writeline(output, scratch);
14
      write(scratch, STRING'("test data on lines - A:")); write(scratch, iChA);
15
      write(scratch, STRING'(", B:")); write(scratch, iChB);
16
      writeline(output, scratch);

... welche folgende Ausgabe erzeugt:
1
# activated test data of row #1
2
# test data should be - A:0000000011011010, B:0000000000100011
3
# test data on lines - A:UUUUUUUUUUUUUUUU, B:UUUUUUUUUUUUUUUU

Die gesamte Testbench ist im Anhang.

Wo liegt mein (Denk-)Fehler?

von Anfänger (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

hab's jetzt zwar selber hinbekommen, aber was mich frustriert: ich weiß 
nicht, warum es jetzt tut.

Gut, beim FPGA-modul hatte ich einen Takt zuwenig gezählt, sodass der 
ADC garkeine Chance hatte, das neue Startsignal mit zu bekommen.
... auch habe ich bei der Testbench 2 Prozesse wieder zusammengefasst.

Schätze mein Verstädnis krankt noch daran, wann ein Signal den ihm 
zugewiesenen Wert erhält.
Also z.B. ich mache das "gleiche" in 3 verschiedenen Prozessen (in 
unterschiedlichen Simulationen, nur zum Verständnis):
1.) ein Prozess mit einer Signalabhängigkeit linear kodiert mit 
Flankenabfrage auf das angegebene Signal:
1
PROCESS (...) BEGIN
2
   if rising_edge(...) then
3
       ...
4
   end if;
5
END PROCESS;

2.) ein Prozess ohne Signalabhängigkeit
1
PROCESS BEGIN
2
   WAIT UNTIL rising_edge(...);
3
   ...
4
END PROCESS;
3.) ein Prozess ohne Signalabhängigkeit, der aber eine (Endlos-)Schleife 
hat, sodass der Prozess nie beendet und neu gestartet wird. In der 
Schleife wird auf unterschiedliche Ereignisse gewartet.

In jedem dieser Prozesse weise ich einem Signal einen neuen Wert zu.
Wovon hängt es jetzt ab, wann der neue Signalpegel aktiv ist? - Egal ob 
für andere Prozesse oder denselben Prozess? Gibt es hier Unterschiede 
zwischen Simulation und Synthese?

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


Lesenswert?

Diese beiden Beschreibungen verhalten sich exakt identisch. Eine 
Sensitivliste ist quasi eine andere Schreibweise für ein wait until.

Das wait until hat den Vorteil, dass es keine unvollständige 
Sensitivliste geben kann. Siehe z.B. 
http://www.lothar-miller.de/s9y/archives/16-Takt-im-Prozess.html

von Anfänger (Gast)


Lesenswert?

Hallo Lothar,

selbst wenn die ersten beiden identisch zu sein scheinen - bleibt für 
mich noch immer die Frage offen, wann denn eine Signalzuweisung aktiv 
wird und wovon es abhängt.

Noch interessanter ist doch diese Variante, bei der der Prozess niemals 
verlassen wird:
1
   PROCESS BEGIN
2
      ...
3
      LOOP
4
         mySignal <= '1';
5
 
6
         WAIT UNTIL rising_edge(clock);
7
         ...
8
      END LOOP;
9
      WAIT;
10
   END PROCESS;

In manchen Beschreibungen steht zu lesen, dass ein Signal erst nach 
Beendigung des Prozesses den zugewiesenen Wert erhält (und wird als 
Gegensatz zu den Variablen hervorgehoben). Das scheint so aber nicht 
ganz zu stimmen ...

von Klaus Falser (Gast)


Lesenswert?

> In manchen Beschreibungen steht zu lesen, dass ein Signal erst nach
> Beendigung des Prozesses den zugewiesenen Wert erhält
Nicht am Ende des Prozesses, also nicht bei der Zeile END PROCESS, 
sondern nachdem alle Prozesse an einen Wait-Statement (implizit oder 
explizit) angekommen sind.
Dann werden alle Signale aktualisiert und der nächste Event 
abgearbeitet.

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


Lesenswert?

Anfänger schrieb:
> Noch interessanter ist doch diese Variante, bei der der Prozess niemals
> verlassen wird:
>
1
>    PROCESS BEGIN
2
>       ...
3
>       LOOP
4
> 
5
>          WAIT UNTIL rising_edge(clock);
6
>          ...
7
>       END LOOP;
8
>       WAIT;
9
>    END PROCESS;
10
>
Schon mal probiert, das zu synthetisieren?

Fehler:
1
Bad condition in wait statement, or only one clock per process.
Ursache: das zweite WAIT;

Wird das auskommentiert, kommt der Folgefehler:
1
Wait statement is not supported in this configuration.
Da liegt daran. dass ein WAIT in LOOPs (derzeit noch) nicht 
synthetisiert werden kann.

von Anfänger (Gast)


Lesenswert?

Danke Klaus,

das war genau die Erklärung, die ich gesucht habe!
Sie ist plausibel und jetzt leuchtet mir auch ein, warum meine Testbench 
mit mehreren Prozessen nicht geklappt hat (Synchronisierung der 
WAIT-statements hat nicht geklappt).

Gibt es die Möglichkeit, Prozesse zu priorisieren, d.h. irgendwie 
festzulegen, in welcher Reihenfolge die Prozesse wieder anlaufen?

Wie sieht das eigentlich mit den Auswirkungen des IO auf das 
Zeitverhalten der Simulation aus?
Das Einlesen der Datei in den Speicher ist in der Zeitachse nicht zu 
sehen, aber wenn ich pro Testzyklus einen Dateizugriff mache, kommt die 
Zeitsteuerung durcheinander (subjektiver laienhafter Eindruck - muss 
nicht stimmen).

@Lothar
Mir ging es nicht darum, zu synthetisieren, sondern zu verstehen.
Das Beispiel ist aus meiner (funktionierenden) Testbench - da besteht 
nichtmal der Wunsch, das zu synthetisieren. Im Augenblick ist mir die 
Synthese völlig egal, da die Verständnislücken noch zu groß sind.
Deshalb "entwickle" ich gerade mit Modelsim und dem Wavediagram. Wenn 
ich dann mit dem Zeitverhalten meiner Module zufrieden bin, dann widme 
ich mich wieder der Synthese.

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


Lesenswert?

> Gibt es die Möglichkeit, Prozesse zu priorisieren, d.h. irgendwie
> festzulegen, in welcher Reihenfolge die Prozesse wieder anlaufen?
Du kannst in einem Prozess ein Signal setzen, und im anderen abfragen, 
wie der Zustand des Signals ist:
1
:
2
signal weiter : std_logic := '0';
3
:
4
process begin
5
   :
6
   tuwas...
7
   wait for 3 us;
8
   machwas...
9
   wait until irgendwas;
10
   :
11
   wait for 300 ns;
12
   weiter <= '1';
13
end process;
14
15
process begin
16
   wait until rising_edge(weiter);
17
   :
18
   :
19
end process;
Aber sowas wird schnell unübersichtlich. Besser wäre da eine zentrale 
State-Machine, die die Verwaltung der einzelnen Prozesse übernimmt.

> Wie sieht das eigentlich mit den Auswirkungen des IO auf das
> Zeitverhalten der Simulation aus?
Während der Dateizugriffe bleibt die simulierte Zeit einfach stehen. Die 
Zugriffe finden quasi mit delta-T=0 statt.

von Klaus F. (kfalser)


Lesenswert?

> Deshalb "entwickle" ich gerade mit Modelsim und dem Wavediagram. Wenn
> ich dann mit dem Zeitverhalten meiner Module zufrieden bin, dann widme
> ich mich wieder der Synthese.

Nur als Empfehlung, aber ich denke nicht, dass dies ein günstige Ansatz 
ist.
Du mußt lernen zu unterscheiden :
- Was darf ich in einem Modul verwenden, das synthetisiert werden soll 
und wie beschreibe mein gewünschtes Verhalten. Dazu gehören Register, 
FFs, FSM usw.
Diese Muster kann man auch anwenden, wenn man nicht alle Feinheiten von 
VHDL verstanden hat.
- Was kann ich in einer Testbench verwenden.
Das sind Delays, File I/O, waits an beliebigen Stellen in Prozessen usw.

Du verwendest File I/O und ähnliches, aber die Grundlagen scheinen noch 
nicht ganz zu sitzen.

von Anfänger (Gast)


Lesenswert?

>> Gibt es die Möglichkeit, Prozesse zu priorisieren, d.h. irgendwie
>> festzulegen, in welcher Reihenfolge die Prozesse wieder anlaufen?
> Du kannst in einem Prozess ein Signal setzen, und im anderen abfragen,
> wie der Zustand des Signals ist:

Ok, das habe ich ja schon gemacht. Danke.

> Du mußt lernen zu unterscheiden :
> - Was darf ich in einem Modul verwenden, das synthetisiert werden soll
> und wie beschreibe mein gewünschtes Verhalten. Dazu gehören Register,
> FFs, FSM usw.
> Diese Muster kann man auch anwenden, wenn man nicht alle Feinheiten von
> VHDL verstanden hat.
> - Was kann ich in einer Testbench verwenden.
> Das sind Delays, File I/O, waits an beliebigen Stellen in Prozessen usw.

Hm, ich denke, das habe ich verstanden.
Die delays, die ich in den Modulen eingesetzt habe, die mal 
synthetisiert werden sollen, sind nur für die Simulation drin.
Irgendwie musste ich meinem "Fehler" ja auf die Spur kommen.
Genauso wie die Konsolen-Ausgaben - die sind nur für die Simulation.

... by the way: gibt es sowas wie ein #ifdef für VHDL?

> Du verwendest File I/O und ähnliches, aber die Grundlagen scheinen noch
> nicht ganz zu sitzen.

File I/O verwende ich aus Faulheit ;) Einmal habe ich die Werte seriell 
kodiert - nur um dann festzustellen, dass die Fehlerquelle und 
Fehlerwahrscheinlichkeit viel zu hoch ist, um einem Testergebnis zu 
vertrauen.

Also habe ich ne Testbench geschrieben, die die Werte aus einer Datei 
liest. Das tut jetzt zuverlässig. Und da ich auch an dieser Stelle sehr 
bequem bin, musste das File I/O auch gleich die Zahlentypen 
konvertieren, sodass ich Werte als Int-, Hex-, Oktal- und Binärwert 
angeben kann.

Die Grundlagen sitzen deshalb noch nicht, weil dies meine erste 
Testbench ist. In den meisten Tutorials wird der BNF-Graph der Sprache 
abgedruckt, ohne auf Sinn und Zweck einzugehen, geschweige denn 
sinnvolle Beispiele zu geben.

Vieles habe ich mir von Lothar, opencores und freemodel abgeschaut. 
Jetzt aber die Theorie und Praxis zusammen zu bringen - da krankt es 
noch etwas.
Deshalb kann es schon vorkommen, dass ich hier scheinbar abstruse Fragen 
stelle.

Dank Eurer Hilfe wird aus den Puzzleteilen langsam ein Bild :)

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

http://www.informatik.uni-kiel.de/en/schimmler/teaching/vorlesungen/fpga-entwurf-mit-vhdl/lesezeichen-vhdl/

Da gibt es zwei "Bücher" als PDF vieleicht hilft dir das und kostet nix 
;)

Bie VHDL Kompakt ist zu der Syntax form auf jedenfall auch noch immer 
ein Beipiel wie man es anwendet dabei.

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.