Forum: FPGA, VHDL & Co. asynchroner up/down counter in vhdl


von Armin (Gast)


Lesenswert?

hi zusammen

ich versuche gerade, einen asynchronen Zähler in einem CPLD zu 
implementieren. Ähnlich beispielsweise einem CD40193.
Das Ganze soll ohne Clocksignal auskommen (->asynchron).

Mit 2 Prozessen klappt das natürlich nicht, da sonst beide Ausgänge auf 
"die Zählvariable" geroutet werden müssten:
1
  process
2
  begin
3
    wait until rising_edge(Up);
4
    if(b"1111" = Counter) then
5
      Counter <= b"1111";
6
    else
7
      Counter <= Counter + 1;
8
    end if;
9
  end process;
10
  
11
  
12
  process
13
  begin
14
    wait until rising_edge(Dn);
15
    if(b"0000" = Counter) then
16
      Counter <= b"0000";
17
    else
18
      Counter <= Counter - 1;
19
    end if;
20
  end process;



also dachte ich, dass ich die beiden Eingänge Up und Dn zu einem 
Triggersignal verodere. Beide sind active low:
1
entity main is
2
port(  Up: in std_logic;
3
    Dn: in std_logic;
4
    Out1: buffer std_logic;
5
    Out2: buffer std_logic;
6
    Out3: buffer std_logic;
7
    Out4: buffer std_logic);
8
9
end main;
10
11
12
13
14
architecture behaviour of main is
15
signal Counter: std_logic_vector (3 downto 0):= b"0011";
16
signal clk: std_logic;
17
begin
18
19
  clk <= (not Up) or (not Dn); -- Active Low
20
  
21
  process
22
  begin
23
    wait until rising_edge(clk);
24
    if('0' = Up) then
25
      if(b"1111" = Counter) then
26
        Counter <= b"1111";
27
      else
28
        Counter <= Counter + 1;
29
      end if;
30
    else
31
      if(b"0000" = Counter) then
32
        Counter <= b"0000";
33
      else
34
        Counter <= Counter - 1;
35
      end if;
36
    end if;
37
  end process;
38
  Out1 <= Counter(0);
39
  Out2 <= Counter(1);
40
  Out3 <= Counter(2);
41
  Out4 <= Counter(3);
42
end behaviour;

Der Zähler funktioniert allerdings nur "einigermaßen":
man kann von 2-5 sauber zählen, darüber mutiert das Ganze zum 
Wechselblinker beim weiter-Hochzählen. Bei kleineren Zahlen fehlen 
einzelne Bits. Zwischen 1 und 0 gibt es noch einen Wechselblinker, wenn 
man wieder hochzählen möchte. Und wenn man auf 0 runterzählt, kommt man 
davon nicht mehr weg.



Ist es nicht möglich, die Logik so, wie sie im CD40193 aufgebaut ist, im 
CPLD nachzubauen?
Gerade herausgefunden:
- Interessanterweise funktioniert der Zähler, wenn man keine Grenzen 
einsetzt, also Überläufe zulässt.
- wenn man
-->if(b"1111" = Counter) then<-- durch
-->if(b"1111" <= Counter) then<-- ersetzt,
geht es etwas besser. Ist allerdings immer noch unbrauchbar.

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


Lesenswert?

>   clk <= (not Up) or (not Dn); -- Active Low
Du darfst nicht einfach einen Takt aus Kombinatorik erzeugen. Da 
bekommst du üble Glitches und unterschiedliche Laufzeiten zu den 
beteiligten FFs. Ein paar FFs erkennen den Takt früher, die anderen 
später. Und wehe, die Taktquelle ist ein Taster...

> Ist es nicht möglich, die Logik so, wie sie im CD40193 aufgebaut ist,
> im CPLD nachzubauen?
Eher nicht, denn dort ist das gesamte Zählerlayout auf diese Laufzeiten 
ausgelegt und optimiert. Im einem CPLD solltest du eher an ein 
synchrones Design denken. Und das hat nur 1 Takt.

BTW:
>  Out1: buffer std_logic;
Vergiss den buffer mal schnell wieder. Für sowas nimmt am lokale 
Signale.

von Falk B. (falk)


Lesenswert?

@  Armin (Gast)

>ich versuche gerade, einen asynchronen Zähler in einem CPLD zu
>implementieren. Ähnlich beispielsweise einem CD40193.
>Das Ganze soll ohne Clocksignal auskommen (->asynchron).

Böse, Böse. Sowas macht man nicht. Solche historisch gewachsenen 
asynchronen Schaltungen sind heute in sauschnellen CPLDs ganz fix am 
wilden Schwingen bzw. Unsinn machen.

