Forum: FPGA, VHDL & Co. Internes Signal "asynchron" fest auf '1' gelegt wird nicht erkannt.


von Dominik (Gast)


Lesenswert?

Hallo zusammen,

ich habe nach sehr langem Testen ein für mein Empfinden äußerst 
komisches Verhalten bei meinem VHDL Code festgestellt. Ich versuche es 
mal kurz zusammenzufassen:
Ich habe eine Schnittstelle umgesetzt, welche Signale nach außen 
sendet/schreibt und empfangen/lesen kann. Die Schnittstelle wird in 
meinem Top-Level instanziiert, mit einem enable-Signal wird der Lese- 
oder Schreibprozess gestartet und mit einem internen Signal readWrite 
wird festgelegt, ob geschrieben oder gelesen werden soll. Alles 
funktioniert wunderbar.
In einer Test-Anwendung muss nun nur geschrieben werden. Um keinen 
Aufwand zu haben, hatte ich in der port map das readWrite Signal einfach 
fest auf '1' gesetzt. Die Verhaltens-Simulation zeigte weiterhin das 
gewünschte Verhalten. Bei einer Post-Translate-Simulation war das 
Verhalten jedoch ziemlich verwirrend und entspricht nicht dem 
erwarteten. Nach langem hin und her hatte ich festgestellt, dass es an 
der festen Zuweisung von readWrite gleich '1' liegt. Verwende ich für 
readWrite ein Signal, welches einfach in einem getakteten Prozess fest 
auf '1' gesetzt wird, funktioniert wieder alles. Die Abfrage innerhalb 
der Schnittstelle ist allerdings ebenfalls getaktet und fragt bei enable 
lediglich ab, ob readWrite gleich '0' oder '1' ist.
Das komische ist nun weiterhin, dass das Verhalten bei fester Zuweisung 
auf '1' weder dem Verhalten für '1' noch dem Verhalten für '0' 
entspricht, sondern eben sehr seltsam ist...
Die "komischen" Ergebnisse der Timing-Simulation wurden durch Messung 
mit dem Oszi bestätigt. Es ist also kein Simulationsfehler, sondern 
entspricht der Realität.

Ich weiß wie gesagt was der Fehler ist und wie er behoben werden kann:
Sobald readWrite entweder asynchron oder in der port map fest auf '1' 
gesetzt wird, ist das Verhalten nicht wie erwartet. Wird ein getakteter 
Prozess verwendet, in welchem readWrite fest auf '1' gesetzt wird, geht 
alles.
Jetzt würde gerne verstehen warum das so ist bzw. woran das liegen kann.

Vielen Dank im Voraus für eure Hilfe und Erklärungen!

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


Lesenswert?

Dominik schrieb:
> Sobald readWrite entweder asynchron oder in der port map fest auf '1'
> gesetzt wird, ist das Verhalten nicht wie erwartet.
Was passiert, wen dieses signal ein Portpin ist, der auf einen 
definieren Pegel gelegt wird?

> Verwende ich für readWrite ein Signal, welches einfach in einem
> getakteten Prozess fest auf '1' gesetzt wird, funktioniert wieder alles.
Dieses Verhalten deutet auf ein Timingproblem durch wegoptimierte und 
gekürzte Pfade hin. Ausgangspunkt dieses Verhaltens sind gern 
irgendwelche asynchronen Pfade oder Resets im System.

Man müsste für eine brauchbare Analyse also mal die fragliche 
Beschreibung sehen...

> Jetzt würde gerne verstehen warum das so ist bzw. woran das liegen kann.
Es könnte auch ein Fehler oder eine Eigenheit der bisher unbekannten 
Toolchain beim bisher unbekannten FPGA sein.

von Dominik (Gast)


Lesenswert?

Vielen Dank für die schnelle Antwort.

Es handelt sich um einen Xilinx Spartan-6 LX9 (auf dem Avnet MicroBoard 
Entwicklungsboard).
Verwendet habe ich ISE Version 14.7. Simuliert wurde mit ISim.

