Autor Tema: Ayuda con código assembler (implementación en C)  (Leído 1028 veces)

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

Desconectado thepilot7

  • PIC10
  • *
  • Mensajes: 3
Ayuda con código assembler (implementación en C)
« en: 02 de Noviembre de 2020, 20:01:43 »
Hola, me gustaría saber si alguien me puede ayudar con una función que esta en un application note implementada en assembler. Aunque aparentemente esta con comentarios explicando la misma implementacion en C, tras hacer esa implementacion en C, el resultado no es el mismo y no logro entender el motivo. La función en assembler es esta:


Código: ASM
  1. .include "general.inc"
  2.  
  3. ; External references
  4.           .include "park.inc"
  5.  
  6. ; Register usage
  7.           .equ ParmW,  w3    ; Ptr to ParkParm structure
  8.  
  9.           .equ Sq3W,   w4    ; OneBySq3
  10.           .equ SinW,   w4    ; replaces Work0W
  11.  
  12.           .equ CosW,   w5
  13.  
  14.  
  15.           .equ IaW,    w6    ; copy of qIa
  16.           .equ IalphaW,w6    ; replaces Ia
  17.  
  18.           .equ IbW,    w7    ; copy of qIb
  19.           .equ IbetaW, w7    ; Ibeta  replaces Ib
  20.  
  21. ; Constants
  22.           .equ OneBySq3,0x49E7   ; 1/sqrt(3) in 1.15 format
  23.  
  24.  
  25. ;=================== CODE =====================
  26.  
  27.           .section  .text
  28.           .global   _ClarkePark
  29.           .global   ClarkePark
  30.  
  31. _ClarkePark:
  32. ClarkePark:
  33.      ;; Ibeta = Ia*OneBySq3 + 2*Ib*OneBySq3;
  34.  
  35.           mov.w     #OneBySq3,Sq3W     ; 1/sqrt(3) in 1.15 format
  36.  
  37.           mov.w     _ParkParm+Park_qIa,IaW
  38.           mpy       Sq3W*IaW,A
  39.  
  40.           mov.w     _ParkParm+Park_qIb,IbW
  41.           mac       Sq3W*IbW,A
  42.           mac       Sq3W*IbW,A
  43.  
  44.           mov.w     _ParkParm+Park_qIa,IalphaW
  45.           mov.w     IalphaW,_ParkParm+Park_qIalpha
  46.           sac       A,IbetaW
  47.           mov.w     IbetaW,_ParkParm+Park_qIbeta
  48.  
  49.      ;; Ialpha and Ibeta have been calculated. Now do rotation.
  50.  
  51.      ;; Get qSin, qCos from ParkParm structure
  52.           mov.w     _ParkParm+Park_qSin,SinW
  53.           mov.w     _ParkParm+Park_qCos,CosW
  54.  
  55.      ;; Id =  Ialpha*cos(Angle) + Ibeta*sin(Angle)
  56.  
  57.           mpy       SinW*IbetaW,A     ; Ibeta*qSin -> A
  58.           mac       CosW*IalphaW,A    ; add Ialpha*qCos to A
  59.           mov.w     #_ParkParm+Park_qId,ParmW
  60.           sac       A,[ParmW++]        ; store to qId, inc ptr to qIq
  61.  
  62.      ;; Iq = -Ialpha*sin(Angle) + Ibeta*cos(Angle)
  63.           mpy       CosW*IbetaW,A     ; Ibeta*qCos -> A
  64.           msc       SinW*IalphaW,A    ; sub Ialpha*qSin from A
  65.           sac       A,[ParmW]          ; store to qIq
  66.           return
  67.           .end

Utiliza una estructura definida así (al principio se puede ver como esta como comentarios la definición de la estructura en C):

