Forum: FPGA, VHDL & Co. rechnen mit unsigned


von Batzi (Gast)


Lesenswert?

Hallo, habe schon andere Beiträge gesichtet, die sich mit unsigned 
befassen, konnte aber das Problem nicht lösen.

Ich habe eine komisches Problem mit ModelSIM. Er meldet:

Time: 10 ns  Iteration: 1  Instance: /btm_tb/dut
# ** Fatal: (vsim-3420) Array lengths do not match. Left is 32 (31 
downto 0). Right is 64 (63 downto 0).

und das hier ist der Befehl:
1
OUT_NEW <= VAL_OLD * 2 + ROT_OLD;

Und das die inits (für alle gleich)
1
signal OUT_NEW : unsigned(WIDTH - 1 downto 0)    := to_unsigned (0, WIDTH);

WIDTH ist 32. ModelSIM zeigt auch alle Signale mit 31 downto 0 an.

Wie schreibe ich die Formel?
Die Werte sind so, dass es kein Überlauf geben kann.

Ich habe auch probiert:
1
OUT_NEW <= VAL_OLD & '0' + ROT_OLD;
dann meckert er wegen 33 Bit.

Ist eigentlich klar, aber ich würde gerne das Herumbasteln mit Vektoren 
vermeiden.

Vor allem würde mich interessieren, wie er auf die doppelte Zahl der 
Bits kommt!

von Theor (Gast)


Lesenswert?

Modelsim weiß jetzt nicht so genau.
Aber ich würde vermuten, dass er die 2 schon als 32 Bit auffasst und 
deswegen auf die 64 Bit kommt. Das könnte er besser wissen und 33 
annehmen, aber naja. Muss man mal in der Doku schauen.

VAL_OLD & '0' ist, nach dem was Du über die Breite von VAL_OLD sagst, 
nun einmal auch 33 Bit lang. Mit dem Summanden ergibt das potentiell 
noch 34 Bit.

Du wirst nicht darum herum kommen, mit Vektoren zu fummeln, denke ich.

Es gibt Ausdrücke mit denen man auf der rechten Seite, den Vektor nach 
der Operation in der Breite verändern kann.

Aber ich rate Dir zunächst mal ganz genau zu überlegen wie groß die 
Summanden sein können. Du sagst ja nur, die sind nicht so groß, aber da 
Du keine Einzelheiten angibst, kann man da nichts raten, als das: Schaus 
Dir genau an.

von A. S. (Gast)


Lesenswert?

Das + bei Signalen ist keine Rechenoperation, sondern eine 
Aneinanderreihung.

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


Lesenswert?

Achim S. schrieb:
> Das + bei Signalen ist keine Rechenoperation, sondern eine
> Aneinanderreihung.
Das ist neu. In welchem Package ist dieser '+' Operator derartig 
überladen?
Im numeric_std (z.B. bei 
https://www.csee.umbc.edu/portal/help/VHDL/packages/numeric_std.vhd 
)findet sich:
1
     -- Id: A.3
2
  function "+" (L,R: UNSIGNED ) return UNSIGNED;
3
     -- Result subtype: UNSIGNED(MAX(L'LENGTH, R'LENGTH)-1 downto 0).
4
     -- Result: Adds two UNSIGNED vectors that may be of different lengths.

Also ist üblicherweise für unsigned Vektoren der '+' Operator eine 
Addition. Für eine Concatenation nimmt man den '&' Operator.

> Das + bei Signalen ist keine Rechenoperation, sondern eine
> Aneinanderreihung.
Signale können alles Mögliche sein, z.B. auch die Zustände von FSM. 
Irgendwie ist dann auch klar, dass man den Zustand "idle" natürlich 
nicht mit dem '+' Operator auf den Zustand "run" addieren kann um einen 
anderen Zustand zu "berechnen".
Und natürlich kann ich für meine selbst definierten Typen den '+' 
Operator mit beliebigen Funktionen überladen. Aber für bekannte und 
allgemein verwendete Datentypen und Vektoren ist der '+' Operator eine 
Addition.

