用于设计的可综合SV:SystemVerilog不仅仅用于验证!

导言

在数字电路设计领域,通常我们认为Verilog是一种设计语言,而SystemVerilog是专门用于验证的语言,不能用于设计。然而,这种观念是不准确的!事实上,SystemVerilog同样适用于设计,在某些方面甚至比Verilog更为便利。SystemVerilog在其设计之初,主要目标之一是更准确地创建可综合(synthesizable)的复杂硬件设计模型,并且使用更少的代码行数来实现这一目标。SystemVerilog-2005 并不是一种独立的语言,它只是 Verilog-2005 之上的一组扩展。IEEE在2009年,已将"Verilog"正式更名为"SystemVerilog"。

1SV的优劣

优势

  • 大多数主流EDA工具中都被支持,如Vivado、Quarturs、VCS、Modelsim都支持SystemVerilog设计。

  • SystemVerilog是Verilog的扩展,因此兼容Verilog。这意味着现有的Verilog代码可以逐步迁移到SystemVerilog,而且系统设计团队可以逐步采用SystemVerilog,无需彻底改变整个设计流程。

  • SystemVerilog引入了许多高级建模特性,如类、接口、和多态性。这些特性使得对复杂硬件结构进行更抽象和结构化的建模变得更为容易,减少了代码量,提高了可读性和可维护性。

  • SystemVerilog在某种程度上可以帮助减少编码时的拼写错误(coding typo)。

劣势

  • 一些SystemVerilog特性可能会导致生成的硬件设计占用更多的资源。使用某些高级特性可能增加逻辑的复杂性,从而影响实际的硬件资源消耗。

总体分析

总的来说,虽然SystemVerilog存在一些被批评的缺点,我们可以通过在设计上引入一些限制来避免这些问题,就像在编写Verilog时会避免使用for循环来设计电路一样。同时,由于SystemVerilog具备向下兼容性,意味着verilog不需要改任何代码,我们可以轻松地从".v"文件切换到".sv"文件,这为团队提供了更灵活的选择。

2SV可综合语法介绍

logic

传统的Verilog对于信号类型有严格的限制,通过reg和wire来描述一个信号。有时候,由于笔误或者混淆,我们可能会误将reg和wire搞混,从而导致编译错误。而SystemVerilog简化了这一过程,所有的信号都可以使用logic进行声明,使得代码更加清晰和一致。

lua 复制代码
module chip
(input wire in1,
input wire in2,
output reg out1,
output wire out2
);
lua 复制代码
module chip
(input logic in1,
input logic in2,
output logic out1,
output logic out2
);

enum

枚举类型在SystemVerilog中有一些严格的规则:

  • 标签值必须与变量相同大小:枚举类型在定义时会指定每个标签(即枚举值)的二进制表示的大小。在使用时,赋给枚举变量的值必须与该大小一致。这确保了枚举变量的合法取值范围。

  • 只能被赋予枚举列表中的标签:枚举变量只能被赋予在枚举类型定义中列举的标签值。这防止了变量被赋予无效或不相关的值。

  • 可以赋值为相同枚举类型变量的标签值:允许将一个枚举变量赋值为同一枚举类型的另一个变量的标签值。这有助于在程序中传递和比较枚举状态。

  • 其他赋值操作是非法的:除了上述规定的情况,任何其他的赋值操作都是非法的。这种限制确保了枚举类型的严格性,防止不同类型之间的混淆或错误的赋值。

在设计状态机时,需要定义状态参数。在状态参数的定义中,如果存在重复,普通的Verilog编写是无法在编译时检测出的。这种问题可能在仿真或者上板测试之后才会被发现。然而,使用enum(枚举)类型可以在编译阶段及时发现这类问题,从而提高了代码的可靠性和调试的效率。类似下面的代码,WAIT和DONE的值是相同的,显然这是错误的,但是verilog是无法检测出的,而使用enum,在编译时或者通过语法检查工具能立刻检查出来。

ini 复制代码
parameter [2:0] WAIT = 3'b001,   
                LOAD = 3'b010, 
                DONE = 3'b001;
