SystemVerilog学习笔记(可综合的部分)(一)

1. Verilog-1995的数据类型

         Verilog-1995有两种基本数据类型:变量(variables)网络(nets),包含4个状态值:0,1,Z,X。

变量(variables)的几种类型
1.无符号单bit或多bit变量 — reg [7:0] sum
2.有符号32bit — integer i
3.无符号64bit — time t
4.浮点数 — real r

         SystemVerilog向Verilog语言添加了bitlogic关键字,分别表示2状态和c。
         SystemVerilog的 网络(nets) 类型(如wire)仅使用4状态值集,而 变量(variables) 类型中一部分使用4状态值集,另一部分使用2状态值集

         bitlogic关键字也可以在不明确定义网络或变量的情况下使用,在这种情况下,网络或变量是从上下文推断出来的。关键字bit总是推断为变量。关键字logic在大多数中推断为变量,但如果与模块输入inout端口声明一起使用,则推断为网络。以下声明说明了这些推理规则

module A;
...
endmodule
module M (
	// module ports with inferred types
	input i1, 				// infers a 4-state net,推断为4状态网络
	input logic i2, 		// infers a 4-state net,推断为4状态网络
	input bit i3, 			// infers a 2-state variable,推断为2状态变量
	output o1, 				// infers a 4-state net,推断为4状态网络
	output logic o2, 		// infers a 4-state variable,推断为4状态变量
	output bit o3 			// infers a 2-state variable,推断为2状态变量
);
// internal signals with inferred and explicit types 
bit clock; 					// infers a 2-state variable
logic reset; 				// infers a 4-state variable
logic [7:0] data;			 // infers a 4-state variable
wire [7:0] n1; 				// explicitly declares a net, infers 4-state logic
wire logic [7:0] n2; 		// explicitly declares a 4-state net
var [7:0] v1;				 // explicitly declares a variable, infers logic
var logic [7:0] v2; 		// explicitly declares a 4-state variable
...
endmodule

2. 网络(net)类型

  1. wire和tri —— 互连网络,允许并解决多个驱动因素
  2. supply0和supply1 —— 分别具有常数0或1的互连网络
  3. wand,triand, wor, trior —— 将AND和OR多个驱动器连接在一起的网络互连

3.变量(variable)类型

  1. reg —— 用户定义向量大小的通用4状态变量
  2. integer —— 32位4状态变量
  3. logic —— 除模块输入/输入输出端口外,推断用户定义的向量大小的通用4状态变量
  4. bit —— -推断用户定义向量大小的通用2状态变量
  5. byte, shortint, int, longint —— 分别具有8位、16位、32位和64位向量大小的2状态变量

         logic关键字并不是真正的变量类型,但在几乎所有上下文中,logic都会推断为reg变量。由于这种推理,可以使用logic关键字代替reg,让语言自己来推断变量

4.向量(packed arrays)

         通过在方括号中指定一系列位,后跟向量名称来声明向量。在SystemVerilog中将向量定义为packed arrays,即压缩数组。例如:

wire [31:0] a;     // 32-bit向量 ,LSB
logic [1:32] b;     // 32-bit向量,MSB

SystemVerilog添加压缩数组的一个显著增强是,通过使用多个范围将向量声明划分为子字段的能力。例如:

 logic [3:0][7:0] bytes;		//32-bit向量,被分为4个8-bit子向量
 bytes = 32'hCAFE_DADA;	
 bytes[2] = 8'hFF;				//分配给向量的第2个子向量值
 bytes[1][0] = 1'b1;			//选择向量的第1个子向量的第0个bit			

         对于某些数据类型,您可能既要访问整个值,也要将其划分为较小的元素。例如,您可能有一个32位寄存器,有时需要将其视为四个8位值,有时需要将其视为单个无符号值。
         SystemVerilog压缩数组被视为数组和单个值。与 未压缩数组(unpacked arrays) 不同,它存储为一组连续的位,没有未使用的空间。
在这里插入图片描述可以看到向量在存储时是连续存储的,与后文要介绍的未压缩数组不同

packed arrays的维数定义在变量标识符之前
unpacked arrays的维数的定义在变量标识符之后

5.数组(unpacked arrays)

SystemVerilog允许声明网络、变量和用户定义类型的一维和多维数组。数组维度在数组名称之后声明。例如:

logic [7:0] LUT [0:255];				//一维数组,数组单个元素是8-bit
logic [7:0] RGB [0:15][0:15][0:15];		//三维数组,数组单个元素是8-bit

可能有些人会混淆数组和向量,数组的维数定义是在标识名称之后,而向量的维数定义是在标识名称之前

数组定义:
1. bit a [7:0][15:0] 		// 二维数组,数组元素为bit
2. int b [7:0][15:0] 		// 二维数组,数组元素是32位
3. bit [7:0] c [7:0][15:0]	// 二维数组,数组元素是8-bit

向量定义:
1.wire [31:0] a;			// 32-bit向量 ,LSB
2.logic [1:32] b;			// 32-bit向量,MSB

如何调用数组的元素:

logic Arrays [7:0][7:0] ;
Arrays [2][3] = 1'b1;

数组(unpacked arrays)将值存储在字(word,32-bit)的较低部分,而较高的位未使用。如下图所示,字节数组b_unpack存储在三个字中:
在这里插入图片描述

6. 数组和向量混合的用法(重要)

您可以混合数组向量。您可能希望创建一个数组,该数组表示可以以位、字节或长字形式访问的内存。如下图显示了barray数组,他是一个由五个packed arrays组成的unpacked arrays,每个元素宽四个字节,存储在内存中
在这里插入图片描述

7. C语言风格的数组定义

SystemVerilog允许通过指定数组大小来声明数组,方法与C语言中的方法相同。例如:

logic [7:0] LUT [256]; 				// one-dimensional array of 256 bytes
logic [7:0] RGB [16][16][16]; 		// three-dimensional array of bytes

使用此语法时,数组寻址始终以地址0开始,以数组大小减1结束。

8. 数组操作:复制

Verilog一次只允许访问阵列的单个元素。要将数据从一个数组复制到另一个数组,需要编写通过每个数组的每个元素建立索引的循环。SystemVerilog允许将数组复制为单个赋值语句。可以复制整个阵列或阵列的一部分。例如:

logic [31:0] big_array [0:255]; 		// array with 256 32-bit elements
logic [31:0] small_array [0:15]; 		// array with 16 32-bit elements
assign small_array = big_array[16:31]; 	// copy 16 elements of big_array

数组复制要求指定两侧的维度数和每个维度中的元素数相同。每个元素的位数也必须具有相同的大小和兼容的数据类型

9.数组操作:赋值

可以使用 '{} 中包含的值列表来指定数组的所有或多个元素。该列表可以包含单个数组元素的值,也可以包含整个数组的默认值。例如:

logic [7:0] a, b, c;
logic [7:0] d_array [0:3]; 				// array with 4 8-bit elements

always_ff @(posedge clock or negedge rstN) begin
	if (!rstN) begin
		d_array <= '{default:0}; 		// reset all elements of the array
	end
	else begin
		d_array <= '{8'h00, c, b, a}; 	// load the array
	end
end

10.数组操作:for和foreach

操作数组最常用的方法是使用for或foreach循环。SystemVerilog函数$size返回数组的大小。在foreach循环中,指定数组名和方括号中的索引,SystemVerilog会自动遍历数组的所有元素。索引变量会自动为您声明,并且是循环的局部变量

initial begin
	bit [31:0] src[5],dst[5] ;
	for (int i=0; i<$size(src) ; i++)
		src [i]=i;					// Initialize src array 
	foreach (dst [j] )
		dst[j] = src[j] * 2; 			// Set dst array to 2 * src
end

请注意,多维数组的foreach循环语法可能与您预期的不同。它们不是用单独的方括号[ i ] [ j ]列出每个下标,而是用逗号[i,j]组合。

int md[2][3] = '{'{0,1,2}, '{3,4,5}};
initial begin
	$display ("Initial value:") ;
	foreach (md[i,j]) 			// Yes, this is the right syntax 
		$display ("md[%0d] [%0d] = %0d", i, j, md[i] [j]) ;
		
	$display ("New value:") ;	// Replicate last 3 values of 5
	md = '{'{9, 8, 7},'{3{5}}};
	foreach (md[i,j]) 			// Yes,this is the right syntax
		$display ("md[&0d] [%0d] = %0d",i, j, md[i] [j]) ;
end

输出结果如下:

Initial value :
md[0] [0] = 0
md[0] [1] = 1
md[0] [2] = 2
md[1] [0] = 3
md[1] [1] = 4
md[1] [2] = 5
New value : 
md[0] [0] = 9
md[0] [1] = 8
md[0] [2] = 7
md[1] [0] = 5
md[1] [1] = 5
md[1] [2] = 5

如果不需要逐级遍历所有维度,可以在foreach循环中省略一些维度:

initial begin
	byte twoD[4] [6] ;
	foreach (twoD [i,j] )
		twoD[i] [j] = i*10+j;
		
	foreach (twoD[i]) begin			// Step through first dim.
		$write ("%2d:",i) ;
		foreach (twoD[,j] )				// Step through second
			$write ("%3d", twoD [i] [j] ) ;
		$display;
	end
end

输出结果如下:

0:	0	1	2	3	4	5
1:	10	11	12	13	14	15
2:	20 	21 	22	23	24	25
3:	30	31	32	33	34	35 

最后加深理解:

1.数组 f [ 5 ] 等价于 f [ 0 : 4 ]
2.foreach ( f [ 5 ] ) 等价于 for ( int i = 0 ; i <= 4 ; i++ )
3.对于数组 rev [ 6 : 2 ]
   foreach( rev [ i ] ) 等价于 for ( int i = 6 ; i >= 2 ; i-- )

11.通过模块端口将阵列传递给任务和函数

分配给一个数组的多个元素的功能还可以将数组用作模块端口或任务/函数参数。以下示例定义了一个用户定义的类型,该类型表示一个由32位元素组成的8x256二维数组,然后将该数组传递给函数和从函数传递出去,并通过模块端口传递出去。

typedef logic [31:0] d_array_t [0:7][0:255];
module block_data (
	input d_array_t d_in, 							// input is an array
	output d_array_t q_out, 						// output is an array
	input logic clock, rstN
);

function d_array_t transform (input d_array_t d); 	// input is an array
	// ... perform operations on all elements of d
	return d; // return is an array
endfunction

always_ff @(posedge clock or negedge rstN) 
	if (!rstN) q_out <={default:0}; // reset entire q_out array
	else q_out <= transform(d_in); // transform and store entire array
endmodule

12.数组查询系统功能

SystemVerilog提供了许多特殊的系统功能,使操作数组更容易,而无需对数组大小进行硬编码。可综合的数组查询功能包括:

$left()
$right()
$low()
$high()
$increment()
$size()
$dimensions()
$unpacked_dimensions()

13.用户定义的数据类型

SystemVerilog允许设计和验证工程师创建新的、用户定义的数据类型。变量和网络都可以声明为用户定义的类型。如果未指定var或net type关键字,则假定用户定义的类型为变量。
可综合的用户定义类型有:

1.enum-具有法定值枚举列表的变量或网络-参见第12.12.struct-包含多个网络或变量的结构-参见第12.23.union-可以在不同时间表示不同类型的变量-参见第12.34.typedef-类型定义-参见第12.4

13.1 enum枚举类型

枚举类型允许使用一组特定的命名值定义变量和网络。声明枚举类型的基本语法为:

// 一个具有3个合法值的变量
enum {WAITE, LOAD, DONE} State;

枚举类型有一个基本数据类型,默认情况下是int(一种2状态32位类型)。在上面的示例中,State是int类型,WAITE、LOAD和DONE将具有32位int值。

枚举列表中的标签是具有关联逻辑值的常量。默认情况下,列表中的第一个标签的逻辑值为0,随后的每个标签都会递增一。因此,在上面的示例中,WAITE是0,LOAD是1,DONE是2。

你也可以显式的指定每个标签的基类型:

// 两个3位、4状态枚举变量,带有一个热值枚举逻辑
enum logic [2:0] {	WAITE = 3’b001,
					LOAD = 3’b010, 
					DONE = 3’b100} State, NextState;
// State,NextState是3-bit的4状态变量,其值只能是WAITE/LOAD/DONE中的一个
// 枚举类型内每个标签的值都是唯一的,不可以和别的标签相同

枚举类型定义的变量,其值只能是枚举标签其中之一(有特殊情况下也会取到标签之外的值,后文会说)

枚举类型在状态机中使用有很大的便利,使用枚举类型可以将逻辑错误转换成语法错误,这样可以在编译阶段就能排查出错误,而不必在功能验证阶段才发现,从而大大节省时间。下面两段代码分别展示了使用reg类型和enum类型编写状态机的代码,其中有一些bug,在第一段代码里可能是可以编译通过的,但状态机功能错误,而在第二段代码里是语法错误,编译无法通过。

