Forum: FPGA, VHDL & Co. std_logic Rotation


von Ralf (Gast)


Lesenswert?

Hallo zusammen,

ich habe einen 24-bit Vektor.
Dieser beinhaltet immer ein Paket von zusammenhängenden Einsen. Die 
Anzahl der Einsen ist variabel von 0 bis 24.

Nun möchte ich dieses Paket von Einsen nach links rotieren lassen.
Also habe ich folgendes in einem getakteten process geschrieben.
1
rotation_vektor <= rotation_vektor(rotation_vektor'HIGH-1 downto rotation_vektor'LOW) & rotation_vektor(rotation_vektor'HIGH);

Nun möchte ich die Anzahl der Einsen vergrößern bzw. verkleinern und 
zwar immer so, dass die Einsen am rechten Ende das Paketes dazu- oder 
wegkommen.
Nun kommt das Entscheidende:
Ich möchte/muss die Lage des Paketes mit der neuen Anzahl von Einsen 
nicht neu festlegen, sondern die Rotation soll wie gewohnt weiter gehen, 
nur eben im kommenden Takt mit ein paar Einsen mehr oder weniger.

Ich müsste also irgendwie das rechte Ende das Paketes finden und dann 
die Einsen dranhängen oder abziehen. Dazu habe ich leider noch keine 
sinnvolle Idee gefunden.

Oder könnte man vielleicht ein Signal mit nur Einsen mit variabler 
Länge, nämlich der Anzahl von Einsen, durch den 24-bit Vektor schieben?

Vielen Dank!
Ralf

von Spartanist (Gast)


Lesenswert?

Beschreibe das mal beispielhaft in einer Grafik, damit wir wissen, was 
Du tun willst.

von berndl (Gast)


Lesenswert?

z.B. indem du einen 'head' und einen 'tail' pointer baust. Der 'head' 
wird einfach mit inkrementiert, der 'teil' hat dann halt inkrement sowie 
addition/subtraktion dabei.
Wenn du das einmal richtig initialisierst laeuft das spaeter auch 
einfach so mit und gibt dir die Start/Endeposition des Einserblocks 
an...

von Ralf (Gast)


Lesenswert?

Hallo,

Spartanist schrieb:
> Beschreibe das mal beispielhaft in einer Grafik, damit wir wissen, was
> Du tun willst.

hier mal ein Bsp mit 12bit:

000000001111 (Anzahl = 4)
000000011110
000000111100
000001110000 (neue Anzahl = 3)
000011100000
000111000000
001111110000 (neue Anzahl = 6)
011111100000
111111000000
111110000001
000000000010 (neue Anzahl = 1)
000000000100

Die linke 1 wird also immer ein Bit weiter nach links geschoben, während 
sich die Anzahl der Einsen dahiner ändern kann.

Die Begriffe 'head' und 'tail' pointer höre ich das erste Mal, ich 
versuche mal etwas darüber herauszufinden.

Vielen Dank!
Ralf

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


Lesenswert?

Ich würde da eine einzige '1' durchschieben, und hinterher die übrigen 
Einsen "anhängen". Das Ganze gibt dann einen Riesenmonstermultiplexer...

von berndl (Gast)


Lesenswert?

eigentlich sind das doch nur read/write pointer eines FIFOs, ok, mit 
etwas Zusatzlogik.
Die Anzahl der Einsen kriegt man dann durch einfache Subtraktion raus

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


Lesenswert?

Aber der Trick ist, dass das nach der bisherigen Beschreibung offenbar 
in 1 Takt erledigt werden muss, und man sich den Ergebnisvektor nicht in 
bis zu 24 Takten "zusammenbasteln" kann...

Hier mal meine Lösung:
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
entity SRvarOnes is
6
    Port ( clk    : in  STD_LOGIC;
7
           Ones   : in  STD_LOGIC_VECTOR (4 downto 0);
8
           Output : out  STD_LOGIC_VECTOR (23 downto 0));
9
end SRvarOnes;
10
11
architecture Behavioral of SRvarOnes is
12
  signal mastersr : std_logic_vector (23 downto 0) := x"000001";
13
begin
14
  mastersr <= mastersr(22 downto 0) & mastersr(23) when rising_edge(clk);
15
16
  process 
17
  variable einser : integer range 0 to 24;
18
  variable sr : std_logic_vector (23 downto 0);
19
  begin
20
    wait until rising_edge(clk);
21
    sr     := (others=>'0');  -- default: Alles Nullen
22
    einser := to_integer(unsigned(Ones));
23
    if (einser /= 0) then     -- wenn mindestens 1 Einser
24
      sr     := mastersr;
25
      einser := einser-1;
26
      for i in sr'range loop  -- 24 Bits durchmachen ...
27
        if (einser/=0) then   -- ... aber nur, wenn noch Einser aufzufüllen sind
28
          sr     := sr or (sr(0) & sr(23 downto 1)); 
29
          einser := einser-1;                    
30
        end if;
31
      end loop;
32
    end if;
33
    Output <= sr;
34
  end process;
35
end Behavioral;

Wenn natürlich nicht in jedem Quarz-Takt um eins weitergeschoben und 
der neue Registerwert berechnet werden muss, dann ist jede andere Lösung 
einfacher und ressourcensparender...

von Ralf (Gast)


Lesenswert?

DANKE alle zusammen und vor allem Lothar!!!

Ich hatte in der Zwischenzeit mit den HEAD und TAIL Pointern gearbeitet 
und im Ansatz eine Lösung erarbeitet, hatte nur noch Probleme mit dem 
"Überlauf" auf die Stelle 0, weil dann der HEAD Pointer auf einmal 
kleiner war als der TAIL Pointer... wärhend ich darüber gegrübelt hatte, 
wollte ich mir Lothar's Lösung anschauen und bin begeistert.

Ich möchte nur noch eine Frage an Lothar loswerden:
Wie kommt man aus dem Stehgreif auf so eine Lösung?
Wenn man sich den Code anschaut und Schritt für Schritt durchgeht, 
versteht man alles und es ist logisch.
Aber ich würde aktuell nie alleine auf eine solche Lösung kommen.
Nur immer eine 1 durchshiften und dann immer mit der Rechtsrotation und 
der OR Verknüpfung die umliegenden Stellen bestimmen... mega gut!

Ich glaube da muss ich meine Synapsen noch etwas in dieser Richtung 
trainieren, um auch irgendwann eine solche Lösung erarbeiten zu können.

Vielen, vielen Dank!

von T. M. (xgcfx)


Lesenswert?

Lothar Miller schrieb:
> mastersr <= mastersr(22 downto 0) & mastersr(23) when rising_edge(clk);

Hoppla, das geht ja wirklich durch die Synthese. Rein syntaktisch ist 
das klar, aber ich hätte nicht gedacht, dass das funktioniert.
Schön kurze Beschreibung eines Registers :)

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


