Forum: FPGA, VHDL & Co. Ist timing violation entscheidend, auch wenn keines der Signale im naechsten CLK cycle benoetigt?


von ppuls (Gast)


Lesenswert?

Hallo!

Im Beispiel unten wird ein Zeitstempel fuer die Zeit, die zwischen vier 
low/high Wechseln von (i_signal_sync) vergeht, ermittelt, durch vier 
geteilt (untere zwei Bits von r_timersum verworfen), und letztlich an 
ein FIFO weitergegeben (o_timerout, o_newdata). Nachdem ich den 10-Bit 
timer in einen 4-bit und einen 6-bit Timer aufgeteilt habe (warum macht 
Xilinx ISE dies eigentlich nicht selber im Fall der Faelle?!), lief das 
Design das erste Mal ohne violation der (von der DCM Routine automatisch 
erstellten) timing constraints durch (worst slack +0.1ns).

Als ich nun jedoch noch die Overflow-Kondition (r_overflow) mit 
eingebaut habe, findet selbst SmartXplorer keine Strategie mehr, um das 
Ganze bei 250 MHz noch ohne Violation unterzubringen (worst slack -0.2ns 
fuer timersum2 oder timerout). Wenn ich das richtig verstehe, bedeutet 
das aber doch lediglich, dass timerout nicht direkt zum naechsten 
clk_250 Zyklus korrekte Daten enthaelt, oder?

Tatsaechlich uebernehme ich den Timerstand ja schon direkt nach dem 
vierten low/high Wechsel von i_signal, aber das newdata Signal, was 
letztlich zum Export ins FIFO fuehrt, wird ja erst beim darauffolgenden 
low/high Wechsel gesetzt (zu dem Zeitpunkt liegen dann sowohl Timerstand 
und newdata Signal fuer die gesamte Zeit bis zum wiederum naechsten 
low/high Wechsel an). Das FIFO ist mit 50 MHz getaktet, und sowohl 
newdata als auch timerout werden noch ueber ein FF von der 250MHz in die 
50MHz Domaene ueberfuehrt, obwohl das wegen DCM vielleicht noch nicht 
einmal noetig gewesen waere.

i_signal ist ein externes Signal und kann sich vielleicht maximal alle 
40ns einen low/high Wechsel aufweisen, bin ich damit auf der sicheren 
Seite, auch wenn das Design diese Timing-Violation zeigt?
1
library ieee;                     
2
use ieee.std_logic_1164.all;            
3
use ieee.numeric_std.all;             
4
5
entity proc250 is
6
 Port ( i_clk_250      : in  STD_LOGIC;
7
        i_signal_sync  : in  STD_LOGIC;
8
        i_signal_last  : in  std_logic;
9
        i_rst_sync     : in  std_logic;
10
        o_timerout     : out unsigned(7 downto 0);
11
        o_newdata      : out std_logic
12
       );
13
end proc50;
14
15
architecture Behavioral of proc250 is
16
17
  signal r_counter        : unsigned(3 downto 0) := (others => '0');
18
  
19
  signal r_timersum       : unsigned(3 downto 0) := (others => '0');
20
  signal r_timersum2      : unsigned(5 downto 0) := (others => '0');
21
  signal r_timerout       : unsigned(7 downto 0) := (others => '0');
22
  signal r_overflow       : std_logic;
23
  
24
  signal r_newdata        : std_logic;
25
26
begin
27
28
process(i_clk_250) 
29
begin
30
  if(rising_edge(i_clk_250)) then
31
   r_timersum <= r_timersum + 1;
32
   if(r_timersum2 = 63) then
33
    r_overflow <= '1';
34
   else
35
    r_overflow <= r_overflow;  
36
   end if;
37
   if(r_timersum = 15) then
38
    r_timersum2 <= r_timersum2 + 1;
39
   else
40
    r_timersum2 <= r_timersum2;
41
   end if;
42
   
43
    if(i_signal_sync = '1' and i_signal_last = '0') then
44
      if(r_counter = 3) then
45
        r_counter <= (others => '0');
46
        r_timersum <= (others => '0');
47
        r_timersum2 <= (others => '0');
48
        if(r_overflow = '1') then
49
         r_timerout <= "00000000";
50
        else
51
         r_timerout <= r_timersum2 & r_timersum(3 downto 2);
52
        end if;
53
        r_newdata <= '0';
54
        r_overflow <= '0';
55
        
56
      elsif(r_counter = 0) then
57
         r_counter <= r_counter + 1;
58
        r_timerout <= r_timerout;
59
        r_newdata <= '1';     
60
        
61
      else
62
         r_counter <= r_counter + 1;
