通过阅读本文,你将知道:
什么是TLM?
为什么使用TLM?
怎样使用TLM?
介绍
TLM的英文全称是Transaction Level Modeling,也就是事务级建模。
我们知道,对一个系统建模可以分为很多个层次,比如算法抽象级别、寄存器传输级别等。事务级建模是介于算法抽象级别和寄存器传输级别之间的一个层次。
事务级建模中的事务指的是模块之间的数据和事件的交互。数据可以是一个或多个字,或者是一种数据结构,而同步或者中断等则属于事件的交互。
事务级建模的核心思想是在一个系统建模的过程中将运算功能和通信功能分开。模块之间的通信使用函数调用的方法来实现。这样模块不需要关注端口定义以及端口时序,从而建模速度更快,最终的仿真程序运行速度也要更快。

事务级模型可以分为三种:没有时序信息的模型、周期近似的模型和精确到每个周期的模型。根据一些公司的经验,没有时序信息的事务级模型的仿真速度要比RTL模型快1000到10000倍,而有时序信息的模型比RTL模型快100到1000倍,而精确时钟的模型比RTL模型快10到100倍。

使用TLM的好处很多(这也是为什么使用TLM的原因):
使用TLM可以快速搭建架构模型。这里的快速是相对于RTL模型而言的。
这个架构模型可以:1. 验证架构的可行性 2. 做性能分析 3. 硬件功能验证阶段的Golden Model 4. 让软件开发提前在架构模型上进行
运行速度快。同样的,这里的速度快也是相对于RTL模型而言的。
TLM实践
下面以一个最简单的例子来说明怎样使用TLM。
这个例子完成的功能是一个模块向另一个模块发送一个数据,发送模块命名为:Initiator,接收模块命名为:Target。
整个过程分为三个步骤:
- 连接Initiator和Target
 - Initiator发送数据
 - Target接收数据
 
连接Initiator和Target
两个模块通过Socket连接,下面是连接代码:
    Initiator i("Initiator"); // 创建发起者对象
    Target t("Target"); // 创建接收者对象
    i.initiator_socket(t.target_socket); // 使用Socket连接发起者和接收者
所以我们需要分别在Initiator和Target类中声明Socket,代码分别为:
    tlm_utils::simple_initiator_socket<Initiator> initiator_socket;
    tlm_utils::simple_target_socket<Target>  target_socket;
至此,两个模块的连接已经完成。
Initiator发送数据
下面我们开始发送事务。
我们需要在Initiator模块创建一个线程方法用来构建并发送事务:
    Initiator(sc_module_name name) : sc_module(name) {
        SC_THREAD(initiator_thread); // 注册线程方法
    }
    void
    initiator_thread() {
        while (true) {
            tlm::tlm_generic_payload *transaction_ptr = new tlm::tlm_generic_payload(); // 创建数据对象
            sc_time delay = SC_ZERO_TIME;
            initiator_socket->b_transport(*transaction_ptr, delay); // 开始传输
            gp_status = transaction_ptr->get_response_status(); // 获取接收者返回的状态
            if (gp_status == tlm::TLM_OK_RESPONSE) {
                std::cout << "TLM_OK_RESPONSE" << std::endl;
                wait(SC_ZERO_TIME);
            } else {
                std::cout << "TLM_OK_RESPONSE_NOT" << std::endl;
            }
        }
    }
至此,发送者的数据发送逻辑已经OK。
Target接收数据
下面添加Target接收数据的逻辑。
在上一小节的代码中,我们看到Initiator是通过下面的代码发送的数据:
            initiator_socket->b_transport(*transaction_ptr, delay); // 开始传输
上面代码中调用了b_transport方法,其实,这个方法的定义是在Target中,这样数据的传输通过方法调用就完成了。要传输的数据在transaction_ptr对象中。
所以,我们需要在Target中需要:
- 声明b_transport方法
 - 注册b_transport方法
 - 实现b_transport方法
 
声明b_transport方法
这里的方法名字可以根据业务逻辑换成其他名字。
    void
    b_transport(tlm::tlm_generic_payload  &payload, sc_core::sc_time &delay_time);
注册b_transport方法
    Target(sc_module_name name) : sc_module(name) {
        target_socket.register_b_transport(this, &Target::b_transport); // 注册我们自己定义的b_transport到target_socket中
    }
实现b_transport方法
    void
    b_transport(tlm::tlm_generic_payload  &payload, sc_core::sc_time &delay_time) {
        std::cout << "b_transport" << std::endl;
        payload.set_response_status(tlm::TLM_OK_RESPONSE); // 设置返回值
        return;
    }
至此,接收者就完成了数据的接收。
完整代码
#include "systemc.h"
#include "tlm.h"
#include "tlm_utils/simple_initiator_socket.h"
#include "tlm_utils/simple_target_socket.h"
class Initiator : public sc_module {
public:
    SC_HAS_PROCESS(Initiator);
    Initiator(sc_module_name name) : sc_module(name) {
        SC_THREAD(initiator_thread);
    }
    void
    initiator_thread() {
        while (true) {
            tlm::tlm_generic_payload *transaction_ptr = new tlm::tlm_generic_payload();
            sc_time delay = SC_ZERO_TIME;
            initiator_socket->b_transport(*transaction_ptr, delay);
            gp_status = transaction_ptr->get_response_status();
            if (gp_status == tlm::TLM_OK_RESPONSE) {
                std::cout << "TLM_OK_RESPONSE" << std::endl;
                wait(SC_ZERO_TIME);
            } else {
                std::cout << "TLM_OK_RESPONSE_NOT" << std::endl;
            }
        }
    }
    tlm_utils::simple_initiator_socket<Initiator> initiator_socket;
private:
    tlm::tlm_response_status gp_status;
};
class Target : public sc_module {
public:
    SC_HAS_PROCESS(Target);
    Target(sc_module_name name) : sc_module(name) {
        target_socket.register_b_transport(this, &Target::b_transport);
    }
    tlm_utils::simple_target_socket<Target>  target_socket;
private:
    void
    b_transport(tlm::tlm_generic_payload  &payload, sc_core::sc_time &delay_time) {
        std::cout << "b_transport" << std::endl;
        payload.set_response_status(tlm::TLM_OK_RESPONSE);
        return;
    }
};
int sc_main(int argc, char *argv[]) {
    Initiator i("Initiator");
    Target t("Target");
    i.initiator_socket(t.target_socket);
    sc_start();
    return 0;
}
参考
《SoC设计方法与实现》第三版 郭炜、魏继增、郭筝等
《TLM_2_0_presentation.pptx》