SystemVerilog for design

2025-08-07

SystemVerilog硬件设计及建模

一、SystemVerilog中的变量

1.1 变量类型介绍

  • logic——四态变量,类似于reg;可以声明为任意大小的向量

  • enum——一组由符号表示的值得枚举型net或变量

  • typedef——一种用户定义的数据类型,由内建类型或其它用户定义类型构成

  • struct——一种可单独或同时引用的变量得集合体,类似于C语言得struct

二、SystemVerilog声明的位置

2.1包(package)

2.1.1 包的定义

 //  类似与module与endmodule
 //  为了使多个模块共享用户定义类型的定义
 package definitions;
     parameter VERSION="1.1";                //声明一个常数
     typedef enum {ADD,SUB,MUL} opcodes_t;       //自定义一个变量类型opcodes_t,后面可以用opcodes_t声明变量,这个变量可以取ADD,SUB,MUL三个值
     typedef struct {            
         logic [31:0]    a       ;
         logic [31:0]    b       ;
         opcodes_t       opcode  ;
     } instructions_t;                       //自定义一个变量类型instructions_t,
     function automatic [31:0]   multiplier(
         input [31:0]    a,
         input [31:0]    b
     );
         //用户定义的32位乘法代码从这开始
         return a*b;//抽象的乘法器(无错误检测)
     endfunction     //定一个函数,输入两个32位数,返回它们相乘的结果,并截取低32位
 endpackage

包中可以包含的可综合的结构有

  1. parameterlocalparam常量定义

  2. const变量定义

  3. typedef用户定义类型

  4. 全自动taskfunction定义

    包中task和function必须位automatic

  5. 从其他包中import语句

  6. 操作符重载定义

2.1.2 包的用法

  • 显式引用包中内容可读性高,但是麻烦

module ALU(
	input definitions::instruction_t  IW	  ,
    input logic						clock	,
    output logic [31:0]				 result	 ,
);
    always_ff @(posedge clock)begin
        case(IW.opcode)
            definitions::ADD:begin
                result = IW.a + IW.b;
            end
            
            definitions::SUB:begin
                result = IW.a - IW.b;
            end
            
            definitions::MUL:begin
                result = definitions::multiplier(IW.a,IW.b);
            end
        endcase
    end
endmodule
  • 导入包中特定子项

module ALU(
	input  definitions::instruction_t IW	  ,
	input  logic					clock	,
	output logic [31:0]				 result	 ,
);
    import definitions::ADD;
    import definitions::SUB;
    import definitions::MUL;
    import definitions::multiplier;
    always_comb begin
        case(IW.opcode)
            ADD:begin
                result = IW.a + IW.b;
            end
            
            SUB:begin
                result = IW.a - IW.b;
            end
            
            MUL:begin
                result = definitions::multiplier(IW.a,IW.b);
            end
        endcase
    end
  • 使用包通配符导入

module ALU(
	input definitons::instruction_t	  IW  ,
    input logic						clock,
    output logic [31:0]				 result
);
    import definitions::*;	//通配符导入
    always_comb begin
        case(IW.opcode)
            ADD:begin
                result = IW.a + IW.b;
            end
            
            SUB:begin
                result = IW.a - IW.b;
            end
            
            MUL:begin
                result = multiplier(IW.a,IW.b);
            end
        endcase
    end
endmodule

2.2 编译单元$unit

2.2.1 编译单元定义

编译单元是同时编译的所有源文件。可以包含:

  • 时间单位和精度声明

  • 变量声明

  • net声明

  • 常量声明

  • 用户定义数据类型,使用typedefenumclass

  • 任务和函数定义

/********************************外部声明********************************/
parameter VERSION="1.2a";   //外部变量
reg rst_n = 1;              //外部变量(低有效)
typedef struct packed {     //外部用户定义类型
    reg [31:0]  address ;
    reg [31:0]  data    ;
    reg [31:0]  opcode  ;
} instruction_word_t;

function automatic int log2(input int n);   //外部函数
    if(n<1)begin
        return(1);
    end
    else begin
        log2 = 0;
        while(n>1)begin
            n = n / 2;
            log2++;
        end
    return(log2)
    end
endfunction

/********************************模块定义********************************/
//用外部声明定义端口类型
module register (
    input instruction_word_t    d       ,
    input wire                  clock   ,

    output instruction_word_t   q
);
    always_ff @( posedge clock or negedge rst_n ) begin
        if(!rst_n)begin
            q <= 1'b0;
        end
        else begin
            q <= d;
        end
    end
endmodule

2.2.2 编码指导

  1. 不要在$unit域中进行任何声明!声明在命名包内进行

  2. $unit域可以导入对应的包

2.2.3 源代码顺序

module parity_gen (
    input wire [63:0]   data
);
    assign parity = ^data;  //parity是一个隐式局部net
endmodule

reg parity;     //因为声明在被perity_gen引用之后出现
                //因此外部声明没被perity_gen使用

module parity_check (
    input wire [63:0]   data    ,
    output logic        error
);
    assign err = (^data != parity);     //parity是$unit变量
endmodule

2.2.4 将包导入$unit的编码原则

//将包中特定子项导入到$unit中
import definitions::instruction_t;

module ALU (
    input instruction_t IW      ,
    input logic         clock   ,

    output logic [31:0] result
);
endmodule

//使用通配符将包导入到$unit中
import definitions::*;

module ALU (
    input instruction_t IW      ,
    input logic         clock   ,

    output logic [31:0] result
);
endmodule
/********************************带条件编译的包********************************/
`ifndef DFFS_DONE    //如果已编译标志没设置
    `define DFFS_FONE    //设置该标志
package definitions;
    parameter VERSION="1.1";
    typedef enum {ADD,SUB,MUL} opcodes_t;
    typedef struct {
        logic [31:0]	a		;
        logic [31:0]	b		;
        opcodes_t		opcode	;
    } instructions_t;						//自定义一个变量类型instructions_t,
    function automatic [31:0]	multiplier(
        input [31:0]	a,
        input [31:0]	b
    );
        //用户定义的32位乘法代码从这开始
        return a*b;//抽象的乘法器(无错误检测)
    endfunction		//定一个函数,输入两个32位数,返回它们相乘的结果,并截取低32位
endpackage
`endif

每个包都应该带条件编译,可以有效避免重复声明变量

/********************************包含条件编译包文件的设计文件********************************/
`include "package\2.2.6.sv" //编译包文件
module ALU (
    input instructions_t    IW      ,
    input logic             clock   ,
    output logic [31:0]     result
);
    always_comb begin
        case(IW.opcode)
            ADD:begin
                result = IW.a + Iw.b;
            end

            SUB:begin
                result = IW.a - IW.b;
            end

            MUL:begin
                result = multiplier(IW.a,IW.b);
            end
        endcase
    end
endmodule
/********************************包含条件编译包文件的测试平台文件********************************/
`include "package\2.2.6.sv"
module tb();
    instructions_t  test_word   ;
    logic [31:0]    result      ;
    logic           clock = 0   ;
    ALU u_ALU(
        .IW     ( IW        ),
        .clock  ( clock     ),
        .result ( result    )
    );

    always #10 clock = ~clock;

    initial begin
        @(negedge clock)
        test_word.a         = 5     ;
        test_word.b         = 7     ;
        test_word.opcode    = ADD   ;
        @(negedge clock)
        $display("alu_out = %d (expected 12)",result);
        $finish;
    end
endmodule

2.3 未命名语句块中的声明

2.3.1 未命名块中的局部变量

SystemVerilog扩展了Verilog,允许在未命名块中声明变量。

2.4 仿真时间单位和精度

2.4.1 Verilog中的timescale

timsescale是告诉仿真工具应该以什么仿真精度去跑,而且与读入timescale的顺序有关。

2.4.2 包含时间单位的时间值

SystemVerilog扩展了Verilog,可以给时间值指定时间单位

forever #5ns clock = ~clock

时间值和时间单位之间不能有空格

2.4.3 范围级(scope-level)时间单位和精度

使用关键字timeunittimeprecision进一步增强时间单位的说明

module chip(…);
    timeunit 1ns;
    timeprecision 10ps;
    …
endmodule

2.4.4 时间单位和精度搜索次序

  • 如果时间值带单位,则使用指定的单位

  • 否则,使用在模块、接口和程序块内部指定的时间单位和精度

  • 否则,如果模块或接口声明嵌入到其他的模块和接口内,使用父模块或接口指定的时间单位和精度。

  • 否则,使用模块编译时,有效的`timescale时间单位和精度

  • 否则,使用在编译单元域中定义的实践单位和精度

  • 否则,使用仿真器默认的时间单位和精度

/********************************时间单位和精度的混合声明********************************/
timeunit 1ns;   //外部声明的时间单位和精度
timeprecision 1ns;

module my_chip (…);
    timeprecision 1ps;  //局域精度(优先于外部精度)
    always @(posedge data_request)begin
        #2.5 send_packet;   //使用外部单位和局部精度
        #3.75ns check_crc;  //使用指定的单位
    end

    task send_packet();
        …
    endtask

    task check_crc();
        …
    endtask
endmodule

`timescale 1ps/1ps  //timescale指令指定的单位和精度,优于外部声明
module FSM(…)
    timeunit 1ns;       //局部声明优先于timescale的指定

    always @(State)begin
        #1.2 case(State)    //使用局部声明的单位和`timescale指定的精度
            WAITE:#20ps …;  //使用此处指定的单位
        endcase
    end
endmodule

三、SystemVerilog文本值和数据类型

