Forum: FPGA, VHDL & Co. Zählen bei Ereignis


von vhtl (Gast)


Lesenswert?

Hallo,

noch eine Anfänger-Frage:

Ich möchte Signalwechsel zählen, und habe das naiv so formuliert:
1
architecture Behavioral of counter is
2
  signal value : STD_LOGIC_VECTOR(3 downto 0) := "0000";
3
  signal prev : STD_LOGIC := '0';
4
begin
5
  count: process(BUTTON)
6
  begin
7
    if rising_edge(BUTTON) then
8
      value <= value + '1';
9
    end if;
10
  end process;
11
  do_sth: process(value)
12
  begin
13
   ...
14
  end process;
15
end Behavioral;

Dabei erhalte ich die Fehlermeldung "Bad condition in wait statement, or 
only one clock per process." Die gleiche Meldung erhalte ich, wenn ich 
es umformuliere:
1
  count: process
2
  begin
3
    wait until BUTTON /= prev;
4
    value <= value + '1';
5
    prev <= BUTTON;
6
  end process;

Ich sehe aber gar keinen zweiten Takt!

Die Umformulierung
1
  count: process(BUTTON)
2
  begin
3
    if BUTTON /= prev then
4
      value <= value + '1';
5
    end if;
6
    prev <= BUTTON;
7
  end process;

führt hingegen dazu, daß erst das gesamte Register und dann der Button 
wegoptimiert werden.

Also: Wie wartet man richtig auf einen Signalwechsel pro Prozeß, um 
danach z.B. einen Zähler zu erhöhen?

von ich (Gast)


Lesenswert?

value <= value + '1'

<= ???

von ich (Gast)


Lesenswert?

ist das < nicht zu viel?

von vhtl (Gast)


Lesenswert?

Einen hab ich noch:
1
count: process
2
  begin
3
    wait until BUTTON = '0';
4
    value <= value + '1';
5
    wait until BUTTON = '1';
6
  end process;

ergibt "Same wait conditions expected in all Multiple Waits."

von ich (Gast)


Lesenswert?

und fehlt ein :

von Stephan N. (someone_like_you)


Lesenswert?

Ich weiß ja nicht welche Libs du eingebunden hast, aber einen STD Logic 
Vector kann man nicht so ohne weiteres inkrementieren.

Ich benutze da immer:
1
 value <= std_logic_vector(unsigned(value) + 1);

Außerdem musst du mit deinem Tastersignal aufpassen: Du verwendest das 
direkt als Takt! Wenn dein Signal von einem normalen Taster kommt, 
kann der noch ein bisschen prellen, was zu falschen inkrements führt.

Du könntest einen Takt von einem Oszillator nehmen, damit dein design 
takten und eine einfache Flankenerkennung für deinen Button 
implementieren.

von vhtl (Gast)


Lesenswert?

Der Einfachheit halber habe ich
1
use IEEE.STD_LOGIC_UNSIGNED.ALL;

genommen - ich weiß, daß macht man nicht.

Mit geht's jetzt aber um das asynchrone Erkennen von Signaländerungen 
(ob der Zähler hier eine besondere Rolle spielt weiß ich nicht).

Der Button ist jetzt auch nur ein Beispiel für ein Signal, also Prellen 
bitte ignorieren.

von vhtl (Gast)


Lesenswert?

OK, ich habe mich da wohl vertan ... Version 1 funktioniert doch, 
jedenfalls sofern man nicht zu viele Signale überwachen möchte.

von Mike (Gast)


Lesenswert?

Nicht alles was man in VHDL beschreiben kann lässt sich auch in reale 
Hardware umsetzen. Ein FPGA stellt an den Takt ziemlich strenge 
Anforderungen und mit einem externen Button als Taktgenerator kommst du 
da nicht weit...

Die Seiten hier solltest du dir einmal anschauen:

http://www.mikrocontroller.net/articles/Taktung_FPGA/CPLD
http://www.mikrocontroller.net/articles/Metastabilit%C3%A4t
Beitrag "Verständnisfrage Einsynchronisieren"

Für echte Hardware (und nicht nur Simulation) verwendest du besser nur 
einen einzigen Takt. Mit dem arbeiten dann alle deine getakteten 
Prozesse. Deine Tasten synchronisiert du Schieberegister ein und machst 
damit eine Flankenerkennung. Wenn du eine Flanke erkannt hast, dann 
kannst du deinen Zähler aktualisieren.

