Forum: Mikrocontroller und Digitale Elektronik STM32 und externer RAM


von Stephan M. (stmiko)


Lesenswert?

Hallo,

ich versuche bei dem besagten uC mit externen SRAM diesen zu beschreiben 
und auszulesen. Die Konfiguration ist soweit in Ordnung.
Aus dem Datenblatt habe ich entnommen, dass die erste Adresse die 
0x60000000 ist.

Bei dieser Adresse soll ein Header geschrieben werden, der 32 Byte lang 
ist. Dem folgenden die eigentliche Werte, die ich mittels des Headers 
später auslesen möchte.

Leider funktionieren meine Funktionen nicht so, wie ich mir das 
vorstelle. Deshalb meine Frage an euch, hat jemand auch schon mal sowas 
gemacht und könnte mir weiterhelfen?
Momentan habe ich eine Funktion, die den Header in den SRAM bei eine 
angegebenen Adresse schreiben soll und eine andere die diesen auslesen 
soll. Dabei bediene ich mir eines Makros, dass 32Byte in den interen 
Registern des Cortex beschreibt und das ist scheinbar der Knackpunkt...
1
 #define copyData32(pSrc, pDest) asm volatile(                      \
2
                      "ldmia   r0!,{r2-r6,r8-r10}\n"        \
3
                      "stmia   r1,{r2-r6,r8-r10}\n"        \
4
                      : /* no outputs  */            \
5
                      : "r" (pSrc), "r" (pDest)          \
6
                      : "r2", "r3", "r4", "r5", "r6", "r8", "r9", "r10");

Die Funktion zum Aufruf sieht folgendermaßen aus
1
AddrHead_Write = AddrHead[MessNr];
2
copyInBufferH((u32*)&HeaderR, (u32*)AddrHead_Write);
3
4
.
5
.
6
.
7
8
void copyInBufferH(register u32* pSrc, register u32* pAddr)
9
{
10
  copyData32(pSrc, pAddr);
11
}

Die Konfiguration des RAMS
1
 /*-- FSMC Configuration ------------------------------------------------------*/
2
  p.FSMC_AddressSetupTime = 1;    //no effect
3
  p.FSMC_AddressHoldTime = 1;    //no effect
4
  p.FSMC_DataSetupTime = 1;      //no effect
5
  p.FSMC_BusTurnAroundDuration = 1;  //no effect
6
  p.FSMC_CLKDivision = 1;      //HCLK / 2
7
  p.FSMC_DataLatency = 0;      //must be 0
8
  p.FSMC_AccessMode = FSMC_AccessMode_C;
9
10
  //FSMC asynchronen Mode für PSRAM Init setzen
11
  FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM1;  //2;
12
  FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Enable;
13
  FSMC_NORSRAMInitStructure.FSMC_MemoryType = FSMC_MemoryType_PSRAM;
14
  FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
15
  FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
16
  FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
17
  FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
18
  FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
19
  FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
20
  FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
21
  FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
22
  FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
23
  FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &p;
24
  FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &p;
25
26
  FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure); 
27
28
  FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM2;
29
  FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure); 
30
31
  /* Enable FSMC Bank2_PSRAM Bank */
32
  FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE);  //entfällt später  
33
34
  /* Enable FSMC Bank2_PSRAM Bank */
35
  FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM2, ENABLE);
Nach dem zweiten Ausführen der Fkt. "copyInBufferH" bleibt der
Prozessor an dem Marko hängen. In der AddrHead[MessNr] steht immer
die Anfangsadresse des zu beschreibenden RAMs...

Stephan

von (prx) A. K. (prx)


Lesenswert?

Musst den Befehlen schon die Register mitgeben, die der Compiler für die 
Adressen auserkoren hat:
1
#define copyData32(pSrc, pDest) asm volatile(                      \
2
                      "ldmia   %0,{r2-r6,r8-r10}\n"        \
3
                      "stmia   %1,{r2-r6,r8-r10}\n"        \
4
                      : /* no outputs  */            \
5
                      : "r" (pSrc), "r" (pDest)          \
6
                      : "r2", "r3", "r4", "r5", "r6", "r8", "r9", "r10");

Das "!" bei LDMIA könnte den Compiler bei Inlining durcheinanderbringen.

von (prx) A. K. (prx)


Lesenswert?

Wenn immer nur die 32 Bytes kopiert werden und keine grösseren Blöcke, 
dann würde ich nicht drauf wetten, dass deine Version viel effizienter 
ist als eine, die weniger Register braucht. Denn die Zwischenregister 
müssen gesichert werden, was auch Zeit braucht.

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

A. K. schrieb:
> Wenn immer nur die 32 Bytes kopiert werden und keine grösseren Blöcke,
> dann würde ich nicht drauf wetten, dass deine Version viel effizienter
> ist als eine, die weniger Register braucht.

Nur die Codegröße unterscheidet sich möglicherweise. Aber warum nicht 
einfach memcpy() verwenden?

> Denn die Zwischenregister müssen gesichert werden, was auch Zeit braucht.

Ja, der Teil fehlte mir auch irgendwie :-)

--
Marcus

von Stephan M. (stmiko)


Lesenswert?

Hallo,

danke erstmal für eure Ausführungen, werde es morgen gleich mal testen
und Rückmeldung geben!

von (prx) A. K. (prx)


Lesenswert?

Marcus Harnisch schrieb:

> Ja, der Teil fehlte mir auch irgendwie :-)

Nicht unbedingt, denn da die Register korrekt als "verwendet" 
gekennzeichnet wurden kümmert sich der Compiler selbst darum.

Allerdings werden so aus den 2 LDM/STMs insgesamt 4, sofern das als 
eigenständige Funktion implementiert ist. Andernfalls steht diese Aktion 
einer effektiven Registeroptimierung im Weg.

von Stephan M. (stmiko)


Lesenswert?

Hallo,

ich habe mich für die Funktion memcpy() entschieden!
Diese funktioniert bestens :-)

Vielen Dank für eure Hilfe

Stephan

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

A. K. schrieb:
> Nicht unbedingt, denn da die Register korrekt als "verwendet"
> gekennzeichnet wurden kümmert sich der Compiler selbst darum.

Irgendwann werde ich mal versuchen, diese kryptische Syntax des GCC 
Inline Assemblers zu verstehen. Wenn ich mir die memcpy() 
Implementierung bei CodeSourcery anschaue, dann sollte man wirklich 
darüber nachdenken...

Und ich dachte schon, der RealView Compiler hätte das durchaus besser 
machen können. Erst bei einem doppelten memcpy() mit jeweils vier Worten 
wird das von diesem Compiler richtig effizient umgesetzt.
1
void copyData32(u32 *pSrc, u32 *pDest)
2
{
3
    memcpy(pDest, pSrc, 16);
4
    memcpy(pDest + 4, pSrc + 4, 16);
5
}

Aber wenn Zeit nicht das eigentliche Problem ist, dann ist memcpy() in 
jedem Fall sinnvoller als Inline Assembler.

--
Marcus

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.