>Mit 2 Prozessen klappt das natürlich nicht, da sonst beide Ausgänge auf
>"die Zählvariable" geroutet werden müssten:

Logisch.

>also dachte ich, dass ich die beiden Eingänge Up und Dn zu einem
>Triggersignal verodere. Beide sind active low:

Auch schlecht, siehe Taktung FPGA/CPLD.

Mach einen ordentlichen, SYNCHRONEN Zähler und alles ist gut.

VHDL
entity main is
port(  Up: in std_logic;
    Dn: in std_logic;
    Out1: buffer std_logic;
    Out2: buffer std_logic;
    Out3: buffer std_logic;
    Out4: buffer std_logic);

end main;

architecture behaviour of main is
signal Counter: std_logic_vector (3 downto 0):= b"0011";
signal clk: std_logic;
begin

  -- BÖSE, BÖSE!!!!!!
  clk <= (not Up) or (not Dn); -- Active Low
  -- BÖSE, BÖSE!!!!!!

  process
  begin
    wait until rising_edge(clk);
    if('0' = Up) then
      if(b"1111" = Counter) then
        Counter <= b"1111";
      else
        Counter <= Counter + 1;
      end if;
    else
      if(b"0000" = Counter) then
        Counter <= b"0000";
      else
        Counter <= Counter - 1;
      end if;
    end if;
  end process;
  Out1 <= Counter(0);
  Out2 <= Counter(1);
  Out3 <= Counter(2);
  Out4 <= Counter(3);
end behaviour;

VHDL

>Der Zähler funktioniert allerdings nur "einigermaßen":

Logisch, lies mal den Artikel oben. Mach die Verknüpfung mit dem Takt 
weg und alles ist gut.
Nimm einen sauberen Takt.

>Ist es nicht möglich, die Logik so, wie sie im CD40193 aufgebaut ist, im
>CPLD nachzubauen?

Es ist möglich, aber nicht sinnvoll.

MFG
Falk

von Armin (Gast)


Lesenswert?

Alles klar.
Ursache verstanden.





ich hab aber gar keinen Takt.
Ein Quarz fehlt. :P
was nun tun?
du sagst, es gibt eine Möglichkeit?

von Schrotty (Gast)


Lesenswert?

Du kannst das prinzipiell schon auch asynchron machen, so wie du das vor 
hast.
Wenn du dir einen kombinatorischen Takt generierst und diesen an die 
Register legst, bist du ja prinzipiell wieder synchron. Allerdings 
darfst du diesen Takt und auch die Signale aus diesen der Takt gebildet 
wurde keinesfalls anderweitig kombinatorisch in der Steuerung deines 
Zählers verknüpfen.

Wenn du das machst, dann hast du die Laufzeiten zwischen den Registern 
nicht mehr im Griff und es kann zu Setupzeit-Verletzungen kommen.
Und diese erklären das "seltsame" Verhalten.

die "böse" Zeile ist
if('0' = Up) then

denn der Takt wurde aus genau diesem Signal kombinatorisch gebildet.

von Der Besucher (Gast)


Lesenswert?

Hallo,

also ich mag asynchrone Schaltungen, wenn sie zweckdienlich sind :) 
jaja, ganz ganz böse :)
Ein solcher Counter (hier mal nur der up-Counter Teil) ist relativ 
sicher zu bauen, indem man jeweils den Ausgang eines Flipflops als 
Clock-Eingang des nächsten Flipflops verwendet und ansonsten die 
Flipflops bei jedem Pulse toggeln lässt (invertierter Ausgang an den 
Eingang zurückführen).

Natürlich sollte man aufpassen, wie das Ergebnis weiterverarbeitet wird. 
Die Schaltung sollte sich ausgetoggelt haben.

Synchrones Schaltungsdesign wurde doch eigentlich nur eingeführt, um die 
Entwicklung besser automatisieren zu können und das Timing auf einfache 
Art und Weise in den Griff zu bekommen. Aber es hat auch Nachteile 
(Stichwort EMV).

Und nun könnt ihr auf mich einschlagen...
Der Besucher

von zu meinem Verständnis (Gast)


Lesenswert?

>darfst du diesen Takt und auch die Signale aus diesen der Takt gebildet
>wurde keinesfalls anderweitig kombinatorisch in der Steuerung deines
>Zählers verknüpfen.


Das ist mir immer noch nicht 100% klar. Ursache ist, daß ein 'richtiger' 
clk über ein spezielles clk-Netz verteilt wird und der Obige eben nicht? 
Resultierend in arg verschiedene interne Laufzeit und damit Jitter an 
den kombinatorischen Teilen?

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


