Hallo, ich versuche seit ein paar Tagen einen ADC anzusteuern, welcher mit einem externen Trigger für die Wandlung funktionieren soll. Der Chip ist ein AD7961, beziehungsweise AD7960, welcher dann 18 bit hat aber sonst gleich ist. Für das conversion-timing und die Wartezeit bis zum MSB habe ich zwei state-machines gebaut, die soweit ok funktionieren. Der conversion clock wird dann aktiviert und die Antwort des ADC sowohl im Daten- als auch Clock-Signal mit der Sync-Clock synchronisiert. Das Einlesen der Daten jedoch funktioniert nicht im späteren Design (die Ausgaben des ILA von Vivado sind leider auch nicht weiter zu gebrauchen). Eine Simulation des VHDL-Code allein funktioniert auch, nicht jedoch die Simulation mit dem Ergebnis aus der Synthese. Ich würde mich freuen, wenn jemand mal ein Auge drauf werfen könnte, vielleicht gibt es etwas, das ich eklatant falsch mache. Hab dafür den Code samt Testbench hier reingepackt. Vielen Dank schonmal
Also ich vermisse schonmal die IBUFDS. Da ich den Baustein selber verwende baue ich dir mal ne kleine Testbench, kommt also nachher an dieser Stelle. Edith hat die Dateien hochgeladen, das simuliert in Vivado einen Sinus Sweep. data ist dabei 18 Bit Signed.
:
Bearbeitet durch User
Die IBUFDS hab ich extern eingebunden, war mir etwas flexibler mit allem drum und dran. Die Chips können ja auch nicht-differenzielle Ansteuerung, was man ja für niedrige Frequenzen noch gut nehmen kann. Gustl, bei Dir läuft das alles sehr straight in einem Rutsch durch. Ich hab es durch den niedrigen Systemtakt von 100 MHz zum synchronisieren und den 50 MHz Datenclock etwas schwieriger, wenn ich wenigstens 3 Msps schaffen will. Daher diese Verschachtelung von state machines. Der Fehler scheint in der letzten State Machine zu sein. Nach der Synthese läuft der Zähler für die empfangenen Bits genau ein mal hoch, dann wird er nicht mehr resettet. Vor der Synthese ist es kein Problem.
Naja du kannst intern einen 250 MHz Takt machen, dann die Daten in einen DC FIFO schreiben und mit 100 MHz daraus lesen. Ein Clock Domain Crosssing ist sowieso drinnen.
So, noch ein kleiner Nachtrag: Bei Prozessen in VHDL gibt es verschiedene Wege der Beschreibung. Wenn das ein getakteter Prozess sein soll der NUR mit einer Taktflanke abläuft, dann kannst du process begin wait until rising_edge(clk); ... end process; schreiben. Das ist gleichbedeutend mit process (clk) begin if rising_edge(clk) then ... end if; end process; Wenn du noch Kombinatorik mit drinnen hast, also der Prozess auch dann neu "evaluiert" werden soll wenn sich andere Signale ändern, dann müssen diese zusätzlichen Signale auch in der Sensitivitätsliste auftauchen. Für die Synthese ist das nicht wichtig, da wird die Sensitivitätsliste ignoriert, aber das ist für die Simulation wichtig. Wenn du also möchtest, dass Simulation und Hardware übereinstimmen, dann müssen alle Signale die den Prozell anstoßen in die Sensitivitätsliste. Bei deinem Prozess clock_out: process (sync_clk, enableclkout) begin DCOp <= enableclkout AND clk; end process; muss also clk mit hinein in die Liste, sync_clk hat da aber nichts drinnen verloren. Genauso weiter unten bei synchronisation : process (sync_clk, clk, dclkin, conv, din) begin if rising_edge(sync_clk) then ... end if; end process; da sollte nur sync_clk in der Liste stehen. Man fährt in der Regel gut damit möglichst alles getaktet zu beschreiben. Also getaktete Prozesse und Kombinatorik wenn man sie braucht ohne Prozess. Klar, gibt Ausnahmen, aber bisher konnte ich meine Designs so schreiben. Zu der Sache mit dem Takt: Du könntest auch bei deinen 100 MHz DDR IOs verwenden, dann bekommst du auch zumindest 100 MHz SPI hin. Für > 3 MSample/s würde das genügen.
Danke für Deine Tipps! Die Sache mit einem externen 200 MHz-Takt hatte ich versucht, das hat aber dann leider gar nicht mehr funktioniert, jedoch hatte ich keinen dualclock FIFO eingesetzt. Ich werde mal die Sensitivitätslisten überarbeiten und schaue mal wie es dann aussieht. Vielleicht komme ich so ja schon zu einem Hinweis, was ich falsch mache. Wie gesagt, scheint es ja nur die letzte SM zu betreffen.
Welchen Baustein verwendest du denn? Auch für den AD7961 steht im Datenblatt typischerweise 250 MHz SPI Takt. Ich sehe gerade, dass ich das auch nicht richtig gemacht habe, ich verwende einen SPI Takt von 125 MHz. Funktioniert trotzdem aber wohl nicht so gut weil die Pause nicht so lang ist. Korrekt wäre wenn man da wirklich DDR IOs nimmt oder das gleich mit SerDes macht.
Dirk N. schrieb: > Wie gesagt, scheint es ja nur die letzte SM zu > betreffen. in deinem letzten Prozess (data_process) hast du eine wilde Mischung von taktsynchroner Logik innerhalb eines kombinatorischen Prozesses. CASE data_state IS WHEN init => count_bits <= 0; WHEN read => IF rising_edge(sync_clk) THEN ................ Darauf hat auch Gustl schon hingewiesen. Lass sowas sein. Mach den Prozess isgesamt getaktet oder insgesamt kombinatorisch. Wenn es ein getakteter Prozess ist, dann sollte "oberhalb der Taktung" bestenfalls ein asynchroner Reset stehen, aber keine ausführliche kombinatorische Logik. Ansonsten: ich würde das Reinrotieren der Datenbits ins Ergebniswort mit einem Schieberegister machen (ein SPI-Register ist im wesentlichen ein Schieberegister), du verwendest stattdessen einen großen Mutliplexer case count_bits is when 0 => dataregister(15) <=data_in_synced(1); when 1 => dataregister(14) <= data_in_synced(1); when 2 => dataregister(13) <= data_in_synced(1); Und hier elsif (clk = '1' and clk'event) then state_clkout <= next_state_clkout; case state_clkout is when idle => enableclkout <= '0'; -- clkout_counter <= 1; if trigger_msb = '1' then next_state_clkout <= clocking; -- versuchst du wohl die Zwei-Prozess-Schreibweise von FSM auf einen Prozess zusammenzuziehen. Da du dabei beide Signale state_clkout und next_state_clkout in einem getakteten Prozess zuweist, bekommst du bei jedem State-Übergang wahrscheinlich einen Takt zusätzlicher Latenz, die du eigentlich nicht willst Würdest du next_state_clkout in einem anderen, kombinatorischen Prozess zuweisen, dann hättest du die normale Zwei-Prozess-Schreibweise von FSMs (ohne zusätzliche Latenz). Wenn du die Zuweisung aber innerhalb des getakteten Prozesses stattfindet, in dem du auch die Zuweisung zu state_clkout machst, müsstest du ohne next_state_clkout arbeiten. Also statt if trigger_msb = '1' then next_state_clkout <= clocking; -- direkt schreiben if trigger_msb = '1' then state_clkout <= clocking; -- Ob die Abtastung des Datensignals (getaktet mit sync_clk, kombiniert mit einer Flankentetektion von data_clk_synced ) vom Timing her aufgeht, bin ich mir nicht sicher. Ich könnte mir vorstellen, dass du manchmal das Datenbit vom "falschen" Taktzyklus einliest Denn wenn sync_clk und dclkin keine feste Phasenbeziehung haben wirst du manchmal mit data_clk_synced <= data_clk_synced(0) & dclkin; schon die fallende Flanke detektieren während data_in_synced <= data_in_synced(0) & din; noch die Daten des vorherigen dcklin-Zyklus speichert.
Die Angaben zur SPI Übertragung sind eher schwammig im Datenblatt: https://www.analog.com/media/en/technical-documentation/data-sheets/AD7960.pdf Der letzte Absatz auf Seite 19 sagt aus, dass man die 18 Clock Pulse (16 Pulse bei AD7961) bis t_CLKL der nächsten Wandlung angelegt haben muss. t_CLKL sind 160 ns. Bei 5 MSps dauert eine Wandlung 200 ns. Man hat also von diesen 200 ns die 160 ns Zeit für die 18 oder 16 Pulse. Wann man nach CNV damit anfangen darf ist nicht spezifiziert. Sondern es steht da wann das MSB (das als erstes herausgetaktet wird) spätestens verfügbar ist - und zwar nach t_MSB = 200 ns. Man könnte also mit den 18 oder 16 Taktpulse auch schon während CNV high ist anfangen. Laut Datenblattzeichnung überlappt auch t_Acquisition mit dem Auslesen, da muss man also keine Ruhe einhalten auf den digitalen Leitungen. Wenn das so stimmt, dann kann man auch einen deutlich niedrigeren Takt fahren als die im Datenblatt typischerweise angegebenen 250 MHz. Das mache ich auch und verwende aktuell 125 MHz. Aber geht noch weniger? Beim AD7960 muss man in 160 ns 18 Pulse anlegen. Wobei t_CLKL nur sagt wann die Clock wieder low sein muss, das sind also 17,5 Taktperioden. 160 ns / 17,5 = 9,14... ns entspricht einem Takt von 109 MHz. Beim AD7961 muss man nur 15,5 Taktperioden in den 160 ns schaffen. 160 ns / 15,5 = 10,32.. ns entspricht einem Takt von 97 MHz. Man kann also den AD7961 mit vollen 5 MSps betreiben wenn man einen 100 MHz SPI Takt verwendet. Wenn man auch nur einen 100 MHz Takt im FPGA verwenden will, dann braucht man dazu ein ODDR um den Takt auszugeben. Ich werde da mal was basteln und wieder eine Testbench anhängen wenn das fehlerfrei simuliert.
Gustl, guter Hinweis mit den 250 MHz Clock. Da es keine Mindestzeit in der Tabelle gab, habe ich das erst mal ignoriert. Im Kommentar zur clock period gibt es noch einen Hinweis auf die Mindestfrequenz: "For the maximum CLK± period, the window available to read data is tCYC − tMSB + tCLKL". Habe aber diese Periodendauer jetzt für meinen Fall noch nicht berechnent. Ich hab ich mal nach den DDR IOs bei Xilinx gesucht, aber noch nichts gefunden. Achim, danke für Deine Ausführliche Erklärung. Wäre es also besser, alles asynchron zu machen? Es müsste eigentich Phasensynchron sein, da ich für den ADC-Clock die halbe Frequenz der Sync-Clock verwende.
Wir haben wohl gerade den gleichen Teil des Datenblatts gelesen, Gustl. Hatte nur noch meinen Beitrag nicht abgeschickt. Aus dem Grund, den Du beschreibst betreibe ich den ADC mit maximal 3 MHz. Im Normalfall werde ich zunächst weniger haben, will es erst einmal zum Laufen bringen und ggf. später schneller werden.
Dirk N. schrieb: > Wäre es also besser, alles asynchron zu machen? auf keinen Fall. du könntest alles synchron machen. wenn du z.B. für die zwei-Prozess Schreibweise einige rein kombinatorische Prozesse hättest, wäre das auch ok. aber deine"gemischten" Prozesse sind nicht im k.
So, jetzt hab ich das auch für den AD7961 (16 Bits) und zwar mit 100 MHz Clock und ODDR. Simulation sieht gut aus, aber ich kann das nicht auf Hardware testen weil ich hier einen AD7960 habe. Dirk N. schrieb: > Ich hab ich mal nach den DDR IOs bei Xilinx gesucht, aber noch nichts > gefunden. Wie das geht siehst du in der AD7961.vhd ist eigentlich recht einfach. IDDR brauchst du hier nicht wenn du den DCO verwendeset um die Daten entgegenzunehmen.
:
Bearbeitet durch User
Ich bin etwas weiter gekommen, habe die letzte Statemachine entfernt und habe einfach den Enableclkout-Status für die Fallunterscheidung genutzt, ob neue Daten gelesen werden oder die alten gehalten werden. Dadurch klappt das Zählen der Bits schon einmal. Was ich leider noch nicht messen konnte sind aussagekräftige Signale aus der Schaltung mit dem ILA, da muss ich noch mal neu Implementieren. Hatte rausgefunden, dass sich die SM aufhängt, weil einmal data_next_state und dann data_state da stehen. Da hatte ich mich selbst verwirrt, dann den Überblick verloren und bin letztendlich dran verzweifelt. Data_next_state hatte im idle-State gar keine Relevanz, weshalb sich das Ganze dann aufgehängt hat.
Dirk N. schrieb: > Hatte rausgefunden, dass sich die SM aufhängt, weil einmal > data_next_state und dann data_state da stehen Ja, ich weiß auch nicht, warum diese unübersichtliche state/state_next-Schreibweise an Hochschulen noch so oft propagiert wird. Bei mir gibt es i.d.R. einen state-type und dazu das passende state-Signal:
1 | type state_t is (IDLE, START, SHIFT); |
2 | ...
|
3 | process
|
4 | begin
|
5 | wait until rising_edge( clk); |
6 | case state is |
7 | when IDLE => |
8 | -- warte auf irgend ein Signal
|
9 | when START => |
10 | -- tue dies
|
11 | when SHIFT => |
12 | -- tue das
|
13 | end case; |
14 | end process; |
15 | ...
|
Oberste Priorität ist es (bei mir) den Code schnell wieder verstehen zu können, wenn ich da nach 14 Tagen, drei Monaten oder fünf Jahren mal wieder ran muß. Duke
Gustl, ich habe festgestellt, dass man beim Wandeln Probleme bekommt wenn man zu schnell ausliest. Also die tMSB muss wohl eingehalten werden. Ansonsten kommen in den obersten Bits noch Werte der unteren aus der letzen Konvertierung mit. Scheinbar ein Problem, das aus internen Schieberegistern im ADC herrührt. Ich habe den zeitlichen Versatz hergestellt und dann erst mal korrekte Ergebnisse bekommen. Was ich noch nicht hinbekommen habe ist eine externe Triggerung des Wandlers...
Dirk N. schrieb: > Also die tMSB muss wohl eingehalten werden. Ja nun, das sind maximal 200 ns, also bei 5 MSps genau eine Wandlung. Man liest also immer das Ergebnis der letzten Wandlung und nicht das der Aktuellen. Macht bei mir keinen Ärger. Ich stelle mir das so vor: Der ADC legt nach t_CLKL das Ergebnis der aktuellen Wandlung auf sein Ausgangsregister. Und spätestens nach t_MSB ist es dort und kann herausgetaktet werden. Man hat also die Zeit von steigender Flanke CNV bis t_CLKL Zeit um das Ergebnis abzuholen. Das ist aber das Ergebnis der vorangegangenen Wandlung.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.