Memoria Ram em FPGA - Placa DE0

Memoria RAM em FPGA

Nesse artigo será apresentado um programa em VHDL utilizado para a escrita e leitura de uma memória RAM implementada dentro de um FPGA (CICLONE III), presente na placa de desenvolvimento DE0 . Para a implementação da memória utilizou-se a ferramenta de megafunções do QUARTUS© II. Devido a limitação da quantidade de pinos disponíveis, optou-se por não disponibilizar a entrada de endereços, somente a entrada de dados. Os endereços de escrita e leitura serão independentes e serão incrementados a cada escrita e leitura, respectivamente. Este exercício tem como objetivo a familiarização com as lógicas de escrita e leitura utilizadas por uma memória RAM. Este programa será simulado utilizando o Modelsim-Altera, por meio de um testbench.

Nesse projeto será utilizado apenas a Placa DE0®.

Descrição do Funcionamento

A memória criada com a ferramenta de Mega-Wizard Plug-in Manager do QUARTUS® II (Para aprender como criar megafunções clique aqui) possui 2 portas de endereços, uma para leitura e outra para escrita, uma porta para clock, 8 portas de entrada de dados, 8 portas de saída de dados, uma porta de habilitação de leitura e uma porta de habilitação de escrita. O programa de controle de leitura e escrita vai identificar se alguma das ações está habilitada, quando a escrita está habilitada, atribui-se o valor do dado de entrada do projeto a posição correspondente ao endereço de escrita. De forma similar, quando a leitura está habilitada, é atribuido à saida o valor correspondente a posição do endereço de leitura. O diagrama de blocos abaixo ilustra o funcionamento do projeto:

diagrama

Figura 2: Diagra de blocos do projeto implementado.

Descrição em VHDL

O projeto utiliza a placa DE0, utilizando 21 portas do FPGA, sendo uma para clock, uma para reset, uma para habilitação de leitura, uma para habilitação de escrita, uma para habilitação do chip, 8 para as entradas de dados e 8 para as saídas de dados. Abaixo está a declaração da entidade:

 
entity Mem_ram_vs2 is
    port
    (
        -- Declaracao dos sinais de entrada e saida
        clk, resetn, cen, write_en, read_en : in std_logic;
        dado : in std_logic_vector (7 downto 0);
        saida : out std_logic_vector (7 downto 0)
    );
 end entity Mem_ram_vs2;

No inicio da arquitetura é declarado o componente criado por megafunção:

architecture memoria_vs2 of mem_ram_vs2 is
    -- Declaracao da memoria cria via megafuncao
    component Mem_ram_2
        PORT
        (
            clock : IN STD_LOGIC  := '1';
            data : IN STD_LOGIC_VECTOR (7 DOWNTO 0);
            rdaddress : IN STD_LOGIC_VECTOR (2 DOWNTO 0);
            rden : IN STD_LOGIC  := '1';
            wraddress : IN STD_LOGIC_VECTOR (2 DOWNTO 0);
            wren : IN STD_LOGIC  := '0';
            q : OUT STD_LOGIC_VECTOR (7 DOWNTO 0)
        );
    end component;

O sinais usados neste projeto servem para determinar qual o endereço que será lido/escrito pela/na memória. Os sinais de enable são usados para a habilitação da memoria.

signal enable_wr, enable_rd : std_logic:='0';
signal rd_addr, wr_addr : std_logic_vector (2 downto 0);

O process principal pode ser dividido em 4 partes, reset, proteção contra ruído, escrita e leitura. Sendo que a leitura e a escrita podem ocorrer concorrentemente.

Na rotina de resete, atribui-se os valores iniciais para as variáveis e sinais.

process (clk, resetn)
    variable addr_read, addr_write : std_logic_vector (2 downto 0) := "111";
    variable conta_read, conta_write : integer range 0 to 25000000;
    variable flag_write, flag_read : std_logic;
 
    begin
    if resetn = '0' then
        addr_write := "111";
        addr_read := "111";
        flag_write := '0';
        flag_read := '0';
        conta_read := 0;
        conta_write:= 0;
        -- saida <= "ZZZZZZZZ";
        enable_rd <= '0';
        enable_wr <= '0';

A proteção contra ruído serve para rejeitar o ruído de trepidação decorrente dos push bottons presentes na placa e utilizados como sinais de enable.

if conta_write < 25000000 then
    conta_write := conta_write + 1;
end if;
if conta_read < 25000000 then
    conta_read := conta_read +1;
