Forum: FPGA, VHDL & Co. Ampelschaltung - Probleme bei der Synchronisation


von Christian K. (mortiferus)


Lesenswert?

Hallo :)

wiedermal habe ich ein "kleines" Problem mit einer Hardwarebeschreibung. 
Diesmal soll eine kleine Ampelschaltung realisiert werden. Die Ampel 
soll zuerst nur die einzelnen Phasen durchlaufen, d.h.

1. Rot (Ausgangszustand) - dieser Zustand soll eine gewisse Zeit 
gehalten werden.

2. Rot/Gelb - dieser Zustand soll nur einen Clocktakt anhalten.

3. Grün - dieser Zustand soll eine gewisse Zeit gehalten werden.

4. Gelb - auch dieser Zustand soll etwas länger andauern, jedoch kürzer 
als die Grünphase.


Meine VHDL-Beschreibung:
1
Library ieee;
2
use ieee.std_logic_1164.all;
3
use ieee.std_logic_unsigned.all;
4
5
entity Ampel is
6
  generic( k: positive := 6);
7
  port( clk: in std_logic;
8
               lights: out std_logic_vector(2 downto 0));
9
end Ampel;
10
11
12
13
architecture RTL of Ampel is
14
15
type state_type is (A, B, C, D);
16
17
signal state: state_type;
18
19
signal  New_Clock:    std_logic;
20
signal  Globalcount:  integer range 0 to 15;
21
signal   Clear: std_logic;
22
23
begin
24
25
26
Takt: process(clk) -- 16 MHz zu 1 Hz
27
28
variable counter: integer range 0 to 7999999 := 0;
29
30
begin
31
32
if rising_edge(clk) then
33
  if counter = 7999999 then
34
    New_Clock <= not New_Clock;
35
    counter := 0;
36
  else
37
    counter := counter + 1;
38
  end if;
39
end if;
40
41
end process Takt;
42
43
44
45
46
Zaehler: process(New_Clock,Clear) 
47
48
begin
49
50
if Clear = '1' then
51
52
  Globalcount <= '0';
53
54
elsif rising_edge(New_Clock) then
55
  
56
  Globalcount <= Globalcount + 1;
57
58
end if;
59
60
end process Zaehler;
61
62
63
64
65
FSM: process(Clk)
66
67
begin
68
69
70
if rising_edge(clk) then 
71
72
  case state is
73
  
74
    when A => if Globalcount = k then
75
         State <= B;
76
              else 
77
         State <= A;
78
              end if;
79
           
80
      when B => State <= C;
81
    
82
      when C =>  if Globalcount = k then
83
       State <= D;
84
           else
85
               State <= C;
86
           end if;
87
        
88
     when D => if Globalcount = 3 then
89
      State <= D;
90
         else 
91
      State <= A;
92
        end if;
93
       end case;
94
end if;
95
end process FSM;
96
97
98
99
100
Ampel_Zuweisung:
101
 Lights <=           "100" when (State = A) else
102
       "110" when (State = B) else
103
       "001" when (State = C) else
104
       "010" when (State = D) else
105
       "111";
106
       
107
       
108
Clear_Zuweisung: ?????
109
110
111
112
     
113
end RTL;

Wie man sieht habe ich ein Problem mit der Clear-Zuweisung, d.h. 
Globalcount soll immer wieder auf 0 zurückgesetzt werden, sobald man in 
einen neuen Zustand gelangt. Allerdings weiß ich einfach nicht, wie ich 
dies machen soll! Clear soll also immer zu Beginn eines Zustandes auf 
high gehen damit der Counter auf 0 gesetzt wird.

Oder ist das zu schwer gedacht? Seht ihr noch andere Probleme?
Vielen Dank für eure Mithilfe!

LG

von P. K. (pek)


Lesenswert?

Was ich mal von Beginn weg ändern würde: Alles mit dem schnellen Clock 
takten (es gibt verschiedene Threads zum Thema hier drin).

