Forum: FPGA, VHDL & Co. Default Werte für Register/Zustände


von Ole W. (ole_w)


Lesenswert?

Moin zusammen! Ich arbeite aktuell an einem Zyklischem Register, dass je 
nach Zustand einen anderen 8-Bit Wert ausgibt. Bei einer steigenden 
Flanke von einem externen TX_Done Signal (ist Taktsynchron kommt nur aus 
einem anderem Modul) soll der nächste Zustand getriggert werden und 
demnach ein neues Wort an den Ausgang gelegt werden. Ich bin mir aktuell 
nicht sicher, ob ich die Defaultwerte korrekt zugewiesen habe. Mein 
reset Zustand stellt die Vorraussetzung für den Folgezustand. Das klingt 
nicht schlüssig für mich war aber auf den ersten Blick nicht anders 
lösbar. Ebenso bin ich mit der Beschreibung meiner Statemachine in 
Ein-Prozesslogik nicht zufrieden, da ich Zwei-Prozesslogik gelernt habe. 
Die Anforderung war jedoch garantierte Taktsynchronität von daher.... 
Habt Ihr vielleicht Tips, wie ich die Beschreibung noch Sinnvoller 
hinbekomme?

Gruß Ole.
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
4
entity State_Machine is
5
    
6
    port(
7
        CLK, SRESETN: in std_logic;
8
        Stop  : in std_logic;
9
        Tx_Done : in std_logic ;
10
        Data: out std_logic_vector(7 downto 0)
11
        
12
    );
13
end State_Machine;
14
15
architecture BEHAVIORAL of State_Machine is
16
17
    TYPE State_type is (S1,S2,S3,reset);
18
    signal State: State_Type := S1;
19
    signal tx_reg1 : std_logic := '0';
20
    signal tx_edge : std_logic :='0';
21
22
begin
23
24
    
25
    process(CLK)
26
    begin
27
        if CLK = '1' and CLK'event then
28
            if (SRESETN = '1') or (stop = '1')then
29
                
30
                Data <= x"00";
31
                State <= reset;
32
            else
33
                case State is
34
35
36
                    when reset =>
37
38
                        Data <= x"0A";
39
                        State <= S1;
40
41
                    when S1 =>
42
43
                        if (tx_edge = '1')  then
44
                            Data <= x"0B";
45
                            State <= S2;
46
                        else
47
                            State <= S1;
48
                        end if;
49
50
                    when S2 =>
51
                        if (tx_edge ='1') then
52
                            Data <= x"0C";
53
                            State <= S3;
54
                        else
55
                            State <= S2;
56
                        end if;
57
58
                    when S3 =>
59
                        if (tx_edge ='1') then
60
                            Data <= x"0A";
61
                            State <= S1;
62
                        else
63
                            State <= S3;
64
                        end if;
65
66
67
                end case;
68
            end if;
69
        end if;
70
    end process;
71
72
    --Edge detector     
73
    process(clk)
74
    begin
75
        if CLK = '1' and CLK'event then
76
            tx_reg1 <= Tx_Done;
77
        end if;
78
    end process;
79
    tx_edge <=  Tx_Done and (not tx_reg1);
80
81
82
end BEHAVIORAL;

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


Lesenswert?

Ole W. schrieb:
> Ihr vielleicht Tips, wie ich die Beschreibung noch Sinnvoller
> hinbekomme?
Ich würde da statt einer FSM ein RAM/ROM nehmen und mit einem 
Indexzähler da durchfahren.

> tx_edge <=  Tx_Done and (not tx_reg1);
Diese Flankenerkennerei erscheint mir immer suspekt.
Woher kommt Tx_Done? Warum ist das länger als 1 Tatkzyklus aktiv? Kannst 
du das nicht so gestalten, dass das Signal nur 1 Taktzyklus lang 
ansteht? Das ist die übliche Vorgehensweise in synchronen Designs.

> if (SRESETN = '1') or (stop = '1')then
Sind das asynchrone Eingänge oder sind die taktsynchron zum clk?

: Bearbeitet durch Moderator
von Ole W. (ole_w)


Lesenswert?

