SV 数据类型

前言

SV新特性

  对比VERILOG

  • 2值逻辑:提高性能,减少内存使用;
  • 队列,动态数组,关联数组:减少内存使用,内建搜索和排序函数;
  • unions 和 packed;
  • class 和 structures
  • string
  • enumerated

赋值

  • SV  赋全值(全0,全1,全x,全z)时,可以忽略位宽
  • verilog  赋全 1 时,不能忽略位宽,也不能忽略进制。
// verilog
parameter SIZE=64;
logic [SIZE-1:0] data;
data=`b0;
data=`bz;
data=`bx;
data=64'hFFFFFFFFFFFFFFFF;

//sv
parameter SIZE=64;
logic [SIZE-1:0] data;
data=`0;
data=`z;
data=`x;
data=`1;

基本数据类型

类型列表

type 逻辑值 符号类型 位宽 特点
wire 4 值逻辑 无符号 1 bit  
reg 4 值逻辑 无符号 1 bit  
logic 4 值逻辑 无符号

1 bit

  • SV中引入新的数据类型,侧重于验证,并不关心 logic 对应的逻辑应该被综合位寄存器还是线网
  • 如果 logic 被使用的场景是验证环境,那么它只会作为单纯的变量进行赋值操作。
  • 支持连续赋值,门逻辑和模块;
  • 不支持多驱动,(例如:双向端口,inout,只能用wire不能用logic);
  • 可以替代VERILOG中的 reg 类型
bit 2 值逻辑 无符号 1 bit  
byte 2 值逻辑 有符号 8 bit  
shortint 2 值逻辑 有符号 16 bit  
integer 4 值逻辑 有符号 32 bit  
int 2 值逻辑 有符号 32 bit  
longint 2 值逻辑 有符号 64 bit  
time 2 值逻辑 无符号 64 bit 64位整数,默认单位为:秒
real 2 值逻辑 有符号 64 bit 类似 C 的 double 类型
shortreal 2 值逻辑 有符号 32 bit  类似 C 的 float 类型
string       可变长度的字符数组
void       空,用于函数返回

使用示例

var        logic    [63: 0]    addr;      // a 64-bit wide variable
wire       logic    [63: 0]    data;      // a 64-bit wide net

bit                            b;         // 两态,单比特
bit    [31: 0]                 b32;       // 两态,32比特无符号
int                            i32;       // 两态,32比特有符号
byte                           b8;        // 两态,8比特有符号
shortint                       s16;       // 两态,16比特有符号
longint                        l64;       // 两态,64比特有符号

逻辑类型

4值逻辑

  • 逻辑值:0,1,x, z
  • 常用类型:integer,  logic,  reg,  net_type (例如wire,  tri)
  • 仿真时,4值逻辑默认为 x,2值逻辑默认为 0
  • 4值逻辑可以赋值给2值逻辑变量,x 和 z 会转化成 0
  • 使用  $isunknown()  check任何bit是x或z,如果表达式任何一个bit是z或x,返回1;
 if($isunknown(iport)==1);

2值逻辑

  • 逻辑值:0和1;z和x 会转换成0
  • 常用类型:bit, byte, shortint, int, longint
  • 2值逻辑变量不能代表一个未初始化的状态
  • 提升仿真性能和减少内存使用
  • 不适用于DUT,因为DUT识别z和x,但是2值逻辑将会转换为0

数据类型转换

静态转换

  • 在转换的表达式前加上单引号即可
  • 不会対转换值做检查,如果转换失败,无从得知
  • 在编译的时候做检查
  • 有符号转换成无符号: unsigned'(signed_vec)

动态转换

  在仿真的时候做检查,通常使用函数:$cast(tgt, src)

显示转换

  静态转换和动态转化都需要操作符号或者系统函数介入

隐式转换

  不需要进行转换的一些操作

tips

  • Verilog没有严格区分信号的类型,变量和线网类型均是四值逻辑;
  • SV中将硬件信号分为:变量类型 和 线网类型;
    • 线网类型赋值只能使用连续赋值语句(assign),不能出现在过程块(initial / always);
    • 对于变量类型赋值可以使用连续赋值、或者过程赋值

定宽数组

  • 类型的宽度在编译时就确定了。
  • 支持多维数组
  • 超过边界写将会被忽略,超过边界读将会返回x,2值逻辑也是返回x;
  • byte,int, shortint,都是存储在一个32位的空间中
  • longint存储在两个32位的空间中
  • 默认为unpacked数组
  • 将维度都写在左边称为合并型数组,高纬度在左边
  • 只要有一个维度是写在右边,那就是非合并数组,高纬度从变量名右边开始

语法

  type    name    [constant];     type    name  [0: constant];

int         array[20];            // 元素:0-19
int         array[64:83];         // 元素:64-83
logic [31:0]     data [1024];         // 元素: 0-1023, 32位宽
  
int         array1 [4]= '{3,2,4,1};   
array1[0:2]  = '{5,6,7};
array1     = '{4{8}};
array1     = '{1,2, default:-1};

array1     = '{{3}, '{3{8}}};

int md [2][3] = '{'{0,1,2}, '{3,4,5}};                 

pack vs unpack

//  两个变量都可以表示24bit的数据容量
//  b_pack只会占据 1 个 WORD 的存储空间
//  b_unpack占据 3 个 WORD 的存储空间
//  如果变量前后都有容量定义,右边的维度高,即b_unpack的3的维度高

bit [3][7:0]  b_pack
bit [7:0]     b_unpack[3];

// logic是四值逻辑,即需要2bit
logic [3][7:0]   b_pack;        // 2 个WORD    2*8*3 
logic [7:0]      b_unpack[3];   // 3 个WORD   2*8=16, 1个WORD

数组复制和比较

  • 利用赋值符号  "="  直接进行数组的复制

  • 直接利用   "==" 或者  "!=" 来比较数组的内容,结果仅限于内容相同或者不相同

 动态数组

  •  定宽数组的宽度在编译时就确定了;

  • 动态数组可以在仿真运行时灵活调节数组大小,即存储量
// 动态数组在声明时,需要使用  []  声明,此时数组数组是空的,即0容量
// 使用new[]  来分配空间,在方括号中传递数组的宽度
// 也可以在调用 new[] 时将数组名传递,将已有的数组的值复制到新的数组中

initial begin: dynamic_array

  int         dyn1[], dyn2[];
  
  dyn1 = new[5];         // 分配5个元素  
  dyn1 = '{1, 2, 3, 4};
  $display("dyn1 = %p", dyn1);

// copp method option-1
  dyn2 = dyn1;           // 重新复制一份数据到dyn2,相当于深拷贝
  $display("dyn2 = %p", dyn2);
  $display("dyn2 size is %0d", dyn2.size());
  
// copp method option-2
  dyn2 = new[dyn1.size()](dyn1);
  $display("dyn2 = %p", dyn2);
  $display("dyn2 size is %0d", dyn2.size());

  dyn2.delete();   // 删除所有元素
  $display("dyn2 size is %0d", dyn2.size());
end

队列 

  •  可以在任何地方添加或删除元素,并且通过索引实现队任一元素的访问 
  • 使用美元符号声明:[$],元素标号从0到$
  • 不需要使用new[] 去创建空间,一开始其空间为0
  • 使用 push_back 和 pop_front 结合实现FIFO的用法,还有push_front 和 pop_back
initial begin: queue_use
  int que1[$], que2[$];
 
  que1 = {10, 30, 40};            // 不需要使用单引号赋值
  $display("que1 = %p", que1);
  que2 = que1;
  $display("que2 = %p", que1);

  que1.insert(1, 20);
  $display("que1 = %p", que1);   // {10, 20,30,40}  

  que1.delete(3);                   // delete que1[3]==40

  void'(que1.pop_front());         // pop que[0]==10
  $display("que1 = %p", que1);
  
  foreach(que1[i])
       $display(que1[i]);

  que1.insert(3, que2);

  que1.push_front(6);   // 队首插入6
  j = que1.pop_back(); // 队尾弹出,并赋值给 j 
  
  que1.push_bach(7);   // 队尾插入7  
  que.pop_front();        // 队头弹出
    
  que1.delete();
  $display("que1 = %p", que1);


end            

 关联数组

  • 用来保存稀疏矩阵的元素
initial begin: associate_array
  int id_score1[int], id_score2[int];     // key ID, value SCORE
  
  id_score1[101] = 111;
  id_score1[102] = 222;
  id_score1[103] = 333;

  // associate array copy
  id_score2 = id_score1;
  id_score2[101] = 101;
  id_score2[102] = 102;
  id_score2[103] = 103;

  foreach(id_score1[id]) begin
    $display("id_score1[%0d] = %0d", id, id_score1[id]);
  end
  foreach(id_score2[id]) begin
    $display("id_score2[%0d] = %0d", id, id_score2[id]);
  end

   if(id_score.first(idx)) begin
       do
           $display("id_score[%d]=%d", idx, id_score[idx]);
       while(id_score.next(idx));
   end
   
// 找到并删除第一元素
   id_score2.first(idx);
   id_score2.delete(idx);
    
end   

关联数组内建函数

function int num ( ) / function int size ( )    // 返回关联数组中成员的数目。如果数组是空数组,那么返回0;
function void delete ([input index])         // index是一个可选的适当类型的索引,删除特定索引的成员,如果没有指定索引,则删除数组的所有成员;
function int exists (input index)           // index是一个适当类型的索引,如果成员存在,则返回1,否则返回0;
function int first (ref index)            // index是一个适当类型的索引,将指定的索引变量赋值为关联数组中第一个(最小的索引的值);如果数组是空的,则返回0,否则返回1;
function int last (ref index)            // index是一个适当类型的索引,将指定的索引变量赋值为关联数组中最后一个(最大的索引的值);如果数组是空的,则返回0,否则返回1;
function int next (ref index)            // index是一个适当类型的索引,查找索引值大于指定索引的条目。如果存在下一个成员,索引变量被赋值为下一个成员的索引,并且函数返回1。否则,索引不会发生变化,函数返回0;
function int prev (ref index)            // index是一个适当类型的索引,查找索引小于指定索引的成员。如果存在前一个成员,索引变量被赋值为前一个成员的索引,并且函数返回1 。否则,索引不会发生变化,并且函数返回0;

 

结构体

  • 数据的集合
  • 使用struct语句创建
  • 结合typedef可以用来创建新的类型,并利用新类型来声明更多的变量
struct {
              bit [7:0] r,g,b;
          }   pixel;                         // 创建一个pixel结构体  
         
typedef struct {
              bit [7:0] r,g,b;
          }   pixel_s;
pixel_s     my_pixel;    // 声明变量

my_pixel = '{'h10, 'h10, 'h10};   // 赋值
 

枚举

  • 可读性和可维护性更好
  • 结合typedef使用
  • 枚举类型可以直接赋值给整型: int a = INIT;
  • 整型不能直接赋值给枚举类型,需要进行转换
typedef enum {INIT,DECODE,IDLE}  fsmstate_e;

fsmstate_e pstate, nstate;   //  声明自定义类型变量

case(pstate)
        IDLE: nstate = INIT
        INIT : nstate = DECODE;
        default:  nstate = IDLE;
endcase
$display("Next state is %s", nstate.name());

字符串

  • SystemVerilog 包含一个string数据类型,它是一个可变尺寸、动态分配的字节数组。
  • SystemVerilog 还包含许多特殊的方法来对字符串进行操作。
    • string类型的变量可以从0到N-1(数组的最后一个元素)进行索引
    • 可以作用于一个特殊的空字符串:""
    • 从一个字符串读取一个元素会产生一个字节
    • string类型变量的索引从字符串的左侧开始排列,例如:对字符串"Hello World!",索引0对应"H",索引1对应"e",依此类推...
    • 如果在声明中没有指定初始值,变量会被初始化成空字符串("")
    • $sformatf()  函数: 格式化函数
    • $display()  函数: 打印函数
string s;
initial begin 
    s = "IEEE";
    $display (s.getc(0));      //显示第0个字符: I

    $display (s.tolower());     //显示小写字符: ieee

    s.putc(s.len() - 1,"-");       //putc是替换,len() - 1是字符串最后一位,即将最后一位替换成“ -”

    s = {s,"P1800"};                 //字符串拼接 ->IEE-P1800

    $display(s.substr(2,5));         // 显示第2-5位字符 ->E-P1
    //创建一个临时字符串并将其打印
    my_log ($sformatf("%s,%5d",s,42));
end
task my_log (string message); //打印信息 $display ("@%0t:%s",$time,message); endtask

循环结构

for循环

// $size 默认获得最高的维度

initial begin
    bit   [31:0]  src[5], dst[5];
for(int i=0;i<$size(src); i++) src[i] = i; end

foreach循环

//  自动创建变量j, 默认为最高维度
initial begin
    bit   [31:0]  src[5], dst[5];
foreach(dst[j]) dst[j]
= j; end

 

 


 

有符号
posted @ 2021-04-02 10:59  徘徊的游鱼  阅读(1914)  评论(0编辑  收藏  举报