try ai
科普
编辑
分享
反馈
  • 硬件描述语言

硬件描述语言

SciencePedia玻尔百科
核心要点
  • 硬件描述语言(HDL)用于描述数字电路的物理结构和并行特性,更像一张蓝图,而非一份顺序执行的指令集。
  • 所有数字电路都由两种基本类型的逻辑构成:组合逻辑,它无记忆且瞬时响应;以及时序逻辑,它具有状态和记忆功能。
  • 非阻塞赋值(<=)对于精确建模同步硬件至关重要,因为它捕捉了所有寄存器在时钟沿同时更新的现实情况。
  • HDL 描述通过一个设计流程转化为物理器件,该流程包括综合、布局布线、时序分析和比特流生成。

引言

在数字技术的世界里,从智能手机到超级计算机,每一个复杂的设备最初都不是一段软件,而是一台物理机器的蓝图。这些蓝图的语言就是硬件描述语言(HDL)。与为处理器提供逐步指令的传统编程语言不同,HDL 允许工程师描述电子电路的结构本身及其并行行为。然而,许多初学者带着软件开发者的思维模式来接触 HDL,导致了根本性的误解和有缺陷的设计。本文旨在通过建立正确的“硬件优先”范式来弥合这一差距。

在接下来的章节中,您将踏上一段从抽象概念到硅片现实的旅程。首先,在“原理与机制”中,我们将剖析区分 HDL 与软件的核心思想,探索电路的两个灵魂——组合逻辑和时序逻辑——以及用于建模它们的关键语法。随后,“应用与跨学科联系”将揭示这些文本描述如何转化为 FPGA 和 ASIC 等物理设备,并探讨它们在从电气工程到计算科学等不同领域带来的革命性影响。

原理与机制

想象一下,您想盖一栋房子。您不会给施工队一份像“首先,砌一块砖。然后,砌下一块砖”这样的分步食谱。相反,您会给他们一张蓝图。蓝图描述的不是建造的过程,而是最终的结构——墙在哪里,窗户在哪里,房间如何连接。它声明了待建事物的本质。

硬件描述语言(HDL)更像是那张蓝图,而不是烹饪食谱。当您用 Python 或 C++ 这样的语言编写代码时,您通常是在为处理器编写一系列要逐一执行的指令。但是当您用 Verilog 或 VHDL 这样的 HDL 编写时,您是在描述一台物理机器,一个由逻辑门和存储元件组成的集合,它们将全部同时存在并并行运行。您是在用文字绘图。

这个根本性的差异是理解后续所有内容的关键。例如,在普通数学中,我们知道 A+BA + BA+B 与 B+AB + AB+A 是相同的。这是交换律。如果您在编写一个程序,您可能会想计算 a + b 是否比 b + a 更快。但在 HDL 中,如果您写 assign y = a | b;,您只是在告诉综合工具:“我需要一个输入为 a 和 b、输出为 y 的或门。”工具知道或门是可交换的,所以写 assign y = b | a; 描述的是完全相同的硬件。您在描述什么,而工具足够聪明,能弄清楚如何实现。

要描述这些电子蓝图,我们必须首先理解构成所有数字电路的两种基本“物质”。

电路的两个灵魂:组合逻辑与时序逻辑

每一个复杂的数字设备,从简单的计算器到超级计算机,都是由两种基本电路构建而成的。

首先是​​组合逻辑​​。可以把它想象成电路的“条件反射”。它在任何时刻的输出,纯粹是其在同一时刻输入的函数。它没有对过去的记忆。一个简单的例子是与门:如果它的两个输入现在都是‘1’,它的输出现在就是‘1’。如果一个输入改变,输出会立即改变(当然,是以电光火石般的速度)。它仅仅是计算一个逻辑函数。

其次是​​时序逻辑​​。这是电路的“记忆”。它的输出不仅取决于当前的输入,还取决于过去发生过什么。它有一个状态。触发器是计算机存储器的基本构建块,它是一个经典的时序元件。它可以存储一位信息(‘0’或‘1’)并保持它,即使设置该值的输入早已消失。它只在特定的时刻改变其状态,通常由时钟的节拍决定。

因此,HDL 必须为我们提供描述这两种“灵魂”的方法——瞬时的条件反射和有状态的记忆。

描述“当下”:连续赋值

我们如何为组合逻辑绘制蓝图?我们通过声明一种永久的、永恒的关系。在 Verilog 中,这通常通过 assign 关键字完成,它将一个值连续驱动到一个称为​​线网 (wire)​​ 的网络类型上。wire 就像它的名字一样:一根传输信号的连接线。它不存储任何东西;它仅仅承载某个逻辑的结果。

