Forum: FPGA, VHDL & Co. Beschreibung funktioniert nach Synthese nicht


von Steffen Hausinger (Gast)


Lesenswert?

Hallo zusammen,

in den folgenden zwei Beispielen lasse ich mir ein Byte ("ToggleByte") 
permanent invertieren und an eine Senderoutine schicken. Beide sind 
einwandfrei simulierbar und tuen, für was sie gedacht. Nach der Synthese 
sieht das anders aus: Das erste Beispiel funktioniert, das zweite nicht.

Hier die beiden Beispiele:

Beispiel 1:
1
  Debug_Adder : process(CLK)
2
    variable StateMachine: integer range 0 to 2 := 0;
3
    variable ToggleByte : STD_LOGIC_VECTOR(7 downto 0) := x"00";
4
  begin
5
    if rising_edge(CLK) then
6
7
      case StateMachine is
8
      
9
      -- Senden
10
      when 1 =>
11
        if NewTransmitACK = '0' then                  -- Ist der Sender frei?
12
          Transmit <= (others => '0');                -- Nachricht pauschal löschen
13
          Transmit(7 downto 0) <= ToggleByte;         -- Byte zur Überprüfung, ob Nachricht aktualisiert wurde, in Byte 0 senden
14
          NewTransmit <= '1';                         -- Senden anfordern
15
        end if;                                       --
16
17
      -- Toggeln
18
      when 2 =>
19
        if NewTransmitACK = '1' then                  -- Nachricht übernommen?
20
          NewTransmit <= '0';                         -- Anforderung zurücknehmen
21
          ToggleByte := not ToggleByte;               -- Byte toggeln
22
          StateMachine := 0;                          -- StateMachine resetten, um Überlauf zu verhindern
23
        end if;                                       --
24
      
25
      when others =>
26
        null;
27
      end case;
28
      
29
      StateMachine := StateMachine + 1;                -- Automatisch nächsten Schritt wählen
30
      
31
    end if;
32
  end process;

Beispiel 2:
1
  Debug_Direct : process(CLK)
2
    variable StateMachine: integer range 0 to 1 := 0;
3
    variable ToggleByte : STD_LOGIC_VECTOR(7 downto 0) := x"00";
4
  begin
5
    if rising_edge(CLK) then
6
7
      case StateMachine is
8
      
9
      -- Senden
10
      when 0 =>
11
        if NewTransmitACK = '0' then                  -- Ist der Sender frei?
12
          Transmit <= (others => '0');                -- Nachricht pauschal löschen
13
          Transmit(7 downto 0) <= ToggleByte;         -- Byte zur Überprüfung, ob Nachricht aktualisiert wurde, in Byte 0 senden
14
          NewTransmit <= '1';                         -- Senden anfordern
15
          StateMachine := 1;                          -- Im nächsten Schritt Zähler erhöhen
16
        end if;                                       --
17
18
      -- Toggeln
19
      when 1 =>
20
        if NewTransmitACK = '1' then                  -- Nachricht übernommen?
21
          NewTransmit <= '0';                         -- Anforderung zurücknehmen
22
          ToggleByte := not ToggleByte;               -- Byte toggeln
23
          StateMachine := 0;                          -- Im nächsten Schritt wieder das Senden anfordern
24
        end if;                                       --
25
      
26
      end case;
27
      
28
    end if;
29
  end process;


Das meiste davon ist Overhead zum Senden. Der wesentliche Unterschied 
zwischen den beiden Beispielen: Im Ersten gebe ich den nächsten State 
meiner Statemachine im aktuellen State vor, im Zweiten erhöhe ich ihn 
automatisch per Addition. Den Überlauf fange ich dann im letzten Schritt 
der StateMachine ab.


Warum funktioniert das erste Beispiel nicht? Warum dafür das Zweite? Wo 
ist denn nur der Unterschied? Er synthetisiert es anders. Aber was ist 
denn an der ersten Beschreibung nur falsch?

von Frank B. (foobar)


Lesenswert?