Du solltest dir mal ein Buch für die Grundlagen zulegen.

von Burkhard K. (buks)


Lesenswert?

vhtl schrieb:

> Mit geht's jetzt aber um das *asynchrone Erkennen von Signaländerungen*

Klingt nach Flankendetektion - das ist nicht ganz dasselbe wie das 
Zählen von Taktwelchsen.

Flankendetektion lässt sich per Schieberegister ("strobe" soll das 
Signal mit der zu detektieren Flanke sein) implementieren.
1
signal edge     : std_logic;
2
signal delay_reg: std_logic;
3
signal edge_cnt : unsigned(3 downto 0) := (others=>'0');
4
5
process
6
begin
7
    wait until rising_edge(clk);
8
    delay_reg <= strobe;
9
end process;
10
11
edge  <= (not delay_reg) and strobe;
12
...
13
process
14
begin
15
    wait until rising_edge(clk)
16
    if edge then
17
        edge_cnt <= edge_cnt +1;
18
    end if;
19
end process;

Ein asynchrones Signal muss dies zunächst einsynchronisiert werden. 
Beides in einem Rutsch hat Lothar hier beschrieben: 
http://www.lothar-miller.de/s9y/categories/18-Flankenerkennung.

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


Lesenswert?

vhtl schrieb:
> if rising_edge(BUTTON) then
Das ist die komplett falsche Strategie, vermutlich, weil du noch die 
falsche Denkweise hast.

Um es kurz zu machen: man macht nicht unnötig viele Taktdomänen auf. 
Alles, was mit wait oder 'event oder rising_egde() oder falling_edge() 
zu tun hat, ist eine eigene Taktdomäne. Und weil man dann immer Probleme 
mit dem Taktdomänenübergang hat, sollte man versuchen, so einfache Dinge 
wie einen Taster direkt in der Zieldomäne zu erledigen.
Die Zieldomäne ist "der Takt", üblicherweise der eine Takt, der von 
einem Quarz im Bereich um 50..100MHz an einem FPGA-Pin vorgegeben wird.

vhtl schrieb:
> Mit geht's jetzt aber um das asynchrone Erkennen von Signaländerungen
> (ob der Zähler hier eine besondere Rolle spielt weiß ich nicht).
>
> Der Button ist jetzt auch nur ein Beispiel für ein Signal, also Prellen
> bitte ignorieren.
Wenn du ein Signal erkennen willst/musst/sollst, das so kurz ist, dass 
es mit dem einen einzigen FPGA-Takt nicht einsynchronisiert und darauf 
ein Pegelwechsel erkannt werden kann, dann bleibt dir als Notnagel(!!) 
nur, eine Flanke darauf zu erkennen, diesen Flankenwechsel zu speichern, 
einzusynchronisieren und mit dem einen FPGA-Takt darauf zu reagieren.
So etwa:
http://www.lothar-miller.de/s9y/archives/19-Kurzer-Spike-in-Puls-umgewandelt.html

vhtl schrieb:
> Also: Wie wartet man richtig auf einen Signalwechsel pro Prozeß, um
> danach z.B. einen Zähler zu erhöhen?
Man "wartet" eigentlich nicht, sondern man erkennt nur, dass nichts 
relevantes passiert ist und deshalb nichts getant werden muss. Relevant 
bei einem Signalwechsel ist eine Änderung eines Pegels z.B. von 0 auf 1. 
Also muss 1. der Pegelverlauf gespeichert und dann 2. in diesem Speicher 
ein Pegelwechsel erkannt und dann 3. darauf reagiert werden:
1
architecture Behavioral of counter is
2
  signal value : integer := 0;
3
  signal sr_button : STD_LOGIC_VECTOR(1 downto 0) := "00"; -- Schieberegister für asynchrones Eingangssignal
4
begin
5
6
  sr_button <= sr_button(0) & BUTTON when rising_edge(clk); -- Signal einspeichern und einsynchronisieren
7
8
  count: process(clk) begin
9
    if rising_edge(clk) then
10
      if sr_button = "01" then -- gewünschter Pegelwechsel am BUTTON erkannt
