SystemVerilog学习【一】数据类型详解

SystemVerilog学习【一】数据类型详解

📖 扩展学习资源:

1. SystemVerilog——数据类型

1.1 内建数据类型(四值逻辑和二值逻辑)

SV相对与Verilog引入二值逻辑的期望是将硬件的世界与软件的世界分离开,四值逻辑属于硬件世界即硬件设计,软件世界即验证环境更多的是二值逻辑。

1.1.1 逻辑类型

按照逻辑类型进行划分,将常见的变量类型划分为:

四值逻辑,可以表示0、1、X、Z四种状态,默认初始值为x;
二值逻辑,只可以表示0和1两种状态,默认初始值为0。

注:将四值变量赋值给二值变量,X和Z状态会转变为0;可使用系统函数$isunknown(变量名)检查未知值的传播,若表达式中出现存在X或Z时返回1。

四值逻辑类型:

  • wire:(Verilog中)主要用在assign语句中,起连接作用。(SystemVerilog中)用在一个信号存在多个驱动时,例如双向总线建模;
  • reg:(Verilog中)主要用在initial和always语句中,对线路建模。(SystemVerilog中)常用在寄存器类型;
  • logic:(SV增补)logic是对Verilog中reg类型的增强,不仅可以作为变量,还可以被连续赋值语句、门单元和模块驱动。可用在assign、initial、always语句中,但不能多驱动(多驱动时需要定义成线网类型);

典型的四值变量除了上述三种类型外,还有integer、time两种类型。

二值逻辑类型:

  • bit:用户自定义位宽大小,无符号变量;
  • byte:8位宽,有符号变量,取值范围:-128~127;
  • shortint:16位宽,有符号变量,取值范围:-215~215-1;
  • int:32位宽,有符号变量,取值范围:-231~231-1;
  • longint:64位宽,有符号变量,取值范围:-263~263-1。
module logic_types_demo;
    // 四值逻辑类型
    logic [7:0] logic_var;     // 默认初始值为x
    reg [7:0] reg_var;         // 默认初始值为x
    wire [7:0] wire_var;       // 默认初始值为z
    integer int_var;           // 默认初始值为x
    
    // 二值逻辑类型
    bit [7:0] bit_var;         // 默认初始值为0
    byte byte_var;             // 默认初始值为0
    shortint short_var;        // 默认初始值为0
    int int2_var;              // 默认初始值为0
    longint long_var;          // 默认初始值为0
    
    initial begin
        $display("四值逻辑类型初始值:");
        $display("logic_var = %b", logic_var);
        $display("reg_var = %b", reg_var);
        $display("int_var = %d", int_var);
        
        $display("\n二值逻辑类型初始值:");
        $display("bit_var = %b", bit_var);
        $display("byte_var = %d", byte_var);
        $display("short_var = %d", short_var);
        $display("int2_var = %d", int2_var);
        $display("long_var = %d", long_var);
        
        // 测试未知值检查
        logic_var = 8'bxxxx_zzzz;
        if ($isunknown(logic_var))
            $display("logic_var包含未知值");
    end
endmodule
1.1.2 符号类型

SystemVerilog中的数据类型可以分为有符号和无符号两种:

有符号类型:

  • byte, shortint, int, longint:默认为有符号
  • integer:有符号类型
  • 可以使用signed关键字声明有符号类型

无符号类型:

  • bit, logic, reg, wire:默认为无符号
  • 可以使用unsigned关键字声明无符号类型
module signed_types_demo;
    // 有符号类型
    byte signed_byte = -50;           // 8位有符号
    shortint signed_short = -1000;    // 16位有符号
    int signed_int = -100000;         // 32位有符号
    longint signed_long = -1000000;   // 64位有符号
    
    // 无符号类型
    bit [7:0] unsigned_bit = 200;     // 8位无符号
    logic [15:0] unsigned_logic = 50000; // 16位无符号
    
    // 显式声明符号性
    logic signed [7:0] signed_logic = -100;
    bit unsigned [7:0] unsigned_bit2 = 255;
    
    initial begin
        $display("有符号类型:");
        $display("signed_byte = %d", signed_byte);
        $display("signed_short = %d", signed_short);
        $display("signed_int = %d", signed_int);
        $display("signed_long = %d", signed_long);
        
        $display("\n无符号类型:");
        $display("unsigned_bit = %d", unsigned_bit);
        $display("unsigned_logic = %d", unsigned_logic);
        
        $display("\n显式声明:");
        $display("signed_logic = %d", signed_logic);
        $display("unsigned_bit2 = %d", unsigned_bit2);
        
        // 符号扩展示例
        logic [3:0] small = 4'b1111;  // -1 (如果是有符号)
        logic [7:0] extended;
        extended = small;  // 零扩展:00001111
        $display("零扩展结果: %b", extended);
        
        logic signed [3:0] small_signed = 4'b1111;  // -1
        logic signed [7:0] extended_signed;
        extended_signed = small_signed;  // 符号扩展:11111111
        $display("符号扩展结果: %b", extended_signed);
    end
endmodule
1.1.3 类型转换

SystemVerilog提供了多种类型转换方式:

隐式转换:

  • 自动进行位宽调整
  • 符号性可能改变
  • 可能丢失精度

显式转换:

  • 使用$cast()系统函数
  • 使用’()强制转换
  • 更安全的转换方式
