Newsgroup: it.hobby.elettronica.digitale
Titolo: Ancora sull'implementazione di un driver per timer

Riprendo una vecchia discussione da me iniziata su questo ng
riguardante una possibile semplice implementazione di timer in un
microcontrollore.
http://groups.google.com/group/it.hobby.elettronica.digitale/browse_thread/thread/bd9b1a359795d501/93c0074c67840669?q=timer+author:pozz#93c0074c67840669
Ho provato a implementare il driver in quel modo, ma ho avuto una
spiacevole reazione da parte del mio compilatore C,di cui ho parlato
in un thread dell'ng it.comp.lang.c:
http://groups.google.com/group/it.comp.lang.c/browse_thread/thread/402a22adca7368cf/21bfd745ee4e367f?q=author:pozz#21bfd745ee4e367f
Il compilatore non esegue bene il test della differenza tra il valore
attuale di ticks e il valore del timer. Pur essendo entrambi signed,
in caso di overflow (per esempio ticks=32650 e tmr=-32650), il test
ticks-tmr>0 viene superato quando non dovrebbe (32650-(-32650)=-236 in
complemento a 2 su logica a 16 bit).
Ora io non ho capito se il mio compilatore non aderisce allo standard
C oppure no (chissà cosa dice lo standard nel caso di overflow), sta
di fatto che questo metodo non mi va proprio bene (e neanche
l'implementazione dello scheduler di Francesco Sacchi che fa uso dello
stesso test).
Ho trovato una soluzione andando a testare manualmente il bit di segno
del risultato della differenza, ma la cosanon è proprio bella.
Possibile che sia così difficile scrivere un driver per un evento
temporizzato?

 complemento a 2 su logica a 16 bit).
Forse non ho capito bene il problema, ma secondo me
dovresti dichiarare tutti i dati unsigned int.

 temporizzato?

 dovresti dichiarare tutti i dati unsigned int.
Non funziona con unsigned int. L'ho anche spiegato nel thread di
it.comp.lang.c.
Infatti quando ticks fa il wrap-around la differenza tra ticks e tmr
non diminuisce
di una unità. Provare per credere.

 evitare troppe ottimizzazioni da parte del compilatore
Non c'entrano le ottimizzazioni. Il compilatore parte dal presupposto
che
un numero negativo meno un numero positivo non può essere maggiore di
zero. Questo è ovviamente vero, ma in complemento a 2 non è vero
perchè
c'è overflow.
Quindi il compilatore esegue la differenza ma non va a guardare il
segno del
risultato, ma controlla anche se non c'è stato overflow. E' tutto
spiegato in
un mio post nell'ng it.comp.lang.c

 un mio post nell'ng it.comp.lang.c
si lo stavo gia' seguendo
non mi hai risposto sul passaggio tramite una variabile temporanea, una 
cosa del tipo (magari dichiarando xx volatile):
int xx;   // oppure volatile int xx;
xx = ticks-tmr;
if (xx>0) ...
se mi dici che le sottrazioni vengono fatte in modulo ( 2^16 ) dovrebbe 
funzionare

 funzionare
Sì, quella prova l'avevo già fatta e funziona. Però così facendo non
posso
usare una semplice macro per testare l'esaurimento del timer (almeno
non
saprei come fare per dichiarare una variabile temporanea all'interno
di una
espressione).
Così funziona perchè il compilatore non va a testare il risultato
dell'operazione
(che ha avuto overflow) ma la variabile temporanea che ormai è un
numero a
16 bit a tutti gli effetti.
Comunque alla fine ho risolto andando a testare manualmente il bit del
segno,
facendo un AND con 0x8000. Non è bello perchè il codice è
probabilmente
troppo legato all'hw, ma penso che funzioni sulla stragrande
maggioranza
dei processori a 16 bit.
L'ideale è probabilmente testare direttamente nell'interrupt del timer
l'uguaglianza
tra tutti i timer attivi e il valore di ticks, ad ogni colpo di clock.
Però dovrei creare
una lista con i timer attivi da scorrere nell'interrupt. Se mantengo
la lista ordinata
(il primo elemento è il primo timer che scatta), posso evitare di
scorrere tutta la
lista, ma mi fermo subito se non c'è uguaglianza con ticks.
Anche se non sarà facile mantenere la lista ordinata (devo riguardare
il codice che
mi ha postato Francesco Sacchi).
Purtroppo però mi scoccia creare la gestione delle liste, quindi mi va
bane la
soluzione che ho trovato.

 soluzione che ho trovato.
L� per l� sembra complicato ma ti assicuro che non lo �, le liste che 
abbiamo implementato noi sono davvero semplici e non sprecano spazio 
(solo 2 puntatori per elemento), dacci un'occhiata se vuoi prendere 
qualche spunto:
http://dev.bertos.org/doxygen/struct__List.html
-- 
  _|/ Francesco Sacchi - Develer S.r.l., R&D dept.
   |\ 
