Forum: FPGA, VHDL & Co. Timespec von ISE nach Vivado


von Mirko H. (mirkoh)


Lesenswert?

Hallo liebe FPGA Gemeinde,

bitte nicht gleich steinigen, dass ich im Jahr 2020 noch ISE verwendet 
habe :-) Ich bin ambitionierter Hobbyist und ISE funktionierte einfach 
super für mich die ganze Zeit.

Nun bin ich durch äußere Umstände gezwungen auf Vivado zu wechseln und 
verzweifle an einer "Kleinigkeit":

Wie konvertiere ich das hier richtig von UCF nach XDC:
1
## EAE's combinatorial division networks take longer than
2
## the regular clock period, so we specify a timing constraint
3
## for them (see also the comments in EAE.vhd)
4
INST "eae_inst/op*" TNM="EAE_OPS";
5
INST "eae_inst/res*" TNM="EAE_RES";
6
TIMESPEC TS_EAE=FROM "EAE_OPS" TO "EAE_RES" 32 ns;

Weitere Details dazu gibt es hier:

https://github.com/sy2002/QNICE-FPGA/blob/develop/hw/xilinx/MEGA65/ISE/MEGA65.ucf#L12

Ich habe schon relativ intensiv gegoogelt und habe nun die Hypothese, 
dass ich irgendwie mit set_max_delay arbeiten muss?

Insbesondere in den forums.xilinx.com sind viele Beispiele und auch in 
ug911-vivado-migration.pdf, aber ich verstehe nicht, wie ich diese 
beiden "Punkte" in meinem kombinatorischen Netzwerk ("op*" und "res*") 
so im XDC spezifizieren kann, dass Vivado sie in set_max_delay auch 
findet.

Hier ist der zugehörige Quelltext:

https://github.com/sy2002/QNICE-FPGA/blob/develop/vhdl/EAE.vhd

Viele Grüße
  Mirko

von Blechbieger (Gast)


Lesenswert?

Das hört sich mehr nach einem Fall für eine Multi-Cycle Exception an.

von Mirko H. (mirkoh)


Lesenswert?

Danke für den Tipp! Also "set_multicycle_path" anstatt "set_max_delay"?

Aber wie spezifiziere ich die Endpunkte für "set_multicycle_path"? Wie 
transformiere ich die ehmals sehr eleganten
1
INST "eae_inst/op*" TNM="EAE_OPS";
2
INST "eae_inst/res*" TNM="EAE_RES";

in etwas, was Vivado versteht?

von Tobias B. (Firma: www.elpra.de) (ttobsen) Benutzerseite


Lesenswert?

Kannst du den zugehoerigen VHDL Code mal noch hier reinstellen? Dann hat 
man eine Vorstellung was da passiert und was vernuenftige Constraints 
waeren.

von Mirko H. (mirkoh)


Lesenswert?

Danke Dir, Tobias für die Rückmeldung. Der zugehörige VHDL Code ist 
hier:

https://github.com/sy2002/QNICE-FPGA/blob/develop/vhdl/EAE.vhd

von Tobias B. (Firma: www.elpra.de) (ttobsen) Benutzerseite


Lesenswert?

Mirko H. schrieb:
> Danke Dir, Tobias für die Rückmeldung. Der zugehörige VHDL Code
> ist
> hier:
>
> https://github.com/sy2002/QNICE-FPGA/blob/develop/vhdl/EAE.vhd

Ups, war wohl ein Feierabend Bier zuviel. :-(

Welche Periode hat den die Clock? Das ganze ist wahrscheinlich schon ein 
Multicycle, nur dass nicht die Clock PEriode sondern fix 32 ns angegeben 
sind.

In Vivado waere das sowas in etwa
1
set_multicycle_path -from [get_cells eae_inst/op*] -to [get_cells "eae_inst/res*"] -setup 2
2
set_multicycle_path -from [get_cells eae_inst/op*] -to [get_cells "eae_inst/res*"] -hold 1

DAs waere ein Beispiel fuer 2 Clockzyklen. In deinem Fall waeren das 
dann die 32 ns geteilt durch die Clock Periode. Bei -hold traegst dann 
eins weniger ein als bei -setup.

Bisschen Lesestoff dazu ist in UG903 zu finden, Kapitel Timing 
Exceptions -> Multicycle Path. UG911 ist auch ganz interessant fuer 
dich, da sind jede Menge Hilfestellungen zu finden um von IE nach Vivado 
zu migirieren (fuer dein Fall ist auf Seite 25 ein Beispiel).

Kleiner Nachtrag: Ich habs jetzt nicht ausprobiert, evtl. sind noch 
kleine Syntax Fehler drin. Aber vom Prinzip her muesste es korrekt sein. 
;-)

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

Vorausgeschickt: mit X* kenn' ich mich nicht aus. Ob das folgende (das 
in Quartus so funktioniert) auch bei Vivado so läuft, weiß ich nicht.
Jedenfalls ist ein .sdc-File bei Quartus einfach ein tcl Script.

