Autor Tema: PIC 18F4550 y USB, exprimiendo hasta la última gota de RAM [CCS]  (Leído 63701 veces)

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

Desconectado yopepe

  • PIC10
  • *
  • Mensajes: 19
Antes que nada, aprovecho esta humilde primer contribución para agradecer enormemente a todas las personas que forman parte del foro, tanto los que nos ayudan con sus conocimientos de forma desinteresada, como a los que con sus dudas nos hacen pensar y quizás aprender algo nuevo.

En este momento estoy dedicando mi tiempo disponible al desarrollo de un proyecto que involucra RFid (muy similar a la gran labor del amigo NANO1985, con la adición de escritura en el transponder) y otras cuestiones, entre las cuales se encuentra la ya famosa causante de desvelos interfaz PC <-> PIC. Si bien ya estamos bastante "amigados" con este útil recurso gracias a los pioneros como J1M y otros tantos, que pusieron su esfuerzo para sacar este tema de la oscuridad, y por ello casi sin mayores problemas podemos ponerla a funcionar, el hecho de que es un tema con muchas idas y venidas nos lleva a armar nuestro sistema en base a un montón de recursos genéricos de los cuales muchas veces sabemos poco y nada del funcionamiento. Este último detalle es el que ineludiblemente tarde o temprano nos va a pasar factura en algún aspecto de nuestro proyecto, digamos velocidad, utilización de recursos, etc. Si bien es casi una locura desarrollar todos y cada uno de los aspectos de una comunicación PC <-> PIC, existen algunos puntos en los que uno puede meter mano para hacer un "tuning" más acorde a nuestra aplicación y conseguir resultados notablemente mejores. En esta ocasión vamos a hablar del que, en mi caso, me resultó más problemático; como reza el título, la memoria RAM del microcontrolador.

Acotación al margen: todo lo que se explica a continuación es válido para los dispositivos 18F2455, 18F2550, 18F4455 y 18F4550 en USB modo full speed. En el caso de utilizar uno diferente, los conceptos aplicados son exactamente los mismos, aunque las modificaciones del código deberán ser cambiadas para el caso particular. Ah, no se asusten por lo largo, es fácil  ;-).

Todo el código para el microcontrolador está realizado en CCS v 4.088, así como lo ejemplos tomados para modificación. Lo aquí planteado surge después de haber leído el post de Modulay http://www.todopic.com.ar/foros/index.php?topic=21145.40


Ahora sí, pasemos a lo que nos compete. Nos vamos a centrar principalmente en el archivo pic18_usb.h, el cual es en el que se define la capa fisíca USB para los dispositivos de la familia PIC18F.
Como antes dijimos, las cosas diseñadas para ser generales son muy cómodas porque no tenemos que preocuparnos de entender como funcionan, pero justamente por eso su funcionamiento no es óptimo, este es el caso del archivo pic18_usb.h. Primero, vamos a tratar de entender un poco como reserva y realiza la asignación de memoria.

Según el datasheet que nos proporciona Microchip (como dice RedPic, en los datasheet esta todo, algo en lo que coincido totalmente  :mrgreen:), la estructura de memoria RAM es la siguiente:


Figura 1.

Como podemos ver, la zona de los bancos 4 a 7 es lo que se conoce como USB RAM. Sin embargo esto no significa que esa memoria sea exclusiva para el USB, si leemos en la hoja de datos, nos dice que si bien esta es una zona de memoria especial debido a que es "dual port" (puede ser accedida tanto por el core del microcontrolador como por el SIE que gobierna el USB), su asignación no es fija y depende de nuestra aplicación pudiéndose utilizar tanto para datos como para cuestiones relacionadas al USB. Con esto en mente nos vamos directo y sin escalas al código del archivo pic18_usb.h, en el cual nos encontramos, en orden de aparición, con lo siguiente:

Código: [Seleccionar]
#elif ((getenv("DEVICE")=="PIC18F2455") || (getenv("DEVICE")=="PIC18F2550") || \
       (getenv("DEVICE")=="PIC18F4455") || (getenv("DEVICE")=="PIC18F4550"))
 #define __USB_4550__
 #define USB_TOTAL_BUFFER_SPACE  ((int16)0x300)
 #define USB_MAX_NUM_ENDPOINTS  16

