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;