Hallo,
ich implementiere in meinem FPGA (Digilent CMOD7 mit einem Artix7 35)
eine Hardware, die ich als ein SPI Slave ansteuern will. Dazu benutze
ich dieses Modul
https://github.com/nandland/spi-slave/blob/master/Verilog/source/SPI_Slave.v
Als Master benutze ich ein Nucleo Board mit einem STM32L552. Mein
Problem ist, das ich zwischen FPGA und MCU keine zuverlässige Verbindung
aufbauen kann. Ständig passieren Übertragungsfehler (nach ~100kB an
Daten). Bei Debug Versuchen haben ich mir die Clock von dem SPI Slave im
always Block ausgeben lassen, mit diesem Testcode:
1
always @(posedge i_SPI_Clk or posedge i_SPI_CS_n)
2
begin
3
if (i_SPI_CS_n)
4
begin
5
o_SPI_DBG <= 0;
6
end
7
else
8
begin
9
o_SPI_DBG <= ~o_SPI_DBG;
10
end
11
end
und dabei festgestellt, das manchmal auch zur fallenden Flanke von
i_SPI_Clk getacktet wird. Gemessen habe ich alles über einen Logic
Analyzer. Das ist nicht immer der Auslöser von Übertragungsfehlern, aber
sehr oft.
Dabei ist auch kurrios: SPI Frequenzen von unter 1MHz scheinen
problemlos zu funktionieren, dadrüber bekomme ich ziemlich sicher
Übertragungsfehler. Auch Kurios: Mit einem RPi Pico funktioniert es
problemlos, auch bei Frequenzen um die 30MHz.
Die SPI Verbindung ist über Jumper Wires auf einem Breadboard
ausgeführt. Kabellängen sind ca 20cm.
Der i_SPI_Clk benutzt einen clock eingang im FPGA.
Ich muss ehrlich sagen, ich bin gerade ziemlich Ratlos was hier schief
läuft und hoffe, hier ein paar Ideen zu bekommen, was ich ausprobieren
könnte. Leider habe ich kein Oszi um mir die Clockleitung genau
anzuschauen. Ich kann sie nur mit 50MHz mit dem Logic Analyzer
absamplen. Das reicht sicherlich um eine Idee von dem Signal zu
bekommen, aber reicht wohl bei weitem nicht für eine Analyse.
Ein weiterer Versuch war ein Serienwiderstand in die Clockleitung rein
zu setzen. Das hat ein wenig geholfen, habe werte von 22-39Ohm
ausprobiert, aber der große Schuss wars jetzt nicht. Bei den Versuchen
habe ich den Widerstand direkt ins Nucleoboard gesteckt und bin dann
über ein Jumperwire zum FPGA.
So, nun bitte ich um Hilfe, was könnte hier schief laufen? Mache ich
hier was grundsätzlich falsch? Wie sieht es z.B. aus, das der SPI Slave
das Clock Signal vom SPI als Clock benutzt? Wäre es besser den SPI
direkt mit dem FPGA Clock zu samplen?
Vielen Dank
Tobias
Hallo,
deine Infos sind noch ein bisschen dürftig für eine Vermutung, aber ich
versuche es mal.
Wie hoch ist denn dein Clock für das FPGA? Und wie hoch ist dein
angestrebter SPI clock?
Welchen Mode hast du bei der SPI gewählt? Da gibt es ja vier
verschiedene, je nachdem wie und wo geshiftet und gesampled werden soll.
Und ist das auch der gleiche Mode wie bei deinem SPI Master?
Ohne Oszi wird es schwer. Dein Loigc Analyser kann nur digitale Werte
anzeigen, du müsstest dir aber die Signalqualität anschauen. Erreichst
du die richtigen Pegel für high und low?
Da du schreibst, dass nach etwa 100kB Fehler auftreten könnte es auch
gar nichts mit deinen Signalen zu tun haben, sondern damit, wie die
Daten weiter verarbeitet werden. Vielleicht gibt es ja da ein Problem?
Grüße
Als erstes waeren die CPHA/CPOL Einstellungen interessant, klingt eher
nach Abtastungsproblem als eingestreutes Rauschen bei langsamer Slew
Rate (was ungewollte Flanken provozieren koennte), da es ja offenbar bei
langsamen Frequenzen tut und der Widerstand das Verhalten noch
verbessert. Grundsaetzlich wuerde ich mal den externen, im Verhaeltnis
langsamen Clock abtasten und eine Entprell-Option im SPI-Core vorsehen,
damit hat man auch die Slew-Rate-Probleme vom Tisch und die
Moeglichkeiten, Glitches auf lange Zeit zu detektieren.
Hallo,
vielen Dank für die Antworten. Das Design läuft mit 100MHz. SPI soll so
schnell wie möglich betrieben. Aktuell wird er im Mode 0 und mit ~14MHz
betrieben, hoffe aber das ich mich noch auf 25MHz steigern kann. Je
schneller, um so besser.
Mit ein paar weiteren Experimenten konnte ich das Problem weiter
eingrenzen. Meine ursprüngliche Annahme mit dem Clock ist falsch, ganz
offensichtlich ist es die der Chip Select der das Problem verursacht.
Dabei folgender Test Code:
1
always @(posedge i_SPI_Clk or posedge i_SPI_CS_n)
2
begin
3
if (i_SPI_CS_n)
4
begin
5
debug <= 0;
6
end
7
else
8
begin
9
debug <= 1;
10
end
11
end
Ein Serien Widerstand in der Chip Select Leitung erzeugt einen deutlich
besseres Ergebniss, und auch das mit dem Logic Analyzer gemessene
Rauschen ist deutlich geringer, trotzdem passiert es ab und zu, das
während dem SPI Transfer der CS gezogen wird und dadurch der Slave nicht
mehr im Sync mit dem Master ist, wie in dem glitch Bild zu sehen.
Tobias B. schrieb:> ganz offensichtlich ist es die der Chip Select der das Problem> verursacht.
Der ist doch eh dauerhaft auf Low-Pegel (warum eigentlich?).
Das Gezappel, das du da noch im mV Bereich siehst, ist Übersprechen vom
gesamten fliegenden Aufbau zusammen mit den Übergangswiderständen auf
dem Steckbrett.
Sieh dir auch mal an, wie so eine übliche Übertragung per SPI abläuft.
Da geht **vor** dem Telegramm der SS auf low und bleibt dort stabil bis
zum Ende der Übertragung. Und dann geht er wieder auf high, damit sich
der Slave wieder mal auf den Master synchronisieren kann, falls er mal
ausser Takt kommt. Solange der SS low ist werden die Daten vom MOSI mit
dem SCLK eingetaktet.
Tobias B. schrieb:> Wie sieht es z.B. aus, das der SPI Slave das Clock Signal vom SPI als> Clock benutzt?
Geht schon. Du musst dann nur einen Mechanismus zum Queren der
Taktdomäne von SCLK zum FPGA-Takt ausdenken.
> Wäre es besser den SPI direkt mit dem FPGA Clock zu samplen?
Das geht laut Nyquist nur, wenn du einen FPGA-Takt >> SCLK hast. Und
wenn du da tatsächlich bis auf 100 MHz hoch willst (bist du sicher, dass
du das brauchst?), dann mustt du mit 500MHz samplen.
> Die SPI Verbindung ist über Jumper Wires auf einem Breadboard ausgeführt.
Nur interessehalber: du hast schon auch die Masse vom µC mit der Masse
vom FPGA verbunden?
Lothar M. schrieb:> Sieh dir auch mal an, wie so eine übliche Übertragung per SPI abläuft.> Da geht **vor** dem Telegramm der SS auf low und bleibt dort stabil bis> zum Ende der Übertragung. Und dann geht er wieder auf high, damit sich> der Slave wieder mal auf den Master synchronisieren kann, falls er mal> ausser Takt kommt. Solange der SS low ist werden die Daten vom MOSI mit> dem SCLK eingetaktet.
So ähnlich läuft es auch ab. Aktuell wird die CSN Leitung auf low
gezogen, sobald das System startet. Danach bleibt sie auf low, da es nur
einen Slave gibt und die Daten gestreamed werden. Natürlich könnte ich
nach jeder Transaktion die CSN Leitung wieder auf high ziehen und vor
der neuen die CSN Leitung wieder auf low. Die Datenübertragung würde
aber dadurch nicht stabiler werden, da eben zwischen den Bits dieses
Event auftreten kann. Warum auch immer, und genau das versuche ich
irgendwie rauszufinden. Oft tritt das Event auch einfach zwischen den
Bytes auf und da ist es nicht weiter schlimm.
Es ist nur so, ich würde erwarten, das wenn ich den Pin am FPGA auf low
ziehe (egal ob durch einen GPIO, Pulldown oder direkt mit GND), das an
der CSN Leitung genau garnichts passiert. Der Reset vom FPGA wird auch
durch einen anderen GPIO getriggert. Ich will genauso wenig das der FPGA
zufällig resetted wird.
> Tobias B. schrieb:>> Wie sieht es z.B. aus, das der SPI Slave das Clock Signal vom SPI als>> Clock benutzt?> Geht schon. Du musst dann nur einen Mechanismus zum Queren der> Taktdomäne von SCLK zum FPGA-Takt ausdenken.
Das wird in dem SPI_Slave erledigt.
>> Wäre es besser den SPI direkt mit dem FPGA Clock zu samplen?> Das geht laut Nyquist nur, wenn du einen FPGA-Takt >> SCLK hast. Und> wenn du da tatsächlich bis auf 100 MHz hoch willst (bist du sicher, dass> du das brauchst?), dann mustt du mit 500MHz samplen.
Nein, der FPGA takt läuft mit 100MHz, beim SPI würde ich so hoch gehen
wollen, wie es geht, aber 25MHz würden mir für den SPI erstmal reichen
:)
>> Die SPI Verbindung ist über Jumper Wires auf einem Breadboard ausgeführt.> Nur interessehalber: du hast schon auch die Masse vom µC mit der Masse> vom FPGA verbunden?
Natürlich, alle Massen sind miteinander verbunden. Das FPGA-Board wird
durch die 5V vom Nucleo Board versorgt.
Tobias B. schrieb:> Aktuell wird die CSN Leitung auf low gezogen, sobald das System startet.> Danach bleibt sie auf low
Das ist ein Designfehler. Ein einziger Störimpuls und du bist den Rest
des Tages um 1 Bit versetzt.
SPI funktioniert nur in der Theorie ohne den SS#.
> Ich will genauso wenig das der FPGA zufällig resetted wird.
Du hast den Reset aber schon so ausgeführt und einsynchronisiert, dass
nicht bereits ein kleiner ESD-Spike ausreicht, um das Design (evtl.
sogar nur partiell) zurückzusetzen?
Lothar M. schrieb:> Nur interessehalber:
Nur interessehalber: man darf auch mal die physikalische
Verbindung von Master und Slave in Zweifel ziehen bzw.
mistrauisch betrachten.
Lothar M. schrieb:> Das ist ein Designfehler. Ein einziger Störimpuls und du bist den Rest> des Tages um 1 Bit versetzt.>> SPI funktioniert nur in der Theorie ohne den SS#.
Mir gehts hier vorallem rauszufinden und zu verstehen, warum überhaupt
so viele Störimpulse auf der SS# Leitung vorhanden sind, und warum ich
es nur mit einem Serienwiderstand in der SS# Leitung zum laufen bekomme.
Das kann doch nicht sein ... da muss doch was grundsätzliches faul sein.
> Du hast den Reset aber schon so ausgeführt und einsynchronisiert, dass> nicht bereits ein kleiner ESD-Spike ausreicht, um das Design (evtl.> sogar nur partiell) zurückzusetzen?
Der Reset wird über das Xilinx Processor System Reset Module
einsynchronisiert. Das Teil wartet auch bis das OK vom Clock Wizard
kommt.
Tobias B. schrieb:> da muss doch was grundsätzliches faul sein.
... an deinem physikalischen Aufbau ....
Tobias B. schrieb:> Die SPI Verbindung ist über Jumper Wires auf einem Breadboard> ausgeführt.
Uuuuaaaaahhhhh ...... und Abblock-Kondensatoren sind vermutlich
auch nur vom Hörensagen bekannt.
Wastl schrieb:> und Abblock-Kondensatoren sind vermutlich auch nur vom Hörensagen bekannt.
Die werden wohl schon auf dem Nucleo- und dem FPGA-Board drauf sein.
Tobias B. schrieb:> warum überhaupt so viele Störimpulse auf der SS# Leitung vorhanden sind
Mal abgesehen davon, dass die paar mV sicher noch nichts ausmachen, ist
die Ursache ganz einfach: es sind Einkopplungen von steilflankigen
Schaltvorgängen (die kurzen, einmal auftretenden Peaks) und eine
undefinierte Signal- bzw. Masseführung (die an den "Bits" erkennbare
Potentialverschiebung).
Mach es einfach mal so: klemm die Masseklemme des Oszis direkt(!) an die
Masse des µC. Also nicht irgendwo in die Nähe des µC, sondern an einen
kurzen Draht, den du an die Masse direkt am µC angelötet hast.
Und miss dann mit der Tastkopfspitze direkt an der *Masse am FPGA*.
Man würde jetzt wegen Masse=Masse erwarten, dass da eine durchgehende
schnurgerade Linie auf der 0V Linie des Oszis zu sehen ist.
Was siehst du stattdessen?
Das, was du da siehst, ist ein Spannungsabfall auf der Masseleitung am
Leitungswiderstand incl. aller Übergangswiderstände im Steckbrett. Dein
Ziel muss nun sein, dieses Gezappel möglichst "schurgerade" auf 0V
herunter zu bekommen.
Wastl schrieb:> Tobias B. schrieb:>> da muss doch was grundsätzliches faul sein.>> ... an deinem physikalischen Aufbau ....
oder an der Leiterplatte, oder den FPGA-Constraints. Das Kuriose ist,
dass die Störungen auf der Leitung ja auch dann sichtbar sind, wenn sich
beim SPI nichts tut, wenn man das Diagram anschaut.
Was Generelles:
> Dazu benutze ich dieses Modulhttps://github.com/nandland
ich weiss nicht, warum jeder reflexartig immer versucht, sich etwas
runterzuladen. Ich habe noch nie irgendwas von diesem Plattformen
gebrauchen können, weil der Code dort nicht passt. Oft ist er
funktionell nicht zur Aufgabe passend und nicht selten auch noch
technisch schlecht.
Das zu vereinen kostet am Ende mehr Aufwand, als die 10 Zeilen selber
hinzuschreiben, wie man es braucht.
Wenn ich z.B. diese Art der Verknüpfung sehe:
"always @(posedge i_SPI_Clk or posedge i_SPI_CS_n)"
und dann die Xilinx System Reset Strat angesprochen wird, dann sehe ich
schon wieder X Probleme.
Ein SPI-Slave-IF muss gefahren werden, wie ein Chip, d.h. mit dessen
Takt, dessen Select und dessen Daten. Entweder komplett aysnchron mit
angepassten timings und späterem CRC des Registers, oder eben komplett
virtualisiert mit Synchronisation der Signale in die/der Zieldomain.
Darüber muss man sich Gedanken machen und dann die Lösung selektieren.
Was sagt denn eigentlich die Simulation?
J. S. schrieb:> Wenn ich z.B. diese Art der Verknüpfung sehe:> "always @(posedge i_SPI_Clk or posedge i_SPI_CS_n)"> und dann die Xilinx System Reset Strat angesprochen wird, dann sehe ich> schon wieder X Probleme.
Welche denn konkret?
J. S. schrieb:> Ein SPI-Slave-IF muss gefahren werden, wie ein Chip, d.h. mit dessen> Takt, dessen Select und dessen Daten. Entweder komplett aysnchron mit> angepassten timings und späterem CRC des Registers, oder eben komplett> virtualisiert mit Synchronisation der Signale in die/der Zieldomain.
Kann man machen, muss man aber ganz sicher nicht.
J. S. schrieb:> oder eben komplett> virtualisiert mit Synchronisation der Signale in die/der Zieldomain.
In meinen Designs synchronisiere ich immer über /CS.
Die fallende Flanke übernimmt die Daten in das Senderegister und die
steigende Flanke übergibt die empfangenen Daten ins restliche Design.
/CS muß natürlich nach allen Regeln der Kunst einsynchronisiert werden
(2xFF).
Rick D. schrieb:> /CS muß natürlich ... einsynchronisiert werden (2xFF).
Das ist hier nicht nötig, denn hier ist der SPI-Slave eine eigene
Taktdomäne mit dem i_SPI_Clk als Takt. Man muss dann eben sehr aufpassen
mit der Übergabe in die
Das ist hier über einen Fifo gelöst, allerdings geht auch so manches
Signal asynchron über diese Taktgrenze.
J. S. schrieb:> nicht selten auch noch technisch schlecht.
Sagen wir mal so: weil der lokale SPI-Takt noch kombinatorisch
invertiert wird, ist es recht spannend, was denn der Synthesizer da
überhaupt draus macht und ob der Takt es auf ein Taktnetz schafft oder
ob er über "normales" Routing händisch lokal verdrahtet wird.
Und die Implementierung des SPI-Senders als Multiplexer macht die Sache
nicht effizienter:
Gustl B. schrieb:>> schon wieder X Probleme.> Welche denn konkret?
weil da regelmäßig Beschreibungen mit asynchronen Resets und synchronen
Resets gemischt werden, was zu teilweise absonderlichen Schaltungen und
problematischen Verhalten führt.
Gustl B. schrieb:> Kann man machen, muss man aber ganz sicher nicht.
Welche dritte Möglichkeit siehst du denn noch ??
Rick D. schrieb:> In meinen Designs synchronisiere ich immer über /CS.
Ja, das wäre die zweite von mir beschriebene Möglichkeit.
> /CS muß natürlich nach allen Regeln der Kunst einsynchronisiert werden> (2xFF).
Läuft dann aber alles stromfressend auf dem sehr hohen Systemtakt und
hat zumindest kleine Latenzen. Bei sehr niedrigem Systemtakt in einem
BAT-System wiederum kann es ein timing Problem geben.
Und dann gibt es spezielle Designs, die keinen kontinuierlichen
Systemtakt mit PLL haben dürfen, weil das FPGA patient sein muss. Dann
geht das nur oldstyle asynchron direkt in Kombinatorik.
Alle möglichen Versionen haben ihre Nischen
Lothar M. schrieb:> weil der lokale SPI-Takt noch kombinatorisch> invertiert wird, ist es recht spannend, was denn der Synthesizer da> überhaupt draus macht
Solche Geschichten fange ich durch meine Design STRAT ab: Es gibt ein
physisches Design und ein darin innliegendes Logik design. In letzterem
gibt es kein invertierten Takte oder low aktive Geschichten. Das wird
alles brav außen abgefangen. Wenn da negierte Takte / Takte vorkommen,
muss man immer entscheiden, ob die wirklich negiert sind, wegen low
activer Ports am Chip, oder ob sie verschoben sind und wie man das
intern korrigiert. Bei einem kontinuierlich laufenden Takt würde man das
z.B. auch an einer PLL abfangen (können).
Negierte Takte gibt es im FGPA nur an den Stellen, wo die Zellen das
physisch erfordern, z.B. an DDR-Takt-Treibern und DDR-IO-Zellen, die
wirklich 2 Takteingänge haben und auch das steckt bei mir immer im
äußeren Toplevel.