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


von Michl (Gast)


Angehängte Dateien:

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

von Michl (Gast)


Angehängte Dateien:

Lesenswert?

Und hier noch der Code.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Versuch es mal so:
1
TIMER: process
2
    begin
3
        wait until rising_edge(clk);
4
        if res='0' or sig='0' then --Zähler rücksetzen
5
            count <= (others=>'0');
6
        else
7
            if sig='1' then
8
                count <= count + 1; --Zähler für Sync
9
                count_bit<= (others=>'0'); --Zähler rücksetzen
10
            else
11
                count_bit <= count_bit+1; --Zähler für Current
12
            end if;
13
        end if;
14
 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.

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


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.

1
TIMER: process(clk, res)
2
begin
3
  if res='0' or sig='0' then  -- asynchroner kombinatorischer Reset !!!
4
    count<=(others=>'0');
5
  elsif clk='1' and clk'event and sig='1' then    -- 1. Takt
6
    count<=(count+1); --Zähler für Sync
7
  end if;
8
9
  if clk='1' and clk'event and sig='0' then       -- 2. Takt
10
    count_bit<=(count_bit+1); --Zähler für Current
11
  end if;
12
13
  if sig='1' then             -- asynchroner Reset !!!               
14
    count_bit<=(others=>'0'); --Zähler rücksetzen
15
  end if;
16
17
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
1
use IEEE.STD_LOGIC_ARITH.ALL;
2
use IEEE.STD_LOGIC_UNSIGNED.ALL;
sondern nur noch die herstellerunabhängige
1
use IEEE.NUMERIC_STD.ALL;

von Michl (Gast)


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

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


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:
1
signal sigsr  : std_logic_vector(1 downto 0);
2
:
3
process begin -- Einsynchronisieren
4
   wait until rising_edge(clk);
5
   -- Schieberegister
6
   sigsr <= sigsr(0) & sig;
7
end process;
sigsr(1) ist hier synchron zum FPGA-Takt.


Hier die kompakteste Variante für die Flankenauswertung:
1
signal sigsr  : std_logic_vector(2 downto 0);
2
:
3
process begin
4
   wait until rising_edge(clk);
5
   -- Schieberegister
6
   sigsr <= sigsr(1 downto 0) & sig;
7
   if (sigsr="100") then 
8
      -- fallende Flanke von sig
9
   end if;
10
   if (sigsr="011") then 
11
      -- steigende Flanke von sig
12
   end if;
13
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
1
   if (sigsr(2)='1' and sigsr(1)='0') then
erkannt. Schöner liest und schreibt sich aber
1
   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
1
-- Takt clk = 50MHz
2
signal sigsr  : std_logic_vector(2 downto 0);
3
signal cnt    : integer range 0 to 50000000 := 0;
4
signal idx    : integer range 0 to 58 := 0;
5
signal bits   : std_logic_vector(58 downto 0);
6
signal bitsout : std_logic_vector(58 downto 0);
7
:
8
process begin
9
   wait until rising_edge(clk);
10
   -- Einsynchronisieren
11
   sigsr <= sigsr(1 downto 0) & sig;
12
13
   -- steigende Flanke von sig
14
   if (sigsr="011") then 
15
      cnt <= 0;
16
      if (cnt=50000000) then -- Sekunde 59 war da?
17
         idx <= 0;           -- ja: wieder von vorn
18
         bitsout <= bits;    -- die gesammelten Daten uebergeben
19
      else
20
         if (idx<58) then    -- nein: Index hochzaehlen
21
            idx <= idx+1;
22
         end if;
23
      end if;
24
   end if;
25
26
   -- fallende Flanke von sig
27
   if (sigsr="100") then 
28
      if (cnt<7500000) then -- kurzer Impuls? Grenze 150ms
29
         bits(idx) <= '0';  -- ja: 0 eintragen
30
      else
31
         bits(idx) <= '1';  -- nein: 1 eintragen
32
      end if;      
33
   end if;
34
35
   -- Zaehler hochzzaehlen (sättigend bei 50000000)
36
   if (cnt<50000000) then 
37
      cnt <= cnt+1; 
38
   end if;
39
end process;
Das kann man durch den Multiplexer bits(idx) schön lesen, ich würde aber 
zu Ressourcenschonung eher eine Schieberegisterlösung vorziehen:
1
-- Takt clk = 50MHz
2
signal sigsr  : std_logic_vector(2 downto 0);
3
signal cnt    : integer range 0 to 50000000 := 0;
4
signal bits   : std_logic_vector(58 downto 0);
5
signal bitsout : std_logic_vector(58 downto 0);
6
:
7
process begin
8
   wait until rising_edge(clk);
9
   -- Einsynchronisieren
10
   sigsr <= sigsr(1 downto 0) & sig;
11
12
   -- steigende Flanke von sig
13
   if (sigsr="011") then 
14
      cnt <= 0;
15
      if (cnt=7500000) then     -- Sekunde 59 war da?
16
         bitsout <= bits;       -- die gesammelten Daten uebergeben
