Forum: FPGA, VHDL & Co. Finite-State-Machine stürzt ab


von Martin S. (maklin)


Lesenswert?

Hallo!

Ich arbeite gerade an einer Finite State Machine, welche einen Satz von 
Befehlen dekodieren soll, dann den Befehl ausführen und zum Schluss den 
Programmzähler auf den nächsten Befehl setzen.

Dummerweise scheint an diesem System irgendetwas fehlerhaft zu sein, vor 
Allem in Bezug auf die beiden SPI-Befehle. Der Zugriff auf das 
SPI-System als solches funktioniert grunsätzlich gut, jedoch nicht aus 
der State-Machine heraus. Interessanterweise läuft die State Machine an, 
wenn man die SPI-Befehle auskommentiert und stattdessen nur den 
Programmcounter erhöht. Jedoch kommt das System auch dann scheinbar nur 
bis zu dem längeren Wartebefehl.

Ich habe den Eindruck, dass das gesamte System vom Grundaufbau her 
fehlerhaft ist, ggf. könnte der Fehler auch an Timing-Problemen liegen. 
Zum Teil ändern sich bedeutende Systemeigenschaften, wenn ich irgendein 
Signal ändere, das eigentlich nur kleinere Auswirkungen haben sollte. 
Ich komme an dieser Stelle schlichtweg nicht weiter (ich kenne mich auch 
kaum mit den Timing-Constraints aus (für ein gutes Tutorial wäre ich 
dankbar)), und würde mich freuen, wenn jemand Fehler finden würde.

Die FSM (Ich möchte hier nicht das komplette Projekt posten, da es den 
Rahmen sprengen würde, kann aber gerne noch gewünschte Programmteile 
nachliefern):
1
  main: process(CLK, Reset, Schiebe_rdy_rec, Schiebe_start, SPI_RDY, SDI)
2
    variable programmcounter: integer range 0 to Anzahl_befehle  := 0;
3
    variable zaehler_prog  : integer                  := 0;
4
    variable for_prog_cnt  : integer range 0 to Anzahl_befehle  := 0; -- Merke, wo er im Programm steht, wenn for kommt.
5
    variable while_prog_cnt  : integer range 0 to Anzahl_befehle  := 0; -- Merke, wo er im Programm steht, wenn for kommt.
6
    variable for_zaehler    : integer                  := 0;
7
    variable wait_zaehler  : std_logic_vector(7 downto 0)    := "00000000";  
8
  begin
9
    if rising_edge(CLK) then
10
      if Reset = '1' then -- Kein Reset
11
        if programmcounter < Anzahl_befehle then -- Nicht mehr weiter im Programm wenn am Ende
12
          
13
          
14
          -- Testweise Debugausgabe
15
          Debug(3 downto 0)      <= Befehlsliste(programmcounter)(11 downto 8);
16
          case zaehler_prog is
17
            when 0 =>
18
              Debug(5 downto 4)  <= "00";
19
            when 1 =>
20
              Debug(5 downto 4)  <= "01";
21
            when 2 =>
22
              Debug(5 downto 4)  <= "10";
23
            when others =>
24
              Debug(5 downto 4)  <= "11";
25
          end case;
26
            
27
          
28
          
29
          case Befehlsliste(programmcounter)(11 downto 8) is
30
            when "0000" => -- 0  Schiebetakt,  Data: 0/1 
31
              -- Senden eines Taktes an das Schieberegister
32
              case zaehler_prog is
33
                when 0 =>
34
                  if Schiebe_rdy_rec = '0' then
35
                    Schiebe_data    <= Befehlsliste(programmcounter)(0);
36
                    Schiebe_rdy_send  <= '1';
37
                    zaehler_prog    := 1;
38
                  end if;
39
                when 1 =>
40
                  if Schiebe_start = '1' then
41
                    Schiebe_rdy_send   <= '0';
42
                    zaehler_prog    := 2;
43
                  end if;
44
                when others =>  
45
                  if Schiebe_rdy_rec = '0' then
46
                    zaehler_prog    := 0;
47
                    programmcounter  := programmcounter + 1;
48
                  end if;
49
              end case;
50
            when "0001" => -- 1  SPI send,    Daten 8 bit
51
              --programmcounter        := programmcounter + 1; -- Testweise
52
              -- SPI Versand
53
              case zaehler_prog is
