Forum: FPGA, VHDL & Co. 'Hochohmig' in der Simulation


von Burkhard K. (buks)


Lesenswert?

Ich verwende eine leicht abgewandelte (ohne Wishbone-Interface) Version 
des I2C Master Core von Richard Herveille 
(http://www.opencores.org/projects/i2c). Auf dem Silizium funktioniert 
das ohne größere Probleme* - in der Simulation dagegen nehmen gleich 
mehrere Signale den Zustand 'X' an. Die Ursache findet sich letztlich im 
Code zum Einsynchronisieren von SCL und SDA (Modul 
i2c_master_bit_ctrl.vhd):
1
    cSCL <= (cSCL(0) & scl_i);
2
    cSDA <= (cSDA(0) & sda_i);
3
  
4
    fSCL <= (fSCL(1 downto 0) & cSCL(1));
5
    fSDA <= (fSDA(1 downto 0) & cSDA(1));
6
  
7
    sSCL <= (fSCL(2) and fSCL(1)) or
8
            (fSCL(2) and fSCL(0)) or
9
            (fSCL(1) and fSCL(0));
10
    sSDA <= (fSDA(2) and fSDA(1)) or
11
            (fSDA(2) and fSDA(0)) or
12
            (fSDA(1) and fSDA(0));
zusammen mit dem (von Herveille empfohlenen) Code zur Erzeugung der 
Tristate-Buffer:
1
    scl         <= scl_pad_o when (scl_padoe_n = '0') else 'Z';
2
    sda         <= sda_pad_o when (sda_padoe_n = '0') else 'Z';
3
    scl_pad_i   <= scl;
4
    sda_pad_i   <= sda;
Der Wertebereich der "and_table" und der "or_table" in 
IEEE.STD_LOGIC_1164 umfasst nur  Werte '0', '1', 'U' und 'X'; 'Z' wird 
auf 'X' gemapped.

Eine spezifische Funktion für 'Z' habe ich nicht gefunden. to_bit() 
mapped leider verkehrt herum, aus 'Z' würde '0'. Wie sollte man 'Z' 
daher simulationsgerecht am besten "auflösen"? Etwa so:
1
    scl         <= scl_pad_o when (scl_padoe_n = '0') else 'Z';
2
    sda         <= sda_pad_o when (sda_padoe_n = '0') else 'Z';
3
    scl_pad_i   <= '1' when scl = 'Z' else
4
                   '1' when scl = '1' else
5
                   '0';
6
    sda_pad_i   <= '1' when sda = 'Z' else
7
                   '1' when sda = '1' else
8
                   '0';
Oder gibt es da noch etwas anderes zu beachten?


*Tatsächlich sehe ich auf meinen Nexys4-DDR zwei Probleme:
  - nach der Konfiguration startet der Core nicht korrekt, ein 
Reset-Generator ist zwingend erforderlich
  - die erste Übertragung nach Reset erfolgt verstümmelt, es fehlt der 
Address-Teil. Danach arbeitet der Core normal

von Andreas (Gast)


Lesenswert?

Eine I2C Leitung ist doch erst mal mit Pullup auf High gezogen. Daher 
müsste es in der TB so etwa aussehen:
1
 
2
SCL <= 'H';
3
scl <= scl_pad_o when (scl_padoe_n = '0') else 'Z';

Nun wird der Core am Eingang mit dem 'H' schlecht etwas anfangen können. 
Dort kann man mit dem Konstrukt etwas nachhelfen:
1
scl_pad_i <= std_ulogic(to_bit(scl));

Damit wird das 'H' heraus gefischt und eine '1' draus.

https://www.cs.sfu.ca/~ggbaker/reference/std_logic/1164/to_bit.html

von J. S. (engineer) Benutzerseite


Lesenswert?

Aus genau solchen Gründen empfehle Ich immer, die Simulation der Physik 
und der Logik zu trennen. Bis auf bestimmte Ausnahmefälle braucht man 
keine Physik bei der Logiksimulation, daher heisst sie ja so :-)  Einen 
Core simuliert man im Verbund einer Hardware, also einem FPGA, das die 
Umsetzung leitet. Den realen externen Baustein muss man entsprechend 
modellieren oder wrappen. Dann kannst Du die vorgesehenen "Pullups" 
Week" verwenden.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Jürgen S. schrieb:
> kannst Du die vorgesehenen "Pullups" Week" verwenden.
Schon wieder was Neues...  ;-)

von Chefkritiker (Gast)


Lesenswert?