11
        value <= value + 1;    -- Reaktion auf die den Pegelwechsel
12
      end if;
13
    end if;
14
  end process;
15
16
17
end Behavioral;

Letztlich ist es einfach: das gesamte FPGA-Design besteht aus 
Zustandsautomaten. Das hört sich jetzt aufwändig an, aber letztlich ist 
ja jeder schäbige Zähler schon so ein Automat. Und der Automat im Fall 
hier ist einfach so gestrickt, dass er bei jedem FPGA-Takt prüft, ob das 
Schieberegister einen Flankenwechsel von 0 nach 1 beinhaltet. Wenn 
nicht, dann tut der Automat nichts. Wenn so ein Wechsel erkannt ist, 
dann zählt er den Zähler hoch.

von larsp (Gast)


Lesenswert?

Lothar M. schrieb:
>> if rising_edge(BUTTON) then
> Das ist die komplett falsche Strategie, vermutlich, weil du noch die
> falsche Denkweise hast.
>
> Um es kurz zu machen: man macht nicht unnötig viele Taktdomänen auf.
> Alles, was mit wait oder 'event oder rising_egde() oder falling_edge()
> zu tun hat, ist eine eigene Taktdomäne. Und weil man dann immer Probleme
> mit dem Taktdomänenübergang hat, sollte man versuchen, so einfache Dinge
> wie einen Taster direkt in der Zieldomäne zu erledigen.
> Die Zieldomäne ist "der Takt", üblicherweise der eine Takt, der von
> einem Quarz im Bereich um 50..100MHz an einem FPGA-Pin vorgegeben wird.

Vielen Dank für die vielen Antworten.

Die "synchrone" Variante kenne ich durchaus, aber ich dachte, man kann 
das auch asynchron machen, zumal wenn man sonst nur kombinatorische 
Logik (heißt das so?) hat. Ich wollte daher gar keinen (externen) Takt 
verwenden.

Gilt das Gesagte nur für FPGAs, oder auch CPLDs?

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


Lesenswert?

larsp schrieb:
> zumal wenn man sonst nur kombinatorische Logik (heißt das so?) hat.
Kombinatorik = Logik

> Ich wollte daher gar keinen (externen) Takt verwenden.
Sowas kann man machen, wenn man sehr lokal bleibt oder nur ein paar 
einzelne Flipflops zu verwalten hat, die man auch "von Hand" 
konfigurieren könnte.

> Gilt das Gesagte nur für FPGAs, oder auch CPLDs?
Weil CPLDs sowieso nur sehr niedrige Komplexität haben, "darf" man dort 
eher mal solche Basteleien anfangen. In einem FPGA-Design, das evtl. 
sogar mal portiert werden soll, würde ich mich das nur in absoluten 
Ausnahmefällen trauen.

von vhtl (Gast)


Lesenswert?

Ich muß nochmal nachhaken ...

Gerade lese ich in einem Vorlesungsskript, daß Prozesse (mit 
Sensitivity-Liste) nur dann ausgeführt werden, wenn sich einer der 
Parameter ändert:

"The process doesn't run continuously! It will be started only after 
change (event) on any signal in its sensitivity list."

Für die Simulation glaube ich das gerne. Aber gilt das wirklich auch für 
das synthetisierte Ergebnis? Wenn dem so wäre könnte ich mir doch die 
Abfrage nach rising_edge oder falling_edge sparen?!

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


Lesenswert?

vhtl schrieb:
> Für die Simulation glaube ich das gerne. Aber gilt das wirklich auch für
> das synthetisierte Ergebnis?
Nein!
Die Sensitivliste ist ausschließlich für den Simulator. Der Synthesizer 
gibt nur eine Info aus, dass bei unvollständiger Sensitivliste die 
Simulation nicht mehr zum Syntheseergebnis passt.

> Wenn dem so wäre könnte ich mir doch die Abfrage nach rising_edge oder
> falling_edge sparen?!
So wie dort etwa:
http://www.lothar-miller.de/s9y/archives/16-Takt-im-Prozess.html
Und dann gleich mal eine kombinatorische Schleife basteln:
http://www.lothar-miller.de/s9y/categories/36-Kombinatorische-Schleife

: Bearbeitet durch Moderator
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.