Autor Tema: Escritura EEPROM I2C, mas rapido!  (Leído 2298 veces)

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

Desconectado Marttyn

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1836
    • IDEAA
Escritura EEPROM I2C, mas rapido!
« en: 05 de Febrero de 2020, 10:40:56 »
¡Hola!
He estado trabajando en un proyecto que necesita escribir en una EEPROM externa y luego continuar haciendo "cosas" muy rápidamente. El problema para mí no era el tiempo entre escritura y escritura (generalmente 5 ms). Ya que mi programa escribe cada 20 ms.
Mi problema era esperar 5 ms después de que los datos I2C se enviaran a la EEPROM.

El punto clave es que una vez que envía los datos, la EEPROM "se escribe sola", puedes quedarte ahí o puede estar haciendo otras cosas ... yo prefiero hacer otras cosas en lugar de esperar :D

Con esto en mente, mejoré las bibliotecas para 256K y 1024K (las que más uso). También implemente la lectura y escritura por página, y agregue una función para mostrar por puerto serie lo que hay dentro de la EEPROM.

Qué hice para mejorar la velocidad? En lugar de esperar DESPUÉS de escribir, mi biblioteca espera ANTES de escribir ... Espera, COMO!!!?
Por lo general, las bibliotecas EEPROM escriben y luego "preguntan" constantemente a la EEPROM si ha acabado. Hasta que la EEPROM no responde que SI, el programa se queda esperando.
Pero mi libreria solo escribe, y luego sale, sin preguntar.
Para eso las funciones de lectura y escritura comprueban si la EEPROM está lista ANTES de enviar un nuevo comando de lectura / escritura. Si la EEPROM todavía está escribiendo por una operación anterior, espera hasta que termine y luego continúa.

De esta manera, el tiempo para una sola operación de escritura es muy rápido:
Entrar en la función -> Verificar si la EEPROM está ocupada -> Escribir -> Salir
No hay que esperar! Esto nos ahorra 3-5ms de espera.

Para escrituras consecutivas si se deberá esperar a que finalice la operación anterior.

Investigué un poco, y esta técnica no se comenta en ninguna parte. Aunque Microchip tiene una "application note" sobre cómo usar la EEPROM y cómo mejorar las velocidades: http://ww1.microchip.com/downloads/en/Appnotes/01028B.pdf

Para ser honesto, como nadie ha usado este enfoque, y además Microchip no habla de esto en ningún lado, pensé que no funcionaría. Pero después de muchas pruebas, parece funcionar de manera consistente, y lo estoy usando en un producto en producción.

Estas son algunas pruebas realizadas a una velocidad de bus de 400Khz con hardware PIC I2C y oscilador interno de 32Mhz, utilizando una memoria 24AA1025:
Citar
- Start -

- 16 BYTES OLD WRITE -
Byte  0: 4205 uS
Byte  1: 4206 uS
Byte  2: 4206 uS
Byte  3: 4205 uS
Byte  4: 4205 uS
Byte  5: 4206 uS
Byte  6: 4206 uS
Byte  7: 4205 uS
Byte  8: 4205 uS
Byte  9: 4206 uS
Byte 10: 4206 uS
Byte 11: 4205 uS
Byte 12: 4205 uS
Byte 13: 4206 uS
Byte 14: 4206 uS
Byte 15: 4204 uS
TOTAL: 0 Sec 68 mS 478 uS

- 16 BYTES NEW WRITE -
Byte  0: 190 uS
Byte  1: 4022 uS
Byte  2: 4022 uS
Byte  3: 4022 uS
Byte  4: 4022 uS
Byte  5: 4021 uS
Byte  6: 4021 uS
Byte  7: 4022 uS
Byte  8: 4022 uS
Byte  9: 4022 uS
Byte 10: 4022 uS
Byte 11: 4022 uS
Byte 12: 4022 uS
Byte 13: 4021 uS
Byte 14: 4021 uS
Byte 15: 4022 uS
TOTAL: 0 Sec 61 mS 707 uS

