www.mikrocontroller.net

Forum: Compiler & IDEs "high word" eines uint32_t "abspalten"


Autor: Heiko L. (drcaveman)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

Es geht hier um den AVR-GCC.

Ich müsste folgendes realisieren:
uint32_t a;
uint8_t b;

b = (a & 0x3FC00000) >> 22;

Der Compiler macht dann genau das, was man erwartet- eine Schleife mit 
22 Durchläufen und benutzt dabei alle vier Register, die für den 32Bit- 
Integer benutzt werden.

Rein logisch könnte man ja auch die oberen 16Bit von a "abknipsen" und 
nur um sechs stellen schieben, so dass auch im assembler nur die beiden 
oberen Register von a angesprochen werden.

Geht das oder kommt man da um den inline- assembler nicht herum?

Danke,
Heiko.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mach es doch einfach mit einer union oder mit Zeigern...
union{
    uint32_t dword;
    uint16_t word16[2];
}
Mit word16[0] und word16[1] kannste dann einfach auf die einzelnen 
16-Bit-Worte zugreifen.

Autor: Heiko L. (drcaveman)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!

Das hier sieht brauchbar aus:
uint32_t a;
uint8_t b;

b = ((uint16_t)*(&a)) >> 6

Danke!

Edit: Oops, da fehlte wohl noch ein "*"...

Autor: >>22 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bei mir gehen die 3 Varianten mit Zyklenzahl dabei
b = (unsigned char)((a/65536) & 0x3FC0) >> 6;  // 33 TZ
b = ((a&0x3FC0000)>>16) >> 6;   // 101 TZ
b = (a & 0x3FC00000) >> 22;    // 241 TZ


Autor: Heiko L. (drcaveman)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oh, da war doch noch mehr zu wurschteln:
uint32_t a;
uint8_t b;

b = (uint8_t) ( ((uint16_t) *( ((uint16_t *)(&a)) + 1 )) >> 6);

Das ist sehr lesbar ;)

Ich versuche dann auch mal die "union Version"

Autor: Heiko L. (drcaveman)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>>22 wrote:
> Bei mir gehen die 3 Varianten mit Zyklenzahl dabei
>
>
> b = (unsigned char)((a/65536) & 0x3FC0) >> 6;  // 33 TZ
> b = ((a&0x3FC0000)>>16) >> 6;   // 101 TZ
> b = (a & 0x3FC00000) >> 22;    // 241 TZ
> 
> 

Lustig, wie das auseinander driftet.

Kannst du auch mal die neue Pointer- Version ausprobieren?

Autor: Heiko L. (drcaveman)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Heiko L. wrote:

> Ich versuche dann auch mal die "union Version"

So:
volatile uint32_t a;

union _u_a_split
{
    uint32_t* a32;
    uint16_t* a16;
};

void bla(void)
{
    uint8_t b;
    union _u_a_split a_split;

    a_split.a32 = &a;
    b = (uint8_t) (a_split.a16[1] >> 6);
}

Benötigt auf einem ATMega48 8 Taktzyklen mehr als die Pointer- Version 
(28 vs. 20). Sieht aber etwas schöner aus.

Autor: Heiko L. (drcaveman)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Noch einmal eine kurze Zusammenfassung:

170 Zyklen:
b = (a & 0x3FC00000) >> 22;

166 Zyklen:
b = a >> 22;

28 Zyklen:
a_split.a32 = &a;
b = (uint8_t) (a_split.a16[1] >> 6);

20 Zyklen:
b = (uint8_t) ( ((uint16_t) *( ((uint16_t *)(&a)) + 1 )) >> 6);

Autor: ... (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die letzte Variante kann man aber auch kürzer schreiben:
b = *((uint16_t*)&a + 1) >> 6);
Beide Schreibweisen erzeugen bei mir den selben Code und brauchen nur 13 
Zyklen (Mega8, GCC 3.4.6 mit -O3)

CU