Esta es la parte de la definición para el caso de los dispositivos de nuestro interés en este caso. Como podemos ver, en USB_TOTAL_BUFFER_SPACE guarda un valor (0x300) que representa la máxima cantidad de posiciones de memoria "disponibles para buffer" (de 500h a 7FFh en figura 1). Vemos que no se está teniendo en cuenta las posiciones de memoria de 400h hasta 4FFh, más adelante veremos porque.

Lo siguiente con lo que nos encontramos es esto:

Código: [Seleccionar]
#define USB_BUFFER_NEEDED (USB_EP0_TX_SIZE+USB_EP0_RX_SIZE+USB_EP1_TX_SIZE+USB_EP1_RX_SIZE+USB_EP2_TX_SIZE+USB_EP2_RX_SIZE+USB_EP3_TX_SIZE+USB_EP3_RX_SIZE+USB_EP4_TX_SIZE+USB_EP4_RX_SIZE+USB_EP5_TX_SIZE+USB_EP5_RX_SIZE+USB_EP6_TX_SIZE+USB_EP6_RX_SIZE+USB_EP7_TX_SIZE+USB_EP7_RX_SIZE+USB_EP8_TX_SIZE+USB_EP8_RX_SIZE+USB_EP9_TX_SIZE+USB_EP9_RX_SIZE+USB_EP10_TX_SIZE+USB_EP10_RX_SIZE+USB_EP11_TX_SIZE+USB_EP11_RX_SIZE+USB_EP12_TX_SIZE+USB_EP12_RX_SIZE+USB_EP13_TX_SIZE+USB_EP13_RX_SIZE+USB_EP14_TX_SIZE+USB_EP14_RX_SIZE+USB_EP15_TX_SIZE+USB_EP15_RX_SIZE)
bastante feo de leer, lo sé  :D, pero no es más que sumar el tamaño de todos los endpoints y guardarlos en USB_BUFFER_NEEDED. El valor de USB_BUFFER_NEEDED es la cantidad total de bytes de RAM que vamos a necesitar para buffer

