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调用结束后才会更新
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律