JOVANA
Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

从逻辑图到 Verilog

工程师不会一个门一个门地画出现代芯片——单是一个模块就能容纳数十亿个晶体管,远远超出任何笔或原理图所能排布的极限。他们改用文本来描述硬件,再让工具把图画出来。在本指南里,你会认识 Verilog 这门负责描述的语言,以及那个让每个新手都栽跟头的关键观念:它不是一门编程语言。把这个观念想通,其余的便会水到渠成。

为什么要用文本描述硬件?

想象一下,要画出一座城市,得亲手摆好每一块砖。当一个设计超过几千个门之后,逐个门去画的原理图就是这种感觉——而真正的芯片有数十亿个门。画到某个程度,画图这件事就再也撑不下去了。你需要一种办法,把*你想要什么*说清楚,再让机器去算每一块砖。

这正是 HDL(硬件描述语言)的用武之地。你写下一段精炼的文本描述——「这个模块把两个 8 位数相加」「那个模块在两路输入之间切换」——再由综合工具把你的话变成一张真正的门级网络。文本才是真理之源;原理图成了工具*替*你画出来的东西,而不是你*亲手*去画的东西。

HDL 不是编程语言

这就是那个让每个新手都栽跟头的观念。在软件里,代码是一行接一行执行的。而在 Verilog 这样的 HDL 里,每一行描述的是同时存在的硬件——更像一支管弦乐队里各个声部,而不像菜谱里的一步步操作。写下 assign y = a & b; 并不是做一次「与」运算;它铺下的是一根永久的导线,时时刻刻都在算这个结果。

module and_gate(input a, b, output y);
  assign y = a & b;   // a permanent wire: y is ALWAYS (a AND b)
endmodule
一个二输入「与」门。module 为一块硬件命名;端口就是它的引脚。

这一点一旦想通,你的 assign 语句的先后顺序基本就不再重要了。两行 assign 描述的是两根导线;先写哪一行都没差别,因为两根线是同时通电的。这正是「描述硬件」与「写程序」之间最深的区别——也是值得从第一天就养成的习惯。

你的第一个模块:一个门

一个 Verilog 设计是由一个个模块搭起来的。模块就是一个贴了标签的硬件盒子:它有一个名字、一组端口(也就是它的引脚,标着 input 或 output),还有一段说明引脚之间关系的主体。你把模块拼接起来的方式,就像把芯片插到电路板上——小盒子装进大盒子里。

module inverter(input  a,      // one pin in
                output y);     // one pin out
  assign y = ~a;               // y is always NOT a
endmodule
一个反相器(「非」门)。~ 把这一位取反;y 永远跟着 a 翻转。

把它读成对一个零件的描述,而不是一连串动作:「这里有个叫 inverter 的盒子;有一个进来的引脚 a 和一个出去的引脚 y;y 永远是 a 的反面。」一通电,它就保持这个关系——没有哪一行会「运行」。你将来会用到的每一个门(与、或、非、异或)都只差一个运算符和一句 assign。

导线、信号与总线

在模块内部、模块之间,数值都在导线上传输。一根导线只承载一个比特——一个 0 或一个 1。但你很少只搬一个比特;一个 8 位数、一种颜色、一个地址,都需要好几根导线捆在一起。这一捆就叫总线,在 Verilog 里你用方括号写出它的位宽。

wire        ready;        // 1 bit
wire [7:0]  data;         // an 8-bit bus: data[7] down to data[0]
wire [7:0]  sum;          // an 8-bit bus carrying the result of a + b
assign      sum = a + b;  // (a and b are 8-bit wires declared elsewhere)
[7:0] 的意思是「8 根线,从 7 编到 0」——一条排线,八股线芯。

把总线想象成一条排线:许多股线芯并排粘在一起,作为一个有名字的整体一起移动。data[0] 是最低位那一股,data[7] 是最高位那一股。位宽不是装饰——它是实打实的物理量。一个存放 data[7:0] 的寄存器就是八个触发器那么宽,而更宽的总线,字面意义上就是芯片上更多的金属线。

行为级与结构级

描述同一块硬件有两种写法,好的设计者能在两者之间自如切换。结构级是自底向上的:你点出各个零件,再亲手把它们连起来,像一张装配图。行为级是自顶向下的:你说出*你想要的行为*,让综合去想出该用哪些门。同一颗芯片,两种高度的描述。

拿一个 2 选 1 多路复用器来说——它是一个开关,根据一根选择线 sel 来挑选输入 a 还是输入 b。用行为级的写法,你只要说出这个选择,再让工具去把它搭出来。

module mux2(input a, b, sel, output y);
  assign y = sel ? b : a;   // sel=1 -> y=b, sel=0 -> y=a
endmodule
一个 2 选 1 多路复用器,行为级写法。你描述的是「选择」,而不是那些门。

*同一个*多路复用器的结构级版本,会把那些门一个个写出来——一个反相器、两个「与」门、一个「或」门——再一股一股地连起来。两种描述综合出来的是同一套硬件。这给我们的启示是:从行为级起步,先描述意图,只有在你需要对具体用哪些门做精确控制时,才下沉到结构级。先站在高处;只在划得来的地方才放大细看。