Forum: FPGA, VHDL & Co. Timingprobleme, welche Constraints benötige ich?


von Gustl B. (-gb-)


Lesenswert?

Hallo,

ich möchte ein externes Signal zu einem einstellbaren Zeitpunkt abtasten 
können. Weil ich das relativ fein auflösen möchte bräuchte ich einen 
schnellen Takt. Dann könnte ich einstellen im wievielten Takt das 
abgetastet werden soll. Aber das schafft das FPGA nicht. Daher nehme ich 
mehrere Takte und zwar 4 Takte mit je 100 MHz und einmal 0°, 45°, 90° 
und 135° Phasenverschiebung. Die Daten werden dann einstellbar zur 
steigenden und zur fallenden Flanke übernommen, ich bekomme also im 
vergleich zu einem einzelnen 100 MHz Takt jetzt 8 unterschiedliche 
Zeitpunkte. Das Problem ist aber, dass die Daten jetzt Taktgrenzen 
überqueren müssen.

Der einstellbare Wert im wievielten Takt die Daten übernommen werden 
kommt ins FPGA und ist da erstmal nur in einer Taktdomäne, der muss also 
noch in die anderen. Dieser Wert liegt aber lange an so dass das meiner 
Meinung nach keinen Ärger macht ausser dass es eben eine Fehlermeldung 
gibt. Wie kann man denn dem Tool mitteilen, dass mir das egal ist weil 
sich dieser Wert nur selten ändert und somit über viele Takte 
unverändert bleibt?

Dann werden mit den 8 Takten die externen Daten abgetastet und jeweils 
in eigene Schieberegister geschrieben. Deren Inhalte sollen dann aber 
wieder zurück in die Ausgangs-Takt-Domäne. Auch hier bleiben die Inhalte 
der Schieberegister über mehrere viele Takte stabil. Ich mache das so:
Das externe Signal kommt von einer SPI-Verbindung. Da wird alle 21 Takte 
bei 100 MHz ein 16 Bit Wert ankommen. Der Dauert aber eben nur 16 Takte 
von diesen 21. Jetzt wird gleichzeitig in allen 8 Domänen dieses externe 
Signal während jeweils 16 Takte in das Schieberegister geschrieben. Dann 
danach wird der Inhalt des Schieberegisters in der jeweiligen Domäne in 
ein neues Register dieser jeweiligen Domäne kopiert. Der Taktzähler wird 
zurückgesetzt und es geht wieder los mit neuen 21 Takten. In diesem 
zusätzlichen Register liegt also der alte Inhalt des Schieberegisters 
für 21 Takte rum. Und während dieser vielen Takte soll der Wert des 
Registers einmal in die Ausgangsdomäne übernommen werden. Wie baut man 
das? Oder mit welchen Constraints sage ich dass die Daten da lange 
liegen oder meinetwegen auch, dass diese Sampletaktdomänen und deren 
Register als völlig asynchron betrachtet werden sollen?

Edit:
Ich habe jetzt viele
set_false_path
und
set_clock_groups -asynchronous
verwendet. Jetzt bekomme ich keine Timingfehler und die Simulation sieht 
gut aus.

Vielen Dank!

: Bearbeitet durch User
von Schlumpf (Gast)


Lesenswert?

Nur so ne Überlegung zu deinem Ansatz:

Du willst ein externes Signal im Raster von 1.25ns abtasten können.
So dass im FPGA ein "Abbild" des Signals mit einer quasi Sampling Rate 
von 800Ms/s vorliegt. Richtig?

Wenn die "Abstände" zwischen den Samplepunkten dann aber äquidistant 
(also 1.25ns) sein sollen, setzt das voraus, dass das Signal vom Pin an 
alle 8 Register eine identische Latenz hat und die relevanten 
Taktflanken auch genau um 1,25ns versetzt an den Registern eintreffen.
Sonst verzerrst du das Abbild des Signals.

Lass dir mal die Pfade vom Pin zu den ersten Registern der Domains 
reporten.
Mich würde nicht wundern, wenn da der Skew sogar größer als ein 
Sampletakt ist. Und dann betrachste analog dazu noch den Skew der Takte 
an den Sampleregistern, ob die überhaupt noch sauber um 1.25ns versetzt 
an den Registern ankommt.
Ich schätze, das Ergebnis wird ernüchternd sein.

