Forum: FPGA, VHDL & Co. Anfänger Taktgenerator


von Nico M. (rharennon)


Lesenswert?

Hallo Zusammen

Da ich ein VHDL-Anfänger bin (aber nicht bleiben will :-) ) möchte ich 
euch hier mal meinen kleinen Frequenzteiler vorstellen und hoffe darauf, 
dass ihr mir vielleicht ein paar Tipps und Tricks nennen könnt, wie ich 
die Schaltung eleganter/effizienter schreiben könnte.

Zum Projekt, ich habe ein Altera DE0 Board mit einem Cyclone III. Das 
Board hat einen internen 50MHz-Takt mit welchem ich verschiedene 
Frequenzen aufbereiten will.
Ich habe mich jetzt für für 7 verschiedene Frequenzen von 10Hz bis 10MHz 
entschieden (siehe Code) welche ich mit verschieden Zählschritten 
erreiche.
Das Programm funktioniert so grundsätzlich ohne Probleme, compilieren 
und auf das Board laden funktioniert... ebenfalls kann ich die Signale 
auf dem KO anschauen, bis auf den 10MHz-Takt sehen die sogar alle 
einigermassen gut aus. (leichte Überschwinger sind aber bei allen 
Frequenzen zu erkennen.)

Hat irgendwer ein paar gute Inputs was ich an diesem Code evtl. noch 
verbessern/anpassen kann?
Bitte keine Posts wie: nicht zu viele Frequenzen in einem Projekt 
verwenden etc. -- das weiss ich selbst, mir gehts hier auch nur darum 
den Syntax von VHDL kennen zu lernen und ein wenig damit zu spielen. :-P

Vielen Dank schon im Voraus

Gruess
Nico

Hier der Code:

------------Wahrheitstabelle und Pinbelegung----------------
--Ein-/Ausgänge  A  B  C  |  Z        Pin's
--        0  0  0  |  10Hz      AA20
--        0  0  1  |  100Hz      AB20
--        0  1  0   |   1kHz      AB19
--        0  1  1  |  10kHz      AA18
--        1  0  0  |  100kHz      Ab17
--        1  0  1  |  1MHz      W17
--        1  1  0  |  10MHz      T15
--        1  1  1  |  Fehler Led0
--              |  leuchtet    J1
-------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity Takt_Geni is
 port
 (
  Set      :  in std_logic_vector(2 downto 0);
  clk_50MHz   :   in std_logic;
  clk_10Hz    :   out std_logic;
  clk_100Hz  :  out std_logic;
  clk_1kHz  :  out std_logic;
  clk_10kHz  :  out std_logic;
  clk_100kHz  :  out std_logic;
  clk_1MHz  :  out std_logic;
  clk_10MHz  :  out std_logic;
  fehler    :  out  std_logic
 );
end Takt_Geni;

architecture Behaviour of Takt_Geni is
 constant count_limit_10Hz     : integer := 5000000;   --1/50MHz x 
5000000 = 100ms -->   10Hz
 constant count_limit_100Hz    : integer := 500000;   --1/50MHz x 500000 
= 10ms   -->   100Hz
 constant count_limit_1kHz    : integer := 50000;   --1/50MHz x 50000 
= 1ms   -->   1kHz
 constant count_limit_10kHz    : integer := 5000;     --1/50MHz x 5000 
= 100us -->   10kHz
 constant count_limit_100kHz  : integer := 500;     --1/50MHz x 500    = 
10us   -->   100kHz
 constant count_limit_1MHz    : integer := 50;     --1/50MHz x 50    = 
1us   -->   1MHz
 constant count_limit_10MHz    : integer := 5;       --1/50MHz x 5    = 
100ns -->   10MHz
 signal cnt                 : integer range 0 to count_limit_10Hz :=0;
 signal clk_100ms           : std_logic :='0';
 signal clk_10ms           : std_logic :='0';
 signal clk_1ms          : std_logic :='0';
 signal clk_100us        : std_logic :='0';
 signal clk_10us        : std_logic :='0';
 signal clk_1us          : std_logic :='0';
 signal clk_100ns        : std_logic :='0';
 signal set_fehler        : std_logic :='0';

