VHDL: el valor no se asigna de inmediato

1

Hice una pregunta similar aquí . Pensé que esa respuesta sería aplicable a este código pero tengo el mismo problema. Tengo una ROM que funciona al doble de la velocidad de mi CPU (dejé fuera todos los comandos, excepto 'OUT' para que sea más fácil de entender). La razón por la que se ejecuta al doble de la velocidad es porque la ROM tiene una tubería.

El primer valor en mi ROM es '0xF100' que ejecutaría la instrucción 'OUT'. Para propósitos de depuración, dejo que mi instrucción 'OUT' ponga 'salida' a "11111111". Cuando simulo el diseño con ModelSim, encuentro que 'output' no se configura en "11111111" inmediatamente, sino que necesita otro borde ascendente de 'ClockDivided' para establecer su valor. Quiero 'output' para obtener su valor inmediatamente, ¿cómo hago esto?

código:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.std_logic_unsigned.all;
entity first is
port(
input : in STD_LOGIC_VECTOR(7 downto 0) := "00000000";
output : out STD_LOGIC_VECTOR(7 downto 0) := "00000000";
PCout : out STD_LOGIC_VECTOR(15 downto 0) := "0000000000000000";
clock : in STD_LOGIC
);
end first;

architecture behavioral of first is
signal PC : STD_LOGIC_VECTOR(7 downto 0) := "00000000";
signal data : STD_LOGIC_VECTOR(15 downto 0) := "0000000000000000";
signal regC : STD_LOGIC_VECTOR(3 downto 0) := "0000";
signal regA : STD_LOGIC_VECTOR(3 downto 0) := "0000";
signal regB : STD_LOGIC_VECTOR(3 downto 0) := "0000";
signal opcode : STD_LOGIC_VECTOR(3 downto 0) := "0000";
type registerFile is array(15 downto 0) of std_logic_vector(7 downto 0);
signal registers : registerFile := (others => "00000000");
signal clockDivided : STD_LOGIC := '1';
component rom is
port(
    address : in STD_LOGIC_VECTOR(7 downto 0);
    q : out STD_LOGIC_VECTOR(15 downto 0);
    clock : in STD_LOGIC := '1'
);
end component;
begin
rom_inst : rom PORT MAP (
        address => PC,
        clock => clock,
        q => data
        );  
PCout <= data;
process(clock)
begin
if rising_edge(clock) then
    clockDivided <= not(clockDivided); --dividing the clock because the ROM runs twice as fast
end if;
end process;
process(clockDivided)
begin
opcode <= data(15 downto 12);
regC <= data(11 downto 8);
regA <= data(7 downto 4);
regB <= data(3 downto 0);
if rising_edge(clockDivided) then   
    registers(0) <= "00000000";
    case opcode is
        when "1111" => output <= "11111111"; --output doesn't get set to "11111111" immediately                                 
        PC <= PC + 1;
        --OUT
        when others =>
        PC <= PC + 1;
        end case;
    end if;
end process;
end behavioral;

banco de pruebas:

library ieee;
use ieee.std_logic_1164.all;
entity testbench is
end testbench;

architecture behavioral of testbench is
signal inputX : STD_LOGIC_VECTOR(7 downto 0) := "00000000";
signal outputX : STD_LOGIC_VECTOR(7 downto 0) := "00000000";
signal PCX : STD_LOGIC_VECTOR(15 downto 0);
signal clockX : STD_LOGIC := '0';
component first
port(
    input : in STD_LOGIC_VECTOR(7 downto 0);
    output : out STD_LOGIC_VECTOR(7 downto 0);
    PCout : out STD_LOGIC_VECTOR(15 downto 0);
    clock : in STD_LOGIC
);
end component;
begin
uut: first port map(
    input => inputX,
    output => outputX,
    PCout => PCX,
    clock => clockX
);
stim_proc: process
begin
    clockX <= '0';
    wait for 1 ns;
    clockX <= '1';
    wait for 1 ns;
end process;
end behavioral;
    
pregunta gilianzz

1 respuesta

3

Mirando el siguiente código:

process(clockDivided)
begin
    opcode <= data(15 downto 12);
    regC <= data(11 downto 8);
    regA <= data(7 downto 4);
    regB <= data(3 downto 0);
    if rising_edge(clockDivided) then
        --
    end if;
end process;

La lista de sensibilidad del proceso solo tiene clockDivided presente. Cuando data cambia como parte de la ROM que se está leyendo, el proceso no se ejecuta; solo cuando ocurre el siguiente evento en clockDivided sucede esto, por lo que su opcode tampoco cambiará hasta ese momento. Esto es lo que causa su retraso.

Lo más fácil es mover las asignaciones concurrentes como opcode <= data(15 downto 12); fuera del proceso. igualmente podría agregar data a la lista de sensibilidad, pero realmente no hay necesidad de que esas declaraciones estén en el proceso en primer lugar.

Además, desde mi experiencia, es una buena idea mover la extracción de varios campos de palabras de programa desde un std_logic_vector a una función. La función acepta la palabra de programa como parámetro y devuelve un tipo de registro que contiene su instrucción "descodificada". Algo como:

type INSTRUCTION_type is record (
    opcode : std_logic_vector(3 downto 0);
    regA : std_logic_vector (3 downto 0);
    regB : std_logic_vector (3 downto 0);
    regC : std_logic_vector (3 downto 0);
end record;

function from_slv(v : std_logic_vector) return INSTRUCTION_type is
    variable rv : INSTRUCTION_type;
begin
    rv.opcode := v(15 downto 0);
    rv.regA := v(7 downto 4);
    rv.regB := v(3 downto 0);
    rv.regC := v(11 downto 8);
    return rv;
end function;

Luego en tu entidad:

program_word <= from_slv(data_out);

...

case (program_word.opcode) is

etc

Esto le permite tener la interpretación de la palabra del programa en un lugar fácilmente modificable en un paquete. El registro permite que los diversos elementos de la palabra del programa se conecten fácilmente entre entidades, segmentados, etc. esta técnica también hace que sea muy fácil agregar más elementos a la palabra del programa, sin tener que cambiar las declaraciones de puerto de la entidad y las instancias.

    
respondido por el scary_jeff

Lea otras preguntas en las etiquetas