- 16 PAGES WRITE (128 bytes/page) -
Page  0: 4119 uS
Page  1: 7951 uS
Page  2: 7951 uS
Page  3: 7951 uS
Page  4: 7951 uS
Page  5: 7951 uS
Page  6: 7951 uS
Page  7: 7951 uS
Page  8: 7958 uS
Page  9: 7953 uS
Page 10: 7953 uS
Page 11: 7952 uS
Page 12: 7953 uS
Page 13: 7953 uS
Page 14: 7953 uS
Page 15: 7952 uS
TOTAL: 0 Sec 124 mS 616 uS

- FULL EEPROM WRITE -
TOTAL: 4 Sec 149 mS 403 uS

Para la escritura "tradicional", una sola escritura emplea 4205 uS por cada byte escrito.
Mientras que con este nuevo procedimiento, el primer byte solo emplea 190 us! Y los siguientes 4022 us.

Esto significa que si se escribe un byte cada 4-5 ms o más, entonces el uC gastará solo 190 us en realizar la escritura.

IMPORTANTE: esta librería no mejorará el tiempo que tarda la memoria en realizar la escritura, pero liberará al uC para hacer otras cosas mientras ocurre la escritura.

Pueden encontrar mi librería y también el programa de ejemplo de la medición del tiempo en github: https://github.com/ideaalab/eeprom_library
Escritas en CCS, y con ejemplo en MPLAB.

También tengo algunas otras librerías, no son perfectas, pero funcionan para mí :)

Espero que sea útil!
La gente ve las cosas que existen y se pregunta por qué.
Yo prefiero imaginar lo que no existe y preguntarme por qué no.

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Escritura EEPROM I2C, mas rapido!
« Respuesta #1 en: 05 de Febrero de 2020, 11:18:30 »
Me parece excelente Marttyn, solo corregirte una sentencia:

Citar
Para ser honesto, como nadie ha usado este enfoque,

No es nada raro, recuerdo muchos códigos acá de otras cosas (SPI/UART) donde se hace así. Es decir se envía y cuando se va a enviar lo siguiente se pregunta si esta ocupado o no. Creo que incluso los codigos ASM EEPROM interna de Microchip lo hacen asi, eso si es escritura, ya que la lectura es inmmediata:

Código: ASM
  1. BSF STATUS,RP1 ;
  2. BSF STATUS,RP0
  3. BTFSC EECON1,WR ; <----------- ESTA LINEA
  4. GOTO $-1 ;
  5. BCF STATUS, RP0 ;Bank 2
  6. MOVF DATA_EE_ADDR,W ;
  7. MOVWF EEADR ;
  8. MOVF DATA_EE_DATA,W ;
  9. MOVWF EEDATA ;
  10. BSF STATUS,RP0 ;
  11. BCF EECON1,EEPGD ;Point to DATA

Respecto a los codigos aca en el foro, el mas reciente que recuerdo es este del ADC:

http://www.todopic.com.ar/foros/index.php?topic=49909.0

Considero que si uno quisiera realmente implementar una buena libreria EEPROM y no perder tiempo destinado a esperar, deberia incluir:

- Wear levelling
- Buffer de escritura y Buffer de Lectura.
- Totalmente "asincrono", con interrupciones y no esperando si la EEPROM esta ocupada.

Obviamente con DMA es mucho mejor, pero debido a que los PICs no lo poseen entonces te manejas con las interrupciones. Esto implica crear un handler para cada interrupción, y también implica que la persona que programa configure las interrupciones, escriba el código llamando a la función del handler + limpieza de flag si es necesario. Y no dedique una interrupción con puras demoras que afecten al trabajo de tu libreria.

Es de observar que hacer esto para I2C, es lo mismo que para UART, etc. La unica capa extra en una EEPROM seria ese wear levelling.

Y tambien importante, de nada sirve todo esto, si vas a leer la EEPROM y vas a esperar hasta que termine de ser leída. Lo cual únicamente serviría para la escritura. Como la mayoría se dedica a leer cuando lo necesita y no puede avanzar hasta que no este el dato, es por eso que Microchip provee esa salida nomas, ya que requiere un esfuerzo extra por parte del programador para programarlo de forma distinta.

----------------------------------------------------------------

