Forum: FPGA, VHDL & Co. Zählermit Einstellmöglichkeiten


von Jonas K. (jonas_k)


Lesenswert?

Servus,

ich habe mir einen Zähler gemacht mit 30 Bit Breite, der außer dem 
reinen Zählen noch folgende Funktionen hat:
 - enable-Signal
 - updown ('1' = up)
 - clr (alles auf 0 setzen)
 - maximale Adresse, bis zu der der Counter läuft, 30 Bit
 - Schrittweite, 20 Bit maximal

Aber jetzt habe ich das Problem, dass mir die Zeit beim gewünschten Takt 
bei der Synthetisierung bei weitem nicht reicht (ca. 15 ns statt 10 ns).
Ich habe jetzt auch ca. 1 Jahr kein VHDL mehr gemacht und bin da noch 
nicht 100%-ig drin.

Wenn jemand Optimierungspotenzial sieht, wäre das super!

Gruß
Jonas


1
LIBRARY IEEE;
2
USE IEEE.STD_LOGIC_1164.ALL;
3
USE IEEE.NUMERIC_STD.ALL;
4
5
6
ENTITY CNT IS
7
  PORT
8
  (
9
        clock    : IN STD_LOGIC ;
10
        reset       : IN STD_LOGIC ;  
11
        cnt_en    : IN STD_LOGIC ;
12
        clr     : IN STD_LOGIC ;
13
        updown    : IN STD_LOGIC ;
14
        max_adr     : IN STD_LOGIC_VECTOR (29 DOWNTO 0);
15
        step        : IN std_logic_vector (19 DOWNTO 0);
16
        q        : OUT STD_LOGIC_VECTOR (29 DOWNTO 0)
17
  );
18
END CNT;
19
20
21
ARCHITECTURE rtl OF CNT IS
22
23
    signal count : unsigned (29 DOWNTO 0);  
24
    signal max   : unsigned (29 DOWNTO 0);
25
    signal stp   : unsigned (29 DOWNTO 0);
26
    
27
BEGIN
28
    q<=std_logic_vector(count);
29
    max <= unsigned(max_adr);
30
    stp <= unsigned("0000000000"&step);
31
    
32
    counting : PROCESS (reset, clock) 
33
    BEGIN
34
        IF(reset ='1') THEN
35
            count <= (others => '0');        
36
        ELSIF(rising_edge(clock)) THEN
37
            IF(clr='1') THEN
38
                count <= (others => '0'); -- bei clr auf 0 setzen
39
            ELSE 
40
                IF(cnt_en = '1') THEN -- enable-Signal
41
                    IF(updown = '1') THEN -- up
42
                        IF(((count-1) >= (max-stp)) and (count /= 0)) THEN --???
43
                            count <= (max-count -1) + stp;
44
                        ELSE
45
                            count <= count + stp; -- hochzählen
46
                        END IF;   
47
                    ELSE -- down
48
                        IF(count < stp) THEN --???
49
                            count <= ( max - stp ) + count + 1;
50
                        ELSE
51
                            count <= count - stp; -- runterzählen
52
                        END IF;                   
53
                    END IF;
54
                END IF;
55
            END IF;
56
        END IF;
57
    END PROCESS;
58
59
END rtl;


Am schlechtesten erscheinen mir die beiden Bedingungen, die ich mit ??? 
markiert habe, wegen der Rechnerei und und dem größer/kleiner. Mir ist 
aber keine bessere Lösung eingefallen.

: Bearbeitet durch User
von Horn (Gast)


Lesenswert?

Schon alle Fälle simuliert?

Was sagt die Synthese? Wieviel Megaherzen schaffst du?

von Jonas K. (jonas_k)


Lesenswert?

Horn schrieb:
> Schon alle Fälle simuliert?
>
> Was sagt die Synthese? Wieviel Megaherzen schaffst du?

Bei Simulieren bin ich gerade.

Zum Takt hab ich oben die Zeiten geschrieben: 100MHz will ich (10ns).
Schaffen tu ich derzeit um die 15ns, sagt die Synthese
Das ganz auf einem Cyclone II mit Speed Grade -8.

Später soll das mal auf einen Cyclone V mit bessserem Speed grade, geht 
aber halt jetzt noch nicht mangels Hardware.


