Forum: FPGA, VHDL & Co. CDC absichtlich falsch machen


von Joschua C. (Gast)


Lesenswert?

Hallo

Ich probiere in Verilog mit meinem CycloneIV gerade etwas mit CDC herum 
und habe dazu folgendes geschrieben:
1
module HardwareTbCDC(
2
  ClkFastI,
3
  ClkSlowI,
4
  nRstI,
5
  aLedO,
6
  ErrorO
7
);
8
9
10
  //Activate this define so leds show lower databits instead of errorcounter
11
  //`define LEDS_SHOW_DATA
12
13
  //Length of testdata which has to cross clockdomains
14
  parameter TESTLENGTH = 64;
15
16
  //If counter with this length overflows, the next test is started
17
  parameter OVERFLOW_COUNTER_LENGTH = 4;
18
19
  //Clocks and Reset
20
  input ClkFastI, ClkSlowI, nRstI;
21
22
  //Error counting or data showing led
23
  output [5:0] aLedO;
24
25
  //Errorled, shows ErrorCDC
26
  output ErrorO;
27
28
  //Inputdata, is modified in each testcycle
29
  reg [TESTLENGTH-1:0] ainputData;
30
31
  //Clock divider for clock which generates testdata
32
  reg [OVERFLOW_COUNTER_LENGTH-1:0] aclockDivider;
33
34
  //Outputdata as reg
35
  reg [TESTLENGTH-1:0] aoutputDataReg;
36
37
  //Trigger for checking testdata at output on changes
38
  reg [TESTLENGTH-1:0] aoutputDataTrigger;
39
40
  //Errorcounter
41
  reg [5:0] aerrorCounter;
42
43
  //Resettasks
44
  task ResetClkSlowI;
45
  begin
46
    ainputData <= 0;
47
    aclockDivider <= 0;
48
  end
49
  endtask
50
51
  task ResetClkFastI;
52
  begin
53
    aoutputDataReg <= 0;
54
    aerrorCounter <= 0;
55
    aoutputDataTrigger <= 0;
56
  end
57
  endtask
58
59
  //Generate testdata
60
  always @(posedge ClkSlowI)
61
  begin
62
63
    //Activate Reset
64
    if(!nRstI)
65
    begin
66
      ResetClkSlowI();
67
68
    end
69
70
    //Or do normal job
71
    else
72
    begin
73
74
      //Increment clock divider which needs to overflow for new testcycle
75
      aclockDivider <= aclockDivider + 1'b1;
76
77
      //Check for overflow
78
      if(&aclockDivider)
79
      begin
80
        //Create new stimulus
81
        ainputData <= ~(ainputData);
82
83
      end
84
    end
85
  end
86
87
88
  //Check changed data for errors
89
  always @(posedge ClkFastI)
90
  begin
91
92
    //Reset
93
    if(!nRstI)
94
    begin
95
      ResetClkFastI();
96
97
    end
98
    else
99
    begin
100
101
      //Copy data between clockdomains
102
      aoutputDataReg <= ainputData;
103
104
      //Trigger for detecting changed outputdata
105
      aoutputDataTrigger <= aoutputDataReg;
106
107
      //Increment error counting led if there is an error at changed outputdata
108
      if((aoutputDataTrigger != aoutputDataReg) && (ainputData != aoutputDataReg))
109
      begin
110
        aerrorCounter <= aerrorCounter + 1'b1;
111
112
      end
113
    end
114
  end
115
116
  //Show errorcounter or data output depending on define
117
  `ifdef LEDS_SHOW_DATA
118
    assign aLedO = aoutputData[5:0];
119
120
  `else
121
    assign aLedO = aerrorCounter;
