SystemVerilog过程块,任务和函数(六)

  1. always_comp过程块表示建立组合逻辑模型。always_comp后面不需要指定敏感表,会被自动推断出来。推断的敏感表包含了所有被过程块读取并在块外赋值的信号。SystemVerilog的敏感表还包括了过程块中调用函数的所有信号。always_comp和always的另一个不同之处在于,在所有initial和always过程块启动后,always_comp块会在仿真的零时刻自动触发,不管推断出的敏感表中的信号是否发生了变化,这样的自动求值都会发生。相对于always,必须等到敏感表中至少有一个信号有变化才会触发语句块。

  2. 在大型设计中常遇到的一个问题是,组合过程块的代码量非常庞大,可能会显得笨重或者引起时序难以收敛。解决办法是把大的过程块拆分成若干个小的过程块。但是这种划分会导致许多信号要在几个过程块中来回传送,从而使代码难以理解。另一种办法是把所有组合逻辑都写在一个过程块中,但使用函数把它分割为更小的子块。由于函数也会综合成组合逻辑,因此这是描述大型组合逻辑的一种有效的代码结构。

  3. 使用函数构造大块组合逻辑时,Verilog的@*可能不会推断出完整的敏感表。always@*推断出的敏感列表只针对于那些被always块直接读取的信号,而对过程块调用的函数中读取的信号不能推断为敏感表。因此每个函数调用必须列出所有作为函数输入的信号,并且每个函数的定义也都必须列出这些信号作为形式输入变量。SystemVerilog中的always_comp过程块则消除了Verilog中always@*的这些缺陷。always_comp过程块对块内读取的信号和块内调用的函数读取的信号都敏感。这样编写函数时就不需要形式参数了。

// 敏感表中只包含data
always @ * begin
	a1 = data << 1;
	b1 = decode();
end
// 敏感表中包含data,sel,d,e,c
always_comp begin
	a2 = data << 1;
	b2 = decode();
end
// 不带输入的函数
function decode;
begin
	case(sel)
		2'b01: decode = d | e;
		2'b10: decode = d & e;
		default: decode = c;
	endcase
end
endfunction
  1. 锁存逻辑always_latch和组合逻辑差不多。唯一区别是:在锁存逻辑中,过程块的输出变量不需要对所有可能的输入条件响应。条件中缺失的部分会产生latch。always_ff表示时序逻辑。

  2. Verilog中函数名本身就是一个与该函数类型相同的变量,函数的返回值通过对函数名的赋值产生。当执行到函数的末尾就会退出,最后赋给函数名的值就是整个函数的返回值。函数必须有返回值,当函数被调用时,调用代码必须获得返回值。

// Verilog的函数,函数的类型默认是线网类型
// 因为函数本身就是为了实现一段组合逻辑
function [31:0] add_and_inc(input [31:0] a,b);
	begin
		add_and_inc = a + b + 1;
	end
endfunction
  1. SystemVerilog为了保持与Verilog的兼容,既可以使用return,也可以通过对函数名赋值来指定函数返回值。如果执行到return语句就会返回一个值,并且函数终止执行。如果函数执行到最后也没遇到return语句,那么赋给函数名变量的值就是函数的返回值。即使使用了return语句,函数名仍然是一个变量,可以在执行return语句之前当做临时内存使用。函数可以显示地声明为void数据类型,表示函数没有返回值。函数可以有output和inout,这样一来,虽然空函数没有返回值,仍然可以传递数据。
function int add_and_inc(input int a,b);
	add_and_inc = a + b;
	return ++add_and_inc;
endfunction
// 空函数
typedef struct {
	logic			valid;
	logic	[7:0] 	check;
	logic	[63:0]	data;
} packet_t;
function void fill_packet(
	input	logic	[63:0] 	data_in;
	output	packet_t		data_out
);
	data_out.data = data_in;
	for(int i = 0; i <= 7; i++)
		data_out.check[i] = ~data_in[(8*i)+:8];
	data_out.valid = 1;
endfunction
  1. Verilog和SystemVerilog的函数都有一些限制:函数不能包含任何类型的延迟或事件控制,也不能使用非阻塞语句,只能使用组合逻辑。Verilog传递数值只能使用顺序传递的方式,而SystemVerilog传递数值可以既可以使用顺序传递也可以使用形式参数来传递。SystemVerilog允许任务和函数为每个形式参数定义一个可选的缺省值。
// 定义函数
function int divide(input int numerator,denominator);
	// 
endfunction
// Verilog使用顺序参数传递数值
always@(posedge clk) begin
	result <= divide(b,a);
end
// SystemVerilog使用形式参数传递数值
always@(posedge clk) begin
	result <= divide(.numerator(b),.denominator(a));
end
// 形式参数可以指定缺省值
function int incrementer (int count = 0, step = 1);
	// 
endfunction
  1. Verilog当调用任务和函数时,系统会将输入值复制到任务和函数中,于是这些值变成任务或函数的局部数值。当任务或函数执行到末尾返回时,系统再将所有的输出复制到任务或函数调用程序。SystemVerilog对自动任务和函数进行了扩展,可以使用引用而不是复制的方法传递数值。为了通过引用传递数值,形式参数用关键字ref取代方向关键字input,output或inout进行声明。
module chip(...);
	typedef struct {
		logic			valid;
		logic	[7:0]	check;
		logic	[63:0] 	data;
	} packet_t;
	
	packet_t data_packet;
	
	bit [7:0] raw_data [0:7];
	
	always@(posedge clk)
		if(data_ready)
			fill_packet(.data_in(raw_data),.data_out(data_packet));
	
	function automatic void fill_packet(
		ref 	logic 	[7:0]	data_in	[0:7];
		ref	packet_t			data_out
	);
		// ...
	endfunction
endmodule
  1. 为了增强可读性,可以在任务和函数的末尾指定名称。
endtask: <task_name>
endfunction: <function_name>
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值