Batzi schrieb:
> Vor allem würde mich interessieren, wie er auf die doppelte Zahl der
> Bits kommt!
Das ist allerdings interessant. Man könnte fast meinen, der kommt mit 
der Priorität der Operatoren durcheinander. Was passiert, wenn du das 
zusätzlich zum impliziten "Punkt vor Strich" noch klammerst?
OUT_NEW <= (VAL_OLD * 2) + ROT_OLD;

Welche Packages verwendest du?
Kannst du mal einen ausführbaren "Dreizeiler" posten?

Batzi schrieb:
> Ich habe auch probiert: OUT_NEW <= VAL_OLD & '0' + ROT_OLD;
> dann meckert er wegen 33 Bit.
Spricht eigentlich was gegen Rechnungen mit Integern, wenn du sowieso 
die "überzähligen" Bits ignorieren willst?

: Bearbeitet durch Moderator
von Rudolph (Gast)


Lesenswert?

Batzi schrieb:
> und das hier ist der Befehl:OUT_NEW <= VAL_OLD * 2 + ROT_OLD;

Das Ausgangsvektor der Multiplikation ist so groß wie die addierte Länge 
der Eingangsvektoren. Siehe die Lothar verlinkte numeric_std:
1
     -- Id: A.15
2
  function "*" (L,R: UNSIGNED ) return UNSIGNED;
3
     -- Result subtype: UNSIGNED((L'length+R'length-1) downto 0).
4
     -- Result: Performs the multiplication operation on two UNSIGNED vectors
5
     --         that may possibly be of different lengths.

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


Lesenswert?

Rudolph schrieb:
> Das Ausgangsvektor der Multiplikation ist so groß wie die addierte Länge
> der Eingangsvektoren.
Das war ja einfach... ;-)
Allerdings gilt hier diese Funktionsdefinition, weil '2' ja kein 
unsigned Vektor, sondern ein natürlicher Wert ist:
1
     -- Id: A.17
2
  function "*" ( L: UNSIGNED; R: NATURAL) return UNSIGNED;
3
     -- Result subtype: UNSIGNED((L'length+L'length-1) downto 0).

Ich würde also ebenso einfach den "resize" Hebel ansetzen. Wenn es eine 
globale WIDTH gibt, dann ist das ja nicht allzu umständlich:
1
OUT_NEW <= resize(VAL_OLD * 2 + ROT_OLD, WIDTH);

: Bearbeitet durch Moderator
von Duke Scarring (Gast)


Lesenswert?

Lothar M. schrieb:
> Ich würde also ebenso einfach den "resize" Hebel ansetzen. Wenn es eine
> globale WIDTH gibt, dann ist das ja nicht allzu umständlich:
> OUT_NEW <= resize(VAL_OLD * 2 + ROT_OLD, WIDTH);
Und wenn es keine globale WIDTH gibt, dann geht es so:
1
OUT_NEW <= resize(VAL_OLD * 2 + ROT_OLD, OUT_NEW'length);

von Batzi (Gast)


Lesenswert?

Lothar M. schrieb:
> Batzi schrieb:
>> Ich habe auch probiert: OUT_NEW <= VAL_OLD & '0' + ROT_OLD;
>> dann meckert er wegen 33 Bit.
> Spricht eigentlich was gegen Rechnungen mit Integern, wenn du sowieso
> die "überzähligen" Bits ignorieren willst?

Weil es keinen Überlauf geben kann und ich nicht unnötig umhercasten 
möchte. Das Ergebnis wird wieder vorne reingeworfen, sodass eine 
Iteration entsteht. Der unsigned würde dann immer größer werden. Er muss 
also beschnitten werden.

Weiter vorne mache ich das - außerhalb eines Prozesses! - auch so:
1
TMP_SUM <= VAL_SUM + ROT_OLD;

Sind auch beides 32 Bit. Dort meckert der ModelSIM nicht wegen eines 
Überlaufes, sondern ihm sind die Breiten genehm. Dort könnte allerdings 
auch ein Überlauf passieren, es ist nur in meiner Anwendung nicht 
möglich, weil ich weiß, was vorne reinkommt und wie lange iteriert wird. 
Das kann ModelSIM aber garantiert nicht sehen.

Wie auch immer: Ich habe nun resize verwendet und es geht!

Ich möchte aber dennoch die Frage stellen, wie man das in VHDL am besten 
macht:

Es kann ja nicht sein, dass man nicht einfach C = A * B schreiben kann 
und er kriegt die Breiten selber raus, bzw multipliziert und addiert so, 
dass das mathematisch richtige Ergebnis rauskommt ohne dass ich immer 
mit den Breiten tricksen muss, dass es stimmt. Das macht den Code nicht 
gerade leserlich.

von Batzi (Gast)


Lesenswert?

Lothar M. schrieb:
> Welche Packages verwendest du?
> Kannst du mal einen ausführbaren "Dreizeiler" posten?

library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;

Posten darf ich den Code nicht. Firmeneigentum :-)