Directamente después vemos que realiza una comparación entre los 2 valores anteriores (USB_BUFFER_NEEDED y USB_TOTAL_BUFFER_SPACE) para determinar si la cantidad de memoria que en nuestro programa asignamos a endpoints (con #define USB_EPn_TX_SIZE y #define USB_EPn_RX_SIZE para cada endpoint) "cabe" en el valor que declaramos con USB_TOTAL_BUFFER_SPACE.

Código: [Seleccionar]
#if (USB_BUFFER_NEEDED > USB_TOTAL_BUFFER_SPACE)
 #error You are trying to allocate more memory for endpoints than the PIC can handle
#endif

Si no "cabe", nos da error al momento de compilar.

Luego, nos encontramos con algo como esto:

Código: [Seleccionar]
#reserve 0x400:0x4FF+USB_BUFFER_NEEDED
La directiva #reserve cumple la función de, como su nombre lo indica, reservar estas posiciones de memoria indicadas para que el compilador no las use para otra cosa. Vemos que la zona de descriptores de buffer la reserva completa (0x400:0x4FF, ver Figura 1) y además también reserva la cantidad de memoria contigua indicada por USB_BUFFER_NEEDED (nuestros buffers de endpoints). Ya aquí notamos un comportamiento conservador, ya que por ejemplo no importa que solo utilicemos los endpoints 0 (0 in y 0 out) y 1 (1 in y 1 out), de todas formas seguirá reservando el bloque de descriptores de buffer completo (0x400 a 0x4FF) y por esto toda la memoria que sobre en la zona de descriptores (ya que solo usamos 4 endpoints) el compilador no la va a utilizar, desperdiciándola. Al final veremos como se pueden modificar las cosas para hacer un uso más eficiente. Sin embargo, aquí no termina la cosa, si seguimos analizando el archivo nos vamos a encontrar con esto:

Código: [Seleccionar]
#define USB_BUFFER 0x500
y más abajo:

Código: [Seleccionar]
char usb_ep0_rx_buffer[USB_MAX_EP0_PACKET_LENGTH];
#locate usb_ep0_rx_buffer=USB_BUFFER

char usb_ep0_tx_buffer[USB_MAX_EP0_PACKET_LENGTH];
#locate usb_ep0_tx_buffer=USB_BUFFER+USB_MAX_EP0_PACKET_LENGTH

char usb_data_buffer[USB_TOTAL_BUFFER_SPACE-USB_MAX_EP0_PACKET_LENGTH-USB_MAX_EP0_PACKET_LENGTH];
#locate usb_data_buffer=USB_BUFFER+USB_MAX_EP0_PACKET_LENGTH+USB_MAX_EP0_PACKET_LENGTH

USB_BUFFER simplemente contiene el valor de la primera posición de memoria donde empiezan a estar los buffers de los endpoints, y por lo tanto también es la dirección que separa los buffers de los descriptores de buffer.
La directiva #locate, como también su nombre lo indica, coloca una variable en una posición de memoria especifica y además, también reserva el lugar para que el compilador no lo use. Sabiendo esto:

Código: [Seleccionar]
char usb_ep0_rx_buffer[USB_MAX_EP0_PACKET_LENGTH];
#locate usb_ep0_rx_buffer=USB_BUFFER

Coloca una variable (usb_ep0_rx_buffer) de tamaño USB_MAX_EP0_PACKET_LENGTH (64 para estos dispositivos a full speed) a partir de la posición USB_BUFFER. En criollo, asigna las posiciones de memorias necesarias para el endpoint 0 OUT, al principio de la zona de buffers.

Código: [Seleccionar]
char usb_ep0_tx_buffer[USB_MAX_EP0_PACKET_LENGTH];
#locate usb_ep0_tx_buffer=USB_BUFFER+USB_MAX_EP0_PACKET_LENGTH

Lo mismo que la anterior para el endpoint 0 IN, notar que lo pone a continuación del anterior (USB_BUFFER+USB_MAX_EP0_PACKET_LENGTH)

Código: [Seleccionar]
char usb_data_buffer[USB_TOTAL_BUFFER_SPACE-USB_MAX_EP0_PACKET_LENGTH-USB_MAX_EP0_PACKET_LENGTH];
#locate usb_data_buffer=USB_BUFFER+USB_MAX_EP0_PACKET_LENGTH+USB_MAX_EP0_PACKET_LENGTH

Acá es donde está la madre del borrego, vemos que a continuación de la anterior, asigna una variable del tamaño total de la memoria que disponemos para el usb, menos lo que ya ocupamos con el endpoint 0 IN y OUT. Es decir, no importa si usamos 1,2,3 o 16 endpoints, siempre va a ocupar toda la memoria (lo pueden ver al compilar para diferente número de endpoints de diferentes tamaños, la ram ocupada no cambia) y gracias a esto podemos llegar a estar desperdiciando hasta un 25~30% de nuestra valiosa RAM. Como podemos resolver esto, es tan fácil que parece mentira, modificando:

Código: [Seleccionar]
char usb_data_buffer[USB_TOTAL_BUFFER_SPACE-USB_MAX_EP0_PACKET_LENGTH-USB_MAX_EP0_PACKET_LENGTH];
#locate usb_data_buffer=USB_BUFFER+USB_MAX_EP0_PACKET_LENGTH+USB_MAX_EP0_PACKET_LENGTH

por

Código: [Seleccionar]
char usb_data_buffer[USB_BUFFER_NEEDED-USB_MAX_EP0_PACKET_LENGTH-USB_MAX_EP0_PACKET_LENGTH];
#locate usb_data_buffer=USB_BUFFER+USB_MAX_EP0_PACKET_LENGTH+USB_MAX_EP0_PACKET_LENGTH

Luego de esta modificación prueben de compilar cambiando los tamaños y cantidades de enpoints y verán que la RAM utilizada va cambiando acorde, además, para proyectos con pocos endpoints y pequeños tamaños disminuye drásticamente la cantidad utilizada.

Pero esto no es todo, a pesar de que este cambio es el más notorio, podemos mejorar aún más la situación. Más arriba vimos que el espacio para los descriptores de buffers se reservaba completo siempre (desde 400h hasta 4FFh), si embargo, esta cantidad de memoria se utiliza solo en el caso extremo en que utilicemos los 32 endpoints (16 in y 16 out), y todos con el modo PING-PONG activado, cosa que el compilador de CCS ni siquiera soporta (no vamos a entrar en mucho detalle de como funciona este modo, solo vamos a decir que cuando un descriptor de buffer tiene habilitado el modo PING-PONG ocupa el doble de memoria). Podemos concluir entonces que en el caso extremo (utilizando los 32 endpoints) vamos a estar ocupando solo la mitad de la memoria destinada a los descriptores de buffer (mínimo nos quedan 128 bytes libres), peor aún, si solo utilizáramos los endpoints 0 y 1 estaremos desperdiciando prácticamente todo el bloque. Es importante notar que con esta modificación no solo obtendremos mas memoria RAM para nuestro uso, sino que además esta memoria es de la "especial" y que en el caso de ser necesaria la podemos utilizar para colocar mas buffers para endpoints, ya que si necesitáramos por ejemplo 7 endpoints (7in y 7 out) de 64 bytes, con las cosas como están no nos alcanzaría la memoria, pero al realizar el cambio si podemos implementarlo (solo por dar un ejemplo). Como buenos ahorradores, vamos a resolver esto, es un poquito más complicado que lo que hicimos anteriormente, pero nada del otro mundo. Comencemos viendo como es la estructura de un descriptor de buffer, según el datasheet:


Figura 2.

Como indica la figura 2, cada descriptor de buffer se compone de 4 bytes, en los cuales almacena toda la información pertinente a su buffer asociado. La organización en memoria de los descriptores, para el caso en que no se utiliza el modo PING-PONG es la siguiente:


Figura 3.

Observamos que como máximo, si utilizamos los 32 endpoints (16 in y 16 out), la última posición utilizada por los descriptores llega a 47Fh, por lo que desde 480h hasta 4FFh podremos utilizarla para buffers. En el caso de utilizar aun menos endpoints, para saber cual es la primera posición que podemos utilizar hacemos una simple cuenta:

                             [(numero_de_endpoints_in + numeros_de_endpoints_out) * 4] = cantidad_de_posiciones_de_memoria_para_descriptores  (expresion 1)

Luego pasamos el valor anterior a hexadecimal (pueden utilizar la calculadora de windows en modo científico para ello), le sumamos 400h y le restamos 1h y el valor resultante es la última posición utilizada por un descriptor, por lo que la primera que podremos utilizar será la siguiente.

Un ejemplo para que se comprenda mejor puede ser, supongamos que utilizamos 4 endpoints, 0 IN y OUT y 1 IN y OUT, el resultado de la expresión 1 será 16, pasado a hexadecimal es 10h, le sumamos 400h, nos da 410h. Esta es la primer posición libre para utilizar para buffers y por lo tanto, 410h - 1h = 40Fh será la última posición utilizada por los descriptores.

Para que quede en claro, lo que vamos a hacer al modificar es "mover el bloque de los buffers para arriba" para que en vez de comenzar en 500h, empiece en la primer posición libre después de los descriptores, y luego agrandarlo para ocupar el espacio nuevo.


Vamos a ver las 3 modificaciones que tenemos que realizar en el código, todas los trozos de código que aparecen a continuación, son los mismos que están explicados parte por parte más arriba.

Primero, debemos modificar el tamaño de la memoria reservada para descriptores, esto lo realizamos en esta parte:

Código: [Seleccionar]
#reserve 0x400:0x4FF+USB_BUFFER_NEEDED
en donde tenemos que cambiar 0x4FF por el valor que calculamos anteriormente como "última posición utilizada por los descriptores" (en el ejemplo, 40Fh, que en esta notación es 0x40F)

Segundo, debemos modificar el valor de inicio de la memoria para buffers, esto lo realizamos en esta parte:

Código: [Seleccionar]
#define USB_BUFFER 0x500
en donde debemos cambiar 0x500 por el valor que calculamos anteriormente como "primer posición libre para utilizar para buffers" (en el ejemplo, 410h, que en esta notación es 0x410)

tercero, vamos a modificar el valor total de memoria para buffers disponible, lo realizamos aquí:

Código: [Seleccionar]
#elif ((getenv("DEVICE")=="PIC18F2455") || (getenv("DEVICE")=="PIC18F2550") || \
       (getenv("DEVICE")=="PIC18F4455") || (getenv("DEVICE")=="PIC18F4550"))
 #define __USB_4550__
 #define USB_TOTAL_BUFFER_SPACE  ((int16)0x300)
 #define USB_MAX_NUM_ENDPOINTS  16

