C-Optimieren für weniger Assambler Zyklen..
-
Hallo FReaks,
ich bin an Microcontroller Programmierung mit C dran.. und will folgenden Code optimeren:
67: lOld=lNew; 04BE 804170 mov.w 0x082e,0x0000 04C0 804181 mov.w 0x0830,0x0002 04C2 8841E0 mov.w 0x0000,0x083c 04C4 8841F1 mov.w 0x0002,0x083e 68: lNew= ((((unsigned long)TMR3<<16 ) | TMR2)); 04C6 800850 mov.w 0x010a,0x0000 04C8 200001 mov.w #0x0,0x0002 04CA DD01C0 sl 0x0000,#0,0x0006 04CC 200002 mov.w #0x0,0x0004 04CE 800830 mov.w 0x0106,0x0000 04D0 200001 mov.w #0x0,0x0002 04D2 710000 ior.w 0x0004,0x0000,0x0000 04D4 718081 ior.w 0x0006,0x0002,0x0002 04D6 884170 mov.w 0x0000,0x082e 04D8 884181 mov.w 0x0002,0x0830 69: if(lNew > lOld){ 04DA 804172 mov.w 0x082e,0x0004 04DC 804183 mov.w 0x0830,0x0006 04DE 8041E0 mov.w 0x083c,0x0000 04E0 8041F1 mov.w 0x083e,0x0002 04E2 510F80 sub.w 0x0004,0x0000,[0x001e] 04E4 598F81 subb.w 0x0006,0x0002,[0x001e] 04E6 340011 bra les, 0x00050a 70: lDiff= 1000000000/((lNew-lOld)*33.9); 04E8 804172 mov.w 0x082e,0x0004 04EA 804183 mov.w 0x0830,0x0006 04EC 8041E0 mov.w 0x083c,0x0000 04EE 8041F1 mov.w 0x083e,0x0002 04F0 510000 sub.w 0x0004,0x0000,0x0000 04F2 598081 subb.w 0x0006,0x0002,0x0002 04F4 07FE45 rcall 0x000180 04F6 2999A2 mov.w #0x999a,0x0004 04F8 242073 mov.w #0x4207,0x0006 04FA 07FE6D rcall 0x0001d6 04FC BE0100 mov.d 0x0000,0x0004 04FE 26B280 mov.w #0x6b28,0x0000 0500 24E6E1 mov.w #0x4e6e,0x0002 0502 07FE9D rcall 0x00023e 0504 07FEDE rcall 0x0002c2 0506 884200 mov.w 0x0000,0x0840 0508 884211 mov.w 0x0002,0x0842
ihr seht immer die C-Anweidungen und der dafür generierte Assambler code eines PIC 30F4011 Mircocipts... ich weis leider nich in wie weit der Compiler den coder schon optimiert hat, und von assambler hab ich kaum ahnung.. könnt ihr mir da was optimieren???
EDIT: das ganze sind ca 50 Zyklen.. wäre cool wenn man da 10-20 zyklen rausholen könnte;) Das problem ist, das der code für die impulsmessung bei input capture Timer sind. Und um so länger der code um wenger hoch kan die frequenz sein die man messen kann..
-
1. benutz keine 'longs' sondern mach alles mit 'int'
2. bei *33.9 ist der eine operand ein 'double'. eventuell besser: *339/10
3. versuch die multiplikationen und divisionen durch shifts und additionen zu ersetzen.
-
hey du, ich muss leider long nehmen, weil dieser 32bit groß ist , und ints nur 16bit
ich habe nun
das
lNew= ((((unsigned long)TMR3<<16 ) | TMR2));
mit
((unsigned int*)&lNew)[0]= TMR2; ((unsigned int*)&lNew)[1]= TMR3;
erstetzt 3 zyklen weniger;)
-
BorisDieKlinge schrieb:
hey du, ich muss leider long nehmen, weil dieser 32bit groß ist , und ints nur 16bit
long ist nur bequemlichkeit. wenn du den code umbaust, kommst du bestimmt ohne longs aus. und guck dir mal an wohin diese ganzen 'rcalls' gehen. das sind bestimmt alles in allem viel mehr taktzyklen, als die von dir geschätzen "50".
-
achso, ging davon aus das eine zeile ein zyklus ist;) sorry ich kenn mich da nich aus... hmm... könnt ihr mir mal ein bsp. machen wie ich das nur mit ints lösehn könnte??
ein kleiner ansatz wäre nic hschlecht
-
du kriegst ja diese grossen werte, weil du TMR3 und TMR2 zusammenschiebst, ne? kannste die nicht getrennt behandeln? also z.b. wenn TMR3 grösser ist als vorher, dann ist der gesamtwert grösser. ist er gleich, dann noch TMR2 überprüfen. auch dieses: *lDiff= 1000000000/((lNew-lOld)33.9) kann man doch bestimmt als lDiff=lNew-lOld abkürzen (um rcalls an der stelle zu vermeiden) und man könnte auch die multiplikation und die division später, in einem zeitunkritischen pfad machen.
aber guck auch mal da: http://www.piclist.com/techref/microchip/math/index.htm
-
1. Aber echt hey...
lDiff= 1000000000/((lNew-lOld)*33.9)
.. die berechnung könnte ich echt später machen, is mir zuerst gar nich in den sinn gekommen:)
2. Hmm.. wäre denn
unsigned int iOld2,iOld3,iNew2,iNew3,iDiff2,iDiff3; iOld2= iNew2; iOld3= iNew3; iNew2= TMR2; iNew3= TMR3; iDiff2= iOld2-iNew2; iDiff3= iOld3-iNew3; //Zeit(un)kritischer abschnitt if(iNew2 > iOld2 && iNew3 > iOld3){ unsigned long lDiff= 1000000000/((((unsigned long)iDiff<<3) | iDiff2)*33.9); }
aquvalent zu:
unsigned long iOld,iNew,iDiff; lOld=lNew; lNew= ((((unsigned long)TMR3<<16 ) | TMR2)); if(lNew > lOld){ //Zeit(un)kritischer abschnitt lDiff= 1000000000/((lNew-lOld)*33.9);
}
????
-
^^ du musst natürlich überträge beachten. aber warum probierst du's nicht einfach aus? fängst ja schon an wie 'hasso' aus dem c-forum.
-
*Diff= 1000000000/((lNew-lOld)33.9);
und da mach mal die 'double'-konstante weg. nimm denn zähler mal 10 und mach aus der 33.9(double) eine 339 (integer) und - schwupps - brauch der compiler keine fliesskommaroutinen mehr aufzurufen.
-
@16bit freak.. ja ich werds versuchen;)
@erweiterungsfreak: Das kann ich nich tun! Würd ich den wert "1000000000" mit 10 multiplizieren, hätte ich 10 Milliareden.. kann aber nur 32Bit variablen anlegen als max 4,3 milliarden;)
-
das läst sich sogar wunderbar optimieren.
1.000.000.000 / ((lNew-lOld)*33.9)
((1.000.000.000 / 339) * 10 ) / (lNew-lOld)
wobei der erste teil sogar konstant ist und extern berechnet werden könnte.
29.498.525 / (lNew-lOld)
ggf ist noch eine Fehlerbetrachtung zu machen. Meine optimierung würde dir bei der konstante 5 klauen. 29.498.525 zu 29.498.520 wobei da der Fehler zum orginalergebnis bei 1.7 * 10-7 liegt.
gruss
-
Respekt.. hätte ich auch selber drauf kommen können;) aber vieelen dank
YEAHH... jetzt kann ich schon 90Khz statt 30khz messen;)