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
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.
> 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; |
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
> 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.
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; |
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 ;-)
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).
> 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.
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!
@ 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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.