Lesenswert?

zu meinem Verständnis schrieb:
> Ursache ist, daß ein 'richtiger'
> clk über ein spezielles clk-Netz verteilt wird und der Obige eben nicht?
Nein. Die Ursache liegt darin, dass der Takt kombinatorisch aus einem 
Signal erzeugt und gleichzeitig in einer parallelen Kombinatorik als 
Weiterschaltbedingung für einen Zähler verwendet wird. Das Signal geht 
also auf einen Takteingang eines FFs und gleichzeitig auf den D-Eingang 
(denn es gibt in programmierbaren Bausteinen nur D-FFs). Zwangsläufig 
wird hier die Setupzeit verletzt.

Man darf ohne weiteres auf einem CPLD/FPGA auch mit lokalen Takten ohne 
Taktnetz arbeiten (z.B. für ein SPI-Ausgangsschieberegister, das von 
extern getaktet wird). Nur sollte man wissen, was man da tut, und die 
Taktdomäne sauber wechseln...

von zu meinem Verständnis (Gast)


Lesenswert?

danke

von Der Besucher (Gast)


Lesenswert?

Schau zu den Countern auch mal hier:

http://www.allaboutcircuits.com/vol_4/chpt_11/2.html

Bei der Implementierung in eine HDL mußt du dann die Struktur per Hand 
beschreiben und kannst dich nicht auf das Synthesetool verlassen. Die 
meisten sind einfach nicht für asynchrones Design gedacht.

Der Besucher

von Schrotty (Gast)


Angehängte Dateien:

Lesenswert?

Die angehängte Grafik verdeutlicht das Problem.

Du hast tsu (Setup-Zeit des Registers*) nicht im Griff. Und wenn tsu zu 
klein wird, dann kann das Register nicht mehr "sauber" schalten.

*Setupzeit ist die Zeit, die die Daten am Register stabil anliegen 
müssen, bevor der Takt am CLK-Eingang kommt.

Ich hoff, es wird dir nun klarer, was passiert.

von Armin (Gast)


Lesenswert?

Der Besucher schrieb:
> Bei der Implementierung in eine HDL mußt du dann die Struktur per Hand
> beschreiben und kannst dich nicht auf das Synthesetool verlassen. Die
> meisten sind einfach nicht für asynchrones Design gedacht

öhm was heißt denn "von Hand"?
Was für Möglichkeiten habe ich da?

wird ja wohl kaum mit dem xilinx-schematic-editor gehen - der erzeugt 
doch sicher auch nur VHDL-Code...
sonst könnte man da schön FlipFlops zusammenkleben =]

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


Lesenswert?

> öhm was heißt denn "von Hand"?
Mit Primitives, die du explizit verdrahtest. Allerdings kann und wird 
dir die Synthese dazwischen funken. Und da brauchst du dann oft Kniffe 
und Tricks, dass Speicherelemente und Signale nicht wegoptimiert werden:
http://www.lothar-miller.de/s9y/categories/29-Ringoszillator
http://www.lothar-miller.de/s9y/categories/35-Einsynchronisieren

> was für Möglichkeiten habe ich da?
Wenn du es realistisch betrachtest: keine, die Sinn machen.
Es ist ganz einfach so, dass du keine T-FFs im Baustein hast. Und solche 
bräuchtest du für einen asynchronen Zähler.

Allerdings plagt mich die Frage, wozu du überhaupt einen CD40193 
nachbauen willst? Den gibt es doch schon fertig  :-o
Also liegt für mich die Annahme nahe, dass du eigentlich ganz was 
anderes willst, und dieser Zähler nur ein Schritt auf deinem Weg sein 
soll.

von Schrotty (Gast)


Lesenswert?

Du könntest es so "hinfummeln", dass in den Pfad zwischen der Erzeugung 
deines Taktes
1
clk <= (not Up) or (not Dn)
 und dem eigentlichen Register
1
wait until rising_edge(clk)
 durch ein paar Inverter künstlich verlängerst. Damit könntest du 
erreichen, dass das UP-Signal mit dem du ja die Zählrichtung steuerst, 
VOR dem Takt am Register anliegt.

Allerdings musst du dann der Synthese sagen, dass sie diese Inverter 
nicht wegoptimieren darf (Siehe Beschreibung deines Synthesetools).

Bei einem CPLD hast du allerdings nicht so viele Möglichkeiten, das 
Timing zu beeinflussen, wie bei einem FPGA.

