domingo, 28 de octubre de 2012

Replicación de Lógica - Inserción de Buffer - Alta Cargabilidad (High Fan-Out)

Introducción

Entre las tantas tareas que llave a cabo la herramienta de síntesis, hay una que se encarga de revisar la cargabilidad (fan-out) de cada señal lógica.Por defecto existe un número límite máximo de cargabilidad permitido. Este numero básicamente depende de la familia del FPGA con que se este trabajando. Por supuesto existe la opción de incrementar ese número, aunque no es lo mas aconsejable. La mejor solución para casos en que la cargabilidad de una señal sea mayor a la permitida, es lo que técnicamente se llama replicación de la lógica que genera la señal de tan alta cargabilidad. La cargablidad de cada registro, compuerta lógica y cualquier otro elemento lógico es analizado por el compilador de la herramienta de síntesis. Si el numero de cargabilidad encontrado supera al establecido por el diseñador, el compilar replica el nodo fuente de la señal hasta que el número de cada copia esté por debajo del número máximo de cargabilidad permitido. 

Cargabilidad (Fan-out)

Fan-out es definido como el número máximo de entradas de componentes lógicos que pueden ser conectadas con la salida de otro componente lógico. De acuerdo al diseño o circuito que se implementa algunas señales internas del mismo (salidas) se conectan a las entradas de otros componentes. Esta cantidad de conexiones puede ser un número desde 1 hasta un numero muy alto (por ej. 10000), este es el números que se conoce como fan-out. No hace falta que uno sea el que lleva la cuenta de la cargabilidad de cada señal, todas las herramientas de síntesis detallan en su reporte las señales que tienen un alto fan-out. 
Por qué existe un número máximo de fan-out? El número conexiones máximas están limitadas,  por un lado, por la degradación que puede tener la señal de del alta cargabilidad debido a que la principal carga que ve la salida es la capacitancia de entrada de los componentes a los que está conectada; así mientras mas conexiones tiene la salida, más capacitancia, más tarda en subir (rise-time) y en bajar (fall-time) la señal. Al tardar mas en subir y en bajar, la señal sufre un retardo generado solo por la cargabilidad, de este modo el rendimiento del sistema puede verse reducido. Finalmente, en el caso de los FPGAs, otro problema de una señal con un número alto de fan-out es que crea dificultades para rutear la señal dentro del FPGA, a no ser que se use una ruta dedicada para la señal de alta cargabilidad. 
Normalmente las herramientas de síntesis de los distintos fabricantes tienen un valor de fan-out para todas las señal (valor global) configurado por defecto. Sin embargo, este valor puede ser sobrescrito usando ya sea restricciones en el archivo correspondiente, (.sdc para Altera; .ucf para Xilinx), o usando atributos de síntesis, cuyo nombre depende de la herramienta de síntesis que se use.  


En el caso de Synplify, la herramienta permite configurar el valor de fanout máximo desde una ventana de configuración: 
Project -> Implementation Options -> Device

en la opción Fanout Guide se muestra el numero máximo de fanout.




El valor configurado para fanout, se aplica a TODAS las señales del diseño, se por ello se lo denomina fanout global. Tal como se explico anteriormente este valor no puede ser demasiado alto, pero tampoco demasiado bajo. En este último caso se corre el riesgo que la herramienta de sintesis duplique lógica o inserte buffers sin realmente necesitarse. Ambos extremos afectan el ruteo y el rendimiento del diseño. 
Para sobrescribir el fnaout global e imponer un fanout distinto en un mas bajo nivel se puede usar el atributo:

syn_maxfan 

Este atributo se aplica a distintos niveles de jerarquía del diseño, descripto en el siguiente cuadro: 


