Uso eficiente del espacio en FPGA

Antecedentes y aclaraciones:

Nunca antes había desarrollado/escrito una sola pieza de hardware, pero actualmente estoy usando Verilog para desarrollar un gran proyecto para un FPGA como mi proyecto final de graduación.

Tengo algunas preguntas sobre cómo escribir código verilog de manera eficiente. (Suponga que, cuando digo "eficiente", me refiero a "la forma en que usa menos área y pines del tablero").

Dentro de los problemas, sugeriré posibles soluciones, pero, si ninguna de ellas es correcta, siéntase libre de negarlas y agregar su solución correcta.

En cierto modo, el problema 1 es el mismo que el problema 3, así que siéntete libre de responder solo uno de ellos. He escrito ambos para proporcionar otro ejemplo del problema, en caso de que uno se sienta más cómodo respondiendo uno que el otro.

En caso de que no sepa cómo responder el problema 2, no se preocupe, ya que no es el problema principal aquí.

Gracias por su ayuda.

Problemas:

1 - Supongamos que tenemos un módulo X que controla si los módulos A, B, ... N están lógicamente encendidos/apagados, es decir, les envía señales para indicar si deben estar habilitados o deshabilitados. Veo dos formas de hacer esto:

Opción 1: Enviar, a cada módulo de destino, un par de cables de habilitación/deshabilitación, desperdiciando espacio con cables. es decir:

module X(o_enable1, o_enable2, o_disable1, o_disable2);

assign i_enable1 = o_enable1;
assign i_disable1 = o_disable1;
module A(i_enable1, i_disable1);

assign i_enable2 = o_enable2;
assign i_disable2 = o_disable2;
module B(i_enable2, i_disable2);

Opción 2: cree un bus, y cada módulo de destino sería responsable de enmascararlo y verificar si se le envió la señal de habilitar/deshabilitar, ahorrando espacio de cableado, pero creando un demux para cada módulo. es decir:

// o_enableDisable -> 0 = `ENABLE, 1 = `DISABLE
module X(o_enableDisable, o_enableDisable_bus);

assign i_enDis1 = o_enableDisable;
assign i_enDis_bus1 = o_enableDisable;
module A(i_enDis1, i_enDis_bus1);

assign i_enDis2 = o_enableDisable2;
assign i_enDis_bus2 = o_enableDisable2;
module B(i_enDis2, i_enDis_bus2);

// ... Inside A
if ((if i_enDis1 == `ENABLE)&&(i_enDis_bus1 == ENABLE_FOO1)) // Do something
if ((if i_enDis1 == `DISABLE)&&(i_enDis_bus1 == DISABLE_FOO1)) // Do something

2 - En el problema anterior asumimos que íbamos a habilitar/deshabilitar módulos lógicamente. En caso de que quisiéramos encenderlos/apagarlos físicamente para ahorrar energía, ¿hay alguna manera de hacerlo, o simplemente tenemos una instrucción if dentro del módulo? es decir:

if (enabled) begin
  // Contents of module while on
end else begin
  // Contents of module while off
end

3 - Supongamos que tenemos un módulo decodificador X que envía comandos a los módulos A, B, ..., N. Veo 3 formas de hacer esto:

Opción 1: Comandos como cables individuales, perdiendo pines, pero ahorrando el espacio de un demux. es decir:

module X(o_com1, ..., o_com_m, ..., o_com_n);

assign i_com1 = o_com1;
// ...
assign i_com_m = o_com_m;
module A(i_com1, ... , i_com_m);

module B(i_com_m_plus_1, ..., i_com_n);

Opción 2: entrada logarítmica para decodificar dentro del módulo de destino, desperdiciando espacio con un demux. (Tenga en cuenta que X decodifica una instrucción, decide a qué módulo debe enviar un comando y luego le envía un comando)

module X(o_command_A, o_command_B, ...);

assign i_command_A = o_command_A;
module A(i_command_A);

assign i_command_B = o_command_B;
module B(i_command_B);

// Inside A
case (i_command_A)
  `COMMAND_1: begin /* ... */ end
  // ...
  `COMMAND_n: begin /* ... */end
endcase

Opción 3: Una mezcla de ambos.

Tal vez también pueda pensar en hacer AND en salidas específicas de cada módulo con una línea HABILITAR/DESHABILITAR. Casi lo mismo que la opción 1, pero puedes elegir tácticamente qué salidas (y/o entradas) quieres enmascarar.
@Evert, el problema con esa solución es que el módulo, dependiendo del contenido de su estado inactivo, operaría, posiblemente consumiendo más energía de la que debería.

Respuestas (2)

Primero, cuando diseñamos FPGA, en su mayoría solo pensamos en "recursos" en lugar de "espacio". Los recursos son cosas como recursos de enrutamiento, celdas lógicas, bloques de RAM, bloques de multiplicación y adición, etc. Parece que le preocupa minimizar el uso de recursos de enrutamiento y está dispuesto a usar más celdas lógicas para hacerlo.

En realidad, la mejor manera de responder a sus preguntas (y, en primer lugar, averiguar si la lógica comercial para el enrutamiento es lo correcto) es sintetizar su primer borrador de diseño y observar el uso de recursos. Si los recursos de enrutamiento están casi completamente utilizados, comience a buscar formas de reducirlos. Si la lógica está cerca de utilizarse por completo, comience a buscar formas de hacer el tipo de optimización opuesto.

