Forum: FPGA, VHDL & Co. Bitfolge getriggert ausgeben


von Mark W. (kram) Benutzerseite


Lesenswert?

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?

von Blubb (Gast)


Lesenswert?

Warum nicht einen UART oder SPI core nehmen und gescheit verschalten & 
parametretisieren?

von Mark W. (kram) Benutzerseite


Lesenswert?

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.

von Mark W. (kram) Benutzerseite


Lesenswert?

Eines koennte man noch erwaehnen. Im folgenden Schritt moechte ich die 
Ausgangssignale auch 32 aufbohren.

von C. A. Rotwang (Gast)


Lesenswert?

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.

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


Lesenswert?

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
von Mark W. (kram) Benutzerseite


Lesenswert?

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.

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


Lesenswert?

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
von Mark W. (kram) Benutzerseite


Lesenswert?

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

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


Angehängte Dateien:

Lesenswert?

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
von Mark W. (kram) Benutzerseite


Lesenswert?

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.

von Mark W. (kram) Benutzerseite


Angehängte Dateien:

Lesenswert?

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?

von Achim S. (Gast)


Lesenswert?

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.

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


Lesenswert?

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

von Mark W. (kram) Benutzerseite


Lesenswert?

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
von Duke Scarring (Gast)


Lesenswert?

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

von Mark W. (kram) Benutzerseite


Lesenswert?

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.

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


Lesenswert?

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
Noch kein Account? Hier anmelden.