Cuál es la diferencia entre límite duro y blando? 
En el limite blando la herramienta puede o no puede replicar la logica o introducir un buffer, una vez superado el valor límite. En el caso de aplicar syn_maxfan a un registro por ejemplo, si se supera el valor de fanout configurado por el atributo SI o SI la lógica es replicada o se agrega un buffer a la señal.  
A continuación se presenta un ejemplo de un valor alto de fanout que afecta el rendimiento máximo del sistema. Como puede apreciarse el retardo de ruteo es excesivamente largo, principalmente debido a un muy alto valor de fan-ou (seguramente también esta señal no esta usando un ruteo dedicado). 


Esta señal, sig, es un típico caso en el que un alto fanout está afectando la frecuencia máxima de funcionamiento del sistema, por lo que se debería replicar la lógica generada de esa señal.

Quartus Configuración y Atributo

El atributo que controla el valor de fanout máximo en la herramienta de síntesis del Quartus II maxfan
El valor global de fanout se configura en en el Assignment Editor

XST Configuración y Atributo

El atributo que controla el valor de fanout máximo en la herramienta de síntesis XST del ISE de Xilinx se denomina max_fanout
El valor global de fanout se configura en: 
Process -> Properties -> Xilinx Specific Options -> Max Fanout

Detalles Lógicos

La herramienta de síntesis a fin de cumplir con los requisitos del valor máximo de fanout, puede hacer dos cosas: 
- replicación de lógica
- introducción de un buffer

 

Replicacion de Lógica

Cuando el fanout de un registro o una lógica combinacional supera el valor configurado, la herramienta de síntesis reduce el fanout al replicar la lógica de salida, ya sea secuencial o combinacional.

 

Introducción de Buffer

Un buffer es introducido por la herramienta de síntesis cuando una señal de entrada al FPGA tiene un alto fanout. 

 

Replicación Manual

Teniendo en cuenta que no todas las señales con alta cargabilidad originan problemas de bajo rendimiento, se puede proceder a una replicación manual del componente con una alta cargabilidad. Para ello en el código VHDL o Verilog se describe el componente las veces que sea necesario. Un punto muy importante en estos casos, es el uso de atributos de síntesis para prevenir que la lógica replicada sea optimizada por la herramienta de síntesis. Para ello se usan atributos tales como syn_preserve, syn_keep (en el caso de usar Synplify).


Excepciones

Como siempre ocurre hay excepciones a estas reglas y algunas son las siguientes:
  • Se puede 'exigir' a la herramienta de síntesis que use un 'buffer' en lugar de replicar la lógica. En el caso de Synplify esto se logra configurando el atributo syn_replicate a valor lógico '0', ya sea globalmente, o sobre el modulo o sobre un registro en particular. Este atributo previene la replicación, por lo que el software usará buffers para evitar los problemas relacionados con un fanout muy elevado
  • Para especificar que un puerto de entrada, por ejemplo una entrada de reloj, no sea usada con un buffer, se puede usar el atributo syn_noclockbuf, sobre esa entrada. Esto se usa comúnmente cuando se esta muy al limite con el numero de buffers globales y se necesita el buffer para una senal que no tiene un alto fanout pero que tiene una restricción de tiempo muy critica. 
  • Para casos en que el rendimiento del sistema no se muy crítico, es decir cuando la frecuencia de trabajo del sistema es baja o muy baja, se puede directamente evitar cualquier replicación de lógica o introducción de buffer al configurar el valor de fanout a un numero muy alto.  
  • Otros atributos que se pueden usar para que la lógica no sea replicada y en su lugar se introduzca un buffer, son los atributos syn_keep y syn_preserve. syn_keep es usado cuando se quiere mantener una señal, mientras que syn_preserve se utiliza para preservar un registro. En ambos casos si se supera el numero máximo de fanout, un buffer sera introducido en lugar de replicar la lógica.
  •  

Conclusión 

Es importante resaltar que las señales con alta cargabilidad en un diseño  no deberían ser tocadas a menos que esté en riesgo el rendimiento del diseño, es decir que la técnicas detalladas mas arriba se deberían usar en caso que la señal que tiene alta cargabilidad este afectando la frecuencia máxima a la que se desea funciones el diseño
La cargabilidad de las señales es detallada en los distintos reportes presentados por la herramientas de síntesis, ya sea Synplify, Quartus, XST, etc. En función de ese número y de que si se trata de una señal que es crítica para el máximo rendimiento del diseño, es que se deben aplicar las técnicas presentadas en este blog, para sea replicar la lógica o insertar un buffer.

