Forum: FPGA, VHDL & Co. Alternative Zählerbeschreibung


von Harry (Gast)


Lesenswert?

Hallo,

ich habe folgenden Zähler:

1
process(clk)
2
begin
3
   if rising_edge(clk) then
4
         if cnt < cMAX then
5
             cnt <= cnt + 1;
6
         end if;
7
   end if;
8
end process;


Gibt es eine alternative Beschreibung, mittels derer der Zähler beim 
Maximalwert stehenbleibt, man jedoch keinen Komparator verwenden muss?

Grüsse, Harry

von René D. (Firma: www.dossmatik.de) (dose)


Lesenswert?

> Gibt es eine alternative Beschreibung, mittels derer der Zähler beim
> Maximalwert stehenbleibt, man jedoch keinen Komparator verwenden muss?
>

Ich weiss nicht, was dich dabei stört? Oder was genau dein Problem ist.
Dein Code muss noch zwei Sachen sinnvoller Weise beinhalten.
--ein Wert von wo aus gezählt wird
--Rücksetzmechanismus

von JBB (Gast)


Lesenswert?

Wenn der Zähler so gestrickt ist, dass der Endwert eine Binärzahl ist 
und er rückwärtsläuft, dann ist der "Komparator" nur ein einziges Bit, 
dass den enable des Zählers freigibt.

von Klaus F. (kfalser)


Lesenswert?

JBB schrieb:
> Wenn der Zähler so gestrickt ist, dass der Endwert eine Binärzahl ist
> und er rückwärtsläuft, dann ist der "Komparator" nur ein einziges Bit,
> dass den enable des Zählers freigibt.

Ach, geh...
Der Komparator ist immer über die volle Breite, weil alle Bits gestestet 
werden müssen, ob sie 0 sind.
Es stimmt aber, dass ein Vergleich auf Gleichheit wahrscheinlich 
effizenter ist als ein Vergleich auf kleiner.
Ob er nach oben oder unten zählt ist egal.

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


Lesenswert?

Harry schrieb:
> Gibt es eine alternative Beschreibung, mittels derer der Zähler beim
> Maximalwert stehenbleibt, man jedoch keinen Komparator verwenden muss?
Nur, wenn du (wie schon erwähnt) eine Zweierpotenz hast, dann kannst du 
ein Bit mehr spendieren und diesen Überlauf (beim Hochzählen) zum 
Anhalten des Zählers verwenden...

von Fpgakuechle K. (Gast)


Lesenswert?

Klaus Falser schrieb:
> JBB schrieb:
>> Wenn der Zähler so gestrickt ist, dass der Endwert eine Binärzahl ist
>> und er rückwärtsläuft, dann ist der "Komparator" nur ein einziges Bit,
>> dass den enable des Zählers freigibt.
>
> Ach, geh...
> Der Komparator ist immer über die volle Breite, weil alle Bits gestestet
> werden müssen, ob sie 0 sind.
> Es stimmt aber, dass ein Vergleich auf Gleichheit wahrscheinlich
> effizenter ist als ein Vergleich auf kleiner.
> Ob er nach oben oder unten zählt ist egal.

es geht aoch ohne all bits zu vergleichen:

1) One-Hot schieberegister verwenden, dann braucht man nur ein bit 
vergleichen

2) Zähler als binärinkrement bauen mit Maximalwert "alle bits '1'",
dann ist das carry_out der höchsten Zählerstelle das einzige zu testende 
bit.

MfG,

von Sigi (Gast)


Lesenswert?

(signed!) Zähler von MAX-1 runterzählen lassen und
bei Überlauf stoppen.

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


Lesenswert?

Sigi schrieb:
> (signed!) Zähler von MAX-1 runterzählen lassen und bei Überlauf stoppen.
So wurde das schon in den Urgesteinen 8048 und 8051 uCs gemacht... ;-)

>  bei Überlauf stoppen
Eigentlich ist das ein Unterlauf...

von Harry (Gast)


