Verilog 基础知识- Verilog 基础语法与注意事项

基础知识

1  模块(Module)

     Verilog中的module可以看成一个具有输入输出端口的黑盒子,该黑盒子有输入和输出接口(信号),通过把输入在盒子中执行某些操作来实现某项功能。(类似于C语言中的函数)

图片

图1  模块示意图

1.1 模块描述

   图1 所示的顶层模块(top_module)结构用Verilog语言可描述为:

图片

 🔹 模块以module 开始,endmodule结束

 🔹 top_module 为模块名

 🔹 input : 为输入端口

 🔹 output:  为输出端口

 🔹 所有代码必须处于module模块中!

    同理,图1 所示的次级模块(mod_a)结构用Verilog语言可描述为:

图片

    注意事项:每个模应单独块处于一个.v文件中,模块名即为文件名(规范代码!)

1.2  模块输入输出信号

  🔹  输出:output

  🔹  输入:input

    模块的输入输出端口都可看出模块的信号,若不写信号类型则默认为wire类型信号!

图片

 除了wire型信号,还有reg型信号,具体详见1.4节!

1.3 模块实例化

如图1所示,top_module的两个输入端口连接到次级模块(mod_a)的输入端口,那如何在top_module模块模块中使用mod_a模块的功能呢?这就需要通过模块实例化,可以把top_module看成C语言中的主函数,次级模块mod_a看成普通函数,这样就可以在主函数中调用其他函数来完成相应的功能!

 在top_module中实例化mod_a的方式为:

 模块实例化语法:模块名 实例名(定义连接port的信号);

图片

 🔹 按mod_a定义的端口顺序实例化: mod_a instance1 (a, b, out);

 🔹 按mod_a端口名实例化: mod_a instance2 (.in1(a), .in2(b), .out(out));   (推荐此种写法)

2  逻辑块(always、generate)

2.1 always逻辑块

      always块可构建 组合逻辑块 和 时序逻辑块,复杂的逻辑操作都需要处于该逻辑块中,如if、case、for等

(1) 组合逻辑块

图片

 🔹 always逻辑块中任意信号变化时立即触发,执行begin - end之间的语句

 🔹 begin - end用于将多条语句组成一个代码块,只有一条语句时可省略

(1) 时序逻辑电路

图片

 🔹  clk 信号的上升沿触发

 🔹  posedge:  上升沿

 🔹  negedge: 下降沿

2.2 generate逻辑块

 generate主要结合for循环使用,主要用途有:

 🔹 对向量中的多个位进行重复操作

 🔹 对同一个模块进行多次重复实例化(主要用途)

(1) 操作向量

图片

(2) 模块重复多次实例化

图片

  🔹  注意:模块多次实例化时必须写每个begin_end结构的名称(gen_mod_a)

 🔹 仿真器会通过gen_mod_a来标识生成结构: gen_mod_a[0],gen_mod_a[1]....

2.3 initial块 

      initial块可以理解为一个初始化块,在initial的起始位置的语句在0时刻即开始执行,之后如果遇到延时,则延时之后执行接下来的语句

     初始块是不可综合的,因此不能将其转化为带有数字元素的硬件原理图。因此初始块除了在仿真中使用外,并没有太大的作用

如:在仿真文件中初始化各种参数:

图片

 注意:

  🔹  initial 块在电路中不可综合,故一般不出现在RTL代码中

  🔹  initial 一般只在仿真文件中使用

 若需要在RTL代码中初始化参数,需要用always块,用initial块会导致错误!

 如下所示,在RTL代码中初始化存储器的方式为:

图片

3  赋值方式

 Verilog 中赋值方式有三种:连续赋值、阻塞赋值、非阻塞赋值

3.1 连续赋值(assign)

图片

 🔹 该语句表示把x和y两个信号进行连接,真实的物理连接!

 🔹 不能在always块中使用

3.2 阻塞赋值(=)

图片

 🔹  组合always块中用阻塞式赋值

 🔹  执行顺序:按照begin_end语句块中的顺序依次执行,上述输出结果为:out1 = a ,out2 = b

3.3 非阻塞赋值(<=)

图片