Wo ist denn der Vorteil, die Physik abzukoppeln und extra zu simulieren? 
Das löst ja das Problem nicht. Ein "Z" muss der TE so oder so einführen 
und benutzen.

von J. S. (engineer) Benutzerseite


Lesenswert?

Lothar M. schrieb:
> Jürgen S. schrieb:
>> kannst Du die vorgesehenen "Pullups" Week" verwenden.
> Schon wieder was Neues...  ;-)

Ja, dieser spezielle pullup muss nur einmal gesetzt werden und ist dann 
eine ganze Woche gültig! :-)

Chefkritiker schrieb:
> Wo ist denn der Vorteil, die Physik abzukoppeln und extra zu simulieren?
Weil man in der Logiksimulation keine Physik braucht und auch keine 
wirkliche Aussage bekommt, wenn man nicht Pegel, Timing etc ins Spiel 
bringt.

> Das löst ja das Problem nicht. Ein "Z" muss der TE so oder so einführen
> und benutzen.
Wenn man das Verhalten aus irgendeinem Grund benötigt, ja. Nötig ist das 
aber nur in Netzen mit komplexem Timing z.B. mit mehreren Endgeräten die 
alle für sich pullen können.

von Burkhard K. (buks)


Lesenswert?

Jürgen S. schrieb:
> Wenn man das Verhalten aus irgendeinem Grund benötigt, ja. Nötig ist das
> aber nur in Netzen mit komplexem Timing z.B. mit mehreren Endgeräten die
> alle für sich pullen können.

Möglicherweise steh ich ja auf der SDA-Leitung, nur: Wie sonst bringe 
ich I2C-Master (FPGA) und -Slave (Peripherie-Modul) zusammen? Natürlich 
könnte ich einen Tristate per externer Hardware implementieren, aber 
wozu, wenn ich das benötigte Design-Element (OBUFT) bereits auf dem FPGA 
habe?

Für Spartan/Artix und Co ist Zuweisung von 'Z' (OBUFT: Design Entry: 
"inference - recommended") die empfohlene Methode - siehe auch 
"Tristates" in den "HDL Coding Examples" des XST UG.

Damit ergibt sich doch zwangsläufig die Frage, wie den Code gestalten, 
dass er sowohl fürs Silizium als auch die Simulation passt? Oder was 
übersehe ich?

von Markus (Gast)


Lesenswert?

Ich habe vor kurzem in der Firma einen bidirektionalen UART simuliert.
Da hatte ich auch irgendein Konstrukt um einen "pullup" zu machen.
Weiß aber nicht mehr wie genau ich es gemacht hatte.. Ich schaue morgen 
mal rein und poste es hier (wenn ich dran denke)

von Strubi (Gast)


Lesenswert?

Moin,


Burkhard K. schrieb:
> Damit ergibt sich doch zwangsläufig die Frage, wie den Code gestalten,
> dass er sowohl fürs Silizium als auch die Simulation passt? Oder was
> übersehe ich?

Ich sehe grade das Problem nicht, oben wurde doch schon auf die 
Pullupwochen bei IKEA (Spass beiseite: 'H') verwiesen.
Architekturspezifische Buffer würde ich vermeiden, die Synthese sollte 
den Tristate-Buffer richtig instanzieren.
Das tut dann für die Simulation z.B. mit einem virtuellen I2C EEPROM 
genauso wie in real, nur dass in der TB SDA und SCL auf 'H' gezogen 
werden.
Im architekturunabhängigen Core machst du dann nur das Tristaten wie 
Andreas
oben schon beschrieben hat. Wenn's die Synthese aus irgendeinem Grund 
nicht schaffen sollte, muss halt ein architekturabhängiger Wrapper her.

von WS (Gast)


Lesenswert?

Wie simuliert man dann bitte "Z"?

von Max707 (Gast)


Lesenswert?

Hochohmig ist relativ, zu was hochohmig?

Schätze mal x mal 1000

von J. S. (engineer) Benutzerseite


Lesenswert?

Burkhard K. schrieb:
> Möglicherweise steh ich ja auf der SDA-Leitung, nur: Wie sonst bringe
> ich I2C-Master (FPGA) und -Slave (Peripherie-Modul) zusammen?

Wenn Du ein Modell des Empfängers hast, hängst Du die direkt zusammen. 
Muss in Logik funktionieren. Es gibt nur ein Problem mit dem Lesen des 
Signals, wenn der Core auf sich selber reagiert, wenn keine zeitlichen 
Abstände eingehalten werden. Daher braucht es ein gewisses Delay der 
Leitungen gegenüber den Signalen. Entweder an den entsprechenden Stellen 
ein wait for 0 oder eine getaktete Simulation, was Ich empfehlen würde.


 Wenn der Empfänger allerdings mit Tristate-Signalen arbeitet, oder nur 
