Construyendo un controlador SDRAM (VHDL)

4

Estoy utilizando la placa de evaluación Spartan SP601, que incluye 1 GB Elpida EDE1116ACBG-8E-E SDRAM. Me gustaría construir un controlador de RAM, pero no tengo experiencia en trabajar con RAM antes. En gran parte basé 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 bien 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 tiempo más relevantes que debo tener en cuenta para mi SDRAM? La cantidad de parámetros de tiempo que figuran en la hoja de datos de Elpida es abrumadora de ver.

  • ¿Tiene algún otro consejo para desarrollar un controlador SDRAM que funcione? ¿Es mi diseño debajo de un buen comienzo?

Circuito de prueba SDRAM básico

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 de 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;
    
pregunta Eugene Wu

2 respuestas

4

Parece que estás haciendo la mayor parte de tu 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. Recomendaría encarecidamente ubicar un modelo funcional para el chip específico que está utilizando o uno muy similar y realizar toda la depuración en el banco de pruebas. Desde el banco de pruebas, debería ser obvio lo que está sucediendo 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.

    
respondido por el alex.forencich
2

Otra opción para un modelo funcional de la RAM sería utilizar el analizador lógico en chip de Xilinx. De esta forma, 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 deseado.

En la cadena de herramientas ISE se llama Chipscope. Se 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 y se inicia un asistente para configurar el analizador lógico en el chip. La primera ventana del asistente incluye un enlace a la Guía de usuario de Vivado Design Suite: Programación y depuración (UG908)

    
respondido por el Martin Zabel

Lea otras preguntas en las etiquetas