54
                when 0 =>  
55
                  if SPI_RDY = '0' then
56
                    SPI_DATA_send    <= Befehlsliste(programmcounter)(7 downto 0);
57
                    SPI_sel        <= '1';
58
                    zaehler_prog    := 1;
59
                  end if;
60
                when others =>  
61
                  if SPI_RDY = '1' then
62
                    SPI_sel        <= '0';
63
                    zaehler_prog    := 0;
64
                    programmcounter  := programmcounter + 1;
65
                  end if;
66
              end case;
67
            when "0010" => -- 2  SPI rec,      Datenspeicherposition von 0 bis 7         -- SPI Empfang
68
69
              --programmcounter        := programmcounter + 1; -- Testweise
70
              case zaehler_prog is
71
                when 0 =>  
72
                  if SPI_RDY = '0' then
73
                    SPI_DATA_send    <= "00000000";
74
                    SPI_sel        <= '1';
75
                    zaehler_prog    := 1;
76
                  end if;
77
                when 1 =>  
78
                  if SPI_RDY = '1' then
79
                    SPI_sel        <= '0';
80
                    zaehler_prog    := 2;
81
                  end if;
82
                when others =>
83
                  if SPI_RDY = '0' then
84
                    zaehler_prog    := 0;
85
                    programmcounter  := programmcounter + 1;
86
                    case Befehlsliste(programmcounter)(1 downto 0) is
87
                      when "00" =>
88
                        Datenvector(7 downto 0)    <=  SPI_DATA_rec;
89
                        --TODO: Erstmal nicht speichern, um Fehlermeldungen zu vermeiden.
90
                      when "01" =>
91
                        Datenvector(15 downto 8)  <=  SPI_DATA_rec;
92
                      when others =>
93
                        Datenvector(23 downto 16)  <=  SPI_DATA_rec;
94
                    end case;
95
                  end if;
96
              end case;
97
            when "0011" => -- 3  wait_spi_rdy,  -
98
              --Warte, bis MISO auf low geht => dann weiter machen (Wollen die ADCs so...)
99
              if SDI = '0' then
100
                programmcounter      := programmcounter + 1;
101
              end if;
102
            when "0100" => -- 4  Übertrage empfangende Daten   -
103
              -- Befehl an anderen Programmteil zur Übernahme der SPI-Daten
104
              case zaehler_prog is
105
                when 0 =>
106
                  Neue_Daten        <= '1';
107
                  Datennummer        <= for_zaehler - 1;
108
                  zaehler_prog      := 1;
109
                when 1 =>
110
                  zaehler_prog      := 2;
111
                when others =>
112
                  zaehler_prog      := 0;
113
                  programmcounter    := programmcounter + 1;
114
                  Neue_Daten        <= '0';
115
              end case;
116
            when "0101" => -- 5  wait,        Zeit in Takten - 1
117
              -- Wartebefehl
118
              case zaehler_prog is
119
                when 0 =>
120
                  if Befehlsliste(programmcounter)(7 downto 0) = "00000000" then
121
                    zaehler_prog    := 0; --Sofortabbruch
122
                    programmcounter  := programmcounter + 1;
123
                  elsif Befehlsliste(programmcounter)(7 downto 0) = "00000001" then
124
                    zaehler_prog    := 2; -- Springe zum Ende
125
                  else
126
                    wait_zaehler    := Befehlsliste(programmcounter)(7 downto 0);
127
                    zaehler_prog    := 1;
128
                  end if;
129
                when 1 =>
130
                  if wait_zaehler = 0 then
131
                    zaehler_prog    := 2;
132
                  else
133
                    wait_zaehler    := wait_zaehler - 1;
134
                  end if;
135
                when others=>
136
                  zaehler_prog      := 0;
137
                  programmcounter    := programmcounter + 1;
138
              end case;      
139
            when "0110" => -- 6  for start,    Schleifendurchläufe
140
              -- Eine einfache For-Schleife
141
              -- !! Nicht For-Schleifen verschachteln !! Es existiert kein Stack.
142
              if Befehlsliste(programmcounter)(7 downto 0) >= 64 then --Damit das For noch bei dem Wait geht
143
                for_zaehler          := conv_integer(Befehlsliste(programmcounter)(7 downto 0));
144
              else
145
                for_zaehler          := Anzahl_ADC; 
