【芯片设计】绑一个超然于环境的带宽统计器

乐活   2024-12-02 12:01   北京  

继续最近的交付总结系列更新,本篇遇到和处理的问题和带宽统计以及性能有关。简单说就是在推进过程中,需要一些方法来实时/分阶段/整体的统计一下有效带宽,以确定一些环境的异常报错是环境引起的还是RTL内部的问题。

最开始想到了多年前的一个统计带宽的脚本,这个脚本是tcl做的工具,能够嵌入verdi里,然后对着你的波形一顿扫描。只要你的波形里做了en和size两个信号,脚本就能够统计出从黄线到白线之间的总数据量,然后根据时间计算出来平均带宽。

对了,统计的时候这个黄线还在实时动态的往白线那移动,所以很长时间我都觉得这个脚本太神奇了。后来试了好几次想复现但是因为tcl的基础太弱对verdi的dpi也不熟悉,所以一直搞不出来,如果谁会咱们好好聊一聊。所以没有办法,就另辟蹊径吧。

目前需求比较明确:

1.能够进行感兴趣时间段的流量带宽统计;

2.不影响已有的RTL结构和验证环境,独立于RTL交付和验证环境之外;

3.可以同时监控多个模块、多个接口的流量;

基于这三个需求,我觉得用一个RTL的组件来做可以了,没有太难。于是快进到组件的开发,在此也没研究有没有现成的组件哈,总觉得简单的这个功夫自己就写完了(ಥ_ಥ)。先看进行带宽统计,这个其实不难办,核心就是跟刚刚提到的脚本一样做一个模块对关心的接口进行采样。所以把这个模块的顶层搞出来:

module flow_cnt #(  //parameter  parameter realtime START_TIME = 0ns,  parameter realtime END_TIME = -1ns,   parameter BIT_W  = 10, //max 1023bit per cycle  parameter MAX_W  = 64  //bit cnt max)( /*AUTOARG*/   // Inputs   clk, rst_n, power, bit_cnt   );
// ----------------------------------------------------------------// Interface declare// ----------------------------------------------------------------input clk;input rst_n;input power;input [BIT_W -1:0]bit_cnt;
endmodule
四个parameter,START_TIME~END_TIME就是我们关心的时间段,BIT_W是送进来的size(模块里我叫bit_cnt)的位宽,MAX_W是内部数据量计数器的位宽。

接口就更简单了,时钟复位使能和使能时的数据有效量。顶层搞定了,那里面怎么办呢,也很简单在关心的时间段里power有效就把bit_cnt往上累加呗(要是累加加翻了就把MAX_W设置的大一些嗷):

