mikrocontroller.net

Forum: FPGA, VHDL & Co. Schieberegister andersrum


Autor: shaun (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich muss einen Entwurf von uC auf CPLD umstellen, da im Protokoll einer 
seriellen Übertragung im Nachhinein ein paar Randbedingungen 
implementiert werden, wie meine Software-Statemachine so groß werden 
lässt, dass sie sich auf einem vernünftigen Controller nicht mehr 
implementieren lässt (zumindest nicht für die maximale spezifizierte 
Taktfrequenz der Schnittstelle).

Es wird nach einer Startmarkierung ein Datenwort variabler Länge 
übertragen, diese wird vorher auch nicht bekannt gegeben. Die 
Übertragung beginnt mit dem LSB.

Würde ich nun ein normales Schieberegister einsetzen, hätte ich das MSB 
"rechtsbündig", müsste aber mitzählen, wo mein LSB ist - doof.

In der uC-Realisierung habe ich es so gemacht, dass ich mit der 
Startmarkierung ein Register "A" genullt und ein Register "B" auf 0x0001 
gesetzt habe (beide 16bit, beim 16. Takt wird implizit angehalten, da 
nur die ersten 12-14 bits interessieren)
Mit jedem Takt wurde dann regA |= regB ausgeführt, wenn die Datenleitung 
log. 1 war, ansonsten nix. Danach wurde in jedem Fall regB um eins nach 
links geschoben (mit 0en auffüllend). Da nach dem 16. Takt nur noch 
Nullen in regB stehen, kann ab dann so viel ge-odert werden wie will...

Das könnte ich zwar auch in den CPLD giessen, aber irgendwie habe ich 
das Gefühl, etwas zu übersehen...

Jemand 'ne Idee?

Autor: Jan M. (mueschel)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das ist ein einfaches Schieberegister das du da beschreibst:

signal register : std_logic_vector(16 downto 0);
...
if rising_edge(CLK) then
  register <= register(16 downto 0) & input;


Da du jetzt noch implizit zaehlen moechtest, setzt du das Register am 
Anfang einfach auf 00000...001 und wartest, bis Bit 16 1 ist.

Autor: shaun (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da kann ich aber lange warten, da ein Datenwort wie geschrieben VARIABLE 
Länge hat.In der Regel wird das initiale 1-Bit also nie ankommen. Bei 
einem "einfachen" Schieberegister, wie Du es nennst, steht das zuletzt 
reingeschobene Bit auf der 0-Position ("rechtsbündig"), das zuerst 
reingeschobene Bit, bei mir das LSB, bei variabler Länge irgendwo.
Bei >=16 Takten wäre das mit Deinem Vorschlag die MSB-Position.
Verstehst' was ich meine?
Nach Deinem Vorschlag müsste ich parallel mitzählen, wo mein LSB gerade 
ist und dann entsprechend für die Ausgabe zuweisen. DAS möchte ich 
umgehen.
(Die Bits haben festgelegte Bedeutungen und steuern v.a. LEDs an)

Autor: Jan M. (mueschel)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ah okay, woher weisst du die Laenge des Wortes? Dann wenn der Takt 
aufhoert zu laufen?

Aber straeube dich nicht so gegen einen Zaehler, von 0 bis 16 zu zaehlen 
kostet dich gerade einmal 4 Flipflops / LUTs (Zeit spielt im FPGA ja 
keine Rolle), alleine das Schieberegister sind schon 16 Flipflops, da du 
am Ende ja parallel auf das gesamte Wort zugreifen willst.

Autor: shaun (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Richtig, aber ein "breites" Register brauche ich ja in jedem Fall, weil 
ich, wie Du sagtest, das gesamte Wort brauche (zumindest die ersten 
12-14 bits davon).
Das Ende des Datenstroms ist da erreicht, wo die Startbedingung erfüllt 
wird (LOAD-Leitung wird nach min. 5 aufeinanderfolgenden 0-Zyklen wieder 
1).

Wenn ich Deinen Vorschlag mit Zähler und Decoder (LUT) aufgreife komme 
ich zu einer Lösung, die im Prinzip aus 12 D-FlipFlops besteht, die 
D-Leitungen hängen zusammen an der seriellen Datenleitung, die Clocks 
werden nacheinander durch den aktuellen Zählerstand bestimmt bedient. 
Ich muss mir das glaube ich mal aufmalen, auf jeden Fall war das wohl 
der entscheidende Tipp, ich hing noch zu sehr meiner ersten Realisierung 
in C nach.

Dann habe ich ja auch wieder Ressourcen für die andere Richtung, da 
reicht aber ein einfaches Schieberegister, da auch hier das erste 
geschobene Bit das LSB ist, dann kommen 11 weitere und der Rest ist mir 
egal.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sowas in der Art vielleicht:
signal register : std_logic_vector(15 downto 0); -- 16 Bit breites Register
signal cnt : integer range 0 to 16;
...
if (start='1') then  -- bei Start alles zurücksetzen
  register <= (others=>'0'); 
  cnt <= 0;
elsif rising_edge(CLK) then
  if(cnt<16)then             -- nur die Bits von 0..15 beackern
     register(cnt) <= input; -- beim LSB beginnen
     cnt <= cnt+1;
  end if;
end if;
Dann hast du als Abfallprodukt auch noch den Zähler mit der Anzahl der 
übertragenen Bits.

EDIT:
> Das Ende des Datenstroms ist da, wo die Startbedingung erfüllt wird
Das ist ja mal ein krasses Protokoll (Ende wo Start) ;-)
> (LOAD-Leitung wird nach min. 5 aufeinanderfolgenden 0-Zyklen wieder 1).
Also gut: überarbeitet, Schieberegister+Flankenerkennung für die 
LOAD-Leitung zugefügt.
signal ausgaberegister : std_logic_vector(15 downto 0); -- 16 Bit breites Register
signal empfangsregister : std_logic_vector(15 downto 0); -- 16 Bit breites Register
signal cnt : integer range 0 to 16;
signal startsr : std_logic_vector(5 dowto 0) := "000000";
...
if rising_edge(CLK) then
  startsr <= startsr(4 downto 0) & LOAD;
  if (startsr="000001") then  -- 5 mal 0 und dann eine 1 ==> Start 
    ausgaberegister <= empfangsregister ; -- abspeichern, denn es beginnt ja sofort die nächste Übertragung
    cnt <= 0;
  elsif(cnt<16)then               -- nur die Bits von 0..15 beackern
     empfangsregister (cnt) <= input;   -- beim LSB beginnen
     cnt <= cnt+1;
  end if;
end if;

Autor: shaun (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das sieht super aus. Bis auf dass theoretisch das letzte interessierende 
Bit der Nutzdaten auch gleichzeitig mit dem Startbit auf der 
LOAD-Leitung kommen kann.
Dann müsste ich wohl das

 empfangsregister (cnt) <= input;   -- beim LSB beginnen
 cnt <= cnt+1;

ohne die elsif-clause vor die Prüfung der Startbedingung ziehen und noch 
ein

  if(cnt < 16)

drumlegen.

So habe ich zwar wieder die (in der anfangs von mir angedachten Lösung) 
doppelten Register, aber dafür sollten die Ressourcen reichen. Der 
andere Ansatz, den ich oben schon nach Jans Anregung weitergedacht habe, 
wäre ja Flip-Flop-mäßig wesentlich sparsamer, in Logikblöcken gedacht 
(pfui, schematic entry ;) ) bräuchte ich da ja nur die 12 Flipflops für 
die Nutzdaten, vier für den 4-Bit-Zähler und ansonsten nur ein Rudel 
Logik, die die 4 Bit ausdecodiert und den Clock auf die Flipflops 
verteilt.
Zugeben, Dein VHDL-Ansatz sieht wesentlich eleganter aus ;)

Wie sieht's bei dem VHDL-Code eigentlich mit der zeitlichen Parallelität 
aus - startsr wird geschoben, mit LOAD aufgefüllt und verglichen - aber 
wann? Definiert nach dem Schieben?

Du merkst schon: VHDL war vor einigen Jahren mal kurz Thema im Studium, 
seit dem habe ich mich mit nichtprogrammierbarer Elektronik und 
Microcontrollern beschäftigt, aber definitiv nicht mit PLDs. Ich glaube, 
das letzte PLD, das ich in einem Design verbaut habe, war ein PALCE16V8 
um 1999 herum.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> bräuchte ich da ja nur die 12 Flipflops für die Nutzdaten,
> vier für den 4-Bit-Zähler und ansonsten nur ein Rudel Logik...
Dann würde dir aber noch der Puffer fehlen, wenn eine Übertragung 
beginnt wird sofort das Ausgaberegister=Empfangsregister überschrieben.

> Wie sieht's bei dem VHDL-Code eigentlich mit der zeitlichen Parallelität
> aus - startsr wird geschoben, mit LOAD aufgefüllt und verglichen - aber
> wann? Definiert nach dem Schieben?
In meinem Beispielcode wird der eingetaktete Wert erst beim nächsten 
Takt auf die steigende Flanke geprüft. Das muß noch genau aufs Protokoll 
angepasst werden. Das konnte ich aus der Beschreibung noch nicht so ganz 
entnehmen.
Z.B. könnte man den einen Takt Latency so wegbekommen:
signal startsr : std_logic_vector(4 dowto 0) := "00000";
  :
  startsr <= startsr(3 downto 0) & LOAD;
  if (startsr="00000" and LOAD='1') then  -- 5 mal 0 und eine 1 ==> Start 
    ausgaberegister <= empfangsregister ; 
    cnt <= 0;
    :

> Bis auf dass theoretisch das letzte interessierende
> Bit der Nutzdaten auch gleichzeitig mit dem Startbit
Da sollte man mal eine genauere Beschreibung des gesamten Protokolls mit 
ein paar Grenzfällen gesehen haben. Es gibt da offenbar einen Takt 
(läuft der immer durch?), eine Datenleitung und ein Frame-Signal (LOAD). 
Wie spielen die zueinander und miteinander?

Autor: shaun (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, noch mal drübergelesen (hätte ich mich mal eingeloggt, dann könnte 
ich meine Postings vielleicht auch mal editieren...naja, morgen ;) )

Also: zuerst brauche ich bei cnt<16 auf jeden Fall mal das Datenbit bei 
der (übrigens fallenden) Clock-Flanke.

Parallel dazu darf meinetwegen das startsr behandelt werden.

Die Prüfung, ob startsr = 000001 ist, darf aber erst erfolgen, wenn das 
startsr seinen aktuellen Zustand hat - nicht parallel dazu, sonst 
besteht doch wohl die Gefahr, dass die Prüfung von startsr während es 
verändert wird falsch anschlägt, oder...?
Dann muss das Empfangsregister ins Ausgaberegister gerettet und parallel 
dazu cnt auf 0 gesetzt werden.

Preisfrage: reicht es, wenn ich die

  if (startsr="000001") then
    ausgaberegister <= empfangsregister;
    cnt <= 0;
  end if;

in einen Prozess kapsele, oder muss das davor ( Empfangsregister 
schieben, startsr schieben) auch ein Prozess werden?

Hirn wie Sieb...

Autor: shaun (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nebenbei: nein, den Puffer bräuchte ich nicht, weil ich 12 unabhängige 
D-Flipflops nehmen würde und diese nacheinander clocken, die 
Ausgangswerte sind dann zwar asynchron, aber das wäre egal.

Egal, die VHDL-Beschreibung finde ich eleganter.
Das Protokoll sieht aus wie folgt: ein Takt läuft permanent durch.
Data und Load ändern sich mit der steigenden Taktflanke, gesampelt 
werden soll auf die Fallende.
Taucht nach min. 5 Taktzyklen Load=0 dort eine 1 auf, kennzeichnet dies 
das letzte Bit des aktuellen Wortes auf der Datenleitung, bei der 
nächsten(!) fallenden Flanke kann dann das LSB gesampelt werden.
Nach dieser ersten 1 auf Load folgen vier arbiträre Zustände, die nicht 
weiter beachtet werden müssen, danach muss Load bis zum Startkriterium 0 
sein. Das wird durch Dein startsr-Register ja bereits erfüllt, nur die 
Nebenläufigkeit ist mir noch nicht ganz klar.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> oder muss das davor ( Empfangsregister
> schieben, startsr schieben) auch ein Prozess werden?
1) Alles, was getaktet ist, muß in einen Prozess.
2) Ein Signal kann nur von 1 Prozess aus geändert werden.

