Hallo.
Wenn ich zB für eine Art SPI-Slave folgendes Code-stück verwende, ist
das dann gröbster Müll, wegen gated clock oder wird das zu einem
clock-enable? kann man es prinzipiell so machen, also dass die
rising_edge so wie da nach einer bedingung abgefragt wird?
also zumindest ist es seeehr unüblich auf solche weise synchrone und
asynchrone Zuweisungen innerhalb eines Prozesses zu vermischen. Das wäre
nämlich ein beliebtes Stilmittel für einen Obfurscated VHDL Contest :D
Peter schrieb:> Obfurscated VHDL Contest
Da ist ein 'r' zuviel drin... ;-)
Wie auch immer: der original gepostete Code wäre ein Extrembeispiel für
einen eigenartigen Clock-Enable. Aber die Synthesetools können
heutzutage einiges ab:
http://www.lothar-miller.de/s9y/categories/6-Clock-EnableMax schrieb:> eine Art SPI-Slave
Der sieht schon auf die paar geposteten Zeilen bezogen unglaublich
umständlich aus. SPI ist erst mal nicht anderes als zwei
Schieberegister, und alles was du "dranbastelst", macht die Sache sicher
nicht besser. Bestenfalls umständlicher.
Was mir noch auffällt:
1
if(rising_edge(spiClk))then
Was ist die Zielplattform? CPLD/FPGA?
Wieviele Takte hast du?
Hallo.
Danke für die Antworten!
Was ich bauen wollte/will ist ein SPI-Slave der sich mittels TDMA in
einen 4096-Tick-langen Frame einhängt. Der Mechanismus an sich passt
soweit mal (also das die Slaves wissen wann wer dran kommt, die zählen
SpiClk-ticks mit...).
Ich habe jetzt den "bösen" Code geändert:
gated clocks weggetan, alles mit signale realisiert mehr processe mit
mehr flags, usw
4 processe:
+ einen für die rising_edge der spiClk, fürs shiften selber und SDI
reinladen
+ einen für die falling_edge der spiClk, für um die Daten mal aufn Bus
zu legen
+ einen rein kombinatorischen für diverse interne enable-flags
+ und noch einen richtigen synchronen, der eine State-Machine enthaltet
und eben die asynchron empfangenen Daten in die synchronen Register
schreibt, die dann nachher noch verarbeitet werden.
Wie ich bemerkt habe, ist diese Vorgehensweise nicht das Optimum, da es
(zumindest für mich noch) echt kompliziert ist den Überblick zu
behalten. Vor allem aber hatte ich dann aber das Problem das man nicht
in zwei Prozessen auf dasselbe Signal schreiben darf. Genau das bräuchte
ich aber um im synchronen process die Daten, wenn die SPI nicht busy
ist, in das Schieberegister zu schreiben. Sind shared variablen cool
oder gehören die auch in die Kategorie "böser Code" (also wenn die
wirklich geschützt sind)?
Was mir als Lösung einfiel war, da die Systemclock 100MHz und die
SPI-Clock nur 16MHz hat, die SCLK zu samplen und so die Flankenwechsel
mitzubekommen. Dann hätte ich nur mehr zwei Prozesse: einen von der
rising_edge von der SCLK(spiclock) und einen mit dem Systemtakt.
@Lothar:
Danke für die Links. Ich kannte die zwar schon, aber schaue sie mir
immer wieder gerne durch, da ich mehr und mehr nachvollziehen kann :)
Zielplattform wird wahrscheinlich ein FPGA werden.
Wenn man zB sowas in der Art schreibt:
1
process(SpiClk,falling_edge_was_detected)
2
begin
3
if(falling_edge_was_detected='1')then
4
there_was_a_falling_edge<='0';
5
elsif(falling_edge(SpiClk))then
6
there_was_a_falling_egde<='1';
7
endif;
8
endprocess;
9
10
11
process(SpiClk,rising_edge_was_detected)
12
begin
13
-- dasselbe wie oben, aber halt rising_edge und anderen "reset" des FF
14
endprocess;
15
16
process(SysClk)
17
-- handle alles rund um die Buffer und Schieberegister...
18
endprocess
Meiner Ansicht ist das abgesehen, dass mit diesem "Mechanismus" bei
komplexeren Aufgaben ein schnelleres Verwirren erreicht wird, der
Vorteil, dass weniger Systemticks vergehen, als wenn man die SpiClk in
einem Prozess absampled. Ist das so oder wieder mal
epic-beginner-logic-fail?
Max schrieb:> Ist das so oder wieder mal epic-beginner-logic-fail?
Ja.
Weil du wieder 2 "Takte" hast, die zueinander asynchron sind: den
100MHz-Master-Takt und den SPI-Takt. Du wirst also auch mit trickreicher
Umschreibung immer noch den Taktdomänenübergang vor dir haben. Und so
einen Übergang bekommst du auch mit dem Prinzip "Sand in die Augen
streuen" nicht weg.
Kurz: der SPI-Takt ist in deinem Design kein Takt. Deshalb steht er auch
nicht hinter rising_edge oder falling_edge. Eine steigende oder fallende
Flanke in diesem Signal (namens SPI-Takt) wird wie bei jedem anderen
Signal auch nach dem Einsynchronisieren mit einem Schieberegister
ermittelt:
1
signalspiclk_sr:std_logi_vector(2downto0);
2
:
3
spiclk_sr<=spiclksr(1downto0)&SpiClkwhenrising_edge(SysClk);-- das Schieberegister: Einsynchronisieren und Flanke(n) erkennen
4
:
5
processbegin
6
waituntilrising_edge(SysClk);-- es kann nur einen geben!
Lothar Miller schrieb:> Weil du wieder 2 "Takte" hast, die zueinander asynchron sind: den> 100MHz-Master-Takt und den SPI-Takt. Du wirst also auch mit trickreicher> Umschreibung immer noch den Taktdomänenübergang vor dir haben.
Ja, den Taktdomänenübergang den hab ich dann ja im Prozess mit der
100MHz-Systemclock. Also wenn asynchron zu diesem mittels falling_- und
rising_edge(spiclk) ein DFF gesetzt wird und dessen Ausgang dann im
100MHz-Prozess abgefragt wird, dann erkenne ich das Problem nicht so
deutlich. Ist das Problem oder mein Denkfehler, dass das asynchron
getriggerte DFF auch metastabile Zustände haben kann und deshalb das
nicht 100% korrekt im 100MHz-Takt übernommen wird?
Oder passt es dann doch, weil ja nur bei einer gewissen Schwelle das
Flag there_was_edge_detected gesetzt wird und erst dann im 100MHz Takt
wahrgenommen und über ein Rücksignal reseted wird?
Danke für die ausführliche Antwort.
Ja, vom Prinzip her ist das wie die Spikeerkennung ohne dem zweiten FF.
Ich habe ja dann auch ein DFF im wirklich synchronen Prozess, das nur
gesetzt wird wenn mir die Asynchronen eine Flanke detektiert haben. Wenn
Metastabilität eh bei 100MHz unkritisch ist, dann wird falls die beiden
Flanken, also spiclock und sysclock wirklich "schlecht zueinander"
stehen, doch höchstens die Flanke von der Spiclock einen Takt später
erkannt, was auch egal ist. Das reset-signal für das asynchrone DFF
kommt aus dem synchronen Prozess und wird nur gecleared, wenn es
ausgelesen und dann there_was_edge_detected gesetzt wurde. Oder kommts
genau da zu Problemen, falls die Flanken ein unglückliches Timing haben?
Meiner Ansicht nach nicht, weil diese Signale eigentlich implizit
zusammenhängen, also mein synchroner Prozess wird nie erst so spät ein
detection_flag clearen, dass eine SPI-Clk-Flanke verloren geht.
Was ich vorher meinte war nachstehender Code (um ggf Missverständnisse
zu vermeiden):
1
process(SpiClk,falling_edge_was_detected)
2
begin
3
if(falling_edge_was_detected='1')then
4
there_was_a_falling_edge<='0';
5
elsif(falling_edge(SpiClk))then
6
there_was_a_falling_egde<='1';
7
endif;
8
endprocess;
9
10
11
process(SpiClk,rising_edge_was_detected)
12
begin
13
if(rising_edge_was_detected='1')then
14
there_was_a_rising_edge<='0';
15
elsif(rising_edge(SpiClk))then
16
there_was_a_rising_egde<='1';
17
endif;
18
endprocess;
19
20
process(SysClk)
21
if(there_was_a_rising_edge='1')then
22
spiClkCnt<=spiClkCnt+'1';-- nur prinzipmäßig...
23
-- usw
24
rising_edge_was_detected<='1';
25
else
26
rising_edge_was_detected<='0';
27
endif;
28
29
if(there_was_a_falling_edge='1')then
30
-- lege Daten auf Bus
31
falling_edge_was_detected<='1';
32
else
33
falling_edge_was_detected<='0';
34
endif;
35
endprocess
Was ich aber nicht ganz verstehe, wenn ich mein Teil betrachte und den
Testaufbau vom Lothar dann ist ja so, dass mein Signal ja nur von einem
FF aus dem synchronen Prozess bewertet wird, daher ist meiner Meinung
nach ein Laufzeitunterschied nicht gegeben (zu einem zweiten FF synchron
zum SysTick). Aber wie Lothar schreibt: "Besser 2 FFs um auf Nummer
sicher zu sein."... weil das Prinzip dann wirklich immer funktioniert,
auch wenn mehrere FF ein asynchrones Signal einsynchronisieren.
Noch immer Denkfehler?
Max schrieb:> process (SysClk)> if(there_was_a_rising_edge = '1')then
Eine kleine Korrektur vorab:
1
process(SysClk)begin
2
ifrising_edge(SysClk)then-- das hat gerade noch gefehlt ;-)
3
if(there_was_a_rising_edge='1')then
4
spiClkCnt<=spiClkCnt+'1';
Max schrieb:> dass mein Signal ja nur von einem FF aus dem synchronen Prozess> bewertet wird
Eben nicht! Der nächste Zählerwert wird vom Systemtakt UND vom Enable
(=there_was_a_rising_edge) UND vom aktuellen Zählerwert bestimmt. Und
damit wirkt dein Enable auf ALLE Flipflops deines Zählers. Und wie breit
ist der?
Und damit sieht das also so aus:
1
process(SpiClk,rising_edge_was_detected)
2
begin
3
if(rising_edge_was_detected='1')then
4
there_was_a_rising_edge<='0';
5
elsif(rising_edge(SpiClk))then-- eine steigende Flanke zum Zeitpunkt t=99.9ns
6
there_was_a_rising_egde<='1';-- sorgt dafür, dass dieses Enablesignal gesetzt wird.
7
endif;
8
endprocess;
9
10
process(SysClk)begin
11
ifrising_edge(SysClk)then-- der Takt kommt dann zum Zeitpunkt t=100ns (also 100ps später).
12
if(there_was_a_rising_edge='1')then-- Frage: ist dieses Enablesignal (there_was_a_rising_edge)
13
spiClkCnt<=spiClkCnt+'1';-- dann schon an allen Zähler-FFs stabil und gültig?
Danke Lothar.
Ja, LOL... das hab ich gleich mal vergessen, also begin und so, aber
gemeint so wie du es ausgebessert hast.
Zur Frage: ist dieses Enablesignal (there_was_a_rising_edge)dann schon
an allen Zähler-FFs stabil und gültig?
Ich denke mir "egal", wenn nur der eine Zähler mitzählt, dann ist das
Enablesignal (there_was_a_rising_edge) noch nicht bemerkt worden, und
deshalb erst beim nächsten Systemtick high und erst dann ist das
if-gültig und das flag wird gecleared. Verloren gehen tut nix und wenn
wirklich nur an einer Stelle das Signal abgefragt wird, dann muss es
doch egal sein? Bitte sag ja ,Lothar! ;)
Max schrieb:> und wenn wirklich nur an einer Stelle das Signal abgefragt wird
Wenn es wirklich nur genau 1 Stelle ist, dann ist es das jetzt noch
fehlende Synchronisations-Flipflop. Oder hat dein Zähler nur 1 Flipflop?
> Ich denke mir "egal", wenn nur der eine Zähler mitzählt...
Es geht nicht um "den einen Zähler", sondern darum, dass jedes einzelne
Flipflop dieses einen Zählers einen falschen Zustand annehmen kann/wird.
Und das bedeutet: dein Zähler zählt falsch. Stell dir mal vor, ein
3-Bit-Zähler steht auf 0111. Mit dem nächsten Enable werden also die
unteren 3 Bits zu 000 und das oberste gesetzt. Wenn aber jetzt das
oberste Bit den Enable nicht/zu spät bekommt, dann werden die unteren
auf 000 umschalten, das oberste aber NICHT! Und dann wird der neue
Zählerstand 0000 statt 1000 sein... :-o
> Bitte sag ja ,Lothar! ;)
Kann ich schon. Hilft dir aber nicht. Glaub es oder probiers aus... ;-)
Ok, vielen Dank für Deine Hilfe.
Ich glaub es Dir und werde es im größeren Projekt so machen wie von Dir
vorgeschlagen und dann für mich noch mal in einem Mini-Beispiel
anschauen.
Hallo.
Ich versteh das Einsynchronisierprinzip vom Lothar nicht
http://www.lothar-miller.de/s9y/categories/35-Einsynchronisieren
Krieg ich da nicht nur das asynchrone Signal 2 Takte verschoben zurück?
Wo findet die das Einsychronisieren statt? Müsste man nicht das Signal
"samplen" oder irgendwie vergleichen, wenn man wirklich sicher sein will
dass alles synchron ist?
Das zweite FF hat nach dem 2. Takt doch den Wert des Ersten und wenn das
Signal schlecht zum system-tick kam, dann steht im ersten FF von vorhin
ein Mist und das Zweite kriegts weiter...
LOL,ich checks nicht.
:(
wenigstens mal jemand, der sich mit der Materie wohl wirklich befasst
:o)
Angenommen, deine Chipclock ist >> als deine SPIclock (also 100MHz vs.
10MHz oder so):
* Vergiss Metastabilitaet, sie wird bei dir normalerweise nie und nimmer
auftreten
* Du kannst also im ersten Synchronisier-FF einen Wert eintakten, der
evtl. die Setup/Hold-Zeit verletzt
* Da du dich um Metastabilitaet aber nicht kuemmerst, kannst du getrost
davon ausgehen, dass das 2. FF dahinter ein sauberes und stabiles Signal
sieht und auch eintaktet
-> Du wirst also eine Flanke sehen, die kann aber zeitlich um einen Takt
(FPGA Takt) zu spaet kommen
-> Hinter der Flankenerkennung hocken jetzt Zaehler oder FSMs, die
werden sicher ein stabiles Signal am Eingang haben und damit richtig
funktionieren
PS: Ich hab' mir gerade Lothars Seite angesehen und kann mir da einen
[Haarspaltmodus] nicht verkneifen:
Wenn du nur 2 FFs verwendest, dann kann dir bei Setup/Holdtime
Verletzung folgendes passieren: Dein erstes FF hat eben nicht bei tH
einen gueltigen Wert, sondern etwas spaeter (z.B. 100ps). Wenn du jetzt
dieses und das 2. FF zur Flankenerkennung nimmst (also "01" bzw. "10"),
dann verlierst du zum ansteuern deiner nachfolgenden Logik eben diese
z.B. 100ps. Und das kann und wird dir der Timinganalyzer nicht melden,
er weiss es einfach nicht.
Ganz sicher bist du, wenn du noch ein 3. FF einbaust und die FFs 2 und 3
zur Flankenerkennung verwendest (also noch einen Takt Latency). Dann
kann auch die Toolchain sicher damit umgehen.
PPS: Wegen der Metastabilitaet: Der Hersteller garantiert dir, dass ein
FF innerhalb eines gewissen Taktes definitiv auf einen stabilen '0' oder
'1' Wert geht. Metastabil waere, wenn das im naechsten Takt auch noch
'oszillieren' wuerde. Und bei z.B. 100MHz hast du da normalerweise nie
ein Problem
Max schrieb:> Das zweite FF hat nach dem 2. Takt doch den Wert des Ersten und wenn das> Signal schlecht zum system-tick kam, dann steht im ersten FF von vorhin> ein Mist und das Zweite kriegts weiter...
Der Fachbegriff dazu heißt (wie schon erwähnt) Metastabilität. Und das
gnaze ist bei heutigen FPGAs und Takten unter ca. 200MHz kein Thema. Bis
zur nächsten Taktflanke ist das FF entweder wieder stabil high oder low.
"Mist" hat also eine extrem kurze Halbwertszeit... ;-)
berndl schrieb:> * Du kannst also im ersten Synchronisier-FF einen Wert eintakten, der> evtl. die Setup/Hold-Zeit verletzt
Also jedes asynchrone externe Signal...
berndl schrieb:> Ganz sicher bist du, wenn du noch ein 3. FF einbaust
Aber mein Code macht das doch:
1
signalinsr:std_logic_vector(2downto0);-- hier sind die 3 FFs
2
:
3
processbegin
4
waituntilrising_edge(clk);
5
-- Schieberegister
6
insr<=insr(1downto0)&input;
7
endprocess;
8
9
processbegin
10
waituntilrising_edge(clk);
11
if(insr(2downto1)="10")then
12
-- fallende Flanke von input
13
endif;
14
if(insr(2downto1)="01")then
15
-- steigende Flanke von input
16
endif;
17
endprocess;
> Wenn du nur 2 FFs verwendest
Ist da auf meiner HP noch irgendwo so eine Leiche? :-/
Mit dem ersten schieberegister speichert man doch ggf den metastabilen
zustand. Zb zählerstand 1000 statt den wechsel von 1111 auf 0000. Wie
wird durch zwei oder mehr FF ein falsch "eingefangener" wert durch im
takt herumschieben besser? Falsch ist der zählerstand auch nach 4 FFs.
Wisst ihr was ich mein?
Max schrieb:> Mit dem ersten schieberegister speichert man doch ggf den metastabilen> zustand.
Beim Frequenzen unter 200MHz ist beim nächsten Takt keines der Flipflops
mehr in einem ungültigen Zustand. Und vor allem: ein Flipflop KANN
keinen metastabilen Zustand SPEICHERN!
> Zb zählerstand 1000 statt den wechsel von 1111 auf 0000.
Wo kommt in einem Schieberegister ein Zähler vor? Ein Schieberegister
sind Flipflops hintereinander. Da weiß das Vierte erst nach 3 Takten
was vom Ersten. Beim Schieberegister gibt es keine direkte Beeinflussung
des vierten FFs durch das erste. Beim Zähler schon...
Sieh dir mal den RTL-Schaltplan an:
http://www.lothar-miller.de/s9y/archives/52-Kompakte-Flankenerkennung.html
Wenn ich dort die FFs so benenne:
A B C D
E F
Dann sind die FFs A,B und E einfach nur hintereinander verdrahtet, und
dann erst geht es in die weitere Schaltung...
> Wisst ihr was ich mein?
Nein.
Danke für Eure Erklärmühen!
In meinem Design habe(werde haben) ich einen Zähler, der die die
master-spi-clock zählt und sich entsprechend des Zähler standes auf den
Bus hängt (TDMA). Ist der Wert erreicht bei dem die eigenen Daten gerade
reingeshiftet wurde, lese ich diese Daten aus dem Schieberegister. Mit
diesem Schieberegister meine ich aber nicht das oben beschriebene,
sondern oben rede ich von dem Einsynchronisieren, das man anscheinend
über zwei FF macht und für mich ein Schieberegister darstellt. Meine
Statemachine lauft mit dem FPGA Takt, aber ein Zähler der für das
Weiterschalten benötigt wird, zählt asynchrone Flanken.
>> Zb zählerstand 1000 statt den wechsel von 1111 auf 0000.>Wo kommt in einem Schieberegister ein Zähler vor? Ein Schieberegister>sind Flipflops hintereinander. Da weiß das Vierte erst nach 3 Takten>was vom Ersten.
Ja.
Deswegen:
>>Ich denke mir "egal", wenn nur der eine Zähler mitzählt...>Es geht nicht um "den einen Zähler", sondern darum, dass jedes einzelne>Flipflop dieses einen Zählers einen falschen Zustand annehmen kann/wird.>Und das bedeutet: dein Zähler zählt falsch. Stell dir mal vor, ein>3-Bit-Zähler steht auf 0111. Mit dem nächsten Enable werden also die>unteren 3 Bits zu 000 und das oberste gesetzt. Wenn aber jetzt das>oberste Bit den Enable nicht/zu spät bekommt, dann werden die unteren>auf 000 umschalten, das oberste aber NICHT! Und dann wird der neue>Zählerstand 0000 statt 1000 sein...
Genau der Fall ist doch durch zwei FFs(2-bit-Schieberegister)
hintereinander nicht besser geworden: das Auslese-FF vom synchronen
Prozess hat dann den korrupten Wert des asynchronen Zählers, halt zwei
Takte später. Es geht mir also nicht um die Metastabilität. Genau das
verstehe ich nicht.
>> Mit dem ersten schieberegister speichert man doch ggf den metastabilen>> zustand.>Beim Frequenzen unter 200MHz ist beim nächsten Takt keines der Flipflops>mehr in einem ungültigen Zustand. Und vor allem: ein Flipflop KANN>keinen metastabilen Zustand SPEICHERN!
Ja, ich weiß. Blöd ausgedrückt. Ich meinte den falschen Zählerstand der
praktisch gerade umschaltet.
Max schrieb:> Ich meinte den falschen Zählerstand der praktisch gerade umschaltet.
Du musst natürlich auch so einen Zählerstand über ein "quasistatisches"
Zwischenregister (am besten per Handshake) übergeben...
Denn sonst ist es ganz einfach: ein jedes einzelne deriner Bits ist
asynchron zum "restlichen" FPGA und muß daher einsynchronisiert werden.
Weil aber die Bits zusammengehören, müssen sie erst mal in der einen
Domäne zusammen zwischengespeichert werden. Und danach kannst du ein
zusätzliches Signal lossenden, das sagt "da wären wieder mal gültige
Daten da".
Ok, super! Danke Lothar!
Genau mit so einem Handshake kann ich es mir vorstellen, dass alles
passt.
Wenn das ganze mal lauft, sag ich Bescheid... Wird aber noch dauern bis
ich richtig anfangen werde. Zuerst noch ein paar kleine Übungsbeispiele,
damit ich es kapier...
Mahlzeit!
Hallo.
Also... lang is es her, aber es geht jetzt alles so wie ich es wollte.
Das Konzept wurde noch umgeändert, aber ich brauchte trotzdem 2
Clockdomains.
Danke an die lieben "Thread-Freunde" -speziell an Lothar- für Ihre
Antworten. Lang hat's gedauert bis ich es endlich überrissen habe was
Ihr mir sagen wolltet.
Beste Grüße, Max