tags
type
status
date
slug
summary
category
password
icon
硬件描述语言(HDL)
Analysis: network ⇒ function
Synthesis: function ⇒ network
Verilog HDL或VHDL是通用的硬件描述语言。
一般包含两部分,(1)用于生成硬件(一般不能使用递归、while等)(2)用于测试(TestBench,更像计算语言,可以使用while等)
iverilog
cmd命令:
iverilog -o 1.out 1.v
把1.v输出为1.out,-sv是调用systemverilogvvp -n 1.out -lxt2
或 vvp 1.out
gtkwave 1.vcd
查看波形需要在1.v中添加如下命令。在哪个module里加入这个命令就会输出那个module和其调用的module里的波形
编译指令
`define
`undef
`include
常用:
$finish;
终止仿真$stop;
将仿真过程暂停在某一时刻`timescale time_unit/time_precision
`default_nettype none
注释
//
或 /* xxx */
模块
实例化
必须给模块取个名字
数值
0低电平,1高电平,x/X未知,z/Z高阻
仿真中出现X往往代表有bug,比如“线与”对烧
4'b1011
,4表示bit数,‘DHBO表示基数,最后是数值可以加入下划线增加可读性,如
32'h3022_c0de
负数在最前面加
-
都写上长度和基数!不然会有意料之外的bug
数据类型
wire
导线节点,默认的变量类型。信号流向有方向性,因此只能用assign赋值一次。只能在always块外赋值
reg
“寄存器”(不对应生成的电路中的寄存器),类似程序设计语言中的变量
wire用于数据流描述,reg用于行为级描述。reg在always块内赋值。
vector
多位wire或reg组成的bus。
- 定义
reg [3:0] counter;
表示3-0(含)共4bit,且最高有效位为3。reg [3:0] counter[3:0];
则说明counter是4*4bit的数组。要注意向量(packed/bus)
reg[3:0]
与数组(unpacked)counter[3:0]
是不同的概念。向量作为一种数据类型可以整个赋值,比如counter[0]=4'hA
- 引用
counter[31-:8]
与counter[31:24]
等价,其中31-/+
表示从第31个bit开始递减/增,取几位- 合成
temp1 = {data1, data2};
其中的常数必须给出长度。
temp2 = {32{1’b0}}
赋值32位0- 容易出错的赋值
假设a是一个向量,直接进行
b=a;
会导致b默认为1bit的wire,从而出现bug。为防止这种情况出现,可以加上`default_nettype none
integer
位宽与编译器有关的有符号数(reg为无符号数)
time
通过
$time
获取当前仿真时间parameter/localparam
常量,在module中有效
赋值与运算
❗三种描述方式(实例化,门级结构;数据流,assign,wire;行为级,always,reg)不能互相嵌套!!
style1-门级原始结构
and and1(y,x1,x2)
or or1(y,x1,x2,x3)
not not1(y,x)
style2-数据流
- wire的赋值
assign f=(~s&x1)|(x&x2);
wire类型只能被赋值一次。
assign语句替代了一个always块,因此不能用在always里面
- 多变量赋值:
assign {o2, o1, o0} = vec;
style3-行为级
- reg的赋值
b=4'd10
reg不是定义了一个硬件中的寄存器,它未必会体现在最后的硬件中,而只是一个EDA综合时用于运算的中间变量,是我们自己电脑的CPU中的一块内存!
- 多变量赋值:
{o2, o1, o0} = vec;
- 注意阻塞赋值
<=
与非阻塞赋值=
的区别
运算符
~&|^
分别代表非、与、或、异或。注意这些都是bit-wise operator,对向量按位计算且结果也是向量特别的,如果把
&|^
作为单目运算符使用,意为reductioon operator,&4'b0111
结果是0
;而~4'b0111
结果是4'b1000
&& || ! == !== === !==
等是logical operator,结果是1bit的值。== !=
有X或Z时就返回X;而=== !==
则逐位比较,3'b1XZ===3'b1XZ = 1


时延
`timescale time_unit/time_precision
#x
延时x个time_unit过程结构
initial
每个initial语句都是独立的,从0时刻开始执行,只执行一次。多用于初始化、仿真,不用在设计中
always
always @(*)
从0时刻开始执行,执行完最后一条后,如果敏感列表有变化就重复执行。内部赋值采用阻塞赋值
x=y
always @(posedge clk)
内部采用非阻塞赋值
x<=y
。非阻塞赋值同时进行,且采用上一时刻的数据,其结果直到下一个时刻才会生效。如下面代码可以实现reg值的交换。- 注意所有
always
等过程结构内部的赋值都不用assign
,只能对reg等赋值(除非是在用generate生成组合逻辑电路)
generate
用来生成组合逻辑电路。控制变量为genvar。
generate中可以嵌套if,case,for等等,需要一个名字
- 所有的分支和循环(if,case,for,while,repeat,forever,generate)都必须包含在always或者initial或者generate块中
分支
- if和case必须包含在always块中,因此内部不能对wire赋值。对wire赋值可以采用
out = (sel == 0) ? a:b
- casez中,当满足多个条件时,采用最前面那个。但最好的方式还是让输入无论如何都只满足一个条件,因此
bzz10
比bzz1z
更好。
- if和case中最好采用互斥条件!这是一个好习惯,利于EDA形成最好的电路
- 避免latch。如果在一个模块后,某个值没有被确定,那么verilog会自动继承其上一时刻的值,称为latch。除非有意为之,否则latch的警告很有可能是bug。 为避免latch,必须确保在任何情况下所有变量都被赋值 除了使用default之外,也可以在case前赋予默认值:
循环
for
控制变量一般定义为integer,该变量只用于辅助生产代码,而在仿真时会消失。
repeat
repeat(N)
代表重复多少次while
forever
相当于
while(1)
时序电路的Verilog
- 门级结构描述(RS-Latch)
理论上这可以描述所有电路,但这太繁琐了
- posedge/negedge(DFF)
- 阻塞赋值与非阻塞赋值(移位寄存器)
阻塞赋值是依次执行,因此赋值顺序非常重要;
非阻塞赋值是统一计算完所有RHS的结果之后,一起赋值给LHS(类似python),更符合硬件设计的思维
在同一模块中不要混用阻塞赋值和非阻塞赋值!
- D-Latch
- 作者:XiaoTianyao
- 链接:https://www.xty27.top/article/Verilog
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。