module type_conversion_demo;
    bit [7:0] bit8;
    bit [15:0] bit16;
    int signed_int;
    byte signed_byte;
    real real_var;
    
    initial begin
        // 隐式转换
        bit8 = 8'hFF;
        bit16 = bit8;        // 零扩展:0x00FF
        $display("隐式转换 - bit8: 0x%h, bit16: 0x%h", bit8, bit16);
        
        bit16 = 16'h1234;
        bit8 = bit16;        // 截断:0x34
        $display("截断转换 - bit16: 0x%h, bit8: 0x%h", bit16, bit8);
        
        // 符号转换
        signed_int = -100;
        bit16 = signed_int;  // 有符号到无符号
        $display("符号转换 - signed_int: %d, bit16: 0x%h", signed_int, bit16);
        
        // 显式转换使用$cast
        real_var = 3.14;
        if ($cast(signed_int, real_var))
            $display("$cast成功 - real: %f, int: %d", real_var, signed_int);
        else
            $display("$cast失败");
        
        // 强制转换
        signed_byte = byte'(signed_int);
        $display("强制转换 - int: %d, byte: %d", signed_int, signed_byte);
        
        // 类型转换函数
        bit16 = 16'h8000;
        $display("$signed(bit16) = %d", $signed(bit16));    // 转为有符号
        $display("$unsigned(bit16) = %d", $unsigned(bit16)); // 转为无符号
    end
endmodule
1.1.4 流操作符

流操作符用于在不同数据类型之间进行打包和解包操作:

打包操作符 {>>}:

  • 将数据流打包成向量
  • 支持不同的打包方式

解包操作符 {<<}:

  • 将向量解包成数据流
  • 支持不同的解包方式
module streaming_operators_demo;
    bit [31:0] word;
    bit [7:0] bytes[4];
    bit [3:0] nibbles[8];
    
    initial begin
        // 初始化数据
        bytes[0] = 8'hAA;
        bytes[1] = 8'hBB;
        bytes[2] = 8'hCC;
        bytes[3] = 8'hDD;
        
        // 打包操作 - 将字节数组打包成字
        word = {>>byte{bytes}};
        $display("打包结果: word = 0x%h", word);
        
        // 解包操作 - 将字解包成字节数组
        word = 32'h12345678;
        {>>byte{bytes}} = word;
        $display("解包结果:");
        for (int i = 0; i < 4; i++)
            $display("  bytes[%0d] = 0x%h", i, bytes[i]);
        
        // 不同粒度的流操作
        word = 32'hABCDEF01;
        
        // 按半字节解包
        {>>4{nibbles}} = word;
        $display("\n按半字节解包:");
        for (int i = 0; i < 8; i++)
            $display("  nibbles[%0d] = 0x%h", i, nibbles[i]);
        
        // 反向流操作
        {<<byte{bytes}} = word;
        $display("\n反向解包:");
        for (int i = 0; i < 4; i++)
            $display("  bytes[%0d] = 0x%h", i, bytes[i]);
        
        // 带大小的流操作
        bit [15:0] data_stream = 16'h1234;
        bit [3:0] four_bits[4];
        {>>4{four_bits}} = data_stream;
        $display("\n流操作结果:");
        for (int i = 0; i < 4; i++)
            $display("  four_bits[%0d] = 0x%h", i, four_bits[i]);
    end
endmodule

1.2 定宽数组

1.2.1 一维定宽数组

定宽数组是在编译时确定大小的数组,类似于Verilog中的数组,但SystemVerilog提供了更多的功能和语法糖。

module fixed_array_demo;
    // 一维定宽数组声明
    int fixed_array[10];           // 10个整数的数组
    bit [7:0] byte_array[0:15];    // 16个字节的数组
    logic [31:0] word_array[1:8];  // 8个字的数组,索引从1开始
    
    // 数组初始化
    int init_array[5] = '{1, 2, 3, 4, 5};  // 列表初始化
    bit [3:0] pattern[4] = '{4'h1, 4'h2, 4'h3, 4'h4};
    
    initial begin
        // 数组赋值
        for (int i = 0; i < 10; i++)
            fixed_array[i] = i * i;  // 平方数
        
        // 数组访问
        $display("定宽数组内容:");
        for (int i = 0; i < 10; i++)
            $display("  fixed_array[%0d] = %0d", i, fixed_array[i]);
        
        // 初始化数组显示
        $display("\n初始化数组:");
        for (int i = 0; i < 5; i++)
            $display("  init_array[%0d] = %0d", i, init_array[i]);
        
        // 数组切片操作
        byte_array[0:3] = '{8'hAA, 8'hBB, 8'hCC, 8'hDD};
        $display("\n数组切片:");
        for (int i = 0; i < 4; i++)
            $display("  byte_array[%0d] = 0x%h", i, byte_array[i]);
    end
endmodule
1.2.2 常量数组

常量数组在声明时初始化,之后不能修改:

module const_array_demo;
    // 常量数组声明
    const int const_array[5] = '{10, 20, 30, 40, 50};
    const bit [7:0] lookup_table[16] = '{
        8'h00, 8'h01, 8'h04, 8'h09, 8'h10, 8'h19, 8'h24, 8'h31,
        8'h40, 8'h51, 8'h64, 8'h79, 8'h90, 8'hA9, 8'hC4, 8'hE1
    };
    
    // 参数化常量数组
    parameter int COEFF[4] = '{1, 2, 4, 8};
    
    initial begin
        $display("常量数组内容:");
        for (int i = 0; i < 5; i++)
            $display("  const_array[%0d] = %0d", i, const_array[i]);
        
        $display("\n查找表:");
        for (int i = 0; i < 16; i++)
            $display("  lookup_table[%0d] = 0x%h", i, lookup_table[i]);
        
        $display("\n参数数组:");
        for (int i = 0; i < 4; i++)
            $display("  COEFF[%0d] = %0d", i, COEFF[i]);
        
        // 常量数组不能修改
        // const_array[0] = 100;  // 编译错误
    end
