Tach auch,
ich habe für ein Projekt einen Countdown-Timer mit Abbruchbedingung
geschrieben, der noch eine kleine Macke hat. Jedesmal wenn der Counter
eingeschaltet (EN='1') bzw. (RST_N='0') zurückgesetzt wird, gibt er
zunächst ein RDY-Signal aus und fängt dann mit dem zählen an. Das
RDY-Signal soll jedoch erst auf '1' gesetzt werden, wenn der Zähler auch
wirklich bis 0000...0 heruntergezählt hat. Ich muss also irgendwie die
Reset-Bedingungen umschreiben...
Hat jemand ne Idee wie ich das Problem lösen kann ? Ich habe gerade nen
Brett vorm Kopf...
Hier mein Code:
Herzlichen Glückwunsch!
Du hast das entdeckt, was sich "Latency" nennt.
Sowas passiert gern mal mit Signalen... :-o
Konkret:
Wenn RST_N = 0 war, dann ist d_int = 0.
Wenn jetzt RST_N = '1' inaktiv wird, ist d_int immer noch 0.
Wenn jetzt ein Takt kommt, ist für den gesamten Prozess d_int
immer noch 0.
Erst am Ende des Prozesses wird die letzte Zuweisung an d_int
tatsächlich an d_int zugewiesen.
1
ifRST_N='0'then
2
d_int<=(others=>'0');
3
RDY<='0';
4
elsif(CLK='1'andCLK'event)then
5
d_int<=D;-- Latch ?
6
if(EN='1')then
7
if(d_int=zeros)then-- beim ersten Takt nach reset ist hier d_int = 0 !!!
8
RDY<='1';-- --> RDY wird gesetzt.
9
else
10
d_int<=d_int-1;
11
RDY<='0';
12
endif;
13
elsif(EN='0')then
14
RDY<='0';
15
d_int<=(others=>'0');
16
endif;
17
endif;
Marten V. schrieb:> Hat jemand ne Idee wie ich das Problem lösen kann ?
Probier mal das:
1
ifRST_N='0'then
2
d_int<=D;
3
:
4
:
> d_int <= D;-- Latch ?
Nein, weil getaktet.
BTW:
Zum Sinn und Unsinn von asynchronen Resets wurde hier schon einiges
geschrieben. Benutz mal die Suche hier im Forum zum Thema "Xilinx und
die Resets" als Denkanstoss.
Ein Signal ist keine Variable! Egal wo du in einem synchronen Prozess
dem Signal einen Wert zuweist, du kannst ihn erst im nächsten Taktzyklus
lesen.
Beachte außerdem: In der sensitivity list eines synchronen processes
steht nur clock und reset, sonst GAR NICHTS. Dein VHDL Stil ist sehr
gewöhnungsbedürftig. Verwende für Konstanten constant nicht variable.
Mein Tipp: Ein ordentlicher Editor a la Xemacs.
Guten Morgen,
und vielen Dank für eure nette Hilfe. Ich bin momentan dabei VHDL zu
lernen, deswegen bin ich über jeder Kritik an meinem Code dankbar ;-)
Xemacs habe ich mir angeschaut, fühle mich aber son bisschen vom
Funktionsumfang erschlagen. Es hat aber deutlich mehr auf dem Kasten als
Notepad++, blöd sind nur die ganzen neuen Shortcuts die man lernen muss.
Und man muss sich das STRG+C, STRG+V abgewöhnen ;-) Ansonsten echt
mächtig der Editor.
Mein Design funktioniert nun so wie ich es mir wünsche, besten Dank
nochmal ;-)
Gruß
Marten
Marten V. schrieb:> Mein Design funktioniert nun so wie ich es mir wünsche, besten Dank> nochmal ;-)
Schön wäre, wenn du jetzt noch sagen würdest, was du gemacht hast. Das
würde dann evtl. anderen helfen, die so ein Problem haben...
Hi,
Hier meine Lösung, die jedoch noch nicht entgültig richtig ist.
Ich bin bei der Simulation noch auf ein Problem gestoßen, zu beginn des
Prozesses sind die Daten am Eingang noch nicht gültig und werden erst zu
einem späteren Zeitpunkt angelegt. Daraus ergibt sich, dass wenn ich im
Reset die anliegenden Daten übernehme, mein Design Daten vom Typ 'U'
übernimmt.
Hier mein Code:
Marten V. schrieb:> Ich bin bei der Simulation noch auf ein Problem gestoßen, zu beginn des> Prozesses sind die Daten am Eingang noch nicht gültig und werden erst zu> einem späteren Zeitpunkt angelegt. Daraus ergibt sich, dass wenn ich im> Reset die anliegenden Daten übernehme, mein Design Daten vom Typ 'U'> übernimmt.
Dieses D kommt aus deiner TB.
Dort sind offenbar bis zum ersten en='1' die Daten undefiniert.
Und dann wird natürlich auf einem undefinierten Zähler herumgezählt...
Zeig mal die TB.
Dort sollten für D einfach ein paar Defaultwerte angegeben werden.
Zum zeitlichen Ablauf:
es wäre besser, das en nicht genau mit dem Takt anzusteuern, denn sonst
mußt du dich immer fragen: hat er jetzt bei der steigenden Taktflanke
en='1' oder en='0' gesehen?
(Die Antwort daruf ist klar: er nimmt den vorhergehenden Wert, aber es
sieht eben seltsam aus)
1
d_int<=D;
2
if(EN='1')then
3
if(d_int=zeros)then
4
RDY<='1';
5
else
6
d_int<=d_int-1;
7
RDY<='0';
8
endif;
9
elsif(EN='0')then-- *****
10
RDY<='0';
11
d_int<=D;
12
endif;
13
endif;
***** hier reicht ein einfache else, denn sonst bastelst du dir für die
Simulation ein sehr seltsames Verhalten, wenn der 9-wertige std_logic EN
mal nicht den Wert '0' oder '1' hat... :-o
Ich würde diese Zeilen so schreiben:
1
d_int<=D;-- Defaultzuweisungen
2
RDY<='0';-- werden später bei Bedarf überschrieben
Ich habe das in der Testbench absichtlich so vorgesehen, denn der
Counter soll in einem größeren Projekt eingebunden werden. Der Zähler
enthält von einer anderen Einheit die Daten, die er herunterzählen soll.
Wann die Daten anliegen ist daher unbekannt. Deswegen hatte ich gedacht,
nach einem initialen Reset die Daten im Register des Counters auf
"000...0" zu setzen. Da beißt sich dann aber wieder die Katze in den
Schwanz, denn dann wird ein RDY Signal ausgegeben...
Hier meine TB:
Marten Vohrmann schrieb:> Wann die Daten anliegen ist daher unbekannt.
Auf jeden Fall sollten sie aber (einen Takt) vor dem Enable anliegen,
denn sonst ist es ja schnurzegal, was da heruntergezählt werden soll...
Wenn die Daten zusammen mit dem Enable kommen, dann kannst du sie nicht
mehr übernehmen, denn der Enable ist ja für den Zählvorgang
verantwortlich.
Ja du hast recht, ich sollte das in der FSM der anderen Einheit
berücksichtigen.
Wenn ich das enable jeweils immer einen Takt nach neuen Daten anlege,
klappt alles wunderbar.
Gibt es dennoch eine Möglichkeit, den Counter so zu designen, dass Daten
und EN-Signal gleichzeitig angelegt werden können ?
Mir fällt da momentan nichts besseres ein...
Vielen Dank für deine tolle Hilfe Lothar! :-)
Marten V. schrieb:> Gibt es dennoch eine Möglichkeit, den Counter so zu designen, dass Daten> und EN-Signal gleichzeitig angelegt werden können ?
Jein.
Du könntest sagen: wenn vorher en='0' war, und jetzt en='1' ist, dann
soll nicht gezählt, sondern die Daten übernommen werden. Also eine
Flankenerkennung basteln:
d_int<=D;-- unnötig, denn erst bei Flanke von en werden die Daten validiert
9
RDY<='0';
10
elsif(CLK='1'andCLK'event)then
11
last_en<=EN;-- Merker
12
d_int<=D;-- unnötig, denn erst bei Flanke von en werden die Daten validiert
13
RDY<='0';
14
if(EN='1')then
15
if(last_en='0')then-- steignede Flanke am en
16
d_int<=D;-- --> Daten übernehmen
17
elsif(d_int=zeros)then
18
RDY<='1';
19
else
20
d_int<=d_int-1;
21
endif;
22
endif;
23
endif;
24
endprocess;
Aber ich würde das eher als eine Designschwäche ansehen denn du zählst
dann eigentlich einen Takt falsch, weil der erste Takt ja für die
Datenübernahme verwendet wird.
Es sei denn, du mogelst dich so raus:
1
:
2
if(EN='1')then
3
if(last_en='0')then
4
d_int<=D-1;
5
elsif(d_int=zeros)then
6
:
Aber Achtung: was machst du, wenn D = 0 ist?
> Vielen Dank für deine tolle Hilfe Lothar! :-)
De Nada... ;-)
Du hast mal wieder recht, ganz so rosig ist das mit der Flankenerkennung
nicht. Ich denke ich bleibe bei meinem Design und beachte bei der
Verwendung einfach die Tatsache, dass die Daten einen Takt vor dem
EN-Signal anliegen müssen.
Hier meine Lösung für einen Count-Down Zähler mit generischer
Bit-Breite:
Hmm auch gut :-)
Kannst du mir vielleicht kurz erklären warum du Integer-Werte genommen
hast? Wenn man sich die Hardware anschaut, was ändert sich durch die
Verwendung von Integerwerten?
Marten V. schrieb:> Kannst du mir vielleicht kurz erklären warum du Integer-Werte genommen> hast?
Weil dann Vergleich und Zuweisungen einfach im Klartext gemacht werden
können. Oder vergleich mal du deinen std_logic_vector und unsigned
Zähler auf den Wert 12345.
> Wenn man sich die Hardware anschaut, was ändert sich durch die> Verwendung von Integerwerten?
Wenn der richtige Range angegeben ist: Nichts.
So, ich habe meinen Code nochmal überarbeitet. Die Daten sollen einfach
übergeben werden und erst beim setzen des enable-Signals "en=1" soll der
Zählvorgang beginnen.
In meiner letzten Beschreibung hatte ich eine Mischung aus
kombinatorischen und sequentiellen Processen verwendet, die sich u.U.
nicht auf einen FPGA abbilden lassen.
Über Feedback zu der neuen Beschreibung wäre ich euch dankbar :-)
Ja, gut, wird schon tun...
Aber warum nimmst du zum Zählen nicht einen Integer?
Sieh dir mal an, wie man das auch machen könnte:
1
libraryieee;
2
useieee.std_logic_1164.all;
3
useieee.numeric_std.all;
4
5
entitycounteris
6
generic(TAPS:integer:=11);
7
port(
8
RST_N:instd_logic;
9
EN:instd_logic;
10
CLK:instd_logic;
11
D:instd_logic_vector(TAPSdownto0);
12
RDY:outstd_logic);
13
endentitycounter;
14
15
architecturebehofcounteris
16
signalcnt:integerrange0to2**TAPS-1:=0;
17
begin
18
19
RDY<='1'whencnt=0else'0';
20
21
processbegin
22
waituntilrising_edge(clk);
23
if(cnt/=0andEN='1')then
24
cnt<=cnt-1;
25
endif;
26
if(RST_N='0'orEN='0'or(EN='1'andcnt=0))then
27
cnt<=to_integer(unsigned(D));
28
endif;
29
endprocess;
30
31
endarchitecture;
Marten V. schrieb:> Die Daten sollen einfach übergeben werden und erst> beim setzen des enable-Signals "en=1" soll der Zählvorgang beginnen.
Wenn das EN weiter gesetzt bleibt, wird dann aber auch sofort
weitergezählt...
BTW: wozu eigentlich ein Reset?
Ich dachte ich verwende lieber std_logic da ich, was die Simulation und
die Synthese angeht, auf der sicheren Seite bin. Std_logic hat ja die
Zustände 'X', 'U', usw.
Ein Reset-Signal scheint nun wirklich überflüssig zu sein, da mein
enable-Signal ja schon für ein Rücksetzen des Zählers sorgt.
Das Enable-Signal kommt aus einer FSM in einer höheren Hierarchie-Ebene.
Die FSM wertet das RDY-Signal aus und setzt entsprechend nach einer
positiven Flanke des RDY-Signals den Enable-Eingang auf 0 und setzt
damit den Counter zurück.
Marten V. schrieb:> Ich dachte ich verwende lieber std_logic da ich, was die Simulation und> die Synthese angeht, auf der sicheren Seite bin.
Der Synthese ist das egal. Die Simulation liefe mit std_ulogic schneller
ab (so heißt es), weil keine Auflösungstabellen (u=unresolved)
durchforstet werden müssen...
Aber in der Praxis hat sich der std_logic durchgesetzt.