mikrocontroller.net

Forum: FPGA, VHDL & Co. Tesbench synthetisierbar machen


Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
...
data <= '0';
wait for 10ns;
clk <= '1';
wait for 10ns;
clk <= '0';
data <= '0';
wait for 10ns;
clk <= '1';
...
hat jemand eine Idee, wie man sowas am schnellsten implementieren kann?

Gruß,
Thomas

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
einen realen Takt musst Du schon anlegen. In Deinem Fall z.B 50 MHz.

Autor: matzunami (Gast)
Datum:

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

MfG
matzunami

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
:
data <= '0';
wait until rising_edge(100MHz);
clk <= '1';
wait until rising_edge(100MHz);
clk <= '0';
data <= '0';
wait until rising_edge(100MHz);
clk <= '1';
:
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.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
process
begin
...
wait until rising_edge(toggle);
data_stimulus <= '0';
wait until falling_edge(toggle);
clk_stimulus <= '1';
wait until rising_edge(toggle);
clk_stimulus <= '0';
data_stimulus <= '0';
wait until falling_edge(toggle);
clk_stimulus <= '1';
....
end process;
Aber anscheinend ist wohl höchstens ein "wait until" in einem Process 
synthetisierbar.

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:
Angehängte Dateien:

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

So was geht z.B.:
entity wait_im_process is
    Port ( clk100MHz : in  STD_LOGIC;
           din : in  STD_LOGIC;
           dout : out  STD_LOGIC := '0';
           clk  : out  STD_LOGIC := '0');
end wait_im_process;

architecture Behavioral of wait_im_process is

begin

  process begin
    dout <= '0';
    wait until rising_edge(clk100MHz);
    clk <= '1';
    wait until rising_edge(clk100MHz);
    clk <= '0';
    dout <= '0';
    wait until rising_edge(clk100MHz);
    clk <= '1';
    wait until rising_edge(clk100MHz);
    clk <= '0';
    dout <= din;
    wait until rising_edge(clk100MHz);
    dout <= '1';
    wait until rising_edge(clk100MHz);
    clk <= '0';
    dout <= '1';
    wait until rising_edge(clk100MHz);
  end process;

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.

Autor: Michael (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
entity wait_im_process is
    Port ( clk100MHz : in  STD_LOGIC;
           din : in  STD_LOGIC;
           dout : out  STD_LOGIC := '0';
           clk  : out  STD_LOGIC := '0');
end wait_im_process;

architecture Behavioral of wait_im_process is

signal init : std_logic := '1';

begin

  process begin
  if init = '1' then
    dout <= '0';
    wait until rising_edge(clk100MHz);
    clk <= '1';
    wait until rising_edge(clk100MHz);
    clk <= '0';
    dout <= '0';
    wait until rising_edge(clk100MHz);
    clk <= '1';
    wait until rising_edge(clk100MHz);
    clk <= '0';
    dout <= din;
    wait until rising_edge(clk100MHz);
    dout <= '1';
    wait until rising_edge(clk100MHz);
    clk <= '0';
    dout <= '1';
    wait until rising_edge(clk100MHz);
  end if;
  init <= '0';
  end process;

end Behavioral;
funktioniert prima.

Danke nochmal,
Thomas

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
entity wait_im_process is
    Port ( clk100MHz : in  STD_LOGIC;
           din : in  STD_LOGIC;
           dout : out  STD_LOGIC := '0';
           clk  : out  STD_LOGIC := '0');
end wait_im_process;

architecture Behavioral of wait_im_process is

signal init : std_logic := '1';

begin

  process begin
    dout <= '0' and init;
    wait until rising_edge(clk100MHz);
    clk <= '1' and init;
    wait until rising_edge(clk100MHz);
    clk <= '0' and init;
    dout <= '0' and init;
    wait until rising_edge(clk100MHz);
    clk <= '1' and init;
    wait until rising_edge(clk100MHz);
    clk <= '0' and init;
    dout <= din and init;
    wait until rising_edge(clk100MHz);
    dout <= '1' and init;
    wait until rising_edge(clk100MHz);
    clk <= '0' and init;
    dout <= '1' and init;
    wait until rising_edge(clk100MHz);
  init <= '0' and init;
  end process;

end Behavioral;

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

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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ß ;-)

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klar,
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.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.
  .
  .
  .
  constant config4rom : std_logic_vector(0 to 145) :=
  "1"&
  "00000001"&
  "00000001"&
  "1"&
  "00000010"&
  "00000001"&
  "1"&
  "00000011"&
  "00000010"&
  "1"&
  "00000100"&
  "00000010"&
  "1"&
  "00000101"&
  "00000011"&
  "1"&
  "00000110"&
  "00000011"&
  "1"&
  "00000111"&
  "00000100"&
  "1"&
  "00001000"&
  "00000100"&
  "1"&
  "11110000"&
  "0";
  .
  .
  .
  type state_type is (idle, push0, push1, push2, push3);
  signal state : state_type := idle;
  signal counter : unsigned(8 downto 0) := (others => '0');

begin
  process begin
    wait until rising_edge(clk);
    case state is
      when idle  =>  counter <= (others => '0'); 
              if    push(0) = '1' then state <= push0;
              elsif push(1) = '1' then state <= push1;
              elsif push(2) = '1' then state <= push2;
              elsif push(3) = '1' then state <= push3;
              end if;
      when push0  =>  counter <= counter + 1;
              cfg <= config1rom(to_integer(counter(8 downto 1))); 
              cfgclk <= counter(0);
              if counter(8 downto 1) = 145 then state <= idle; end if;
      when push1  =>  counter <= counter + 1;
              cfg <= config2rom(to_integer(counter(8 downto 1))); 
              cfgclk <= counter(0);
              if counter(8 downto 1) = 145 then state <= idle; end if;
      when push2  =>  counter <= counter + 1;
              cfg <= config3rom(to_integer(counter(8 downto 1))); 
              cfgclk <= counter(0);
              if counter(8 downto 1) = 43 then state <= idle; end if;
      when push3  =>  counter <= counter + 1;
              cfg <= config4rom(to_integer(counter(8 downto 1))); 
              cfgclk <= counter(0);
              if counter(8 downto 1) = 145 then state <= idle; end if;
    end case;
  end process;
.
.
.
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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.