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.
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.
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
typestate_tis(IDLE,START,SHIFT);
2
...
3
process
4
begin
5
waituntilrising_edge(clk);
6
casestateis
7
whenIDLE=>
8
-- warte auf irgend ein Signal
9
whenSTART=>
10
-- tue dies
11
whenSHIFT=>
12
-- tue das
13
endcase;
14
endprocess;
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.