Estrategia de doble búfer de FPGA

Estoy trabajando en un proyecto de FPGA en el que una CPU host escribe una tabla de búsqueda de 10 240 x 16 bits en la lógica de FPGA. Para implementar esto, he utilizado la memoria en el chip para almacenar los valores y leerlos cuando esté listo.

Un pulso de activación/arranque externo inicia un ciclo de procesamiento que dura varios cientos de miles de ciclos de reloj. Una vez que obtengamos este disparador, el estado de la LUT de 10,240x16 debe congelarse o bloquearse, para que pueda utilizarse durante el ciclo de procesamiento. Desafortunadamente, los datos deben estar disponibles bastante pronto después de este pulso "GO", por lo que no hay suficiente tiempo para hacer una copia de búfer completa.

El host también debe poder actualizar continuamente algunos valores de la tabla de búsqueda mientras se ejecuta el ciclo actual, a fin de configurar el siguiente ciclo de procesamiento. Para permitir ambos casos (bloquear el estado de la tabla de búsqueda, pero también dejar que el host la actualice cuando quiera), creo que el estilo ping/pong de doble búfer es el camino a seguir: el host escribe en un búfer hasta que lleguemos a Comando "GO", luego el host escribe en el otro. La lógica FPGA siempre lee del búfer en el que no se escribe.

Sin embargo, debido a que el host no está reescribiendo todos los valores de 10,240x16 cuando realiza sus actualizaciones esporádicas, el búfer en el que no se está escribiendo básicamente está "descartando" las actualizaciones mientras está congelado.

¿Hay una forma novedosa de manejar este escenario? Estoy pensando que debe haber algún tipo de proceso de resincronización del búfer una vez que el búfer se descongele.

¿Existen razones de poder por las que no puede tener un búfer de "configuración" dedicado que esté bloqueado en el búfer de "procesamiento" en el pulso GO?

Respuestas (2)

Una posible estrategia podría ser utilizar bits obsoletos. No sé si esa es la terminología estándar, pero es similar a un poco sucio. Escribir una nueva entrada borrará el bit obsoleto correspondiente en el búfer desbloqueado y establecerá el bit en el búfer bloqueado. Después de cambiar de búfer, haga que una rutina de copia interna transfiera cada entrada marcada como obsoleta en el búfer desbloqueado del búfer bloqueado al búfer desbloqueado. De esta forma, los nuevos datos escritos mientras la copia está en curso no se sobrescribirán, y todas las actualizaciones antiguas deberían conservarse. Lo único que debe hacer es asegurarse de que haya suficiente tiempo para que se complete la operación de copia entre los cambios de búfer, o necesita algún tipo de optimización para realizar un seguimiento de qué entradas están obsoletas para que no tenga que iterar sobre todas. ellos, haciendo que la operación de copia sea más rápida.

Otra estrategia posible podría ser almacenar las entradas actualizadas mientras un solo búfer está bloqueado y luego aplicar solo esas actualizaciones cuando esté desbloqueado. Si solo se actualizan un puñado de entradas, entonces esto podría ser más eficiente. Las actualizaciones se pueden almacenar como una lista vinculada o una estructura de datos similar para que la lista se pueda recorrer de manera eficiente mientras se pueden fusionar múltiples actualizaciones en la misma ubicación.

A menos que haya razones poderosas para no hacer esto, optaría por esto:

  • Tener dos buffers de 10240*16 bits
  • Complete uno de ellos usando la CPU, llámelo el búfer de "configuración"
  • En Go Pulse, copie el búfer de "configuración" en el búfer de "procesamiento", no hay razón para que esto no se pueda hacer en un ciclo de reloj (excepto por los posibles problemas de energía antes mencionados). Si puede enviar su pulso GO un ciclo antes de que realmente comience el procesamiento de los datos, puede incluso evitar muxing adicionales donde el búfer de configuración se usa para el primer ciclo del procesamiento de datos. Esto también permitiría la actualización y el procesamiento consecutivos (sin embargo, probablemente requeriría algo de trabajo en el espacio del kernel de la CPU).
Eso solo funcionaría si los búferes estuvieran en RAM distribuida donde podría tener transferencias totalmente paralelas para que funcionen en un reloj, pero es MUCHA área y probablemente se atragante con el enrutamiento.
@DanMills Claro, pero asumiendo RAM distribuida, esta es menos área que tener dos búferes completos donde cada bit debe ser multiplexado.