Respecto a la libreria, no se si es la vista de simular un OOP en C, es conocido que los DEFINE son malos, una plaga :P, entonces una solución a esto seria crear una estructura inicial y ahí definir el tipo de memoria, y luego de acuerdo a la memoria que uses, lo manejas con switch cases si es que no podes resumirlo en una formula.
« Última modificación: 05 de Febrero de 2020, 12:27:33 por KILLERJC »

Desconectado Marttyn

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1836
    • IDEAA
Re:Escritura EEPROM I2C, mas rapido!
« Respuesta #2 en: 05 de Febrero de 2020, 15:16:46 »
Gracias Killer por tu detallada respuesta!
Cuando decia:
Citar
Para ser honesto, como nadie ha usado este enfoque
Me referia a que en EEPROM I2C no he visto esta tecnica. Ni en las librerias EEPROM de CCS, ni librerias de Arduino, ni librerias publicadas en diferentes foros y paginas web, ni en los datasheets de cada tipo de memoria, ni siquiera en los App notes de Microchip, donde hablan de como mejorar las velocidades de escritura.

* Captura.PNG
(87.14 kB, 639x498 - visto 410 veces)


Si bien es verdad que no lo he mirado en ASM.

Citar
Considero que si uno quisiera realmente implementar una buena libreria EEPROM y no perder tiempo destinado a esperar, deberia incluir:

- Wear levelling
- Buffer de escritura y Buffer de Lectura.
- Totalmente "asincrono", con interrupciones y no esperando si la EEPROM esta ocupada.
Concuerdo que todo lo que comentas mejoraria mucho los tiempos muertos, pero tambien complicaria mucho su uso y requeriria mucha memoria para implementarlo. En micros de baja gama se va la ROM en cuatro tonterias. Prefiero sacrificar tiempo pero usar menos ROM.

Los cambios que yo incluyo no incrementan el tamaño en ROM, pero si hacen ganar "algo" de tiempo. Y "mucho" tiempo cuando hay al menos 5ms entre escrituras.

Con DMA ya ni me meto, se va del foco de trabajo de los PICs.

Citar
Y tambien importante, de nada sirve todo esto, si vas a leer la EEPROM y vas a esperar hasta que termine de ser leída. Lo cual únicamente serviría para la escritura. Como la mayoría se dedica a leer cuando lo necesita y no puede avanzar hasta que no este el dato, es por eso que Microchip provee esa salida nomas, ya que requiere un esfuerzo extra por parte del programador para programarlo de forma distinta.
Para la EEPROM la lectura se podria decir que es "inmediata". Una vez que se le envio la posicion a leer, la memoria responde con el valor en 9 ciclos de reloj del bus por cada byte, mas un ciclo extra para el "stop bit". Esto, a 400Khz, se traduce en ~25uS de lectura, que si bien podriamos hacer otras cosas en ese tiempo, para un micro trabajando a 16Mhz por ejemplo (lo minimo para bus de 400Khz), serian unas 100 instrucciones. Da tiempo para hacer cosas, pero tambien se sale del foco llegar a ese nivel de optimizacion. Ni hablar si tenemos un micro de 4Mhz, en los cuales "solo ahorrariamos" unas 25 instrucciones, ya que el bus es de 100Khz.

Citar
Respecto a la libreria, no se si es la vista de simular un OOP en C, es conocido que los DEFINE son malos, una plaga :P, entonces una solución a esto seria crear una estructura inicial y ahí definir el tipo de memoria, y luego de acuerdo a la memoria que uses, lo manejas con switch cases si es que no podes resumirlo en una formula.
He leido en varias ocasiones lo de que los define son malos, pero quizas sea por costumbre, o porque aun no me he encontrado con ningun inconveniente a la hora de usarlos, ahi siguen siendome de gran ayuda. Tambien he visto mucho codigo que utiliza variables para valores constantes y definibles, lo que me parece un desperdicio de recursos.
Con respecto a hacer una libreria unica, con todas las opciones: lo he tenido en cuenta y es un objetivo para futuro. Pero ahora mismo no queria invertir mas tiempo en esto, ya que cumple sobradamente con mis expectativas. Ademas, queria mantener una retrocompatibilidad con todos mis programas que utilizan estas librerias. Desde luego una libreria diferente por cada "variante" de memoria no es eficiente ni mantenible. Pero como solo trabajo con estas dos, de momento me lo puedo permitir  :mrgreen:
La gente ve las cosas que existen y se pregunta por qué.
Yo prefiero imaginar lo que no existe y preguntarme por qué no.