Der Unterschied ist, daß du im ersten Beispiel die Statemachine-Variable 
unbedingt erhöhst und im zweiten Beispiel nur innerhalb des States. 
Daher würde ich mal vermuten, daß das erste Beispiel nicht läuft. Ist 
sowieso keine gute Idee, das so zu schreiben, da z.B. manche Simulatoren 
einen Overflow-Error ausgeben würden und ich bin mir nicht sicher, ob 
das in VHDL definiert ist, daß es ein Wraparound geben muß bei 
interger-Variablen. Ich mache das immer so, daß ich Überläufe abfange 
bzw. so wie du im zweiten Beispiel, daß die gar nicht erst auftreten 
können.

Noch zwei Tipps: Verwende besser sowas wie "type STATE_TYPE is 
(aussagekraefteriger_name, nochwas);" für die Codierung von 
Statemachines. Für binäre Statemachinen kannst du aber auch eine 
Variable vom Typ "boolean" nehmen. Und "variable" brauchst du an der 
Stelle nicht, nimm besser ein "signal" und dann den becomes-Operator 
("<="). Zu beachten dabei: signale haben während des gesamten Prozesses 
den Wert bei Eintritt des Prozesses, unabhängig davon, was du denen im 
Laufe des Prozesses zuweist und der letzte zugewiesene Wert wird bei 
Verlassen des Prozesses übernommen. Ist nicht schwierig, wenn man sich 
mal dran gewöhnt hat, kann aber manchmal ein paar Gatter sparen 
gegenüber der Programmierung per variable.

von Steffen Hausinger (Gast)


Lesenswert?

Hallo foobar,

Frank Buss schrieb:
> Der Unterschied ist, daß du im ersten Beispiel die Statemachine-Variable
> unbedingt erhöhst und im zweiten Beispiel nur innerhalb des States.
> Daher würde ich mal vermuten, daß das erste Beispiel nicht läuft.

Es ist aber genau umgekehrt: Das erste Beispiel funktioniert, das zweite 
nicht. Ich kann aber auch nicht erkennen, was daran falsch sein soll. Im 
nicht-funktionierenden Beispiel wird die Variable nur innerhalb des 
States erhöht - aber was ist daran denn schlimm? Wieso kann er sie nicht 
einfach abspeichern und beim nächsten Durchlauf wieder aufrufen?! Welche 
Regel wird da denn verletzt?

Frank Buss schrieb:
> Noch zwei Tipps [...]

Vielen Dank für Deine Tipps. Ich beherzige sie normalerweise. Für dieses 
Posting habe ich aber allen Ballast rausgeworfen (type). Und die 
Variablen habe ich verwendet, weil ich sie innerhalb des Prozesses 
benennen kann (finde ich für das Verständnis kompakter). Mit Signalen 
ändert sich das Verhalten leider nicht.



Grüße
Steffen

von Frank B. (foobar)


Lesenswert?

Woher kommt "NewTransmitACK"? Am besten mit rising_edge(CLK) in ein 
zusätzliches "signal" Latch übernehmen und dann dieses verwenden, falls 
die dadurch verursachte Verzögerung von einem zusätzlichen Takt erlaubt 
ist. Asynchron änderbare Signale verursachen schöne Probleme mit 
Setup/Hold-Zeitverletzungen und können zu nicht-determinitischen 
Verhalten führen.

Hatte ich mal bei einer Statemachine gehabt: die blieb nach einiger Zeit 
einfach in irgendeinem illegalen Zustand hängen.

von Steffen Hausinger (Gast)


Lesenswert?

Frank Buss schrieb:
> Woher kommt "NewTransmitACK"?

Das Signal wird intern erzeugt und ist synchron. Du könntest aber 
trotzdem auf der richtigen Spur sein. Es kann an "NewTransmitACK" 
liegen, denn das ist in Beispiel 2 eine Stelle, an der er sich aufhängen 
kann, während in Beispiel 1 dort weitergezählt wird (da externer 
Zähler).

Wenn das stimmt, war ich die ganze Zeit auf dem falschen Dampfer!!

von Steffen Hausinger (Gast)


Lesenswert?

Ok, es stimmt leider. Das Signal "NewTransmitACK" war Schuld! Und ich 
habe mir hier einen Wolf gesucht :-((

Vielen Dank für Deinen Hinweis, er hat mir sehr weitergeholfen!

Grüße
Steffen

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.