Verilog语言- 和c语言的区别

本文详细介绍了Verilog的基础知识,包括数值种类、整数表示、数据类型、算术操作符、时延控制、编译指令、过程结构、赋值方式、时序控制、语句块、循环语句、过程连续赋值、模块与端口例化、函数和任务。重点阐述了Verilog中的wire与reg类型、非阻塞赋值的重要性、timescale的使用以及模块例化和参数传递。此外,还对比了Verilog与C语言在语法上的差异,如字符串处理、预处理指令、条件语句等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、Verilog基础

1.1 特别的数值种类

x 或 X:未知
z 或 Z:高阻

(Z 常见于信号(input, reg)没有驱动时的逻辑结果。例如一个 pad 的 input 呈现高阻状态时,其逻辑值和上下拉的状态有关系。上拉则逻辑值为 1,下拉则为 0 。)

1.2、需要注意的整数数值表示方法

减号放在基数和数字之间是非法的,例如下面的表示方法是错误的:

4’d-2 //非法说明

1.3、强调Verilog 数据类型

2 种数据类型就是线网(wire)与寄存器(reg)

always块里赋值对象不能是wire型

1.4、算术操作符

无符号数乘法时,结果变量位宽应该为 2 个操作数位宽之和。

逻辑相等/不等操作符不能比较 x 或 z,当操作数包含一个 x 或 z,则结果为不确定值

全等比较时,如果按位比较有相同的 x 或 z,返回结果也可以为 1,即全等比较可比较 x 或 z。所以,全等比较的结果一定不包含 x。

1.5、Verilog 时延

在 Verilog 模型中,时延有具体的单位时间表述,并用 `timescale 编译指令将时间单位与实际时间相关联。

`timescale      time_unit / time_precision

time_unit 表示时间单位,time_precision 表示时间精度,它们均是由数字以及单位 s(秒),ms(毫秒),us(微妙),ns(纳秒),ps(皮秒)和 fs(飞秒)组成。时间精度可以和时间单位一样,但是时间精度大小不能超过时间单位大小。

由于在 Verilog 中没有默认的 timescale,如果没有指定 timescale,Verilog 模块就有会继承前面编译模块的 `timescale 参数。有可能导致设计出错。

如果一个设计中的多个模块都带有 `timescale 时,模拟器总是定位在所有模块的最小时延精度上,并且所有时延都相应地换算为最小时延精度,时延单位并不受影响。

`timescale 的时间精度设置是会影响仿真时间的。时间精度越小,仿真时占用内存越多,实际使用的仿真时间就越长。所以如果没有必要,应尽量将时间精度设置的大一些。

1.6、编译指令

`default_nettype none,由于编译指令的存在,变量没定义就使用,系统会报Error,从而检查出书写错误

1.7、过程结构

assign 为关键词,任何已经声明 wire 变量的连续赋值语句都是以 assign 开头

一个模块中可以包含多个 initial 和 always 语句,但 2 种语句不能嵌套使用

这些语句在模块间并行执行,与其在模块的前后顺序没有关系。

但是 initial 语句或 always 语句内部可以理解为是顺序执行的(非阻塞赋值除外)

每个 initial 语句或 always 语句都会产生一个独立的控制流,执行时间都是从 0 时刻开始initial 语句从 0 时刻开始执行,只执行一次,多个 initial 块之间是相互独立的。

如果 initial 块内包含多个语句,需要使用关键字 begin 和 end 组成一个块语句

如果 initial 块内只有一条语句,关键字 begin 和 end 可使用也可不使用。

1.8、过程赋值

连续性赋值总是处于激活状态,任何操作数的改变都会影响表达式的结果过程赋值只有在语句执行的时候,才会起作用。这是连续性赋值与过程性赋值的区别。

Verilog 过程赋值包括 2 种语句:阻塞赋值与非阻塞赋值。阻塞赋值语句使用等号 = 作为赋值符非阻塞赋值语句使用小于等于号 <= 作为赋值符

非阻塞赋值属于并行执行语句,即下一条语句的执行和当前语句的执行是同时进行的,它不会阻塞位于同一个语句块中后面语句的执行。

实际 Verilog 代码设计时,切记**不要在一个过程结构中混合使用阻塞赋值与非阻塞赋值。**两种赋值方式混用时,时序不容易控制,很容易得到意外的结果。