endmodule
1.2.3 多维数组

SystemVerilog支持多维数组,可以创建矩阵和更复杂的数据结构:

module multidim_array_demo;
    // 多维数组声明
    int matrix[4][4];              // 4x4矩阵
    bit [7:0] image[32][32];       // 32x32图像
    logic [15:0] cube[8][8][8];    // 8x8x8立方体
    
    // 初始化多维数组
    int identity[3][3] = '{
        '{1, 0, 0},
        '{0, 1, 0},
        '{0, 0, 1}
    };
    
    initial begin
        // 初始化矩阵
        for (int i = 0; i < 4; i++) begin
            for (int j = 0; j < 4; j++) begin
                matrix[i][j] = i * 4 + j;
            end
        end
        
        // 显示矩阵
        $display("4x4矩阵:");
        for (int i = 0; i < 4; i++) begin
            $write("  ");
            for (int j = 0; j < 4; j++) begin
                $write("%3d ", matrix[i][j]);
            end
            $display("");
        end
        
        // 显示单位矩阵
        $display("\n单位矩阵:");
        for (int i = 0; i < 3; i++) begin
            $write("  ");
            for (int j = 0; j < 3; j++) begin
                $write("%d ", identity[i][j]);
            end
            $display("");
        end
        
        // 数组切片
        matrix[0] = '{10, 11, 12, 13};  // 设置第一行
        $display("\n修改后的第一行:");
        for (int j = 0; j < 4; j++)
            $display("  matrix[0][%0d] = %0d", j, matrix[0][j]);
    end
endmodule
1.2.4 数组的基本操作for和foreach

SystemVerilog提供了for循环和foreach循环来遍历数组:

module array_iteration_demo;
    int array1[10];
    int matrix[3][4];
    bit [7:0] bytes[8];
    
    initial begin
        // 使用for循环初始化
        for (int i = 0; i < 10; i++)
            array1[i] = i * 2;
        
        // 使用foreach循环显示(推荐方式)
        $display("使用foreach遍历一维数组:");
        foreach (array1[i])
            $display("  array1[%0d] = %0d", i, array1[i]);
        
        // 多维数组的foreach
        foreach (matrix[i, j])
            matrix[i][j] = i * 10 + j;
        
        $display("\n使用foreach遍历二维数组:");
        foreach (matrix[i]) begin
            $write("  ");
            foreach (matrix[i][j])
                $write("%3d ", matrix[i][j]);
            $display("");
        end
        
        // 嵌套foreach
        $display("\n使用嵌套foreach:");
        foreach (matrix[i]) begin
            foreach (matrix[i][j]) begin
                $display("  matrix[%0d][%0d] = %0d", i, j, matrix[i][j]);
            end
        end
        
        // foreach的优势:自动处理数组边界
        bytes = '{8'h01, 8'h02, 8'h04, 8'h08, 8'h10, 8'h20, 8'h40, 8'h80};
        $display("\nforeach自动处理边界:");
        foreach (bytes[idx])
            $display("  bytes[%0d] = 0x%h", idx, bytes[idx]);
    end
endmodule

1.3 动态数组

动态数组可以在运行时改变大小,提供了更灵活的内存管理:

module dynamic_array_demo;
    // 动态数组声明
    int dyn_array[];           // 动态整数数组
    bit [7:0] byte_dyn[];      // 动态字节数组
    string str_array[];        // 动态字符串数组
    
    initial begin
        // 分配内存
        dyn_array = new[10];       // 分配10个元素
        byte_dyn = new[5];         // 分配5个元素
        
        // 初始化
        foreach (dyn_array[i])
            dyn_array[i] = i * i;
        
        $display("初始动态数组:");
        foreach (dyn_array[i])
            $display("  dyn_array[%0d] = %0d", i, dyn_array[i]);
        
        // 调整大小(保持原有数据)
        dyn_array = new[15](dyn_array);
        
        // 初始化新元素
        for (int i = 10; i < 15; i++)
            dyn_array[i] = i * 10;
        
        $display("\n扩展后的动态数组:");
        foreach (dyn_array[i])
            $display("  dyn_array[%0d] = %0d", i, dyn_array[i]);
        
        // 数组方法
        $display("\n数组大小: %0d", dyn_array.size());
        
        // 释放内存
        dyn_array.delete();
        $display("删除后数组大小: %0d", dyn_array.size());
        
        // 字符串动态数组
        str_array = new[3];
        str_array[0] = "Hello";
        str_array[1] = "World";
        str_array[2] = "SystemVerilog";
        
        $display("\n字符串数组:");
        foreach (str_array[i])
            $display("  str_array[%0d] = %s", i, str_array[i]);
    end
endmodule

1.4 队列

队列是一种动态数据结构,支持在两端进行插入和删除操作:

module queue_demo;
    // 队列声明
    int queue_unbounded[$];        // 无界队列
    bit [7:0] queue_bounded[$:15]; // 有界队列,最大16个元素
    string str_queue[$];
    
    initial begin
        // 队列操作
        queue_unbounded.push_back(10);    // 尾部插入
        queue_unbounded.push_back(20);
        queue_unbounded.push_back(30);
        queue_unbounded.push_front(5);    // 头部插入
        
        $display("初始队列:");
        foreach (queue_unbounded[i])
            $display("  queue[%0d] = %0d", i, queue_unbounded[i]);
        
        // 队列访问
        $display("\n队列信息:");
        $display("  大小: %0d", queue_unbounded.size());
        $display("  第一个元素: %0d", queue_unbounded[0]);
        $display("  最后一个元素: %0d", queue_unbounded[$]);
        
        // 弹出操作
        int front_val = queue_unbounded.pop_front();
        int back_val = queue_unbounded.pop_back();
        $display("\n弹出操作:");
        $display("  从前端弹出: %0d", front_val);
        $display("  从后端弹出: %0d", back_val);
        
        $display("\n弹出后的队列:");
        foreach (queue_unbounded[i])
            $display("  queue[%0d] = %0d", i, queue_unbounded[i]);
        
        // 插入到指定位置
        queue_unbounded.insert(1, 15);    // 在索引1处插入15
        $display("\n插入后的队列:");
        foreach (queue_unbounded[i])
            $display("  queue[%0d] = %0d", i, queue_unbounded[i]);
        
        // 删除指定位置
        queue_unbounded.delete(0);        // 删除索引0的元素
        $display("\n删除后的队列:");
        foreach (queue_unbounded[i])
            $display("  queue[%0d] = %0d", i, queue_unbounded[i]);
        
        // 字符串队列示例
        str_queue.push_back("First");
        str_queue.push_back("Second");
        str_queue.push_back("Third");
        
        $display("\n字符串队列:");
        foreach (str_queue[i])
            $display("  str_queue[%0d] = %s", i, str_queue[i]);
    end
endmodule

1.5 关联数组

关联数组使用任意类型作为索引,适用于稀疏数据和查找表:

module associative_array_demo;
    // 关联数组声明
    int assoc_int[string];         // 字符串索引的整数数组
    bit [31:0] sparse_array[int];  // 整数索引的稀疏数组
    string name_table[int];        // 整数索引的字符串表
    
    // 自定义类型作为索引
    typedef struct {
        int x, y;
    } point_t;
    
    int point_values[point_t];     // 结构体索引的数组
    
    initial begin
        // 字符串索引数组
        assoc_int["apple"] = 100;
        assoc_int["banana"] = 200;
        assoc_int["cherry"] = 300;
        assoc_int["date"] = 400;
        
        $display("字符串索引数组:");
        foreach (assoc_int[key])
            $display("  %s = %0d", key, assoc_int[key]);
        
        // 稀疏数组 - 只存储非零值
        sparse_array[1000] = 32'hDEAD;
        sparse_array[5000] = 32'hBEEF;
        sparse_array[10000] = 32'hCAFE;
        sparse_array[50000] = 32'hBABE;
        
        $display("\n稀疏数组:");
        foreach (sparse_array[idx])
            $display("  [%0d] = 0x%h", idx, sparse_array[idx]);
        
        // 关联数组方法
        $display("\n数组信息:");
        $display("  字符串数组大小: %0d", assoc_int.size());
        $display("  稀疏数组大小: %0d", sparse_array.size());
        
        // 检查键是否存在
        if (assoc_int.exists("apple"))
            $display("  'apple'存在,值为: %0d", assoc_int["apple"]);
        
        if (!assoc_int.exists("grape"))
            $display("  'grape'不存在");
        
        // 删除元素
        assoc_int.delete("banana");
        $display("\n删除'banana'后:");
        foreach (assoc_int[key])
            $display("  %s = %0d", key, assoc_int[key]);
        
        // 结构体索引示例
        point_t p1 = '{x: 10, y: 20};
        point_t p2 = '{x: 30, y: 40};
        
        point_values[p1] = 100;
        point_values[p2] = 200;
        
        $display("\n结构体索引数组:");
        foreach (point_values[pt])
            $display("  (%0d,%0d) = %0d", pt.x, pt.y, point_values[pt]);
    end
endmodule

1.6 数组的方法

1.6.1 数组直接赋值

SystemVerilog提供了多种数组赋值方法:

module array_assignment_demo;
    int array1[5];
    int array2[5];
    int dyn_array[];
    int queue_data[$];
    
    initial begin
        // 列表赋值
        array1 = '{1, 2, 3, 4, 5};
        $display("列表赋值:");
        foreach (array1[i])
            $display("  array1[%0d] = %0d", i, array1[i]);
        
        // 重复赋值
        array2 = '{5{10}};  // 所有元素都是10
        $display("\n重复赋值:");
        foreach (array2[i])
            $display("  array2[%0d] = %0d", i, array2[i]);
        
        // 混合赋值
        array1 = '{1, 2, default: 0};  // 前两个元素为1,2,其余为0
        $display("\n混合赋值:");
        foreach (array1[i])
            $display("  array1[%0d] = %0d", i, array1[i]);
        
        // 索引赋值
        array2 = '{0: 100, 2: 200, 4: 300, default: -1};
        $display("\n索引赋值:");
        foreach (array2[i])
            $display("  array2[%0d] = %0d", i, array2[i]);
        
        // 动态数组赋值
        dyn_array = '{10, 20, 30, 40};
        $display("\n动态数组赋值:");
        foreach (dyn_array[i])
            $display("  dyn_array[%0d] = %0d", i, dyn_array[i]);
        
        // 队列赋值
        queue_data = '{100, 200, 300};
        $display("\n队列赋值:");
        foreach (queue_data[i])
            $display("  queue_data[%0d] = %0d", i, queue_data[i]);
        
        // 数组复制
        array1 = array2;
        $display("\n数组复制后array1:");
        foreach (array1[i])
            $display("  array1[%0d] = %0d", i, array1[i]);
    end