Código: ASM
  1. ;;------------------  ASM30 API for Park Clarke routines ---------------------
  2. ; ParkParm stucture:
  3.  
  4. ; typedef struct {
  5. ;    short   qAngle;
  6. ;    short   qCos;
  7. ;    short   qSin;
  8. ;    short   qIa;
  9. ;    short   qIb;
  10. ;    short   qIalpha;
  11. ;    short   qIbeta;
  12. ;    short   qId;
  13. ;    short   qIq;
  14. ;    short   qVd;
  15. ;    short   qVq;
  16. ;    short   qValpha;
  17. ;    short   qVbeta;
  18. ;    short   qV1;
  19. ;    short   qV2;
  20. ;    short   qV3;
  21. ;    } tParkParm;
  22.  
  23. ; Note that pairs qCos,qSin, qIa,qIb, etc must remain consecutive
  24.  
  25.           .equ Park_qAngle,0       ; qAngle
  26.           .equ Park_qSin,2         ; qSin
  27.           .equ Park_qCos,4         ; qCos
  28.           .equ Park_qIa,6          ; qIa
  29.           .equ Park_qIb,8          ; qIb
  30.           .equ Park_qIalpha,10     ; qIalpha
  31.           .equ Park_qIbeta,12      ; qIbeta
  32.           .equ Park_qId,14         ; qId
  33.           .equ Park_qIq,16         ; qIq
  34.           .equ Park_qVd,18         ; qVd
  35.           .equ Park_qVq,20         ; qVq
  36.           .equ Park_qValpha,22     ; qValpha
  37.           .equ Park_qVbeta,24      ; qVbeta
  38.           .equ Park_qV1,26         ; qV1
  39.           .equ Park_qV2,28         ; qV2
  40.           .equ Park_qV3,30         ; qV3
  41.  
  42.           .extern _ParkParm

La implementación en C, la he interpretado de esta forma:

Código: C
  1. // 1/sqrt(3) in Q1.15
  2. #define dqOneBySq3 0x49E7
  3.  
  4. void ClarkePark(void){
  5.     ParkParm.qIalpha = ParkParm.qIa;
  6.     ParkParm.qIbeta = ParkParm.qIa * dqOneBySq3 + 2 * ParkParm.qIb * dqOneBySq3;
  7.  
  8.     ParkParm.qId = ParkParm.qIalpha * ParkParm.qCos + ParkParm.qIbeta * ParkParm.qSin;
  9.     ParkParm.qIq = -ParkParm.qIalpha * ParkParm.qSin + ParkParm.qIbeta * ParkParm.qCos;
  10.  
  11. }

Al ejecutar las funciones, obtengo resultados diferentes partiendo con las mismas variables en Hexadecimal. A continuación pongo los valores de la estructura usados en esta función:

Código: CSS
  1. ParkParm.qIa= 0xFE3E
  2. ParkParm.qIb= 0xFD26
  3. ParkParm.qSin= 0x0143
  4. ParkParm.qCos= 0x7FF7

Se supone que deben calcular qIalpha, qIbeta, qId y qIq. En la funcion en Assembler obtengo:

ParkParm.qIalpha= 0xFE3E
ParkParm.qIbeta= 0xFBB1
ParkParm.qId= 0x0000
ParkParm.qIq= 0x0000

Mientras que en la funcion en C obtengo ():

ParkParm.qIalpha= 0xFE3E
ParkParm.qIbeta= 0x9E86
ParkParm.qId= 0x0000
ParkParm.qIq= 0x0000

Supongo que se me está escapando algo muy basico, especialmente al haber multiplicaciones entiendo que se va del rango de los 16 bits, pero no doy con la formula (he intentado usar una variable uint32_t para guardar el resultado y luego dividir el resultado por 2^16, pero en este caso no ha funcionado...) Como no entiendo assembler, algo entre la línea 38 a 47 se me esta escapando....


Alguien me puede ayudar por favor?

Gracias!
« Última modificación: 02 de Noviembre de 2020, 20:09:44 por thepilot7 »

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Ayuda con código assembler (implementación en C)
« Respuesta #1 en: 03 de Noviembre de 2020, 10:19:13 »
Mil disculpas por no poder darte una solución. Estuve probando y no logro encontrarle la vuelta (y realmente no puedo dedicarle mas tiempo), pero voy a tratar de guiarte por donde es que voy, así te das cuentas de algunos errores que tenes y además vas con un poco mas de ideas para probar.

