在SystemC/TLM建模中,我们经常需要加一些payload monitor来进行performance counter的统计,或者使用data checker来进行unit test。很多情况下,为了保持代码的整洁和提高仿真速度,我们不希望将monitor和checker的代码直接写在module内部。此时,就可以使用tlm提供的analysis port,然后根据需求来添加特定的monitor或checker。一个tlm::tlm_analysis_port可以绑定0个 /1个或多个tlm::tlm_analysis_if。
具体实现为:
monitor或checker (如下图的module B和C)继承于tlm::tlm_analysis_if,同时对write 函数进行改写,在函数内部进行 performance counter的统计计算,或test check。
在module内部(如下图的module A)定义一个tlm::tlm_analysis_port的成员变量,在module 内部 调用这个成员变量的write 函数。
在top中,对tlm::tlm_analysis_port 和多个tlm::tlm_analysis_if进行bind。这样module内call write函数时,所有bind的tlm::tlm_analysis_if 子类模块中的write函数 都会被调用一次。
在SystemC spec中给出了一个analysis port的示例,以下代码是在此基础上的一个完整使用,其中class parent和 child都定义了一个tlm::tlm_analysis_port;child->ap.bind(parent_ap);是两个tlm_analysis_port的层次间绑定,在top module中的两个bind是 tlm_analysis_port和tlm_analysis_if的绑定。
// execute:
// g++ -g -Wall -lsystemc -m64 -pthread main.cpp
// -L/$(your systemc path)/lib-linux64
// -I/$(your systemc path)/include -I/$(your systemc
// path)/src/tlm_utils -o sim
#include "tlm"
#include <systemc>
using namespace std;
struct Trans // Analysis transaction class
{
int i;
};
struct Subscriber : sc_core::sc_module, tlm::tlm_analysis_if<Trans> {
Subscriber(sc_core::sc_module_name name) : sc_core::sc_module(name) {}
virtual void write(const Trans &t) {
cout << this->name() << " Hello, got " << t.i
<< "\n"; // Implementation of the write method
}
};
class Child : public sc_core::sc_module {
public:
tlm::tlm_analysis_port<Trans> ap;
SC_HAS_PROCESS(Child); // must have this line
explicit Child(sc_core::sc_module_name name)
: sc_core::sc_module(name), ap("ap") {
SC_THREAD(thread);
}
void thread() {
Trans t = {999};
ap.write(
t); // Interface method call to the write method of the analysis port
}
};
class Parent : public sc_core::sc_module {
public:
tlm::tlm_analysis_port<Trans> parent_ap;
Child *child;
explicit Parent(sc_core::sc_module_name name)
: sc_core::sc_module(name), parent_ap("ap") {
child = new Child("child");
child->ap.bind(
parent_ap); // Bind analysis port of child to analysis port of parent
}
};
class Top : public sc_core::sc_module {
public:
Parent *parent;
Subscriber *subscriber1;
Subscriber *subscriber2;
explicit Top(sc_core::sc_module_name name) : sc_core::sc_module(name) {
parent = new Parent("parent");
subscriber1 = new Subscriber("monitor");
subscriber2 = new Subscriber("checker");
parent->parent_ap.bind(
*subscriber1); // Bind analysis port to two separate subscribers
parent->parent_ap.bind(
*subscriber2); // This is the key feature of analysis ports
}
};
int sc_main(int argc, char **argv) {
Top m_top("top");
sc_core::sc_start();
return 0;
}