前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >读一本书 | systemverilog之内建数据类型(下)

读一本书 | systemverilog之内建数据类型(下)

作者头像
根究FPGA
发布2020-10-10 14:33:19
1.9K0
发布2020-10-10 14:33:19
举报
文章被收录于专栏:根究FPGA根究FPGA

数据类型

01

内建数据类型

Logic的引入背景

相比于verilog仍和net区分的如此清楚,在sv中新引入了一个数据类型logic,他们的区别和联系在于:

  1. verilog作为硬件描述语言,倾向于设计人员自身懂得所描述的电路中那些变量应该被视为寄存器,而那些变量被视为线网(wire),这不但有利于后端综合工具综合,也便于阅读和理解。
  2. sv(verilog3.0)作为侧重于验证的语言,并不十分关心logic对应的逻辑应该被综合为寄存器还是线网,因为logic被使用的环境是验证环境,logic只会作为单纯的变量进行赋值操作,而这些变量也只属于软件环境构建。
  3. logic被推出的另外一个原因也是为了方便验证人员驱动和连接硬件模块,而省去考虑究竟该使用reg还是wire的精力、节省时间的同时也避免了出错的可能。

bit

与logic对应的是bit类型,他们均可建立矢量类型(vector),区别在于:

  1. logic为四值逻辑,0、1、x(不确定)、z(高阻态,不受驱动)
  2. bit为二值逻辑,只可为0、1

要点

  1. sv为何在四值逻辑的基础上还要引入二值逻辑? 因为sv在一开始被设计的时候,就期望将硬件的世界与软件的世界分离开,硬件的世界指的就是硬件设计,所以四值逻辑属于硬件逻辑,而软件的世界即验证环境,更多的是二值逻辑。
  2. 常见的四值逻辑与二值逻辑 四值逻辑: integer、logic、reg、net-type(例如wire、tri) 二值逻辑: bit、byte、shortint、int、longint (注意区分interger和int、integer表示32位的四值逻辑,名字越长表示四值逻辑,而且integer的初始化值为:x)
  3. 常见的有符号和无符号类型 有符号: byte、shortint、int、longint、integer 无符号: bit、logic、reg、net-type

exp

代码语言:javascript
复制
logic [7:0]  logic_vec=8’b1000_0000;
logic [7:0]  logic_vec=8'b1000_0000;
bit  [7:0]  bit_vec=8'b1000_0000;
byte signed_vec=8'b1000_0000;
initial begin
$display("logic_vec=%d",logic_vec);
$display(  "bit_vec=%d",bit_vec);
$display("signed_vec=%d",signed_vec);
$stop();
end

猜一下运行结果是多少?

在遇到这些变量类型时,应注意他们的逻辑类型和符号类型,因为在变量运算中,应该尽量避免两种不一致的变量的进行操作,进而导致意外的错误!

exp

代码语言:javascript
复制
byte signed_vec=8'b1000_0000;  //有符号的8位数
bit [8:0]result_vec;            //无符号的9位数
  
initial begin   
result_vec = signed_vec;  
 #10;  
$display("@1 result_vec= 'h%x",result_vec);  
 #20;  
result_vec=unsigned'(signed_vec);    //转为无符号数
$display("@2 result_vec= 'h%x",result_vec);  
$stop();  
end  

运行结果为:

有符号数signed_vec转为无符号数,首先是用自己的符号位拓宽一位,变为:9’b1_1000_0000。

在编码时,一定要注意操作符左右两侧的符号类型保持一致,如果不一致,首先将其转换为同一类型再进行运算。

对于转换方式,分为静态转换和动态转换,静态转换即在需要转换的表达式前加上单引号“`”即可,仅在编译时完成检查,该方式并不会对转换值做检查,如果转换失败也无从得知。动态转换$cast(tgt,src)在仿真转换过程中检查。

静态转换和动态转换均需要操作符号或系统函数介入,统称为显式转换(expilicit transfer)。

而不需要进行转换的一些操作,称为隐式转换(implicit transfer)。

exp

代码语言:javascript
复制
logic [3:0] x_vec=4'b111x;  //四值逻辑
bit  [2:0] b_vec;          //二值逻辑
 //隐式转换
 initial begin
$display("@1 x_vec='b%b",x_vec);
b_vec=x_vec;
 $display("@2 b_vec='b%b",b_vec);
$stop;
 end

运行结果:

首先“111x”会截掉高位,x四值逻辑会转化为二值逻辑,关键在于x,x变为0!

在不同数据类型进行操作时要注意变量的:

  1. 逻辑数值类型
  2. 符号类型
  3. 矢量位宽

02

数组部分

数组声明

int lo_hi[0:15]; //16个int类型变量,数组中有16个元素,从int [0]--->[15]

int c_style[16];

多维数组声明和使用

int array2[0:7][0:3]; //完整声明

int array3[8][4]; //紧凑声明,8:高维度,4:低维度

array2[7][3]=1; //设置最后一个元素为1

初始化和赋值

int ascend[4] = `{0,1,2,3};