El bit US del registro CORCON establece si las instrucciones DSP son signed/unsigned, siendo signed por default (US=0) y fraccionario por default tambien (bit IF=0), siendo el MSB el signo. formato Q15
El DSP, transforma de los 16 bits a 17 bits extendiendo el signo. Y luego realizando la multiplicación 17x17bits. Depositando en el acumulador, nuevamente si todo tiene signo hay que tener cuidado en la suma.

Citar
The multiplier takes in 16-bit input data and converts the data to 17 bits. Signed operands to the multiplier are sign-extended. Unsigned input operands are zero-extended. The internal 17-bit representation of data in the multiplier allows correct execution of mixed-sign and unsigned 16-bit by 16-bit multiplication operations.

Citar
The multiplier scales the result one bit to the left for fractional operation. The Least Significant bits (LSbs) of the result are always cleared. The multiplier defaults to Fractional mode for DSP operations at a device Reset.
Tal ves con esto, le quita el signo (MSB) al qIa y qIb

Citar
The data accumulators have a 40-bit adder/subtractor with automatic sign extension logic for the multiplier result (if signed).
El resultado de la multiplicacion es negado si tiene signo. que seria el caso de que qIa se tome como negativo y sqrt(3) no.

Cuando se guarda el valor del acumulador, lo que se hace es guardar los bits 31 al 16, recorda que el acumulador en estos es de 40bits. En tu código solo estas tomando la parte baja nomas.


Mi consejo si podes ver los registros es:
- Ver el registro CORCON los bits US e IF
- Ver el "paso a paso" de las operaciones, es decir primero el resultado de multiplicar qIa*sqrt(3) en el Acumulador A, o podes hacer qIb = 0, sin tener que modificar nada.
- Ver el resultado de qIb*sqrt(3) si haces qIA=0
- Ver las sumas de qIa*sqrt(3) + qIb*sqrt(3)


Esto para tratar de entender como se maneja internamente el DSP.
« Última modificación: 03 de Noviembre de 2020, 10:25:28 por KILLERJC »

Desconectado thepilot7

  • PIC10
  • *
  • Mensajes: 3
Re:Ayuda con código assembler (implementación en C)
« Respuesta #2 en: 03 de Noviembre de 2020, 19:27:23 »
Hola, muchas gracias, y no hay que disculparse por intentar ayudar!!! (lo agradezco igualmente). La verdad que es bastante curioso y por eso he decidido postearlo porque hoestamente haciendo paso a paso las ejecuciones no logro darme cuenta, pero voy a mirar los registros que comentas y luego comento el resultado (en paralelo si a alguien se le ocurre algo más será más que bienvenido). Por cierto, por si sirve de ayuda, lo estoy simulando con el MPLABX. El ejemplo sobre el que me baso y que tiene la función en assembler es para un dsPIC33FJ12MC202 y en C uso un dsPIC33EP128MC204.

Miestras estaba escribiendo esta respuesta me he puesto a ejecutar mirando los registros. Efectivamente en el registro CORCON los bits US e IF están por default (US=0 y IF=0). El único bit que está en 1 es el SATDW, con lo que el valor del registro completo es CORCON=0x0020

En assembler:
Antes de ejecutar la línea 35 del primer post mov.w     #OneBySq3,Sq3W     ; 1/sqrt(3) in 1.15 format

Los registros tienen estos valores:
Código: CSS
  1. ACCA=0xFFFE930000
  2. ACAAH=0xFE93
  3. ACCAL=0x0000
  4. ACCAU=0xFFFF
  5. WREG0=0xFFFF
  6. WREG1=0xFFFF
  7. WREG2=0x4B55
  8. WREG3=0x430B
  9. WREG4=0xFD26
  10. WREG5=0xFD26
  11. WREG6=0xE103
  12. WREG7=0x00B2

