Newsgroup: it.hobby.elettronica.digitale
Titolo: Architettura ARM: un vostro parere

Sto avendoa che fare con un micro Atmel dotato di architettura ARM e mi sono 
fatto una mia idea non proprio positiva. Voi cosa ne pensate di tale 
architettura? 


A me sembra molto buona, cosa non ti convince?
Ciao

Deathrider <d@d.d> wrote in message
442c39ba$0$18285$4faf...@reader1.news.tin.it...

In generale non è male...
Cos'è che non ti piace?
Ciao,
A.D.



Quale micro?
Non starai pensando di programmare un ARM in assembler, vero?
--
ciao
  Stefano


E' un micro come tanti altri. Va conosciuto quel tanto che basta...
Che cosa c'è che non ti piace?
Davide b.
--------------------------------
Inviato via 
http://arianna.libero.it/usenet/

Sicuramente non intendo usare l'assembler. Quello che non mi piace è la 
ridondanza dei registri: non puoi leggere gli interrupt abilitati nello 
stesso registro in cui li abiliti ma devi leggerli in un registro apposta e 
in un altro ancora li devi disabilitare. Vuoi controllare un pin? in un 
registro lo mandi basso, in un altro lo mandi alto e in un altro ancora (se 
lo abiliti altrove) puoi fare le due cose. Vuoi abilitare un interrupt? c'è 
un bit nel controller degli int e uno nella periferica che stanno come due 
interruttori in serie mentre non esiste un bit di abilitazione globale per 
tutti gli interrupt, devi ogni volta impostarli tutti se vuoi isolare il 
sistema. Può darsi che la particolare implementazione di atmel sia fatta 
così ma non mi sembra così "pulita" anzi è un casino di registri per fare 
anche la cosa più scema. Su altre architetture bastava un solo registro per 
fare una cosa e potevi leggerlo e scriverlo mentre qua devi dibatterti fra 
registri che puoi solo leggere e registri che puoi solo scrivere col 
risultato che hai tre volte il numero di registri che in realtà ti 
servirebbero. 

 in un altro ancora li devi disabilitare.
Può sembrare ridondante, ma in realtà è fatto apposta per semplificare 
la vita e rendere il codice più efficiente.
Tutti i programmatori embedded hanno prima o poi incontrato quelle che 
in gergo si chiamano "race conditions", condizioni cioè in cui, 
contemporaneamente, due parti di codice tentano di accedere 
simultaneamente allo stesso hardware.
La maggior parte delle CPU, per diversi motivi, non ha un operatore che 
permette di settare direttamente lo stato di un bit all'interno di un 
registro. Hanno solo i classici operatori logici. Per settare il bit 2 
del registro hardware PIPPO, in C scriveresti:
PIPPO |= 0x04;
L'istruzione sopra, viene tradotta dal compilatore in minimo 3 
istruzioni assembly separate, cioè:
leggi PIPPO in R1
fai l'OR logico tra R1 e 0x04
scrivi R1 su PIPPO
E questo non sarebbe un male se non esistessero gli interrupt.
Gli interrupt non interrompono un'istruzione in corso, ma vengono 
serviti non appena l'istruzione attuale termina.
Metti il caso che la routine di interrupt vada a modificare proprio 
PIPPO e che essa venga eseguita proprio durante le 3 righe viste sopra:
leggi PIPPO in R1
fai l'OR logico tra R1 e 0x04
<<< interrupt
scrivi R1 su PIPPO
Cosa accade?
Le modifiche effettuate su PIPPO nell'interrupt vengono sovrascritte 
alla sua uscita, in modo del tutto imprevedibile.
Questo causa bug di difficilissima risoluzione, il programma comincia a 
comportarsi in maniera casuale, nemmeno con un'esecuzione passo passo si 
riesce a riprodurli, proprio a causa della natura asincrona delle 
interruzioni.
L'unica soluzione al problema sopra elencato è di disabilitare tutti gli 
interrupt prima di accedere all'hardware.
Riprendendo l'esempio in C:
DISABLE_INTERRUPT();
PIPPO |= 0x04;
ENABLE_INTERRUPT();
Chiaramente questo va fatto solo per l'hardware che è condiviso da più 
parti di codice, ma in genere, a priori, non si sa quali parti di codice 
accederanno, per esempio, ai port con il risultato che è necessario 
frequentemente disabilitare gli interrupt. Questo può causare ritardi a 
volte non trascurabili.
Avere invece registri separati per leggere, settare e resettare permette 
di evitare tutti i problemi posti sopra.
Se il registro hardware PIPPO di prima fosse diviso in PIPPO_R per la 
lettura, PIPPO_1 per settarlo e PIPPO_0 per resettarlo, l'istruzione in 
C per settare il 2 bit sarebbe:
PIPPO_1 = 0x04;
Che viene tradotta in assembly in una sola istruzione, quindi non 
"interruptabile"; vengono di colpo tolti tutti i problemi fin qui 
esposti, ed il codice è anche più veloce (1 istruzione assembly contro 
3, nessuna necessità di disabilitare gli interrupt).
Devi solo scegliere il registro giusto e tutto si fa con una sola 
istruzione.
Spero di esserti stato utile
ciao
Francesco