Mirko H. schrieb:
> Aber wie spezifiziere ich die Endpunkte für "set_multicycle_path"? Wie
> transformiere ich die ehmals sehr eleganten
> INST "eae_inst/op*" TNM="EAE_OPS";
> INST "eae_inst/res*" TNM="EAE_RES";

um ehrlich zu sein: besonders "elegant" finde ich das nicht. Das ist 
doch nichts anderes als eine Gruppenbildung?
1
set eae_ops [get_cells eae_inst/op*]
2
set eae_res [get_cells eae_inst/res*]

macht praktisch dasselbe: alle Elemente, auf die die Wildcards passen, 
sind anschliessend unter den Namen $eae_ops bzw. $eae_res ansprechbar.

Dann - wie von Tobias B. schon geschrieben, diese Gruppen einfach in 
set_multicycle_path verwenden:
1
set_multicycle_path -from $eae_ops -to $eae_res -setup 2
2
set_multicycle_path -from $eae_ops -to $eae_res -hold 1

Das erste Statement sagt, dass auf allen Pfaden von $eae_ops nach 
$eae_res nicht mehr die Default-Regel gilt, dass das/die Signal(e) vor 
der nächsten (1 = default) Latch-Taktflanke stabil am Ziel verfügbar 
sein müssen, sondern dass die übernächste (2) auch noch reicht.
Das zweite Statement sagt, dass das/die Signal(e) statt frühestens 
nach der (0. = default) an der (1). Latch-Taktflanke danach am Ziel 
ankommen dürfen.

Ich finde das ehrlich gesagt viel eleganter und eingängiger als
1
TIMESPEC TS_EAE=FROM "EAE_OPS" TO "EAE_RES" 32 ns;
Noch dazu musst Du das Statement ändern, wenn Du den Takt änderst. 
set_multicycle_path musst Du nur ändern, wenn Du das Design änderst.

von Mirko H. (mirkoh)


Lesenswert?

Hallo Tobias und Markus,

herzlichen Dank für Eure Hilfe! Auch die Pointer zu der Doku, sowie die 
Erklärung zu -setup und -hold waren hilfreich.

Ich konnte inzwischen Timing Closure erreichen. Für das Archiv, falls 
mal jemand anders danach googelt - hier sind die finalen Constraints, 
die bei mir geholfen haben:
1
set_multicycle_path -from [get_cells {{eae_inst/op0_reg[*]} {eae_inst/op1_reg[*]}}] -to [get_cells {eae_inst/res_reg[*]}] -setup 3
2
3
set_multicycle_path -from [get_cells {{eae_inst/op0_reg[*]} {eae_inst/op1_reg[*]}}] -to [get_cells {eae_inst/res_reg[*]}] -hold 2

(Jedes set_multicycle_path stellt eine Zeile dar; der Zeilen-Umbruch 
wird hier von der Forumsoftware gemacht.)

Man sieht, dass Vivado im Gegensatz zu ISE deutlich spezfischer ist: Man 
gibt noch mit _reg[*] die Endpunkte (in meinem Fall sind es Register) 
an.

von Blechbieger (Gast)


Lesenswert?

Ein wichtiger Punkt noch, es gibt keinerlei Konsistenzcheck zwischen 
Multi-Cycle-Timing-Constraints und VHDL-Code. D.h. du musst selber 
sicherstellen dass das Ergebnis erst nach drei Takten im Flipflops 
gespeichert wird. Deutlich unsauberer ist die hier genutzte Variante 
jeden Taktzyklus abzuspeichern und erst in der aufrufenden Einheit 
sicherzustellen dass lange genug gewartet wird bevor das Ergebnis 
gelesen wird.

von Tobias B. (Firma: www.elpra.de) (ttobsen) Benutzerseite


Lesenswert?

Allgemein ist das ganze Design etwas verhunzt:
1
               when eaeDIVU =>
2
                  res(15 downto 0)  <= std_logic_vector(op0_u / op1_u);
3
                  res(31 downto 16) <= std_logic_vector(op0_u mod op1_u);
4
                  
5
               when eaeDIVS =>
6
                  res(15 downto 0)  <= std_logic_vector(op0_s / op1_s);
7
                  res(31 downto 16) <= std_logic_vector(op0_s mod op1_s);

finde ich doch ein bisschen viel verlangt in einem Takt Zyklus.

von Mirko H. (mirkoh)


Lesenswert?

@Blechbieger und @Tobias: Im Allgemeinen kann ich Euch da natürlich 
nicht widersprechen.

In meinem speziellen Anwendungsfall aber ist die Grenze zwischen 
"verhunzt" und "elegant oder zumindest zweckmäßig" fließend :-)

