Effektivisera C kod

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 47013
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Inlägg av TomasL »

Du måste alltid använd nån form av "mjukvaru-stack" när du skall göra funktions-anrop, vad man sedan kallar det för är av akademiskt intresse.

Oavsett vilket språk du anväönder, ASM, C, BASIC osv, så måste du på nått sätt spara undan vissa register när du gör ett funktionsanrop.

Pic 16 har vill jag minnas bara en stack med ett djup av 1 (rätta mig om jag har fel, har aldrig jobbat med PIC16), de pic18 som jag jobbar med har ett djup om 31 tror jag, det innebär att nästade funktionsanrop med en PIC16 är omöjliga utan mjukvarustack för PC, och för PIC18 går naturligtvis gränsen vid 31.

För att kunna hanter "n" antal funktionsanrop måste man allstså använda nån form av logisk mekanism för att snabbt kunna återställa diverse register och PC, och den enklaste (samt snabbaste) metoden är att använda en mjukvarustack, om du tittar på en 18F8x22 och liknande ser du att det finns tre hårdvaru-register för just stackhantering, orsaken är att PIC18 är konstruerad för just detta med högnivåspråk och stackar.
En pic16 har max ett sådant register vilket krånglar till saker i onödan, och PIC16 är inte anpassad för högnivåspråk såsom C mfl.

Mitt råd är, byt ut din PIC16 mot en 18F452a eller nått liknande så får du en betydligt effektivare processor (mha stackhantering), och som dessutom kan snurra lite fortare.
v-g
EF Sponsor
Inlägg: 7875
Blev medlem: 25 november 2005, 23:47:53
Ort: Kramforce

Inlägg av v-g »

Angående PIC16

Saxxat ur manualen:
4.3.2 STACK
The PIC16F627A/628A/648A family has an 8-level
deep x 13-bit wide hardware stack (Figure 4-1). The
stack space is not part of either program or data space
and the Stack Pointer is not readable or writable. The
PC is PUSHed onto the stack when a CALL instruction
is executed or an interrupt causes a branch. The stack
is POPed in the event of a RETURN, RETLW or a
RETFIE instruction execution. PCLATH is not affected
by a PUSH or POP operation.
Användarvisningsbild
Icecap
Inlägg: 26659
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

För att fixa ett avbrott i PIC16-serien utan att fucka up STATUS, register osv (alltså helt transparent) gör jag följande:

Kod: Markera allt

	org	0x004		; interrupt vector location
	bcf	PIR1,TMR1IF	; Clear interrupt flag first of all
	movwf	w_temp		; save off current W register contents
	movf	STATUS,W	; move status register into W register
	movwf	status_temp	; save off contents of STATUS register
	movf	PCLATH,W	; Read it
	movwf	pclath_temp	; Save it for later
	clrf	PCLATH		; Zerorize it

        ......; Do the woodoo that i do so well

ExitInt
	movf	status_temp,W	; retrieve copy of STATUS register
	movwf	STATUS		; restore pre-isr STATUS register contents
	movf	pclath_temp,W
	movwf	PCLATH
	swapf	w_temp,F
	swapf	w_temp,W	; restore pre-isr W register contents
	retfie			; return from interrupt
Ja, det är "gammal" absolut kod men det duger åt mig. Det är ung. det samma som kompilern gör så jag ser inget problem i detta, vill du ha ett snabbare sätt är det dags att byta processor.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 47013
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Inlägg av TomasL »

v-g skrev:Man kommer normalt inte åt stacken på en PIC16 tex finns inga PUSH eller POP-kommandon ens i ASM utan detta sköts av processorn.

Varför spara undan temporära variabler? Antigen skriver man en ny variabel eller återanvänder en gammal. Jämför med i i c (for i = 1 to 10 osv) som man kan ha i många funktioner där den används bara i just den funktionen så gör man INTE i ASM. Har man variabler som man förändrar data i så ja då förändrar man det i den variabeln. Men i i-exemplet ovan kan du ju återanvända i flera gången om vi säger att du istället för funktioner kodar allt i main, på samma sätt gör jag iaf i ASM.

Lokala och globala variabler finns inte i asm på samma sätt som i c. Är dock inte 100% på detta men så har jag förstått det iaf.

Finns optimeringsprogram iagf för PC vet jag sen om de fungerar är en annan femma. Frågan är verkligen om det är rätt väg att gå :roll: En usel lösning från början kan aldrig bli bra. (därmed inte sagt att C är en usel lösning) C kanske inte är rätt sak för tidskritiska applikationer på PIC. Tror att AVR är bättre när det gäller detta men det är så gott som en ren gissning.
Jo man kan med hjälp av sinrik kodning komma åt HW-stacken, men det ligger utanför denna diskussionen.

