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

如何在 VHDL 中创建链接列表

链表是一种动态数据结构。当事先不知道元素的总数时,可以使用链表。它在内存中的增长和收缩,相对于它包含的项目数。

使用面向对象编程语言中的类最方便地实现链表。 VHDL 具有一些面向对象的特性,可用于从用户那里抽象出实现的复杂性。

在本文中,我们将使用访问类型、记录和受保护类型在 VHDL 中实现链表。我们将创建一个新的 VHDL 包,我们将在其中编写所有链表代码。

我们要做的第一件事是声明一个包含我们代码的包。 VHDL 包是可以导入另一个 VHDL 文件的类型、对象或子程序的集合。大多数 VHDL 模块从 IEEE 库中导入 std_logic_1164 包。我们正在创建自己的包,就像它一样。

我们新的 DataStructures.vhd 文件的内容:

package DataStructures is
   -- Public declarations go here
end package DataStructures;
 
package body DataStructures is
   -- Private declarations and implementations go here
end package body DataStructures;

尽管该包仅包含链表实现,但我们将通过将其命名为 DataStructures 来保证它的未来。可以很容易地想象,以后有人会想向其中添加其他数据结构,例如树或哈希映射。

一个包由两部分组成;一个声明性区域和一个主体。声明性区域是您放置包的用户应该可见的所有内容的地方。正文是私有范围的,不能从外部访问。

受保护的类型

受保护类型是 VHDL 中的类结构。它们可以包含子程序、类型声明和变量。受保护类型由公共声明和私有主体组成。我们将声明添加到包声明中,并将主体添加到包主体中。

添加受保护类型后我们的 DataStructures.vhd 文件:

package DataStructures is

   type LinkedList is protected

      -- Prototypes of subprograms
      procedure Push(constant Data : in integer);
      impure function Pop return integer;
      impure function IsEmpty return boolean;

   end protected;

end package DataStructures;

package body DataStructures is

   type LinkedList is protected body
   end protected body;

end package body DataStructures;

我们将受保护类型命名为 LinkedList,因为它将包含与链表相关的所有内容。如果我们要在包中添加另一种数据结构,那就意味着另一种受保护的类型。

在受保护类型的声明区域内,我们声明了三个子程序。目前还没有实现,我们稍后会谈到。为了使子程序在受保护类型之外可见,它们必须在此处声明。

这三个子程序是标准的链表方法:Push、Pop 和 IsEmpty。 Push 将一个新元素添加到列表的开头。 Pop 从列表末尾删除一个元素。 IsEmpty 是一个实用函数,它返回 true 如果列表为空。

记录

记录是一种复合类型,可能包含多个成员类型。它的工作方式有点像 C 编程语言中的结构。当从记录类型创建信号或变量时,可以使用“.”来引用数据成员。符号。例如 MyItem.Data .

我们在受保护类型体内的记录声明:

   type LinkedList is protected body

      type Item is record
         Data : integer;
         NextItem : Ptr;
      end record;

   end protected body;

我们已经在受保护类型的主体中声明了记录类型。受保护类型的声明区域不能包含其他类型或信号声明,因此我们必须在受保护类型体中声明它们。

这对我们来说不是问题,因为 Item 是内部使用的类型。它不需要从外面看到。链表的用户只能通过三个公开声明的子程序访问它。

我们的记录包含两个数据成员;数据和 NextItem。而数据的类型是 integer , NextItem 目前属于神秘的 Ptr 类型。

访问类型

访问类型是 VHDL 指针。通过使用它们,我们可以在运行时动态创建对象。我们将声明一个名为 Ptr 的新访问类型,它将指向 Item 类型的动态创建的对象。

添加了新访问类型的包体:

package body DataStructures is

   type LinkedList is protected body

      -- Declaration of incomplete Item type
      type Item;

      -- Declaration of pointer type to the Item type
      type Ptr is access Item;

      -- Full declaration of the Item type
      type Item is record
         Data : integer;
         NextItem : Ptr; -- Pointer to the next Item
      end record;

      -- Root of the linked list
      variable Root : Ptr;

end package body DataStructures;

