Estoy usando la placa de evaluación Spartan SP601, que incluye SDRAM Elpida EDE1116ACBG-8E-E de 1 GB. Me gustaría construir un controlador de RAM, pero no tengo experiencia trabajando con RAM antes. Basé en gran medida mi diseño en un controlador de RAM en un libro de texto para dispositivos Spartan-3. Proporcioné mi diseño a continuación en caso de que sea de ayuda. Las señales de control para la SDRAM son cas_b
, ras_b
y we_b
.
Tengo las siguientes preguntas:
¿Hay alguna forma de depurar mi controlador SDRAM? Me parece que funciona o no funciona, y realmente no puedo decir qué interacciones están ocurriendo entre el controlador y la SDRAM.
¿Cuáles son los parámetros de temporización más relevantes que debo tener en cuenta para mi SDRAM? La cantidad de parámetros de tiempo enumerados en la hoja de datos de Elpida es abrumadora de ver.
¿Tiene algún otro consejo para desarrollar un controlador SDRAM que funcione? ¿Mi diseño a continuación es un buen comienzo?
Circuito básico de prueba de SDRAM
library IEEE;
library UNISIM;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use UNISIM.vcomponents.all;
entity ram_ctrl_test is
Port ( reset : in STD_LOGIC;
-- clk : in STD_LOGIC;
I, IB : in STD_LOGIC;
btn : in STD_LOGIC_VECTOR (2 downto 0);
rx : in STD_LOGIC;
tx : out STD_LOGIC;
led : out STD_LOGIC_VECTOR (7 downto 0);
ad : out STD_LOGIC_VECTOR (12 downto 0);
dio_a : inout STD_LOGIC_VECTOR (15 downto 0);
bank : out STD_LOGIC_VECTOR (2 downto 0);
cke : out STD_LOGIC;
cas_b, ras_b, we_b : out STD_LOGIC);
end ram_ctrl_test;
architecture arch of ram_ctrl_test is
constant ADDR_W: integer := 13;
constant DATA_W: integer := 16;
signal clk, O: std_logic;
signal addr: std_logic_vector(ADDR_W-1 downto 0);
signal sw : std_logic_vector(7 downto 0);
signal data_f2s, data_s2f: std_logic_vector(DATA_W-1 downto 0);
signal mem, rw: std_logic;
signal data_reg: std_logic_vector(7 downto 0);
signal db_btn: std_logic_vector(2 downto 0);
signal rx_empty, update: std_logic;
signal in_data, out_data: std_logic_vector(7 downto 0);
begin
IBUFGDS_inst : IBUFGDS
generic map (
DIFF_TERM => FALSE, -- Differential Termination
IBUF_LOW_PWR => FALSE, -- Low power (TRUE) vs. performance (FALSE) setting for referenced I/O standards
IOSTANDARD => "DEFAULT")
port map (
O => O, -- Clock buffer output
I => I, -- Diff_p clock buffer input (connect directly to top-level port)
IB => IB -- Diff_n clock buffer input (connect directly to top-level port)
);
BUFG_inst : BUFG
port map (
O => clk, -- 1-bit output: Clock buffer output
I => O -- 1-bit input: Clock buffer input
);
ctrl_unit: entity work.sram_ctrl
port map(clk=>clk,reset=>reset,mem=>mem,rw=>rw,addr=>addr,data_f2s=>data_f2s,ready=>open,data_s2f_r=>data_s2f,
data_s2f_ur=>open,ad=>ad,dio_a=>dio_a,bank=>bank,cke=>cke,cas_b=>cas_b,ras_b=>ras_b,we_b=>we_b);
debounce_unit0: entity work.debounce
port map(clk=>clk,reset=>reset,sw=>btn(0),db_level=>open,db_tick=>db_btn(0));
debounce_unit1: entity work.debounce
port map(clk=>clk,reset=>reset,sw=>btn(1),db_level=>open,db_tick=>db_btn(1));
debounce_unit2: entity work.debounce
port map(clk=>clk,reset=>reset,sw=>btn(2),db_level=>open,db_tick=>db_btn(2));
uart: entity work.uart(str_arch)
port map(clk=>clk,reset=>reset,rx=>rx,rd_uart=>update,wr_uart=>update,w_data=>out_data,tx_full=>open,
rx_empty=>rx_empty,r_data=>in_data,tx=>tx);
enabler: entity work.enable(fsm_arch)
port map(clk=>clk,reset=>reset,rx_empty=>rx_empty,en=>update);
address: entity work.addr_controller(fsm_arch)
port map(clk=>clk,reset=>reset,update=>update,in_data=>in_data,out_data=>out_data,sw=>sw);
-- data registers
process(clk)
begin
if rising_edge(clk) then
if (db_btn(0) = '1') then
data_reg <= sw;
end if;
end if;
end process;
-- address
addr <= "00000" & sw;
-- command
process(db_btn,data_reg)
begin
data_f2s <= (others => '0');
if db_btn(1) = '1' then -- write
mem <= '1';
rw <= '0';
data_f2s <= "00000000" & data_reg;
elsif db_btn(2) = '1' then -- read
mem <= '1';
rw <= '1';
else
mem <= '0';
rw <= '1';
end if;
end process;
-- output
led <= data_s2f(7 downto 0);
end arch;
Controlador RAM
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity sram_ctrl is
Port ( clk, reset : in STD_LOGIC;
-- to/from main system
mem : in STD_LOGIC;
rw : in STD_LOGIC;
addr : in STD_LOGIC_VECTOR (12 downto 0);
data_f2s : in STD_LOGIC_VECTOR (15 downto 0);
ready : out STD_LOGIC;
data_s2f_r, data_s2f_ur : out STD_LOGIC_VECTOR (15 downto 0);
-- to/from chip
ad : out STD_LOGIC_VECTOR (12 downto 0);
bank : out STD_LOGIC_VECTOR (2 downto 0);
cke : out STD_LOGIC;
dio_a : inout STD_LOGIC_VECTOR (15 downto 0);
cas_b, ras_b, we_b : out STD_LOGIC);
end sram_ctrl;
architecture arch of sram_ctrl is
type state_type is (idle, rd1, rd2, wr1, wr2);
signal state_reg, state_next: state_type;
signal data_f2s_reg, data_f2s_next: std_logic_vector(15 downto 0);
signal data_s2f_reg, data_s2f_next: std_logic_vector(15 downto 0);
signal addr_reg, addr_next: std_logic_vector(12 downto 0);
signal cas_buf, ras_buf, we_buf, tri_buf: std_logic;
signal cas_reg, ras_reg, we_reg, tri_reg: std_logic;
begin
-- state & data registers
process(clk,reset)
begin
if (reset = '1') then
state_reg <= idle;
addr_reg <= (others => '0');
data_f2s_reg <= (others => '0');
data_s2f_reg <= (others => '0');
cas_reg <= '1';
ras_reg <= '1';
we_reg <= '1';
tri_reg <= '1';
elsif rising_edge(clk) then
state_reg <= state_next;
addr_reg <= addr_next;
data_f2s_reg <= data_f2s_next;
data_s2f_reg <= data_s2f_next;
cas_reg <= cas_buf;
ras_reg <= ras_buf;
we_reg <= we_buf;
tri_reg <= tri_buf;
end if;
end process;
-- next-state logic
process(state_reg, mem, rw, dio_a, addr, data_f2s, data_f2s_reg, data_s2f_reg, addr_reg)
begin
addr_next <= addr_reg;
data_f2s_next <= data_f2s_reg;
data_s2f_next <= data_s2f_reg;
ready <= '0';
case state_reg is
when idle =>
if mem = '0' then
state_next <= idle;
else
addr_next <= addr;
if rw='0' then --write
state_next <= wr1;
data_f2s_next <= data_f2s;
else --read
state_next <= rd1;
end if;
end if;
ready <= '1';
when wr1 =>
state_next <= wr2;
when wr2 =>
state_next <= idle;
when rd1 =>
state_next <= rd2;
when rd2 =>
data_s2f_next <= dio_a;
state_next <= idle;
end case;
end process;
-- "look-ahead" output logic
process(state_next)
begin
cas_buf <= '1'; --default
ras_buf <= '1';
we_buf <= '1';
tri_buf <= '1';
case state_next is
when idle =>
when wr1 =>
tri_buf <= '0';
cas_buf <= '0';
we_buf <= '0';
when wr2 =>
tri_buf <= '0';
when rd1 =>
cas_buf <= '0';
when rd2 =>
cas_buf <= '0';
end case;
end process;
-- to main system
data_s2f_r <= data_s2f_reg;
data_s2f_ur <= dio_a;
-- to SRAM
cas_b <= cas_reg;
ras_b <= ras_reg;
we_b <= we_reg;
ad <= addr_reg;
bank <= "001";
-- i/o for SRAM chip a
cke <= '0';
dio_a <= data_f2s_reg when tri_reg = '0' else (others => 'Z');
end arch;
Parece que está haciendo la mayor parte de su desarrollo en el hardware real. En algunos casos, tiene sentido hacer esto, pero este no es realmente uno de esos casos, ya que los modelos funcionales completos deberían estar disponibles para los chips SDRAM. Recomiendo encarecidamente ubicar un modelo funcional para el chip específico que está utilizando o uno muy similar y hacer toda la depuración en el banco de pruebas. Desde el banco de pruebas, debería ser obvio lo que está pasando con la interfaz y debería poder ver cualquier señal interna que desee mientras se ejecuta la simulación, algo que no es tan fácil cuando su diseño se ejecuta en el FPGA.
Otra opción para un modelo funcional de la RAM sería usar el analizador lógico en chip de Xilinx. De esta manera, puede analizar los valores reales de la señal durante la "ejecución" de su diseño en tiempo real. Por ejemplo, puede observar las salidas de su diseño a la RAM y las entradas de la RAM y comparar esto con el comportamiento previsto.
En la cadena de herramientas ISE se llama Chipscope. Puede encontrar una intrusión paso a paso en este documento de Xilinx: Tutorial de ISE: uso de Xilinx ChipScope Pro ILA Core con Project Navigator para depurar aplicaciones FPGA
Vivado también incluye un analizador lógico en chip. Debajo del paso de diseño "Abrir diseño sintetizado" encontrará una entrada "Configurar depuración". Al seleccionar esto, se abre la vista de diseño sintetizado e inicia un asistente para configurar el analizador lógico en chip. La primera ventana del asistente incluye un enlace a la Guía del usuario de Vivado Design Suite: Programación y depuración (UG908)
usuario_1818839
eugenio wu
alex.forencich
usuario_1818839
usuario_1818839
usuario8352