Lesenswert?

T. M. schrieb:
> Hoppla, das geht ja wirklich durch die Synthese. Rein syntaktisch ist
> das klar, aber ich hätte nicht gedacht, dass das funktioniert.
Geht auch erst seit diesem Jahrzehnt...   ;-)

Ralf schrieb:
> Ich möchte nur noch eine Frage an Lothar loswerden:
> Wie kommt man aus dem Stehgreif auf so eine Lösung?
Man hat ca. 400 mehr oder weniger kleine Testprojekte auf der Platte, 
von denen ca. 2/3 funktionieren...  ;-)

von Ralf (Gast)


Lesenswert?

Lothar Miller schrieb:
1
for i in sr'range loop  -- 24 Bits durchmachen ...
2
         if (einser/=0) then
3
           sr     := sr or (sr(0) & sr(23 downto 1)); 
4
           einser := einser-1;                    
5
         end if;
6
       end loop;

Sehe ich das richtig, das die Laufzeit mit der Anzahl der Einsen 
variiert? Also je mehr EInsen vorhanden sind, umso mehr Gatter werden 
durchlaufen?

Ich versuche nun rein interesse halber, den Code mal auf vielleicht 2 
Takte auszubauen, um ihn ressourcenschonender zu machen.
Laut Quartus sind es aktuell ca. 5000 Gatter...