17
         bits <= (others=>'0'); -- Bit-Schiebegregister zuruecksetzen
18
      else
19
      end if;
20
   end if;
21
22
   -- fallende Flanke von sig
23
   if (sigsr="100") then 
24
      if (cnt<20000000) then           -- kurzer Impuls?
25
         bits <= '0'&bits(58 dowto 1); -- ja: 0 von links einschieben
26
      else
27
         bits <= '1'&bits(58 dowto 1); -- nein: 1 von links einschieben
28
      end if;      
29
   end if;
30
31
   -- Zaehler hochzzaehlen (sättigend bei 50000000)
32
   if (cnt<50000000) then 
33
      cnt <= cnt+1; 
34
   end if;
35
end process;
Was jetzt noch fehlt ist z.B. eine Fehlerbehandlung bei Bitfehler...
Aber da kannst du auf dem bits-Vektor leicht machen.

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


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:
1
-- Takt clk = 50MHz
2
signal sigsr  : std_logic_vector(2 downto 0);
3
signal cnt    : integer range 0 to 50000000 := 0;
4
signal idx    : integer range 0 to 58 := 0;
5
signal bits   : std_logic_vector(58 downto 0);
6
signal bitsout : std_logic_vector(58 downto 0);
7
:
8
process begin
9
   wait until rising_edge(clk);
10
   -- Einsynchronisieren
11
   sigsr <= sigsr(1 downto 0) & sig;
12
13
   -- Zaehler hochzzaehlen (sättigend bei 60000000)
14
   if (cnt<60000000) then 
15
      cnt <= cnt+1; 
16
   end if;
17
18
   -- steigende Flanke von sig
19
   if (sigsr="011") then 
20
      cnt <= 0;              -- Zaehler zuruecksetzen
21
      if (cnt=60000000) then -- Sekunde 59 war da?
22
         idx <= 0;           -- ja: wieder von vorn
23
         bitsout <= bits;    -- die gesammelten Daten uebergeben
24
      else
25
         if (idx<58) then    -- nein: Index hochzaehlen
26
            idx <= idx+1;
27
         end if;
28
      end if;
29
   end if;
30
31
   -- fallende Flanke von sig
32
   if (sigsr="100") then 
33
      if (cnt<7500000) then -- kurzer Impuls? Grenze 150ms
34
         bits(idx) <= '0';  -- ja: 0 eintragen
35
      else
36
         bits(idx) <= '1';  -- nein: 1 eintragen
37
      end if;      
38
   end if;
39
end process;

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


Angehängte Dateien:

Lesenswert?

Und hier die Schieberegistervariante:
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
6
entity DCF77 is
7
    Port ( clk : in  STD_LOGIC;
8
           sig : in  STD_LOGIC;
9
           bitsout : out  STD_LOGIC_VECTOR (58 downto 0));
10
end DCF77;
11
12
architecture Behavioral of DCF77 is
13
signal sigsr  : std_logic_vector(2 downto 0);
14
signal cnt    : integer range 0 to 60000000 := 0;
15
signal bits   : std_logic_vector(58 downto 0):= (others=>'0');
16
begin
17
   process begin
18
      wait until rising_edge(clk);
19
      -- Einsynchronisieren
20
      sigsr <= sigsr(1 downto 0) & sig;
21
   
22
      -- erst Zaehler hochzzaehlen (sättigend bei 60000000)
23
--      if (cnt<60000000) then 
24
      if (cnt<60) then ---- für Simulation kürzer
25
         cnt <= cnt+1; 
26
      end if;
27
      
28
      -- steigende Flanke von sig
29
      if (sigsr="011") then 
30
         cnt <= 0;
31
--         if (cnt=60000000) then -- Sekunde 59 war da?
32
         if (cnt=60) then ---- für Simulation kürzer
33
            bits <= (others=>'0'); -- Bit-Schiebegregister zuruecksetzen
34
            bitsout <= bits;    -- die gesammelten Daten übergeben
35
         end if;
36
      end if;
37
   
38
      -- fallende Flanke von sig
39
      if (sigsr="100") then 
40
--         if (cnt<7500000) then -- kurzer Impuls? Grenze 150ms
41
         if (cnt<7) then ---- für Simulation kürzer
42
            bits <= '0'&bits(58 downto 1); -- ja: 0 von links einschieben
43
         else
44
            bits <= '1'&bits(58 downto 1); -- nein: 1 von links einschieben
45
         end if;      
46
      end if;
47
   
48
   end process;
49
end Behavioral;

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

von Michl (Gast)


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).

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


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)
1
:
2
   -- Takt
3
   clk <= not clk after 10 ms;
4
:
und die geänderten Zählerabfragen
1
:
2
--         if (cnt=60000000) then
3
         if (cnt=60) then ---- für Simulation um den Faktor 1000000 kürzer
4
:
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.

von Michl (Gast)


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!

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

In der Codesammlung gibt es ein "lowcost" Flash mit uC.

von Falk B. (falk)


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

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.