Forum: FPGA, VHDL & Co. Wie Testbench schreiben


von ope (Gast)


Lesenswert?

Hi,

wie schreibt man am besten seinen Testbench? Ich bin langsam am
verzweifeln. Den TB sequential machen wie: assert(), wait for ...,
maches jenes, assert(), wait ...  klappt nur bei einfachen Dingen imo.
Bei komplizierten, sequentiell zeitlichen Zusammenhängen wird's
schnell hässlich, wenn man dann noch etwas ändert war ist die ganze
Mühe u.U. futsch, da die sorgsam eingefügten waits nicht mehr an der
Stelle stehen, wo sie sollten.

Mit reset zB. bin ich gut klargekommen mit
1
reset <= 0, '1', after PERIOD, '0' after 6*PERIOD, '1' after
2
7*PERIOD;
jetzt wollte ich das auch mal für Signalzuweisungen machen, und später
für asserts, aber es klappt leider nicht:
1
architecture behavior of tb_edge_trigger is 
2
   constant PERIOD  : time   := 10 ns;   
3
   signal clk      : std_logic   := '0';
4
   ...
5
   type edge_t is
6
   record
7
      at     : natural;
8
      rise   : std_logic_vector(7 downto 0);
9
      fall   : std_logic_vector(7 downto 0);
10
      mask   : std_logic_vector(7 downto 0);
11
   end record;
12
   type edges_t is array (8 downto 0) of edge_t;
13
   signal edges : edges_t;
14
begin
15
   uut: ...
16
17
   cond_setup_proc: process
18
   begin
19
      edges(0).at   <= 0;
20
      edges(0).rise <= b"0000_0000";
21
      edges(0).fall <= b"0000_0000";
22
      edges(0).mask <= b"0000_0000";  
23
            
24
      edges(1).at   <= 1;
25
      edges(1).rise <= b"0000_0001"; 
26
      edges(1).fall <= b"0000_0000";
27
      edges(1).mask <= b"0000_0001";
28
      ...
29
   end process;
30
       
31
  tb : process
32
  begin
33
    reset    <= '1',
34
                '0' after 1*PERIOD,
35
                '1' after 3*PERIOD,
36
                '0' after 4*PERIOD,
37
                '1' after 6*PERIOD,
38
                '0' after 7*PERIOD;
39
    
40
      for i in edges'low to edges'high loop
41
         -- HIER SOLLTE DIE SIGNALZUWEISUNG REIN, geht das?
42
      end loop;
43
     
44
      -- und hier klappt es einfach nicht
45
      trigger_rising <= edges(0).rise,
46
                        edges(1).rise after edges(1).at*PERIOD;     
47
    
48
      ...
49
  end process;
50
51
end;
Bei letzter Variante kommt nach run sogar:
  Delay in signal assignment is not ascending.
was ich nicht nachvollziehen kann, da at hierbei 1 ist. Die
Signalzuweisung wird auch einfach nicht durchgeführt.

Viele Grüße
Olaf

von ope (Gast)


Angehängte Dateien:

Lesenswert?

so, jetzt wollte ich mal ganz clever sein, und einzelne Tests in einen
process packen. Allerdings zerrren jetzt die einzelnen procs
(reset_proc und test_proc) untereinander an den Signalen, hier konkret
trigger_xxxx:
1
architecture behavior of tb_edge_trigger is 
2
  constant PERIOD    : time   := 10 ns;   
3
  signal clk         : std_logic   := '0';
4
  signal reset       : std_logic   := '0';
5
  ...
6
  signal trigger_rise :  std_logic_vector(7 downto 0);
7
  signal trigger_fall :  std_logic_vector(7 downto 0);
8
  signal trigger_mask :  std_logic_vector(7 downto 0);
9
10
  signal match :  std_logic := '0';
11
begin
12
13
   uut: entity ...
14
15
   clock_proc: ...
16
17
   probe: ...
18
19
   ...
20
21
  reset_block: block
22
  begin
