Verilog编程入门

Last updated on March 27, 2025 pm

与 C++ 和 Python 不同,Verilog 是一种硬件描述语言,主要用于描述和设计电数字电路。本文是一篇从零开始的 Verilog 入门指南。

数字的表示

  • 无符号数表示<位宽>' <进制><数字>
1
2
4'b1010    // 4位二进制数1010(十进制10)
8'd255 // 8位十进制数255(二进制1111111)
  • 有符号数表示<位宽>' <sb><数字>(数字以补码表示)
1
4'sb1010    // 4位有符号二进制数1010(十进制-6)

逻辑值

Verilog 中有 4 种逻辑值:

  • 1:逻辑1,高电平
  • 0:逻辑0,低电平
  • x:未知值
  • z:高阻态

运算符

  1. 算数运算
运算符 名称 运算符 名称
* 乘法 / 除法
+ 加法 - 减法
% 求余 ** 求幂
  1. 逻辑运算
运算符 名称 运算符 名称
&& 逻辑与 || 逻辑或
! 逻辑非
  • 逻辑型运算的结果可能是10x
  1. 关系运算
运算符 名称 运算符 名称
> 大于 < 小于
>= 大于等于 <= 小于等于
  • 关系型运算的结果可能是10x
  1. 等价运算
运算符 名称 运算符 名称
== 等于 != 不等于
=== case等于 !== case不等
  • 等于和不等于运算的结果可能是10x。对于xz认为是不确定的值,比较结果为x
  • case等于和case不等的结果只能是1O,对于xz认为是确定的值,参加比较。
  1. 按位运算
运算符 名称 运算符 名称
& 按位与 | 按位或
~ 按位非 ^ 按位异或
^~ / ~^ 按位同或
  • 如操作数位宽不同,位宽小的左侧补0,运算结果位宽与操作数相同。
  1. 缩减运算
运算符 名称 运算符 名称
& 缩减与 ~& 缩减与非
| 缩减或 ~| 缩减或非
^ 缩减异或 ^~ / ~^ 缩减同或
  • 缩减运算的操作数是 1 个二进制数。
  • 将操作数的各位自左向右进行逻辑运算,运算结果为 1 位。
  1. 移位运算
运算符 名称 运算符 名称
>> 逻辑右移 << 逻辑左移
>>> 算数右移 <<< 算数左移
  • 格式:<操作数><移位符><移动位数>
  • 逻辑右移动补0,有符号数算数右移动补符号位。
  1. 拼接复制运算
运算符 名称 格式
{} 拼接 {<操作数1>, <操作数2>, ...}
{{}} 复制拼接 {<复制次数>{<操作数1>, <操作数2>, ...}}
  1. 条件运算
运算符 名称 格式
?: 条件赋值 <表达式1>?<表达式2>:<表达式3>
  • 若表达式1的值为x,则结果为x,其余同C语言。

基本数据类型

Verilog 中最常见的数据类型是 wirereg

特性 wire(线型) reg(寄存器型)
存储能力 无存储能力(实时反映输入变化) 可存储值(直到下次赋值)
赋值方式 使用assign连续赋值或模块端口驱动 使用=<=过程赋值
典型应用场景 模块间信号连接、组合逻辑输出 时序逻辑存储
  • 对端口信号,输入信号一般是wire型,输出信号可以是wire型或reg型。

assign(连续赋值)语句

  • 格式:
1
assign <赋值目标> = <表达式>
  • 特点:始终处于激活状态,即只要表达式中的操作数有变化,立即进行计算和赋值。
  • 赋值目标:必须是wire型。
  • 例:
1
assign y = a & b;

always 语句块 与 过程赋值

always 语句块

  • 格式:
1
2
always @(敏感信号条件表)
各类顺序语句;
  • 特点:不总是处于激活状态,只有当满足激活条件时才会被激活。

过程赋值

always 语句块中的赋值称为过程赋值。

  • 特点:只有激活该过程时,才会进行计算和赋值。而未激活时,即使操作数有变化,也不执行计算和赋值,
  • 赋值目标:必须是reg型。
  • 例:
1
2
always @(posedge CLK)
Q = D;

激活条件

  1. 边沿敏感
1
2
(posedge <信号名>)  // 信号上升沿到来
(negedge <信号名>) // 信号下降沿到来
  1. 电平敏感
1
2
(<信号名1>, <信号名2>, ...)  // 其中任何一个信号变化
(<信号名1> or <信号名2> or ...) // "," 可以换成 "or"

多条语句

  • always 语句块中可以使用ifcasefor等语句。
  • 语句块中有多条语句,需要用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: <默认语句>; // 可以没有 default
endcase

模块结构

  • 格式:
1
2
3
4
5
module <模块名>([端口列表]);
[端口信号声明];
[参数声明];
[内部信号声明] / [assign语句] / [always语句块] / [门原语或模块调用]
endmodule
  • 端口列表:指电路的输入 / 输出信号名称列表,用逗号隔开。
  • 端口信号声明格式:
1
<输入输出属性> <数据类型> <位宽> <名称>;
  • 内部信号声明格式:
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>(<外接信号名1>), .<底层端口名2>(<外接信号名2>), ...)
  • 例:
1
DFF dff1(.CLK(clk), .D(d1), .Q(q1));
  1. 顺序法
  • 格式:
1
<模块名> <实例名>(<外接信号名1>, <外接信号名2>, ...)
  • 例:
1
DFF dff1(clk, d1, q1);

门原语

  • Verilog 语言提供的逻辑门,可以直接调用。
  • 格式:
1
<门原语名> <实例名>(<外接信号名1>, <外接信号名2>, ...)
  • 实例名可省略,输出在前,输入在后。
  • 例:
1
and (out, in1, in2);

Reference

https://www.bilibili.com/video/BV1iv4y1F7Km


Verilog编程入门
https://cny123222.github.io/2025/03/24/Verilog编程入门/
Author
Nuoyan Chen
Posted on
March 24, 2025
Licensed under