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
waitfor10ns;
4
clk<='1';
5
waitfor10ns;
6
clk<='0';
7
data<='0';
8
waitfor10ns;
9
clk<='1';
10
...
hat jemand eine Idee, wie man sowas am schnellsten implementieren kann?
Gruß,
Thomas
> 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
waituntilrising_edge(100MHz);
4
clk<='1';
5
waituntilrising_edge(100MHz);
6
clk<='0';
7
data<='0';
8
waituntilrising_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.
@ 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
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
waituntilrising_edge(toggle);
5
data_stimulus<='0';
6
waituntilfalling_edge(toggle);
7
clk_stimulus<='1';
8
waituntilrising_edge(toggle);
9
clk_stimulus<='0';
10
data_stimulus<='0';
11
waituntilfalling_edge(toggle);
12
clk_stimulus<='1';
13
....
14
endprocess;
Aber anscheinend ist wohl höchstens ein "wait until" in einem Process
synthetisierbar.
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
> Aber anscheinend ist wohl höchstens ein "wait until" in einem Process> synthetisierbar.
Nein. Aber das wird aber garantiert nicht gehen:
1
waituntilrising_edge(toggle);
2
:
3
waituntilfalling_edge(toggle);
Es ist nur die selbe Flanke auf dem selben Signal synthetisierbar.
So was geht z.B.:
1
entitywait_im_processis
2
Port(clk100MHz:inSTD_LOGIC;
3
din:inSTD_LOGIC;
4
dout:outSTD_LOGIC:='0';
5
clk:outSTD_LOGIC:='0');
6
endwait_im_process;
7
8
architectureBehavioralofwait_im_processis
9
10
begin
11
12
processbegin
13
dout<='0';
14
waituntilrising_edge(clk100MHz);
15
clk<='1';
16
waituntilrising_edge(clk100MHz);
17
clk<='0';
18
dout<='0';
19
waituntilrising_edge(clk100MHz);
20
clk<='1';
21
waituntilrising_edge(clk100MHz);
22
clk<='0';
23
dout<=din;
24
waituntilrising_edge(clk100MHz);
25
dout<='1';
26
waituntilrising_edge(clk100MHz);
27
clk<='0';
28
dout<='1';
29
waituntilrising_edge(clk100MHz);
30
endprocess;
31
32
endBehavioral;
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.
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
> 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:
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
entitywait_im_processis
2
Port(clk100MHz:inSTD_LOGIC;
3
din:inSTD_LOGIC;
4
dout:outSTD_LOGIC:='0';
5
clk:outSTD_LOGIC:='0');
6
endwait_im_process;
7
8
architectureBehavioralofwait_im_processis
9
10
signalinit:std_logic:='1';
11
12
begin
13
14
processbegin
15
dout<='0'andinit;
16
waituntilrising_edge(clk100MHz);
17
clk<='1'andinit;
18
waituntilrising_edge(clk100MHz);
19
clk<='0'andinit;
20
dout<='0'andinit;
21
waituntilrising_edge(clk100MHz);
22
clk<='1'andinit;
23
waituntilrising_edge(clk100MHz);
24
clk<='0'andinit;
25
dout<=dinandinit;
26
waituntilrising_edge(clk100MHz);
27
dout<='1'andinit;
28
waituntilrising_edge(clk100MHz);
29
clk<='0'andinit;
30
dout<='1'andinit;
31
waituntilrising_edge(clk100MHz);
32
init<='0'andinit;
33
endprocess;
34
35
endBehavioral;
In der Implementierung mit dem Zustandsautomaten schreibst du:
1
if(cnt<6)thencnt<=cnt+1;
2
elsecnt<=0;
3
endif;
reicht da nicht einfach ein
1
cnt<=cnt+1;
Ist doch egal, wenn die Bits hinten "raus fallen"...
> 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ß ;-)
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 Aufwand kann also keine Rede sein.
Ja, ich meinte damit den Aufwand (Ressourcenbedarf) imFPGA ;-)
> Meine nächste Testbench werde ich wohl nicht mehr zu dreckig> zusammenwürfeln...
Schön, wenn du das gelernt hat :-)
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
constantconfig4rom:std_logic_vector(0to145):=
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
typestate_typeis(idle,push0,push1,push2,push3);
36
signalstate:state_type:=idle;
37
signalcounter:unsigned(8downto0):=(others=>'0');
38
39
begin
40
processbegin
41
waituntilrising_edge(clk);
42
casestateis
43
whenidle=>counter<=(others=>'0');
44
ifpush(0)='1'thenstate<=push0;
45
elsifpush(1)='1'thenstate<=push1;
46
elsifpush(2)='1'thenstate<=push2;
47
elsifpush(3)='1'thenstate<=push3;
48
endif;
49
whenpush0=>counter<=counter+1;
50
cfg<=config1rom(to_integer(counter(8downto1)));
51
cfgclk<=counter(0);
52
ifcounter(8downto1)=145thenstate<=idle;endif;
53
whenpush1=>counter<=counter+1;
54
cfg<=config2rom(to_integer(counter(8downto1)));
55
cfgclk<=counter(0);
56
ifcounter(8downto1)=145thenstate<=idle;endif;
57
whenpush2=>counter<=counter+1;
58
cfg<=config3rom(to_integer(counter(8downto1)));
59
cfgclk<=counter(0);
60
ifcounter(8downto1)=43thenstate<=idle;endif;
61
whenpush3=>counter<=counter+1;
62
cfg<=config4rom(to_integer(counter(8downto1)));
63
cfgclk<=counter(0);
64
ifcounter(8downto1)=145thenstate<=idle;endif;
65
endcase;
66
endprocess;
67
.
68
.
69
.
70
endBehavioral;
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