Autor: ... (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
PS: -Os erzeugt 8 Byte weniger Code, braucht dann aber 34 Zyklen.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
10 Takte, gcc 4.3.3:
  b = ((unsigned)(a >> 16) << 2) >> 8;

Varianten über Speicher haben Nachteile. Der übrige Code, der auf die 
Variablen zugreift, wird aufwendiger weil nicht in Register. Und es 
erwingt oft einen teuren Stackframe.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A. K. wrote:
> 10 Takte, gcc 4.3.3:
>   b = ((unsigned)(a >> 16) << 2) >> 8;
>
> Varianten über Speicher haben Nachteile. Der übrige Code, der auf die
> Variablen zugreift, wird aufwendiger weil nicht in Register. Und es
> erwingt oft einen teuren Stackframe.

Kommt drauf an, ob der Wert auto/parameter ist oder nicht.
unsigned long x;
unsigned char y;

void shift22_a (unsigned long x)
{
    y = ((unsigned)(x >> 16) << 2) >> 8;
}

void shift22_b ()
{
    y = (1[(unsigned*) &x] << 2) >> 8;
}

Ergibt in 4.3.2 mit -O2
shift22_a:
/* prologue: function */
/* frame size = 0 */
  movw r22,r24   ;  25  *lshrsi3_const/3  [length = 3]
  clr r24
  clr r25
  lsl r22   ;  24  *ashlhi3_const/4  [length = 4]
  rol r23
  lsl r22
  rol r23
  sts y,r23   ;  9  *movqi/3  [length = 2]
/* epilogue start */
  ret   ;  22  return  [length = 1]

shift22_b:
/* prologue: function */
/* frame size = 0 */
  lds r24,x+2   ;  6  *movhi/2  [length = 4]
  lds r25,(x+2)+1
  lsl r24   ;  24  *ashlhi3_const/4  [length = 4]
  rol r25
  lsl r24
  rol r25
  sts y,r25   ;  9  *movqi/3  [length = 2]
/* epilogue start */
  ret   ;  22  return  [length = 1]

Schneller dürfte es kaum gehen.

Johann

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Johann:
(unsigned*)

Hör mir auf mit Standard Int Datentypen. Dachte sowas gehört auf die 
Liste der verbotenen C-Features.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Simon K. wrote:
> Hör mir auf mit Standard Int Datentypen.

Es geht lediglich um einen Testfall. Ich kann gerne Testfälle posten mit 
den beliebten
#include "my-incredible-foo-defines.h"
#include "my-fancy-bar-types.h"

T f (T x)
{
   return F(x);
}

Autor: let (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>170 Zyklen:
>
b = (a & 0x3FC00000) >> 22;

4 Zyklen:
shift2:
ldr  r3, .L3
mov  r0, r0, lsr #22
strb  r0, [r3, #0]
bx  lr

4 Zyklen:
void shift22_a (unsigned long x)
{
    y = ((unsigned)(x >> 16) << 2) >> 8;
}

shift22_a:
ldr  r3, .L7
mov  r0, r0, lsr #22
strb  r0, [r3, #0]
bx  lr
arm-none-eabi-gcc -O2 -S dummy.c

Sorry ;)

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
let wrote:

> 4 Zyklen:
>
> shift2:
> ldr  r3, .L3
> mov  r0, r0, lsr #22
> strb  r0, [r3, #0]
> bx  lr
> 
> arm-none-eabi-gcc -O2 -S dummy.c
> Sorry ;)

Und?

Heiko L. wrote:
> Es geht hier um den AVR-GCC.

Oder hast du per avr-gcc -B... nen anderen cc1 reingestöpselt? ;-)

Johann

Autor: Heiko L. (drcaveman)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A. K. wrote:
> 10 Takte, gcc 4.3.3:
>   b = ((unsigned)(a >> 16) << 2) >> 8;
>
> Varianten über Speicher haben Nachteile. Der übrige Code, der auf die
> Variablen zugreift, wird aufwendiger weil nicht in Register. Und es
> erwingt oft einen teuren Stackframe.

Das braucht bei mir 19 Takte (a ist eine globale Variable im ram -> 4 
mal LDS...), also einen Takt weniger als die "Pointer- Version", aber 
immerhin!

Jetzt habe ich noch etwas probiert:
b = (*((uint16_t*)&a + 1) << 2) >> 8;

17 Takte!

Diese Version profitiert wohl einmal davon, dass hier wirklich nur das 
"high word" aus dem Speicher geholt wird (a ist global) und dann von dem 
einen "2er shift", der "8er shift" wird ja zu einer "Kopieraktion".

Danke!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.