Die SM wollte ich nutzen, da ich entsprechend auf die Verschiedenen 
Werte für "Data" in einem anderem Modul auf Antworten erwarte. Darüber 
möchte ich also mitschneiden was für eine Antwort ich für die gesendeten 
Daten erwarte und dann die Daten in entsprechenden Registern ablegen. 
Das State Signal wird später dann ein Ausgang aus der Entity. Es handelt 
sich dabei um ein "Zyklisches Ablauf Register" für einen SPI Treiber der 
immer wieder die gleichen Daten an N-Slaves verschickt. Ich dachte mir, 
dass ich irgendwie sinnvoll mitschneiden musste, zu welchen gesendeten 
Daten meine Antworten passen.

Das Tx_Done Signal kommt aus dem SPI-Treiber. Das Auf einen Takt zu 
kürzen dürfte kein Problem sein. Wenn ich so darüber nachdenke dann ist 
meine Flankenerkennung da tatsächlich eher ein Notbehelf.

Der Reset kommt von Extern über einen Pin, wird jedoch 
einsynchronisiert.

von Vancouver (Gast)


Lesenswert?

Lothar M. schrieb:
> Diese Flankenerkennerei erscheint mir immer suspekt.

Warum stört dich das? Flankenerkennung wird doch häufig benötigt. Z.B. 
wenn du das Clock-Signal eines I2C (SCL) auswerten willst, das aufgrund 
der geringen Frequenz im Design nicht als eigene Clockdomain sondern als 
Datensignal behandelt wird. Ein SCL-Zyklus geht dann über viele 
Systemtakte, aber interessant ist nur die aktive Flanke des SCL.

@TO:

Warum solltest du bei einer 2-Prozess-Implementierung keine 
Taktsynchronität mehr haben? Deine gesamte Logik hängt vom Stateregister 
ab, und wenn du das in einen eigenen Prozess packst, ist das ganze 
Design immer noch synchron.
Die 2-P-Variante ist auf jeden Fall die saubere Lösung. Im Prozess für 
das Zustandregister wertest du nur clk und sreset aus, und alles 
andere machst du in einem separaten und rein kombinatorischen Prozess 
für den Folgezustand.

Solche Sachen wie
1
if (SRESETN = '1') or (stop = '1')then
bringen dich mitunter in Teufels Küche, weil du kombinatorische Logik 
auf der Reset-Leitung hast. Der Reset soll nur zur Initialisierung des 
Designs verwendet werden, und das stop-Signal hat evtl ganz andere 
Aufgaben. Die beiden zu verodern ist mindestens "bad practice" bei 
manchen Tools und Technologien aber ein dicker Fehler. Zumindest im 
ASIC-Design und bei asnychronen Resets. Auch wenn du FPGA machst, 
gewöhne dir manche Sachen erst gar nicht an.

Zur den Defaultwerten: Wenn du schon ein Reset-Signal hast, dann 
solltest du das auch verwenden, um den Edge-Detektor zu initialisieren, 
das erspart dir die Defaults. Und warum setzt du das Stateregister beim 
sreset auf "reset", aber auf den Defaultwert "S1"?

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


Lesenswert?

Vancouver schrieb:
> Flankenerkennung wird doch häufig benötigt. Z.B. wenn du das
> Clock-Signal eines I2C (SCL) auswerten willst
Aber eben nicht, wenn Kommunikation zwischen FPGA-internen und 
taktsynchronen Modulen stattfindet.

von Ole W. (ole_w)


Lesenswert?

Vancouver schrieb:
> Warum solltest du bei einer 2-Prozess-Implementierung keine
> Taktsynchronität mehr haben? Deine gesamte Logik hängt vom Stateregister
> ab, und wenn du das in einen eigenen Prozess packst, ist das ganze
> Design immer noch synchron.

Naja ich habe mir das so erklärt, dass zwar das Zustandsregister 
Synchron ist, aber man ja nicht zwingend im Datenpfad ein 
Ausgangsregister nach der Übergangslogik hat. Zu dem wird ja dann das 
Zustandsregister mit den Übergangsbedingungen zum Zustandswechsel in 
einen Prozess gepackt. Das stelle ich mir als Realisierung mit einer MUX 
vor + Logik außen rum. Daher meine Annahme, dass das nicht zwingend 
Synchron ist.

Vancouver schrieb:
> Solche Sachen wieif (SRESETN = '1') or (stop = '1')then
> bringen dich mitunter in Teufels Küche, weil du kombinatorische Logik
> auf der Reset-Leitung hast. Der Reset soll nur zur Initialisierung des
> Designs verwendet werden, und das stop-Signal hat evtl ganz andere
> Aufgaben. Die beiden zu verodern ist mindestens "bad practice"

