Morgen zusammen,
ich habe derzeit ein komisches Problem mit einer SPI Schnittstelle.
Hier erstmal der Code:
1
Schieberegister:process(Takteingang,SPI_Clock)
2
begin
3
ifTakteingang'eventandTakteingang='1'then
4
SPI_Clock_old<=SPI_Clock;-- saving the actual values
5
ifSPI_Clock_old='0'andSPI_Clock='1'then-- detect the rising edge of the SPI clock
6
foriin0toRegister_width-2loop
7
temp_signal(i+1)<=temp_signal(i);
8
endloop;
9
Counter<=Counter+1;
10
temp_signal(0)<=SPI_Data;
11
endif;
12
endif;
Der SPI_Clock liegt bei 50 kHz und der "Masterclock" Takteingang bekommt
von mit 13 MHz.
ich taste den SPI-Takt ab und zähle auf die steigende Flanke einen
Zähler auf 16. Danach sollte das Schieberegister voll sein und die Werte
werden übernommen.
Jetzt passiert jedoch folgendes: Der Counter kommt häufig nur bis 15 und
damit ist das Schieberegister noch nicht voll. Die Signale am Oszi sind
aber prima (also Clock, CS und Daten sind da und haben schöne, steile
Flanken).
Muss ich da noch etwas grober "entprellen" oder gibts andere
Fehlerquellen für den falschen Zähler?
Danke schonmal für eure Tipps!
Also zunächst würde ich mal SPI_Clock aus der Sensitivity Liste
herausnehmen und durch einen ordentlichen Reset Eingang ersetzen
(welchen Wert hat SPI_Clock_old nach dem Einschalten?).
Dann muss SPI_Clock natürlich auf Takteingang synchronisiert werden
bevor es verwendet wird. Am besten nach der klassischen Methode zweier
hintereinander geschalteter Register. Wie man Schieberegister
implementiert weißt Du ja schon.
Den Rest kann man aus diesem Fragment nicht erkennen.
In jedem Fall schließe ich mich Duke's Meinung bezüglich einer
Simulation an.
Gruß
Marcus
Der Counter wird mit der steigenden Enable Flanke auf 16 geprüft und
zurückgesetzt. Wenn 16 erreicht wird ausgegeben, zurückgesetzt wird
immer.
Simulation tut...
Ist SPI_Clock direkt der Pin?
Falls ja: dann solltest du noch (mindestens) 1 FF dazwischenschalten.
Stichwort hier wieder: Metastabilität.
SPI_Data muß übrigens nicht synchronisiert werden, das ist zu dem
Zeitpunkt garantiert stabil. tsu und th werden nicht verletzt.
Wenn schon generisch programmiert, dann doch nicht so verschleift. Das
hier sieht doch viel kompakter aus (und ist genauso generisch):
1
Schieberegister:process(Takteingang,SPI_Clock)
2
begin
3
ifTakteingang'eventandTakteingang='1'then
4
SPI_Clock_old<=SPI_Clock;-- saving the actual values
5
ifSPI_Clock_old='0'andSPI_Clock='1'then-- detect the rising edge of the SPI clock
Danke schonmal für eure Tipps.
Das mit der sens. Liste gibts natürlich keinen Sinn - da habt ihr
vollkommen recht!
Zum Thema Synchronisieren habe ich noch eine Frage - ich war der Meinung
ich müsste die beiden Takte bei dieser Methode nicht synchronisieren,
wenn ich auf den Master-Clock den Zustand vergleiche und so die Flanke
detektiere.
Ich dachte schlimmstenfalls erhalte ich dabei einen Jitter von meinen 13
MHz bei der "Abtastung" - oder wo liegt bei den nicht synchronisierten
Signalen das Problem?
Das Eingangs-FF werde ich der Metastabilität wegen implementieren (ich
erinnere mich an eine Vorlesung zu dem Thema... da war was, ja irgenwas
war da :P)
> oder wo liegt bei den nicht synchronisierten Signalen das Problem?>> Stichwort hier wieder: Metastabilität.
Auf gut Deutsch heißt das:
mal geht es und mal geht es nicht.
In der Simulation funktioniert es aber immer.
EDIT:
Was macht die Simulation, wenn beide Flanken gleichzeitig kommen?
Ja: immer das selbe.
Und was macht deine reale Schaltung?
"Gleichzeitig" gibt es hier nicht. Du hast aber auf jeden Fall eine tsu
(Setup) und th (Hold) Verletzung, und deine FFs machen, was sie wollen.
Ich hab hier mal den kompletten Code angehängt.
Mein Problem habe ich soweit eingrenzen können:
- wenn ich auf die SPI-Flanke triggere, dann tut meine Schnittstelle,
ich kann jedoch nicht mit dem Counter prüfen ob tatsächlich alle Bits
geschoben wurden.
- wenn ich den Vergleich von Signal zu Signal_old als event nutze,
erhalte ich nicht nachvollziehbares verhalten, obgleich die Simulation
funktioniert
Ist mein erstr Code zu SPI - demnach bin ich über jegliche Tipps dankbar
:D
Dein SPI_Enable ist komplett asynchron, und wird einfach mit dem Takt
abgefragt. Das geht schief, ab und zu.
1
ifSPI_Enable='1'then
2
counter<="00000";
3
endif;
4
5
ifSPI_Enable='0'then
Mach das mal auch so hübsch synchron wie
1
SPI_Clock<=SPI_Clock_ext;
2
SPI_Clock_old<=SPI_Clock;-- saving the actual values
Aber i.A. solltest du das mit jedem Signal, das von Aussen kommt,
machen. Ab und zu brauchst du es nicht, das muß aber gut überlegt sein.
Schöner sieht das übrigens aus, wenn du ein Schieberegister nimmst:
Okay Danke Lothar,
ich habe nun alle Eingänge synchronisiert und damit treten deutlich
seltener Aussetzer auf.
Leider kommt dennoch nicht jedes SPI-Packet am Ziel an.
Ist es denn üblich, den SPI-Takt mit einem Masterclock "abzustasten" und
mit den geänderten Zuständen zu arbeiten?
Grüße
> und damit treten deutlich seltener Aussetzer auf.
Dann ist immer noch irgendwo so ein Bock drin ;-)
> Ist es denn üblich, den SPI-Takt mit einem Masterclock "abzustasten" und> mit den geänderten Zuständen zu arbeiten?
Nein, ich würde das etwas anders machen. Zum Eintakten wird der SCLK
verwendet, und wichtig ist nur, dass die Daten bei der Übernahme in
die Masterclock-Domäne stabil sind.
Falk Brunner hat es hier mal schön aufgezeigt:
Beitrag "Re: SPI im CPLD mit State Machine - geht das so?"
sind komplett unnötig, wenn man sich das SPI-Protokoll mal genau
durchliest.
In dem Protokoll wird nämlich nicht eine bestimmte Anzahl eingeschobener
Bits zur Datenübernahme spezifiziert, sondern das Deselektieren des
Bausteins. Die Datenübernahme erfolgt also per Definition mit der
steigenden Flanke des SS-Signals. Und damit werden alle Zähler, die
irgendwelche eingetaktete Bits zählen, unnötig. Ich könnte hier also
z.B. 123 Bits eintakten, verwendet würden die letzten 16 davon.
Und der andere Vorteil bei der Datenübernahme per SS ist der, dass
mehrere Slaves einfach hintereinander geschaltet werden können, und
diese Diasy-Chain mit 1 SS bedient werden kann. Siehe dazu z.B.
http://www.maxim-ic.com/appnotes.cfm/an_pk/3947
Allerdings gibt es durchaus kommerzielle Bausteine, bei denen explizit
im DB steht, dass nicht die letzten, sondern die ersten eingeschobenen
Bits intern weiterverwendet werden. Das deutet wieder auf einen Zähler
oder ein nicht nur vom SCLK gesteuertes Schieberegister hin.
Okay ich hab das ganze nun nochmal ohne irgendwelche Zähler-Abfragen
implementiert.
Recht nahe angelehnt an den verlinkten Code von Falk Brunner. Leider
bekomme ich auch weiterhin einige Bits falsch detektiert. Verstehen will
ich es nicht mehr so recht, da das SPI Signal wirklich keinen Fehler
aufweist, und im FPGA dennoch falsch übergeben wird...
Also herzlichen Dank für eure Hilfe und wenn ihr weitere Tipps habe
freue ich mich natürlich. Werde jetzt mal versuchen noch einige
Sicherheiten einzubauen, dass ein falsches Ergebnis nicht übernommen
wird. Das wäre bei meiner Anwendung nämlich ziemlich blöd - lieber sende
ich die Daten nochmal.
> Verstehen will ich es nicht mehr so recht, da das SPI Signal> wirklich keinen Fehler aufweist, und im FPGA dennoch falsch> übergeben wird...
Es ist ohne weiteres möglich, auf beide angesprochenen Arten (alles
einsynchronisieren bzw. lokaler SPI-Takt) das SPI-Protokoll auf einem
FPGA zu implementieren. Und dazu ist keine Fehlerbehandlung nötig.
Ich habe so eine Fehlerbehandlung bei mir mal implementiert, nur, um
nach mehreren 10000 Maschinenjahren (5000 Maschinen * 6 Jahre)
festzustellen, daß diese Fehlerbehandlung unnötig ist.
Wenn du meinst, deine Beschreibung wäre ok, dann kommt als nächstes:
Hardware?
Layout?
Masseführung?
Blockkondensatoren?
Okay ich hab das ganze heute nochmal geprüft. Das problem ist nicht mehr
der SPI - der klappt. Ich habe ihn wie oben beschrieben ohne Zähler und
detektiere bisher alles fehlerfrei.
Das Problem das ausftrat, hatte mit meinem auswertenden Code zu tun und
nicht mit dem SPI.
Also vielen Dank euch allen - mein SPI-Problem ist gelöst :D