3.1 增强的文本值赋值

  • verilog中赋值全1data = ~0data = -1

  • SystemVerilog中全1data = 'b1

  • SystemVerilog中全0data = 'b0

  • SystemVerilog中全xdata = 'bx

  • SystemVerilog中全zdata = 'bz

3.2 `define增强

3.2.1 字符串内的宏变量替换

`define print(v)\
	$display(`"variable v = %h`",v);
`print(data);

//本例中,宏`define将扩展为
$display("variable data = %h",data);

//verilog字符串中嵌入双引号必须加转义符\,在SystemVerilog中转义符变成了`\`
`define print(v)\
$display(`"variable `\`"v`\`" = %h`",v);
`print(data);

//本例中,宏`define将扩展为
$display("variable \"data\" = %h",data);

3.2.2 通过宏建立标识符名

在无文本替换的源文件中,声明可以是

bit d00_bit;wand d00_net = d00_bit;
bit d01_bit;wand d01_net = d01_bit;
……	//对每一位都重复这个操作,重复60多次
bit d62_bit;wand d62_net = d62_bit;
bit d63_bit;wand d63_net = d63_bit;

使用SystemVerilog对`define的增强,代码可简化为

`define TWO_STATE_NET(name) bit name``_bit;\
wand name``_net = name``_bit;
`TWO_STATE_NET(d00)
`TWO_STATE_NET(d01)
……
`TWO_STATE_NET(d62)
`TWO_STATE_NET(d63)

3.3 SystemVerilog变量

3.3.1 对象类型和数据类型

SystemVerilog标准定义设计中的信号同时具有类型和数据类型。类型指示信号是线网还是变量,数据类型指示线网或变量的值系统:对两态数据类型是0或1,对四态数据类型是0、1、Z或X。logic定义的对象是四态数据类型。变量类型可以是两态的也可以是四态的,线网只能是四态的。

3.3.2 SystemVerilog四态变量

logicreg用法非常类似

3.3.3 SystemVerilog两态变量

  • bit——1位两态整数

  • byte——8位两态整数,类似于char

  • shortint——16位两态整数,类似于short

  • int——32位两态整数,类似于int

  • longint——64位两态整数,类似于longlong

四态类型的X和Z赋值给两态类型的数据时,会转换为逻辑0

  • void——表示五存储,void类型可以用在标签联合体中以及定义无返回值的函数

  • real——与double类似,双精度浮点数

  • shortreal——与float类似,单精度浮点数

3.3.4 显式及隐式变量和线网类型

所有数据类型前都可以加var,没别的作用,就是好看,让人能知道这里声明了变量,增加代码可读性

3.4 在RTL模型中使用两态类型

3.4.1 两态类型的特点

四态值到两态值的转换

四态值

两态值

0

0

1

1

X

0

Z

0

3.4.2 两态类型和四态类型仿真

3.4.3 在case语句中使用两态类型

不如四态类型能检测错误

3.5 数据类型规则的放宽

在SystemVerilog中,任何数据类型的变量都可以通过下列方式赋值,但是每个变量只能用其中的一种。原因是变量不能被多个源驱动

  • 在任意的initialalways过程块中赋值

  • 在单个 always_comb,always_ffalways_latch过程块中赋值

  • 通过单个的持续赋值语句赋值

  • 通过单个模块或原语的output/inout端口驱动赋值

/********************************变量的放宽应用********************************/
module compare (
    input logic [63:0]  a   ,
    input logic [63:0]  b   ,

    output logic        lt  ,
    output logic        eq  ,
    output logic        gt
);

    always_comb begin
        if(a < b)begin
            lt = 1'b1;  //过程赋值
        end
        else begin
            lt = 1'b0;
        end
    end

    assign gt = (a > b);    //持续赋值
    comparator u1(eq,a,b);  //模块实例化
endmodule

module comparator (
    input logic [63:0]  a   ,
    input logic [63:0]  b   ,

    output logic        eq
);

    always_comb begin
        eq = (a ==b );
    end
endmodule

3.6 有符号和无符号修饰符

signedunsigned只能跟在数据类型声明的后面,例如int signed [31:0] s1

3.7 静态和自动变量

static——静态变量关键字

automatic——自动变量关键字

module一级声明变量不能显式地声明位static或automatic,模块级所有变量都是静态的

两个任务同时调用静态变量和动态变量时,静态变量会两个任务共用,自动变量针对每个任务创建一个变量。两个任务中的这个变量互不相关。

3.7.1 静态变量和自动变量的初始化

verilog变量的内嵌初始化

Verilog只允许在模块级声明的变量进行内嵌初始化,在任务、函数、begin…end块及fork…join块中声明的变量不能在声明时置初始值

SystemVerilog的内嵌初始化

SystemVerilog对Verilog进行了扩展,在任务、函数声明的变量可以有内嵌初始值

3.7.2 静态和自动变量的使用原则

  • alwaysinitial块中,如果无内嵌初始化则使用静态变量,而需要内嵌初始化的使用自动变量。使用带内嵌初始化的变量,所表现的行为最直观,因为过程块每次被重新执行,自动变量都会重新初始化

  • 如果一个任务和函数是可重入的,则应该设成自动的,变量也应该是自动的,除非有特殊的原因需要在两次调用之间保持变量的值,一个简单的例子,如果需要用一个变量来记录自动任务或者函数被调用的次数,则这个变量应该是静态的

  • 如果一个任务和函数用来描述硬件的独立部分,并且不是可重入的,那么应该把它声明为静态的,任务和函数中的所有变量也应该是静态的

3.8 变量初始化的确定性

3.8.1 初始化确定机制

SystemVerilog内嵌初始化优先于在仿真时刻0执行的事件。

测试平台应该将变量初始化为无效状态,比如复位信号低电平有效,则复位信号初始化为高电平

3.8.2 时序逻辑的异步输入初始化

/********************************在仿真时刻0,使用两态数据类型施加复位********************************/
module counter (
    input wire              clock   ,
    input wire              rst_n   ,

    output logic [15:0]     count
);

    always_ff @( posedge clock or negedge rst_n ) begin
        if(!rst_n)begin //rst_n低电平有效
            count <= 0;
        end
        else begin
            count <= count + 1;
        end
    end
endmodule

module test();

wire [15:0]     count       ;
bit             clock       ;
bit             rst_n = 1   ;   //rst_n初始化为无效值

counter u_counter(
    .clock  ( clock  ),
    .rst_n  ( rst_n  ),
    .count  ( count  )
);

always #10 clock = ~clock;

initial begin
    rst_n = 0;  //在0时刻插入低电平有效的复位
#2  rst_n = 1;  //在clock上升沿之前去除复位
    $display("\n count = %0d (expect 0)\n",count);
#1  finish;
end
endmodule

3.9 强制类型转换

3.9.1 静态转换(编译时转换)

  • <type>'(<expression>)——将一个值强制转换成任何数据类型,包括用户自定义类型。如:7 + int'(2.0 * 3.0)(2.0*3.0)的结果强制转换成整形,然后加7

  • <size>'(<expression>)'——将一个值强制转换成任意向量宽度,如:

logic [15:0]	a,b,y;
y = a + b**16'(2);	//将文本值2强制转换为16位宽
  • <size>'(<expression>)'——将一个值转换成有符号数或无符号数,如:

shortint a,b;
int 	 y;
y = y - signed'({a,b});//将a,b拼接结果强制转换为有符号值

3.9.2 动态强制类型转换

SystemVerilog提供了一个新的系统函数cast,这是动态的,在运行时进行待转换数值的检查。系统函数cast有两个变量,一个目标变量和一个源变量,语法如下:$cast(dest_var,source_exp)。举个例子:

int radius,area;
always @(posedge clock)begin
    $cast(area,3.1415*radius**2);//强制转换操作符的结果被转换为area类型

3.10 常数

Verilog中的常数,只能在模块、静态任务和静态函数中声明:

  • parameter是一个可以在确立(elaboration)时使用的defparam或者内嵌参数重定义定性重新定义的常数

  • specparam是一个可以在确立时从SDF文件中重定义的常数

  • localparam是确立期常数,不能重定义。但是它的值可以基于其他常数

SystemVerilog扩展的常数const关键字,const形式的常数直到elaboration后才被赋值,因此可以:

  • 在自动任务和函数等动态环境中声明

  • 被赋予一个线网或者变量值而非常数表达式

  • 被赋予一个对象值,这个值可以在任何设计层次定义

const常数的声明必须包含数据类型

const logic [23:0] C1 = 7;	//24位常数
const int C2 = 15;		//32位常数
const real C3 = 3.14;	//实数常数
const C4 = 5;			//错误,没有数据类型

四、用户自定义和枚举数据类型

4.1 用户自定义类型

4.1.1 局部typedef声明

4.1.2 共享typedef声明

`ifndef DFFS_DONE
    `define DFFS_DONE
package chip_types;
    `ifdef TWO_STATE
        typedef bit dtype_t;
    `else
        typedef logic dtype_t;
    `endif
endpackage
`endif

/********************************直接从包中引用用户自定义类型********************************/
import chip_types::*;
module counter (
    input dtype_t           clock   ,
    input dtype_t           rst_n   ,

    output dtype_t [15:0]   count
);

    always @(posedge clock or negedge rst_n)begin
        if(!rst_n)begin
            count <= 0;
        end
        else begin
            count <= count + 1;
        end
    end
endmodule

4.1.3 用户自定义类型的命名习惯

使用"_t"结尾作为用户自定义类型的结尾

4.2 枚举数据类型

/********************************用verilog的`define和parameter常数建模的状态机********************************/
`define FETCH   3'h0
`define WRITE   3'h1
`define ADD     3'h2
`define SUB     3'h3
`define MULT    3'h4
`define DIV     3'h5
`define SHIFT   3'h6
`define NOP     3'h7

module controller (
    input wire [2:0]        instruction ,
    input wire              clock       ,
    input wire              rst_n       ,

    output logic            read        ,
    output logic            write
);

    parameter WAITE = 0     ;
    parameter LOAD  = 1     ;
    parameter STORE = 2     ;

    logic [1:0] curr_state;
    logic [1:0] next_state;

    always_ff @( posedge clock or negedge rst_n ) begin
        if(!rst_n)begin
            curr_state <= WAITE;
        end
        else begin
            curr_state <= next_state;
        end
    end

    always_comb begin
        case(curr_state)
            WAITE:begin
                next_state = LOAD;
            end

            LOAD:begin
                next_state = STORE;
            end

            STORE:begin
                next_state = WAITE;
            end
        endcase
    end

    always @(curr_state or instruction) begin
        read    = 0;
        write   = 0;
        if(curr_state == LOAD && instruction == `FETCH)begin
            read = 1;
        end
        else if(curr_state == STORE && instruction == `WRITE)begin
            write = 1;
        end
    end
endmodule
/********************************使用枚举类型建模的状态机********************************/
package chip_types;
    typedef enum {FETCH,WRITE,ADD,SUB,MULT,DIV,SHIFT,NOP} instruction_t;
endpackage
import chip_types::*;

module controller (
    input instruction_t     instruction ,
    input wire              clock       ,
    input wire              rst_n       ,

    output logic            read        ,
    output logic            write
);
    enum {WAITE,LOAD,STORE} curr_state,next_state;

    always_ff @( posedge clock or negedge rst_n ) begin
        if(!rst_n)begin
            curr_state <= WAITE;
        end
        else begin
            curr_state <= next_state;
        end
    end

    always_comb begin
        case(curr_state)
            WAITE:begin
                next_state = LOAD;
            end

            LOAD:begin
                next_state = STORE;
            end

            STORE:begin
                next_state = WAITE;
            end
        endcase
    end

    always_comb begin
        if(curr_state == LOAD && instruction == FETCH)begin
            read = 1;
        end
        else if(curr_state == STORE && instruction == WRITE)begin
            write = 1;
        end
        else begin
            read    = 0;
            write   = 0;
        end
    end
endmodule

4.2.1 枚举类型标签序列

创建一个枚举列表,带有标签RESET,S0S5,W6W9:

enum {RESET,S[5],W[6:9]} state;

state

创建单个标签state

state[N]

创建标签序列,state0,state1,…stateN

state[N:M]

创建标签序列,从N到M

4.2.2 枚举类型标签作用域

4.2.3 枚举类型值

可以显式给标签赋值

enum {A = 1,B = 2,C} list

C会被自动赋值3,因为C= B+1

4.2.4 枚举类型的基类

默认基类是int,32位两态整形。

可以显式定义基类,比如:enum logic [1:0] {WAITE,LOAD,READY} state;

4.2.5 自定义和匿名枚举

typedef enum {WAITE,LOAD,READY} state_t

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

枚举类型是半强类型,一个枚举类型只可以进行下列赋值:

  • 枚举类型列表中的一个标签

  • 同类枚举类型的其他变量

  • 通过cast转换成枚举类型变量的数值

4.2.7 将表达式强制转换为枚举类型

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

  • <枚举变量名>.first——返回指定变量枚举列表中的第一个成员的值

  • <枚举变量名>.last——返回枚举列表最后一个成员的值

  • <枚举变量名>.next(<N>)——返回枚举列表中下一个成员的值。可以用一个整数值作为next的参数。这种情况下,从枚举列表的当前位置算起,返回后面第N个成员的值。如果到达了枚举列表的末尾,则会返回到列表的开头。如果枚举变量的当前值不在枚举列表中,则会返回列表中第一个成员的值。

  • <枚举变量名>.prev(<N>)——同next,但是是往前

  • <枚举变量名>.num——返回变量的枚举列表中元素个数

  • <枚举变量名>.name——返回枚举变量中代表这个值的字符串。如果这个值不在枚举变量列表中,则返回一个空字符串

/********************************使用专用方法循环访问枚举类型列表********************************/
module confidence_counter (
    input logic         synced      ,
    input logic         compare     ,
    input logic         clock       ,
    input logic         rst_n       ,

    output logic        in_sync
);
    typedef enum {cnt[0:15]} state_t;
    state_t curr_state;
    state_t next_State;

    always_ff @( posedge clock or negedge rst_n ) begin
        if(!rst_n)begin
            curr_state <= cnt0;
        end
        else begin
            curr_state <= next_State;
        end
    end

    always_comb begin
        case(curr_state)
            cnt0:begin
                if(compare && synced)begin
                    next_State = curr_state.next;
                end
                else begin
                    next_State = curr_state;
                end
            end

            cnt1:begin
                if(compare && synced)begin
                    next_State = curr_state.next;
                end
                else if(compare && !synced)begin
                    next_State = curr_state.first;
                end
                else begin
                    next_State = curr_state;
                end
            end

            cnt15:begin
                if(compare && !synced)begin
                    next_State = curr_state.prev(2);
                end
                else begin
                    next_State = curr_state;
                end
            end

            default:begin
                if(compare && synced)begin
                    next_State = curr_state.next;
                end
                else if(compare && !synced)begin
                    next_State = curr_state.prev(2);
                end
                else begin
                    next_State = curr_state;
                end
            end
        endcase
    end

    always_ff @( posedge clock or negedge rst_n ) begin
        if(!rst_n)begin
            in_synce <= 0;
        end
        else begin
            if(curr_state == cnt8)begin
                in_sync <= 1;
            end
            else (curr_state == cnt0)begin
                in_sync <= 0;
            end
        end
    end
endmodule

4.2.9 打印枚举类型

/********************************打印枚举类型变量的值和名称********************************/
module FSM (
    input logic         clock   ,
    input logic         rst_n   ,

    output logic [3:0]  control
);

    typedef enum logic [2:0] {WAITE = 3'b001,LOAD = 3'b010,READY = 3'b100} state_t;

    state_t curr_state;
    state_t next_state;

    always_ff @( posedge clock or negedge rst_n ) begin
        if(!rst_n)begin
            curr_state <= WAITE;
        end
        else begin
            curr_state <= next_state;
        end
    end

    always_comb begin
        $display("\nCurrent state is %s(%b)",curr_state.name,curr_state);
        case(curr_state)
            WAITE:begin
                next_state = LOAD;
            end

            LOAD:begin
                next_state = READY;
            end

            READY:begin
                next_state = WAITE;
            end
        endcase
        $display("Next state will be %s(%b)",next_state.name,next_state);
    end

    assign control = curr_state;
endmodule

五、数组、结构体和联合体

5.1 结构体

5.1.1 结构体声明

var struct {                    //结构体变量
    logic [31:0]    a,b     ;
    logic [7:0]     opcode  ;
    logic [23:0]    address ;
}Instruction_Word_Var;

wire struct {                   //结构体线网
    logic [31:0]    a,b     ;
    logic [7:0]     opcode  ;
    logic [23:0]    address ;
}Instruction_Word_Net;

struct {                        //结构体变量
    logic [31:0]    a,b     ;
    logic [7:0]     opcode  ;
    logic [23:0]    address ;
}Instruction_Word_var;

typedef struct {                //结构体定义
    logic [31:0]    a,b     ;
    logic [7:0]     opcode  ;
    logic [23:0]    address ;
}instruction_word_t;
instruction_word_t IW;          //结构体分配存储区

struct {                        //匿名结构体
    logic [31:0]    a,b     ;
    logic [7:0]     opcode  ;
    logic [23:0]    address ;
}isntruction;

5.1.2 结构体赋值

结构体初始化

typedef struct {
    logic [31:0]    a,b     ;
    logic [7:0]     opcode  ;
    logic [23:0]    address ;
}instruction_word_t;
instruction_word_t IW = '{100,3,8'hFF,0};

SystemVerilog用'{}符号包含数值列表,C语言用{}

结构体成员赋值

typedef struct {
    logic [31:0]    a,b     ;
    logic [7:0]     opcode  ;
    logic [23:0]    address ;
}instruction_word_t;
instruction_word_t IW;

always_ff @( posedge clock or negedge rst_n ) begin
    if(!rst_n)begin
        IW.a = 100;     //引用结构体成员
        IW.b = 5;
        IW.opcode = 8'hFF;
        IW.adress = 0;
    end
    else begin
        …
    end
end

将结构体表达式赋给结构体

typedef struct {
    logic [31:0]    a,b     ;
    logic [7:0]     opcode  ;
    logic [23:0]    address ;
}instruction_word_t;
instruction_word_t IW;

always_ff @( posedge clock or negedge rst_n ) begin
    if(!rst_n)begin
        IW = '{100,5,8'hFF,0};      //根据顺序赋值
    end
    else begin
        …
    end
