RPi GPIO-Geschwindigkeitsproblem in Bare Metal

Nachdem ich den hervorragenden Artikel über Bare Metal von David Welch ( https://github.com/dwelch67/raspberrypi/tree/master/baremetal ) gelesen habe, versuchen ein Freund und ich, einen einfachen GPIO-Schalter zu implementieren. Es basiert auf David Welchs blinker01 ( https://github.com/dwelch67/raspberrypi/tree/master/blinker01 ). Ich habe einfach die Peripherieregister aktualisiert, um GPIO 3 auf dem Himbeer-Pi zu entsprechen. Ich habe diese Adressen gefunden, indem ich das BCM2835-Datenblatt durchgesehen habe ( https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf ). Kapitel 6 listet alle Positionen der GPIO-Pin-Einstellungen auf.

Wir laden den Code auf eine SD-Karte und schalten dann ein. Das Programm funktioniert: Wir erhalten eine 2,5-MHz-Rechteckwelle auf dem Oszilloskop.

** Warum ist es so langsam? **

Der Prozessor hat eine Taktrate von 1 Ghz. Die von uns implementierte Operation durchläuft 10 Montagezeilen (dh führt 10 Montagezeilen aus und verzweigt dann zurück zur ersten Zeile dieser 10). Ich bin neu bei Bare-Metal/Prozessoren und verstehe, dass eine bestimmte Montagelinie mehrere Taktzyklen dauern kann. Unter der Annahme, dass jede Zeile 10 Taktzyklen benötigt, würde ich immer noch eine Periode von 100 ns für die Ausgabe erwarten, was 10 MHz entsprechen würde. Ich denke, dass dies eine konservative Untergrenze für die Ausgangsfrequenz ist, da einige Montagelinien nur einen einzigen Zyklus benötigen.

Außerdem habe ich diesen Artikel gefunden: http://codeandlife.com/2012/07/03/benchmarking-raspberry-pi-gpio-speed/

Diese Person hat es geschafft, einen 22-MHz-Ausgang auf einem Himbeer-Pi 1 mit einem ähnlichen Ansatz, aber unter Verwendung von mmap von Linux, zu erhalten. Der Code, den sie als Basis verwendet haben, befindet sich als erstes Beispiel auf dieser Seite: http://elinux.org/RPi_GPIO_Code_Samples

** Bearbeiten: Ich dachte ursprünglich, dass die 22 MHz auf einem Himbeer-Pi 2 erreicht wurden, aber das ist falsch. Sie geben an, dass sie diese Ausgaberate auf einem Pi 1 erreicht haben, der den BCM 2835-Chip enthält **

Hinweis: Obwohl dies zweifellos zu StackOverflow gehört, hatte ich das Gefühl, dass es ein "schwierigeres" Problem ist, da es mit der Schaltung im Prozessor und den Peripheriegeräten zu tun hat.

Bearbeiten: Assembly-Code ist hier: Disassemblierung des Abschnitts .text:

00008000 `<_start`>:

    8000:   e3a0d902    mov sp, #32768  ; 0x8000
    8004:   eb000005    bl  8020 <notmain>

00008008 `<hang`>:

    8008:   eafffffe    b   8008 <hang>

0000800c `<PUT32`>:

    800c:   e5801000    str r1, [r0]
    8010:   e12fff1e    bx  lr

00008014 `<GET32`>:

    8014:   e5900000    ldr r0, [r0]
    8018:   e12fff1e    bx  lr

0000801c `<dummy`>:

    801c:   e12fff1e    bx  lr

00008020 `<notmain`>:

    8020:   e92d4010    push    {r4, lr}
    8024:   e59f002c    ldr r0, [pc, #44]   ; 8058 <notmain+0x38>
    8028:   ebfffff9    bl  8014 <GET32>
    802c:   e3c01c0e    bic r1, r0, #3584   ; 0xe00
    8030:   e3811c02    orr r1, r1, #512    ; 0x200
    8034:   e59f001c    ldr r0, [pc, #28]   ; 8058 <notmain+0x38>
    8038:   ebfffff3    bl  800c <PUT32>
    803c:   e3a01008    mov r1, #8
    8040:   e59f0014    ldr r0, [pc, #20]   ; 805c <notmain+0x3c>
    8044:   ebfffff0    bl  800c <PUT32>
    8048:   e3a01008    mov r1, #8
    804c:   e59f000c    ldr r0, [pc, #12]   ; 8060 <notmain+0x40>
    8050:   ebffffed    bl  800c <PUT32>
    8054:   eafffff8    b   803c <notmain+0x1c>
    8058:   20200000    eorcs   r0, r0, r0
    805c:   2020001c    eorcs   r0, r0, ip, lsl r0
    8060:   20200028    eorcs   r0, r0, r8, lsr #32

`

Könnten Sie einfach ein Code-Snippet für Ihre eigentliche Schleife einfügen? Referenzen sind in Ordnung, außer dass ich dort keine Anzeichen von Assembler-Code finden kann.
Welche Bandbreite hat Ihr Oszilloskop?
Die Bandbreite beträgt 100 MHz
Höchstwahrscheinlich ist die Systemuhr und/oder die interne PLL nicht richtig eingestellt. Das Hauptproblem bei Bare Metal auf diesen High-End-MCUs besteht darin, dass Sie alles selbst einrichten müssen , einschließlich Speicherzuordnung und MMU, was alles andere als trivial ist, es sei denn, Sie können den Setup-Code von irgendwoher abrufen.
@Lundin Der BCM2835 ist eine Art seltsames Teil. Soweit ich weiß, liegt die Taktsteuerung auf der GPU-Seite, die vor dem ARM-Kern auftaucht.
Ich wette, Lundins Vorschlag ist richtig. Ich würde denken, dass es höchst unwahrscheinlich ist, dass die GPU die Uhr der CPU steuert. Das einzige andere, was sofort in den Sinn kommt, ist, dass die SD-Karte sehr langsam gelesen wird. Wenn es diese Anweisungen ständig von der SD-Karte liest, anstatt dass ihm der RAM oder sein Cache ausgeht, könnte es am Ende vielleicht sehr langsam werden. Das ist aber ein echter Stich ins Blaue. Können Sie eine konstante SD-Kartenaktivität sehen, während das Programm ausgeführt wird?
@DiBosco Die Anwendung wird vom GPU-Kern von der SD-Karte in den DRAM geladen, bevor der ARM-Prozessor gestartet wird. Habe ich erwähnt, dass der BCM2835 ein seltsames Teil ist? :)
Ich denke, der Speicherabruf könnte das Problem sein. Ich habe diesen Beitrag gefunden: raspberrypi.stackexchange.com/questions/61186/… Wie, glauben Sie, beschleunigt mmap das? Ist es nicht trivial, die Peripheriegeräte für schnellere Schreibzeiten in einem Cache zu speichern?

Antworten (2)

Der Hauptschuldige ist eine Kombination aus Hardware-/Peripheriebeschränkungen und Taktkonfiguration. Obwohl ich nicht speziell mit BCM Baremetal arbeiten musste, sind dies häufige Probleme von Baremetal-Projekten auf jeder komplexen Architektur.

Als Hinweis auf die Einschränkungen der GPIO-Ausgangstreiber können Sie sehen, dass bei fester Verdrahtung als Hardware-Taktausgang die maximale Ausgangsfrequenz 125 MHz beträgt

Von Seite 106 des von Ihnen bereitgestellten BCM-Peripheriedatenblatts

Betriebsfrequenz : Die maximale Betriebsfrequenz der Allzweck-Taktgeber beträgt ~125 MHz bei 1,2 V, wird jedoch reduziert, wenn die GPIO-Pins stark belastet sind oder eine kapazitive Last haben.

Dies steht im Zusammenhang mit der Konfiguration des GPIO-Peripheriegeräts, um den Peripherietakt direkt ohne Softwareumschaltung auszugeben.

Ich würde sagen, dass es vernünftig ist zu erwarten, dass selbst wenn die Uhren richtig konfiguriert sind und die CPU mit maximaler Geschwindigkeit läuft, Sie aufgrund von Hardwarebeschränkungen nicht erwarten können, dass ein GPIO schneller umschaltet.

AKA, nur weil das Peripheriegerät Ihren Softwarebefehl rechtzeitig verriegeln kann, bedeutet das nicht, dass die physischen Ausgangstreibertransistoren, die groß und kräftig sind und eine Menge Eigenlast aufweisen, so schnell schalten können, wie Ihr Code ausgeführt werden kann. Wenn Sie Tests durchführen, müssen Sie unbedingt ein Oszilloskop mit ausreichender analoger Bandbreite und hochwertigen Tastköpfen verwenden, da Sie mit dem Messsystem auch Ihr Ergebnis verändern. Ein Logikanalysator ist möglicherweise nicht ausreichend, eine langsame Anstiegsgeschwindigkeit ist bei schwellenwertbehafteten Eingängen nicht erkennbar.


So gehen Sie vor

Es scheint, dass Sie, wenn Sie GPIO so schnell wie nötig ansteuern möchten, für Taktzwecke diese eingebauten Taktausgangspins des Peripheriegeräts verwenden sollten. Diese werden über Register konfiguriert

CM_GP0CTL CM_GP0DIV(Wiederholen Sie dies für GP-Taktausgänge 1 und 2)

Abhängig von den Ergebnissen ermitteln Sie dann die maximale Schaltfrequenz für Ihr Hardwaresystem unter Berücksichtigung der maximalen GPIO-Last und der VDD Ihrer PIO-Schaltung.

Wenn die Taktausgabe langsamer als für Ihre nominellen Teilereinstellungen erwartet ist, weist dies darauf hin, dass Sie die Systemuhr, das Taktrouting und die PLLs nicht richtig konfiguriert haben.

Sobald Sie diese Meinungsverschiedenheit identifiziert haben, können Sie Ihren Bare-Metal-Bootstrap-Code optimieren, um die PLLs zu konfigurieren und zu sehen, ob ein softwaregesteuertes Umschalten so schnell wie der hardwaregesteuerte Taktausgang ausgeführt werden kann, und von dort aus weitergehen.

Zusätzliche beitragende Faktoren können in den Befehls- und Daten-Caches liegen, die eine Softwarekonfiguration erfordern, wenn Sie nicht in der Lage sind, das Umschalten der Software mit der Hardwaregrenze allein durch PLL auszurichten, wäre dies der nächste Ort, an dem ich suchen würde.

Bin gerade durch einen Crosspost auf diese Frage gestoßen.
Das Hauptproblem, das nicht in allen obigen Kommentaren behandelt wird, ist die Busgeschwindigkeit. Der Prozessor läuft möglicherweise mit 3 GHz, die Busse auf dem Chip jedoch nicht. Der Bus auf der ARM-Seite ist viel langsamer als 3 GHz. Der Bus zum GPIO ist noch langsamer und läuft mit der GPU-Geschwindigkeit. Hinzu kommt der Zeitverlust für das Überqueren der Taktdomäne und die Tatsache, dass die Busse eine gemeinsame Ressource zwischen den ARM-Kernen und der GPU sind. Außerdem verwendet der Chip das AXI-System, das über eine Schreibbestätigung verfügt, sodass der Prozessor auf das Eintreffen der Schreibbestätigung warten kann, was der doppelten Busdurchlaufzeit entspricht. In der Businfrastruktur dauert es mehrere Taktzyklen, um das Signal von einer Seite des Busses zur anderen zu übertragen (aber es ermöglicht mehrere Transaktionen gleichzeitig auf dem Bus).

Dies erklärt wahrscheinlich nicht das volle Ausmaß des beobachteten Verhaltens.