Forum: FPGA, VHDL & Co. Bitte um Hilfe bei Taktteilerprogrammierung


von Marcus (Gast)


Angehängte Dateien:

Lesenswert?

Hallo!

Ich stehe im Moment vor der Aufgabe, einen programmierbaren Taktteiler 
zu schreiben, der mir leider große Probleme bereitet. Grundsätzlich soll 
er so arbeiten, wie es die Simulation von meinem bisherigen Code 
darstellt: Ausgehend von einem 275MHz Takt soll jeweils nach einer, von 
einem Teiler (DIVISOR) einstellbaren Taktzahl ein Impuls erzeugt werden 
(CLK_DIV). Der Impuls muss vier Takte des 275MHz Clocks lang High sein. 
Ein zweites Signal (SEL) muss mit der fallenden Flanke des CLK_DIV 
Signals auf Low gehen, mit der steigenden Flanke des CLK_DIV Signals 
wieder auf High.

Der Gedanke hinter meinem bisherigen Code ist, dass kombinatorisch das 
nächste Signal vorbereitet und dann durch einen getakteten Prozess 
übernommen wird. Das Funktioniert bisher in der Simulation und auch auf 
dem FPGA - jedoch nur bis etwa 200MHz. Darüber hinaus macht das SEL 
Signal nur noch mist. Das Codefragment:
1
elsif (next_cnt(2) = '1') and (sel_cnt = '0') then
scheint durch laufzeitunterschiede nicht mehr richtig zu arbeiten, 
weswegen das CLK_DIV Signal praktisch mit im SEL Signal auftaucht. Ich 
habe leider kein Bild um das Verhalten zu zeigen.

Wie müsste ich den Taktteiler umsetzen, damit er "sauber" ist, also 
nicht mehr von laufzeitunterschieden abhängig ist? Klar, CE Eingang von 
FFs, aber ehrlich gesagt komm ich auch mit der Info nicht weiter.

Ich würde mich über jede Antwort freuen.

Viele Grüße, Marcus
1
library ieee;
2
use ieee.std_logic_1164.all;
3
use ieee.numeric_std.all;
4
5
6
entity CLKDIV is
7
  port(CLK_275      : in  std_logic;
8
       RST          : in  std_logic;
9
       CLK_DIV      : out std_logic;
10
       SEL          : out std_logic;
11
       EN_CLK_DIV   : in  std_logic;
12
       EN_SEL       : in  std_logic;
13
       DIVIDER      : in  std_logic_vector(9 downto 0)
14
  );
15
end CLKDIV;
16
17
18
architecture BEHAVE of CLKDIV is
19
20
signal cnt          : unsigned(9 downto 0);
21
signal next_cnt     : unsigned(9 downto 0);
22
signal div          : unsigned(9 downto 0);
23
signal next_clk_div : std_logic;
24
signal next_sel     : std_logic;
25
signal sel_cnt      : std_logic;
26
signal next_sel_cnt : std_logic;
27
28
begin
29
30
    process(CLK_275)
31
    begin
32
      if rising_edge(CLK_275) then
33
        if RST = '1' then
34
          cnt <= (others => '0');
35
          div <= unsigned (DIVIDER);
36
        else
37
          cnt <= next_cnt;
38
          if next_cnt = 0 then
39
             div <= unsigned (DIVIDER);
40
          end if;
41
        end if;
42
      end if;
43
    end process;
44
45
    process (cnt, next_cnt, div)
46
    begin
47
      if cnt = (div-1) then
48
         next_cnt <= (others => '0');
49
      else
50
         next_cnt <= cnt + 1;
51
      end if;
52
    end process;
53
54
55
    process(CLK_275)
56
    begin
57
      if rising_edge(CLK_275) then
58
        if RST = '1' then
59
          CLK_DIV <= '0';
60
        else
61
          CLK_DIV <= next_clk_div;
62
        end if;
63
      end if;
64
    end process;
65
66
    process (RST, next_clk_div, next_cnt, EN_CLK_DIV)
67
    begin
68
      if RST = '1' then
69
        next_clk_div <= '0';
70
      elsif (next_cnt = 0) then
71
        next_clk_div <= EN_CLK_DIV;
72
      elsif (next_cnt(2) = '1') then
73
        next_clk_div <= '0';
74
      end if;
75
    end process;
76
77
78
    process(CLK_275)
79
    begin
80
      if rising_edge(CLK_275) then
81
        if RST = '1' then
82
          SEL <= '1';
83
          sel_cnt <= '0';
84
        else
85
          SEL <= next_sel;
86
          sel_cnt <= next_sel_cnt;
