System Verilog的基本数据类型

原文地址

和verilog相比system verilog提供了很多数据改进的数据结构,一些数据对于创建者和测试者都有用。

内建数据类型

逻辑(logic)类型

system verilog对经典的reg数据类型进行了改进,使他除了作为变量以外,还能被连续赋值、门单元、和驱动模块所驱动,这种改进的数据类型被称为logic

它既可被过程赋值也能被连续赋值,编译器可自动推断logicreg还是wire。唯一的限制是logic只允许一个输入,不能被多重驱动,所以inout类型端口不能定义为logic。不过这个限制也带来了一个好处,由于大部分电路结构本就是单驱动,如果误接了多个驱动,使用logic在编译时会报错,帮助发现bug。所以单驱动时用logic,多驱动时用wire

Jason的博客评论中,Evan还提到一点logicwire的区别。wire定义时赋值是连续赋值,而logic定义时赋值只是赋初值,并且赋初值是不能被综合的。

wire mysignal0 = A & B;     // continuous assignment, AND gate
logic mysignal1 = A & B;    // not synthesizable, initializes mysignal1 to the value of A & B at time 0 and then makes no further changes to it.
logic mysignal2;
assign mysignal2 = A & B;   // Continuous assignment, AND gate
module tb ;
	logic [7:0]i; //声明logic结构数据
	initial begin
		i = 0;
		repeat(10) begin  //循环10次
			$display("i=%d",i);
			i = i + 1;
		end
		$display("test running!");
	end 
endmodule

仿真结果

Start run at Nov  2 19:42 2020

i=  0
i=  1
i=  2
i=  3
i=  4
i=  5
i=  6
i=  7
i=  8
i=  9

logic不仅能够作为变量,而且可以被连续赋值,门单元和模块所驱动。但是logic不能够被多个结构体驱动。下面这段代码展示了logic的使用方法。

module logic_test(input logic rst_h
);
 parameter CYCLE = 20;
 logic q,q_1,d,clk,rst_1;
 
 initial 
 begin
   clk = 0;
   forever #(CYCLE/2) clk = ~clk;          // 过程赋值
 end

 assign rst_1 = ~ rst_h;                         //连续赋值
 not n1(q_1,q);                                      //q_1被门驱动
 my_dff d1(q,d,clk,rst_1);                      //q被模块驱动
endmodule

SystemVerilog logic的使用方法

  • 单驱动时logic可完全替代regwire,除了Evan提到的赋初值问题。
  • 多驱动时,如inout类型端口,使用wire

双状态数据类型

相比于verilog中的4状态数据类型(1,0,x,z),SV引入双状态数据类型有利于提高仿真器的性能并减少内存的使用量。下面逐例解释。

bit                  b;                      // 双状态 单bit
bit [31:0]        b32;                  // 双状态,32bit,无符号
int  unsigned ui;                     // 双状态,32bit,无符号
int                  i;                       // 双状态,32bit,有符号
byte               b8;                    // 双状态,8bit  ,有符号
shortint         s;                       // 双状态,16bit,有符号
longint          l;                        // 双状态,64bit,有符号
integer          i4;                      // 四状态,32bit,有符号
time              t;                        // 四状态,64bit,无符号
real               r;                        // 双状态,双精度浮点数。

双状态数据类型有利于提高仿真器的性能并减少内存。

对四态信号的检查:($isunknown)
这会让任意位置出现X或者Z时返回1,使用实例如下:

if( $isunknown(iport) == 1)
	$display("@%0t: 4-state velue detected on iport %b",$time,iport)

部分仿真结果如下:

module tb ;
        longint i; //声明logic结构数据
        byte b8;
        initial begin
                i = 0;
                b8 = 2;
                repeat(3) begin  //循环
                        $display("i=%0d , b8=%0d",i,b8);
                        i = i - 1;
                        b8=b8*2;
                end
        end 
