Last updated on March 27, 2025 pm
与 C++ 和 Python 不同,Verilog 是一种硬件描述语言,主要用于描述和设计电数字电路。本文是一篇从零开始的 Verilog 入门指南。
数字的表示
- 有符号数表示:
<位宽>' <sb><数字>
(数字以补码表示)
逻辑值
Verilog 中有 4 种逻辑值:
1
:逻辑1,高电平
0
:逻辑0,低电平
x
:未知值
z
:高阻态
运算符
- 算数运算
运算符 |
名称 |
运算符 |
名称 |
* |
乘法 |
/ |
除法 |
+ |
加法 |
- |
减法 |
% |
求余 |
** |
求幂 |
- 逻辑运算
运算符 |
名称 |
运算符 |
名称 |
&& |
逻辑与 |
|| |
逻辑或 |
! |
逻辑非 |
|
|
- 关系运算
运算符 |
名称 |
运算符 |
名称 |
> |
大于 |
< |
小于 |
>= |
大于等于 |
<= |
小于等于 |
- 等价运算
运算符 |
名称 |
运算符 |
名称 |
== |
等于 |
!= |
不等于 |
=== |
case等于 |
!== |
case不等 |
- 等于和不等于运算的结果可能是
1
、0
、x
。对于x
或z
认为是不确定的值,比较结果为x
。
- case等于和case不等的结果只能是
1
或O
,对于x
、z
认为是确定的值,参加比较。
- 按位运算
运算符 |
名称 |
运算符 |
名称 |
& |
按位与 |
| |
按位或 |
~ |
按位非 |
^ |
按位异或 |
^~ / ~^ |
按位同或 |
|
|
- 如操作数位宽不同,位宽小的左侧补0,运算结果位宽与操作数相同。
- 缩减运算
运算符 |
名称 |
运算符 |
名称 |
& |
缩减与 |
~& |
缩减与非 |
| |
缩减或 |
~| |
缩减或非 |
^ |
缩减异或 |
^~ / ~^ |
缩减同或 |
- 缩减运算的操作数是 1 个二进制数。
- 将操作数的各位自左向右进行逻辑运算,运算结果为 1 位。
- 移位运算
运算符 |
名称 |
运算符 |
名称 |
>> |
逻辑右移 |
<< |
逻辑左移 |
>>> |
算数右移 |
<<< |
算数左移 |
- 格式:
<操作数><移位符><移动位数>
- 逻辑右移动补0,有符号数算数右移动补符号位。
- 拼接复制运算
运算符 |
名称 |
格式 |
{} |
拼接 |
{<操作数1>, <操作数2>, ...} |
{{}} |
复制拼接 |
{<复制次数>{<操作数1>, <操作数2>, ...}} |
- 条件运算
运算符 |
名称 |
格式 |
?: |
条件赋值 |
<表达式1>?<表达式2>:<表达式3> |
基本数据类型
Verilog 中最常见的数据类型是 wire
和 reg
。
特性 |
wire (线型) |
reg (寄存器型) |
存储能力 |
无存储能力(实时反映输入变化) |
可存储值(直到下次赋值) |
赋值方式 |
使用assign 连续赋值或模块端口驱动 |
使用= 或<= 过程赋值 |
典型应用场景 |
模块间信号连接、组合逻辑输出 |
时序逻辑存储 |
- 对端口信号,输入信号一般是
wire
型,输出信号可以是wire
型或reg
型。
assign(连续赋值)语句
- 特点:始终处于激活状态,即只要表达式中的操作数有变化,立即进行计算和赋值。
- 赋值目标:必须是
wire
型。
- 例:
always 语句块 与 过程赋值
always 语句块
1 2
| always @(敏感信号条件表) 各类顺序语句;
|
- 特点:不总是处于激活状态,只有当满足激活条件时才会被激活。
过程赋值
在 always
语句块中的赋值称为过程赋值。
- 特点:只有激活该过程时,才会进行计算和赋值。而未激活时,即使操作数有变化,也不执行计算和赋值,
- 赋值目标:必须是
reg
型。
- 例:
1 2
| always @(posedge CLK) Q = D;
|
激活条件
- 边沿敏感
1 2
| (posedge <信号名>) (negedge <信号名>)
|
- 电平敏感
1 2
| (<信号名1>, <信号名2>, ...) (<信号名1> or <信号名2> or ...)
|
多条语句
always
语句块中可以使用if
、case
、for
等语句。
- 语句块中有多条语句,需要用
begin
end
包裹。
阻塞赋值 与 非阻塞赋值
阻塞赋值
1 2
| <赋值目标1> = <表达式1> <赋值目标2> = <表达式2>
|
- 特点:计算表达式后立即完成赋值,中间无其他操作。
- 常用于设计组合逻辑电路。
非阻塞赋值
1 2
| <赋值目标1> <= <表达式1> <赋值目标2> <= <表达式2>
|
- 特点:先按顺序计算右侧表达式的值,全部计算完后按顺序赋值。
- 常用于设计时序逻辑电路。
if 语句和 case 语句
if 语句
- 格式:与 C++ 一致。
- 条件表达式如果值为
x
,当作 0
处理。
case 语句
1 2 3 4 5 6
| case (<表达式>) <取值1>: <语句1>; <取值2>: <语句2>; ... default: <默认语句>; endcase
|
模块结构
1 2 3 4 5
| module <模块名>([端口列表]); [端口信号声明]; [参数声明]; [内部信号声明] / [assign语句] / [always语句块] / [门原语或模块调用] endmodule
|
- 端口列表:指电路的输入 / 输出信号名称列表,用逗号隔开。
- 端口信号声明格式:
1
| <输入输出属性> <数据类型> <位宽> <名称>;
|
- 数据类型不说明则默认为
wire
型。
- 位宽用
[n1:n2]
表示,不说明则默认是 1 位。
例1:多路复用器
1 2 3 4 5
| module MUX(a, b, s, y); input a, b, s; output y; assign y = (s ? a : b); endmodule
|
例2:D 触发器
1 2 3 4 5 6
| module DFF(CLK, D, Q); input CLK, D; output reg Q; always @(posedge CLK) Q <= D; endmodule
|
例3:全加器
1 2 3 4 5 6 7
| module full_adder(A, B, CIN, S, COUT); input [3:0] A, B; input CIN; output reg [3:0] S; output COUT; ... endmodule
|
底层模块和门原语调用
底层模块调用
- 命名法
1
| <模块名> <实例名>(.<底层端口名1>(<外接信号名1>), .<底层端口名2>(<外接信号名2>), ...)
|
1
| DFF dff1(.CLK(clk), .D(d1), .Q(q1));
|
- 顺序法
1
| <模块名> <实例名>(<外接信号名1>, <外接信号名2>, ...)
|
门原语
- Verilog 语言提供的逻辑门,可以直接调用。
- 格式:
1
| <门原语名> <实例名>(<外接信号名1>, <外接信号名2>, ...)
|
Reference
https://www.bilibili.com/video/BV1iv4y1F7Km