Forum: FPGA, VHDL & Co. Tesbench synthetisierbar machen


von Thomas (Gast)


Lesenswert?

Hallo,

Ich habe ein eine digitale Schaltung in VHDL entwickelt.
In der Simulation läuft diese bereits prima.
Jetzt möchte ich sie gerne in real testen.
Dabei soll ein Bereich allerdings noch nicht in den Prozess mit 
eingebunden werden.
Dieser muss also immer noch stimuliert werden.
Dazu möchte ich gerne Teile der Testbench verwenden, die ich auch zur 
Simulation genutzt habe.
Diese Teile sollen mit auf das FPGA synthetisiert werden.
Das Design bekommt für diese Testphase also zusätzliche Ausgänge 
spendiert, die direkt mit den entsprechenden Eingängen verbunden werden.

Die Testbench enthält ellenlange Bitfolgen nach folgendem Muster:
1
...
2
data <= '0';
3
wait for 10ns;
4
clk <= '1';
5
wait for 10ns;
6
clk <= '0';
7
data <= '0';
8
wait for 10ns;
9
clk <= '1';
10
...
hat jemand eine Idee, wie man sowas am schnellsten implementieren kann?

Gruß,
Thomas

von Gast (Gast)


Lesenswert?

einen realen Takt musst Du schon anlegen. In Deinem Fall z.B 50 MHz.

von matzunami (Gast)


Lesenswert?

Ich versteh nicht ganz was du machen willst. 10 ns kannst du nicht 
synthetisieren.

MfG
matzunami

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


Lesenswert?

> hat jemand eine Idee, wie man sowas am schnellsten implementieren kann?
Dein Stichwort lautet: Statemachine

Wenn du Xilinx verwendest, kannst du das mit einem 100Mhz-Takt auch so 
probieren:
1
:
2
data <= '0';
3
wait until rising_edge(100MHz);
4
clk <= '1';
5
wait until rising_edge(100MHz);
6
clk <= '0';
7
data <= '0';
8
wait until rising_edge(100MHz);
9
clk <= '1';
10
:
Zumindest für ein Signal (clk oder data) zwischen den wait funktioniert 
das.

> Die Testbench enthält ellenlange Bitfolgen...
Sieht übel aus...
Sowas gehört üblicherweise in eine procedure innerhalb der Testbench.

von Falk B. (falk)


Lesenswert?

@  Lothar Miller (lkmiller)

>> Die Testbench enthält ellenlange Bitfolgen...
>Sieht übel aus...
>Sowas gehört üblicherweise in eine procedure innerhalb der Testbench.

Oder noch viel besser. In ein konstantes Array. Das ist

a) viel besser überschaubar/änderbar
b) Problemlos synthetisierbar und per Zähler/Takt ausgebbar

MfG
Falk

von Thomas (Gast)


Lesenswert?

Mir ist schon klar, dass ich einen richtigen Takt brauche.
Die 10ns sind egal, können auch 10ms sein.
Natürlich kann man 10ns nicht synthetisieren, aber das ist ja gerade 
meine Frage.
Bei kurzen Bitfolgen habe ich das schon mal mit einem Zustandsautomaten 
gemacht. Aber hier geht es um ca. 200 Bit hintereinander...
Mein erster Versuch war in etwa so:
1
process
2
begin
3
...
4
wait until rising_edge(toggle);
5
data_stimulus <= '0';
6
wait until falling_edge(toggle);
7
clk_stimulus <= '1';
8
wait until rising_edge(toggle);
9
clk_stimulus <= '0';
10
data_stimulus <= '0';
11
wait until falling_edge(toggle);
12
clk_stimulus <= '1';
13
....
14
end process;
Aber anscheinend ist wohl höchstens ein "wait until" in einem Process 
synthetisierbar.

von Thomas (Gast)


Lesenswert?

