Newsgroup: it.hobby.elettronica.digitale
Titolo: MCU a 32 bit e tipi di dati

Ciao,
sappiamo che usare tipi di dati gestibili direttamente dal datapath 
micro portano a codice più breve e quindi più veloce. In architetture a 
8 bit questo è vero per i char, mentre usare int, long o addirittura 
float comporta più codice.
Se prendiamo un ARM a 32 bit invece abbiamo che i tipi ideali sono int e 
long (16 e 32 bit). Usare tipi di dati più lunghi (long long) o più 
corti (char) porta agli stessi problemi di prima.
Domande:
- quando non ha senso usare un int al posto di un char? Una prima 
risposta la do io: se ad esempio devo scrivere su un bus I2C è più 
corretto che il buffer di trasmissione sia di tipo char.
- quando ha senso usare un int al posto di un char? Qui io direi: 
praticamente in tutti gli altri casi, fermo restando che i grandi array 
dovrebbero tenere conto anche dell'occupazione di memoria.
Ha senso quello che ho scritto?
Marco / iw2nzm

 Ha senso quello che ho scritto?
Beh, in genere si usa il tipo più piccolo possibile tenendo conto del
massimo valore che dovrà contenere. Se dovrò contenre per esempio 300
non posso più usare un char ma dovrò usare un int...
-- 
This time it will surely run.
News 2002 [v 2.08] / StopDialer / PopDuster - 
http://www.socket2000.com
Akapulce portal: 
http://www.akapulce.net

 non posso più usare un char ma dovrò usare un int...
Non è detto che usare il tipo più piccolo possibile sia sempre la scelta 
ideale. O meglio, il mio intervento era proprio a questo proposito.
Anche se un char potrebbe essere adeguato per un contatore io userei un 
int su un ARM. Chiedevo conferma proprio di questo...
Ciao!
Marco / iw2nzm

 
dipende se vuoi ottimizzare per dimensione a runtime (in RAM) o velocità. 
sulla dimensione in ROM del programma non si può dire a priori senza sapere 
l'architettura e come tratta i diversi tipi di dato.
-- 
Nuno on zx-6r '04 & CRe 250 '99 working in progress...
La riforma della scuola è pronta
dieci casse Marshall dentro ogni aula
*** 
www.gladio.org
 ***

 corti (char) porta agli stessi problemi di prima.
Il C non definisce la grandezza dei dati. Con un particolare compilatore 
ti potresti trovare anche un char di 8 byte. I tipi sono standard, la 
loro lunghezza è tutt'altro che standard. Che poi il char al 99.9% dei 
casi è un byte ( 8 bit ) è un altro discorso.
L'unica cosa standard è la lunghezza del INT che è essattamente largo 
quanto i registri del processore ( 32bit in questo caso ).
Di solito troviamo:
char 8 bit
short 16 bit
long 32 bit
long long 64 bit ( ma non tutti lo supportano )
comunque per rispondere alla tua domanda:
- Se vuoi ottimizare l'uso della ram ( a scapito di prestazioni e di 
codice ) allora ti convine "comprimere" il più possibile.
- Se vuoi ottimizare la velocità e l'uso della ROM ( a scapito della RAM 
) allora usa sempre INT ( se ti basta ).
Un contatore che conta da 1 a 10 anche se lo dichiari int non succede 
niente.
Questo a meno ovvio che il compilatore non allini di suo in memoria le 
cose e allora tutto questo discorso va a remengo perché anche se tu 
dichiari char lui usa un int...
-- 
Il Razziatore,
"Lo sviluppo di una nazione si misura anche dallo stato della sua rete 
ferroviaria". Camillo Benso Conte di Cavour
"Per tutto quanto non previsto nel presente regolamento il capostazione 
deve usare senno e ponderatezza." Regolamento d'esercizio FS
-----------------------------------------------
MSN   : 
IlRazz...@netscape.net
ICQ   : 67552596
Yhaoo : Razziatore82
-----------------------------------------------
Founder of MediaPlayer Project
http://razziatore.no-ip.com/mpp/

 agli stessi problemi di prima.
Gli ARM hanno anche istruzioni che gestiscono i byte
in memoria, quindi il problema non si pone. Se usi i tipi
ad 8 bit le operazioni di lettura e scrittura in memoria
avranno delle istruzioni corrispondenti e quindi non
si perde niente. Una volta copiati nei registri poi il
datapath potrebbe funzionare anche a 32 bit e non
farebbe alcuna differenza. Il problem è solo nelle
operazioni di load e store in memoria, perchè è li
che se un tipo non è supportato devi usare più
istruzioni per gestire l'allineamento e la mascheratura.
Quindi a mio parere quello che dici vale per i tipi
più lunghi del datapath, non per quelli più corti (che
di solito hanno delle istruzioni di movimento apposite).
A.D.

 di solito hanno delle istruzioni di movimento apposite).