Dann generierst Du beim Erreichen des jeweiligen Zählerstandes ein 
Triggersignal, dass Dir die zu implementierende Ampel-FSM einen Zustand 
weiter bringt.

Ein Clear brauchst Du nicht (schon gar kein asynchrones). Du setzt den 
Zähler mit demselben Triggersignal zurück, wie Du die Ampel-FSM 
weiterbringst.

von Morti (Gast)


Lesenswert?

Vielen Dank für deine Antwort! Leider "hinke" ich deiner Ausführung noch 
etwas hinterher. Verstehe ich es richtig, dass du die 16 MHz-Clock 
generell anwenden willst, d.h. mein Taktprozess würde entfallen. Aber so 
etwas wie einen Counter muss ich doch integrieren, oder etwa nicht? Wie 
soll dieses Triggersignal aussehen, ich kann mir da gerade nicht 
wirklich etwas vorstellen? Wenn ich das richtig verstehe, müsste ich 
dann eine komplett andere FSM entwickeln ?

von Christian K. (mortiferus)


Lesenswert?

Oder meinst du als Trigger vllt einen Counter, der bis zu einem gewissen 
Wert hochläuft. Hat er diesen erreicht, wird dann per if-Abfrage die FSM 
angesprochen. Neben der Aktivierung der FSM wird dann auch noch 
zeitgleich der Counter zurückgesetzt. Allerdings verstehe ich nicht, wie 
ich den Counter für die Zustandsdauer die Ampelphasen takten soll...mit 
16 MHz zählt der ja ein bissel schnell ;).

von Alexander F. (alexf91)


Lesenswert?

Für den Zähler-Prozess würde ich keinen selbstgenerierten Takt verwenden 
sondern ein Clock-Enable Signal, welches du im Takt-Prozess jeweils für 
einen Takt setzt.
Etwa so:
1
Takt: process(clk) -- 16 MHz zu 1 Hz
2
3
variable counter: integer range 0 to 7999999 := 0;
4
5
begin
6
7
if rising_edge(clk) then
8
  CLK_EN <= '0';
9
  if counter = 7999999 then
10
    CLK_EN <= '1';
11
    counter := 0;
12
  else
13
    counter := counter + 1;
14
  end if;
15
end if;
16
end process Takt;
17
18
Zaehler: process(New_Clock,Clear) 
19
begin
20
   if rising_edge(clk) then
21
      if CLK_EN = '1' then
22
         Globalcount <= Globalcount + 1;
23
      end if;
24
   end if;
25
26
end process Zaehler;

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


Lesenswert?

Christian Klingler schrieb:
> Allerdings weiß ich einfach nicht, wie ich dies machen soll!
Der pragmatische Ansatz wäre, alles im selben Prozess zu machen:
1
Zaehler: process(New_Clock,Clear) 
2
3
begin
4
5
FSM: process(Clk) begin
6
7
if rising_edge(clk) then 
8
9
  if CLK_EN = '1' then -- siehe Code von Alexander 
10
     Globalcount <= Globalcount + 1;
11
  end if;
12
13
  case state is
14
    when A => if Globalcount = k then
15
                 State <= B;
16
              else 
17
                 State <= A;
18
              end if;
19
         Globalcount <= '0';           
20
      when B => State <= C;
21
    
22
      when C =>  if Globalcount = k then
23
                    State <= D;
24
                 else
25
                    State <= C;
26
                 end if;
27
                 Globalcount <= '0';        
28
29
      when D => if Globalcount = 3 then
30
                   State <= D;
31
                else 
32
                   State <= A;
33
                end if;
34
                Globalcount <= '0';        
35
36
       end case;
37
end if;
38
end process FSM;


BTW:
Wenn schon States, warum nicht gleich sprechende:
1
 Lights <=
2
       "100" when (State = gruen)   else
3
       "110" when (State = rotgelb) else
4
       "001" when (State = rot)     else
5
       "010" when (State = gelb)    else
6
       "111";

