Forum: FPGA, VHDL & Co. Prozess mit kombinatorischer und getakteter Logik


von Marius S. (lupin) Benutzerseite


Lesenswert?

Ich habe hier eine State-Machine und einen Barrelshifter.

Die State-Machine gibt vor um wie viel Bits der Barrelshifter weiter 
schieben soll. Der BarrelShifter ist am Ausgang rein kombinatorisch.

Also wenn die State-Machine z.B. vorgibt um 4 Bits weiter zu schieben, 
dann wird das auch sofort am Ausgang des Barrelshifters transparent.
Erst wollte ich den Ausgang des Barrelshifters auch getaktet 
realisieren, aber ich möchte im ersten Takt z.B. vorgeben um 4 Bits 
weiter zu schieben und im nächsten Takt möchte ich das Ergebnis des 
Barrelshifters zur Verfügung haben.

Jetzt habe ich mein Design das erste mal synthetisiert und die 
Xilinx-Synthese-Tools geben mir eine ganze Liste mit Signalen, welche in 
den Prozess stehen sollten. Darunter auch der Ausgang des Barrelshifters 
(ist ein Eingang für die Statemachine).

Setze ich den Ausgang des Barrelshifters allerdings in die Statemachine, 
so erhalte ich von ISIM folgende Fehlermeldung:
ERROR: at 85 ns(10000): Iteration limit 10000 is reached. Possible zero 
delay oscillation detected where simulation can not advance in time 
because signals can not resolve to a stable value in....


Also was mache ich da jetzt?

Im Grunde soll nur beim Zustandswechsel der State-Machine (welcher 
synchron zum Clock ist) irgendwas passieren. Also reicht es doch 
eigentlich den aktuellen Zustand in der Sensitivity-Liste der 
State-Machine zu haben, oder?

von Daniel__m (Gast)


Lesenswert?

Hm, zwar viel geschrieben, aber wenig gezeigt. Wie sieht denn deine 
Beschreibung aus?

Es klingt ein wenig, als wenn der Ausgang der Kombinatorik gleichzeitig 
auch Eingang ist, also eine kombinatorische Schleife?

von Marius S. (lupin) Benutzerseite


Angehängte Dateien:

Lesenswert?

Daniel__m schrieb:
> aber wenig gezeigt

Leute, benutzt doch mal eure Phantasie :-)

Ist halt etwas mehr Code, das hier alles rein zu stellen wäre ein wenig 
zu viel.

Also erstmal der Zustandsübergang für die State-Machine:
1
  -- Getakteter Zustandsübergang
2
  StateTransition : process(rst,clk,enable)
3
  begin
4
    if rst = '1' OR enable = '0' then
5
      -- Reset-Zustand
6
      state <= sReset;
7
    elsif rising_edge(clk) then
8
      -- Zustandsübergang
9
      state <= next_state;
10
    end if;
11
  end process;

Jetzt die State-Machine:
1
  StateMachine : process(state)
2
  begin
3
    
4
    next_state <= state;
5
    
6
    -- Default-Werte
7
    shifter_cnt <= 0;
8
    
9
    case state is
10
      when sReset=>
11
        next_state <= sState1;
12
        
13
      when sState1=>
14
        -- Hier wird noch irgendwas mit shifter_data berechnet
15
        -- Eigentlich wird aus shifter_data auch shifter_cnt berechnet (!)
16
        shifter_cnt <= shifter_data(15 downto 11);
17
        next_state <= sState2;
18
      when sState2=>
19
        -- Hier wird noch irgendwas mit shifter_data berechnet
20
        -- Eigentlich wird aus shifter_data auch shifter_cnt berechnet (!)
21
        shifter_cnt <= shifter_data(15 downto 11);
22
        next_state <= sState1;
23
    end case;
24
  end;

shifter_cnt gibt an um wieviel der Shifter im nächsten Takt verschoben 
werden soll.
shifter_data sind die Ausgangsdaten des Barrelshifters.

Im Anhang ist der Barrelshifter. Wenn ihr zum Shifter noch eine Idee / 
Anregung habt wäre ich auch ganz dankbar :-)