Al pasar a la siguiente linea mov.w     _ParkParm+Park_qIa,IaW (tener en cuenta que ejecuta la línea anterior, con lo que marco con << los registros que han cambiado de aqui en adelante):
Código: CSS
  1. ACCA=0xFFFE930000
  2. ACAAH=0xFE93
  3. ACCAL=0x0000
  4. ACCAU=0xFFFF
  5. WREG0=0xFFFF
  6. WREG1=0xFFFF
  7. WREG2=0x4B55
  8. WREG3=0x430B
  9. WREG4=0x49E7  <<
  10. WREG5=0xFD26
  11. WREG6=0xE103
  12. WREG7=0x00B2

Al pasar a la siguiente linea, mpy       Sq3W*IaW,A:
Código: CSS
  1. ACCA=0xFFFE930000
  2. ACAAH=0xFE93
  3. ACCAL=0x0000
  4. ACCAU=0xFFFF
  5. WREG0=0xFFFF
  6. WREG1=0xFFFF
  7. WREG2=0x4B55
  8. WREG3=0x430B
  9. WREG4=0x49E7
  10. WREG5=0xFD26
  11. WREG6=0xFE3E  <<
  12. WREG7=0x00B2

Al pasar a la siguiente linea, mov.w     _ParkParm+Park_qIb,IbW:
Código: CSS
  1. ACCA=0xFFFEFC2FE4  <<
  2. ACAAH=0xFEFC        <<
  3. ACCAL=0x2FE4        <<
  4. ACCAU=0xFFFF
  5. WREG0=0xFFFF
  6. WREG1=0xFFFF
  7. WREG2=0x4B55
  8. WREG3=0x430B
  9. WREG4=0x49E7
  10. WREG5=0xFD26
  11. WREG6=0xFE3E
  12. WREG7=0x00B2

Al pasar a la siguiente linea, mac       Sq3W*IbW,A (he seguido consejo de mantener qIb=0):
Código: CSS
  1. ACCA=0xFFFEFC2FE4
  2. ACAAH=0xFEFC
  3. ACCAL=0x2FE4
  4. ACCAU=0xFFFF
  5. WREG0=0xFFFF
  6. WREG1=0xFFFF
  7. WREG2=0x4B55
  8. WREG3=0x430B
  9. WREG4=0x49E7
  10. WREG5=0xFD26
  11. WREG6=0xFE3E
  12. WREG7=0x0000   <<

Al pasar a la siguiente linea, mac       Sq3W*IbW,A (no he notado cambio ¿?):
Código: CSS
  1. ACCA=0xFFFEFC2FE4
  2. ACAAH=0xFEFC
  3. ACCAL=0x2FE4
  4. ACCAU=0xFFFF
  5. WREG0=0xFFFF
  6. WREG1=0xFFFF
  7. WREG2=0x4B55
  8. WREG3=0x430B
  9. WREG4=0x49E7
  10. WREG5=0xFD26
  11. WREG6=0xFE3E
  12. WREG7=0x0000

Al pasar a la siguiente linea, mov.w     _ParkParm+Park_qIa,IalphaW(no he notado cambio ¿?):
Código: CSS
  1. ACCA=0xFFFEFC2FE4
  2. ACAAH=0xFEFC
  3. ACCAL=0x2FE4
  4. ACCAU=0xFFFF
  5. WREG0=0xFFFF
  6. WREG1=0xFFFF
  7. WREG2=0x4B55
  8. WREG3=0x430B
  9. WREG4=0x49E7
  10. WREG5=0xFD26
  11. WREG6=0xFE3E
  12. WREG7=0x0000

Al pasar a la siguiente linea (mov.w     _ParkParm+Park_qIa,IalphaW, como copia el mismo valor no hay cambios esperados...):
Código: CSS
  1. ACCA=0xFFFEFC2FE4
  2. ACAAH=0xFEFC
  3. ACCAL=0x2FE4
  4. ACCAU=0xFFFF
  5. WREG0=0xFFFF
  6. WREG1=0xFFFF
  7. WREG2=0x4B55
  8. WREG3=0x430B
  9. WREG4=0x49E7
  10. WREG5=0xFD26
  11. WREG6=0xFE3E
  12. WREG7=0x0000