end

always_ff @( posedge clock or negedge rst_n ) begin
    if(!rst_n)begin
        IW = '{address:0,opcode:8'hFF,a:100,b:5};      //根据名称赋值
    end
    else begin
        …
    end
end

结构体表达式的默认值

typedef struct {
    real        r0,r1   ;
    int         i0,i1   ;
    logic [7:0] opcode  ;
    logic [23:0]address ;
} instruction_word_t;

instruction_word_t IW;

always_ff @( posedge clocl or negedge rst_n ) begin
    if(!rst_n)begin
        IW = '{real:1.0,default:0};
        //指定real类型的成员默认值为1.0
        //指定其他成员默认值0
    end
    else begin
        …
    end
end

5.1.3 压缩和非压缩结构体

struct packed{
    logic       valid   ;
    logic [7:0] tag     ;
    logic [31:0]data    ;
}data_word;

1

压缩结构体被当作向量存储

5.1.4 通过端口传递结构体

5.1.5 将结构体作为自变量传递至任务和函数

5.2 联合体

联合体只储存一个元素,但是这个元素可以有多种表示方法,每种表示可以是不同的数据类型,联合体的声明语法类似于结构体,联合体的成员的引用也跟结构体一样。

union {
    int             i       ;
    int unsigned    u       ;
}   data;
…
data.i = -5;
$display("data is %d",data.i);
data.u = -5;
$display("Now data is %d",data.u);