parameter [1:0] READY = 3'b101,
                SET = 3'b010,
                GO = 3'b110;

reg [2:0] state, next_state; 
reg [2:0] mode_control; 

always @(posedge clk or negedge rstN)
    if (!resetN) 
        state <= 0;
    else 
        state <= next_state;

always @(state) // next state decoder
    case (state)
        WAIT : next_state = state + 1;
        LOAD : next_state = state + 1;
        DONE : next_state = state + 1;
    endcase

always @(state) // output decoder
    case (state)
        WAIT : mode_control = READY;
        LOAD : mode_control = SET;
        DONE : mode_control = DONE;
    endcase
ini 复制代码
enum logic [2:0] {
    WAIT = 3'b001,
    LOAD = 3'b010,
    DONE = 3'b001
}state, next_state;

enum logic [1:0]{
    READY = 3'b101,
    SET = 3'b010,
    GO = 3'b110
}mode_control;

always_ff @(posedge clk or negedge rstN)
    if (!resetN) 
        state <= 0;
    else 
        state <= next_state;

always_comb // next state decoder
    case (state)
        WAIT : next_state = state + 1;
        LOAD : next_state = state + 1;
        DONE : next_state = state + 1;
    endcase

always_comb // output decoder
    case (state)
        WAIT : mode_control = READY;
        LOAD : mode_control = SET;
        DONE : mode_control = DONE;
    endcase

struct

结构体在SystemVerilog中的作用是将多个变量捆绑在一起,形成一个单一的结构。通过结构体,我们可以将相关信号组织在一个统一的名称下,提高了代码的清晰度和可读性。这种方式可以大幅减少代码行数,使得代码更为简洁。同时,结构体的使用也降低了因为声明不匹配而引起的错误的风险。结构体在设计周期较晚才能发现的一些错误,比如模块间不匹配或者遗漏的赋值,也可以得到有效的排除。这些优势共同使得结构体成为SystemVerilog中一种强大的工具。

ini 复制代码
struct {
    logic [ 7:0] opcode;
    logic [31:0] data;
    logic status;
} operation;

operation = '{8'h55, 1024, 1'b0};

operation.data = 32'hFEEDFACE;

typedef

SystemVerilog引入了用户自定义类型(User-Defined Types)的概念,以扩展Verilog的类型系统。使用typedef关键字可以定义新的类型,这些类型可以基于内建类型或其他用户自定义类型。然后,可以将变量和网络声明为这些用户自定义类型。通过使用typedef,可以一次性地定义复杂的数据类型,然后在代码中多次使用这个新类型。这提高了代码的可重用性,降低了代码的冗余度,使得代码更加简洁。使用用户自定义类型有助于确保在模块内部使用的数据类型是一致的。这有助于防止因为使用不一致的数据类型而引起的错误,提高了代码的可读性和可维护性。

arduino 复制代码
typedef logic [31:0] bus32_t;
typedef enum [7:0] {ADD, SUB, MULT, DIV, SHIFT, ROT, XOR, NOP} opcodes_t; 
typedef enum logic {FALSE, TRUE} boolean_t;
typedef struct {
    opcodes_t opcode;
    bus32_t data;
    boolean_t status;
} operation_t;
arduino 复制代码
module ALU (
    input operation_t operation,
    output bus32_t result
);
operation_t registered_op;
...
endmodule

数组

「打包数组」 打包数组(packed array)是一种允许存储多个元素的数据结构,其中元素被紧密地打包在一起。

css 复制代码
logic [3:0][7:0] b;

「非打包数组」 非打包数组不再仅限于基本数据类型,还可以包含结构体、用户自定义类型等复杂的数据结构。这使得数组能够更灵活地存储和组织各种类型的数据。然而,需要注意的是,不建议使用非打包数组,因为在仿真过程中,非打包数组无法一次记录所有波形,可能会导致波形显示不完整的情况。

ini 复制代码
logic [7:0] a1 [0:1][0:3];
logic [7:0] a2 [2][4];
a1 = '{'{7,3,0,5},'{default:'1}};
a2 = a1;

package