Eventuell sieht ja jemand etwas, was extrem schlecht und langsam ist. Es 
kommt ja oft auc himmer auf die Schreibweise an.
Ich wüsste eben, dass größer/kleiner deutlich schlechter ist als =, aber 
ich seh keinen Weg, das zu vermeiden....

von Maxx (Gast)


Lesenswert?

Muss der Zähler strikt binär zählen, oder reicht dir evtl. eine andere 
Ordnung? Eine Alternative kann ein LFSR mit entsprechender Periodenlänge 
sein.

Das macht jedoch nur dann Sinn, wenn die Umwandlung zwischen LFSR und 
Periodenindex in Binärzählform nicht so häufig vorkommt, dass du den 
Geschwindigkeitsvorteil wieder verlierst.
Der Geschwindigkeitsvorteil des LFSR kommt dadurch, dass es keine 
Situation gibt in der ein Zählschritt durch den gesamten Vektor 
propagiert.

Beispielsweise praktisch bei Speicherort-Zählern in LAs und co, wo der 
reale Speicherort egal ist solange die Ordnung erhalten bleibt.

von Achim S. (Gast)


Lesenswert?

Maxx schrieb:
> Der Geschwindigkeitsvorteil des LFSR kommt dadurch, dass es keine
> Situation gibt in der ein Zählschritt durch den gesamten Vektor
> propagiert.

Jonas zählt ja nicht in Schritten von 1 hoch sondern in einstellbarer 
Schrittweite von 1 bis 2^29-1. Da hilft ihm imho auch eine andere 
Codierung des Zählers nichts, und da lässt sich auch ein Vergleich mit 
"größer/kleiner" statt nur mit "gleich" kaum vermeiden.

Wenn dein FPGA DSP-Kerne hat, dann können diese die Addition und den 
Vergleich vielleicht schneller durchführen als die Logik in LUTs. (der 
Vergleich "kleiner als" lässt sich als Subtraktion mit anschließendem 
Vorzeichencheck bauen). Bin aber nicht sicher, ob du es damit wirklich 
schneller hinkriegst...

von Jonas K. (jonas_k)


Lesenswert?

Achim S. schrieb:
> Wenn dein FPGA DSP-Kerne hat, dann können diese die Addition und den
> Vergleich vielleicht schneller durchführen als die Logik in LUTs. (der
> Vergleich "kleiner als" lässt sich als Subtraktion mit anschließendem
> Vorzeichencheck bauen). Bin aber nicht sicher, ob du es damit wirklich
> schneller hinkriegst...

Das werd ich morgen mal ausprobieren!

Maxx schrieb:
> Muss der Zähler strikt binär zählen, oder reicht dir evtl. eine andere
> Ordnung? Eine Alternative kann ein LFSR mit entsprechender Periodenlänge
> sein.

wie bereits gesagt, einstellbare Schrittweite zwischen 1 und ...


Was mir jetzt noch am ehesten eingefallen ist, wäre "manuelles 
Pipelining". Nämlich, dass ich die IF-Bedingung
1
IF(((count-1) >= (max-stp)) and (count /= 0)) THEN
 in mehrere Schritte aufteile: erst das arithmische (also max-stp), dann 
der Vergleich, gespeichert in einem Signal, und damit im 3. Takt in das 
if und neue count-Zuweisung (also bspw. 3 Takte statt in einem).

Damit müsste es schneller zu schaffen sein.


Ich werde morgen mal die beiden Möglichkeiten austesten und dann 
hoffentlich zu einer Lösung kommen...

von Achim S. (Gast)


Lesenswert?

Jonas K. schrieb:
> in mehrere Schritte aufteile: erst das arithmische (also max-stp), dann
> der Vergleich, gespeichert in einem Signal, und damit im 3. Takt in das
> if und neue count-Zuweisung (also bspw. 3 Takte statt in einem).
>
> Damit müsste es schneller zu schaffen sein.

richtig, so schaffst du voraussichtich einen schnelleren Takt. Dafür 
brauchst du dann aber mehrere Takte für einen Zählschritt, das 
eigentliche Zählen wird also eher langsamer.

Im Prinzip könntest du auch so pipelinen, dass der Zähler pro Takt um 
eins hochzählt. Dann müsstest du aber gehörig um die Ecke denken: also 
nicht vergleichen, ob der Zähler im nächsten Schritt über die Grenze 
läuft, sondern vergleichen ob der Zähler im überübernächsten Schritt 
über die Grenze läuft.

von VHDL++ (Gast)


