www.mikrocontroller.net

Forum: FPGA, VHDL & Co. DCF-Signal auswerten


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

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich bin gerade dabei, eine Funkuhr mit einem Spartan 3 zu bauen. Das 
Signal liegt demoduliert und invertiert am Port des FPGA.
Aktuell kann ich bereits die 59. Sekunde sowie das aktuelle Bit 
erkennen.

Jetzt bin ich allerdings bereits seit über einer Woche am überlegen, wie 
ich das ausgewertete Signal in ein 60-Bit Schieberegister bekomme.
Sämtliche bisherige Versuche und Ideen waren leider nicht 
synthetisierbar (bad syncronous description) oder haben nicht 
funktioniert.

Über ein paar Vorschläge oder Anregungen wäre ich sehr dankbar!

Anbei noch der aktuelle VHDL Code sowie ein kleines Diagramm.


mfg

Michl

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

Bewertung
0 lesenswert
nicht lesenswert
Und hier noch der Code.

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Versuch es mal so:
TIMER: process
    begin
        wait until rising_edge(clk);
        if res='0' or sig='0' then --Zähler rücksetzen
            count <= (others=>'0');
        else
            if sig='1' then
                count <= count + 1; --Zähler für Sync
                count_bit<= (others=>'0'); --Zähler rücksetzen
            else
                count_bit <= count_bit+1; --Zähler für Current
            end if;
        end if;
 end process;
In deiner ursprünglichen Beschreibung lief ziemlich was durcheinander, 
u.A. war dein einer Zähler nicht abhängig vom Takt und wurde da 
zurückgesezt.
Ich würde nicht versuchen Takt und Enable/Disable/etc... in eine Abfrage 
zusammenzufassen, sonder geerell erstmal auf den Takt warten und dann 
belibiege ABfragen starten dann kann (fast) nix schiefgehen.

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

Bewertung
0 lesenswert
nicht lesenswert
> Dateianhang: VHDL.txt (1,5 KB, 8 Downloads)
VHDL-Dateien enden auf *.vhd
Dann werden sie schön formatiert und syntaxgehighlightet angezeigt ;-)

> elsif clk='1' and clk'event and sig='1' then
sig ist ein Clock-enable.
Der Synthesizer kann solche Konstrukte zwar schon umsetzen, die 
Schreibweise ist trotzdem unüblich und sollte vermieden werden :-o
Ich habe das dort mal zusammengefasst:
http://www.lothar-miller.de/s9y/categories/6-Clock-Enable

>   if res='0' or sig='0' then
Keine Kombinatorik im Resetpfad.
Keine asynchronen Signale wie z.B. sig im Resetpfad.

TIMER: process(clk, res)
begin
  if res='0' or sig='0' then  -- asynchroner kombinatorischer Reset !!!
    count<=(others=>'0');
  elsif clk='1' and clk'event and sig='1' then    -- 1. Takt
    count<=(count+1); --Zähler für Sync
  end if;

  if clk='1' and clk'event and sig='0' then       -- 2. Takt
    count_bit<=(count_bit+1); --Zähler für Current
  end if;

  if sig='1' then             -- asynchroner Reset !!!               
    count_bit<=(others=>'0'); --Zähler rücksetzen
  end if;

end process;
Du solltest nicht in einem Prozess mehrere Takte beschreiben.

Du mußt dein sig vor der Verwendung auf den clk einsynchronisieren. 
Das geht am einfachsten über ein Schieberegister.

Mach Zähler am besten mit integer, das kann man besser lesen.

Die Sensitivity-List ist unvollständig, da fehlt sig.
In einem getakteten Prozess findest du bei mir nur den Clock in der 
Sensitivity-Liste, oder gleich gar nichts, weils mit
wait until rising_edge realisiert ist.

Schreib statt
> if clk='1' and clk'event then
besser
> if rising_edge(clk) then
Oder machs gleich so, wie Läubi das schon beschrieben hat.

Verwende nicht die alten Libs
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
sondern nur noch die herstellerunabhängige
use IEEE.NUMERIC_STD.ALL;