en donde debemos cambiar 0x300 por el nuevo valor total de memoria, que será, el valor final de la memoria USB (0x7FF o 7FFh) menos la última posición que ocupan los descriptores, en el ejemplo seria, 0x7FF (o 7FFh) menos 0x40F (o 40Fh) igual a 0x3F0 (o 3F0h)
IMPORTANTE: esta resta deben realizarla en hexadecimal (pueden utilizar la calculadora de windows en modo científico para ello)

Listo, a compilar y a disfrutar. Para ver la diferencia con el pic18_usb.h original, y con los dos cambios explicados, les dejo unas imágenes, que valen más que mil palabras:

ANTES


DESPUES



Bueno, esto es todo por el momento, espero que les sea útil, trate de ser lo más claro posible (por eso me extendí un poco  :mrgreen: ), pero si no se entiende algo no duden en preguntar.

Les saluda desde Mar del Plata, Argentina, Francisco.  8)
« Última modificación: 30 de Agosto de 2009, 23:59:45 por yopepe »

Desconectado MGLSOFT

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 7907
Re: PIC 18F4550 y USB, exprimiendo hasta la última gota de RAM [CCS]
« Respuesta #1 en: 30 de Agosto de 2009, 23:55:50 »
Excelente explicacion!! :mrgreen: :mrgreen:
Todos los dias aprendo algo nuevo, el ultimo día de mi vida aprenderé a morir....
Mi Abuelo.

