verilog笔记
逻辑值
1、0、z(高阻态,无驱动)、x(未知逻辑电平,实际不存在)
实际电路中,只存在:1、0、z,还有亚稳态不稳定状态
参数定义
parameter 可以在顶层文件中对模块中参数修改
localparam 只能模块内部使用
换算二进制后位宽的总长度可有可无,Verilog 会为常量自动匹配合适的位宽。当总位宽大于实际位宽,则自动在左边补 0;总位宽小于实际位宽,则自动截断左边超出的位数
如果直接写参数 100,则表示 32 位的十进制数 100
阻塞赋值与非阻塞赋值
| 赋值方式 | 特点 |
|---|---|
阻塞赋值 = | 顺序赋值 |
非阻塞赋值 <= | 同时赋值 |
时序逻辑(always @(posedge clk))用非阻塞赋值 <=,组合逻辑(always @(*))用阻塞赋值 =,混用会导致仿真与综合结果不一致。
乘和除一般不宜使用,消耗资源大。有乘法器时使用乘法器,除法通过别的方法计算。求余一般用在测试文件中
归约运算符与按位运算符
& 可以作为一元运算符(仅有一个参与运算的量),也可以作为二元运算符(有两个参与运算的量)
一元运算
&m 表示归约与,将 m 中所有 bit 相与,最后结果为 1 bit
二元运算
按位与 m&n,m 中每一位与 n 中每一位相与。在运算时候要保证 m 和 n 的比特数相等,最后的结果和 m(n)的比特数相同
~& ^ ~^ | ~| 与上面同理
逻辑运算符
&& 与 C 语言相同,逻辑与运算符结果只有真或假
||(逻辑或) ==(逻辑相等) !=(逻辑不等)
关系运算符
< > <= >=
移位运算符
<< 左移运算符 >> 右移运算符
位拼接符号
拼接是一堆花括号加逗号组成 {,},不同数据之间用 , 隔开。8bit 的 a、3bit 的 b、5bit 的 c 顺序拼接成 16 位的 d
1 | d = {a,b,c} |
条件运算符
?: 和 C 语言中三目运算符相同
优先级
归约运算符 > 算术运算符 > 移位运算符 > 关系运算符 > == 和 != > 按位运算符 > && 和 || > 条件运算符
总体:一元运算符 > 二元运算符 > 三元运算符
使用括号添加优先级
if - else 语句
分支语句
case casex casez
case 分支
系统函数
有一些任务和函数,完成一些特殊的功能,称为系统任务和系统函数。这些函数大多数只能在 Testbench 仿真中使用
`timescale 1ns/1ns //时间尺度预编译指令,时间单位/时间精度
时间单位和时间精度由值 1、10 和 100 以及单位 s、ms、us、ns、ps 和 fs 组成。
- 时间单位:定义仿真过程所有与时间相关量的单位。仿真中使用
#数字表示延时相应时间单位的时间,例#10表示延时 10 个单位的时间,即 10ns。 - 时间精度:决定时间相关量的精度及仿真显示的最小刻度。
`timescale 1ns/10ps 精度 0.01,#10.11 表示延时 10110ps。
下面这种写法就是错误的,因为时间单位不能比时间精度小。
1 | //错误 |
主要的函数有如下这些,在支持 Verilog 语法的编辑器中都会显示为高亮关键字
1 | $display //打印信息,自动换行 |
1 | $display() //自动换行 |
| 格式符 | 输出形式 |
|---|---|
%h 或 %H | 以十六进制的形式输出 |
%d 或 %D | 以十进制的形式输出 |
%o 或 %O | 以八进制的形式输出 |
%b 或 %B | 以二进制的形式输出 |
initial 只执行一次
文件读取函数 $readmemb
$readmemb:用于读二进制文件函数。
$readmemh:用于读十六进制文件函数。
1 | $readmemb("<数据文件名>",<存贮器名>); // 读二进制文件 |
1 |
|
存储器定义 reg [7:0] a [20:0];
1 | reg [7:0] a [20:0]; |
reg [7:0]表示定义 8 位宽的寄存器类型数据,每位索引范围为 7(最高位)到 0(最低位)a [20:0]表示定义深度为 21 的数组,索引范围为 20 到 0- 整体含义:定义了一个包含 21 个存储单元的存储器,每个单元为 8 位寄存器,总存储容量为 21×8 位
实际应用
该定义合法且常见于硬件描述中,可用于建模 RAM、ROM 等存储结构
示例初始化方式:
1 | reg [7:0] a [20:0] = '{21{8'h00}}; // 全部初始化为0 |
注意事项
- 存储器索引通常按降序定义(如
[20:0]而非[0:20]),符合 Verilog 规范 - 每个存储单元需通过完整下标访问(如
a[15]表示第 16 个单元的 8 位数据)
该定义可直接用于实际设计文件(如 .v 文件),综合工具会将其映射为实际硬件存储单元
1 | case (sel) |
双向 IO 使用
1 | inout io; |
右移循环
1 | reg [7:0]shift_a; |
左移循环
1 | reg [7:0]shift_a; |
位拼接
1 | wire [3:0]x; |
除法器
注意:FPGA 内部一般没有硬件除法器,/ 运算会综合成大量加减法和移位逻辑,资源消耗大、时序差。设计时应尽量避免除法;若除数是 2 的 N 次方,改用右移实现(a/8 即 a>>3)。
时序逻辑
连续赋值
1 | input a; |
只要是使用 always 中输出的结果,都需要定义 reg 类型。
1 | module add2(a,b,c); |
所有敏感信号 always@(*)
1 | module |
前级仿真
功能仿真
设置仿真工具
在 Quartus 中,通过 Assignments > Settings > Simulation 选择仿真工具(如 ModelSim),并确保不勾选 Run gate-level simulation(否则会触发后仿真流程)。
配置测试文件
在 Native Link 选项卡中添加测试文件(如 testbench.v),并指定顶层模块名称需与 RTL 代码中的模块名一致。
执行仿真
通过 Tools > Run RTL Simulation 或 Tools > Run Functional Simulation 启动前仿真,此时仅验证逻辑功能,无时序延迟。
前仿真:基于 RTL 代码,速度快,用于功能验证。
后级仿真
生成网表文件
完成布局布线后,Quartus 会自动生成网表文件(.vo)和时序文件(.sdo),这些文件将用于后仿真。
启用时序仿真
在 Settings > Simulation 中勾选 Run gate-level simulation,并确保已加载正确的器件库(如 altera_ver)。
配置延时文件
在 ModelSim 中,需将生成的 .sdo 时序文件与网表文件一同加载,以反映实际布线延迟。
后仿真:基于网表文件,包含布线延迟,用于时序验证。
生成网表文件(.vo)和时序文件(.sdo)
参数 parameter
模块
1 | module led(clk,rst,out); |
例化
1 | module led_top(); |
可以通过 parameter 设置实例模块产生不同频率的输出,一个模块多场景应用,也可以使用端口来设置值
parameter 具体可以查看《Verilog HDL 入门 (4)》;用于模块内部的参数定义,可通过模块例化时的 #() 语法修改值
defparam 具体可以查看《Verilog HDL 入门 (4)》;用于在例化后通过层次化路径强制修改已定义的 parameter 值
冲突与优先级
若同一参数被 #() 和 defparam 同时修改,defparam 的赋值会最终生效(因其在编译后执行)。
但此用法易导致代码可读性差,且部分综合工具可能不支持 defparam。
设计建议
优先使用 #() 语法在例化时传递参数,避免混用 defparam
defparam 通常仅用于仿真调试或遗留代码维护
注意
1. 设计一个功能,分成控制模块与数据处理模块编写,然后通过顶层模块联合在一起
串口
收数据通过采样,在一定时间内通过采样次数去比较是高或低电平,最低采样次数大于 16 次,找中间点数据分析。