Den Barrelshifter brauche ich, weil ich einen Stream mit variabler 
Wort-Breite habe und ich mit 8 Bit Bus-Breite die Daten nachladen 
möchte. Wird ein Wort dekodiert, so wird der Stream um die Wortbreite 
weiter geschoben.
Wenn wieder volle 8 Bit im Shifter frei sind, dann werden über den 
Datenbus die nächsten 8 Bit rein geladen.

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


Lesenswert?

> Also erstmal der Zustandsübergang für die State-Machine:
Ein asynchroner kombinatorischer Reset. Das wird dir noch viel Spass und 
einige graue Haare verschaffen. Das Stichwort dazu heißt: Glitch (hier 
besonders auf der enable-Leitung).

> Den Barrelshifter brauche ich,weil ich einen Stream ... habe
Woher kommen die Daten? Wohin gehen Sie? Welche Datenrate hast du? 
Welche Taktfrequenz hat dein System?

> ERROR: at 85 ns
Was passiert denn eigentlich zum Zeitpunkt "85 ns"?
BTW: Der Fehler ist (wie üblich) nicht im geposteten Code. Ich tippe 
(wie Daniel) auf eine kombinatorische Schleife im auskommentierten 
Bereich der geposteten FSM....

> Im Anhang ist der Barrelshifter.
Der sieht auch wild aus.... :-o

von PittyJ (Gast)


Lesenswert?

Ich weiss nur, dass immer wenn ich so etwas versucht habe, es später 
irgendwelche Probleme gab.
Deshalb habe ich nur noch Clock-getaktete Prozesse.
Andere Spielereien überlasse ich echten Profis, die das jeden Tag 
machen.

von dden (Gast)


Lesenswert?

Hallo,
also ich würde sagen er hat eine kombinatorische Schleife durch die 
Kombination von FSM und Barrelshifter da der barrelshifter Ausgang 
kombinatorisch mit shiftcnt gebildet wird, und er Shiftcnt im ebenfalls 
kombinatorischen FSM Process mit ebenjenem Ausgang bildet.
Könnte mich natürlich auch täuschen, wenn man zusammenhangslosen 
unvollständigen Code postet.
Aber mein Vorschlag wäre Shiftcnt zu registern.

Mfg

von Marius S. (lupin) Benutzerseite


Lesenswert?

Den Reset kann ich auch synchron ausführen.
Wo ist das Problem mit asynchronen Resets? Sehe ich noch nicht.

Dateninput, Datenrate und Takt ist alles nicht definiert.
Beim Takt würde ich mal so unter 50 MHz annehmen.

Der Fehler passiert beim zustandsübergang von Reset.

Nur Clock-getaktete Prozesse? Dann müsste ich einiges umschreiben :-/

Was sieht denn am barrelshifter wild aus?

Ich glaube dden hat recht. Nur das Problem ist, wenn ich shiftcnt 
registriere, dann steht mir das Ergebnis der Schiebeoperation erst einen 
Takt später zur Verfügung.
Mit einer Taktflanke würde der neue Wert für Shiftcnt durch die 
Statemachine erzeugt werden, der Wert wird dann aber erst mit der 
nächsten Taktflanke in das Register übernommen.

Ich kann nochmal versuchen das Problem zu isolieren und kürzeren aber 
vollständigen Code posten. Meinen vollen Code nur ungern, da das meine 
Bachelor Arbeit werden soll.

Das ganze funktioniert ansonsten in der Simulation, wenn ich in der 
sensitivity-list nur "state" stehen habe, erst wenn ich "shifter_data" 
zufüge läuft die Simulation nicht mehr.

von Falk B. (falk)


Lesenswert?

@ Marius S. (lupin) Benutzerseite

>vollständigen Code posten. Meinen vollen Code nur ungern, da das meine
>Bachelor Arbeit werden soll.

Na dann wird es höchgste Zeit, dass du mal die Grundlagen der 
Digitaltechnik verstehst. Einfach nur wild was in VHDL hinschreiben ist 
wenig zielführend.

Eigentlich [tm] ist es recht einfach. Eine kombinatorische Logik kann 
eine beliebige logische Funktion abbilden, u.a. deinen Barrelshifter.

