transferencia de registro

0

Estoy tratando de entender los sistemas RTL (Nivel de Registro-Transferencia) y, en particular, la implementación del subsistema de control por medio de gráficos de ejecución secuencial. No entiendo cómo implementar una construcción como esta:

  

si x > = x_ref

     

(hacer algo)

     

else

     

(hacer otra cosa)

     

fin

Creo que debería incluir un comparador dentro de la ALU. Sin embargo, no está claro cómo debo administrar el resultado del comparador para saltar a otro estado.

Ya que, si usamos secuenciación implícita, las instrucciones de control y las instrucciones de bifurcación son intrínsecamente diferentes, ¿cómo puedo usar el resultado de una instrucción de control (es decir, de la salida del comparador) para asignar el estado sucesor? Intenté buscar en Internet y en mi libro de texto (Introducción a los sistemas digitales, Ercegovac Lang Moreno), pero hasta ahora no he encontrado nada relevante.

    
pregunta A. Darwin

1 respuesta

4

La forma exacta en que lo implementas depende del rendimiento que necesites, lo que determina la profundidad con la que lo canalizas.

Voy a suponer que (do something) y (do something else) tardan aproximadamente la comparación en x>=x_ref , y que el sistema está lo suficientemente lento para que haya suficiente tiempo adicional para propagar señales a través de un multiplexor antes del siguiente reloj. (También asumo que todo x, x_ref, y las entradas a do_something_* están sincronizadas con la señal del reloj, y que tiene un conocimiento básico de semántica de asignación de señal VHDL )

Luego la implementación inicial

process(clk) is
begin
  if rising_edge(clk) then
    if x>=x_ref then
       Result <= (do something);
    else
       Result <= (do something else)
    end if;
  end if;
end process;

es equivalente a lo siguiente, todos evaluados en paralelo (es decir, fuera de un proceso)

Test <= x >= xref;   -- Test is a boolean signal
A <= (do something);
B <= (do something else);
A_or_B <= A when Test else B;
Result <= A_or_B when rising_edge(clk);

En otras palabras, ambos brazos del MI, así como la comparación, se evalúan en paralelo. Luego, sus salidas A,B se desplazan a través del selector (multiplexor) a A_or_B , que funciona en paralelo a lo anterior, pero cuya salida no es válida hasta que todos los valores de A, B y Test sean estables.

También funciona en paralelo un registro temporizado, que espera hasta el siguiente flanco del reloj y engancha su entrada en Result . Esto es solo una taquigrafía para el proceso normal:

process(clk) is
begin
  if rising_edge(clk) then
    Result <= A_or_B;
  end if;
end process;

Si ha seguido esta explicación, acaba de ver que se ejecuta una instrucción IF completa en un solo ciclo de reloj.

Los bloques básicos reales pueden parecerse un poco al lenguaje ensamblador compilado a partir del código anterior, pero recuerde que cada uno es un pequeño bloque de hardware, que opera en paralelo, en lugar de instrucciones de ensamblador ejecutadas secuencialmente.

La ventaja de la forma IF en un proceso cronometrado es que hace que la operación de alto nivel sea relativamente clara y fácil de seguir. ¡Si puedes seguir la lógica a través de todas esas tareas paralelas más fácilmente, entonces tu mente funciona de manera diferente a la mía!

Ambas formas deben sintetizarse en el mismo hardware, asumiendo que la herramienta de síntesis reconoce la sintaxis when rising_edge(clk) . Si no es así, simplemente use el proceso cronometrado equivalente.

Y siendo todo lo demás igual, se prefiere la forma de nivel superior, que es más fácil de leer, comprender y mantener.

Ahora supongamos que no es lo suficientemente rápido, pero (do something) , (do something else) y x>=x_ref toman menos de un ciclo de reloj. Podemos canalizar esto más profundamente al realizar estas operaciones en un solo ciclo (en paralelo) y luego realizar la selección (el if real) en un segundo ciclo.

Test <= x >= xref when rising_edge(clk);   -- insert a register
A <= (do something) when rising_edge(clk); -- ditto
B <= (do something else) when rising_edge(clk); -- ditto
A_or_B <= A when Test else B;
Result <= A_or_B when rising_edge(clk); -- second stage pipeline register

Si se está preguntando por qué A_or_B NO TIENE su propio registro de canalización ... buena pregunta. La respuesta es que podría: escribiríamos las 2 últimas líneas como una sola línea:

Result <= (A when Test else B) when rising_edge(clk);

O igualmente podría haber separado la operación del registro en los casos anteriores:

A_int <= (do something);
A <= A_int when rising_edge(clk);

Esto deja una pregunta pendiente: ¿podemos mantener la claridad de la operación de alto nivel mientras lo canalizamos más profundamente? Bueno, aquí está mi enfoque ...

Process(clk) is
begin
  if rising_edge(clk) then
    -- Pipeline stage 1
    Test <= x >= x_ref;
    A    <= (do something);
    B    <= (do something else);
    -- Pipeline stage 2
    if Test then
       Result <= A;
    else
       Result <= B;
    end if;
  end if;
end process;
    
respondido por el Brian Drummond

Lea otras preguntas en las etiquetas