Skip to content

Memoria RAM serie por SPI

Juan Gonzalez-Gomez edited this page Jul 19, 2024 · 37 revisions

Introducción

Para el cuaderno técnico 17: Periféricos PMODs quiero poner en marcha, parcialmente, el PMOD QQSPI PSRAM de 32MB. El objetivo inicial es leer el identificador del chip. Este es el ejemplo que se incluirá en el cuaderno técnico 17. En el futuro se desarrollará un controlador completo para leer y escribir bytes/half words y words en la memoria (y poder usarlo con diferentes CPUs). Esto nos da.... RAM prácticamente ilimitada!! De hecho, con este módulo se puede correr Linux!!!

Esta es la hoja de datos de referencia que vamos a utilizar para obtener todas las temporizaciones y datos necesarios:

2024-07-04: Jueves, 4 de Julio de 2024

La idea es reutilizar los componentes ya creado en el CT.15: Memoria flash SPI. Parece que las temporizaciones son similares... y en principio se podría intentar utilizar directamente los controladores ahí creados, pero prefiero ser cauto y empezar a desarrollar desde cero...

El Objetivo es leer el identificador de la memoria. Pero hay que hacerlo desde la placa Icebreaker. Y por tanto tengo que verificar que todos los componentes de depuración funcionan... Esto me servirá también para sistematizar, documentar y estructurar mejor todos los módulos

Ejemplo 1: Inicializar el escenario de trabajo

Utilizamos como placa base la icebreaker a la que le conectamos la memoria RAM en el PMOD1A y los 8 leds en el PMOD1B. Usaremos los botones y leds del PMOD2 integrado para hacer pruebas

Lo primero es apagar todos los LEDs, conectar las placas y dejarlo todo listo y funcionando para comenzar el desarrollo

Este es el circuito que cargamos en la icebreaker:

Y este es el escenario de trabajo:

¡¡Todo Listo para empezar!!

Ejemplo 2: Puesta en marcha del puerto serie

El siguiente hito es comprobar que el puerto serie del SPI funciona correctamente. Lo voy a probar con la velocidad estándar de 115200 baudios, que es conservadora. Utilizamos el bloque iceSerial/TX/Serial-TX-sys

Al apretar el pulsador B1 de la icebreaker (PMOD2) se envía el carácter A por el puerto serie, y también se muestra en los LEDs. De esta forma también ponemos en marcha el PMOD de LEDs y nos aseguramos que funciona correctamente

Para probarlo abrimos el terminal serie de Icestudio y seleccionamos el puerto /dev/ttyUSB1

Ahora apretamos el pulsador B1 varias veces... y ahí nos aparecen los caracteres 'A'... ¡El puerto serie funciona!

Además, en los LEDs vemos que aparece el número binario 01000001 (0x41) que efectivamente es el carácter A:

Ejemplo 3: Puesta en marcha del LEDoscopio

Vamos a poner en marcha el LEDOscopio de 8 LEDs, para asegurarnos que funciona, y sobre todo para asegurarnos que somos capaces de generar una señal conocida en la icebreaker. La señal más sencilla posible es dividir entre 2 la señal del sistema, que es de 12Mhz, lo que nos genera una señal de 6Mhz

Esta es la señal que mediremos en el LEDoscopio. El patrón que se debe observar es 01010101

Este es el circuito:

Y esto es lo que se muestra en los LEDs:

Efectivamente observamos el patrón correcto en los LEDs. Hemos generado una señal de 6Mhz... Esta será nuestra señal de referencia para usarla con iceRok...

Ejemplo 4: Puesta en marcha de iceRok

Ahora que ya tenemos garantizada la generación de una señal de referencia conocida (6Mhz), vamos a medirla con el analizador lógico interno del proyecto iceRok. Como es una señal "rápida" (Tiene de periodo 2 ciclos de reloj), no necesitamos tomar muchas muestras para visualizarla. Así que vamos a tomar sólo 16 muestras. Esto nos permitirá ver aproximadamente 8 periodos de la señal

