FPGA设计过程

原文翻译自:An Introduction to the FPGA Design Process

在这篇文章中,我们将更详细地讨论FPGA的设计过程。这包括对设计过程的所有主要阶段的讨论——设计架构、FPGA设计建模和测试我们的设计。我们还研究了两种主要硬件描述语言(HDL)之间的区别——Verilog和VHDL。

在本系列的前一篇文章中,我们对整个FPGA开发过程进行了概述。从这里我们看到了FPGA项目中涉及的三个主要过程: 设计、验证和实现。

在接下来的文章中,我们将讨论构成设计过程的任务。

虽然这里的主要任务是编写代码来创建模型,但这个过程还有其它重要的方面,这包括构建我们的芯片和测试我们的模型。

我们将在单独的文章中更详细地讨论FPGA的实现过程和FPGA验证。

1. 架构设计

设计过程的第一阶段是构建我们的设计。这涉及到将设计分解成许多更小的块,以简化VHDL编码过程。

对于大型设计,这是特别有益的,因为它允许工程师并行工作。在这种情况下,我们应该认为这是整个过程的一个组成部分。对于较小的项目,我们不需要正式执行这个步骤。然而,在开始详细的设计工作之前,总是值得仔细考虑架构。

2. 模型设计

该过程的下一个阶段是创建设计模型。我们使用这个模型来模拟我们的设计行为,并为我们的FPGA构建一个编程文件。 我们通常使用两种主要的硬件描述语言(HDL)之一——verilog或VHDL——来编写这个模型。我们在这个过程中使用了两种主要的建模风格。这些通常被称为寄存器传输级别(RTL)和门级别,让我们更详细地看看这两者。

3. RTL编码风格

使用RTL方法的设计包括描述FPGA内寄存器之间的数据流的代码。这意味着我们要编写代码,明确地描述FPGA在触发器、逻辑门、有限状态机和RAM方面的行为。 让我们考虑下面的示例电路来演示这个概念。 dffdff_example.png

在这个电路的RTL模型中,我们将编写代码来描述两个D型触发器和非门的行为。下面的代码给出了该电路的VHDL实现。如果语法不熟悉,请不要担心,因为它并不重要。

dff1_d <= not dff2_q;
dff1:
process (clock) is
begin
  if rising_edge(clock) then
    dff1_q <= dff1_d;
  end if;
end process dff1;
dff2:
process (clock) is
begin
  if rising_edge(clock) then
    dff2_q <= dff1_q;
  end ill
end process dff2;

在这段代码中,第一行模拟非门的行为。标签为dff1和dff2的触发器使用VHDL过程建模。当时钟上有上升沿时,这些进程锁存输入的值。这与D类型触发器的预期行为相匹配。 从这个例子中,我们可以看到我们是如何描述设计中组件的行为的,以及它们之间的相互连接。

这是RTL建模的定义特性,而其他类型的建模只是简单地定义组件之间的连接。

4. 门级模型

门级建模由代码组成,这些代码定义了不同预先存在的组件的互连。这些组件是集成FPGA元素的实例,如pll、查找表(LUT)或寄存器单元。

下面显示的代码是门级建模的一个例子。这实现了我们之前考虑过的相同的双触发器电路。

inverter : component not_gate
  port map (
    inv_in  => dff2_q,
    inv_out => dff1_d
  );
dff1 : component dff
  port map (
    clock => clock,
    D     => dff1_d,
    Q     => dff1_q,
    Q_bar => open
  );
 
dff2 : component dff
  port map (
    clock => clock,
    D     => dff1_q,
    Q     => dff2_q,
    Q_bar => open
  );

我们可以看到,在这个示例模型中有三个独立的组件:dff组件有两个实例,它是一个D类型触发器的实现,此外还有一个非门组件的实例。 我们使用信号将这些组件的IO连接起来,这导致了我们上面看到的电路的物理实现。

我们很少会想要创建一个门级模型。然而,综合或地点和路线工具经常创建这样的模型,我们将在后面的FPGA构建过程中更详细地讨论这两个过程。 然而,使用我们以前以类似方式编写的组件RTL模型是很常见的。实例化组件并将它们连接起来的语法与门级建模中使用的语法相同。尽管这与门级建模类似,但不同之处在于我们不直接在FPGA中使用单元。这种方法被称为结构化RTL建模。

