AVR Embedded - wo finde ich die Implementierung?

Rund um die LCL und andere Komponenten
Antworten
Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

AVR Embedded - wo finde ich die Implementierung?

Beitrag von Timm Thaler »

Bei der Multiplikation größerer Zahlen baut der Compiler statt der nativen mul-Befehle einen Aufruf von fpc_mul_dword oder fpc_mul_longint ein. Allerdings sind die Routinen sehr langsam und sehr speicherfressend.

Nun hab ich mir das mal im Disassembler angesehen, und tatsächlich scheint mir das extrem ineffizient, es wird alles in einer Schleife durchgezogen statt die nativen mul-Befehle zu verwenden.

Wo finde ich denn die Implementierung des Codes für fpc_mul_dword, fpc_mul_longint und so? Ich finde nur die Deklaration in compproc.inc. Ich würde mir gern mal ansehen, ob man da für AVR etwas optimieren kann.

Code: Alles auswählen

17e:   df 93          push   r29
 180:   cf 93          push   r28
 182:   1f 93          push   r17
 184:   0f 93          push   r16
 186:   ff 92          push   r15
 188:   ef 92          push   r14
 18a:   cf 92          push   r12
 18c:   bf 92          push   r11
 18e:   af 92          push   r10
 190:   9f 92          push   r9
 192:   8f 92          push   r8
 194:   7f 92          push   r7
 196:   6f 92          push   r6
 198:   5f 92          push   r5
 19a:   4f 92          push   r4
 19c:   3f 92          push   r3
 19e:   2f 92          push   r2
 1a0:   cd b7          in   r28, 0x3d   ; 61
 1a2:   de b7          in   r29, 0x3e   ; 62
 1a4:   c8 50          subi   r28, 0x08   ; 8
 1a6:   d0 40          sbci   r29, 0x00   ; 0
 1a8:   0f b6          in   r0, 0x3f   ; 63
 1aa:   f8 94          cli
 1ac:   de bf          out   0x3e, r29   ; 62
 1ae:   0f be          out   0x3f, r0   ; 63
 1b0:   cd bf          out   0x3d, r28   ; 61
 1b2:   6f 83          std   Y+7, r22   ; 0x07
 1b4:   17 2f          mov   r17, r23
 1b6:   f8 2e          mov   r15, r24
 1b8:   e9 2e          mov   r14, r25
 1ba:   61 2d          mov   r22, r1
 1bc:   71 2d          mov   r23, r1
 1be:   81 2d          mov   r24, r1
 1c0:   91 2d          mov   r25, r1
 1c2:   a1 e0          ldi   r26, 0x01   ; 1
 1c4:   ca 2e          mov   r12, r26
 1c6:   b1 2c          mov   r11, r1
 1c8:   a1 2c          mov   r10, r1
 1ca:   61 2c          mov   r6, r1
 1cc:   af ef          ldi   r26, 0xFF   ; 255
 1ce:   aa 83          std   Y+2, r26   ; 0x02
 1d0:   8a 80          ldd   r8, Y+2   ; 0x02
 1d2:   83 94          inc   r8
 1d4:   8a 82          std   Y+2, r8   ; 0x02
 1d6:   cb 82          std   Y+3, r12   ; 0x03
 1d8:   9b 2c          mov   r9, r11
 1da:   2a 2c          mov   r2, r10
 1dc:   86 2c          mov   r8, r6
 1de:   0b 81          ldd   r16, Y+3   ; 0x03
 1e0:   02 23          and   r16, r18
 1e2:   0b 83          std   Y+3, r16   ; 0x03
 1e4:   93 22          and   r9, r19
 1e6:   24 22          and   r2, r20
 1e8:   85 22          and   r8, r21
 1ea:   0b 81          ldd   r16, Y+3   ; 0x03
 1ec:   01 15          cp   r16, r1
 1ee:   91 04          cpc   r9, r1
 1f0:   21 04          cpc   r2, r1
 1f2:   81 04          cpc   r8, r1
 1f4:   a1 f0          breq   .+40        ;  0x21e
 1f6:   36 2e          mov   r3, r22
 1f8:   47 2e          mov   r4, r23
 1fa:   58 2e          mov   r5, r24
 1fc:   79 2e          mov   r7, r25
 1fe:   8f 80          ldd   r8, Y+7   ; 0x07
 200:   28 2c          mov   r2, r8
 202:   81 2e          mov   r8, r17
 204:   9f 2c          mov   r9, r15
 206:   ee 82          std   Y+6, r14   ; 0x06
 208:   26 0e          add   r2, r22
 20a:   87 1e          adc   r8, r23
 20c:   98 1e          adc   r9, r24
 20e:   0e 81          ldd   r16, Y+6   ; 0x06
 210:   09 1f          adc   r16, r25
 212:   0e 83          std   Y+6, r16   ; 0x06
 214:   62 2d          mov   r22, r2
 216:   78 2d          mov   r23, r8
 218:   89 2d          mov   r24, r9
 21a:   2e 80          ldd   r2, Y+6   ; 0x06
 21c:   92 2d          mov   r25, r2
 21e:   8f 80          ldd   r8, Y+7   ; 0x07
 220:   28 2c          mov   r2, r8
 222:   81 2e          mov   r8, r17
 224:   9f 2c          mov   r9, r15
 226:   ed 82          std   Y+5, r14   ; 0x05
 228:   22 0c          add   r2, r2
 22a:   88 1c          adc   r8, r8
 22c:   99 1c          adc   r9, r9
 22e:   0d 81          ldd   r16, Y+5   ; 0x05
 230:   00 1f          adc   r16, r16
 232:   0d 83          std   Y+5, r16   ; 0x05
 234:   2f 82          std   Y+7, r2   ; 0x07
 236:   18 2d          mov   r17, r8
 238:   f9 2c          mov   r15, r9
 23a:   2d 80          ldd   r2, Y+5   ; 0x05
 23c:   e2 2c          mov   r14, r2
 23e:   2c 2c          mov   r2, r12
 240:   8b 2c          mov   r8, r11
 242:   9a 2c          mov   r9, r10
 244:   6c 82          std   Y+4, r6   ; 0x04
 246:   22 0c          add   r2, r2
 248:   88 1c          adc   r8, r8
 24a:   99 1c          adc   r9, r9
 24c:   0c 81          ldd   r16, Y+4   ; 0x04
 24e:   00 1f          adc   r16, r16
 250:   0c 83          std   Y+4, r16   ; 0x04
 252:   c2 2c          mov   r12, r2
 254:   b8 2c          mov   r11, r8
 256:   a9 2c          mov   r10, r9
 258:   2c 80          ldd   r2, Y+4   ; 0x04
 25a:   62 2c          mov   r6, r2
 25c:   0a 81          ldd   r16, Y+2   ; 0x02
 25e:   0f 31          cpi   r16, 0x1F   ; 31
 260:   08 f4          brcc   .+2         ;  0x264
 262:   b6 cf          rjmp   .-148       ;  0x1d0
 264:   c8 5f          subi   r28, 0xF8   ; 248
 266:   df 4f          sbci   r29, 0xFF   ; 255
 268:   0f b6          in   r0, 0x3f   ; 63
 26a:   f8 94          cli
 26c:   de bf          out   0x3e, r29   ; 62
 26e:   0f be          out   0x3f, r0   ; 63
 270:   cd bf          out   0x3d, r28   ; 61
 272:   2f 90          pop   r2
 274:   3f 90          pop   r3
 276:   4f 90          pop   r4
 278:   5f 90          pop   r5
 27a:   6f 90          pop   r6
 27c:   7f 90          pop   r7
 27e:   8f 90          pop   r8
 280:   9f 90          pop   r9
 282:   af 90          pop   r10
 284:   bf 90          pop   r11
 286:   cf 90          pop   r12
 288:   ef 90          pop   r14
 28a:   ff 90          pop   r15
 28c:   0f 91          pop   r16
 28e:   1f 91          pop   r17
 290:   cf 91          pop   r28
 292:   df 91          pop   r29
 294:   08 95          ret
 