🔹 时序always块中用非阻塞赋值

🔹 执行顺序:begin_end中所有语句并行执行,上述输出结果为:out1 = a ,out2 = a

基础语法

1.1 标识符

 (1) 用途:标识符用于定义常数、变量、信号、端口、参数名、模块名等。

 (2) 组成:字母、数字、$、下划线任意组合而成

 (3) 注意事项:

🔹 区分大小写(Verilog 和 verilog是不同的)

🔹 第一个字符只能是字母或下划线(123demo 是非法标识符)

1.2 逻辑值与逻辑运算        

1.2.1 逻辑值

 Verilog中有4中逻辑值:0、1、x、z

🔹  0:  低电平

🔹  1:高电平

🔹  x:  表示状态未知

🔹  z:表示高阻状态

 注意:这里的z、x是不区分大小写的(X、Z也可)

1.2.2 逻辑运算

 (1) 逻辑运算符:&&(与)、==(相等)、||(或)、!=(不等)

🔹 如 m&&n  : 判断m和n是否全为真(非0即为真),真则输出1'b1,否则输出1'b0 (4’b1010&4’b0101 = 1’b1)

🔹 最后输出结果只有1bit

 (2) 按位运算符:&、|、~、^、~&、~^、~| 

🔹 如 m&n  : 是把m的每一位与n的每一位按位做与运算 (4’b1010&4’b0101 = 4’b0000)

🔹 输出结果与m/n的bit数相同

 (3) 归约运算符:  &、|、~、^、&、~^、~| 

🔹 只有一个参量参与运算时( &为一元运算符),表示规约与,即向量内部进行与运算

图片