87
        end if;
88
      end if;
89
    end process;
90
91
    process (RST, next_cnt, sel_cnt, EN_SEL)
92
    begin
93
      if RST = '1' then
94
          next_sel_cnt <= '0';
95
          next_sel <= '1';
96
        else
97
          if (next_cnt = 0) then
98
            next_sel_cnt <= not sel_cnt;
99
          else
100
            next_sel_cnt <= sel_cnt;
101
          end if;
102
          if (next_cnt = 0) then
103
            next_sel <= '1';
104
          elsif (next_cnt(2) = '1') and (sel_cnt = '0') then
105
            next_sel <= not EN_SEL;
106
          end if;
107
        end if;
108
    end process;
109
110
end;

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


Lesenswert?

> Ein zweites Signal (SEL) muss mit der fallenden Flanke des CLK_DIV
> Signals auf Low gehen, mit der steigenden Flanke des CLK_DIV Signals
> wieder auf High.
Lies dir den Satz nochmal genau durch.
Nach dieser Beschreibung wären die beiden Signale genau gleich...   :-/

> auch auf dem FPGA
Welches FPGA?

> jedoch nur bis etwa 200MHz.
200MHz ist schnell. Das Wort "nur" ist hier fehl am Platz.
Du könntest u.U. von der Gigahertz-Manie bei PCs fehlgeleitet sein...
Wenn es dir zu langsam ist, mußt du den kritischen Pfad herausfinden und 
überarbeiten. Dazu verwendest du am besten die statische Timing-Analyse.

>  Ausgehend von einem 275MHz Takt
Woher kommt der Takt?

von Marcus (Gast)


Lesenswert?

@Lothar Miller

>Lies dir den Satz nochmal genau durch.

Ach mist - ja, da hast du natürlich recht. Was ich meine ist wohl am 
Besten am Screenshot der Simulation zu erkennen. Also bei der fallenden 
Flanke von einem CLK_DIV Impuls auf Low, bei der steigenden Flanke des 
nächsten Impulses wieder High, fallende Flanke des nächsten Impulses 
wieder Low und so weiter.

>Welches FPGA?

Spartan 3

>200MHz ist schnell. Das Wort "nur" ist hier fehl am Platz.
>Du könntest u.U. von der Gigahertz-Manie bei PCs fehlgeleitet sein...

Klar ist das extrem schnell und das ich darauf zurück greifen musste hat 
mir von Anfang an Bauchschmerzen bereitet. Ohne jetzt ins Detail zu 
gehen bin ich gezwungen mit einem so hohen Takt arbeiten zu müssen. Er 
wird aus 25MHz mit einem DCM erzeugt.

>Wenn es dir zu langsam ist, mußt du den kritischen Pfad herausfinden und
>überarbeiten

Den problematischen Pfad habe ich ja im Prinzip schon rausgefunden 
(siehe Ursprungsposting). Die Frage ist, wie ich diese Bedingung anders 
beschreiben kann, damit sie schneller läuft - bzw. ob die gesamte 
Herangehensweise vielleicht murks ist und ich die Sache neu aufsetzen 
sollte, und wenn ja, wie.

von Marcus (Gast)


Lesenswert?

@Lothar Miller

Wenn ich dich schon am Wickel habe ;)
Ich lese hier schon länger und meistens passiv mit und wollte nur mal 
sagen, dass ich's toll finde, das jemand mit Erfahrung sich die Zeit 
nimmt anfängern unter die Arme zu greifen und Hilfestellung zu geben. Du 
schreibst hier so regelmäßig rein, dass ein dickes Danke angesagt ist.

von Jan M. (mueschel)


Lesenswert?

Deine Beschreibung von next_sel und next_clk_div erzeugt Latche, das 
solltest du auf alle Fälle beheben.


An ein paar Stellen kannst du die Geschwindigkeit auf Kosten der Größe 
auch noch erhöhen.
> if cnt = (div-1)
Hier könntest du div-1 schon einen Takt vorher berechnen und das 
Ergebnis in ein Register packen.

Außerdem benutzt du
> if next_cnt = 0 then
Hier solltest du besser gleich das praktisch äquivalente "cnt = (div-1)" 
benutzen, das sollte ebenfalls Zeit sparen.

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


Lesenswert?

> auch auf dem FPGA - jedoch nur bis etwa 200MHz.
> Darüber hinaus macht das SEL Signal nur noch mist.
Bis zu welcher Geschwindigkeit sagt dir der Report, das das gehen 
sollte?
Hast du Timing-Constraints gesetzt?