想象一个电路,输入为 x、y 和 z。我们想计算函数 f=(x+y)⋅z‾f = (x + y) \cdot \overline{z}f=(x+y)⋅z,其中 + 是或,· 是与,上划线是非。在 Verilog 中,我们可以这样写:

loading

注意这里的措辞:“永远是”。这些不是程序中的步骤。它们是关于硬件的三个并行的、同时有效的事实陈述。它们描述了一个被永久连接起来以计算该函数的逻辑门网络。同样,在 VHDL 中,人们可能用一个单一的并发语句来描述同样类型的永恒逻辑关系。您在声明您的小宇宙中不变的物理定律。

捕捉时间:时钟块

描述记忆是另一回事。记忆涉及随时间的变化,而在数字世界中,时间不是连续的。它被时钟的节拍所量化。我们需要一种方式来说:“什么都别做……什么都别做……就是现在!在时钟节拍的精确时刻,捕获这个值。”

这就是时钟过程块的工作,比如 Verilog 中的 always @(posedge clk) 或 VHDL 中的时钟 process。这种结构告诉系统忽略时钟节拍之间发生的一切,只在“上升沿”——即时钟信号从低电平转换到高电平的瞬间——给予关注。

在这个块内部,我们描述我们的存储元件(称为​​寄存器​​,在 Verilog 中用 reg 关键字声明)应该捕获什么值。也正是在这里,我们遇到了 HDL 设计中所有概念里最美妙、最微妙也最关键的一个:观察与行动的区别。这就是阻塞赋值与非阻塞赋值的故事。

宏大的幻觉:并行世界中的顺序执行

让我们想象两位工程师,Alice 和 Bob,正在构建一个简单的两级流水线。其思想是,在每个时钟节拍,寄存器 y 应该捕获输入 x,而另一个寄存器 z 应该捕获 y 的前一个值。这就像一条装配线,零件从 x 工位移动到 y 工位,而原来在 y 工位的零件移动到 z 工位。

Alice 像传统程序员一样思考,使用阻塞赋值(=)写下了这段代码:

loading

Bob 考虑的是并行硬件,使用非阻塞赋值(<=)写下了这段代码:

loading

假设 y 和 z 的初始值都是 0。就在时钟节拍到来之前,x 变成了 1。节拍到来时会发生什么?

在 Alice 的代码中,= 运算符创建了一个阻塞序列。仿真器会说:“首先,执行 y = x;。好的,y现在是1。然后,执行 z = y;。y的当前值是1,所以z变成1。” 时钟节拍之后,Alice 发现 z 的值是 1。这是一个连锁反应。

但硬件不是这样工作的!你不可能让一个信号在零时间内穿过两个寄存器。Bob 的代码模拟了现实。<= 运算符是非阻塞的。它分两个阶段工作。在时钟沿,它会说:“首先,让我们对所有赋值语句的右侧进行快照。”

  • 它看到 y <= x; 并注意到 x 是 1。
  • 它看到 z <= y; 并注意到 y 的旧值是 0。

然后,在这个“快照”阶段完成后,它说:“现在,同时更新所有的寄存器。”所以 y 变成 1,而 z 变成 0。时钟节拍之后,Bob 发现 z 的值是 0,这正确地模拟了一个周期的延迟。

非阻塞赋值(<=)是语言捕捉同步硬件真正并行特性的方式。系统中的所有触发器在时钟沿到来之前“观察”它们的输入,然后在同一瞬间“跃迁”到它们的新状态。代码 q2 <= q1; q1 <= d; 是对一个两级移位寄存器的优美而简洁的描述,其中第一个触发器(q1)的输出物理上连接到第二个触发器(q2)的输入。阻塞赋值对于描述单个时钟周期内的计算序列(组合逻辑)很有用,但对于建模并行更新的状态保持寄存器,非阻塞赋值才是王道。混淆它们会导致仿真与现实不符,以及硬件无法正常工作。

机器中的幽灵:意料之外的后果

因为我们是在描述物理硬件,所以我们的描述必须精确和完整。任何含糊不清都可能导致综合工具做出假设,从而在我们的机器中产生“幽灵”——那些导致令人费解的错误的非预期电路。

最常见的幽灵之一是​​推断锁存器​​。想象你正在描述一个简单的组合逻辑块。你写了一个 if 语句:“如果 A=1 且 B=1,则输出 Z 应该是 1。”如果这个条件不成立呢?你必须告诉电路在所有可能的情况下该怎么做。如果你没有提供 else 子句,综合器就会面临一个问题:“好了,条件不成立。我该如何处理 Z?”唯一合乎逻辑的做法就是保持它之前的值。而“保持”这个行为正是记忆的定义!你本应是组合逻辑的设计,却刚刚长出了一个时序锁存器,一个你从未打算创建的记忆元件。这就是为什么在过程块内赋值的任何变量都必须是能够保持值的类型,比如 reg,因为语言预料到了这种可能性。

