Forum: FPGA, VHDL & Co. AXI I2C: Welchen Zustand darf der Pin annehmen?


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Alex441 (Gast)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
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

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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:
Tx_Data = 0;         // immer fest auf 0
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:
// gibt eine "starke" 0 oder (via Pullup) eine "schwache" 1 aus
Tx_Data = SDA_Intern;
Tri_En  = SDA_Intern;  

von Alex441 (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank, das
Tx_Data = 0;         // immer fest auf 0
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:
module tristate(IO_Data, Tx_Data, Rx_Data, Tri_En);

    inout  IO_Data;   // bidirectional data line

    input  Tx_Data;   // verbunden mit scl_o/sda_o
    output Rx_Data;   // verbunden mit scl_i/sda_i
    input  Tri_En;    // verbunden mit scl_t/sda_t
 
    assign IO_Data = (Tri_En == 1 | Tx_Data == 1)? 1'bz : 0;
    assign Rx_Data = IO_Data;

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

von -gb- (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Nein, da ist noch ein "Z" zu viel.

von Alex441 (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Welches denn?

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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...

von Alex441 (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Gustl B. (-gb-)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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

von Gustl B. (-gb-)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Alex441 (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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...

von Gustl B. (-gb-)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Alex441 (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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?

von Gustl B. (-gb-)


Bewertung
0 lesenswert
nicht lesenswert
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.

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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:
Tx_Data = 0;     // immer fest auf 0
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
von Alex441 (Gast)


Bewertung
0 lesenswert
nicht lesenswert
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

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
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"

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.