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
waituntilrising_edge(Up);
4
if(b"1111"=Counter)then
5
Counter<=b"1111";
6
else
7
Counter<=Counter+1;
8
endif;
9
endprocess;
10
11
12
process
13
begin
14
waituntilrising_edge(Dn);
15
if(b"0000"=Counter)then
16
Counter<=b"0000";
17
else
18
Counter<=Counter-1;
19
endif;
20
endprocess;
also dachte ich, dass ich die beiden Eingänge Up und Dn zu einem
Triggersignal verodere. Beide sind active low:
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.
> 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.
@ 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
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.
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
>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?
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...
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
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.
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 =]
> ö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-Ringoszillatorhttp://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.
Du könntest es so "hinfummeln", dass in den Pfad zwischen der Erzeugung
deines Taktes
1
clk<=(notUp)or(notDn)
und dem eigentlichen Register
1
waituntilrising_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.
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
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
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
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.
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
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