Autor: Michl (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für die schnellen Antworten. Ich werde den Code entsprechend 
überarbeiten, sobald ich wieder Zugang zum EvalBoard habe.
Mit

wait until rising_edge(clk)

warte ich also auf eine steigende Flanke von clk und kann dann beliebige 
Signale auswerten.

Das mit dem Einsynchronisieren des Taktes auf das Signal würde mich 
näher interessieren. Wie gehe ich da am besten vor? Man könnte ja dann 
das Bitmuster wieder durch einen Zähler abfragen und dann die 
entsprechende Stelle in den 60-Bit-Vektor laden. Also z.B. Zählerstart 
bei rising_edge von sig (welches dann synchron mit dem Takt steigt), 
wenn Zählerstand > X eine log. Eins, sonst Null. Bei Zählerstand X+1 
dann die Signalzuweisung in den Vektor.
Was ist davon zu halten? Ich hatte mir sowas bereits überlegt, jedoch 
bestand eben das Problem, dass sig und clk nicht synchron laufen.
Und es stellt sich die Frage nach dem Reset dieses Zählers. Man müsste 
ihn also bei rising_edge von sig zurücksetzen. Eventuell würde es 
funktionieren, dass er bei Zählerstand == 50e6 (entspricht einer Sekunde 
bei 50 MHz) zurückgesetzt wird. Direkt bei der Flanke zurücksetzen und 
dann gleichzeitig laufen lassen bis sig wieder Null wird habe ich bis 
jetzt noch nicht hinbekommen.

Deine Seite ist übrigens sehr informativ, werde ich gleich am Wochenende 
mal durcharbeiten ;)


mfg


Michl

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

Bewertung
0 lesenswert
nicht lesenswert
> Deine Seite ist übrigens sehr informativ,
Dort sammle ich genau das Zeug, das ich selber von überall her immer 
wieder brauche ;-)

> werde ich gleich am Wochenende mal durcharbeiten ;)
dann wirst du auch die Einsynchronisierung und Flankenerkennung finden.

> wait until rising_edge(clk)
> ... bei rising_edge von sig ...
Nein, du hast im Idealfall (und davon solltest du nicht unnötig 
weggehen) nur einen Master-Takt im FPGA (so im Bereich 10-100MHz). Zu 
diesem ist dein Design synchron. Alle Signale, die von aussen kommen, 
werden erst über 2 FF-Stufen einsynchronisiert (Stichwort 
Metastabilität) und dann intern verwendet. Am einfachsten geht das über 
ein Schieberegister:
signal sigsr  : std_logic_vector(1 downto 0);
:
process begin -- Einsynchronisieren
   wait until rising_edge(clk);
   -- Schieberegister
   sigsr <= sigsr(0) & sig;
end process;
sigsr(1) ist hier synchron zum FPGA-Takt.


Hier die kompakteste Variante für die Flankenauswertung:
signal sigsr  : std_logic_vector(2 downto 0);
:
process begin
   wait until rising_edge(clk);
   -- Schieberegister
   sigsr <= sigsr(1 downto 0) & sig;
   if (sigsr="100") then 
      -- fallende Flanke von sig
   end if;
   if (sigsr="011") then 
      -- steigende Flanke von sig
   end if;
end process;
Hier kommt 1 FF als Pegelmerker dazu.
sigsr(2) merkt sich den alten Pegel vom einsynchronisierten sigsr(1), 
und wenn z.B. das sigsr(2) = '1' ist und zudem sigsr(1) = '0', dann hat 
sich der Pegel des Eingangssignals von 1 nach 0 gewechselt.
Ein fallende Flanke würde demnach mit
   if (sigsr(2)='1' and sigsr(1)='0') then 
erkannt. Schöner liest und schreibt sich aber
   if (sigsr="100") then 


Diese DCF-Geschichte könnte ich mir etwa so vorstellen:
- Mastertakt z.B. 50HMz
- Einsynchronisieren von sig
- einen sättigenden Zähler für 1 Sekunde
  (wegen Erkennung von Sekunde 59)
- bei der steigenden Flanke von sig wird der Zähler zurückgesetzt
  und der Index entweder auf 0 gesetzt (wenn der Zähler aufgelaufen ist)
  oder hochgezählt
- bei der fallenden Flanke von sig wird geschaut,
  wie weit der Zähler gelaufen ist und dementsprechend eine 0 oder 1
  an den Index eingetragen
-- Takt clk = 50MHz
signal sigsr  : std_logic_vector(2 downto 0);
signal cnt    : integer range 0 to 50000000 := 0;
signal idx    : integer range 0 to 58 := 0;
signal bits   : std_logic_vector(58 downto 0);
signal bitsout : std_logic_vector(58 downto 0);
:
process begin
   wait until rising_edge(clk);
   -- Einsynchronisieren
   sigsr <= sigsr(1 downto 0) & sig;

   -- steigende Flanke von sig
   if (sigsr="011") then 
      cnt <= 0;
      if (cnt=50000000) then -- Sekunde 59 war da?
         idx <= 0;           -- ja: wieder von vorn
         bitsout <= bits;    -- die gesammelten Daten uebergeben
      else
         if (idx<58) then    -- nein: Index hochzaehlen
            idx <= idx+1;
         end if;
      end if;
   end if;

   -- fallende Flanke von sig
   if (sigsr="100") then 
      if (cnt<7500000) then -- kurzer Impuls? Grenze 150ms
         bits(idx) <= '0';  -- ja: 0 eintragen
      else
         bits(idx) <= '1';  -- nein: 1 eintragen
      end if;      
   end if;

   -- Zaehler hochzzaehlen (sättigend bei 50000000)
   if (cnt<50000000) then 
      cnt <= cnt+1; 
   end if;
