Forum: Mikrocontroller und Digitale Elektronik PIC16 Anweisungen wegoptimiert


von Silvio G. (technofreak)


Lesenswert?

Hallo

ich lese hier schon seit längerem mit und habe auch schon so manches 
Problem lösen können durch die Beiträge anderer User. Heute hab ich auch 
mal ein Problem was ich noch nicht lösen konnte.

Ich nutze folgende Funktion um die Parität eines Zeichens zu berechnen 
und übergebe dann die 9 Bit an die Hardware-UART. Bei der Compoilierung 
werden die beiden Zeilen, die um 1 bzw. 2 Bit shiften einfach 
wegoptimiert, alle anderen bleiben bestehen.

Wie kann das sein? Wie kann ich das verhindern?

Silvio
1
//
2
/*  Zeichen per UART senden*/
3
void zeichen_senden(unsigned char zeichen)
4
{
5
  bit9 = (bit9 ^ (zeichen & 0b00000001));                    //  \
6
  bit9 = (bit9 ^ ((zeichen >> 1) & 0b00000001));                //   \
7
  bit9 = (bit9 ^ ((zeichen >> 2) & 0b00000001));                //    \Paritätsberechnung
8
  bit9 = (bit9 ^ ((zeichen >> 3) & 0b00000001));                //    /
9
  bit9 = (bit9 ^ ((zeichen >> 4) & 0b00000001));                //   /
10
  bit9 = (bit9 ^ ((zeichen >> 5) & 0b00000001));                //  /
11
  bit9 = (bit9 ^ ((zeichen >> 6) & 0b00000001));                // /
12
  bit9 = (bit9 ^ ((zeichen >> 7) & 0b00000001));
13
  TX2STAbits.TX9D = bit9;                            //9. Bit zuweisen
14
  TX2REG = zeichen;                              //Daten ins Ausgaberegister schieben, Übertragung beginnt
15
}

von Kein Name (Gast)


Lesenswert?

Der Backslash maskiert das Newline?

von Karl H. (kbuchegg)


Lesenswert?

Kein Name schrieb:
> Der Backslash maskiert das Newline?


Oh, ja. Der Programmier-Fehler ist wirklich fies.

INteressant ist, dass auch die Forensoftware das hinkriegt, dass die 
beiden Zeilen auskommentiert sind, weil sie mit dem \ alle zu einer 
Zeile zusammengezogen wurden und daher alles nach dem \\ in der ersten 
Zeile als Kommentar gewertet wird. Hätte ich jetzt nicht gedacht, dass 
die das schafft.

von Karl H. (kbuchegg)


Lesenswert?

