¿Cómo es posible controlar pantallas VGA a frecuencias de reloj de píxeles tan altas?

Estoy trabajando en un circuito digital usando componentes discretos para controlar una pantalla VGA de 640x480 en un modo de texto de 80x30.

Para una pantalla de 640x480, el reloj de píxeles es de 25,175 MHz, que tiene un período de alrededor de 40 ns. No entiendo cómo se supone que puedo proporcionar un nuevo píxel a la pantalla con tanta frecuencia.

La arquitectura básica de mi circuito es la siguiente:

  1. El contador binario para píxeles horizontales cuenta hasta 25,175 MHz hasta 800 (640 píxeles visibles + 160 para porche delantero, sincronización, porche trasero). En 800, incrementa el contador de líneas verticales (y reinicia en 525 líneas)

  2. Utilizando la posición horizontal y vertical, obtenga la coordenada x,y del carácter actual.

  3. Usando la coordenada x,y del carácter, indexe en la memoria de video para recuperar el carácter ASCII.

  4. Use el carácter ASCII para indexar en la ROM de caracteres para obtener un patrón de bits para el carácter

  5. Use el registro de desplazamiento paralelo al serial para convertir una línea de caracteres de 8 píxeles en bits individuales a la frecuencia del reloj de píxeles

Si sigue la cadena, dice: Contador -> RAM -> ROM -> Paralelo al registro de desplazamiento en serie

Usando los componentes más rápidos que puedo encontrar, los retrasos de propagación y el tiempo de acceso suman alrededor de 15 ns + 20 ns + 70 ns + 15 ns = 120 ns, mucho más que el período de 40 ns para 25 MHz.

Con resoluciones y frecuencias de actualización aún más altas, puede tener relojes de píxeles muy por encima de 100 MHz, lo que será un período de 10 ns.

¿Cómo es posible proporcionar nuevos píxeles a la pantalla cada 10 ns cuando solo el tiempo de acceso a RAM/ROM ya está muy por encima, sin siquiera considerar todas las demás señales en su sistema?

Utiliza una memoria RAM de video dedicada y la registra directamente en su señal de video. Trabajas para averiguar qué mostrar mucho antes de que realmente lo muestres.
Ve a leer sobre Maximite . Solo usa el hardware periférico de una MCU y algunas resistencias para controlar un puerto VGA. Comience examinando el periférico PIC32 que usa. Funciona bien. (Tengo un Maximite aquí.)
"El video recetario barato" de "Don Lancaster"

Respuestas (4)

Hay dos razones principales por las que encuentras esto desafiante.

En primer lugar, está utilizando piezas más antiguas y más discretas (integración a menor escala) de las que se habrían utilizado para hacer esto en la era de VGA.

Pero luego, los estás usando de una manera atípica. Específicamente, su enfoque no es pipelinedlo que significa que tiene que sumar múltiples retrasos al determinar su intervalo y, por lo tanto, la tasa.

Por el contrario, los diseños digitales sincrónicos que intentan alcanzar la velocidad intentan hacer lo menos posible entre registros.

Si bien los detalles probablemente difieran un poco, en términos generales, funcionaría de la siguiente manera:

  • Incrementas o restableces la dirección, luego eso va en un registro.
  • Guardas la dirección en la memoria síncrona
  • Bloqueas la salida de la memoria síncrona
  • Engancha esto en la dirección del generador de caracteres síncronos
  • Enclava la salida del generador de caracteres en el registro de salida
  • aplicar la paleta de búsqueda...
  • en el DAC síncrono...

Cuando desglosa una tarea como esta, solo obtiene un retraso combinatorio más un retraso de propagación y registra los tiempos de configuración y espera que deben ajustarse entre los relojes.

Un diseño construido de esta manera necesitará muchos relojes para producir una salida: la latencia será en realidad más alta que un diseño puramente combinatorio. Pero produce una nueva salida correcta en cada ciclo de un reloj mucho más rápido.

Y oye, es video, realmente no importa si el CRT está dibujando una docena de píxeles detrás del contador de píxeles; por supuesto, debes tener eso en cuenta en la sincronización de las señales de sincronización para que sean correctas en comparación con cuando los datos realmente sale del DAC.

En la práctica, casi todos los sistemas digitales complejos funcionan de esta manera, ya que es una gran idea, justo hasta que una CPU canalizada llega a una dependencia de un resultado computacional anterior o una rama condicional... Entonces las cosas se ponen interesantes, ya que hablarían de en la próxima lección de una clase de sistemas digitales, pero afortunadamente su situación VGA es mucho más simple, especialmente si aún no se preocupa por los efectos de desgarro si el búfer de caracteres cambia mientras se dibuja la pantalla.

Como cuestión práctica, si desea construir esto, hágalo en un FPGA. Eso prácticamente forzará las memorias sincrónicas si usa las internas, o los registros IO sincrónicos si usa la memoria externa. Obtendrá muchos empujones hacia un diseño adecuado, la tela en sí será más rápida que sus partes discretas y, por supuesto, si comete un error, solo necesita girar los pulgares mientras se vuelve a compilar en lugar de pasar un largo día volviendo a cablear. .

