打印格式的定制

打印函数:

Useful SystemVerilog System Tasks
Task NameDescription
$sscanf(str,format,args);

$sscanf 将字符串按照某个模板格式进行扫描,其字符串格式和C语言中的printf()函数类似

$sformat(str,format,args);

$sformat是$sscanf的反函数。将字符串按照给定的格式填入相应的参数args中

$display(format,args);

$display就是Verilog的printf语句,在stdout上显示格式化的字符串

$sformatf(format,args);

$sformatf任务和$sformat相似,除了其返回字符串结果。字符串作为$sformatf的返回值,而不是像$sformt一样放在第一个参数上。

我们在网上查资料看到的打印输出都是这么几个函数,我们也知道怎么用,但是怎么用才能发挥打印函数的威力呢?

        当我在用synopsys的 svt_eth_vip 验证 PCS时,要通过vip将数据包发送给serdes,serdes将数据串转并之后送给pcs,pcs内部不论经过64/66b解码还是经过8b/10b解码之后,再将数据送给MAC,在MAC的rx端我们通过 xgmii,gmii,cgmii等等 接口就能得到 serdes-> PMA-> PCS整个解码流程解析出来的数据,通过比对svt_eth_vip 发送的数据与MAC的rx端接收到的数据我们就能知道整个链路是否正常工作。但是整个过程与我们打印数据有什么关系呢?当然有关系,关系还非常大,我们在调试初期,可能由于rtl代码并不完善或者验证环境设计的不合理,常常会出现一些错误,需要我们根据解析出来的数据反推到底是环境还是代码出现了问题,由于数据量一般都比较大,这时候合理的设计打印输出的结果,非常有助于帮助我们分析这些错误。

        简单的说明:

        $sformat ,这个函数需要一个 str来接收最后产生的字符串,也就是说,调用这个函数是将 args 以 format的形式填入 str。

        $sfomratf,这个函数就有意思了, 他的结果就是一个字符串,可以直接放到任何可以接收字符串的函数,task上,当然也可以直接赋值给 string 类型的变量。

填充形式

mac报文是由 前导码+源地址+目的地址+内容+crc校验组成,前导码 7个 0x55,一个0xd5,源地址和目的地址48 bit,crc校验32bit,我在存储的过程中,去掉前导码,将源地址和目的地址单独拉出来,将内容进行显示输出,显示形式如下图:

 这样一排十个数,源地址,目的地址分开显示,不管package中有多少个数,数据显示很整齐,对比起来也很容易发现错误的地方。那接下来我们来看我是怎么实现显示这种方格的。

打印函数:

print_pkt 函数 print_pkt (string name , xgmii_rx_item#(width) item);

该函数有两个入参 分别为: name, xgmii_rx_item , name代表你要显示的是谁的数据包,向上图 GMII0 RX LANE0 就是显示的是lane0 的 GMII0的包, xgmii_rx_item 代表你要显示的数据包的内容。

处理 xgmii_rx_item 

1,跳过前导码,截取到 源地址 和目的地址:

// 伪代码,非重点的定义等不做说明

        bit[7:0] mac_pkt [$];

        mac_pkt = {>>{item.pkt_data}};

        dst_addr = {>>{mac_pkt[pre_cnt:(pre_cnt+5)]}};

        src_addr = {>>{mac_pkt[(pre_cnt+6):(pre_cnt+11)]}};

        print_str = "";

        print_str = {print_str, "\n"};

        print_str = {print_str, "+-----+-----+-----+-----+-----+-----"};

        print_str = {print_str,$psprintf("+----------%s----------+",name)};

        print_str = {print_str, "+-----+-----+-----+-----+-----+-----"};

        print_str = {print_str, "|  TX  | 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|"};

        print_str = {print_str, "+-----+-----+-----+-----+-----+-----"};

        print_str = {print_str,$psprintf("| Source Add:     %h   |",src_addr)};

        print_str = {print_str, "+-----+-----+-----+-----+-----+-----"};

        print_str = {print_str,$psprintf("| Dest   Add:     %h   |",dst_addr)};

        print_str = {print_str, "+-----+-----+-----+-----+-----+-----"};

        print_str = {print_str, "|INDX0| 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|"};


        while(mac_cnt < item.pkt_data.size()-1) begin

        print_str = {print_str, $psprintf("|%5d|%x|%x|%x|%x|%x|%x|%x|%x|%x",mac_cnt/10
                                                                           
,item.pkt_data[0+mac_cnt]                                                              
,item.pkt_data[1+mac_cnt]
,item.pkt_data[2+mac_cnt]
,item.pkt_data[3+mac_cnt]
,item.pkt_data[4+mac_cnt]
,item.pkt_data[5+mac_cnt]
,item.pkt_data[6+mac_cnt]
,item.pkt_data[7+mac_cnt]
,item.pkt_data[8+mac_cnt]
,item.pkt_data[9+mac_cnt]), "\n"};
        mac_cnt = mac_cnt + 10;
        end

        print_str = {print_str, "+-----+-----+-----+-----+-----+-----"};
$display("%s",print_str);

endfunction:print_pkt

        通过这一系列的操作,我们把本来需要`uvm_info 或者 display杂乱无章的显示在每一行的打印数据,统一到一个方块内,这样看起来简洁高效了很多。

在这里想说的是:

foreach(item.pkt_data[i]) begin
    `uvm_info("PKT",$psprintf("item.pkt_data[%3d] = %x",i,item.pkt_data[i]),UVM_MEDIUM)
end

这种打印方法不是不行,但是非常的不直观,对比前后数据会比较痛苦,把所有的数据集中在一起,按照同样的格式打印出来,帮助我们看清楚数据,把时间花在解决问题上,并且像这样的做法,也可以放在 reference_module , monitor 等等需要打印大段数据的地方。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值