如何覆盖请求和响应之间的延迟
How to cover latency between request and response
假设我们有一个协议,其中请求 req
使用 req_id
断言,相应的 rsp
将使用 rsp_id
断言。这些可能是乱序的。我想涵盖具有特定 req_id
的 req
和具有相同 ID 的 rsp
之间的时钟数或延迟。我试过这样的事情。这是正确的做法吗?还有其他有效的方法吗?
covergroup cg with function sample(int a);
coverpoint a {
a1: bins short_latency = {[0:10]};
a2: bins med_latency = {[11:100]};
a3: bins long_latency = {[101:1000]};
}
endgroup
// Somewhere in code
cg cg_inst = new();
sequence s;
int lat;
int id;
@(posedge clk) disable iff (~rst)
(req, id = req_id, lat = 0) |-> ##[1:$] ((1'b1, lat++) and (rsp && rsp_id == id, cg_inst.sample(lat)));
endsequence
基本上你的想法是正确的,但看起来当条件为真时序列的右侧将被评估一次,因此纬度只会增加一次。
您将需要一个循环机制来计算延迟。
下面是一个示例工作示例。您可以根据生成信号的接近程度更改 [1:$]、##1 等
property ps;
int lat;
int id;
@(posedge clk)
disable iff (~rst)
(req, id = req_id, lat = 0) |=> (1'b1, lat++)[*1:$] ##1 (rsp && rsp_id == id, cg_inst.sample(lat));
endproperty
assert property (ps);
您正在尝试在序列中使用 |->
运算符,这只允许在 属性 中使用。
如果rsp
只能在req
之后出现一个周期,那么这段代码应该可以工作:
property trans;
int lat, id;
(req, id = req_id, lat = 0) |=> (1, lat++) [*0:$] ##1 rsp && rsp_id == id
##0 (1, $display("lat = %0d", lat));
endproperty
##0
之后的元素用于调试。您可以在生产代码中省略它。
不过,我不会像这样混合断言和覆盖,因为我已经看到蕴涵运算符会导致变量流出现问题(即 lat
无法正确更新)。您应该有一个 属性,它仅涵盖您在请求后看到的匹配响应:
property cov_trans;
int lat, id;
(req, id = req_id, lat = 0) ##1 (1, lat++) [*0:$] ##1 rsp && rsp_id == id
##0 (1, $display("cov_lat = %0d", lat));
endproperty
cover property (cov_trans);
请注意,我使用 ##1
将请求与响应分开。
或者...
property/sequences 虽然它们看起来是小代码,但在这种情况下,对于每个请求(尚未收到响应),都会分叉一个具有自己计数器的单独进程。这导致许多计数器执行非常相似的工作。如果有很多请求在运行中(and/or 许多 属性 或序列的实例)它将开始添加到模拟 运行-time [即使这只是一小段代码]
所以另一种方法是使触发器更简单,我们尽量使处理保持线性。
int counter=0; // you can use a larger variablesize to avoid the roll-over issue
int arr1[int] ; // can use array[MAX_SIZE] if you know the max request id is small
always @( posedge clk ) counter <= counter + 1 ; // simple counter
function int latency (int type_set_get , int a ) ;
if ( type_set_get == 0 ) arr1[a] = counter; // set
//DEBUG $display(" req id %d latency %d",a,counter-arr1[a]);
// for roll-over - if ( arr1[a] > counter ) return ( MAX_VAL_SIZE - arr1[a] + counter ) ;
return (counter - arr1[a]); //return the difference between captured clock and current clock .
endfunction
property ps();
@(posedge clk)
disable iff (~rst)
##[0:$]( (req,latency(0,req_id) ) or (rsp,cg_inst.sample(latency(1,rsp_id))) );
endproperty
assert property (ps);
上面的属性只有在看到req/rsp并且只有1个线程在寻找它时才会触发。
如果需要,可以在函数中添加额外的检查,但对于延迟计数来说,这应该没问题。
轶事:
Mentor AE - Dan 发现了一个断言,它使我们的模拟速度减慢了 40%。写得不好的断言是我们块 tb 的一部分,它的影响在那里没有被注意到,因为我们的块级测试,运行 次是有限的。然后它潜入我们的顶级 tb,造成无法估量的 运行 时间损失,直到一年后才被发现 :) 。 [猜想我们应该在 运行s 之前分析我们的模拟 ]
例如,如果上述协议在稍后实现了中止,则 req-rsp 线程将继续处理并等待(直到模拟结束)中止的事务,尽管这不会影响功能,它将偷偷摸摸地继续占用处理器资源,在 return 中无所作为。直到最后一个供应商 AE 介入来挽救这一天:)
假设我们有一个协议,其中请求 req
使用 req_id
断言,相应的 rsp
将使用 rsp_id
断言。这些可能是乱序的。我想涵盖具有特定 req_id
的 req
和具有相同 ID 的 rsp
之间的时钟数或延迟。我试过这样的事情。这是正确的做法吗?还有其他有效的方法吗?
covergroup cg with function sample(int a);
coverpoint a {
a1: bins short_latency = {[0:10]};
a2: bins med_latency = {[11:100]};
a3: bins long_latency = {[101:1000]};
}
endgroup
// Somewhere in code
cg cg_inst = new();
sequence s;
int lat;
int id;
@(posedge clk) disable iff (~rst)
(req, id = req_id, lat = 0) |-> ##[1:$] ((1'b1, lat++) and (rsp && rsp_id == id, cg_inst.sample(lat)));
endsequence
基本上你的想法是正确的,但看起来当条件为真时序列的右侧将被评估一次,因此纬度只会增加一次。
您将需要一个循环机制来计算延迟。
下面是一个示例工作示例。您可以根据生成信号的接近程度更改 [1:$]、##1 等
property ps;
int lat;
int id;
@(posedge clk)
disable iff (~rst)
(req, id = req_id, lat = 0) |=> (1'b1, lat++)[*1:$] ##1 (rsp && rsp_id == id, cg_inst.sample(lat));
endproperty
assert property (ps);
您正在尝试在序列中使用 |->
运算符,这只允许在 属性 中使用。
如果rsp
只能在req
之后出现一个周期,那么这段代码应该可以工作:
property trans;
int lat, id;
(req, id = req_id, lat = 0) |=> (1, lat++) [*0:$] ##1 rsp && rsp_id == id
##0 (1, $display("lat = %0d", lat));
endproperty
##0
之后的元素用于调试。您可以在生产代码中省略它。
不过,我不会像这样混合断言和覆盖,因为我已经看到蕴涵运算符会导致变量流出现问题(即 lat
无法正确更新)。您应该有一个 属性,它仅涵盖您在请求后看到的匹配响应:
property cov_trans;
int lat, id;
(req, id = req_id, lat = 0) ##1 (1, lat++) [*0:$] ##1 rsp && rsp_id == id
##0 (1, $display("cov_lat = %0d", lat));
endproperty
cover property (cov_trans);
请注意,我使用 ##1
将请求与响应分开。
或者...
property/sequences 虽然它们看起来是小代码,但在这种情况下,对于每个请求(尚未收到响应),都会分叉一个具有自己计数器的单独进程。这导致许多计数器执行非常相似的工作。如果有很多请求在运行中(and/or 许多 属性 或序列的实例)它将开始添加到模拟 运行-time [即使这只是一小段代码]
所以另一种方法是使触发器更简单,我们尽量使处理保持线性。
int counter=0; // you can use a larger variablesize to avoid the roll-over issue
int arr1[int] ; // can use array[MAX_SIZE] if you know the max request id is small
always @( posedge clk ) counter <= counter + 1 ; // simple counter
function int latency (int type_set_get , int a ) ;
if ( type_set_get == 0 ) arr1[a] = counter; // set
//DEBUG $display(" req id %d latency %d",a,counter-arr1[a]);
// for roll-over - if ( arr1[a] > counter ) return ( MAX_VAL_SIZE - arr1[a] + counter ) ;
return (counter - arr1[a]); //return the difference between captured clock and current clock .
endfunction
property ps();
@(posedge clk)
disable iff (~rst)
##[0:$]( (req,latency(0,req_id) ) or (rsp,cg_inst.sample(latency(1,rsp_id))) );
endproperty
assert property (ps);
上面的属性只有在看到req/rsp并且只有1个线程在寻找它时才会触发。 如果需要,可以在函数中添加额外的检查,但对于延迟计数来说,这应该没问题。
轶事:
Mentor AE - Dan 发现了一个断言,它使我们的模拟速度减慢了 40%。写得不好的断言是我们块 tb 的一部分,它的影响在那里没有被注意到,因为我们的块级测试,运行 次是有限的。然后它潜入我们的顶级 tb,造成无法估量的 运行 时间损失,直到一年后才被发现 :) 。 [猜想我们应该在 运行s 之前分析我们的模拟 ]
例如,如果上述协议在稍后实现了中止,则 req-rsp 线程将继续处理并等待(直到模拟结束)中止的事务,尽管这不会影响功能,它将偷偷摸摸地继续占用处理器资源,在 return 中无所作为。直到最后一个供应商 AE 介入来挽救这一天:)