Mit Kombinatorik allein kann man aber keine sequezielle Logik ala 
Statemachines etc., aufbauen, dafür benötigt man Speicherelemente, aka 
FlipFlops.

Und drittens darf kombinatorische Logik immer nur in eine Richtung Daten 
transportieren, kombinatorische Rückkopplungen sind bis auf wenige 
Ausnahmen ein No Go! Dort müssen praktisch immer Register 
zwischengeschaltet werden.

>Das ganze funktioniert ansonsten in der Simulation, wenn ich in der
>sensitivity-list nur "state" stehen habe, erst wenn ich "shifter_data"
>zufüge läuft die Simulation nicht mehr.

Du klebst viel zuviel am VHDL, du musst dir bildlich klar machen, wo die 
Register liegen und wo die Logik. In dem Punkt war der Entwurf mittels 
Schaltplaneingabe und Logiksymbolen besser, weil man direkt gesehen hat, 
was wo langgeht. Eigentlich [tm] dollte man mit VHDL NICHT anfangen, 
bevor man nicht die graphische Methode bis zu ein bestimmten Punkt geübt 
und WIRKLICH verstanden hat. Dazu reichen auch einfache Schaltungen.

Schau dir die Grundschaltungen der Meele, More und Medvedev-Automaten 
an, dann verstehtst du hoffentlich, was ich meine.

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


Lesenswert?

Marius S. schrieb:
> Das ganze funktioniert ansonsten in der Simulation, wenn ich in der
> sensitivity-list nur "state" stehen habe, erst wenn ich "shifter_data"
> zufüge läuft die Simulation nicht mehr.
Das ist es, was der Synthesizer dir sagen möchte:
"Da fehlen etliche Signale in der Sensitivliste. Mich schert das nicht, 
weil ich diese Liste nicht verwende. Aber deine Simulation wird nicht 
zur Realität passen!"
Und das ist diese Liste, die du da dann angezeigt bekommst:
Marius S. schrieb:
> die Xilinx-Synthese-Tools geben mir eine ganze Liste mit Signalen,
> welche in den Prozess stehen sollten.

Fazit: deine Simulation mag zwar "funktionieren", sie ist aber schlicht 
und einfach falsch... :-(

Falk Brunner schrieb:
> Du klebst viel zuviel am VHDL, du musst dir bildlich klar machen, wo die
> Register liegen und wo die Logik. ...
Ich würde sogar sagen, man muss sich vorher ein Bild von der Hardware 
machen, und diese mit der HardwareBESCHREIBUNGSsprache VHDL 
beschreiben. Es wird auf jeden Fall niemals gut gehen, wenn man mit 
VHDL ins blaue "programmiert" wie man das bei PC- oder uC-Programmen 
gerne sieht...

von Marius S. (lupin) Benutzerseite


Lesenswert?

Okay, zurück ans Reißbrett :-)

Also ich habe mir das immer so vorgestellt, dass die Sensitivity-Liste 
des Prozesses angibt mit welchen Signalen der Prozess ausgelöst werden 
soll.

Bei meiner Statemachine habe ich halt nur den Status der State-Machine 
in der Sensitivity-Liste.

Ich dachte jetzt, dass wenn ich in der State-Machine ein kombinatorisch 
erzeugtes Signal verarbeite (wie z.B. das vom Barrelshifter) dieses 
Signal nur bei Änderung des Zustands eingelesen wird (und auch gelatcht 
wird).
Ich glaube die Synthese-Tools geben darüber auch Warnungen aus (das 
Latche erzeugt werden).

Aber okay, selbst wenn das funktioniert (durch die indirekt erzeugten 
Latches) ist das wohl gegen die Idee von VHDL, dass man alles bis ins 
Detail beschreiben muss ;-(

Kann man davon ausgehen, wenn die Synthese-Tools keine Warnungen mehr 
anzeigen, dass dann die Synthese mit der Simulation übereinstimmt?

Ich werde mal versuchen die Post-Synthese Simulation durch zu führen und 
schauen was dann dabei raus kommt. Ich kann mir vorstellen, dass die 
Synthese-Tools durch Einfügen von Latches etc. was lauffähiges erzeugt 
haben.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Marius S. (lupin) Benutzerseite

>Okay, zurück ans Reißbrett :-)