Mathias
Beiträge: 6165
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: AVR Embedded - wo finde ich die Implementierung?

Beitrag von Mathias »

ATtiny oder ATmega ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: AVR Embedded - wo finde ich die Implementierung?

Beitrag von Timm Thaler »

ATmega natürlich. Dass die Tinys üblicherweise keinen Hardware-mul haben ist mir schon klar.

Ich hab jetzt in einer BME280-Berechnung (Drucksensor von Bosch, liefert ADC-Werte, übelst aufwendig daraus die physikalischen Werte zu berechnen) von FPC-Multiplikation auf eigene ASM-Multiplikation und Division umgebaut - und das Programm braucht 8kB Speicher weniger.

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6199
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: AVR Embedded - wo finde ich die Implementierung?

Beitrag von af0815 »

Kannst du für das immer ein minimal Beispiel in Lazarus/FPC machen. Man kann es damit leichter nachvollziehen und auch das ganze dann in den Bugtracker oder Mailingliste geben. Weil mit dem Projekt ist auch komplett klar was du an Einstellungen verwendet hast. Und bei AVR gehe ich sowieso davon aus, das du den aktuellsten Trunk verwendest.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: AVR Embedded - wo finde ich die Implementierung?

Beitrag von Timm Thaler »

Naja, ich würde gern erstmal geklärt haben, nach welchen Kriterien hier programmiert wurde. Ich persönlich verwende einige Optimierungen:

1. Man kann Multiplikation deutlich verkürzen, wenn man zwei 16bit-Ints zu einem 32bit-Int verrechnet. Und oft ist das ja der Fall: Man hat einen ADC-Wert mit 12bit und einen Umrechnungsfaktor mit 14bit, multipliziert das zu 26bit und teilt durch 1024 (10bit). Da reicht es, wenn 16bit x 16bit zu 32bit werden und nach dem div 1024 wieder 16bit rauskommen.

