Pagina 1 di 1

Frammento ARM Assembly per STM32

MessaggioInviato: mercoledì 2 gennaio 2019, 11:42
da DoctorStrange
Buongiorno a tutti,

Io programmo un microcontrollore della ST Microelectronics in Assembly nativo. In particolare il controller che uso si chiama STM32F103RB. Il suo Reference Manual si trova qui.

Ora, andando a pagina 171 di questo manuale, si trova uno dei suoi registri interni che si chiama GPIO_CRL. Lo scopo della routine che ho realizzato, è quello di fare in modo che i bit da 31 a -> 24 siano impostati in questo modo: 1 0 0 1 1 0 0 1.

Una condizione però indispensabile è che, mentre questi otto bit vanno impostati con quel pattern, tutti gli altri bit dello stesso registro devono rimanere nello stato precedente che suppongo essere sconosciuto, e devono rimanere identici, anche dopo che io ho impostato i bit che mi interessano.

Per farlo ho sviluppato questa routine, e vorrei sapere se nella comunity c'è qualcuno con un pò di esperienza in ARM Assembly che mi possa dare una controllata a questo frammento di codice:

Codice: Seleziona tutto
@Presupposto = nel registro r1 leggo lo stato attuale del registro GPIO_CRL, prima di impostare i miei bit. Quindi R1=Stato del registro GPIO_CRL
mov       r2,#0x06
lsl            r2,r2,#0x018
mvn.w    r2,r2
and         r2,r2,r1
mov        r3,#0x018
orr           r2,r2,r3
str           r2,[r0]
@In r0 c'è l'indirizzo del registro GPIO_CRL


Sò che è una richiesta un pò strana e che magari l'ARM assembly è un linguaggio molto di nicchia, ma confido che esista qualche irriducibile come me :)

Grazie mille.

Re: Frammento ARM Assembly per STM32

MessaggioInviato: mercoledì 2 gennaio 2019, 16:57
da Eresia
scusa la domanda non sono molto ferrato nell'assembly [le uniche mie competenze derivano da vari debug], ma perchè usi mvn.w e non mvn o mvnne ?

Re: Frammento ARM Assembly per STM32

MessaggioInviato: mercoledì 2 gennaio 2019, 17:20
da DoctorStrange
Eresia ha scritto: ma perchè usi mvn.w e non mvn o mvnne ?


mvn.w Significa "Move NOT" ed il ".w" alla fine specifica che stò definendo una word, ovvero una variabile a sedici bit, nonostante il core sia a 32, mi serve per allineare questa not a tutte le altre istruzioni. Questa particolare istruzione serve ad invertire lo stato di tutti i bit definiti in quel registro.

mvn (senza il ".w") avrebbe potuto creare qualche problema, dal momento che il core del processore è a 32 bit, ma quesl particolare registro è definito a sedici bit, quindi mi serviva specificare un'istruzione che fosse minore del normale standard a 32 bit.

mvnne non mi risulta essere un'istruzione esistente. Forse deriva da qualche altro tipo di linguaggio, o per qualche altro tipo di micro controllore?

Re: Frammento ARM Assembly per STM32

MessaggioInviato: mercoledì 2 gennaio 2019, 20:52
da Eresia
perfetto, grazie per l'appunto!
http://www.keil.com/support/man/docs/ar ... 882734.htm

però ripeto, non sono un programmatore assembly, mi limito a disassemblare parti di programmi con gdb

Re: Frammento ARM Assembly per STM32

MessaggioInviato: giovedì 3 gennaio 2019, 21:04
da melfnt
DoctorStrange Immagine ha scritto:Buongiorno a tutti,

Io programmo un microcontrollore della ST Microelectronics in Assembly nativo. In particolare il controller che uso si chiama STM32F103RB. Il suo Reference Manual si trova qui.

Ora, andando a pagina 171 di questo manuale, si trova uno dei suoi registri interni che si chiama GPIO_CRL. Lo scopo della routine che ho realizzato, è quello di fare in modo che i bit da 31 a -> 24 siano impostati in questo modo: 1 0 0 1 1 0 0 1.

Una condizione però indispensabile è che, mentre questi otto bit vanno impostati con quel pattern, tutti gli altri bit dello stesso registro devono rimanere nello stato precedente che suppongo essere sconosciuto, e devono rimanere identici, anche dopo che io ho impostato i bit che mi interessano.

Per farlo ho sviluppato questa routine, e vorrei sapere se nella comunity c'è qualcuno con un pò di esperienza in ARM Assembly che mi possa dare una controllata a questo frammento di codice:

Codice: Seleziona tutto
@Presupposto = nel registro r1 leggo lo stato attuale del registro GPIO_CRL, prima di impostare i miei bit. Quindi R1=Stato del registro GPIO_CRL
mov       r2,#0x06
lsl            r2,r2,#0x018
mvn.w    r2,r2
and         r2,r2,r1
mov        r3,#0x018
orr           r2,r2,r3
str           r2,[r0]
@In r0 c'è l'indirizzo del registro GPIO_CRL


