论文概述
本文题目 Multithreading a UVM Testbench for Faster Simulation,作者是 Intel 的加拿大验证工程师。
本文提出了一种C/C++ model和simulation并行的方法,来提升 UVM 测试平台的仿真速度。
研究目的
为了验证仿真结果的正确性,simulation 中常用UVM来创建predictor和scoreboard组件。predictor可以用SV直接写,但对于偏算法的、较为复杂的DUT,尤其是Vender提供的IP,常采用DPI调用C/C++ model等软件模型作为参考模型(如图1)。
正常情况下,SV DPI调用C/C++ model时,simulation会停在原地等待C model执行完毕,从而影响simulation效率。即便目前先进的simulator支持多线程的DUT仿真,但对于整个UVM测试平台而言,CPU内仍然时按照单线程运行的。本文目的便是为了提升这种DPI 调用软件模型的simulation效率,为了突破单线程桎梏,来进一步提升 UVM 测试平台的仿真速度。
图1 UVM testbench using a C++ Predictor Model
新方法
方法提出
本文提出一种方法(图2),开辟一个甚至多个新线程单独跑C/C++ model(其他任何软件模型),与原有的UVM TB并行,能够缩短仿真耗时。运行中的simulation和C/C++ model采用进程间通信机制来交换数据。
为了对多进程启动顺序、数据交换等进行管理,本文构建了一个线程池 管理器(thread pool manager)。线程池是一个可配置线程数目的软件对象,其中的每个线程都可以单独执行所分配的任务(Job)。Job通过输入队列丢到线程池,由线程池中的空闲线程执行该Job。执行完毕后线程池采用异步方式返回Job执行结果。
图2 Modified UVM testbench with an asynchronous predictor model
方法实现
线程池的初始化及访问
通过单独开辟新线程的方式来初始化及访问thread_pool class。这种设计隐藏了初始化行为及全局状态,为人所诟病。但该方法使得thread pool class能够响应多个不同组件的调度请求,多个传输单元的code只做微调即可轻松获取 thread pool instance,总体而言是利大于弊的。C++惰性计算的特性也保证了thread pool object只在初次执行thread_pool::get_instance进行调用的时候进行一次初始化。
线程池类的实现需要 :
- 一个私有默认构建函数(private default constructor)
- get_instance 函数,以返回thread_pool静态实例的引用
class thread_pool { public: static thread_pool& get_instance() { static thread_pool inst; //Class will be initialized a single time return inst; } private: thread_pool() = default; //Prevent creation of other instances }
线程调度
线程池初始化时即生成了制定数量的线程 std::thread,初始化完毕后进入idle状态。线程由std::condition_variable唤醒,从输入任务队列中pop出job然后执行job,Input Job Queue的保证了线程的执行顺序。若线程池所有线程处于忙碌状态,则暂时停止从任务队列中pop job,待有job执行完毕、线程ilde之后再行pop。
多个job虽然是按照既定顺序开始执行的,但不同job的执行时间不同,并不能保证按顺序完成job。对于scoreboard等对job反馈结果有严格顺序要求的情况,需按照顺序同步返回job执行结果。本文采用std::packaged_task返回值std::future来解决该问题。
std::future具有按序立即返回的特性,因此可以在调用线程池时把std::future压入一个特殊队列,在scoreboard需获取新数据时检查std::future队列相关job是否完成并将其弹出,便于scoreboard区分其所需的data,也避免了早前丢出的多个任务抢同一个返回结果。
线程池集成到UVM TB
为了实现model和UVM并排走,需要把线程池集成到原有的UVM环境中,需要对C model和UVM TB做响应调整。
C/C++的改动
需要对原有的C/C++ 代码做点微调才能把线程池集成到UVM环境中,主要改动如下:
添加std::future及其队列std::queue<std::future> futures
添加predictor中调用的function predict_call()
添加scoreboard中调用的scoreboard_call()
示意代码如下:
//C++ Code //Store futures in the order they are created for a particular task type std::queue<std::future<int>> futures; //Function that would have previously been called by the predictor //Returns the expected result from a single integer input int calc_result(int num) { int result = 0; //Do complex math work or any other modelling here return result; } //New function called from the UVM Predictor extern "C" void predict_call(const int num) { thread_pool& tp = thread_pool::get_instance(); //Queue the job on the thread_pool and store the std::future futures.emplace(tp.add_job(calc_result, num)); } //New function called from the UVM Scoreboard extern "C" int scoreboard_call() { int num = futures.front().get(); futures.pop(); return num; }
SV/UVM的改动
之前的方式是在UVM中 predictor中DPI调用C/C++ model,等model执行完毕后通过 analysis fifo 把结果传递给scoreboard。为了实现C/C++ model与UVM TB的并行,UVM环境中原有model调用方式改为在predictor中调用model(丢job)然后立即退出,在scoreboard中调用model(丢job)收集job计算结果,不再通过analysis fifo收结果。
//Predictor //DPI call to submit work to the thread pool import "DPI-C" function void predict_call(input int num); class my_predictor extends uvm_subscriber #(my_item); //Predictor code function void write(my_item item); predict_call(item.data); endfunction endclass : my_predictor //Scoreboard //DPI call to retrieve the next predicted value import "DPI-C" function int scoreboard_call(); class my_scoreboard #(type T = my_item) extends uvm_scoreboard; uvm_analysis_imp_received #(T, my_scoreboard) received_export; //Scoreboard code virtual function void write_received(T txn); int rx_data; int pred = scoreboard_call(); //Get predicted data rx_data = txn.data; if (rx_data != pred) begin `uvm_error("Scoreboard", "Failure") end endfunction endclass : my_scoreboard
实验结果
本文以不同predictor time和有无thread pool为变量做了几组对比试验,证明了本文所提出的方法的确能够加速仿真。predictor time越大,提升效果越明显。
讨论
根据本文实验结果,该方法对于predictor time 2~20ms的仿真,提速效果为3~4x。对于1ms以内的仿真,提速效果并不明显。鉴于复杂度并不高,无论如何大家都可以试一下,学点新知识嘛。 😀
完