当我们的仿真模型不能准确反映现实时,会产生另一个幽灵。在 VHDL 中,一个 process 有一个​​敏感列表​​——一个能“唤醒”代码块的信号列表。如果你在建模一个透明锁存器,它应该在使能信号 E 为高时将输入 D 传递到输出 Q,那么你的仿真模型必须对 E 和 D 的变化都敏感。如果你忘记将 D 包含在列表中,当 D 变化时,仿真器将不会重新评估该块。你的仿真将显示 Q 保持其旧值,尽管一个真实的物理锁存器会立即将新的 D 传递过去。蓝图是正确的,但你用来可视化的工具收到了不完整的指令。

掌握 HDL 的旅程,就是从顺序步骤的思维转向并行结构的思维;从编写食谱到绘制蓝图的旅程。这是学习如何不仅描述行为,而且描述一台物理机器的美丽、互联和同步的现实。

应用与跨学科联系

我们现在已经学习了硬件描述语言的基本语法。我们已经看到了如何编写语句来声明输入、输出以及它们之间的逻辑关系。但这就像学习字母表和句子结构规则;真正的魔力不在于规则本身,而在于你能用它们写出什么样的诗篇。我们可以用这种新发现的语言构建什么样的数字诗篇,什么样的电子机器呢?事实证明,HDL 不仅仅是学术练习的工具;它们是几乎所有现代高性能数字技术构思和诞生的基础媒介。它们是工程师脑中的一个想法与蚀刻在硅片上的功能现实之间的桥梁。

数字雕塑家的工具箱:从抽象规则到物理现实

让我们从最直接的应用开始。想象一下,你需要一个简单的电路,比如一个将3位二进制数转换为八个对应输出线之一的解码器。在过去,你会用分立的逻辑门来连接。而使用 HDL,你只需描述这种行为。你可以写一个单一、优雅的语句,说“根据这个3位输入,选择要产生的输出模式”,然后列出条件。这种语言提供了一种直接、并发的方式来表达输入和输出之间的关系,完美地捕捉了组合逻辑的本质。

但在这里,我们遇到了一个美丽而有时棘手的微妙之处。HDL 不是像 Python 或 C++ 这样的编程语言。在典型的编程语言中,你编写一系列要逐一执行的命令。相比之下,HDL 主要用于描述一个物理结构。这个区别是深刻的。考虑一个代码块,它应该在任何输入改变时更新输出。如果你不小心从“敏感列表”——即代码中指定什么触发更新的部分——中遗漏了一个输入,语言不会产生错误。相反,它会正确地解释你的描述。你描述了一个只对所列信号的变化做出反应的电路。如果另一个输入改变,输出会保持其旧值,等待一个合适的触发器。这样做,你不经意间描述了一个存储元件,一个锁存器!。这个“错误”揭示了核心原则:你不是在告诉计算机一步一步地做什么;你是在描述你希望创建的硬件的本质和布线。每一行代码,以及每一次疏忽,都有其物理后果。

这种描述能力使我们能够构建远比简单解码器复杂的东西。任何智能系统的真正核心是其遵循一系列操作的能力,即拥有“状态”。想想一台自动售货机,它等待投币,分发产品,然后找零。这是一个“状态机”。HDL 非常适合描述这些。我们可以定义状态——IDLE(空闲)、TAKING_MONEY(收钱)、DISPENSING(出货)——以及根据投币传感器或按钮按下等输入在它们之间转换的规则。HDL 允许我们将一个高层次的流程图,即算法状态机(ASM)图,直接转化为对将保持状态的寄存器和将在每个时钟节拍计算下一个状态的逻辑的描述。从简单的交通灯控制器到复杂的微处理器,它们的“大脑”就是这样诞生的。

宏伟蓝图:从描述到器件

那么,我们有了这个美丽的机器的 HDL 描述。接下来呢?这个文本文件如何变成一个物理上工作的器件?在这里,我们需要放大视野,看看 HDL 运作的整个生态系统,这个过程本身就是一个工程奇迹。

旅程始于​​综合(Synthesis)​​。一个称为综合工具的特殊编译器会读取你的抽象 HDL 代码。它不会将其编译成 CPU 的机器码;相反,它会推断你所描述的逻辑门、触发器和多路选择器,并生成一个门级“网表”——即你电路的详细原理图。