//注意单引号的使用,对4个元素初始化,默认是从低到高,如果是非合并类型的(数据不是紧挨着存储的),那么就需要用一个“`”来赋值,队列类型数据是连续存放的,无需使用“`”。

代码语言:javascript
复制
int descend[5];
descend=`{4,3,2,1,0};   //为5个元素赋值
descend[0:2]=`{5,6,7};   //为前3个元素赋值
ascend=`{4{8}};            //4个值全为8
descend=`{9,8,default:-1011}; //{9,8,-1011,-1010,-1011}

存储空间变量

下面的两个变量,都可以表示24bit的数据容量,那么以硬件的角度出发,哪种方式的所需存储空间更小?

  1. bit [3][7:0] b_pack; //合并型,3x8类型矢量,3:高纬度,8:低维度
  2. bit [7:0] b_unpack[3]; //非合并型,同上

合并型: 所有的维度都写到变量的左边

非合并型:数组的任何一个维度写到变量的右边,所有空间连续的存储,更加节省空间。

b_unpack实际会占据3个WORD的存储空间,但是b_pack则只会占据一个WORD的存储空间。

exp

如果用logic类型来存储上面的数据,即24bit,且分别声明变量为:

代码语言:javascript
复制
logic [3][7:0] l_pack;
logic [7:0]   l_unpack[3];

那么他们占据的实际存储空间为:______WORD,______WORD。

解析:

“logic [3][7:0] l_pack;”为合并型变量,存储空间连续,表面上看是占据:3*8个bit,实际上每个variable存在0、1两种可能情况,所以需要48bit(>32bit),也就是2WORD。

“logic [7:0] l_unpack[3];”为非合并型变量,存储空间不连续,但是每一组需要8*2=16bit<32bit(1WORD),所以需要3WORD实际存储空间。

03

基本数组操作

for和foreach

exp

代码语言:javascript
复制
initial begin
bit [31:0] src[5],dst[5];  //定义两个非合并型5*32数组,行数为5,列数为32
for(int i=0;i<$size(src);i++)   //获取src行数(5),
src[i]=i;
foreach(dst[j])   //对于foreach而言,默认创建j
dst[j]=src[j]*2;
end

复制和比较

对于复制,可以使用复制符号“=”直接进行数组的复制;

对于比较,在不适用循环的情况下,也可以使用“==”或者“!=”来比较数组的内容,不过结果仅限于内容相同或不相同的情形。

exp

代码语言:javascript
复制
bit[31:0]src[5]=`{0,1,2,3,4};  //5行32列的非合并型存储空间
bit [31:0]dst[5]=`{5,4,3,2,1};
if(src==dst)
$display(“src==dst”);
else
$display(“src!=dst”);
src[0]=5;   //修改src数组中的第一个元素,src[0实际上指向的是数组的首地址,与第一个元素相同。

动态数组

定宽数组类型:数组宽度在编译时就已确定,如果在程序运行时再确定数组的宽度就需要使用动态数组。

动态数组:最大特点就是在仿真时灵活调节数组的大小,即存储量。

动态数组在一开始声明时,需要使用“[]”来声明,而数组此时是空的,empty,其后需要使用“new[x]”来分配空间,x表示分配的存储空间列数(宽度)。

此外,也可以在调用“new[]”时将数组名也一并传递,将已有数组的值复制到新数组中。

exp

代码语言:javascript
复制
int d1[],d2[];  //声明动态数组
initial begin
        d1=new[5];  //分配5个int类型元素
        foreach(d1[j]) 
d1[j]=j;  //对d1[5]进行初始化
d2=d1;             //复制一个数组
d2[0]=5;          //修改复制值
$display(“d1[0],d2[0]”);  //显示0,5
        d1=new[20](d1);  //分配20个数并进行复制
