Hallo zusammen,
ich habe ein mehr oder weniger großes Verständigungsproblem bei State
Machines in VHDL. Im Studium haben wir dieses Design gelernt.
1
libraryieee;
2
useieee.std_logic_1164.all;
3
4
entitymoore_4sis
5
6
port(
7
clk:instd_logic;
8
data_in:instd_logic;
9
reset:instd_logic;
10
data_out:outstd_logic_vector(1downto0)
11
);
12
13
endentity;
14
15
architecturertlofmoore_4sis
16
17
typestate_typeis(s0,s1,s2,s3);
18
19
signalstate:state_type;
20
21
begin
22
process(clk,reset)
23
variablenext:state_type;
24
begin
25
ifreset='1'then
26
next:=s0;
27
elsif(rising_edge(clk))then
28
casestateis
29
whens0=>
30
ifdata_in='1'then
31
next:=s1;
32
else
33
next:=s0;
34
endif;
35
whens1=>
36
ifdata_in='1'then
37
next:=s2;
38
else
39
next:=s1;
40
endif;
41
whens2=>
42
ifdata_in='1'then
43
next:=s3;
44
else
45
next:=s2;
46
endif;
47
whens3=>
48
ifdata_in='1'then
49
next:=s0;
50
else
51
next:=s3;
52
endif;
53
endcase;
54
endif;
55
state<=next;
56
endprocess;
57
58
-- Output depends solely on the current state
59
process(state)
60
begin
61
62
casestateis
63
whens0=>
64
data_out<="00";
65
whens1=>
66
data_out<="01";
67
whens2=>
68
data_out<="10";
69
whens3=>
70
data_out<="11";
71
endcase;
72
endprocess;
73
74
endrtl;
Dieses Design habe ich nun mal übernommen und mein Design funktioniert
jetzt in der Simulation wunderbar, aber wenn ich dann eine
Post-Implementation Timing Simulation mache, stimmen die übertragenen
Daten nicht mehr. Bei der Synthese bekomme ich zudem die Meldung
[Synth 8-327] inferring latch for variable 'TLAST_reg'
Wobei das Signal im nicht getakteten Teil der State Machine steht (also
im Ausgangsnetzwerk). Die Ursache der Warnung verstehe ich schon, nur
frage ich mich gerade ob dieser Designentwurf für State Machines
überhaupt sinnvoll oder eher veraltet ist, da diese Warnung bei jedem
Ausgangssignal auftritt, welches im Ausgangsnetzwerk geschaltet wird.
Daniel K. schrieb:> Im Studium haben wir dieses Design gelernt.
Übel, sowas...
Probiers mal so:
1
process(clk,reset)
2
variablenext:state_type;
3
begin
4
5
next:=state;--<<< Defaultzuweisung an die Variable muss sein!!!
6
7
ifreset='1'then
8
next:=s0;
9
elsif(rising_edge(clk))then
Und was lernen wir daraus: lass die ersten paar Monate Variablen aus dem
Spiel. Denn durch diese Variable wird das hier zum wilden Hack, weil im
selben Prozess völlig unerwartet ausserhalb des getakteten Bereichs noch
Zuweisung erfolgen. Der Synthesizer müsste einem sowas als Fehler
ankreiden.
> nur frage ich mich gerade ob dieser Designentwurf für State Machines> überhaupt sinnvoll oder eher veraltet ist
Es ist irgendein zusammengeflickter akademischer Ansatz. Im echten Leben
sieht bei mir eine FSM z.B. so aus wie im
Beitrag "Re: Hilfe für Code - VHDL" die
Kaffeeautomat_FSM0b.vhd
Es ist halt gerade ziemlich frustrierend, weil anscheinend auch noch
falsche Zustände in der Timing-Analyse dazu kommen (siehe Bild) :)
Ich schau mir dein Beispiel mal an, werf meinen Code in die Tonne und
mach es neu ^^
Gute Entscheidung,es neuzumachen:
1. signal state : state_type := S0; --<<< Startzustand!
(oder beliebigen anderen Startzustand). Muss nicht
sein, aber heutzutage motzt da kein Tool mehr.
2. Reset innerhalb des RisingEdge-Blocks
(bei einigen Herstellern ist es egal (Altera),
bei anderen ist ein syncr. Reset besser und wird
glaube ich auch von allen Herstellern akzeptiert)
3. Die next-Variable kann komplett weggelassen
und durch das state-Signal ersetzt werden
(aber mit der Zuweisung <= statt :=)
4. die next-Zuweisung am Schluss ist ja wohl das
gefährlichste, inklusive der fehlenden
Default-Zuweisung ein Latch-Generator!
5. Der nachfolgende Block für die IO-Zuweisung
ist asynchron (die asynchr. next-Zuweisung im
ersten Prozess macht's auch nicht schlechter),
daher deine illegalen Ausgabezustände.
Wandle diesen Prozess auch ich einen synchronen um.
Insgesammt: Lerne auf jeden Fall die Formulierung
von Mealy/Moore/Medwedew-FSMs in Ein- als auch
Zweiprozessschreibweise, und zwar in und auswendig.
Sowas muss in der Praxis blind sitzen!
.. und was ich noch vergessen habe:
Nach der Substitution von next durch state
sind in deinem Code alle Zuweisungen im
ELSE-Teil innerhalb des CASE-Blocks
überflüssig. state ist ja registriert.
Mein Punkt 5 ist natürlich gefährlich,
denn er führt ja zu einer Latanz von einem
Takt!
Zu den Intel-Templates: Das Mealy-Beispiel
ist ja grausam: einmal steht da
1
..
2
ifdata_in='1'then
3
state<=s1;
4
else
5
state<=s0;
6
endif;
7
..
(fur S0,S1,S2), und für S3
1
..
2
ifdata_in='1'then
3
state<=s3;
4
else
5
state<=s1;
6
endif;
7
..
statt besser und damit einheitlich
1
..
2
ifdata_in/='1'then
3
state<=s1;
4
else
5
state<=s3;
6
endif;
7
..
und damit lassen sich wieder die ELSE-Teile
weglassen. Ist einfach viel lesbarer/weniger
irreführend (find' ich zumindestens).
Hallo,
ich habe noch etwas weiter geschaut und sogar ein Beispiel von Xilinx
für FSM gefunden (ich nutze selber Xilinx).
https://www.xilinx.com/support/documentation/university/Vivado-Teaching/HDL-Design/2015x/VHDL/docs-pdf/lab10.pdf
Xilinx nutzt dort einen asynchronen Prozess für die Ausgabe. Jetzt ist
auch da wieder die Frage: Ist das sinnvoll? Immerhin empfiehlt es
Xilinx?
Und dann noch eine Frage am Rande...
Ich bin gerade auch am Überlegen welcher Automat für meine Anwendung
sinnvoll ist. Ich möchte ein ROM (BRAM) mit einem AXI4-Stream Interface
ausstatten. Das ganze Ding soll nachher als Master an was anderes dran
gemacht werden und Daten senden (u. a. zum Debuggen in der Hardware,
weshalb die Stream Verification IPs leider weg fallen). Für die
Implementierung des Stream-Interfaces wollte ich dann den
Zustandsautomaten nehmen. Wenn ich es jetzt richtig verstanden habe,
muss es sich dabei um einen Mealy-Automaten handeln, da der Zustand des
Automaten für den Bus auch von dem TREADY-Signal des Slaves abhängt.
Sehe ich das so richtig?
Daniel K. schrieb:> Xilinx nutzt dort einen asynchronen Prozess für die Ausgabe. Jetzt ist> auch da wieder die Frage: Ist das sinnvoll?
Ist halt umständlich. Und je nach Geschmack des Entwicklers.
> Ist das sinnvoll?
Wenns drauf ankommt, dann implementiere einfach mal beide Varianten und
vergleiche das Syntheseergebnis.
> Immerhin empfiehlt es Xilinx?
In irgendeinem Lab-Workbook für VHDL-Anfänger...
Man darf zudem nicht alles bedenkenlos glauben, was irgendwer irgendwo
schreibt:
Beitrag "Re: Variable vs Signal"> Immerhin empfiehlt es Xilinx?
Fazit: bei FSM muss jeder seinen Stil finden. Die suche kann dabei
durchaus ein paar Jahre dauern. Ich schäme mich heute fast, wenn ich
meine ersten Designs so ansehe... ;-)
Lothar M. schrieb:> bei FSM muss jeder seinen Stil finden.
Hi,
wobei der Stil auch von den Vorgaben abhängt. "Langsame" FSMs mache ich
oft in einem synchronen Process (Moore FSM). Jedoch habe ich oft mit
Handshaking-Signalen zu tun und möchte aus Performance-Gründen
"back-to-back" übertragen, dann brauche ich eine Mealy FSM, machmal im
2-Process-Stil, machmal 1-Process + concurrent Statements, jenachdem was
dokumentativer bzw. verständlicher ist.
Daniel K. schrieb:> ich habe noch etwas weiter geschaut und sogar ein Beispiel von Xilinx> für FSM gefunden (ich nutze selber Xilinx).>> https://www.xilinx.com/support/documentation/university/Vivado-Teaching/HDL-Design/2015x/VHDL/docs-pdf/lab10.pdf>> Xilinx nutzt dort einen asynchronen Prozess für die Ausgabe. Jetzt ist> auch da wieder die Frage: Ist das sinnvoll? Immerhin empfiehlt es> Xilinx?
Als kleines Katastrophenbeispiel sicherlich..
1. Beispiel: Wie schon Oben von mir beschrieben
können die ELSE-Teile im Case-Block entfallen
(enfach durch next_state <= state als Default-Zuweisung
ersetzen).
2. Beispiel: next_state <= S0: grausam! Du hast hier
eine 3-Prozess-Schreibweise. Kombinatorische Prozesse
dienen eiglich der Bestimmung des nächsten Zustands,
falls sich nichts ändert, dann bleibt der aktuelle
Zustand. Also ist die Default-Zuweisung next_state<=state
die beste Wahl und nicht next_state<=S0.
3. "when others"-Fälle sind bei selbstdefinierten Datentypen
total überflüssig.
4. Startzustand-Zuweisung fehlt in der Deklaration
(akzeptiert jedes Tool, seit wann eigentlich? 15 Jahren?)
Standard-Frame für die 1-Prozess-Schreibweise:
..ups, es fehlt noch die Default-Mealy-Zuweisung
in der 2-Prozess-Schreibweise
1
..
2
-- default assignments
3
nxt_state<=reg_state;
4
nxt_outsig<=reg_outsig;
5
6
-- default mealy assignments
7
mealy_out<="00";-- or whatever
8
..
.. und es ist natürlich überflüssig zu erwähenen,
dass in der 1-Prozess-Schreibweise keine
Mealy-Signale beschrieben werden können. Dafür
ist zu dem 1-Prozess ein zusätzlicher kombinatorischer
Prozess bzw. concurrent-Zuweisungen erforderlich.
Danke für die Anmerkungen. Dann werde ich das Dokument mal schnell
wieder schließen :)
Ich habe nun mal den ersten Teil meines verpfuschten Projektes neu
gemacht und mich dabei an das Kaffeeautomatbeispiel von Lothar
orientiert.
Das Ziel war es hier ein ROM mit einem AXI-Stream Interface
auszustatten, damit mir das ROM als Datenquelle für einen AXI-Stream
Slave dienen kann. Getestet habe ich das Ding mit der Stream
Verification IP von Xilinx und da scheint es (in der normalen)
Simulation zu funktionieren. Eine Post-Implementation Timing Simulation
kann ich mit der Testbench aber leider nicht machen, da dort der
Verification-Core nicht funktioniert - was in meinen Augen irgendwie
Blödsinn ist. Ich dachte die Post-Implementation Timing Simulation nimmt
die Testbench und steckt diese Signale in das erzeugte Design...
Daniel K. schrieb:> process(ACLK, ARESETN, TREADY, DataBuffer, CurrentState, Address)
Da muss nur ACLK rein. Denn nur bei einer Änderung von ACLK muss der
Simulator den Prozess neu berechnen. Der Synthesizer schert sich sowieso
nicht um die Sensitvliste.
> Eine Post-Implementation Timing Simulation kann ich mit der Testbench> aber leider nicht machen
Du brauchst bei einem korrekten synchronem Design (asynchrone externe
signale werden über 2 FF einsynchronisiert) und korrekten Constraints
keine Timing-Simulation. Sie bringt keinen Erkenntnisgewinn. Ich z.B.
habe seit Jahren keine mehr gemacht.
Ein Wort zum Thema Latency.
Dein Kommentar hier lässt vermuten, dass das Signal TLAST aktiv sein
soll, wenn die Adresse=99 ist:
1
-- Set TLAST to indicate the end of the package
2
ifAddress=99thenTLAST<='1';
3
elseTLAST<='0';
4
endif;
5
6
-- Reset the address counter if the end is reached
7
ifAddress<99thenAddress<=Address+1;
8
elseAddress<=0;
9
endif;
Das wird nicht der Fall sein, weil mit dem selben Takt, mit dem TLAST
gesetzt wird, auch der Adress Zähler auf 0 gesetzt wird. TLAST ist also
genau dann aktiv, wenn Adress 0 ist.
Du erkennst es recht einfach, wenn du den Code mal umschreibst:
1
-- Set TLAST to indicate the end of the package
2
ifAddress=99thenTLAST<='1';
3
elseTLAST<='0';
4
endif;
5
6
-- Reset the address counter if the end is reached
7
ifAddress=99thenAddress<=0;
8
elseAddress<=Address+1;
9
endif;
Was ja gleich ist wie das hier:
1
-- Set TLAST to indicate the end of the package
2
-- And reset the address counter if the end is reached
Lothar M. schrieb:> Was ja gleich ist wie das hier:
Stimmt...
Lothar M. schrieb:> Ein Wort zum Thema Latency.> Dein Kommentar hier lässt vermuten, dass das Signal TLAST aktiv sein> soll, wenn die Adresse=99 ist:
Das stimmt schon, aber da die Signale ohnehin alle verzögert kommen,
passt TLAST auch wieder (siehe Screenshot). Die Ausgabedaten sind eine
Sinuskurve mit Offset 0x8000. Der letzte Wert ist 0x77F6 und der wird
korrekt mit TLAST markiert :)