package 主要用于封装相关的功能、任务、函数、变量等,并提供了一种结构化的组织方式。package 定义了一个独立的命名空间,防止命名冲突,使得在不同的包中可以使用相同的标识符而不发生冲突。通过 package,可以将相关功能块打包起来,以便在不同的设计中重用,提高了代码的可重用性。package 提供了封装代码的能力,可以将内部实现细节隐藏起来,只暴露外部接口,有助于降低代码的复杂性。

ini 复制代码
package design_types;

typedef struct {
    logic [ 3:0] GFC;
    logic [ 7:0] VPI;
    logic [15:0] VCI;
    logic CLP;
    logic [ 2:0] T;
    logic [ 7:0] HEC;
    logic [ 7:0] Payload [48];
} uni_t; // UNI cell definition

endpackage
lua 复制代码
module transmit_reg (
    output design_types::uni_t data_reg,
    input design_types::uni_t data_packet,
    input logic clock, resetN
);

always @(posedge clock or negedge resetN) 
    if (!resetN) 
        data_reg <= '{default:0};
    else 
        data_reg <= data_packet;

endmodule

interface

在SystemVerilog中,接口(interfaces)是一种复合的、多信号端口的机制。接口能够捆绑任意数量的信号(网络和变量),将它们有机地组合在一起,形成一个单一的接口。除了包含信号外,接口还可以捆绑"方法"(任务和函数),从而将信号与相关的功能一起组合。另外,接口还支持捆绑断言检查,将断言与信号关联,用于执行设计中的一些必要检查。

通过将多个信号、方法和断言捆绑到接口中,可以显著简化复杂总线的定义以及模块之间的互连。这种结构化的设计方法使得整体设计更加模块化和易于理解。使用接口有助于确保在整个设计中使用的总线和互连是一致的。由于所有相关的信号和方法都被有序地组织在接口中,可以有效减少在设计中引入错误的可能性。这使得接口成为处理大型系统中复杂总线和模块间通信的一种有力工具。

ini 复制代码
interface chip_bus;
    logic [31:0] data, address;
    logic request, grant,
    boolean_t ready;
endinterface

module CPU 
    (   chip_bus            bus     ,
        input       logic   clk     ,
        input       logic   reset    
);
...

4总结


本文主要介绍一些SystemVerilog可综合的基本语法,未来将继续深入探讨一些更高级的逻辑块。

5往期回顾

FPGA------FIFO千万不要这样操作!赛灵思FIFO跑飞问题记录

术语摘录------集成电路中的断言(assert)是什么意思?

FPGA------深入了解硬件结构LFSR伪随机数生成

网络QoS系列:以太网流量整形原理及FPGA实现概述

网络QoS系列:带你了解IP数据报TOS字段的前世今生(从IP优先级到DSCP)

6写在最后

创作不易,如果觉得这篇文章对您有用的话,记得点赞关注哦~

个人创作难免纰漏,如有问题欢迎交流

END

本文由本账号所属公众号AdriftCoreFpga提供,欢迎关注获取第一时间更新

相关推荐
Asthenia041211 分钟前
不知道LVS是什么?那你的系统设计题怎么回答!
后端
pedestrian_h12 分钟前
springboot+vue3+mysql+websocket实现的即时通讯软件
spring boot·后端·websocket
AskHarries26 分钟前
使用Cloudflare加速网站的具体操作步骤
后端
Asthenia041229 分钟前
深入剖析架构设计中的接入层:Nginx、LVS、F5详解与面试应对
后端
Captaincc2 小时前
如何利用 HTML 的 `<picture>` 元素,在 GitHub Markdown 中根据用户主题模式
github
yuhaiqiang2 小时前
在公司写代码是工作,在开源社区写代码是生活
前端·后端
追逐时光者2 小时前
C#/.NET/.NET Core技术前沿周刊 | 第 35 期(2025年4.14-4.20)
后端·.net
洛神灬殇3 小时前
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
数据库·redis·后端
雷渊3 小时前
DDD经典的四层架构和洋葱架构、六边形架构区别
后端
SimonKing3 小时前
【Spring Boot配置终极指南】1分钟让你精准指定配置文件,使应用部署游刃有余!
java·后端