endmodule
Start run at Nov  2 20:01 2020

i=0 , b8=2
i=-1 , b8=4
i=-2 , b8=8

定宽数组

verilog要求在声明中必须给出数组的上下边界,因为几乎所有的数组都是用0作为索引下界,所以sv允许只给出数组的宽度,跟c语言类似。

例 :数组的声明

int arr[0:15];    //16个整数
int c_style[16];    //c风格,16个整数

可以通过在变量后面指定维度的方式来声明多维数组

例 : 声明8行4列多维数据

int arr2 [0:7][0:3];    //完整声明
int arr2_cstyle[8][4];

特别注意:如果读取的数据地址越界,那么sv会返回数组元素的缺省值

  • logic类型:返回 X
  • 双状态类型:返回 0
  • 线网在没有驱动时输出为 Z

数组的遍历

module tb ;
	int md[2][3] = '{'{1,2,3},'{4,5,6}};	//	声明2行3列数组并赋初值,这里使用常量数组来初始化md数组
	initial begin
		foreach(md[i,j]) //遍历数组
			$display("md[%0d][%0d]=%0d",i,j,md[i][j]);
	end 
endmodule

输出结果

VCS Build Date = May 24 2016 20:38:43
Start run at Nov  2 20:26 2020

md[0][0]=1
md[0][1]=2
md[0][2]=3
md[1][0]=4
md[1][1]=5
md[1][2]=6

按维度遍历

module tb ;
	int md[2][3] = '{'{1,2,3},'{4,5,6}};	//	声明2行3列数组并赋初值
	initial begin
		foreach( md[i] ) begin //遍历第1维
			$write("%2d:",i);
			foreach( md[,j] )  //遍历第2维
				$write("%3d",md[i][j]);
			$display;//显示一个维度
		end
	end 
endmodule

结果

 0:  1  2  3
 1:  4  5  6

数组的比较与复制

module tb ;
	bit [31:0] src[5] = '{0,1,2,3,4},
			   dst[5] = '{5,4,3,2,1};
	initial begin
		//比较两个数组
		if( src == dst ) $display("src == dst");
		else $display("src != dst");

		//把src元素拷贝到dst
		dst = src;

		//只改变一个元素
		dst[0] = 5;

		//所有元素的值是否相等
		$display("src %s dst",(src == dst)? "==" : "!=");
		//使用数组片段对第 1-4 个元素进行比较
		$display("src[1:4] %s dst[1:4]",(src[1:4] == dst[1:4]) ? "==" : "!=");
	end 
endmodule

输出结果

Start run at Nov  2 20:41 2020

src != dst
src != dst
src[1:4] == dst[1:4]

使用数组位下标和数组下标

例 : 位下标和数组下标

module tb ;
	bit [31:0] src[5] = '{5,1,2,3,4};
	initial begin
		$display(src[0],,  // 5
				src[0][0],, // 1
				src[0][2:1]); // 2
	end 
endmodule

合并数组

对于一些数据你可能既希望作为整体访问,也希望拆分为更小单元访问,使用sv的合并数组可以实现这个功能。

例 : 合并数组例子

module tb ;
	bit [3:0][7:0] bytes ; //四个字节组装而成的32 bit数据
	initial begin
		bytes = 32'hCafe_Dada;
		$display(bytes,,  //显示所有32 bit
				bytes[3],, //显示最高字节 "CA"
				bytes[3][7]); //显示最高bit位 ”1“
	end 
endmodule

结果

3405699802 202 1

扩展说明:
已知一个多维混合数组的定义为:

bit [3:0][7:0][15:0] Array [3:0][7][6];

那么当我们写下

Array[2][3][2][2] = xxxx;

的时候,到底是对哪个位置赋值了??

话不多说,直接看解答好啦~最后的答案其实很简单,因为有一个简单的图示估计很多人知道,就是逆时针索引法:

合并数组和非合并数组的选择

  • 合并数组:和标量进行相互转换,等待数组中的变化必须使用合并数组

动态数组

sv提供动态数组,动态数组在声明时使用空下标[ ]。宽度将不会在编译时给出,而是在程序运行时在指定。数组开始时为空,因此你必须调用new [ ]操作来分配空间。

例 :动态数组实例

module tb ;
	int dyn[],d2[] ; //声明动态数组
	initial begin
		dyn = new[5] ;//分配5个元素空间
		foreach( dyn[i] ) dyn[i] = i; //对5个元素空间的值进行初始化
		d2 = dyn ;  //复制数组dyn到d2
		d2[0] = 5 ; //修改d2[0]的值为5 
		$display(dyn[0],d2[0]); //显示数值
		dyn = new[20](dyn); //分配20个证书值并进行复制
		dyn = new[100]; //分配 100 个新的整数值
		dyn.delete(); //删除所有元素
	end 
endmodule

例 : 使用动态数组保存元素数量不定的列表

bit [7:0] mask[] = '{  8'h00,8'h01,
                       8'h03,8'h04,
                       8'h23,8'h36};

队列

队列与链表类似,可以在一个队列中的任何地方增加和删除元素,这类操作在性能上的损失比动态数组小的多!

队列的声明使用[$],队列元素的编号从0到$。sv的队列类似与STL的双端队列。可以通过增加元素来创建队列。你可以扩大和缩小队列,但是不用向动态数组一样付出很大的代价

例 : 队列操作

module tb ;
	//注意,队列常量不需要使用“’”
	int q2[$] = {3,4};
	int q[$]= {0,2,5};
	initial begin
		//在2之前插入1
		q.insert(1,1); // 0 1 2 5
		foreach(q[i])$write("%3d",q[i]); $display; //显示结果
		//在5之前插入队列q2
		q.insert(3,q2); // 0 1 2 3 4 5 
		foreach(q[i])$write( "%3d",q[i]); $display; //显示结果
		//删除q下标为1的元素
		q.delete(1); //0 2 3 4 5
		foreach(q[i])$write( "%3d",q[i]); $display; //显示结果

		//以下操作速度很快
		//在队列最前面插入 8 
		q.push_front(8); //8 0 2 3 4 5
		foreach(q[i])$write( "%3d",q[i]); $display; //打印整个链表
		//在队列最前后面插入 6
		q.push_back(6); //8 0 2 3 4 5 6
		foreach(q[i])$write( "%3d",q[i]); $display; //显示结果
		//读出队列最前面的元素并从队列中移除该元素
		$display(q.pop_front());
		foreach(q[i])$write( "%3d",q[i]); $display; //显示结果 0 2 3 4 5 6
		//读出队列最后面的元素并从队列中移除该元素
		$display(q.pop_back());
		foreach(q[i])$write( "%3d",q[i]); $display; //显示结果 0 2 3 4 5 

	end 
endmodule

结果

Start run at Nov  2 21:56 2020

  0  1  2  5
  0  1  2  3  4  5
  0  2  3  4  5
  8  0  2  3  4  5
  8  0  2  3  4  5  6
          8
  0  2  3  4  5  6
          6
  0  2  3  4  5

说明: 可以使用字下标串联来代替方法。对于队列q[$]={0,2,4},如果把$放在一个范围表达式的左边,那么$将代表最小值,例如[$:2]就代表[0:2]。如果把$放在一个范围表达式的右边,那么$将代表最大值,例如[1:$]就代表[1:2]

使用上述方法的操作如下:

例 : 队列串联表达式

