Asignar un valor "don't care" como salida en un módulo combinacional en Verilog

Diseño HDL con este lenguaje. Módulos y testbenchs. Estilos y trucos de codificación, etc. NOTA: dado que hay entornos como ISE que soportan Verilog pero no SystemVerilog, señalad dentro de un post que de lo que se va a tratar es SystemVerilog si es el caso.
Avatar de Usuario
mcleod_ideafix
Site Admin
Mensajes: 80
Registrado: 14 Ago 2018, 01:15

Asignar un valor "don't care" como salida en un módulo combinacional en Verilog

Mensaje por mcleod_ideafix » 15 Ago 2018, 12:36

Esto es una traducción de un post que escribí hace tiempo en Stack Overflow. El post original, con los comentarios que suscitó, está aquí:
https://stackoverflow.com/q/29451175/3011009

Supongamos que queremos diseñar un circuito combinacional que responda a la siguiente tabla de verdad:
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
(donde "d" significa "don't care", es decir, que me da igual que el valor sea 1 ó 0)

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:
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
Y las ecuaciones correspondientes serían estas (usando notación Verilog)
s0 = ~a & ~b; s1 = ~a; s2 = ~b; s3 = 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)

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
Se supone que en Verilog no se puede asignar un valor "x" a una salida. Sólo puedo usarlo para testear valores, y si uso "z" la cosa se pone peor, ya que el circuto resulta ser mucho más complejo, por culpa de los triestados.

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
Que me lleva a las siguientes ecuaciones (dejando aparte el "default" de momento)
s0 = ~a & ~b; s1 = ~a & b; s2 = a & ~b; s3 = a & b;
Y a esta implementación (de la salida de YOSIS 0.3.0 en EDA Playground)
preg1.png
preg1.png (44.96 KiB) Visto 3881 veces
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.

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:
preg2.png
preg2.png (28.19 KiB) Visto 3881 veces
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.
preg3.png
preg3.png (5.7 KiB) Visto 3881 veces
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"?
ADDENDUM

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:
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
El correspondiente módulo Verilog, sin los "don't cares", puede escribirse de varias formas, por ejemplo, ésta:

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
Que produce el peor resultado, en términos de recursos utilizados (2 slices, 4 LUTs en una FPGA Spartan 3E)

Si se usa esta versión de la tabla de verdad se puede llegar a un resultado más optimizado:
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
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)

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
Si uso el truco de "x" como "don't care"...:

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
El diseño sintetiza, pero el resultado no es el óptimo. XST reporta 1 slice, 2 LUTs.

Este artículo:
Verilog_X_Bugs.pdf
(402.08 KiB) Descargado 431 veces
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.

Responder

Volver a “Verilog / SystemVerilog”