begin
 process (clk_50MHz)
  begin
   if (rising_edge(clk_50MHz)) then
   cnt <= cnt + 1;
   if (cnt = count_limit_10Hz) and (Set = "000") then
    cnt <=0;
    clk_100ms <= not clk_100ms;
    set_fehler <= '0';
   end if;
   if (cnt = count_limit_100Hz) and (Set = "001") then
    cnt <=0;
    clk_10ms <= not clk_10ms;
    set_fehler <= '0';
   end if;
   if (cnt = count_limit_1kHz) and (Set ="010") then
    cnt <= 0;
    clk_1ms <= not clk_1ms;
    set_fehler <= '0';
   end if;
   if (cnt = count_limit_10kHz) and (Set ="011") then
    cnt <= 0;
    clk_100us <= not clk_100us;
    set_fehler <= '0';
   end if;
   if (cnt = count_limit_100kHz) and (Set ="100") then
    cnt <= 0;
    clk_10us <= not clk_10us;
    set_fehler <= '0';
   end if;
   if (cnt = count_limit_1MHz) and (Set ="101") then
    cnt <= 0;
    clk_1us <= not clk_1us;
    set_fehler <= '0';
   end if;
   if (cnt = count_limit_10MHz) and (Set ="110") then
    cnt <= 0;
    clk_100ns <= not clk_100ns;
    set_fehler <= '0';
   end if;
    if (Set ="111") then
    cnt <= 0;
    set_fehler <= '1';
   end if;
  end if;
  end process;
  clk_10Hz     <= clk_100ms;
  clk_100Hz   <= clk_10ms;
  clk_1kHz     <= clk_1ms;
  clk_10kHz    <= clk_100us;
  clk_100kHz  <= clk_10us;
  clk_1MHz    <= clk_1us;
  clk_10MHz    <= clk_100ns;
  fehler     <= set_fehler;
end Behaviour;

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


Lesenswert?

Warum teilst du jede Frequenz mit einem eigenen Zähler? Sinnvoller wäre 
hier doch ein gestufter Teiler:

Eingang 50MHz
:5
10MHz
:10
1MHz
:10
100kHz
:10
10kHz
usw...

Auf diese Art bekommst du alle Frequenzen gleichzeitig und musst nur 
noch einen Multiplexer dahinterschalten.

BTW: aus den 50MHz wirst du nur asymmetrische 10MHz (TV 3:5 oder 2:5) 
bekommen...

von Nico M. (rharennon)


Lesenswert?

Hmmm, danke für den Input habe das jetzt mal versucht aber irgendwie 
scheitere ich an den integer-Variabeln...

Mein Code sieht jetzt wie folgt aus:

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;

entity Takt_GeniV2 is
 port
 (
  clk_50MHz   :   in   std_logic;
  clk_10Hz    :   out integer;
  clk_100Hz  :  out integer;
  clk_1kHz  :  out integer;
  clk_10kHz  :  out integer;
  clk_100kHz  :  out integer;
  clk_1MHz  :  out integer;
  clk_10MHz  :  out integer
 );
end Takt_GeniV2;

architecture Behaviour of Takt_GeniV2 is
 constant count_limit_10Hz     : integer := 5000000;   --1/50MHz x 
5000000 = 100ms -->   10Hz
 constant teiler_5        : integer := 5;      --Teilverhältnis 5
 signal cnt                 : integer range 0 to count_limit_10Hz :=0;
 signal clk_100ms           : integer := 0;
 signal clk_10ms           : integer := 0;
 signal clk_1ms          : integer := 0;
 signal clk_100us        : integer := 0;
 signal clk_10us        : integer := 0;
 signal clk_1us          : integer := 0;
 signal clk_100ns        : integer := 0;