122
123
  `endif
124
125
endmodule

Das Modul wird mit zwei Clocks instanziiert (bei mir ist ClkFastI 25Mhz 
und ClkSlowI keinen MHz langsamer) und soll mit dem langsamen Takt immer 
ein paar Bit (ainputData) kippen lassen. Diese Bits sollen aus der 
langsamen clockdomain in die schnelle überführt werden. Das mache ich 
mit der Zeile

aoutputDataReg <= ainputData;

absichtlich falsch, das CDC wird hier nicht beachtet. Direkt darunter 
erkennt aoutputDataTrigger als Flankenmerker Änderungen der Daten in der 
Zieldomain und prüft, ob die richtig angekommen sind. Wenn nicht, wird 
ein Fehlerzähler hochgezählt. Entweder dieser Zähler oder die untersten 
6 Datenbits werden als LEDs ausgegeben (je nachdem ob LEDS_SHOW_DATA 
definiert ist)

Ersetze ich

aoutputDataReg <= ainputData;

durch

aoutputDataReg <= ainputData + 1'b1;

und bringe so beim Daten Übertragen zwischen den Domains absichtlich 
einen Fehler rein, rennt der Zähler hoch. Sonst aber wird kein Fehler 
angezeigt. In der Simulation passt das Design soweit, nur dass ich 
CDC-Fehler dort nicht simulieren kann. Wo seht ihr den Fehler in dem 
Modul, der dafür verantwortlich ist, dass kein CDC-Fehler passiert?

von Markus F. (mfro)


Lesenswert?

Joschua C. schrieb:
> Das Modul wird mit zwei Clocks instanziiert (bei mir ist ClkFastI 25Mhz
> und ClkSlowI keinen MHz langsamer)

d.h. beide Clocks sind gleich schnell und ohne ein .sdc-File, das 
genaueres dazu erzählt, erst mal synchron?

Welche Art Fehler würdest Du dann erwarten?

: Bearbeitet durch User
von Joschua C. (Gast)


Lesenswert?

Oh sorry, der langsamere Clock ist 24,666 MHz groß, also die Clocks sind 
schon unterschiedlich, aber nur um einen kleinen Unterschied.

Meine Erwartung wäre, dass die Situation vorkommt, dass eine ClkFastI 
Flanke direkt nach einer ClkSlowI Flanke kommt, sodass die FFs von 
aoutputDataReg in Metastabiität kommen, weil aoutputDataReg am Takt 
ClkFastI hängt, aber Daten aus der Clockdomain ClkSlowI liest. Ich 
erwarte dann, dass einige der Bits, die aoutputDataReg aus ainputData 
erhält, nicht stimmen. Und genau das wird mit

if((aoutputDataTrigger != aoutputDataReg) && (ainputData != 
aoutputDataReg))

abgeprüft. Der Fehlerzähler zählt um eins hoch, wenn aputputDataReg sich 
geändert hat und wenn diese neuen Daten nicht mit der Quelle ainputData 
übereinstimmen. Doch genau diese rechte Bedingung (ainputData != 
aoutputDataReg) wird niemals wahr.

Mein TimeQuest eskaliert brutal und meckert, dass die Setup-Zeit auf dem 
Datenpfad

aoutputDataReg <= ainputData;

nicht eingehalten werden kann. Und genau das sollte doch eigentlich 
dafür Sorgen, dass die Bedinung (ainputData != aoutputDataReg) mal wahr 
wird, weil bei der Zuweisung aoutputDataReg <= ainputData; das CDC 
Problem auftritt.

von Markus F. (mfro)


Lesenswert?

Joschua C. schrieb:
> Und genau das wird mit
>
> if((aoutputDataTrigger != aoutputDataReg) && (ainputData !=
> aoutputDataReg))
>
> abgeprüft.

Mag sein, dass ich falsch liege (meine Verilog-Kenntnisse sind 
ausserordentlich rudimentär) aber wenn ich
1
b <= a;  // kann schiefgehen
2
c <= b;  // geht nicht schief (gleiche clock domain)
schreibe, muss meinem bescheidenen Verständnis nach
1
if (c != b)

immer false sein. Egal, ob blocking oder non-blocking assignment. Mag 
sein, dass der Wert nicht der ist, den Du gerne sehen würdest, aber 
identisch sollten die Werte schon sein. Wenn dein Zähler hochzählen 
soll, würde ich eher mehrfach aus der (falschen) clock domain lesen und 
die Werte vergleichen:
1
b <= a;
2
c <= a;
3
if (c != b)

von Lattice User (Gast)


Lesenswert?

Möglicherweise hat der Synthesizer dein 64 bit breites Register auf ein 
einzelnes reduziert. Du verändest diese ja immer auf die identische Art.

Wenn das passiert ist, bekommst du nur noch Fehler bei Metastabilität, 
und das tritt bei 24 MHz sehr selten auf.

Mach ein 64 bit breites Schieberegister, und füttere es mit einem LFSR.

von Joschua C. (Gast)


Lesenswert?

Zuerstmal danke für die Antworten und entschuldigt, dass ich so selten 
hier reinschaue. Bin gerade hart beschäftigt mit Regelungstechnik II :D

Markus F. schrieb:
> immer false sein. Egal, ob blocking oder non-blocking assignment. Mag
> sein, dass der Wert nicht der ist, den Du gerne sehen würdest, aber
> identisch sollten die Werte schon sein. Wenn dein Zähler hochzählen
> soll, würde ich eher mehrfach aus der (falschen) clock domain lesen und
> die Werte vergleichen:
> b <= a;
> c <= a;
> if (c != b)

Die Bedingung c != b wird wahr, b != a ist das Problem. Hab beide 
einzeln im FPGA getestet und den Zähler beobachtet. Deinen Vorschlag 
werde ich aber trotzdem mal ausprobieren. Der Ablauf soll sein:

posedge ClkSlowI: ainputData ändert sich

posedge ClkFastI: aoutputDataReg <= ainputData macht den CDC Fehler, 
aoutputDataTrigger <= aoutputDataReg ändert keine Daten, weil 
aoutputDataReg noch den gleichen Wert wie am Anfang hat

posedge CLKFastI: aoutputDataTrigger != aoutputDataReg wird jetzt wahr, 
weil jetzt der aktuelle, neue Wert von aoutputDataReg gelesen wurde. 
Dieser Wert wird gleichzeitig mit aoutputDataTrigger <= aoutputDataReg 
in die Flankenerkennung eingelesen, damit die Bedingung im nächsten Takt 
nicht mehr wahr wird.

Im Simulator läuft das auch.

Lattice User schrieb:
> Möglicherweise hat der Synthesizer dein 64 bit breites Register auf ein
> einzelnes reduziert. Du verändest diese ja immer auf die identische Art.
>
> Wenn das passiert ist, bekommst du nur noch Fehler bei Metastabilität,
> und das tritt bei 24 MHz sehr selten auf.
>
> Mach ein 64 bit breites Schieberegister, und füttere es mit einem LFSR.

Auch ein guter Hinweis. Einerseits sieht die .sdc Datei so aus, als 
hättest du Recht, andererseits wird der Chip aber randvoll, wenn ich die 
Bitbreite vergrößere.

Also werde ich mal den LFSR mit reinnehmen und zwei Mal die CDC 
Verletzung kopieren. Und dann drehe ich die PLL nochmal an die Grenze 
auf (TimeQuest sagt ja wo die Grenzen liegen) und lasse den zweiten 
always-Block mal auf fallende und steigende Flanke hören. Das sollte die 
Wahrscheinlichkeit von CDC Fehlern noch weiter steigern. Aber erst 
Regelungstechnik...

Viele Grüße
Joschua

von Joschua C. (Gast)


Lesenswert?

Lattice User schrieb:
> Möglicherweise hat der Synthesizer dein 64 bit breites Register auf ein
> einzelnes reduziert. Du verändest diese ja immer auf die identische Art.
>
> Wenn das passiert ist, bekommst du nur noch Fehler bei Metastabilität,
> und das tritt bei 24 MHz sehr selten auf.
>
> Mach ein 64 bit breites Schieberegister, und füttere es mit einem LFSR.

Genau das war das Problem. Mit LFSR wird nichts mehr wegoptimiert. Jetzt 
läufts wie erwartet. Ca. jede 10. Operation bei 64 Bit und einem PLL 
Verhältnis von 199/200 schlägt fehl. TimeQuest moniert jedes Bit von den 
64. Vielen Dank!

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.