Leider bin ich mir nicht ganz sicher, was genau mit "Portpin" gemeint 
ist. Eine "abgespeckte" Version meines Codes (zeigt gleiches Verhalten) 
ist hier:
1
use IEEE.STD_LOGIC_1164.ALL;
2
3
entity tmp is
4
   Generic (CLKIN_PERIOD : real := 10.0);
5
    Port ( enable : in std_logic;
6
        clk : in std_logic; 
7
        rst : in std_logic; 
8
        out1 : out std_logic;
9
          out2 : out std_logic;
10
        out3 : out std_logic;
11
        out4 : out std_logic
12
       );
13
end tmp;
14
15
architecture Behavioral of tmp is
16
17
18
  signal selectController : integer range 1 to 3;
19
  signal writeAllRegToDriver : std_logic; 
20
  signal writeOneRegToDriver : std_logic; 
21
  signal regAddress : std_logic_vector(2 downto 0); 
22
  signal dataToIntReg : std_logic_vector(11 downto 0); 
23
24
25
  component SPI_writeReg   
26
    Port (  clk : in  STD_LOGIC;    
27
        enable : in STD_LOGIC;   
28
        rst : in STD_LOGIC;  
29
        selectController : in integer range 1 to 3;
30
        writeAllRegToDriver : in std_logic; 
31
        writeOneRegToDriver : in std_logic; 
32
        regAddress : in  std_logic_vector(2 downto 0);
33
        dataToIntReg : in std_logic_vector(11 downto 0);
34
        SCS : out  STD_LOGIC; 
35
        SCLK : out  STD_LOGIC;
36
        DATAtoDRIVER : out  STD_LOGIC;
37
        writeDone : out std_logic
38
      ); 
39
  end component; 
40
  
41
begin
42
43
  SPI_writeReg_inst : SPI_writeReg 
44
    port map ( clk => clk,    
45
          enable => enable,   
46
          rst => rst,  
47
          selectController => selectController,
48
          writeAllRegToDriver => writeAllRegToDriver,
49
          writeOneRegToDriver => writeOneRegToDriver,
50
          regAddress => regAddress,
51
          dataToIntReg => dataToIntReg,
52
          SCS => out1,
53
          SCLK => out2,
54
          DATAtoDRIVER => out3,
55
          writeDone => out4
56
          ); 
57
          
58
59
--process     
60
--begin
61
--wait until rising_edge(clk);
62
  dataToIntReg <= "000000000000";
63
  regAddress <= "000";
64
  writeAllRegToDriver <= '1';
65
  writeOneRegToDriver <= '0';
66
  selectController <= 1;
67
--end process; 
68
  
69
end Behavioral;

Wird process... einkommentiert, funktioniert alles. Ist der Teil 
auskommentierten, funktioniert es nicht.

Innerhalb von SPI_writeReg wird wie folgt abgefragt:
1
  process
2
  begin
3
    wait until rising_edge(clk);
4
    
5
   ...
6
    if enable = '1' then 
7
   ...
8
      if writeAllRegToDriver = '1' then
9
   ...

Der Tipp mit den wegoptimierten Pfaden hört sich sehr plausibel an. Kann 
ich das zum testen irgendwie ausstellen?

Nochmals vielen Dank für die Hilfe und die Bemühungen!!

von Dominik (Gast)


Lesenswert?

Ich kann auch gerne den ganzen Code zur Verfügung stellen. Aber ich 
fürchte da werden einige die Hände über dem Kopf zusammenschlagen was da 
so wie gemacht wurde...

von Schlumpf (Gast)


Lesenswert?

Dein Eingang "enable" ist nicht synchronisiert, wenn ich das richtig 
gesehen habe.
Eventuell liegt das Problem daran.