von Kalle (Gast)


Lesenswert?

Gustl B. schrieb:
> Das Problem ist aber, dass die Daten jetzt Taktgrenzen
> überqueren müssen.

Das ist nicht "das Problem" bei dem Ansatz.

Gustl B. schrieb:
> Wie kann man denn dem Tool mitteilen, dass mir das egal ist weil
> sich dieser Wert nur selten ändert und somit über viele Takte
> unverändert bleibt?

zB mit einem multicycle path

Der Ansatz, die Takte als asynchron zu betrachten, ergibt ja von 
vorneherein keinen Sinn, da du ja ein konstantes Phasenverhältnis hast. 
Das ist im Prinzip schon gar kein Taktübergang mit den üblichen 
Problemen mehr.

von Gustl B. (-gb-)


Lesenswert?

Schlumpf schrieb:
> Wenn die "Abstände" zwischen den Samplepunkten dann aber äquidistant
> (also 1.25ns) sein sollen, setzt das voraus, dass das Signal vom Pin an
> alle 8 Register eine identische Latenz hat und die relevanten
> Taktflanken auch genau um 1,25ns versetzt an den Registern eintreffen.
> Sonst verzerrst du das Abbild des Signals.

OK, da müsste ich also eine Constraint für schreiben?

Die einzelnen Pfade irgendwie constrainen, also ein MAXDELAY und 
MINDELAY zwischen Pad und dem Register setzen und die Werte so setzten, 
dass das Zeitfenster dazwischen recht klein ist?

Oder wofür sind diese OFFSET_IN Constraints? Ich weiß ja nicht wie der 
Offset ist, ich will also nicht einen bekannten Offset mitteilen, 
sondern einen Wert mitteilen damit dann Pfade die gleiche Verzögerung 
haben. Ich stelle mit das so vor, dass ich sagen kann.
Zeit von A nach B minimal x ns, maximal y ns. Und dann wird das so 
geroutet damit die Werte eingehalten werden. Geht sowas?

Oder kann man gleich einem über einen ganzen Bus sagen, dass sich die 
Laufzeit von A nach B nur um maximal x ns unterscheiden darf?

Schlumpf schrieb:
> Und dann betrachste analog dazu noch den Skew der Takte
> an den Sampleregistern, ob die überhaupt noch sauber um 1.25ns versetzt
> an den Registern ankommt.

Nun, das hatte ich eigentlich erwartet, dass ein Takt überall 
gleichzeitig ankommt.

Kalle schrieb:
> zB mit einem multicycle path

Stimmt, die Takte sind ja phasenstarr. Gute Idee, werde ich mir 
angucken.

Aber damit meine 8 Taktzähler immer schon synchron zählen setze ich den 
über einen der Takte zurück.
Jeder Zähler zählt von 0 bis 20 in der eigenen Taktdomäne. Und wenn aber 
der Zähler der Ausgangsdomäne der Wert 20 besitzt, dann werden die 
Zähler der anderen Domänen zurückgesetzt auf 0.
1
process begin
2
  wait until rising_edge(clk100_0);
3
  if counter_0 = 20 then
4
    counter_0 <= 0;
5
  else
6
    counter_0 <= counter_0 +1;
7
  end if;
8
end process;

Und in allen anderen Domänen:
1
process begin
2
  wait until rising_edge(clk100_45);
3
  if counter_0 = 0 then
4
    counter_45 <= 0;
5
  else
6
    counter_45 <= counter_45 +1;
7
  end if;
8
end process;

Das müsste gehen weil egal in welcher Domäne ich counter_0 auswerte, es 
lag vorher mindestens 1,25 ns an.

von Markus F. (mfro)


Lesenswert?

Gustl B. schrieb:
> set_false_path
> und
> set_clock_groups -asynchronous

das mag funktionieren, ist aber ungefähr so gut (schlecht) wie gar keine 
Timing-Analyse (bzw. Timing-getriebene Synthese).

Deine Takte stehen ja in einem festen, bekannten Verhältnis zueinander, 
so daß Du die Daten nur zum richtigen Zeitpunkt übernehmen mußt.