5.2.1 非压缩联合体

不可综合

5.2.2 标签联合体

可以给联合体的某个类型设置隐含标签,比如:data = tagged i 5,这样data.i就被打上了隐含标签,下一次如果读data.r软件就会报错

5.2.3 压缩联合体

压缩联合体成员的位数必须相同,只能储存整数值

压缩的标签联合体的位数可以不相同,但是也只能储存整数值

5.2.4 综合指导

压缩联合体和压缩的标签联合体是可综合的

5.2.5 使用结构体和联合体的一个例子

/********************************使用结构体和联合体********************************/
`ifndef DFFS_DONE
    `define DFFS_DONE
package definitions
    typedef enum {ADD,SUB,MULT,DIV,SL,SR} opcode_t;
    typedef enum {UNSIGNED,SIGNED} operand_type_t;
    type union packed{
        logic           [31:0]      u_data;
        logic signed    [31:0]      s_data;
    }data_t;
    typedef struct packed {
        opcode_t        opc     ;
        operand_type_t  op_type ;
        data_t          op_a    ;
        data_t          op_b    ;
    } instr_t;
endpackage
`endif

import definitions::*;

module alu (
    input instr_t       IW      ,
    output data_t       alu_out
);
    always_comb begin
        if(IW.op_type == SIGNED)begin
            case(IW.opc)
                ADD :alu_out.s_data = IW.op_a.s_data + IW.op_b.s_data   ;
                SUB :alu_out.s_data = IW.op_a.s_data - IW.op_b.s_data   ;
                MULT:alu_out.s_Data = IW.op_a.s_data * IW.op_b.s_data   ;
                DIV :alu_out.s_Data = IW.op_a.s_data / IW.op_b.s_data   ;
                SL  :alu_out.s_Data = IW.op_a.s_data <<< 2              ;
                SR  :alu_out.s_Data = IW.op_a.s_data >>> 2              ;
            endcase
        else begin
            case(IW.opc)
                ADD :alu_out.u_data = IW.op_a.u_data + IW.op_b.u_data   ;
                SUB :alu_out.u_data = IW.op_a.u_data - IW.op_b.u_data   ;
                MULT:alu_out.u_Data = IW.op_a.u_data * IW.op_b.u_data   ;
                DIV :alu_out.u_Data = IW.op_a.u_data / IW.op_b.u_data   ;
                SL  :alu_out.u_Data = IW.op_a.u_data <<< 2              ;
                SR  :alu_out.u_Data = IW.op_a.u_data >>> 2              ;
            endcase
        end
        end
    end
endmodule

5.3 数组

5.3.1 非压缩数组

Verilog数组声明的基本语法是:<data_type><vector_size><array_name><array_dimensions>

Verilog限制一次只能访问数组的一个元素,或者一个元素的1位或者部分位

logic [7:0] data [1024]等效于logic [7:0] data [0:1023]

5.3.2 压缩数组

logic [3:0][7:0] data //2维压缩数组

存储方式如图所示,存储为一个向量

2

5.3.3 使用压缩和非压缩数组

非压缩数组用于建模:

  • 下列数据类型的数组:byteintintegerreal、非压缩结构体、非压缩联合体以及其他非位形式的数据类型

  • 通常每次只访问一个元素的数组,例如RAM和ROM

压缩数组用于建模:

  • 由只有1位的数据类型组成的向量

  • 需要子段访问的向量

5.3.4 声明时对数组进行初始化

/********************************压缩数组初始化********************************/
logic [3:0][7:0]    a = 32'h0           ;//向量赋值
logic [3:0][7:0]    b = {16'hz,16'h0}   ;//拼接操作符
logic [3:0][7:0]    c = {16{2'b01}}     ;//复制操作符

/********************************非压缩数组初始化********************************/
int d1[0:1][0:3] = '{'{7,3,0,5},'{2,0,1,6}};
//d1[0][0] = 7
//d1[0][1] = 3
//d1[0][2] = 0
//d1[0][3] = 5
//d1[1][0] = 2
//d1[1][1] = 0
//d1[1][2] = 1
//d1[1][3] = 6

int d1[0:1][0:3] = '{2{7,3,0,5}};
//d1[0][0] = 7
//d1[0][1] = 3
//d1[0][2] = 0
//d1[0][3] = 5
//d1[1][0] = 7
//d1[1][1] = 3
//d1[1][2] = 0
//d1[1][3] = 5

5.3.5 数组赋值

Verilog支持两种方式的非压缩数组赋值:

  • 给一个元素赋值

  • 给一个元素的1位或者部分位赋值

SystemVerilog扩展了两种新的非压缩数组赋值方法:

  • 用一个值序列给整个数组赋值

  • 用一个值序列给数组的一段赋值

5.3.6 数组复制

  • 压缩数组到压缩数组:直接赋值,多余的位截掉,缺少的位补0

  • 非压缩数组到非压缩数组:两个结构完全一样的非压缩数组才能复制,否则需要通过位流转换

  • 压缩数组到非压缩数组:只能通过位流转换

  • 非压缩数组到压缩数组:只能通过位流转换

5.3.7 使用位流转换复制数组和结构体

/********************************位流转换********************************/
typedef int data_t [3:0][7:0];//非压缩类型
data_t a;//非压缩数组
int b[1:0][3:0][3:0];//非压缩数组
a = data_t'(b);//将非压缩数组赋给不同结构的非压缩数组

5.3.8 由数组构成的数组

3

5.3.9 数组中使用用户自定义类型

5.3.10 数组通过端口及任务和函数的传送

相比于Verilog,SystemVerilog允许非压缩数组通过模块端口传送,或者传进/传出函数和任务

5.3.11 结构体和联合体构成的数组

压缩数组中的结构体和联合体必须也是压缩的

5.3.12 结构体和联合体中的数组

压缩的联合体和结构体中只能包含压缩数组

5.3.13 综合指导

5.3.14 使用数组的例子

/********************************使用由结构体构成的数组对指令寄存器建模********************************/
`ifndef DFFS_DONE
    `define DFFS_DONE
    package definitions;
        typedef enum {ADD,SUB,MULT,DIV,SL,SR} opcode_t;
        typedef enum {UNSIGNED,SIGNED} operand_type_t;
        typedef union packed {
            logic       [31:0]      u_data;
            logic signed[31:0]      s_data;
        }data_t;

        typedef struct packed {
            opcode_t        opc     ;
            operand_type_t  op_type ;
            data_t          op_a    ;
            data_t          op_b    ;
        } instr_t;
    endpackage
`endif

import definitions::*;

module instruction_register(
    input data_t            operand_a       ,
    input data_t            operand_b       ,
    input operand_type_t    op_type         ,
    input opcode_t          opcode          ,
    input logic [4:0]       write_pointer   ,

    output instr_t [0:31]   instr_reg           //结构体构成的压缩数组
);

    always_comb begin
        instr_reg[write_pointer].op_type    = op_type   ;
        instr_reg[write_pointer].opc        = opcode    ;

        //用op_type来确定存储在输入操作数联合体中的操作数类型
        if(op_type == SIGNED)begin
            instr_reg[write_pointer].op_a.s_data = operand_a.s_data;
            instr_reg[write_pointer].op_b.s_data = operand_b.s_data;
        end
        else begin
            instr_reg[write_pointer].op_a.u_data = operand_a.u_data;
            instr_reg[write_pointer].op_b.u_data = operand_b.u_data;
        end
    end
endmodule

5.4 foreach数组循环结构体

5.5 用于数组查询的系统函数

  • $dimensions(array_name)返回数组的位数(如果对象不是数组则返回0)

  • $left(array_name,dimension)返回指定维度的最高有效位(msb)。比如:

logic [1:2][7:0]    word [0:3][4:1];
$left(word,1)返回0
$lest(word,2)饭回4
$lest(word,3)返回1
$lest(word,4)返回7
  • $right(array_name,dimension)返回指定维度的最低有效位(lsb)。

  • $low(array_name,dimension)返回指定维度的最低位数,可能是msb或lsb。比如:

logic [1:2][7:0]    word [0:3][4:1];
$low(word,1)返回0
$low(word,2)饭回1
$low(word,3)返回1
$low(word,4)返回0
  • $high(array_name,dimension)返回指定维度的最高位数,可能是msb或lsb。

  • $size(array_name,dimension)返回指定维数的总个数,即$high - $low + 1

  • increment(array_name,dimension)对于指定的维度,如果$left大于或等于$right则返回1,否则返回-1

5.6 $bits位宽系统函数

$bits(expression)返回任意表达式的位数,比如:

bit     [63:0]      a       ;
logic   [63:0]      b       ;
wire    [3:0][7:0]  c [0:15];
struct packed{
    byte            tag ;
    logic   [31:0]  addr;
}d;
$bits(a)返回64
$bits(b)返回64
$bits(c)返回512
$bits(d)返回40
$bits(a + b)返回128

5.7 动态数组、联合数组、稀疏数组和字符串

SystemVerilog fot Verification中介绍

六、SystemVerilog过程块、任务和函数

6.1 Verilog通用目的always过程块

6.2 SystemVerilog特有的过程块

6.2.1 组合逻辑过程块

always_comb过程块表示建立组合逻辑模型

/********************************使用always过程块建模的有限状态机********************************/
import chip_types::*

module controller(
    input instruction_t     instruction     ,
    input logic             clock           ,
    input logic             rst_n           ,

    output logic            read            ,
    output logic            write
);

    typedef enum {WAITE,LOAD,STORE} state_t;
    state_t curr_state;
    state_t next_state;

    always @(posedge clock or negedge rst_n)begin
        if(!rst_n)begin
            curr_state <= WAITE;
        end
        else begin
            curr_state <= next_state;
        end
    end

    always @(curr_state)begin
        case(curr_state)
            WAITE:begin
                next_state = LOAD;
            end

            LOAD:begin
                next_state = STORE;
            end

            STORE:begin
                next_state = WAITE;
            end
        endcase
    end

    always @(curr_state or instruction)begin
        read    = 0;
        write   = 0;
        if(curr_state == LOAD && instruction == FETCH)begin
            read = 1;
        end
        else if(curr_state == STORE && instruction == WRITE)begin
            write = 1;
        end
    end
endmodule

如果next_state默认为WAITE,那么curr_state的值永远不会变化,那么always @(curr_state)的敏感列表不会变化,状态机就锁住了

/********************************使用always_comb过程块建模的状态机********************************/
import chip_types::*;

module controller(
    input instruction_t     instruction ,
    input logic             clock       ,
    input logic             rst_n       ,

    output logic            read        ,
    output logic            write
);

    typedef enum {WAITE,LOAD,STORE} state_t;
    state_t curr_State;
    state_t next_state;

    always_ff @( posedge clock or negedge rst_n ) begin
        if(!rst_n)begin
            curr_State <= WAITE;
        end
        else begin
            curr_State <= next_state;
        end
    end

    always_comb begin
        case(curr_State)
            WAITE:begin
                next_state = LOAD;
            end

            LOAD:begin
                next_state = STORE;
            end

            STORE:begin
                next_state = WAITE;
            end
        endcase
    end

    always_comb begin
        read    = 0;
        write   = 0;
        if(curr_State == WAITE && instruction == FETCH)begin
            read = 1;
        end
        else if(curr_State == STORE && instruction == WRITE)begin
            write = 1;
        end
    end
endmodule

6.2.2 锁存逻辑过程块

always_latch表示过程块描述的是基于锁存器的逻辑

/********************************使用always_latch过程块锁存输入脉冲********************************/
module register_reader (
    input   wire        clk     ,
    input   wire        ready   ,
    input   wire        rst_n   ,

    output logic [4:0]  read_pointer
);

    logic enable    ;   //计数器的内部使能信号
    logic overflows ;   //内部的计数器溢出标志

    always_latch begin  //锁存输入ready
        if(!rst_n)begin
            enable <= 0;
        end
        else if(ready)begin
            enable <= 1;
        end
        else if(overflow)begin
            enable <= 0
        end
    end

    always_ff @( posedge clk or negedge rst_n ) begin//5位计数器
        if(!rst_n)begin
            {overflow,read_pointer} <= 0;
        end
        else if(enable)begin
            {overflow,read_pointer} <= read_pointer + 1;
        end
    end
endmodule

6.2.3 时序逻辑过程块

always_ff表示设计的意图是描述可综合的时序逻辑

6.3 对任务和函数的改进

6.3.1 任务和函数的隐式语句组

function里面不需要再加begin…end(感觉好像没啥用,还是加了好看)

6.3.2 返回函数值

Verilog返回与函数名相同的变量的值,SystemVerilog添加了return语句,有return返回return的内容,没有return返回函数名变量值

6.3.3 在任务和函数结束前返回

return

6.3.4 空函数

可以声明函数为void类型,这样的函数还可以有输入输出,感觉就像是task

6.3.5 使用名称传递任务/函数的参数

Verilog根据顺序传递参数,SystemVerilog增加了用形式参数的名称来传递参数,与端口实例化时端口连接的语法一样。

6.3.6 增强型函数形式参数

可以定义输入输出形式参数

6.3.7 无形式参数的函数

完全就是task

6.3.8 形式参数的缺省方向和类型

输入可以缺省,默认是输入

6.3.9 缺省的形式参数值

function int increment (int count = 0, step = 1);
    increment = count + step;
endfunction

调用函数increment的时候,如果没有传递count和step的值,则他们使用缺省值

6.3.10 数组、结构体和联合体作为形式参数

SystemVerilog允许非压缩数组、压缩或非压缩结构体和压缩、非压缩或标签联合体传递进/出任务和函数

6.3.11 用引用取代复制来传递参数

  • ref——只有自动任务和函数可以具有ref参数,引用调用的信息

  • const ref——只读引用参数,禁止任务或函数改动信息的内容

好处是调用的是真时值,而不是函数被调用的时候的值

6.3.12 命名的任务和函数结尾

endfunctionendtask后面可以加个冒号,跟函数或任务的名字,有助提升代码可读性

6.3.13 空任务和函数

七、过程语句

7.1 新操作符

7.1.1 递增和递减操作符

语句

操作

描述

j=i++

后加

i的值赋给j,然后i加1

j=++i

先加

i加1,然后i的值赋给j

j=i--

后减

i的值赋给j,然后i减1

j=--i

先减

i减1,然后i的值赋给j

递增和递减操作符都是阻塞赋值。(感觉不如不用,为难自己,可以不用,但是不能不知道)

7.1.2 赋值操作符

/********************************使用SystemVerilog的赋值操作符********************************/
import definitions::*;

module alu(
    input instruction_t     instr   ,

    output data_t           alu_out
);

    always_comb begin
        if(instr.op_type == SIGNED)begin
            alu_out.s_data = instr.op_a.s_data;
            unique case (instr.opc)
                ADD :alu_out.s_data += instr.op_b.s_data;
                SUB :alu_out.s_data -= instr.op_b.s_data;
                MULT:alu_out.s_data *= instr.op_b.s_data;
                DIV :alu_out.s_data /= instr.op_b.s_data;
                SL  :alu_out.s_data <<<= 2              ;
                SR  :alu_out.s_data >>>= 2              ;
            endcase
        end
        else begin
            alu_out.u_data = instr.op_a.u_data;
            unique case (instr.opc)
                ADD :alu_out.u_data += instr.op_b.u_data;
                SUB :alu_out.u_data -= instr.op_b.u_data;
                MULT:alu_out.u_data *= instr.op_b.u_data;
                DIV :alu_out.u_data /= instr.op_b.u_data;
                SL  :alu_out.u_data <<<= 2              ;
                SR  :alu_out.u_data >>>= 2              ;
            endcase
        end
    end
endmodule

7.1.3 有无关通配符的相等操作符

a

b

a==b

a===b

a==?b

a!=b

a!==b

a!=?b

0000

0000

1

1

1

0

0

0

0000

0101

0

0

0

1

1

1

010Z

0101

X

0

1

X

1

0

010Z

010Z

X

1

1

X

0

0

010X

010Z

X

0

1

X

1

0

010X

010X

X

1

1

X

0

0

7.1.4 设置成员操作符——inside

if (a inside {3'b001,3'b010,3'b100})等效于if((a == 3'b001)||(a == 3'b010)||(a == 3'b100))

7.2 操作数改进

7.2.1 四态和两态类型数据的运算

7.2.2 类型强制转换

y = a + logint'(r**3)

7.2.3 尺寸强制转换

sum = a + 16'(5)

7.2.4 符号强制转换

sum = signed'(a) + signed'(a)

7.3 改进的for循环

7.3.1 for循环中声明的局部变量

always_ff @( posedge clk ) begin
    for(bit[4:0]i = 0;i <= 15; i = i + 1)
    …
end

在局部用完,这个变量就消失

7.3.2 多重for循环赋值

for(int i =0,int j = 0; i * j < 128;i = i + 1,j= j + 3)

7.3.3 for循环中生命的变量的层次化引用

7.4 底部检测的do…while循环

7.5 foreach数组循环结构

7.6 新的跳转语句——break、continue、return

7.7 改进的块名

/********************************使用未命名的嵌套的begin…end块的代码段********************************/
always_ff @( posedge clock or negedge rst_n ) begin
    logic breakVar;
    if(!rst_n)begin
        ……//将所有输出复位
    end
    else begin
        case(SquatState)
            wait_rx_valid:begin
                Rxready <= 'b1;
                breakVar = 1;
                for(int j = 0; j < NumRx; j = j + 1)begin
                    for(int i = 0; i < NumRx; i = i + 1)begin
                        if(Rxvalid[I] && RoundRobin[i] && breakVar)begin
                            ATMcell <= RxATMcell[i];
                            Rxready <= 0;
                            SquatState <= wait_rx_not_valid;
                            breakVar = 0;
                        end
                    end
                end
            end
            ……//处理其他SquatState状态
        endcase
    end
