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;
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_counter
realiza un seguimiento de cuántas iteraciones (o cambios) 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á 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_in
al estado de espera (fuera del reinicio asíncrono), estamos permitiendo que el módulo de control parallel_in
se 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;
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.
Naranja
sherrellbc
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_in
haciendo exactamente aquí? Este registro de desplazamiento parece engancharse en los datos paralelos y luego desplazarse en serie.Stanri
Stanri
Máquina virtual Shashank