www.mikrocontroller.net

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


Autor: Marcus (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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:
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
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;


entity CLKDIV is
  port(CLK_275      : in  std_logic;
       RST          : in  std_logic;
       CLK_DIV      : out std_logic;
       SEL          : out std_logic;
       EN_CLK_DIV   : in  std_logic;
       EN_SEL       : in  std_logic;
       DIVIDER      : in  std_logic_vector(9 downto 0)
  );
end CLKDIV;


architecture BEHAVE of CLKDIV is

signal cnt          : unsigned(9 downto 0);
signal next_cnt     : unsigned(9 downto 0);
signal div          : unsigned(9 downto 0);
signal next_clk_div : std_logic;
signal next_sel     : std_logic;
signal sel_cnt      : std_logic;
signal next_sel_cnt : std_logic;

begin

    process(CLK_275)
    begin
      if rising_edge(CLK_275) then
        if RST = '1' then
          cnt <= (others => '0');
          div <= unsigned (DIVIDER);
        else
          cnt <= next_cnt;
          if next_cnt = 0 then
             div <= unsigned (DIVIDER);
          end if;
        end if;
      end if;
    end process;

    process (cnt, next_cnt, div)
    begin
      if cnt = (div-1) then
         next_cnt <= (others => '0');
      else
         next_cnt <= cnt + 1;
      end if;
    end process;


    process(CLK_275)
    begin
      if rising_edge(CLK_275) then
        if RST = '1' then
          CLK_DIV <= '0';
        else
          CLK_DIV <= next_clk_div;
        end if;
      end if;
    end process;

    process (RST, next_clk_div, next_cnt, EN_CLK_DIV)
    begin
      if RST = '1' then
        next_clk_div <= '0';
      elsif (next_cnt = 0) then
        next_clk_div <= EN_CLK_DIV;
      elsif (next_cnt(2) = '1') then
        next_clk_div <= '0';
      end if;
    end process;


    process(CLK_275)
    begin
      if rising_edge(CLK_275) then
        if RST = '1' then
          SEL <= '1';
          sel_cnt <= '0';
        else
          SEL <= next_sel;
          sel_cnt <= next_sel_cnt;
        end if;
      end if;
    end process;

    process (RST, next_cnt, sel_cnt, EN_SEL)
    begin
      if RST = '1' then
          next_sel_cnt <= '0';
          next_sel <= '1';
        else
          if (next_cnt = 0) then
            next_sel_cnt <= not sel_cnt;
          else
            next_sel_cnt <= sel_cnt;
          end if;
          if (next_cnt = 0) then
            next_sel <= '1';
          elsif (next_cnt(2) = '1') and (sel_cnt = '0') then
            next_sel <= not EN_SEL;
          end if;
        end if;
    end process;

end;


Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Marcus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Marcus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Jan M. (mueschel)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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
--   Minimum period: 8.455ns (Maximum Frequency: 118.271MHz)
--   Minimum input arrival time before clock: 3.723ns
--   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:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity Taktteiler is
  port(CLK_275      : in  std_logic;
       RST          : in  std_logic; -- wozu?
       CLK_DIV      : out std_logic;
       SEL          : out std_logic;
       EN_CLK_DIV   : in  std_logic;
       EN_SEL       : in  std_logic;
       DIVIDER      : in  std_logic_vector(9 downto 0)
  );
end Taktteiler;


architecture BEHAVE of Taktteiler is

signal cnt          : unsigned(9 downto 0) := (others=>'0');
signal clksr        : unsigned(3 downto 0) := (others=>'0');
signal toggle       : std_logic := '0';

begin

  process begin 
     wait until rising_edge(clk_275);
     if cnt > 0 then
        cnt   <= cnt-1;
        clksr <= clksr(2 downto 0) & '0';
     else
        cnt   <= unsigned(DIVIDER);
        clksr <= "1111";
     end if;
  end process;
  CLK_DIV <= clksr(3);

  process begin 
     wait until rising_edge(clk_275);
     if (toggle='1' and clksr="1000") then -- jedes zweite Mal zurücksetzen
        SEL    <= '0';
     end if;
     if (cnt=0) then -- setzen, wenn cnt=0, schlimmstenfalls wirds doppelt gesetzt
        SEL    <= '1';
        toggle <= not toggle;
     end if;
  end process;
  
end Behave;
und erhalte vom Fleck weg ein deutlich schnelleres Design:
--   Minimum period: 6.019ns (Maximum Frequency: 166.143MHz)
--   Minimum input arrival time before clock: 5.341ns
--   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.

Autor: Natan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nimm ne rekonfigurierbare PLL

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Nimm ne rekonfigurierbare PLL
Was ist das?
Konkret, mit Beispiel bitte.

Autor: Marcus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Jan M. (mueschel)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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
architecture BEHAVE of Taktteiler is
signal cnt          : unsigned(9 downto 0) := (others=>'0');
signal toggle       : std_logic := '0';
begin
   process begin 
      wait until rising_edge(clk_275);
      if (cnt < unsigned(DIVIDER)) then
         cnt   <= cnt+1;
      else
         cnt   <= (others=>'0');
      end if;
   end process;
  
   process begin 
      wait until rising_edge(clk_275);
      if (toggle='1' and cnt=4) then
         SEL    <= '0';
      end if;
      if (cnt=4) then 
         CLK_DIV <= '0';
      end if;
      if (cnt=0) then -- setzen, wenn cnt=0
         SEL     <= '1';
         CLK_DIV <= '1';
         toggle  <= not toggle;
      end if;
   end process;

end Behave;
komme ich bei Spartan 3 Speedgrade -5 auf den von Jan angesprochenen 
Takt:
   Minimum period: 5.056ns (Maximum Frequency: 197.791MHz)

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

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

Autor: Antti (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Christian R. (supachris)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lothar Miller schrieb:

> komme ich bei Spartan 3 Speedgrade -5 auf den von Jan angesprochenen
> Takt:
>
>    Minimum period: 5.056ns (Maximum Frequency: 197.791MHz)
> 
>
> Ein Virtex 2P mit Speedgrade -7 bringt
>
>    Minimum period: 3.450ns (Maximum Frequency: 289.830MHz)
> 
>
> Ein Spartan 3A mit Speedgrade -5 kommt auf
>
>    Minimum period: 3.956ns (Maximum Frequency: 252.803MHz)
> 
> 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.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: youssef (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Michael O. (mischu)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Michael O. (mischu)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.