Lesenswert?

>> IF(count < stp) THEN --???
>> count <= ( max - stp ) + count + 1;

Der Pfad ist extrem lang.

Du hast aber wenig Chancen, weil sowohl deine Zählschritte als auch die 
obere Grenze voll variabel sind und du deshalb nicht im Voraus rechnen 
kannst, das Ergebnis aber schon 1 Takt später brauchst.

Wenn du diese Beiden zum Generic machst bekommst du das Modul 
warscheinlich locker auf 200 MHz, auch im Cyclone 2.

von Winfried J. (Firma: Nisch-Aufzüge) (winne) Benutzerseite


Lesenswert?

Hallo

ich halte die Schachtelung der if-statements für kontraproduktiv

die zählschleife sollte so kurz wie möglich sein

für die Bereichskontrolle verwende flags

zurücksetzen, parameter ändern etc gehört gar nicht in die Schleife 
sondern sollte eine externe funktion

Das alles bläht nur die main loop und kostet dich zeit welche dir zum 
zählen fehlt.

bin kein VHDL Freak aber selbst in C sähe das bei mir nicht so aus

statt der ersten 3 If-Statements würde ich eine Logische Veknüpfung 
wählen

so das der clock eingefrohren wird solange reset, clr anliegen oder 
cnt-enable nicht freigegeben sind .....



sorry
MFG Winne

von Maxx (Gast)


Lesenswert?

Das ist richtig. Die Schrittweite liesse sich durch die geeignete wahl 
des Polynoms variieren, jedoch ist der < / > Vergleich in der Ordnung 
aufwendig.

Es braucht aber keine volle Substraktion bei dem Urspünglichen Konzept. 
(Du willst ja nicht wissen wieviel, sondern nur ob der eine Wert größer 
ist. Das lässt sich auch durch andere Art testen.

z.B. in dem man das höchstwertigste abweichende Bit testet. ist dies im 
Substrahenden gesetzt, so ist dieser Größer.

wenn A xor B = 0 -> A == B
sonst wenn (B and HighestBitValue(A xor B)) -> A < B

HighestBitValue kann in einer Baumstruktur aufgebaut werden und hat 
einen zu der Bitbreite logarithmische Pfadlänge. Addition/Substraktion 
durch das Carry eine Lineare.

Bei 30 Bit würde ich das als alternativen Vergleich mal testen.

von bko (Gast)


Lesenswert?

zusätzlich könnte auch ein Synchroner Reset helfen.
http://www.mikrocontroller.net/articles/VHDL#Synchroner_oder_asynchroner_Reset
Denn er könnte möglicherweiße den Delay im Zähler verringern:
http://www.xilinx.com/support/documentation/white_papers/wp272.pdf

Beim FPGA  könnte man den reset evtl auch ganz weglassen,
und count initialisieren:
alt:
>  IF(reset ='1') THEN
>            count <= (others => '0');
>        ELSIF(rising_edge(clock)) THEN
>            IF(clr='1') THEN
>                count <= (others => '0'); -- bei clr auf 0 setzen

neu:
signal count : unsigned (29 DOWNTO 0)  := "0000"; //syntax evtl falsch!
(...)
 IF(rising_edge(clock)) THEN
            IF(clr='1') THEN
                count <= (others => '0'); -- bei clr auf 0 setzen

Beitrag "Re: Xilinx und die Resets"

von chris (Gast)


Lesenswert?

Ev., wenn deine Anwendung es hergibt, nicht den Maximalen counterwert 
einstellbar machen, sondern anstelle von 30bit 31bit verwenden, und wenn
das MSB darin 1 ist, ist der Zähler voll und Zählt nicht weiter, mittels
Zählinterval kann man dann die maximale Anzahl von Schritten einstellen.

von Jonas K. (jonas_k)


Lesenswert?

VHDL++ schrieb im Beitrag #3403391:
>>> IF(count < stp) THEN --???
>>> count <= ( max - stp ) + count + 1;
>
> Der Pfad ist extrem lang.
>
> Du hast aber wenig Chancen, weil sowohl deine Zählschritte als auch die
> obere Grenze voll variabel sind und du deshalb nicht im Voraus rechnen
> kannst, das Ergebnis aber schon 1 Takt später brauchst.
>
> Wenn du diese Beiden zum Generic machst bekommst du das Modul
> warscheinlich locker auf 200 MHz, auch im Cyclone 2.

Im Voraus rechnen kann ich schön, dann sieht halt die Bedingung anders 
aus, und die Einstellungen max und step haben einen Takt Verzögerung.

Wenn ich die beiden Eingangsgrößen als Generic mache, kann ich das ganze 
ja nicht zur Laufzeit umkonfigurieren. Das ist irgendwie unpassend.


Winfried J. schrieb:
> bin kein VHDL Freak aber selbst in C sähe das bei mir nicht so aus
>
> statt der ersten 3 If-Statements würde ich eine Logische Veknüpfung
> wählen

Da hast du wohl noch nicht viel mit VHDL zu tun gehabt ;) Alles was du 
siehst, geschieht in einem Takt! Und solche If's, auch verschachtelt - 
werden theoretisch von der Synthese-Software optimiert. Also IF(x1 and 
X2 and X3) ist identisch mit IF(X1) IF(X2) IF (X3)