virtuell in der Testbench läuft, dann musst Du die Tristatefunktion 
nachbilden. Dazu muss aber wie schon gesagt, nicht der reale Buffer 
benutzt werden, sondern es reicht die übliche Instanzierung über Z bei 
output enable.

Die Signale, die wieder reingehen, hängen da direkt dran. Es braucht 
keine Unterscheidung über die Zustände, wenn Du im VHDL wirklich auf 0 
und 1 arbeitest. Probleme gibt das gfs bei Takten, wenn nicht 
"rising_edge" sondern "clock'event" verwendet wird.

Solche Konstrukte, mit "intelligenten pullups" braucht man nur, wenn man 
kein VHDL verdrahten kann, weil irgendwas aus fertigen Blöcken kommt. 
Bei Vivado/AXI hatte ich kürzlich so einen Fall und vor Längerem mit 
einem Entwicklungswerkzeug für ASICs, das keinen echten pullup 
verdrahten wollte. Auch der System Generator mit Simulink hatte sich da 
mal quer gestellt. Dann schreibt man sich einen eigenen virtuellen 
Buffer, der mit der hochohmigen Leitung und auch mit eventuellen 
Konflikten klarkommmt und den Input der Leitung entsprechend bedient. 
Wichtig ist auch hier ein Delay.

Bei mehreren Teilnehmern und unterschiedlichen Schaltschwellen habe Ich 
es mal komplett analog mit einem Kondensaor (Integrator) und 
zufliessender Information über Widerstände gelöst. Damit konnte man auch 
das Zeitverhalten und die Arbitirerung checken. (Der Bus war auf 
Kollision ausgelegt und die Teilnehmer testen, oder er frei ist, indem 
sie mal senden).

von Weltbester FPGA-Pongo (Gast)


Lesenswert?

Markus schrieb:
> Ich habe vor kurzem in der Firma einen bidirektionalen UART simuliert.
> Da hatte ich auch irgendein Konstrukt um einen "pullup" zu machen.
> Weiß aber nicht mehr wie genau ich es gemacht hatte.. Ich schaue morgen
> mal rein und poste es hier (wenn ich dran denke)
Nur her damit. Bidirektionale UARTs ... mit welcher Leitungstechnik war 
das? Die Seriellen die Ich kenne, haben immer TDX und RXD getrennt.

Jürgen S. schrieb:
> Der Bus war auf Kollision ausgelegt und die Teilnehmer testen,
> oder er frei ist, indem sie mal senden
Wer baut sowas? Wozu soll das gut sein? Hauen die den anderen 
Teilnehmern nicht die Daten kaputt, wenn sie senden?

Strubi schrieb:
> Das tut dann für die Simulation z.B. mit einem virtuellen I2C EEPROM
> genauso wie in real, nur dass in der TB SDA und SCL auf 'H' gezogen
> werden.
Das Problem wird aber sein, dass im VHDL irgendwo in der Testbench oder 
in einem Core auf 1 und nicht auch auf H geprüft wird. Nicht alle 
Simulatoren lösen das richtig auf. D.h. die Leitung, die Du simulierst, 
geht zwar auf high, aber das VHDL "merkt" es nicht.

von Duke Scarring (Gast)


Lesenswert?

Weltbester FPGA-Pongo schrieb im Beitrag #5106790:
> Das Problem wird aber sein, dass im VHDL irgendwo in der Testbench oder
> in einem Core auf 1 und nicht auch auf H geprüft wird. Nicht alle
> Simulatoren lösen das richtig auf. D.h. die Leitung, die Du simulierst,
> geht zwar auf high, aber das VHDL "merkt" es nicht.

Dafür gibt es die Funktion to_01 in der numeric_std. Damit kann man auch 
die nervigen metavalues-Warnungen beseitigen, die u.a. vor dem Reset 
auftreten können:
1
    process (ctrl, source)
2
    begin
3
        case to_integer( to_01( ctrl.scale)) is
4
            when 0 =>
5
                ...
6
            when 1 =>
7
                ...

Duke

von Andreas (Gast)


Lesenswert?

Weltbester FPGA-Pongo schrieb im Beitrag #5106790:
> Das Problem wird aber sein, dass im VHDL irgendwo in der Testbench oder
> in einem Core auf 1 und nicht auch auf H geprüft wird. Nicht alle
> Simulatoren lösen das richtig auf. D.h. die Leitung, die Du simulierst,
> geht zwar auf high, aber das VHDL "merkt" es nicht.

