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.