endmodule
1.6.2 数组大小返回

不同类型的数组提供了不同的大小查询方法:

module array_size_demo;
    int fixed_array[10];
    int dyn_array[];
    int queue_data[$];
    int assoc_array[string];
    
    initial begin
        // 固定数组大小
        $display("固定数组大小: %0d", $size(fixed_array));
        $display("固定数组维度: %0d", $dimensions(fixed_array));
        $display("固定数组左边界: %0d", $left(fixed_array));
        $display("固定数组右边界: %0d", $right(fixed_array));
        $display("固定数组低位: %0d", $low(fixed_array));
        $display("固定数组高位: %0d", $high(fixed_array));
        
        // 动态数组
        dyn_array = new[15];
        $display("\n动态数组大小: %0d", dyn_array.size());
        
        // 队列
        queue_data = '{1, 2, 3, 4, 5, 6};
        $display("\n队列大小: %0d", queue_data.size());
        
        // 关联数组
        assoc_array["key1"] = 100;
        assoc_array["key2"] = 200;
        assoc_array["key3"] = 300;
        $display("\n关联数组大小: %0d", assoc_array.size());
        
        // 多维数组
        int matrix[3][4];
        $display("\n多维数组信息:");
        $display("  总大小: %0d", $size(matrix));
        $display("  第一维大小: %0d", $size(matrix, 1));
        $display("  第二维大小: %0d", $size(matrix, 2));
        
        // 数组为空检查
        int empty_queue[$];
        if (empty_queue.size() == 0)
            $display("\n队列为空");
        
        // 动态调整大小
        dyn_array = new[20](dyn_array);
        $display("\n调整后动态数组大小: %0d", dyn_array.size());
    end
endmodule
1.6.3 数组运算方法

SystemVerilog提供了丰富的数组运算方法:

module array_operations_demo;
    int numbers[] = '{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int even_numbers[$];
    int sum_result;
    
    initial begin
        // 数组求和
        sum_result = numbers.sum();
        $display("数组求和: %0d", sum_result);
        
        // 数组乘积
        int product = numbers.product();
        $display("数组乘积: %0d", product);
        
        // 数组异或
        int xor_result = numbers.xor();
        $display("数组异或: %0d", xor_result);
        
        // 数组与运算
        int and_result = numbers.and();
        $display("数组与运算: %0d", and_result);
        
        // 数组或运算
        int or_result = numbers.or();
        $display("数组或运算: %0d", or_result);
        
        // 查找方法
        even_numbers = numbers.find(x) with (x % 2 == 0);
        $display("\n偶数查找结果:");
        foreach (even_numbers[i])
            $display("  even_numbers[%0d] = %0d", i, even_numbers[i]);
        
        // 查找索引
        int indices[$] = numbers.find_index(x) with (x > 5);
        $display("\n大于5的元素索引:");
        foreach (indices[i])
            $display("  index[%0d] = %0d, value = %0d", i, indices[i], numbers[indices[i]]);
        
        // 查找第一个匹配
        int first_even[$] = numbers.find_first(x) with (x % 2 == 0);
        if (first_even.size() > 0)
            $display("\n第一个偶数: %0d", first_even[0]);
        
        // 查找最后一个匹配
        int last_even[$] = numbers.find_last(x) with (x % 2 == 0);
        if (last_even.size() > 0)
            $display("最后一个偶数: %0d", last_even[0]);
        
        // 最小值和最大值
        int min_val[$] = numbers.min();
        int max_val[$] = numbers.max();
        $display("\n最小值: %0d", min_val[0]);
        $display("最大值: %0d", max_val[0]);
        
        // 唯一值
        int duplicates[] = '{1, 2, 2, 3, 3, 3, 4, 5};
        int unique_vals[$] = duplicates.unique();
        $display("\n去重后的数组:");
        foreach (unique_vals[i])
            $display("  unique[%0d] = %0d", i, unique_vals[i]);
    end
endmodule
1.6.4 数组排序方法

SystemVerilog提供了多种排序方法:

module array_sorting_demo;
    int unsorted[] = '{5, 2, 8, 1, 9, 3, 7, 4, 6};
    int sorted_array[];
    
    // 自定义结构体用于排序
    typedef struct {
        string name;
        int age;
        real salary;
    } person_t;
    
    person_t people[] = '{
        '{"Alice", 30, 50000.0},
        '{"Bob", 25, 45000.0},
        '{"Charlie", 35, 60000.0},
        '{"David", 28, 48000.0}
    };
    
    initial begin
        $display("原始数组:");
        foreach (unsorted[i])
            $display("  [%0d] = %0d", i, unsorted[i]);
        
        // 升序排序
        sorted_array = unsorted;
        sorted_array.sort();
        $display("\n升序排序:");
        foreach (sorted_array[i])
            $display("  [%0d] = %0d", i, sorted_array[i]);
        
        // 降序排序
        sorted_array = unsorted;
        sorted_array.rsort();
        $display("\n降序排序:");
        foreach (sorted_array[i])
            $display("  [%0d] = %0d", i, sorted_array[i]);
        
        // 随机排序
        sorted_array = unsorted;
        sorted_array.shuffle();
        $display("\n随机排序:");
        foreach (sorted_array[i])
            $display("  [%0d] = %0d", i, sorted_array[i]);
        
        // 反转数组
        sorted_array = unsorted;
        sorted_array.reverse();
        $display("\n反转数组:");
        foreach (sorted_array[i])
            $display("  [%0d] = %0d", i, sorted_array[i]);
        
        // 结构体排序 - 按年龄排序
        $display("\n原始人员信息:");
        foreach (people[i])
            $display("  %s: age=%0d, salary=%.0f", people[i].name, people[i].age, people[i].salary);
        
        people.sort(x) with (x.age);
        $display("\n按年龄排序:");
        foreach (people[i])
            $display("  %s: age=%0d, salary=%.0f", people[i].name, people[i].age, people[i].salary);
        
        // 按薪水降序排序
        people.rsort(x) with (x.salary);
        $display("\n按薪水降序排序:");
        foreach (people[i])
            $display("  %s: age=%0d, salary=%.0f", people[i].name, people[i].age, people[i].salary);
    end
endmodule
1.6.5 数组定位方法

数组定位方法用于查找满足特定条件的元素:

module array_locator_demo;
    int data[] = '{10, 25, 30, 45, 50, 65, 70, 85, 90};
    string words[] = '{"apple", "banana", "cherry", "date", "elderberry"};
    
    initial begin
        $display("原始数据:");
        foreach (data[i])
            $display("  data[%0d] = %0d", i, data[i]);
        
        // find - 查找所有匹配的元素
        int multiples_of_5[$] = data.find(x) with (x % 5 == 0);
        $display("\n5的倍数:");
        foreach (multiples_of_5[i])
            $display("  [%0d] = %0d", i, multiples_of_5[i]);
        
        // find_index - 查找匹配元素的索引
        int indices[$] = data.find_index(x) with (x > 50);
        $display("\n大于50的元素索引:");
        foreach (indices[i])
            $display("  index %0d: data[%0d] = %0d", i, indices[i], data[indices[i]]);
        
        // find_first - 查找第一个匹配的元素
        int first_large[$] = data.find_first(x) with (x > 40);
        if (first_large.size() > 0)
            $display("\n第一个大于40的元素: %0d", first_large[0]);
        
        // find_first_index - 查找第一个匹配元素的索引
        int first_index[$] = data.find_first_index(x) with (x > 40);
        if (first_index.size() > 0)
            $display("第一个大于40的元素索引: %0d", first_index[0]);
        
        // find_last - 查找最后一个匹配的元素
        int last_even[$] = data.find_last(x) with (x % 2 == 0);
        if (last_even.size() > 0)
            $display("\n最后一个偶数: %0d", last_even[0]);
        
        // find_last_index - 查找最后一个匹配元素的索引
        int last_index[$] = data.find_last_index(x) with (x % 2 == 0);
        if (last_index.size() > 0)
            $display("最后一个偶数的索引: %0d", last_index[0]);
        
        // 字符串数组查找
        $display("\n字符串数组:");
        foreach (words[i])
            $display("  words[%0d] = %s", i, words[i]);
        
        // 查找长度大于5的字符串
        string long_words[$] = words.find(s) with (s.len() > 5);
        $display("\n长度大于5的字符串:");
        foreach (long_words[i])
            $display("  [%0d] = %s (length: %0d)", i, long_words[i], long_words[i].len());
        
        // 查找包含特定字符的字符串
        string words_with_a[$] = words.find(s) with (s.substr(0, 0) == "a" || 
                                                     s.substr(0, 0) == "A");
        $display("\n以'a'或'A'开头的字符串:");
        foreach (words_with_a[i])
            $display("  [%0d] = %s", i, words_with_a[i]);
    end
endmodule

1.7 结构体

结构体允许将相关的数据组合在一起,提供更好的数据组织方式:

module struct_detailed_demo;
    // 基本结构体定义
    typedef struct {
        logic [31:0] addr;
        logic [31:0] data;
        logic [3:0]  be;     // byte enable
        logic        valid;
    } bus_transaction_t;
    
    // 打包结构体 - 可以作为向量使用
    typedef struct packed {
        logic [7:0]  opcode;
        logic [4:0]  rs1;
        logic [4:0]  rs2;
        logic [4:0]  rd;
        logic [10:0] immediate;
    } instruction_t;
    
    // 嵌套结构体
    typedef struct {
        string       name;
        int          id;
        struct {
            string   street;
            string   city;
            int      zip;
        } address;
    } employee_t;
    
    // 联合体
    typedef union packed {
        logic [31:0] word;
        logic [15:0] half[2];
        logic [7:0]  byte[4];
        struct packed {
            logic [15:0] high;
            logic [15:0] low;
        } parts;
    } word_union_t;
    
    bus_transaction_t trans1, trans2;
    instruction_t inst;
    employee_t emp;
    word_union_t word_data;
    
    initial begin
        // 结构体初始化
        trans1 = '{addr: 32'h1000, data: 32'hDEADBEEF, be: 4'hF, valid: 1'b1};
        
        // 成员访问
        $display("总线事务:");
        $display("  地址: 0x%h", trans1.addr);
        $display("  数据: 0x%h", trans1.data);
        $display("  字节使能: 0x%h", trans1.be);
        $display("  有效: %b", trans1.valid);
        
        // 结构体赋值
        trans2 = trans1;
        trans2.addr = 32'h2000;
        trans2.data = 32'hCAFEBABE;
        
        $display("\n复制并修改后的事务:");
        $display("  地址: 0x%h", trans2.addr);
        $display("  数据: 0x%h", trans2.data);
        
        // 打包结构体
        inst = '{opcode: 8'h12, rs1: 5'd1, rs2: 5'd2, rd: 5'd3, immediate: 11'h123};
        $display("\n指令结构体:");
        $display("  操作码: 0x%h", inst.opcode);
        $display("  源寄存器1: %0d", inst.rs1);
        $display("  源寄存器2: %0d", inst.rs2);
        $display("  目标寄存器: %0d", inst.rd);
        $display("  立即数: 0x%h", inst.immediate);
        $display("  完整指令: 0x%h", inst);  // 作为向量显示
        
        // 嵌套结构体
        emp.name = "John Doe";
        emp.id = 12345;
        emp.address.street = "123 Main St";
        emp.address.city = "Anytown";
        emp.address.zip = 12345;
        
        $display("\n员工信息:");
        $display("  姓名: %s", emp.name);
        $display("  ID: %0d", emp.id);
        $display("  地址: %s, %s %0d", emp.address.street, emp.address.city, emp.address.zip);
        
        // 联合体使用
        word_data.word = 32'h12345678;
        $display("\n联合体数据:");
        $display("  字: 0x%h", word_data.word);
        $display("  高半字: 0x%h", word_data.parts.high);
        $display("  低半字: 0x%h", word_data.parts.low);
        $display("  字节0: 0x%h", word_data.byte[0]);
        $display("  字节1: 0x%h", word_data.byte[1]);
        $display("  字节2: 0x%h", word_data.byte[2]);
        $display("  字节3: 0x%h", word_data.byte[3]);
    end
endmodule

1.8 枚举类型

1.8.1 定义枚举值

枚举类型提供了一种定义命名常量的方式,提高代码可读性:

module enum_definition_demo;
    // 基本枚举定义
    typedef enum {RED, GREEN, BLUE} color_t;
    
    // 指定枚举值
    typedef enum {IDLE = 0, READ = 1, WRITE = 2, ERROR = 15} state_t;
    
    // 指定基础类型
    typedef enum bit [1:0] {S0, S1, S2, S3} state2_t;
    
    // 枚举范围
    typedef enum {LOW = 0, MID = 5, HIGH = 10} priority_t;
    
    // 字符串枚举(仅用于显示)
    typedef enum {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY} day_t;
    
    color_t my_color;
    state_t current_state;
    state2_t fsm_state;
    priority_t task_priority;
    day_t today;
    
    initial begin
        // 枚举赋值
        my_color = RED;
        current_state = IDLE;
        fsm_state = S0;
        task_priority = HIGH;
        today = MONDAY;
        
        $display("枚举值:");
        $display("  颜色: %s (值: %0d)", my_color.name(), my_color);
        $display("  状态: %s (值: %0d)", current_state.name(), current_state);
        $display("  FSM状态: %s (值: %0d)", fsm_state.name(), fsm_state);
        $display("  优先级: %s (值: %0d)", task_priority.name(), task_priority);
        $display("  今天: %s (值: %0d)", today.name(), today);
        
        // 枚举比较
        if (my_color == RED)
            $display("\n颜色是红色");
        
        if (current_state != ERROR)
            $display("状态正常");
        
        // 枚举范围检查
        if (task_priority >= MID)
            $display("高优先级任务");
        
        // 遍历枚举值
        $display("\n所有颜色:");
        my_color = my_color.first();
        do begin
            $display("  %s = %0d", my_color.name(), my_color);
            my_color = my_color.next();
        end while (my_color != my_color.first());
    end
endmodule
1.8.2 枚举类型的子程序

枚举类型提供了多个内置方法:

module enum_methods_demo;
    typedef enum {RESET, INIT, RUN, PAUSE, STOP, ERROR} state_t;
    typedef enum bit [2:0] {S0, S1, S2, S3, S4} counter_state_t;
    
    state_t current_state;
    counter_state_t cnt_state;
    
    initial begin
        current_state = RESET;
        
        $display("枚举方法演示:");
        
        // first() - 获取第一个枚举值
        $display("  第一个状态: %s", current_state.first().name());
        
        // last() - 获取最后一个枚举值
        $display("  最后一个状态: %s", current_state.last().name());
        
        // next() - 获取下一个枚举值
        current_state = RUN;
        $display("  RUN的下一个状态: %s", current_state.next().name());
        
        // prev() - 获取前一个枚举值
        $display("  RUN的前一个状态: %s", current_state.prev().name());
        
        // num() - 获取枚举值的数量
        $display("  状态总数: %0d", current_state.num());
        
        // name() - 获取枚举值的名称
        current_state = ERROR;
        $display("  当前状态名称: %s", current_state.name());
        
        // 遍历所有枚举值
        $display("\n所有状态:");
        current_state = current_state.first();
        repeat (current_state.num()) begin
            $display("  %s = %0d", current_state.name(), current_state);
            current_state = current_state.next();
        end
        
        // 计数器状态演示
        cnt_state = S0;
        $display("\n计数器状态方法:");
        $display("  当前: %s, 下一个: %s", cnt_state.name(), cnt_state.next().name());
        $display("  第一个: %s, 最后一个: %s", cnt_state.first().name(), cnt_state.last().name());
    end
