2017-01-16 6 views
3

Ich muss alternative Bytes in einem Puffer so schnell wie möglich in einem eingebetteten System mit ARM Cortex M4-Prozessor austauschen. Ich benutze gcc. Die Datenmenge ist variabel, aber die maximale ist etwas über 2K. Es spielt keine Rolle, ob ein paar zusätzliche Bytes konvertiert werden, weil ich einen übergroßen Puffer verwenden kann.Schnellste Möglichkeit, alternative Bytes auf ARM Cortex M4 mit gcc auszutauschen

Ich weiß, dass der ARM die REV16 Anweisung hat, die ich verwenden kann, um alternative Bytes in einem 32-Bit-Wort zu tauschen. Was ich nicht weiß ist:

  1. Gibt es eine Möglichkeit, diese Anweisung in gcc ohne Assembler zu bekommen? Das __builtin_bswap16 intrinsische System scheint nur mit 16-Bit-Wörtern zu arbeiten. Das Konvertieren von 4 Bytes gleichzeitig ist sicherlich schneller als das Konvertieren von 2 Bytes.

  2. Hat der Cortex M4 einen Umordnungspuffer und/oder registriert er Umbenennen? Wenn nicht, was muss ich tun, um Pipeline-Blockierungen zu minimieren, wenn ich die dwords des Puffers in einer teilweise abgerollten Schleife konvertiere?

Zum Beispiel dieser Code effizient ist, wo REV16 in geeigneter Weise zu lösen (1) definiert ist:

uint32_t *buf = ... ; 
size_t n = ... ; // (number of bytes to convert + 15)/16 
for (size_t i = 0; i < n; ++i) 
{ 
    uint32_t a = buf[0]; 
    uint32_t b = buf[1]; 
    uint32_t c = buf[2]; 
    uint32_t d = buf[3]; 
    REV16(a, a); 
    REV16(b, b); 
    REV16(c, c); 
    REV16(d, d); 
    buf[0] = a; 
    buf[1] = b; 
    buf[2] = c; 
    buf[3] = d; 
    buf += 4; 
} 
+0

Was ist falsch mit [Inline-Assembler] (https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html).Es ist nicht tragbar, aber auch nicht "__builtin_bswap16". '__builtin_bswap16' ist nur gut für GCC mit mehreren CPU-Typen. Hauptsächlich für libc-Autoren, die auf GCC abzielen. Ein sehr gezielter bedingter Code, der beim Portieren einen Fehler verursacht, wäre für die meisten Leute wahrscheinlich akzeptabel. Schreiben Sie einfach ein paar gute Kommentare. Siehe: [bswapdi2.S] (https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/arch/arm/lib/bswaps- di2.S) im Linux-Kernel. Ihr System ist möglicherweise speichergebunden und nicht an die CPU gebunden. –

+0

Siehe: [godbolt] (https://godbolt.org/g/nEydDt) für einige gerade 'C' mit der Maskenmethode von 'bswapdi2.s' für arch

+0

Danke, ich kann Inline-Assembler verwenden, wenn kein anderer Weg gleich schnell ist. Kann ich einfach verwenden: 'asm (" rev16 a, a ");' um die rev16-Operation auf meine Variable 'a' im obigen Beispiel anzuwenden? – dc42

Antwort

2

Sie können nicht die __builtin_bswap16 Funktion aus dem Grund, verwenden Sie erklärte, es funktioniert auf 16 Bit Wörter, so wird 0 das andere Halbwort. Ich vermute, der Grund dafür ist, das intrinsische Arbeiten auf Prozessoren, die keinen ähnlichen Befehl wie REV16 auf ARM haben, gleich zu halten.

Die Funktion

uint32_t swap(uint32_t in) 
{ 
    in = __builtin_bswap32(in); 
    in = (in >> 16) | (in << 16); 
    return in; 
} 

zu kompiliert (ARM GCC 5.4.1 -O3 -std = C++ 11 -march = ARMv7-m -mtune = cortex-m4 -mthumb)

rev  r0, r0 
    ror  r0, r0, #16 
    bx  lr 

Und Sie könnten wahrscheinlich den Compiler bitten, es inline, was Ihnen 2 Anweisungen pro 32-Bit-Wort geben würde. Ich kann mir keinen Weg vorstellen, GCC dazu zu bringen, REV16 mit einem 32bit-Operanden zu erzeugen, ohne eine eigene Funktion mit Inline-Assembly zu deklarieren.

EDIT

Als Follow-up, und basierend auf Kommentar des artless Lärms über die Nichtübertragbarkeit der __builtin_bswap Funktionen, the compiler recognizes

uint32_t swap(uint32_t in) 
{ 
    in = ((in & 0xff000000) >> 24) | ((in & 0x00FF0000) >> 8) | ((in & 0x0000FF00) << 8) | ((in & 0xFF) << 24); 
    in = (in >> 16) | (in << 16); 
    return in; 
} 

und schafft die gleiche 3 wie oben Anweisung Funktion, so Das ist ein portabler Weg, um es zu erreichen. obwohl, ob verschiedene Compiler würde die gleiche Leistung erzeugen ...

EDIT EDIT

Wenn Inline-Assembler erlaubt ist, die folgende Funktion

inline uint32_t Rev16(uint32_t a) 
{ 
    asm ("rev16 %1,%0" 
      : "=r" (a) 
      : "r" (a)); 
    return a; 
} 

inlined wird, und fungiert als eine einzelne Anweisung als kann gesehen werden here.

Verwandte Themen