end if;
 

A parte de escrita apresenta três variáveis de controle: habilitação de escrita, flag de escrita e a variavel de proteção contra ruído. A habilitação de escrita verififica a requisição de uma escrita. A flag evita que sejam escritos em mais de um endereço por requisição de escrita. Por ultimo, como ja foi explicado, a variável de proteção contra ruído para eliminar os efeitos do ruído de trepidação.

if write_en = '0' and flag_write = '0' and conta_write = 25000000 then
    flag_write := '1';
    conta_write := 0;
    addr_write := addr_write + 1;
elsif write_en = '1' and flag_write = '1' then
    flag_write := '0';
    enable_wr <= '1';
elsif enable_wr = '1' then
    enable_wr <= '0';
end if;

A rotina de leitura funciona de forma analoga a rotina de escrita.

if read_en = '0' and flag_read = '0' and conta_read = 25000000 then
    flag_read := '1';
    conta_read:= 0;
    addr_read := addr_read + 1;
elsif read_en = '1' and flag_read = '1' then
    flag_read := '0';
    enable_rd <= '1';
elsif enable_rd = '1' then
    enable_rd <= '0';
end if;

No final de processo há a passagem dos valores das variáveis para os sinais.

    rd_addr <= addr_read;
    wr_addr <= addr_write;
end process; 

Por ultimo, faz-se a instanciação da memória, atribuindo os sinais da seguinte forma:

-- instanciacao da memoria ram
Mem_ram_2_inst : Mem_ram_2 PORT MAP (
    clock => clk, -- atribui o mesmo clock para a memoria
    data => dado,
    rdaddress => rd_addr,
    rden => enable_rd, -- Atribui o sinal inverso do botao de write enable,
                                   -- pois quando pressionado o botao apresenta '0'
     wraddress => wr_addr,
     wren => enable_wr, -- Atribui o sinal inverso do botao de read enable,
                                       -- pois quando pressionado o botao apresenta '0'
     q => saida
);

Simulação

Para a simulação do projeto, emulou-se a gravação de todos os 8 endereços de memória, atribuindo valores de 1 a 8. Em seguida, fez-se as leituras dos endereços para averiguar o funcionamento da memória. Como a leitura e escrita são síncronas, também emulou-se o clock. O código abaixo apresenta as primeiras linhas do testbench:

UUT:  entity work.Mem_ram_vs2
port map
(
     clock, resetn, cen,
     wren, rden, dado, saida 
);

 

Gera_Clock:
process
    begin
    -- simulação do pressionamento do botão de reset.
    resetn <= '0' after 0 ms,
    '1' after 1  ms,
    '0' after 50 ms;
 
    clock <= '0';
    wait for 1 ms;
    -- rotina de geração de clock
    while resetn = '1' loop
        clock <= not clock;
        wait for 20833 ps;
    end loop;
 
    wait;
end process;

 

Envia_dados:
process
 
    begin
    cen <= '0';
    wren <='1';
    rden <='1';
    dado <= x"01";
    wait for 1 ms;
    wren <= '0';
    wait for 500 us;
    wren <= '1';
    wait for 500us;
    dado <= x"02";
    wait for 1 ms;
    wren <= '0';
    wait for 1 ms;
    wren <= '1';
    wait for 5 ms;
    rden <= '0';
    wait for 1 ms;
    wren <='1';
    rden <='1';

As formas de ondas encontradas na simulação com o Modelsim®-Altera estão apresentadas abaixo:

sim_1

 Figura 3: Formas de ondas da simulação.

sim_2

Figura 4: Detalhe dos dados lidos

Estrutura Física

 Placa_DE0

Figura 5: Projeto implementado na placa DE0®.

O projeto é composto apenas pela placa DE0, em usa os 8 LEDs como sendo a saída da memória, 9 switches, 8 como entrada de dados e 1 como pino de chip enable, e 3 push bottoms, 1 como resete, 1 como habilitação de escrita e 1 como habilitação de leitura. A pinagem utilizada está apresentada abaixo, utilizando como base o manual do usuário da placa DE0.

 Pinagem

Figura 6: Atribuição de pinos do projeto.

Gravação e Teste

Para fazer o teste basta gravar o programa seguindo as intruções encontradas no tutorial ou no próprio manual da placa DE0. Em seguida basta dar os comandos de gravação  e leitura e observar o resultado. O teste pode ser observado no video no início da página.

Arquivos de Projeto

Links