https://stackoverflow.com/q/29451175/3011009
Supongamos que queremos diseñar un circuito combinacional que responda a la siguiente tabla de verdad:
(donde "d" significa "don't care", es decir, que me da igual que el valor sea 1 ó 0)a b | s0 s1 s2 s3 ----------------- 0 0 | 1 d d d 0 1 | 0 1 d d 1 0 | 0 0 1 d 1 1 | 0 0 0 1
Si vamos por el camino del diseño tradicional, podemos aprovecharnos de estos "don't cares" y asignarles el valor que más nos convenga, de forma que las ecuaciones que obtengamos (y por tanto el circuito) sea el más simple posible. Por ejemplo, podríamos modificar la anterior tabla de verdad por esta otra:
Y las ecuaciones correspondientes serían estas (usando notación Verilog)a b | s0 s1 s2 s3 ----------------- 0 0 | 1 1 1 1 0 1 | 0 1 0 1 1 0 | 0 0 1 1 1 1 | 0 0 0 1
(recordad que esto es equivalente a lo que se hace en los mapas de Karnaugh con los valores "d", que los escoges según te dé la gana)s0 = ~a & ~b; s1 = ~a; s2 = ~b; s3 = 1;
Pero... ¿y si quiero usar Verilog? No puedo usar "d" como el ejemplo anterior. Este código no funcionará:
Código: Seleccionar todo
module encoder (
input wire a,
input wire b,
output reg [3:0] s
);
always @* begin
case ({a,b})
2'b00 : s = 4'b1ddd;
2'b01 : s = 4'b01dd;
2'b10 : s = 4'b001d;
2'b11 : s = 4'b0001;
default: s = 4'bdddd;
endcase
end
endmodule
Así que no tengo más remedio que elegir de antemano qué valores, 1 ó 0, quiero en la salida, y claro... estos valores no tienen por qué ser los mejores a la hora de optimizar el circuito.
Código: Seleccionar todo
module encoder (
input wire a,
input wire b,
output reg [3:0] s
);
always @* begin
case ({a,b})
2'b00 : s = 4'b1000;
2'b01 : s = 4'b0100;
2'b10 : s = 4'b0010;
2'b11 : s = 4'b0001;
default: s = 4'b0000;
endcase
end
endmodule
Y a esta implementación (de la salida de YOSIS 0.3.0 en EDA Playground) Que puede o no ser la mejor solución para una cierta tecnología, pero al sintetizador no le queda más remedio, ya que tiene que generar las salidas que le hemos indicado.s0 = ~a & ~b; s1 = ~a & b; s2 = a & ~b; s3 = a & b;
Usando el XST para una Spartan 3E, el módulo anterior usa 2 slices y 4 LUTs.
Había supuesto que Verilog (o cualquier otro HDL) debería dejar libertad al diseñador para usar tales opciones, de forma que el sintetizador podría aplicar cualesquiera optimizaciones que tuviera disponible si el diseñador permite al sintetizador que elija qué valores puede tener una salida para un conjunto concreto de entradas. Si ese fuera el caso, el diseño anterior podría haber sido optimizado para parecerse a este:
Usando la misma FPGA, la versión optimizada a mano del módulo usa 2 slices y 3 LUTs.
Para este ejemplo he podido realizar las optimizaciones a mano, pero supóngase un módulo controlador con un montón de salidas a un módulo de ruta de datos. Podría haber salidas del controlador que podrían tener un valor "don't care" para un cierto estado del mismo.
Por ejemplo: un controlador saca una señal "select" hacia un multiplexor para que éste elija un valor del registro A o el B, y otra señal "load" para cargar el registro C con el valor que provenga de A o B. Si la señal "load" no está activa, C no acepta ninguna carga y se queda con su valor actual.
Si la señal "load" vale 0, realmente nos nos importa qué valor tenga "select". Por tanto, en la descripción Verilog del controlador, cuando en un estado digo que load = 0, debería poder decir también que select en ese caso es un "don't care" y que el sintetizador elija qué valor quiere para él.
Lo que pregunto por tanto es:
- ¿Hay alguna forma de escribir una descripción en Verilog de forma que pueda dar un "don't care" a una salida desde un bloque combinacional?
- Si no, ¿es esto una limitación del lenguaje de descripción, o es más una cuestión de "deberías hacer tus diseños de forma que los valores "don't care" no sean necesarios"?
Para mi sorpresa, XST resconoce "x" como una salida válida. Es sintetizable y parece comportarse en la forma que uno espera, resultando en que el mismo circuito usa 2 slices y 3 LUTs. YOSIS, por otra parte, parece ignorarlo y da la misma salida que un diseño no optimizado.
Rectificación: he probado XST con otro diseño: un circuito que produce la siguiente tabla de verdad:
El correspondiente módulo Verilog, sin los "don't cares", puede escribirse de varias formas, por ejemplo, ésta:a b | s0 s1 s2 s3 ----------------- 0 0 | 0 d d d 0 1 | 1 0 d d 1 0 | 1 1 0 d 1 1 | 1 1 1 0
Código: Seleccionar todo
module encoder (
input wire a,
input wire b,
output reg [3:0] s
);
always @* begin
case ({a,b})
2'b00 : s = 4'b0111;
2'b01 : s = 4'b1011;
2'b10 : s = 4'b1101;
2'b11 : s = 4'b1110;
default: s = 4'b1111;
endcase
end
endmodule
Si se usa esta versión de la tabla de verdad se puede llegar a un resultado más optimizado:
Se puede observar que 3 de los 4 resultados pueden conseguirse sin una sola puerta lógica. Así, XST reporta 1 slice, 1 LUT (la única que se necesita para calcular s0)a b | s0 s1 s2 s3 ----------------- 0 0 | 0 0 0 0 0 1 | 1 0 1 0 1 0 | 1 1 0 0 1 1 | 1 1 1 0
Código: Seleccionar todo
module encoder (
input wire a,
input wire b,
output reg [3:0] s
);
always @* begin
case ({a,b})
2'b00 : s = 4'b0000;
2'b01 : s = 4'b1010;
2'b10 : s = 4'b1100;
2'b11 : s = 4'b1110;
default: s = 4'b1110; // yes, same output as above
endcase
end
endmodule
Código: Seleccionar todo
module encoder (
input wire a,
input wire b,
output reg [3:0] s
);
always @* begin
case ({a,b})
2'b00 : s = 4'b0xxx;
2'b01 : s = 4'b10xx;
2'b10 : s = 4'b110x;
2'b11 : s = 4'b1110;
default: s = 4'bxxxx;
endcase
end
endmodule
Este artículo: deja muy clara la cuestión: evita usar "x" en tus diseños, pero entonces, y de acuerdo con este ejemplo, eso hace que el lenguaje no permita ayudar a que el sintetizador minimice un circuito.
Ahorrarse una o dos LUTs puede que no sea para tanto, pero si el ahorro permite que un módulo completo se quede dentro de un único slice, el P&R tendrá luego más libertad para alojar ese módulo donde quiera.