El circuito es este:

Lo cargamos en la icebreaker y ahora hay que ejecutar el programa servidor que reciba las muestras en el PC. Esto hay que sistematizarlo y documentarlo, es lo que incluiré en el proyecto iceLab... pero de momento voy a documentar cómo hacerlo con lo que hay

El servidor a ejecutar es el programa pyicerok-16B-loop.py que se encuentra dentro del Cuaderno técnico 9 de Memorias.

Lo ejecutamos directamente desde el VSCODE, desde el modo DEBUG:

Al pinchar en el botón "Play" de Debug, se ejecuta el programa y se nos abre la ventana del terminal donde aparecen los mensajes del programa servidor

Ahora apretamos el pulsador B1 de la icebreaker, se realiza la captura, se envía al PC, la recibe el servidor y se genera el fichero data.raw con las 16 muestras

Ahora lo vamos a visualizar con el programa PulseView. Lo lanzamos. Posiblemente al ejecutarlo aparezca la última sesión abierta. Aparezca lo que aparezca lo primero que hacemos es crear una sesión nueva pichando en el icono indicado:

Se nos crea una sesión nueva en blanco.

Ahora importamos el fichero data.raw:

Seleccionamos el fichero data.raw y pinchamos en open:

Ahora ponemos la frecuencia del reloj del sistema de la placa que estamos usando, que en este caso es de 12Mhz. Lo escribimos en Hz: '12000000', y pinchamos en OK

Se nos muestran las señales. La que viene con el canal 0 es la señal de disparo, que sólo está activa un ciclo. La del canal 1 es la señal de 6 Mhz, que la reconocemos porque está un ciclo a 1 y un ciclo a 0.

Note

Ojo! La señal capturada puede ser diferente a esta: que presente un desfase de 180 grados. Esto es debido a que la captura NO es síncrona. Se realiza al apretar el pulsador, por tanto en el primer ciclo de captura la señal puede estar bien a 0 (como en mi caso) o bien a 1. Al volver a capturar la señal, puede ser la misma que esta o la desfasada

Ahora lo configuramos para verlo mejor:

  • Oculatar los canales no usados (canales del 2 al 8)
  • Poner nombre a las señales
  • Añadir el decodificador Timing para mostrar la frecuencia y el periodo de la señal medida

Ahora ya comprobamos que efectivmamente la señal generada es de 6 MHz

Realizando una segunda captura

Lo bueno es que una vez que se ha tomado la primera captura, y tenemos todo configura, ahora para tomar otra medida la cosa es muy fácil. Sólo hay que hacer lo siguiente:

  1. Apretar el pulsador B1 para tomar una nueva captura
  2. Apretar en iceRok el botón de Reload

Ejemplo 5: Envío de un byte por el SPI

Vamos a transmitir un byte por el SPI, basándonos en los transmisores hechos para la memoria Flash, y lo vamos a medir con IceRok en la icebreaker, a ver qué pasa...

El byte que se envía es 0xAA al apretar el botón B1:

Generamos la captura igual que antes, y configuramos ICEROK para ver las señales. Usamos el decodificador del SPI para ver qué se está enviando...

Todas las señales son correctas. Efectivamente comprobamos que por MOSI sale el byte 0xAA... PERO el decodificador NO está funcionando
Y es que hay un problema: nos falta la señal de reloj. Pero no la podemos incluir porque es justo a la frecuencia la que muestreamos... si la incluimos leeríamos unas señal que está siempre a 1...

Es decir, que para poder usar el decodificador del SPI necesitamos incluir la señal de reloj, como un canal más...

Ejemplo 6: Envío de un byte por el SPI (II)

La señal de reloj la incluiremos por Software. Pero es necesario dejar un canal libre para colocarla ahí. Seleccionamos el canal 0. Es necesario ponerlo a '1':