von Jonas K. (jonas_k)


Lesenswert?

Maxx schrieb:
> wenn A xor B = 0 -> A == B
> sonst wenn (B and HighestBitValue(A xor B)) -> A < B
>
> HighestBitValue kann in einer Baumstruktur aufgebaut werden und hat
> einen zu der Bitbreite logarithmische Pfadlänge. Addition/Substraktion
> durch das Carry eine Lineare.

Sehr gut, Merce :)


bko schrieb:
> zusätzlich könnte auch ein Synchroner Reset helfen.
> ..............
> .............
> neu:
> signal count : unsigned (29 DOWNTO 0)  := "0000"; //syntax evtl falsch!
> (...)
>  IF(rising_edge(clock)) THEN
>             IF(clr='1') THEN
>                 count <= (others => '0'); -- bei clr auf 0 setzen
>
> Beitrag "Re: Xilinx und die Resets"

Interessant, ist mir neu. Einen Reset werde ich überhaupt brauchen, 
inwiefern das alle Teile betrifft oder nur manche, ist aber zu 
überlegen, wenn Altera das genauso wie Xilinx macht. Werd ich mal 
nachlesen!

von Jonas K. (jonas_k)


Lesenswert?

Achim S. schrieb:
> Im Prinzip könntest du auch so pipelinen, dass der Zähler pro Takt um
> eins hochzählt. Dann müsstest du aber gehörig um die Ecke denken: also
> nicht vergleichen, ob der Zähler im nächsten Schritt über die Grenze
> läuft, sondern vergleichen ob der Zähler im überübernächsten Schritt
> über die Grenze läuft.

So wars gemeint :)
wohl zu kompliziert ausgedrückt^^

chris schrieb:
> Ev., wenn deine Anwendung es hergibt, nicht den Maximalen counterwert
> einstellbar machen, sondern anstelle von 30bit 31bit verwenden, und wenn
> das MSB darin 1 ist, ist der Zähler voll und Zählt nicht weiter, mittels
> Zählinterval kann man dann die maximale Anzahl von Schritten einstellen.

Das funktioniert so nicht, da der Zählerstand ja z. B. eine Adresse im 
RAM/LUT repräsentiert und deswegen SOWOHL die maximale Adresse (Anzahl 
aktuell gespeicherter Werte) ALS AUCH die Schrittweite 
(Frequenzveränderung) anpassbar sein soll.



Und Danke mal für all eure Antworten, ist wirklich sehr aufschlussreich 
:)

: Bearbeitet durch User
von Jonas K. (jonas_k)


Lesenswert?

So, jetzt hab ich die Erkennung größer / kleiner gepipelinet. Damit bin 
ich von 15 ns auf 10.012 ns Laufzeit gekommen. (benötigt für 100MHz sind 
ja 10 ns)
Also so gut wie geschafft, mit dem anderen Algorithmus zur Vergleich 
sollte das ganze ja noch mal besser werden...
1
    counting : PROCESS (reset, clock) 
2
    BEGIN
3
        IF(reset ='1') THEN
4
            count <= (others => '0');     
5
            flag <= false;
6
        ELSIF(rising_edge(clock)) THEN
7
            IF(clr='1') THEN
8
                IF(updown='0') THEN
9
                    flag <= false;
10
                    count <= max-stp; 
11
                ELSE
12
                    count <= stp; 
13
                    flag <= false;                
14
                END IF;
15
            ELSE 
16
                IF(cnt_en = '1') THEN