end
/********************************使用命名的begin…end块的代码段********************************/
always_ff @( posedge clock or negedge rst_n ) begin:FSM_procedure
    logic breakVar;
    if(!rst_n)begin:reset_logic
        ……//将所有输出复位
    end:reset_logic
    else begin:FSM_sequencer
        case(SquatState)
            wait_rx_valid:begin:rx_valid_state
                Rxready <= 'b1;
                breakVar = 1;
                for(int j = 0; j < NumRx; j = j + 1)begin:loop1
                    for(int i = 0; i < NumRx; i = i + 1)begin:loop2
                        if(Rxvalid[I] && RoundRobin[i] && breakVar)begin:match
                            ATMcell <= RxATMcell[i];
                            Rxready <= 0;
                            SquatState <= wait_rx_not_valid;
                            breakVar = 0;
                        end:match
                    end:loop2
                end:loop1
            end:rx_valid_state
            ……//处理其他SquatState状态
        endcase
    end:FSM_sequencer
end:FSM_procedure

7.8 语句标号

begin:block1 //已命名的块
    ……
end:block1

block2:begin//带标号的块
    ……
end

7.9 改进的case语句

7.9.1 unique case条件判断

unique case语句指定:

  • 只有一个条件选项与条件表达式匹配

  • 必须有一个条件选项与条件表达式匹配

7.9.2 priority case 语句

priority case语句指定:

  • 至少有一个条件选项的值与条件表达式匹配

  • 如果有多个条件选项的值与条件表达式匹配,必须执行第一个匹配分支

7.9.3 unique、priority与parallel_case、full_case的对比

推荐使用uniquepriority

7.10 改进的if…else判断语句

7.10.1 unique if…else判断语句

无优先级

7.10.2 priority if判断语句

有优先级

八、有限状态机建模

8.1 使用枚举类型建立状态机模型

/********************************使用枚举类型建模的有限状态机********************************/
module traffic_light(
    //Input
    input wire          sensor          ,
    input wire          clock           ,
    input wire          rst_n           ,
    input wire [15:0]   green_downcnt   ,
    input wire [15:0]   yellow_downcnt  ,

    output logic        green_light     ,
    output logic        yellow_light    ,
    output logic        red_light
);

    typedef enum logic[1:0] {RED,GREEN,YELLOW} state_t;
    state_t curr_state;
    state_t next_state;

    always_ff @( posedge clock or negedge rst_n ) begin
        if(!rst_n)begin
            curr_state <= RED;      //复位为红灯
        end
        else begin
            curr_state <= next_state;
        end
    end

    always_comb begin:set_next_state
        next_state = curr_state;
        unique case(curr_state)
            RED:        if(sensor)              next_state = GREEN  ;
            GREEN:      if(green_downcnt == 0)  next_state = YELLOW ;
            YELLOW:     if(yellow_downcnt == 0) next_state = RED    ;
        endcase
    end:set_next_state

    always_comb begin:set_output
        {green_light,red_light,yellow_light} = 3'b0;
        unique case(curr_state)
            RED:        red_light   = 1'b1;
            GREEN:      green_light = 1'b1;
            YELLOW:     yellow_light= 1'b1;
        endcase
    end:set_output
endmodule

8.1.1 使用枚举类型表示状态编码