Ahora ejecutamos el script CT9-Memorias/software/16B/pyicerok-16B-clk-loop.py. Es igual que el anterior, pero añade la señal de reloj en el canal 0. Al importar el fichero data.raw en el Pulseview hay que indicar que la frecuencia de muestreo es de 24Mhz

Este es el resultado. Ahora el decodificador ya sí que funciona. Vemos que la primera señal es la de reloj. Para comprobar que está todo ok hemos calculado su frecuencia en la parte inferior: freq_clk. Efectivamente es de 12Mhz.

También podemos ver que el byte transmitido por el SPI es el 0xAA

El SPI nos funciona a nivel teórico.. pero no sabemos todavía si la memoria recibe lo enviado...

2024-07-05: Viernes, 5 de Julio de 2024

Ejemplo 7: Transmisión de 2 bytes consecutivos

Vamos a transmitir más de un byte. Esto se puede hacer fácilmente conectando en cascada bloques SPI-write-bytes. Comenzamos transmitiendo sólo 2 bytes, por lo que conectamos en cascada dos bloques SPI-write-Bytes.

Este es el circuito:

Se envía el dato 0x01 y luego el 0x02. Lo medimos con icerok. Este es el resultado:

Vemos en la señal de MISO que el primer byte que se transmite es efectivamente 0x01. El segundo byte no se puede ver porque faltan muestras, y por tanto el decodificador no lo muestra. Aunque viendo directamente las señales se intuye que efectivament el segundo bytes podría ser 0x02

Tenemos un problema de resolución: Es necesario utilizar un icerok de más muestras. 16 se queda corto...

Y aquí es donde viene el problema... he probado con el icerok de 16KB y se obtiene un mensaje de error en la icebreaker...

No tengo muy claro cuál es el problema. Intuyo que podría ser porque no hay suficiente espacio en la FPGA... Lo que voy a hacer es utilizar un icerok de menor tamaño... el problema es que hay que crearlo... en icerok sólo tengo de 16B y 16KB...

2024-07-06: Sábado, 6 de Julio de 2024

Ya tengo listo un icerok de 32B para retomar las pruebas

Ejemplo 8: Transmisión de 2 bytes consecutivos (II)

Es el mismo ejemplo que el 7, pero con un icerok de 32B... Ya no se produce el error de síntesis. Todavía no lo tengo confirmado, pero casi seguro que es por el tamaño

Este es el circuito:

Y este es el resultado de la medición:

Se transfieren los bytes 0x01 y 0x02... El protocolo es correcto. Sin embargo todavía no podemos estar seguro de que esos bytes se hayan recibido en la memoria

Lectura del identificador del chip

El comando de lectura de identificación (READ_ID) tiene el código 0x9F y está documentado en la página 15 de la hoja de datos de la memoria. El formado del comando es:

  • READID: 9F Dummy2 Dummy1 Dummy0, donde Dummyx son bytes de rellono, que pueden tener cualquier valor. Yo voy a utilizar el 0

El formato de esta instrucción es el mismo que el de leer la memoria, donde se especifica el comando y luego los 3 bytes de la dirección

Por tanto, para hacer esta primera prueba hay que enviar 4 bytes por el SPI. Al terminar de enviar el último, se recibe una respuesta de 8 bytes, con este formato:

  • Byte 1: 0x0D
  • Byte 2: 0x5D
  • 6 bytes: Identificador (EID)

Basta leer el primer byte para tener feedback de si está funcionando o no... es la prueba definitiva....Pero necesito al menos medir 4 bytes del comando y 1 byte de la respuest: 5 bytes: 40 bits.... es decir, que hay que tomar 40 muestras. Con el icerok de 32B no nos sirve, tenemos que crear el icerok de 64B

Para leer el identificador completo necesitamos 4 + 8 = 12 bytes = 96 bits = 96 muestras... Necesitamos icerok 128B

