System verilog 避免支持SVA序列处理流水线事务的代码
假设我们有一个协议,上面写着。一旦主设备将System verilog 避免支持SVA序列处理流水线事务的代码,system-verilog,system-verilog-assertions,System Verilog,System Verilog Assertions,假设我们有一个协议,上面写着。一旦主设备将req设置为fill,从设备将通过rsp发出4次传输信号: 整个事务的SVA序列是(假设从机可以在trans周期之间插入idle周期): 现在,假设允许主机对请求进行管道处理。这意味着在完成4个trans循环之前,允许开始下一个fill: 上面的SVA序列没有帮助,因为对于第二个fill循环,它将错误地匹配4个trans循环,使最后一个trans循环处于“浮动”状态。只有在先前的填充循环匹配后,才需要开始匹配trans循环 序列需要在单个评估中不可用
req
设置为fill
,从设备将通过rsp
发出4次传输信号:
整个事务的SVA序列是(假设从机可以在trans
周期之间插入idle
周期):
现在,假设允许主机对请求进行管道处理。这意味着在完成4个trans
循环之前,允许开始下一个fill
:
上面的SVA序列没有帮助,因为对于第二个fill
循环,它将错误地匹配4个trans
循环,使最后一个trans
循环处于“浮动”状态。只有在先前的填充
循环匹配后,才需要开始匹配trans
循环
序列需要在单个评估中不可用的全局信息。基本上,它需要知道它的另一个实例正在运行。我能想到的实现这一点的唯一方法是使用一些RTL支持代码:
int num_trans_seen;
bit trans_ongoing;
bit trans_done;
bit trans_queued;
always @(posedge clk or negedge rst_n)
if (!rst_n) begin
num_trans_seen;
trans_ongoing <= 0;
trans_done <= 0;
trans_queued <= 0;
end
else begin
if (trans_ongoing)
if (num_trans_seen == 3 && req == trans) begin
trans_done <= 1;
if (req == fill || trans_queued)
trans_queued <= 0;
else
trans_ongoing <= 0;
num_trans_seen == 0;
end
else
if (trans_queued) begin
trans_queued <= 0;
trans_ongoing <= 1;
end
if (trans_done)
trans_done <= 0;
end
这应该行得通,但我并不特别高兴我需要支持代码。这里面有很多冗余,因为我基本上重新描述了事务是什么以及流水线是如何工作的。它也不容易重复使用。序列
可以放在一个包中,然后导入到其他地方。支持代码只能放在某个模块中并重用,但它是与存储序列的包不同的逻辑实体
这里的问题是:在不需要支持代码的情况下,是否有任何方法可以编写序列的流水线版本?一种可能的解决方案可以通过以下两种断言实现 对于第一个图像-
(req == fill) && (rsp == idle) |=> ((rsp == trans)[->1])[*4]
第二张图片-
(req == fill) && (rsp == trans) |=> ((rsp == trans)[->1])[*0:4] ##1 (rsp == idle) ##1 ((rsp == trans)[->1])[*4]
一个问题是,如果每个周期上都有连续的“填充”请求(连续的4个“填充”请求,没有任何中间的“空闲”),那么第二个断言将不会为每个“填充”请求计算“转换”周期(相反,它将只在第二组“转换”周期本身上完成)
到目前为止,我无法修改给定错误的断言。在传输开始之前,rsp似乎总是处于空闲状态。如果rsp的
idle
是一个常量值,并且它是trans
永远不会出现的值,那么您可以使用:
req == fill ##0 (rsp==idle)[->1] ##1 trans[*4];
当管道支持1至3级时,上述操作应有效
对于4+深度的管道,我认为您需要一些辅助代码。断言的成功/失败块可用于取消已完成的trans
计数;这样可以避免编写额外的RTL。属性中的局部变量可用于采样填充的计数值。采样值将用作标准,以开始对预期的传输模式进行采样
int fill_req;
int trans_rsp;
always @(posedge clk, negedge rst_n) begin
if(!rst_n) begin
fill_req <= '0;
trans_rsp <= '0;
end
else begin
if(req == fill) begin
fill_req <= fill_req + 1; // Non-blocking to prevent risk of race condition
end
end
end
property fill_trans();
int id;
@(posedge clk) disable iff(!rst_n)
(req == fill, id = fill_req) |-> (rsp==idle && id==trans_rsp)[->1] ##1 trans[*4];
endproperty
assert property (fill_trans()) begin
// SUCCESS
trans_rsp <= trans_rsp + 1; // Non-blocking to prevent risk of race condition
end
else begin
// FAIL
// trans_rsp <= trans_rsp + 1; // Optional for supporting pass after fail
$error("...");
end
除非没有提到另一个限定符,否则不能使用典型的assert属性()因为每次有填充请求时,它都会启动新的断言线程。改为使用expect
语句,该语句允许等待属性评估(§16.17 expect语句)
我尝试重新创建您的描述行为以进行测试解码rsp的空闲和传输之间的差异是否容易?我的理解是,如果另一个“填充”在“填充”和4个“传输”周期之间到达,则后面的“填充”将被简单忽略(在图中,第二个“填充”将被忽略)。请评论我的理解。@KaranShah不,第二次填充是在正在进行的循环完成后开始的4个“trans”循环。@Greg是的,它们是枚举类型。在一个时钟中你看到空闲,在另一个时钟中你看到传输。要弄清楚发生了什么事是很容易的。将TRANS循环映射到适当的填充是困难的部分。不正确。注意,我说过从机可以在trans
之间插入idle
周期。如果trans
周期以连续数据包的形式出现,If将很容易。@Tudor修改了断言,实际上还有另一种情况,第二个断言将无法正常运行。当req
出现时,在trans
周期中,很明显它是管道化的。即便如此,你也不知道它是在4个周期中的哪一个周期出现的。当req
在idle
周期中出现时,您无法知道它是idle
,因为管道是空的,或者它是两个trans
周期之间的idle
。是的,但是当req在idle周期中出现时,我们当然可以知道空闲后必须有4个trans周期。所以第一个断言应该是真的,不管它是流水线请求还是普通请求。@KaramShah不,这是假的。你不能仅仅通过查看rsp
来判断请求的位置(完成请求后或请求期间)。这假设在事务的4个trans
周期之间不存在idle
周期,但事实并非如此。我想我没有真正理解你之前的问题,你问idle
是否容易与trans
区分。你的波形在4个trans之间没有idle,所以我简化了(trans[->1])[*4]
。如果同一个填充请求的两个trans之间允许空闲,那么第二个示例在修改后仍然有效。id==trans\u rsp
将阻止一组trans的采样,直到前一个完成。我问过如何区分trans
和idle
,因为我需要了解您的协议。我见过一些协议,其中rsp的含义基于位限定符,而其他协议只能从其相对位置确定。不过,第二个示例使用支持代码,这正是我想要避免的。纯SVA可能不可能。你能添加另一个显示更坏情况的波形吗?是否有任何附加的限定符
req == fill ##0 (rsp==idle)[->1] ##1 trans[*4];
int fill_req;
int trans_rsp;
always @(posedge clk, negedge rst_n) begin
if(!rst_n) begin
fill_req <= '0;
trans_rsp <= '0;
end
else begin
if(req == fill) begin
fill_req <= fill_req + 1; // Non-blocking to prevent risk of race condition
end
end
end
property fill_trans();
int id;
@(posedge clk) disable iff(!rst_n)
(req == fill, id = fill_req) |-> (rsp==idle && id==trans_rsp)[->1] ##1 trans[*4];
endproperty
assert property (fill_trans()) begin
// SUCCESS
trans_rsp <= trans_rsp + 1; // Non-blocking to prevent risk of race condition
end
else begin
// FAIL
// trans_rsp <= trans_rsp + 1; // Optional for supporting pass after fail
$error("...");
end
sequence fill_trans;
int cnt; // local variable
@(posedge clk)
(req==FILL,cnt=4) ##1 ( // initial request set to 4
(rsp!=TRANS,cnt+=4*(req==FILL))[*] // add 4 if new request
##1 (rsp==TRANS,cnt+=4*(req==FILL)-1) // add 4 if new request, always minus 1
)[*] ##1 (cnt==0); // sequence ends when cnt is zero
endsequence
always @(posedge clk) begin
if(req==FILL) begin
expect(fill_trans);
end
end