23
    reset_proc: process(reset)
24
    begin
25
      if(reset = '1') then
26
        trigger_rise <= b"0000_0000" after PERIOD;
27
        trigger_fall <= b"0000_0000" after PERIOD;
28
        trigger_mask <= b"0000_0000" after PERIOD;
29
      end if;
30
    end process;
31
    time_reset_proc: process
32
    begin
33
      reset <= '1',
34
               '0' after 1*PERIOD,
35
               '1' after 3*PERIOD,
36
               '0' after 4*PERIOD,
37
               '1' after 6*PERIOD,
38
               '0' after 7*PERIOD;
39
      wait;
40
    end process;
41
  end block;
42
43
  test_proc: process
44
    constant DELAY : integer := 1;
45
  begin       
46
    trigger_rise  <= b"0000_0001" after DELAY*PERIOD; 
47
    trigger_fall  <= b"0000_0000" after DELAY*PERIOD;
48
    trigger_mask  <= b"0000_0001" after DELAY*PERIOD;
49
    wait;
50
  end process;
51
52
end;

Wie bekomme ich das nun wieder korrekt hin? Da Modelsim kein Schematics
anzeigt, habe ich keine Ahnung, was 'draus gemacht wurde.

Innerhalb der Tests wollte ich dann mit assert die Ergebnisse testen
(evtl. muss ich auch einen block 'draus machen mit "stimuli" und
"check").

Viele Grüße
Olaf

von ope (Gast)


Lesenswert?

auch hier keine Hinweise? schreiben alle sequentiell mit waits?

von Werner A. (Gast)


Lesenswert?

beschreib doch erstmal grundsätzlich worum es geht, pc, microcontroller,
 programmiersprache ...

von ope (Gast)


Lesenswert?

Man kann die Frage bzgl. obigen Ansatzes auch vereinfachen:

Wie kann ich in VHDL (nicht Target spezifisch) die event list (angelegt
mit
1
foo <= '0', '1' after X, '0' after Y ....
) in verschiedenen processes erweitern; tja, ohne die alten events zu
überschreiben bzw. ohne Kolisionen durch die Erweitung in den einzelnen
processes?

Es taucht öfters mal in den fremden sources das keyword "transport"
auf - dies hilft aber auch nicht (wenn ich es recht verstanden habe
verschiebt es ja nur die events).

Viele Grüße
Olaf

von FPGA-User (Gast)


Lesenswert?

@ope

schreib doch mal, wie Du Dir den Test des LAs vorstellst!
Nicht als VHDL, sondern verbal.

Ich könnte mir vorstellen, dass man für einzelne Module
jeweils eine TB anlegt und dann erstmal ein Modul testet.
Am Schluss gibt es eine TB für das Gesamtdesign.

Da gibt es nun mehrere Möglichkeiten. Entweder man macht
für jeden Test ein eigenes File (Stimuli_test1.vhd,
stimuli_test2.vhd ...) oder man packt eben alles in eine TB
oder ...es gibt zig weitere Varianten : TCL-Script...)

Für eine Serie von Tests würde ich mir ein Signal nehmen, dass
meine Test-Nummer speichert, z.B.:

type TEST_TYPE is (test1, test2 ...);

Dann könnte man die Testnummer entweder basierend auf der Zeit
oder auf Events hochzählen, je nachdem, wie der Testablauf
sein soll.

Alle Prozesse, die am Test beteiligt sind, können sich nun
auf dieses Signal synchronisieren. Z.B. könnte man alle Parameter
die zu Beginn des Tests gesetzt werden sollen abh. von der Test-
nummer setzen
   case test_nummer is
      when test_1 => Input_Params <= Param_Set1;
      when test_2 => ...;

Natürlich dürfen verschiedene Prozesse nicht auf 1 Signal
zugreifen, aber dafür sehe ich auch keinen Grund.

Vielleicht hilft Dir auch eine Statemachine in der Testbench.
So kompliziert kann der LA doch nicht sein, dass man dafür
keine einfache TB schreiben kann.