Ups, die letzten beiden Antworten habe ich noch gar nicht gelesen...
Ich werde das mal so probieren, wie du schrieben hast, Lothar.
Danke für den Tip mit dem konstantem Array, Falk.
Das werde ich mir mal anschauen.

Gruß,
Thomas

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


Angehängte Dateien:

Lesenswert?

> Aber anscheinend ist wohl höchstens ein "wait until" in einem Process
> synthetisierbar.
Nein. Aber das wird aber garantiert nicht gehen:
1
wait until rising_edge(toggle);
2
:
3
wait until falling_edge(toggle);
Es ist nur die selbe Flanke auf dem selben Signal synthetisierbar.

So was geht z.B.:
1
entity wait_im_process is
2
    Port ( clk100MHz : in  STD_LOGIC;
3
           din : in  STD_LOGIC;
4
           dout : out  STD_LOGIC := '0';
5
           clk  : out  STD_LOGIC := '0');
6
end wait_im_process;
7
8
architecture Behavioral of wait_im_process is
9
10
begin
11
12
  process begin
13
    dout <= '0';
14
    wait until rising_edge(clk100MHz);
15
    clk <= '1';
16
    wait until rising_edge(clk100MHz);
17
    clk <= '0';
18
    dout <= '0';
19
    wait until rising_edge(clk100MHz);
20
    clk <= '1';
21
    wait until rising_edge(clk100MHz);
22
    clk <= '0';
23
    dout <= din;
24
    wait until rising_edge(clk100MHz);
25
    dout <= '1';
26
    wait until rising_edge(clk100MHz);
27
    clk <= '0';
28
    dout <= '1';
29
    wait until rising_edge(clk100MHz);
30
  end process;
31
32
end Behavioral;
Daraus wird eine State-Machine mit 7 Zuständen gebaut (Screenshot). 
Allerdings wirst du Probleme haben, den Verlauf der Dinge aufzuhalten. 
Die beginnt am Anfang und donnert bis zum Ende durch, um sofort wieder 
vorne anzufangen.

von Michael (Gast)


Lesenswert?

Je nach Länge könntest Du doch über eine RAM-Zelle nachdenken.
Die einzelnen Bits entsprechen deinen Signalen (clk, dout, etc).
Dann brauchst Du nur einen Binärzähler an die Adressleitungen zu hängen 
und im VHDL-Code die Wertetabelle mit den Zuständen zum jeweiligen 
Zeitpunkt einzutragen (Stichwort Init-Vektor).

Alternativ kannst Du ja auch Schieberegister für jeden einzelnes Signal 
aufbauen und mit dem jeweiligen Wert initialisieren. Das ist deutlich 
Rescourcenschonender als viele LUTs mit den WaitUntil aufzubauen :).

Gruß
Michael

von Thomas (Gast)


Lesenswert?

> Allerdings wirst du Probleme haben, den Verlauf der Dinge aufzuhalten.
> Die beginnt am Anfang und donnert bis zum Ende durch,
> um sofort wieder vorne anzufangen.

kein Problem, ich habe es jetzt so gemacht:
1
entity wait_im_process is
2
    Port ( clk100MHz : in  STD_LOGIC;
3
           din : in  STD_LOGIC;
4
           dout : out  STD_LOGIC := '0';
5
           clk  : out  STD_LOGIC := '0');
6
end wait_im_process;
7
8
architecture Behavioral of wait_im_process is
9
10
signal init : std_logic := '1';
11
12
begin
13
14
  process begin
15
  if init = '1' then
16
    dout <= '0';
17
    wait until rising_edge(clk100MHz);
18
    clk <= '1';
19
    wait until rising_edge(clk100MHz);
20
    clk <= '0';
21
    dout <= '0';
22
    wait until rising_edge(clk100MHz);
23
    clk <= '1';
24
    wait until rising_edge(clk100MHz);
25
    clk <= '0';
26
    dout <= din;
27
    wait until rising_edge(clk100MHz);
28
    dout <= '1';
29
    wait until rising_edge(clk100MHz);