146
              end if;
147
              for_prog_cnt           := programmcounter + 1;
148
              programmcounter        := programmcounter + 1;
149
            when "0111" => -- 7  for ende      -
150
              -- Teil 2 der For-Schleife
151
              if for_zaehler > 1 then
152
                for_zaehler          := for_zaehler - 1;
153
                programmcounter      := for_prog_cnt;
154
              else
155
                programmcounter      := programmcounter + 1;
156
                for_zaehler          := 0;--"00000000";
157
              end if;
158
            when "1000" => -- 8  Endlosschleife Start    -
159
              -- vgl. while(1) in c
160
              while_prog_cnt          := programmcounter + 1;
161
              programmcounter        := programmcounter + 1;
162
            when "1001" => -- 9  Endlosschleife Ende    -
163
              programmcounter        := while_prog_cnt;
164
            when "1111" => -- 15 RESET/INIT
165
              Schiebe_data           <= '0';
166
              zaehler_prog          := 0;
167
              programmcounter        := 1;
168
              Datenvector(23 downto 0)  <=  (others =>'0');
169
            when others  =>
170
              null;
171
          end case;
172
        end if;
173
      end if;
174
    end if;
175
  end process main;

Die Befehlsliste:
1
  --Befehlsliste: Vier Bit Befehlsnummer + Acht Bit Daten
2
  constant Anzahl_befehle:  integer  :=  55;
3
  type t_Befehlsliste is array (0 to Anzahl_befehle-1) of std_logic_vector(11 downto 0);
4
  constant Befehlsliste: t_Befehlsliste  :=
5
  (  ("111100000000"), -- Reset
6
    ("011000000100"), -- For so häufig wie in "Anzahl ADC" definiert
7
    ("000000000001"), -- Schiebetakt '1'
8
    ("011100000000"), -- For Ende
9
    ("000000000000"), -- Schiebetakt '0'
10
    ("011000000100"), -- For so häufig wie in "Anzahl ADC" definiert
11
    ("000111111111"), -- SPI send "FF"
12
    ("000111111111"), -- SPI send "FF"
13
    ("000111111111"), -- SPI send "FF"
14
    ("000111111111"), -- SPI send "FF"
15
    ("000111111111"), -- SPI send "FF"
16
    ("000000000001"), -- Schiebetakt '1'
17
    ("011100000000"), -- For Ende
18
    --delay 500 us
19
    --(
20
    ("011001111101"), -- For 125x
21
    ("010111001000"), -- Wait 200 Takte, d.h. 4us 
22
    ("011100000000"), -- For Ende
23
    --)
24
    ("000000000000"), -- Schiebetakt '0'
25
    ("011000000100"), -- For so häufig wie in "Anzahl ADC" definiert
26
    --AD7192 INIT
27
    --(
28
    ("001100000000"), -- SPI Wait Ready
29
    ("000100001000"), -- SPI send "(1<<RS0)"
30
    ("000100001000"), -- SPI send "(1<<CLK1)"
31
    ("000100000000"), -- SPI send "0"
32
    ("000100000001"), -- SPI send "(1<<FS0)"
33
    ("010111001000"), -- Wait 200 Takte, d.h. 4us 
34
    ("001100000000"), -- SPI Wait Ready
35
    ("000100101000"), -- SPI send "(1<<RS2)|(1<<RS0)"
36
    ("000101000000"), -- SPI send "(1<<BPDSW)"
37
    --)
38
    ("000000000001"), -- Schiebetakt '1'
39
    ("011100000000"), -- For Ende
40
    ("000000000000"), -- Schiebetakt '0'
41
    ("011000000100"), -- For so häufig wie in "Anzahl ADC" definiert
42
    --(
43
    ("001100000000"), -- SPI Wait Ready
44
    ("000100010000"), -- SPI send "(1<<RS1)"
45
    ("000100000000"), -- SPI send "0"
46
    ("000100000001"), -- SPI send "(1<<CH0)"
47
    ("000100000000"), -- SPI send "0" 
48
    --Starte kontinuierliche Messung
49
    ("001100000000"), -- SPI Wait Ready
50
    ("000101011100"), -- SPI send ((1<<RW)|(1<<RS1)|(1<<RS0)|(1<<CREAD)) Data Register select  !! BEIM 2. UND 4. MAL FALSCHE CLK !!
51
    ("000000000001"), -- Schiebetakt '1'
52
    --)
53
    ("011100000000"), -- For Ende
54
    --while 1
55
    --(
56
    ("100000000000"), -- Endlos start
57
    ("000000000000"), -- Schiebetakt '0'    
58
    ("011000000100"), -- For so häufig wie in "Anzahl ADC" definiert
59
    ("001100000000"), -- SPI Wait Ready
60
    ("010110000101"), -- Wait 5 Takte
61
    ("001000000010"), -- SPI high byte
62
    ("010110000101"), -- Wait 5 Takte
63
    ("001000000001"), -- SPI middle byte
64
    ("010110000101"), -- Wait 5 Takte
65
    ("001000000000"), -- SPI lowbyte
66
    ("010110000101"), -- Wait 5 Takte
67
    ("010000000000"), -- Übertrage Daten
68
    ("000000000001"), -- Schiebetakt '1'
69
    ("011100000000"), -- For Ende
70
    ("100100000000")); -- Endlos ende
71
    --)

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


