亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Industrial programming >> VHDL

如何使用 TEXTIO 从文件初始化 RAM

使用初始值填充块 RAM 的一种方便方法是从 ASCII 文件中读取二进制或十六进制文字。这也是在 VHDL 中创建 ROM(只读存储器)的好方法。毕竟在 FPGA 中 RAM 和 ROM 是一回事,ROM 是你只能读取的 RAM。

本文中的示例将假定以下常量和 RAM 类型已在 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);

这篇博文是关于在 VHDL 中使用 TEXTIO 库的系列文章的一部分。在此处阅读其他文章:

使用 TEXTIO 在测试台中读取刺激文件

使用 TEXTIO 读取 BMP 文件位图图像

READLINE、LINE、HREAD、OREAD 和 BREAD

VHDL读写外部文件所需的子程序和类型位于TEXTIO 包裹。这个包是 std 的一部分 图书馆。标准库总是被加载;因此,我们不必使用 library 显式导入它 关键字。

我们可以简单地继续使用 TEXTIO 像这样在我们的 VHDL 文件头中打包:

use std.textio.all;

我们将 RAM 数据存储在一个 ASCII 文件中,其中一行文本对应一个内存插槽。要读取一行文本,我们使用 READLINE TEXTIO 中的程序 包裹。该过程有两个参数,文件名作为常量输入,解析的文本行作为 inout 多变的。 READLINE的原型声明 程序和LINE 取自 VHDL 标准规范的类型如下所示。

procedure READLINE (file F: TEXT; L: inout LINE);

type LINE is access STRING; -- A LINE is a pointer
                            -- to a STRING value.

虽然LINE的类 READLINE 的原型声明中没有明确指定参数 ,它是一个变量,因为这是 inout 的默认类 参数。 LINE type 只是一个对字符串的访问类型,一个指向动态分配的字符串对象的指针。

VHDL-2008 定义了 OREAD , HREAD , 和 BREADLINE 中提取八进制、十六进制和二进制值的过程 目的。读取八进制和十六进制值的方法非常相似,八进制值只是十六进制的子集。为简单起见,本文将跳过八进制读取,重点介绍如何从文本文件中读取十六进制和二进制值。

下面的代码显示了与我们相关的过程的定义,它们仅在 VHDL-2008 和更新版本中可用。 OREADHREAD 对于每种支持的输出类型,过程都有两种重载风格。可选的 GOOD 输出可用于检测读取错误,但是,无论是否使用此输出,大多数工具都会产生错误或警告。

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];
单击此处查看 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];
单击此处查看 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];

从文件中读取十六进制值

十六进制是描述 RAM 内容的方便格式,因为两个十六进制字符直接转换为一个字节,八位。每个字符描述一个半字节(半字节),文本文件中的每一行描述一个 RAM 插槽的内容。下面的清单显示了 ram_content_hex.txt 的摘录 文件。它已经填充了从十进制 1 到 256 的示例值,写为十六进制。

12–255256 00000001 00000002 ... 000000FF 00000100

为了从文本文件中加载数据,我们使用在 ram_type 下声明的不纯函数 ,但在 RAM 信号声明之上。下面的代码显示了 init_ram_hex 从文本文件中读取数据并将其返回为 ram_type 的函数 对象。

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;

readline for循环内的过程每次读取一行文本并将其分配给text_line 多变的。此对象的类型为 line ,它是对字符串对象的访问类型,是指向动态分配的字符串的指针。在下一行,hread 过程从 line 读取字符串 对象并将其转换为 std_ulogic_vector .这个类型可以直接赋值给std_logic_vector 每个 RAM 单元都是由它构成的。

最后,我们在调用 init_ram_hex 时声明 RAM 信号 为其提供初始值的函数:

signal ram_hex : ram_type := init_ram_hex;

VHDL-2002 和 VHDL-93 中的 HREAD

