Forum: Mikrocontroller und Digitale Elektronik Cortex M4: Sättigende Multiplikation


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,

ich habe einen Cortex M4 (STM32F446) und muß einen Haufen Berechnungen 
der Form
 int32_t a, b, c;
 x(+)= a(*)b
und
 x(-)= a(*)b
durchführen. Dabei soll sowohl die Addition als auch die Multiplikation 
sättigendend sein (deswegen habe ich Multiplikations- und 
Additionszeichen etwas abgewandelt).

Ich wühle mich gerade durch die arm_math.h, die jede Menge interessanter 
Operationen bietet, aber eine so einfache häufig gebrauchte (zumindest 
in der Computergrafik) Funktion nicht zu bieten scheint.

Der Cortex M3 bietet ja durchaus sättigende Operation im Instruction 
Set. Wie kann ich diese Operation unter C sinnvoll nutzen?

 *Muß es inline Assembler sein?
 *Oder die Operation in C nachbauen und hoffen, daß der Compiler die 
richtige Operation daraus macht?
 *Oder gibt es einfach noch eine andere ARM-Mathe-Library, die ich 
übersehen habe?

Viele Grüße
W.T.

von Vincent H. (vinci)


Angehängte Dateien:

Lesenswert?

Du sprichst ein (meines Wissens nach) Alleinstellungsmerkmal des ARM 
Compilers an. Es gibt dort eine gewisse Palette an Idiomen, die 
garantiert direkt in die entsprechenden DSP Befehle umgewandlt werden. 
Eine Tabelle dieser Idiome hab ich mal eben angehängt.

Persönlich hab ich die Erfahrung gemacht, dass GCC leider nicht alle 
dieser Formulierungen so direkt schluckt. Nutzt du alles GCC (oder 
Clang?), dann würde ich zur Nutzung der inline Varianten raten... Die 
lassen sich ja gegebenenfalls in eigenen Funktionen nach belieben 
schachteln.

von Walter T. (nicolas)


Lesenswert?

Hallo Vincent,

danke erstmal für die schöne Tabelle. Ist sie aus dem Buch von Joseph 
Yiu?

Erste Erkenntnis: Die sättigenden Multiplikationen beziehen sich nur auf 
1x2 Vektoren mit beiden Werten verpackt in einem 32-Bit Wert. Zwar 
sicher nett für so manche Anwendung, aber für mich nicht hilfreich.

Zweite Erkenntnis: Es gibt keine sättigende Multiplikation zweier 
32-Bit-Werte mit Sättigung auf INT32_MAX/MIN. Aber ich kann zwei 
31-Bit-Werte multiplizieren und anschließend auf 31 Bit sättigen. Auch 
gut.

Ich habe es mal ausprobiert:
1
int32_t ssat30(int32_t x)
2
{
3
    const int32_t lim = (1L<<30);
4
    return (x < -lim ? -lim : (x > lim-1 ? lim-1 : x  )   );
5
}
6
7
void main(void)
8
{
9
    [...]
10
    int32_t i = INT32_MIN;
11
    while( 1 ) {
12
        int32_t j = ssat30(i);
13
        printf("i=%li, j=%li \n",i, j);
14
        i ++;
15
    }
16
}
Wird mit dem ARM-GCC 5.4.1 zu:
1
(102)      while( 1 ) {
2
(103)          int32_t j = ssat30(i);
3
(104)          printf("i=%li, j=%li \n",i, j);
4
0800034A  mvn.w  r6, #3221225472  ; 0xc0000000
5
0800034E  ldr  r5, [pc, #52]  ; (0x8000384 <main+212>)
6
08000350  mov  r2, r4
7
08000352  cmp  r4, r6
8
08000354  it  ge
9
08000356  movge  r2, r6
10
08000358  cmp.w  r2, #3221225472  ; 0xc0000000
11
0800035C  it  lt
12
0800035E  movlt.w  r2, #3221225472  ; 0xc0000000
13
08000362  mov  r1, r4
14
08000364  mov  r0, r5
15
08000366  bl  0x8000df8 <printf>
16
(105)          i ++;
17
0800036A  adds  r4, #1
18
0800036C  b.n  0x8000350 <main+160>
19
0800036E  nop
20
08000370  movs  r0, r0
21
08000372  movs  r0, #0
22
08000374  udf  #131  ; 0x83
23
08000376  orrs  r3, r3
24
08000378  b.n  0x800039c <dda+20>

d.h. der Compiler kennt das Idiom nicht. Wahrscheinlich wird es doch mal 
Zeit, Inline-Assembler zu lernen.

von Walter T. (nicolas)


Lesenswert?

Nachtrag: Oben war natürlich ein Denkfehler drin. Richtig wäre:
1
int32_t ssat30(int32_t x)
2
{
3
    const int32_t lim = (1L<<30);
4
    return (x < -lim-1 ? -lim-1 : (x > lim ? lim : x  )   );
5
}
und das Ergebnis des Compilers ist:
1
while( 1 ) {
2
(106)          int32_t j = ssat30(i);
3
(107)          printf("i=%li, j=%li \n",i, j);
4
08000352  mvn.w  r6, #1073741824  ; 0x40000000
5
08000356  ldr  r5, [pc, #60]  ; (0x8000394 <main+228>)
6
08000358  mov  r2, r4
7
0800035A  cmp.w  r4, #1073741824  ; 0x40000000
8
0800035E  it  ge
9
08000360  movge.w  r2, #1073741824  ; 0x40000000
10
08000364  cmp  r2, r6
11
08000366  it  lt
12
08000368  movlt  r2, r6
13
0800036A  mov  r1, r4
14
0800036C  mov  r0, r5
15
0800036E  bl  0x8000e08 <printf>

von Walter T. (nicolas)


Lesenswert?

Nachtrag: In der core_cmInstr.h hat schon jemand genau das für mich 
gemacht. Sehr schön. Der Befehl lautet "__SSAT".

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.