Multicycle-Pfade scheinen mir auch angemessen.

von Kalle (Gast)


Lesenswert?

Ich nehme mal das Ergebnis vorweg: Wenn du es nicht schaffst, ein 
einzelnes Eingangsregister (im IOB) mit deinem gewünschten Sampletakt zu 
takten, dann wird das nichts werden. Also entweder SDR mit 800 MHz oder 
DDR mit 400, sonst kannst du es vergessen

von -gb- (Gast)


Lesenswert?

Wäre ein SerDes eine Lösung? Also dann könnte man auch z. B. 8-fach 
überabtasten.

von Kalle (Gast)


Lesenswert?

Das geht, bringt aber bezogen auf die Taktfrequenzen keine Vorteile. 
Auch mit SerDes musst du den Sampletakt irgendwo in einer PLL erzeugen 
und damit ein FF takten. Die Auswertung ist aber natürlich einfacher.

von Blechbieger (Gast)


Lesenswert?

Kalle schrieb:
> Das geht, bringt aber bezogen auf die Taktfrequenzen keine Vorteile.

Das gilt aber nur wenn Serdes in Logik implementiert wird. Neuere FPGA 
haben das aber hart
in den IO-Blöcken implementiert und können entsprechend schnell getaktet 
werden. Der parallele Ausgang arbeitet dann entsprechend langsamer und 
kann vom Fabric verarbeitet werden.

von Gustl B. (-gb-)


Lesenswert?

Nun, 800 MHz kann der Spartan7 nicht. Ich werde jetzt mal mit 400 
versuchen, das sollte von der zeitlichen Auflösung auch genügen.

von Kalle (Gast)


Lesenswert?

Blechbieger schrieb:
> Das gilt aber nur wenn Serdes in Logik implementiert wird. Neuere FPGA
> haben das aber hart
> in den IO-Blöcken implementiert und können entsprechend schnell getaktet
> werden.
Da hat sich wohl jemand vom Marketing bla-bla auf der ersten Seite des 
Datenblatts einlullen lassen. Der Serialisierungstakt wird auch bei den 
aktuellsten FPGAs in den normalen PLLs / MMCMs erzeugt und muss 
dementsprechend über ein normales Taktnetz zum serdes kommen. Man ist 
also was die Abtastfrequenz des Eingangs betrifft ganz genau so 
eingeschränkt wie ohne serdes.

von Kalle (Gast)


Lesenswert?

Gustl B. schrieb:
> Nun, 800 MHz kann der Spartan7 nicht. Ich werde jetzt mal mit 400
> versuchen, das sollte von der zeitlichen Auflösung auch genügen.

Klar, der kann selbst auf den Billig-Speedgrades 950 Mb/s DDR

von Gustl B. (-gb-)


Lesenswert?

OK, also muss ich den von der PLL nur mit der halben Frequenz füttern? 
400 MHz kann die PLL, 800 MHz nicht.

clk_in ist dann also der schnelle Sampletakt, also bei DDR die Hälfte, 
wenn ich also 800 MHz Abtastrate haben möchte gebe ich da 400 MHz rein.

Dann wähle ich einen Serialisierungsfaktor von z. B. 8 Das bedeutet 
doch, dass während 4 Takten vom 400 MHz Takt diese 8 Bits reingeschoben 
werden.

clk_div_in ist dann quasi der Frametakt für jeweils 8 Samples. Das wäre 
dann wegen DDR nicht der Sampletakt durch den Serialisierungsfaktor, 
sondern der Sampletakt nur durch den halben Serialisierungsfaktor, bei 
mir also 100 MHz weil 1 Takt bei 100 MHz entspricht 4 Takten bei 400 MHz 
und das entspricht wegen DDR wieder den 8 Bits die geschoben werden. Mit 
diesem 100 MHz Takt würde ich dann auch die data_in_to_device 
übernehmen.

Muss ich den Port bitslip bedienen oder kann ich den dauerhaft auf '0' 
legen?

Danke!

: Bearbeitet durch User
von Blechbieger (Gast)


Lesenswert?

Kalle schrieb:
> Der Serialisierungstakt wird auch bei den aktuellsten FPGAs in den
> normalen PLLs / MMCMs erzeugt und muss dementsprechend über ein normales
> Taktnetz zum serdes kommen.