http://www.develer.com/
 - 
http://www.bertos.org

 qualche spunto:
http://dev.bertos.org/doxygen/struct__List.html
Riprendo un attimo il codice che hai postato tempo fa, in particolare
la
routine timer_add() che si occupa di aggiungere un nuovo nodo alla
lista dei timer in modo che rimanga sempre ordinata (la testa della
lista
è il successivo timer che scade):
void timer_add(Timer *timer)
{
         Timer *node;
         /* Calculate expiration time for this timer */
         timer->tick = _clock + timer->_delay;
         /*
          * Search for the first node whose expiration time is
          * greater than the timer we want to add.
          */
         node = (Timer *)LIST_HEAD(timer_list);
         while (node->link.succ)
         {
                 /*
                  * Stop just after the insertion point.
                  * (this fancy compare takes care of wrap-arounds).
                  */
                 if (node->tick - timer->tick > 0)
                         break;
                 /* Go to next node */
                 node = (Timer *)node->link.succ;
         }
         /* Enqueue timer request into the list */
         INSERT_BEFORE(&timer->link, &node->link);
}
Immagino che l'espressione nell'if del ciclo while debba essere
modificata
proprio alla luce di quanto ci siamo detti.

 proprio alla luce di quanto ci siamo detti.
Esattamente, infatti stiamo gi� discutendo su come fare un fix.


 Esattamente, infatti stiamo già discutendo su come fare un fix.
Se ti va, posta cosa avete deciso di fare.
Dopo tutto questo ragionamento sui timer e sulle operazioni critiche
in C,
mi sembra che il "vecchio" metodo del contatore a decremento sia un
metodo
più affidabile.
Utilizzo lo stesso quantitativo di memoria (un int) e il test di fine
timer è molto
semplice (tmr == 0). L'operazione di decremento nell'ISR è banale e
non
va incontro a problemi di portabilità o implementazione del
compilatore:
  if (tmr) tmr--;
Inoltre si può passare a tipi unsigned avendo così a disposizione il
doppio del
tempo.
Si avrebbe pure un risparmio di una variabile int, ticks, che non
serve più.
L'unico svantaggio è che l'ISR diventa più lenta perchè deve
decrementare
ogni timer. Se non si ha un meccanismo di liste (come nel mio caso),
sono
costretto a modificare l'ISR ogni volta che aggiungo un timer,
altrimenti
basta scorrere la lista e decrementare ogni timer. Non è neanche
necessario
mantenere la lista ordinata, tanto bisogna scorrerla tutta.

 mantenere la lista ordinata, tanto bisogna scorrerla tutta.
Mi sento di sconsigliarlo, c'� un calo di prestazioni notevole, e 
proprio in un punto critico come una ISR. Prova le due soluzioni sopra 
che sono corrette anche da un punto di vista dello standard.


 che sono corrette anche da un punto di vista dello standard.
Lo farò.

 falsa).
Ma nel resto del codice se fai -32500 - 32500 ottieni un overflow. Su 
(quasi) tutte le architetture del mondo l'aritmetica � in complemento 
a 2. L'aritmetica degli unsigned � anch'essa in complemento a 2.
La regola dello standard � stata fatta per consentire ottimizzazioni 
del tipo:
(X - C > D) --> (X > C + D)
Dove X � una variabile e C e D sono costanti.
In questo modo C+D viene risolto a compile time e si risparmiano cicli 
macchina. Il codice sopra per� assume che X - C non vada in overflow, 
altrimenti le due espressioni non sono uguali.


 altrimenti le due espressioni non sono uguali.
Penso che il mio compilatore voglia azzeccare anche il caso di
overflow.
Se fai (-32500 - 2500 > 30000) è in matematica ovviamente falsa (un
numero
negativo non può essere maggiore di un numero positivo), ma in
complemento
a 2 è vera (perchè c'è overflow). Forse il compilatore pensa di essere
più furbo a voler fare i calcoli secondo logica matematica. E
l'istruzione BLE
glielo permette.

 unsigned (che hanno l'overflow definito).
Attenzione qui, sul mio compilatore funziona soltanto se faccio:
  ((signed)((unsigned)ticks - (unsigned)tmr) > 0)

   ((signed)((unsigned)ticks - (unsigned)tmr) > 0)
e solo se la differenza e' limitata al 50% del ticks massimo (32k in 
caso 16bit); per andare oltre ti serve almeno un flag.
saluti
-- 
  lowcost

 per ovvi motivi.
Beh, ma questo codice � corretto, � la trasposizione esatta del check 
(tmr > ticks), no?
Invece tu avevi detto che il controllo che facevi era (tmr - ticks > 
0) che con gli overflow *non* � uguale a fare (tmr > ticks).