Oder ins Kornfeld ;-)

>Also ich habe mir das immer so vorgestellt, dass die Sensitivity-Liste
>des Prozesses angibt mit welchen Signalen der Prozess ausgelöst werden
>soll.

Das ist auch so. Aber man kann sich damit auch schön verarschen.

>Bei meiner Statemachine habe ich halt nur den Status der State-Machine
>in der Sensitivity-Liste.

Das ist schön, nützt dir aber wenog. Den der Synthesizer merkt, dass du 
ihm eine unvollständige Liste gegeben hast, schmeißt ne Warnung und 
synthetisiert vollständig. Aber der Simulator hält sich strikt an die 
Sensitivity-Liste. Schwupps, schon hast du ein vollkommen anderes 
Verhalten zwischen Simulation und Synthese. 8-0

>Ich dachte jetzt, dass wenn ich in der State-Machine ein kombinatorisch
>erzeugtes Signal verarbeite (wie z.B. das vom Barrelshifter) dieses
>Signal nur bei Änderung des Zustands eingelesen wird (und auch gelatcht
>wird).

So ist es aber nicht. Siehe mein anderes Posting.

>Ich glaube die Synthese-Tools geben darüber auch Warnungen aus (das
>Latche erzeugt werden).

Jo.

>Aber okay, selbst wenn das funktioniert (durch die indirekt erzeugten
>Latches) ist das wohl gegen die Idee von VHDL, dass man alles bis ins
>Detail beschreiben muss ;-(

???

>Kann man davon ausgehen, wenn die Synthese-Tools keine Warnungen mehr
>anzeigen, dass dann die Synthese mit der Simulation übereinstimmt?

Hmmm, jain. Meistens schon, aber es gibt immer Stolperfallen.

>Ich werde mal versuchen die Post-Synthese Simulation durch zu führen und
>schauen was dann dabei raus kommt.

Ist relativ sinnlos. Mach es einfach richtig (tm), ohne Tricks. Die 
braucht es hier ganz sicher nicht.

> Ich kann mir vorstellen, dass die
>Synthese-Tools durch Einfügen von Latches etc. was lauffähiges erzeugt
>haben.

Aber nichts, was du wirklich willst. Oder brauchst. Siehe oben.

von Marius S. (lupin) Benutzerseite


Lesenswert?

Falk Brunner schrieb:
> Ist relativ sinnlos. Mach es einfach richtig (tm), ohne Tricks. Die
> braucht es hier ganz sicher nicht.

Ja, habe ich jetzt versucht. Das Simulieren des Post-Synthese-Modells 
habe ich mir mal gespart, werde ich machen wenn ich meine das ich fertig 
bin :-)


Auf Lothars Seite habe ich noch folgendes gefunden:
http://www.lothar-miller.de/s9y/categories/37-FSM

> Diese Ein-Prozess-Beschreibung ergibt genau das selbe Simulationsergebnis,
> sie hat aber deutliche Vorteile:
> 1) Deutlich weniger Schreibaufwand und bessere Übersicht
> 2) Ich brauche weniger Signale (next_counter, next_state)
> 3) Das Ganze ist garantiert synchron
> 4) Die Ausgangssignale sind registriert und damit synchron
> 5) Es werden garantiert keine Kombinatorischen Schleifen erzeugt

Also habe ich mal alles in einen Prozess beschrieben. Jetzt 
synthetisiert auch alles ohne Warnungen und die Simulation sieht auch 
gut aus, bis auf eine Kleinigkeit.

Ich verwende ein Block-RAM um Werte aus zu lesen. In einem Zustand der 
FSM wird die Adresse erzeugt, im nächsten Zustand soll der Wert 
ausgelesen werden. Bei der Beschreibung mittels Ein-Prozess FSM ist aber 
alles irgendwie um einen Takt verschoben.
1
  StateMachine : process
2
  begin
3
    wait until rising_edge(clk);
4
    -- Default-Zustände für Cases in denen Werte nicht beschrieben werden
5
    lut_addr <= '0';
6
    lut_addr <= (others => '-');
7
    case state is
8
      when sReset=>