Ich weiß ja nicht wie es beim Spartan 7 aussieht aber beim Cyclone 10 GX 
geht der Serdes-Takt von der IO-PLL derselben Bank über dedizierte 
Taktleitungen zur Serdes-Hardware.

Gustl B. schrieb:
> OK, also muss ich den von der PLL nur mit der halben Frequenz füttern?
> 400 MHz kann die PLL, 800 MHz nicht.

Du kannst die fast mit einem beliebigen Takt füttern, es muss nur einen 
N/M Teiler geben der passt. Am besten im Wizard ausprobieren.

von Gustl B. (-gb-)


Lesenswert?

Im Wizard stellt man keinen Takt ein, da erzeugt man nur den SerDes oder 
besser weil der ja schon da ist in Hardware wird nur der Code erzeugt 
der den mit den passenden Einstellungen bedient und benutzbar macht. 
Aber gut, das versuche ich mal.

von Blechbieger (Gast)


Lesenswert?

Ich meinte im Wizard für die PLL. Falls das mit im Serdes-Wizard gemacht 
wird natürlich nicht.

von Gustl B. (-gb-)


Lesenswert?

Der Plan ist da ein 2^n-Faches zu nehmen. Also 400 MHz an den SerDes der 
dann mit 800 MHz abtastet. Die Daten kommen dann in eine 100 MHz Domäne 
(clk_div).

Die Kommunikation läuft über 21 Takte mit 100 MHz, macht bei 8-facher 
Überabtastung 168 Bits die ich dann bekomme. Daraus kann ich dann die 16 
sinnvollen Bits auswählen. Also mit einem Offset und nach diesem Offset 
16 mal jedes 8te Bit. Das wäre also schon variabel einstellbar.

von Gustl B. (-gb-)


Lesenswert?

So, da bin ich wieder. Hatte leider erst jetzt etwas Zeit.

Zur Übersicht:

Ich habe an den ADC 3 ADCs angeschlossen die jeweils 4 SDO Leitungen 
haben. Dazu habe ich für jeden dieser ADCs eine VHDL Komponente. In jede 
dieser Komponenten möchte ich jetzt für jede der 4 SDO Leitungen einen 
SerDes einbauen. Einen habe ich schonmal reingetan aber das macht Ärger 
und ich weiß nicht warum.

Die Komponente bekommt ein SDO und die Takte als
clk100 : in std_logic;
clk400 : in std_logic;
LTC2325_SDO_1: in std_logic;
rein.

Da geht es dann weiter in der SerDes:
1
SerDes_1: selectio_wiz_0
2
generic map(
3
  SYS_W => 1,
4
  DEV_W => 8)
5
port map(
6
  data_in_from_pins => "" & LTC2325_SDO_1,
7
  data_in_to_device => data_parallel_1,
8
  bitslip => "0",
9
  clk_in => clk400,
10
  clk_div_in => clk100,
11
  io_reset => '0');

Wobei:
signal data_parallel_1: std_logic_vector(7 downto 0):=(others => '0');

Also sollten jetzt schön immer neue 8Bit Daten rausfallen.

Die werden dann in einem Prozess
1
process begin
2
  wait until rising_edge(clk100);
3
  data_buffer_1 <= data_buffer_1(159 downto 0) & data_parallel_1;
4
end process;

In ein fettes Schieberegister gepackt woraus dann später mal die 
korrekten 16 Bits auserwählt werden.

signal data_buffer_1: std_logic_vector(167 downto 0):=(others => '0');

Tja, was geht nicht?
Das weiß ich nicht so genau. In der Simulation ist alles was aus dem 
SerDes rauskommt rot. Warum steght nicht da und wenn ich mir die Driver 
reporten lasse steht da immer der gleiche da?! Ich habe auch alle 
Signale mit Defaultwerten belegt.

Edit: Hatte den Reset nicht bedient. Habe jetzt aber ein weiteres 
Problem: Die Daten auf dem parallelen Ausgang sind jetzt entweder alle 
'1' oder alle '0'. Genau das kann ich aber nicht gebrauchen.

: Bearbeitet durch User
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.