Sò che è una richiesta un pò strana e che magari l'ARM assembly è un linguaggio molto di nicchia, ma confido che esista qualche irriducibile come me :)

Grazie mille.


Ciao!
Nel documento che hai linkato non c'è l'instruction set, quindi risulta abbastanza difficile aiutarti. In particolare, ci sono alcune istruzioni che non capisco cosa facciano:
lsl (è uno shift a sinistra?), mvn.w (già chiarito nel precedente post), orr.

Comunque il tuo codice mi sembra fin troppo complicato per lo scopo che devi raggiungere, io lo avrei fatto usando due maschere di bit.
Probabilmente lo sai già e le avrai già usate in altri contesti: l'osservazione principale è che se fai l'AND bit a bit fra il valore di un registro e una maschera composta da zeri e uni, il risultato avrà zero nelle posizioni in corrispondenza degli zeri nella maschera e il valore del registro originale in corrispondenza degli uni della maschera. Similmente, se fai l'OR bit a bit fra il valore di un registro e una maschera composta da zeri e uni, il risultato avrà uno nelle posizioni in corrispondenza degli uni della maschera e il valore del registro originale in corrispondenza degli zeri nella maschera.
Questo fatto è facilmente verificabile: qualsiasi sia il valore binario X,
1 AND X = X
0 AND X = 0
1 OR X = 1
0 OR X = X

Sfruttando questo fatto, bastano un AND e un OR con due maschere appropriate per riuscire a settare tutti i bit che ti servono in un registro:
Codice: Seleziona tutto
@Presupposto = nel registro r1 leggo lo stato attuale del registro GPIO_CRL, prima di impostare i miei bit. Quindi R1=Stato del registro GPIO_CRL
and       r1, r1, #MASCHERA_AND
or          r1, r1, #MASCHERA_OR
str         r1,[r0]
@In r0 c'è l'indirizzo del registro GPIO_CRL


Esercizio: trova i valori corretti di MASCHERA_AND e MASCHERA_OR affinché i bit da 24 a 31 vengano settati come vuoi.

Soluzione:
assumendo che il bit 31 sia quello meno significativo, altrimenti devi ribaltare le maschere
MASCHERA_AND = #0b11111111111111111111111111001001
MASCHERA_OR = #0b00000000000000000000000001001001

Quindi il codice diventa:
Codice: Seleziona tutto
@Presupposto = nel registro r1 leggo lo stato attuale del registro GPIO_CRL, prima di impostare i miei bit. Quindi R1=Stato del registro GPIO_CRL
and       r1, r1, #0b11111111111111111111111111001001
or          r1, r1, #0b00000000000000000000000001001001
str         r1,[r0]
@In r0 c'è l'indirizzo del registro GPIO_CRL


Se non puoi usare le costanti in binario, trasforma quei due numeri in esadecimale :)

Re: Frammento ARM Assembly per STM32

MessaggioInviato: giovedì 3 gennaio 2019, 21:17
da DoctorStrange
Quelle che ho fatto sono esattamente due maschere. Una di and per azzerare ed una di or per settare.

Sono in totale sei istruzioni. 100 nano secondi per completare l'intera procedura. Non credo di poter pretendere di meglio, per in controllore da due euro. Giusto?

La tua soluzione non l'ho studiata ma è di sicuro errata, perchè usi variabili a 32 bit, mentre quel registro è a 16.

Per questo motivo mi sono servito di una shift e di una move-not. Per non appestare il compilatore di una sfilza di uni e zero.

Re: Frammento ARM Assembly per STM32

MessaggioInviato: venerdì 4 gennaio 2019, 12:34
da melfnt
Ciao!

DoctorStrange Immagine ha scritto:Quelle che ho fatto sono esattamente due maschere. Una di and per azzerare ed una di or per settare.



melfnt ha scritto:Ciao!
Nel documento che hai linkato non c'è l'instruction set, quindi risulta abbastanza difficile aiutarti. In particolare, ci sono alcune istruzioni che non capisco cosa facciano:
lsl (è uno shift a sinistra?), mvn.w (già chiarito nel precedente post), orr.


Se mi rispondi anche a questo provo a controllare anch'io il tuo frammento (:


Sono in totale sei istruzioni. 100 nano secondi per completare l'intera procedura. Non credo di poter pretendere di meglio, per in controllore da due euro. Giusto?



Completamente in disaccordo. Se sei un programmatore assembler e puoi fare una cosa in due istruzioni, perché farla con sei?
Non è solo questione di quanto tempo ci mettono ad essere eseguite, ma anche di quanto è lungo il codice, che influisce sui cache fault.


