图9.1 局部变量-基础
局部变量是您可能经常使用的功能。它们可以用于序列和属性中。它们被称为本地的,因为它们确实是一个序列本地的,并且不可见或不可用于其他序列或属性。当然,这个限制是有解决办法的,我们将在这一章节进一步研究。图9.1指出了局部变种的关键元素。局部变量的最重要和有用的方面是它允许多线程应用程序,并为每个使用它的序列实例创建局部变量的新副本。用户无需担心在每次调用序列时都要创建局部变量的副本。上面的应用程序说,只要RdWr在posedge clk处采样为高,那么rData将在5个时钟后与wData进行比较。这个例子展示了如何实现这个规范。在clk posedge局部变量int local_data将存储rData,然后在5个时钟后将其与wData进行比较。请注意,RdWr可以在每个posedge时钟采样为真。序列data_check将进入每个时钟;创建一个local_data的新副本,并创建一个新的线程,它将在5个钟后用wData检查local_data+'hff。图9.2显示了局部变量的其他语义。密切关注局部变量必须附加到表达式,而比较不能附加到表达式的规则!
图9.2局部变量-Dos和Donts
如图9.2所示,当存储一个值时,必须将一个局部变量附加到表达式上。但是,当比较存储在局部变量中的值时,它不能附加到表达式上。
在最上面的示例中,local_data = rData附加到序列rdC。换句话说,赋值“local_data = rData”将仅在序列rdC完成时发生。继续将这个值存储到局部变量的故事中,如果在存储值时没有任何附加到局部变量的内容,该怎么办?使用1'b1(始终为true)作为表达式。这意味着无论何时输入一个序列,表达式总是为真,并且应该将该值存储在局部变量中。简单!
同样,如果您比较表达式上的值为真,该怎么办?如图9.2所示,您可以通过如图所示分离表达式来实现此目的。结果序列(图9.2中的最后一个序列)将被理解为“进入dataCheck,将rData存储到local_data中,等待5个时钟,然后如果b(序列rdC)在5个时钟内为真,则将wData与存储的 local_data+ h'ff 比较”。
图9.3局部变量 - 和形式参数
图9.3指出了其他一些重要特性。首先,在序列或属性中使用局部变量没有限制。此外,您不能将局部变量声明为形式参数,并将其作为另一个序列/属性中的实际值传递。这是有道理的,否则为什么它会被称为本地?
图9.4局部变量 - 可见性
在图9.4中,我们看到序列中的局部变量对实例化它的序列是不可见的。解决方案非常简单。不要直接使用局部变量,只需将参数传递给包含局部变量的序列即可。当序列L_seq在本地更新参数时,它将对调用序列(H_seq)可见。请注意,Ldata未在序列L_seq声明为局部变量(否则这将是我们讨论的错误)。 L_seq只是更新一个形式参数并将其传递给调用序列,其中实际被声明为局部变量。这显示在图9.4, 9.5的底部。
图9.6,9.7,9.8,9.9,9.10,9.11,9.12显示了更精细的规则。当你着手写复杂的断言时,将它们作为参考。图中的注释解释了情况。
图9.5带有‘OR’的局部变量复合序列
图9.6局部变量 - 用于OR分配本地数据 - 在复合序列之前
图9.6描述了在两个序列的‘OR’中使用时控制局部变量的语义。局部变量必须在OR的序列中分配。但是,如果你不能这样做呢?图9.7中提供了几个解决方案。
图9.7局部变量 - 在OR的两个序列中分配局部数据
图9.9描述了管理一个‘and’两个序列的语义。与一个或两个序列相比,一个局部变量不能附加到‘and’中所涉及的两个序列上。第一种解决方案与‘or’相同。如图所示,在两个序列之外分配局部变量。或者,只需在两个序列中的一个中分配局部变量,这是一个明显的解决方案。除了图9.8中的解决方案#1之外,图9.9还显示了解决方案#2。
图9.8局部变量 - ‘and’复合序列
图9.9局部变量 - 更细微的细微差别III
图9.10局部变量 - 进一步的细微差别IV
图9.10描述了控制局部变量的更多规则。首先,您可以将多个局部变量分配给一个表达式。其次,您也可以按照相同的顺序操作指定的本地数据(与ldata2的情况相同)。但是和以前一样,在分配(存储)局部变量和比较它们的存储值方面存在差异。您不能在序列中的单个表达式中比较多个局部变量值,就像行(// wData == ldata1,wretryData ==!ldata2)中的情况一样。这是非法的。当然,总是有一个解决方案,如图所示。简单地将两个子序列中的多个值进行比较,两者之间没有延迟。图中的‘Solution’注释说明了这一点。
图9.11局部变量不能在延迟范围内使用
图9.11显示了你不能在范围运算符中使用局部变量。 但是,这不是局部变量错。 这是事实,我们不能在#m或#[m:n]延迟运算符中有可变延迟。 从软件角度来看,延迟范围操作符需要在elaboration时已知。 因此他们不能是变数? 从硬件角度来看,这是一个无赖!
图9.12显示你不能用正式的形参来定义局部变量。 同样,矢量(总线)声明的大小只能是一个常量。 再次,这有一个软件原因和硬件原因。 我会留给读者猜测我的想法!
图9.12局部变量 - 不能使用形参来确定定义变量的大小
9.1 应用程序:局部变量
图9.13局部变量 - 应用程序
图9.13中的应用程序分解如下。
($rose(read),localID=readID
在$ rose(read)上,readID存储在localID中。
not (($rose(read) && readID==localID) [*1:$])
然后,我们检查是否发生了另一个读取($ rose(read)),它的readID与我们为先前存储在localID的readID相同。我们继续连续检查,直到出现## 0($ rose(readAck)&& readAckID == localID)。
如果连续检查确实导致了匹配,那么这意味着我们获得了另一个$ rose(read),其中具有与前一次读取相同的readID。这违反了规范。这就是为什么我们采用这种表达式取反(not)来看到它在匹配中检查到错误并且属性会结束。
如果在## 0($ rose(readAck)&& readAckID == localID)到达之前连续检查没有导致匹配,那么我们确实得到了一个readAck,它具有与发出原始读取相同的readAckID。该属性将通过。
简而言之,我们已经证明,一旦发起一次'读',在相同ID的'readAck'返回之前,来自同一个readID的另一次'读'不能被重发。