Tengo problemas para conseguir que el diseño de mi computadora FPGA 80's encaje en una placa Papilio Duo que es un Spartan 6 - xcs6slx9. El problema radica en que la RAM se deduce como distribuida en lugar de bloque.
Versión corta: estoy usando una entidad genérica para inferir los bloques de RAM (ver más abajo) y al encontrar que para cualquier cosa hasta un ancho de dirección de 11 parece estar distribuida, un ancho de dirección de 12 o más XST es feliz para ponerlo en bloques. He intentado atributos para marcarlo como bloque, pero eso no parece funcionar.
Solución actual: amplíe el ancho de la dirección de una instancia, poniendo a cero el bit de dirección alta ... ahora el diseño encaja.
Versión larga:
El diseño requiere tres módulos ram de 2048x8bit de doble puerto. Un puerto necesita acceso de lectura / escritura (acceso a la CPU), el otro requiere solo lectura (controlador de video). Los puertos son asíncronos y se ejecutan en diferentes dominios de reloj.
Originalmente utilicé este módulo: RamDualPort para esto.
entity RamDualPort is
generic
(
ADDR_WIDTH : integer;
DATA_WIDTH : integer := 8
);
port
(
-- Port A
clock_a : in std_logic;
clken_a : in std_logic;
addr_a : in std_logic_vector(ADDR_WIDTH-1 downto 0);
din_a : in std_logic_vector(DATA_WIDTH-1 downto 0);
dout_a : out std_logic_vector(DATA_WIDTH-1 downto 0);
wr_a : in std_logic;
-- Port B
clock_b : in std_logic;
addr_b : in std_logic_vector(ADDR_WIDTH-1 downto 0);
dout_b : out std_logic_vector(DATA_WIDTH-1 downto 0)
);
end RamDualPort;
architecture behavior of RamDualPort is
constant MEM_DEPTH : integer := 2**ADDR_WIDTH;
type mem_type is array(0 to MEM_DEPTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
shared variable ram : mem_type;
begin
process (clock_a)
begin
if rising_edge(clock_a) then
if clken_a='1' then
if wr_a = '1' then
ram(to_integer(unsigned(addr_a))) := din_a;
end if;
dout_a <= ram(to_integer(unsigned(addr_a)));
end if;
end if;
end process;
process (clock_b)
begin
if rising_edge(clock_b) then
dout_b <= ram(to_integer(unsigned(addr_b)));
end if;
end process;
end;
Un par de problemas con esto: 1) dependiendo del ancho de la dirección, algunos se deducen como distribuidos (el problema principal que pregunto) pero también 2) los que se estaban inferiendo para bloquear RAMS se implementaron como lecturas primero, que para los relojes asíncronos tiene problemas en Spartan 6.
La única forma que encontré para solucionar el problema de la lectura en primer lugar fue hacer que ambos puertos lean / escriban con un nuevo módulo "RamTrueDualPort" de la siguiente manera:
entity RamTrueDualPort is
generic
(
ADDR_WIDTH : integer;
DATA_WIDTH : integer := 8
);
port
(
-- Port A
clock_a : in std_logic;
clken_a : in std_logic;
addr_a : in std_logic_vector(ADDR_WIDTH-1 downto 0);
din_a : in std_logic_vector(DATA_WIDTH-1 downto 0);
dout_a : out std_logic_vector(DATA_WIDTH-1 downto 0);
wr_a : in std_logic;
-- Port B
clock_b : in std_logic;
clken_b : in std_logic;
addr_b : in std_logic_vector(ADDR_WIDTH-1 downto 0);
din_b : in std_logic_vector(DATA_WIDTH-1 downto 0);
dout_b : out std_logic_vector(DATA_WIDTH-1 downto 0);
wr_b : in std_logic
);
end RamTrueDualPort;
architecture behavior of RamTrueDualPort is
constant MEM_DEPTH : integer := 2**ADDR_WIDTH;
type mem_type is array(0 to MEM_DEPTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
shared variable ram : mem_type;
begin
process (clock_a)
begin
if rising_edge(clock_a) then
if clken_a='1' then
if wr_a = '1' then
ram(to_integer(unsigned(addr_a))) := din_a;
end if;
dout_a <= ram(to_integer(unsigned(addr_a)));
end if;
end if;
end process;
process (clock_b)
begin
if rising_edge(clock_b) then
if clken_b='1' then
if wr_b = '1' then
ram(to_integer(unsigned(addr_b))) := din_b;
end if;
dout_b <= ram(to_integer(unsigned(addr_b)));
end if;
end if;
end process;
end;
De modo que se solucionó el problema de lectura en primer lugar y los rams que van a bloquear el ram ahora se implementan como write-first (NB: En realidad no me importa leer-first / write-first, excepto el Spartan 6 corrupting ram read -primera edición).
Ahora el problema es obtener las instancias 2k (addrWidth 11) más pequeñas en el ram del bloque. Como se mencionó, he probado los atributos, pero todavía insiste en ponerlo en un ram distribuido. No pude encontrar ninguna documentación sobre ram_style para las variables (a diferencia de las señales) pero adiviné esto: (Note el bit ram:variable
)
constant MEM_DEPTH : integer := 2**ADDR_WIDTH;
type mem_type is array(0 to MEM_DEPTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
shared variable ram : mem_type;
ATTRIBUTE ram_extract: string;
ATTRIBUTE ram_extract OF ram:variable is "yes";
ATTRIBUTE ram_style: string;
ATTRIBUTE ram_style OF ram:variable is "block";
Ahora XST escupe esto, lo que sugiere que se entiende la sintaxis del atributo: (Note la mención de ram_extract
y ram_style
)
Synthesizing Unit <RamTrueDualPort_1>.
Related source file is "C:/Documents and Settings/Brad/Projects/fpgabee/Hardware/FPGABeeCore/RamTrueDualPort.vhd".
ADDR_WIDTH = 12
DATA_WIDTH = 8
Set property "ram_extract = yes" for signal <ram>.
Set property "ram_style = block" for signal <ram>.
Found 4096x8-bit dual-port RAM <Mram_ram> for signal <ram>.
Found 8-bit register for signal <dout_b>.
Found 8-bit register for signal <dout_a>.
Summary:
inferred 1 RAM(s).
inferred 16 D-type flip-flop(s).
inferred 1 Multiplexer(s).
Unit <RamTrueDualPort_1> synthesized.
Synthesizing Unit <RamTrueDualPort_2>.
Related source file is "C:/Documents and Settings/Brad/Projects/fpgabee/Hardware/FPGABeeCore/RamTrueDualPort.vhd".
ADDR_WIDTH = 11
DATA_WIDTH = 8
Set property "ram_extract = yes" for signal <ram>.
Set property "ram_style = block" for signal <ram>.
Found 2048x8-bit dual-port RAM <Mram_ram> for signal <ram>.
Found 8-bit register for signal <dout_b>.
Found 8-bit register for signal <dout_a>.
Summary:
inferred 1 RAM(s).
inferred 16 D-type flip-flop(s).
inferred 2 Multiplexer(s).
Unit <RamTrueDualPort_2> synthesized.
Sin embargo, los bloques de 2k todavía se distribuyen:
2048x8-bit dual-port distributed RAM : 2
4096x8-bit dual-port block RAM : 1
Si saco la línea de dirección redundante (es decir, la devuelvo a addrWidth = 11), las tres instancias terminan distribuidas y el diseño ya no se ajusta:
2048x8-bit dual-port distributed RAM : 3
¿Qué hacer? Realmente no quiero volver a Coregen para esto.
PD: Soy un aficionado a esto, sé amable.