9
        state <= sTableLookup;
10
        
11
      when sTableLookup=>
12
        -- Daten aus Stream nehmen um Tabelle zu indizieren (table lookup)
13
        lut_addr <= uint2slv(lut_offs + slv2uint(shifter_data(WINDOW_WIDTH-1 downto WINDOW_WIDTH-lut_size)), 12);
14
        lut_en <= '1';
15
        state <= sReadVal;
16
        
17
      when sReadVal=>
18
        -- Value-Dekodierung
19
        xy(0) <= GETX(lut_data);
20
        xy(1) <= GETY(lut_data);
21
....
22
    end case;
23
  end process;

Das Block-RAM ist ein synchroner Prozess, der bei steigender Flanke und 
lut_en='1' den Wert an lut_addr ausliest.

Hier seht ihr auch, dass ich aus shifter_data einen Wert mit variabler 
Bit-Länge raus ziehen muss. Das führt zu einem ganzen Haufen 
Kombinatorik :-(
Aber das ist ein anderes Problem.

Wenn ich das im Kopf durch simuliere und annehme, dass Reset inaktiv 
ist:

1. Steigende Flanke
sReset-Zustand wird abgearbeitet.
Wechsel sReset -> sTableLookup

2. Steigende Flanke
sTableLookup-Zustand wird abgearbeitet.
Jetzt liegen lut_addr und lut_en an.
Wechsel sTableLookup -> sReadVal

3. Steigende Flanke
sReadVal-Zustand wird abgearbeitet.
Erst jetzt werden die Daten aus dem Block-RAM geladen.
Die Verarbeitung erfolgt also noch mit ungültigen / alten Daten.



Einzige Möglichkeiten, die ich sehe um das Problem zu lösen:
1) Über kombinatorischen Prozess
In einem extra Prozess wird der Status abgefragt, wenn 
status=sTableLookup dann werden die Signal lut_addr und lut_en erzeugt.
Das führt dazu, dass die Daten schon bei der 2ten steigenden Flanke 
gelesen werden.

2) Taktung des Block-RAMs bei fallender Flanke
Ist wahrscheinlich ziemlich schlecht, weil lut_addr und lut_en am 
Block-RAM dann nur für eine halbe Taktperiode stabil anlegen bevor das 
Block-RAM gelesen wird. Aber das Block-RAM würde dann bei der fallenden 
Flanke nach der 2ten steigenden Flanke gelesen werden.

3) Zusätzlicher Wartezustand
In der 2ten steigenden Flanke wird nicht nach sReadVal sondern in einen 
Wartezustand gewechselt, welcher mit der 3ten steigenden Flanke 
abgearbeitet wird und in den Zustand sReadVal wechselt.


Alle drei Möglichkeiten habe ich mal ausprobiert und funktionieren auch.
Nur möchte ich eigentlich ungern noch einen Wartezustand einbauen, das 
wäre wahrscheinlich von der Beschreibung her die schönste Lösung (ohne 
Tricks :)).

Was kann man da noch machen? Oder wie sollte ich das implementieren, 
wenn ich keine Zeit verlieren will? :-)

von Falk B. (falk)


Lesenswert?

@ Marius S. (lupin) Benutzerseite

>ausgelesen werden. Bei der Beschreibung mittels Ein-Prozess FSM ist aber
>alles irgendwie um einen Takt verschoben.

Ist halt so. Geht mit der Ein-Prozess Methode nicht anders. Aber man 
muss nicht immer krampfhaft damit arbeiten, die zwei Prozess Methode 
geht auch. Die Nachteile sind zu verschmerzen.


>Wenn ich das im Kopf durch simuliere und annehme, dass Reset inaktiv
>ist:

Stimmt soweit.

>Einzige Möglichkeiten, die ich sehe um das Problem zu lösen:
>1) Über kombinatorischen Prozess

Ist OK.

>2) Taktung des Block-RAMs bei fallender Flanke

Kann man machen, beschränkt aber die maximal möglicke Taktrate. Würde 
ich nicht empfehlen.

>3) Zusätzlicher Wartezustand