Lesenswert?

> main: process(
Aha, ein Softwerker, der auf Hardware umsteigt...

> main: process(CLK, Reset, Schiebe_rdy_rec, Schiebe_start, SPI_RDY, SDI)
Dieser Pozess ist komplett synchron und somit lediglich auf den Takt 
sensitiv. In dieser Sensitivliste steht also viel zuviel Zeug. Das ist 
zwar "nur" ein Schönheitsfehler, zeigt aber ein leichtes Unverständnis 
auf.

>    variable programmcounter: integer range 0 to Anzahl_befehle  := 0;
>    variable zaehler_prog  : integer                  := 0;
>    variable  ...
>    variable  ...
>    variable  ...
Softwerker nehmen gern Variablen (weil der Name geläufig ist!). 
Allerdings haben diese Dinger in einer Hadrwarebeschreibung anderes 
Verhalten und auch mal unschöne Auswirkungen wie der 
Beitrag "Variable vs Signal" aufzeigt.

Martin S. schrieb:
> Dummerweise scheint an diesem System irgendetwas fehlerhaft zu sein
Was sagt die Simulation?

> ggf. könnte der Fehler auch an Timing-Problemen liegen.
Hast du Timing-Constraints angegeben? Weiß die Toolchain wenigstens, was 
du für einen Takt hast?

> Zum Teil ändern sich bedeutende Systemeigenschaften, wenn ich irgendein
> Signal ändere, das eigentlich nur kleinere Auswirkungen haben sollte.
Hört sich nach asynchronen bzw. nicht einsynchronisierten Signalen an.
Woher kommt z.B. das Schiebe_rdy_rec oder Schiebe_start?

von Falk B. (falk)


Lesenswert?

@  Martin S. (maklin)

>Ich arbeite gerade an einer Finite State Machine, welche einen Satz von
>Befehlen dekodieren soll, dann den Befehl ausführen und zum Schluss den
>Programmzähler auf den nächsten Befehl setzen.

Kleiner Tip. Nutze symbolische Namen für die States, das macht die Sache 
DEUTLICH lesbarer.

>Dummerweise scheint an diesem System irgendetwas fehlerhaft zu sein,

Wie stellst du das fest?

>Ich habe den Eindruck, dass das gesamte System vom Grundaufbau her
>fehlerhaft ist, ggf. könnte der Fehler auch an Timing-Problemen liegen.

Rätselraten. Du musst messen und prüfen. Simulation, Oszi, 
Logicanalyzer, in der Reihenfolge.

>Zum Teil ändern sich bedeutende Systemeigenschaften, wenn ich irgendein
>Signal ändere, das eigentlich nur kleinere Auswirkungen haben sollte.

Rumstochern. Noch schlimmer.

>Ich komme an dieser Stelle schlichtweg nicht weiter (ich kenne mich auch
>kaum mit den Timing-Constraints aus (für ein gutes Tutorial wäre ich
>dankbar)),

Als ganz einfache Massnahme reicht es, die Frequenz des Taktes 
anzugeben. Das ist leicht, schau in deine Hilfe der Tools. Wenn dann 
keine asynchronen Sauerein und vergessene Synchronisationen drin sind 
(naja, die Hoffnung stirbt zuletzt), sollte es reichen.

> und würde mich freuen, wenn jemand Fehler finden würde.

Das ist kein Ingenieurbüro.

MfG
Falk

von Martin S. (maklin)


Lesenswert?

Zugegebenermaßen, trotz des langen Posts fehlen noch viele Informationen 
meinerseits. Ich werde die Kommentare der Reihe nach beantworten und 
dadurch etwas Klarheit bringen:

Kommentare von Lothar Miller:

> Aha, ein Softwerker, der auf Hardware umsteigt...

Grundsätzlich bin ich bei der Thematik VHDL schon seit knapp einem Jahr 
dabei. Jedoch handelte es sich größtenteils um reine Simulationen, 
welche von Anfang an nie den Sinn hatten, synthetisiert zu werden. Somit 
sind die eigentlichen, synthetisierbaren Projekte relativ wenige. Eines 
war z.B. das Ping-Pong-Spiel für den PC-Monitor, über welches wir ja 
noch vor kurzem in einem anderen Thread gesprochen hatten.

> In dieser Sensitivliste steht also viel zuviel Zeug. Das ist
zwar "nur" ein Schönheitsfehler, zeigt aber ein leichtes Unverständnis
auf.

Genau genommen waren in den Sensitivlisten bis vor Kurzem nur die 
Clock-Signale. Irgendwo hatte ich Beispiele gesehen, in denen alle vom 
Prozess gelesenen Signale in der Liste enthalten waren, sodass ich es 
einfach mal ausprobiert hatte. Aber dann kann ich die zu vielen (und aus 
meiner Sicht auch unübersichtlichen) Signale herausnehmen.

> Softwerker nehmen gern Variablen (weil der Name geläufig ist!).
Allerdings haben diese Dinger in einer Hadrwarebeschreibung anderes
Verhalten und auch mal unschöne Auswirkungen wie der
Beitrag "Variable vs Signal" aufzeigt.

Dieser Beitrag hat mir sehr weitergeholfen. Ich werde versuchen, 
möglichst alle Variablen zu entfernen, und dann vom Ergebnis berichten. 
Für mich war es bislang so:
Variablen: innerhalb einzelner Prozesse, ggf. auch in einem 
Clock-Durchgang mehrere Variablenoperationen nacheinander.
Signale: Verbindungen zwischen Prozessen und nach außen hin

> Was sagt die Simulation?

Die Simulation ist schon seit Tagen problemlos, daher verzweifelte ich 
auch so an der ganzen Sache.

> Hast du Timing-Constraints angegeben? Weiß die Toolchain wenigstens, was
du für einen Takt hast?

Ja grundsätzlich schon, wobei ich auf dem Gebiet sehr unerfahren bin 
(btw.: Hast du die Email bekommen?). Es gibt eine Clock-Angabe:

NET "CLK" TNM_NET = CLK;
TIMESPEC TS_CLK = PERIOD "CLK" 24 MHz HIGH 50% INPUT_JITTER 60 ps;

(Die Angabe "INPUT_JITTER" habe ich mehr oder weniger aus einem (nicht 
so nützlichem) Tutorial abgeschrieben, ich muss mich dringend auf dem 
Gebiet der Constraints informieren.)

> Hört sich nach asynchronen bzw. nicht einsynchronisierten Signalen an.
Woher kommt z.B. das Schiebe_rdy_rec oder Schiebe_start?

Diese Signale gehen zu einem Prozess, welcher ein High- oder Low-Signal 
und ein Taktsignal an ein Schieberegister geben. Dieser Prozess ist 
grunsätzlich auch über das Clock-Signal angebunden.


Kommentare von Falk Brunner:

> Kleiner Tip. Nutze symbolische Namen für die States, das macht die Sache
DEUTLICH lesbarer.

Ich habe versucht die Befehle als 12-Bit-Vector-Arrays aufzubauen. Wenn 
die Befehle Namen bekommen würden, dann müsste ich 2 Arrays definieren, 
eines mit dem jeweiligen Namen und eines mit dem Befehlsdaten. Ich weiß 
nicht, ob das nicht umständlicher wird...

>> Dummerweise scheint an diesem System irgendetwas fehlerhaft zu sein,

> Wie stellst du das fest?

>> Ich habe den Eindruck, dass das gesamte System vom Grundaufbau her
fehlerhaft ist, ggf. könnte der Fehler auch an Timing-Problemen liegen.

> Rätselraten. Du musst messen und prüfen. Simulation, Oszi,
Logicanalyzer, in der Reihenfolge.

Folgendes habe ich gemacht: Simulation (wie bereits erwähnt lief diese 
problemlos) sowie Oszilloskopmessungen. Am Oszilloskop konnte man ein 
plötzliches Beenden der (eigentlich unenedlich widerholenden) 
SPI-Übertragung feststellen. Das Ende kam zu einem zufälligem Zeitpunkt, 
jedoch meist im ms-Bereich nach dem Begin der Endlosschleife. Dabei 
wurde grundsätzlich das letzte SPI-Byte vollständig übertragen, jedoch 
blieb das System im Befehl "0010" (SPI_rec) an der Position 
zaehler_prog=2 hängen, obwohl das SPY_rdy bereits vom SPI-Modul auf '1' 
gesetzt wurde (herausgefunden über Debug-Signale, die ich am Oszilloskop 
überprüft habe). Huh, das war wohl eine etwas klarere Fehlerbeschreibung 
:-)

