n registro de desplazamiento de 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 continúa siendo alta o baja. He puesto un ciclo for para cambiar n veces dentro de un proceso. Mi código se da a continuación.

No creo que el ciclo for esté funcionando, ya que el cambio no está restringido a n veces. ¿Dónde 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;

Respuestas (2)

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 ciclo se ejecuta dentro de un ciclo de reloj, y solo se muestra el resultado final del ciclo al final. Esto es lo que está sucediendo en su código. Todo el ciclo se ejecuta en un solo ciclo de reloj.

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

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

Se activa un proceso cada vez que cambia una de las señales de la lista de sensibilidad ("clk, reset" en este caso). Esto significa que el proceso ya está repitiendo cada ciclo de reloj (si un reloj está en la lista de sensibilidad). Puede usar esto a su 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_counterrealiza un seguimiento de cuántas iteraciones (o cambios) se han producido hasta el momento. Compararás shift_countercon n-1para ver si ya has terminado.

A continuación, podría ser una buena idea pensar en los estados en los que se encontrará su proceso. Tal vez un estado de espera para cuando el proceso no esté cambiando y un estado cambiante para cuando sí lo esté.

La definición de señal de estado:

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

En el proceso propiamente dicho:

case state is
   when waiting =>

Bien, 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 sabe exactamente cuáles son los valores de su señal cuando la habilitación es alta. Además, al final del turno, puede cambiar los estados nuevamente a "esperando" 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;

Bien, entonces el 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 salir del estado de cambio y volver a esperar:

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

¡Y eso es!

En el siguiente fragmento de código, tenga en cuenta que el estado de "restablecimiento" y el estado de "espera" son distintos. Esto es útil porque, por lo general, el restablecimiento asincrónico solo ocurre al inicio y no se espera que procese ningún dato durante este tiempo. Al mover el temp_reg <= parallel_inal estado de espera (fuera del reinicio asíncrono), estamos permitiendo que el módulo de control parallel_inse inicie correctamente sin tener que enviar datos durante el reinicio. Además, ahora se puede ingresar al estado de espera según sea necesario, sin tener que realizar un reinicio asíncrono.

También tenga en cuenta que solo manejo 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 debería activarse en ningún otro lugar que no sea ese proceso. No fuera del proceso, no en otro proceso. Una señal es impulsada dentro de un proceso y solo un proceso. Puede comparar la señal con otras señales en otros lugares (si declaraciones, y demás), 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 reinicio y, luego, donde sea necesario en el proceso propiamente dicho. Pero solo 1 proceso. Si me hubieran dicho esto, me habría ahorrado toneladas de tiempo mientras estaba aprendiendo.

Aquí está el código completo en una sola 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;
Stanri, eso me enseñó mucho. Le agradezco sinceramente su ayuda.
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 "controlador múltiple"! ¿Qué está s_inhaciendo exactamente aquí? Este registro de desplazamiento parece engancharse en los datos paralelos y luego desplazarse en serie.
@ShashankVM, en el caso de la pregunta, parallel_in proporciona el valor de reinicio del registro de desplazamiento interno. Así que lo mantuve con el mismo nombre y función. De lo contrario, no se usa, OP debe explicar para qué estaba destinado originalmente. Acepto que un registro SISO debe ser estrictamente serial.
@ShashankVM Escribí esta respuesta hace 7 años con la intención de demostrar el pensamiento detrás del uso de una máquina de estado para resolver problemas como este. La intención es demostrar un concepto general. El OP puede modificarlo según sus necesidades. Mis prácticas de codificación han cambiado desde entonces y estoy seguro de que hay muchas cosas que cualquiera podría encontrar mal con esto, ya que lo escribí en mi tiempo libre y no como un ejercicio académico riguroso. Le invitamos a escribir una respuesta si cree que la mía es inadecuada.
Consulte una discusión de su respuesta en Meta electronics.meta.stackexchange.com/q/7298/238188

La respuesta de @stanri es impresionantemente completa 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. 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 la construcción 'for'.

No comience escribiendo 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.

Aunque las personas tienen diferentes enfoques del diseño digital, su descripción de la declaración for no es del todo correcta. Si considera la declaración de generación, entonces su descripción es verdadera, pero si considera la declaración de bucle dentro de un proceso, este es un código secuencial. Si el bucle contiene asignaciones de señal a la misma señal, la asignación anterior se sobrescribe con la siguiente, pero si se trata de una variable, se comporta como un código secuencial normal.