目录
数据类型
一、内建数据类型
SV中相比于Verilog新引入了一个数据类型logic,是因为Verilog作为硬件描述语言,倾向于设计人员自身懂得所描述的电路中哪些变量应该被实现为寄存器,而哪些变量应该被实现为线网类型。这不但有利于后端综合工具,也更便于阅读和理解;;
SV作为侧重于验证的语言,并不是十分关心logic对应的逻辑应该被综合为寄存器还是线网,因为logic被使用的场景如果是验证环境,那么ta只会作为单纯的变量进行赋值操作,且这些变量也属于软件环境构建。logic被推出的另外一个原因也是为了方便验证人员驱动和连接硬件模块、而省去考虑究竟该使用reg还是wire的精力。这既节省了时间,也避免了出错的可能。
除了多个驱动,logic任意用。若存在多个驱动,会编译报错。
至于有logic还要bit是因为在硬件里logic更多,在软件里bit更多。(下面第二张图也加以说明)
注意:
不同数据类型进行操作时应该注意变量的:逻辑数据类型、符号类型、矢量位宽。在变量的运算中,尽可能避免两种不一致的变量进行操作。
1.对于有符号类型,例如:byte变量的最大值是127,而不是255(它的范围是-128–127)。
2.对于逻辑变量类型,四值逻辑类型默认值为X,二值逻辑类型的默认值为0。
二、定宽数组
存储空间考量
数组里面的索引
有for和foreach两种,但是我们最长用的是foreach。
这里foreach就会自动给你把j从低到高排列,非常方便。
在foreach循环中只需要指定数组名并在其后方括号中给出索引变量,SV会自动遍历数组中的元素。索引变量将会自动声明,并只在循环内收敛。
数组的复制和比较
我们可以利用赋值符号“=”直接进行数组的复制。
比较:在不适合循环的情况下,也可以利用“==”or“!=”来比较数组的内容,不过结果仅限于内容相同或者不同!!!
三、动态数组
- 定宽数组的宽度在编译时就确定了,相比之下,动态数组就灵活的多,在程序运行时再确定数组的宽度。
- 特点:在仿真运行时灵活调节数组的大小(存储量)
- 在一开始声明时,需要“[ ]”来声明,此时为空的 (0容量);;其后需要使用“new[ ]”来分配空间,括号中传递数组的宽度!!!
- 可以在调用new[ ] 时将数组名一起传递,将已有数组的值复制到新数组中。。。
四、队列
五、关联数组
如果偶尔创建一个大容量数组,动态数组就可以了!但如果需要一个超大容量的呢?动态数组的限制在于其存储空间在一开始就被固定,对于超大数组这无疑是一种浪费,因为很多可能该数组又相当多的数据不会被存储和访问。
关联数组可以用来保存稀疏矩阵的元素。当你对一个非常大的地址空间寻址时,,该数组只为实际写入的元素分配空间,这种实现方法所需要的空间比定宽或动态数组所占用的空间要小得多。
六、结构体
- 在SV中,可以使用struct语句创建结果。
- 不过struct的功能少,它只是一个数据的集合,其通常的使用的方式是将若干相关的变量组合到一个struct结构定义中。
- 伴随typedef可以用来创建新的类型,并利用新类型来声明更多变量
七、枚举类型
上面图片中红色框住的意思shi:
枚举类型赋值给int可以直接赋值
Int不能直接赋值给enum 需要先把int转化成enum。。。
八、字符串
- 所有与字符串相关的处理,都请使用string来保存和处理
- 与字符串处理相关的还包括字符串的格式化函数即如何形成一个你想要的字符串句子呢?可以使用SV系统方法sformatf (),,,如果只需要将它打打印输出,那么就使用sdisplay()
过程块和方法
一、硬件过程块
在SV中同学们首先需要清楚哪些语句应该被放置于硬件世界,哪些程序应该被放置于软件世界。
怎么来区分硬件世界和软件世界呢? 我们先引申出一个概念域(scope)。
为了区分硬件设计、软件世界,,我们将定义的软件变量或者例化的硬件其所在的空间称之为域。
因此,module/endmodule,interface/endinterface可以被视为硬件世界!!!
program/endprogram和class/endclass可以被视为软件世界!!!
掌握了这一清晰的概念,有助于我们接下来分析initial和always的使用域。
always
- always是为了描述硬件的行为
- always中的@(event..)敏感列表是为了模拟硬件信号的触发行为,同学们需要正确对标硬件行为和always过程块描述
- alwavs过程块是用来描述硬件时序电路和组合电路的正确打开方式,因此只可以在module或者interface中使用。
一个提问:可以在always里面初始化变量吗?
(在always时序逻辑里,只能说做相应的复位;初始化变量是软件的概念 ,,在initial里面or定义变量里面进行初始化)
initial
- initial非常符合软件的执行方式,只执行一次。
- initial和always一样,无法被延迟执行,即在仿真一开始它们都会同时执行,而不同initial和always之询在执行顺序上是没有顺序可言的
- initial从其执行路径的属性来看,它不应该存在于硬件设计代码中,它本身不可综合,对于描述电路没有任何帮助
- initial就是为了测试而生的由于测试需要按照时间顺序的习惯即软件方式来完成,所以initial便可以实现这一要求。
- 在Verilog时代,所有的测试语句都可以被放置在initial中,为了便于统一管理测试顺序,建议将有关测试语句都放置在同一个initial过程块中
- initial过程块可以在module、ihterface和progfam中使用
- 对于过程块的书写方式,请记住用begin..end将其作用域“包"住
二、软件方法
函数function
- 可以在参数列表中指定输入参数 (input)、输出参数(output)、输入输出参数(inout)、引用参数(ref)
- 可以返回数值或者不返回数值 (void)
- 默认的数据类型是为logic,例如 input [7:0] addr
- 数组可以作为形式参数传递
- function可以返回或者不返回结果,如果返回即需要用关键词return如果不返回则应该在声明funetion时采用void function()
- 只有数据变量可以在形式参数列表中被声明为ref类型而线网类型则不能被声明为ref类型。
- 在使用ref时,有时候为了保护数据对象只被读取不被写入,可以通过const的方式来限定ref声明的参数。
- 在声明参数时,可以给入默认数值,例如 input [7:0] addr = 0,同时在调用时如果省略该参数的传递,那么默认值即会被传递给function.
任务task
注意:在SV函数里面,如果不标明方向所有的参数默认方向都是input!!!
变量的生命周期
- 声明周期可分为动态(automatic)和静态(static)!!!
- 局部变量的生命周期同其所在域共存亡,例如function/task中的临时变量在其方法调用结束后,临时变量的生命也将终结,所以它们是动态生命周期
- 全局变量即伴随着程序执行开始到结束一真存在,例如module中的变量默认情况下全部为全局变量,用户也可理解为module中的变量由于在模拟硬件信号,所以它们是静态生命周期。
- 如果数据变量被声明为automatic,那么在进入该进程/方法后automatic变量会被创建,而在离开该进程/方法后automatic变量会被销毁
- 而static变量在仿真开始时即会被创建,而在进程/方法执行过程中,自身不会被销毁,且可以被多个进程和方法所共享
注意:
- 对于static方法,其内部的所有变量默认也是static类型!!!
- 对于automatic或者static方法,用户可以对其内部定义的变量做单个声明,使其类型被显式声明为automatic或者static!!!
- 对于static变量,用户在声明变量时应该同时对其做初始化,而初始化只会伴随它的生命周期发生一次并不会随着方法调用被多次初始化!!
- 在module、program和interface中定义的task、function默认都是static类型
- 对于上述程序块默认的生命周期类型为static