然后,这个网表被交给​​布局布线(Place & Route)​​工具。这里的魔法变得真正令人难以置信,特别是对于像现场可编程门阵列(FPGA)这样的现代器件,它们包含数百万个逻辑单元。该工具必须解决一个巨大的难题:首先,它必须将你网表中的每一个逻辑门和触发器放置到硅片上的特定物理位置。然后,它必须布线连接,在一个巨大的、可配置的导线网络中找到路径,以按照你的原理图要求连接所有部分。对于像旧式 PAL 这样的简单器件来说,这微不足道。但对于现代 FPGA 来说,这是一项计算量巨大的任务,类似于设计一个拥有数百万建筑和完美高效道路网络的城市,并在几分钟或几小时内完成。

一旦城市规划完成,该工具会执行​​时序分析(Timing Analysis)​​,计算通过其选择的特定逻辑单元和布线路径的实际信号延迟。它检查你的设计是否能以期望的时钟速度运行。如果一个信号从 A 点到 B 点花费的时间太长,设计就可能失败。

最后,在整个过程成功完成后,工具会生成最终的成果:​​比特流(Bitstream)​​。这是一个原始的二进制文件,一个由1和0组成的流。它不是一个程序。它是主蓝图。当你将这个比特流加载到 FPGA 上时,你正在配置数以百万计的微小开关。你告诉每个查找表(Look-Up Table)它的逻辑功能将是什么,编程布线开关以建立正确的连接,并设置 I/O 引脚。你不是在运行软件;你是在物理上重新布线芯片,使其成为你在 HDL 代码中描述的那个精确的、定制的硬件。

跨学科的交响乐:HDL 在科学与工程中的应用

这种从简单描述中创造定制数字硬件的能力,在无数领域都具有革命性的意义。

在实践​​电气工程​​中,想象一下构建一个包含微处理器、存储器和各种外围设备的复杂电路板。这些组件天生不会说同一种语言。你需要“胶合逻辑”(glue logic)来转换信号、管理时序和解码地址。工程师无需使用几十个小型的分立逻辑芯片——这会占用空间、使制造复杂化,并且没有烙铁就无法修复——而是可以使用单个复杂可编程逻辑器件(CPLD)。所有的胶合逻辑都在 HDL 中描述,并在那一个芯片上实现。这减小了电路板尺寸,简化了物料清单,最重要的是,提供了令人难以置信的灵活性。如果发现了错误或需要升级,你不需要重建硬件;你只需重新编程 CPLD。

在​​计算科学和数字信号处理(DSP)​​中,通用 CPU 对于繁重的数值任务可能太慢。许多算法,如天气预报、金融建模或图像处理中使用的算法,都涉及数十亿次重复相同的数学运算。借助 HDL,我们可以设计一个硬件加速器——专用集成电路(ASIC)——它专为以惊人速度执行某一特定算法而定制。例如,通过使用霍纳法则(Horner's method)描述一个用于多项式求值的深度“流水线”,其中流水线的每个阶段执行一次乘加步骤,我们可以比必须获取、解码和执行通用指令的 CPU 快得多地处理连续的数据流。这就是驱动人工智能、5G 通信和实时科学仪器的定制芯片背后的原理。

最后,这种能力也带来了巨大的责任:我们如何知道我们极其复杂的设计是正确的?如果我们为了更好的性能优化了 HDL 代码,创造了一个新的结构,它在功能上还是一样的吗?详尽的仿真是常常不可能的。这就是 HDL 与​​形式化方法和计算机科学​​的深层理论联系起来的地方。我们可以使用一种称为形式等价性验证的技术。一个工具可以采用两个不同的 HDL 模型——比如一个简单易读的版本和一个复杂、高度优化的版本——并从数学上证明它们对于所有可能的输入在功能上是完全相同的。它通过将两者都综合成一种规范形式,并将它们组合成一个特殊的“Miter”电路来实现这一点,该电路的输出仅在两个设计的输出不同时才为真。然后,它使用一种称为布尔可满足性(SAT)求解器的强大算法来证明,不存在任何可能的输入能使这个 Miter 电路的输出为真。这不是测试;这是一个严谨的逻辑证明,给了我们构建运行我们世界的那些极其复杂的系统的信心。

从雕刻一个简单的门到在芯片上指挥一台超级计算机,硬件描述语言是通用的记录者。它们提供的能力不仅在于使用计算机,更在于创造计算机,开启了一个仅受我们想象力限制的定制计算工具的宇宙。

wire p, q; assign p = x | y; // p 永远是 x 或 y 的结果 assign q = ~z; // q 永远是 NOT z 的结果 assign f = p & q; // f 永远是 p 与 q 的结果
// Alice's code: The illusion of sequence always @(posedge clk) begin y = x; z = y; end
// Bob's code: The reality of parallel hardware always @(posedge clk) begin y <= x; z <= y; end