SV过程块

6.1 verilog通用目的always过程块

always begin
	wait(reset_n == 0) //电平敏感的延迟
	@(negedge clock) //边沿敏感的延迟
	#2 t <= d ;		  //基于时间的延迟
	@(posedge clock)
	#1.5 q <= t ;
end

在RTL级always过程块可对组合逻辑、锁存逻辑、时序逻辑进行建模,也可对算法逻辑进行更抽象的建模。但由于太过通用,大大增加了综合编译器和形式验证工具的负担。

下面例子编译仿真都可通过,但不可综合,工具无法识别这是什么类型逻辑

always @(posedge clock) begin
	wait(!reset_n)
 	if(mode) q1 = a + b ;
 	else	 q1 = a - b ;
    q2 <= q1 | (q2<<2)	;
    q2++	;
end

6.2 SV特有的过程块

组合逻辑过程块

always_comb
	if(!mode)
		y = a + b ;
	else
		y = a - b ;	

always_comb只能设计组合逻辑,无需指明敏感列表,由工具自动推断。

此外,always_comb在开始仿真的零时刻会自动触发,确保组合逻辑在零时刻产生与输入相对应的结果。

组合过程块如果代码量非常庞大,可以划分为多个过程块,但会涉及多个信号在过程块之间传递,从而使代码难以理解。另一种解决办法是使用函数将其分割为更小的子块,函数也会综合成组合逻辑,但涉及函数时@*推断的敏感列表可能不完整。

always@(*) begin   // @ * 只能推断出@(data)
	a1 = data << 1 ;
	b1 = decode()	;
end

always_comb begin // 可以推断出@(data,sel,c,d,e)
	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

锁存逻辑过程块

always_latch
	if(enable) q < d ;
module register_reader(input	clk,ready,reset_n,
	output	logic[4:0] read_pointer);
	logic enable	;
	logic overflow	;
	
	always_latch begin
		if(!reset_n)
			enable <= 0;
		else if(ready)
			enable <= 1;
		else if(overflow)
			enable <= 0;
	end
	
	always @(posdge clk or negedge reset_n) begin
		if(!reset_n)
			{overflow,read_pointer} <= 0;
		else if(enable)
			{overflow,read_pointer} <= read_pointer + 1 ;
	end
endmodule

时序逻辑过程块

always_ff @(posedge clk or negedge reset_n) begin
	if(!reset_n) q <= 0 ;
	else		 q <= d ;
end

6.3对任务和函数的改进

1、以function 和endfunction包含函数内容,不需要begin - end

2、返回值

verilog

function [31:0] add_and_inc (input [31:0] a,b)
	begin
		add_and_inc = a + b + 1;
	end
endfunction

SV

function int add_and_inc(input int a , b)
	return a+b+1 ;
endfunction

使用return可以在任何时刻退出任务或函数

function automatic int log2(input in n)
    if(n<=1) return 1;
	log2 = 0;
	while(n>1) begin
		n = n/2 ;
		log2++  ;
     end
endfunction  

加入void关键字,在综合模块中可以使用空函数代替任务

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

增加通过参数名传递变量值(verilog通过顺序传递参数值,以下例来看容易被除数和除数弄混)

always@(posedge clk)
	result <= divide(.denominator(b),.numerator(a));

function int divide(input int numerator, denominator);
	if(denominator == 0) begin
	   $display("Error,divide by zero");
	   return 0;
	end
	else 
		return numerator / denominator;
endfunction

SV中function参数若未声明方向,默认为input类型

verilog调用任务或函数时,系统将输入值复制到任务或函数中,执行完后将输出复制到任务或函数调用程序。

SV中增加ref关键字(只有自动任务和函数能用),实现引用的方法传递数值。我的理解是类比指针。

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
);
	for(int i=0;i<=7;i++) begin
		data_out.data[(8*i)+:8]=data_in[i];
		data_out.check[i]=^data_in[i];
	end
	data_out.valid = 1 ;
endfunction

若用const 修饰ref,则其对应信息只能读不能修改

ref传参函数或任务内部可以一直获取外部信号的更新,而采用复制方式传参,函数或任务内部使用的一直是调用时传进来的值。

同样,在task中,ref修饰的输出可以随时更新,而复制传参只有task调用结束后才会更新

posted @   骑猪上树的少年  阅读(216)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
回到顶部
点击右上角即可分享
微信分享提示

目录导航