Das EAE (Extended Arithmetic Element) ist ein Coprozessor für die QNICE 
CPU, der via Memory-Mapped IO (MMIO) angesprochen wird. Das dauert 
relativ lange, da die CPU in so einem Fall 4-5 Zyklen für eine 
Instruktion braucht.

Bis also nach dem Laden der Operanden auf das Ergebnis zugegriffen wird, 
ist aufgrund der Systemarchitektur garantiert, dass genug Zyklen 
vergangen sind und das Rechenergebnis sich gesettled hat.

Früher hatte ich eine VHDL Version, die ein BUSY Flag erzeugt hat, 
während der Rechnerei. Dieses Flag hatte ich dann im Assembler Code 
abgefragt (natürlich hätte man alternativ auch mit CPU Waitstates 
arbeiten können):

https://github.com/sy2002/QNICE-FPGA/blob/master/monitor/math_library.asm#L61

Das war aber aufgrund der garantierten Timing-Gegebenheiten in dieser 
speziellen Architektur Konstellation dann doch nicht mehr nötig...

von Tobias B. (Firma: www.elpra.de) (ttobsen) Benutzerseite


Lesenswert?

Mirko H. schrieb:
> In meinem speziellen Anwendungsfall aber ist die Grenze zwischen
> "verhunzt" und "elegant oder zumindest zweckmäßig" fließend :-)

Was mich hier halt wundert ist, dass das ueberhaupt mit einem Multicycle 
funktioniert. Ich habe gerade mal folgendes mit Vivado synthetisieren 
lassen:
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
entity toplevel is
6
    Port ( a : in STD_LOGIC_VECTOR (15 downto 0);
7
           b : in STD_LOGIC_VECTOR (15 downto 0);
8
           res : out STD_LOGIC_VECTOR (31 downto 0));
9
end toplevel;
10
11
architecture Behavioral of toplevel is
12
13
begin
14
15
  res(15 downto  0) <= std_logic_vector(unsigned(a) / unsigned(b));
16
  res(31 downto 16) <= std_logic_vector(unsigned(a) mod unsigned(b));
17
18
end Behavioral;

Benoetigt ganze 575 LUTs und ich bin etwas verwundert, dass das 
innerhalb von 3 Taktzyklen laufen soll. Wird da evtl. ein riesen ROM 
instanziert?

von Mirko H. (mirkoh)


Lesenswert?

Tobias B. schrieb:
> Benoetigt ganze 575 LUTs und ich bin etwas verwundert, dass das
> innerhalb von 3 Taktzyklen laufen soll. Wird da evtl. ein riesen ROM
> instanziert?

Sehr gute Frage!

Ich bin leider nicht firm genug sie zu beantworten.

Mir war auch aufgefallen, dass sowohl bei ISE als auch bei Vivado mit 
dem Target Artix-7 (ich nutze einen XC7A100T) die Multiplikation mit dem 
DSP implementiert wird und die Division mit eine großen Menge von LUTs.

Als reiner "self-taught" Hobby-VHDL-Entwickler fehlt mir etwas der 
Hintergrund, ob das nun OK so ist, oder ob ich da nochmal 'ran sollte.

Sowohl mit ISE als auch nun (dank Euren Tipps und Hinweisen) mit Vivado 
erreiche ich Timing Closure.

Und es funktioniert einwandfrei: Die Division wird bei der FAT32 
Implementierung des QNICE ausgiebig verwendet und sie scheint seit dem 
Jahr 2016, als ich das implementierte, 100% stabil zu funktionieren.

==> Von daher würde ich diesen Thread als "erfolgreich beantwortet" 
betrachten wollen. Danke nochmal an alle für die Hilfe.

Falls aber jemand von Euch noch Tipps hat, wie ich das Ganze besser 
machen könnte oder sollte und noch ein paar Pointer für mich zum 
Lesen/Lernen hat - oder falls jemand Lust hat, das Ganze auf eine 
wirklich neue (professionellere) Ebene zu heben und auf GitHub einen 
Pull Request zu machen: Herzlich willkommen, würde mich freuen.

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

Tobias B. schrieb:
> Benoetigt ganze 575 LUTs und ich bin etwas verwundert, dass das
> innerhalb von 3 Taktzyklen laufen soll. Wird da evtl. ein riesen ROM
> instanziert?

Quartus macht 576 (warum eigentlich?) LEs draus und der Timing Analyzer 
ermittelt für das Kombinatorik-Monster eine Worst-Case Durchlaufzeit von 
etwa 31 ns für einen Cyclone III (Speedgrade 6) im besten Fall.

Könnte also mit einem etwas moderneren Gerät gerade so hinkommen.

von Tobias B. (Firma: www.elpra.de) (ttobsen) Benutzerseite


Lesenswert?

Ok, das ist stark. Das ist eine wirkliche Alternative zu nemm Divider 
IP, sofern man kein Pipelining braucht. Definitiv etwas was ich mir im 
Hinterkopf behalten. :-)

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.