Det är inte temporära variabler man vill spara undan utan snarare de lokala varibler man har i en funktion, eftersom de flesta kompilatorer använder ett gemensamt utrymme för just dessa lokala variabler, så måste dessa sparas undan så de inte blir överskrivna.
v-g
EF Sponsor
Inlägg: 7875
Blev medlem: 25 november 2005, 23:47:53
Ort: Kramforce

Inlägg av v-g »

Icecap:Särskilt avancerat är det egentligen inte heller när man ser koden! :)

TomasL:Jag fattade lite fel där,sorry.
ankan
Inlägg: 1091
Blev medlem: 12 november 2004, 01:50:35

Inlägg av ankan »

Låter som att PIC18 är rätt väg att gå om jag vill hålla mig till C-programmering och vill ha effektiv kod.

Ang stackdjupet hade jag för mig att det var 8 på PIC16.

Ska nog ta och kolla på PIC18 familjen. Vilket typ av minne är det som är snabbast på PICen som man kan mellanlagra data som man får in från UART?
EEPROMen är väll bara att glömma. Men är det RAM-minnet man ska kolla på när man väljer PIC?

Frågar min fråga om ni missade den. Finns det inget 1-wire minne som man skulle kunna lagra data i på annat sätt än via 1-wire och sedan låta mastern läsa av 1-wire minnet. Att använda samma ingång och utgång för att switcha mellan 1-wire mastrarna är väll bara att glömma.
Användarvisningsbild
Icecap
Inlägg: 26659
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Nej, det är inte så mycket egentligen men då kompilern ALLTID är satt till att ha en totalt transparent ISR blir det lite overhead, jag vet inte hur det är med AVR men jag vet att processorer som är "lite större" har ofta möjlighet att byta registerbank, antingen automatisk vid interrupt eller med en enkelt instruktion ("välj register bank x") just för att få kort latency.
Användarvisningsbild
GrodanB
Inlägg: 245
Blev medlem: 11 februari 2006, 16:46:25
Ort: Göteborg
Kontakt:

Inlägg av GrodanB »

En sak jag har märkt när jag använde C på "enklare" processorer är att switch/case brukar bara en dyr konstruktion och if/else billigare...

Så kolla upp asmeblerkoden runt switch/case kontra if/else...

Använde IAR's kompilator till en processor och det var många rader asmebler extra i switch/case statsen som jag "optimerade" bort genom att byta ut den till en enkel if/else kedja.

När man ökade optimeringarna lite försvann en del av "slentrian" koden i kompilatorn... Dvs. den slutade att alltid allockera stack för funktionen trots att den inte hade anropsparametrar och inte använde lokala variabler...
ankan
Inlägg: 1091
Blev medlem: 12 november 2004, 01:50:35

Inlägg av ankan »

Var rädd för att switch-case skulle vara en dyr grej. Ska prova att skriva om till if-else satser i stället. Några andra tips?

Här får ni ett exempel hur den håller på och hoppar omkring. Koden som är skriven i C är följande:

Kod: Markera allt

void main() {
  Usart_Init(9600);
  Usart_Write('X');
}
Den sätter uarten till 9600 baud och skickar tecknet X. Den kompilerade C-koden blir följande:

Kod: Markera allt

; ADDRESS	OPCODE	ASM
; ----------------------------------------------
$0000	$2824			GOTO	_main
$0004	$	_Usart_Write:
$0004	$	L_Usart_Write_3:
$0004	$3000			MOVLW	0
$0005	$1303			BCF	STATUS, RP1
$0006	$1683			BSF	STATUS, RP0
$0007	$1898			BTFSC	TXSTA, 1
$0008	$3001			MOVLW	1
$0009	$00F1			MOVWF	STACK_1
$000A	$0871			MOVF	STACK_1, 0
$000B	$3A00			XORLW	0
$000C	$1D03			BTFSS	STATUS, Z
$000D	$2810			GOTO	L_Usart_Write_4
$000E	$0000			NOP
$000F	$2804			GOTO	L_Usart_Write_3
$0010	$	L_Usart_Write_4:
$0010	$1283			BCF	STATUS, RP0
$0011	$0820			MOVF	FARG_Usart_Write+0, 0
$0012	$0099			MOVWF	TXREG
$0013	$0008			RETURN
$0014	$	_Usart_Init:
$0014	$1303			BCF	STATUS, RP1
$0015	$1683			BSF	STATUS, RP0
$0016	$1698			BSF	TXSTA, 5
$0017	$3090			MOVLW	144
$0018	$1283			BCF	STATUS, RP0
$0019	$0098			MOVWF	RCSTA
$001A	$1683			BSF	STATUS, RP0
$001B	$1787			BSF	TRISC, 7
$001C	$1307			BCF	TRISC, 6
$001D	$	L_Usart_Init_0:
$001D	$1283			BCF	STATUS, RP0
$001E	$1E8C			BTFSS	PIR1, 5
$001F	$2823			GOTO	L_Usart_Init_1
$0020	$081A			MOVF	RCREG, 0
$0021	$00A4			MOVWF	Usart_Init_tmp_L0
$0022	$281D			GOTO	L_Usart_Init_0
$0023	$	L_Usart_Init_1:
$0023	$0008			RETURN
$0024	$	_main:
;Led_Blinking.c,19 :: 		void main() {
;Led_Blinking.c,20 :: 		Usart_Init(9600);
$0024	$3033			MOVLW	51
$0025	$1303			BCF	STATUS, RP1
$0026	$1683			BSF	STATUS, RP0
$0027	$0099			MOVWF	SPBRG
$0028	$1518			BSF	TXSTA, BRGH
$0029	$2014			CALL	_Usart_Init
;Led_Blinking.c,21 :: 		Usart_Write('X');
$002A	$3058			MOVLW	88
$002B	$00A0			MOVWF	FARG_Usart_Write+0
$002C	$2004			CALL	_Usart_Write
;Led_Blinking.c,22 :: 		} //~!
$002D	$282D			GOTO	$
Inte särskillt effektiv alltså. Tycker den hoppar för mycket. Varför inte stoppa in all kod i rad utan massa hopp. Skulle gärna vilja att den duplicerade koden på en del ställen för att öka hastigheten. Har plats så det räcker i alla fall.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> Skulle gärna vilja att den duplicerade koden på en del ställen för att öka hastigheten...