> Rumstochern. Noch schlimmer.

Ja, wenn die klassischen Methoden zuende gehen, dann gehts bei mir 
Öfters so weiter...;-)

> Als ganz einfache Massnahme reicht es, die Frequenz des Taktes
anzugeben. Das ist leicht, schau in deine Hilfe der Tools. Wenn dann
keine asynchronen Sauerein und vergessene Synchronisationen drin sind
(naja, die Hoffnung stirbt zuletzt), sollte es reichen.

Siehe oben in der Antwort zu Lothar Miller.

Vielen Dank schonmal an euch beide, dass ihr euch die Zeit genommen habt 
in den Code zu schauen!!

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


Lesenswert?

Martin S. schrieb:
> Ja grundsätzlich schon, wobei ich auf dem Gebiet sehr unerfahren bin
> (btw.: Hast du die Email bekommen?).
Ja, aber ich habe leider keine Literaturempfehlung zum Thema 
Constraints. Das Thema ist ein relativ hartes Stück Brot, auf dem du so 
lange herumprobieren und herumkauen mußt, bis du es schlucken kannst...

Zum Glück gibt es eigentlich nur 4 Arten von Timing Constraints:
1) Laufzeit vom Eingang zum Flipflop
2) Laufzeit von Flipflop zu Flipflop (das ist das Period Constraint)
3) Laufzeit vom Flipflop zum Ausgang
4) Laufzeit vom Eingang zum Ausgang (Kombinatorik)