Lesenswert?

>1) One-Hot schieberegister verwenden, dann braucht man nur ein bit
>vergleichen

Na das wird von der Größe des Schieberegisters für größere Zähler nicht 
haltbar sein.

von Harry (Gast)


Lesenswert?

Was haltet ihr von folgender Variante?
Man lagert den Vergleich getaktet aus. Dabei muss man dafür sorgen, dass
aufgrund des Delays der Zähler rechtzeitig aufhört zu zählen.

Hintergrund meiner Überlegungen: Für große Zähler möchte ich NICHT
Zähler und Komparator in einem Takt behandeln, um die Performance
zu steigern.
1
process(clk)
2
begin
3
    if rising_edge(clk) then
4
        if ena then
5
           cnt <= cnt + 1;
6
        end if;
7
8
        if cnt = cMAX-1 or cnt = cMAX then
9
           ena <= false;
10
        else
11
           ena <= true;
12
        end if;
13
    end if;
14
end process;


Grüsse, Harry

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


Lesenswert?

Harry schrieb:
> Man lagert den Vergleich getaktet aus.
Hört sich an wie: man lügt sich was in die Tasche...
Warum sollte da ein Vergleicher gespart werden?
Was ist denn eigentlich dein Ziel?

>     if cnt = cMAX-1 or cnt = cMAX then
Wie bitte?
Das solltest du dir nochmal überlegen.
Ich würde da einfach so schreiben:
   if cnt>=cMAX-1 then

von Duke Scarring (Gast)


Lesenswert?

Harry schrieb:
> Für große Zähler möchte ich NICHT
> Zähler und Komparator in einem Takt behandeln, um die Performance
> zu steigern.
Wie groß sollen denn Deine Zähler werden? Und wie schnell möchtest Du 
damit zählen. Üblicherweise sind in einem Design nicht die Zähler das 
limitierende Element.

Duke

von Harry (Gast)


Lesenswert?

>Hört sich an wie: man lügt sich was in die Tasche...
>Warum sollte da ein Vergleicher gespart werden?
>Was ist denn eigentlich dein Ziel?

OK, man spart keinen Vergleicher, sondern separiert Zähler und 
Komparator derart, dass die kombinatorische Logik kleiner wird.


>if cnt>=cMAX-1 then

ACK



>Was ist denn eigentlich dein Ziel?

Kombinatorik verkleinern und somit fMAX steigern.


Grüsse, Harry

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


Lesenswert?

Harry schrieb:
> OK, man spart keinen Vergleicher, sondern separiert Zähler und
> Komparator derart, dass die kombinatorische Logik kleiner wird.
Nein. Sie bleibt genau gleich. Denn es ist ja auch der selbe 
Vergleicher (es steht ja auch genau der selbe VHDL-code da)...

Harry schrieb:
> Kombinatorik verkleinern und somit fMAX steigern.
Du kannst mit dem Einfügen von zusätzlichen Registern und dem Aktivieren 
von "Register Balancing" eventuell Taktfrequenz gegen Latenz 
eintauschen. Aber dein Vergleicher wird in der Summe niemals 
"schneller":
Wenn der 8 Logikebenen hat, dann kannst du z.B. in die 4. Ebene einen 
Zwischenspeicher einfügen und dadurch die Taktfrequenz (fast) 
verdoppeln. Dein Vergelich ist dann aber erst nach 2 Takten fertig....

von Harry (Gast)


Lesenswert?

>Nein. Sie bleibt genau gleich

Das ist so aber nicht richtig, denn ich generiere mir ein getaktetes
Enable-Signal und verwende dieses statt des Komparators für den 
Zählvorgang.
Durch das Enable-Signal habe ich die Kombinatorik gesplittet.

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


Lesenswert?