Das Timing ändert sich, abhängig davon, ob du die anderen Signale 
synchronisierst oder nicht.
Und dann tritt das letztendlich vom asynchronen enable verursachte 
Problem auf, oder auch nicht.

von Dominik (Gast)


Lesenswert?

Hallo Schlumpf,
vielen Dank für die Antwort. Ich habe das etwas unschön hingeschrieben. 
Das enable ist in dem gleichen Prozess und nach dem wait until. Es 
sollte also alles synchronisiert sein.

von Mike (Gast)


Lesenswert?

Dominik schrieb:
> Hallo Schlumpf,
> vielen Dank für die Antwort. Ich habe das etwas unschön hingeschrieben.
> Das enable ist in dem gleichen Prozess und nach dem wait until. Es
> sollte also alles synchronisiert sein.

Ich vermute mal, er wollte dich auf das Problem mit der Metastabilität 
hinweisen. Enable wird jetzt nicht aus einer anderen Taktdomäne 
gesteuert? Es wäre mir aber neu wenn die Tools deswegen Dinge 
wegoptimieren.

Was noch ein Problem sein könnte: gibt es eventuell noch eine andere 
Stelle wo du das Signal beschreibst? Oder wird es nie gesetzt? Du 
solltest dir mal alle Warnungen anschauen die ISE so ausspuckt.

von Vancouver (Gast)


Lesenswert?

Ist wahrscheinlich nicht der Grund, aber "wait until" in einem 
synthetiserten process ist zumindest etwas ungewöhnlich. Es ist 
syntaxmäßig nicht verboten, aber viele Synthesetools wollen hier lieber 
ein "if rising_edge(clk) then .. else" sehen. Das steht zumindest in den 
Guidelines von Xilinx überall so drin, und nach meiner Erfahrung sind 
manche Tools etwas zickig, wenn man es anders macht

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


Lesenswert?

Mike schrieb:
> Du solltest dir mal alle Warnungen anschauen die ISE so ausspuckt.
Dabei im Besonderen auf "Latches" oder "Combinatorial Loops" achten.

Wieviele Takte hast du in dem Design (alles mit 'event oder 
rising_edge() oder falling_edge() ist ein Takt)?

Vancouver schrieb:
> Ist wahrscheinlich nicht der Grund, aber "wait until" in einem
> synthetiserten process ist zumindest etwas ungewöhnlich.
Jeder Synthesizer kann seit gut 10 Jahren das "wait until" sinnvoll 
interpretieren.

> Es ist syntaxmäßig nicht verboten, aber viele Synthesetools wollen hier
> lieber ein "if rising_edge(clk) then .. else" sehen.
Ich weiß, es ist Pfennigfuchserei, aber kein Synthesetools möchte nach 
einem "if rising_edge() then" noch ein "else" sehen... ;-)

von berndl (Gast)


Lesenswert?

Lothar M. schrieb:
> Ich weiß, es ist Pfennigfuchserei, aber kein Synthesetools möchte nach
> einem "if rising_edge() then" noch ein "else" sehen... ;-)

hmm, Stichwort 'asynchroner Set/Reset'?
Ich weiss, klassisch schreibt man das andersrum, aber so geht's auch...

von Schlumpf (Gast)


Lesenswert?

Dominik schrieb:
> Das enable ist in dem gleichen Prozess und nach dem wait until. Es
> sollte also alles synchronisiert sein.

Das verstehe ich nicht ganz.. du verbindest in deinem Design den 
enable-Port direkt mit dem enable-Eingang des SPI. Damit ist es nicht 
synchronisiert.

Wenn du einfach nur eine Zeile wie z.B.
1
enable_sync <= enable;
vergessen hast, dann fehlt da aber auch noch die Signaldeklaration des 
synchroniserten Signals und auf das SPI ist dann auch das falsche Signal 
gemapped.

Bist du sicher, dass wir vom Gleichen reden?