La tua risposta è stata più che che sufficiente e ti ringrazio per avermi 
illuminato. A questo punto mi chiedo: non era più conveniente includere una 
istruzione per accedere ai registri piuttosto che creare il triplo dei 
registri necessari? Una istruzione del tipoBSF r,2 come si fa con i Pic. E' 
una sola istruzione e, da quello che mi hai descritto, non dovrebbe 
presentare i problemi relativi agli interrupt. O forse devo pensare che 
l'architettura dei pic è più efficiente di quella arm? 

 l'architettura dei pic è più efficiente di quella arm? 
Sì, in questo particolare caso i PIC sono più efficienti, ma la 
soluzione applicata nei PIC non è scalabile in generale. Per esempio 
vale solo se devi settare 1-2 bit contemporaneamaente al massimo, 
altrimenti ti tocca scrivere BSF tante volte quante bit devi settare, e 
quindi non è più efficiente.
La BSF esiste nei PIC perchè in genere hanno un numero di registri 
hardware limitato. Questo consente di creare istruzioni assembly 
apposite in cui l'indirizzo del registro e il bit da settare sono 
codificate nel codice dell'istruzione stessa.
Questo è possibile se hai pochi bit, poche periferiche e la loro 
configurazione è fissa.
L'architettura ARM invece è a 32bit, è generica e le periferiche possono 
cambiare anche di parecchio. Anche l'ARM ha l'istruzione per 
settare/resettare un singolo bit, ma solo dei registri della CPU, non di 
qualunque locazione dello spazio di indirizzamento.
La soluzione adottata dalla Atmel quindi è di pari efficienza nel caso 
singolo bit e più efficiente di quella Microchip nel caso generale, 
richiede solo un po' di abitudine ai registri da usare, ma ripaga in 
termini di prestazioni del codice.
Ciao

 scrivi R1 su PIPPO
In genere io cerco di evitare queste situazioni a priori. L'interrupt scrive
dei registri, il programma principale li legge.
Tipico esempio è l'ISR che gestisce la ricezione di un byte dalla seriale.
L'ISR si limita a memorizzare il byte in RAM (in buffer, ciclico o lineare)
e a incrementare un puntatore o un contatore di byte ricevuti.
Il programma principale *legge* il valore di questo contatore e, se maggiore
di zero, *legge* il contenuto del buffer.
In questo modo è praticamente impossibile creare condizioni di cui parli tu.

 ha
scritto:


Nel tuo caso della seriale si, anche perchè la seriale è unica e di solito usata
solo da una routine, ma su periferiche con diversi inputs e usate da diverse
routines succede.
Per esempio a me succedeva con l'A/D converter a 8 ingressi di un ATMega, di
solito gestito da una routine per l'acquisizione di temperature, era anche usato
in un'altra per controllare lo stato della batteria ogni tanto in INT\.
Quando si verificava il contemporaneo accesso delle routine all'ADC, anzi al
risultato dell'ADC avevo una lettura errata nello stato batteria che, anche se
mediata da altre letture, mi causava ogni tanto un allarme di batteria scarica.
La batteria però era ancora carica, e prima di avere l'intuizione giusta mi sono
dovuto spremere per bene le meningi...
--
ciao
  Stefano

 tu.