logic [MAX_W -1:0]whole_bit_cnt;
always @(posedge clk or negedge rst_n) begin if(!rst_n) begin whole_bit_cnt <= {MAX_W{1'b0}}; end else if($realtime>=START_TIME && ($realtime<END_TIME || END_TIME<=0)) begin if(power) whole_bit_cnt <= whole_bit_cnt + bit_cnt; end end
然后,等到时间一超过END_TIME,就根据累加值和时间间隔,把带宽给算出来:
initial begin: BANDWIDTH_REPORT  real flow_bandwidth;  realtime time_duration;  while(1)begin    @(posedge clk);    if($realtime<END_TIME)begin      continue;    end    else begin      time_duration  = $realtime - START_TIME;      flow_bandwidth = whole_bit_cnt/time_duration;      $display("==============================================================================");      $display("%m bandwidth report at %t:", $realtime);      $display("    whole_bit_cnt = %0dbit = %0dByte", whole_bit_cnt, whole_bit_cnt/8);      $display("    time_duration  = %t", time_duration);      $display("    flow_bandwidth = %fGbps", flow_bandwidth);      $display("==============================================================================");      break;    end  endend
好了,代码看起来就搞定了。开始思考第二个需求,如何合入已有的RTL结构和验证环境中,还能尽量不影响原本的代码呢?例化在RTL内是肯定不可能的,例化在验证环境中还要去动别人的代码,这也不行那也不行,就只能借助bind来搞定了。

在绝大部分场景中,bind都是用来搞并发断言的,但其实bind的使用场景很多,各种我们自己写的不需要交付的辅助小组件小模块都可以用bind的方式与需要监控的模块或结构进行绑定。那么再看下第三个需求,可以监控多模块多接口。那其实这件事也就简单了,在flow_cnt上封装一个顶层,在顶层里对确切的某个inst进行bind就可以了呀!

我们借助sync_fifo那个验证环境,在该环境中sync_fifo例化在testbench中:

//-------------------------------------{{{rtl instsync_fifo #(    .WIDTH(WIDTH),    .DEPTH(DEPTH)) u_sync_fifo(    .clk(clk),    .rst_n(rst_n),    .afull_th(afull_th),    .aempty_th(aempty_th),    .winc(winc),    .wdata(wdata),    .wfull(wfull),    .almost_wfull(almost_wfull),    .rinc(rinc),    .rdata(rdata),    .rempty(rempty),    .rvld(rvld),    .almost_rempty(almost_rempty));
我们监控其一段时间内的输出带宽,那么power即rvld信号,bit_cnt其实就是WIDTH每笔数据固定的数据量为WIDTH。因此在flow_cnt.sv同一层级做bind_fifo_flow_cnt.sv文件,文件内容如下:
bind testbench.u_sync_fifo flow_cnt #(  .START_TIME(800ns),  .END_TIME(80000ns)) u_fifo_flow_cnt(  .clk(clk),  .rst_n(rst_n),  .power(rvld),  .bit_cnt(WIDTH));
将testbench.u_sync_fifo的信号和parameter作为flow_cnt的输入,将flow_cnt bind在testbench.u_sync_fifo模块上进行接口采样,采样时间为800ns~80000ns。

然后只需要把这两个文件放到filelist中,而不需要对验证环境进行任何的改动:

+libext+.v+.sv+incdir+/home/ICer/gitee_path/verilog_fifo_code/src/ut_ver/../rtl-y /home/ICer/gitee_path/verilog_fifo_code/src/ut_ver/../rtl
/home/ICer/gitee_path/verilog_fifo_code/src/bind/flow_cnt.sv/home/ICer/gitee_path/verilog_fifo_code/src/bind/bind_fifo_flow_cnt.sv
../ver/sync_fifo_pkg.sv/home/ICer/gitee_path/verilog_fifo_code/src/ut_ver/../rtl/sync_fifo.v../top/testbench.sv
咱们先来理论上算一下,目前环境的配置是时钟周期10ns,fifo位宽WIDTH=16,如果满通路输出那么计算的平均带宽应该是:
16bit/10ns = 1.6 * 10^9 bps = 1.6Gbps
在sim目录下执行仿真,得到仿真结果:

对应时间段的波形确实是满带宽输出:

看起来好像算的还是挺准的。那么因为有个bind_fifo_flow_cnt.sv这个顶层存在,只需要在bind时给定具体inst路径,就可以在该文件里对感兴趣的多个模块和内部总线进行监控了。

基本的工作就完成啦!后面只需要按自己的需求,在flow_cnt.sv的基础上分装一个axi_flow_cnt.sv来对AXI总监挂接监控带宽就可以,这里就不多聊了。

系列文章入口——

【芯片设计】SoC 101(一):绪论
【芯片设计】FIFO漫谈(零)从无处不在的FIFO开始说起
【芯片设计】计算机体系结构(一)虚拟内存

【芯片设计】深入理解AMBA总线(零)绪论

【芯片设计】握手协议的介绍与时序说明
【芯片设计】复位那些小事 —— 复位消抖
【芯片设计】快速入门数字芯片设计(一)Introduction
【芯片验证】UVM源码计划(零)下定决心读源码前的自测环节
【芯片设计】异步电路碎碎念(一) 到底什么是异步电路
【芯片设计】从RTL到GDS(一):Introduction
其他文章链接——
【芯片验证】sva_assertion: 15道助力飞升的断言练习
【芯片验证】可能是RTL定向验证的巅峰之作
【芯片验证】RTL仿真中X态行为的传播 —— 从xprop说起
【芯片验证】年轻人的第一个systemVerilog验证环境全工程与解析
【芯片设计】verilog中有符号数和无符号数的本质探究
【芯片设计】论RTL中always语法的消失术
【芯片设计】代码即注释,注释即代码
【芯片设计】700行代码的risc处理器你确实不能要求太多了
入职芯片开发部门后,每天摸鱼之外的时间我们要做些什么呢
如何计算系统的outstanding 和 burst length?
芯片搬砖日常·逼死强迫症的关键词不对齐事件
熟人社会里,一群没有社会价值的局外人

芯时代青年
专心数字前端全流程,芯时代有为青年的自我修养
 最新文章