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;