4. Verilog语法基础(一):模块结构、端口定义、assign语句、常用运算符
好,咱们正式开始啃Verilog语法了。说实话,很多新手一上来就被各种语法细节吓住,其实没必要。你想想看,Verilog说白了就是描述硬件的一种语言,跟C语言那种“顺序执行”的思路完全不同。今天这一章,我带你先把最核心的骨架搭起来。
4.1 模块结构——Verilog的基本单元
在FPGA的世界里,模块(module)就是一切。每个模块相当于一个“黑盒子”,有输入、有输出,内部实现具体的逻辑功能。我刚开始学的时候,总觉得模块就是C语言里的函数,后来发现完全不是一回事——模块是并行执行的,不是顺序调用。
一个完整的模块长这样:
module 模块名 (
端口列表
);
// 内部信号声明
// 逻辑功能描述
endmodule
注意看,module和endmodule是一对,就像括号一样,必须成对出现。我见过不少新手漏掉endmodule,结果编译报错半天找不到原因。
核心要点:每个Verilog文件通常只包含一个模块,模块名最好跟文件名保持一致。这是行业惯例,也是我个人的习惯,方便后期维护。
4.2 端口定义——模块的“手脚”
端口就是模块跟外界通信的通道。分为三种:input(输入)、output(输出)、inout(双向)。实际项目中,inout用得不多,咱们先把前两个搞明白。
举个例子,一个最简单的与门模块:
module and_gate (
input wire a, // 输入端口a
input wire b, // 输入端口b
output wire y // 输出端口y
);
// 逻辑功能
assign y = a & b;
endmodule
这里有个细节:wire表示线网类型,是默认的。如果你不写wire,编译器也会自动推断。但我建议你养成好习惯,明确写出来,代码可读性会高很多。
个人经验:我在项目中遇到过一位同事,端口定义时把input和output写反了,结果仿真怎么跑都不对。查了两天才发现是端口方向搞错了。所以,每次写完端口列表,我习惯再扫一遍,确认方向没问题。
4.3 assign语句——连续赋值
assign是Verilog里最常用的语句之一,用于描述组合逻辑。它的意思是“只要右边表达式发生变化,左边就立即更新”。说白了,就是一根导线,一端接逻辑表达式,另一端接输出。
看个例子:
assign sum = a + b;
assign flag = (a > b) ? 1'b1 : 1'b0;
注意,assign语句的左边必须是wire类型,不能是reg。这个坑我踩过——刚开始写代码时,总想把assign赋值给reg,结果编译报错“左侧不是net类型”。
避坑指南:我曾经在一个项目里,用assign给一个reg类型信号赋值,仿真器没报错,但综合时报了一堆警告。后来查资料才知道,assign只能驱动wire。记住:assign配wire,always配reg,这是铁律。
4.4 常用运算符——算术、逻辑、位运算
运算符这部分,跟C语言很像,但有些细节不一样。我挑几个重点说说。
4.4.1 算术运算符
| 运算符 | 含义 | 示例 |
|---|---|---|
| + | 加法 | a + b |
| - | 减法 | a - b |
| * | 乘法 | a * b |
| / | 除法 | a / b |
| % | 取模 | a % b |
注意,乘法和除法在FPGA里很“贵”。乘法器会消耗大量LUT,除法更是如此。我一般建议:能用移位实现的,就别用乘除法。比如乘以2,写成a << 1,比a * 2高效得多。
4.4.2 逻辑运算符
逻辑运算符返回的结果是布尔值(0或1)。常用于条件判断。
| 运算符 | 含义 | 示例 |
|---|---|---|
| && | 逻辑与 | (a > 0) && (b > 0) |
| || | 逻辑或 | (a > 0) || (b > 0) |
| ! | 逻辑非 | !a |
这里有个容易混淆的点:&&和&不一样。前者是逻辑与,后者是按位与。我见过有人写if (a & b),本意是判断a和b都不为0,结果变成了按位与,逻辑完全错了。
4.4.3 位运算符
位运算符是对二进制位逐位操作,在FPGA里用得非常多。
| 运算符 | 含义 | 示例 |
|---|---|---|
| & | 按位与 | a & b |
| | | 按位或 | a | b |
| ^ | 按位异或 | a ^ b |
| ~ | 按位取反 | ~a |
举个例子,如果你想取一个8位信号的低4位,可以这样写:
assign low_4bit = data & 8'b0000_1111;
这种写法比用data[3:0]更清晰,尤其是在做位掩码操作时。
小技巧:异或运算在FPGA里特别有用。比如做奇偶校验、数据翻转,都可以用^。我曾在项目中用异或实现了一个简单的CRC校验,代码简洁,综合效率也高。
4.5 知识体系结构图
下面这张图,帮你把本章的核心知识点串起来:
嗯,这张图把今天的内容都串起来了。模块是骨架,端口是接口,assign是血肉,运算符是工具。四者结合,就能写出最基本的Verilog代码。
总结一下:今天讲了模块的写法、端口怎么定义、assign怎么用,还有三种常用运算符。这些都是基本功,就像练武要先扎马步一样。别急,慢慢来,后面几章我们会把这些基础组合起来,做出真正有用的东西。
公众号:蓝海资料掘金营,微信deep3321