来源| 杰瑞IC验证(ID:Jerry_IC) |原创作者| Jerry
what? 100个agent??? 这个台子怎么搭?
漩涡鸣人不说暗话~
今天jerry和各位初学者一起玩一把UVM验证平台设计的一个点~
话不多说,传RTL设计! 威~武~
RTL设计:“话说我弄了这么一个RTL,它主要特点是有100组相同的信号端口!求一个验证平台来验证它。请您费费心~”
Jerry:“哥们先平身,擦擦眼泪洗个澡!既然话都说到这个地步了,那我必须出手帮你摆平!这趟活我接定了!”
活好接,可这个东西怎么玩呢??我们一起来捋一捋~
RTL如果1组信号交互端口,我这边就写1个agent嘛。
RTL如果有2组相同的信号接口,我就写1个agent,然后用这个agent例化2个不同名字的agent。
RTL如果有3组相同的信号接口,我就写1个agent,然后用这个agent例化3个不同名字的agent。
想明白了,很简单嘛~~
RTL如果有100组相同的信号接口,我就写1个agent,然后用这个agent例化100个不同名字的agent。
“Wait一下,100个agent我怎么例化?一个个写是不写的有点多啊?”
兄弟,可以啊!没写代码前可以想到这里已经有很大的进步了啊!
Jerry顺着你的思路带着大家来一起躺在床上仔细想想:
除了刚才这位兄弟说的这么多agent例化怎么写?
还有,100个agent,就有100个interface,这100个interface怎么例化?
怎么在顶层和RTL连接?
怎么传递?
100个agent就有100个sequence啊,这么多sequence怎么管理和同步,这个又怎么写?
诶~?功能覆盖率收集的时候如果需要采集这100个接口相关信息,这个覆盖率的又怎么写?
万一这个RTL设计哥们是一个善变的人,玩弄了我们的感情,今天给我说100个,明天给我变了说200个怎么办?
咦~?这么多的agent,跑起来的时候我的UVM验证平台运行速度绝逼慢啊?平台跑都跑不动怎么验?
……
行了行了,不能再想了,再想就不单纯了~下床!
首先,我们先要写好一个agent吧,不过本文重点不在这个细节,假装我们已经写好了一个agent。
这个agent很标准,有:driver、monitor、sequence、sequencer、agent顶层,当然还有它的transaction、interface。
为了方便讲解,这些组件的名字我们假装叫做:
jerry_driver;
jerry_monitor;
jerry_sequence;
jerry_sequencer;
jerry_agent;
jerry_transaction;
jerry_interface。
写好了agent了,很开心,下面我们写什么呢?去顶层连接RTL信号走?不,先不着急,顶层连接之前我们先写一个东西---宏!
正如刚才我们在床上思考的那样,万一这个RTL设计哥们是一个善变的人,今天需要100个agent,明天需要改成200个agent,我写平台的时候用一个固定的纯数字,岂不是每个写数字的地方都要改??
划重点!!!
这种可能在平台中多次出现的、且有变动可能性的文本内容,建议一定要使用SystemVerilog的宏来处理。
这条初学者可能感觉不到,但是等你碰到有修改需要的时候,等你含泪穿梭在你写的验证平台中,不断的寻觅和替换文本的时候,你一定会想到Jerry,与Jerry在床上一起思考设计验证平台的日子。
好了!让我们开开心心的写出这句宏定义:
`define AGENT_NUM 100
3
关键一步:顶层怎么搞
我们又为了方便讲解,假装RTL设计module名字叫做apple,它就一个位宽100bit信号aa,但是这个aa其实代表的是100组位宽为1的信号,每一bit就需要一个agent。
(这里纯属为了方便讲解,时钟复位都省了)
如下所示:
module apple(input [100-1:0] aa);
……
endmodule
比如我们的每个agent中的jerry_interface都如下,只有一个1bit的变量aa:
interface jerry_interface( );
logic aa;
endinterface
我们在顶层tb_top.sv中怎么把它们例化和连接?
先想想怎么完成一个过程?
这里我们需要把100个agent的interface拿出来都一例化,然后把这100个jerry_interface中的1bit变量aa,都对应连接到名字叫apple的RTL module上位宽为100bit aa信号上。
有人说了,接口例化可不可以做成一个size为100的数组啊?
哥们思路不错啊,但是! 接口的例化方式如下和RTL类似不能做成数组哦~
jerry_interface jerry_vif( );
那这个怎么写?老老实实的一行行写?像这样:
jerry_interface jerry_vif0( );
jerry_interface jerry_vif1( );
jerry_interface jerry_vif2( );
……
jerry_interface jerry_vif99( );
直接这样一行行的写,总是感觉哪里不太对啊,都写了半个小时了还没例化完?
有人说,这你不懂了吧,可以使用gvim等编辑器的插件,迅速写100行,数字可以自动自加排序!
不错,这样当然也没问题,可以提高一定的速度,但是就像我们之前说的,agent数量变了呢?
本来我们就是想靠一个宏文本改一个字迅速搞定变化,现在工作量还是要有点啊?
而且文件一打开翻了好几页都还是接口的例化,平台的颜值有点不太美。
那这个我们怎么写呢?
上代码~
generate
for(genvar i=0;i<`AGENT_NUM;i++)
begin:jagt
jerry_interface jerry_vif( );
end
endgenerate
没错!对于顶层的多组例化,我们不要忘记了generate~
谁说这个generate只有RTL设计中有可能会用到,验证这不是也用到了嘛,本来要写100行,几句就可以了啊,而且使用起来了我们早就定义好了的宏`AGENT_NUM,再也不怕风吹雨打的变化,真香~
可能有人不太熟悉generate,这个怎么引用呢?
其实很简单,比如按照上面的代码,我们要操作第3个agent接口中的变量,可以如下引用:
jagt[2].jerry_vif.aa
行,例化好了接口,那我们就开始连接吧,我们比较方便的其实是做一个wire变量中间转一下,然后把这个中间变量和rtl顶层连接起来(注意看引用的部分就像刚才讲的那样引用哦):
wire [`AGENT_NUM-1:0] tb_aa;
generate
for(genvar i=0;i<`AGENT_NUM;i++)
begin:blk
assign tb_aa[i]= jagt[i].jerry_vif.aa;
end
endgenerate
顶层例化连接RTL时候只需要把刚才定义的中间变量连进去就好了:
apple apple_tb (.aa ( tb_aa ));
接口例化和顶层RTL连接问题就解决了,说到这里顺便想提一下,想在顶层把这100个接口用config_db set出去怎么写?
不卖关子了,还是可以这么玩:
generate
for(genvar i=0;i<`AGENT_NUM;i++)
begin:config
initial begin
uvm_config_db#(virtual jerry_interface)::set(null,$sformatf(“*u_jerry_agt[%0d]*”,i), “jerry_vif”, jagt[i].jerry_vif);
end
end
endgenerate
(这里提到的$sformatf的玩法可以回顾之前的文章哦~下方链接直达)
哈哈,快乐的时间总是短暂的,又到了告别的时刻,今天就先说这么多吧,至于100个agent,seq怎么设计?
功能覆盖率怎么收集?
验证的时候怎么一个策略?
……种种问题,我们下次再聊啦~。
(各位也可以提前想想,在留言区说说自己的想法~),祝大家早日无敌~加油哦!
——The End——