Verilog快速入门(由于要实验亡羊补牢版)
写在前面
由于Markdown并不方便的图片管理,很多应该有图片的地方缺少相应的图片对应。如果在遇到不熟悉的电路名称,建议可以自行搜索相对应的电路图片和逻辑功能。
Verilog模块结构
第一部分:
1 | module 模块名 ([端口列表]); |
第二部分(功能描述部分):
- 内部信号声明:assign语句
- 底层模块或门原语调用(包括生产块):Initial或always语句块
- 任务和函数定义:specify块(路径延迟)
三种语句顺序无关,都是可选的。
第三部分(必写):
1 | endmodule |
例1:2选1多路选择器的Verilog描述
关于多路数据选择器,大致就是指经过选择,把多个通道的数据传送到唯一的公共数据通道上去,实现数据选择功能的逻辑电路称为数据选择器。它的作用相当于多个输入的单刀多掷开关,这样应该更容易懂点.
1 | module MUX21a(a,b,s,y); |
例2:边沿D触发器的Verilog描述
边沿D触发器是一种数字电路存储元件,在时钟信号的上升沿或下降沿时,将输入数据(D)的值存储到输出(Q)上,并保持该输出直到下一个时钟边沿。它通过时钟信号的触发来控制数据的更新,这意味着只有在时钟信号的某个边沿(通常是上升沿)时,输入数据才会被传递到输出,其他时间输入的变化不会影响输出。这使得边沿D触发器能够同步地存储数据,常用于寄存器、计数器和其他需要数据稳定性的数字电路中。
1 | module DFF1(CLK, D, Q); |
模块说明部分
- 模块名是指电路的名字,又用户指定,最好与文件名一致
- 端口列表是指电路的输入/输出信号名称列表,信号名由用户指定,各名称间用逗号隔开
- 端口信号声明是要说明端口信号的输入输出属性、信号的数据类型,以及信号的位宽;输入输出属性有input,output,inout三种,信号的数据类型常用的有wire和reg两种;信号的位宽用[n1:n2]来表示;同一类信号之间用逗号隔开
- 参数声明要说明参数的名称和初值
例
1 | module full_adder(A,B,CIN,S,COUT); |
- 位宽如果不做说明的话,默认是1位;
- 数据类型不做说明的话,默认是wire型的。
- S位宽为4位,对应的信号位S[3]、S[2]、S[1]、S[0]
assign语句
assign 赋值目标 = 表达式
例如
1 | assign y = a; |
1 | assign M = B|C; |
(M和Y都必须是wire型的)
之所以称为连续赋值语句是指其总是处于激活状态,只要表达式中的操作数有变化,立刻进行计算和赋值。
(与连续赋值语句对应的另一种语句称为过程赋值语句)赋值目标必须是wire型的,wire表示电路之间的连线。
Verilog具有丰富的表达式运算功能,包括
- 算术型运算符
- 逻辑型运算符
- 关系运算符
- 等价运算符
- 按位运算符
- 缩减运算符
- 移位运算符
- 拼接复制运算符
- 条件运算符
可用于assign语句。详见夏宇闻教材第六章。
解释一下按位运算符,这个和其它编程语言的差距大一点:
例子
1 | Y = ~ 4' b1001; |
首先数字右上方加一撇,代表的是位宽,b表示二进制,按位运算符就是按每一位进行计算。
比如第一个表达式对4位二进制1001每一位求非,那么为0110,第二个表达式则是对每一位进行与运算,得到的结果为0001,依次往下推理,第三个表达式就是对每一位进行或运算,值为1111,第四个表达式为0111,第五个表达式结果为0101。(如果操作数的位宽不同,位宽小的会自动左添0补齐)
always语句块
基本格式:
1 | always @(敏感信号条件表) |
例:
1 | always @ (posedge CLK) |
- always语句本身不是单一的有意义的一条语句,而是和下面的语句一起构成的一个语句块,称之为过程块;过程块中的赋值语句称过程赋值语句
- 该语句块不是总处于激活状态,当满足激活条件时才能被执行,否则被挂起,挂起时即使操作数有变化,也不执行赋值,赋值目标值保持不变
- 赋值目标必须是reg型的
激活条件由敏感信号条件表决定,当敏感条件满足时,过程块被激活。敏感条件有两种,一种是边沿敏感,一种是电平敏感。
边沿敏感:(posedge 信号名) 信号上升沿到来;(negedge 信号名) 信号下降沿到来
电平敏感:(信号名列表) 信号列表中的任一个信号有变化
例:
1 | always @ (posedge CLK) |
当CLK上升沿到来时,激活该语句块,将D的值赋值给Q;否则,将该语句块挂起,即使D有变化,Q的值也保持不变,直到下一次赋值。
1 | always @ (D) |
当D有变化时(不管是由1变0还是由0变1),激活该语句块,将D的值赋给Q;否则,将该语句块挂起,Q的值保持不变,直到下一次赋值。
always语句块中除了可以使用表达式赋值以外,还可以使用if,case等行为描述语句,还能够描述边沿变化,因此其功能比assign语句更加强大(assign语句不能使用if语句,也不能描述边沿变化)
1 | module DFF2(CLK,D,Q,RST,EN) |
always语句块中如果有多条赋值语句必须将其用begin end包括起来,assign语句中没有begin end.
例:
1 | module adder(a,b,cin,s,cout) |
看看能不能画出该模块的端口符号图和电路图。
阻塞赋值和非阻塞赋值
阻塞赋值的实质:右边表达式的计算和对左边寄存器变量的赋值是一个统一的原子操作中的两个动作,这两个动作之间不能再插入其他的任何动作。
用”=”来进行赋值非阻塞赋值的实质:首先按顺序计算右边表达式,但是并不马上赋值,而是要等到过程结束时再按顺序赋值
应用:
- 设计组合电路时常使用阻塞赋值;
- 设计时序电路时常用非阻塞赋值;
- 但不是绝对;
- 不建议在一个always块中混合使用阻塞赋值和非阻塞赋值
例:阻塞赋值实现的组合电路
1 | module MY(A,B,C,Y) |
例:非阻塞赋值实现的移位寄存器
1 | module DFF3(CLK,D,Q) |
综合举例:4位二进制加法计数器
此程序中有always和assign两条语句,他们之间是并行的;此程序中有一个内部变量Q1,使用前要进行声明。
内部信号声明格式:
数据类型 位宽 信号名称 元素个数
1 | module CNT4(CLK,Q); |
底层模块和门原语调用
底层模块调用
- 底层模块的描述
1 | module DFF(CLK,D,Q) |
- 顶层模块描述
为了调用底层模块,需要加两个内部变量d1和q1;并给两次调用的模块进行命名;调用时例化名不能省略。
- 底层模块的调用格式: 底层模块名 例化名 (端口映射);
1 | module examp(clk,d,a,q) |
端口映射有两种办法:
端口名关联法 (命名法)
位置关联法 (顺序法)
命名法格式:
1 | (.底层端口名1(外接信号名1),.底层端口名2(外接信号名2),...) |
顺序法格式(必须严格按照底层模块的端口信号列表顺序书写):
1 | (外接信号名1,外接信号名2,...) |
门原语调用(用得很少,如需要自行了解)
Verilog语言提供已经设计好的门,称为门原语(primitive,共12个),这些门可以直接调用,不用再对其功能进行描述。
门原语调用格式:门原语名 实例名 (端口连接)
例如
1 | and (out,in1,in2) |
Verilog中的数据类型
Verilog中的数据类型分为两大类:线网类(net类),变量类(veriable)
由于连续赋值语句和过程赋值语句的激活特点不同,故赋值目标特点也不同,前者不需要保存,而后者则需要保存,因此规定两种数据类型,net型用于连续赋值的赋值目标或门原语的输出,且仿真时不需要分配内存空间,variable用于过程赋值的赋值目标,且仿真时需要分配内存空间。
net中最常用的wire类型,而variable最常用的是reg类型。
何时使用net和veriable
net类型(如 wire):当你需要表示电路中纯粹的信号传递,并且不需要存储该信号的状态。例如,模块间的输入输出连接、门级原语的输出、使用 assign 的连续赋值。
variable类型(如 reg):当你需要在时序电路中存储信号状态(如寄存器、触发器),或者在 always 块中赋值时,通常使用 reg 或其他 variable 类型,因为它需要记住上一次的状态。
总结来说,net 类型用于信号传播,而 variable 类型用于保存信号状态。在硬件设计中,当你描述电路逻辑如何连接时用 net,而当你需要描述状态变化(例如触发器、寄存器的行为)时用variable。
Verilog中数字的表示格式
无符号数的表示方法:
1 | <位宽>' <进制><数字> |
有符号数的表示方法:
1 | <位宽>' <sb><数字> |
注意有符号数是按照补码表示的,即第一位是符号位
逻辑值
Verilog语言中的逻辑值有四种
- 1:逻辑1,高电平,数字1
- 0:逻辑0,低电平,数字0
- x: 不确定
- z: 高阻态
if语句
这个和python语言类似,看看例子。
实现一个计数器
1 | always @(posedge CLK) |
- 在用if语句设计“组合电路”时要注意,如果条件不完整,就会综合出寄存器。(也就是会出现不确定的状态)
解决方法
1.加else
1 | always @(a,b) |
2.设初值
1 | always @(a,b) |
case语句
1 | case(表达式) |
四选一选择器:
1 | module MUX41 (a,b,c,d,s1,s0,y); |
-------------本文结束感谢您的阅读-------------