Desconectado MLO__

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 4581
Re: PIC 18F4550 y USB, exprimiendo hasta la última gota de RAM [CCS]
« Respuesta #2 en: 31 de Agosto de 2009, 00:02:54 »
Excelente aporte.

Gracias  :-/
El papel lo aguanta todo

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18269
    • MicroPIC
Re: PIC 18F4550 y USB, exprimiendo hasta la última gota de RAM [CCS]
« Respuesta #3 en: 31 de Agosto de 2009, 02:23:59 »
Magnífica aportación, mejor explicación y excelente resultado. Le pongo una chincheta. Gracias yopepe

Desconectado Suky

  • Moderador Local
  • DsPIC33
  • *****
  • Mensajes: 6758
Re: PIC 18F4550 y USB, exprimiendo hasta la última gota de RAM [CCS]
« Respuesta #4 en: 31 de Agosto de 2009, 09:11:44 »
Muchas gracias por el aporte!!  :-/ Aparte muy bien explicado  8)



Saludos!
No contesto mensajes privados, las consultas en el foro

Desconectado J1M

  • Moderadores
  • PIC24H
  • *****
  • Mensajes: 1960
Re: PIC 18F4550 y USB, exprimiendo hasta la última gota de RAM [CCS]
« Respuesta #5 en: 31 de Agosto de 2009, 09:17:36 »
Bravo, bravo y bravo! genial y útil explicación :)

Gracias!!

Desconectado elmasvital

  • Administrador
  • PIC24H
  • *******
  • Mensajes: 1713
Re: PIC 18F4550 y USB, exprimiendo hasta la última gota de RAM [CCS]
« Respuesta #6 en: 31 de Agosto de 2009, 10:01:28 »
Gran comienzo yopepe felicidades... un 25% de memoria no se saca todos los dias

Desconectado yopepe

  • PIC10
  • *
  • Mensajes: 19
Re: PIC 18F4550 y USB, exprimiendo hasta la última gota de RAM [CCS]
« Respuesta #7 en: 31 de Agosto de 2009, 12:51:51 »
Bueno, me alegro que haya tenido aceptación, se agradece!  :mrgreen:. Esperemos que el tiempo me lo permita y pueda continuar con esta práctica del escribir que se me hace muy interesante. Un saludo a todos.