end process;
Das kann man durch den Multiplexer bits(idx) schön lesen, ich würde aber 
zu Ressourcenschonung eher eine Schieberegisterlösung vorziehen:
-- Takt clk = 50MHz
signal sigsr  : std_logic_vector(2 downto 0);
signal cnt    : integer range 0 to 50000000 := 0;
signal bits   : std_logic_vector(58 downto 0);
signal bitsout : std_logic_vector(58 downto 0);
:
process begin
   wait until rising_edge(clk);
   -- Einsynchronisieren
   sigsr <= sigsr(1 downto 0) & sig;

   -- steigende Flanke von sig
   if (sigsr="011") then 
      cnt <= 0;
      if (cnt=7500000) then     -- Sekunde 59 war da?
         bitsout <= bits;       -- die gesammelten Daten uebergeben
         bits <= (others=>'0'); -- Bit-Schiebegregister zuruecksetzen
      else
      end if;
   end if;

   -- fallende Flanke von sig
   if (sigsr="100") then 
      if (cnt<20000000) then           -- kurzer Impuls?
         bits <= '0'&bits(58 dowto 1); -- ja: 0 von links einschieben
      else
         bits <= '1'&bits(58 dowto 1); -- nein: 1 von links einschieben
      end if;      
   end if;

   -- Zaehler hochzzaehlen (sättigend bei 50000000)
   if (cnt<50000000) then 
      cnt <= cnt+1; 
   end if;
end process;
Was jetzt noch fehlt ist z.B. eine Fehlerbehandlung bei Bitfehler...
Aber da kannst du auf dem bits-Vektor leicht machen.

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

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag:
der obige Code funktioniert noch nicht ganz richtig.
Zwei Fehler sind drin, ein Denkfehler mit der 59.Sekunde. Hier muß der 
Zahler mehr als 1 Sekunde zählen können, damit das Fehlen einer Sekunde 
erkannt werden kann :-o
Also statt 50000000 einfach 60000000 schreiben.

Der andere ist ein Ablauf-Schlampigkeitsfehler.
Der Zähler cnt muß am Anfang des Prozesses hochgezählt werden, denn die 
letzte Zuweisung im Prozess gewinnt. Beim oben geposteten Code wird also 
jede 2. Sekunde ausgelassen, weil das Zurücksetzen überschrieben wird. 
:-/


So klappt die Simulation:
-- Takt clk = 50MHz
signal sigsr  : std_logic_vector(2 downto 0);
signal cnt    : integer range 0 to 50000000 := 0;
signal idx    : integer range 0 to 58 := 0;
signal bits   : std_logic_vector(58 downto 0);
signal bitsout : std_logic_vector(58 downto 0);
:
process begin
   wait until rising_edge(clk);
   -- Einsynchronisieren
   sigsr <= sigsr(1 downto 0) & sig;

   -- Zaehler hochzzaehlen (sättigend bei 60000000)
   if (cnt<60000000) then 
      cnt <= cnt+1; 
   end if;

   -- steigende Flanke von sig
   if (sigsr="011") then 
      cnt <= 0;              -- Zaehler zuruecksetzen
      if (cnt=60000000) then -- Sekunde 59 war da?
         idx <= 0;           -- ja: wieder von vorn
         bitsout <= bits;    -- die gesammelten Daten uebergeben
      else
         if (idx<58) then    -- nein: Index hochzaehlen
            idx <= idx+1;
         end if;
      end if;
   end if;

   -- fallende Flanke von sig
   if (sigsr="100") then 
      if (cnt<7500000) then -- kurzer Impuls? Grenze 150ms
         bits(idx) <= '0';  -- ja: 0 eintragen
      else
         bits(idx) <= '1';  -- nein: 1 eintragen
      end if;      
   end if;
end process;

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Und hier die Schieberegistervariante:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;


entity DCF77 is
    Port ( clk : in  STD_LOGIC;
           sig : in  STD_LOGIC;
           bitsout : out  STD_LOGIC_VECTOR (58 downto 0));
end DCF77;

architecture Behavioral of DCF77 is
signal sigsr  : std_logic_vector(2 downto 0);
signal cnt    : integer range 0 to 60000000 := 0;
signal bits   : std_logic_vector(58 downto 0):= (others=>'0');
begin
   process begin
      wait until rising_edge(clk);
      -- Einsynchronisieren
      sigsr <= sigsr(1 downto 0) & sig;
   
      -- erst Zaehler hochzzaehlen (sättigend bei 60000000)
--      if (cnt<60000000) then 
      if (cnt<60) then ---- für Simulation kürzer
         cnt <= cnt+1; 
      end if;
      
      -- steigende Flanke von sig
      if (sigsr="011") then 
         cnt <= 0;