Lothar M. schrieb:
> OUT_NEW <= (VAL_OLD * 2) + ROT_OLD;

Das hatte keine Wirkung zurfolge.
Ich gehe davon aus, dass er zunächst die 2 in einen passenden Vektor 
übersetzt und dann ein 32Bit x 32Bit erkennt.

Ich werde mich jetzt mal an der Nutzung eines signed probieren. Das 
steht als kommend an. (Meine Iteration muss sich auch pendelnd annähern 
können).

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


Lesenswert?

Batzi schrieb:
> Es kann ja nicht sein...
Weil VHDL stark typisiert ist und nicht hintenrum implizit gemacht wird, 
ist es aber trotzdem so. Fazit: hinnehmen oder Verilog machen. Da ist 
das wesentlich lascher gehalten... ?

Batzi schrieb:
> Ich gehe davon aus, dass er zunächst die 2 in einen passenden Vektor
> übersetzt und dann ein 32Bit x 32Bit erkennt.
Ja, ich habe ja die Funktion für die Multiplikation gepostet. Da steht 
es drin, was mit dem Eingangsvektor passiert.

: Bearbeitet durch Moderator
von Duke Scarring (Gast)


Lesenswert?

Batzi schrieb:
> Es kann ja nicht sein, dass man nicht einfach C = A * B schreiben kann
> und er kriegt die Breiten selber raus, bzw multipliziert und addiert so,
> dass das mathematisch richtige Ergebnis rauskommt ohne dass ich immer
> mit den Breiten tricksen muss, dass es stimmt.
In meinen Augen ist das eine Inkon­sis­tenz im VHDL-Standard.
Bei der Addition wird leider ungefragt abgeschnitten, während die 
Multiplikation saubere Bitbreiten erfordert.

Duke

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


Lesenswert?

Duke Scarring schrieb:
> In meinen Augen ist das eine Inkon­sis­tenz im VHDL-Standard.
Oder eher in der Umsetzung...

Denn die letztlich zur Addition verwendete Funktion ADD_UNSIGNED() nimmt 
zwar ein Carry an, gibt aber keinen Übertrag mehr aus:
1
 -- this internal function computes the addition of two UNSIGNED
2
 -- with input CARRY
3
 -- * the two arguments are of the same length
4
5
function ADD_UNSIGNED ( L,R: UNSIGNED; C: STD_LOGIC ) return UNSIGNED  is
6
constant L_left:INTEGER:= L'length-1;
7
alias XL: UNSIGNED(L_left downto 0) is L;
8
alias XR: UNSIGNED(L_left downto 0) is R;
9
variable RESULT: UNSIGNED(L_left downto 0);
10
variable CBIT : STD_LOGIC:= C;
11
begin
12
  for i in 0 to L_left loop
13
    RESULT(i) := CBIT xor XL(i) xor XR(i);
14
    CBIT := (CBIT and XL(i)) or (CBIT and XR(i)) or (XL(i) and XR(i));
15
    end loop;
16
  return RESULT;
17
  end ADD_UNSIGNED;
