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(1downto0)&cSCL(1));
5
fSDA<=(fSDA(1downto0)&cSDA(1));
6
7
sSCL<=(fSCL(2)andfSCL(1))or
8
(fSCL(2)andfSCL(0))or
9
(fSCL(1)andfSCL(0));
10
sSDA<=(fSDA(2)andfSDA(1))or
11
(fSDA(2)andfSDA(0))or
12
(fSDA(1)andfSDA(0));
zusammen mit dem (von Herveille empfohlenen) Code zur Erzeugung der
Tristate-Buffer:
1
scl<=scl_pad_owhen(scl_padoe_n='0')else'Z';
2
sda<=sda_pad_owhen(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_owhen(scl_padoe_n='0')else'Z';
2
sda<=sda_pad_owhen(sda_padoe_n='0')else'Z';
3
scl_pad_i<='1'whenscl='Z'else
4
'1'whenscl='1'else
5
'0';
6
sda_pad_i<='1'whensda='Z'else
7
'1'whensda='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
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.
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.
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.
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?
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)
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.
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).
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.
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:
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.
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?
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
libraryIEEE;
2
useIEEE.STD_LOGIC_1164.ALL;
3
useIEEE.NUMERIC_STD.ALL;
4
...
5
scl<='H';
6
scl<=scl_pad_owhen(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"
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
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_owhen(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_owhen(scl_padoe_n='0')else'Z';
3
scl_pad_i<='0'whenscl='0'else
4
'1'whenscl='1'else
5
'1'whenscl='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?
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'whensda='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.