--         if (cnt=60000000) then -- Sekunde 59 war da?
         if (cnt=60) then ---- für Simulation kürzer
            bits <= (others=>'0'); -- Bit-Schiebegregister zuruecksetzen
            bitsout <= bits;    -- die gesammelten Daten übergeben
         end if;
      end if;
   
      -- fallende Flanke von sig
      if (sigsr="100") then 
--         if (cnt<7500000) then -- kurzer Impuls? Grenze 150ms
         if (cnt<7) then ---- für Simulation kürzer
            bits <= '0'&bits(58 downto 1); -- ja: 0 von links einschieben
         else
            bits <= '1'&bits(58 downto 1); -- nein: 1 von links einschieben
         end if;      
      end if;
   
   end process;
end Behavioral;

Im Anhang die Testbench. Für eine schnellere Simulation habe ich die 
Zeiten ein wenig angepasst ;-)

Autor: Michl (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,


vielen Dank für Deine ausführlichen Antworten! Ich werde beide Varianten 
testen, sobald ich wieder Zugang zum EvalBoard habe!
Welchen Simulationszeitraum würdest Du für die angepassten Zeiten 
empfehlen? Eine Simulation des normalen Codes von 0-100 sek. habe ich 
bis jetzt noch nicht zustande gebracht (zu wenig Speicher, trotz 4 GB 
RAM).

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

Bewertung
0 lesenswert
nicht lesenswert
> Welchen Simulationszeitraum würdest Du für die angepassten Zeiten
> empfehlen?
Durch das angepasste Taktsignal in der Testbench
(50Hz statt 50MHz, also um den Faktor 1000000 langsamer)
:
   -- Takt
   clk <= not clk after 10 ms;
:
und die geänderten Zählerabfragen
:
--         if (cnt=60000000) then
         if (cnt=60) then ---- für Simulation um den Faktor 1000000 kürzer
:
kann ich ohne Probleme und blitzschnell die 3 Minuten durchsimulieren.

Es geht hier wie gesagt um eine Funktionskontrolle durch eine 
Verhaltenssimulation. Und da darf ich solche Annahmen ohne Einschränkung 
treffen.

Denn durch das Einsynchronisieren des DCF-Signals habe ich alle 
Unwägbarkeiten aus dem Taktdomanenübergang (DCF-Takt nach FPGA-Takt) 
herausgenommen.

Autor: Michl (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo nochmal,

ich habe das ganze heute auf dem FPGA zum Laufen gebracht. Es 
funktioniert alles so, wie es soll. Ich habe jetzt noch den 
Sekundenzähler eingebaut sowie ein paar kosmetische Änderungen.
Im Moment läuft das ganze auf dem Entwicklungsboard. Jetzt würde ich das 
ganze natürlich gerne als Stand-Alone-Hardware laufen lassen (mit dem 
DCF-Empfänger von Reichelt).
Habe diesbezüglich an den Spartan3E gedacht, da dieser ja aus einem SPI 
Flash booten kann. Gibt es bei der Beschaltung des FPGA etwas besonderes 
zu beachten (Kondensatoren nahe an den Pins)? Soweit ich das jetzt 
mitbekommen habe, müssen die Spannungen des FPGA in einer bestimmten 
Reihenfolge geschaltet werden. Design werd ich mal mit Eagle entwerfen 
(max. 2 Lagen, also zum selbst basteln). Programmieren kann ich den FPGA 
selbst mit dem Parallel Cable und den Flash mit einem normalen JTAG? 
Welcher Flash ist empfehlenswert? Die von Xilinx selbst sollen ja schwer 
zu bekommen sein.
Dann ist der Empfänger wahrscheinlich einige Meter (ca. 10) vom FPGA 
entfernt. Ich werde erst eine direkte Übertragung versuchen. Ansonsten 
eben ein Leitungstreiber (u/i-Schaltung eines OpAmp).

Nochmals vielen Dank!

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In der Codesammlung gibt es ein "lowcost" Flash mit uC.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Michl (Gast)

>(max. 2 Lagen, also zum selbst basteln). Programmieren kann ich den FPGA
>selbst mit dem Parallel Cable und den Flash mit einem normalen JTAG?

Ja.

>Welcher Flash ist empfehlenswert? Die von Xilinx selbst sollen ja schwer
>zu bekommen sein.

Low Cost FPGA Konfiguration

>Dann ist der Empfänger wahrscheinlich einige Meter (ca. 10) vom FPGA
>entfernt. Ich werde erst eine direkte Übertragung versuchen. Ansonsten
>eben ein Leitungstreiber (u/i-Schaltung eines OpAmp).

???
Ein einfacher CMOS Puffer mit 74HC04 reicht dicke.

MFG
Falk

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.