begin
 process (clk_50MHz)
  begin
   if (rising_edge(clk_50MHz)) then
   cnt <= cnt + 1;
   if (cnt = count_limit_10Hz) then
    cnt <=0;
    clk_100ms   <= clk_100ms;
    clk_10ms   <= (clk_100ms   / teiler_5);
    clk_1ms    <= (clk_10ms   / teiler_5);
    clk_100us  <= (clk_1ms    / teiler_5);
    clk_10us  <= (clk_100us  / teiler_5);
    clk_1us    <= (clk_10us  / teiler_5);
    clk_100ns  <= (clk_1us    / teiler_5);
   end if;
  end if;
  end process;
  clk_10Hz     <= clk_100ms;
  clk_100Hz   <= clk_10ms;
  clk_1kHz     <= clk_1ms;
  clk_10kHz    <= clk_100us;
  clk_100kHz  <= clk_10us;
  clk_1MHz    <= clk_1us;
  clk_10MHz    <= clk_100ns;
end Behaviour;

Das Problem ist jetzt, dass die Ausgänge in integer sind (weil ich sonst 
nicht durch 5 teilen kann). Das heisst am Ausgang meines Moduls sind 
jetzt Ausgänge mit einer Bitbreite von 32. Ich habe mit den Befehlen 
"to_bit" und "to_stdulogic" rumprobiert aber da hat er mir immer diverse 
Fehler ausgegeben.

Kann ich nicht einfach sagen clk_10Hz to_stdulogic(integer)???
Hoffentlich nerv ich jetzt nicht zu stark mit "Basic-Probleme" aber ich 
komm da irgendwie nicht weiter...?

Gruess
Nico

von Nico M. (rharennon)


Lesenswert?

Ups, ich merk gerade der Teiler müsste ja durch 10 teilen...
:-P

von Duke Scarring (Gast)


Lesenswert?

Nico M. schrieb:
> Hmmm, danke für den Input habe das jetzt mal versucht aber irgendwie
> scheitere ich an den integer-Variabeln...
...

1. Bibliotheken
> library ieee;
> use ieee.std_logic_1164.all;
> use ieee.std_logic_unsigned.all;
Die std_logic_unsigned fliegt bitte raus. Die ist obsolet. Siehe hier: 
Beitrag "IEEE.STD_LOGIC_ARITH.ALL obsolete"

Rechnen kann man auch ohne Bibliothek mit integer. Wenn man die Werte 
irgendwie an den Pins baucht (als Eingang und/oder als Ausgang), dann 
verwendet man die numeric_std-Bibliothek zum Konvertieren.
Schön grafisch dargestellt ist das hier:
http://www.lothar-miller.de/s9y/categories/16-Numeric_Std


2. Taktteilung
>     clk_100ms   <= clk_100ms;
>     clk_10ms   <= (clk_100ms   / teiler_5);
Du bist hier auf einem FPGA und nicht auf einem Mikrocontroller.
Du mußt objektorientiert Denken. Ein FPGA ist ein riesiges Steckbrett 
mit Look-up-Tables und Flip-Flops (1-Bit-Speicher). Die kannst Du frei 
miteinander verdrahten. AND, OR, XOR, NOT etc. lässt sich direkt in den 
Loop-up-Tables abbilden. Darauf bauen Addierer, Subtrahierer, 
Multiplexer u.ä. auf. Für Schieberegister, Zähler und State-Machines 
werden die Flip-Flops gebraucht.

Zeitliche Zusammenhänge werden mit Zählern abgebildet, die mit dem 
Systemtakt arbeiten (und es gibt nur einen Systemtakt!).


3. divide-et-impera
Du hast zwei Teilprobleme:
* die Signalgenerierung
* die Signalselektion
Trenne sie!
Für das erste Problem nimmt man in Deinem Fall einen Taktteiler (bzw. 
mehrere hintereinander) und für das Zweite einen Multiplexer.

Duke

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


Lesenswert?

Nico M. schrieb:
> Ups, ich merk gerade der Teiler müsste ja durch 10 teilen...
Ja, hoffentlich anhand einer Simulation. Genau dieses simple Design 
lässt sich nämlich ganz einfach elementar simulieren: Takt rein und 
Bildchen angucken...

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.