Forum: FPGA, VHDL & Co. Schieberegister andersrum


von shaun (Gast)


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?

von Jan M. (mueschel)


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.

von shaun (Gast)


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)

von Jan M. (mueschel)


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.

von shaun (Gast)


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.

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


Lesenswert?

Sowas in der Art vielleicht:
1
signal register : std_logic_vector(15 downto 0); -- 16 Bit breites Register
2
signal cnt : integer range 0 to 16;
3
...
4
if (start='1') then  -- bei Start alles zurücksetzen
5
  register <= (others=>'0'); 
6
  cnt <= 0;
7
elsif rising_edge(CLK) then
8
  if(cnt<16)then             -- nur die Bits von 0..15 beackern
9
     register(cnt) <= input; -- beim LSB beginnen
10
     cnt <= cnt+1;
11
  end if;
12
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.
1
signal ausgaberegister : std_logic_vector(15 downto 0); -- 16 Bit breites Register
2
signal empfangsregister : std_logic_vector(15 downto 0); -- 16 Bit breites Register
3
signal cnt : integer range 0 to 16;
4
signal startsr : std_logic_vector(5 dowto 0) := "000000";
5
...
6
if rising_edge(CLK) then
7
  startsr <= startsr(4 downto 0) & LOAD;
8
  if (startsr="000001") then  -- 5 mal 0 und dann eine 1 ==> Start 
9
    ausgaberegister <= empfangsregister ; -- abspeichern, denn es beginnt ja sofort die nächste Übertragung
10
    cnt <= 0;
11
  elsif(cnt<16)then               -- nur die Bits von 0..15 beackern
12
     empfangsregister (cnt) <= input;   -- beim LSB beginnen
13
     cnt <= cnt+1;
14
  end if;
15
end if;

von shaun (Gast)


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.

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


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:
1
signal startsr : std_logic_vector(4 dowto 0) := "00000";
2
  :
3
  startsr <= startsr(3 downto 0) & LOAD;
4
  if (startsr="00000" and LOAD='1') then  -- 5 mal 0 und eine 1 ==> Start 
5
    ausgaberegister <= empfangsregister ; 
6
    cnt <= 0;
7
    :

> 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?

von shaun (Gast)


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...

von shaun (Gast)


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.

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


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:
1
process begin -- das geht, startsr wird nur hier manipuliert
2
  if rising_edge(CLK) then
3
    startsr <= startsr(4 downto 0) & LOAD;
4
  end if;
5
end process;
6
7
process begin
8
  if rising_edge(CLK) then
9
    if (cnt<16) then
10
       cnt <= cnt+1;   -- das geht, cnt wird nur in diesem Prozess manipuliert
11
    end if;
12
    RX_done <= '0';
13
    if (startsr="00000" and LOAD='1') then
14
--      ausgaberegister <= empfangsregister; -- offenbar unnötig
15
      cnt <= 0;
16
      RX_done <= '1';
17
    end if;
18
19
  end if;
20
end process;
21
22
process begin
23
  if rising_edge(CLK) then
24
    empfangsregister (cnt) <= input;
25
--      cnt <= cnt+1;   -- das geht nicht, cnt wird in 2 Prozessen geändert
26
  end if;
27
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.

von shaun (Gast)


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.

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


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.

von shaun (Gast)


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?

von shaun (Gast)


Lesenswert?

> Genau das wird mit meinem letzten Ansatz gemacht:

Stimmt, übersehen!

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

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


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:
1
process begin
2
  wait until rising_edge(CLK);
3
  empfangsregister(cnt) <= input;
4
end process;
statt:
1
process (CLK) begin
2
  if rising_edge(CLK) then
3
    empfangsregister(cnt) <= input;
4
  end if;
5
end process;
Aber das wäre für den Anfang etwas verwirrend geswesen ;-)
Man möge mir diese Unzulänglichkeit nachsehen.

von schwarz (Gast)


Lesenswert?

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

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


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.

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.