SystemVerilog的隐含随机约束
SystemVerilog的隐含随机约束A -> B中,如果表达式A是真的话,那么约束块B起作用。隐含执行器在本质上倾向于显示它们是单向的,但是,这里有一个双向效应,即如果约束B为真的可能性很小的话,则A为真的可能性也非常小。
1. 基本随机
如果隐含的两面都包含随机变量,就要检查两面的双向可能性是否为真。在下面的实例中,在第7行的变量enum中有一个隐含约束,即m_op必须是READ、WRITE或 NOP中的一个。类名叫Packet0。
typedef enum {READ, WRITE, NOP} kind;class Packet0;rand bit [31:0] m_data;rand kind m_op;constraint data_range {(m_op == READ) -> m_data inside {[1:100]};(m_op == WRITE) -> m_data inside {[101:300]};(m_op == NOP) -> m_data inside {0};}endclass : Packet0
如果m_op等于READ,m_data就有100个可能值满足第一个隐含。如果m_op等于WRITE,m_data就有200个可能值满足第二个隐含。但是,如果m_op等于NOP,m_data就只有1个可能值满足第三个隐含。因为m_data只有1/301可能值满足第三个隐含,所以m_op的值为NOP的机率仅为0.003。
2. 控制求解顺序
实际上,随机化过程包括计算求解空间,然后随机选取一个解。这个解就作为给定值赋给随机变量。通常,求解器用相同的机率来选取每个解。也可以建议求解器在选取其它变量之前选取特定的随机变量的值。所有随机变量的求解空间保持不变;但是,只在最早选取的随机变量选择中存在机率一致。如果将前面的例子改为先求解m_op,后求解_data,则在为m_data选择值之前READ、WRITE和NOP具有相同的入选机率。类名叫Packet1。
class Packet1;rand bit [31:0] m_data;rand kind m_op;constraint data_range {(m_op == READ) -> m_data inside {[1:100]};(m_op == WRITE) -> m_data inside {[101:300]};(m_op == NOP) -> m_data inside {0};}constraint order {solve m_op before m_data;}endclass : Packet1
除了建议首先为哪个随机变量选择值以外,分配约束为选择值增加了权重因子。这不需要修改求解空间,除非有一个权重为0。在下面的实例中,m_op选择READ的机率为2/5(40%),选择WRITE的机率为2/5(40%)选择NOP的机率为1/5(20%)。类名叫Packet2。
class Packet2;rand bit [31:0] m_data;rand kind m_op;constraint data_range {(m_op == READ) -> m_data inside {[1:100]};(m_op == WRITE) -> m_data inside {[101:300]};(m_op == NOP) -> m_data inside {0};}constraint op_dist { m_op dist {READ := 2, WRITE := 2, NOP := 1}; }endclass : Packet2
在一系列交错的随机变量中只针对一个随即变量使用分配约束。当出现多重的,相冲突的约束时,就很难计算出选择随机变量值的机率。在创建了求解空间后采用分配约束,且不能保证满足分配约束。
3. 实验结果
测试程序如下所示:
beginPacket0 pkt0 = new();Packet1 pkt1 = new();Packet2 pkt2 = new();int pkt0_read_num, pkt0_write_num, pkt0_nop_num;int pkt1_read_num, pkt1_write_num, pkt1_nop_num;int pkt2_read_num, pkt2_write_num, pkt2_nop_num;for (int unsigned i=0; i<100_0000; i++) beginassert ( pkt0.randomize() ) else $fatal;case ( pkt0.m_op )READ : pkt0_read_num++;WRITE : pkt0_write_num++;NOP : pkt0_nop_num++;default : $fatal;endcaseassert ( pkt1.randomize() ) else $fatal;case ( pkt1.m_op )READ : pkt1_read_num++;WRITE : pkt1_write_num++;NOP : pkt1_nop_num++;default : $fatal;endcaseassert ( pkt2.randomize() ) else $fatal;case ( pkt2.m_op )READ : pkt2_read_num++;WRITE : pkt2_write_num++;NOP : pkt2_nop_num++;default : $fatal;endcaseend$display("For Packet0 constraint, READ:WRITE:NOP = %0d:%0d:%0d = %.1f:%.1f:1.0", pkt0_read_num, pkt0_write_num, pkt0_nop_num, (pkt0_read_num*1.0/pkt0_nop_num), (pkt0_write_num*1.0/pkt0_nop_num));$display("For Packet1 constraint, READ:WRITE:NOP = %0d:%0d:%0d = %.1f:%.1f:1.0", pkt1_read_num, pkt1_write_num, pkt1_nop_num, (pkt1_read_num*1.0/pkt1_nop_num), (pkt1_write_num*1.0/pkt1_nop_num));$display("For Packet2 constraint, READ:WRITE:NOP = %0d:%0d:%0d = %.1f:%.1f:1.0", pkt2_read_num, pkt2_write_num, pkt2_nop_num, (pkt2_read_num*1.0/pkt2_nop_num), (pkt2_write_num*1.0/pkt2_nop_num));end
使用xcelium/21.12.001的仿真工具对上述Packet0、Packet1和Packet2进行随机100万次,并统计每个packet中m_op 分别为READ:WRITE:NOP的概率,结果如下:
| For Packet0 constraint, READ:WRITE:NOP = 3324498:6642523:32979 = 100.8:201.4:1.0| For Packet1 constraint, READ:WRITE:NOP = 3336314:3332631:3331055 = 1.0:1.0:1.0| For Packet2 constraint, READ:WRITE:NOP = 4000065:4000809:1999126 = 2.0:2.0:1.0
Packet0、Packet1和Packet2的m_op变量随机值分布符合上述分析的概率。
使用questasim/2021.3的仿真工具同样对上述Packet0、Packet1和Packet2进行随机100万次,并统计每个packet中m_op 分别为READ:WRITE:NOP的概率,结果如下:
| # For Packet0 constraint, READ:WRITE:NOP = 3327366:4987807:1684827 = 2.0:3.0:1.0| # For Packet1 constraint, READ:WRITE:NOP = 3334638:3333648:3331714 = 1.0:1.0:1.0| # For Packet2 constraint, READ:WRITE:NOP = 3998633:4000231:2001136 = 2.0:2.0:1.0
根据Packet0的仿真结果可以看出,Questasim的随机仿真数有bug,哈哈。