Problem bei der Clock-Gating-Steuerung mit ARM Tiva C

Wenn ich direkt nach dem Aktivieren des Takt-Gatings des Peripheriegeräts auf ein Peripherieregister (z. B. einen GPIO-Port) zugreife, tritt ein schwerer Fehler auf. Zum Beispiel:

  • Dieser Code funktioniert nicht (erzeugt einen schweren Fehler):

    /* enable clock to GPIOF at clock gating control register */
    SYSCTL_RCGCGPIO_R |= SYSCTL_RCGCGPIO_R5;
    /* enable the GPIO pins for the LED (PF3, 2 1) as output */
    GPIO_PORTF_DIR_R = 0x0E;
    while(1) {}
    
  • Aber mit diesem kleinen nopTweak funktioniert es:

    /* enable clock to GPIOF at clock gating control register */
    SYSCTL_RCGCGPIO_R |= SYSCTL_RCGCGPIO_R5;
    asm(" nop");
    asm(" nop");
    /* enable the GPIO pins for the LED (PF3, 2 1) as output */
    GPIO_PORTF_DIR_R = 0x0E;
    while(1) {}
    

Der Code sollte ohne Verzögerung nach dem Aktivieren der Uhr funktionieren, aber ich muss wissen, was hier passiert?

Lange Geschichte:

Wenn ich den ersten Code ausführe, gibt es mir einen Busfehler. Ich habe andere Ports in GPIO und auch einen UART-Kanal ausprobiert, und das Problem bleibt bestehen. Was ich habe, ist Folgendes: Wenn Sie nur genügend asm(" nop");Anweisungen an der richtigen Stelle hinzufügen, funktioniert der Code.

Es scheint, dass das Problem mit der Verzögerungszeit zwischen dem Aktivieren des Clock-Gating eines Peripheriegeräts und dem Zugriff auf seine Register zusammenhängt. Jede Lösung, die das Hinzufügen einer ausreichenden Verzögerung implementiert, funktioniert. Ich habe verschiedene Dinge ausprobiert, wie die Verwendung einer Verzögerungsfunktion oder die Verwendung von asm("nop")-Anweisungen oder das Hinzufügen eines Debug-Haltepunkts in der Zeile direkt nach dem Aktivieren von clock. Hat das etwas mit Clock-Gating-Latenz oder Clock-Skew oder solchen Ausdrücken zu tun ?

Ich habe den Code gemäß diesem Dokument von TI debuggt. Mein Fehlerregister liest einen Wert von 0x0000.0400after fault, was bedeutet, dass "ein Busfehler aufgetreten ist, aber die genaue Fehleradresse nicht bekannt ist" . Ich habe den Ausnahmestapelrahmen überprüft und den Disassemblierungscode analysiert, der ausgeführt wird, bevor der Fehler auftritt, und es scheint, dass es kein Problem gibt. Das Dokument sagt, dass dieser Fehler normalerweise aufgrund der STRAnweisung auftritt, aber ich habe die Speicherzeiger überprüft und alles ist in Ordnung.

Demontage des nicht funktionierenden Codes:

11            SYSCTL_RCGCGPIO_R |= SYSCTL_RCGCGPIO_R5;
          main():
0000026c:   4913                ldr        r1, [pc, #0x4c]
0000026e:   6808                ldr        r0, [r1]
15            GPIO_PORTF_DIR_R = 0x0E;
00000270:   4A13                ldr        r2, [pc, #0x4c]
11            SYSCTL_RCGCGPIO_R |= SYSCTL_RCGCGPIO_R5;
00000272:   F0400020            orr        r0, r0, #0x20
00000276:   6008                str        r0, [r1]
15            GPIO_PORTF_DIR_R = 0x0E;
00000278:   200E                movs       r0, #0xe
0000027a:   6010                str        r0, [r2]

Demontage des Arbeitscodes:

11            SYSCTL_RCGCGPIO_R |= SYSCTL_RCGCGPIO_R5;
          main():
0000026c:   4914                ldr        r1, [pc, #0x50]
0000026e:   6808                ldr        r0, [r1]
00000270:   F0400020            orr        r0, r0, #0x20
00000274:   6008                str        r0, [r1]
00000276:   BF00                nop        
00000278:   BF00                nop        
15            GPIO_PORTF_DIR_R = 0x0E;
0000027a:   4912                ldr        r1, [pc, #0x48]
0000027c:   200E                movs       r0, #0xe
0000027e:   6008                str        r0, [r1]

Aus meinen Experimenten geht hervor, dass nach dem Aktivieren der Uhr für GPIO mehr als 1 Taktzyklus erforderlich ist, bevor auf die GPIO-Portregister zugegriffen werden kann. Für UART ist es höher, etwa 5 bis 6 Taktzyklen (das habe ich mit dem Debugger gemessen).

IDE: Code-Composer-Studio. Compiler: TI v18.12.2.LTS (mit Optimierungsstufe 2). Betriebssystem: Ubuntu Bionic 18.04.2 LTS.

Ist diese Verzögerung also erforderlich? oder habe ich leider ein Hardwareproblem?

Sie können versuchen, eine Speichersperranweisung hinzuzufügen, um sicherzustellen, dass Ihr Befehl zum Aktivieren des Schreibvorgangs das Ziel vollständig erreicht hat.
@Oldfart Entschuldigung, könnten Sie die Speicherbarriere erklären?
Entschuldigung, war nach dem Kommentieren offline, aber wie ich sehe, hat Filo geantwortet, wie das geht. Sie können das WWW auch nach dem Begriff „Memory Barrier Instruction“ durchsuchen.

Antworten (2)

Soweit ich mich erinnere, ist dies ein bekanntes Problem bei den Tiva-Prozessoren: Sie müssen zwei oder drei Taktzyklen warten, nachdem Sie die Uhr aktiviert haben, bevor Sie das Peripheriegerät tatsächlich verwenden können. Einige Leute würden leider eine Aussage hinzufügen, die nur den Wert in einem flüchtigen Register liest, um die Zeit totzuschlagen. Leider war manchmal eine Aussage nicht genug. Die Situation führte zu einigen sehr hässlichen Hacks.

TI fügte schließlich das PRGPIO-Register (General-Purpose Input/Output Peripheral Ready) hinzu. Dieses Register hat ein Bit für jeden Port, und Sie können das Bit lesen, um zu sehen, wann der Port einsatzbereit ist. Ähnliche Register sind für andere Peripheriegeräte verfügbar, wie etwa das Universal Asynchronous Receiver/Transmitter Peripheral Ready-Register.

DANKE SCHÖN! Ich habe versucht, die PR-Register zu verwenden, und es funktioniert gut!

Tiva ist ein Cortex-M4F. Wenn eine Art Schreibpufferung beteiligt ist, erreicht die Taktfreigabe möglicherweise noch nicht das Peripheriegerät (oder wird wirksam), während der Code versucht, auf das (immer noch deaktivierte) Peripheriegerät zuzugreifen, und Sie erhalten einen harten Fehler.

__DSB();Ich würde zunächst versuchen, eine (oder asm("dsb");) Anweisung zwischen der Taktfreigabe und dem Zugriff auf das Peripheriegerät hinzuzufügen .

Meinst du asm(" dsb");? Compiler erkennt nicht __DSB();. Der asm funktioniert aus irgendeinem Grund für GPIO, aber nicht für UART wie dieser Code:SYSCTL_RCGCUART_R |= SYSCTL_RCGCUART_R7; asm(" dsb"); UART7_CTL_R = 0x00;