VHDL: Demultiplexación de una señal a una de muchas salidas mientras se llevan las salidas no utilizadas a '0'

Estoy intentando crear VHDL sintetizable que desmultiplexará un flujo de señal continuo de un bit a una de muchas salidas. Las salidas que no se envían a esta secuencia deben establecerse en '0'. Vea la imagen a continuación para tener una idea de lo que estoy tratando de lograr.

Diagrama de demultiplexación

El código que he escrito para crear esta funcionalidad es:

send_s         <= (line_select_s => prbs_i(0), others => '0');

donde 'prbs_i(0)' es el flujo de señal.

Desafortunadamente, esto no se puede sintetizar en Vivado, dando el error:

[Synth 8-211] could not evaluate expression: aggregate choice expression

¿Hay una manera adecuada de hacer esto? El VHDL simula bien pero no sintetiza.

Respuestas (2)

El problema con su código es que una asignación de la forma:

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

debe usar constantes para las compensaciones que se asignan (en este caso, 3).

Si necesita que el desplazamiento se configure para que varíe, debe dividirlo en dos asignaciones. Tenga en cuenta que esto solo funcionará dentro de un proceso; con un par de asignaciones concurrentes, encontraría un problema de controlador múltiple.

process (line_select_s, prbs_i)
begin
  send_s <= (others => '0');
  send_s(line_select_s) <= prbs_i(0);
end process;

Esto funciona porque aunque comencemos asignando '0'a todo el vector, tendrá prioridad la última asignación a una señal en particular en un proceso.

Una alternativa es usar un bucle:

process (line_select_s, prbs_i)
begin
  send_s <= (others => '0');
  for i in send_s'range loop
    if (i = line_select_s) then
      send_s(i) <= prbs_i(0);
    end if;
  end loop;
end process;

Esto tiene la ventaja de que si su señal seleccionada puede representar un desplazamiento mayor que el ancho de su vector objetivo, no se producirá ningún error en la simulación. Un escenario de ejemplo sería una señal de selección de 3 bits, pero un vector objetivo con solo 5 elementos; el primer método produciría un error si la señal de selección representara 6, 7 u 8, pero el segundo no.

Como nota al margen, es posible que desee realizar un proceso como este sincrónico si desea obtener el mejor rendimiento (en términos de frecuencia operativa máxima) de su diseño.

Solo para ampliar por qué no puede simplificar el bucle a send_s(line_select_s) <= prbs_i(0);: recuerde que este proceso tiene que generar hardware para permitir que cualquier salida transporte la señal (de ahí el bucle), y solo controle la seleccionada (de ahí el si). El loop se desenrolla efectivamente por síntesis, generando un conjunto de drivers que operan en paralelo, uno para cada salida.
Genial, gracias @scary_jeff! Eso tiene sentido. Olvidé mencionarlo: ya tenía esto en un proceso sincrónico, por lo que su idea fue fácil de agregar a mi código existente. @BrianDrummond, lo sospechaba, pero me decepcionó que el estándar de sintetizador / VHDL no fuera "lo suficientemente inteligente" para permitir el enfoque de una línea.
Esa es la diferencia entre la simulación (donde debería funcionar) y el hardware de generación de síntesis. Las herramientas de sintetizador se han vuelto mucho más inteligentes, pero siguen funcionando básicamente viendo patrones de diseño (como este bucle) y transformándolos.
@BrianDrummond me has hecho dudar de mí mismo, no veo por qué send_s <= (others => '0'); send_s(line_select_s) <= prbs_i(0);no funcionaría.
Respuesta editada para simplificar. El bucle no era necesario.
¡Ja! y, de hecho, Xilinx XST (14.4) sintetiza cualquiera de las versiones en cinco LUT. ¡Bien cuestionado, Jeff! (No he probado las herramientas Synplicity o Altera)
Gracias @BrianDrummond, creo que el método de bucle puede tener una ventaja después de todo, mira mi segunda edición. Salud.
Ahhh Elegí mejores tipos ... mi selección fue natural range 0 to 4(o el rango del puerto de salida) por lo que tales errores no fueron posibles. Pero me gusta ver ambos enfoques en la respuesta, nunca se sabe cuándo puede ser necesario el ciclo.

Hay muchas maneras. Una forma sencilla de hacerlo es, por modelo de flujo de datos:

with line_select_s select
send_s <= (send_s & "0000") when "000",
        ('0' & send_s & "000") when "001",
        ("00" & send_s & "00") when "010",
        ("000" & send_s & '0') when "011";
        ("0000" & send_s) when "100";
         "00000" when others;
Esto funcionaría, sin embargo, no es realmente utilizable cuando tiene más de unas pocas líneas de salida. En mi caso tengo más de 30.
Este segmento de código sería más largo para 30 líneas. Pero recuerde que el tamaño del código y el tamaño del hardware sintetizado no tienen relación entre sí.
Pero su código no es mantenible. Un demux se puede describir en 3 líneas: declaración for-generate y una puerta AND simple. La segunda entrada de la puerta AND es la versión codificada en caliente de la señal de selección. La codificación onehot se puede hacer en una función. Esto escala a cualquier bit de tamaño y mantiene un código mantenible/legible.
Mantenibilidad. Bien de acuerdo. Pero el resultado sigue siendo el mismo hardware. No significa que mi respuesta sea incorrecta más o menos para golpear un voto negativo.