abgesehen davon
1
void zeichen_senden(unsigned char zeichen)
2
{
3
  bit9 = (bit9 ^ (zeichen & 0b00000001));                    //  \

bist du dir sicher, dass die Paritätsberechnung mit einem nicht näher 
bekannten Wert von bit9 beginnen soll?

Alles in allem ist deine Paritätsberechnung mit den vielen 
Schiebeoperationen alles andere als µC-schonend implementiert. Hoffen 
wir mal, dass der Optimizer des Compilers erkennt, dass er das jeweilige 
Ergebnis der Stufe davor weiterverwenden kann.

von Silvio G. (technofreak)


Lesenswert?

Hallo

auf diese Idee mit dem fortsetzen der Zeile bin ich noch gar nicht 
gekommen. Ich dachte imme nach // ist alles Kommentar bis zum Zeilenende 
also auch ein \. Habe aber jetzt im Disassambly Listing gesehen daß auch 
an anderen Stellen was als Kommentar angesehen wurde was eigentlich Code 
sein sollte. Hat aber bisher keine Auswirkungen gehabt weil in der 
Initialisierung und nur bei Werten die ich auch so setze wie sie nach 
einem Reset automatisch sind.

Mit dem Startwert für bit9 ist geändert. Die Berechnung in Assambler 
erfolgt folgendermaßen:

bit9 testen
Ergebnis in temporäre Speicherstelle
Zeichen laden
Zeichen shiften
Zeichen Bit 0 testen
Ergebnis in Arbeitsregister
XOR von Arbeitsregister und temporärer Speicherstelle
Ergebnis in Arbeitsregister
Arbeitsregister Bit 0 testen
Ergebnis in Bit9 speichern

Ist für alle 8 Bit gleich bis auf die Anzahl der Stellen beim Shift. 
Sollte also passen. Für die Berechnung werden etwa 200 Assambler-Befehle 
gebraucht.

Vielen dank für die Hilfe, ohne euch hätte ich den Fehler garantiert 
nicht gefunden.

Silvio

von __ (Gast)


Lesenswert?

Es gibt auch einen Denkfehler.
{
  bit9 = (bit9 ^ (zeichen & 0b00000001));

sollte eigentlich sein:
{
  bit9 = (zeichen & 0b00000001);


Wechsle das aus und poste auch den ASM Code, welcher der Compiler 
generiert.

Ansonten, eventuell eine ASM Funktion:

 swapf zeichen,W
 xorwf zeichen,W
 andlw 0xf0
 movwf tmp

 rlf   tmp,w
 rlf   tmp
 btfsc tmp,7
 incf  tmp
 btfsc tmp,6
 incf  tmp
 btfsc tmp,
 incf  tmp

 bcf   TX2STAbits,TX9D
 btfsc tmp,0
 bsf   TX2STAbits,TX9D

von Silvio G. (technofreak)


Angehängte Dateien:

Lesenswert?

Hallo

ich habe jetzt vor der Berechnung bit9 auf 0 gesetzt. Damit kann der 
Code so bleiben.

Die ASM-Variante ist zwar schön kurz und auch schnell, aber da ich weder 
mit dem Platz (8k Befehle, 4k genutzt) noch der Ausführungszeit (ca. 
40µs) Probleme habe werde ich das erstmal so lassen.

Der ASM-Code der erzeugt wird ist im Anhang.

Silvio

von Peter II (Gast)


Lesenswert?

sa geht doch wohl auch mit C schneller
1
uint8_t bit9 = 0;
2
uint8_t tmp = zeichen;
3
for( uint8_t bit = 0; bit < 7; ++bit ) {
4
   bit9 = bit9 ^ (zeichen & 1);
5
   zeichen = zeichen >> 1;
6
}
7
TX2STAbits.TX9D = bit9;   
8
TX2REG = zeichen;

von Karl H. (kbuchegg)


Lesenswert?

Silvio G. schrieb:

> Die ASM-Variante ist zwar schön kurz und auch schnell

Sorry. Das würde ich weder kurz noch schnell nennen.

Probier mal diese Variante, ob die schneller geht bzw. weniger Code 
produziert
1
unsigned char Parity( unsigned char i )
2
{
3
  static const unsigned char nibbleParity[] =    // die Parität für jeweils 4 Bit
4
  {
5
    0, 1, 1, 0,     // 0000, 0001, 0010, 0011
6
    1, 0, 0, 1,     // 0100, 0101, 0110, 0111
7
    1, 0, 0, 1,     // 1000, 1001, 1010, 1011
8
    0, 1, 1, 0      // 1100, 1101, 1110, 1111
9
  };
10
11
  return nibbleParity[i&0x0F] ^ nibbleParity[i>>4];
12
}

von Karl H. (kbuchegg)


Lesenswert?

Silvio G. schrieb:
> Hallo
>
> auf diese Idee mit dem fortsetzen der Zeile bin ich noch gar nicht
> gekommen. Ich dachte imme nach // ist alles Kommentar bis zum Zeilenende
> also auch ein \.

Die Reihenfolge in der der Präprozessor arbeitet ist:

  Zuerst Line Continuations zusammenziehen
  dann Kommentare durch 1 Leerzeichen ersetzen

von spess53 (Gast)


Lesenswert?


von (prx) A. K. (prx)


Lesenswert?

Silvio G. schrieb:
> Der ASM-Code der erzeugt wird ist im Anhang.

Nur aus Neugierde: Ist das der erzeugte Code mit eingeschalteter 
Optimierung?

von Silvio G. (technofreak)


Angehängte Dateien:

Lesenswert?

Hallo

mit dem schnell und kurz meinte ich aus dem Post vor mit das

swap...

Meine Version sind ca. 300 Befehlszyklen bei ca. 200 Befehlen.

Die Funtion Parity ergibt 74 Befehle mit ca. 130 Befehlszyklen (ASM im 
Anhang).

Silvio

von (prx) A. K. (prx)


Lesenswert?

Probier lieber mal PeDas Variante auf die Spess verlinkt hat.

von (prx) A. K. (prx)


Lesenswert?

Silvio G. schrieb:
> Die Funtion Parity ergibt 74 Befehle mit ca. 130 Befehlszyklen (ASM im
> Anhang).

Zähl nochmal nach und lass dabei jene Daten weg, die nicht dazu gehören. 
Von der Tabelle sind nur die ersten 16 relevant, der Rest sind 
irgendwelche Strings.

von Karl H. (kbuchegg)


Lesenswert?

Silvio G. schrieb:


> Die Funtion Parity ergibt 74 Befehle mit ca. 130 Befehlszyklen (ASM im
> Anhang).


Autsch.
Wenn der Compiler ein static Array so einbaut, dass er es jedesmal beim 
Betreten der Funktion wieder neu erzeugt, würde ich ganz schnell den 
Compiler wechseln.

von (prx) A. K. (prx)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wenn der Compiler ein static Array so einbaut, dass er es jedesmal beim
> Betreten der Funktion wieder neu erzeugt, würde ich ganz schnell den
> Compiler wechseln.

Wie kommst du darauf?

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Silvio G. schrieb:
>
>
>> Die Funtion Parity ergibt 74 Befehle mit ca. 130 Befehlszyklen (ASM im
>> Anhang).
>
>
> Autsch.
> Wenn der Compiler ein static Array so einbaut, dass er es jedesmal beim
> Betreten der Funktion wieder neu erzeugt, würde ich ganz schnell den
> Compiler wechseln.

Edit:
Was treibt der Compiler da? Da ist noch mehr in der Funktion als nur die 
Tabelle.

Hast du den Optimizer eingeschaltet?
Oder die Generierung von Debug-Information aktiviert?

von Silvio G. (technofreak)


Lesenswert?

@ A. K.

leider nicht, da es für den Compiler 3 Varianten gibt. Die besseren 
beiden kosten Geld, die schlechteste ist frei. Die beste Variante soll 
um 40% kleineren Code erzeugen.

Silvio

von Karl H. (kbuchegg)


Lesenswert?

A. K. schrieb:
> Karl Heinz Buchegger schrieb:
>> Wenn der Compiler ein static Array so einbaut, dass er es jedesmal beim
>> Betreten der Funktion wieder neu erzeugt, würde ich ganz schnell den
>> Compiler wechseln.
>
> Wie kommst du darauf?

OK. Ich bin nicht 100% sicher. Aber das hier
1
653:               unsigned char Parity( unsigned char i )
2
654:               {
3
655:                 static const unsigned char nibbleParity[] =    // die Parität für jeweils 4 Bit
4
  0263    3400     RETLW 0
5
  0264    3401     RETLW 0x1
6
  0265    3401     RETLW 0x1
7
  0266    3400     RETLW 0
8
  0267    3401     RETLW 0x1
9
  0268    3400     RETLW 0
10
  0269    3400     RETLW 0
11
  026A    3401     RETLW 0x1
12
  026B    3401     RETLW 0x1
13
  026C    3400     RETLW 0
14
  026D    3400     RETLW 0
15
  026E    3401     RETLW 0x1
16
  026F    3400     RETLW 0
17
  0270    3401     RETLW 0x1
18
  0271    3401     RETLW 0x1
19
  0272    3400     RETLW 0
20
  0273    3420     RETLW 0x20
21
  0274    3450     RETLW 0x50
22
  0275    3461     RETLW 0x61
23
  0276    3472     RETLW 0x72
24
  0277    3469     RETLW 0x69
25
  0278    3474     RETLW 0x74
26
  0279    3461     RETLW 0x61
27
  027A    3465     RETLW 0x65
28
  027B    3474     RETLW 0x74
29
  027C    3420     RETLW 0x20
30
  027D    3400     RETLW 0
31
  027E    3430     RETLW 0x30
32
  027F    3431     RETLW 0x31
33
  0280    3432     RETLW 0x32
34
  0281    3433     RETLW 0x33
35
  0282    3434     RETLW 0x34
36
  0283    3435     RETLW 0x35
37
  0284    3436     RETLW 0x36
38
  0285    3437     RETLW 0x37
39
  0286    3438     RETLW 0x38
40
  0287    3439     RETLW 0x39
41
  0288    3400     RETLW 0
42
  0289    3420     RETLW 0x20
43
  028A    3457     RETLW 0x57
44
  028B    3465     RETLW 0x65
45
  028C    3472     RETLW 0x72
46
  028D    3474     RETLW 0x74
47
  028E    3420     RETLW 0x20
48
  028F    3400     RETLW 0
49
  0290    344E     RETLW 0x4e
50
  0291    3472     RETLW 0x72
51
  0292    342E     RETLW 0x2e
52
  0293    3420     RETLW 0x20
53
  0294    3400     RETLW 0
54
  0295    3430     RETLW 0x30
55
  0296    3431     RETLW 0x31
56
  0297    3400     RETLW 0
57
656:                 {
58
657:                   0, 1, 1, 0,     // 0000, 0001, 0010, 0011
59
658:                   1, 0, 0, 1,     // 0100, 0101, 0110, 0111
60
659:                   1, 0, 0, 1,     // 1000, 1001, 1010, 1011
61
660:                   0, 1, 1, 0      // 1100, 1101, 1110, 1111
62
661:                 };
63
662:               
64
663:                 return nibbleParity[i&0x0F] ^ nibbleParity[i>>4];
65
  02C8    00F3     MOVWF 0x73

sieht für mich danach aus. Die RETLW rese.....


Vergiss es. Da hab ich mich vertan.
Das hat er schon gut gemacht.

von (prx) A. K. (prx)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Oder die Generierung von Debug-Information aktiviert?

Da sind beim Disassembleren irgendwelche Strings mit zugerordnet worden, 
die nicht zu diesem Teil des Codes zählen aber keine Symbole haben.

von (prx) A. K. (prx)


Lesenswert?

Silvio G. schrieb:
> leider nicht, da es für den Compiler 3 Varianten gibt. Die besseren
> beiden kosten Geld, die schlechteste ist frei. Die beste Variante soll
> um 40% kleineren Code erzeugen.

Gratuliere. Der erzeugte Code der ersten Variante ist selbst für einen 
nicht optimierenden Compiler geradezu abschrenkend schlecht.

von Peter D. (peda)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Oh, ja. Der Programmier-Fehler ist wirklich fies.

Der AVR-GCC gibt bei sowas aussagekräftige Meldungen aus.
1
  irgendwas // \
warning: multi-line comment


Peter

von Karl H. (kbuchegg)


Lesenswert?

A. K. schrieb:
> Karl Heinz Buchegger schrieb:
>> Oder die Generierung von Debug-Information aktiviert?
>
> Da sind beim Disassembleren irgendwelche Strings mit zugerordnet worden,
> die nicht zu diesem Teil des Codes zählen aber keine Symbole haben.

Sieht wohl so aus. Mal rückübersetzen
1
  0273    3420     RETLW 0x20    ' '
2
  0274    3450     RETLW 0x50    'P'
3
  0275    3461     RETLW 0x61    'a'
4
  0276    3472     RETLW 0x72    'r'
5
  0277    3469     RETLW 0x69    'i'
6
  0278    3474     RETLW 0x74    't'
7
  0279    3461     RETLW 0x61    'a'
8
  027A    3465     RETLW 0x65    'e'
9
  027B    3474     RETLW 0x74    't'
10
  027C    3420     RETLW 0x20    ' '
11
  027D    3400     RETLW 0
12
  027E    3430     RETLW 0x30    '0'
13
  027F    3431     RETLW 0x31    '1'
14
  0280    3432     RETLW 0x32    '2'
15
  0281    3433     RETLW 0x33    '3'
16
  0282    3434     RETLW 0x34    '4'
17
  0283    3435     RETLW 0x35    '5'
18
  0284    3436     RETLW 0x36    '6'
19
  0285    3437     RETLW 0x37    '7'
20
  0286    3438     RETLW 0x38    '8'
21
  0287    3439     RETLW 0x39    '9'
22
  0288    3400     RETLW 0
23
  0289    3420     RETLW 0x20    ' '
24
  028A    3457     RETLW 0x57    'W'
25
  028B    3465     RETLW 0x65    'e'
26
  028C    3472     RETLW 0x72    'r'
27
  028D    3474     RETLW 0x74    't'
28
  028E    3420     RETLW 0x20    ' '
29
  028F    3400     RETLW 0
30
  0290    344E     RETLW 0x4e    'N'
31
  0291    3472     RETLW 0x72    'r'
32
  0292    342E     RETLW 0x2e    '.'
33
  0293    3420     RETLW 0x20    ' '
34
  0294    3400     RETLW 0
35
  0295    3430     RETLW 0x30    '0'
36
  0296    3431     RETLW 0x31    '1'
37
  0297    3400     RETLW 0


Jep. Das sind wohl Strings von woanders aus dem Programm, die nur 
zufällig hier beim Disassemblieren mitgekommen sind. Die eigentliche 
Funktion ist das hier
1
 02C8    00F3     MOVWF 0x73
2
  02C9    0873     MOVF 0x73, W
3
  02CA    00F1     MOVWF 0x71
4
  02CB    3004     MOVLW 0x4
5
  02CC    36F1     LSRF 0x71, F
6
  02CD    0B89     DECFSZ 0x9, F
7
  02CE    2ACC     GOTO 0x2cc
8
  02CF    0871     MOVF 0x71, W
9
  02D0    3E63     ADDLW 0x63
10
  02D1    3182     MOVLP 0x2
11
  02D2    000A     CALLW
12
  02D3    3182     MOVLP 0x2
13
  02D4    00F2     MOVWF 0x72
14
  02D5    0873     MOVF 0x73, W
15
  02D6    390F     ANDLW 0xf
16
  02D7    3E63     ADDLW 0x63
17
  02D8    3182     MOVLP 0x2
18
  02D9    000A     CALLW
19
  02DA    3182     MOVLP 0x2
20
  02DB    0672     XORWF 0x72, W
21
  02DC    2ADD     GOTO 0x2dd
22
664:               }
23
  02DD    0008     RETURN

von Peter D. (peda)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Die eigentliche
> Funktion ist das hier

Die obersten 16 RETLW gehören aber noch dazu.


Peter

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.