17
                    IF(updown = '1') THEN
18
                        flag <= (((count-1+stp) >= (max-stp)) and ((count) /= (max-stp+1)));
19
                        IF(flag) THEN
20
                    -- new: ((count-1-stp) >= (max-stp)) and (count /= stp)
21
                            count <= (max-count -1) + stp;
22
                        ELSE
23
                            count <= count + stp;
24
                        END IF;   
25
                    ELSE
26
                        flag <= ((count -stp) < stp);
27
                        IF(flag) THEN
28
                    -- new: (count -stp) < stp
29
                            count <= ( max - stp ) + count + 1;
30
                        ELSE
31
                            count <= count - stp;
32
                        END IF;                   
33
                    END IF;
34
                END IF;
35
            END IF;
36
        END IF;
37
    END PROCESS;

von Leonard Lebewohl (Gast)


Lesenswert?

Schon beim reset sehe ich optimierungspotential:


Brauchst du einen asynchronen Reset? Falls nein, dann forme ihn in einen 
synchronen um. Zumindest bei Xilinx bringt das vorteile, weil der 
FF-Reset entweder synchron oder asynchron sein kann, ist es asynchron 
stehen die RS Eingange nicht mehr zur Verfügung und müssen über Logik 
vor dem D- Eingang des FF nachgebildet werden.

Wenn du nur einen PowerUp reset benötigst, dann den resetzweig komplett 
rausnehmen. Signale werden bei der Konfiguration schon initialisiert, da 
brauchst keine extra Initialisierungsschaltung.

Extra reset für flag scheint mir auch unnötig. Flag wird vom counter 
abgeleitet, wird dieser gereset edann hat das auch einfluß auf das Flag.

MfG,

von Jonas K. (jonas_k)


Lesenswert?

Leonard Lebewohl schrieb:
> Schon beim reset sehe ich optimierungspotential:
>
>
> Brauchst du einen asynchronen Reset?

Ist bei Altera allerdings anders als bei Xilinx, die haben einen 
asynchronen reset integriert, und dann kann ich den ja auch verwenden.



Mit dem Pipelining habe ich die Verzögerung des Vergleichs und des 
Addierens+Subtrahierens vor den eigentlichen Zähler auf unter 8ns 
(gefordert 10) gebracht.

Am längsten braucht jetzt noch folgender Teil - nämlich der eigentliche 
Zähler:
1
               IF(cnt_en = '1') THEN
2
                    IF(updown = '1') THEN
3
                        IF(flag) THEN
4
                            count <= (tmp)-count ;
5
                        ELSE
6
                            count <= count + stp;
7
                        END IF;   
8
                    ELSE
9
                        IF(flag) THEN
10
                            count <= (tmp) + count ;
11
                        ELSE
12
                            count <= count - stp;
13
                        END IF;                   
14
                    END IF;
15
                END IF;

von PowerHeinz (Gast)


Lesenswert?

Wenn Deine Anwendung es erlaubt, hilft's ja vielleicht, den Counter zu 
splitten, also einen Up- und einen Down-Counter parallel zu bauen. Das 
updown-Signal steuert dann einen Muxer, der den gewünschten Counter 
auswählt. Das geht natürlich nur, wenn der Counter nicht hin und 
herzählen soll...

von Fpgakuechle K. (Gast)


Lesenswert?

Jonas K. schrieb:
> ENTITY CNT IS
>   PORT
>   (
>         clock    : IN STD_LOGIC ;
>         reset       : IN STD_LOGIC ;
>         cnt_en    : IN STD_LOGIC ;
>         clr     : IN STD_LOGIC ;
>         updown    : IN STD_LOGIC ;
>         max_adr     : IN STD_LOGIC_VECTOR (29 DOWNTO 0);
>         step        : IN std_logic_vector (19 DOWNTO 0);
>         q        : OUT STD_LOGIC_VECTOR (29 DOWNTO 0)
>   );
> END CNT;


Das Updown kann man sich sparen wenn man fürs decrement step mit dem 
Zweikomplement der stepsize lädt,

x-a = x + (-a)

Dann braucht es keinen Muxer, allerdings muss step auf 30 bit erweitert 
werden.

MfG,

von Jonas K. (jonas_k)


Lesenswert?

