Hallo! Muss gerade mal meckern, sorry :-) Ich programmiere zum resten mal einen CM0+ in Assembler. Cortex M0(+)/M4F/M3 und ARM7TDMI/ARM9 hab ich bisher immer in C programmiert, oder höchstens mal mit inline assembler (DSP code Optimierungen).. Dabei will ich eine kleine Bitbang seriell => Parallel Wandlungsroutine schreiben. Damit es deterministisch läuft, wird die Routine ins RAM gelinkt (Thema flash waitstates...). Erstmal: Ich finde keine komplette Kommandoreferenz. Das, was auf der ARM-Seite so zu finden ist, ist doch ein Witz. Man muss sich alles aus mehreren Quellen zusammensuchen. z.B. das hier: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0484c/CHDCICDF.html oder das: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0497a/BABJCCDH.html Keine Angaben, wie die Befehle genau arbeiten. Wann werden die PSR flags upgedated und nach welchen Regeln? Schlecht informiert bin ich gerade in eine Falle getappt. In der Annahme, dass der RORS (rotate right) Befehl genauso arbeitet, wie auf fast jeder anderen MCU, musste ich feststellen, dass das Carry-flag bei dem THUMB Befehlssatz lediglich upgedated wird, aber nicht bei z.B. einem rotate right um 1 Bit ins MSB geschoben wird. Mein konkreter Code sah so aus: LSRS r2, r2, #1 RORS r4, r4, r1 Das LSB von r2 (stammt von einem IO port) sollte ins MSB von r4 geschoben werden und r4 um 1 Bit nach rechts. Hat jemand eine Idee, wie man das gewünschte Verhalten effizient erreichen kann? Also ohne aus dem 2 Zeiler ein 4 Zeiler zu machen? Das könnte ich auch selbst :-) VG, Kai Achja, was ich sonst noch nicht so toll finde (bin von anderen MCUs verwöhnt): - die meisten Befehle unterstützen nur R0-R7 als Operanden (low-register) - es gibt keine schönen IF-THEN konstrukte (wie z.B. bei CM3) - es gibt nicht sowas nettes wie SBIS / SBIC (AVR) - Kein decrement mit check auf 0 und sprung, oder vergleichbares Ein paar Sachen werden durch die potentiell höhere Clockrate wieder gut gemacht, um mal was positives zu nennen :-) Nein, ich schreibe nicht alles in Assembler. Aber beim CM0+ wird ja extra damit geworben, dass man gut bitbanging IO machen kann. Daher hat er ja auch den single-cycle GPIO Zugriff. Was macht man meistens mit bitbang IO? Serielle busse in SW implementieren... Was wäre dafür nützlich? Richtig, ein ROR / ROL mit Carry flag, was auch REINgeshiftet wird.
:
Bearbeitet durch User
Update: Ein "normales" ROL sollte sich mit ADCS (Add with carry) realisieren lassen, indem man 2 mal dasselbe Register übergibt. Also z.B. R0+R0+carry rechnet. Hilft leider nicht weiter, wenn man ein ROR braucht, da das serielle Protokoll LSB first schickt. Also: Weitersuchen :(
Da sieht man mal was man an seinem AVR hat... zumindest in Assembler. Der verwöhnt einen ja richtig :) Dach't ich mir's doch- ARM Programmierer sind mit dieser undurchsichtigen, komplizierten Architektur wirklich arm dran!
Na ja, hab noch etliche Threads im Hinterkopf in denen immer behauptet wird ARM sei nicht komplizierter als AVR und bereits jedem Einsteiger zu empfehlen...
Jede Architektur hat ihre Vor- und Nachteile. Ich denke in dem Fall ist das Problem, dass man durch andere MCUs bestimmte Lösungsansätze gewohnt ist, die man hier anders anpacken muss.
Kai G. schrieb: > Erstmal: Ich finde keine komplette Kommandoreferenz. => armv6-m architecture reference http://infocenter.arm.com/help/topic/com.arm.doc.ddi0419c/index.html > In der Annahme, dass der RORS (rotate right) Befehl genauso arbeitet, > wie auf fast jeder anderen MCU Bei 32-Bit Prozessoren arbeitet ROL/ROR normalerweise grad so wie bei ARM, also ohne aktive Beteiligung vom Carry. Soweit meine Kenntnis - welche "fast jede" kennst du, bei denen das anders ist? Original-ARM und ARMv7-M (Cortex-M3) besitzen einen RRX Befehl für 1 Bit durch Carry. Mehr als 1 Bit durch Carry wird nicht oft benötigt.. > Hat jemand eine Idee, wie man das gewünschte Verhalten effizient > erreichen kann? Nicht beim Cortex-M0, wenn "effizient" für dich 2 Takte bedeutet. In 3 Takten gehts aber: LSRS r2, r2, #1 ADCS r4, r4 RORS r4, r4, #1 > Achja, was ich sonst noch nicht so toll finde (bin von anderen MCUs > verwöhnt): Wenn du dich verwöhnen lassen willst - wieso hast du dir dann mit dem Cortex-M0 ausgerechnet den schmalspurigsten Core ausgesucht? Das ist nun einmal ein Schrumpf-Core für mässige Anforderungen.
:
Bearbeitet durch User
Kai G. schrieb: > Achja, was ich sonst noch nicht so toll finde (bin von anderen MCUs > verwöhnt): Welche diese anderen MCUs erfüllt denn alle genannten Forderungen? Also die - mindestens 16 Register direkt adressiert, - deine schönen IF-THEN konstrukte kennt, - sowas nettes wie SBIS / SBIC hat, - und auf ein decrement mit check auf 0 und sprung, und das mit 1 Takt pro Befehl, ähnlich knappem Codebedarf und als Core so billig und klein, dass er es mit dem CM0 aufnehmen kann.
:
Bearbeitet durch User
> => armv6-m architecture reference > http://infocenter.arm.com/help/topic/com.arm.doc.ddi0419c/index.html Merci! > Bei 32-Bit Prozessoren arbeitet ROL/ROR normalerweise grad so wie bei > ARM, also ohne aktive Beteiligung vom Carry. Soweit meine Kenntnis - > welche "fast jede" kennst du, bei denen das anders ist? Naja, ich habe von der 8-Bit Welt gesprochen. Die wird doch durch die 32 Bit Controller "angegriffen". Aber auch viele 32-Bit Architekturen können doch ein Carry links / rechtsreinshiften. > Original-ARM und ARMv7-M (Cortex-M3) besitzen einen RRX Befehl für 1 Bit > durch Carry. Mehr als 1 Bit durch Carry wird nicht oft benötigt.. Klar, macht auch nicht viel Sinn mehr als 1 mal zu shiften wenn es komplett durchs Carry geht. Der M0+ hat einen Prima barrelshifter, wie alle ARMs. Kann doch nicht viel Chipfläche kosten noch ein RRX zu realisieren. > Nicht beim Cortex-M0, wenn "effizient" für dich 2 Takte bedeutet. 3 wären auch noch zu ertragen :-) Also muss ich wohl mit linksshiften (ADCS) arbeiten und nachträglich mit einer 8-Bit Tabelle die Bitreihenfolge invertieren, zu einem Zeitpunkt an dem etwas mehr Zeit zur Verfügung steht. RBIT gibt es ja auch nicht. > Wenn du dich verwöhnen lassen willst - wieso hast du dir dann mit dem > Cortex-M0 ausgerechnet den schmalspurigsten Core ausgesucht? Das ist nun > einmal ein Schrumpf-Core für mässige Anforderungen. Wie üblich: Der Preis. Wo bekomm ich sonst eine MCU mit vernünftigen Speicherausbau in Stückzahlen für <0.50 USD? Oder in Einzelstückzahlen aus dem Katalog für 0.89 USD? Und der M0+ ist nicht schlecht, auch wenn das hier anders rüberkommen mag. Was mich mal interessieren würde: Von dem Befehlssatz her ist der M0+ durchaus mit einem 8-Bitter vergleichbar. Es gibt wenige spezielle Vorkehrungen für 32-Bit in dem Befehlssatz. Man kann z.B. keine 16 oder 32 Bit immediates direkt in ein Register laden. Andere Sachen fehlen halt. z.B. auch sowas wie ein STR mit automatischem pointer decrement/increment. Ist die Chipfläche bei gleicher Prozesstechnologie so viel kleiner als z.B. bei einem AVR? VG, Kai
:
Bearbeitet durch User
Kai G. schrieb: > Aber auch viele 32-Bit Architekturen > können doch ein Carry links / rechtsreinshiften. Das liegt ein wenig daran, wie der Thumb Befehlssatz entstand. Nämlich als minimalisierte Abbildung eines einfachen in 16 Bits codierten Befehlssatzes auf den ARM Befehlssatz. Da RRX durch 2 Befehle ersetzt werden kann (ebd. ADC/ROR), sah ARM wohl keine Veranlassung, deshalb Bäume auszureissen. > 3 wären auch noch zu ertragen :-) Das Beispiel dafür hatte ich ja gezeigt. > Also muss ich wohl mit linksshiften (ADCS) arbeiten und nachträglich mit > einer 8-Bit Tabelle die Bitreihenfolge invertieren, zu einem Zeitpunkt > an dem etwas mehr Zeit zur Verfügung steht. Auch das ist ein möglicher Weg. > Von dem Befehlssatz her ist der M0+ durchaus mit einem 8-Bitter > vergleichbar. Es gibt wenige spezielle Vorkehrungen für 32-Bit in dem > Befehlssatz. Man kann z.B. keine 16 oder 32 Bit immediates direkt in ein > Register laden. Das hängt direkt mit der Codierung zusammen und ist eine Eigenheit vieler Achitekturen mit fester Befehlslänge. Wenn die Daten nicht deutlich schmaler sind als die fix definierte Befehlsbreite, dann geht das schlicht und einfach nicht. AVR und PIC/8-Bit sind insofern die Ausnahme von der Regel: Nur weil deren (fast) feste Befehlsbreite grösser als die Datenbreite ist, sind Immediates voller Breite überhaupt möglich. Die native ARM Architektur kann es ebenso wenig wie das daraus entstandene Thumb. Auch Thumb2 kann es nicht in einem Befehl, sondern implementiert es so, wie die viele anderen 32-Bit RISCs: Ein 32-Bit Immediate wird aus 2 Befehlen mit je 16 Bits zusammengesetzt. Man hat dann die Wahl: Schnell mit 64 Bits (2 Befehle à 32 Bits) oder knapper aber langsamer mit 48 Bits (1 Load-Befehl mit 16 Bits, plus 1 32-Bit Wort im constant pool dahinter). Bei 64-Bit Architekturen sind Immediates voller Breite erst recht selten. Sogar x64/AMD64 mit sehr variabler Befehlslänge codiert bei 64-Bit Befehlen meist nur 32-Bit Immediates, ausser bei Move to Register. > Andere Sachen fehlen halt. z.B. auch sowas wie ein STR mit automatischem > pointer decrement/increment. Platzfrage. Nicht auf dem Die, sondern im Programmcode. Solche Optionen sind nett, aber wenn auf die Länge des gesamten Codes eines Programms betrachtet nicht ganz so wichtige Operationen durch 2 Befehle ersetzt werden können, dann sind sie im Zielmarkt der Thumb Architektur verzichtbar. Thumb entstand, weil ARM Code recht raumgreifend war und manche aufkommende Konkurrenz wesentlich sparsamer zu sein versprach (z.B. Hitachi). Es ging dabei um Platz im ROM, nicht um Laufzeit. Das Ergebnis waren die sehr populären ARM7-Txxx Cores, die über einen grösseren Befehlsdekoder verfügten als ARM7 ohne -T. Weil beides möglich wurde. Aufgrund der Dekoderstruktur musste aber jeder Thumb Befehl 1:1 in einen ARM Befehl übersetzbar sein, mehr als ARM schon hatte ging also in Thumb per Definition nicht. Dass Thumb Code bei sonst gleichen Rahmenbedingungen langsamer als ARM Code ist, war vor vorneherein einkalkuliert. Es gab ja beides, wer Tempo brauchte nahm nativen ARM Code, wer Platz sparen wollte nahm Thumb Code. ARMv6-M, also die Architektur der Cortex M0/M1, zielt auf einen sehr einfachen Core mit moderaten Anforderungen ab und basiert direkt auf diesem historisch entstandenen Thumb-Teil ohne nativem ARM Befehlssatz, leicht erweitert. Beim Cortex M1 geht es ausserdem nicht um mm² Silizium, sondern um noch viel knappere FPGA-Zellen. Parallel dazu entstand ARMv7-M basierend auf Thumb2 im Markt für mhr Performance bei komplexerem Core und entsprechend mehr Platz auf dem Die. Man hat als Kunde die Wahl: Kleiner, billiger, langsamer, oder etwas grösser, teurer, schneller. Wenn der Kunde dabei die falsche Wahl getroffen hat, weil Geiz geil ist, dann sollte er sich selbst an die Nase fassen und sich nicht bei ARM beklagen. Zum Vergleich mit AVRs: In jedem x-beliebigen Vergleich zwischen Architekturen wirst du in solchen Detailfragen, wie sie von dir betrachtet wurden, Aspekte finden, die mal die eine besser kann und mal die andere.
:
Bearbeitet durch User
Ich mecker nicht, ich "bemerke". Mich reizt es aus wenig viel zu machen :-) Wenn man sich tiefer mit beschäftigt fallen einen Tricks und Möglichkeiten auf die Probleme geschickt zu lösen. Wenn auch teilweise anders, als man es gewohnt ist. Da ich in den letzten Tagen Abends mit einem ASM Projekt zubringe und ständig was dazu lerne, will ich die Aussage, dass es kein Load / Store mit post increment revidieren: Die STM und LDM Instruktionen (Store / Load multiple) inkrementieren den Pointer (post inc)! Nicht auf Anhieb ersichtlich, aber man kann sie auch Nutzen um nur einen einzelnen 32 Bit Wert zu lesen / zu schreiben und bekommt danach einen um 4 inkrementierten Pointer. Das ganze dauert 2 Zyklen, wie ein normales STR/LDR. Also z.B. so: MOVS r0, #0 <- Adresse nur zur Veranschaulichung... STM r0!, {r1} <- r0 hat danach den Wert #4 Ich habe mal probiert, ob IAR den Kniff kennt, als sowas gemacht: int i; volatile int u; volatile uint32_t *ptr = (uint32_t*)buf; for (i=0; i<u; i++) { *ptr++ = 0x12345678; } Die volatiles und Variable "u" sind eingestreut, damit dem Compiler die Möglichkeit zum loop unrolling genommen werden. Bei höchster Optimierungseinstellung wird die Schleife realisiert und im inneren steht: STR R2, [r0] ADDS R0, R0, #4 Auch in anderen Tests, die den Optimierer nicht beschränken, wird immer ein ADDS eingefügt. Spätestens, wenn die Immidiate Offset Werte nicht mehr ausreichen wird ein: ADDS r0, r0, #132 eingefügt. An der Stelle könnte IAR seine Codeeffizienz doch noch etwas steigern. Der STM Trick ist denen wohl noch nicht eingefallen. Oder gibt es mir momentan nicht bekannte Nachteile? OK, ein LDM/STM wird beim M0 durch einen IRQ unterbrochen (nicht konfigurierbar wie z.B. beim M3), aber wird auch bei einem einzelnen Wert auch unterbrochen? Ist mir bei der vorliegenden Dokumentation nicht deutlich. Ansonsten: Ich habe das Problem mit dem ROR so gelösst, dass ich erstmal ein "ROL" mache, mittels ADCS und dann an späterer Stelle ein Bitreversal durchführe. Das Bitreversal braucht momentan 16 Zyklen, aber ich habe schon eine Idee zur Beschleunigung. So, Zeit fürs Bett... VG, Kai
Kai G. schrieb: > Ich habe mal probiert, ob IAR den Kniff kennt, als sowas gemacht: Anständigen Compiler verwenden, nicht so einen Billigkram. Aus
1 | void f(int *p, int n) |
2 | { |
3 | int *q = p + n; |
4 | while (p < q) |
5 | *p++ = 0x12345678; |
6 | } |
und
1 | void f(int *p, int n) |
2 | { |
3 | for (int i = 0; i < n; ++i) |
4 | p[i] = 0x12345678; |
5 | } |
wird im Kern dann
1 | .L5: |
2 | stmia r0!, {r3} |
3 | cmp r1, r0 |
4 | bhi .L5 |
:
Bearbeitet durch User
Keil? Wenn mein projekt fertig ist, werd ich mal schauen, was gcc draus macht. Spannend, dass er es im 2. Beispiel schafft das ++i in ein postinkrement umzusetzen. Vermutlich fuegt er etwas vorher ein adds r0, r0, #4 ein. Und beeindruckend, dass er nicht jedesmal die adresse berechnet.
Amüsant :-) Dann kann ich mir den Test ja sparen. Wollte Danach sowieso auf gcc gehen.
Haha, hab gerade ne 2 Zyklen Lösung gefunden! Wenns interessiert, verrat ich sie sogar :-)
OK :-) Sorry, freu mich gerade halt :-) LDR r2, GPIO_DIN_REGISTER MOVS r3, #0 ; in r3 landet das parallele Datenwort Schleifchen: LDR r1, [r2] ; GPIO DIN auslesen ORRS r3, r3, r1 RORS r3, r3, #1 B Schleifchen OK, es gibt 2 Limitierungen, die bei mir kein Problem darstellen: 1.) Alle Bits, ausser Bit 0 müssen eine 0 beinhalten, damit es klappt 2.) Das parallele Register r3 muss auf jeden Fall mit 0 vorinitialisiert werden. Das hat jetzt ein Weilchen gedauert... Die Lösung ist an und für sich einfach, aber das war wieder ein Fall von "festgefahren sein in bekannten Lösungswegen mit anderen CPUs".
Kai G. schrieb: > 1.) Alle Bits, ausser Bit 0 müssen eine 0 beinhalten, damit es klappt Bit 0 muss nicht unbedingt sein, jedes anderes tut es auch. Aber es ist schon ziemlich speziell, dass diese Randbedingung akzeptabel ist.
Ja, stimmt schon. Bei mir liegt halt das serielle Signal auf Bit 0. Weil ich ja mit einem rechtsshift das Bit ins Carry übertragen wollte. So speziell ist die Bedingung garnicht. Es sind nicht alle GPIO Bits von dem Port in dem Package herausgeführt. Ich verliere also keine 15, bzw. 31 IO pins.
Kai G. schrieb: > Update: > Ein "normales" ROL sollte sich mit ADCS (Add with carry) realisieren > lassen, indem man 2 mal dasselbe Register übergibt. Also z.B. > R0+R0+carry rechnet. > > Hilft leider nicht weiter, wenn man ein ROR braucht, da das serielle > Protokoll LSB first schickt. > > Also: Weitersuchen :( Verstehe ich jetzt nicht. Dann setzt man ein Bit im UART, und der eingehende Bitstrom wird anders herum interpretiert. Da brauchts keinen Rechenschritt für.
Der OP geht aus irgend einem Grund davon aus, dass man immer das Carry bei einem Rotate braucht. Das ist aber (so pauschal[1]) Käse. Wozu soll ich denn ein 33.Bit brauchen (nichts anderes ist der Carry bei einer 32Bit CPU) wenn ich die 32 vorhandenen Bits nur rotieren/schieben will. Wenn mich das Carry interessiert lese ich es vor dem Rotieren aus. Wobei ich mich mit dem "Thumb-Befehlssatz" der Cortex nie auseinandergesetzt habe, da ich bisher keinen praktischen Nutzen daraus ziehen konnte. Wobei ich dem OP auch unterstelle, nicht richtig geschaut zu haben, denn auf der ersten von ihm verlinkten Seite ist (ua) das komplette Instruction-Set alle Cortex-M aufgeführt. Inklusive des M0. http://infocenter.arm.com/help/topic/com.arm.doc.dui0662a/DUI0662A_cortex_m0p_r0p0_dgug.pdf Dort ist auch beschrieben, dass die Schiebebreite (also die Nutzung des Carry) mit einer Option angegeben wird. Roland [1]Viele CPUs haben einen zusätzlichen Rotier/Schiebebefehl der durchs Carry schiebt/rotiert.
Roland Ertelt schrieb: > Dort ist auch beschrieben, dass die Schiebebreite (also die Nutzung des > Carry) mit einer Option angegeben wird. Bei keiner Shift-Operation des CM0[+] wird das Carry als Input-Operand verwendet (nur ADC/SBC), unabhängig von der Anzahl Stellen. Das gibts nur ab CM3 oder im ursprünglichen ARM.
Ja, dass du einen Würgaround für deinen Carry-Zwang gefunden hast, habe ich gesehen. Er ist nur gar nicht nötig, weil du eigentlich nicht durchs Carry schieben musst. Ich verstehe dein Problem so, dass du auf irgend einem Port ein Bit bekommst, welches du (per Software) auf einer anderen Stelle wieder haben willst. Das kannst du schieben, sinnvoller Weise den kürzeren Weg, oder per Bittest erkennen und ins Zielregister addieren. PS: Nach der von der ARM-Seite kommenden Instructionset für den M0 berücksichtigen alle Schiebebefehle das Carry je nach Wunsch mit. Stattdessen wird dann halt das LSB weggeworfen. Das Bit mehr im Barrelshifter hätte wohl ein Vermögen gekostet... :-D
:
Bearbeitet durch User
Roland. Du hast Recht: Wollte ich nur ein einzelnes Bit nach dem Auslesen des GPIO Port Registers an einer anderen Stelle haben, würd ich nicht den Aufstand machen. Liess am besten nochmal den 1. Post. Der Praktische Nutzen des von mir gewünschten ROR ist in meinem Anwendungsfall durchaus gegeben und mit der erwähnten 2-Zyklen Lösung gelösst. Die praktische Implementierung sieht leicht anders aus und ich kann jetzt in nur 2 Zyklen pro Bit (+Branch) einen seriellen Bitstrom NRZ dekodieren und seriell => Parallel wandeln. In dem von Dir verlinkten PDF stehen z.B. nirgendwo die Anzahl der Instruktionszyklen erwähnt, weder in der Befehlszusammenfassung, noch in der jeweils ausführlichen Beschreibung der Befehle. In dem ARM CM0+ TRM stehen hingegen die Zyklen, aber nicht die modifizierten xPSR Flags. Suboptimal, wenn man immer aus mehreren Quellen zusammensuchen muss. Ich weiss, warum das bei ARM so ist, aber es störte mich halt. Aber auch das ist geklärt: Ich habe mir mittlerweile eine eigene handliche 2 DINA4 Seiten grosse Befehlsreferenz erstellt, die sich als sehr nützlich herausgestellt hat. VLG, Kai
Kai G. schrieb: > Ich habe mir mittlerweile eine eigene > handliche 2 DINA4 Seiten grosse Befehlsreferenz erstellt, die sich als > sehr nützlich herausgestellt hat. Magst Du Dir hier posten oder ins Wiki stellen? Dann können sich auch andere die dumme Hin- und Herspringerei zwischen den Dokumenten sparen.
Kai G. schrieb: > Roland. Du hast Recht: > > Wollte ich nur ein einzelnes Bit nach dem Auslesen des GPIO Port > Registers an einer anderen Stelle haben, würd ich nicht den Aufstand > machen. > Natürlich willst du genau das. Halt mehrmals hintereinander. Du bastelst dir einen Mux, weil du aus irgend einem Grund den eingebauten in der UART nicht benutzen kannst/willst. Im Normalfall brauchst du dazu Schleife Port lesen Und Portregister, Maske Add Portregister, Zielregister -> Zielregister Zielregister eins schieben/rotieren Endprüfung Sprung Zielregister Bündig rotieren. ggf Maske drüber. Um einen BCD oder ähnlichen Mehrbitcode < Portbreite aus dem Port zu lesen gehts sogar noch einfacher: Port lesen Register Bit 0..31 schieben Und Maske drüberlegen um den Müll auszunullen. fertig. Nirgendwo wird das Carry benötigt. Ich verstehe halt dein Problem mit dem Carry gerade nicht. Da die Arbeitsregister 32Bit breit sind, und die I/O-Register auch kannst du es während der Operation ignorieren. Schon dein MOVS ist übertrieben. Ein MOV reicht völlig, da du offenbar keine "Zero-Prüfung" machst. Roland
:
Bearbeitet durch User
Roland Ertelt schrieb: > Schon dein MOVS ist übertrieben. Ein > MOV reicht völlig, da du offenbar keine "Zero-Prüfung" machst. Diese Aufregung darüber aber auch, weil völlig irrelevant.
@Gerd: Ich werd es hochladen! @Roland: Ich bin glücklich und zufrieden. Es gibt keinen Grund sich aufzuregen :-)
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.