module tb ;
	//注意,队列常量不需要使用“’”
	int q2[$] = {3,4};
	int q[$]= {0,2,5};
	initial begin
		//在2之前插入1
		q={q[0:1],1,q[2:$]}; // 0 1 2 5
		foreach(q[i])$write("%3d",q[i]); $display; //显示结果
		//在5之前插入队列q2
		q={q[0:2],q2,q[3:$]}; // 0 1 2 3 4 5 
		foreach(q[i])$write( "%3d",q[i]); $display; //显示结果
		//删除q下标为1的元素
		q.delete(1); //0 2 3 4 5
		foreach(q[i])$write( "%3d",q[i]); $display; //显示结果
		//删除整个队列
		q={};
	end 
endmodule

关联数组

当你只需要偶尔创建一个大容量数组,那么动态数组已经足够好用了,但是如果需要超大容量的呢?sv提供了关联数组用来保存稀疏矩阵的元素。也就是说只为实际写入的数据开辟存储空间。可以将其理解为哈希表,虽然哈希表带来了额外开销,但是在这种情况下,这是可以也接受的。

例 : 关联数组声明,初始化和使用

module tb ;
	bit [63:0] assoc[ bit [63:0] ],idx = 1;
	initial begin
		//对稀疏分布的元素进行赋值
		repeat (3) begin
			assoc[idx] = idx;
			idx = idx << 1;
		end	
		//foreach遍历数组
		foreach(assoc[i])$display("assoc[%h]=%h",i,assoc[i]);
		$display("+++++++++++++++++++++++");
		//使用函数遍历数组
		if( assoc.first(idx) ) begin
			do
				$display("assoc[%h]=%h",idx,assoc[idx]);
			while( assoc.next(idx) );  //得到下一个索引
		end	

		//找到并删除第一个元素
		assoc.first(idx);
		assoc.delete(idx);
		$display("The array now has %0d elements",assoc.num);
	end 
endmodule

结果

Start run at Nov  2 22:22 2020

assoc[0000000000000001]=0000000000000001
assoc[0000000000000002]=0000000000000002
assoc[0000000000000004]=0000000000000004
+++++++++++++++++++++++
assoc[0000000000000001]=0000000000000001
assoc[0000000000000002]=0000000000000002
assoc[0000000000000004]=0000000000000004
The array now has 2 elements

例 : 使用带字符串索引的关联数组

//关联数组也可以用字符串索引进行寻址,使用字符串索引读取文件,并建立关联数组switch,可以实现字符串到数字的映射。
/*
		输入文件内容如下:
			42 min_address
			1492 max_address
*/

int switch[string],min_address,max_address;   //定义变量
initial begin    //初始化
	  	int i,r,file;
	 	 string s;//string    用来年保存长度可变的字符串常量

file = $fopen(“switch.txt”,r);	//在当前目录下以只读的方式打开文件switch.txt,使用指针向量file指向该文件
 while(! $feof(file) ) begin		//未读取到文件的结尾时
  	 r=$fscanf (file, “%d %s”, i, s);	//将文件中的数字以10进制的方式保存到i中,字符串保存到s中
  	 switch[s] = i;	//为数组中的字符串指定地址
end
$fclose(fire);	//关闭文件


//获取最小地址值,缺省为0
min_address=switch[“min_address”]  //将min_address对应的地址(数字)赋值给min_address

//获取最大地址值,缺省为1000
if(switch.exists(“max_address”))       //使用exists函数判断switch数组中是否含有“max_address”
  max_address = switch[“max_address”]//含有的话,将内容的地址赋值给  max_address
else
 max_address =1000 //不存在指定内容,则默认地址为1000

//打印数组的所有元素
foreach(switch [s])	
$display(“switch[ ‘ %s ’ ] = %0d ”, s ,switch[s] );//打印数组的内容和相应的地址
end


例 : 字符关联数组操作

module tb ;
	bit [63:0] assoc[ string ];
	initial begin
		//对稀疏分布的元素进行赋值
		assoc["a"] = 1;
		assoc["b"] = 2;
		assoc["c"] = 3;
		//foreach遍历数组
		foreach(assoc[i])$display("assoc[%s]=%h",i,assoc[i]);
	end 