"especialmente si aún no se preocupa por los efectos de desgarro si el búfer de caracteres cambia mientras se dibuja la pantalla"; es por eso que desde los primeros días de los coprocesadores de video, los coprocesadores tenían una forma de informar al proceso principal que no son actualmente descargan su memoria en la pantalla y si quieren cambiar el búfer de video, deben hacerlo ahora.
Creo que estás complicando demasiado esto. Ya dijo que está usando un registro de desplazamiento de 8 bits que genera un reloj de un bit por píxel. Presumiblemente, este es un registro de desplazamiento de 8 bits con pestillo. Eso significa que solo tiene que buscar un nuevo byte una vez cada reloj de 8 píxeles, por lo tanto, una tasa de 3.125 MHz. Eso le da todos los 320 ns para llevar los datos al pestillo del registro de desplazamiento, que es mucho más largo que los 120 ns que dijo que tomaría.
Para un caso monocromático de baja resolución muy simple, sí, la sincronización de los bytes no sería demasiado desafiante, pero una parte clave de la pregunta era que el autor de la pregunta estaba tratando de comprender cómo funciona el rendimiento de los sistemas "reales" típicos de resolución no trivial. es posible. Y la respuesta es la misma que para todos los demás sistemas digitales útiles: tecnología más rápida y diseño síncrono canalizado.

Aparte de la canalización (que es en gran medida lo que debe hacer), se está perdiendo algo importante...

Los relojes de registro de desplazamiento de entrada en paralelo y salida en serie se puntean a 25 Mhz impares, claro, pero si sus caracteres tienen, digamos, 8 píxeles de ancho, su entrada es de solo ~ 3.2MHz, que está fácilmente al alcance de la serie LS de la era VGA, por todo eso necesita tener el siguiente byte listo cuando el registro de desplazamiento termine con el actual (aquí es donde entra la canalización).

Genere un reloj de píxeles a ~ 25 MHz y un reloj de memoria a 1/8 de eso para controlar el búfer de texto y la ROM CG, luego canalice esa memoria y el acceso a la ROM CG.

Un truco más, la salida del búfer de texto se repetirá para cada línea dentro de cualquier línea de texto dada, por lo que tal vez podría registrar los 80 bytes de texto en un búfer de anillo y luego dejar de leer el RAM para las próximas 7 líneas (suponiendo un 8 carácter de línea), esto le permite liberar la memoria para que la use la CPU, a costa de necesitar 80 bytes de RAM colgados en el costado de la cosa.

Usando los componentes más rápidos que puedo encontrar, los retrasos de propagación y el tiempo de acceso suman alrededor de 15 ns + 20 ns + 70 ns + 15 ns = 120 ns, mucho más que el período de 40 ns para 25 MHz.

Olvida que un adaptador de gráficos nunca dibujaría un solo píxel, sino al menos una línea de escaneo completa. Por lo tanto, este sería un problema completamente canalizable.

Además, no olvide que ha habido cinco décadas de hardware de producción de video hasta el momento. Su problema generalmente se resolvería con un tipo especial de RAM, en el que procesa sus letras en un puerto, y que se lee secuencialmente en un DAC de señal de video. Ese hardware es mucho, mucho más rápido de lo que estás viendo.

La arquitectura básica de mi circuito es la siguiente:

  1. El contador binario para píxeles horizontales cuenta hasta 25,175 MHz hasta 800 (640 píxeles visibles + 160 para porche delantero, sincronización, porche trasero). En 800, incrementa el contador de líneas verticales (y reinicia en 525 líneas)

  2. Utilizando la posición horizontal y vertical, obtenga la coordenada x,y del carácter actual.

No, ¿por qué harías eso? Simplemente colocaría su píxel de fila en un área contigua de memoria y lo enviaría linealmente a su DAC; si se trata de una implementación de CPU / MCU, ni siquiera dejaría que su CPU haga eso, pero una unidad DMA, programada no hacer nada más que tomar un valor tras otro y ponerlo, por ejemplo, en un puerto de datos paralelo, sin ninguna interacción con el núcleo de la CPU.

  1. Usando la coordenada x,y del carácter, indexe en la memoria de video para recuperar el carácter ASCII.

Ah, desea renderizar sobre la marcha: una buena opción, pero inusual a los costos modernos de RAM. En su lugar, simplemente convertiría el carácter en un búfer de cuadros de antemano, o si su dispositivo es extremadamente delgado, canalice directamente (vea mi explicación de DMA arriba) la fila de caracteres al DAC.

Mientras que las cosas modernas tienden a preferir los framebuffers renderizados previamente, obviamente son una mala elección si estás tratando de trabajar sin mucha RAM. Si está haciendo esto en un FPGA, puede hacer que la máquina de estado DMA tome direcciones del mapa de celdas de caracteres y luego lea de los glifos de caracteres correspondientes.
totalmente de acuerdo aquí! por lo tanto, mi sección de respuesta a la tercera pregunta.

Así que obviamente eso no funciona; necesitas una tubería.

1) Almacene los caracteres de forma contigua en la memoria. Comience en la parte superior izquierda.

2) Obtener un carácter durante el intervalo de borrado. Continúe buscando caracteres en orden de memoria.

3) Canalice cada carácter decodificado más el índice de línea en la ROM.

4) Canalice la salida de la ROM en un búfer.

5) Canalice el búfer en un registro de desplazamiento. Lea los píxeles continuamente a intervalos de 40 ns a partir de esto.

(Eso implica que debe cargar un nuevo carácter en el registro de desplazamiento cada 320 ns, lo que incluso podría ser factible sin canalizar todo el resto del sistema).

6) Durante el borrado horizontal, regrese al comienzo de la línea o avance al siguiente carácter (es decir, al comienzo de la siguiente línea).

Característica adicional: dado que solo necesita un carácter cada 320 ns, también puede leer un par de carácter + color y hacer caracteres de color de estilo MSDOS o Spectrum-Style.