Harry schrieb:
> Das ist so aber nicht richtig, denn ich generiere mir ein getaktetes
> Enable-Signal und verwende dieses statt des Komparators für den
> Zählvorgang.
> Durch das Enable-Signal habe ich die Kombinatorik gesplittet.
Hast du dir den RTL-Plan mal angeschaut? Dieses zusätzliche Flipflop 
sitzt einfach hinter dem gleichen Vergleicher und bringt dir 1 Takt 
Latency. Sonst ändert sich nichts.

von Harry (Gast)



Lesenswert?

Habe mal einen Screenshot gemacht von Synplify: Man sieht dass bei 
Verwendung von "ena" die Kombinatorik gesplittet wird.

von Harry (Gast)


Lesenswert?

Beide Schaltungen sollten funktional gleich sein.

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


Lesenswert?

Harry schrieb:
> dass bei Verwendung von "ena" die Kombinatorik gesplittet wird.
In der Praxis sieht so ein Zähler aber ganz anders aus, weil dieser 
Addierer einfach als Carryeingang im FPGA realisiert wird, und daher 
keine zusätzliche Logikebene erfordert. Dies wird vom Synthesetool hier 
nur unzureichend abgebildet...

von Harry (Gast)


Lesenswert?

Hier noch der VHDL-Code, mit dem ich das getestet habe
1
library ieee;
2
use ieee.std_logic_1164.all;
3
use ieee.numeric_std.all;
4
5
6
entity komparator is
7
port(
8
  reset : in  std_logic;
9
  clk : in  std_logic;
10
  sig : out std_logic
11
);
12
end komparator;
13
14
15
16
architecture rtl of komparator is
17
18
signal cnt : unsigned(15 downto 0);
19
signal ena : std_logic;
20
21
begin
22
23
process(reset, clk)
24
begin
25
  if reset = '1' then
26
    cnt <= (others => '0');
27
    ena <= '0';
28
  
29
  elsif rising_edge(clk) then
30
    if (ena = '1') then  -- Splittung der Kombinatorik
31
      cnt <= cnt + 1;
32
    end if;
33
    
34
    -- if cnt < 2**16-1 then-- Komparator und zähler zusammen
35
      -- cnt <= cnt + 1;
36
    -- end if;
37
    
38
    if cnt >= 2**16-2 then
39
      ena <= '1';
40
    else
41
      ena <= '0';
42
    end if;
43
  end if;
44
end process;
45
46
47
sig <= cnt(12) when rising_edge(clk);
48
49
50
end architecture;

von Harry (Gast)


Lesenswert?

>In der Praxis sieht so ein Zähler aber ganz anders aus, weil dieser
>Addierer einfach als Carryeingang im FPGA realisiert wird

Mit welchem Tool kann man sich die Praxis im FPGA denn anschauen?

von Harry (Gast)


Lesenswert?

Interessantes Timing-Ergebnis:


Variante alles in einem Takt:

397.614 MHz, 3 Logiklevel


Variante gesplittet:

486.618 MHz, 8 Logiklevel



fMAX-Constraint: 300MHz
FPGA: Lattice ECP3(70er) Speedgrade 8

von Lattice User (Gast)


Lesenswert?

Harry schrieb:
> Beide Schaltungen sollten funktional gleich sein.

Sind sie aber nicht, in der 2. Variante wird der Zähler einen Takt 
später gestoppt. Und das ist genau das was Lothar meint, fMax gegen 
Latency getauscht.

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


Angehängte Dateien:

Lesenswert?