(Auszug aus 
https://www.csee.umbc.edu/portal/help/VHDL/packages/numeric_std.vhd)

> während die Multiplikation saubere Bitbreiten erfordert.
Oder im Falle einer Multiplikation z.B. eines n-Bit Vektors mit einem 
Integer >2**n-1 wenigstens eine Warnung ausgibt, weil ja die 
Multiplikation nur 2*n Bits Platz vorsieht und den Integer auf die 
Anzahl Bits des unsigned Vektros zusammenstutzen will:
1
     -- Id: A.17
2
function "*" ( L: UNSIGNED; R: NATURAL) return UNSIGNED is
3
begin
4
  return L *  TO_UNSIGNED( R , L'length); -- <-- der Integer muss in die Länge des UNSIGNED Vektors passen
5
  end;
6
7
:
8
:
9
10
function TO_UNSIGNED(ARG,SIZE: NATURAL) return UNSIGNED is
11
  variable RESULT: UNSIGNED (SIZE-1 downto 0) ;
12
  variable i_val:natural := ARG;
13
  begin
14
  if (SIZE < 1) then return NAU; end if;
15
  for i in 0 to RESULT'left loop
16
    if (i_val MOD 2) = 0 then
17
       RESULT(i) := '0';
18
    else RESULT(i) := '1' ;
19
      end if;
20
    i_val := i_val/2 ;
21
    end loop;
22
  if not(i_val=0) then
23
    assert NO_WARNING 
24
    report "numeric_std.TO_UNSIGNED : vector truncated" -- <-- sonst kommt eine Fehlermeldung!
25
    severity WARNING ;
26
    end if;
27
  return RESULT ;
28
  end TO_UNSIGNED;

von Batzi (Gast)


Lesenswert?

Lothar M. schrieb:
> Das war ja einfach... ;-)
> Allerdings gilt hier diese Funktionsdefinition, weil '2' ja kein
> unsigned Vektor, sondern ein natürlicher Wert ist:

Da fällt mir natürlich spontan die Frage auf, warum das so ist?
Wenn die 2, hier ein natural, massgeblich ist für die effektive Breite 
des Vektors, müsste man nicht pauschal die Länge der Vektors L als 
Breite nehmen, wie es dort steht:

-- Result subtype: UNSIGNED((L'length+L'length-1) downto 0).

Denn was passiert, wenn ein 8 Bit unsigned mit 700 multipliziert ?
Dann würden 2x8 = 16 nicht reichen.

Warum benutzt er nicht "Länge von A" + "Länge von B"?

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


Lesenswert?

Batzi schrieb:
> Denn was passiert, wenn ein 8 Bit unsigned mit 700 multipliziert ?
Probiers einfach aus. Oder sieh dir den Code dieses Operators 
UNSIGNED*INTEGER an.

Fazit: Meldung "vector truncated"

> Warum benutzt er nicht "Länge von A" + "Länge von B"?
Weil es so im Code steht.
Wer aber auf die kreative Idee gekommen ist, hier implizit einfach den 
UNSIGNED Vektor zu verdoppeln, das dürfte sich in den Annalen verloren 
haben.

: Bearbeitet durch Moderator
von J. S. (engineer) Benutzerseite


Lesenswert?

Lothar M. schrieb:
> Wer aber auf die kreative Idee gekommen ist, hier implizit einfach den
> UNSIGNED Vektor zu verdoppeln, das dürfte sich in den Annalen verloren
> haben.

Möglicherweise einfach Bequemlichkeit? Am Ende ist es egal, ob man 1 
oder n Bits addiert, weil sich der Anwender in beiden Fällen Gedanken 
über die letztlich zu verwendenden Bits machen und passend abschneiden 
muss.

Wenn ich es mir recht überlege, dann wäre es aber schon sinnvoll, es 
konsistent zu haben, d.h. die Verbreiterung des Vektors so anzulegen, 
wie es die Zahl erfordert.

von S. R. (svenska)


Lesenswert?

Jürgen S. schrieb:
> Wenn ich es mir recht überlege, dann wäre es aber schon sinnvoll, es
> konsistent zu haben, d.h. die Verbreiterung des Vektors so anzulegen,
> wie es die Zahl erfordert.

Das stört aber bei Parametrierung, wenn plötzlich interne Signale 
unterschiedliche, parameterabhängige Breiten haben. Überflüssige Bits 
wirft die Optimierung raus, die muss man im Code nicht behandeln, exakte 
Bitbreiten schon.

: Bearbeitet durch User
von J. S. (engineer) Benutzerseite


Lesenswert?

S. R. schrieb:
> Jürgen S. schrieb:
>> Wenn ich es mir recht überlege, dann wäre es aber schon sinnvoll, es
>> konsistent zu haben, d.h. die Verbreiterung des Vektors so anzulegen,
>> wie es die Zahl erfordert.
>
> Das stört aber bei Parametrierung, wenn plötzlich interne Signale
> unterschiedliche, parameterabhängige Breiten haben. Überflüssige Bits
> wirft die Optimierung raus, die muss man im Code nicht behandeln, exakte
> Bitbreiten schon.

Ja, aber neben anderen Nachteilen tritt dann der Fall aus, dass die 
Synthese massenhaft warnings produziert, womit das nicht mehr als 
Korrektiv für richtigen Code nutzbar ist.

von S. R. (svenska)


Lesenswert?

Stimmt, wobei ich den Sinn bei manchen Warnungen nicht verstehe.

In C achte ich darauf, Warnungen zu vermeiden (-Wall, manchmal -Wextra), 
aber in VHDL/Verilog scheint das ein Ding der Unmöglichkeit zu sein. Und 
wenn sogar Vivados selbst-generierter Code in der Synthese mit Warnungen 
nur so um sich schmeißt, ...naja.

Ein __attribute__((i_know_you_will_remove_the_high_bits)) wäre schön, 
ist aber zumindest in VHDL nicht vorgesehen (oder mehr Schreibaufwand 
als nötig). Ich bin wahrscheinlich zu sehr Anfänger, aber ein Gefühl für 
"sauberes HDL" habe ich noch nicht gefunden.

von Markus F. (mfro)


Lesenswert?

In Quartus II lassen sich Warnungen im "Message Suppression Manager" 
unterdrücken. Dann muß man sie nicht 100x angucken.

Ich bin fast überzeugt, die Xilinx-Tools können so was auch.

von Batzi (Gast)


Lesenswert?

Markus F. schrieb:
> Ich bin fast überzeugt, die Xilinx-Tools können so was auch.

Idee, wo ich das einstelle? Ich klicke immer die warning an und lasse 
sie wegfiltern, trotzdem kommen immer wieder neue, die ähnlich sind. Das 
ist mühsam.

Aber nochmals zum eigentlichen Problem:

Wenn ich ich nicht jedesmal resize verwenden möchte, bin ich eigentlich 
der Willkür des Compilers ausgeliefert. Bei einer Kombination aus den 
beiden Beispielen von weiter vorn, nämlich der Addition und der 
Multiplikation handhabt er den Überlauf auch wieder falsch, weil die 
"*"-Operation einen gesonders großen Vektor erzeugt und die parallele 
Addition da unter geht. Dass in Wirklichkeit (k)ein weiteres Bit erzeugt 
wird und damit das Ergebnis nicht stimmt, kümmert ihn wenig.



Duke Scarring schrieb:
> In meinen Augen ist das eine Inkon­sis­tenz im VHDL-Standard.
> Bei der Addition wird leider ungefragt abgeschnitten, während die
> Multiplikation saubere Bitbreiten erfordert.
Alerdings und nicht nur da.

Wenn ich im Taschenrechner mal und plus eingebe, kriegt er es auch 
gebacken und erhöhrt seine Stellen richtig. Das müsste doch mit einem 
FPGA auch gehen.

von Stefan U. (stefan_032)


Lesenswert?

Batzi schrieb:
> Wenn ich ich nicht jedesmal resize verwenden möchte, bin ich eigentlich
> der Willkür des Compilers ausgeliefert.

Hallo,

die VHDL-Sprache ist die am wenigsten willkürliche die ich kenne. Das 
Problem liegt leider im Code:
1
OUT_NEW <= VAL_OLD * 2 + ROT_OLD;

OUT_NEW ist 32 bit breit, wie auch die synthese bestätigt. VAL_OLD ist 
ebenfalls 32 bit breit. Die '2' ist aber ein Integer, der, nach 
Standard, 64 Bit breit ist. Hier hast du deine 64 Bit.

Wenn du es unbedingt auf diese Breiten begrenzen willst, kannst du es 
folgendermaßen machen:
1
OUT_NEW <= VAL_OLD(VAL_OLD'high -1 downto VAL_OLD'low) & '0' + ROT_OLD;

Also: Schiebe eins nach links und entferne das höchste Bit.
Möglicherweise habe ich im Code einen Tippfehler (nicht getestet), aber 
so würde es (ohne Willkür) gehen.

von S. R. (svenska)


Lesenswert?

Stefan U. schrieb:
> die VHDL-Sprache ist die am wenigsten willkürliche die ich kenne.

Dafür ist deine Erklärung aber erstaunlich falsch. :-)

