【问题标题】:Avoiding support code for SVA sequence to handle pipelined transaction避免 SVA 序列的支持代码来处理流水线事务
【发布时间】:2016-08-29 16:18:37
【问题描述】:

假设我们有一个协议,其中包含以下内容。一旦主机将req 设置为fill,从机将通过rsp 发出4 次传输信号:

整个事务的 SVA 序列将是(假设从站可以在 trans 周期之间插入 idle 周期):

req == fill ##1 (trans [->1]) [*4];

现在,假设允许主节点处理请求。这意味着下一个fill 可以在 4 个trans 循环完成之前开始:

上面的 SVA 序列无济于事,因为第二个 fill 将错误地匹配 4 个 trans 循环,而最后一个 trans “浮动”。只有在前一个 fill 的循环匹配后,它才需要开始匹配 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

上面的代码应该在事务正在进行时提高trans_ongoing 位,并在发送fill 的最后一个trans 时在时钟周期内脉冲trans_done。 (我说应该是因为我没有测试它,但这不是重点。让我们假设它有效。)

有了这样的东西,可以将序列重写为:

req == fill ##0 (trans_ongoing ##0 trans_done [->1]) [*0:1]
  ##1 (trans [->1]) [*4];

这应该可行,但我对我需要支持代码这一事实并不感到特别兴奋。其中有很多冗余,因为我基本上重新描述了事务是什么以及流水线如何工作的大部分内容。它也不那么容易重复使用。 sequence 可以放在一个包中并导入到其他地方。支持代码只能放在某个模块中并重复使用,但它是一个不同于存储序列的包的逻辑实体。

这里的问题是:有什么方法可以编写序列的流水线版本,同时避免需要支持代码?

【问题讨论】:

  • rsp的idle和trans的区别容易解码吗?
  • 我的理解是,如果另一个“填充”在“填充”和 4 个“反式”周期之间到达,那么后面的“填充”将被简单地忽略(在图像中,第二个“填充”将被忽略)。请评论我的理解。
  • @KaranShah 不,第二次填充有 4 个“反式”循环在正在进行的循环完成后开始。
  • @Greg 是的,它们是枚举类型。在一个时钟中您看到 IDLE,在另一个时钟中您看到 TRANS。弄清楚发生了 TRANS 是很容易的部分。将 TRANS 循环映射到适当的 FILL 是困难的部分。

标签: system-verilog system-verilog-assertions


【解决方案1】:

看起来 rsp 在 trans 开始之前总是空闲的。如果 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

仅供参考:我还没有时间对此进行全面测试。它至少应该让你朝着正确的方向前进。



我进行了更多实验,找到了一个可能更符合您喜好的解决方案;没有支持代码。

根据IEEE Std 1800-2012 § 16.9.2 序列中的重复trans[-&gt;4] 的等价物是 (!trans[*] ##1 trans)[*4]。因此,我们可以使用局部变量来检测带有扩展表单的新填充请求。比如下面的序列

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

除非有另一个未提及的限定符,否则您不能使用典型的assert property();,因为每次有填充请求时它都会启动新的断言线程。而是使用expect 语句,它允许等待属性评估(IEEE Std 1800-2012 § 16.17 Expect 语句)。

always @(posedge clk) begin
  if(req==FILL) begin
    expect(fill_trans);
  end
end

我尝试重新创建您的描述行为以测试 https://www.edaplayground.com/x/5QLs

【讨论】:

  • 这假定事务的 4 个 trans 周期之间不能有 idle 周期,但事实并非如此。我想我不太明白你之前的问题,即idle 是否易于与trans 区分开来。
  • 您的波形在 4 个反式之间没有空闲,所以我简化了 (trans [-&gt;1]) [*4]。如果在同一个填充请求的trans之间允许空闲,那么第二个示例在修改后仍然可以工作。 id==trans_rsp 将阻止对一组 trans 的采样,直到之前完成。我询问了区分 transidle 的问题,因为我需要了解您的协议。我见过一些协议,其中 rsp 的含义基于位限定符,而其他协议只能从其相对位置确定。
  • 不过,第二个示例使用了支持代码,这正是我想要避免的。
  • 纯 SVA 可能是不可能的。您可以添加另一个显示更糟情况的波形吗?是否有任何额外的限定符来阻止、门控或排队集合?
  • 我知道你在expect 那里所做的事情,尽管它需要一些时间才能完全理解。最重要的是,使用简单序列的组合实际上不可能做到这一点。 (我所说的简单序列是指对单个事务建模的序列,例如 trans[-&gt;4],而不必将流水线硬编码到其中。)
【解决方案2】:

一种可能的解决方案可以通过以下 2 个断言来实现。

对于第一张图片 -

(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 个“填充”请求,没有任何中间“空闲”),那么第二个断言将不会为每个“填充”计算“反”周期请求(相反,它只会在第二组“trans”周期本身完成)。

到目前为止,我无法修改给定错误的断言。

【讨论】:

  • 不正确。请注意,我说过从站可以在trans 之间插入idle 循环。如果trans 周期以连续数据包的形式出现,这将很容易。
  • @Tudor 修改了断言,实际上还有另一种情况,第二个断言将无法正常运行。
  • req 出现时,在trans 循环中很明显它是流水线的。但是,即使那样,您也不知道它是在 4 个周期中的哪个周期出现的。当reqidle 循环中出现时,您无法知道它是idle,因为管道是空的,还是两个trans 循环之间的idle
  • 是的,但是当req在空闲周期到来时,我们当然可以知道空闲之后必须有4个trans周期。所以第一个断言应该是真的,不管它是流水线req还是普通req。
  • @KaramShah 不,这是错误的。您无法仅通过查看rsp 来说出您的请求(完成后或期间)。
猜你喜欢
  • 1970-01-01
  • 2011-09-25
  • 1970-01-01
  • 1970-01-01
  • 2023-03-04
  • 2011-06-12
  • 2018-11-25
  • 1970-01-01
  • 2011-12-06
相关资源
最近更新 更多