martes, 2 de octubre de 2012

Inicialización y Asignación de Vectores - Aggregate

 Introducción

Un punto de mucha importancia cuando se escribe código VHDL es la escritura en forma parametrizada del mismo. Esto significa usar los distintos atributos e instrucciones de VHDL a fin de evitar hacer uso de referencias fijas las que deberán ser cambiadas si se modifica el tamaño de algún dato o señal del código escrito. Así, las referencias a tamaños de un dato o señal deberían ser expresadas en términos de atributos de VHDL o parámetros definidos por el diseñador. 

Aspectos Prácticos

Para simplificar los ejemplos comenzaremos con la inicialización de una señal. La asignación de un valor a una señal de tipo arreglo (array) normalmente se hace de la siguiente manera: 

data_out <= "00000000";

Si bien esa asignación es correcta, no es la más adecuada si se quiere escribir el código de forma parametrizada. La misma función cumple la siguiente asignación conocida como aggregate: 

data_out <= (others => '0');

Que también se puede usar para inicializar a todos ‘1’:

data_out <= (others => '1');

Similarmente se puede escribir: 

data_out <= ( 0 => ‘1, 3 => '1, others => ‘0);

Para formar de este modo el dato “00001010”. 
Un error común cuando se escribe código parametrizado es que un aggregate no puede ser usado en una expresión y debe usarse con un objeto de tamaño conocido. Así, el siguiente código dará un error el ejecutarse: 

signal internal_bus: std_logic_vector(width-1 downto 0);
 . . .
output_bus <= x"FF" when (internal_bus=(others=>0)) else . . .


En este comparación el tamaño de internal_bus es conocido, pero no el tamaño del dato con quien se esta comparando internal_bus. Para solucionar este problema se usa el atributo ‘range para proveer el tamaño del objeto. 

output_bus <= x"FF" when (internal_bus=(internal_bus’range=>0)) else . . .

Otro manera de hacer algo similar a lo detallado en el ejemplo anterior, sería mediante el uso de una constante tal como se detalla a continuación: 

constant all_zero: std_logic_vector(width-1 downto 0) := (others => ‘0);

signal internal_bus: std_logic_vector (width-1 downto 0);
. . .

output_bus <= x"FF" when (internal_bus = all_zero) else . . .

Una nota con respecto al uso de aggregate, es que cuando se usa en un arreglo compuesto por solo un elemento, la asociación por nombre debe ser usada para especificar el valor. Por ejemplo: 

constant valor_ini: std_logic_vector(2 downto 2):= (2=>); -- correcto

constant valor_final: std_logic_vector(2 downto 2):= (0); -- incorrecto  

Finalmente quiero recordarles que un aggregate también se usa para asignar valores de señales a otra señal en particular. Ejemplo: 

signal a, b, c, d: std_logic;
signal tmp: std_logic_vector(3 downto 0);

 .. .

tmp <= (a, b, c, d);

De este modo tmp(3) toma el valor de a, tmp(2) el de b, tmp(1) el de c y tmp(0) el de d. En caso de querer cambiar el orden se lo debe especificar en la asignación, por ejemplo: 

tmp <= (3 => c, 2 => a, 1 => d, 0 => b);

Asi, tmp tiene los siguientes vlaores asignados (c, a, d, b).
Tambien se puede expresar un aggregate de la siguiente manera: 

tmp <= (3 => ‘1, 2 | 1 | 0 => ‘0);

tmp <= (3 => ‘1, (2 downto 0) => ‘0);

tmp <= (3=> ’1, others => ‘0);

tmp <= (1, ‘0, ‘0, ‘0, ‘0);  


Todas estas ultimas cuatro asignaciones son equivalentes.