Vielen Dank!
Ralf

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


Lesenswert?

Ralf schrieb:
> Sehe ich das richtig, das die Laufzeit mit der Anzahl der Einsen
> variiert? Also je mehr EInsen vorhanden sind, umso mehr Gatter werden
> durchlaufen?
Nein, das Ganze wird sowieso optimiert und parallel aufgebaut. Sieh dir 
einfach den RTL-Plan an...

> Ich versuche nun rein interesse halber, den Code mal auf vielleicht 2
> Takte auszubauen, um ihn ressourcenschonender zu machen.
Na bitte, schon das 2. Projekt. Dieser Prozess nennt sich "Lernen".
Weiter so... ;-)

von Ralf (Gast)


Lesenswert?

Lothar Miller schrieb:
> Nein, das Ganze wird sowieso optimiert und parallel aufgebaut. Sieh dir
> einfach den RTL-Plan an...

Das habe ich versucht. Da spuckt mir Quartus 164 Seiten schematic aus, 
mit wild verschalteten Elementen, die mir die Interpretations des 
Syntheseergebnisses nicht gerade einfach machen. :-)

Ich glaube ich vertraue erstmal Quartus mit den "Total logic elements" 
und freue mich, wenn ich die Funktion auch mit zwei Takten Delay 
hinbekomme und dafür vielleicht nur 1200 Zellen benötige.

Ich werde meinen Fortschritt wieder hier posten.

Vielen Dank!

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


Lesenswert?

Ralf schrieb:
> Da spuckt mir Quartus 164 Seiten schematic aus
Klar: viel Logik braucht viel Platz...  ;-)

von Ralf (Gast)


Lesenswert?

Mir fällt gerade auf, dass ich statt einem Delay von zwei Takten, eine 
Pipeline mit zwei Takten Latenz brauche.

Ich müsste also am Ausgang mit jedem Takt ein Ergebnis haben, dass eben 
nur dem Eingang von vor zwei Takten entspricht. Das dürfte einen 
komplett anderen Dankansatz erfordern, als die Lösung von Lothar 
hergibt. Denn ich habe gerade keine Idee, wie ich innerhalb von zwei 
Takten eine Eins in dem Vektor rotieren lassen kann.

Das vereinfacht die Sache nicht gerade, aber ich versuche mal mein Glück 
:)

Vielen Dank!
Ralf

von Uwe (Gast)


Lesenswert?

> Denn ich habe gerade keine Idee, wie ich innerhalb von zwei
> Takten eine Eins in dem Vektor rotieren lassen kann.
Riesigen Multiplexer bzw. Barrel Shifter bzw. Hardwaremultiplizierer 
lassen sich als Barrelshifter benutzen.
Du könntest aber auch einfach den Takt mit nem PLL vervielfachen und das 
Desdign von Lthar mit einer n mal höheren Clock als das restliche Design 
laufen lassen.

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


Lesenswert?

Uwe schrieb:
> Desdign von Lothar mit einer n mal höheren Clock
> als das restliche Design laufen lassen.
Tja, wenn das Monster das könnte. Ich habe nur 40MHz erreicht...  :-(

von Ralf (Gast)


Lesenswert?

Der Takt sollte nicht das Problem sein, denn ich arbeite nur mit 25MHz. 
Dann schon eher die Ressourcen... ich möchte beide Varianten mal direkt 
vergleichen, was Platz und Latenz anbelangt...

von Ralf (Gast)


Lesenswert?

Hallo,

ich habe jetzt den Prozess der OR-Verknüpfung geteilt, um damit eine Art 
Pipeline zu bauen, die dann einen Takt Latenz bringt.

Ich hatte erwartet, dass ich dadurch die Hälfte der Gatter einsparen 
kann. Nach der Simulation und beim Betrachten des Syntheseergebisses ist 
mir dann aber aufgefallen, dass bei einer Pipeline ja nichts eingespart 
werden kann, da ja die Gatter nötig sind, um dann wieder den nächsten 
Eingangswert zu bearbeiten.

Ich habe also die Hälfte der Gatter um den ersten Teil zu bearbeiten und 
die zweite Hälfte bearbeitet den letzten Teil in der Pipeline. dazu 
kommt noch die Glue-Logic der beiden Komponenten... in Summe ist es also 
noch mehr geworden.

Habe ich da noch irgendeinen Denkfehler, oder kann ich bei der 
Pipelinegeschichte partout keine Gatter einsparen?
Irgendwie sehe ich darin keinerlei Vorteile, weil ich jetzt eigentlich 
mit mehr Gattern einen Takt mehr Latenz habe... sehr sinnfrei ;-)

