n bit shift register (Serial in Serial out) en VHDL

5

Estoy creando un registro de desplazamiento de n bits. Cuando la señal de habilitación es alta, quiero que el registro de desplazamiento cambie n veces, independientemente de si la habilitación continúa siendo alta o baja. He puesto un bucle for para cambiar n veces dentro de un proceso. Mi código se da a continuación.

No creo que el bucle for esté funcionando, ya que el cambio no está restringido a n veces. ¿A dónde me voy mal?

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;
    
pregunta Orange

2 respuestas

21

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, con solo el resultado final del bucle que se muestra al final. Esto es lo que está pasando en tu código. El bucle completo se ejecuta en un solo ciclo de reloj, y el valor de s_out solo cambiará una vez, al valor que tenía cuando finalizó el bucle, que en este caso es s_in cambiado en 4.

Lo que realmente quieres es un bucle donde se produce cada iteración en un nuevo borde de reloj. Esto permite que s_in se desplace fuera de cada ciclo de reloj.

Realizar un bucle en el que cada iteración ocurre en un borde del reloj no requiere un comando de bucle for, sino que aprovecha la lista de sensibilidad del proceso. Aquí es cómo:

Se activa un proceso cada vez que cambia una de las señales en lista de sensibilidad ("clk, reset" en este caso). Esto significa que el proceso ya está repitiendo cada ciclo de reloj (si hay un reloj en la lista de sensibilidad). Puede usar esto para su ventaja a fin de realizar una operación de tipo de bucle for, donde cada iteración del bucle se produce en un ciclo de reloj.

Primero necesitas un contador:

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

shift_counter realiza un seguimiento de cuántas iteraciones (o turnos) se han producido hasta el momento. compararás shift_counter con n-1 para ver si ya has terminado.

A continuación, podría ser una buena idea pensar en los estados en los que se encontrará el proceso. Tal vez un estado de espera para cuando el proceso no cambie, y un estado de cambio para cuando sea.

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

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

En el proceso adecuado:

case state is
   when waiting =>

Ok, entonces, ¿qué sucede cuando estamos esperando una habilitación? Sería una buena idea establecer todas las variables (controladas) en 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 porque entonces sabes exactamente cuáles son los valores de tu señal cuando la habilitación aumenta. Además, al final del turno, puede cambiar los estados a "espera" para estar listo para habilitar nuevamente.

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;

Ok, entonces siguiente estado. cambiando.

Primero, queremos incrementar el contador de turnos y realizar el turno 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 cuándo se realiza el cambio, para dejar el estado de cambio y volver a esperar:

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

Y eso es todo!

En el siguiente fragmento de código, tenga en cuenta que el estado de "reinicio" y el estado de "espera" son distintos. Esto es útil porque, en general, el restablecimiento asíncrono solo se produce durante el inicio 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 se inicie correctamente sin tener que enviar datos durante el reinicio. Además, ahora se puede ingresar el estado de espera según sea necesario, sin tener que realizar un reinicio asíncrono.

También note que solo estoy manejando 3 señales (4 contando la variable) en mi proceso, y solo esas señales. Si una señal se activa en un proceso, no se debe conducir en ningún otro lugar que no sea ese proceso. No fuera del proceso, no en otro proceso. Una señal se dirige dentro de un proceso y un proceso solamente. Puede comparar la señal con otras señales en otros lugares (si hay afirmaciones, y así), pero no le dé un valor a la señal en ningún lugar, excepto en un proceso. Y, en general, se define en la parte de restablecimiento, y luego, cuando sea necesario, en el proceso adecuado. Pero solo 1 proceso. Si me hubieran dicho esto, me habría ahorrado un montón de tiempo mientras aprendía.

Aquí está el código completo en una porción:

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;
    
respondido por el stanri
0
La respuesta de

@ stanri es impresionante y bastante precisa ... si puedo resumir / aclarar la primera declaración, la declaración 'for' en un HDL simplemente expresa 'replicación sintáctica' no 'ejecución secuencial'.

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

No empiece escribiendo el código VHDL, comience dibujando esquemas lógicos (al menos en algún nivel de abstracción). Al final del día, HDL es solo una forma basada en texto de expresar el contenido de los esquemas lógicos.

    
respondido por el vicatcu

Lea otras preguntas en las etiquetas