4 votos

Registro de desplazamiento de n bits (Serial Out) en VHDL

Estoy creando un registro de desplazamiento de n bits. Cuando la señal de habilitación es alta, quiero que el registro de desplazamiento se desplace n veces, independientemente de si la habilitación sigue siendo alta o baja. He puesto un bucle for para desplazar n veces dentro de un proceso. Mi código es el siguiente.

No creo que el bucle for funcione, ya que el desplazamiento no está restringido a n veces. ¿En qué me estoy equivocando?

library ieee;
use ieee.std_logic_1164.all;

entity SReg is

  generic (
     n  : integer := 4 
    );

  port(
    clk:              in std_logic; 
    reset:            in std_logic; 
    enable:           in std_logic;    --enables shifting
    parallel_in:      in std_logic_vector(n-1 downto 0);
    s_in:             in std_logic;    --serial input
    s_out:            out std_logic   --serial output
    );
end SReg;

architecture behavioral of SReg is
  signal temp_reg: std_logic_vector(n-1 downto 0) := (Others => '0');
begin
  process (clk,reset)
  begin
    if (reset = '1') then
      temp_reg <= parallel_in;   
    elsif (clk'event and clk='1') then
      if (enable ='1') then
        --shifting n number of bits
        for i in 0 to n-1 loop
          s_out <= temp_reg(0);
          temp_reg <= s_in & temp_reg(n-1 downto 1);
        end loop;
      end if;
    end if;
  end process;
end behavioral;

23voto

Jonas Puntos 1764

En VHDL, un bucle for se ejecuta en tiempo cero . Esto significa que en lugar de esperar un ciclo de reloj entre cada iteración, todo el bucle se ejecuta dentro de un ciclo de reloj, y sólo se muestra el resultado final del bucle al final. Esto es lo que ocurre en tu código. Todo el bucle se ejecuta en un solo ciclo de reloj.

Lo que realmente quieres es un bucle en el que cada iteración ocurra en un nuevo flanco de reloj. Esto permite que s_in se desplace fuera de s_out cada ciclo de reloj.

La realización de un bucle en el que cada iteración se produce en un flanco de reloj no requiere un comando de bucle for, sino que aprovecha la función lista de sensibilidad del proceso. He aquí cómo:

Un proceso se activa cada vez que una de las señales del lista de sensibilidad ("clk, reset" en este caso) cambia. Esto significa que el proceso ya está haciendo un bucle cada ciclo de reloj (si un reloj está en la lista de sensibilidad). Puedes usar esto a tu favor para realizar una operación de tipo bucle for, donde cada iteración del bucle ocurre en un ciclo de reloj.

Primero necesitas un contador:

process(clk,reset)
    variable shift_counter: integer := 0;
begin

shift_counter lleva la cuenta de cuántas iteraciones (o desplazamientos) se han producido hasta el momento. Comparará shift_counter a n-1 para ver si ya has terminado.

A continuación, podría ser una buena idea pensar en los estados en los que estará su proceso. Tal vez un estado de espera para cuando el proceso no se desplaza, y un estado de desplazamiento para cuando lo hace.

La definición de la señal de estado:

 TYPE POSSIBLE_STATES IS (waiting, shifting);
 signal state : POSSIBLE_STATES;

En el proceso propiamente dicho:

case state is
   when waiting =>

Vale, ¿y qué pasa cuando estamos esperando una habilitación? Sería una buena idea establecer todas las variables (manejadas) a un valor conocido. Esto significa que tal vez algo como esto es una buena idea:

shift_counter := 0;
temp_reg <= parallel_in;
s_out <= '0';

Esto es útil para hacer porque entonces usted sabe exactamente lo que sus valores de la señal son cuando la habilitación es alta. Además, al final del turno, puedes volver a cambiar los estados a "espera" para estar preparado para la habilitación de nuevo.

Entonces, ¿qué va a desencadenar un cambio de estado de espera a cambio? Eso es fácil:

if(enable = '1') then
    state <= shifting;
else
    state <= waiting;
end if;

Bien, entonces el siguiente estado. Cambiando.

En primer lugar, queremos incrementar el contador de desplazamiento, y realizar el desplazamiento real:

when shifting =>
    shift_counter := shift_counter + 1;
    s_out <= temp_reg(0);
    temp_reg <= s_in & temp_reg(n-1 downto 1);

Y luego también detectar cuando el cambio está hecho, para dejar el estado de cambio y volver a la espera:

if (shift_counter >= n-1) then
    state <= waiting;
else
    state <= shifting;
end if; 

Y eso es todo.

En el siguiente trozo de código, observe que el estado de "reinicio" y el estado de "espera" son distintos. Esto es útil porque generalmente el reinicio asíncrono sólo se produce en el arranque y no se espera que procese ningún dato durante este tiempo. Al mover el temp_reg <= parallel_in al estado de espera (fuera del reinicio asíncrono), estamos permitiendo que el módulo que conduce parallel_in para arrancar correctamente sin tener que enviar datos durante el reinicio. Además, ahora se puede entrar en el estado de espera cuando sea necesario, sin tener que realizar un reinicio asíncrono.

También note que sólo estoy manejando 3 señales (4 contando la variable) en mi proceso, y sólo esas señales. Si una señal es conducida en un proceso, no debería ser conducida en ningún otro lugar más que en ese proceso. No fuera del proceso, no en otro proceso. Una señal es conducida dentro de un proceso y sólo un proceso. Puedes comparar la señal con otras señales en otros lugares (sentencias if, y similares), pero no le des un valor a la señal en ningún lugar excepto en un proceso. Y generalmente, se define en la porción de reinicio, y luego donde sea necesario en el proceso propiamente dicho. Pero sólo en un proceso. Si me hubieran dicho esto, me habría ahorrado toneladas de tiempo mientras aprendía.

Aquí está todo el código en un trozo:

library ieee; 

use ieee.std_logic_1164.all;

entity SReg is

generic ( n : integer := 4);

port( clk: in std_logic; 
      reset: in std_logic; 
      enable: in std_logic; --enables shifting 
      parallel_in: in std_logic_vector(n-1 downto 0); 
      s_in: in std_logic; --serial input 
      s_out: out std_logic --serial output

);
end SReg;

architecture behavioral of SReg is

signal temp_reg: std_logic_vector(n-1 downto 0) := (Others => '0');
TYPE POSSIBLE_STATES IS (waiting, shifting);
signal state : POSSIBLE_STATES;
begin

process(clk,reset)
    variable shift_counter: integer := 0;
begin

   if(reset = '1') then
      temp_reg <= (others => '0');   
      state <= waiting;
      shift_counter := 0;
   elsif(clk'event and clk='1') then
        case state is
            when waiting =>
                shift_counter := 0;
                temp_reg <= parallel_in;
                s_out <= '0';
                if(enable = '1') then
                    state <= shifting;
                else
                    state <= waiting;
                end if;
            when shifting =>
                shift_counter := shift_counter + 1;
                s_out <= temp_reg(0);
                temp_reg <= s_in & temp_reg(n-1 downto 1);
                if (shift_counter >= n-1) then
                    state <= waiting;
                else
                    state <= shifting;
                end if; 
        end case;
    end if;
end process;

end behavioral;

1 votos

Stanri, eso me ha enseñado mucho. Te agradezco sinceramente tu ayuda.

1 votos

If a signal is driven in one process, it shouldn't be driven anywhere else but that process. Esto tiene mucho sentido. Con demasiada frecuencia me encuentro con el error de "múltiples conductores". ¿Qué es exactamente s_in ¿que hace aquí? Este registro de desplazamiento parece que se engancha a los datos en paralelo y luego se desplaza en serie.

1 votos

@ShashankVM, En el caso de la pregunta, parallel_in suministra el valor de reset del registro de desplazamiento interno. Así que lo he mantenido con el mismo nombre y función. Por lo demás no se utiliza, el OP debe explicar para qué estaba destinado originalmente. Estoy de acuerdo en que un registro SISO debe ser, por lo demás, estrictamente serial.

-1voto

JW. Puntos 145

La respuesta de @stanri es impresionantemente completa y bastante precisa... si puedo resumir/aclarar la primera afirmación, sin embargo, la declaración 'for' en una HDL simplemente expresa 'replicación sintáctica' no 'ejecución secuencial'.

Es decir, simplemente genera más elementos de hardware (puertas), y no informa del flujo del proceso. Yo diría que el bucle se expande en tiempo de elaboración (compilación), no que "se ejecuta en tiempo cero", después de todo en tiempo de ejecución todavía habrá retardo de propagación a través de los elementos generados por la construcción "for".

No empieces escribiendo código VHDL, empieza dibujando esquemas lógicos (al menos a cierto nivel de abstracción). A fin de cuentas, el HDL no es más que una forma de expresar en texto el contenido de los esquemas lógicos.

1 votos

Aunque la gente tiene diferentes enfoques para el diseño digital, su descripción de la declaración for no es del todo correcta. Si consideras la sentencia generate entonces tu descripción es cierta, pero si consideras la sentencia loop dentro de un proceso, esto es código secuencial. Si el bucle contiene asignaciones de señales a la misma señal entonces la asignación anterior es sobrescrita por la siguiente, pero si se trata de una variable se comporta como un código secuencial regular.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X