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
|
2 | Tx_Data = SDA_Intern; |
3 | Tri_En = SDA_Intern; |
Vielen Dank, das
1 | Tx_Data = 0; // immer fest auf 0 |
2 | Tri_En = SDA_Intern; // 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 | module tristate(IO_Data, Tx_Data, Rx_Data, Tri_En); |
2 | |
3 | inout IO_Data; // bidirectional data line |
4 | |
5 | input Tx_Data; // verbunden mit scl_o/sda_o |
6 | output Rx_Data; // verbunden mit scl_i/sda_i |
7 | input Tri_En; // verbunden mit scl_t/sda_t |
8 | |
9 | assign IO_Data = (Tri_En == 1 | Tx_Data == 1)? 1'bz : 0; |
10 | assign Rx_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.
:
Bearbeitet durch Moderator
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"
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.