不幸的是,HREAD 程序仅在 VHDL-2008 中可用。在所有以前版本的 VHDL 中,标准 READ 必须改用程序。 READ 过程重载了许多不同的输出类型,但没有读取十六进制值的选项。

让我们编写一个自定义算法,用于将十六进制 ASCII 字符转换为 VHDL std_logic_vector .首先,我们需要从text_line中逐一读取字符 对象,然后我们解码它们的值并将它们分配给 RAM 槽向量的正确切片。下面的代码显示了 init_ram_hex 的等效实现 也适用于旧版 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;

该算法在查看每个字符的同时简单地遍历每一行,将其转换为正确的二进制值。如果遇到不在 0x0-0xF 范围内的字符,则会在 when others 中引发断言失败 分支。 offset 变量控制每个存储单元内的切片位置,将解码后的值分配给。

您可能会问自己,为什么我们不创建自定义 hread 过程而不是在 init_ram_hex 内对其进行编码 功能?这样我们就不必更改 init_ram_hex 函数,我们只需使用我们自定义的 hread 程序代替缺少的标准程序。

这适用于大多数模拟器和一些合成器,如 Lattice iCEcube2,但它不会在 Xilinx Vivado 中合成。下面的错误信息清楚地说明了问题所在。

在 Vivado 中:
[Synth 8-27] 不支持“line”类型的过程参数 [init_ram_tb.vhd:15]

单击此处查看 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;

从文件中读取二进制值

如果 RAM 宽度不是 8 的倍数,您可能希望将 RAM 值存储为二进制文字而不是十六进制字符。下面的清单显示的内容与以前相同,但仅使用字符 01 .

12–255256 00000000000000000000000000000001 00000000000000000000000000000010 ... 00000000000000000000000011111111 00000000000000000000000100000000

下面显示的算法用于从文件中读取二进制值。它类似于读取十六进制,但在 VHDL-2008 中您应该使用 BREAD 过程调用而不是 HREAD .它将一个 ASCII 字符转换为单个 std_ulogic 值,隐式转换为 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;

最后,我们通过调用新的不纯函数来初始化 RAM 信号,如下面的代码所示。

signal ram_bin : ram_type := init_ram_bin;

VHDL-2002 和 VHDL-93 中的 BREAD

我们可以通过调用 READ 轻松地将我们的代码移植到旧版 VHDL 版本 而不是 BREAD .下面的 VHDL 标准摘录显示了 READ 的原型 我们有兴趣使用。

procedure READ (L: inout LINE; VALUE: out BIT);

READ 输出 std_ulogic 的过程 在 VHDL-2008 之前不存在,因此我们必须使用 bit TEXTIO 的版本 图书馆。幸运的是,这种类型可以很容易地转换为 std_logic 通过使用标准 To_StdLogicVector 功能。

init_ram_bin的实现 如下所示适用于 VHDL-2002 和 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;

IEEE std_logic_1164 库的向后移植

更改旧 VHDL 版本代码的替代方法是使用 std_logic_1164_additions 第三方包。通过下载这个库并将其添加到您的项目中,您将能够在 VHDL-2002 和 VHDL-93 中使用新程序。当然,那么您将导入更多内容,并且您的代码将始终依赖于这个包。


VHDL

  1. 如何保护铝免受腐蚀
  2. 金属元素与非金属元素的区别
  3. 如何使用 AWS 创建 CloudFormation 模板
  4. 云计算与传统计算有何不同?
  5. 使用来自 FPGA 引脚的 PWM 的 RC 伺服控制器
  6. 如何使用存储在 Block RAM 中的正弦波创建呼吸 LED 效果
  7. 如何在 C 编程中写注释
  8. Java BufferedReader:如何通过示例在 Java 中读取文件
  9. Python 平均值:如何在 Python 中找到列表的平均值
  10. 什么是千分尺? |你如何阅读千分尺
  11. 如何使用信息模型从 OPC UA 客户端调用功能块
  12. 如何阅读 CNC 蓝图