Stefan U. schrieb:
> Die '2' ist aber ein Integer, der, nach
> Standard, 64 Bit breit ist.

Ein Integer in VHDL ist immer 32 Bit breit und signed.

Das Problem ist, dass in VHDL eine 32x32-Multiplikation immer einen 64 
Bit breiten Vektor ergibt, unabhängig von den Wertebereichen.

Beitrag #5686509 wurde vom Autor gelöscht.
von Markus F. (mfro)


Lesenswert?

S. R. schrieb:
> Ein Integer in VHDL ist immer 32 Bit breit und signed.

Tatsächlich legt der VHDL-Standard die Breite von Integern nicht fest, 
sondern läßt sie als "implementation defined" offen. Er sagt nur, daß 
mindestens der Wertebereich eines 32-Bit unsigned abgedeckt werden 
muß.

Es könnte also (auch wenn mir das bislang noch nicht untergekommen 
ist) auch Implementierungen mit breiteren integer-Typen geben.

von Stefan U. (stefan_032)


Lesenswert?

S. R. schrieb:
> Ein Integer in VHDL ist immer 32 Bit breit und signed.

Das ist richtig. Ich war irgendwie bei den Physical-Types..

@TO:
Du nutzt ja numeric_std als package. Hier mal ein Link dazu: 
https://www.csee.umbc.edu/portal/help/VHDL/packages/numeric_std.vhd