30
    clk <= '0';
31
    dout <= '1';
32
    wait until rising_edge(clk100MHz);
33
  end if;
34
  init <= '0';
35
  end process;
36
37
end Behavioral;
funktioniert prima.

Danke nochmal,
Thomas

von Thomas (Gast)


Lesenswert?

Hallo Lothar,

durch Zufall sehe ich gerade, dass du diesen Fall in deinen Blog 
übernommen hast.
Wenn dich die Warnung wegen der unvollständigen Sensitiv-Liste stört, 
dann kannst du es auch so machen:
1
entity wait_im_process is
2
    Port ( clk100MHz : in  STD_LOGIC;
3
           din : in  STD_LOGIC;
4
           dout : out  STD_LOGIC := '0';
5
           clk  : out  STD_LOGIC := '0');
6
end wait_im_process;
7
8
architecture Behavioral of wait_im_process is
9
10
signal init : std_logic := '1';
11
12
begin
13
14
  process begin
15
    dout <= '0' and init;
16
    wait until rising_edge(clk100MHz);
17
    clk <= '1' and init;
18
    wait until rising_edge(clk100MHz);
19
    clk <= '0' and init;
20
    dout <= '0' and init;
21
    wait until rising_edge(clk100MHz);
22
    clk <= '1' and init;
23
    wait until rising_edge(clk100MHz);
24
    clk <= '0' and init;
25
    dout <= din and init;
26
    wait until rising_edge(clk100MHz);
27
    dout <= '1' and init;
28
    wait until rising_edge(clk100MHz);
29
    clk <= '0' and init;
30
    dout <= '1' and init;
31
    wait until rising_edge(clk100MHz);
32
  init <= '0' and init;
33
  end process;
34
35
end Behavioral;

In der Implementierung mit dem Zustandsautomaten schreibst du:
1
  if (cnt<6) then cnt <= cnt+1;
2
  else            cnt <= 0;
3
  end if;
reicht da nicht einfach ein
1
  cnt <= cnt+1;
Ist doch egal, wenn die Bits hinten "raus fallen"...

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


Lesenswert?

> In der Implementierung mit dem Zustandsautomaten schreibst du:
> :
> Ist doch egal, wenn die Bits hinten "raus fallen"...
Die Ursache liegt darin, dass bei der Simulation ein Problem 
auftaucht, wenn cnt als integer range 0 to 6 definiert ist, und vor 
dem Rücksetzen cnt+1 zugewiesen wird.
Zudem ist der Zustand 7 (wenn eine 3 Bit binär codierte SM erzeugt wird) 
auch nicht definiert.

> kannst du es auch so machen: ...
Mit deiner Beschreibung kannst du den Lauf der Dinge (bzw. der SM) aber 
nicht (wirklich) aufhalten...

> init <= '0' and init;
Hmmm, gibt '0'
Damit willst du den Ablauf ein einziges mal durchlaufen, danach wird das 
Ganze zwar weiter durchlaufen, aber immer eine '0' ausgegeben?
Ehrlich gesagt, der Aufwand wäre mir zu groß ;-)

von Thomas (Gast)


Lesenswert?

Klar,
1
init <= '0' and init;
 macht auf dem ersten Blick keinen Sinn.
Aber ich tacker hier ca. 50 Bytes (400 bit!) nacheinander raus.
Natürlich schön kommentiert und jedes Byte zusätzlich durch einen Absatz 
getrennt.
Und indem ich jedes Bit genau gleich behandel kann ich jederzeit auch 
mal schnell welche kippen.
Genau so lag das ja auch schon in der Testbench zur Simulation vor.
Das schöne an der Sache ist ja, dass ich diese nur durch Suchen + 
Ersetzten innerhalb kürzester Zeit synthetisierbar hatte, und mir sicher 
sein konnte dadurch keine Fehler eingebaut zu haben.
Von Aufwand kann also keine Rede sein.