在设计电路时,always 时序逻辑块中多用非阻塞赋值,always 组合逻辑块中多用阻塞赋值;在仿真电路时,initial 块中一般多用阻塞赋值。

1.9、时序控制

遇到常规延时时,需要等待一定时间,然后将计算结果赋值给目标信号。

遇到内嵌延时时,先将计算结果保存,然后等待一定的时间后赋值给目标信号。修饰常数时,延时效果一样

事件是指某一个 reg 或 wire 型变量发生了值的变化。事件控制用符号 @ 表示语句执行的条件是信号的值发生特定的变化

关键字 posedge 指信号发生边沿正向跳变(上升沿),negedge 指信号发生负向边沿跳变(下降沿),未指明跳变方向时,则 2 种情况的边沿变化都会触发相关事件。

任意一个发生变化都能够触发语句的执行时,Verilog 中使用"或"表达式来描述这种情况,用关键字 or 连接多个事件或信号。这些事件或信号组成的列表称为"敏感列表"。当然,or 也可以用逗号 , 来代替。更为简洁的写法是 @* 或 @(*),表示对语句块中的所有输入变量的变化都是敏感的。

Verilog 中还支持使用电平作为敏感信号来控制时序,即后面语句的执行需要等待某个条件为真。Verilog 中使用关键字 wait 来表示这种电平敏感情况

1.10、语句块

顺序块用关键字 begin 和 end 来表示。顺序块中每条语句的时延总是与其前面语句执行的时间相关。

并行块有关键字 fork 和 join 来表示

disable 可以终止命名块的执行,可以用来从循环中退出、处理错误等。

与 C 语言中 break 类似,但是 break 只能退出当前所在循环,而 disable 可以禁用设计中任何一个命名的块

需要说明的是,disable 在 always 或 forever 块中使用时只能退出当前回合,下一次语句还是会在 always 或 forever 中执行。因为 always 块和 forever 块是一直执行的,此时的 disable 有点类似 C 语言中的 continue 功能

1.11、循环语句

repeat 的功能是执行固定次数的循环,它不能像 while 循环那样用一个逻辑表达式来确定循环是否继续执行。repeat 循环的次数必须是一个常量、变量或信号。

如果循环次数是变量信号,则循环次数是开始执行 repeat 循环时变量信号的值。即便执行期间,循环次数代表的变量信号值发生了变化,repeat 执行次数也不会改变。

forever 语句表示永久循环,不包含任何条件表达式,一旦执行便无限的执行下去,系统函数 $finish 可退出 forever

1.12、过程连续赋值

与过程赋值不同的是,过程连续赋值的表达式能被连续的驱动到 wire 或 reg 型变量中,即过程连续赋值发生作用时,右端表达式中任意操作数的变化都会引起过程连续赋值语句的重新执行。

assign(过程赋值操作)与 deassign (取消过程赋值操作)表示第一类过程连续赋值语句。赋值对象只能是寄存器或寄存器组,而不能是 wire 型变量。

force (强制赋值操作)与 release(取消强制赋值)表示第二类过程连续赋值语句。但赋值对象可以是 reg 型变量,也可以是 wire 型变量。因为是无条件强制赋值,一般多用于交互式调试过程,不要在设计模块中使用。

1.13、模块与端口

结构建模方式有 3 类描述语句: Gate(门级)例化语句,UDP (用户定义原语)例化语句和 module (模块) 例化语句。

变量具体声明的位置不要求,但必须保证在使用之前的位置。

端口是模块与外界交互的接口。对于外部环境来说,模块内部是不可见的,对模块的调用只能通过端口连接进行。

模块的定义中包含一个可选的端口列表,一般将不带类型、不带位宽的信号变量罗列在模块声明里。

一个模块如果和外部环境没有交互,则可以不用声明端口列表。例如之前我们仿真时 test.sv 文件中的 test 模块都没有声明具体端口。

在这里插入图片描述端口类型有 3 种: 输入(input),输出(output)和双向端口(inout)。

input、inout 类型不能声明为 reg 数据类型,因为 reg 类型是用于保存数值的,而输入端口只能反映与其相连的外部信号的变化,不能保存这些信号的值