Geht auch, wob der einfach Ansatz dabei Takte vershwendet, sprich, der 
Datendurchsatz wird geringer. Wenn man es gut machen will, nutzt man 
Pipelining, sprich, die FSM zur Verarbeitung läuft halt einen Takt 
versetzt zur Datenausgabe des RAMs. Dann braucht man nur ein einziges 
Mal einen Wartezustand, nicht immer wieder.

>Was kann man da noch machen? Oder wie sollte ich das implementieren,
>wenn ich keine Zeit verlieren will? :-)

Du hast doch genügend brauchare Vorschläge. Ich empfehle

1) kombinatorischer Prozess
2) Pipelining

von Marius S. (lupin) Benutzerseite


Lesenswert?

Danke Falk, wollte nur wissen was ihr so empfehlt :)

Pipelining hatte ich auch schon im Kopf. Nur sehe ich keine Möglichkeit, 
dass die FSM ohne die Daten aus dem Block-RAM irgendwie weiter arbeiten 
kann.

Ist eine sehr sequentielle Verarbeitung das ganze.

Also ich habe es jetzt kombinatorisch gelöst.
Das lässt sich dann auch ohne Prozess usw. hin schreiben.

Auch die Entnahme der Bits (mit variabler Länge) habe ich jetzt schöner, 
nicht mehr in der FSM sondern auch kombinatorisch ohne Prozess 
beschrieben.
Da ich an mehreren Stellen in der FSM eine variable Bit-Länge entnehmen 
muss hat das auch einiges an LUTs in der Synthese gespart (weil die FSM 
jetzt nur noch auf ein kombinatorisches Signal zugreift).

So langsam wird das ganze schön aufgeräumt.

von Marius S. (lupin) Benutzerseite


Lesenswert?

Eine Frage noch zur Ein-Prozess FSM...

Bei der Zwei-Prozess FSM ist es möglich als Default-Werte für die 
Ausgänge auch "don't cares" also '-' an zu geben (wenn es sich um 
std_logic oder davon abgeleitete handelt).
Ich denke bei der Zwei-Prozess FSM ist es ersichtlich, dass die Ausgänge 
soweit möglich kombinatorisch durch den Zustand und die Eingänge 
abgebildet werden. Die Definition von "don't care"-Zuständen hilft den 
Synthese-Tools dann sicherlich die kombinatorische Logik zu optimieren.


Nur wie sieht es bei der Ein-Prozess FSM aus? Sind dort alle Ausgänge 
garantiert immer getaktet? So verstehe ich das zumindest. Dann bringt es 
mir ja nichts "don't cares" zu verwenden, oder?

Oder sollte man dann das machen, was ich bereits mit der Block-RAM 
Adressierung gemacht habe und Signale, welche kombinatorisch durch den 
Zustand erzeugt werden könnten auch so erzeugen?

von Falk B. (falk)


Lesenswert?

@ Marius S. (lupin) Benutzerseite

>Also ich habe es jetzt kombinatorisch gelöst.
>Das lässt sich dann auch ohne Prozess usw. hin schreiben.

Es gibt auch kombinatorische Prozesse. Ist aber eher Geschmackssache.

>So langsam wird das ganze schön aufgeräumt.

Gut.

>abgebildet werden. Die Definition von "don't care"-Zuständen hilft den
>Synthese-Tools dann sicherlich die kombinatorische Logik zu optimieren.

Kann sein.

>Nur wie sieht es bei der Ein-Prozess FSM aus? Sind dort alle Ausgänge
>garantiert immer getaktet?

Ja.

> So verstehe ich das zumindest. Dann bringt es
>mir ja nichts "don't cares" zu verwenden, oder?

Nein.

von Marius S. (lupin) Benutzerseite


Lesenswert?

Okay, verstehe. Ich denke es ist mir überlassen ob ich gewisse Signale 
durch Kombinatorik abbilden will (was natürlich den Coding-Style der 
Ein-Prozess FSM ein wenig kaputt macht). Aber da ich sowieso schon ein 
LUT-Grab erzeugt habe lasse ich das mal lieber als registrierte Logik 
:-)

Ich denke dabei kann ich es belassen.

Vielen Dank an Falk und Lothar, ihr habt mir echt weiter geholfen! :-)

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.