63
        r_timerout <= (others => '0');
64
        r_newdata <= '0';         
65
      end if;
66
      
67
    else
68
      r_counter <= r_counter;
69
      r_timerout <= r_timerout; 
70
      r_newdata <= r_newdata;
71
    end if;
72
  end if;
73
end process;
74
75
o_timerout <= r_timerout; 
76
o_newdata <= r_newdata; 
77
78
end Behavioral;

Viele Gruesse

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


Lesenswert?

Definiere für solche Signale doch einfach Multi Cycle Constraints.

von ppuls (Gast)


Lesenswert?

Danke, Lothar, für den Begriff der Multi Cycle Constraints. Kannte ich 
so gar nicht, dass diese FROM TO Felder für diesen Zweck sind! Das 
scheint ja exakt zu beschreiben, was ich meinte damit, dass diese zwei 
Signale ja gar nicht im nächsen Cycle, sondern erst deutlich später 
gültig sein müssen. Viele Infos habe ich dazu jetzt nicht auf die 
Schnelle gefunden, ausser einem ellenlangen Guide über Constraints 
generell, und beispielsweise diese Xilinx Answer
https://www.xilinx.com/support/answers/9416.html

Also die Paths muss ich alle einzeln identifizieren, und dann selber 
überlegen, nach wie vielen Clock Cycles das Signal frühestens gültig 
ist? Die Xilinx Tools können nicht erkennen, dass wie im oben genannten 
Beispiel ja aufgrund der

aufeinanderfolgenden Zuweisung von timerout und newdata,
der alleinigen Verwendung beider Signale gemeinsam,
und dem ausschließlichen Auftauchen der Signale in den entsprechend 
getakteten Prozessen,

sowieso bereits mindestens X cycles 'ins Land gehen' bevor die Signale 
verwendet werden? Oder hätte das erkannt werden sollen, und ich hab in 
meinem Code möglicherweise noch irgendwo irgendeine "Leiche", die ohne 
dass ich das direkt sehe doch bereits ein gültiges Signal im nächsten 
Clock cycle erwartet?

Besten Dank schonmal!

von Weltbester FPGA-Pongo (Gast)


Lesenswert?

ppuls schrieb:
> else
>     r_overflow <= r_overflow;
>    end if;

Was soll das denn bitte bewirken?

von ppuls (Gast)


Lesenswert?

Weltbester FPGA-Pongo schrieb im Beitrag #5557309:
> Was soll das denn bitte bewirken?

Die Idee ist, wenn irgendwann zwischen dem 1. Und 4. Signalwechsel 
low/high der Timer überlauft, diesen overflow festzuhalten und statt 
einer falschen Zeit eben diesen Überlauf (0) auszugeben. Der zeitstempel 
0 ist real nicht möglich, eine doppelbelegung dieses Ausgabewertes ist 
deshalb hier unkritisch.

Die Schreibweise, r_name für alles, was ein register bewirken wird, 
w_name für direkte Verbindungen, und i_name und o_name lediglich für 
echte inputs und outputs zu benutzen, habe ich vermutlich zu Beginn in 
einem vhdl Lehrbuch so gesehen und seitdem so verwendet. Ich fand, es 
kann helfen, z. B. Bei allen r_ daran zu denken, in  jedem möglichen 
case einen entsprechenden Wert zuzuweisen und ungewollte latches zu 
verhindern.

Würde ich die von dir zitierte Zeile einfach weglassen, wäre doch 
overflow nur direkt nach jedem timer2 Überlauf für 15 Timer cycles 
gesetzt, und danach nicht mehr? Sicher gibt es da andere Möglichkeiten, 
das umzusetzen. Aber dies schien seinen Zweck perfekt zu erfüllen.

Viele Grüße!

von Christoph (Gast)


Lesenswert?

ppuls schrieb:
> Also die Paths muss ich alle einzeln identifizieren, und dann selber
> überlegen, nach wie vielen Clock Cycles das Signal frühestens gültig
> ist?

Ja. Habe bisher kein Tool angetroffen, dass so etwas selbständig 
erkennt.

Immerhin darfst du bei den meisten Tools in den From/To Feldern Wildcard 
Symbole (*/?) nutzen um gleich ganze Busse oder mehrere Instanzen 
abzudecken.

von Zeigefinger (Gast)


Lesenswert?

ppuls schrieb:
> Also die Paths muss ich alle einzeln identifizieren, und dann selber
> überlegen, nach wie vielen Clock Cycles das Signal frühestens gültig
> ist? Die Xilinx Tools können nicht erkennen, dass wie im oben genannten
> Beispiel ja aufgrund der
>
> aufeinanderfolgenden Zuweisung von timerout und newdata,
> der alleinigen Verwendung beider Signale gemeinsam,
> und dem ausschließlichen Auftauchen der Signale in den entsprechend
> getakteten Prozessen,

