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

这篇具有很好参考价值的文章主要介绍了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) 不同,它存储为一组连续的位,没有未使用的空间。
SystemVerilog学习笔记(可综合的部分)(一)可以看到向量在存储时是连续存储的,与后文要介绍的未压缩数组不同

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存储在三个字中:
SystemVerilog学习笔记(可综合的部分)(一)

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

您可以混合数组向量。您可能希望创建一个数组,该数组表示可以以位、字节或长字形式访问的内存。如下图显示了barray数组,他是一个由五个packed arrays组成的unpacked arrays,每个元素宽四个字节,存储在内存中
SystemVerilog学习笔记(可综合的部分)(一)

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();

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

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;

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

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文章来源地址https://www.toymoban.com/news/detail-463879.html

到了这里,关于SystemVerilog学习笔记(可综合的部分)(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Verilog 学习第五节(串口发送部分)

    1:串口通信模块设计的目的是用来发送数据的,因此需要有一个数据输入端口 2:串口通信,支持不同的波特率,所以需要一个 波特率设置端口 3:串口通信的本质就是将8位并行数据通过一根信号线,在不同的时刻传输并行数据的不同位,通过多个时刻,最终将8位并行数据

    2023年04月26日
    浏览(39)
  • 使用 VHDL、Verilog、SystemVerilog、SystemC、HLS(C++、OpenCL)进行数字硬件建模

    目录 引言 1. 数字硬件建模概述 1.1 硬件描述语言 1.2 系统级建模语言

    2024年02月08日
    浏览(90)
  • Java学习笔记---多态综合练习

    运行结果:

    2024年02月09日
    浏览(31)
  • Verilog学习笔记(5):Verilog高级程序设计

    串行加法器: 一个四位串行加法器由4个全加器构成。全加器是串行加法器的子模块,而全加器是由基本的逻辑门构成,这些基本的逻辑门就是所说的叶子模块。这个设计中运用叶子模块(基本逻辑门)搭建成子模块(全加器),再用子模块搭建成所需要的电路(串行加法器

    2024年02月11日
    浏览(42)
  • 「Verilog学习笔记」根据RTL图编写Verilog程序

    专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点,刷题网站用的是牛客网 观察题目给出的RTL图,主要的器件是两个D触发器,一个与门。D触发器含有异步复位信号,且为低电平有效。data_in输入到D触发器,D触发器的输出Q是前一时刻的data_in,即data_in打一

    2024年03月24日
    浏览(39)
  • verilog入门学习笔记

    第一部分 (必填) module 模块名 ([端口列表]); [端口信号声明;] [参数声明;] 1、模块名是指电路的名字,由用户指定, 最好与文件名一致 2、端口列表是指电路的输入/输出信号名称列表,信号名由用户指定,各名称间用逗号隔开。 3、端口信号声明是要说明端口信号的

    2024年02月07日
    浏览(35)
  • Verilog 学习笔记

    模块 • Verilog HDL程序是由模块构成的。每个模块的内容都是嵌在module和endmodule两个语句之间。每个模块实现特定的功能。模块是可以进行层次嵌套的。正因为如此,才可以将大型的数字电路设计分割成不同的小模块来实现特定的功能,最后通过顶层模块调用子模块来实现整体功

    2024年02月09日
    浏览(42)
  • Verilog学习笔记(3):Verilog数字逻辑电路设计方法

    例:用Verilog设计模256(8bits)计数器 (a)可综合程序描述方式 (b)常见的错误描述方式 同时Verilog的电路描述方式具有多样性,这也决定了对于电路设计的多样性。 例:用Verilog设计数字多路选择器 (a)采用真值表形式的代码 (b)采用逻辑表达式形式的代码 (c)采用结

    2023年04月08日
    浏览(116)
  • redis学习笔记 - 进阶部分

    单线程:主要是指Redis的网络IO和键值对读写是由一个线程来完成的,Redis在处理客户端的请求时包括获取 (socket 读)、解析、执行、内容返回 (socket 写) 等都由一个顺序串行的主线程处理,这就是所谓的“单线程”。这也是Redis对外提供键值存储服务的主要流程。 多线程:主要

    2024年02月11日
    浏览(31)
  • 「Verilog学习笔记」时钟分频(偶数)

    专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点,刷题网站用的是牛客网

    2024年02月03日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包