Wenn man den nich hizufügen darf, bitte entfernen.

Unter dem Punkt A.17 ist die Multiplikation von unsigned und natural 
definiert. In der Beschreibung steht, dass die Größe des Return-Vektors 
doppelt so groß ist wie der unsigned-Input-Vektor.

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


Lesenswert?

Stefan U. schrieb:
> Hier mal ein Link dazu:
> https://www.csee.umbc.edu/portal/help/VHDL/packages/numeric_std.vhd
> Wenn man den nich hizufügen darf, bitte entfernen.
Darf man schon, habe ich ja vor zweieinhalb Monaten auch schon gemacht:
Beitrag "Re: rechnen mit unsigned"

> Unter dem Punkt A.17 ist die Multiplikation von unsigned und natural
> definiert. In der Beschreibung steht, dass die Größe des Return-Vektors
> doppelt so groß ist wie der unsigned-Input-Vektor.
Jetzt sind wir dann aber echt in einer Zeitschleife gefangen:
Beitrag "Re: rechnen mit unsigned"

Wiederholtes Fazit: wer was mit der numeric_std und den darin 
definierten Datentypen machen will, der sollte sich das Package einfach 
mal anschauen. Man lernt was dabei...  ;-)

: Bearbeitet durch Moderator
von Stefan U. (stefan_032)


Lesenswert?

Lothar M. schrieb:
> Jetzt sind wir dann aber echt in einer Zeitschleife gefangen:

Oh! Sorry.. Das hab ich wohl überlesen. Aber da stimmen wir überein:

--Erstmal das eingebundene Package lesen.--

von Markus F. (mfro)


Lesenswert?

wenn's dir nur darum geht, mit einer festen Vektorbreite zu rechnen 
(also alle höherwertigen Bits abzuschneiden) und dir resize() zu sperrig 
ist, hätte ich hier eine kürzere Alternative:
1
    signal e : unsigned(31 downto 0);
2
...
3
    e <= "+"("*"(a, b)(e'range), c)(e'range);

Zugegeben, ein bißchen ungewohnt zu lesen; aber völlig korrektes VHDL 
(und man kann gleich testen, ob der Leser mit der Sprache vertraut ist 
;) ).

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.