Hallo MC,
ich habe einen Screenshot zu einer Frage aus dem digilent-Forum
angehängt (hier originale Frage):
https://forum.digilentinc.com/topic/19288-i2c-tristate-pins-without-board-definition-files/?sortby=date
Das Problem:
Der FPGA-Pin, der die physikalische i2c-Leitungen SDA oder SCL berührt,
darf nur "0" sein oder "High Z", wobei bei Letzterem der Pull-Up
Widerstand die Leitung auf 1 zieht.
In dem Forumseintrag sieht es aber so aus, als könnte der Pin auch auf 1
stehen, also sozusagen VCC an beiden Enden des Pull-Up Widerstandes.
Das wäre der Fall, wenn
Tri_En = 0
Tx_Data = 1
Das verstehe ich nicht. Angeblich soll es so aber funktionieren.
Gruß
Alex
Alex441 schrieb:> In dem Forumseintrag sieht es aber so aus, als könnte der Pin auch auf 1> stehen
Auf welche Textzeile beziehst du dich da?
> also sozusagen VCC an beiden Enden des Pull-Up Widerstandes.
Das ist technisch korrekte, wenn auch etwas eigenartige Sichtweise. Denn
wenn der Pullup nach Vcc "pullen" soll und deshalb einseitig an Vcc
angeschlossen ist und zudem niemand den anderen Pin des Pullups auf GND
zieht und deshalb kein Strom fließt, dann ist auch "Vcc an beiden Enden
des Pull-Up Widerstandes". Insofern ist das sogar der übliche
Ruhezustand des I2C Busses...
> Das wäre der Fall, wenn> Tri_En = 0> Tx_Data = 1> Das verstehe ich nicht. Angeblich soll es so aber funktionieren.
Das ist die übliche Funktion eines jeden Tristate Buffers: entweder ist
er hochohmig (wenn Tri_EN=1) oder er gibt seine Datenleitung an den Pin
aus (wenn Tri_En=0). Und im zweiten Fall wird der Pegel dann eben von
Tx_Data kontrolliert. Und das Tx_Data kann eben 0 oder 1 sein.
Zur korrekten Ansteuerung dieses I2C-Busses muss die Datenleitung auf 0
gelegt und der gewünschte interne Bussignal-Pegel über Tri_En ausgegeben
werden:
1
Tx_Data=0;// immer fest auf 0
2
Tri_En=SDA_Intern;// gibt eine "starke" 0 oder (via Pullup) eine "schwache" 1 aus
Wer das falsch macht, der steuert eben den Bus falsch an. Und in dem
geposteten Screenshot sind die Signale scl_o und scl_t so wie sda_o und
sda_t zumindest irreführend benannt. Ob der Bus so funktioniert, hängt
davon ab, was diese Signale tatsächlich machen. Schlimmstenfalls kommt
an den jeweiligen Leitungen immer das Selbe heraus. Das würde nämlich
auch funktionieren:
1
// gibt eine "starke" 0 oder (via Pullup) eine "schwache" 1 aus
macht Sinn.
Nochmal zum Verständnis:
Ohne Datenübertragung ist Tri_En = 1 und damit der FPGA-Pin an den sda-
und scl-Leitungen hochohmig. Wir lauschen nur an der Leitung.
Sobald wir Daten als Master übertragen wollen, wechselt Tri_En = 0 und
Tx_Data bestimmt von nun den Pegel der Leitung:
Wenn Tx_Data = 0, geht der FPGA-Pin auf 0 bzw. GND und die Leitung wird
auf GND gezogen, logische 0.
Wenn hingegen Tx_Data = 1, wird der FPGA-Pin hochohmig und die Leitung
über den Pull-Up auf VCC gezogen, logische 1.
Das erreichen wir, wenn der Code aus dem Forumseintrag wie folgt lauten
würde:
1
moduletristate(IO_Data,Tx_Data,Rx_Data,Tri_En);
2
3
inoutIO_Data;//bidirectionaldataline
4
5
inputTx_Data;//verbundenmitscl_o/sda_o
6
outputRx_Data;//verbundenmitscl_i/sda_i
7
inputTri_En;//verbundenmitscl_t/sda_t
8
9
assignIO_Data=(Tri_En==1|Tx_Data==1)?1'bz:0;
10
assignRx_Data=IO_Data;
11
12
endmodule
Die Wahrheitstabelle lautet:
Tri_En | Tx_Data | FPGA-Pin | Busleitung
0 0 0 0
0 1 Z 1
1 0 Z 1
1 1 Z 1
Alex441 schrieb:> assign IO_Data = (Tri_En == 1 | Tx_Data == 1)? 1'bz : 0;
Das sieht bezogen auf das Verhalten der Schnittstellensignale gut aus.
Und auch die Wahrheitstabelle passt, denn jede auszugebende 1 wird zu Z,
und wenn gelesen werden soll, muss die Leitung ja auch hochohmig sein.
> Sobald wir Daten als Master übertragen wollen, wechselt Tri_En = 0 und> Tx_Data bestimmt von nun den Pegel der Leitung:
Ein Master, der eine aktive '1' auf den Bus treibt, ist schlecht
implementiert. Denn so ein Master hat wohl von "clock stretching" noch
nichts gehört. Den hat wohl der Drittsemester-Praktikant gemacht...
Der Block "axi_ixx_0" ist ein Block, der standartmäßig in Vivado
enthalten ist. Ich kann mich nicht dafür verbürgen, das bei Xilinx keine
Praktikaten die Blöcke schreiben.
Aber ich denke, das clock stretching in der obigen Lösung enthalten ist:
Der Master bringt die Leitung auf 0, für einen Pegelwechsel auf der
Leitung schaltet er auf Z. Wenn aber ein Slave die Leitung auf 0 halten
will und damit den clock strechted, sieht der Block "axi_ixx_0" das über
"scl_i", da die Leitung ständig belauscht wird.
Alex441 schrieb:> Welches denn?
Sorry, war mein Fehler.
Ich verstehe Lothars Antwort so, dass ein Master der eine '1' treiben
würde schlecht wäre, die Lösung hier also OK ist.
Alex441 schrieb:> Der Master bringt die Leitung auf 0, für einen Pegelwechsel auf der> Leitung schaltet er auf Z.
Ich dachte, der schaltet nicht auf Z solange was gesendet wird, denn
Alex441 schrieb:>>> Sobald wir Daten als Master übertragen wollen, wechselt Tri_En = 0
Alex441 schrieb:
> assign IO_Data = (Tri_En == 1 | Tx_Data == 1)? 1'bz : 0;
Das bedeutet doch:
Der Pin wird hochohmig wenn entweder Tri_En == 1 oder Tx_Data == 1, und
andernfalls wird der Pin '0'.
Bedeutet also, dass der IO hochohmig ist, wenn gelesen wird, aber der
ist auch hochohmig, wenn der Master eine '1' schreibt.
Lothar M. schrieb:> Alex441 schrieb:>> Der Master bringt die Leitung auf 0, für einen Pegelwechsel auf der>> Leitung schaltet er auf Z.> Ich dachte, der schaltet nicht auf Z solange was gesendet wird, denn> Alex441 schrieb:>>>> Sobald wir Daten als Master übertragen wollen, wechselt Tri_En = 0
Deine Kenntnisse übersteigen meine gänzlich. Ich hoffe also, dass wir
nicht gleich aneinander vorbeireden.
Gustl B. schrieb:> Alex441 schrieb:>> assign IO_Data = (Tri_En == 1 | Tx_Data == 1)? 1'bz : 0;>> Das bedeutet doch:>> Der Pin wird hochohmig wenn entweder Tri_En == 1 oder Tx_Data == 1, und> andernfalls wird der Pin '0'.>> Bedeutet also, dass der IO hochohmig ist, wenn gelesen wird, aber der> ist auch hochohmig, wenn der Master eine '1' schreibt.
Ganz genau, selbst wenn Tri_En == 0 ist, die Leitung also "freigegeben"
ist, kann der Pin auf Z stehen, wenn der Master eine 1 schreiben will.
Das wirft aber die Frage auf, warum es das Signal Tri_En überhaupt
gibt...
Alex441 schrieb:> as wirft aber die Frage auf, warum es das Signal Tri_En überhaupt> gibt...
Weil es eben einen Unterschied zwischen hochohmig und '0' gibt.
Würdest du den IO dauerhaft auf 'Z' legen, dann könntest du keine '0'
treiben und anders herum wenn du den auf '0' legst, dann könnte der
Slave machen was er will, es wird keine '1' übertragen.
Gustl B. schrieb:> Alex441 schrieb:>> as wirft aber die Frage auf, warum es das Signal Tri_En überhaupt>> gibt...>> Weil es eben einen Unterschied zwischen hochohmig und '0' gibt.>> Würdest du den IO dauerhaft auf 'Z' legen, dann könntest du keine '0'> treiben und anders herum wenn du den auf '0' legst, dann könnte der> Slave machen was er will, es wird keine '1' übertragen.
Das habe ich nicht verstanden, könntest du das nochmal ausführlicher
erläutern?
Dein Slave soll dem Master ja eine '1' senden können. Das erledigt der
Pullup. Aber der kann die Spannung nur dann hoch ziehen, wenn weder
Master noch Slave die Spannung runter ziehen. Master und Slave müssen
also beide hochohmig sein.
(Gut, der Slave dürfte in dem Fall sogar aktiv high treiben.)
Du willst aber ebenfalls eine '0' senden können vom Master zum Slave.
Das bedeutet also, dein Master muss die Spannung herunterziehen können
gegen den Pullup. Dazu muss er selber low treiben.
Alex441 schrieb:> warum es das Signal Tri_En überhaupt gibt...
Im Grunde wäre es unnötig, wenn diese AXI I2C Schnittstelle von sich aus
am sda_o und scl_o nur Z und 0 ausgeben würde. Jetzt kommen aber die
ganzen scl_xx und sda_xx Signale aus dem Inneren des FPGAs, wo es ein
Z gar nicht gibt. Denn intern kennt ein FPGA nur 0 und 1. Das Z wird
also erst im Pintreiber des FPGAs realisiert. Deshalb werden "von innen
heraus" die nötigen Signale als 0 und 1 zum Ansteuern des Bustreibers
geliefert. Blöd ist jetzt nur, dass diese Signale im Fall hier
eigentlich falsch oder mindestens überdefiniert sind. Den wie gesagt:
man könte den korrekten Ausgangspegel des Pins auch nur mit 1 einzigen
Signal definieren und die oben beschriebene Transformation durchführen:
1
Tx_Data=0;// immer fest auf 0
2
Tri_En=sda_o;// gibt eine "starke" 0 oder (via Pullup) eine "schwache" 1 aus
Allerdings muss dazu der Master so sauber programmiert sein, dass er
nicht unnötig an der sda-Leitung herumzupft.
> Das wirft aber die Frage auf, warum es das Signal Tri_En überhaupt> gibt...
Du meinst jetzt das Tri_En des Bustreibers?
Ich glaube fast, du musst dir mal den Ausgangstreiber im Datenblatt
deines FPGAs bis hinunter zur physikalischen Realisierung mir P und N
Mosfet genauer anschauen.
Dieser Treiber hat in Richtung Vcc einen P-Fet und in Richtung GND einen
N-Fet. Wenn keiner der beiden Angeteuert wird, dann ist der Ausgang
hochohmig. Wenn eine 0 ausgegeben werden soll, dann wird der N-Fet
eingeschaltet, für eine 1 der P-Fet.
Auf https://www10.edacafe.com/book/ASIC/CH02/CH02.7.php oder auf
https://www.researchgate.net/profile/Shih_Lun_Chen3/publication/221379058/figure/fig3/AS:667654406819847@1536192623436/Conventional-tri-state-I-O-buffer-in-a-025-m-CMOS-process.png
ist das dargestellt, allerdings wurde dort statt einem Tri_En
(Hochohmig, wenn Tri_En==1) wie üblich ein OE (Output Enable --> Augänge
aktiv, wenn OE==1) verwendet.
auf
https://nptel.ac.in/content/storage2/courses/117101058/downloads/Lec-33.pdf
im Kapitel 33.4 ist dann ein /OE verwendet, das vom Pegel wieder wieder
dem hiesigen Tri_En entspricht.
Im Fall des I2C Busses hier reicht es aus, nur den N-Fet für eine aktive
0 anzusteuern. Und eben keinen der beiden Fets anzusteuern, wenn ein 1
Pegel "ausgegeben" werden soll. Denn diese 1 wird durch den externen
Pullup errreicht.
Ich danke euch beiden für eure Hilfe.
Ich habe jetzt für mein Z7-10 Board ein funktionierendes AXI-IIC
Beispiel gefunden.
Es hat sich herausgestellt, dass am Ausgang ein Tristate-Buffer, wie im
ersten Post gezeigt, unnötig ist. Man kann direkt das Signal IIC als
Port definieren, in die Constraints eintragen und Vivado kümmert sich
dann wohl im Hintergrund darum, dass der I2C-Bus richtig angesteuert
wird. 100%ig ist mir das nach wie vor nicht klar.
So oder so, I2C ist für mich eigentlich nur Mittel zum Zweck, um die
Peripherie ansteuern zu können. Das funktioniert jetzt, der Audio-Codec
schnurt wie ein Kätzchen. Jetzt kann ich endlich an die eigentliche
Signalverarbeitung gehen.
Extrem steile Lernkurve + wenige gute Beispiele = FPGA
Alex441 schrieb:> der Audio-Codec schnurt wie ein Kätzchen.
Er schnurrt... ;-)
Alex441 schrieb:> Es hat sich herausgestellt, dass am Ausgang ein Tristate-Buffer, wie im> ersten Post gezeigt, unnötig ist.
Das steht aber im Grunde schon im zweiten Post des von dir verlinkten
Threads: "you can directly make the AXI IIC's IIC interface port
external"