Also nehme ich besser das Stop Signal dort raus und dann handelt es sich 
wieder um einen Synchronen Reset?

Vancouver schrieb:
> Und warum setzt du das Stateregister beim
> sreset auf "reset", aber auf den Defaultwert "S1"?

Das kommt daher, da S1 ja erst Aktiv wird wenn der Prozess verlassen 
wird. Im nächsten Zyklus nach dem Reset hätte ich immer gerne S1 als 
Startbedingung, (Und in S1 dann S2 als Folgezustand, S2 -> S3 usw.) 
Damit die Zustände "nacheinander" abgearbeitet werden.

Bin mir nicht sicher ob das das richtige vorgehen ist, aber das war so 
nach meinem ASM Chart das beste was mir eingefallen ist

von Vancouver (Gast)


Lesenswert?

Ole W. schrieb:
> Naja ich habe mir das so erklärt, dass zwar das Zustandsregister
> Synchron ist, aber man ja nicht zwingend im Datenpfad ein
> Ausgangsregister nach der Übergangslogik hat.

Das Zustandregister ist das Ausgangsregister der Übergangslogik. Die 
Eingänge werden zusammen mit dem aktuellen Zustandsregister in die 
Übergangslogik gesteckt, und deren Ergebnis landet im nächsten Takt 
wieder im Zustandsregister. Der Wert am Data-Ausgang hängt nur vom 
Zustand ab und vom Edge-Detektor, die sind beide synchron, damit ist 
Data auch synchron.
Ich vermute du meinst die Gatterlaufzeit der Übergangs- und 
Ausgangsfunktion. Die ist natürlich vorhanden, aber solange die kürzer 
ist als eine Taktperiode minus Setup-Zeit der Flops, hat sie keinen 
Effekt. Ob das der Fall ist, sagt dir der Timing-Analyzer deiner 
Toolchain.
Asynchron wirst du nur, wenn ein asynchroner Eingang deiner Schaltung 
kombinatorisch auf einen Ausgang wirkt, aber das ist hier nicht der 
Fall.

Ole W. schrieb:
> Also nehme ich besser das Stop Signal dort raus und dann handelt es sich
> wieder um einen Synchronen Reset?

Wie gesagt, schreib einen getakteten Prozess der nur clk und sreset 
auswertet, und stop verwendest du im Zustandsübergangsprozess. stop wird 
damit Teil der Übergangsfunktion, und sreset ist der Reset. Dann 
schaffst du klare Verhältnisse.
Du brauchst bei der 2-P-Struktur natürlich noch ein next_state-Signal, 
das den Eingang des Stateregisters bildet. Ein vorbildlich 
implementiertes Beispiel findest du hier: 
https://vhdlguru.blogspot.com/2010/04/how-to-implement-state-machines-in-vhdl.html
nur dass dort ein asynchroner Reset verwendet wird.

Ole W. schrieb:
> Das kommt daher, da S1 ja erst Aktiv wird wenn der Prozess verlassen
> wird.

Du möchtest also, dass die FSM im Zustand S1 ist, während der sresetn 
aktiv ist?


Ole W. schrieb:
> Im nächsten Zyklus nach dem Reset hätte ich immer gerne S1 als
> Startbedingung,

Warum schreibst du dann
1
           
2
 if (SRESETN = '1') or (stop = '1')then
3
 [...]
4
    State <= reset;

und nicht
1
           
2
 [...]
3
    State <= S1;
?

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


Lesenswert?

Ole W. schrieb:
> Ebenso bin ich mit der Beschreibung meiner Statemachine in
> Ein-Prozesslogik nicht zufrieden, da ich Zwei-Prozesslogik gelernt habe.
Die Argumentation ist ehrlich gesagt eher schlecht nachzuvollziehen. Was 
ist das Problem jenseits dieser "Unzufriedenheit"?

Ole W. schrieb:
> Der Reset kommt von Extern über einen Pin, wird jedoch  einsynchronisiert.
Wie oft kommt der? Nur 1x beim Einschalten? Oder sogar noch von einem 
Resetknopf (und wenn ja: wer drückt den wie oft)? Oder wid der Reset 
sogar nur für die Entwicklung "gebraucht"?

Dann nutze den üblichen "Lade- und Konfugurationsmechanismus" des FPGAs. 
Das führt dann zum Schluss dieses Initialisierungsprozesses sowieso 
einen Reset auf die definierten Zustände aus. Und du musst nicht 
"Reset-Code" verwalten, der sowieso irrelevant ist und dir eh' nur 
unnötige Sorgen macht.

