T&t - PIC 

 

PIC Baseline


Le Subroutines nei Baseline

In base al codice a 12 bit utilizzato per le istruzioni e alle limitazioni della famiglia, le subroutine nei Baseline risentono di un paio di limitazione:

  • come per i Midrange, la memoria programma può essere divisa in pagine nel momento in cui supera 512 words (una pagina = 512)
  • l' istruzione call agisce solamente nelle prime 256 words di ogni pagina.

Ci si potrebbe chiedere, allora, che strategia utilizzare per decidere dove va scritta la subroutine nel sorgente. 
Però per i Baseline, oltre a quelle indicate c'è ancora una difficoltà supplementare:

  • lo stack dei Baseline è minimo, solo due livelli, ovvero si può chiamate una subroutine (la cui call copia l' indirizzo di ritorno nello stack), la quale piò a sua volta chiamare un' altra subruotine. Però a questo punto lo stack è pieno e non è possibile una ulteriore call a meno di essere rientrati da una precedente. 

Risulta quindi evidente che la possibilità di chiamare subroutines nei Baseline è fortemente limitata. Certamente occorre cura particolare nell' evitare l' annidamento delle subroutine in modo da non  superare mai i due livelli: va tenuto presente che i Baseline non hanno sistemi di protezione per overflow o underflow dello stack e un errore nella sua gestione provoca il crash del programma.
Così, un ampio uso di subroutines è possibile anche in questi piccoli PIC.

Per quello che riguarda il problema delle pagine, ne sono esclusi solamente i 18F e i PIC superiori, dove la memoria programma non è paginata. Per Baseline e Midrange occorre forzatamente gestire le pagine ogni volta che il programma supera l' ampiezza di 512 words.
Quindi, si ricorrerà al solito pagesel in abbondanza.

La limitazione della prima metà della pagina si genera dal fatto che il bit del PC è, nella call, sempre a 0. Quindi è obbligatorio che l' indirizzo di destinazione dell' istruzione sia entro i primi 256 indirizzi, in qualunque pagina ci si trovi.  Questa è una delle limitazioni sensibili per questi piccoli PIC, che, d'altro canto, sono volutamente molto semplificati e quindi privi dei meccanismi e delle prestazioni delle famiglie superiori.

Se il programma non supera in alcun modo il limite di 256 word, il problema non si pone, ma questo, però, è un caso limitato ad un programma di poche righe. E' ben possibile che si abbia a che fare con programmi più corposi, che sforano il limite.
In tal caso sono implementabili due strategie:

  • posizionare sempre le subroutines entro i primi 256 indirizzi della pagina
  • utilizzare una salto per raggiungere la subroutine ovunque si trovi

Facciamo un esempio pratico: poniamo la subroutine nei primi elementi del programma:

Vettore_Reset

    
goto    mainloop

   
;==================================================================
;=                      SUBROUTINES                               =
===================================================================
subroutine:
    ......
     retlw   0

===================================================================

mainloop:            
    ......
    
call    subroutine 

Osserviamo la necessità di inserire un salto (goto mainloop) che permetta di oltrepassare le istruzioni della subroutine. Se questa linea mancasse, il program counter, dopo il salto al vettore del reset, proseguirebbe eseguendo le istruzioni della subroutine, fino al retlw dove sarebbe sostituito da un valore casuale preso dallo stack, che non è stato caricato da alcuna call, mandando in crash il programma.

Questa soluzione è semplice, ma lo spazio di 256 word non è poi tanto grande e se utilizziamo varie subroutines si può riempire facilmente, anche perchè è l' area richiesta per piazzarci altre cose, come ad esempio tabelle (lookup tables). Possiamo comunque piazzare altre subroutines nei primi 256 indirizzi delle pagine seguenti, avendo cura di utilizzare il comando per il cambio pagina:

  pagesel subroutine 
 
call    subroutine 

Non va dimenticato che gli switches di pagina restano inalterati fini alla prossima ri scrittura e quindi, se occorre agire in una altra pagina, si richiede ancora un pagesel.

La seconda opzione è quella di indirizzare alla subroutines con un sistema indiretto.

Vettore_Reset

    
goto    mainloop

   
;==================================================================
;=                      SUBROUTINES                               =
===================================================================
sub1 goto    rsub1
sub2 goto    rsub2
    ......

===================================================================

mainloop:            
    ......
    
call    sub
1
    ......
===================================================================
; qualsiasi posizione

rsub1:            
    ......
    
retlw   0 
           

rsub2:            
    ......
    
retlw   0

Il meccanismo è semplice:

  •  call sub1 invia non alla subroutine, che è stata rinominata  rsub1, ma alla label sub1 che corrisponde ad un goto rsub1
  • la call  ha salvato l' indirizzo di ritorno nello stack; questo indirizzo corrisponde all' istruzione successiva alla call stessa
  • il goto non comporta alcuna modifica del contenuto dello stack, ma cambia solo il pc indirizzandolo alla label rsub1
  • una volta esaurite le istruzioni a rsub1, il retlw finale recupera dallo stack l' indirizzo di ritorno successivo alla call

Siccome il goto non ha la limitazione dei 256 indirizzi, la subroutine si può trovare anche oltre questo limite.
Questa soluzione consente di non affollare i primi 256 indirizzi. Il goto mainloop è sempre necessario per passare oltre le chiamate indirette alle subroutines.
Si può obiettare che il rimando indiretto con un salto richiede l'esecuzione di una istruzione in più, ma, a meno di situazioni del tutto eccezionali, questo non comporta che 1us in più nell' esecuzione a 4MHz di clock: del tutto trascurabile.

Se le subroutines finiscono in pagine diverse, di nuovo, con pagesel  effettueremo il cambio pagina necessario.

Vettore_Reset

    
goto    mainloop

   
;==================================================================
;=                      SUBROUTINES                               =
===================================================================
sub1 pagesel rsub1
     goto
    rsub1
sub2 pagesel rsub2
    
goto    rsub2
    ......

===================================================================

mainloop:            
    ......
     
call    sub
1
local
pagesel local
    ......
===================================================================
; qualsiasi posizione

rsub1:            
    ......
  
   retlw   0 
           

rsub2:            
    ......
  
   retlw   0

Da notare che:

  • non serve uno switch di pagina nel rientro dalla subroutine, in quanto lo stack contiene tutti i bit dell' indirizzo di ritorno, mentre
  • serve riportare la pagina a quella dove risiedono le istruzioni in corso di esecuzione.

Sfortunatamente la paginazione della memoria programma costituisce il "peccato originale" dei PIC e, a meno di utilizzare 18F o superiori, occorre conviverci.
L' alternativa è l' uso di linguaggi superiori all' Assembly dove il problema viene gestito dal compilatore. 


 

 

Copyright © afg. Tutti i diritti riservati.
Aggiornato il 27/05/13.