La tua soluzione non l'ho studiata ma è di sicuro errata, perchè usi variabili a 32 bit, mentre quel registro è a 16.


Ho visto che nella tua prima risposta hai parlato di registri a 16 bit ma c'è una cosa che non mi è chiara: quale registro è a 16 bit? r2 oppure GPIO_CRL? In questo secondo caso, perché parli di bit da 24 a 31 se i bit sono 16?

E ancora, nell'istruzione str r2,[r0] stai spostando il contenuto di r2 in GPIO_CRL: se uno dei due è a 32 bit e l'altro a 16, dev'essere definito il modo in cui i 32 bit vengono spostati in un registro a 16 bit (per esempio: vengono usati solo i 16 bit meno significativi).
Altrimenti, se sono entrambi registri a 16 bit puoi semplicemente usare maschere a 16 bit, troncando le mie.


Per questo motivo mi sono servito di una shift e di una move-not. Per non appestare il compilatore di una sfilza di uni e zero.


Qui non ti seguo: cosa intendi con "appestare"?

:)

Re: Frammento ARM Assembly per STM32

MessaggioInviato: venerdì 4 gennaio 2019, 12:57
da DoctorStrange
melfnt ha scritto:Se mi rispondi anche a questo provo a controllare anch'io il tuo frammento (:


L'assembly è un linguaggio di programmazione, quello che ho postato è un Reference Manual, dunque se vuoi le specvifiche dell'assembly, devi cercare le specifiche del linguaggio.

melfnt ha scritto:
Completamente in disaccordo. Se sei un programmatore assembler e puoi fare una cosa in due istruzioni, perché farla con sei?
Non è solo questione di quanto tempo ci mettono ad essere eseguite, ma anche di quanto è lungo il codice, che influisce sui cache fault.



La spasmodica ricerca di efficienza andando a pistolare nei codici per renderli più rapidi di cento nano secondi, mi sembra un'inutilità. Anche se sviluppo in assembly non mi faccio problemi a rendere il codice un pò più chiaro, rendendolo un pò più prolisso, a costo di dover rallentare di qualche nano secondo l'istruzione.


melfnt ha scritto:Ho visto che nella tua prima risposta hai parlato di registri a 16 bit ma c'è una cosa che non mi è chiara: quale registro è a 16 bit? r2 oppure GPIO_CRL?


Logicamente è GPIO_CRL a sedici bit, come scritto sul Reference Manual.

melfnt ha scritto: In questo secondo caso, perché parli di bit da 24 a 31 se i bit sono 16?


Qui in effetti hai ragione tu. E' stato un mio errore. Il registro di destinazione è a sedici bit.

melfnt ha scritto:Qui non ti seguo: cosa intendi con "appestare"?


Scrivere costanti numeriche composte da interminabili stringhe di uni e di zeri rende il codice poco leggibile e poco portabile. Questo vuol dire che, impiegando notazioni esadecimali ed istruzioni di shisfting, e di inversione (move-not) aumentano ereditarietà, e polimorfismo nel codice. Quindi evito di dichiarare maschere bitwise con interminabili sequenze binarie.

Re: Frammento ARM Assembly per STM32

MessaggioInviato: venerdì 4 gennaio 2019, 14:25
da melfnt
DoctorStrange Immagine ha scritto:
melfnt ha scritto:Se mi rispondi anche a questo provo a controllare anch'io il tuo frammento (:



L'assembly è un linguaggio di programmazione, quello che ho postato è un Reference Manual, dunque se vuoi le specvifiche dell'assembly, devi cercare le specifiche del linguaggio.



Sì, questo l'avevo capito, non stavo criticando la tua risposta. Mi sembra semplicemente molto strano che non ci siano le specifiche dell'assembler in quel manuale.


La spasmodica ricerca di efficienza andando a pistolare nei codici per renderli più rapidi di cento nano secondi, mi sembra un'inutilità. Anche se sviluppo in assembly non mi faccio problemi a rendere il codice un pò più chiaro, rendendolo un pò più prolisso, a costo di dover rallentare di qualche nano secondo l'istruzione.



Ancora una volta, il mio punto di vista è completamente diverso dal tuo: nei linguaggi di programmazione ad alto livello è ragionevole ricercare chiarezza e manutenibilità del codice, ma solo perché a fare le ottimizzazioni ci pensa il compilatore (e ti assicuro che il codice assembler generato dal compilatore è veramente veramente uno schifo, vedi sotto).

Se invece programmi direttamente in assembler, vuol dire o che stai scrivendo tu il compilatore (non è questo il caso) o che hai dei requisiti di efficienza, e in questo caso la la comprensibilità passa in ultimo piano. Fra l'altro non sono molto sicuro che il tuo codice sia più chiaro del mio.