Rispondo qui anche a Razziatore.
Spulciando ho trovato l'AN1265 della Luminary (disponibile sul sito, ma 
bisogna registrarsi) sull'ottimizzazione del codice.
A titolo di cronaca riporto alcuni stralci che potrebbero essere 
interessanti da discutere.
"Size of Variables
As mentioned in Table 1, use of variables larger than the processor is 
naturally comfortable with usually means many extra instructions and/or 
calls to functions. On 8-bit and 16-bit processors, this often means 
that anything larger than 16-bits (a short int) will have this effect. 
On Stellaris, only long long ints can cause this. However, the 
instruction set fully supports most long long operations inline
using only one additional instruction. For example, long long add, uses 
ADD followed by ADDC. Likewise, subtract uses SUB and SUBB (borrow). 
Multiply has special operations to support 64-bit multiply. Only divide 
will require a callout, unless the result is into a 32-bit result."
E ok, qui ci siamo. Poi aggiunge:
"When the local variables are smaller than the register size, then extra 
code is usually needed. On a Stellaris part, this means that local 
variables of size byte and halfword (char and short int respectively) 
require extra code. Since code ported from an 8-bit or 16-bit 
microcontroller may have had locals converted to smaller sizes (to avoid 
the too large problem), this means that such code will run slower and 
take more code space than is needed. Changing the locals to int/unsigned 
(or long and unsigned long) often saves 40% or more in code space. 
SeeTable 3 for a comparison."
Ovviamente il discorso è generico e non centrato su un particolare 
compilatore.
Infine, da un altro documento:
"Another technique implemented by the Cortex-M3 processor that provides 
for efficient SRAM utilization is unaligned data access."
Cioè i dati sono memorizzati in aree contigue di memoria a prescindere 
dalla loro dimensione. Non vi è quindi allineamento.
Ciao!
Marco / iw2nzm


Io direi che dipende da diversi fattori.
Innanzitutto, come ha già detto Razziatore, è bene non fare assunzioni 
sulla grandezza dei tipi standard. Se vuoi proprio ragionarci è meglio 
usare i tipi ISO.
Per rispondere alle tue domande direi che molto dipende dal contesto:
- Devi riusare il codice?
- Devi poterlo eseguire su cpu/architetture diverse?
- Le strutture dati devo essere condivise/salvate/inviate da qualche 
parte?
- Vuoi che il tuo codice sia veloce o che occupi poca memoria?
Le mie esigenze di solito sono:
- Codice riusabile il più possibile
- Strutture molto spesso condivise
- Il codice deve prioritariamente occupare poco spazio
Con queste premesse, come linea generale, io mi comporto così:
- Definisco tipi base tramite typedef, non ragiono in termini di int o 
int16_t ma eeprom_addr_t o speed_t. In questo modo sono agnostico 
rispetto al tipo usato e posso cambiarlo al volo se necessario 
andando in un solo posto del codice (magari anche tramite #if a 
seconda della piattaforma).
- Per i campi delle strutture dati, uso il tipo che è sufficiente a 
contenere il range di valori che mi interessa (usando sempre tipi 
personalizzati).
- Ordino i campi di dette strutture dal più grande al più piccolo: in 
questo modo riduco il padding necessario pur mantenendo un accesso 
allineato e veloce.
-Se una struttura deve essere condivisa/inviata/salvata aggiungo io a 
mano il padding dove voglio che stia oppure uso un attributo del 
compilatore per disattivare il padding su quella specifica struttura. 
L'accesso a dati disallineati è più lento e richiede più codice in 
ROM, ma non si può fare altrimenti in questi casi. Inoltre aggiungo 
static_assert un po' dovunque per assicurarmi che dette strutture 
siano davvero lunghe come penso io.
- Nei cicli uso sempre il tipo appropriato per l'indice: se è un 
indirizzo di una eeprom avrò il mio eeprom_addr_t, se è un'area di 
memoria un size_t etc... Dove non c'è un'esigenza specifica uso un 
generico int/unsigned int.
-- 
  _|/ Francesco Sacchi - Develer S.r.l., R&D dept.
   |\ 
http://www.develer.com/
 - 
http://www.bertos.org

 comporta più codice.
Scusami Marco se proprio te lo dico, secondo me sei bravo e capace ma 
perche' ti poni di questi problemi che sanno tanto di  pippe mentali :) se 
vuoi essere perfezionista in quello che fai non ti conviene scrivere il 
programma nel modo piu' semplice possibile? poi dopo aver visto che funziona 
puoi affinare il tutto? provando cosa puo' ridurre e/o rendere piu' 
efficiente il codice? non ultima cosa se proprio si e' perfezionisti si puo' 
usare un analizzatore di stati logici per vedere con mano la velocita' di 
una routine rispetto ad un'altra. 

 una routine rispetto ad un'altra. 
Sinceramente capire perché fare una scelta piuttosto che un'altra non mi 
sembrano "pippe mentali". Non si tratta di perfezionismo, anzi. Giusto 
cercare di scrivere codice decente e di imparare a mano a mano che procedo.
Ciao!
Marco / iw2nzm