Harry schrieb:
> Mit welchem Tool kann man sich die Praxis im FPGA denn anschauen?
Das ist der RTL-Schaltplan. Wie du den bekommst, das kommt auf den 
Synthesizer an. Mit XST geht es seit der Version 13 gar nicht mehr: das 
Ding vergisst einfach, die Module miteinander zu verbinden.. :-(

Dann kommt mit dieser Beschreibung nur noch sowas raus wie im Anhang...
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
entity cto is
6
    Port ( clk : in  STD_LOGIC;
7
           o   : out STD_LOGIC_VECTOR(7 downto 0)
8
          );
9
end cto;
10
11
architecture Behavioral of cto is
12
  signal cnt : integer := 0;
13
  constant cMAX : integer := 8;
14
begin
15
16
  process(clk)
17
  begin
18
    if rising_edge(clk) then
19
        if cnt/=cMAX then
20
           cnt <= cnt + 1;
21
        end if;
22
    end if;
23
  end process;
24
25
  o <= std_logic_vector(to_unsigned(cnt,8));
26
27
end Behavioral;

Lattice User schrieb:
> fMax gegen Latency getauscht.
Richtig, und das kann man machen, solange man noch einen Takt "Luft" 
hat.

von Harry (Gast)


Lesenswert?

Die funktionale Simulation zeigt mir, dass beide Varianten bei 0xFFFF 
aufhören zu zählen.



1
library ieee;
2
use ieee.std_logic_1164.all;
3
4
5
entity tb_komparator is
6
end entity;
7
8
architecture testbench of tb_komparator is
9
10
11
signal clk : std_logic := '0';
12
signal reset : std_logic := '1';
13
14
begin
15
16
17
18
uut: entity work.komparator
19
port map(
20
  reset => reset,
21
  clk => clk,
22
  sig => open
23
);
24
25
26
27
28
bCLK: block
29
begin
30
  clk <= not clk after 10 ns;
31
end block;
32
33
34
bRST: block
35
begin
36
  reset <= '0' after 33 ns;
37
end block;
38
39
40
end architecture;



Kleine Korrektur noch im eigentlichen Modul:
1
if cnt >= 2**16-2 then
2
    ena <= '0';
3
else
4
    ena <= '1';
5
end if;

von Harry (Gast)


Angehängte Dateien:

Lesenswert?

Hier ein Modelsim-Screenshot.


Leicht modifizierter Code (cnt2 hinzugekommen):
1
library ieee;
2
use ieee.std_logic_1164.all;
3
use ieee.numeric_std.all;
4
5
6
entity komparator is
7
port(
8
  reset : in  std_logic;
9
  clk : in  std_logic;
10
  sig : out std_logic
11
);
12
end entity;
13
14
15
16
architecture rtl of komparator is
17
18
signal cnt : unsigned(15 downto 0);
19
signal cnt2 : unsigned(15 downto 0);
20
signal ena : std_logic := '0';
21
22
begin
23
24
process(reset, clk)
25
begin
26
  if reset = '1' then
27
    cnt <= (others => '0');
28
    cnt2 <= (others => '0');
29
    ena <= '1';
30
  
31
  elsif rising_edge(clk) then
32
    if (ena = '1') then          -- Splittung der Kombinatorik
33
      cnt <= cnt + 1;
34
    end if;
35
    
36
    
37
    if cnt2 < 2**16-1 then      -- Komparator und zähler zusammen
38
      cnt2 <= cnt2 + 1;
39
    end if;
40
    
41
42
    
43
    if cnt >= 2**16-2 then
44
      ena <= '0';
45
    else
46
      ena <= '1';
47
    end if;
48
  end if;
49
end process;
50
51
52
sig <= cnt(12) when rising_edge(clk);
53
54
55
end architecture;

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


Lesenswert?

Harry schrieb:
> if cnt2 < 2**16-1 then      -- Komparator und zähler zusammen
>     if cnt >= 2**16-2 then
Ja. Das ist Latency...  ;-)

Es macht übrigens in der Realität auch was aus, ob du das schreibst:
  if cnt2 <  2**16-1 then
Oder das:
  if cnt2 != 2**16-1 then
Und das, obwohl die Funktion hier exakt gleich ist: es wird gezählt 
solange cnt kleiner als 65535 ist.

von Harry (Gast)


Lesenswert?

Fakt ist: "cnt" und "cnt2" verhalten sich identisch.

Fakt ist auch: Die "cnt"-Variante kann ein höheres fmax fahren.


Ergo: Für Performance ist die cnt-Variante mit Enable die Bessere.


Grüsse, Harry