Fpga Kuechle schrieb:
> Jonas K. schrieb:
>> ENTITY CNT IS
>>   PORT
>>   (
>>         clock    : IN STD_LOGIC ;
>>         reset       : IN STD_LOGIC ;
>>         cnt_en    : IN STD_LOGIC ;
>>         clr     : IN STD_LOGIC ;
>>         updown    : IN STD_LOGIC ;
>>         max_adr     : IN STD_LOGIC_VECTOR (29 DOWNTO 0);
>>         step        : IN std_logic_vector (19 DOWNTO 0);
>>         q        : OUT STD_LOGIC_VECTOR (29 DOWNTO 0)
>>   );
>> END CNT;
>
>
> Das Updown kann man sich sparen wenn man fürs decrement step mit dem
> Zweikomplement der stepsize lädt,
>
> x-a = x + (-a)
>
> Dann braucht es keinen Muxer, allerdings muss step auf 30 bit erweitert
> werden.
>
> MfG,

mag sein, dass ich dann denselben addier/subtrahier-Vorgang habe. Der 
Überlauf ist aber flexibel, dafür brauche ich doch noch zwei 
Bedingungen, oder?

Überlauf mit positivem Step
1
count <= (max-count -1) + stp;
2
-- up ^    down v
3
count <= ( max - stp ) + count + 1;

Änderung bei updown-abhängigem Vorzeichen von Step (immer + vor step)
1
count <= (max-count -1) + stp;
2
-- up ^    down v
3
count <= ( max + stp ) + count + 1;
Der verbleibende Unterschied ist also noch die +/- 1!

von Fpgakuechle K. (Gast)


Angehängte Dateien:

Lesenswert?

Am besten man zeichnet ein kleines Blockbild (siehe Anhang).