Aber wie ja bereits weiter oben mehrfach erwähnt wurde gibt es wohl auch 
bessere Methoden sowas zu simulieren.
Meine nächste Testbench werde ich wohl nicht mehr zu dreckig 
zusammenwürfeln...

Gruß,
Thomas


Ach ja, der Code dient ja nur zum Stimulus und fliegt später eh wieder 
raus.

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


Lesenswert?

> Von Aufwand kann also keine Rede sein.
Ja, ich meinte damit den Aufwand (Ressourcenbedarf) im FPGA  ;-)

> Meine nächste Testbench werde ich wohl nicht mehr zu dreckig
> zusammenwürfeln...
Schön, wenn du das gelernt hat :-)

von Thomas (Gast)


Lesenswert?

Jetzt wo alles läuft, habe ich mich mal an die Aufräumarbeiten gemacht 
und die Testbensh komplett umgeschrieben.
So ist sie viel kürzer, übersichtlicher, wartbarer und läuft in der 
Simulation genauso wie im Stimulus als Hülle um die eigentliche 
Top-Level Entity.
1
  .
2
  .
3
  .
4
  constant config4rom : std_logic_vector(0 to 145) :=
5
  "1"&
6
  "00000001"&
7
  "00000001"&
8
  "1"&
9
  "00000010"&
10
  "00000001"&
11
  "1"&
12
  "00000011"&
13
  "00000010"&
14
  "1"&
15
  "00000100"&
16
  "00000010"&
17
  "1"&
18
  "00000101"&
19
  "00000011"&
20
  "1"&
21
  "00000110"&
22
  "00000011"&
23
  "1"&
24
  "00000111"&
25
  "00000100"&
26
  "1"&
27
  "00001000"&
28
  "00000100"&
29
  "1"&
30
  "11110000"&
31
  "0";
32
  .
33
  .
34
  .
35
  type state_type is (idle, push0, push1, push2, push3);
36
  signal state : state_type := idle;
37
  signal counter : unsigned(8 downto 0) := (others => '0');
38
39
begin
40
  process begin
41
    wait until rising_edge(clk);
42
    case state is
43
      when idle  =>  counter <= (others => '0'); 
44
              if    push(0) = '1' then state <= push0;
45
              elsif push(1) = '1' then state <= push1;
46
              elsif push(2) = '1' then state <= push2;
47
              elsif push(3) = '1' then state <= push3;
48
              end if;
49
      when push0  =>  counter <= counter + 1;
50
              cfg <= config1rom(to_integer(counter(8 downto 1))); 
51
              cfgclk <= counter(0);
52
              if counter(8 downto 1) = 145 then state <= idle; end if;
53
      when push1  =>  counter <= counter + 1;
54
              cfg <= config2rom(to_integer(counter(8 downto 1))); 
55
              cfgclk <= counter(0);
56
              if counter(8 downto 1) = 145 then state <= idle; end if;
57
      when push2  =>  counter <= counter + 1;
58
              cfg <= config3rom(to_integer(counter(8 downto 1))); 
59
              cfgclk <= counter(0);
60
              if counter(8 downto 1) = 43 then state <= idle; end if;
61
      when push3  =>  counter <= counter + 1;
62
              cfg <= config4rom(to_integer(counter(8 downto 1))); 
63
              cfgclk <= counter(0);
64
              if counter(8 downto 1) = 145 then state <= idle; end if;
65
    end case;
66
  end process;
67
.
68
.
69
.
70
end Behavioral;
Außerdem lässt sich jetzt die Konfiguration von außen frei vorgeben.
In der Simulation läuft dann noch eine übergeordnete Testbensh, die 
nicht synthetisierbar ist.
Der Aufbau ist also so:
[Simulations-TB [Stimulus-TB (device under test)]]

Danke an alle, die geholfen haben.
Sollte es jetzt noch Verbesserungsbedarf geben, so lasst es mich bitte 
wissen.

Gruß,
Thomas

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.