1.2 一份电路的三种讲法:建模视角 本节摘要:Verilog 用三种建模视角描述电路:结构化建模像装配清单,数据流建模像逻辑函数表,行为级建模像响应规则说明书。三种讲法可以混用,综合器最终会把它们全部翻译成门电路。分清自己站在哪一层,是避免「仿真对、上板错」的第一道防线。 上一节交代了语言的来路,这一节回答一个更实际的问题:面对同一个电路,Verilog 提供哪几种讲法,各自适合什么场合。这是建模视角的「立案登记」——后面所有章节的语法,都能归进这三种讲法里。 别把三种讲法当成三种语言 初学者最常见的误判,是把结构化、数据流、行为级当成三门不同的东西。其实它们是同一门语言里并存的描述粒度,写在同一份文件里都合法。
本节摘要:Verilog 用三种建模视角描述电路:结构化建模像装配清单,数据流建模像逻辑函数表,行为级建模像响应规则说明书。三种讲法可以混用,综合器最终会把它们全部翻译成门电路。分清自己站在哪一层,是避免「仿真对、上板错」的第一道防线。
上一节交代了语言的来路,这一节回答一个更实际的问题:面对同一个电路,Verilog 提供哪几种讲法,各自适合什么场合。这是建模视角的「立案登记」——后面所有章节的语法,都能归进这三种讲法里。
初学者最常见的误判,是把结构化、数据流、行为级当成三门不同的东西。其实它们是同一门语言里并存的描述粒度,写在同一份文件里都合法。真正的区别在于「你描述的是电路的哪一面」:是元件之间的接线(结构)、是输出与输入的逻辑函数关系(数据流)、还是电路对事件的响应规则(行为)。
三种讲法的证据力不同。结构化描述最接近真相——你实例化一个门,综合后就真的是那个门,没有解释空间。数据流描述次之——assign 写的是逻辑表达式,综合器会做布尔化简,结果可能与你的表达式形态不同但逻辑等价。行为级描述弹性最大——always 块只规定响应规则,具体长成什么电路由综合器推断,推断错了不怪它,怪你没把规则写清楚。

以二选一多路选择器为例——信号 sel 为高选 a,为低选 b。这个电路小到一眼能看穿,正好用来对比讲法差异。
结构化写法,直接实例化门原语,自己当装配工:
module mux2_struct( input wire a, b, sel, output wire y ); wire sel_n; // sel 的反相 wire ch_a; // a 与 sel 相与 wire ch_b; // b 与 sel 反相相与 not g1(sel_n, sel); and g2(ch_a, a, sel); and g3(ch_b, b, sel_n); or g4(y, ch_a, ch_b); endmodule
数据流写法,把逻辑函数一行写完:
module mux2_dataflow( input wire a, b, sel, output wire y ); assign y = (a & sel) | (b & ~sel); endmodule
行为级写法,描述响应规则而不画门:
module mux2_behavior( input wire a, b, sel, output reg y // 过程块内赋值的目标要声明为 reg ); always @(*) begin if (sel) y = a; else y = b; end endmodule
案例展开:三种写法的综合对账。 背景是想确认「讲法不同是否导致电路不同」。操作:把三个模块分别交给综合工具跑同一工艺库,比对网表与面积报告。结果值得细看:结构化版本几乎原样保留四个门,因为结构描述没有解释空间;数据流版本被工具化简,两个与门、一个或门、一个非门保持,连线可能重排,逻辑等价;行为级版本综合出的电路与前两者等价,具体结构取决于工具——多数工具会生成与数据流版本相同的门,也有些会映射到库里的多路选择器单元。解读:三种讲法殊途同归,「讲法」影响的是综合器的工作方式和你代码的可读性、可维护性,而不是电路的逻辑功能。变式:把选择条件改成三选一、四选一,行为级写法的优势立刻显现——case 语句加几行就行,结构化写法则要手工装配的门数翻倍,此时没人再坚持画门。
真实工程里三种讲法混着用。顶层模块用结构化方式把几个子模块装配起来;子模块内部的组合逻辑用 assign 写;时序逻辑用 always 写。这种「层次化装配加内部行为描述」是 RTL 工程的标准形态,1.3 节讲模块语法时会给出完整骨架。
抽象层级还有个方向感:越偏行为级,代码越短、仿真越慢、综合解释空间越大;越偏结构级,代码越长、控制力越强。所谓 RTL(寄存器传输级),大致是数据流与行为级的混合层——它足够抽象让人类写得动,又足够具体让综合器推得出确定电路。全书的重点就钉在这一层。
建模视角是全书的检索系统。第三章讲赋值语义,本质是数据流与行为级各自遵守的规则;第五章门级建模就是结构化视角的展开;第八章综合流程,讲的就是三种讲法如何被统一翻译成门。以后你读任何一份陌生代码,第一步就是分辨:这段是接线、是函数、还是规则?分对了,后面的工具行为就能预判;分错了,连报错信息都看不懂。
还有个工程上的推论:review 别人代码时,对结构化描述提意见要谨慎——它就是设计者的明确意图,改成「更简洁的写法」可能违背本意(比如故意手工例化的触发器链,可能是为了精确控制布局)。而对行为级描述则要多问一句——这里综合器会推断出什么?两者审查的注意力方向相反,这正是分清视角的实际用处。
不仅可以,而且是标准形态。顶层装配用结构化,组合逻辑用 assign,时序逻辑用 always——一份模块三层齐备各管一段。真正要避免的不是混用,而是「同一信号多处驱动」:一个输出只能属于一种讲法,混着驱动是编译错误。
分场景。可综合 RTL 里,抽象要停在「综合器能确定推断」的边界内——无限循环、动态数据结构这类软件级抽象没有电路对应物。测试台里则相反,越抽象越好:事务级、协议级的描述让用例易读易维护。所以「抽象到什么程度」的答案取决于这份代码给谁执行——给综合器就停在 RTL,给仿真器可以一路抽象上去。
看关键词。出现实例化语句是结构化;出现 assign 是数据流;出现 always 要再看敏感形式——通配敏感是组合行为级,边沿敏感是时序行为级。这个三秒判断法在 review 大文件时非常省力:先扫关键词把代码分段归类,再对每段用对应的评审标准。结构段看连接一致性,数据流段看表达式正确性,行为段看综合推断结果。
下一节进入语言的基本容器:模块怎么定义、端口怎么声明、层次怎么搭。