von Dominik (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

nochmals vielen Dank für die Kommentare und Tipps.

Zu den einzelnen Punkten: Es wird nur ein Taktsignal verwendet. Enable 
wird auch nur an einer Stelle gesetzt. Um sicher zu gehen, habe ich auch 
alle wait until durch if rising_edge (und die Sensitivity list) ersetzt, 
leider ohne Erfolg.
Sämtliche ausgegebenen Warnungen beziehen sich auf "Signal hat 
konstanten Wert / Signal ist gleich einem anderen Signal. Das FF/Latch 
wird im Optimierungsprozess entfernt." oder "Signal ist nicht 
verbunden". Da ich nur einen Teil der beschriebenen Hardware verwende, 
sind diese Warnungen natürlich logisch und nachvollziehbar.

Wenn ich mir das RTL Schematic anschaue, dann wird bei getaktetem 
writeAllRegToDriver <= '1'; das Signal direkt mit VCC verbunden. Ist 
writeAllRegToDriver <= '1'; asynchron, dann ist die Leitung einfach 
nicht verbunden.
Daher würde ich gerne nochmal versuchen das "Optimieren" zu 
unterdrücken. Gibt es da eine Einstellung um ISE zu sagen, dass nichts 
entfernt werden soll?

Falls jemand Zeit und Lust hat sich das einmal anzuschauen, ich habe den 
Code jetzt doch angehängt. Die Top-Level Datei ist tmp.vhd

von Schlumpf (Gast)


Lesenswert?

... und was machst du mit deinem enable im toplevel?
synchronisierst du das? Sieht zumindest so aus, als wäre das nicht der 
Fall

von Dominik (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Schlumpf,

du hast recht, das enable-Signal wird hier nicht einsynchronisiert. Das 
hatte ich bei der "abgespeckten" Version vergessen. Allerdings ändert 
das leider auch nichts. Bei der "realen" Anwendung hatte ich noch eine 
entsprechende Stufe davor geschaltet.
Wobei ich dachte, dass bei einer taktsynchronen Abfrage des 
enable-Signals und Einhaltung aller Timing-Constraints zumindest in der 
Simulation mit einem sauberen Übergang der Signale ohne Spikes, 
Rauschen, Jitter usw. das an dieser Stelle egal sein sollte (auch wenn 
es dann in der Realität vielleicht nicht zuverlässig laufen würde...).

Bei dem obigen Code hatte ich die Test-Bench vergessen. Diese habe ich 
hier noch angehängt.

von Schlumpf (Gast)


Lesenswert?

Dominik schrieb:
> Wobei ich dachte, dass bei einer taktsynchronen Abfrage des
> enable-Signals und Einhaltung aller Timing-Constraints zumindest in der
> Simulation mit einem sauberen Übergang der Signale ohne Spikes,
> Rauschen, Jitter usw. das an dieser Stelle egal sein sollte (auch wenn
> es dann in der Realität vielleicht nicht zuverlässig laufen würde...).

Das enable-Signal wirkt in deinem Desing auf viele Register.
Diese Register sind synchron zueinander (alle haben den gleichen Takt)
Aber der logische Pfad vom enable-Pin bis zum Dateneinang der Register 
hat "irgendeine" Länge.
Kommt nun das enable-Signal zu einem beliebigen Zeitpunkt, so kann es 
passieren, dass die Information bei einem Teil der Register noch 
rechtzeitig vor der Taktflanke da ist und somit übernommen wird.
Bei einem anderen Teil der Register kann es passieren, dass sie 
Metastabil werden, wenn tsu unterschritten wird. Und bei einem weiteren 
Teil der Register kann es passieren, dass die Information erst nach der 
Taktflanke kommt und somit erst mit der darauffolgenden Flanke 
übernommen wird.
Wie du siehst, kann alles mögliche in deinem Design passieren, wenn du 
enable nicht synchronisierst.

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


Lesenswert?

berndl schrieb:
> hmm, Stichwort 'asynchroner Set/Reset'?
> Ich weiss, klassisch schreibt man das andersrum
Das ist aber eigentlich prinzipiell falsch und könnte sogar bei einer 
Verhaltenssimulation zu Race-Conditions führen (Stichwort Timestep).
Denn diese Beschreibung müsste dann "während" des steigenden Taktes 
(also für die theoretische Dauer 0) ein high am Ausgang zeigen:
1
  if rising_edge(clk) then
2
     ausgang <= '1';
3
  else 
4
     ausgang <= '0';
5
  end if;
Und diese Schaltung hier muss das bei aktivem Reset(!) tun:
1
  if rising_edge(clk) then
2
     ausgang <= '1';
3
  elsif reset='1' then
4
     ausgang <= '0';
5
  end if;

> aber so geht's auch...
Das hast du aber selber rausgefunden...  ;-)

von Dominik (Gast)


Lesenswert?

Das mit dem einsynchronisieren sehe ich ein und ist natürlich völlig 
richtig. Ich habe es gleich nochmal probiert. Leider hat es mein 
aktuelles Problem auch nicht gelöst...

von Achim S. (Gast)


Lesenswert?

Dominik schrieb:
> Leider hat es mein
> aktuelles Problem auch nicht gelöst...

writeAllRegToDriver ist bei dir ja nur ein internes signal. Wenn du es 
fest auf 1 legst, dann hat der Synthesizer keinen Grund, ein solches 
Signal zu erzeugen. Sondern er kann die Abfragen des Signals schon zur 
Synthesezeit auswerten.  Also z.B. in deiner Abfrage

      if writeAllRegToDriver = '1' then
         Anweisungsblock1
      elsif ...
         Anweisungsblock2

wird er gleich nur den ersten Block synthetisieren. Der elsif-Teil wird 
ignoriert. Und damit braucht man auch kein Signal writeAllRegToDriver 
mehr, da keine if-Abfrage mehr übrig ist, die das Signal auswerten 
würde.

Wenn alles richtig läuft, brauchst du in der Simu also kein Signal 
writeAllRegToDriver zu sehen (das zu nichts mehr genutzt wird). Du 
solltest aber sehen, dass der erste Block deiner If-Abfrage 
synthetisiert wurde (und der zweite Block nicht).

Wenn du ein definiertes Signal writeAllRegToDriver sehen willst, dann 
kannst du das z.B. auf der Top-Ebene eine Kopie davon als Ausgang 
treiben - dann solltest du dort die erwartete 1 sehen können.

von VHDL hotline (Gast)


Lesenswert?

Wenn es mit Initialwerten aus einem getakteten Prozess funktioniert und 
mit direkt angelegten nicht, passiert möglicherweise in dem allerersten 
Takt irgendwas Unerwartetes. Die getakteten Signale (zumindest die 
ungleich 0) sind ja erst im zweiten Takt an deiner weiteren Logik, die 
ungetakteten sofort. Im allerersten Systemtakt läuft das System da 
vielleicht schon in einen anderen Zustand.

Außerdem solltest du in deiner TB deine Signale nicht genau zur 
Taktflanke ändern, das hat schon gelegentlich zu Verwirrung mit 
Simulatordeltazyklen geführt.

von Schlumpf (Gast)


Lesenswert?

Hast du auch den rst_port synchronisiert? Oder ist der im "echten" 
Design asynchron?

von Dominik (Gast)


Lesenswert?

Vielen Dank nochmal für die sehr interessanten und hilfreichen Tipps.

Es stimmt auf jeden Fall, dass sehr viel wegoptimiert wird. Und es ist 
natürlich ein guter Punkt, dass die if-Abfrage nach writeAllRegToDriver 
nicht umgesetzt wird, wenn diese immer gleich '1' ist. Wobei dieser Teil 
ja auch noch korrekt ausgeführt wird (zumindest scheinbar). Trotzdem 
natürlich ein logischer Punkt, den ich nicht richtig auf dem Schirm 
hatte.

Auch den Tipp mit der TB und dem Zeitpunkt der Änderung der Signale 
werde ich in Zukunft auf jeden Fall berücksichtigen. Danke!

Den Reset hatte ich auch synchronisiert. Da hatte ich mich auf der Seite 
von Lothar Miller schlau gemacht und viel dazu gelernt.

An den Reset als Übeltäter hatte ich auch schon gedacht. Jetzt hatte ich 
mich gewundert, warum die Adresse der Register regAddress_int zum Teil 
wegoptimiert wird, obwohl die sich eigentlich aendert. Dieses Signal 
hatte ich bei einem Reset nicht berücksichtigt, da ich dachte es wird 
sowieso sobald es verwendet wird neu gesetzt. Deshalb habe ich 
regAddress_int einfach ebenfalls bei einem Reset nochmals auf "000" 
gesetzt - und siehe da, es funktioniert. Ich muss leider zugeben, dass 
ich es immer noch nicht ganz verstehe warum. Ich dachte der 
Initialisierungswert eines std_logic_vectors sei überall 0 und das 
Signal wird ja bei einem enable überschrieben. Aber es liegt 
offensichtlich an der "Optimierung" und dem Reset...

von Achim S. (Gast)


Lesenswert?

Dominik schrieb:
> Jetzt hatte ich
> mich gewundert, warum die Adresse der Register regAddress_int zum Teil
> wegoptimiert wird, obwohl die sich eigentlich aendert.

sie ändert sich imho nicht. Im Block

   if writeAllRegToDriver = '1' then

weist du der regAddress_int eine Null zu.

Und die Case-Struktur, in der du die Adresse hochzählst, erreichst du 
nicht, weil die Bedingung

   SPI_waitBetweenWriteCycles(SPI_waitBetweenWriteCycles'left to 
....)="10"

nie erfüllt ist (du weist SPI_waitBetweenWriteCycles nie etwas anderes 
als 0 zu).

von Dominik (Gast)


Lesenswert?

Ich habe jetzt das "Optimieren" von regAddress_int mit dem Attribut KEEP 
unterdrückt und jetzt funktioniert alles!
Es liegt also tatsächlich daran, dass durch die Optimierung ein 
eigentlich benötigter Teil aus der Schaltung entfernt wird und dies dann 
zu einem Fehler führt.

Ich möchte mich hiermit noch einmal ganz herzlich für die vielen Tipps 
und hilfreichen Kommentare bedanken!!!

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


Angehängte Dateien:

Lesenswert?

Dominik schrieb:
> dachte der Initialisierungswert eines std_logic_vectors sei überall 0
Wenn kein Initialwert angegeben wird, kann sogar ein Register wie das 
hier wegoptimiert werden:
1
signal flag : std_logic;
2
:
3
process (clk) begin
4
  if rising_edge(clk) then   -- Ganz logisch: ein Takt
5
    flag <= '1';             -- ergibt ein Flipflop
6
  end if;
7
end process;
Denn der Synthesizer sieht, dass der Wert von flag eigentlich egal ist 
und dann auf '1' gesetzt wird. Also setzt er ihn sofort auf '1' gesetzt 
und kann entsprechende Codeteile wegoptimieren. Und jetzt kommt der 
überraschende Trick: das passiert sogar, wenn ein enable im Spiel ist:
1
signal flag : std_logic;
2
:
3
process (clk) begin
4
  if rising_edge(clk) then   -- Ganz logisch: ein Takt
5
    if enable='1' then       -- mit enable
6
      flag <= '1';           -- ergibt ein Flipflop
7
    end if;
8
  end if;
9
end process;

Erst mit Initialisierung wird tatsächlich ein Flipflop daraus...

von Dominik (Gast)


Lesenswert?

Again what learned ;-) Man muss wirklich an allen Ecken und Enden 
aufpassen...

Vielen Dank für die Erklärung!!!

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.