Al pasar a la siguiente linea (mov.w     IalphaW,_ParkParm+Park_qIalpha, aqui tampoco se esperan cambios, copia a la estructura):
Código: CSS
  1. ACCA=0xFFFEFC2FE4
  2. ACAAH=0xFEFC
  3. ACCAL=0x2FE4
  4. ACCAU=0xFFFF
  5. WREG0=0xFFFF
  6. WREG1=0xFFFF
  7. WREG2=0x4B55
  8. WREG3=0x430B
  9. WREG4=0x49E7
  10. WREG5=0xFD26
  11. WREG6=0xFE3E
  12. WREG7=0x0000

Al pasar a la siguiente linea (sac       A,IbetaW):
Código: CSS
  1. ACCA=0xFFFEFC2FE4
  2. ACAAH=0xFEFC
  3. ACCAL=0x2FE4
  4. ACCAU=0xFFFF
  5. WREG0=0xFFFF
  6. WREG1=0xFFFF
  7. WREG2=0x4B55
  8. WREG3=0x430B
  9. WREG4=0x49E7
  10. WREG5=0xFD26
  11. WREG6=0xFE3E
  12. WREG7=0xFEFC  <<

finalmente al ejecutar la linea mov.w     IbetaW,_ParkParm+Park_qIbeta, no se esperan cambios en los registros porque actualiza la estructura 0xFEFC=-260 porque el valor de qIa=-450 .


En C:

Código: C
  1. ParkParm.qIbeta = ParkParm.qIa * dqOneBySq3 + 2 * ParkParm.qIb * dqOneBySq3;

Como no puedo analizarlos registros y veo el resultado directamente (incluso mirando el acumulador tras el calculo veo que esta todo a 0). El resltado teniendo los mismos valores incluido qIb=0x0000 es 0x17F2=6130


Seguiré investigando, pero se me está escapando algo... quiza de tanto hacer y deshacer....

Gracias por la ayuda!


Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Ayuda con código assembler (implementación en C)
« Respuesta #3 en: 03 de Noviembre de 2020, 22:28:25 »
Citar
Al pasar a la siguiente linea, mov.w     _ParkParm+Park_qIb,IbW:

Se ve que tomo el valor 0, por eso luego no cambio, o lo dejaste en 0. De todas formas acabo encontre la unica forma de realizar la multiplicacion para que me de como en el acumulador.

0x0000 49E7 * 0xFFFF FE3E   (Extension de signo) = 0x49E6 FF7E 17F2

Multiplicandolo por 2 ->   0x93CD FEFC 2FE4

Recortandolo a los 32 bits   0x FEFC 2FE4

Puedo entender el porque lo multiplica por 2, ya que si tenes 16 bits y 1 es de signo, al multiplicarlo, te quedarian 2 de signos y luego lo fraccionario (30 de fracciones).
Por lo tanto asi queda en Q.31   1 signo y demas fraccion.


Ahora si lo hago con 17x17 bits, no me da ni queriendo.... realmente nunca me puse a invetigar demasiado los binarios en punto fijo.


EDIT: Una cosa que me olvide de avisarte... el compilador C no usa las instrucciones DSP, por lo tanto hacer instrucciones en C no va a usar el motor DSP y por lo tanto es mas lo que perdes que lo que ganas.
Incluso los ejemplos de DSP que da Microchip, al final tiene una linea en ASM que hace el MAC
« Última modificación: 03 de Noviembre de 2020, 23:48:57 por KILLERJC »

Desconectado thepilot7

  • PIC10
  • *
  • Mensajes: 3
Re:Ayuda con código assembler (implementación en C)
« Respuesta #4 en: 04 de Noviembre de 2020, 16:41:52 »
Ok entiendo, voy a seguir investigando. Muchas gracias por las aclaraciones, se agradecen!!! si veo que no avanzo investigaré de hacer esa parte en assembler para aprovechar las capacidades de los DSP (no sabia eso que comentas...)

Un saludo