数字逻辑期末复习二
数字逻辑期末复习(2)
易错点:
隐式网
在 Verilog 语言中,如果你在一个
assign语句中使用了一个没有提前声明的信号名称,或者将未声明的信号连接到模块端口,Verilog 编译器并不会报错,而是会“自作聪明”地帮你自动创建一个信号。默认行为:这个自动创建的信号(隐式网)默认类型是 1-bit 的 wire。
1 | wire [2:0] a, c; // 声明了两个 3 位的向量 a 和 c |
- 后果:导致数据丢失,而编译器可能不会报错,导致逻辑错误很难被发现
打包数组 非打包数组
- Packed (打包) -> 写在名字左边
- 形式:
reg [7:0] data; - 含义:这些位是紧挨在一起的,被视为一个整体。
- 理解:把它想象成一个“数值”或一个“字”。比如
[7:0]就是一个 8 位的整数。你可以直接对它进行数学运算(加减乘除)。 - 硬件视角:这是一组连续的导线(总线)。
- 形式:
- Unpacked (非打包) -> 写在名字右边
- 形式:
reg data [7:0]; - 含义:这些是独立的元素集合。
- 理解:把它想象成 C 语言或 Java 中的“数组”或“列表”。这里有 8 个独立的寄存器,每一个都互不相干。你通常不能直接拿整个数组去做加法,只能一个一个取出来用。
- 主要用途:用来做内存(RAM/ROM)。
- 形式:
访问向量元素
- 简单来说,如果你把一个多位的信号(向量)看作是一排并列的电线,这张图就是在教你如何只接通其中的某一根或某几根电线。
1.整体赋值 (Accessing Entire Vector)
图片上半部分讲的是最简单的情况:
- 语法:
assign w = a; - 规则:直接把向量
a的值赋给向量w。 - 自动调整:如果
w和a的位宽(长度)不一致,Verilog 会自动处理:- 如果右边短,左边长:零扩展(高位补0)。
- 如果右边长,左边短:截断(丢弃高位,只保留低位)。
- 部分选择 (Part-Select) —— 核心内容
图片下半部分展示了如何只操作向量的一部分。
- 选取一段 (切片):
w[3:0]:选中w的低 4 位(第3位到第0位)。
- 选取一位:
x[1]:选中x的第 1 位。x[1:1]:效果同上,也是选中第 1 位。
- 特殊索引:
z[-1:-2]:如果定义时用了负数索引,取值时也可以用负数。
- ⚠️ 重要规则:方向必须匹配 (Direction Matching)
图片中特意举了一个**非法(Illegal)**的例子,这是初学者最容易犯的错:
- 错误代码:
b[3:0] // Illegal - 正确代码:
b[0:3] - 原因:这取决于变量
b是如何声明的。- 如果声明时是
wire [0:7] b;(升序,0是高位/起始位),那么你在切片时也必须遵守这个方向,写成[0:3]。 - 你不能反着来(用
[3:0]去访问一个升序声明的变量),这在 Verilog 中是非法的。
- 如果声明时是
- 复杂的跨位赋值
最后一行代码展示了如何把两个不同方向或位置的切片连在一起: assign w[3:0] = b[0:3];
这意味着一一对应的连接:
w的第 3 位 <— 连接到 —>b的第 0 位w的第 2 位 <— 连接到 —>b的第 1 位- …以此类推。
位操作符 逻辑操作符
- 位操作符:是对总线上的每一根电线分别进行操作,输入几位,输出通常也是几位。
- 逻辑操作符:是把整个信号看作**“真”或“假”**,输出结果永远只有 1 位 (1 或 0)。
位操作符
这类操作符会对两个操作数的对应位(第0位对第0位,第1位对第1位……)独立进行逻辑运算。
符号:
&:按位与 (AND)|:按位或 (OR)^:按位异或 (XOR)~:按位取反 (NOT,这是一元操作符)~^或^~:按位同或 (XNOR)
逻辑操作符
这类操作符用于判断条件的真 (True) 或 假 (False)。
- 规则:如果操作数是 0,则视为“假”;如果操作数是任何非零值(不管有多少位,只要有一位是1),则视为“真”。
- 结果:结果永远只有 1 bit(
1代表真,0代表假)。
符号:
&&:逻辑与 (AND)||:逻辑或 (OR)!:逻辑非 (NOT)
例子
- **&与&&**的差别
- 假设:
A = 4'b0100(十进制 4)B = 4'b0010(十进制 2)
1 | //位操作符 |
Verilog简介
Verilog建模方式
- 结构化描述方式 (对应逻辑门)
- 连线,调用元件
- 数据流描述方式 (对应逻辑表达式)
- assign,连续赋值
- 行为描述方式 (对应真值表)
- always块,逻辑判断
仿真与测试激励
测试模块(tb.v)的编写
- 输入信号产生:initial语句,always语句
- 输出结果检查:$monitor,$display,波形图
- top模块为什么没有端口:top已经是最顶层模块,不会被其他模块实例化,因此不需要有端口
被测模块(DUT)
- 测试模块不需要知道被测模块具体的实现细节
- 测试模块可以通过模块名及端口说明使用被测模块
- 自上而下设计方法
时间尺度
- timescale 时间尺度
1 |
|
时间精度不能大于时间单位
理解:尺子的最小刻度不能比测量单位大
数字只能是1,10,100,不要出现5,20这样的数字
尽可能的使时间精度与时间单位接近
精度和单位差距越大,仿真越慢
测试模块 — 激励+输出
- 只有always/initial过程块中赋值的变量才需要定义为reg
- initial过程块只执行1次,且不可综合
- #:延迟,不可综合
测试模块 — 过程块
- initial:赋初值,产生激励信号,检查输出结果(只执行一次)
- always:产生周期性激励信号(重复执行)
产生激励信号
1. 左侧:产生周期信号(如时钟 clk)
在仿真中,我们需要人为造一个不停跳变的时钟信号。
写法 1:利用
always块1
always #10 clk = ~clk;
- 含义:每过 10 个时间单位,
clk翻转一次。 - 结果:产生一个周期为 20(10高电平+10低电平)的方波。
- 注意:这种写法通常需要在
initial块里先把clk初始化为 0,否则x(未知)取反还是x。
- 含义:每过 10 个时间单位,
写法 2:利用
forever循环1
2
3
4
5initial begin
clk = 0; // 先初始化
forever // 无限循环
#10 clk = ~clk;
end- 含义:效果和上面完全一样。
forever是死循环语句,专门用于仿真产生时钟。 - 循环控制语句(蓝框): 列出了 Verilog 中用于控制循环的关键词:
for,while,forever,repeat。这些多用于 Testbench,在实际硬件设计(RTL)中较少使用(除了for有时用于批量生成电路)。
- 含义:效果和上面完全一样。
2. 右侧:产生非周期信号(如复位 rst)
这里展示了三种不同的写法来实现同一个时序波形,重点在于理解 “串行执行” 和 “并行执行” 以及 “时间累加” 的区别。假设我们需要这样一个波形:0时刻为1 -> 15时刻变为0 -> 25时刻变回1 -> 80时刻结束。
A. 串行执行 (
begin ... end) - 左上角
1 | initial begin |
核心逻辑:
begin...end块内的语句是顺序执行的。延迟叠加:下一句的延时是在上一句执行完的基础上叠加的。
计算:第二个动作发生在
15 + 10 = 25时刻。B. 并行执行 (
fork ... join) - 右下角
1 | initial fork |
核心逻辑:
fork...join块内的语句是并行执行的(同时启动)。绝对时间:所有的延时都是相对于0时刻(块开始时刻)算的,互不影响。
计算:你看这里的数字变成了
#25,因为它直接指定了在第 25ns 发生动作,而不是像上面那样写#10(增量)。红框提醒:这种
fork...join写法完全不可综合,只能在仿真里用。C. 非阻塞赋值调度 - 右上角(比较少见但在仿真中有效)
1 | initial begin |
- 核心逻辑:这里使用了非阻塞赋值
+内部延迟。 - 非阻塞特性:这几行代码几乎是瞬间执行完的(没有阻塞),但是它们预约了未来的赋值。
- 结果:效果和
fork...join类似,指定的是绝对时间点(相对于当前时刻)。
Verilog可综合基本语法
连续赋值语句assign(实现组合逻辑电路)
过程赋值语句always过程块

常用数据类型
- wire(线网):表示元件中的物理连接
- reg(寄存器):always、initial过程的输出
- 除了应该定义为reg的都定义为wire
- wire缺省值为z,reg缺省值为x
Verilog中的四值逻辑
- 0,1,x,z
- 赋值的时候,x代表不在意这个逻辑状态,可以减少bug
- 仿真结果中x表示状态未知或状态冲突
- z:悬空、相当于短路的状态,高阻态
数据表示方式
- 解题:把题面四个十六进制数字写成二进制,前面的6表示只有六位数据,留下后六位,相当于001111,十进制的15,四个选项代表的都是十进制下的15,答案选ABCD
- “`”表示一个编译预处理
- 宏定义:`define
- 文本包含:`include…… 当前文件中插入一个文件
- 时间尺度:`timescale