Kompliziert wird es erst durch Gruppenbildung und dadurch, dass bei 
verschiedenen Implementierungsschritten Signale wegoptimiert werden 
können...

Martin S. schrieb:
> Genau genommen waren in den Sensitivlisten bis vor Kurzem nur die
> Clock-Signale. Irgendwo hatte ich Beispiele gesehen, in denen alle vom
> Prozess gelesenen Signale in der Liste enthalten waren, sodass ich es
> einfach mal ausprobiert hatte. Aber dann kann ich die zu vielen (und aus
> meiner Sicht auch unübersichtlichen) Signale herausnehmen.
Nein, es geht nicht ums wahlfreie Rein- oder Rausnehmen von Signalen, 
sondern, dass du siehst, was da rein gehört und was nicht.
Vorneweg: nur die Simulation braucht die Sensitivliste!
Und dann müssen in der Sensitivliste alle die Signale sein, die eine 
Neuberechnung des Prozesses nötig machen. In einem komplett synchronen 
Prozess ist das nur der Takt, denn nur wenn sich der Takt ändert werden 
neue Werte berechnet. In einem kombinatorischen Prozess müssen dann alle 
Eingangssignale rein, und alle Signale, deren Änderung einen Augangswert 
ändern.
Die Synthese wird sich herzlich wenig um die Liste scheren und fehlende 
Signale einfach ergänzen.
Aber Achtung! Zu früh gefreut: dieser Automatismus führt dazu, dass mit 
einer unvollständigen Liste die Simulation und die Hardware nicht mehr 
zusammenpassen!