Naja, du hast doch die Zeilen geschrieben, also musste auch wissen was 
du damit erreichen willst.
Und eigentlich analysiert man nicht den code nachher, sondern überlegt 
sich vorher, in welchem Takt die signale gültig sein sollten und 
schreibt den code entsprechend.Also erst ein timing 
Diagramm,Ablaufdiagramm zeichnen und dann coden. Die entscheidung, was 
du überhaupt haben willst, können dir die tools nicht abnehmen.

Das ist eben einen Unterschied zur C-Programmierung, wo man die 
zeitlichen Abläufe komplett dem Compiler/runtime-environment überlässt 
und drauf hofft das compiler schon selber optimiert.

Du musst halt ein Gefühl für "chiplevel" entwickeln: wieviel Logik/LUT's 
werden zwischen 2 FF's geklemmt und felche maximale taktfrequenz 
resultiert daraus. Wenns zu langsam ist, kann man immer noch Pipeline-FF 
einbauen und manchmal schubsen die tools die FF auch so das die optimale 
Taktfrequenz realisiert wird ("register balancing")

von Markus F. (mfro)


Lesenswert?

Zeigefinger schrieb:
>> Also die Paths muss ich alle einzeln identifizieren, und dann selber
>> überlegen, nach wie vielen Clock Cycles das Signal frühestens gültig
>> ist?

Natürlich.

Und dein Code sollte natürlich dazu passen - wenn Du einen 
Multicycle-Pfad im Design definierst und nachher trotzdem zu früh 
ungültige Daten abholst, ist das dein Fehler - nicht der der Tools.

Der Multicycle-Pfad macht nur für die Timing-Analyse das Timing-Fenster 
breiter. Ob dein Design das hergibt, musst Du selber wissen ...

von ppuls (Gast)


Lesenswert?

Vielen Dank fuer eure Antworten! Damit habe ich jetzt auf jeden Fall 
verstanden, was an dieser Stelle von mir gefordert ist. Vielleicht muss 
ich da nachher noch einmal laenger drueber gruebeln, aber ganz klar ist 
mir noch nicht, wieso ich das fuer interne Signale ...

> Natürlich

... selbst ueberlegen muss. Ich meine, klar, wenn ich so ein schnelles 
Signal einfach hart auf irgendeinen Ausgangspin lege, und die IDE dann 
natuerlich keine Ahnung hat, was fuer Timing Anforderungen die externe 
Peripherie hat, logisch dass ich das selbst formulieren muss. Aber 
intern, wenn ich doch keine andere Zeile VHDL geschrieben habe, die 
irgendetwas beschreibt, was sich dieses Signal jemals frueher als "drei 
Takte spaeter" ansehen kann, wieso sollte sich das nicht automatisch 
herausfinden lassen?

Nur als Beispiel, fuer welchen Fall ich vor kurzem an anderere Stelle 
als Laie eine "Entspannung der Timings" erwartet haette (die wegen des 
nicht definierten Multi Path dann wohl nicht eingetreteten ist): Anstatt 
in einem State alle Counter zu aktualiseren, und das entsprechende 
"valid" Signal im selben State zu setzen (das nun letztlich anderen 
Prozessen sagt, diese Counter zu "lesen"), habe ich das Setzen des valid 
Signals erst im Folgetakt erledigt, zusammen mit den "festgehaltenen" 
Counterwerten des vorigen Taktes. Das war fuer mich an dieser Stelle 
logisch, dass das direkt beruecksichtigt wird: Keins der Zeahlerstaende 
ist nach extern geroutet, und keins der internen Beschreibungen "liest" 
sie bis zum naechsten State.

Wie auch immer, ich glaube ich werde mir ein genaues Timingdiagramm, so 
wie ich es geplant habe, entwerfen, dann ueberpruefen, ob es mit meiner 
Beschreibung zusammen passt, und als letztes an all den Stellen, wo 
solche gewollten Multi Clk Pathes "entstanden" sind, die Constraints 
entsprechend aendern. Wahrscheinlich fuehrt das letztlich noch zu einer 
viel hoehreren maximal errechneten Taktfrequenz (wobei ich nun nach 
einigen kleinen Aenderungen an anderer Stelle auch gerade eben knapp 
"positiv" durch die Timing Ueberpruefung schramme, ohne irgendwelche 
Multi Path Definitionen).

Besten Dank nochmal!

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.