output 可以声明为 wire 或 reg 数据类型。

端口隐式的声明为 wire 型变量,即当端口具有 wire 属性时,不用再次声明端口类型为 wire 型。reg 型端口要么在 module 声明时声明,要么在 module 实体中声明。

当端口有 reg 属性时,则 reg 声明不可省略。(设计代码中并没有声明为reg类型,所以test bench中使用reg是会报错的)

需要例化的模块端口与外部信号按照其名字进行连接,端口顺序随意,可以与引用 module 的声明端口顺序不一致,只要保证端口名字与外部信号匹配即可

如果某些输出端口并不需要在外部连接,例化时 可以悬空不连接,甚至删除。一般来说,input 端口在例化时不能删除,否则编译报错,output 端口在例化时可以删除。

1.14、模块例化

建议采用命名端口方式对模块进行例化。

建议 input 端口不要做悬空处理,无其他外部连接时赋值其常量

当例化端口与连续信号位宽不匹配时,端口会通过无符号数的右对齐或截断方式进行匹配。

信号名字可以与端口名字一样,但他们的意义是不一样的,分别代表的是 2 个模块内的信号。

generate 语句进行多个模块的重复例化,可大大简化程序的编写过程。

Verilog 中,通过使用一连串的 . 符号对各个模块的标识符进行层次分隔连接,就可以在任何地方通过指定完整的层次名对整个设计中的标识符进行访问。

层次访问多见于仿真中。Verilog 中,通过使用一连串的 . 符号对各个模块的标识符进行层次分隔连接,就可以在任何地方通过指定完整的层次名对整个设计中的标识符进行访问。

当一个模块被另一个模块引用例化时,高层模块可以对低层模块的参数值进行改写。这样就允许在编译时将不同的参数传递给多个相同名字的模块,而不用单独为只有参数不同的多个模块再新建文件。

1.15、函数

在 Verilog 中,可以利用任务(关键字为 task)或函数(关键字为 function),将重复性的行为级设计进行提取,并在多个地方调用,来避免重复代码的多次编写,使代码更加的简洁、易懂。

函数在声明时,会隐式的声明一个宽度为 range、 名字为 function_id 的寄存器变量,函数的返回值通过这个变量进行传递

函数主要有以下几个特点:
1)不含有任何延迟、时序或时序控制逻辑
2)至少有一个输入变量
3)只有一个返回值,且没有输出
4)不含有非阻塞赋值语句
5)函数可以调用其他函数,但是不能调用任务

Verilog 用关键字 automatic 来对函数进行说明,此类函数在调用时是可以自动分配新的内存空间的,也可以理解为是可递归的。因此,automatic 函数中声明的局部变量不能通过层次命名进行访问,但是 automatic 函数本身可以通过层次名进行调用。

1.16、任务

函数一般用于组合逻辑的各种转换和计算,而任务更像一个过程,不仅能完成函数的功能,还可以包含时序控制逻辑

任务没有返回值

进行任务的逻辑设计时,可以把 input 声明的端口变量看做 wire 型,把 output 声明的端口变量看做 reg 型。但是不需要用 reg 对 output 端口再次说明。

2、Verilog语言和c语言区别

(1)、c语言可以包含回车符 ‘\r’。Verilog字符串不能多行书写,即字符串中不能包含回车符。Verilog 将字符串当做一系列的单字节 ASCII 字符队列。

(2)、c语言用 # 符号标识预处理、头文件等。Verilog语言 ` 来标识

`define   S    2
`include         "../../param.v"
`include         "header.v"

(3)、c语言是 #elif。Verilog是 `elsif,中间多了个s

(4)、c语言的多个条件执行相同语句时,需要将每个条件用 && 连接。Verilog的条件语句,多个条件选项可以用逗号分开。

(5)、c语言是 switch(条件),case 条件:的形式。Verilog是case(条件),条件:的形式。且还有case 语句的变形casex、 casez,用来表示条件选项中的无关项。casex 用 “x” 来表示无关值,casez 用问号 “?” 来表示无关值
但是 casex、casez 一般是不可综合的,多用于仿真。

(6)、Verilog的i = i + 1 不能像 C 语言那样写成 i++ 的形式,i = i -1 也不能写成 i – 的形式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值