Supongamos que tenemos un módulo X que controla si los módulos A, B, ... N están lógicamente encendidos/apagados, es decir, les envía señales para indicar si deben estar habilitados o deshabilitados.

La forma típica de hacer esto en un FPGA es simplemente dejar que todos los N módulos se ejecuten continuamente. Si solo se usa uno de los N en un momento dado, a menudo puede usar un mux para seleccionar cuál de las salidas se usa por la lógica descendente. Si las combinaciones de módulos que se "deshabilitan" son más complejas, a menudo simplemente le dice a la lógica descendente que ignore esas entradas, en lugar de deshabilitar el módulo que las genera.

Normalmente no tiene cables separados para enabley disablecomo propuso en su primera opción. Solo un cable que es (por ejemplo) alto cuando el módulo debe estar habilitado y bajo cuando debe estar deshabilitado.

Aunque, por supuesto, también es posible tener líneas separadas enabley disableque solo pulsen intermitentemente si tiene una buena razón para hacerlo. Por lo general, controlarían las entradas SET y RESET de un flip-flop para generar la enableseñal real.

En el problema anterior asumimos que íbamos a habilitar/deshabilitar módulos lógicamente. En caso de que quisiéramos encenderlos/apagarlos físicamente para ahorrar energía, ¿hay alguna manera de hacerlo, o simplemente tenemos una instrucción if dentro del módulo?

Los FPGA normalmente no tienen provisiones para apagar selectivamente partes de la estructura.

Es posible usar pines de habilitación para reducir el consumo de energía (debido a la reducción de la conmutación), pero si la cantidad de lógica que se deshabilita no es una fracción muy grande de su diseño, no es probable que mejore significativamente su presupuesto de energía.

Además, debe demostrar que, en un momento dado, al menos una fracción conocida de la lógica está deshabilitada, ya que de todos modos tendrá que diseñar sus fuentes de alimentación y el disipador de calor para tener en cuenta las peores condiciones de funcionamiento.

Supongamos que tenemos un módulo decodificador X que envía comandos a los módulos A, B, ..., N. Veo 3 formas de hacerlo:

Su opción 1 (una señal separada para cada comando) es un estilo de codificación FPGA muy común. Del mismo modo, los proveedores de herramientas de síntesis suelen recomendar la codificación one-hot para codificar los estados de la máquina de estado.

Estaba planeando hacer una codificación one-hot en el problema de habilitar/deshabilitar (en mi diseño actual, todos los módulos se ejecutan en paralelo, pero, si están deshabilitados, solo pasan la entrada a la salida) y tienen un flip flop para cada uno de los módulos en el decodificador, con un cable que los conecta. ¿Recomiendas este enfoque? Además de eso, ¿recomienda algún libro/sitio web para aprender sobre técnicas de codificación de hardware (soy un tipo C)?
@DanielCarvalho, aprendí principalmente de las notas de aplicación de Xilinx. Asegúrese de leer detenidamente la Guía del usuario de su herramienta de síntesis.
Para ver un ejemplo de la forma típica de implementar una ALU, por ejemplo, mire aquí: edaboard.com/thread311433.html Básicamente, solo haga todas las operaciones, luego coloque un mux al final para elegir cuál obtiene la salida.

El diseño de FPGA realmente no se preocupará por sus módulos en absoluto, sino que creará un gran conglomerado de puertas y luego intentará encontrar un diseño óptimo.

Las señales de habilitación en los FPGA generalmente se usan solo en los registros, que tienen una enableentrada dedicada. Si deshabilita un módulo bajando su señal de habilitación, está cerca del consumo de energía más bajo posible, ya que las salidas LUT ya no cambiarán y, por lo tanto, las puertas conectadas a sus salidas no necesitan cambiar.

Si usa dos cables que se elevan para un cambio de estado, necesita un flip-flop que los combine en un solo nivel lógico, y el optimizador sería estúpido si no se acerca tanto a su componente de controlador y solo enruta una sola línea de habilitación , por lo que simplemente puede hacer esto explícito. Probablemente sucedería lo mismo con la arquitectura del bus, pero el optimizador tendría que trabajar más duro y podría verse impedido de encontrar la solución óptima debido a algún caso crítico.

Un error común es cambiar la línea del reloj con la señal de activación. Esto es común en el diseño de ASIC, porque conservará una gran cantidad de energía, pero en los FPGA, esto realmente no funciona porque la distribución del reloj es una red separada. Los FPGA tampoco tienen una red de distribución de energía programable, por lo que tampoco hay forma de eliminar la energía de un área del chip. Si tuviera que construir un ASIC con esa lógica, agregaría estas cosas. Hay una buena charla de FOSDEM sobre esto si está interesado.

Para la multiplexación de comandos nuevamente, no importa tanto, porque el optimizador hará lo suyo de todos modos. Lo mantendría en el módulo, donde lógicamente pertenece.

OP también debe tener en cuenta que las señales RESET a menudo también tienen recursos de enrutamiento dedicados. Esto significa que si puede usar la misma señal para restablecer la mayor cantidad posible de su lógica, evitará el uso de recursos de enrutamiento generales y probablemente también obtendrá un mejor rendimiento de tiempo.
Simon, ¿entonces debería aplicar la ley de escritura/legibilidad a mis diseños actuales/futuros? Hasta ahora, creo que la solución de un solo cable sería la mejor, ya que el compilador podría hacer su trabajo de una manera más fácil. Acerca de la multiplexación de comandos, ¿no pertenecería al módulo decodificador, en lugar del módulo de destino?