Martin S. schrieb:
> Dieser Beitrag hat mir sehr weitergeholfen. Ich werde versuchen,
> möglichst alle Variablen zu entfernen, und dann vom Ergebnis berichten.
Auch hier bitte nicht einfach blind alle Variablen entfernen, sondern 
verstehen, wo du damit Probleme bekommen kannst und wirst. Ganz kritisch 
sind vor allem SPEICHERNDE Variablen in längeren Prozessen:
[/vhdl]
    variable for_prog_cnt  : integer range 0 to Anzahl_befehle  := 0; -- 
Merke, wo er im Programm steht, wenn for kommt.
    variable while_prog_cnt  : integer range 0 to Anzahl_befehle  := 0; 
-- Merke, wo er im Programm steht, wenn for kommt.
[vhdl]
Denn dann gibt es ein "vorher" und ein "nachher" innerhalb des 
Prozesses, und evtl. müsste der Prozess neu berechnet werden, kann das 
aber nicht, weil ja Variablen nicht in die Sensitivliste aufgenommen 
werden können...
Und wehe, innerhalb des Prozesses wird mal etwas umgestellt (einfach 
mal eine Zeile von oben nach unten kopiert), dann kann es sein, nichts 
geht mehr...

Wo Variablen problemlos verwendet werden können und gern gesehen sind, 
ist dort, wo es gilt, eine komplizierte Berechnung in kleinere Schritte 
aufzuteilen. So etwa wie bei lokalen (aber nicht statischen) Variablen 
in C.

>> Woher kommt z.B. das Schiebe_rdy_rec oder Schiebe_start?
> Diese Signale gehen zu einem Prozess, welcher ein High- oder Low-Signal
> und ein Taktsignal an ein Schieberegister geben. Dieser Prozess ist
> grunsätzlich auch über das Clock-Signal angebunden.
Dann sind diese Signale eigentlich problemlos...

von Ralf (Gast)


Lesenswert?

>Zum Teil ändern sich bedeutende Systemeigenschaften, wenn ich irgendein
>Signal ändere, das eigentlich nur kleinere Auswirkungen haben sollte.


> Genau genommen waren in den Sensitivlisten bis vor Kurzem nur die
> Clock-Signale. Irgendwo hatte ich Beispiele gesehen, in denen alle vom
> Prozess gelesenen Signale in der Liste enthalten waren, sodass ich es
> einfach mal ausprobiert hatte. Aber dann kann ich die zu vielen (und aus
> meiner Sicht auch unübersichtlichen) Signale herausnehmen.


> Was sagt die Simulation?

> Die Simulation ist schon seit Tagen problemlos, daher verzweifelte ich
> auch so an der ganzen Sache.

Oje, die Simulation und die Synthese sind zwei unterschiedliche Sachen.
Mach erst mal deine Sensitivliste richtig und simulier dann nochmal

Gruss

Ralf

von Martin S. (maklin)


Angehängte Dateien:

Lesenswert?

Also die Simulation funktioniert in beiden Fällen, also sowohl mit den 
vielen Signalen in der Senitiv-Liste, als auch nur mit dem CLK-Signal.

Ich habe zudem nun die Variablen gegen Signale ersetzt, jedoch bleibt 
das Verhalten so wie vorher. Es gibt 4 Digital-Analyzer-Aufnahmen im 
Anhang des Posts, die ich hier beschreiben möchte. Die Bilder sind mit 
80 Mhz aufgenommen, ein Debugsignal gibt mir Auskunft über die MSB der 
Befehlsliste, dem Wert von zaehler_prog sowie das Reset- und CLK-Signal. 
Die Clock wird mit 24 MHZ betrieben, daher wird sie bei 80 MHZ in 
unterschiedlichen Längen bezeichnet, obwohl der Quarz natürlich ein 
normales Signal ausgibt.

Bild 1 (SPI_geht_1.png):
Da SDI vom externen ADC auf 0 gezogen wird, kann der Zustand 
wait_spi_rdy verlassen werden. Es folgt ein Wartebefehl (0101), welcher 
im "zaehler_prog=1"-Zustand herunterzählt...