5. HDL编程

尽管HDL语言通常被称为编程语言,但它们与传统语言几乎没有共同之处。在使用C或Java等语言时,我们是在编写抽象算法或描述程序行为。 CPU实际实现这个程序的方式不太可能是我们真正感兴趣的。相反,当我们编写HDL代码时,我们描述的是数字电路的行为。在设计fpga时,记住这种不同的方法是很重要的。

事实上,我们已经在RTL建模的简单示例中看到了这个过程。我们从一个基本的电路开始,然后编写一些描述电路行为的VHDL。由于这是一篇介绍性的文章,我们在这篇文章中没有充分考虑verilog和VHDL的细节。不过,在这个网站上有一系列VHDL教程和verilog教程,介绍了这些语言。在阅读了这些介绍性文章之后,学习其中一种语言应该是学习FPGA设计的下一步。

6. Verilog还是VHDL?

许多人在学习FPGA设计时遇到的第一个问题是,他们应该学习VHDL还是Verilog。 对于任何想成为专业FPGA设计人员的人来说,学习这两种语言都是有益的。因此,在这种情况下,一个更重要的问题是,应该先学习哪一种语言。虽然这两种语言确实有很多不同之处,但对于初学者来说可能太细微了,无法完全理解。事实上,即使是专业工程师,很多时候也会归结为个人偏好或公司内部预先存在的政策。 当谈到RTL建模时,地理位置通常是选择学习语言的主要决定因素。

Verilog是加州大型科技公司使用最多的语言。因此,对于希望在Verilog工作的人来说,首先学习Verilog是有意义的。在欧洲,VHDL的使用更为广泛,从这种语言开始是有意义的。对于爱好者来说,verilog也是一个不错的选择,因为它通常不那么冗长。对于门级建模,Verilog比VHDL更受欢迎。 这主要是因为它具有定义原语行为的固有能力。这意味着我们可以使用称为用户定义原语的语言特性来定义FPGA内单元的行为。这对于必须开发定义芯片中单元行为的库的芯片供应商来说特别有用。使用基于verilog的模型的模拟也倾向于比VHDL的等价物有更快的执行时间。

7. 测试设计

在对FPGA设计进行架构和编码之后,我们需要测试我们的模型。这对于识别错误和证明我们的模型按预期运行是至关重要的。 尽管对于较大的设计,我们通常有一个独立的验证团队,但有些测试应该由设计人员进行。这将在验证开始前消除最明显和最容易找到的错误。因此,验证团队可以专注于更彻底地测试设计,识别更细微的错误。

通常,我们的测试将包括2部分-修复编译错误和功能模拟。 让我们更详细地看看这两者。

8. 编译警告

作为模拟设计的第一步,我们必须编译代码。这个过程将功能性HDL代码转换为模型或可执行文件。然后仿真工具使用该文件来模拟设计的行为。在此过程中,编译工具将创建许多警告。这些代码的问题不会阻止编译,但可能会导致模拟错误。

这方面的一些例子包括已弃用的代码构造、非合成代码或向量大小错误。一个常见的警告例子是试图用9位数据分配一个8位信号。 设计人员在运行任何模拟之前纠正这些警告是正常的。对于大型设计,这可能是一项耗时的任务,而且可能不是所有的警告都是可修复的。 对于警告的解决方案,改变我们模型的行为也是很常见的。如果使用正式流程,我们至少应该在开始验证流程之前修复警告。 当不需要单独的验证过程时,我们应该在开始实现阶段之前修复这些警告。

9.功能仿真

即使在使用正式的验证过程时,FPGA设计者通常也需要执行基本的功能测试。这就是验证和设计之间的界限可能变得相当模糊的地方,特别是对于小型芯片。 这种情况下的功能模拟包括创建模拟电路正常运行条件的测试。这允许设计师在验证开始之前修复最明显的功能错误。 这意味着验证团队可以利用他们的时间开发更复杂的测试。这可能会导致验证过程发现更微妙的错误。对于业余爱好者来说,没有必要再做更多的模拟了。对于小型项目,也不需要做更多的测试。

例外情况是我们遵循正式流程的项目,如DO-254。当我们在大型项目中工作时,验证会比这更全面。 当我们遵循正式的设计过程时也是这样。在这些情况下,我们应该将FPGA的验证视为一个独立的正式过程。