这是设计代码:
module mul_clock (input clkA, clkB, in, output out);
bit temp;
reg x[2:0];
always @ (posedge clkA)
temp <= temp ^ in;
always @ (posedge clkB)
x <= {x[1:0], temp};
assign out = x[2] ^ x[1];
endmodule
如何为&#34; Out&#34;编写断言,因为它是一种多时钟设计。
我尝试了一个,但仍然遇到了一些错误。请帮我修改这个断言或写另一个断言:
property p1;
bit t;
bit x[2:0];
@(posedge clkA)
(1'b1, t ^= in) |=> @(posedge clkB) (1'b1, x[0] = t) |=> @(posedge clkB) (out == x[2] ^ x[1], x[1] = x[0]) |=> @(posedge clkB) (out == x[2] ^ x[1], x[2] = x[1]);
endproperty
注意:通过始终阻塞和单个时钟断言,我们可以验证输出端口。但是如果可能的话,我希望通过多锁断言来实现它,而不会总是阻塞。
答案 0 :(得分:3)
免责声明:我没有对此进行测试。
你试过了吗?
#1 @(posedge clkA) (1'b1, t ^= in) |->
#2 @(posedge clkB) (1'b1, x[0] = t) |=>
#3 @(posedge clkB) (out == x[2] ^ x[1], x[1] = x[0]) |=>
#4 @(posedge clkB) (out == x[2] ^ x[1], x[2] = x[1]);
也就是说,时钟切换中存在重叠的含义。根据我的经验,非重叠的含义将导致断言不在下一个clkB上进行采样,而是跳过一个clkB然后在clkB上进行采样。
此外,我不太明白为什么你在断言中一直使用含义。第2行不是第3行的先决条件,对于第3行和第4行也是如此。我读这个的方式断言应该从每个clkA开始,然后一个序列总是跟随。在这种情况下,以下更正确,尽管前面的代码可能仍然有用。
@(posedge clkA)
(1'b1, t ^= in) |->
@(posedge clkB)
(1'b1, x[0] = t) ##1
(1'b1, x[1] = x[0]) ##1
(out == x[2] ^ x[1], x[2] = x[1]);
最后,如果clkA比clkB快得多,会发生什么?几个断言将并行开始,并且不同意在clkB的第一个构造上t的实际值。我必须承认,我对一个属性的本地值的范围很朦胧,但检查这是否会导致你的麻烦。可能的修复方法可能是在属性范围外声明变量t。因此t将在clkA的每个posedge上更新为新值,并且您将有 n 断言检查相同的事情(这不是问题)。
编辑:
我从第3行删除了out == x[2] ^ x[1]
支票,因为x
是属性的本地。因此,您无法检查此断言的某个其他实例所做的值。
增加: 如果上述方法不起作用,或者如果开始并行断言检查相同的事情似乎是浪费,则以下代码可能会起作用。
Edit2:将x放在属性中并更改属性中的两个最后一行以更新x以更正值。
bit t;
always_ff@(posedge clkA)
t ^= in;
property p1;
bit[2:0] x;
@(posedge clkB)
(1'b1, x[0] = t) |=>
(1'b1, x[1] = x[0]) ##0 (1'b1, x[0] = t) ##1
(1'b1, x[2] = x[1]) ##0 (1'b1, x[1] = x[0]) ##0 out == x[2] ^ x[1];
endproperty
最后的奖金提示: 创建的触发器应该重置。也就是说,x和temp都应该在各自的时钟域本地重置。您可以选择同步或异步重置。这也必须添加到您的财产。另外:始终使用always_ff或always_comb,永远不要使用always。
异步重置:
always_ff @ (posedge clkA or posedge arstClkA)
if(arstClkA)
temp <= 0;
else
temp <= temp ^ in;