Hallo, ich moechte mit einem FPGA eine 64-stellige Bitfolgen ausgeben. Die Bitlaenge soll 100ns betragen. Sonst ist das Signal high. Die Ausgabe soll getriggert erfolgen. Also benoetige ich ein Modul, mit zwei Eingaengen und einem Ausgang. Die Bitfolge soll im VHDL code als Konstante hinterlegt werden. Jetzt weis ich nicht so recht wie ich anfangen soll. Ich habe zwei Ansaetze um die ich mich erstal kuemmern werde: 1. Einen Parallel-Reihen Umsetzer, wo die Eingaenge mit konstanten Signalen belegt sind. Wie ich diesen allerdings triggerbar mache, weis ich noch nicht. 2. Einen Zaehler nehmen und eine LUT. Bei der LUT trage ich dann meine gewuenschte Bitfolge ein und ich benoetige einen Zaehler, der beim Triggersignal einmal durchlaeuft und dann wieder auf den naechsten Triggerimpuls warted. Geht das vielleicht auch einfacher, wie wurdet Ihr das machen?
Warum nicht einen UART oder SPI core nehmen und gescheit verschalten & parametretisieren?
Blubb schrieb: > Warum nicht einen UART oder SPI core nehmen und gescheit verschalten & > parametretisieren? Darueber bin ich auch schon gestolpert. Im Prinzip suche ich sowas. Ich bin aber Anfaenger, was VHDL betrifft und aus den Beispielen kann ich mir die Funktion nicht immer komplett herleiten. Deswegen suche ich was Einfaches und Nachvollziebares.
Eines koennte man noch erwaehnen. Im folgenden Schritt moechte ich die Ausgangssignale auch 32 aufbohren.
qnd-snippets und ohne Garantie auf Tippfehler die wesentlichen Teilfunktionalitäten.
1 | --Deklaration counter und ROM
|
2 | |
3 | constant ROM is std_logic_vector(63 downto 0) := x"FFFFFFFFFFFFFFFF"; |
4 | subype T_count_idx is natural range 63 downto 0; |
5 | signal count_q:T_count_idx := T_count_idx'low; |
6 | |
7 | --Steigende Flanke Trigger erkennen und einmal (run once) durchzählen:
|
8 | |
9 | --innerhalb getakteten process
|
10 | trig_in_del_q <= trig_in; |
11 | |
12 | if (trig_in_del_q = '0') and (trig_in = '1') then --start count ny Loading index of MSb |
13 | count_q <= T_count_idx'high; |
14 | end if; |
15 | if (count_q /= T_count_idx'low) then --decrement as long lower boundary reached |
16 | count_q <= count_q - 1; |
17 | end if; |
18 | |
19 | |
20 | |
21 | --64x1 ROM auslesen
|
22 | |
23 | data_out <= ROM(count_q); --bit-counter is also address of ROM |
Da fehlt noch jede menge drumherum (entity, architectur Rahmen) aber das sollte mensch der guten Willens ist, einer VHDL-Syntax Beschreibung oder Beispielen entnehmen können.
Mark W. schrieb: > Die Ausgabe soll getriggert erfolgen. Woher kommt der Trigger? Wie lange ist der aktiv? Wenn der auch nur für 1 Taktzyklus aktiv ist, dann ist es einfacher, sonst muss eine Flankenerkennung eingebaut werden. Und was soll passieren, wenn während der Übertragung erneut ein Trigger kommt: ignorieren, sofortiger Neustart der Übertragung oder Neustart nach Ende der Übertragung? > 1. Einen Parallel-Reihen Umsetzer, wo die Eingaenge mit konstanten > Signalen belegt sind. Wie ich diesen allerdings triggerbar mache, weis > ich noch nicht. Mal angenommen, der Trigger käme von extern, dann muss er einsynchronisiert und dann die Flanke erkannt werden. Mit der steigenden Flanke wird dann das Schieberegister geladen, sonst wird dauernd von rechts eine '1' eingeschoben:
1 | :
|
2 | constant PATTERN : std_logic_vector(63 downto 0) := x"1234567890abcdef"; |
3 | signal sr_pattern : std_logic_vector(63 downto 0) : (others => '1'); |
4 | signal sr_trigger : std_logic_vector(2 downto 0) : (others => '0'); |
5 | :
|
6 | -- Trigger für Flankenerkennung einsynchronisieren
|
7 | sr_trigger <= sr_trigger(1 downto 0) & in_trigger when rising_edge(clk); |
8 | |
9 | process begin |
10 | wait until rising_edge(clk); |
11 | -- ständig von rechts eine '1' einschieben
|
12 | sr_pattern <= sr_pattern(62 downto 0) & '1'; |
13 | if sr_trigger(2 downto 1)="01" then -- aber bei steigender Flanke: Schieberegister laden |
14 | sr_pattern <= PATTERN; |
15 | end if; |
16 | end process; |
17 | |
18 | serial_out <= sr_pattern(63); |
19 | :
|
:
Bearbeitet durch Moderator
Danke Euch Beiden fuer die Beispiele. Ich sehe mir das mal an und versuche es umzusetzen. Lothar M. schrieb: > Mark W. schrieb: >> Die Ausgabe soll getriggert erfolgen. > Woher kommt der Trigger? Wie lange ist der aktiv? Wenn der auch nur für > 1 Taktzyklus aktiv ist, dann ist es einfacher, sonst muss eine > Flankenerkennung eingebaut werden. Ich denke CLK wird 10MHz sein. Dann wollte ich mir mit einem Taktteiler den Trigger generieren, also syncron mit CLK. Benoetigen tue ich bis 10kHz. Der Triggerpuls ist also laenger als die Bitfolge selbst. > Und was soll passieren, wenn während > der Übertragung erneut ein Trigger kommt: ignorieren, sofortiger > Neustart der Übertragung oder Neustart nach Ende der Übertragung? > Kann ich erstmal ignorieren, weil ich das ausschliesse.
Mark W. schrieb: > Ich denke CLK wird 10MHz sein. Dann wollte ich mir mit einem Taktteiler > den Trigger generieren, also syncron mit CLK. Benoetigen tue ich bis > 10kHz. Der Triggerpuls ist also laenger als die Bitfolge selbst. Wenn du es schlau anstellst, dann nicht. Denn dann generierst du dir den Triggerimpuls so, dass er genau einen Taktzyklus lang aktiv ist. So funktioniert synchrones Design am einfachsten...
1 | :
|
2 | constant PATTERN : std_logic_vector(63 downto 0) := x"1234567890abcdef"; |
3 | signal sr_pattern : std_logic_vector(63 downto 0) : (others => '1'); |
4 | signal trigger : std_logic : '0'; |
5 | signal trigger_cnt : integer range 0 to 1000000 := 0; |
6 | :
|
7 | -- Triggerimpuls erzeugen
|
8 | process begin |
9 | wait until rising_edge(clk); |
10 | trigger_cnt <= trigger_cnt+1; |
11 | trigger <= '0'; |
12 | if trigger_cnt=999999 then -- Zeit abgelaufen |
13 | trigger_cnt <= 0; |
14 | trigger <= '1'; -- trigger für 1 Taktzyklus aktiv |
15 | end if; |
16 | end process; |
17 | |
18 | -- Trigger für Flankenerkennung einsynchronisieren
|
19 | sr_trigger <= sr_trigger(1 downto 0) & in_trigger when rising_edge(clk); |
20 | |
21 | process begin |
22 | wait until rising_edge(clk); |
23 | -- ständig von rechts eine '1' einschieben
|
24 | sr_pattern <= sr_pattern(62 downto 0) & '1'; |
25 | if trigger='1' then -- mit Trigger: Schieberegister laden |
26 | sr_pattern <= PATTERN; |
27 | end if; |
28 | end process; |
29 | |
30 | serial_out <= sr_pattern(63); |
31 | :
|
:
Bearbeitet durch Moderator
Ich habe jetzt mal den Code von Lothar versucht, aber es geht nicht. Ich bekomme immer Fehlermeldungen bezueglich der Deklaration der Signale. Hier ist mein VHDL Code:
1 | library ieee; |
2 | use ieee.std_logic_1164.all; |
3 | use ieee.numeric_std.all; |
4 | |
5 | entity PULSER is |
6 | |
7 | port( |
8 | clk : in std_logic; |
9 | serial_out : out std_logic); |
10 | |
11 | end PULSER; |
12 | |
13 | architecture BEHAVIOR of PULSER is |
14 | |
15 | constant PATTERN : std_logic_vector(63 downto 0) := x"aaaaaaaaaaaaaaaa"; |
16 | signal sr_pattern : std_logic_vector(63 downto 0) : (others => '1'); |
17 | signal trigger : std_logic : '0'; |
18 | signal trigger_cnt : integer range 0 to 1000000 := 0; |
19 | begin
|
20 | |
21 | process begin |
22 | wait until rising_edge(clk); |
23 | trigger_cnt <= trigger_cnt+1; |
24 | trigger <= '0'; |
25 | if trigger_cnt=999999 then |
26 | trigger_cnt <= 0; |
27 | trigger <= '1'; |
28 | end if; |
29 | end process; |
30 | |
31 | sr_trigger <= sr_trigger(1 downto 0) & in_trigger when rising_edge(clk); |
32 | |
33 | process begin |
34 | wait until rising_edge(clk); |
35 | sr_pattern <= sr_pattern(62 downto 0) & '1'; |
36 | if trigger = '1' then |
37 | sr_pattern <= PATTERN; |
38 | end if; |
39 | end process; |
40 | |
41 | serial_out <= sr_pattern(63); |
42 | |
43 | end BEHAVIOR; |
Und hier die Fehlermeldungen: ERROR - C:/AllMyFiles/LatticeDiamondProjects/Pulser01/pulser.vhd(16,52-16,53) (VHDL-1261) syntax error near : ERROR - C:/AllMyFiles/LatticeDiamondProjects/Pulser01/pulser.vhd(23,3-23,14) (VHDL-1241) trigger_cnt is not declared ERROR - C:/AllMyFiles/LatticeDiamondProjects/Pulser01/pulser.vhd(24,3-24,10) (VHDL-1241) trigger is not declared ERROR - C:/AllMyFiles/LatticeDiamondProjects/Pulser01/pulser.vhd(26,6-26,17) (VHDL-1241) trigger_cnt is not declared ERROR - C:/AllMyFiles/LatticeDiamondProjects/Pulser01/pulser.vhd(27,6-27,13) (VHDL-1241) trigger is not declared ERROR - C:/AllMyFiles/LatticeDiamondProjects/Pulser01/pulser.vhd(25,6-25,17) (VHDL-1241) trigger_cnt is not declared ERROR - C:/AllMyFiles/LatticeDiamondProjects/Pulser01/pulser.vhd(31,2-31,12) (VHDL-1241) sr_trigger is not declared ERROR - C:/AllMyFiles/LatticeDiamondProjects/Pulser01/pulser.vhd(35,3-35,13) (VHDL-1241) sr_pattern is not declared ERROR - C:/AllMyFiles/LatticeDiamondProjects/Pulser01/pulser.vhd(37,6-37,16) (VHDL-1241) sr_pattern is not declared ERROR - C:/AllMyFiles/LatticeDiamondProjects/Pulser01/pulser.vhd(36,6-36,13) (VHDL-1241) trigger is not declared ERROR - C:/AllMyFiles/LatticeDiamondProjects/Pulser01/pulser.vhd(41,15-41,25) (VHDL-1241) sr_pattern is not declared ERROR - C:/AllMyFiles/LatticeDiamondProjects/Pulser01/pulser.vhd(13,1-43,14) (VHDL-1284) unit behavior ignored due to previous errors
Mark W. schrieb: > signal sr_pattern : std_logic_vector(63 downto 0) : (others => '1'); > signal trigger : std_logic : '0'; Ja, da fehlen wohl noch 2 '=' Zeichen. Aber keine Sorge, du findest den richtigen Platz. Einfach mal die Zeile drüber oder drunter anschauen... ;-) Und den sr_trigger gibt es wie den in_trigger in der zweiten Variante nicht mehr, weil er nicht mehr nötig ist. Fazit: einfach die unnötige Eintakterei rauslöschen. Und dann sieht das nicht mehr so schlecht aus (zur Beschleunigung der Simulation zähle ich nur 1000 Pulse und starte neu).
:
Bearbeitet durch Moderator
Das hoert sich ja beruhigend an. Schau ich mir gleich morgen nochmal an. Ich dachte schon, dass es etwas Grundlegendes ist, im Aufbau oder so.
Ich habe es jetzt bis zur Simulation hinbekommen. Danke schon mal bis hierher. Jetzt kann ich mit den Parametern spielen und mir die Funktionen klar machen. Hier ist dann der Code fuer die angehaengte simulation:
1 | library ieee; |
2 | use ieee.std_logic_1164.all; |
3 | use ieee.numeric_std.all; |
4 | |
5 | entity PULSER is |
6 | |
7 | port( |
8 | clk : in std_logic; |
9 | serial_out : out std_logic); |
10 | |
11 | end PULSER; |
12 | |
13 | architecture BEHAVIOR of PULSER is |
14 | |
15 | constant PATTERN : std_logic_vector(63 downto 0) := x"0123456789abcdef"; |
16 | signal sr_pattern : std_logic_vector(63 downto 0) := (others => '1'); |
17 | signal trigger : std_logic := '0'; |
18 | signal trigger_cnt : integer range 0 to 1000000 := 0; |
19 | begin
|
20 | |
21 | process begin |
22 | wait until rising_edge(clk); |
23 | trigger_cnt <= trigger_cnt+1; |
24 | trigger <= '0'; |
25 | if trigger_cnt = 1000 then |
26 | trigger_cnt <= 0; |
27 | trigger <= '1'; |
28 | end if; |
29 | end process; |
30 | |
31 | --sr_trigger <= sr_trigger(1 downto 0) & in_trigger when rising_edge(clk);
|
32 | |
33 | process begin |
34 | wait until rising_edge(clk); |
35 | sr_pattern <= sr_pattern(62 downto 0) & '1'; |
36 | if trigger = '1' then |
37 | sr_pattern <= PATTERN; |
38 | end if; |
39 | end process; |
40 | |
41 | serial_out <= sr_pattern(63); |
42 | |
43 | end BEHAVIOR; |
Und Testbench:
1 | library ieee; |
2 | use ieee.std_logic_1164.all; |
3 | use ieee.numeric_std.all; |
4 | |
5 | entity TEST is |
6 | end TEST; |
7 | |
8 | architecture BEHAVIOR of TEST is |
9 | signal CLK: std_logic; |
10 | signal serial_out: std_logic; |
11 | |
12 | component PULSER |
13 | port( CLK: in std_logic; |
14 | serial_out: out std_logic); |
15 | end component; |
16 | |
17 | for all: PULSER use entity work.PULSER(BEHAVIOR); |
18 | begin
|
19 | |
20 | Clock: process begin |
21 | CLK <= '0'; |
22 | wait for 100 ns; |
23 | CLK <= '1'; |
24 | wait for 100 ns; |
25 | end process Clock; |
26 | |
27 | C1: PULSER port map(CLK, serial_out); |
28 | end BEHAVIOR; |
Allerdings ist mir eine Sache nicht ganz klar, und zwar was C1 in der vorletzten Zeile vom Testbench bedeutet. Ist das die Verbindung vom Testbench, wo das DUT quasi angedockt wird?
Mark W. schrieb: > Ist das die Verbindung vom Testbench, wo das DUT quasi angedockt wird? Ja: du erzeugst damit in der Testbench eine Instanz deiner Komponente Pulser.
Mark W. schrieb: > was C1 in der vorletzten Zeile vom Testbench bedeutet. Dort wird eine Instanz namens C1 der zuvor bekannt gegebenen Komponente PULSER angelegt. Das ist exakt das selbe, wie auch andere Untermodule in einem darüber liegenden VHDL Modul eingebunden werden. Eine Testbench ist verglichen mit anderen VHDL Modulen aber einfach zu erkennen: die Entity hat keine Ports, also keine Verbindung "nach außen".
Danke Euch Beiden, ich glaube, ich habe es verstanden mit der Instanz. Ich kann den Namen offenbar frei waehlen, deswegen hatte ich mich Anfangs gewundert, weil das nirgends auftaucht. Und in meinem Buch steht nur, dass deklarierte Signale als aktuelle Signale an die als Komponente C1 platzierte Entity uebergeben werden.
:
Bearbeitet durch User
Zur Unterscheidung bekommen bei mir (fast) alle Signale in der Testbench den Präfix 'tb_':
1 | library ieee; |
2 | use ieee.std_logic_1164.all; |
3 | use ieee.numeric_std.all; |
4 | |
5 | entity TEST is |
6 | end TEST; |
7 | |
8 | architecture BEHAVIOR of TEST is |
9 | signal tb_CLK: std_logic; |
10 | signal tb_serial_out: std_logic; |
11 | |
12 | component PULSER |
13 | port( CLK: in std_logic; |
14 | serial_out: out std_logic); |
15 | end component; |
16 | |
17 | for all: PULSER use entity work.PULSER(BEHAVIOR); |
18 | begin
|
19 | |
20 | Clock: process begin |
21 | tb_CLK <= '0'; |
22 | wait for 100 ns; |
23 | tb_CLK <= '1'; |
24 | wait for 100 ns; |
25 | end process Clock; |
26 | |
27 | C1: PULSER port map(tb_CLK, tb_serial_out); |
28 | end BEHAVIOR; |
Duke
Ja, gute Idee. Mir ist auch schon aufgefallen, dass es viele verschiedene "Schreibstile" und Hervorhebungsmerkmale gibt. Auch mit dem Einruecken muss man erstmal klarkommen. Ich achte darauf, was mir am Besten entgegen kommt.
Mark W. schrieb:
C1: PULSER port map(CLK, serial_out);
Ich empfehle hier, nicht die implizite Zuordnung per Position zu nehmen,
sondern explizit den Port dem Signal zuzuordnen:
C1: PULSER port map(CLK=>CLK, serial_out=>serial_out);
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.