BTW2:
Wenn du schon alle States verwendet hast, dann brauchst du kein else 
mehr, denn es wird schlicht&einfach ignoriert, weil es ja keinen 
weiteren Zustand mehr gibt! Fazit:
1
 Lights <=
2
       "100" when (State = gruen)   else
3
       "110" when (State = rotgelb) else
4
       "001" when (State = rot)     else
5
       "010" ; --  State = gelb
Das ist so ähnlich, wie die when others Sache:
http://www.lothar-miller.de/s9y/categories/25-when-others

Christian Klingler schrieb:
> if rising_edge(clk) then
>   if counter = 7999999 then
>     New_Clock <= not New_Clock;
>     counter := 0;
>   else
>     counter := counter + 1;
>   end if;
> end if;
So werden keine Takte erzeugt! Neimals! Arbeite mit Clock-Enables! Wie 
es geht hat Alexander schon gezeigt, und du siehst es auch in meinen 
Blinklicht. Da wird im letzten Codebeispiel die Taktung mit Clock-Enable 
gezeigt:
http://www.lothar-miller.de/s9y/archives/80-Hello-World!.html

Und deine Ampel ist eigentlich (noch) nichts anderes als ein Lauflicht:
http://www.lothar-miller.de/s9y/archives/61-Lauflicht.html

von Christian K. (mortiferus)


Lesenswert?

Erstmal vielen Dank an euch beide! Eure Vorschläge kann ich gut 
nachvollziehen und umsetzen. Mal schauen, ob es dann funktioniert!

von Christian K. (mortiferus)


Lesenswert?

So ich habe die Hardwarebeschreibung angepasst:
1
Library ieee;
2
use ieee.std_logic_1164.all;
3
use ieee.std_logic_unsigned.all;
4
5
entity Ampel is
6
  
7
  port( clk: in std_logic;
8
      lights: out std_logic_vector(2 downto 0));
9
end Ampel;
10
11
12
13
architecture RTL of Ampel is
14
15
type state_type is (rot,rot_gelb,gruen,gelb);
16
17
signal state: state_type;
18
19
20
signal  Count:  integer range 0 to 15;
21
signal  CLK_EN: std_logic;
22
23
24
25
begin
26
27
28
29
Takt: process(clk)
30
31
variable counter: integer range 0 to 7999999 := 0;
32
33
begin
34
35
if rising_edge(clk) then
36
 
37
  if counter = 7999999 then
38
    CLK_EN <= '1';
39
    counter := 0;  
40
  else
41
       CLK_EN <= '0';
42
       counter := counter + 1;
43
  end if;
44
end if;
45
46
end process Takt;
47
48
49
50
51
FSM: process(clk)
52
53
begin
54
55
56
if rising_edge(clk) then 
57
58
  if CLK_EN = '1' then
59
    Count <= Count + 1;
60
  end if;
61
    
62
63
  case state is
64
  
65
    when rot => if Count = 5 then
66
           State <= rot_gelb;
67
       else 
68
           State <= rot;
69
          end if;
70
               Count <= 0;
71
           
72
      when rot_gelb => if Count = 2 then
73
         State <= gruen;
74
       else
75
         State <= rot_gelb;
76
           end if;
77
      Count <= 0;
78
    
79
    when gruen => if Count = 5 then
80
            State <= gelb;
81
          else
82
             State <= gruen;
83
          end if;
84
          Count <= 0;
85
        
86
    when gelb => if Count = 3 then
87
          State <= rot;
88
             else 
89
          State <= gelb;
90
              end if;
91
              Count <= 0;
92
  end case;
93
94
95
 
96
97
end if;
98
end process FSM;
99
100
101
102
103
Ampel_Zuweisung:
104
 Lights <= "100" when (State = rot) else
105
    "110" when (State = rot_gelb) else
106
    "001" when (State = gruen) else
107
            "010";
108
         
109
     
110
end RTL;

Allerdings erhalte ich nun die folgenden Warnungend es 
Synopsis-Compilers:

Pruning register CLK_EN
Register bit Count(0) is always 0, optimizing ...
|Register bit Count(1) is always 0, optimizing ...
Register bit Count(2) is always 0, optimizing ...
|Register bit Count(3) is always 0, optimizing ...
Found inferred clock Ampel|clk with period 5.00ns. Please declare a 
user-defined clock on object "p:clk"
WARNING - ngdbuild: logical net 'clk' has no load
WARNING - ngdbuild: DRC complete with 1 warnings


So, ich denke das Problem in diesem Fall ist die count <= 0 Zuweisung. 
Da ja alles parallel in der Hardware implementiert ist (oder?), ist 
count immer 0. Das heißt, das zurücksetzen des Zählers muss ich anders 
realisieren.

von Lattice User (Gast)


Lesenswert?

Christian Klingler schrieb:
> So, ich denke das Problem in diesem Fall ist die count <= 0 Zuweisung.
> Da ja alles parallel in der Hardware implementiert ist (oder?), ist
> count immer 0.

Richtig, daran liegt es. Kleiner Fehler der Lothar unterlaufen ist, auch 
Chuck Norris ist nicht perfekt :-)

Christian Klingler schrieb:
> Das heißt, das zurücksetzen des Zählers muss ich anders
> realisieren.

Einfach nur jeweils im if-Zweig in dem auf den nächsten State gewechselt 
wird.

von Christian K. (mortiferus)


Lesenswert?

Okay es funktioniert...Stufe 2 :) Wie schwierig ist es jetzt, das 
Durchlaufen einer Phase, mittels einem Taster auszulösen. Sprich, man 
drückt auf einen Taster und die Ampel beginnt aus dem Zustand Rot die 
verschiedenen anderen Zustände zu durchlaufen.

Die Schwierigkeit, die ich in diesem Fall sehe, ist die Erzeugung der 
Bedingung, d.h. es muss ein drücken des Schalters detektiert werden, 
vllt so: ?
1
Push: process (clk)
2
3
begin
4
5
if rising_edge(clk)
6
 
7
 if Button = 1 and Button_old = 0 then
8
      Button_Pushed <= '1';
9
 else 
10
      Button_Pushed <= '0';
11
 end if;
12
13
Button_old <= Button;
14
15
end if;
16
end process Push;

Und kann ich dann einfach noch eine If-Abfrage um count und um das 
case-Statement packen?

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


Lesenswert?

1. Externe Signale müssen einsynchronisiert/eingetaktet werden.
2. Taster und Schalter müssen entprellt werden.

von Christian K. (mortiferus)


Lesenswert?

Ähm..erfülle ich denn nicht genau diese beiden Aspekte mit meiner Idee? 
Oder was genau meinst du mit "eintakten"?

Bezüglich der Entprellung...mal ganz naiv gefragt, entprelle ich den 
Schalter nicht, da ich eine Flankenerkennung durchführe?

von Christian K. (mortiferus)


Lesenswert?


von Christian K. (mortiferus)


Lesenswert?

@ Lothar

ich habe mir jetzt einfach mal dein Beispiel mit dem Schieberegister 
angeschaut, hätte aber noch zwei Fragen.

Mir ist auf der einen Seite die Funktion des Vorteilers noch nicht ganz 
klar, warum wird dieser gebraucht?

Und zweitens ...inputsr <= inputsr(2 downto 0) & input;

Wie interpretiert der Compiler das? Ist Input dann das LSB ?

Vielen Dank!


Edit: Ich habe auf dieser Seite ein Beispiel für die Softwareentprellung 
mittels reine Flankenerkennung gefunden, wie kann das dann 
funktionieren? Da ja zB eine steigende Flanke mehrmals vorliegt?!

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


Lesenswert?

Der Vorteiler wird gebraucht, weil sonst bei 50 MHz das Schieberegister 
in 80ns durch wäre. Ein Taster prellt sicher länger.

Der & Operator ist bei VHDL ein Verkettungsoperator, der input wird 
daher zum LSB.

von Falk B. (falk)


Lesenswert?


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.