von Harry (Gast)


Lesenswert?

Manchmal muss man einen Schritt zurücktreten ;-)

von Sigi (Gast)


Lesenswert?

Harry hat zwar Latenz erzeugt (und idR auch mehr Logik+FF),
bei ZMAX>>1 ist das mit der Latenz aber egal, wei's nur
zählerintern gebraucht wird. Und viel schneller lässt's sich
auch takten (Stopp hängt nur noch von einem Signal ab).

x /= y bzw. x < y:
bei modernen FPGAs (Xilinx,Altera CarryChains) ist das bei
idealer Implementierung gleich aufwendig, Synthesetools
sollte es aber auch können.

von Lattice User (Gast)


Lesenswert?

Harry schrieb:
> Ergo: Für Performance ist die cnt-Variante mit Enable die Bessere.

Streitet niemand ab.
Man muss nur die Latency beachten, ich habe übersehen dass du genau 
deswegen auf einen anderen Endwert vergleichst.

IMO geht noch mehr:
Keinen < Vergleich verwenden um die Komplexität des Vergleichs zu 
reduzieren.
Beim Resetwert des Zählers kompensieren und nur auf Überlauf abfragen (1 
bit vs viele).

Im Technology View von Synplify kann man sehen wie das ganze auf die 
FPGA Primitiven abgebildet wird.

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


Lesenswert?

Sigi schrieb:
> x /= y bzw. x < y:
> bei modernen FPGAs (Xilinx,Altera CarryChains) ist das bei
> idealer Implementierung gleich aufwendig
Leider nein. Probiers aus. Da war letzthin wieder mal ein Thread zum 
Thema, und ich habe das selbe behauptet. Naja, Pech... ;-)

von Sigi (Gast)


Lesenswert?

@Lothar Miller,

>> x /= y bzw. x < y:
>..
> Leider nein...

doch, zumindestens bei Xilinx Spartan3/Virtex4 (LUT4-CarryChain)
und Virtex5 (LUT5/6+CarryChain) bin ich mir sicher, habe mal die
CarryChain-Logik in eine Gleichung gefasst um das max. Mögliche
für mich zu ermitteln. Daraus habe ich dann die Gleichungen für
eine Menge von Operationen (ADD/SUB/CMP etc.) ermittelt. Vorteil
von CMP ist ausserdem, dass gleichzeitig le LUT 2Bits mit 2Bits
verglichen werden können (sozusagen braucht man nur die
"halbe Länge" an CarryChain).

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


Lesenswert?

Sigi schrieb:
> doch, zumindestens bei Xilinx Spartan3/Virtex4 (LUT4-CarryChain)
> und Virtex5 (LUT5/6+CarryChain) bin ich mir sicher
Für welche Synthese (Version)?

> habe mal die CarryChain-Logik in eine Gleichung gefasst um das
> max. Mögliche für mich zu ermitteln.
Hat der Synthesizer das kapiert?

von Sigi (Gast)


Lesenswert?

Mein Ansatz funktioniert ab ISE 5.1 (?), 6.1 sicher.
Allerdings benutze ich das Synthesetool nur für
die Instanziierung der Spartan3/Virtex4-Komonenten
(LUT4/FF/CARRYCHAIN etc.), d.h. die komplette Logik
muss von Hand erstellt werden (geht aber sehr leicht,
insb. generisch). Angewendet habe ich es z.B. in einer
eigenen CPU, die im Virtex4 (10er Speedgrade) mit
400 MHz läuft.

Von ISE selber kannst du eine solche Performance nicht
erwarten, selbst Harry's Ansatz mit dem vorgezogenen
Vergleich liefert eine laue Leistung.
Das zeigt mal wieder, wie mittelmässig ISE (Quartus
kenne ich kaum) einfach zu erkennende Konstrukte in
die Fabrikeigene Logik giessen kann.

Gruss

von Dani (Gast)


Lesenswert?

Welches Synthesetol verwendest Du?

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.