Inicializando Block RAM de FPGA a partir de Arquivos de Texto Usando a Biblioteca TEXTIO do VHDL
Popular block RAM com valores predefinidos é feito mais eficientemente lendo literais binários ou hexadecimais de um arquivo ASCII. Esta técnica também serve para criar ROMs, pois ROM de FPGA é simplesmente uma RAM que só é lida.
Para os exemplos abaixo, as seguintes constantes e tipo de RAM são declarados na região declarativa do arquivo VHDL:
constant ram_depth : natural := 256; constant ram_width : natural := 32; type ram_type is array (0 to ram_depth - 1) of std_logic_vector(ram_width - 1 downto 0);
Este artigo é parte de uma série sobre a biblioteca TEXTIO em VHDL. Leitura de arquivo de estímulo em testbench usando TEXTIO | Leitura de imagem bitmap BMP usando TEXTIO
Lendo Linhas e Dados com TEXTIO
O pacote TEXTIO, localizado na biblioteca padrão std, fornece os subprogramas necessários para I/O de arquivo. Ele está disponível automaticamente, então você só precisa importá-lo:
use std.textio.all;
Nossos dados de RAM serão armazenados em um arquivo ASCII onde cada linha representa uma palavra de memória. O procedimento READLINE lê uma única linha em um objeto LINE. Seu protótipo é:
procedure READLINE (file F: TEXT; L: inout LINE); type LINE is access STRING; -- pointer to a dynamically allocated string
VHDL-2008 introduz OREAD, HREAD e BREAD para extrair valores octais, hexadecimais e binários de um objeto LINE. Para brevidade, focaremos em leituras hexadecimais e binárias. As declarações relevantes são:
procedure HREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR; GOOD : out BOOLEAN); procedure HREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR); alias BREAD is READ [LINE, STD_ULOGIC_VECTOR, BOOLEAN]; alias BREAD is READ [LINE, STD_ULOGIC_VECTOR];
Para versões VHDL legadas (pré-2008), o procedimento padrão READ deve ser usado, que não suporta hexadecimal diretamente. Nesse caso, uma rotina de conversão customizada é necessária.
Carregando Dados Hexadecimais
Notação hexadecimal é conveniente porque cada par de dígitos hex mapeia diretamente para um byte. O arquivo de exemplo ram_content_hex.txt contém 256 linhas, cada uma com uma string hex de 8 caracteres de 00000001 a 00000100:
| 1 2 … 255 256 | 0000000100000002…000000FF00000100 |
A seguinte função impura lê o arquivo e retorna um array ram_type totalmente inicializado. Funciona com VHDL-2008 e mais recente:
impure function init_ram_hex return ram_type is file text_file : text open read_mode is "ram_content_hex.txt"; variable text_line : line; variable ram_content : ram_type; begin for i in 0 to ram_depth - 1 loop readline(text_file, text_line); hread(text_line, ram_content(i)); end loop; return ram_content; end function;
Após definir a função, o sinal RAM é instanciado com o conteúdo inicial:
signal ram_hex : ram_type := init_ram_hex;
VHDL Legado: Implementando HREAD Manualmente
Quando HREAD não está disponível (ex: VHDL-93 ou VHDL-2002), uma rotina de conversão manual pode ser embutida na função de inicialização. O exemplo a seguir demonstra esta abordagem:
impure function init_ram_hex return ram_type is file text_file : text open read_mode is "ram_content_hex.txt"; variable text_line : line; variable ram_content : ram_type; variable c : character; variable offset : integer; variable hex_val : std_logic_vector(3 downto 0); begin for i in 0 to ram_depth - 1 loop readline(text_file, text_line); offset := 0; while offset < ram_content(i)'high loop read(text_line, c); case c is when '0' => hex_val := "0000"; when '1' => hex_val := "0001"; when '2' => hex_val := "0010"; when '3' => hex_val := "0011"; when '4' => hex_val := "0100"; when '5' => hex_val := "0101"; when '6' => hex_val := "0110"; when '7' => hex_val := "0111"; when '8' => hex_val := "1000"; when '9' => hex_val := "1001"; when 'A' | 'a' => hex_val := "1010"; when 'B' | 'b' => hex_val := "1011"; when 'C' | 'c' => hex_val := "1100"; when 'D' | 'd' => hex_val := "1101"; when 'E' | 'e' => hex_val := "1110"; when 'F' | 'f' => hex_val := "1111"; when others => hex_val := "XXXX"; assert false report "Found non-hex character '" & c & "'"; end case; ram_content(i)(ram_content(i)'high - offset downto ram_content(i)'high - offset - 3) := hex_val; offset := offset + 4; end loop; end loop; return ram_content; end function;
Embora um procedimento HREAD dedicado pudesse ser definido, a maioria das ferramentas de síntese (ex: Xilinx Vivado) não suporta procedimentos que aceitam argumento LINE. O erro a seguir ilustra esta limitação:
No Vivado:
[Synth 8-27] Procedure argument of type 'line' is not supported [init_ram_tb.vhd:15]
Lendo Dados Binários
Se a largura da RAM não é múltiplo de 8, literais binários podem ser preferíveis. O arquivo de exemplo ram_content_bin.txt contém 256 linhas, cada uma com uma string binária de 32 bits:
| 1 2 … 255 256 | 0000000000000000000000000000000100000000000000000000000000000010…0000000000000000000000001111111100000000000000000000000100000000 |
Em VHDL-2008, o procedimento BREAD pode ser usado para ler valores binários diretamente:
impure function init_ram_bin return ram_type is file text_file : text open read_mode is "ram_content_bin.txt"; variable text_line : line; variable ram_content : ram_type; begin for i in 0 to ram_depth - 1 loop readline(text_file, text_line); bread(text_line, ram_content(i)); end loop; return ram_content; end function;
Instancie a RAM com:
signal ram_bin : ram_type := init_ram_bin;
VHDL Legado: Leituras Binárias com READ
Para VHDL-2002 ou VHDL-93, o procedimento READ deve ser usado. Como ele retorna um vetor bit, o resultado é convertido para std_logic_vector via To_StdLogicVector:
impure function init_ram_bin return ram_type is file text_file : text open read_mode is "ram_content_bin.txt"; variable text_line : line; variable ram_content : ram_type; variable bv : bit_vector(ram_content(0)'range); begin for i in 0 to ram_depth - 1 loop readline(text_file, text_line); read(text_line, bv); ram_content(i) := To_StdLogicVector(bv); end loop; return ram_content; end function;
Retroportando Recursos Modernos do TEXTIO
Se você preferir não modificar o código legado, pode importar o pacote terceirizado std_logic_1164_additions. Esta biblioteca retroporta os procedimentos HREAD e BREAD para versões VHDL mais antigas, ao custo de uma dependência adicional.
Para exemplos mais detalhados e leitura adicional, veja os artigos acompanhantes desta série.
VHDL
- Protegendo o Alumínio Contra a Corrosão: Estratégias de Especialistas para Durabilidade Duradoura
- Elementos Metálicos vs. Não Metálicos: Principais Diferenças Explicadas
- Crie Seu Primeiro Template AWS CloudFormation — Um Guia Prático
- Computação em Nuvem vs. Computação Tradicional: Uma Comparação Prática
- Dominando Comentários em C: Escrevendo Notas Claras e Eficazes
- Lendo Arquivos em Java com BufferedReader – Guia Prático com Exemplos
- Calculando Médias em Python: Guia Prático
- Micrômetro Explicado: Como Ler e Usar Esta Ferramenta de Medição de Precisão
- Invocando Function Blocks PLC via Clientes OPC UA: Guia Prático
- Dominando a Leitura de Desenhos Técnicos CNC: Habilidades Essenciais para Usinadores Modernos