melfnt ha scritto:Ho visto che nella tua prima risposta hai parlato di registri a 16 bit ma c'è una cosa che non mi è chiara: quale registro è a 16 bit? r2 oppure GPIO_CRL?


Logicamente è GPIO_CRL a sedici bit, come scritto sul Reference Manual.

melfnt ha scritto: In questo secondo caso, perché parli di bit da 24 a 31 se i bit sono 16?


Qui in effetti hai ragione tu. E' stato un mio errore. Il registro di destinazione è a sedici bit.



Allora puoi usare costanti binarie a 16 bit, e magari un registro general purpose a 16 bit per fare l'AND e l'OR:

Codice: Seleziona tutto
@Presupposto = nel registro r1 leggo lo stato attuale del registro GPIO_CRL, prima di impostare i miei bit. Quindi R1=Stato del registro GPIO_CRL
and       r1, r1, #0b1111111111001001
or          r1, r1, #0b0000000001001001
str         r1,[r0]
@In r0 c'è l'indirizzo del registro GPIO_CRL


Scrivere costanti numeriche composte da interminabili stringhe di uni e di zeri rende il codice poco leggibile e poco portabile. Questo vuol dire che, impiegando notazioni esadecimali ed istruzioni di shisfting, e di inversione (move-not) aumentano ereditarietà, e polimorfismo nel codice. Quindi evito di dichiarare maschere bitwise con interminabili sequenze binarie.


Sulla leggibilità mi sono già espresso prima.
Sulla portabilità mi sembra una battaglia contro i mulini a vento: stai scrivendo in assembler :D
Il tuo codice sorgente viene letto dall'assemblatore e le istruzioni vengono trasformate in codice macchina in corrispondenza un a uno con quelle scritte in assembler mnemonico, non vedo quindi come un codice di questo tipo possa essere portabile.

I termini ereditarietà e polimorfismo mi sembrano estremamente fuori luogo in questo contesto: sono concetti che si applicano ai linguaggi di programmazione ad alto livello e che poco hanno a che fare con l'assembler. Non so se li hai tirati in ballo solo per darti un tono mostrando che conosci questi termini, spero di no, in caso contrario per favore spiegati meglio.

Bonus:
Mi trovi d'accordo sul fatto che un codice ad alto livello (C) di questo tipo :
Codice: Seleziona tutto
#include <stdio.h>

int main ()
{
   int a = 5;
   a = a+3;
   a = a+17;
   printf ("%d",a);
   return 0;
}


talvolta è più leggibile dell'equivalente:
Codice: Seleziona tutto
#include <stdio.h>

int main ()
{
   int a = 25;
   printf ("%d",a);
   return 0;
}


Soprattutto se i numeri 5, 3, 17 hanno un qualche significato semantico nel contesto o se sono altre variabili. La portabilità del codice non è influenzata da quale delle due versioni viene utilizzata, e in ogni caso, abilitando l'opzione di ottimizzazione di gcc, in entrambi i codici la parte che assegna la variabile a viene compilata con questa istruzione assembler:
Codice: Seleziona tutto
mov    $0x19,%edx


Quindi, per me la regola d'oro quando si tratta di maschere (e di costanti in generale): il linguaggio ad alto livello deve essere il più chiaro possibile, l'assembler il più performante possibile.

Re: Frammento ARM Assembly per STM32

MessaggioInviato: venerdì 4 gennaio 2019, 15:55
da runblade
Ora non so che sistema di sviluppo utilizzi, ma compilarlo e debuggarlo sul target mediante jtag (o SWD) direttamente sul core del micro? :(

E pensare che ARM con questi core M3 (e in generale con tutti i suoi 32bit) ha cercato in tutti i modi di svincolare i programmatori di microcontrollori dall'utilizzo dell'assembly...Manco aveste un uPD75pxx o un MCS51 con 4K di ROM :muro:

Re: Frammento ARM Assembly per STM32

MessaggioInviato: domenica 6 gennaio 2019, 16:21
da melfnt
runblade Immagine ha scritto:Ora non so che sistema di sviluppo utilizzi, ma compilarlo e debuggarlo sul target mediante jtag (o SWD) direttamente sul core del micro? :(

E pensare che ARM con questi core M3 (e in generale con tutti i suoi 32bit) ha cercato in tutti i modi di svincolare i programmatori di microcontrollori dall'utilizzo dell'assembly...Manco aveste un uPD75pxx o un MCS51 con 4K di ROM :muro:


In una situazione normale ti darei ragione, in questo caso ci sono ben due motivi per cui un programmatore lo vorrebbe scrivere direttamente in assembler:

1) l'efficienza su cui mi sono già espresso prima.
2) Se ci fosse un modo ad alto livello di impostare i pin GPIO probabilmente sarebbe una chiamata di libreria a cui va specificata una maschera di bit, allora tanto vale farlo in assembler direttamente.