Ich habe einen Zähler, der zwei Inputs hat, einen zum vorwärts- und
einen zum Rückwärtszählen. Getrennt klappt alles wunderbar, aber sobald
ich beide Inputs auswerten will, geht nichts mehr.
1
process(a,b)
2
begin
3
if(rising_edge(a))then
4
...
5
endif;
6
if(rising_edge(b))then
7
...
8
endif;
9
endprocess
Dazu bekomme ich die Fehlermeldung "Bad synchronous description"
Und sowas
1
signalc:std_logic;
2
...
3
c<=aorb;
4
...
5
process(a,b)
6
begin
7
if(rising_edge(c))then
8
if(a='1')then
9
...-- a ging "high"
10
else
11
...-- b ging "high"
12
endif;
13
...
14
endif;
15
endprocess
Funktioniert auch nicht. Da geht er scheinbar immer in den else-Zweig.
Versuche ich es mit zwei Prozessen, kann ich den Output-Vector nicht
beschreiben. Fehlermeldung "Multiple Drivers ...."
Wie kann man sowas in den Griff kriegen?
[Ich habe einen CoolRunner CPLD und das XST von Xilinx]
Danke!!!
Peterchen
Sowas ist mit der vorhandenen Logik in FPGAs & CPLDs einfach nicht
moeglich.
Sind a und b wirklich echte Clocks aus einem Oszillator oder doch nur
irgendwelche Signale?
> Sind a und b wirklich echte Clocks aus einem Oszillator oder doch nur> irgendwelche Signale?
Es sind echte Clocks, runtergeteilt von einem 1 MHz Oszillator, die über
zwei Tasten ein- und ausgeschaltet werden können (mit einer
UND-Verknüpfung).
Sehen wir uns das mal in Hardware an:
Mit
> c <= a or b;
generierst du eine Flanke.
Und auf dieselbe Flanke fragst du den Zustand der Signale ab. Die FF im
CPLD haben an der steigenden Flanke aber noch gar nichts vom
Pegelwechsel mitbekommen und sehen immer noch die '0'. Deshalb wird 'a'
an einer steigenden Flanke von 'a' immer '0' sein.
Was sollte/würde denn deiner Meinung nach passieren,
wenn beide Signale gleichzeitig den Zustand von 0 nach 1 wechseln?
Runtergeteilt? Mit einem Zaehler?
Normalerweise erzeugt man nicht zwei neue Takte, sondern ein
Clock-Enable-Signal, und taktet die Logik weiterhin mit dem Takt aus dem
Oszillator.
Dazu gibts hier in der Artikelsammlung auch die passenden
Erlaeuterungen, siehe Taktung FPGA/CPLD
Das geht so nicht! Du musst dir immer vorstellen, dass du das auch in
Hardware diskret aufbauen würdest. Und da hast auch kein Flipflop mit 2
Takteingängen.
Ich würd es so lösen:
Systemtakt auf Zähler und dann zwei Stuerleitungen, die die Zählrichtung
angeben. Wenn du das so machst, dann zählt er so lange rauf oder runter,
wie du deine Taste gedrückt hältst.
Wenn du bei jedem Tastendruck nur ein Inkrement zählen willst, dann
musst du dir ne Flankenerkennung für die Tasten bauen. Das geht recht
einfach, indem du das Tastensignal durch zwei Register schiebst (die
auch mit dem Systemtakt getaktet sind) und dann die beiden Register
miteinander verlgeichst. (ist das eine High und das andere noch Low,
dann hast z.B. ne steigende Flanke am Eingang)
>Mit>> c <= a or b;>generierst du eine Flanke.
Ich dachte ein Wechsel von a oder b von 0 nach 1 würde eine Flanke
generieren. ist das nicht so?
> Was sollte/würde denn deiner Meinung nach passieren,> wenn beide Signale gleichzeitig den Zustand von 0 nach 1 wechseln?
Dann sollte c auch von 0 nach 1 wechseln.
> Normalerweise erzeugt man nicht zwei neue Takte, sondern ein> Clock-Enable-Signal, und taktet die Logik weiterhin mit dem Takt aus dem> Oszillator.
Es ist der gleiche Takt, nur auf 2 verschiedenen Leitungen, den ich
getrennt über 2 Taster zuführen kann.
Hintergrund: ich möchte mit der einen Taste ein 7-Segment Display
hochzählen und mit der anderen soll es runterzählen.
> Ich würd es so lösen:> Systemtakt auf Zähler und dann zwei Stuerleitungen, die die Zählrichtung> angeben. Wenn du das so machst, dann zählt er so lange rauf oder runter,> wie du deine Taste gedrückt hältst.
Danke, das ist eine gute Idee. Das werde ich so machen!
Ach, was mir noch einfällt: meine Zähler für die 7-segment Displays (es
sind insgesmt 4) haben Ausgänge für Über- und Unterlauf, um das
benachbarte Display anzutriggern. Wenn ich jetzt alle Zähler
taktsynchron laufen lasse, dann müssen diese Ausgangssignale lang genug
sein, aber auch nicht zu kurz, sonst würde das benachbarte Display gar
nicht, oder zu weit zählen. Kann das zu Problemen führen?
> Ach, was mir noch einfällt: meine Zähler für die 7-segment Displays (es> sind insgesmt 4) haben Ausgänge für Über- und Unterlauf, um das> benachbarte Display anzutriggern.
Was willst du denn in welchem Coolrunner-CPLD machen?
Eine Art Ereignis-Zähler? Welche Zählfrequenz?
Welche 74xx-ICs würdest du dafür nehmen?
Hast du einen globalen Takt mit deutlich höherer Frequenz (z.B. 4MHz)
als deine Ereignisse (z.B. 1MHz)?
> Was willst du denn in welchem Coolrunner-CPLD machen?
Mal sehen. Erst mal ist es nur ein Spielzeug, um zu sehen, was man mit
programmierbarer Logik alles anstellen kann.
> Hast du einen globalen Takt mit deutlich höherer Frequenz (z.B. 4MHz)> als deine Ereignisse (z.B. 1MHz)?
Ich habe einen globalen Takt vom 1Mhz, den habe ich z.B. runtergeteilt
um die die Displays zu multiplexen. Bei 1Mhz leuchten die LEDs zu lange
nach. Dann hatte ich ihn nochmal runtergeteilt, für die beiden Up/Down
Clocks für die Zähler. Eigentlich ist alles Taktgetrieben.
Rückkopplungen und Schleifen sind doof, das habe ich schon gelernt.
> Eigentlich ist alles Taktgetrieben.
Gut, dann brauchst du keine Flankenerkennung, weil an dieser Stelle
eigentlich nichts von "aussen" ins CPLD hineinkommt. Du machst also
"nur" Zähler, die auf Enable-Signale der vorhergehenden Stufe reagieren.
Jedes dieser Enable-Signale darf dann natürlich auch nur 1 Takt lang
aktiv sein.
Hier mal nur die Einer-Zählerstufe:
1
LIBRARYieee;
2
USEieee.std_logic_1164.ALL;
3
USEieee.numeric_std.ALL;
4
:
5
signalup1:std_logic:='0';-- Einer hochzählen
6
signalup1:std_logic:='0';-- Einer runterzählen
7
signalcnt1:unsigned(3downto0):=0;
8
9
signalup10:std_logic:='0';-- Zehner hochzählen
10
signaldn10:std_logic:='0';-- Zehner runterzählen
11
signalcnt10:unsigned(3downto0):=0;
12
:
13
:
14
processbegin
15
waituntilrising_edge(clk);-- auf die nächste Taktflanke warten
waituntilrising_edge(clk);-- auf die nächste Taktflanke warten
sorgt dafür, dass das Genze garantiert ein synchrones Design wird. Sie
ersetzt die Sensitivity-List komplett, und deshalb kann es auch keine
unvollständige Sens-List mehr geben.
> Eigentlich ist alles Taktgetrieben.
Du verstehst unter "taktgetrieben" aber schon, dass es im ganzen Design
nur diesen einen 1MHz-Takt gibt? Das wäre ein synchrones Design.
Alles Andere wäre Käse (auf jeden Fall für einen Anfänger... ;-)
Wenn du mehrere Takte hast, hast du ein asynchrones Design.
Dann kann ich dir nur die Daumen drücken... :-(
> Wenn du mehrere Takte hast, hast du ein asynchrones Design.> Dann kann ich dir nur die Daumen drücken... :-(
Ich habe mehrere Takte, aber alle sind von der 1Mhz "main"-Clock
abgeleitet. Das ist dann wohl auch ein synchrones Design.
Übrigens, Danke für deinen Code. Leider habe ich ihn erst jetzt gesehen
und gestern Abend habe ich mir schon was gebaut, das prima
funktioniert. Das sieht so aus:
1
LIBRARYieee;
2
USEieee.std_logic_1164.ALL;
3
USEieee.numeric_std.ALL;
4
5
ENTITYcount_10IS
6
PORT
7
(
8
up_enable:instd_logic;-- count down input
9
down_enable:instd_logic;-- count up input
10
clk:instd_logic;-- system clock
11
value:outstd_logic_vector(3downto0);-- output value
12
ov:outstd_logic;-- overflow signal
13
uv:outstd_logic-- underflow signal
14
);
15
ENDcount_10;
16
17
ARCHITECTUREsynth1OFcount_10IS
18
BEGIN
19
20
process(clk,up_enable,down_enable)
21
typestate_tis(idle,ovset,uvset);
22
variablecounter:unsigned(3downto0);
23
variablestate:state_t;
24
begin
25
if(rising_edge(clk))then-- system clock goes high
26
casestateis
27
28
whenidle=>if(up_enable='1')then-- count up
29
counter:=counter+1;
30
if(counter="1010")then-- overflow?
31
counter:="0000";-- reset counter
32
ov<='1';-- set overflow signal
33
state:=ovset;
34
endif;
35
endif;
36
if(down_enable='1')then-- count down
37
counter:=counter-1;
38
if(counter="1111")then-- underflow?
39
counter:="1001";-- reset counter
40
uv<='1';-- set underflow signal
41
state:=uvset;
42
endif;
43
endif;
44
45
whenovset=>ov<='0';-- reset overflow signal on next clock cycle
46
state:=idle;
47
48
whenuvset=>uv<='0';-- reset underflow signal on next clock cycle
49
state:=idle;
50
endcase;
51
endif;
52
value<=std_logic_vector(counter);-- send counter value to parent
53
endprocess;
54
55
ENDsynth1;
[Das Ganze sieht hier etwas zerfetzt aus. Ich hoffe ihr könnt es
trotzdem lesen]
ov und uv sind die beiden Impulse, die das benachbarte 7-Segment Display
ansteuern, um entweder rauf- oder runterzuzählen. Sie werden dort als
up_enable und down_enable benutzt. Beim ersten Segment kommen up_enable
und down_enable entweder direkt von zwei Tasten (=Zählgeschwindigkeit
abhängig von clk), oder von einer anderen entity, die aus einem
Tastendruck einen Einzelimpuls mach (=pro Tatendruck wird eins rauf-
oder runtergezählt).
Das ganze funktioniert zwar prächtig, aber vielleicht habt ihr trotzdem
ein paar Verbesserungsvorschläge für mich...?
Ach ja: großes Lob an Euch. Das Forum ist spitze!
Peterchen
Das Problem an deinem Code ist, wenn ein up oder down-Impuls in den
ovset odr uvset-Zuständen kommt, dann wird er nicht gezählt. Denn diese
Zählimpulse sind ja per Definiton nur 1 Taktzyklus lang gültig.
Mein Vorschlag: Lass die Zustände ganz raus.
1
if(rising_edge(clk))then-- system clock goes high
2
ov<='0';-- Default inaktiv
3
uv<='0';-- Default inaktiv
4
if(up_enable='1')then-- count up
5
counter:=counter+1;
6
if(counter="1010")then-- overflow?
7
counter:="0000";-- reset counter
8
ov<='1';-- set overflow signal
9
endif;
10
endif;
11
if(down_enable='1')then-- count down
12
counter:=counter-1;
13
if(counter="1111")then-- underflow?
14
counter:="1001";-- reset counter
15
uv<='1';-- set underflow signal
16
endif;
17
endif;
18
endif;-- clk
Auch so sind ov und uv jeweils genau 1 Takt lang aktiv.
1
if(counter="1010")then-- overflow?
2
:
3
if(counter="1111")then-- underflow?
Damit beschreibst du eigentlich einen Glitch, denn der counter muß nach
deiner Definition erst einen ungültigen Zustand (10 oder -1) erreichen,
bevor du was machst.
Besser wäre, die verbotenen Zustände nie zu erreichen:
1
2
:
3
if(up_enable='1')then-- count up
4
if(counter="1001")then-- max cnt erreicht?
5
counter:="0000";-- ja: reset counter
6
ov<='1';-- set overflow signal
7
else-- sonst hochzählen
8
counter:=counter+1;
9
endif;
10
endif;
11
:
In meinen Augen ein Schönheitsfehler:
Deine Variablen counter und state sind speichernd, das kann u.U.
verwirrend sein.
> Mein Vorschlag: Lass die Zustände ganz raus.
Danke für den Code. Das werde ich mal so probieren.
> Damit beschreibst du eigentlich einen Glitch, denn der counter muß nach> deiner Definition erst einen ungültigen Zustand (10 oder -1) erreichen,> bevor du was machst.
Schon, aber der Glitch kommt im Ausgangsvektor ja nicht an, weil die
Variable erst am Ende des Prozesses rüberkopiert wird. Wäre es so, dann
würde ich bei geringer Taktfrequenz z.B. ein A sehen, was aber nicht
passiert.
Ach ja: bei langsamer Tasktfrequenz ist übrigens erst eine Änderung von
9 nach 0 sichtbar, bevor das Nachbar-Display (im nächsten Takt)
weitergezählt wird. Das fällt bei hoher Frequenz nicht auf, ist aber
trotzdem unschön. Gibt es dafür eine Erklärung? Eigentlich reagiert das
Design doch auf die "Rising Edge", d.h. beide Displays müssten sich
gleichzeitig ändern. Aber vielleicht habe ich an anderer Stelle noch
einen Fehler drin.
> Gibt es dafür eine Erklärung? Eigentlich reagiert das Design doch auf> die "Rising Edge", d.h. beide Displays müssten sich gleichzeitig ändern.
Sieh dir deinen Code an.
Erst zählst du die niedrigere Stelle hoch, und wenn es einen Über- oder
Unterlauf gibt, dann gibst du das Signal an die nächsthöhere Stelle. Und
erst mit dem nächsten Takt kann diese Stelle den Übertrag auswerten.
Dein Übertrag ist quasi immer einen Taktzyklus zu spät :-/
Das nennt sich Latency.
Ein Tipp: mach dein Design so, dass der Systemtakt z.B. immer 1 MHz ist.
Dann siehst du die Latency nicht mehr (weil ja nur noch 1 us Verzögerung
zwischen den Stellen ist).
BTW
Dein Design ist nicht so richtig synchron, denn du taktest es offenbar
mit dem Zähltakt.
Synchron wäre es, wenn du (wie z.B. ein uC) sehr schnell intern
arbeitest, und alle externen Signale (einschliesslich deinem Zähltakt)
mit dieser internen Taktfrequenz bearbeitest.
EDIT
Das wäre ein Würg-Around für das Latency-Problem:
1
ov<='0';-- Default inaktiv
2
:
3
if(up_enable='1')then-- count up
4
if(counter="1000")then-- max cnt-1 erreicht?
5
ov<='1';-- set overflow signal
6
endif;
7
if(counter="1001")then-- max cnt erreicht?
8
counter:="0000";-- ja: reset counter
9
else-- sonst hochzählen
10
counter:=counter+1;
11
endif;
12
endif;
13
:
Hier wird praktisch der Übertrag einen Takt zu früh gesetzt, damit wird
die Latency umgangen. Aber das ist schon fast hässlich... :-/
> Der Prozess braucht noch nicht mal eigene Signale oder Variablen...
Ja, schon aber dafür einen buffer.
Leg für den Zähler besser ein Signal an, das du dem Ausgang dann
zuweist. Eben so, wie es der Rest der Welt auch macht ;-)
> wait until clk = '1';
Krass. Das habe ich jetzt auch noch nie gesehen :-o
Gibt das wirklich einen getakteten Prozess?
Formal korrekt wäre das:
1
waituntilrising_edge(clk);
Das geht, so mache ich das auch...
EDIT:
MAN FASST ES NICHT: der macht da tatsächlich ein FF draus :-O
Aber die Simulation geht schief, weil der Prozess immer wieder neu
durchgerechnet wird, solange clk='1' ist. Und nicht nur an der
steigenden Taktflanke.
>> wait until clk = '1';> Krass. Das habe ich jetzt auch noch nie gesehen :-o> Gibt das wirklich einen getakteten Prozess?> Formal korrekt wäre das:> wait until rising_edge(clk);> Das geht, so mache ich das auch..
OK, das werde ich noch abändern. Mich hats auch erstaunt daß es
funktioniert. Eigentlich müsste er immer wieder durch den Prozess
sausen, so lange clk auf '1' steht.
> Aber die Simulation geht schief...
Bei mir gehen Simulationen fast immer schief, weil ich z.B. vergesse,
Signale zu initialisieren. Der CoolRunner setzt beim Starten sowieso
alles auf 0. Aber auf den CPLD ist es ja schnell draufgeflashed. Gibt es
eigentlich bessere Simulationstools als das im ISE WebPack?
> Modelsim von mentor kann man da nur empfehlen. inner xilinx starter> edition sogar kostenlos erhältlich. muss man sich nur bei xilinx für> registrieren.
Das ist gut. Ich dachte schon, ich muß mir irgendwelche virenbehafteten
Cracks runterladen. ;-)