Alternativ könntest du auch den kombinatorisch generierten Takt auf 
einen Pin ausgeben und zu einem anderen Pin wieder einlesen und erst 
dann auf das Register führen. Somit würde das UP-Signal direkt an das 
Register geführt und der Takt durch den "Umweg" verzögert.
Allerdings muss dir dabei klar sein, dass damit die maximale Taktrate 
des Counters natürlich deutlich sinkt. aber zweistellige MHz sollten so 
noch erreichbar sein.

Es ist auf jeden Fall ein "Gefummel" und hat mit einem sauberen Design 
nicht mehr viel zu tun. Aber für einem Versuchsaufbau auf dem 
Küchentisch würde das schon funktionieren.

von Der Besucher (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

anbei mal der Code plus eine kleine Testbench für so einen asynchronen 
Counter. Man kommt ohne Primitives aus, muß aber jedes Flipflop einzeln 
generieren (generate-Anweisung).
Beispiel hat 4 Bit, ist natürlich erweiterbar.
Die Umschaltung zwischen Up/Down war die eigentliche Herausforderung.
Die geht vielleicht noch eleganter...
Bin mal gespannt, wie das in euren FPGA's läuft.

Natürlich muß das Signal Pulse sauber debounced sein, was man aber auch 
analog hinbekommt.

Der Besucher

von Der Besucher (Gast)


Lesenswert?

Anmerkung zu obigen Code:

Das Design muß so constraint sein, daß switch beim Umschalten auf jeden 
Fall auf low ist, bevor wires(3 downto 1) geschaltet wird. Ansonsten 
wird es unschön.

Der Besucher

von Der Besucher (Gast)


Angehängte Dateien:

Lesenswert?

Hier mal auf die Schnelle eine kleine Verbesserung in der Umschaltung.
Und in meinem vorherigen Text ist natürlich "low" mit "high" zu 
ersetzen...

Der Besucher

von Der Besucher (Gast)


Angehängte Dateien:

Lesenswert?

Und nun noch mit Verhinderung des Überlaufs...

Der Besucher

von Der Besucher (Gast)


Angehängte Dateien:

Lesenswert?

Und hier noch mit Verhinderung des Überlaufs.

Ich hoffe das erscheint jetzt nicht doppelt...

Der Besucher

von Armin (Gast)


Lesenswert?

hat das irgendwer zum Laufen bekommen?
gut, meine Taster prellen, aber das sollte ja kein Problem sein, weil 
die sicher langsamer prellen, als der CPLD schaltet.
Trotzdem, bei mir macht der code nur seltsames Zeug.

Und ich weiß auch nicht, wie man die "constraints" implementiert:
Der Besucher schrieb:
> Das Design muß so constraint sein, daß switch beim Umschalten auf jeden
> Fall auf low ist, bevor wires(3 downto 1) geschaltet wird. Ansonsten
> wird es unschön.

von Der Besucher (Gast)


Lesenswert?

Armin schrieb:
> Trotzdem, bei mir macht der code nur seltsames Zeug.

Liegt vielleicht dadran, das es nur ein Pulse Signal gibt und ein 
Umschaltsignal für up und down. Du wolltest ja eigentlich 2 
Eingangspulsesignale nutzen.
Auf jeden Fall sollte der Counter fehlerfrei von 0 bis 15 Zählen, wenn 
du down auf '0' hältst.

Wenn ich ein paar Minuten Zeit habe, baue ich dir das Teil mal um (up 
und down Eingänge) und kann es auch an deine benötigte Bitbreite 
anpassen.

Der Besucher

von Der Besucher (Gast)


Angehängte Dateien:

Lesenswert?

Sooo...

ich habe die Struktur nochmal komplett überarbeitet. Der Up/Down-Counter 
sollte nun nach deinen Wünschen funktionieren.

Die saubere Umschaltung im vorherigen Code zwischen Up und Down habe ich 
leider nicht wirklich sauber hinbekommen. Up und Down Counter für sich 
funktionierten, aber irgendwie bekam ich das nicht zusammen. Das werde 
ich mal später etwas genauer untersuchen.

Ich habe mir aber was anderes überlegt.
Der Counter selbst ist nun synchron, aber das Clock Signal wird 
asynchron generiert.
Da nur ein Oder-Gatter dafür Verwendung findet, sollten da keine 
Glitches auftauchen. Zur Not das Netz als Clock markieren.
Zusätzlich wurde noch ein bischen asynchrone Logic eingebaut, das ein 
Hilfsignal generiert, welches intern zwischen up und down unterscheidet.

Ich hoffe es stört nicht, das der Zählerstand immer erst zur fallenden 
Flanke vom Up/Down-Pulse weiterzählt.

Der Besucher

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.