/*attention:复制的结果是d1的最低的五个元素为0,1,2,3,4,其余为0*/
        d1=new[100];  //重新分配100个数值,旧值不在存在。
        d1.delete();              //删除所有元素,释放空间
/*也可以用d1=new[0]或d1=`{},注意new[0]中必须有0*/
end

exp

下面的例子中,d1初始为{1,2,3}的物理存储空间是否还在?

代码语言:javascript
复制
int d1=`{1,2,3};  //非合并类型变量
int cp[]=d1;
d1=new[3];

解析:不在,因为是把d1的内容,而new[3]重新分配,释放了之前的地址空间。

04

队列

【队列】:结合了链表和数组的优点,可以在任何地方添加或删除元素,并通过索引实现对任意元素的访问。

队列的声明格式为:name[],表示队列元素的标号从0到

队列不需要new[]去创建空间,因为new[]只作用于动态数组,而使用队列的方法为增减元素,一开始其空间为0。

队列的一个简单使用是通过其自带的push_back()和pop_front()的结合来实现类fifo function。

push_back():从后面给fifo写入一个数据;

pop_front():从前面拿出一个数据。

exp

代码语言:javascript
复制
`timescale 1ns/1ns
module veri();
int j=1;
int v1[$]={3,4};
int v2[$]={0,2,5};  // v1[$]声明列表并初始化,the same to v2[$]
//队列赋值不需要使用“`”,说明是合并类型储存方式
initial begin
#20;
v2.insert(1,j);  //在地址1前存储单元插入j,{0,1,2,5}
foreach(v2[i])
$display("v2[%d] is %d",i,v2[i]);
#10;
v2.insert(3,v1);  //在v2的地址3前插入列表v1,{0,1,2,3,4,5}
foreach(v2[i])  $display("v2[%d] is %d",i,v2[i]);
#10; 
v2.delete(1);       //1为address,相当于指针,删除地址1的元素,{0,2,3,4,5}
  
foreach(v2[i])  $display("v2[%d] is %d",i,v2[i]);
/*在没有1时,即默认情况,删除v2中所有元素*/
$stop();
end
endmodule

运行结果为:

exp

下列操作运行速度更快(通过自有方法)

代码语言:javascript
复制
v2.push_front(6);  //在队列头部插入6,à{6,0,2,3,4,5}
luck=v2.pop_back();  //从队列尾部取出给luck

两个EXP运行结果为:

05

关联数组

如果只是偶尔需要创建一个大容量的数组,那么动态数组就足够了,但如果需要一个超大容量的呢?动态数组的限制在于其存储空间一开始就被固定下来,对于稀疏类型的超大容量的数组,该方式存在着浪费,因为很有可能该大容量的数组中有相当多的数据不会被存储和访问。

关联数组:可以用来保存稀疏矩阵的元素,当对一个非常大的地址空间寻址时,该数组只为实际写入的元素分配空间(潜台词就是没有用到的地址就不会有存储空间映射),这种实现方式比定宽数组或动态数组所占用的空间资源要小得多。

在其他软件中,如martlab中典型的哈希结构(Hash)or词典结构(Dictionary),可以灵活而赋予键值(key)和数值(value)。

exp

代码语言:javascript
复制
bit [63:0] assoc[int] ,idx=1;
/*int规定了索引的数值类型*/
/*整体声明了一个关联数组,里面存储的数据是一个64位无符号类型数据,int表示索引他的是一个int type*/
repeat(64) begin
        assoc[idx]=idx;
        idx=idx<<1;
end
foreach(assoc[i])   //使用foreach遍历数组
        $display(“assoc[%h] = %h”,i,assoc[i]);
//使用函数遍历数组
if(assoc.first(idx)) begin    //得到第一个索引
        do
                 $display(“assoc[%h] is %h”,idx,assoc[idx]);
        while(assoc.next(idx));           //get next index
end
//找到并删除第一个元素
assoc.first(idx);                //删去标号地址对应的元素
assoc.delete(idx);

06

结构体struct

verilog最大的缺陷之一是没有数据结构,在sv中可以使用struct语句创建结构体,与c语言类似。

不过struct的功能较少,只是一个数据的集合,其常用方式是将若干相关的变量组合到一个struct结构定义中。

伴随着typedef可用来创建新的类型,并利用新类型来声明更多的变量。

exp

