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: 6976
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: 6873
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: 3178
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: 6873
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