Wenn ich deinen Code synthetisiere, erhalte ich
1
--   Minimum period: 8.455ns (Maximum Frequency: 118.271MHz)
2
--   Minimum input arrival time before clock: 3.723ns
3
--   Maximum output required time after clock: 6.216ns

Das ist aber noch ein gutes Stück weg, von dem was du willst :-o

Ich habe deine Beschreibung mal in meine Schreibweise gebracht:
1
library ieee;
2
use ieee.std_logic_1164.all;
3
use ieee.numeric_std.all;
4
5
entity Taktteiler is
6
  port(CLK_275      : in  std_logic;
7
       RST          : in  std_logic; -- wozu?
8
       CLK_DIV      : out std_logic;
9
       SEL          : out std_logic;
10
       EN_CLK_DIV   : in  std_logic;
11
       EN_SEL       : in  std_logic;
12
       DIVIDER      : in  std_logic_vector(9 downto 0)
13
  );
14
end Taktteiler;
15
16
17
architecture BEHAVE of Taktteiler is
18
19
signal cnt          : unsigned(9 downto 0) := (others=>'0');
20
signal clksr        : unsigned(3 downto 0) := (others=>'0');
21
signal toggle       : std_logic := '0';
22
23
begin
24
25
  process begin 
26
     wait until rising_edge(clk_275);
27
     if cnt > 0 then
28
        cnt   <= cnt-1;
29
        clksr <= clksr(2 downto 0) & '0';
30
     else
31
        cnt   <= unsigned(DIVIDER);
32
        clksr <= "1111";
33
     end if;
34
  end process;
35
  CLK_DIV <= clksr(3);
36
37
  process begin 
38
     wait until rising_edge(clk_275);
39
     if (toggle='1' and clksr="1000") then -- jedes zweite Mal zurücksetzen
40
        SEL    <= '0';
41
     end if;
42
     if (cnt=0) then -- setzen, wenn cnt=0, schlimmstenfalls wirds doppelt gesetzt
43
        SEL    <= '1';
44
        toggle <= not toggle;
45
     end if;
46
  end process;
47
  
48
end Behave;
und erhalte vom Fleck weg ein deutlich schnelleres Design:
1
--   Minimum period: 6.019ns (Maximum Frequency: 166.143MHz)
2
--   Minimum input arrival time before clock: 5.341ns
3
--   Maximum output required time after clock: 6.280ns

Überleg dir, ob du diese Signale in der Kombinatorik brauchst:
       EN_CLK_DIV   : in  std_logic;
       EN_SEL       : in  std_logic;
Das bringt dir schnell mal eine Lut mehr in den Logikpfad, und das 
kannst du bei 275MHz nicht brauchen...



BTW:
An deinem Zwei-Prozess-Zähler-Code sieht man schön, wie schnell man 
damit ein Latch an der Backe hat.

von Natan (Gast)


Lesenswert?

Nimm ne rekonfigurierbare PLL

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


Lesenswert?

> Nimm ne rekonfigurierbare PLL
Was ist das?
Konkret, mit Beispiel bitte.

von Marcus (Gast)


Lesenswert?

@ Lothar Miller und Jan M.

Vielen Dank für eure Ratschläge und besonderen Dank an Lothar Miller für 
deine - zugegeben weitaus "elegantere" - Version! Ich setze mich nochmal 
daran auf Basis von Lothars Code das Modul neu zu schreiben und dann mit 
(bisher nicht gesetzten) Timing Constraint zu optimieren.

Ich wünsche noch einen schönen Abend :)

viele Grüße, Marcus

von Jan M. (mueschel)


Lesenswert?

Ich habe gerade nochmal einen kurzen Blick auf Lothars Code geworfen: 
Mit einem up- statt dem down-counter wird es schneller (197MHz, Spartan 
3) - das Laden der Register mit einem variablen Wert ist aufwändiger als 
ein 10bit Vergleicher, der dann gebraucht wird.

Auch interessant: Der Virtex4 kann hier sein schnelleres Routing voll 
ausspielen und schafft fast die doppelte Geschwindigkeit: 379 MHz.

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


Lesenswert?

> Ich habe gerade nochmal einen kurzen Blick auf Lothars Code geworfen
Ich auch, und dabei noch das Schieberegister rausgeschmissen, das bringt 
hier nichts, der kritische Pfad liegt woanders (nämlich in der 
Verdrahtung).
Mit diesem Code
1
architecture BEHAVE of Taktteiler is
2
signal cnt          : unsigned(9 downto 0) := (others=>'0');
3
signal toggle       : std_logic := '0';
4
begin
5
   process begin 
6
      wait until rising_edge(clk_275);