// Names for state machine states (one-hot encoding)
parameter [2:0] WAITE=3'b001, LOAD=3'b010, DONE=3'b001; // FUNCTIONAL BUG

// Names for mode_control output values
parameter [1:0] READY=3'b101, SET=3'b010, GO=3'b110; // FUNCTIONAL BUG

// State and next state variables
reg [2:0] state, next_state, mode_control;

// State Sequencer
always @(posedge clock or negedge resetN)
	if (!resetN) state <= 0; // FUNCTIONAL BUG
	else state <= next_state;

// Next State Decoder (sequentially cycle through the three states)
always @(state) 
	case (state)
		WAITE: next_state = state + 1; // DANGEROUS CODE
		LOAD : next_state = state + 1; // FUNCTIONAL BUG
		DONE : next_state = state + 1; // FUNCTIONAL BUG
	endcase

// Output Decoder
always @(state) 
	case (state)
		WAITE: mode_control = READY;
		LOAD : mode_control = SET;
		DONE : mode_control = DONE; // FUNCTIONAL BUG
	endcase
	
endmodule
module bad_fsm_systemverilog_style (...); // only relevant code shown

enum logic [2:0] {WAITE=3'b001, LOAD=3'b010, DONE=3'b001} // SYNTAX ERROR
state, next_state;

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

// State Sequencer
always_ff @(posedge clock or negedge resetN)
	if (!resetN) state <= 0; // SYNTAX ERROR
	else state <= next_state;

// Next State Decoder (sequentially cycle through the three states)
always_comb 
	case (state)
		WAITE: next_state = state + 1; // SYNTAX ERROR
		LOAD : next_state = state + 1; // SYNTAX ERROR
		DONE : next_state = state + 1; // SYNTAX ERROR
	endcase

// Output Decoder
always_comb 
	case (state)
		WAITE: mode_control = READY;
		LOAD : mode_control = SET;
		DONE : mode_control = DONE; // SYNTAX ERROR
	endcase
	
endmodule

SystemVerilog提供了一些枚举类型的方法:

enum logic [3:0] { IDLE = 4'b0001 , sta0 = 4'b0010 , sta1 = 4'b0100} state , nextState;
state = IDLE;

则:
nextState = state.first();
nextState = state.last();
nextState = state.next();
nextState = state.prev();
nextState = state.num();
nextState = state.name();

在这里插入图片描述

13.2 struct结构体类型

SystemVerilog结构提供了一种机制,可以在一个公共名称下收集多个变量:

struct {
 logic [7:0] source_address;
 logic [7:0] destination_address;
 logic [7:0] data;
 logic [3:0] ecc;
} packet;

可以使用点运算符(.)访问结构的各个成员

packet.source_address = 8’h01;

也可以使用'{}对结构体赋值

packet <= '{8'h01 , 8'h03 , 8'haa , 4'hf};
packet <= '{default:0}; 		// reset all members of packet

结构体数据的存储可以通过声明packed使其连续存储

struct packed { // members will be stored contiguously
logic [ 1:0] parity;
logic [63:0] data;
} data_word;

在这里插入图片描述

13.3 union联合体类型

联合体使各成员共用一个存储空间,使用单个存储空间表示多种存储格式。以下示例将无符号位向量b和整数i存储在同一位置:

union {
	bit [31:0] b;
	int i;
} un ;
un.i = -1; 
// 此时i的存储空间为h80000001,有于i和b共用一个存储空间,所以b为h80000001

13.4 typedef定义类型

可以使用typedef定义新的用户自定义类型,新的数据类型是使用typedef从内置类型和其他用户定义的类型构造而成的,类似于C。一些简单的示例如下:

typedef logic [31:0] bus64_t; 		// 64-bit bus
typedef struct { 					// typed structure
	logic [ 1:0] parity;
 	logic [63:0] data;
} data_t;
typedef enum logic {FALSE=1’b0, TRUE=1’b1} bool_t;

module D (input data_t a, b, 
output bus64_t result,
output bool_t aok );
...
endmodule

14. packages 包的导入和引用

原始Verilog语言没有共享的声明空间。每个模块都包含该模块中使用的所有声明。这是一个主要的语言限制。如果在多个模块中需要相同的参数、任务或函数定义,设计者必须纵容笨拙的工作循环,通常使用ifdef和include编译器指令的组合。

SystemVerilog用户定义类型、面向对象类定义和随机化约束的添加使得缺少共享声明空间成为一个严重的问题。

SystemVerilog通过添加用户定义的包解决了Verilog的缺点。包提供了一个声明空间,可以从任何设计模块以及验证代码中引用该空间。

package alu_types;