代码语言:javascript
复制
struct { bit  [7:0] r , g , b; }   pixel;       //rgb三个变量是分开存放的,非合并型
/***创建了一个pixel结构体***/
//为了共享该类型,通过typedef创建新类型
typedef struct { bit [7:0] r,g,b; } pixel_s;
pixel_s my_pixel;    //声明变量
my_pixel= `{‘h10,’h10,’h10};  //结构体类型的赋值

何时用单引号“`”?

如果是非合并类型的(数据不是紧邻存储),在赋值时需要使用“`”来赋值,队列是连续存放,赋值时无需使用“`”。

07

枚举类型

规范的操作代码和指令(例如ADD、WRITE、MULTI)等有利于代码的编写和维护,它比直接使用’h10这样的常量使用起来具有更好的可读性和可维护性。

枚举类型enum常和typedef搭配使用,便于用户自定义的枚举类型的共享使用,同时枚举类型的出现保证了一些非期望值的出现,降低了设计风险。

exp

代码语言:javascript
复制
typedef enum{INIT,DECODE,IDLE} fsmstate_e;   //e表示枚举类型
fsmstate_e psate,nstate;  //声明自定义类型变量
 
initial begin
        case(psate)
                 IDLE:      nstate=INIT;                //数据赋值
INIT:       nstate=DECODE;
                 default:    nstate=IDLE;
$display(“Next state is %s”,nstate.name());  //显示状态名
end

需要注意:枚举值缺省为从0开始递增的整数,也可以自定义枚举值,如:

typedef enum {INIT,DECODE=2,IDLE} fsmstate_e; //0----2----3

08

字符串

所有与字符串相关的处理,都是用string来保存和处理,与字符串处理相关的还包括字符串的格式化函数,即如何形成一个想要的句子?可以使用sv系统方法sformat(),也可以使用$display()。

exp

代码语言:javascript
复制
string s;   //字串符定义,输出化为NULL,而非“\0”
initial begin
        s=”IEEE ”;  //5个char:I-E-E-E-E-_
        $display(s.getc(0));   //显示地址0对应的char,即I
        $display(s.tolower());   //显示小写ieee
s_putc(s.len()-1,”-”);       //将空格替换为“-”
s={s,”P1800”};               //IEEE-P1800拼接
$display(s.substr(2,5));  //显示EE-P
//创建临时字符串
my_log($psprintf(“%s %5d”,s,42));  //生成要打印的字符串
end
task my_log( string message );
//将信息打印到日志中
$display(“@%05:%s”,$time,meesage);
endtask

09

运行环境搭建(以队列为例)

运行环境为VCS,编写Makefile脚本以便于处理,需要注意在编译选项中一定要打开打开“-sverilog”选项,分享一下自己的Makefile脚本:

代码语言:javascript
复制
.PHONY: com sim run_verdi clean
OUTPUT = veri
export demo_name=$(OUTPUT)
#coverage command
CM            = -cm line+fsm+cond+branch+tgl
CM_NAME = -cm_name $(OUTPUT)
CM_DIR  = -cm_dir ./$(OUTPUT).vdb
#vpd file name
VPD_NAME=+vpdfile+$(OUTPUT).vpd
#compile command
VCS= -vcs +v2k -sverilog -debug_all -timescale=1ns/1ns                \
 ${CM} ${CM_DIR} ${CM_NAME}                         \
       -LDFLAGS                                     \
     -rdynamic                                      \
  -P  ${Verdi_HOME}/share/PLI/VCS/LINUX64/novas.tab                \
               ${Verdi_HOME}/share/PLI/VCS/LINUX64/pli.a               \
      +vcs+lic+wait                                 \
      -full64                                                       \
    +notimingcheck                                  \
   +nospecify                                       \
  +vcs+flush+all                                    \
  -o $(OUTPUT)                                      \
  -l compile.log                                    \
  -f file_list.


SIM=./$(OUTPUT)                                     \
 +autoflush                                         \
  -l  $(OUTPUT).log                                 \
 -gui &
  
com:
${VCS}
sim:
${SIM}
run_verdi:
verdi -f file_list.f -nologo -ssf $(OUTPUT).fsdb &
cov:
dve -covdir $(OUTPUT).vdb &
clean:
rm -rf {OUTPUT} ucli.key inter.*  div.* csrc DVEfiles compile.log ${OUTPUT} $(OUTPUT).vdb $(OUTPUT).daidir
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-09-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 根究FPGA 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档