Quello che dici è vero solo in parte e solo nel caso speciale in cui 
devi solo leggere *coerentemente* singoli byte.
I uC maggiormente usati di solito sono a 8 bit. Per cui richiedono più 
istruzioni per leggere un dato a 16 o più bit. Se per esempio nell'ISR 
scrivi un dato a 16bit e nel codice normale lo rileggi, potresti lo 
stesso incorrere in race conditions nel caso in cui la lettura fosse 
interrotta a metà dall'ISR:
leggo parte bassa byte,
<<<<ISR modifica parte bassa e alta della word a 16 bit
leggo parte alta byte
E quindi la lettura non è coerente.
Inoltre, sempre parlando in generale, non puoi dire a priori che nel 
programma non potrai scriverei mai i registri delle periferiche, perchè 
nella maggior parte delle situazioni è necessario.
L'esempio classico è che l'ISR deve alzare/abbassare un pin su un PORT 
di I/O, con però altri pin controllati anche nel normale codice del 
programma al di fuori delle ISR.
Quando si può si cerca di evitare di disabilitare gli interrupt, ma la 
maggior parte delle volte non si può fare altrimenti.

 ha scritto nel messaggio 
news:zduXf.62161$A83.1...@twister1.libero.it...


 mediata da altre letture, mi causava ogni tanto un allarme di batteria scarica.
Secondo me il problema era di fondo e cioè il fatto di utilizzare in
due parti diverse
l'ADC, per di più, una volta, in un ISR. Io avrei usato sempre la mia
solita adc() e,
all'interno di questa routine, prima di iniziare il successivo
campionamento, avrei
controllato l'evento che mi faceva scattare la lettura dello stato
della batteria. Se
questa evento era presente, allora facevo l'acquisizione della tensione
di batteria,
altrimenti continuavo con l'acquisizione della temperatura.
In tutti i miei firmware sono sempre riuscito ad utilizzare le varie
periferiche in un unico
punto del programma. Magari è perché ho fatto solo cose molto
semplici...

 maggior parte delle volte non si può fare altrimenti.
Come detto, penso di non essermi mai messo in queste condizioni
critiche...
o magari sì e non me ne sono mai accorto.

 e come fai quando hai nesting di interrupt con priorità?
Penso che, come al solito, devi stare attento a non condividere
periferiche
o zone di RAM tra le varie ISR... bisognerebbe vedere caso per caso.
In genere io cerco di mantenere le ISR molto molto piccole e di settare
al massimo
qualche flag o qualche byte, nulla di più. E' nel programma principale
che faccio
le operazioni vere e proprio.
Ma magari le mie situazioni sono molto semplici...

Ragazzi scusate forse diro' una str.....ata ma usando dei compilatori non 
dovrebbero essere questi ultimi ad occuparsi di tuttti i registri?
"Deathrider" <d@d.d> ha scritto nel messaggio 
news:442c39ba$0$18285$4faf...@reader1.news.tin.it...


semplici...
Certo, il tuo approccio è corretto, ma nel mio caso si trattava di una versione
a batteria di un apparecchiatura funzionante a tensione di rete, per cui quella
parte di software è stata aggiunta dopo, oltretutto non volendo toccare la parte
già testata.
Era un inconveniente di poco conto e risolvibile con due istruzioni, ma la
difficoltà è capire cosa può dare il problema, perchè è asincrono e si manifesta
in modo apparentemente casuale.
--
ciao
  Stefano

 principale e *altri* pin all'interno di una ISR?
Il problema è che anche se sono pin diversi, sono nello stesso registro.
E siccome non si può atomicamente cambiare un bit, ma è necessario 
leggere, modificare e riscrivere il registro, ecco che si viene a creare 
una condizione pericolosa.

 Su altre architetture...
PIC? E' un'altra architettura... 8 bit.
Peraltro il PIC è di per se stesso un'architettura "strana". (In tutti i
micro "Classici" c'è il registro accumulatore. La prima volta che ho
studiato il PIC mi sono spaventato... salvo poi scoprire che si chiamava
R0.)
Ma oramai nessuno ci fa più caso!
Davide B.


Francesco Sacchi ha scritto:

Come al solito, dipende se l'istruzione di set/reset di un pin di una porta
è atomica oppure no. Se sì (come nei PIC), non c'è da preoccuparsi. Altrimenti
è effettivamente bene stare attenti...