2024-07-07: Domingo, 7 de Julio de 2024 (Viva San Fermin!)

Ejemplo 9: Comando READID

Esta es la prueba de fuego. Vamos a enviar el comando READID junto a sus 3 dummy bytes, y luego un byte adicional para leer de momento sólo el primer byte devuelto. Según la hoja de datos este byte debe ser 0x0D

Este es el circuito:

Comprobamos con Pulseview el resultado....

Siiiiii!!!!!!! FUNCIONA!!!! Vaaaaamos!!! Se lee correctamente el byte 0x0D. Esto significa que la temporización del SPI es correcta, y que el módulo pmod está funcionando bien. Las conexiones y todo están ok!!! Esto es un gran logro!!!!

Ejemplo 10: Comando READID (II)

Vamos a confirmar estos buenos resultados leyendo 2 bytes del READID, que según el datasheet deben ser 0x0D y 0x5D
El circuito es igual que el del ejemplo 9, pero transmitiendo un byte adicional para recibir la respuesta:

Y este es el resultado:

Ejemplo 11: Comando READID (III)

En este ejemplo leemos el identificador completo: los 6 bytes. Este es el circuito:

Y este el resultado:

El identificador completo es: 0x0D5D52F60837

Los dos primeros bytes son siempre 0x0D y 0x5D. Los 4 bytes siguientes son el identificador del chip de RAM. En este caso es:

  • 0x52F60837 --> 52 F6 08 37

Lo más interesante es que este identificador es para cada chip de RAM. En el PMOD hay 4 chips de ram independientes. He probado a cambiar las señales de CS para leer el ID de cada bloque, y este es el resultado:

Bloque Identificador
0,2 52 F6 08 37
1,3 52 F6 08 39

Los bloques 0 y 2 tienen el mismo identificador. Y lo mismo para los bloques 1,3... Esto me resulta muy muy extraño. Creo que deberían tener identificadores diferentes cada uno... puede ser que haya algún error. Bien que el chip select no se esté enviando correctamente a la memoria (por un problema de pinout) o por cualquier otro problema... es algo a tener en cuenta....

2024-07-08: Lunes, 8 de Julio de 2024

Ejemplo 12: Mostrar READID en los LEDs

Este ejemplo se puede probar sin necesidad de usar Pulseview. El primer byte del identificador (0x0D) se lee en un registro y se muestra en los LEDs

Este es el resultado:

Efectivamente vemos el valor 0x0D en los LEDs

Ejemplo 13: READID: Mostrar 4 bytes del identificador en los LEDs

Repetimos pero con la lectura de los 4 bytes, que se muestra en los LEDs. Con el pulsador B2 se selecciona el byte del identificador a mostrar

Comprobamos que efectivamente salen los 4 valores correctos: 0x0D, 0x5D, 0x52 y 0xF6

Ejemplo 14: Bloque READID4

Toda la lógica para la lectura del identificador del la RAM la integramos en el bloque READID_4. En vez de leer los 6 bytes, se leen sólo 4, para poder mostrarlos fácilmente en los LEDs

El funcionamiento es el mismo que el ejemplo 3, pero todo queda más ordenado

Con esto se termina esta primera parte del log... El objetivo era comprobar el PMOD de la RAM, para usarlo como ejemplo en el Cuaderno técnico 17 sobre PMODs. Queda pendiente hacer un controlador específico para la RAM por SPI (lectura y escritura de datos)

2024-07-19: Jueves, 19 de Julio de 2024

Hoy he hecho el ejemplo para probar la placa QQSPI-RAM en la placa ULX3S, que en principio va a 25Mhz, y no he conseguido que funcione. No lo he probado a fondo, pero no lo he conseguido poner en marcha. Lo dejo apartado. Es algo que tendré que depurar poco a poco para encontrar el problema. No parece un tema de frecuencias porque en la go-board sí ha funcionado, que también va a 25Mhz...

Clone this wiki locally