【SystemVerilog学习笔记】4-用户自定义和枚举数据类型

本文详细介绍了SystemVerilog中typedef的用法,用于创建用户自定义类型,增强了代码的可读性和可维护性。同时,枚举类型在SystemVerilog中的引入使得变量的取值范围更加明确,提供了强类型检查。枚举类型可以指定标签序列,具有作用域限制,并且支持自定义和匿名枚举。此外,还讨论了枚举类型的值、类型、操作以及相关的系统任务和方法,强调了枚举类型在值和标签上的强类型特性。


SystemVerilog对Verilog的拓展:

  • 使用typedef建立用户自定义类型。
  • 使用enum建立枚举类型。

4.1 typedef

  • 对比

    VerilogSystemVerilog
    主要用于RTL和门级建模,没有提供类似C语言的高级抽象建模的变量类型为系统级和体系结构级建模加入了许多新的数据类型,允许通过typedef关键字使用现有数据类型建立用户自定义类型

    例子:自定义类型

    typedef int unsigned uint;//利用现有数据类型(int unsigned)建立用户自定义类型(uint)
    ...
    uint a, b;
    
  • 用户自定义类型的位置:

    • 在局部定义。

    • 包内定义。

    • 编译单元域$unit.

    • 在包中定义然后导入到$uint。

      例子:在包中定义然后导入到$uint。

      //包声明
      package chip_types
          `ifdef TWO_STATE
              typedef bit dtype_t;
          `else
              typedef logic dtype_t;
          `endif
      endpackage
      //导入定义到$uint
      import chip_types::dtype_t;
      //模块声明
      module counter (
          output dtype_t[15:0]  count,
          input  dtype_t        clock   
      );
      always @(posedge clock, negedge resetN)
          if (!resetN)
              count <= 0;
          else
              count <= count + 1;
      endmodule
      
  • 命名习惯提示:使用“_t”作为用户自定义类型的结尾,增加代码可读性与便于维护。

4.2 枚举数据类型

  • 定义:
    枚举数据类型提供一种方式来声明一个具有特定允许值列表的抽象变量,每一个值都有一个确定的用户自定义的名字(label)。
    例子:

    //具有red, green, blue三个label的枚举变量RGB
    enum {red, green, blue} RGB;
    
  • 对比:

    VerilogSystemVerilog
    没有枚举类型,通过parameter或者宏定义实现标签值。无法限定信号的有效值(比如状态机的状态)的有效值仅仅是自定义的有效状态。增加了枚举类型,将一个变量的取值范围限制在有意义的label上。
  • 注意:

    • 当枚举类型定义从package中导入时,只有类型名被导入。
    • 为了使枚举类型的label可见,可以显式导入每个label,或者使用通配符导入。

4.2.1 指定枚举类型标签序列的方式:

方式说明
state创建单个名为state的标签。
state[N]创建名为state0,state1,…,stateN的标签序列。
state[N,M]N<M时,创建名为stateN,stateN+1,…,stateN的标签序列。N>M时,创建名为stateN,stateN-1,…,stateN的标签序列。

例子:

enum {RESET,S[5],W[6:9]} state;//label列表为:RESET,S0,...,S5,W6,...,W9。

4.2.2 枚举类型标签作用域:

枚举类型列表中的标签在同一个作用域必须是唯一的。

4.2.3 枚举类型值:

  • 缺省的情况下,从零开始递增。中间缺省的情况下,从前一个已说明的数值开始递增。
  • 允许显式说明值,但是值必须唯一。
    例子:
    enum {A,B,C=5,D,E} list;//标签值为:A=0,B=1,C=5,D=6,E=7。
    

4.2.4 枚举类型标签值的类型:

  • 默认为int类型。
  • 可以显示定义基类,但是标签值得符合基类得取值范围和位宽。
  • 如果X或者Z赋值给枚举列表中的一个标签,下一个标签必须显式赋值。以此类推,如果再下一个也没有显式赋值也必须显式赋值。
    例子:
    enum {A = 3'b001, B = 3'b010, C = 3'b100} list;//错误,位宽不对
    enum logic {A1 = 1'b0, B1, C1} list1;//错误,标签数超过基类所能代表得宽度。
    

4.2.5 自定义和匿名枚举

由typedef声明的枚举类型称为自定义枚举类型,不适用typedef定义的则称为匿名枚举类型。

4.2.6 枚举类型操作的强类型检验

  • 枚举类型合法的赋值:
    • 值为枚举类型列表中的一个标签。
    • 同类枚举类型的其他变量。
    • 通过cast系统函数转换成枚举类型变量的数值。

例子:

typedef enum {WAIT, LOAD, READY} state_t;
state_t state, next_state;
int foo;
foo = 1;
state = WAIT;//合法,值为枚举类型列表中的一个标签。
state = next_state;//合法,同类枚举类型的其他变量。
$cast(state, foo);//合法,通过cast系统函数转换成枚举类型变量的数值。

state = foo; //非法
state = state + 1;//非法
state++;//非法
next_state += state;//非法

4.2.8 枚举类型的专用系统任务和方法

  • <枚举变量名>.first;//返回第一个成员的值。
  • <枚举变量名>.last;
  • <枚举变量名>.next();//返回列表中下N个成员的值,如果到了末尾则会返回到列表开头。
  • <枚举变量名>.prev();
  • <枚举变量名>.num;//返回列表元素个数。
  • <枚举变量名>.num;//返回这个值对应的label,字符串。

4.2.9 打印枚举类型

$display("\nCurrent state is %s(%b)",state.name,state);
### SystemVerilog 中定义复杂用户自定义数据类型SystemVerilog中,`struct``union`是两种重要的复合数据类型,用于创建更复杂的用户自定义数据结构。以下将详细介绍如何使用这两种数据类型,并结合示例展示其用法。 #### 1. 使用 `struct` 定义复杂数据类型 `struct` 是一种复合数据类型,允许将多个不同类型的变量组合成一个单一实体[^1]。它在硬件设计验证中广泛使用,适合表示复杂的数据结构,如数据包、配置寄存器或状态信息。 ##### 示例代码 ```systemverilog typedef struct { bit [7:0] red; bit [7:0] green; bit [7:0] blue; logic valid; } rgb_t; rgb_t pixel; initial begin pixel.red = 8'hFF; pixel.green = 8'hAA; pixel.blue = 8'h55; pixel.valid = 1'b1; $display("Pixel: R=%h G=%h B=%h Valid=%b", pixel.red, pixel.green, pixel.blue, pixel.valid); end ``` 上述代码定义了一个名为 `rgb_t` 的结构体类型,包含红、绿、蓝三个颜色分量以及一个有效位。通过这种方式,可以方便地组织相关数据。 #### 2. 使用 `union` 定义共享存储空间 `union` 是另一种复合数据类型,允许多个成员共享同一块存储空间[^3]。这使得可以在不同的格式或视图下解释相同的数据。 ##### 示例代码 ```systemverilog typedef union { bit [31:0] raw_data; struct { bit [15:0] data_low; bit [15:0] data_high; } split_data; } data_t; data_t my_data; initial begin my_data.raw_data = 32'h DEADBEEF; $display("Raw Data: %h", my_data.raw_data); $display("Data Low: %h, Data High: %h", my_data.split_data.data_low, my_data.split_data.data_high); end ``` 在此示例中,`data_t` 类型的变量 `my_data` 可以通过 `raw_data` 或 `split_data` 访问相同的存储空间。这种灵活性非常适合需要紧凑存储的应用场景。 #### 3. 嵌套结构与参数化 `struct` `union` 可以嵌套使用,并支持参数化,从而进一步增强其功能。 ##### 示例代码 ```systemverilog typedef struct { bit [7:0] address; bit [7:0] length; union { bit [31:0] raw_payload; struct { bit [15:0] command; bit [15:0] status; } parsed_payload; } payload; } packet_t; packet_t pkt; initial begin pkt.address = 8'h AA; pkt.length = 8'h 05; pkt.payload.raw_payload = 32'h ABCD1234; $display("Packet Address: %h, Length: %h", pkt.address, pkt.length); $display("Payload Raw: %h", pkt.payload.raw_payload); $display("Payload Command: %h, Status: %h", pkt.payload.parsed_payload.command, pkt.payload.parsed_payload.status); end ``` 此示例展示了如何将 `struct` `union` 嵌套使用,形成更复杂的包结构[^1]。 #### 4. 枚举类型 `enum` 除了 `struct` `union`,`enum` 也是一种常用的用户自定义数据类型,用于定义一组相关的常量值[^4]。 ##### 示例代码 ```systemverilog typedef enum logic [1:0] { IDLE, READ, WRITE } state_t; state_t current_state; initial begin current_state = IDLE; $display("Current State: %s", current_state.name()); current_state = READ; $display("Current State: %s", current_state.name()); end ``` `enum` 提供了一种简洁的方式定义状态机的状态或其他枚举值集合。 #### 5. 类型转换与 `$cast` 在某些情况下,可能需要在不同数据类型之间进行转换。SystemVerilog 提供了 `$cast` 函数来实现这一功能[^5]。 ##### 示例代码 ```systemverilog bit [31:0] raw_value = 32'h DEADBEEF; logic [31:0] converted_value; initial begin if ($cast(converted_value, raw_value)) begin $display("Cast Successful: %h", converted_value); end else begin $display("Cast Failed"); end end ``` `$cast` 函数可以检查类型转换是否成功,并返回相应的结果[^5]。 ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ChipWeaver

觉得有用的话点个赞吧 :)

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值