Desconectado Marttyn

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1836
    • IDEAA
Re:Escritura EEPROM I2C, mas rapido!
« Respuesta #3 en: 07 de Febrero de 2020, 08:23:58 »
Algo curioso con lo que me he encontrado con las memorias 1024K es lo siguiente:
 

* Captura.PNG
(9.7 kB, 299x92 - visto 452 veces)


Esto quiere decir que para "preguntarle" a la EEPROM si ha acabado de escribir, tengo que hacerlo utilizando el mismo CONTROL BYTE:
 

* Captura2.PNG
(15.63 kB, 304x218 - visto 455 veces)


Para el resto de memorias, hay un solo "bloque", por lo que el CONTROL BYTE siempre es el mismo, y no tendria sentido utilizar otro CONTROL BYTE para hacer el polling. Pero las 1024K, estan formadas por dos bloques de memoria de 512K, y accedes a un bloque o al otro cambiando un bit del CONTROL BYTE.

Cuando haces el polling "tradicional", despues de haber hecho la escritura, no hay duda: utilizas el mismo CONTROL BYTE con el que empezaste.
Pero si haces el polling antes de empezar la escritura, como he propuesto con mi libreria, entonces "en teoria" habria que tener cuidado de hacer el polling con el mismo CONTROL BYTE con el que se realizo la ultima escritura. Este seria el caso de escribir en el bloque 0, y acto seguido escribir en el bloque 1. Segun el datasheet, al comenzar la escritura en el bloque 1 habria que preguntar utilizando el CONTROL BYTE de la ultima escritura (bloque 0). Ya que, si bien no lo dice expresamente, yo sobreentiendo que abortaria la escritura anterior en caso de no haber acabado.

Pero de la teoria a la practica, lo que parece que ocurre es como si los dos bloques de 512K fueran realmente dos "chips" de memoria diferente. Cada uno con su tiempo de escritura independiente del otro.
Eso quiere decir, que puedo escribir un byte en el bloque 0, y acto seguido otro byte en el bloque 1, sin tener que esperar. Y ambos valores son escritos correctamente en la EEPROM.
Tambien lo he probado con escritura secuencial y por paginas con el mismo resultado.
Si antes podia escribir un byte en 190uS, y el siguiente en 4022uS (total 4212uS) ahora puedo escribir 2 bytes en 380uS, siempre que tenga cuidado de escribirlos en bloques diferentes.

En resumen, solo tengo que esperar cuando escribo en el mismo bloque. Y esto es extrapolable a varias memorias en el mismo bus. Cada una tiene su tiempo de escritura, independiente de las demas. Por lo que si necesito tiempos de escritura "mas rapidos" podria dividir la memoria externa en varios chips y alternar la escritura entre ellos, obteniendo siempre el tiempo mas bajo.
Por ejemplo, si imaginemos que necesito escribir cada 1mS en una memoria de 512K, no podria lograrlo con ninguna EEPROM, ya que lo minimo que dan es 3mS de escritura. Pero si la divido en 4 memorias de 128K:
t 0ms: escribo en mem1
t 1ms: escribo en mem2
t 2ms: escribo en mem3
t 3ms: escribo en mem4
t 4ms: puedo volver a escribir en mem1

He encontrado esto haciendo mediciones de tiempo, y las mediciones para una escritura completa de la EEPROM me daban la mitad de tiempo que los calculos teoricos. Investigando me di cuenta que al escribir por paginas, estaba alternando bloques. Por lo que estaba escribiendo practicamente 2 paginas a la vez, eso es 256 bytes en unos 6-8mS, cuando una libreria normal escribe un byte en 5mS  :shock:
La gente ve las cosas que existen y se pregunta por qué.
Yo prefiero imaginar lo que no existe y preguntarme por qué no.


 

anything