endmodule

链表

sv提供了链表数据结构,类似于STL的列表容器,这个容器被定义为参数化的类,可以根据用户所需存放各种类型的数据。

虽然sv提供了链表,但是应该避免使用它。sv使用队列更高效。

数组的方法

sv提供了很多数组的方法,可以用于任何一种非合并数组的类型,包括定宽数组,动态数组,队列和关联数组

sum方法:数组求和

例 : 数组求和

	bit on[5] = '{0,1,0,1,0};//单bit数组
	initial begin
		foreach( on [i])$write("%3d",on[i]);$display;
		//单bit求和
		$display("on.sum=%0d",on.sum);
	end
endmodule

结果

Start run at Nov  2 22:49 2020

  0  1  0  1  0
on.sum=0

sum方法的结果位宽和数组定义的位宽是一致的。

product方法:数组求积

例 :数组求积

module tb ;
	int on[3] = '{1,2,3};//单bit数组
	initial begin
		foreach( on [i])$write("%3d",on[i]);$display;
		//单bit求和
		$display("on.product=%0d",on.product);
	end
endmodule

结果

Start run at Nov  2 22:57 2020

  1  2  3
on.product=6

##and,or,xor方法:数组求与,或,异或
例 :数组求与,或,异或

module tb ;
	int on[3] = '{1,3,3};//单bit数组
	initial begin
		foreach( on [i])$write("%3d",on[i]);$display;
		$display("on.and=%0d",on.and);
		$display("on.or=%0d",on.or);
		$display("on.xor=%0d",on.xor);
	end
endmodule

min,max方法:最大值最小值方法

注意返回值为一个队列!
例 :最大值最小值方法

module tb ;
	int on[] = '{1,3,3,9};
	int rt[$];
	initial begin
		foreach( on [i])$write("%3d",on[i]);$display;
		rt=on.min();foreach(rt[i])$write("%3d",rt[i]);$display;
		rt=on.max();foreach(rt[i])$write("%3d",rt[i]);$display;
	end
endmodule
  

结果

Start run at Nov  2 23:11 2020

  1  3  3  9
  1
  9

unique方法:排除重复数值

注意返回值为一个队列!
例 :排除重复数值

module tb ;
	int on[] = '{1,3,3,9};
	int rt[$];
	initial begin
		foreach( on [i])$write("%3d",on[i]);$display;
		rt=on.unique();foreach(rt[i])$write("%3d",rt[i]);$display;
	end
endmodule

结果

Start run at Nov  2 23:15 2020

  1  3  3  9
  1  3  9

size方法:获取数组大小

例 :获取数组大小

module tb ;
	int on[] = '{1,3,3,9};
	int rt[$];
	initial begin
		foreach( on [i])$write("%3d",on[i]);$display;
		//获取动态数组的大小
		$display("len=%0d",on.size());
		//获取各种数组大小的方法
		$display("len=%0d",$size(on));
endmodule

结果

Start run at Nov  2 23:18 2020

  1  3  3  9
len=4
len=4

find方法:数组定位方法

注意返回值为一个队列!
例 :find数组定位方法

module tb ;
	int on[] = '{4,1,3,3,9};
	int rt[$];
	initial begin
		foreach( on [i])$write("%3d",on[i]);$display;
		//找出所有大于等于3的元素   4 3 3 9
		rt=on.find with (item >= 3);
		foreach(rt[i])$write("%3d",rt[i]);$display;
		//找出值大于等于2的索引    0 2 3 4
		rt=on.find_index with (item >= 2);
		foreach(rt[i])$write("%3d",rt[i]);$display;
		//找出第一个大于4的值   9
		rt=on.find_first with (item >4);
		foreach(rt[i])$write("%3d",rt[i]);$display;
		//找出最后一个小于9的值   3
		rt=on.find_last with (item < 9);
		foreach(rt[i])$write("%3d",rt[i]);$display;
		//找出第一个大于4的数据的索引值   4
		rt=on.find_first_index with (item >4);
		foreach(rt[i])$write("%3d",rt[i]);$display;
		//找出最后一个==3的数据的索引值   
		rt=on.find_last_index with (item == 3);
		foreach(rt[i])$write("%3d",rt[i]);$display;
	end