> nein, den Puffer bräuchte ich nicht,
Dann lass ihn weg ;-)
Du kannst es also auch so schreiben:
process begin -- das geht, startsr wird nur hier manipuliert
  if rising_edge(CLK) then
    startsr <= startsr(4 downto 0) & LOAD;
  end if;
end process;

process begin
  if rising_edge(CLK) then
    if (cnt<16) then
       cnt <= cnt+1;   -- das geht, cnt wird nur in diesem Prozess manipuliert
    end if;
    RX_done <= '0';
    if (startsr="00000" and LOAD='1') then
--      ausgaberegister <= empfangsregister; -- offenbar unnötig
      cnt <= 0;
      RX_done <= '1';
    end if;

  end if;
end process;

process begin
  if rising_edge(CLK) then
    empfangsregister (cnt) <= input;
--      cnt <= cnt+1;   -- das geht nicht, cnt wird in 2 Prozessen geändert
  end if;
end process;

Und irgendwie mußt du nach aussen signalisieren, dass ein Wort komplett 
empfangen wurde (hier RX_done).

> Egal, die VHDL-Beschreibung finde ich eleganter.
Und portabler. Die kannst du ohne Zahnschmerzen dann auch auf einer ganz 
anderen Zielplattform umsetzen.

Autor: shaun (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke,
so erscheint's mir dann auch wieder logisch.
Schade, dass man die Prozesse damals zwar erwähnt hat, aber dann doch 
immer nur Zustandslogik in den Laborstunden zusammengestrickt hat. Wenn 
man's dann nie einsetzen musste...
Den 2. Puffer brauche ich bei Deinem Ansatz aber schon, da habe ich mich 
wohl missverständlich ausgedrückt - hier wird ja der Puffer "irgendwo" 
genullt und dann mit dem LSB beginnend aufgefüllt. Das LSB steht also 
fast den ganzen Zyklus an, das MSB nur für den Rest, der gegen Null 
gehen kann, da per Definition nach dem 10., praktisch nach dem 12. Bit 
ein neuer Start signalisiert werden darf. Folge wäre, dass die LEDs an 
den Registerausgängen unterschiedlich hell wären (sprich: von volle 
Helligkeit bis ganz aus)
Ohne 2. Register ginge es nur mit einzelnen D-Flipflops, wobei jedes 
erst an "seiner" Bitposition getaktet würde.
Dann würden die Ausgänge zwar asynchron schalten, aber immer die gleiche 
Zeitspanne ihren Pegel halten.

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ohne 2. Register ginge es nur mit einzelnen D-Flipflops, wobei jedes
> erst an "seiner" Bitposition getaktet würde.
Genau das wird mit meinem letzten Ansatz gemacht:
Das Empfangsregister selber wird nicht mehr "genullt". Es wird immer 
"nur" das aktuell addressierte Bit überschrieben und am Ende der 
Übertragung (also bei Erkennen der Startbedingung) das RX-done Flag 
gesetzt.

Autor: shaun (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nee doch nicht ... die Prozesse haben doch keine Sensitivitätsangaben, 
die werden doch nach wie vor nebenläufig ausgeführt - oder?!
Eigentlich brauche ich ja nur zwei definiert sequentiell auszuführende 
Blöcke:

1. wenn cnt<16 ( empfangsregister(cnt)=LOAD; cnt++ )
   startsr shiftleft(1) & LOAD

2. wenn startsr=000001 ( ausgabe=empfangsregister; cnt=0; )

(startsr=000001 war doch richtig, das LSB kommt ja erst beim Takt NACH 
dem, in dem LOAD wieder 1 wird)

Wenn 1 und 2 wirklich GLEICHZEITIG bei der fallenden CLK-Flanke 
ausgeführt werden wäre das ja absolut richtig, denn startsr ist ja im 
MOMENT der Flanke noch x00000 und wird erst durch den Schiebevorgang zu 
000001, der GLEICHZEITIG erfolgende Vergleich mit 000001 würde also 
negativ ausfallen und Ausgaberegister ebenso wie cnt in Ruhe gelassen.

Wenn ich in normaler, verdrahteter Logik denke (fällt mir immer noch 
irgendwie leichter...) hätte ich also ein Schieberegister mit 
flankensensitivem Eingang, welches LOAD durchschiebt, da dran hängt ein 
Komparator mit 000001 belegt und dessen Match-Ausgang ist am D-Eingang 
eines flankensensitiven D-FFs angeschlossen, welches ebenfalls am CLK 
hängt.
So eine Schaltung würde den Zustand des Komparators im Moment der 
fallenden Flanke latchen, es sei denn, das Schieberegister und der 
Komparator sind irre schnell und das FF etwas träge, dann kann es zu 
falschen Werten kommen - und genau das ist meine Sorge bei dem 
VHDL-Code.

Weisst Du, was ich meine?

Autor: shaun (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Genau das wird mit meinem letzten Ansatz gemacht:

Stimmt, übersehen!

In meinem Pseudocode habe ich LOAD und DATA verwechselt...

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Nee doch nicht ... die Prozesse haben doch keine Sensitivitätsangaben,
> die werden doch nach wie vor nebenläufig ausgeführt - oder?!
Das macht zum Glück bei der Synthese nichts aus. Da werden nach Ausgeben 
einer freundlichen Warnung die Listen einfach automatisch erweitert.

Für die Simulation sollte allerdings der CLK in die Liste aufgenommen 
werden.

BTW:
Der Fehler kommt daher, dass ich getaktete Prozesse gern so schreibe:
process begin
  wait until rising_edge(CLK);
  empfangsregister(cnt) <= input;
end process;
statt:
process (CLK) begin
  if rising_edge(CLK) then
    empfangsregister(cnt) <= input;
  end if;
end process;
Aber das wäre für den Anfang etwas verwirrend geswesen ;-)
Man möge mir diese Unzulänglichkeit nachsehen.

Autor: schwarz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
wie kann ich einen testbench von einem 16 bits Schieberegister in VHDL 
entwerfen(implementieren)

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Willst du in einer Testbench ein Schieberegister simulieren, oder 
willst du eine Testbench für ein Schieberegister realisieren?

Welche Steuersignale hast du ausser dem Takt und den Daten?


EDIT:
Mach doch besser einen neuen Thread auf, als einen alten zu kapern.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.