¿La forma más eficiente de seleccionar entre 10 autobuses grandes?

2

Necesito seleccionar entre 10 buses de 164 bits diferentes usando BCD de cuatro bits (8421, binario sin signo). ¿Cuál es la forma más eficiente de hacerlo?

Actualmente tengo la siguiente implementación de SystemVerilog

case (bcdIn)
  4'd0: muxOut = optionA;
  4'd1: muxOut = optionB;
  4'd2: muxOut = optionC;
  4'd3: muxOut = optionD;
  4'd4: muxOut = optionE;
  4'd5: muxOut = optionF;
  4'd6: muxOut = optionG;
  4'd7: muxOut = optionH;
  4'd8: muxOut = optionI;
  4'd9: muxOut = optionJ;
  default: muxOut = 'x;
endcase

donde muxOut y cada una de las "opciones" tienen 164 bits de ancho. El informe de tiempo para este fragmento de código revela un gran retraso debido al fanout de las líneas seleccionadas (los dígitos de bcdIn).

¿Hay una forma más eficiente de seleccionar entre los autobuses grandes? Tal vez una construcción de árbol de MUXes de algún tipo?

Estoy dispuesto a codificar esto a mano utilizando las declaraciones assign y similares, pero si hay una manera de inferir una lógica más rápida usando case , soy todo lo que oigo.

    
pregunta Evan W

4 respuestas

1

Creo que el problema no es que esté seleccionando entre 10 buses, sino que su señal de selección tiene que extenderse a varios cientos de multiplexores (~ 4 * 164 bits).

La conducción de un fanout grande de varios cientos de pines creará una carga capacitiva enorme (si tienes un sintetizador tonto) o un árbol de búfer grande (si tienes un mejor sintetizador). Usar un árbol de búfer es mejor, pero puedo darte un mejor consejo si pegas el informe de tiempo que te está dando problemas.

Si no hay forma de cumplir con la temporización en un solo ciclo, podría bloquear bcdIn en varios registros paralelos, y usar estos registros paralelos para controlar diferentes bits de la salida (es decir, bloquear bcdIn en 4 flip flops paralelos idénticos y conducir el muxout) [40: 0] desde el primer flop, unidad muxout [82:41] desde el segundo, etc). Esto reducirá cualquier fanout de la señal durante un ciclo de reloj, aunque es algo ineficiente.

En última instancia, tu fanout no me parece irrazonable, y me sorprende que tengas problemas para cumplir con cualquier tipo de tecnología de proceso, pero tal vez el sintetizador no lo haya optimizado.

    
respondido por el Tim
1

Tengo dos sugerencias. use la directiva de sintetizador de casos en paralelo y / o la codificación de selección instantánea.

Intente codificar como un disparo para equilibrar la carga. De esta manera, cada señal de selección tiene una carga de fan-out de ~ 164. Esto debería ayudar con el árbol de búfer y el enrutamiento.

always @* begin
  bcdIn_1hot = 10'b0;
  if (bcdIn<4'd10) bcdIn_1hot[bcdIn] = 1'b1;
  case (1'b1) // synthesizer's  parallel_case keyword OR prefix unique if SystemVerilog is supported
    bcdIn_1hot[0]: muxOut = optionA;
    bcdIn_1hot[1]: muxOut = optionB;
    /* ... other connections here ... */
    bcdIn_1hot[9]: muxOut = optionJ;
    default: muxOut = 'x;
  endcase
end

Incluso con el código original, el uso de sintetizador las opciones pueden marcar una gran diferencia. parallel_case y full_case son palabras clave comunes entre diferentes herramientas, pero deberá revisar el manual ya que no hay un estilo universal. Sin la opción, su sintetizador generará el equivalente de un paso anidado aunque else-if y tendrá un bajo rendimiento.

Si su herramienta es compatible con SystemVerilog, entonces recomiendo usar unique case

    
respondido por el Greg
1

RTL solo implica la transferencia de datos con una guía suelta en la implementación a nivel de puerta. Una declaración de caso es una tabla de consulta y se podría sintetizar fácilmente como un árbol de muxes. Puede ser crear tablas de consulta más pequeñas divididas por flip flops.

El problema de sincronización aquí es que el ancho del bus para que todos los bits tengan un retardo de rizado equivalente es bastante difícil frenar el bloque con un flop ayudará:

always @(posedge clk)
casez (bcdIn)
  4'b000?: muxOutA <= optionA;
  4'b001?: muxOutA <= optionC;
  4'b010?: muxOutA <= optionE;
  4'b011?: muxOutA <= optionG;
  4'b100?: muxOutA <= optionI;
  default: muxOutA <= 'x;
endcase
end

always @(posegde clk) begin
casez (bcdIn)
  4'b000?: muxOutB <= optionB;
  4'b001?: muxOutB <= optionD;
  4'b010?: muxOutB <= optionF;
  4'b011?: muxOutB <= optionH;
  4'b100?: muxOutB <= optionJ;
  default: muxOutB <= 'x;
endcase
end

always @(posedge clk) begin
case (bcdIn[0])
  1'b0: muxOut <= muxOutA;
  1'b1: muxOut <= muxOutB;
endcase
end
    
respondido por el pre_randomize
0

El código que tiene creará 167 10-1 mux con una salida habilitada, lo que significa que tendrá 2 CLB para cada bit de su bus de salida. Como la mayoría de los FPGA tienen 5 o 6 LUT de entrada, necesitará al menos 2 de ellos por cada mux, lo que significa que tendrá cerca de 400 LUts solo para el mux y probablemente mucho más debido a la congestión de enrutamiento dentro de un FPGA.

Lo que dijo @pre_randomize en realidad es una buena manera de dividir el diseño en 2 bloques, lo que lo hace más amigable con las 'FPGA' y puede acelerar tu diseño significativamente, podría ser incluso mejor dividirlo en 3 para obtener el P & amp ; R más espacio para colocar la lógica dentro de tu FPGA.

Otra opción es crear un bus, y en lugar de tener un mux, use la lógica para crear una habilitación de salida para cada opción, dependiendo del FPGA que esté utilizando, esto puede crear una implementación más rápida para su diseño, pero No estoy seguro de si lo hace más pequeño.

    
respondido por el FarhadA

Lea otras preguntas en las etiquetas