VHDL: ¿Cómo leer dos veces un banco de registro?

1

Se nos ha asignado la tarea de crear un banco de registros que pueda realizar una lectura dual, pero solo escritura única. En el momento lo tengo todo trabajando aparte de la doble lectura. ¿Podría alguien apuntarme en la dirección correcta?

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;


entity RegisterBank is
     Generic(N : integer := 16;
     M: integer := 16);
    Port ( CLK : in  STD_LOGIC;
           Din : in  STD_LOGIC_VECTOR(N downto 0);
              ReadNum : in STD_LOGIC_VECTOR(M downto 0);
              WriteNum : in STD_LOGIC_VECTOR(M downto 0);
              ReadEnable : in STD_LOGIC;
              WriteEnable : in STD_LOGIC;
           Dout : out  STD_LOGIC_VECTOR(N downto 0));
end RegisterBank;

architecture Behavioral of RegisterBank is
    Component RegisterN port(
        D: in STD_LOGIC_VECTOR(N downto 0);
        Q: in STD_LOGIC_VECTOR(N downto 0);
        Load, CLK: in STD_LOGIC);
    end Component;
    Type RegisterArray is Array(N downto 0) of STD_LOGIC_VECTOR(N downto 0);
    Signal RegisterSignal: RegisterArray;
    Signal W: STD_LOGIC_VECTOR(N downto 0);
begin
    Reg0: RegisterN port map(Din, RegisterSignal(0), W(0), CLK);
    Gen: for i in N downto 1 generate
        RegN: RegisterN port map(Din, RegisterSignal(i), W(i), CLK);
    end generate Gen;
    RegisterSignal(0) <= STD_LOGIC_VECTOR(TO_UNSIGNED(0, M));
    process(CLK) begin
        if RISING_EDGE(CLK) then
            if WriteEnable='1' then
                RegisterSignal(TO_INTEGER(UNSIGNED(WriteNum))) <= Din;
            end if;
            if ReadEnable='1' then
                Dout <= RegisterSignal(TO_INTEGER(UNSIGNED(ReadNum)));
            else
                Dout <= (others => 'Z');
            end if;
        end if;
    end process;

end Behavioral;
    
pregunta SRG3006

2 respuestas

2

Aquí hay una versión parametrizada de su archivo de registro (con los nombres cambiados ligeramente) sin usar generar declaraciones. (Nota, no lo he probado, parece que funcionará).

library ieee; 
use ieee.std_logic_1164.all; 
use ieee.numeric_std.all;

entity regfile is
    generic(
        N:      natural := 16;  -- word width in bits
        M:      natural := 4    --  Address bits, number of words = 2**M 
    );

    port ( 
        clk:        in   std_logic;
        Din:        in   std_logic_vector (N-1 downto 0);
        RdAddrA:    in   std_logic_vector (M-1 downto 0);
        RdAddrB:    in   std_logic_vector (M-1 downto 0);
        WrAddr:     in   std_logic_vector (M-1 downto 0);
        RdEnabA:    in   std_logic;
        RdEnabB:    in   std_logic;
        WrEnab:     in   std_logic;
        DoutA:      out  std_logic_vector (N-1 downto 0);
        DoutB:      out  std_logic_vector (N-1 downto 0)
    ); end entity;

architecture behave of regfile is

    type regfile_array is array (natural range 0 to 2**M-1) of 
                               std_logic_vector ( N-1 downto 0);
    signal regfile: regfile_array;
     begin
     RFILE:
    process(clk) begin
        if rising_edge(clk) then
            if WrEnab = '1' then
                regfile(to_integer(unsigned(WrAddr))) <= Din;
            end if;
            if RdEnabA = '1' then
                DoutA <= regfile(to_integer(unsigned(RdAddrA)));
            else
                DoutA <= (others => 'Z');
            end if;
            if RdEnabB = '1' then
                DoutB <= regfile(to_integer(unsigned(RdAddrB)));
            else
                DoutB <= (others => 'Z');
            end if;
        end if;
    end process;