Oder wie hier beschrieben:

Andreas schrieb:
> scl_pad_i <= std_ulogic(to_bit(scl));
>
> Damit wird das 'H' heraus gefischt und eine '1' draus.

von Weltbester FPGA-Pongo (Gast)


Lesenswert?

Das ist alles richtig, aber wer schreibt denn seinen Code um, damit er 
mit H richtig läuft? Da kenne Ich niemanden. Oder macht ihr euren Code 
im Nachgang simulationsfähig?

von Burkhard K. (buks)


Lesenswert?

Andreas schrieb:
> Oder wie hier beschrieben:
>
> Andreas schrieb:
>> scl_pad_i <= std_ulogic(to_bit(scl));
>>
>> Damit wird das 'H' heraus gefischt und eine '1' draus.

Hmm,
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
...
5
   scl         <= 'H';
6
   scl         <= scl_pad_o when (scl_padoe_n = '0') else 'Z';
7
   scl_pad_i   <= std_ulogic(to_bit(scl));

wird mit folgender Fehlermeldung quittiert:
  "Cannot convert type bit to type std_ulogic"

von Markus F. (mfro)


Lesenswert?

Burkhard K. schrieb:
> scl_pad_i   <= std_ulogic(to_bit(scl));

so wird's richtig:

1
scl_pad_i   <= to_stdulogic(to_bit(scl));

: Bearbeitet durch User
von J. S. (engineer) Benutzerseite


Lesenswert?


von Duke Scarring (Gast)


Lesenswert?

Weltbester FPGA-Pongo schrieb im Beitrag #5108988:
> Oder macht ihr euren Code
> im Nachgang simulationsfähig?
Wenn es Gründe gibt, fasse ich jeden Code an.
Und eine Simulation zu verbessern, ist für mich ein Grund...

Duke

von Burkhard K. (buks)


Lesenswert?

Markus F. schrieb:
> so wird's richtig:
>
> scl_pad_i   <= to_stdulogic(to_bit(scl));

Der Code compiliert - dafür mag das Synthesetool dieses Konstrukt 
(Andreas' Vorschlag) überhaupt nicht:
1
   SCL         <= 'H';
2
   SCL         <= scl_pad_o when (scl_padoe_n = '0') else 'Z';
3
   scl_pad_i   <= to_stdulogic(to_bit(SCL));

  ERROR:Xst:528 - Multi-source in Unit <BME280_ctrl> on signal <SCL>; 
this signal is connected to multiple drivers.
  Drivers are:
     Output signal of BUFT instance <BME280_UNIT/SCL_scl_pad_o>
     Signal <BME280_UNIT/SCL> in Unit <BME280_ctrl> is assigned to VCC

Zurück auf Los, keine 4000 Mark eingenommen: Gesucht wird Code, der 
sowohl synthetisiert als auch korrekt simuliert, z.B.:
1
   -- I2C line tristate logic
2
   scl         <= scl_pad_o when (scl_padoe_n = '0') else 'Z';
3
   scl_pad_i   <= '0' when scl = '0' else
4
                  '1' when scl = '1' else
5
                  '1' when scl = 'Z' else
6
                  '0';

Für die Logik muss sowieso auf "bit" gemapped werden - ich sehe nicht 
welchen Vorteil eine Zuweisung von 'H' hier bringen soll?

von Strubi (Gast)


Lesenswert?

Hi Burkhard,

die 'H'-Zuweisung gehört nur in die Testbench für den Toplevel. Die 
Synthese kann typischerweise damit nix anfangen.
Sowas wie 'H' könnte man ansich als Pullup interpretieren, aber die 
Tools wollen es lieber sauber, und erwarten die korrekten 
I/O-Constraints in einer separaten Datei (hier wohl eine .ucf). Da Aber 
i2c einen 1.5k externen PU vorsieht, ist das hinfällig, nur wichtig ist, 
dass du SCL und SDA als Open Drain konfigurierst.
Es reicht ja, wenn du SCL/SDA nur auf '0' testest, wie
1
        sda_buf <= '0' when sda = '0' else '1';

Der Original-Code von Herveille hat, wenn ich mich dunkel erinnere, auch 
schon Probleme bei manchen Tools mit dem alten to_X01-Thema gemacht, 
drum hat Lattice eine bereinigte Version irgendwo in den Refdesigns 
rumliegen.

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
Noch kein Account? Hier anmelden.