Hm, det är sådant som avancerande kompilatorer (eller assembler programmerare :-) ) kan ägna sig åt.

Jag tror inte att MikroC kan räknas till de avancerade kompilatorerna, det
är lite leksak över det (i alla fall gratis versionerna) jämfört med
de stora verktygen. Deras katastrofer till manualer säger väl allt...

Sen måste jag kommentera den genererade koden som du visade...
Notera dessa rader :

BTFSC TXSTA, 1
BTFSS PIR1, 5

och :

BTFSS STATUS, Z
BSF TXSTA, BRGH

Varför i jösse namn blandar de *numeriska* bitvärden med *symboliska* ?
Det spelar ju ingen roll för resultatet, men för den som ska läsa
koden är det oerhört störande. Oproffsigt...

> Tycker den hoppar för mycket.

Var då ? Jag ser inget så där väldigt "hoppigt"...
ankan
Inlägg: 1091
Blev medlem: 12 november 2004, 01:50:35

Inlägg av ankan »

Möjligt att MikroC är något proffsverktyg precis. Den kostar ju bara en bråkdel av vad de andra kompilatorerna kostar. Vilken C-kompilator rekommenderas annars då? Tänker mer på framtida projekt.

Kört med Hitech C tidigare och den var rätt trevlig. Rätt dyr dock.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Jag kan inte påstå att jag har testat en massa C verktyg.
Men å andra sidan ser jag inte varför inte den generella
regeln "dyrare=bättre" inte skulle gälla här också...
Men som sagt, det är bara en gissning... :-)
Användarvisningsbild
vfr
EF Sponsor
Inlägg: 3515
Blev medlem: 31 mars 2005, 17:55:45
Ort: Kungsbacka

Inlägg av vfr »

Är det inte möjligt att skriva hela avbrottsrutinen i asm (alltså inte inline) och resten i C?

Då kan man använda assemblerns fördelar med små effektiva avbrottsrutiner utan att behöva skriva hela applikationen i asm. Eftersom du inte behöver mjukvarustack och andra "C-features" i en asm-ISR så skulle man isåfall kunna byta ut hela avbrottsinitieringen och återställningen mot Icecaps asmversion.

Det är ju naturligtvis beroende på vad MikroC stödjer i fråga att länka ihop kod av olika typ i resp. delar av applikationen.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Någon visade (i en annan tråd) hur man definierar en subrutin
och lägger den "hårt" på h'0004' (d.v.s interrupt vektorn).
Sen om det var separat-kompilering/assemblering eller
inline-asm minns jag inte...
ankan
Inlägg: 1091
Blev medlem: 12 november 2004, 01:50:35

Inlägg av ankan »

Hittade nu att man kan göra följande för att få en viss funktion att hamna på en viss adress:

Kod: Markera allt

void avbrott() org 0x04 {

// Nu hamnar jag här vid avbrott utan någon mjukvarustack eller spar av workregister mm.

}
Tror ni det kommer lira utan att spara undan några mjukvarustackar? Tänker mest på att jag kommer ju ha en kod som ligger och snurrar i main som blir avbruten då och då när det blir avbrott för 1-wire kommunikation.

För övrigt skulle jag kunna göra om switch-case satserna till if-else satser och köra ASM-inline där det behövs. Detta borde väll få koden att bli rätt effektiv i alla fall.
Skriv svar