Der Compiler bläst aber erstmal die 16bit auf 32bit auf und rechnet das komplett in 32bit durch. Ist wahrscheinlich so die "saubere" Vorgehensweise, benötigt aber einen erheblichen Overhead, der auf dem PC nicht stört, auf dem AVR schon.

2. Division auf dem AVR geht ja immer nur in Software. Will ich eine 32bit-Zahl durch 10 teilen, benötige ich dafür eine Schleife mit 8 Durchläufen, da 10 in 8bit passt. (Wenn ich das noch optimiere, benötige ich nur 4 Durchläufe, da 10 auch in 4bit passt. :wink: )

Der Compiler macht aber immer aus der 10 einen 32bit-Wert und ruft eine 32bit-Division auf - und die braucht 32 Durchläufe, und über 4 Byte Dividend, benötigt also deutlich mehr als 4mal so lange.

Derartige Optimierungen wird man aber auf dem PC nicht wollen, und es kann sein, dass man hier versucht die Variablenbehandlung mit dem PC konsistent zu halten: Integer werden immer auf gemeinsame Breite oder die nächstgrößere erweitert. Und deswegen weniger effizienten Code in Kauf nimmt.

Das fällt auch normalerweise nicht auf, aber ich hab jetzt eine kleine Gewächshaussteuerung auf dem ATmega328, da werden eben ein paar Werte verrechnet, einige Sensoren ausgewertet, fürs Display die Werte aufbereitet - und schwupps ist der Speicher alle. Nachdem ich die Muls und Divs durch meine eigenen Routinen ersetzt habe, war 1/3 des Speichers wieder frei. Das haut schon ordentlich rein.

Socke
Lazarusforum e. V.
Beiträge: 3158
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: AVR Embedded - wo finde ich die Implementierung?

Beitrag von Socke »

Timm Thaler hat geschrieben:Wo finde ich denn die Implementierung des Codes für fpc_mul_dword, fpc_mul_longint und so? Ich finde nur die Deklaration in compproc.inc. Ich würde mir gern mal ansehen, ob man da für AVR etwas optimieren kann.

Die Implementierung findest du in <fpcsrc>/rtl/inc/generic.inc. Diese sind plattformunabhänging in Pascal geschrieben und können somit überall verwendet werden. Plattformspezifische Implementierungen gehören wohl in <fpcsrc>/rtl/avr/math.inc.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6199
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: AVR Embedded - wo finde ich die Implementierung?

Beitrag von af0815 »

Mit Beispielen, kann man das sicher in der Mailingliste erfragen bzw. die Hintergründe bekommen, deswegen das mit 'mikro' Beispielen. Es wird beim Compiler sehr wohl zwischen den Plattformen unterschieden, nur ist die AVR Plattform nicht so verbreitet. Deswegen auch nicht wirklich ein starker Fokus darauf.

Der Sensor schaut ja recht interessant aus, allerdings verstehe ich jetzt was der dir für Kopfzerbrechen bei der Berechnung macht :-)
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: AVR Embedded - wo finde ich die Implementierung?

Beitrag von Timm Thaler »

af0815 hat geschrieben:Der Sensor schaut ja recht interessant aus, allerdings verstehe ich jetzt was der dir für Kopfzerbrechen bei der Berechnung macht :-)


Der Sensor ist genial, vor allem weil er Temp, RH und Druck liefert. Die Auflösung, die er liefert brauche ich bei weitem nicht, der kann ja auch Höhenmessung über Druck. Wenn Du die Berechnung siehst, wird da auch viel mit div N gearbeitet, was der Compiler zu einem Shift right macht, oft mit Vorzeichenprüfung. Das geht zwar schnell, frisst in der Summe aber ordentlich Speicher. Also hab ich auch noch eine Funktion div2^n gemacht, die das Geshifte an einer Stelle zusammenfasst. Dauert etwas länger, weil jedesmal Funktionsaufruf, aber spart unheimlich Speicherplatz. Nun müsste ein Compiler entscheiden: Will er lieber Speicher sparen, oder will er schnell sein?

Pascal ist kann bei der Berechnung Überraschungen enthalten, weil es unsigned int und signed int nicht gleich behandelt. Zum Beispiel wird ein uint16 x int16 mit 32 bit gerechnet, ein int16 x int16 aber nur mit 16 Bit. Da kannst Du böse auf die Nase fliegen. Beispiel:

Spannung(int16) := ADC-Wert(uint16) * Konstante(wird als int16 behandelt) div 16384 => funktioniert, weil zwischendurch mit 32 Bit gerechnet wird.

Aber:

Mittelwert(int16) := Mittelwertfunktion(Mittelwert(int16), ADC-Wert(uint16))
Spannung(int16) := Mittelwert(int16) * Konstante(wird als int16 behandelt) div 16384 => funktioniert nicht, weil zwischendurch nur mit 16 Bit Bit gerechnet wird.

Andererseits kann man, wenn man auf uint32 castet für eine Berechnung ganz schnell bei einer 64-Bit Multiplikation landen.

Das ist historisch so gewachsen und entspricht irgendwie auch den Compilerrichtlinien für den Umgang mit Variablen, aber so richtig schön isses nicht.

Antworten