7
      if (cnt < unsigned(DIVIDER)) then
8
         cnt   <= cnt+1;
9
      else
10
         cnt   <= (others=>'0');
11
      end if;
12
   end process;
13
  
14
   process begin 
15
      wait until rising_edge(clk_275);
16
      if (toggle='1' and cnt=4) then
17
         SEL    <= '0';
18
      end if;
19
      if (cnt=4) then 
20
         CLK_DIV <= '0';
21
      end if;
22
      if (cnt=0) then -- setzen, wenn cnt=0
23
         SEL     <= '1';
24
         CLK_DIV <= '1';
25
         toggle  <= not toggle;
26
      end if;
27
   end process;
28
29
end Behave;
komme ich bei Spartan 3 Speedgrade -5 auf den von Jan angesprochenen 
Takt:
1
   Minimum period: 5.056ns (Maximum Frequency: 197.791MHz)

Ein Virtex 2P mit Speedgrade -7 bringt
1
   Minimum period: 3.450ns (Maximum Frequency: 289.830MHz)

Ein Spartan 3A mit Speedgrade -5 kommt auf
1
   Minimum period: 3.956ns (Maximum Frequency: 252.803MHz)
Aber knapp daneben ist auch vorbei :-/

von Antti (Gast)


Lesenswert?

275 mit S3 sollte harter broken sein:

habe physikalisch in FPGA gemessen

S3 (normal speedgrade) bei 375MHz ist fabric TOTAL tot
fur V4 ist das aber 975MHz

275Mhz beduetet ja nicht das design mit 275 laufen muss
aber mit 300+, da temp/vcc toleranzen und CLOCK JITTER
ja auch beruksichigt werden muss.

375MHz in S3 ging wirklich nur "bis nachste LUT" nicht viel
weiter, wenn mit nahe 300mhz ein zahler und noch was laufen
sollte ist schon ganz gut nahe zu allerlezten grenze
(mindestens niegrige speedgrade)

Antti

von Christian R. (supachris)


Lesenswert?

Lothar Miller schrieb:

> komme ich bei Spartan 3 Speedgrade -5 auf den von Jan angesprochenen
> Takt:
>
1
>    Minimum period: 5.056ns (Maximum Frequency: 197.791MHz)
2
>
>
> Ein Virtex 2P mit Speedgrade -7 bringt
>
1
>    Minimum period: 3.450ns (Maximum Frequency: 289.830MHz)
2
>
>
> Ein Spartan 3A mit Speedgrade -5 kommt auf
>
1
>    Minimum period: 3.956ns (Maximum Frequency: 252.803MHz)
2
>
> Aber knapp daneben ist auch vorbei :-/

Naja, das sind doch die Ausgaben der Synthese oder? Eventuell schafft 
der P&R noch etwas Optimierung und wird dann schneller. Sehe ich bei 
unseren Designs oft hier.

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


Lesenswert?

> Eventuell schafft der P&R noch etwas Optimierung und wird dann schneller.
Auch schon rumgespielt, bringt ein paar ps, aber das Problem ist das 
Routing, das ist so dermaßen langsam. Mit maximalem 
Optimierungseinstellungen komme ich beim S3 auf 4.495ns = 222 MHz.

von youssef (Gast)


Lesenswert?

hallo
hast du an schiebe registergedacht ?
es kann auch dafür geeignet sein du kannst ein bit am Anfang des 
registers legen und in eine beliebige stelle wieder emfangen und den 
wiederum als clk benutzen

von Michael O. (mischu)


Lesenswert?

Ich habe einen 32Bit Zähler im Spartan3AN implementiert, der bei 200MHz 
läuft.

Im Prinzip habe ich die Aufgabe heruntergebrochen auf jeweils 
4Bit-Zähler, mit einer Art Look-Ahead Technik. Braucht mehr rescourcen, 
ist aber der einzige Weg, wenn der Vergleicher zu langsam ist.

Jeder 4Bit-Zähler ist wie ein klassischer Zähler aufbegaut. Das Carry 
Out jedoch wird am Ausgang nochmals zwischengepuffert. Die Erzeugung des 
Carry out habe ich so angepasst, dass einen Takt vor dem Ablauf schon 
"Expired" erzeugt wird. Anschließend noch schnell per Generate in ein 
Counter der Breite 4xN umgestöpselt.

Code finde ich leider gerade nicht.

von Michael O. (mischu)


Lesenswert?

Stimmt, kannst auch eine MLS bauen mit Schieberegister.
Müsstest dann per Lookup deinen Countwert umbauen,
und auf ein bestimmtes Wort warten um den Counter neu zu laden.

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.