Autor Tema: Assembler en ARM - Probando  (Leído 19857 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Assembler en ARM - Probando
« Respuesta #15 en: 11 de Octubre de 2015, 09:16:38 »
hombre ya que nos ponemos que sea de v7 para arriba  :D :D, y si ya sabia lo de los lios que traen los ARM con la compatibilidad para procesadores y es un lio de cojones, por eso te preguntaba, por si acaso tu ya lo tenias claro  :D :D

Lo unico claro que tengo es la piel por no salir al sol. Peeero mira como salieron las cosas del MPASM y dsPIC :P, solo ponerlo voluntad y en un dia te programas un satelite  :D

Desconectado juaperser1

  • Colaborador
  • DsPIC30
  • *****
  • Mensajes: 2980
Re:Assembler en ARM - Probando
« Respuesta #16 en: 11 de Octubre de 2015, 09:28:57 »
Citar
Lo unico claro que tengo es la piel por no salir al sol. Peeero mira como salieron las cosas del MPASM y dsPIC :P, solo ponerlo voluntad y en un dia te programas un satelite  :D

toda la razón del mundo, no se puede estar mas de acuerdo, pero si en vez de un satelite es una estrella de la muerte mejor que mejor :D :D

y no hablemos de piel clara, por que si me hacen una foto con flash me tengo que hechar aloevera por las quemaduras  :D :D

Desconectado Carl47D

  • PIC16
  • ***
  • Mensajes: 160
Re:Assembler en ARM - Probando
« Respuesta #17 en: 11 de Octubre de 2015, 15:25:03 »
Que tal,

Lo probé, uso PSoC Creator como IDE, este usa ARM GCC 4.9-2015-q1 como toolchain y un PSoC con un M0, pero el IDE que uso no tiene opciones para medir el tiempo que va pasando con cada instrucción, seria cosa de checar cuantos ciclos toma cada instrucción, pero al menos funciona.

Tengo la opción de añadir un archivo de GNU ASM a la carpeta de source files, lo creo y me da un snippet:

Código: ASM
  1. .syntax unified
  2.     .text
  3.  
  4. /*
  5.     .global FunctionName
  6.     .func FunctionName, FunctionName
  7.     .thumb_func
  8. FunctionName:
  9.     BX lr
  10.     .endfunc
  11. */
  12.  
  13.     .end

Le añadi las instrucciones y termino asi:

Código: ASM
  1. .syntax unified
  2.     .text
  3.     .global _delay
  4.     .func _delay, _delay
  5.     .thumb_func
  6. _delay:
  7.     SUBS    r0, #1
  8.     NOP
  9.     NOP
  10.     NOP
  11.     NOP
  12.     NOP
  13.     NOP
  14.     NOP
  15.     NOP
  16.     NOP
  17.     NOP
  18.     NOP
  19.     BNE.N   _delay
  20.     BX lr
  21.     .endfunc
  22.  
  23.     .end

Y lo añadi al main, que asi quedó:
Código: C++
  1. #include <project.h>
  2.  
  3. extern void _delay(uint16_t us);
  4.  
  5. int main(){
  6.     CyGlobalIntEnable;
  7.  
  8.     _delay(2);
  9.    
  10.     for(;;){
  11.        
  12.     }
  13. }

Lo debuggeo sin optimizaciónes y en el disassembly se ve la llamada a la función:
Código: ASM
  1. 7:     _delay(2);
  2. 0x000001F6 2002     movs        r0, #2
  3. 0x000001F8 F000F802 bl  200 <_delay>

de ahi brinca a la etiqueta <_delay>, que tiene lo mismo que la funcion creada en el archivo asm:
Código: ASM
  1. 0x00000200 <_delay>:
  2.    2:     .text
  3.    3:     .global _delay
  4.    4:     .func _delay, _delay
  5.    5:     .thumb_func
  6.    6: _delay:
  7.    7:     SUBS    r0, #1
  8. 0x00000200 3801     subs        r0, #1
  9.    8:     NOP
  10. 0x00000202 46C0     nop                 ; (mov r8, r8)
  11.    9:     NOP
  12. 0x00000204 46C0     nop                 ; (mov r8, r8)
  13.   10:     NOP
  14. 0x00000206 46C0     nop                 ; (mov r8, r8)
  15.   11:     NOP
  16. 0x00000208 46C0     nop                 ; (mov r8, r8)
  17.   12:     NOP
  18. 0x0000020A 46C0     nop                 ; (mov r8, r8)
  19.   13:     NOP
  20. 0x0000020C 46C0     nop                 ; (mov r8, r8)
  21.   14:     NOP
  22. 0x0000020E 46C0     nop                 ; (mov r8, r8)
  23.   15:     NOP
  24. 0x00000210 46C0     nop                 ; (mov r8, r8)
  25.   16:     NOP
  26. 0x00000212 46C0     nop                 ; (mov r8, r8)
  27.   17:     NOP
  28. 0x00000214 46C0     nop                 ; (mov r8, r8)
  29.   18:     NOP
  30. 0x00000216 46C0     nop                 ; (mov r8, r8)
  31.   19:     BNE.N   _delay
  32. 0x00000218 D1F2     bne.n       200 <_delay>
  33.   20:     BX lr
  34. 0x0000021A 4770     bx  lr

Tiene una ventana para ver los registros y se ve el 2 en el r0 al iniciar la función y como disminuye en una unidad cada que hace el bne.n.

Por que escribí todo esto, no se xD.

Whirlwind Tour of ARM Assembly
http://www.coranac.com/tonc/text/asm.htm
« Última modificación: 11 de Octubre de 2015, 15:33:04 por Carl47D »

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Assembler en ARM - Probando
« Respuesta #18 en: 11 de Octubre de 2015, 17:45:05 »
Lo probaste en el hardware? o te permite simularlo paso a paso viendo los registros?
Sino lo que queda es debug y mirar el systick.

Citar
seria cosa de checar cuantos ciclos toma cada instrucción

El SUB solo ocupa 1 ciclo
El NOP = MOV Rx,Rx = ocupa 1 ciclo
El BNE = 1 + P
el BX = 1 + P

Siendo P:

Citar
The number of cycles required for a pipeline refill. This ranges from 1 to 3 depending on the alignment and width of the target instruction, and whether the processor manages to speculate the address early.

Asi que con todos los NOP, realmente no deberia existir problemas en la especulacion y tal ves considerarlo de 2 ciclos, pero eso no te asegura nada. No hay un tiempo exacto, o al menos todavia no se como calcularlo.
En el micro que tengo por ir a 120Mhz tiene como un "banco" para la flash, el cual va pidiendo instrucciones asi el nucleo no se queda parado esperando por las mismas, sino que solo busca, PERO los saltos son un dolor de cabeza y puede que el primer salto necesite hacer un pedido a la flash por X motivo ( aunque no deberia si es un Pseudo LRU ) Pero eso agrega mas complejidad a saber exactamente cuantos ciclos pasaron. Para un comportamiento deterministico debo de fijar ese banco, al menos en mi caso.

De todas formas para eso esta el SYSTICK, Aunque te pases nuevamente por unos cuantos ciclos es mas exacto que estar acumulando errores (y ocuparia menos instrucciones creo :P).
Solo notar 2 cosas:

.func .endfunc
Citar
.func emits debugging information to denote function name, and is ignored unless the file is assembled with debugging enabled.
Asi que podriamos decir que no es si o si necesario ponerlo

Y pense que el guion bajo de la funcion se iba. Pero no es asi.

Desconectado Carl47D

  • PIC16
  • ***
  • Mensajes: 160
Re:Assembler en ARM - Probando
« Respuesta #19 en: 11 de Octubre de 2015, 18:14:13 »
iba  a citar algo del link que deje  :D:
"it is so closely tied to the CPU, you can make it do everything; but that also means you have to do everything. Being close to hardware also means you're bypassing all the safety features that higher languages may have, so that it's much easier to break things. So yeah, it is harder and more dangerous. Although some may prefer the term ‘adventurous’. "

Si, lo grabé en el micro y puedo ver los registros a cada paso que da el disassembly, el codigo lo saque de una funcion para generar delays de microsegundos en un micro que corre a 16 MHz; cada ciclo es de 62.5 nanosegundos, para hacer el delay de 1 microsegundo se necesitan 16 ciclos:

SUB (1 ciclo) + NOP (11 ciclos en total) + BNE (2 ciclos) + BX (2 ciclos) = 16

Y lo del systick, no habia tenido la necesidad de ver los registros 'en vivo', supongo solo los puedo ver en la memoria, viendo la dirección del SysTick

.func .endfunc no habia buscado para que servian por eso los dejé, el guion bajo es solo parte del nombre de la función.

PD: si digo alguna barbaridad corrijanme xD.

« Última modificación: 11 de Octubre de 2015, 18:17:15 por Carl47D »

Desconectado elgarbe

  • Moderadores
  • PIC24H
  • *****
  • Mensajes: 2178
Re:Assembler en ARM - Probando
« Respuesta #20 en: 11 de Octubre de 2015, 19:21:28 »
De todas formas para eso esta el SYSTICK, Aunque te pases nuevamente por unos cuantos ciclos es mas exacto que estar acumulando errores (y ocuparia menos instrucciones creo :P).

para contar ciclos de instruccion que van transcurriendo se usa el DWT (si el micro lo trae)

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0337h/BIIFBHIF.html

tenía un programa que contaba cuantos ciclos tardaba en hacer la fft un micro que estaba usando, pero no lo encuentro...

sds.

EDITO: Aquí una explicacion para un M3 http://www.microbuilder.eu/Projects/LPC1343ReferenceDesign/DWTBenchmarking.aspx
« Última modificación: 11 de Octubre de 2015, 19:23:50 por elgarbe »
-
Leonardo Garberoglio

Desconectado Carl47D

  • PIC16
  • ***
  • Mensajes: 160
Re:Assembler en ARM - Probando
« Respuesta #21 en: 11 de Octubre de 2015, 21:05:29 »
Citar
La idea de Juanjo es mejor! empezar por el principio siempre es buena idea!  :lol:
+1

Traté de contar los ciclos con el SysTick, buscaré si el micro que tengo tiene el DWT que según esto deberia tener pero no encuentro nada en el reference manual, en total fueron 58, casi todas las instrucciones tomarón 4 ciclos, Es por el fetch/decode/execute de las instrucciones?

Como dijo Killer los ciclos no son algo seguro:
Citar
The cycle counts are based on a system with zero wait-states.

Y bue, quedo a la espera del comienzo formal del 'tuto'

Mas info:
The Effect of the ARM Cortex-M NOP Instruction
« Última modificación: 11 de Octubre de 2015, 21:11:29 por Carl47D »

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Assembler en ARM - Probando
« Respuesta #22 en: 11 de Octubre de 2015, 21:29:48 »
Citar
Y bue, quedo a la espera del comienzo formal del 'tuto'

Me ponen entre la espada y la pared xD, lo mas lindo es que yo soy tan nuevo como todos ustedes en ASM de ARM. Asi que si.. estoy un poco (mucho) perdido.

Ahh y elgarbe hice el codigo que pedias, pero no me gusto :P lo veo muy largo xD
En algo le debo estar errando xD

Desconectado elgarbe

  • Moderadores
  • PIC24H
  • *****
  • Mensajes: 2178
Re:Assembler en ARM - Probando
« Respuesta #23 en: 11 de Octubre de 2015, 23:18:45 »
Citar
Y bue, quedo a la espera del comienzo formal del 'tuto'

Me ponen entre la espada y la pared xD, lo mas lindo es que yo soy tan nuevo como todos ustedes en ASM de ARM. Asi que si.. estoy un poco (mucho) perdido.

Ahh y elgarbe hice el codigo que pedias, pero no me gusto :P lo veo muy largo xD
En algo le debo estar errando xD

La diferencia es que vos ya estas familiarizado con los conceptos de ASM y nosotros ya nos olvidamos de todo. Modos de direccionamiento, set de instrucciones, etiquetas (.text, .func, etc). Pero bueno, tambien teens que ver que sea algo útil para futuros trabajos tuyos.

En cuanto a la funcion que hablamos, eso es si no tenes otro ejemplo, no pierdas tiempo en eso. Como bien sugeriste, seguramente la implementacion final será por SPI...

Saludos y gracias!
-
Leonardo Garberoglio

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Assembler en ARM - Probando
« Respuesta #24 en: 12 de Octubre de 2015, 00:35:26 »
Yo comenze estudiando ejemplos de C pasados a ASM.. Y obtuve distintos resultados:

Cree el siguiente codigo en C

Código: C++
  1. /*
  2.  * main.c
  3.  */
  4. #include <stdint.h>
  5. #include "inc/tm4c1294ncpdt.h"
  6.  
  7. uint32_t suma(uint32_t a,uint32_t b);
  8.  
  9.  
  10. int
  11. main(void)
  12. {
  13.     volatile uint32_t ui32Loop;
  14.     uint32_t c;
  15.     //
  16.     // Alimentamos el puerto
  17.     //
  18.     SYSCTL_RCGCGPIO_R = SYSCTL_RCGCGPIO_R12;
  19.  
  20.     //
  21.     // Una pequeña lectura , asi esperamos mientras se habilita
  22.     //
  23.     ui32Loop = SYSCTL_RCGCGPIO_R;
  24.  
  25.     //
  26.     // Habilitamos como digital ( Digital ENable )
  27.     // Y como salida (DIReccion ) del PORTN
  28.     //
  29.     GPIO_PORTN_DIR_R = 0x01;
  30.     GPIO_PORTN_DEN_R = 0x01;
  31.  
  32.     //
  33.     // Loop
  34.     //
  35.     while(1)
  36.     {
  37.         //
  38.         // Encendemos el LED, No hace uso de la memoria, para seleccionar el bit en especial
  39.         //
  40.         GPIO_PORTN_DATA_R |= 0x01;
  41.  
  42.         //
  43.         // Un hermoso delay
  44.         //
  45.         for(ui32Loop = 0; ui32Loop < 200000; ui32Loop++)
  46.         {
  47.         }
  48.  
  49.         //
  50.         // Apagamos el led
  51.         //
  52.         GPIO_PORTN_DATA_R &= ~(0x01);
  53.  
  54.         //
  55.         // Otro hermoso delay
  56.         //
  57.         for(ui32Loop = 0; ui32Loop < 200000; ui32Loop++)
  58.         {
  59.         }
  60.  
  61.         //
  62.         // Mi suma y uso c solo para usarlo y no genere un Warning diciendo que se seteo pero no se uso
  63.         //
  64.  
  65.         c=suma(0x12345678,0x87654321);
  66.         if(!c) {c=0;}
  67.     }
  68. }

Lo unico que hace es,
- Activar el modulo
- Configurar el pin, Digital y Salida
- Escribirlo y un delay super basico

Mirando el codigo ASM, me encuentro con.... ESTO:

GCC , No optimizado.

Direccion - opcode - instruccion - comentario

Código: ASM
  1. 000002d0 <main>:
  2.  2d0:   b580            push    {r7, lr}        ; Guardo lr y r7 en el stack
  3.  2d2:   b082            sub     sp, #8          ; Resto Stack Pointer para hacer lugar a 64 bits (32bits*2 = 4bytes * 2)
  4.  2d4:   af00            add     r7, sp, #0      ; R7 = Stack Pointer , R7 es mi Frame Pointer
  5.  2d6:   4b1c            ldr     r3, [pc, #112]  ; (0x348) R3 = &SYSCTL_RCGCGPIO
  6.  2d8:   f44f 5280       mov.w   r2, #4096       ; R2 = 0x1000 (SYSCTL_RCGCGPIO_R12) , MOV a 32bits
  7.  2dc:   601a            str     r2, [r3, #0]    ; (R3)SYSCTL_RCGCGPIO = SYSCTL_RCGCGPIO_R12
  8.  2de:   4b1a            ldr     r3, [pc, #104]  ; (348) Carga nuevamente la direccion en R3
  9.  2e0:   681b            ldr     r3, [r3, #0]    ; R3 = SYSCTL_RCGCGPIO
  10.  2e2:   603b            str     r3, [r7, #0]    ; (R7+0) ui32Loop = (R3)SYSCTL_RCGCGPIO
  11.  2e4:   4b19            ldr     r3, [pc, #100]  ; (34c), R3 = &GPIO_PORTN_DIR
  12.  2e6:   2201            movs    r2, #1          ; R2=1, Actualizar las banderas
  13.  2e8:   601a            str     r2, [r3, #0]    ; (R2)GPIO_PORTN_DIR= (R2)1
  14.  2ea:   4b19            ldr     r3, [pc, #100]  ; (350) R3 = &GPIO_PORTN_DEN
  15.  2ec:   2201            movs    r2, #1          ; R2=1
  16.  2ee:   601a            str     r2, [r3, #0]    ; (R3)GPIO_PORTN_DEN = 1
  17.                                                 ; ------------- WHILE --------------
  18.  2f0:   4b18            ldr     r3, [pc, #96]   ; (354) R3 = &GPIO_PORTN_DATA
  19.  2f2:   4a18            ldr     r2, [pc, #96]   ; (354) R2 = &GPIO_PORTN_DATA
  20.  2f4:   6812            ldr     r2, [r2, #0]    ; R2 = GPIO_PORTN_DATA(R3)
  21.  2f6:   f042 0201       orr.w   r2, r2, #1      ; R2 = R2 or 0x1
  22.  2fa:   601a            str     r2, [r3, #0]    ; (R3)GPIO_PORTN_DATA = R2
  23.  2fc:   2300            movs    r3, #0          ; ---------- FOR --------------
  24.  2fe:   603b            str     r3, [r7, #0]    ; ui32Loop = 0
  25.  300:   e002            b.n     308 <main+0x38> ; Branch near 0x308
  26.  302:   683b            ldr     r3, [r7, #0]    ; R3 = ui32Loops
  27.  304:   3301            adds    r3, #1          ; uiLoops += 1
  28.  306:   603b            str     r3, [r7, #0]    ; Guardo
  29.  308:   683a            ldr     r2, [r7, #0]    ; R2 = ui32Loop
  30.  30a:   4b13            ldr     r3, [pc, #76]   ; (358) R3 = NumLoops
  31.  30c:   429a            cmp     r2, r3          ; R2 == R3 ?
  32.  30e:   d9f8            bls.n   302 <main+0x32> ; Branch lower or same R2<R3 -> 0x302
  33.                                                 ; ------------- Fin FOR ------------
  34.  310:   4b10            ldr     r3, [pc, #64]   ; (354)
  35.  312:   4a10            ldr     r2, [pc, #64]   ; (354)
  36.  314:   6812            ldr     r2, [r2, #0]    ; R2 = GPIO_PORTN_DATA
  37.  316:   f022 0201       bic.w   r2, r2, #1      ; Bit Clear (AND NOT), R2 = R2 (and not) 1
  38.  31a:   601a            str     r2, [r3, #0]    ; GPIO_PORTN_DATA = R2
  39.  31c:   2300            movs    r3, #0          ; ui32Loop = 0
  40.  31e:   603b            str     r3, [r7, #0]    ; ----------- FOR ---------------
  41.  320:   e002            b.n     328 <main+0x58> ;     igual que el anterior
  42.  322:   683b            ldr     r3, [r7, #0]
  43.  324:   3301            adds    r3, #1
  44.  326:   603b            str     r3, [r7, #0]
  45.  328:   683a            ldr     r2, [r7, #0]
  46.  32a:   4b0b            ldr     r3, [pc, #44]   ; (358)
  47.  32c:   429a            cmp     r2, r3
  48.  32e:   d9f8            bls.n   322 <main+0x52> ; ------- Fin de FOR -------------
  49.  330:   480a            ldr     r0, [pc, #40]   ; (35c) R0 = Dato1
  50.  332:   490b            ldr     r1, [pc, #44]   ; (360) R1 = Dato2
  51.  334:   f000 f816       bl      364 <suma>      ; Salto y 0x338 -> LR
  52.  338:   6078            str     r0, [r7, #4]    ; c = R0
  53.  33a:   687b            ldr     r3, [r7, #4]    ; R3 = c
  54.  33c:   2b00            cmp     r3, #0          ; c==0?
  55.  33e:   d102            bne.n   346 <main+0x76> ; NO -> 0x346
  56.  340:   2300            movs    r3, #0          ; R3=0
  57.  342:   607b            str     r3, [r7, #4]    ; c=0
  58.  344:   e7d4            b.n     2f0 <main+0x20> ; Salto a 0x2F0
  59.  346:   e7d3            b.n     2f0 <main+0x20>
  60.  348:   400fe608        .word   0x400fe608      ; SYSCTL_RCGCGPIO , Registros
  61.  34c:   40064400        .word   0x40064400      ; GPIO_PORTN_DIR
  62.  350:   4006451c        .word   0x4006451c      ; GPIO_PORTN_DEN
  63.  354:   400643fc        .word   0x400643fc      ; GPIO_PORTN_DATA
  64.  358:   00030d3f        .word   0x00030d3f      ; NumLoops 199.999, no 200.000 , variable
  65.  35c:   12345678        .word   0x12345678      ; Dato 1 pasado a Suma , argumentos
  66.  360:   87654321        .word   0x87654321      ; Dato 2 pasado a Suma
  67.  
  68. 00000364 <suma>:
  69.  364:   b480            push    {r7}            ; Guardo Frame Pointer
  70.  366:   b085            sub     sp, #20         ; Hago lugar en el stack para 20! bytes WTF!
  71.  368:   af00            add     r7, sp, #0      ; R7 = SP , Frame Pointer = Stack Pointer
  72.  36a:   6078            str     r0, [r7, #4]    ; Variables locales, guardo a
  73.  36c:   6039            str     r1, [r7, #0]    ; guardo b , ambo argumentos
  74.  36e:   687a            ldr     r2, [r7, #4]    ; R2 = a
  75.  370:   683b            ldr     r3, [r7, #0]    ; R3 = b
  76.  372:   4413            add     r3, r2          ; R3 = a + b
  77.  374:   60fb            str     r3, [r7, #12]   ; c = R3, c = a + b , como que se salte algo
  78.  376:   68fb            ldr     r3, [r7, #12]   ; R3 = c
  79.  378:   4618            mov     r0, r3          ; R0 = R3
  80.  37a:   3714            adds    r7, #20         ; R7 = R7 + 20 , Vuelvo el Stack pointer a su lugar
  81.  37c:   46bd            mov     sp, r7          ; SP = R7
  82.  37e:   f85d 7b04       ldr.w   r7, [sp], #4    ;Equivalente a POP R7
  83.  382:   4770            bx      lr              ; PC = LR(R14) and 0xFFFF.FFFE

Para notar es que es un DESASTRE!, se puede observar la cantidad de veces que se asigna una y otra ves las variables a los registros
Esta todo explicado en los comentarios Mi funcion suma es aun peor.

Luego para ver que TAN buena es la optimizacion, corri todas, desde -O1 a -O3, sin cambios aparentes, lo unico que note cambio fue cuando elegi que se priorizara la velocidad. Y no un cambio de tamaño ya que eran similares, sino, fue un cambio en la forma de tomar los datos, se eligio hacerlo todo al comienzo y luego trabajar con los registros.

GCC -O1 -Ofast

Código: ASM
  1. 00000404 <main>:
  2.  404:   b430            push    {r4, r5}
  3.  406:   4b16            ldr     r3, [pc, #88]   ; (460) SYSCTL_RCGCGPIO
  4.  408:   4816            ldr     r0, [pc, #88]   ; (464) GPIO_PORTN_DIR
  5.  40a:   4c17            ldr     r4, [pc, #92]   ; (468) GPIO_PORTN_DEN
  6.  40c:   4917            ldr     r1, [pc, #92]   ; (46c) GPIO_PORTN_DATA
  7.  40e:   4a18            ldr     r2, [pc, #96]   ; (470) 199.999
  8.  410:   f44f 5580       mov.w   r5, #4096       ; 0x1000 , usa thumb2 para tener mas rango en el inmmediato
  9.  414:   601d            str     r5, [r3, #0]    ; SYSCTL_RCGCGPIO = 0x1000
  10.  416:   b082            sub     sp, #8          ; Lugar para 8 bytes (32-32 bits las 2 variables ) en el stack
  11.  418:   681b            ldr     r3, [r3, #0]    ; R3 = SYSCTL_RCGCGPIO
  12.  41a:   9301            str     r3, [sp, #4]    ; ui32Loop = R3 , ui23Loop SP+4 y c SP+0
  13.  41c:   2301            movs    r3, #1          ; R3=1 , actualizo flags
  14.  41e:   6003            str     r3, [r0, #0]    ; GPIO_PORTN_DIR = R3
  15.  420:   2000            movs    r0, #0          ; R0=0   (ya que no uso mas la direccion de ese registro)
  16.  422:   6023            str     r3, [r4, #0]    ; GPIO_PORTN_DEN = R3
  17.  424:   680b            ldr     r3, [r1, #0]    ; R3 = GPIO_PORTN_DATA
  18.  426:   f043 0301       orr.w   r3, r3, #1      ; R3 = R3 | 0x1
  19.  42a:   600b            str     r3, [r1, #0]    ; GPIO_PORTN_DATA = R3
  20.                                                 ; -------- FOR ------------
  21.  42c:   9001            str     r0, [sp, #4]    ; ui32Loop = 0 , ( stack en memoria )
  22.  42e:   9b01            ldr     r3, [sp, #4]    ; R3 = ui32Loop
  23.  430:   4293            cmp     r3, r2          ; ui32Loop == 199999?
  24.  432:   d805            bhi.n   440 <main+0x3c> ; Salta si es mayor a 0x440
  25.  434:   9b01            ldr     r3, [sp, #4]    ; R3 = ui32Loop   ---   Esto requiere 2 instrucciones pero por lo "volatile"
  26.  436:   3301            adds    r3, #1          ; R3 ++
  27.  438:   9301            str     r3, [sp, #4]    ; ui32Loop = R3
  28.  43a:   9b01            ldr     r3, [sp, #4]    ; R3 = ui32Loop
  29.  43c:   4293            cmp     r3, r2          ; ui32Loop == 1999999?
  30.  43e:   d9f9            bls.n   434 <main+0x30> ; Salta si es menor a 0x434
  31.                                                 ; -------- Fin FOR ----------
  32.  440:   680b            ldr     r3, [r1, #0]    ; R3 = GPIO_PORTN_DATA
  33.  442:   f023 0301       bic.w   r3, r3, #1      ; R3 = R3 & ~(0x1) , Solo Thumb2
  34.  446:   600b            str     r3, [r1, #0]    ; GPIO_PORTN_DATA = R3
  35.                                                 ; --------- FOR ------------
  36.  448:   9001            str     r0, [sp, #4]    ; ui32Loop = R0 = 0 ???
  37.  44a:   9b01            ldr     r3, [sp, #4]    ; R3 = ui32Loop
  38.  44c:   4293            cmp     r3, r2
  39.  44e:   d8e9            bhi.n   424 <main+0x20>
  40.  450:   9b01            ldr     r3, [sp, #4]
  41.  452:   3301            adds    r3, #1
  42.  454:   9301            str     r3, [sp, #4]
  43.  456:   9b01            ldr     r3, [sp, #4]
  44.  458:   4293            cmp     r3, r2
  45.  45a:   d9f9            bls.n   450 <main+0x4c>
  46.                                                 ; --------- Fin FOR -------
  47.  45c:   e7e2            b.n     424 <main+0x20> ; Vuelta al comienzo, while
  48.  45e:   bf00            nop                     ; NOP de NOP, de 16bits para que lo que sigue este alineado a 32bits
  49.  460:   400fe608        .word   0x400fe608      ; DATOOOOOOSSS
  50.  464:   40064400        .word   0x40064400
  51.  468:   4006451c        .word   0x4006451c
  52.  46c:   400643fc        .word   0x400643fc
  53.  470:   00030d3f        .word   0x00030d3f
  54.  
  55. 0000033c <suma>:                                ; Suma que nadie lo uso, se puede ver que ahora lo hizo bien
  56.  33c:   4408            add     r0, r1          ; R0 = R0 + R1 , y devuelve R0 con el resultado
  57.  33e:   4770            bx      lr              ; Vuelve

Se puede ver que usa mas registros tales como R4 y R5, Lo cual le permite unas cositas mejor. El tema es......... optimizaciones, alineacion, carga seguida de memoria.

Primero notar que con las instrucciones de ASM uno no puede cargar 32bits ahi nomas, es tan asi que Thumb admite solo 8 bits, y Thumb2 11 y 15 bits en sus 2 codificaciones
Asi que lo que se hace es poner los datos como ven ahi al final.
Otra cosa a notar es que no todas las instrucciones son de 16bits, ven que algunas son de 32 y estan metidas entre medio. Es decir hay una hermosa mezcla.
Tambien ese hermoso NOP que puso para poder alinear la memoria, antes de ubicar los datos.
Finalmente la optimizacion quito mi funcion de Suma, SI la hizo correcta, pero la quito por completo, no hay ningun salto a Suma, y se puede ver que en una optimizacion de velocidad, prefiere cargar todo junto.

Eso ultimo es por un tema del pipeline, si yo cargara y luego usara una instruccion que usara ese dato, me encuentro con un problema, ya que mientras ingreso la carga (que normalmente usa 2 ciclos) y todavia no escribi mi registro, tengo la otra instruccion pidiendome los datos, por lo cual lo detiene tal ves un ciclo mas asi puede usar el dato. Haciendo esto no, ya que toma datos y luego procura que los que tomo al comienzo sean los primeros en usarse. Solo tome estos codigos para analizarlos y verlos.

Por que guarda R4 y R5 antes ? Esto es por que para C, el main es una sub-rutina, hay un programa mayor que hace una llamada al main, por eso mismo se comporta de esa forma. Entonces al usar R4 y R5 debe guardarlos y deberia de hacer un POP de esos al salir, pero.... no sale nunca gracias al while.
« Última modificación: 12 de Octubre de 2015, 05:56:30 por KILLERJC »

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Assembler en ARM - Probando
« Respuesta #25 en: 12 de Octubre de 2015, 01:08:16 »
Con respecto a las instrucciones son bastantes especiales.

Ejemplo la instruccion de suma, con un literal

Código: [Seleccionar]
T1 : ADD<c> <Rd>,<Rn>,#<imm3>
        ADDS <Rd>,<Rn>,#<imm3>

T2 : ADD<c> <Rdn>,#<imm8>
        ADDS <Rdn>,#<imm8>

T3: ADD{S}<c>.W <Rd>,<Rn>,#<const>

T4: ADDW<c> <Rd>,<Rn>,#<imm12>

Todos son validos, los 2 primeros encoding T1 y T2 son de 16bits y los otros 2 son de 32bits , los de 32 bits permiten inmediatos de hasta 12bits,
(Con encoding me refiero a su opcode, 4 opcode por cada "instruccion" como si 1 no fuera demasiado :P)

Lo cual permite hacer:

ADD  R0,R1,#7 ; T1 -> R0 = R1 + 7    (distintos registros)
ADD  R0,#0xFF; T2 -> R0 = R0 + 255  (mismos registro)

Eso no actualiza las banderas Z,C,etc, solo cuando uno lo pide, y eso se hace con la S

ADDS R0,#0x10 ; T2 -> R0 = R0 + 16 y actualizo flags

Veran tambien que existe una <c>. Bueno esto es la condicion, para que se ejecute la instruccion. Solo algunas instrucciones pueden hacer uso de estas condiciones por si solas, tales como la instruccion B<c> (branch), como BNE (Branch Not Equal) BCS (Branch Carry Set ), etc, las demas como ADD no pueden condicionar su ejeccucion por si solas.
A lo que uno pregunta, si existe: ¿Para que sirve?

Thumb tiene una instruccion llamada IT ( if-Then ) que permite la ejecucion condicional de 4 instrucciones siguentes. Ejemplo:

ITTEE  CS

Con eso digo que If-Then Then Else Else  (condicion Carry Set), la primera instruccion esta condicionada por lo que vaya donde pusimos el CS ( en el ejemplo CS), las demas responden a lo que esta despues de IT
Es decir las 4 instrucciones van a ser:
IT TEE CS

If Then CS: 1era intruccion
Then CS: 2da intruccion
Else CC: 3ra instruccion 
Else CC: 4ra instruccion

Un ejemplo de esto en C:

Código: C++
  1. if ( ++X == 10)
  2. {
  3.   b++;
  4. }
  5. else
  6. {
  7.   b--
  8. }

Se imaginan el caos en el pipeline por eso no,2 saltos seguro, vaciar el pipeline, etc , especialmente al modificarse X antes. Pero podemos hacer esto:

Código: [Seleccionar]
ADD R0,#1 ;Aumento en 1 X
CMP R0,#0x10 ;R0==10?
ITE EQ                           ; Esto no se ejecuta, solo es indicativo para lo siguiente, pero ocupa 16bits
ADDEQ R1,#1 ;Caso If Equal Then
SUBNE R1,#1 ;Caso Else o Not Equal

Eso hace que no tome saltos sino que siga su camino pero que no lo ejecute, al principio en el encoding puse separado el ADD{S} del ADD<c>, ya que no podria actualizar las banderas mientras esta condicionalmente asi. Tamibien se puede notar que las instrucciones DEBEN tener el condicional correcto que se le da, Si se pide Mayor a, el contrario es menor o igual.
Hay algunas instrucciones que no pueden formar parte del IT y que generalmente son saltos, o que deben estar ubicados al final.

No se exactamente como se comportara con instrucciones de 16bit y 32bits mezcladas el IT, pero bueno, esto es hasta lo que vi por ahora.
« Última modificación: 12 de Octubre de 2015, 01:14:57 por KILLERJC »

Desconectado juaperser1

  • Colaborador
  • DsPIC30
  • *****
  • Mensajes: 2980
Re:Assembler en ARM - Probando
« Respuesta #26 en: 12 de Octubre de 2015, 07:30:00 »
Citar
Me ponen entre la espada y la pared xD, lo mas lindo es que yo soy tan nuevo como todos ustedes en ASM de ARM. Asi que si.. estoy un poco (mucho) perdido.

chicos, dejad de agobiar a killerjc, vamos a dejarlo que  aprenda y luego lo agobiamos todos a la vez ¿vale? :D :D

Citar
La diferencia es que vos ya estas familiarizado con los conceptos de ASM y nosotros ya nos olvidamos de todo. Modos de direccionamiento, set de instrucciones, etiquetas (.text, .func, etc). Pero bueno, tambien teens que ver que sea algo útil para futuros trabajos tuyos.
+1

yo en C todo lo que quieras, pero mi ensamblador se quedo en el motorola 68000 que di en la uni y en algún programa chorra que hice con un pic16, es mas, la imagen que colgué la saque de los apuntes del motorola  :D :D por si queréis verlos:

https://www.dropbox.com/sh/e57jjuynpuagjuv/AAAFt30ae3rZe5m9gbD5bJ_Ga?dl=0

yo particularmente, siendo sincero, en este foro no me entero mas que del 25% tirando alto de lo que estáis hablando, así que hasta que no me ponga al dia no podre participar mucho :(

un saludo.


« Última modificación: 12 de Octubre de 2015, 07:50:12 por juaperser1 »

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Assembler en ARM - Probando
« Respuesta #27 en: 12 de Octubre de 2015, 08:05:41 »
Creo que termine la rutina de elgarbe, en si no era tan larga, probe hacerla en C y luego pasarla por el desamblador y me dio el doble de codigo :P

Código: ASM
  1. .syntax unified
  2.         .thumb
  3.         .text
  4.         .global Funcion
  5.  
  6.  
  7. Funcion:
  8.         PUSH    {R4,R5,R6}              ;@ Por las dudas, no pierdo nada xD
  9.         LDR     R0, =0x5FFFFFE0         ;@ Cargo R0 = MCL Invertido bit a bit
  10.         MOVS    R1, #0x25               ;@ Cargo R1 = MCH Invertido bit a bit
  11.         MOVS    R2, #8                  ;@ Cargo R2 = j
  12.         MOVS    R4, #0x00               ;@ Bit a cambiar datos
  13.         MOVS    R5, #0x00               ;@ Bit a cambiar Clock
  14.         LDR     R3, =0x1234567          ;@ Cargo direccion de registro R3 = &PUERTO
  15.         LDR     R6, [R3, #0]            ;@ Valor del Puerto R6=PUERTO
  16. 1:      RRXS    R1, R1                  ;@ Roto 1 derecha a traves del carry
  17.         ITE     CS                      ;@ Envio de la parte alta
  18.         ORRCS.N R6, R4                  ;@ Carry Set, pongo un uno (OR)
  19.         BICCC.N R6, R4                  ;@ Carry Clear, pongo un 0 (AND NOT)
  20.         STR     R6, [R3, #0]            ;@ Aplico cambios al puerto -datos-
  21.         ORRS    R6, R6, R5              ;@ A 1 -clock-
  22.         STR     R6, [R3, #0]            ;@ Aplico cambios al puerto -clock-
  23.         BICS    R6, R5                  ;@ A 0 -clock-
  24.         STR     R6, [R3, #0]            ;@ Aplico cambios al puerto -clock-
  25.         SUBS    R2, #0x1                ;@ Resto aca para que la prediccion de salto sea mas facil
  26.         BNE     1b
  27.         MOVS    R2, #31
  28.         MOVS    R1, #0x00               ;@ Bit de latch, cargo aca asi es mas rapido
  29. 1:      RRXS    R0, R0                  ;@ Envio de la parte baja
  30.         ITE     CS                     
  31.         ORRCS.N R6, R4
  32.         BICCC.N R6, R4
  33.         STR     R6, [R3, #0]            ;@ Aplico cambios al puerto -datos-
  34.         ORRS    R6, R5                  ;@ A 1 -clock-
  35.         STR     R6, [R3, #0]            ;@ Aplico cambios al puerto -clock-
  36.         BICS    R6, R5                  ;@ A 0 -clock-
  37.         STR     R6, [R3, #0]            ;@ Aplico cambios al puerto -clock-
  38.         SUBS    R2, #0x1
  39.         BNE     1b
  40.         ORRS    R6, R1
  41.         STR     R6, [R3, #0]            ;@ Aplico cambios al puerto -latch-
  42.         BICS    R6, R1
  43.         STR     R6, [R3, #0]            ;@ Aplico cambios al puerto -latch-
  44.         POP     {R4,R5,R6}
  45.         BX      LR
  46.         .end
  47.  
  48.  
  49. @       MOV     R0, =0x07FFFFFA         ;@ Cargo R0 = MCL
  50. @       MOV     R1, =0x148              ;@ Cargo R1 = MCH
  51.  
  52. @ R0 Normal:    0000 0111 1111 1111 1111 1111 1111 1010
  53. @ R0 Inverso:   0101 1111 1111 1111 1111 1111 1110 0000
  54.  
  55. @ R0 Inverso: 5FFF.FFE0
  56.  
  57. @ R1 Normal:    1 0100 1000
  58. @ R1 Inverso:   0 0010 0101
  59.  
  60. @ 0x25
  61. @ 0 0010 0101

Inverti los valores a enviar, asi enviaba desde el bit mas alto al mas bajo, lo hice en 2 partes. Primero el alto y luego el bajo. Los valores sin "tocar" son los que dice R0 Normal y R1 Normal.
Eso era mas facil que ir haciendoles AND y preguntando si era 0 o no.

Y para asegurarme que todo fuera correcto le hice un disassembler al object generado
(Lo hago por que a veces ocurre que si no esta alineado los datos al final son cualquier cosa menos datos)

Código: ASM
  1. 00000000 <Funcion>:
  2.    0:   b470            push    {r4, r5, r6}
  3.    2:   4813            ldr     r0, [pc, #76]   ; (50 <Funcion+0x50>)
  4.    4:   2125            movs    r1, #37 ; 0x25
  5.    6:   2208            movs    r2, #8
  6.    8:   2400            movs    r4, #0
  7.    a:   2500            movs    r5, #0
  8.    c:   4b11            ldr     r3, [pc, #68]   ; (54 <Funcion+0x54>)
  9.    e:   681e            ldr     r6, [r3, #0]
  10.   10:   ea5f 0131       movs.w  r1, r1, rrx
  11.   14:   bf2c            ite     cs
  12.   16:   4326            orrcs   r6, r4
  13.   18:   43a6            biccc   r6, r4
  14.   1a:   601e            str     r6, [r3, #0]
  15.   1c:   432e            orrs    r6, r5
  16.   1e:   601e            str     r6, [r3, #0]
  17.   20:   43ae            bics    r6, r5
  18.   22:   601e            str     r6, [r3, #0]
  19.   24:   3a01            subs    r2, #1
  20.   26:   d1f3            bne.n   10 <Funcion+0x10>
  21.   28:   221f            movs    r2, #31
  22.   2a:   2100            movs    r1, #0
  23.   2c:   ea5f 0030       movs.w  r0, r0, rrx
  24.   30:   bf2c            ite     cs
  25.   32:   4326            orrcs   r6, r4
  26.   34:   43a6            biccc   r6, r4
  27.   36:   601e            str     r6, [r3, #0]
  28.   38:   432e            orrs    r6, r5
  29.   3a:   601e            str     r6, [r3, #0]
  30.   3c:   43ae            bics    r6, r5
  31.   3e:   601e            str     r6, [r3, #0]
  32.   40:   3a01            subs    r2, #1
  33.   42:   d1f3            bne.n   2c <Funcion+0x2c>
  34.   44:   430e            orrs    r6, r1
  35.   46:   601e            str     r6, [r3, #0]
  36.   48:   438e            bics    r6, r1
  37.   4a:   601e            str     r6, [r3, #0]
  38.   4c:   bc70            pop     {r4, r5, r6}
  39.   4e:   4770            bx      lr
  40.   50:   5fffffe0        .word   0x5fffffe0
  41.   54:   01234567        .word   0x01234567

Esto fue "gracioso" me comi MUCHISIMAS veces el load y store, solo hacia OR y AND nomas xD. Una cosa buena es como ven ahi es que si no entra el dato lo agrega solo al final y te calcula solo el offset.

Por otra parte renegue mucho con el tema de las instrucciones, cuadno hacia un:

mov   r2, #31
Me encontraba con que me lo codificaba en 32bits, cuando esta claro que no pasa de 8 bits, intente ponerle un .N a la funcion MOV.N para forzarla a 16bits y seguia con errores, mirando el datasheet con los encoding veo:

Encoding T1

MOVS <Rd>,#<imm8>       -------------         Outside IT block.
MOV<c> <Rd>,#<imm8>   -------------       Inside IT block.

Asi es .. le faltaba la maldita S, no era "opcional" para 16bits, le puse eso y salimos andando xD, ocupando un total de 0x58

Otra cosa que aprendi, se puede poner ;@ asi si uno lo usa con otro compilador ( como el de ASM ) el mismo ; es valido, y usando el de GCC lo toma como comentario a esos 2 juntos tambien. Solo 2 instrucciones de 32bits cada una, que es una rotacion extendida por el carry.

EDIT: Otra mas que aprendi:

PUSH   {R4,R5,R6}

No podes ponerlo fuera de orden, siempre crecientes, esta mas que decir que R0-R3 se usan para pasar los datos, asi que los unicos que deberian preocuparse por esos valores si es que los usan es el que llama. Y no el llamado

Tambien le agregue un nop al final para que quede desalineado y asi ver que ocurria con el ASM:

Código: ASM
  1. 4e:     4770            bx      lr
  2.   50:   46c0            nop                     ; (mov r8, r8)
  3.   52:   0000            .short  0x0000
  4.   54:   5fffffe0        .word   0x5fffffe0
  5.   58:   01234567        .word   0x01234567

El mismo ASM me lo arregla poniendo un dato cualquiera. Si yo hubiera escrito esos .word por mi mismo, y no estuvieran alineados veria cualquier cosa. esta ves tambien le agrego al final del NOP, un .word 0x32324545

quedando:
Código: ASM
  1. BX      LR
  2.         NOP
  3.         .word 0x32324545
Y el disASM:
Código: ASM
  1. 4e:     4770            bx      lr
  2.   50:   46c0            nop                     ; (mov r8, r8)
  3.   52:   4545            .short  0x4545
  4.   54:   00003232        .word   0x00003232
  5.   58:   5fffffe0        .word   0x5fffffe0

Ahi se puede ver lo que ocurre cuando esta desalineado :P


PD: No se si el codigo funciona, segun mi logica si, pero seguro que en algo falle :P
« Última modificación: 12 de Octubre de 2015, 08:28:13 por KILLERJC »

Desconectado elgarbe

  • Moderadores
  • PIC24H
  • *****
  • Mensajes: 2178
Re:Assembler en ARM - Probando
« Respuesta #28 en: 12 de Octubre de 2015, 10:14:48 »

yo particularmente, siendo sincero, en este foro no me entero mas que del 25% tirando alto de lo que estáis hablando, así que hasta que no me ponga al dia no podre participar mucho :(

un saludo.

yo no sé si llego al 25% jajaja. Me parece que estamos medio lejos del ASM para ARM.
Quizás para aprender sea bueno hacer un mix, de cosas básicas, teóricas y algo de funciones simples que sirvan para algo concreto, pero bien simples.
Por ejemplo, debido al error de hard que tengo en el cartel, de los primeros 49 bits que envío a los TLC van "al derecho", pero los segundos 49 bits que tengo que enviar van "al revez". Despues voy a poner en el post del cartel como estoy pensando avanzar con el software y que cosas simples se podrían intentar en ASM. de ese modo puedo ir haciendo algo simple como para ir tomando contacto y por otra parte lo puedo testear en el micro. quizá se debería arrancar con ASM con cosas que tengan que ver con manejo de datos en memoria.
Algo que creo está faltando es el formato genera de un archivo .s para que lo pueda meter en un pryecto y compile. Explicando las partes, etiquetas, etc. Quizá explicar el startup_xxxxx.s que viene en los proyectos...

Saludos!
-
Leonardo Garberoglio

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Assembler en ARM - Probando
« Respuesta #29 en: 12 de Octubre de 2015, 18:05:34 »
Bueno, como yo (repito) soy nuevo en ARM no puedo dar exactamente todo, y leerlo no es lo mismo que hacerlo para el dsPIC, el dsPIC me costo nada, fue una ... por ahi fue mas probar que otra cosa, pero este... recien estoy arrancando. El startup es simple, es una coleccion de saltos, o vectores de interrupcion + algunas funciones que son las de comienzo, el reset lo vi con un poco de ASM y vi como es que crea el copiado de memoria en C. Pero luego tenes el archivo de inicializacion de C, y ese te mete memset y vaaaaaaarias cosas mas.

Citar
de ese modo puedo ir haciendo algo simple como para ir tomando contacto y por otra parte lo puedo testear en el micro. quizá se debería arrancar con ASM con cosas que tengan que ver con manejo de datos en memoria

Por eso decia que den ideas, yo probaba y me ponia a jugar un rato xD,

Citar
Algo que creo está faltando es el formato genera de un archivo .s para que lo pueda meter en un pryecto y compile

Creo que esta, es lo minimo y compila, por ahi tal ves un align, pero bueno eso queda cuestion de cada uno:

Código: ASM
  1. .syntax unified
  2.         .thumb
  3.         .text
  4.         .global Funcion
  5.  
  6. Funcion:
  7.         BX      LR
  8.         .end


 

anything