以APB总线协议的断言检查和断言覆盖率为例,学习断言的基本使用和断言覆盖率覆盖。需要用到关键词cover来收集断言覆盖。
APB总线协议时序图如下:(首图为读操作时序和后图为写操作时序)
PS:关于APB协议的详细的描述可见AMBA总线协议中APB协议总结一文:APB总线协议
断言检查一
- 在PSEL为高时,PADDR总线不可以为x值;
property p_addr_no_x;
@(posedge clk) psel |-> !$isunknow(paddr);
endproperty
assert property(p_addr_no_x) else `uvm_error("ASSERT","PADDR is unknow when PSEL is high");
断言检查二
- 在PSEL拉高的下一个周期,PENABLE也应该为高。
property penable_rise;
@(posedge clk) $rose(psel) |=> $rose(penable);
endproperty
assert property(penable_rise) else `uvm_error("ASSERT","PENABLE not rose after 1 cylce PSEL rose");
断言检查三
- 在PENABLE拉高的下一个周期,PENABLE应该拉低;
property penable_fell;
@(posedge clk) $rose(penable) |=> $fell(penable);
endproperty
assert property(penable_fell) else `uvm_error("ASSERT","PENABLE not fell after 1 cylce PENABLE rose");
断言检查四
- 在PSEL和PWRITE同时保持为高的阶段,PWDATA需要保持。
property pwdata_stable;
@(posedge clk) (psel && !penable) ##1 (psel && penable) |-> $stable(pwdata);
endproperty
assert property(pwadata_stable) else `uvm_error("ASSERT","PENABLE not stable after 1 cylce PENABLE ....");
断言检查五
- 在下一次传输开始前,上一次的PADDR和PWRITE信号应该保持不变。
property paddr_stable_next_trans;
logic [31:0] addr1,addr2;//记录前后两次传输的地址
@(posedge clk)
$first_match( ($rose(penable),addr1==paddr) ##1 ((psel && !penable)[=1],addr2 = $past(paddr)) )
|-> addr1 == addr2;
// ($rose(penable),addr1==paddr)是上一次事务传输正在传输的阶段,将这个地址记录
//((psel && !penable)[=1],addr2 = $past(paddr))是下一次传输准备阶段,
//注意:此时记录的是上一拍传输的地址,这就使得paddr信号保持不变
endproperty
assert property(paddr_stable_next_trans) else `uvm_error("ASSERT","....");
property pwrite_stable_next_trans;
logic pwrite1,pwrite2;
@(posedge clk)
$first_match( ($rose(penable),pwrite1==pwrite) ##1 ((psel && !penable)[=1],pwrite2 = $past(pwrite)) )
|-> pwrite1 == pwrite2;
endproperty
assert property(pwrite_stable_next_trans) else `uvm_error("ASSERT","....");
断言检查六
- 读操作时,在PENABLE拉高的同一周期,PRADTA也应该发生变化。
property prdata_change;
@(posedge clk) $rose(penable) && !pwrite |-> !$stable(prdata);
endproperty
assert property(prdata_change) else `uvm_error("ASSERT","....");
断言覆盖率一
- 在写操时,分别发生连续写和非连续写;
property cov_nonburst;//非连续写
@(posedge clk) $rose(penable) |-> pwrite throughout
( ##1 (!penable)[*2] ##1 penable[=1]);
endproperty
cover property(cov_nonburst) else `uvm_error("ASSERT","....");
property cov_burst;//连续写
@(posedge clk) $rose(penable) |-> pwrite throughout ( ##2 penable);
endproperty
cover property(cov_burst) else `uvm_error("ASSERT","....");
断言覆盖率二
- 对同一个地址先做写做操作,再不间隔做读操作;
property cov_write_read;
logic [31:0] addr;//因为是对同一个地址,要记录地址
@(posedge clk) ($rose(penable) && pwrite,addr= paddr) |->
(##2 ($rose(penable)&& !pwrite && addr==paddr));
endproperty
cover property(cov_write_read) else `uvm_error("ASSERT","....");
断言覆盖率三
- 对同一个地址做连续两次写操作再从中读数据。
property cov_twice_write;
logic [31:0] addr;//因为是对同一个地址,要记录地址
@(posedge clk)
($rose(penable) && pwrite, addr=paddr) |-> (
##2 ($rose(penable) && pwrite && addr==paddr)
##2 ($rose(penable) && !pwrite && addr==paddr)
);
endproperty
cover property(cov_twice_write) else `uvm_error("ASSERT","....");
断言覆盖率四
- 在读操作时,分别发生连续读和非连续读;
property cov_read_nonburst;//非连续读
@(posedge clk)
$rose(penable) |-> !pwrite throughout (##1 (!penable)[*2] ##1 penable [=1]);
endproperty
cover property(cov_read_nonburst) else `uvm_error("ASSERT","....");
property cov_read_burst;//连续读
@(posedge clk)
$rose(penable) |-> !pwrite throughout (##2 penable);
endproperty
cover property(cov_read_burst) else `uvm_error("ASSERT","....");
断言覆盖率五
- 发生对同一个地址的读操作,再不间隔做写操作,再不间隔做读操作。
property cov_twice_write_read;
logic [31:0] addr;//因为是对同一个地址,要记录地址
@(posedge clk)
($rose(penable) && !pwrite, addr=paddr) |-> (
##2 ($rose(penable) && pwrite && addr==paddr)
##2 ($rose(penable) && !pwrite && addr==paddr)
);
endproperty
cover property(cov_twice_write_read) else `uvm_error("ASSERT","....");
注意:
- 在仿真时,要添加额外的仿真选项: -assertdebug -assertcover。assertdebug是为了调试断言,assertcover是为了收集断言覆盖。
- 这些property都可以在TB中的interface内定义;
最后再加上一个对整个assertion控制:
//在外部定义
initial begin
fork
forever begin
wait(rst_n==0);
$assertoff();
wait(rst_n==1);
$asserton();
end
join_none
end