endmodule
1.8.3 枚举类型的转换

枚举类型可以与其他数据类型进行转换:

module enum_conversion_demo;
    typedef enum bit [2:0] {IDLE, READ, WRITE, WAIT, DONE} state_t;
    typedef enum {LOW, MEDIUM, HIGH} priority_t;
    
    state_t current_state;
    priority_t task_priority;
    int state_value;
    bit [2:0] state_bits;
    
    initial begin
        // 枚举到整数转换
        current_state = READ;
        state_value = current_state;
        $display("枚举转换演示:");
        $display("  状态 %s 的值: %0d", current_state.name(), state_value);
        
        // 整数到枚举转换
        state_value = 3;
        if ($cast(current_state, state_value))
            $display("  值 %0d 对应状态: %s", state_value, current_state.name());
        else
            $display("  值 %0d 无效", state_value);
        
        // 位向量转换
        state_bits = 3'b010;
        if ($cast(current_state, state_bits))
            $display("  位向量 %b 对应状态: %s", state_bits, current_state.name());
        
        // 无效值转换
        state_value = 7;  // 超出枚举范围
        if (!$cast(current_state, state_value))
            $display("  值 %0d 转换失败", state_value);
        
        // 枚举比较
        current_state = READ;
        if (current_state > IDLE)
            $display("  %s 状态值大于 %s", current_state.name(), IDLE.name());
        
        // 枚举运算
        task_priority = MEDIUM;
        $display("\n优先级运算:");
        $display("  当前优先级: %s (值: %0d)", task_priority.name(), task_priority);
        
        // 优先级递增
        if (task_priority != task_priority.last()) begin
            task_priority = task_priority.next();
            $display("  提升后优先级: %s (值: %0d)", task_priority.name(), task_priority);
        end
    end
endmodule

1.9 字符串变量

SystemVerilog提供了强大的字符串处理功能:

module string_demo;
    string str1, str2, str3;
    string str_array[5];
    
    initial begin
        // 字符串赋值
        str1 = "Hello";
        str2 = "World";
        str3 = "SystemVerilog";
        
        $display("字符串基本操作:");
        $display("  str1 = %s", str1);
        $display("  str2 = %s", str2);
        $display("  str3 = %s", str3);
        
        // 字符串连接
        string greeting = {str1, " ", str2, "!"};
        $display("  连接结果: %s", greeting);
        
        // 字符串长度
        $display("\n字符串长度:");
        $display("  str1长度: %0d", str1.len());
        $display("  greeting长度: %0d", greeting.len());
        
        // 字符串比较
        $display("\n字符串比较:");
        if (str1 == "Hello")
            $display("  str1 等于 'Hello'");
        
        if (str1 != str2)
            $display("  str1 不等于 str2");
        
        // 字符串方法
        $display("\n字符串方法:");
        $display("  str3转大写: %s", str3.toupper());
        $display("  str3转小写: %s", str3.tolower());
        
        // 子字符串
        string sub = str3.substr(6, 11);  // "Verilog"
        $display("  子字符串(6,11): %s", sub);
        
        // 字符访问
        $display("  str1第一个字符: %c (ASCII: %0d)", str1.getc(0), str1.getc(0));
        
        // 字符串查找
        int pos = greeting.search("World");
        if (pos >= 0)
            $display("  'World'在greeting中的位置: %0d", pos);
        
        // 字符串替换
        string replaced = greeting;
        replaced.putc(6, "w");  // 将'W'替换为'w'
        $display("  替换后: %s", replaced);
        
        // 字符串数组
        str_array[0] = "First";
        str_array[1] = "Second";
        str_array[2] = "Third";
        str_array[3] = "Fourth";
        str_array[4] = "Fifth";
        
        $display("\n字符串数组:");
        foreach (str_array[i])
            $display("  str_array[%0d] = %s", i, str_array[i]);
        
        // 格式化字符串
        int value = 42;
        real pi = 3.14159;
        string formatted = $sformatf("整数: %0d, 实数: %.2f", value, pi);
        $display("\n格式化字符串: %s", formatted);
        
        // 字符串转换
        string num_str = "123";
        int converted_num = num_str.atoi();
        $display("\n字符串转整数: '%s' -> %0d", num_str, converted_num);
        
        string real_str = "3.14";
        real converted_real = real_str.atoreal();
        $display("字符串转实数: '%s' -> %f", real_str, converted_real);
    end
endmodule

总结

本文档详细介绍了SystemVerilog的数据类型系统,包括:

  1. 内建数据类型:四值逻辑和二值逻辑的区别,符号类型,类型转换和流操作符
  2. 定宽数组:一维数组,常量数组,多维数组和遍历方法
  3. 动态数据结构:动态数组,队列和关联数组的使用
  4. 数组方法:赋值,大小查询,运算,排序和定位方法
  5. 结构化数据:结构体和联合体的定义和使用
  6. 枚举类型:枚举定义,内置方法和类型转换
  7. 字符串处理:字符串变量的各种操作和方法

SystemVerilog的丰富数据类型为验证环境提供了强大的建模能力,使得测试平台的开发更加高效和灵活。掌握这些数据类型的使用是学习SystemVerilog验证的重要基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值