Fürt den vergleich ob die Grenze des Adressbereichs erreicht ist führt 
man ein register ein, das fur up und down jeweils mit der ersten 
unzulässigen Adresse vor addition des stepsize geladen wird (up:MAX - 
step + 1; down: Min+step - 1). Ist die Grenze überschritten wird die 
berechnete Adresse nicht in das Ausgangregister geladen. Step ist für 
downwards negativ (zweikomplemant) Das Richtungsbit für den 
Bereichscomperator kann man sich sparen wenn man ein höherwertiges bit 
(z.b (29) von step nimmt. Für negatives step sind diese ja '1'. Dann 
dreht sich allerdings die Polarität um. Den Code habe ich ohne reset 
geschrieben, für asynchronen reset die auskommentierten Zeilen 
verwenden.

MfG:
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.all;
3
use IEEE.NUMERIC_STD.all;
4
5
entity CNT is
6
  port (
7
    clk       : in  std_logic;
8
    reset     : in  std_logic;
9
    cnt_en    : in  std_logic;
10
    clr       : in  std_logic;
11
    updown    : in  std_logic;
12
    limit_adr : in  std_logic_vector (29 downto 0);
13
    step      : in  std_logic_vector (29 downto 0);
14
    count_oq  : out std_logic_vector (29 downto 0));
15
end CNT;
16
17
architecture rtl of CNT is
18
  signal count_q : std_logic_vector(29 downto 0):= (others => '0');
19
  signal count   : std_logic_vector(29 downto 0);
20
  signal cmp            : std_logic;
21
  signal cnt_ena        : std_logic;
22
  
23
begin
24
  P_cnt : process(clk)
25
--  P_cnt : process(clk, reset)
26
  begin
27
    --if reset = '1' then
28
    --  count_q <= (others => '0');
29
    --elsif rising_edge(clk) then
30
    if rising_edge(clk) then
31
      if clr = '1' then
32
        count_q <= (others => '0');
33
      elsif cnt_ena = '1' then
34
        count_q <= count;
35
      end if;
36
    end if;
37
  end process P_cnt;
38
39
  count_oq <= count_q;
40
  count    <= std_logic_vector(unsigned (count_q) + unsigned(step));
41
  cnt_ena  <= cmp and cnt_en;
42
43
  cmp <= '1' when ((unsigned(limit_adr) > unsigned(count_q)) and (updown = '1')) or
44
                  ((unsigned(limit_adr) < unsigned(count_q)) and (updown = '0')) else
45
         '0';
46
  
47
end architecture rtl;

von Jonas K. (jonas_k)


Lesenswert?

Fpga Kuechle schrieb:
> Am besten man zeichnet ein kleines Blockbild (siehe Anhang).
>
> Fürt den vergleich ob die Grenze des Adressbereichs erreicht ist führt
> man ein register ein, das fur up und down jeweils mit der ersten
> unzulässigen Adresse vor addition des stepsize geladen wird (up:MAX -
> step + 1; down: Min+step - 1). Ist die Grenze überschritten wird die
> berechnete Adresse nicht in das Ausgangregister geladen. Step ist für
> downwards negativ (zweikomplemant) Das Richtungsbit für den
> Bereichscomperator kann man sich sparen wenn man ein höherwertiges bit
> (z.b (29) von step nimmt. Für negatives step sind diese ja '1'. Dann
> dreht sich allerdings die Polarität um. Den Code habe ich ohne reset
> geschrieben, für asynchronen reset die auskommentierten Zeilen
> verwenden.
>
> MfG:

Ich hatte jetzt mal deinen Code übernommen und noch weiter angepasst 
(dass beim Überlauf wieder der entgegesnette Wert geladen wird und das 
mit dem negativen Step). Dann hab ich allerdings wieder das alte 
Problem, dass ich noch pipelinen müsste....

Deswegen hab ich doch den Code genommen, den ich bis Freitag fabriziert 
hatte... Da ist das Pipelining der Additionen und der Vergleiche bereits 
gemacht.
Deswegen schaut der Code jetzt so aus:
1
LIBRARY IEEE;
2
USE IEEE.STD_LOGIC_1164.ALL;
3
USE IEEE.NUMERIC_STD.ALL;
4
5
6
ENTITY CNT IS
7
  PORT
8
  (
9
        clock    : IN STD_LOGIC ;
10
        reset       : IN STD_LOGIC ;  
11
        cnt_en    : IN STD_LOGIC ;
12
        clr     : IN STD_LOGIC ;
13
        updown    : IN STD_LOGIC ;
14
        max_adr     : IN STD_LOGIC_VECTOR (29 DOWNTO 0);
15
        step        : IN std_logic_vector (19 DOWNTO 0);
16
        q        : OUT STD_LOGIC_VECTOR (29 DOWNTO 0)
17
  );
18
END CNT;
19
20
21
ARCHITECTURE rtl OF CNT IS
22
23
    signal count : unsigned (29 DOWNTO 0);  
24
    signal max   : unsigned (29 DOWNTO 0);
25
    signal stp   : unsigned (29 DOWNTO 0);
26
    signal twostp: unsigned (29 DOWNTO 0);
27
    signal cmp   : unsigned (29 DOWNTO 0);
28
    signal tmp   : unsigned (29 DOWNTO 0);
29
    signal tmp_pre:unsigned (29 DOWNTO 0);
30
    signal flag  : boolean;
31
    signal flag_delayed  : boolean;
32
    signal updown_delayed : std_logic;
33
    
34
BEGIN
35
  q<=std_logic_vector(count);
36
    max <= unsigned(max_adr);
37
    stp <= unsigned("0000000000"&step);
38
    
39
    
40
    
41
    counting : PROCESS (reset, clock) 
42
    BEGIN
43
        IF(reset ='1') THEN
44
            count <= (others => '0');  
45
            cmp   <= (others => '0');  
46
            tmp   <= (others => '0'); 
47
            tmp_pre<=(others => '0');    
48
            twostp<= (others => '0');    
49
        ELSIF(rising_edge(clock)) THEN
50
            -- make stp+step, as it's needed often in cmp
51
            twostp <= stp + stp;
52
            -- make cmp for camparing with count and tmp for faster adding ...
53
            IF(updown='1') THEN
54
                cmp <= (max-twostp); 
55
                tmp <= ( max + tmp_pre);  -- max -1 +stp
56
                tmp_pre <= (stp-1);
57
            ELSE            
58
                cmp <= twostp;
59
                tmp <= ( max - tmp_pre); -- max +1 -stp
60
                tmp_pre <= (stp-1);
61
            END IF;
62
            -- delay updown, to see if it's changed
63
            updown_delayed <= updown;
64
            
65
            IF(clr='1' or (updown /= updown_delayed)) THEN -- just a clear-signal
66
                IF(updown='0') THEN
67
                    flag <= false;
68
                    count <= max-stp; 
69
                ELSE
70
                    count <= stp; 
71
                    flag <= false;                
72
                END IF;
73
            ELSE -- set flag, comparing count with comp...
74
            -- Optimierungsmöglichkeit: </> ersetzen durch Baumstruktur mit nlogn statt n²
75
                IF(updown='1') THEN
76
                    flag <= ((count > (cmp)));
77
                    flag_delayed <= not flag;
78
                ELSE     
79
                    flag <= (((count) < cmp));       
80
                    flag_delayed <= not flag;
81
                END IF;
82
                
83
                IF(cnt_en = '1') THEN
84
                    IF(updown = '1') THEN
85
                    -- count+step element [max-stp+1;max] => neuer bereich
86
                    -- count element [max-step-step+1;max-step]
87
                    -- falls größer als max-step -> auch auf 0 setzen (verzählt? max-Änderung?)
88
                    -- also muss count größer sein als max -stp -stp +1 -> cmp-Berechnung außerhalb
89
                        IF(flag and flag_delayed) THEN
90
                    -- count element [max;max-stp+1]
91
                    --  IF(((count-1) >= (max-stp)) and (count /= 0)) THEN
92
                            count <= (tmp)-count ;
93
                        ELSE
94
                            count <= count + stp;
95
                        END IF;   
96
                    ELSE
97
                    -- count+step element [0;stp-1] => neuer bereich
98
                    -- count element [stp;step+step-1]
99
                    -- falls kleiner als step -> auch flag setzen (verzählt? max-Änderung?)
100
                    -- also muss count kleiner sein als stp+stp-1 -> cmp-Berechnung außerhalb
101
                        IF(flag and flag_delayed) THEN
102
                    -- count element [0;stp-1]
103
                    --  IF(count < stp) THEN
104
                            count <= (tmp) + count ;
105
                        ELSE
106
                            count <= count - stp;
107
                        END IF;                   
108
                    END IF;
109
                END IF;
110
            END IF;
111
        END IF;
112
    END PROCESS;
113
114
END rtl;

von Fpgakuechle K. (Gast)


Lesenswert?

Jonas K. schrieb:
> Fpga Kuechle schrieb:


Hallo Jonas,

meines Erachtens stösst schneller zum Kern des timingproblems wenn man 
sich vom VHDL-Code löst und die Schaltung im Blockbild analysiert. Hat 
man dann einen neue Lösung gefunden schreibt man diese in VHDL/Code und 
synthetisiert zur Probe. Optimierungsmethoden wie in C (loop unrolling, 
Schachteltiefe begrenzen, Makros (inlinecode) verwenden greifen hier 
nicht.

Meines erachtens ist es auch übersichlicher nur die Speicherung des 
Zählerstandes in den Prozess zu schreiben und die ganze Kombinatorik 
inklusive des kritischen Pfades ausserhalb.
1
                IF(updown='1') THEN
2
                    flag <= ((count > (cmp)));
3
                    flag_delayed <= not flag;
4
                ELSE     
5
                    flag <= (((count) < cmp));       
6
                    flag_delayed <= not flag;
7
                END IF;
ich vermisse den Fall count = tmp. Da flag_deöayed identisch zugewiesen 
wird, sollte man es über das erste IF ziehen, ist übersichtlicher.
1
count <= max-stp;

da max und stp letzlich eingangsport sind, ändert sich max-step nicht 
während der Berechnung. Falls du siese differenz wirklich benötigst, 
dann kann man sie ebenso als eingangsport definieren und wie ein 
normales steuerregister beschreiben. das spart den subtractor. ebenso 
twostp. wobei letzteres durch ein linksshift ersetzbar ist:
1
twostp <= stp + stp;
2
--          step(28 downto 1) & '0' --ist dasselbe ohne adder


Folgendes verstehe ich nicht, clear soll doch alles auf Null setzen?
Wahrscheinlich meinst du "load" laden eines Register/Zählers mit einem 
eingangsparameter.

1
        IF(clr='1' or (updown /= updown_delayed)) THEN -- just a clear-signal
2
                IF(updown='0') THEN
3
                    flag <= false;
4
                    count <= max-stp; 
5
                ELSE
6
                    count <= stp; 
7
                    flag <= false;                
8
                END IF;

wie gesagt, meines Erachtens erst Blockbild skizieren, dann in VHDL 
umsetzen.

MfG,

von Winfried J. (Firma: Nisch-Aufzüge) (winne) Benutzerseite


Lesenswert?

obgleich ich wie gesagt kein FPGA_könstler bin höhre ich meine Gedanken 
aus FPGA küchles Worten siehe oben.

MfG
Winne

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.