endmodule

结果

Start run at Nov  2 23:31 2020

  4  1  3  3  9
  4  3  3  9
  0  2  3  4
  9
  3
  4
  3

例 : 等同的几种描述

		rt=on.find_first with (item >= 3);
		rt=on.find_first() with (item >= 3);
		rt=on.find_first(item) with (item >= 3);
		rt=on.find_first(x) with (x >= 3);

数组排序方法:reverse,sort,rsort,shuffle

例:数组排序

module tb ;
	int on[] = '{4,1,3,3,9};
	int rt[$],msum;
	initial begin
		foreach( on [i])$write("%3d",on[i]);$display;
		//数组反序
		on.reverse();
		foreach( on [i])$write("%3d",on[i]);$display;
		//数组 小->大
		on.sort();
		foreach( on [i])$write("%3d",on[i]);$display;
		//数组 大->小
		on.rsort();
		foreach( on [i])$write("%3d",on[i]);$display;
		//数组洗牌
		on.shuffle();
		foreach( on [i])$write("%3d",on[i]);$display;
	end
endmodule
  

结果

Start run at Nov  3 00:12 2020

  4  1  3  3  9
  9  3  3  1  4
  1  3  3  4  9
  9  4  3  3  1
  3  4  1  9  3

结构数组的使用和排序

例 : 结构数组的使用和排序例子

module tb ;
	int rt[$],msum;
	struct packed {byte red,green,blue;} c[]; //结构数组
	initial begin
		c=new[5];
		foreach(c[i])c[i] = $urandom;//随机赋值
		foreach( c[i])$write("%10d",c[i].red);$display;
		c.sort with (item.red); //up排序red
		foreach( c[i])$write("%10d",c[i].red);$display;
	end
endmodule
  

结果

Start run at Nov  3 00:17 2020

       -30       -34       127       -86       121
       -86       -34       -30       121       127

typedef创建新的数据类型

例 :创建新数据类型

	parameter N = 8;
	typedef reg [N - 1 : 0 ] opreg_t;

	opreg_t c;

struct创建新的数据类型

例 :struct创建新数据类型

module tb ;
	int rt[$];
	typedef struct packed {byte red,green,blue;} pixel_s;//结构数组
	typedef struct packed {int a;bit[2:0] b;} m_s;//结构数组
	pixel_s c='{1,2,3};
	m_s cc = '{
		32'd23,
		3'b101
	};
	initial begin
		$display("%5d %5d %5d",c.red,c.green,c.blue);
		$display("%5d %5d",cc.a,cc.b);
	end
endmodule

结果

Start run at Nov  3 00:38 2020

    1     2     3
   23     5

静态转换

静态转换操作不对转换值进行检查。
例 : 静态转换例子

module tb ;
	int rt[$];
	int i ;
	real r;
	initial begin
		i = int '(10.0 - 0.1); //非强制转换
		r = real '(42);
		$display(i);
		$display(r);
	end
endmodule

流操作

module tb ;
	int rt[$];
	byte h ;
	bit [3:0] q[2] = '{4'h04,4'h01} ;

	initial begin
		$display("%6b %6b",q[0],q[1]);
		//将q打包为byte
		h = {>>{q}};  //0100_0001
		$display("%b",h);
		//位倒叙
		h = {<<{h}};  //1000_0010
		$display("%b",h);
	end
endmodule

上一篇:10.6 system verilog基本语法下一篇:10.6.2 枚举与字符串

相关评论(0)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值