end architecture;

Si nada más muestra cómo agregar un segundo puerto de salida. Tenga en cuenta que un puerto de lectura en una memoria es un multiplexor grande. Otras lecciones que enseña son cómo obtener los parámetros N y M correctamente, suponiendo que desea un archivo de registro de 16 bits con una profundidad de 16 palabras ( 2**M ). El poder de dos implicaciones de tamaño proviene de hacer que los multiplexores de lectura (y la dirección de escritura) sean simétricos (en términos de retrasos).

Observe la relación entre el número de bits de dirección ( M ) y el número de palabras ( 2**M ). Lo -1 es dar 0 como un índice de bits o palabras (el último N ).

La idea aquí es que no estoy haciendo tu tarea, sino que te muestro lo que está mal.

generar modelo basado en sentencias

Vincular el número de bits de dirección al tamaño de la matriz evitaría que obtuvieras un error de rango de índice durante la simulación si incrementaras ' WriteNum , ReadNumA o ReadNumB más allá del equivalente decimal de 17, porque lo modeló, tiene 18 bits de dirección y 17 registros y solo necesita 5 bits (o 4 si realmente desea tener 16 registros).

Para usar una declaración de generación para usar (en su caso) RegisterSignal como cables conectados a la salida de esos RegisterN s en lugar de mantener el contenido de esos registros ( RegisterSignal (2 ** M-1 hasta 0), debe eliminar la parte de escritura de la declaración del proceso.

Sin haber visto ni escrito una descripción de VHDL de RegisterN , necesita controlar a cuál le escribe escribiendo load e (lo que no hace completamente con W ). Tenga en cuenta que también tiene que hacer que genere la declaración directamente.

Suponiendo que no desea que 2**(M+1) se registre para M := 16 , y que se pretende que tenga 16 registros (el número genérico predeterminado), necesita una forma genérica de decodificación de WriteNum de cualquier longitud a W bits , y para especificar esos W bits como entradas a sus respectivas RegisterN instanciaciones.

W no se relaciona actualmente con 'M':

Signal W: STD_LOGIC_VECTOR(N downto 0);

Debería estar relacionado con el número de registros (y para una potencia de 2 que es 2**M ):

signal W: std_logic_vector(2**M-1 downto 0);

(Y de nuevo, -1 nos permite incluir 0 como valor de identidad en el conjunto 2**M ).

Entonces, nuestra declaración de generación generaría un RegisterN para cada registro, con Din conectado como entrada universal, como es CLK y Q s real y RegisterSignal s reals como usted muestra.

El esquema de iteración de su declaración de generación podría ser for natural range i in 2**M-1 downto 0 generate

Además de hacer que los registros tengan el número de W', WriteNum and ReadNum bits write the other bit you are missing is write enable steering (which W (i) 'es verdadero).

Lo más fácil sería crear una declaración de asignación de señal concurrente dentro del ciclo de generación con:

    W(i) <= '1'  when 
                    TO_INTEGER(UNSIGNED(WriteNum)) = i and WriteEnable= '1' 
                 else 
            '0';

Lo que generará un 'reconocedor' para la dirección WriteNum correspondiente y hará que W(TO_INTEGER(UNSIGNED(WriteNum)) sea alto ( '1' ) cuando escriba en ese RegisterN .

¿Se te ocurre algo más compacto?

Así que para resumir. Use 'N' y 'M' correctamente. Teniendo en cuenta que entity RegisterN también debe tener N como una constante genérica o suministrada). Elimine la parte de escritura de la instrucción de proceso y agregue una instrucción simultánea en la instrucción de generación para generar el RegisterN load s individual.

    
respondido por el user8352
2

Para comenzar, su entity necesitará un segundo conjunto de puertos ReadNum , ReadEnable y Dout . ¿Es esto un consejo suficiente?

    
respondido por el Dave Tweed

Lea otras preguntas en las etiquetas