von ope (Gast)


Lesenswert?

>Ich könnte mir vorstellen, dass man für einzelne Module
>jeweils eine TB anlegt und dann erstmal ein Modul testet.
>Am Schluss gibt es eine TB für das Gesamtdesign.

so handhabe ich es derzeit auch - jede entity (zB. trigger_edge,
trigger_pattern) hat ihren eigenen Testbench TB_xyz, wobei ich einem
Bottom-up Entwurf fröne. Somit bekommen die Top entity (zB. trigger,
instanziert trigger_edge, trigger_pattern u.a.) einen weiteren, eigenen
TB.

Exemplarisch versuche ich es mal mit dem edge_trigger, da dazu schon
etwas in vhdl hier steht. Der Test geht zB. auf

* die Erkennung von rising edge vom sample bit0 in bitmuster (bm) #0,
anschl.
* Erkennung falling edge sample bit 3 in bm #1
* Erkennung rise/falling edge sample bit 2,3 in bm #2
* Erkennung any edge edge sample bit 5 in bm #3
* Erkennung rise/falling/any edge sample bit 0,2,6,5,8 in bm #4

Somit habe ich versch. Kombis durch. Das Bitmuster generiert derzeit
einfach nur ein counter (d.h. ist einfach eine Binärzahl), der
TB_trigger läuft mit einem prbs signal.

clock läuft hoch und bietet mir immer neue pattern, auf die ich
triggere - eben sequentiell. Das heisst, das Timing bzw. die
Reihenfolge wie ich den vhdl TB schreibe ist sehr starr und unflexibel.
Eine Änderung darin und schon stimmt es nicht mehr und das assert (will
nicht immer waves anschauen) ist an der falschen Stelle - das ist mir
nun zu oft passiert.

Daher die Idee, diese fünf Bitmuster jeweils einzeln als absolute Zeit
in einem process anzugeben - dies geschieht ja bei der event Vergabe
mit after ...

>Natürlich dürfen verschiedene Prozesse nicht auf 1 Signal
>zugreifen, aber dafür sehe ich auch keinen Grund.

Dies ist der Grund für meinen Ärger oben. Anscheinend gehts wirklich
nicht, daher sind Alternativen gesucht.

>schreib doch mal, wie Du Dir den Test des LAs vorstellst!
>Nicht als VHDL, sondern verbal.

OK, also. In einer Stelle, process, file, entity setze ich jeweils die
5 Testkonditions, zB cond_1:
1
        trigger_rise <= b"0000_1010";
2
        trigger_fall <= b"0000_1001";
3
        trigger_mask <= b"0000_1011";
4
        time := 30 ns;
Diese signale werden zur absoluten Zeit (t0+time ns) aktiv. Damit kann
ich zu jedem beliebigen Zeitpunkt meine trigger pattern setzen. Das uut
triggert zB. mit steigender clk Flanke und setzt den output match auf
'1' mit der fallenden Flanke clk. Mit der nächsten steigenden soll
assert prüfen, ob es true ist - also genau einen Taktzyklus später.
Anschliessend muss ich das uut wieder reseten um den Urzustand wieder
herzustellen, oder div. enable signal zu setzten und kann mit der
nächsten cond weiter machen (derweil mein bm generator ja weiter zählt
und andere bitmuster liefert).

Ziel ist also, diese 5 mini TB, bei denen es einzeln geschrieben und
gestartet keine Probleme geben würde, in einem grossen zu verpacken, so
dass ich weitere Testfälle vorne oder hinten anhängen kann.

Je mehr entities ich in den Tops instanziert habe, desto komplizierter
werden die Testfälle, da nun zB. "edges match, pattern matches for 5
clk cycles" etc. als cond. auftreten können. Alles kann ich nicht
testen, aber zumindest das Offensichtliche.

Viele Grüße
Olaf

von ope (Gast)


Lesenswert?

soll ich weiter ausführen?

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.