链表节点需要包含对链表中下一个节点的引用。我们已经在带有 NextItem 成员的 Item 记录中实现了这一点。它是 Ptr 类型,而 Ptr 又是指向 Item 类型的指针。为了避免这种先有鸡还是先有蛋的问题,我们首先将 Item 声明为不完整类型。然后,我们将 Ptr 类型声明为指向不完整类型的指针。最后,我们指定Item类型的完整声明。

我们做的最后一件事是声明一个名为 Root 且类型为 Ptr 的新变量。这将是链表的根。如果列表为空,则 Root 的值为 null .否则,它将指向列表中的第一项。

链表方法

现在是时候实现我们在受保护类型的声明区域中声明的子程序了。它们是 Push 过程,以及两个不纯函数:Pop 和 IsEmpty。

我们将子程序的实现放在受保护类型的主体中。

Push 是唯一一个是过程的子程序。 VHDL 中的函数需要一个不能省略的返回值。为避免使用虚拟返回值,最好使用过程。

Push 过程:

procedure Push(Data : in integer) is
   variable NewItem : Ptr;
   variable Node : Ptr;
begin
   -- Dynamically allocate a new item
   NewItem := new Item;
   NewItem.Data := Data;

   -- If the list is empty, this becomes the root node
   if Root = null then
      Root := NewItem;

   else
      -- Start searching from the root node
      Node := Root;

      -- Find the last node
      while Node.NextItem /= null loop
         Node := Node.NextItem;
      end loop;

      -- Append the new item at the end of the list
      Node.NextItem := NewItem;
   end if;
end;

发生的第一件事是动态分配了一个 Item 类型的新对象。这是通过使用 new 关键词。每次new 使用关键字,动态内存被模拟器消耗。

其余的代码只是一个标准的链表算法。新项目被附加到列表的末尾,或者如果列表为空,它将成为根项目。如果您需要更多背景知识,请阅读 Wikipedia 上的链接列表。

流行音乐

Pop 从列表中删除最旧的元素并将其返回给调用者。如果我们只是删除对弹出元素的引用并返回它,模拟器中会出现内存丢失。函数调用完成后,对动态创建的对象的引用将永远丢失。

在 VHDL-2017(也称为 VHDL-2018 或 VHDL-2019)之前的 VHDL 中没有垃圾收集。大多数模拟器不支持 VHDL-2017。因此,我们必须调用 deallocate 在从函数返回之前。

deallocate 运算符释放动态分配的对象使用的内存。每次调用 new ,应该调用 deallocate 在对象的引用被删除之前。

弹出功能:

impure function Pop return integer is
   variable Node : Ptr;
   variable RetVal : integer;
begin
   Node := Root;
   Root := Root.NextItem;

   -- Copy and free the dynamic object before returning
   RetVal := Node.Data;
   deallocate(Node);

   return RetVal;
end;

在我们调用了deallocate之后 ,我们无法引用 Node 变量所指向的数据。它已被模拟器释放。解决方法是在释放前先将数据复制到一个局部变量中,然后返回该变量的值。

IsEmpty

检查列表是否为空可以简单地通过检查根节点是否指向非空值来完成:

impure function IsEmpty return boolean is
begin
   return Root = null;
end;

测试台

要运行我们的代码,我们需要创建一个测试平台。实际上,链表只能在测试平台中使用。访问类型不可综合,但它们对于验证目的非常有用。

使用库包

在测试台中,我们首先导入work 图书馆。这是 VHDL 中的默认库,也是我们的 DataStructures 包所在的位置。之后,我们可以像这样导入 LinkedList 保护类型:

library work;
use work.DataStructures.LinkedList;

共享变量

共享变量是可以被多个进程访问的变量。与只能在单个进程中声明的常规变量不同,共享变量可以在体系结构的声明区域中声明。因此,它们与信号具有相同的范围。

我们必须使用共享变量,因为不可能声明受保护类型的信号。如果我们尝试,ModelSim 会抱怨:(vcom-1464) 非法声明类型为 work.DataStructures.LinkedList 的信号“List”(类型是受保护的类型)。

我们在测试台的声明区声明了 LinkedList 类型的共享变量:

architecture sim of LinkedListTb is

   shared variable List : LinkedList;

begin

测试平台的最终代码

为了测试我们的三个效用函数,我们创建了一个新进程。在这个过程中,我们添加了一个将五个整数推送到链表的 For 循环。最后,我们在 While 循环中清空链表,直到 IsEmpty 函数返回 true

library work;
use work.DataStructures.LinkedList;

entity LinkedListTb is
end entity;

architecture sim of LinkedListTb is

   shared variable List : LinkedList;

begin

   process is
   begin

      for i in 1 to 5 loop
         report "Pushing " & integer'image(i);
         List.Push(i);
      end loop;

      while not List.IsEmpty loop
         report "Popping " & integer'image(List.Pop);
      end loop;

      wait;
   end process;

end architecture;

DataStructures 包的最终代码

在写完我们之前在本文中谈到的所有代码之后,DataStructures 包应该如下所示:

package DataStructures is
   type LinkedList is protected

      procedure Push(constant Data : in integer);
      impure function Pop return integer;
      impure function IsEmpty return boolean;

   end protected;
end package DataStructures;

package body DataStructures is

   type LinkedList is protected body

      type Item;
      type Ptr is access Item;
      type Item is record
         Data : integer;
         NextItem : Ptr;
      end record;

      variable Root : Ptr;

      procedure Push(Data : in integer) is
         variable NewItem : Ptr;
         variable Node : Ptr;
      begin
         NewItem := new Item;
         NewItem.Data := Data;

         if Root = null then
            Root := NewItem;

         else
            Node := Root;

            while Node.NextItem /= null loop
               Node := Node.NextItem;
            end loop;

            Node.NextItem := NewItem;
         end if;
      end;

      impure function Pop return integer is
         variable Node : Ptr;
         variable RetVal : integer;
      begin
         Node := Root;
         Root := Root.NextItem;

         RetVal := Node.Data;
         deallocate(Node);

         return RetVal;
      end;

      impure function IsEmpty return boolean is
      begin
         return Root = null;
      end;

   end protected body;

end package body DataStructures;

结果

当我们在 ModelSim 中按下运行按钮时输出到模拟器控制台:

VSIM 10> run
# ** Note: Pushing 1
#    Time: 0 ns  Iteration: 0  Instance: /linkedlisttb
# ** Note: Pushing 2
#    Time: 0 ns  Iteration: 0  Instance: /linkedlisttb
# ** Note: Pushing 3
#    Time: 0 ns  Iteration: 0  Instance: /linkedlisttb
# ** Note: Pushing 4
#    Time: 0 ns  Iteration: 0  Instance: /linkedlisttb
# ** Note: Pushing 5
#    Time: 0 ns  Iteration: 0  Instance: /linkedlisttb
# ** Note: Popping 1
#    Time: 0 ns  Iteration: 0  Instance: /linkedlisttb
# ** Note: Popping 2
#    Time: 0 ns  Iteration: 0  Instance: /linkedlisttb
# ** Note: Popping 3
#    Time: 0 ns  Iteration: 0  Instance: /linkedlisttb
# ** Note: Popping 4
#    Time: 0 ns  Iteration: 0  Instance: /linkedlisttb
# ** Note: Popping 5
#    Time: 0 ns  Iteration: 0  Instance: /linkedlisttb

从打印输出中我们可以看到,五个整数被推送到链表中。然后,测试台中的 While 循环按插入顺序将它们从列表中弹出。


VHDL

  1. 如何在 VHDL 中创建字符串列表
  2. 如何为 VHDL 代码锁定模块创建 Tcl 驱动的测试平台
  3. 如何在 VHDL 测试平台中停止仿真
  4. 如何在 VHDL 中创建 PWM 控制器
  5. 如何在 VHDL 中生成随机数
  6. 如何在 VHDL 中创建环形缓冲区 FIFO
  7. 如何创建自检测试平台
  8. 如何在 VHDL 中的进程中使用过程
  9. 如何在 VHDL 中使用不纯函数
  10. 如何在 VHDL 中使用函数
  11. 如何在 VHDL 中创建有限状态机
  12. 如何在 VHDL 中使用过程