🔹 即(&4’b0101 = 0&1&0&1 = 1'b0 )

🔹 最后输出结果只有1bit

1.3 常量的表示方法

与C语言类似,常量主要有:整数型、实数型和字符串型三种

1.3.1 用十进制整数表示整型常量

(1) 正数:直接写 10 表示位宽为32bit的十进制整数(系统默认)

(2) 负数:-10需要用二进制补码表示,多了一位符号位(1 1010)

(3) 用科学计数法表示:12.345e3   表示 12345

1.3.2 用基数法表示整数型常量

图片

 (1)  二进制(b):     8'b1000_1100      

 (2)  十六进制(h):  8'h8c

 (3)  八进制(o):      8'o214

 (4)  十进制(d):      8'140

  注意事项:

🔹 当表示二进制时,最好每4位写一个下划线以增强可读性:如8'b1000_1100   与8'b10001100 是一样的

🔹 基数表示法中遇到x时:十六进制表示4个x,八进制中表示3个x  

🔹 当位宽大于二进制位数时左边自动补0,小于二进制数时2从左边截断!

1.3.3 字符串(用双引号)

(1) 每个字符由1个8位的ASCII码值表示,即需要1byte存储空间

(2) 如:“Hello world”   字符串由11个ASCII符号构成,需要11byte存储空间

1.4  注释方式

 Verilog中注释主要有行注释(//)和块注释(/*  ....     */)两种,表示方法与C语言一致!

图片

1.5 变量(wire、reg)

 Verilog中的变量主要有两种:wire和reg

1.5.1 wire

(1) 线网型(wire): 表示电路间的物理连接,wire定义的变量也可看成信号端口

(2) 当两个wire信号被连续赋值时,在逻辑块中会被映射成真实的物理连线,此时这两个信号端口的变化是同步的!

图片

1.5.2 reg

(1) 寄存器型(reg): 表示一个抽象的数据存储单元

(2) reg 具有对某一时间点状态进行保持的功能

1.5.3 用法与注意事项

(1) 在always、initial语句中被赋值的变量(赋值号左边的变量)都是reg型变量

(2) 在assign语句中被赋值的变量,为wire型变量

1.6 向量(vector)与 参数(常量)

1.6.1 parameter 参数(常量)

(1) 参数是一种常量,通常出现在module内部,常被用于定义状态、数据位宽等

图片

(2) 只作用于声明的那个文件,且可以被灵活改变

(3) 局部参数localparam,只在本模块中使用

图片

(4) 参数的名称一般为大写,以区分其他变量 

1.6.2 向量(vector)

vector(向量),是一组信号的集合,可视为位宽超过1bit  的 wire 信号。

(1) 定义方式:

图片

 🔹 [upper:lower] 定义位宽,如 [7:0] 表示位宽为8 bit ,即upper=7,lower=0

 🔹 vector_name可以一次写多个向量

1.6.3 向量片选

 🔹 a[3:0]   取向量a的0~4位数据

 🔹 b[n]      取向量b的第n位数据

 🔹 c[-1:-2]  取向量c的最低2位数据

 🔹 c[0:3]     取向量c的最高4位数据

   多路选择器应用:实现一个 256 选 1 选择器,sel 信号作为选择信号,当 sel = 0 时选择 in[3:0],sel = 1 时选择 in[7:4],以此类推。

图片

 🔹 片选信号sel输入为n位二进制数,当参与运算、充当索引时会自动转换成十进制数

 🔹 该题所选取的信号片段为: in[sel*4+3: sel*4] ,但这不符合Verilog的片选语法规则故应写成:

in[sel*4 +: 4]   表示索引从sel*4开始的高4bit信号

in[sel*4+3 -: 4] 表示索引从sel*4+3开始的低4bit信号

 🔹 或是直接选出需要的每一位,再用{ }拼接成新向量:

{in[sel*4+3], in[sel*4+2], in[sel*4+1], in[sel*4+0]}

1.7 三元表达式

(1) 与C语言相同,Verilog也有三元表达式:

图片

当条件为真,表达式值为if_true ,否则表达式值为if_false。

(2) 应用

图片

1.8 分支语句(if-else、case)

1.8.1 if-else语句

(1) 最常用的形式:(优势:输出的所有可能都写到,不存在未知电平输出!)

图片

(2)  不建议使用if-else嵌套,会存在优先级问题,导致逻辑混乱,

(3)  所有if-else语句都应写成(1)的形式

(4) 根据条件表达式依次比较,存在优先级

1.8.2 case 语句

(1) 书写形式:

图片

比较<控制表达式>与<分支语句n>的取值相等则执行对应语句,否则执行default后语句!

(2) 执行完某一分支语句后立即跳出case语句结构,终止case语句执行

(3) <分支语句n>的取值必须互不相同

(4) 以encase结束case语句块

(5) 各分支语句间不存在优先级

(6) 具体应用: 用case语句搭建多路选择器,(以9选1多路选择器为例)

图片

1.9 for循环语句

(1) 书写形式:

图片

🔹 执行<循环语句>n次

🔹 for_name为每一次循环的名称

2   关系运算符(>、<、>=、<=)

🔹 运算结果为真返回 1

🔹 运算结果为假返回 0

🔹 若某个操作数值不定(x),则返回值为 x

2.1  拼接运算符({ , })

2.1.1 拼接

用一对花括号加逗号组成“{ , }”拼接运算符,逗号隔开的数据按顺序拼接成新数据

图片

2.1.2 通过拼接实现移位

在左边拼接实现右移,右边拼接实现左移!

图片

2.1.3 连接符中重复多次的操作

语法: {重复次数{vector}}

图片

2.2  移位运算符

   移位运算符用于将左边操作数左移或右移指定的位数!移位后空闲位用0填充。

🔹 左移运算符:<<

如:4‘b1101 << 3 结果为:4‘b1000

🔹 右移算法符:   >>

如:4‘b1101 >> 3 结果为:4‘b0001

🔹 移位运算符其他用途:左移一位可以看成是乘以 2,右移一位可以看成是除以 2

🔹 移位运算符代替乘除法可以节省资源!

3   二进制全加器

🔹 a、b为输入 1bit 数据

🔹 cin为上一个加法器进位输入

🔹 cout为本加法器的进位输出

🔹 sum = a+b

图片

 代码实现:

图片

4  16进制全加器

图片

 16进制全加器如上图所示,它可由上节中16个二进制全加器组合而成。

 用Verilog实现16进制全加器代码为:

图片

5  模块中的参数传递

5.1 定义可传递参数的模块

图片

5.2 带参数模块的实例化

图片

图片

  • 15
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值