Como inicializar a RAM do arquivo usando TEXTIO
Uma maneira conveniente de preencher a RAM do bloco com valores iniciais é ler literais binários ou hexadecimais de um arquivo ASCII. Essa também é uma boa maneira de criar uma ROM (memória somente leitura) em VHDL. Afinal, RAM e ROM são a mesma coisa em FPGAs, ROM é uma RAM que você só lê.
Os exemplos ao longo deste artigo assumirão que as seguintes constantes e o tipo de RAM foram declarados no início da 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);
Esta postagem de blog é parte de uma série sobre o uso da biblioteca TEXTIO em VHDL. Leia os outros artigos aqui:
Arquivo de estímulo lido no testbench usando TEXTIO
Imagem de bitmap de arquivo BMP lida usando TEXTIO
READLINE, LINE, HREAD, OREAD e PÃO
Os subprogramas e tipos necessários para ler e escrever arquivos externos em VHDL estão localizados no arquivo
TEXTIO
pacote. Este pacote faz parte do std
biblioteca. A biblioteca padrão é sempre carregada; portanto, não precisamos importá-lo explicitamente com o library
palavra-chave. Podemos simplesmente seguir em frente e usar o
TEXTIO
pacote no cabeçalho do nosso arquivo VHDL assim:use std.textio.all;
Armazenaremos os dados da RAM em um arquivo ASCII onde uma linha de texto corresponde a um slot de memória. Para ler uma linha de texto usamos o
READLINE
procedimento do TEXTIO
pacote. O procedimento recebe dois argumentos, o nome do arquivo como uma entrada constante e a linha de texto analisada como um inout
variável. A declaração de protótipo do READLINE
procedimento e o LINE
tipo retirado da especificação padrão VHDL é mostrado abaixo. procedure READLINE (file F: TEXT; L: inout LINE); type LINE is access STRING; -- A LINE is a pointer -- to a STRING value.
Embora a classe do
LINE
parâmetro não é especificado explicitamente na declaração de protótipo de READLINE
, é uma variável porque é a classe padrão para inout
parâmetros. O LINE
type é simplesmente um tipo de acesso a uma string, um ponteiro para um objeto string alocado dinamicamente. VHDL-2008 define o
OREAD
, HREAD
e BREAD
procedimentos para extrair valores octais, hexadecimais e binários de um LINE
objeto. Os métodos para leitura de valores octais e hexadecimais são bastante semelhantes, os valores octais são apenas um subconjunto dos hexadecimais. Para simplificar, vamos pular leituras octais neste artigo e focar em como ler valores hexadecimais e binários de um arquivo de texto. O código abaixo mostra as definições dos procedimentos que são relevantes para nós, estão disponíveis apenas em VHDL-2008 e revisões mais recentes. O
OREAD
e HREAD
procedimentos vêm em dois tipos sobrecarregados para cada um dos tipos de saída suportados. O opcional GOOD
A saída pode ser usada para detectar erros de leitura, embora a maioria das ferramentas produza um erro ou aviso, independentemente de essa saída ser usada ou não. procedure OREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR; GOOD : out BOOLEAN); procedure OREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR); 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];Clique aqui para ver as definições do procedimento de entrada da biblioteca TEXTIO
procedure READLINE (file F: TEXT; L: inout LINE); procedure READ (L: inout LINE; VALUE: out BIT; GOOD: out BOOLEAN); procedure READ (L: inout LINE; VALUE: out BIT); procedure READ (L: inout LINE; VALUE: out BIT_VECTOR; GOOD: out BOOLEAN); procedure READ (L: inout LINE; VALUE: out BIT_VECTOR); procedure READ (L: inout LINE; VALUE: out BOOLEAN; GOOD: out BOOLEAN); procedure READ (L: inout LINE; VALUE: out BOOLEAN); procedure READ (L: inout LINE; VALUE: out CHARACTER; GOOD: out BOOLEAN); procedure READ (L: inout LINE; VALUE: out CHARACTER); procedure READ (L: inout LINE; VALUE: out INTEGER; GOOD: out BOOLEAN); procedure READ (L: inout LINE; VALUE: out INTEGER); procedure READ (L: inout LINE; VALUE: out REAL; GOOD: out BOOLEAN); procedure READ (L: inout LINE; VALUE: out REAL); procedure READ (L: inout LINE; VALUE: out STRING; GOOD: out BOOLEAN); procedure READ (L: inout LINE; VALUE: out STRING); procedure READ (L: inout LINE; VALUE: out TIME; GOOD: out BOOLEAN); procedure READ (L: inout LINE; VALUE: out TIME); procedure SREAD (L: inout LINE; VALUE: out STRING; STRLEN: out NATURAL); alias STRING_READ is SREAD [LINE, STRING, NATURAL]; alias BREAD is READ [LINE, BIT_VECTOR, BOOLEAN]; alias BREAD is READ [LINE, BIT_VECTOR]; alias BINARY_READ is READ [LINE, BIT_VECTOR, BOOLEAN]; alias BINARY_READ is READ [LINE, BIT_VECTOR]; procedure OREAD (L: inout LINE; VALUE: out BIT_VECTOR; GOOD: out BOOLEAN); procedure OREAD (L: inout LINE; VALUE: out BIT_VECTOR); alias OCTAL_READ is OREAD [LINE, BIT_VECTOR, BOOLEAN]; alias OCTAL_READ is OREAD [LINE, BIT_VECTOR]; procedure HREAD (L: inout LINE; VALUE: out BIT_VECTOR; GOOD: out BOOLEAN); procedure HREAD (L: inout LINE; VALUE: out BIT_VECTOR); alias HEX_READ is HREAD [LINE, BIT_VECTOR, BOOLEAN]; alias HEX_READ is HREAD [LINE, BIT_VECTOR];Clique aqui para ver as definições do procedimento de entrada da biblioteca std_logic_1164
procedure READ (L : inout LINE; VALUE : out STD_ULOGIC; GOOD : out BOOLEAN); procedure READ (L : inout LINE; VALUE : out STD_ULOGIC); procedure READ (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR; GOOD : out BOOLEAN); procedure READ (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]; alias BINARY_READ is READ [LINE, STD_ULOGIC_VECTOR, BOOLEAN]; alias BINARY_READ is READ [LINE, STD_ULOGIC_VECTOR]; procedure OREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR; GOOD : out BOOLEAN); procedure OREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR); alias OCTAL_READ is OREAD [LINE, STD_ULOGIC_VECTOR, BOOLEAN]; alias OCTAL_READ is OREAD [LINE, STD_ULOGIC_VECTOR]; procedure HREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR; GOOD : out BOOLEAN); procedure HREAD (L : inout LINE; VALUE : out STD_ULOGIC_VECTOR); alias HEX_READ is HREAD [LINE, STD_ULOGIC_VECTOR, BOOLEAN]; alias HEX_READ is HREAD [LINE, STD_ULOGIC_VECTOR];
Ler valores hexadecimais do arquivo
Hexadecimal é um formato útil para descrever o conteúdo da RAM porque dois caracteres hexadecimais se traduzem diretamente em um byte, oito bits. Cada caractere descreve um nibble (meio byte) e cada linha no arquivo de texto descreve o conteúdo de um slot de RAM. A lista abaixo mostra um trecho do ram_content_hex.txt Arquivo. Ele foi preenchido com valores de exemplo que variam de 1 a 256 decimal, escritos como hexadecimal.
12–255256 | 00000001 00000002 ... 000000FF 00000100 |
Para carregar os dados do arquivo de texto, usamos uma função impura declarada abaixo do
ram_type
, mas acima da declaração do sinal RAM. O código abaixo mostra o init_ram_hex
função que lê os dados do arquivo de texto e os retorna como um ram_type
objeto. 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;
O
readline
O procedimento dentro do loop for lê uma linha de texto por vez e a atribui ao text_line
variável. Este objeto é do tipo line
, que é um tipo de acesso a um objeto string, um ponteiro para uma string alocada dinamicamente. Na próxima linha, o hread
procedimento lê a string do line
objeto e o converte em um std_ulogic_vector
. Este tipo pode ser atribuído diretamente ao std_logic_vector
que cada célula de RAM é construída. Por fim, declaramos o sinal de RAM enquanto chamamos nosso
init_ram_hex
função para fornecer os valores iniciais para ela:signal ram_hex : ram_type := init_ram_hex;
HREAD em VHDL-2002 e VHDL-93
Infelizmente, o
HREAD
procedimento está disponível apenas em VHDL-2008. Em todas as versões anteriores do VHDL, o padrão READ
procedimento deve ser usado em seu lugar. O READ
O procedimento está sobrecarregado com muitos tipos de saída diferentes, mas não há uma opção para ler valores hexadecimais. Vamos escrever um algoritmo personalizado para converter caracteres hexadecimais ASCII em VHDL
std_logic_vector
. Primeiro, precisamos ler os caracteres um por um do text_line
objeto, então decodificamos seus valores e os atribuímos à fatia correta do vetor de slot RAM. O código abaixo mostra uma implementação equivalente do init_ram_hex
função que também funciona em versões legadas de VHDL. 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;
O algoritmo simplesmente passa por todas as linhas enquanto observa todos os caracteres, convertendo-os no valor binário correto. Se um caractere que não está no intervalo 0x0-0xF for encontrado, uma falha de declaração será gerada no
when others
ramo. O offset
A variável controla a posição da fatia dentro de cada célula de memória à qual atribuir o valor decodificado. Você pode estar se perguntando por que não criamos um
hread
personalizado procedimento em vez de codificá-lo dentro do init_ram_hex
função? Então não precisaríamos alterar o init_ram_hex
funcionar, simplesmente usaríamos nosso hread
personalizado procedimento no lugar do padrão ausente. Isso funcionaria na maioria dos simuladores e alguns sintetizadores como o Lattice iCEcube2, mas não será sintetizado no Xilinx Vivado. A mensagem de erro abaixo indica claramente qual é o problema.
Em Vivado:
[Synth 8-27] O argumento de procedimento do tipo 'linha' não é suportado [init_ram_tb.vhd:15]
Clique aqui para ver a implementação alternativa do procedimento HREAD
procedure hread(l: inout line; value: out std_logic_vector) is variable c : character; variable ok : boolean; variable i : integer := 0; variable hex_val : std_logic_vector(3 downto 0); begin while i < value'high loop read(l, 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; value(value'high - i downto value'high - i - 3) := hex_val; i := i + 4; end loop; end procedure;
Ler valores binários do arquivo
Você pode querer armazenar os valores de RAM como literais binários em vez de caracteres hexadecimais se a largura da RAM não for um múltiplo de 8. A lista abaixo mostra o mesmo conteúdo de antes, mas representado em formato binário usando apenas os caracteres
0
e 1
. 12–255256 | 00000000000000000000000000000001 00000000000000000000000000000010 ... 00000000000000000000000011111111 00000000000000000000000100000000 |
O algoritmo mostrado abaixo é para ler valores binários do arquivo. É semelhante à leitura de hexadecimais, mas no VHDL-2008 você deve usar o
BREAD
chamada de procedimento em vez de HREAD
. Ele traduzirá um caractere ASCII para um único std_ulogic
value, que é convertido implicitamente em std_logic
. 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;
Finalmente, inicializamos o sinal de RAM chamando nossa nova função impura conforme mostrado no código abaixo.
signal ram_bin : ram_type := init_ram_bin;
PÃO em VHDL-2002 e VHDL-93
Podemos facilmente tornar nosso código portátil para versões legadas de VHDL chamando READ
em vez de BREAD
. O trecho do padrão VHDL abaixo mostra o protótipo de READ
que estamos interessados em usar.
procedure READ (L: inout LINE; VALUE: out BIT);
O READ
procedimento que gera um std_ulogic
não existia antes do VHDL-2008, portanto, temos que usar o bit
versão do TEXTIO
biblioteca. Felizmente, esse tipo pode ser facilmente convertido para std_logic
usando o padrão To_StdLogicVector
função.
A implementação de init_ram_bin
mostrado abaixo funciona em VHDL-2002 e também em VHDL-93.
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;
Backport da biblioteca IEEE std_logic_1164
Uma alternativa para alterar o código das versões herdadas do VHDL é usar o pacote de terceiros std_logic_1164_additions. Ao baixar e adicionar esta biblioteca ao seu projeto, você poderá usar os novos procedimentos também em VHDL-2002 e VHDL-93. Claro, então você estará importando muito mais e seu código sempre dependerá desse pacote.
Arquivo de estímulo lido no testbench usando TEXTIO
Como fazer um AXI FIFO na RAM do bloco usando o handshake pronto/válido
VHDL
- Como proteger o alumínio da corrosão
- Como os elementos metálicos diferem dos elementos não metálicos
- Como criar um modelo CloudFormation usando AWS
- Como a computação em nuvem difere da computação tradicional?
- Como escrever comentários em programação C
- Java BufferedReader:Como Ler Arquivo em Java com Exemplo
- Python Average:Como encontrar a AVERAGE de uma lista em Python
- O que é Micrômetro? | Como você lê um micrômetro
- Como chamar um Bloco Funcional de um Cliente OPC UA usando um Modelo de Informação
- Como ler plantas CNC