Desconectado Leon Pic

  • Colaborador
  • DsPIC30
  • *****
  • Mensajes: 3610
    • Impresiones en 3D
Re: PIC 18F4550 y USB, exprimiendo hasta la última gota de RAM [CCS]
« Respuesta #8 en: 06 de Septiembre de 2009, 09:58:06 »
Wow..
Muchas gracias yopepe :-/ :-/
Jesús dijo, yo soy el CAMINO, la VERDAD y la VIDA, nadie llega al PADRE si no es por mi.

Desconectado geronimoo

  • PIC10
  • *
  • Mensajes: 34
    • Divide & Conquer
Re: PIC 18F4550 y USB, exprimiendo hasta la última gota de RAM [CCS]
« Respuesta #9 en: 10 de Septiembre de 2009, 12:21:57 »
Mil millones de gracias.
Me viene al pelo!!
.::GERO::.

Desconectado manuel_zazu

  • PIC10
  • *
  • Mensajes: 2
Re: PIC 18F4550 y USB, exprimiendo hasta la última gota de RAM [CCS]
« Respuesta #10 en: 28 de Septiembre de 2009, 23:03:00 »
el tamaño del archivo hex antes del truco es de 14,9 kb y cuando aplico las modificaciones respectivas al driver el tamaño del hex resultante es de 15kb... en codigo maquina el tamaño aumenta?????

Desconectado geronimoo

  • PIC10
  • *
  • Mensajes: 34
    • Divide & Conquer
Re: PIC 18F4550 y USB, exprimiendo hasta la última gota de RAM [CCS]
« Respuesta #11 en: 06 de Noviembre de 2009, 02:32:52 »
Me sacaste del horno!!!  :mrgreen:
Sos un capo!
EN EL HORNO:


FUERA DEL HORNO  :P:

.::GERO::.

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: PIC 18F4550 y USB, exprimiendo hasta la última gota de RAM [CCS]
« Respuesta #12 en: 13 de Noviembre de 2009, 14:16:20 »
Hola, para los que usan la versión 4.093 parece ser que ya está contemplado el ahorro en ram.

Tengo un programa que compilado en la 4.084 marca un 53% de ram mientras que en la 4.093 marca 14%.

He notado que la 4.093 genera más instrucciones en mi aplicación que la 4.084, pero quién sabe si sea un comportamiento generalizado.

Código: [Seleccionar]
CCS PCH C Compiler, Version 4.093               13-Nov-09 11:02

               Filename: T_adq_v03.lst

               ROM used: 4968 bytes (15%)
                         Largest free fragment is 27796
               RAM used: 269 (13%) at main() level
                         285 (14%) worst case
               Stack:    10 worst case (3 in main + 7 for interrupts)

Código: [Seleccionar]
CCS PCH C Compiler, Version 4.084               13-Nov-09 11:13

               Filename: T_adq_v03.lst

               ROM used: 4670 bytes (14%)
                         Largest free fragment is 28094
               RAM used: 1093 (53%) at main() level
                         1106 (54%) worst case
               Stack:    10 worst case (3 in main + 7 for interrupts)
« Última modificación: 13 de Noviembre de 2009, 14:18:23 por migsantiago »

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 18269
    • MicroPIC
Re: PIC 18F4550 y USB, exprimiendo hasta la última gota de RAM [CCS]
« Respuesta #13 en: 14 de Noviembre de 2009, 02:27:40 »
Uff, pues sí que hay diferencias. Gracias por el dato.

Estas son las compilaciones de un proyecto en el que trabajo,
con la 4.084


y con la 4.093

Desconectado migsantiago

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8257
    • Sitio de MigSantiago
Re: PIC 18F4550 y USB, exprimiendo hasta la última gota de RAM [CCS]
« Respuesta #14 en: 14 de Noviembre de 2009, 11:50:11 »
Tiene pros y contras la v4.093, menos RAM pero más ROM.

Yo regresé a la 4.084 porque uso la ram USB con apuntadores, porque sé por la datasheet que está desocupada por los endpoints.