Mein Code würde etwa so aussehen:
1
:
2
:
3
    process  begin
4
        wait until rising_edge(CLK);
5
        if SRESETN='1' or stop='1'then
6
            Data <= x"0A"; -- hier fehlt mir jeweils noch irgendwie der 'Tx_Start'
7
            State <= S1;
8
        else
9
            case State is
10
            when S1 => if Tx_Done='1'  then
11
                          Data <= x"0B";
12
                          State <= S2;
13
                       end if;
14
                       -- kein else, denn wenn ich nicht weiterschalte, dann 
15
                       -- bleibt die FSM einfach beim jeweils aktuellen Zustand
16
17
            when S2 => if Tx_Done='1'  then
18
                          Data <= x"0C";
19
                          State <= S3;
20
                       end if;
21
22
            when S3 => if Tx_Done='1'  then
23
                          Data <= x"0A";
24
                          State <= S1;
25
                       end if;
26
            end case;
27
        end if;
28
    end process;
29
:
30
:
Und wenn man sich das nochmal anschaut, dann könnte man sogar auf so 
eine Idee kommen:
1
:
2
:
3
    process  begin
4
        wait until rising_edge(CLK);
5
        if SRESETN='1' or stop='1'then
6
            Data <= x"0A";
7
            State <= S1;
8
        else
9
            if Tx_Done='1'  then -- die Tx_Done Abfrage "nach vorne rausziehen"
10
               case State is
11
               when S1 =>  Data <= x"0B";
12
                           State <= S2;
13
14
               when S2 =>  Data <= x"0C";
15
                           State <= S3;
16
17
               when S3 =>  Data <= x"0A";
18
                           State <= S1;
19
              end case;
20
           end if;
21
        end if;
22
    end process;
23
:
24
:

: Bearbeitet durch Moderator
von Christoph Z. (christophz)


Lesenswert?

Ole W. schrieb:
> Naja ich habe mir das so erklärt, dass zwar das Zustandsregister
> Synchron ist, aber man ja nicht zwingend im Datenpfad ein
> Ausgangsregister nach der Übergangslogik hat.

Ob du ein Register nach der Überganslogik für deinen Ausgang hast oder 
nicht, das beschreibst du ja in deinem Code.
Und du kannst beide Varianten sowohl mit in einem oder zwei Prozessen 
beschreiben (oder in noch viel verwirrenderen Schreibstilen).

Ein Stil mit zwei Prozessen und passenden Namensregeln (z. B. einen 
Record für alle Register), kann es vereinfachen zu sehen, dass da 
jememand einen Ausgang direkt treibt und sich fragen, ob das so gedacht 
war oder nicht.

von Ole W. (ole_w)


Lesenswert?

Lothar M. schrieb:
> Die Argumentation ist ehrlich gesagt eher schlecht nachzuvollziehen. Was
> ist das Problem jenseits dieser "Unzufriedenheit"?

Nunja unzufrieden war evt. das falsche Wort. Es ist in meiner Firma so 
üblich alles in ein Prozess-Logik zu schreiben. Daher kam mein 
Gedankengang.

Lothar M. schrieb:
> Wie oft kommt der? Nur 1x beim Einschalten? Oder sogar noch von einem
> Resetknopf (und wenn ja: wer drückt den wie oft)? Oder wid der Reset
> sogar nur für die Entwicklung "gebraucht"?

Der kommt meines Wissens nach von einer externen CPU und wird dann 
Softwareseitig gesteuert. Über das "wann" habe ich keine Kontrolle, ich 
muss es eben nur abfangen.

Vancouver schrieb:
> Warum schreibst du dann

Gute Frage, je länge ich da rauf schaue, desto mehr Frage ich mich das 
auch.

von Ole W. (ole_w)


Lesenswert?

Christoph Z. schrieb:
> Ein Stil mit zwei Prozessen und passenden Namensregeln (z. B. einen
> Record für alle Register), kann es vereinfachen zu sehen, dass da
> jememand einen Ausgang direkt treibt und sich fragen, ob das so gedacht
> war oder nicht.

Das ist ein sehr guter Punkt. Danke dafür!

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


Lesenswert?

Ich schreibe solche kombinatorischen Zuweisungen auf den Ausgang dann 
meist nebenläufig hin, so wie grade im 
Beitrag "Re: VHDL Verbindung nur wenn "enabled""

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.