Bild 2 (SPI_geht_2.png):
Das Warten ist beendet, der Zustand SPI_rec wird aufgerufen. Dabei kann 
das SPI gestartet werden (zaehler_prog von 0 nach 1), und es wird eine 
Bestätigung vom SPI empfangen ((zaehler_prog von 1 nach 2). Nun muss im 
Zustand 2 gewartet werden, bis das SPI fertig ist.

Bild 3 (SPI_geht_3.png):
Das SPI ist fertig. Aus irgendeinem unbekannten Grund wird in der Mitte 
des CLK-Signaltals (!) kurzzeitig die Debug-Ausgabe auf 0000 gezogen, 
dann geht es nach der nächsten positiven Flanke wie gewünscht weiter mit 
dem Wartebefehl.

Bild 4 (SPI_Fehler.png):
Hier nun die Stelle, an der das merkwürdige Verhalten auftritt (siehe 
auch kleine Leiste am unteren Bildrand). Der Zustand wait_spy_rdy wird 
wegen der fallenden SDI-Flanke beendet und SPI_rec begonnen. Jedoch wird 
kurzzeitig von SPI_rec (zaehler_prog=0) zum Wartebefehl gesprungen, 
welcher aber wohl wegen wait_zaehler=0 schnell wieder beendet wird und 
zum folgenden SPI_rec übergegangen wird. Was man hier nicht gut erkennen 
kann: Es wird tatsächlich ein letztes Byte über SPI übertragen, d.h. das 
SPI konnte gestartet werden (ein letztes Mal...).

Das nachfolgende wechselnde SDI-Signal (siehe unterer Bildrand) wird vom 
ADC generiert.

Interessantes Nebenphänomen: Wenn man statt der ADCs an SDI einfach GND 
anschließt, so funktioniert das Ganze problemlos, es kommt zu keinem 
Absturz. Es wäre also naheliegend, eine bestimmte SDI-Wertkombination 
als Absturzursache zu nehmen, jedoch wird die Simualtion mit einem 
aufzählendem SDI-Signal betrieben, und hier stürzt auch nichts ab.

von Mathi (Gast)


Lesenswert?

Werden asynchrone Signale in Deiner Zustandsmachine abgefragt? Z.B. aus 
der Spi-Clk-Domäne? Sind evtl. asynchrone Signale eingetaktet worden?

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


Lesenswert?

Martin S. schrieb:
> Bild 1 (SPI_geht_1.png):
> Da SDI vom externen ADC auf 0 gezogen wird,
Ein externes Signal! Achtung, ROTE FLAGGE!!!
> kann der Zustand wait_spi_rdy verlassen werden.
Ist SDI synchron eingetaktet?

Martin S. schrieb:
> Interessantes Nebenphänomen: Wenn man statt der ADCs an SDI einfach GND
> anschließt, so funktioniert das Ganze problemlos, es kommt zu keinem
> Absturz. Es wäre also naheliegend, eine bestimmte SDI-Wertkombination
> als Absturzursache zu nehmen, jedoch wird die Simualtion mit einem
> aufzählendem SDI-Signal betrieben, und hier stürzt auch nichts ab.
Das ist ein bekannter Effekt. Der Hintergrund ist dieser da:
http://www.lothar-miller.de/s9y/archives/64-State-Machine-mit-asynchronem-Eingang.html

Mathi schrieb:
> Werden asynchrone Signale in Deiner Zustandsmachine abgefragt?
Die können durchaus auch versteckte Wege über ein höheres Modul in 
andere (scheinbar synchrone) Signale finden...
Es ist einfach so: JEDES externe Signal für sich muss auf Synchronität 
bewertet und ggs. eingetaktet werden.

von Martin S. (maklin)


Lesenswert?

Vielen Vielen Dank! Das asynchrone SDI war der Fehler. Ein einfaches FF 
ganz am Anfang davor geschaltet, und schon klappt es wunderbar.

Und ich habe wohl einen elementaren Punkt zum Thema FPGA dazugelernt.

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


Lesenswert?

Martin S. schrieb:
> Und ich habe wohl einen elementaren Punkt zum Thema FPGA dazugelernt.
Ja, das hast du. Ich würde fast sagen, das ist die Fehlerquelle, die am 
meisten Ärger bringt und Zeit kostet. Und vor allem: es gibt keinen 
Automatismus, der dir diese Arbeit (Aufspüren von asynchronen Signalen) 
abnimmt.

Deshalb schon ganz zu Beginn mein Verdacht:
Lothar Miller schrieb:
>> Zum Teil ändern sich bedeutende Systemeigenschaften, wenn ich irgendein
>> Signal ändere, das eigentlich nur kleinere Auswirkungen haben sollte.
> Hört sich nach asynchronen bzw. nicht einsynchronisierten Signalen an.

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.