Esta página es la segunda de una serie de cinco sobre cómo convertirse en diseñador profesional de FPGA. En esta página repasaré los distintos temas teóricos que, en mi opinión, todo diseñador de FPGA debería dominar, e intentaré también explicar por qué creo que cada uno es importante.
Teoría de la lógica
Conocer la teoría no es un lujo. Es el cimiento sobre el que se construye todo lo demás, aunque no siempre lo parezca mientras luchas con una herramienta de desarrollo que se niega a cooperar o cuando la electrónica parece ir a su aire.
Un ingeniero de FPGA tiene que ser capaz de imaginar cómo se implementa en elementos lógicos el Verilog que escribe. No con todo lujo de detalles, pero sí lo suficiente para saber qué partes ralentizarán el diseño y limitarán la frecuencia de reloj y qué partes van a devorar una gran cantidad de recursos lógicos. Esto es crucial, por ejemplo, para saber cuándo hay que segmentar (pipeline) una tarea añadiendo registros. Segmentar suele complicar el diseño, pero a menudo es la única forma de alcanzar la velocidad requerida. Otras decisiones importantes de diseño dependen de esta capacidad.
También es crucial para entender lo que ha hecho el sintetizador (synthesizer) y por qué. Por ejemplo, saber qué tiende a optimizar y eliminar el sintetizador te permite escribir código Verilog que sea a la vez legible y eficiente. Si no sabes qué hará el sintetizador con tu código, estás escribiendo a ciegas.
Entonces, ¿qué temas son relevantes? Te sugiero empezar por esta lista de comprobación. No pretendo que lo abarque todo.
- Puertas (AND, OR, XOR, etc.), biestables, ROMs/RAMs, LUTs y multiplexores. Estos son los bloques básicos de todo, y aparecen en las hojas de características, en las guías de usuario y cuando las herramientas explican lo que han hecho. No son complicados, y sin esto estarás perdido.
- Lógica combinacional (combinatorial logic) frente a lógica secuencial, y los principios del diseño síncrono. Son conceptos centrales en todo diseño lógico.
- Máquinas de estados (state machines). Entender cómo esos diagramas se convierten en código Verilog y tener una buena intuición de cómo funcionan las máquinas de estados. Casi siempre que una lógica se comporta como software, en el sentido de que hace algo secuencial que depende de señales de entrada, hay una máquina de estados detrás.
- Álgebra de Boole. Cosas como x && (y || z) = x && y || x && z, y la ley de De Morgan: !(x && y) = !x || !y. Se usan a menudo para simplificar el código. Es como conocer lo básico del álgebra.
- Aritmética de coma fija, complemento a dos, desbordamiento (overflow) y saturación. Cómo se implementan la suma y la resta. Entender el problema del retardo de propagación (propagation delay) del acarreo. También es recomendable echar un vistazo superficial a cómo se implementa un multiplicador. Las operaciones aritméticas están por todas partes en un diseño lógico, y necesitas saber cómo se comportan exactamente, cuántos recursos consumen y hasta qué punto ralentizan tu diseño.
Paradigmas y técnicas de la lógica
Además de conocer la teoría pura de la lógica, hay algunos temas necesarios para escribir código que funcione de forma fiable y eficiente y que se comporte igual en el hardware que en la simulación. Esto es casi una obviedad en el mundo del software, pero los diseñadores de FPGA vivimos en un territorio distinto. Todo diseñador de FPGA tiene que tener estos temas presentes, todo el tiempo.
El paradigma RTL (Register Transfer Level) es la idea central. En resumen, significa que todo lo que almacena un valor solo cambia ese valor en respuesta a un flanco de reloj. El reset asíncrono (asynchronous reset) puede ser la única excepción cuidadosamente controlada. Una vez interiorizas esta forma de pensar, tienes muchas posibilidades de hacerlo bien.
El reset es un tema en sí mismo. El reset síncrono (synchronous reset) frente al asíncrono, y la sincronización del reset, son cosas que hay que resolver en cada diseño en el que trabajes. Si no lo haces, tu diseño funcionará bien la mayor parte del tiempo, con fallos esporádicos de vez en cuando. Tendrás un sistema que funciona para entregar, pero no podrás publicarlo por culpa de esos fallos esporádicos. Tu proyecto se irá retrasando cada vez más y no tendrás ni idea de qué va mal. Un reset mal hecho no es lo primero que se te ocurre en esas situaciones. De verdad que quieres entender este tema a fondo. Hay una breve serie de páginas sobre este tema en este sitio.
Muy relacionada está la cuestión de las habilitaciones de reloj (clock enables) frente a los relojes conmutados (gated clocks). En esencia son dos técnicas distintas para hacer que el diseño lógico no se ejecute en cada ciclo de reloj. La FPGA tiene recursos dedicados para la conmutación de reloj, pero hacerlo mal es una forma estupenda de crear problemas misteriosos. La costumbre segura es usar habilitaciones de reloj en tu lógica, pero si quieres aprovechar la menor frecuencia de reloj efectiva para relajar las restricciones de temporización (timing constraints), puede que también te topes con problemas.
La codificación de la máquina de estados también merece una mención. La codificación binaria, one-hot y Gray tienen su sitio, y la elección influye tanto en la velocidad como en el uso de recursos. Las herramientas a menudo elegirán por ti, pero deberías saber qué significan las opciones. En particular, la codificación one-hot es excelente para máquinas de estados grandes. Pero si la máquina de estados no se resetea correctamente, esta codificación puede hacer que haga cosas imposibles según el código Verilog. Parece brujería dentro de la FPGA o un fallo del sintetizador, pero no: es la codificación one-hot y un reset mal aplicado.
Y por último, la segmentación (pipeline). No es solo una técnica, es una forma de pensar. Añadir un registro es trivial, pero el hecho de que el procesado de los datos abarque varios ciclos de reloj crea toda una plétora de posibles problemas. Por ejemplo, ¿qué debería hacer el receptor de estos datos antes de que hayan llegado las primeras piezas de datos válidos? ¿Qué pasa si los datos de entrada no llegan de forma continua y la segmentación tiene que congelarse temporalmente? ¿Cómo se hace eso sin una única habilitación de reloj con un fan-out enorme hacia todos los elementos lógicos de la cadena de procesado?
Cada escenario requiere un tipo distinto de diseño de segmentación, y cada uno tiene sus propios retos y peculiaridades.
Diseño digital
Como se ha comentado más arriba, un diseñador de FPGA necesita imaginar cómo acaba el Verilog convertido en elementos lógicos dentro de la FPGA. Veamos un ejemplo concreto.
Supongamos que escribo un simple "assign x = a + b;" en Verilog. ¿Cómo se implementa ese sumador? ¿Usa esta FPGA concreta trucos especiales para implementar un sumador? ¿Usa un bloque hard dedicado, un bloque DSP, por ejemplo? Y si lo hace, ¿cuánto ayuda eso si el resultado del sumador va a un registro en lugar de a un cable? ¿Qué pasa si sumo tres números, como en "assign x = a + b + c;"? ¿Tiene la FPGA algún atajo para sumar tres números? Lo más probable es que no, así que esto se implementará como algo del estilo (a + b) + c. Dos operaciones lógicas en cascada pueden ralentizar el diseño considerablemente. Pero, ¿cómo funciona en tu FPGA concreta?
Ser consciente de los recursos disponibles en la FPGA concreta con la que trabajas es importante. Determina la forma en que escribes Verilog, para que el sintetizador (synthesizer) pueda crear una lógica rápida y eficiente. El sintetizador no es un mago que resuelva cualquier problema que le eches. Si escribes Verilog con criterio y conoces sus limitaciones, obtienes un diseño que consume pocos recursos, usa relativamente poca energía y funciona a una frecuencia de reloj alta. Eso viene con la experiencia, y los conocimientos de diseño digital aceleran el proceso de adquirir ese tipo de sabiduría.
En la práctica, normalmente nos vemos obligados a mirar la implementación lógica dentro de la FPGA solo cuando resolvemos problemas de cierre de tiempos (timing closure), porque necesitamos entender por qué el resultado era lento o consumía demasiados recursos. Así que repasamos los pequeños detalles de la implementación lógica, uno a uno, buscando un derroche de tiempo o de recursos. Pero también es buena idea analizar los resultados por voluntad propia de vez en cuando, solo para adquirir más conocimientos o para verificar que no ha pasado nada raro. Eso da sus frutos a largo plazo.
Conoce tu FPGA
Otro aspecto de imaginar cómo se convierte el Verilog en elementos lógicos tiene que ver con los recursos lógicos básicos. Es decir, conocer los bloques constituyentes de la FPGA y saber para qué sirven.
En primer lugar, la estructura de la propia FPGA: los elementos básicos, los CLB y los slice de tu FPGA. Cuando escribes código Verilog, es una ventaja enorme poder imaginar cómo se usan esos elementos para implementar la lógica necesaria. Así puedes saber si lo que escribes puede convertirse en el cuello de botella del cierre de tiempos (timing closure) del diseño o si este fragmento de código nunca volverá a darte problemas.
La mayoría de las FPGA están estructuradas más o menos de la misma manera. Todas tienen una forma eficiente de implementar sumadores aritméticos, multiplexores, demultiplexores, etcétera, porque estas funciones se usan mucho. Por ejemplo, muchas FPGA tienen multiplicadores como bloque hard, lo que permite que operaciones como "assign x = a * b;" funcionen a una frecuencia de reloj alta.
Pero las características exactas de estos multiplicadores pueden diferir. En algunas FPGA, los operandos de estos bloques multiplicadores son enteros con signo de 18 bits. Así que una multiplicación con operandos de esa anchura o menos funcionará a una frecuencia de reloj mucho mayor que si uno de los operandos tiene 19 bits de anchura. Ese es otro ejemplo de por qué es importante conocer los bloques lógicos de la FPGA: un solo bit de más puede reducir la frecuencia de reloj considerablemente. Ten en cuenta que, para otra familia de FPGA, puede ser un juego completamente distinto con otras anchuras de operando.
Los recursos de rutado también importan. Los elementos lógicos son solo la mitad de la historia; los cables que los conectan son la otra mitad. Muy a menudo, un diseño no alcanza su frecuencia de reloj porque el cableado se congestiona y por tanto se vuelve lento. Esto es algo en lo que hay que pensar si pretendes usar tu FPGA a cerca del 100% de su capacidad lógica y tienes muchos buses de datos anchos en tu diseño. Algunos recursos de rutado ("cables") de la FPGA son rápidos, otros son más lentos. Cuando se agotan los rápidos, todo el diseño se ralentiza.
También deberías entender los FIFO y las BRAM. Son los recursos de memoria dentro de la FPGA, y los usarás constantemente, tanto para almacenar y almacenar en búfer datos como para cruzar entre dominios de reloj (clock domains). Saber trabajar con un FIFO es el pan de cada día. Hay una serie de páginas en este sitio web sobre este tema.
Los recursos de reloj son un pequeño universo aparte: los PLL, los buffers de reloj y las redes de reloj globales y regionales. El PLL es el componente que convierte un reloj entrante en los relojes que tu lógica necesita realmente. Multiplica y divide frecuencias, y también permite desfasar la fase. También es importante saber qué relojes están alineados en fase y cuáles no. Por ejemplo, supongamos que un PLL emite dos relojes y uno tiene el doble de frecuencia que el otro: ¿es seguro que un registro cronometrado por el primer reloj muestree la salida de un registro cronometrado por el segundo? Normalmente sí, pero depende de si esos dos relojes están alineados en fase. Pero tienes que saber cómo comprobar que efectivamente es así. Hay una página en este sitio que trata este tema.
A continuación tenemos los recursos de los bloques de E/S. En diseños que implican señales de E/S de alta velocidad, como DDR y SERDES, necesitas entender qué pueden hacer por ti los bloques de E/S y cómo permiten E/S a velocidades de datos mucho más altas que la frecuencia de reloj de tu diseño. También se pueden configurar para ayudar con la adaptación de impedancias (impedance matching) añadiendo una terminación y otras funciones electrónicas de bajo nivel.
Y si necesitas velocidades de datos por encima de un gigabit por segundo, los transceptores multigigabit (MGT, Multi-Gigabit Transceiver) son para ti. Es un tema relativamente avanzado y complicado, y no es para principiantes, a menos que un proyecto concreto lo requiera. También hay una serie de páginas en este sitio web sobre ese tema.
Y un último tema algo aburrido: la memoria de configuración y la carga del flujo de bits (bitstream). Cuando termines de diseñar, no querrás cargar el flujo de bits en la FPGA desde el ordenador cada vez. Por eso la FPGA puede cargarlo por sí misma desde una memoria flash. O desde una tarjeta SD. O un componente aparte de la placa puede empujar el flujo de bits hacia la FPGA, una solución que se elige a menudo cuando hay un procesador independiente en la placa.
Las técnicas para cargar la FPGA no tienen nada de divertido, pero si trabajas en un equipo pequeño, tú también serás responsable de esto. Y a menudo hay requisitos sobre cuánto tarda la FPGA en estar operativa después de que la placa se haya encendido. Eso tendrás que resolverlo tú.
Otra razón para estar al tanto de este tema es que la FPGA a menudo se carga automáticamente desde la memoria flash al encenderse, en particular en las placas de evaluación. Para hacerlo aún más complicado, las placas de FPGA a menudo tienen más de una posible fuente de memoria desde la que cargar el flujo de bits. Hay sesiones de depuración larguísimas cuando la gente no se da cuenta de que tiene una versión antigua de su proyecto cargada en la FPGA: hagan los cambios que hagan en el diseño, la FPGA se comporta igual, porque actualizan la memoria flash equivocada cada vez.
Temporización
Primero, unas palabras sobre qué es la temporización (en el contexto de un diseño lógico). Para abreviar, toda señal digital dentro y fuera de la FPGA tiene que ser eléctricamente estable durante intervalos de tiempo concretos, normalmente en relación con los flancos de reloj. De lo contrario, el comportamiento de la lógica se vuelve impredecible. Las herramientas de desarrollo se encargan de que estos requisitos de temporización se cumplan donde haga falta. Pero para que eso ocurra, tenemos que dar a esas herramientas información concreta, y hacerlo con mucha precisión. Además, el diseño lógico también tiene que hacerse de manera que permita a las herramientas cumplir los requisitos de temporización. Esto es, en esencia, de lo que va la temporización.
La temporización es posiblemente la parte más difícil de ser diseñador de FPGA. También es el ámbito más importante que hay que dominar de verdad e implementar correctamente. Lamentablemente, es bastante fácil descuidar este tema y aun así conseguir un diseño que funcione razonablemente bien, o quizá solo lo justo para parecer que ya casi has terminado. Todo funciona bien, pero parece haber fuerzas sobrenaturales en la FPGA que provocan fallos repentinos cuando hay luna llena. Yo llamo a esa mentalidad el "modo magia negra", y hay una página aparte sobre ello.
En el mejor de los casos, un diseño de FPGA escrito sin pensar en la temporización se puede arreglar fácilmente añadiendo unas cuantas restricciones de temporización (timing constraints). A veces hay que rehacer casi todo desde cero, porque cuando se imponen los requisitos de temporización correctos, las herramientas de desarrollo no logran hacerlo funcionar a la frecuencia de reloj requerida. Así que hay que refactorizar el propio código Verilog.
Y en el peor de los casos, hay que rediseñar parte de la PCB, porque es imposible garantizar que todas las señales físicas de la placa tengan una tensión estable dentro de los intervalos en los que tienen que serlo. Un análisis de temporización en condiciones antes de aprobar la PCB para producción lo habría revelado, pero si se descuidó, puede que el hardware no esté a la altura de la tarea.
Pero si haces la temporización con cuidado y correctamente, la FPGA es el componente más fiable del mundo. Muchos diseñadores de FPGA tienen miedo de hacer el más mínimo cambio en el diseño, y miedo cada vez que se usa un lote nuevo de chips FPGA en producción. Se aplica una refrigeración exagerada, porque no faltaría más que la FPGA se calentara un poco. Yo digo: asegúrate de hacer bien la temporización y olvídate de todas las preocupaciones.
¿Necesitas dominarlo a la perfección desde el primer día? Yo diría que sí, de hecho. En principio, estaría de acuerdo en que, si estás en un equipo de diseñadores de FPGA y solo uno es realmente bueno en temporización, es suficiente. Sé tú ese diseñador de FPGA, por una sencilla razón: lo más probable es que no lo sea nadie más.
En este sitio web hay una bastante larga serie de páginas sobre temporización, así que no tiene mucho sentido extenderse aquí sobre los temas. Así que aquí va solo una breve lista de los temas que creo que todo diseñador de FPGA debería manejar con soltura:
- Retardo de propagación (propagation delay), tsu y thold
- Escribir restricciones de temporización (timing constraints): restricciones de periodo, relojes no relacionados (unrelated clocks), excepciones de temporización, caminos falsos (false paths), etc.
- Leer informes de temporización: setup, hold, margen (slack), interacción entre dominios de reloj (clock domains), caminos críticos (critical paths).
- Análisis de temporización de las cuatro esquinas (four-corner timing analysis)
- Frecuencia de reloj y fluctuación temporal (jitter), y las implicaciones de la fluctuación temporal para el cierre de tiempos (timing closure), la pérdida de enganche (lock), el muestreo deficiente de señales, la propagación de la fluctuación temporal en los PLL (degradación de los MGT debido a la fluctuación temporal, si tienes ese tipo de componentes en tu diseño).
- Recursos de reloj: el buffer de reloj, el árbol de distribución de reloj, el skew de reloj (clock skew), cómo compensa el PLL el retardo del árbol de reloj, etc.
- Cruce de dominios de reloj (clock domain crossing). Hay una serie de páginas específicamente sobre este tema.
Electrónica
La FPGA es un dispositivo electrónico, y el diseño de FPGA es un campo de especialización dentro de la ingeniería eléctrica. Y es el tipo de ingeniería eléctrica que trata con esquemáticos y hojas de características, tensiones y corrientes, integridad de señal y adaptación de impedancias. Son los conocimientos que todo diseñador de PCB debe tener, y la vida de un diseñador de FPGA es mucho más fácil si también cuenta con ellos.
Es habitual que al diseñador de FPGA se le asigne la tarea de garantizar que la FPGA se interconecte correctamente con los demás componentes de la placa. De hecho, no es raro que los diseñadores de PCB conecten a la FPGA todo lo que pueden para eximirse de la responsabilidad de hacer funcionar esos componentes. Por eso, a menudo se exige al diseñador de FPGA que comprenda a fondo cómo se interconectan los componentes entre sí en una PCB. A menudo es el ingeniero de FPGA quien tiene que señalar los posibles problemas de integridad de señal que hay que resolver en cables que conmutan a altas frecuencias.
Cuando se diseña una PCB nueva, normalmente se pide al diseñador de FPGA del equipo que apruebe el diseño, es decir, que compruebe que las conexiones a la FPGA son correctas. No todos los pines de la FPGA sirven para cualquier propósito y, en ocasiones, es necesario, o significativamente mejor, que ciertas conexiones pertenezcan a un grupo concreto ("bank") de pines de la FPGA. Son cosas que el diseñador de FPGA debe dominar, o al menos tener una opinión sólida al respecto.
No es raro que los diseñadores de FPGA sean antiguos diseñadores de PCB. El camino (path) del diseño de placas al diseño de FPGA es natural, y quienes lo han recorrido tienen una ventaja clara. Aunque el diseñador de PCB sea el responsable y diseñe una placa perfectamente buena, el diseñador de FPGA sigue necesitando entender los retos de las señales de alta velocidad en una PCB, para configurar correctamente los bloques de E/S de la FPGA.
Como he mencionado antes, no todos los ingenieros de FPGA trabajan directamente con la electrónica. Algunos se limitan a desarrollar lógica interna, lo que yo llamaba "lógica de procesado", y a veces las empresas contratan a un ingeniero solo para hacer simulación y verificación. Pero la mayoría trabajamos con la electrónica directamente y, en ese caso, se requieren algunos conocimientos relacionados.
Se empieza por lo más básico: tensión, corriente, resistencias y la ley de Ohm. Así es como las señales digitales pasan de un componente a otro. También es buena idea entender la capacidad eléctrica y cómo se carga y descarga un condensador. Esto te permite comprender cómo influyen la corriente y la capacidad en las velocidades de conmutación y el consumo de energía.
Deberías ser capaz de leer esquemáticos: circuitos integrados, módulos, inductores, fuentes de alimentación, masa digital y analógica. También aparecen a veces MOSFET y transistores bipolares, y al menos deberías reconocerlos. No está de más hacerse una idea de cómo se comporta un transistor MOSFET.
También pasarás mucho tiempo leyendo hojas de características. La parte más difícil e importante es traducir las especificaciones de temporización de la hoja de características a restricciones de temporización (timing constraints) y/o a un diseño de E/S adecuado en la FPGA. El skew de la placa es algo que puede que tengas que tener en cuenta en la temporización de tus E/S.
Pero también serás responsable de interpretar las especificaciones de tensión y corriente de la hoja de características y de configurar el bloque de E/S correspondiente de la FPGA con el estándar de tensión correcto: interfaces unipolares (single-ended) y diferenciales, LVCMOS, SSTL, LVDS, etcétera.
¿Todos los diseñadores de FPGA hacen esto bien de verdad? La respuesta es no. Es bastante común ver diseños que no deberían funcionar en absoluto por sus fallos electrónicos y que, sin embargo, parecen funcionar a la perfección. No es raro que el pin de salida de un componente alimente el pin de entrada de otro con una tensión superior a la clasificación máxima absoluta. Es un error enorme, por supuesto, y el componente que recibe la tensión excesiva puede en principio quemarse en cualquier momento, según la hoja de características. Y aun así, todo funciona por los siglos de los siglos. Hasta que un día se rompe de repente, y los listos buscan excusas absurdas para explicar por qué ha pasado.
Añadiré también una breve lista de temas que son claramente responsabilidad del diseñador de PCB. Pero cuando esta persona no es precisamente excelente, ayuda que el ingeniero de FPGA entienda algo de ellos también:
- Fuentes de alimentación y reguladores de tensión.
- Osciladores de reloj, relojes de referencia y fluctuación temporal (jitter).
- Integridad de señal: adaptación de impedancias y terminaciones, reflexiones, diafonía (crosstalk), EMI.
- Consideraciones térmicas.
Entonces, ¿qué necesita saber realmente de todo esto un diseñador de FPGA? ¿Qué es crucial desde el principio? La verdad es que me cuesta decirlo. Cuanto mejor sea el equipo que tengas a tu alrededor, menos electrónica necesitas saber. Pero yo diría que cualquier diseñador de FPGA debería ser capaz de leer esquemáticos y de entender qué está conectado a la FPGA, y de comprender más o menos por qué está conectado de esa forma concreta.
Aquí termina la segunda página de esta serie. La página siguiente pasa a las habilidades prácticas, con el mismo espíritu que lo anterior.1