/********************************指定使用枚举类型指明用one-hot码进行建模********************************/
module traffic_light(
    input wire          clock           ,
    input wire          rst_n           ,
    input wire          sensor          ,
    input wire [15:0]   yellow_downcnt  ,
    input wire [15:0]   green_downcnt   ,

    output logic        green_light     ,
    output logic        yellow_light    ,
    output logic        red_light
);

    typedef enum logic[2:0] {   RED   = 3'b001,
                                GREEN = 3'b010,
                                YELLOW= 3'b100 } state_t;
    state_t curr_State;
    state_t next_state;

    always_ff @( posedge clock or negedge rst_n ) begin : set_curr_stsate
        if(!rst_n)begin
            curr_State <= RED;
        end
        else begin
            curr_State <= next_state;
        end
    end

    always_comb begin : set_next_state
        next_state = curr_State;
        unique case(curr_State)
            RED:        if(sensor)              next_state = GREEN  ;
            GREEN:      if(green_downcnt == 0)  next_state = YELLOW ;
            YELLOW:     if(yellow_downcnt == 0) next_state = RED    ;
        endcase
    end:set_next_state

    always_comb begin : set_output
        unique case(curr_State)
            RED:        red_light   = 1'b1;
            GREEN:      green_light = 1'b1;
            YELLOW:     yellow_light= 1'b1;
        endcase
    end:set_output
endmodule

8.1.2 使用枚举类型的反向case语句(非常好的新写法,值得学习)

/********************************使用反向case语句风格用one-hot码进行编码********************************/
module traffic_light(
    input wire          clock           ,
    input wire          rst_n           ,
    input wire          sensor          ,
    input wire [15:0]   yellow_downcnt  ,
    input wire [15:0]   green_downcnt   ,

    output logic        green_light     ,
    output logic        yellow_light    ,
    output logic        red_light
);

    enum {  R_BIT = 0,      //状态寄存器中RED状态的索引
            G_BIT = 1,      //状态寄存器中GREEN状态的索引
            Y_BIT = 2   }state_bit;
    //将1移到表示每个状态的位上

    typedef enum logic[2:0] {   RED   = 3'b001  <<  R_BIT,
                                GREEN = 3'b001  <<  G_BIT,
                                YELLOW= 3'b001  <<  Y_BIT} state_t;
    state_t curr_State;
    state_t next_state;

    always_ff @( posedge clock or negedge rst_n ) begin : set_curr_stsate
        if(!rst_n)begin
            curr_State <= RED;
        end
        else begin
            curr_State <= next_state;
        end
    end

    always_comb begin : set_next_state
        next_state = curr_State;
        unique case(1'b1)
            curr_State[R_BIT]:      if(sensor)              next_state = GREEN  ;
            curr_State[G_BIT]:      if(green_downcnt == 0)  next_state = YELLOW ;
            curr_State[Y_BIT]:      if(yellow_downcnt == 0) next_state = RED    ;
        endcase
    end:set_next_state

    always_comb begin : set_output
        {red_light,green_light,yellow_light}    <= 3'b000;
        unique case(1'b1)
            curr_State[R_BIT]:      red_light   = 1'b1;
            curr_State[G_BIT]:      green_light = 1'b1;
            curr_State[Y_BIT]:      yellow_light= 1'b1;
        endcase
    end:set_output
endmodule

Sunberst Design公司的Cliff Cummings有很多使用位移操作符来指定状态变量枚举值的巧妙编码技巧。其他FSM编码技巧可以在Cliff的网站上找到

8.1.3 枚举类型与unique case语句

8.1.4 指定未使用的状态值

unique case不用加default,这合乎语法规范吗?主要是加也加不了吧,最多加一个default: next_state = default_state,其中default_state = 'bx'

8.1.5 将状态值赋给枚举类型变量

尽量使用枚举列表中的标签而不是具体数值对枚举变量赋值

8.1.6 对枚举类型变量的操作

8.2 在FSM模型中使用两态数据类型

8.2.1 使用两态类型和枚举类型对FSM复位

最好用四态枚举类型

九、层次化设计

9.1 模块原型

9.1.1 原型和实际定义

SystemVerilog要求extern module声明的端口列表必须与模块的实际定义严格匹配,包括端口顺序和端口位数。

9.1.2 避免端口冗余声明

extern module counter #(
    parameter N = 15
)(
    input wire          clock   ,
    input wire          load    ,
    input wire          rst_n   ,
    input wire [N:0]    d       ,

    ouptut logic [N:0]  cnt
);
endmodule

module counter(.*);
    always_ff @(posedge clock or negedge rst_n)begin
        if(!rst_n)begin
            cnt <= 0;
        end
        else if(load)begin
            cnt <= d;
        end
        else begin
            cnt <= cnt + 1;
        end
    end
endmodule

9.2 命名的结束语句

9.2.1 命名的模块结尾

module的嵌套就不学了吧,感觉很不规范的样子,可读性直线下降

十、接口

10.1 接口的概念

4

/********************************简单设计的Verilog模块互联********************************/
module top(
    input wire      clock       ,
    input wire      rst_n       ,
    input wire      test_mode
);

    wire [15:0]     data                ;
    wire [15:0]     address             ;
    wire [15:0]     program_address     ;
    wire [15:0]     jump_address        ;
    wire [7:0]      instruction         ;
    wire [7:0]      next_instruction    ;
    wire [3:0]      slave_instruction   ;
    wire            slave_request       ;
    wire            slave_ready         ;
    wire            bus_request         ;
    wire            bus_grant           ;
    wire            mem_read            ;
    wire            mem_write           ;
    wire            data_ready          ;

processor u_processor(
    .inout   data       ( inout   data       ),
    .bus_request        ( bus_request        ),
    .slave_ready        ( slave_ready        ),
    .address            ( address            ),
    .slave_instruction  ( slave_instruction  ),
    .slave_request      ( slave_request      ),
    .bus_grant          ( bus_grant          ),
    .mem_read           ( mem_read           ),
    .mem_write          ( mem_write          ),
    .instruction        ( instruction        ),
    .clock              ( clock              ),
    .rst_n              ( rst_n              ),
    .test_mode          ( test_mode          ),
    .jump_address       ( jump_address       )
);

slave u_slave(
    .inout   data       ( inout   data       ),
    .inout   address    ( inout   address    ),
    .slave_instruction  ( slave_instruction  ),
    .slave_request      ( slave_request      ),
    .bus_grant          ( bus_grant          ),
    .data_ready         ( data_ready         ),
    .bus_request        ( bus_request        ),
    .slave_ready        ( slave_ready        ),
    .mem_read           ( mem_read           ),
    .mem_write          ( mem_write          ),
    .clock              ( clock              ),
    .rst_n              ( rst_n              )
);

dual_port_ram u_dual_port_ram(
    .inout   data     ( inout   data     ),
    .address          ( address          ),
    .mem_read         ( mem_read         ),
    .mem_write        ( mem_write        ),
    .data_ready       ( data_ready       ),
    .program_address  ( program_address  ),
    .p7:0             ( p7:0             )
);

test_generator u_test_generator(
    .data       ( data       ),
    .address    ( address    ),
    .mem_read   ( mem_read   ),
    .mem_write  ( mem_write  ),
    .clock      ( clock      ),
    .rst_n      ( rst_n      ),
    .test_mode  ( test_mode  )
);

instruction_reg u_instruction_reg(
    .jump_address      ( jump_address      ),
    .next_instruction  ( next_instruction  ),
    .clock             ( clock             ),
    .rst_n             ( rst_n             ),
    .program_address   ( program_address   ),
    .instruction       ( instruction       )
);

endmodule

/********************************模块定义********************************/
module processor(
    //main_bus端口
    inout   wire    [15:0]      data                ,
    input   wire                bus_request         ,
    input   wire                slave_ready         ,
    output  reg     [15:0]      address             ,
    output  reg     [3:0]       slave_instruction   ,
    output  reg                 slave_request       ,
    output  reg                 bus_grant           ,
    output  wire                mem_read            ,
    output  wire                mem_write           ,

    //其他端口
    input   wire    [7:0]       instruction         ,
    input   wire                clock               ,
    input   wire                rst_n               ,
    input   wire                test_mode           ,
    output  reg     [15:0]      jump_address
);
    ……//模块功能代码
endmodule

module slave(
    //main_bus端口
    inout   wire    [15:0]      data                ,
    inout   wire    [15:0]      address             ,
    input   wire    [3:0]       slave_instruction   ,
    input   wire                slave_request       ,
    input   wire                bus_grant           ,
    input   wire                data_ready          ,
    output  reg                 bus_request         ,
    output  reg                 slave_ready         ,
    output  wire                mem_read            ,
    output  wire                mem_write           ,

    //其他端口
    input   wire                clock               ,
    input   wire                rst_n
);
    ……//模块功能代码
endmodule

module dual_port_ram(
    //main_bus端口
    inout   wire    [15:0]      data                ,
    input   wire    [15:0]      address             ,
    input   tri0                mem_read            ,
    input   tri0                mem_write           ,
    output  wire                data_ready          ,

    //其他端口
    input   wire    [15:0]      program_address     ,
    output  reg     p7:0[       data_b]
);
    ……//模块功能代码
endmodule

module test_generator(
    //main_bus端口
    output  wire    [15:0]      data                ,
    output  reg     [15:0]      address             ,
    output  reg                 mem_read            ,
    output  reg                 mem_write           ,

    //其他端口
    input   wire                clock               ,
    input   wire                rst_n               ,
    input   wire                test_mode
);
    ……//模块功能代码
endmodule

module instruction_reg(
    input   wire    [15:0]      jump_address        ,
    input   wire    [7:0]       next_instruction    ,
    input   wire                clock               ,
    input   wire                rst_n               ,
    output  reg     [15:0]      program_address     ,
    output  reg     [7:0]       instruction         ,
);
    ……//模块功能代码
endmodule

10.1.1 Verilog模块端口的缺点

  • 在多个模块中必须重复声明端口

  • 在多个模块中通信协议也必须重复

  • 在不同模块中有声明不匹配的风险

  • 设计规范中的一个改动需要修改多个模块

10.1.2 SystemVerilog接口的优势

/********************************接口定义********************************/
interface main_bus;
    wire    [15:0]  data                        ;
    wire    [15:0]  address                     ;
    logic   [7:0]   slave_instruction           ;
    logic           slave_request               ;
    logic           bus_grant                   ;
    logic           bus_request                 ;
    logic           slave_ready                 ;
    logic           data_ready                  ;
    logic           mem_ready                   ;
    logic           mem_write                   ;
endinterface

/********************************顶层网表********************************/
module top(
    input   wire                clock       ,
    input   wire                rst_n       ,
    input   wire                test_mode
);

    logic [15:0]    program_address ;
    logic [15:0]    jump_address    ;
    logic [7:0]     instruction     ;
    logic [7:0]     next_instruction;

    main_bus bus();     //接口实例化,实例名字是bus

    processor procl(
        //main_bus端口
        .bus            ( bus           ),//接口连接
        //其它端口
        .junp_address   ( jump_address  ),
        .instruction    ( instruction   ),
        .clock          ( clock         ),
        .rst_n          ( rst_n         ),
        .test_mode      ( test_mode     )
    );

    slave slave1(
        //main端口
        .bus            ( bus           ),
        //其他端口
        .clock          ( clock         ),
        .rst_n          ( rst_n         )
    );

    dual_port_ram ram(
        //main_bus端口
        .bus            ( bus               ),
        //其他端口
        .program_address( program_address   ),
        .data_b         ( next_instruction  )
    );

    test_generator test_gen(
        //main_bus端口
        .bus            ( bus               ),
        //其他端口
        .clock          ( clock             ),
        .rst_n          ( rst_n             ),
        .test_mode      ( test_mode         )
    );

    instruction_reg ir(
        .program_address    ( program_address   ),
        .instruction        ( instruction       ),
        .jump_address       ( jump_address      ),
        .next_instruction   ( next_instruction  ),
        .clock              ( clock             ),
        .rst_n              ( rst_n             )
    );
endmodule

/********************************模块定义********************************/
module processor(
    //main_bus的接口端口
    main_bus        bus,
    input   logic   [7:0]   instruction ,
    input   logic           clock       ,
    input   logic           rst_n       ,
    input   logic           test_mode   ,
    output  logic   [7:0]   jump_address
);
    ……//模块功能代码
endmodule

module slave(
    //main_bus端口
    main_bus                    bus                 ,

    //其他端口
    input   wire                clock               ,
    input   wire                rst_n
);
    ……//模块功能代码
endmodule

module dual_port_ram(
    //main_bus端口
    main_bus                    bus                 ,

    //其他端口
    input   wire    [15:0]      program_address     ,
    output  reg     p7:0[       data_b]
);
    ……//模块功能代码
endmodule

module test_generator(
    //main_bus端口
    main_bus                    bus                 ,

    //其他端口
    input   wire                clock               ,
    input   wire                rst_n               ,
    input   wire                test_mode
);
    ……//模块功能代码
endmodule

module instruction_reg(
    input   wire    [15:0]      jump_address        ,
    input   wire    [7:0]       next_instruction    ,
    input   wire                clock               ,
    input   wire                rst_n               ,
    output  reg     [15:0]      program_address     ,
    output  reg     [7:0]       instruction         ,
);
    ……//模块功能代码
endmodule

10.1.3 接口的内容

使用接口可以:

  • 在一个地方——接口中定义通信所需的各个信号和端口

  • 在接口中定义通信协议

  • 在接口中直接建立协议校验和其他验证程序

10.1.4 接口与模块的不同点

  1. 接口不可以包含设计层次

  2. 接口可以用作模块端口,表示模块间的通信通道

  3. 接口可以包含modport

10.2 接口声明

/********************************接口定义********************************/
interface main_bus(
    input logic clock       ,
    input logic rst_n       ,
    input logic test_mode
);

    wire [15:0] data                ;
    wire [15:0] address             ;
    logic [7:0] slave_instruction   ;
    logic       slave_request       ;
    logic       bus_grant           ;
    logic       bus_request         ;
    logic       slave_ready         ;
    logic       data_ready          ;
    logic       mem_read            ;
    logic       mem_write           ;
endinterface //main_bus

/********************************顶层网表********************************/
module top(
    input logic         clock       ,
    input logic         rst_n       ,
    input logic         test_mode
);

    logic   [15:0]      program_address     ;
    logic   [15:0]      jump_address        ;
    logic   [7:0]       instruction         ;
    logic   [7:0]       next_instruction    ;

    main_bus bus(
        .clock      ( clock     ),
        .rst_n      ( rst_n     ),
        .test_mode  ( test_mode )
    );

    processor procl(
        .bus            ( bus           ),
        .jump_address   ( jump_address  ),
        .instruction    ( instruction   )
    );
	
    ...
endmodule

/********************************使用.*连接的接口来简化复杂网表********************************/
interface main_bus(
    input logic clock       ,
    input logic rst_n       ,
    input logic test_mode
);

    wire [15:0] data                ;
    wire [15:0] address             ;
    logic [7:0] slave_instruction   ;
    logic       slave_request       ;
    logic       bus_grant           ;
    logic       bus_request         ;
    logic       slave_ready         ;
    logic       data_ready          ;
    logic       mem_read            ;
    logic       mem_write           ;
endinterface //main_bus

/********************************顶层网表********************************/
module top(
    input logic         clock       ,
    input logic         rst_n       ,
    input logic         test_mode
);

    logic   [15:0]      program_address     ;
    logic   [15:0]      jump_address        ;
    logic   [7:0]       instruction         ;
    logic   [7:0]       next_instruction    ;
    logic   [7:0]       data_b              ;

    main_bus            bus             (.*);
    processor           procl           (.*);
    slave               slave1          (.*);
    instruction         ir              (.*);
    test_generator      test_gen        (.*);
    dual_port_ram       ram             (.*,.data_b(next_instruction));
endmodule

10.2.1 源代码声明次序

10.2.2 全局与局部接口定义

10.3 将接口用作模块端口

10.3.1 显式命名的接口端口

10.3.2 通用接口端口

10.3.3 综合指导

10.4 接口的实例化和连接

10.5 接口内部信号的引用

/********************************接口内部信号的引用********************************/
module slave(
    //main_bus接口端口
    main_bus bus
    //其他端口
);
//内部信号
    logic [15:0]    slave_data      ;
    logic [15:0]    slave_address   ;
    logic [15:0]    operand_A       ;
    logic [15:0]    operand_B       ;
    logic           mem_select      ;
    logic           read            ;
    logic           write           ;

    assign bus.slvae_address    = mem_select?slave_address  :'z;
    assign bus.data             = bus_slave_ready?slave_Data:'z;

    typedef enum logic [4:0]    { RESET     = 5'b00001,
                                  START     = 5'b00010,
                                  REQ_DATA  = 5'b00100,
                                  EXECUTE   = 5'b01000,
                                  DONE      = 5'b10000} state_t;

    state_t curr_state  ;
    state_t next_state  ;

    always_ff @( posedge bus.clock or negedge bus.rst_n ) begin
        if(!bus.rst_n)begin
            curr_state <= RESET;
        end
        else begin
            curr_state <= next_state;
        end
    end

    always_comb begin
        unique case(curr_state)
            START:begin
                if(!bus.slave_request)begin
                    bus.bus_request = 0;
                    next_state = state;
                end
                else begin
                    operand_A       = bus.data      ;
                    slave_address   = bus.address   ;
                end
            end

            //其他状态译码
        endcase
    end
endmodule

10.6 接口的modport

/********************************选择在模块实例中使用哪种modport********************************/
interface chip_bus (
    input logic     clock   ,
    input logic     rst_n
);
    modport master(...);
    modport slave(...);
endinterface

module primary (interface pins);//通用接口端口
    ...
endmodule

module secondary (chip_bus pins);//特定接口端口
    ...
endmodule

module chip(
    input logic     clock   ,
    input logic     rst_n
);
    chip_bus bus (clock,rst_n); //接口实例
    primary i1  (bus.master);   //使用master modport方式
    secondary i2 (bus.slave);   //使用slave modport方式
endmodule

/********************************选择在模块实例中使用哪种modport********************************/
interface chip_bus (
    input logic     clock   ,
    input logic     rst_n
);
    modport master(...);
    modport slave(...);
endinterface

module primary (chip_bus.master pins);//通用接口端口
    ...
endmodule

module secondary (chip_bus.slave pins);//特定接口端口
    ...
endmodule

module chip(
    input logic     clock   ,
    input logic     rst_n
);
    chip_bus bus (clock,rst_n); //接口实例
    primary i1  (bus);   //使用master modport方式
    secondary i2 (bus);   //使用slave modport方式
endmodule

10.6.2 使用modport定义不同的连接

可以层次化连接端口

10.7 在接口中使用任务和函数

10.7.1 接口的方法

可以封装连接模块的数据,也可以用来封装模块间的通信协议

10.7.2 接口方法的导入(import)

/********************************使用modport选择接口内的可选方法********************************/
/********************************接口定义********************************/
interface math_bus(
    input logic         clock   ,
    input logic         rst_n
);

    int                 a_int       ;
    int                 b_int       ;
    int                 result_int  ;
    real                a_real      ;
    real                b_real      ;
    real                result_real ;
    ...
    task IntergeRead(
        output int      a_int,
        output int      b_int
    );
    ...//进行握手来提取a和b的值
    endtask

    task FloatingPointRead(
        output real     a_real      ,
        output real     b_real
    );
    ...//进行握手来提取a和b的值
    endtask

    modport int_io (
        import  IntegerRead ,
        input   clock       ,
        input   rst_n       ,
        output  result_int
    );

    modport fp_i0 (
        import  FloatingPointRead   ,
        input   clock               ,
        input   rst_n               ,
        output  result_real
    );
endinterface

/********************************顶层网表********************************/
module dual_mu(
    input logic         clock   ,
    input logic         rst_n
);

    math_bus bus_a;
    math_bus bus_b;

    interger_math_unit i1 (bus_a.int_io);
    floating_point_unit i2 (bus_b.fp_io);
endmodule

/********************************模块定义********************************/
module integer_math_unit(interface io);
    int     a_reg,b_reg;
    always_ff @( posedge io.clock ) begin
        io.IntergeRead(a_reg,b_reg);//调用接口中的方法
        ..//进行算术运算
    end
endmodule

module floating_point_unit( interface io);
    real    a_reg,b_reg;
    always_ff @( posedge io.clock ) begin
        io.FloatingPointRead(a_reg,b_reg);
        ..
    end
endmodule

10.7.3 接口方法的综合原则

导入的函数和任务必须声明为自动类型,并且不能包含静态声明,这样才是可综合的。

10.7.4 导出(export)任务和函数

10.8 接口中的过程块

10.9 可重构接口

/********************************在模块中使用参数********************************/
interface math_bus #(
    parameter type DTYPE = int
)(
    input logic clock
);
    DTYPE a,b,result;//参数化的类型
    ...
    task Read(
        output DTYPE a,b
    );
    ...
    endtask

    modport int_io(
        import  Read        ,
        input   clock       ,
        output  result
    );

    modport fp_io(
        import  Read        ,
        input   clock       ,
        output  result
    );
endinterface

module top (
    input logic     clock   ,
    input logic     rst_n
);
    math_bus                    bus_a(clock);
    math_bus (#.DTYPE(real))    bus_b(clock);

    integer_math_unit i1 (bus_a.int_io);
    floating_point_unit i2(bus_b.fp_io);
endmodule

10.10 接口验证

十一、一个完整的建模

11.1 ATM实例

本章中用作例子的设计,是基于来自加尼克·伯杰验证行业协会(Janick Bergeron’s Verification Guild)的一个例子。原始的例子是用Verilog(遵循Verilog-1995标准)编写的不可综合的行为级模型。这个例子描述了一个用户与网络之间的四阶异步传输模式ATM的前向接口。在本书中,这个例子做了三项重大修改。第一代码使用了许多SystemVerilog的结构加以重写;第二原先的不可综合的行为级模型通过使用SystemVerilog的可综合子集加以重写;第三,模型修改为可配置的,这样可以很容易的把一个4X4的四阶开关扩展成 16X16 的开关或任何其他想要的配置。 ​ 本章中的例子说明了使用SystemVerilog的结构体联合体和数组是如何简化复杂的设计描述的。接口和接口方法的使用,则进一步简化了设计模块间复杂的数据通信。在这个例子中使用的SystemVerilog编码风格,也说明了如何在同一个源代码中自动的设置位数和配置。使用+define命令行选择项,设计的体系结构可以配置成一个NXP端口的前向节点,其中N和P可以是任意的正整数。原先的例子中,使用Verilog-1995只描述了一个固定的4X4设计。这个SystemVerilog的版本可以生成一个128X12816X128128X16或者其他任何可能的配置。模块和数据声明的大小设置和实例化是隐含处理的(包括这个例子中相对简单的测试平台)

11.2 数据抽象

这个ATM设计使用了两种ATM格式:UNI格式和NNI格式。

5

一个ATM单元只包含53字节的数据,这在Verilog中可以描述为一个字节数组。但是以这种方式建模时,单元中的这些字节的含义就会丢失。在SystemVerilog中,可以很容易的将这两种不同的格式定义为压缩结构体,并使单元的各个成员容易识别。

//UNI结构单元
typedef struct packed {
    logic       [ 3:0]  GFC     ;
    logic       [ 7:0]  VPI     ;
    logic       [15:0]  VCI     ;
    logic               CLP     ;
    logic       [ 2:0]  PT      ;
    logic       [ 7:0]  HEC     ;
    logic [0:47][ 7:0]  Payload ;
} uniType;

//NNI结构单元
typedef struct packed {
    logic       [11:0]  VPI     ;
    logic       [15:0]  VCI     ;
    logic               CLP     ;
    logic       [ 2:0]  PT      ;
    logic       [ 7:0]  HEC     ;
    logic [0:47][ 7:0]  Payload ;
} nniType;

//测试视图单元格式(payload部分)
typedef struct packed{
    logic [0:4 ][7:0]   Header  ;
    logic [0:3 ][7:0]   PortID  ;
    logic [0:3 ][7:0]   PacketID;
    logic [0:39][7:0]   Padding;
}tstType;

//UNI/NNI测试试图/字节流的联合体
typedef union packed {
    uniType             uni ;
    nniType             nni ;
    tstType             tst ;
    logic [0:52][7:0]   Mem ;
}ATMCellType;

现在,这53字节的数据可以很容易的以四种不同的方式配置:

  • UNI单元

  • NNI单元

  • 测试平台中带标签的信息包

  • 53字节的数据数组

11.3 接口封装

十二、行为级和交易级建模

本章定义属于行为及建模范畴得交易级建模(transaction level modeling,TLM)。包含以下主题:

  • 交易的定义

  • 总线的交易级模型

  • 多个从模块

  • 多个主模块间的仲裁

  • 旗语(semaphore)

  • 寄存器传输级模型和交易级模型接口

12.1 行为建模

12.2 什么是交易

SystemVerilog的交易级建模

行为级建模提高了模块功能的抽象层次,而交易级建模则通过隐藏接口间控制和数据流的细节,提高了模块和子系统间通信的抽象层次。