Wenn ich nur jeden zweiten Eingangswert bearbeiten müsste, könnte ich 
einsparen, aber so...

Ich habe den Eingangswert jetzt auf 23 Bit gekürzt...
1
mastersr  <= mastersr (21 downto 0) & mastersr (22) when rising_edge(CLK);
2
3
rotation1 : process (CLK)
4
  variable einser : integer range 0 to 23;
5
  variable sr : std_logic_vector (22 downto 0);
6
begin
7
  if rising_edge(CLK) then
8
   sr   := (others => '0');
9
     einser := conv_integer(Ones);
10
     if (einser /= 0) then
11
        sr   := mastersr ;
12
        einser := einser - 1;
13
        for i in 0 to 11 loop 
14
           if (einser/=0) then
15
              sr     := sr or (sr(0) & sr(22 downto 1)); 
16
              einser := einser - 1;                    
17
           end if;
18
        end loop;
19
     end if;
20
     s_sr <= sr;       -- Wert an rotation2 übergeben
21
   s_einser <= einser; -- Wert an rotation2 übergeben
22
  end if;  
23
end process;
24
25
rotation2 : process (CLK)
26
  variable einser : integer range 0 to 23;
27
  variable sr : std_logic_vector (22 downto 0);
28
begin
29
  if rising_edge(CLK) then
30
   sr   := s_sr;     -- Wert von rotation1 übernehmen
31
   einser := s_einser; -- Wert von rotation1 übernehmen
32
     if (einser /= 0) then -- wenn mehr als 11 Einsen im Eingangsvektor
33
        sr   := s_sr;
34
        for i in 0 to 12 loop
35
           if (einser/=0) then
36
              sr     := sr or (sr(0) & sr(22 downto 1)); 
37
              einser := einser - 1;                    
38
           end if;
39
        end loop;
40
     end if;
41
     Output <= sr;
42
  end if;  
43
end process;

von Ralf (Gast)


Lesenswert?

Ich habe meinen Denkfehler gefunden:
Die Pipeline würde nur Sinn machen, wenn die beiden Teilaufgaben nicht 
in einem Takt abhandelbar wären, also meinetwegen bei 60MHz. Dann 
zerteilt man die Aufgabe und hat bei doppelter Fläche eine effektive 
Taktrate von 30MHz, aber bei jedem 60Mhz Takt dennoch ein Ergebnis am 
Ausgang mit einer Latenz von einem Takt.

Folgende Darstellung der Arbeitspakete:

Serielle Bearbeitung:
|  A  |  B  |  A  |  B  |

Pipeline mit doppelter Gatteranzahl:
      |  A  |  B  |  A  |  B  |
|  A  |  B  |  A  |  B  |

Man erkauft sich also mit doppelter Gatteranzahl und einem Takt Latenz, 
dass man dennoch mit jedem Takt ein Ergebnis erhält. "There's no free 
lunch!"

In meinem Fall sieht es so aus:
Lösung Lothar:
|  A  |  A  |  A  |  A  |
|  B  |  B  |  B  |  B  |

Lösung Lothar mit Pipeline:
      |  A  |  B  |  A  |  B  |
|  A  |  B  |  A  |  B  |

Ich habe also bei gleicher Gatteranzahl nur einen Takt Latenz dazu 
bekommen und habe dadurch keinen Vorteil.

Ich muss also wohl, wenn ich mit jedem Takt ein Ergebnis haben möchte, 
mit den 5k Gattern leben.

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


Lesenswert?

Was mich noch interessieren würde: Wofür braucht man  sowas?

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.