	localparam DELAY = 1;
	
	typedef logic [31:0] bus32_t;
	typedef logic [63:0] bus64_t;
	
	typedef enum logic [3:0] {ADD, SUB, ...} opcode_t;
	
	typedef struct {
		bus32_t i0, i1;
		opcode_t opcode;
	} instr_t;
	
	function automatic logic parity_gen(input d);
		return ^d;
	endfunction
	
endpackage

声明空间中的用户自定义类型可以在引用了该声明空间的module中任意使用

模块引用package有三种方式:

1.显示包引用
2.显示导入语句
3 通配符导入语句

1.显示包引用:

module alu (
	input alu_types::instr_t instruction, 	// use package item in port list
	output alu_types::bus64_t result 
);
	alu_types::bus64_t temp; 				// use package item within module
...
endmodule

对包项的显式引用不会使该项在模块中的其他位置可见。每次在模块中使用该定义时,都必须使用显式引用。

2.显示导入语句:

module alu
import alu_types::bus64_t;
(
	input alu_types::instr_t instruction, 	// explicit package reference
	output bus64_t result 
); 											// bus64_t has been imported

bus64_t temp; 								// bus64_t has been imported
...
endmodule

导入后,该项可以在模块中被任意引用

3.通配符导入语句

module alu
import alu_types::*;
(
	input instr_t instruction, 		// instr_t has been imported
	output bus64_t result 
); 									// bus64_t has been imported

bus64_t temp;					    // bus64_t has been imported
...
endmodule

通配符导入使用*表示包中的所有定义。通配符导入使包的所有项在模块内可见。

包可以消除重复代码、设计的不同块中不匹配的风险以及维护重复代码的困难。
推荐使用软件包!包提供了一种干净、简单的方法,可以在整个设计和验证项目中重用任务、函数和用户定义类型的定义。

模块中包导入的位置很重要,如果希望在端口列表中就使用包中的自定义类型,则包的导入就要在端口列表之前

一个包可以引用另一个包的定义,也可以从另一个包导入定义

15. RTL编程系统

SystemVerilog比传统的Verilog增加了许多重要的编程功能。这些增强的目的有三个:

1.能够用更少的代码行建模更多的功能
2.降低设计中出现功能错误的风险
3.帮助确保仿真和综合以相同的方式解释设计功能。

未完待续…
20220528

  • 7
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SystemVerilog是一种硬件描述语言(HDL),用于设计和验证数字电路。SystemVerilog具有丰富的特性,适用于各种设计和验证任务。以下是SystemVerilog入门笔记的一些重点内容。 首先,了解SystemVerilog的基本语法是很重要的。SystemVerilog继承了Verilog的语法,同时加入了一些额外的特性,如数据类型、类和接口等。熟悉这些语法规则对于编写有效的代码至关重要。 其次,了解模块和端口的概念。SystemVerilog中的模块是最基本的构建单元,而端口则是模块与外部环境之间的接口。了解如何定义和使用模块和端口是编写可重用代码的关键。 另外,掌握数据类型和运算符是必不可少的。SystemVerilog支持多种数据类型,如整数、浮点数、逻辑值等,同时也提供了丰富的运算符,如加法、减法、逻辑运算符等。这些知识对于设计和验证数字电路非常重要。 此外,了解时序控制和并发控制是必要的。SystemVerilog提供了多种时序控制和并发控制的语法和特性,如always块、initial块和fork/join语句等。掌握这些知识对于设计复杂的数字电路至关重要。 最后,了解Verilog验证中的一些基本概念也是很重要的。例如,了解如何使用assert语句进行断言检查、如何使用coverage语句进行覆盖率分析等。 总之,SystemVerilog入门需要掌握其基本语法、模块和端口、数据类型和运算符、时序控制和并发控制,以及一些基本的验证概念。这些知识是成为一名合格的SystemVerilog工程师所必备的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值