【问题标题】:SystemC Transfer Level Model Sockets: Two Way CommunicationSystemC 传输级模型套接字:双向通信
【发布时间】:2017-12-23 06:35:11
【问题描述】:

在 Doulos 的 SystemC Transfer Level Model 文档中是这样写的

层次结构的顶层模块实例化一个启动器和 一个内存,并将启动器上的启动器套接字绑定到 目标内存上的目标套接字。套接字封装了一切 您需要在模块之间进行双向通信,包括端口 和双向通信的出口。一个启动器插座 始终绑定到一个目标套接字。

我对此的理解是,当你创建一个发起者和一个目标时,发起者通过调用 b_transport 开始通信,从而触发目标,目标可以回复。但是,我一直在编写一些代码,但似乎并非如此。让我们看一个例子。

我有一个非常基本的加法器实现,可以使用传输级别建模进行讨论。该模块作为目标服务器。

adder.cc

#define SC_INCLUDE_DYNAMIC_PROCESS

#include "tlm_utils/simple_initiator_socket.h"
#include "tlm_utils/simple_target_socket.h"
#include <iostream>

using namespace sc_core;
using namespace std;

#include "adder.h"

adder::adder(sc_module_name name)
    : sc_module(name), socket("socket2")
{
    socket.register_b_transport(this, &adder::b_transport);
    socket.register_transport_dbg(this, &adder::transport_dbg);
}

void adder::b_transport(tlm::tlm_generic_payload& trans, sc_time& delay)
{
    tlm::tlm_command cmd = trans.get_command();
    sc_dt::uint64   addr = trans.get_address();
    uint32_t    *ptr = (uint32_t*)trans.get_data_ptr();
    unsigned int    len = trans.get_data_length();
    unsigned char   *byt = trans.get_byte_enable_ptr();
    unsigned int    wid = trans.get_streaming_width();

    addend1 = *ptr;
    addend2 = *(++ptr);
    add();
    cout << "addend1: " << addend1 << endl;
        cout << "addend2: " << addend2 << endl;     
    cout << "sum: " << sum << endl;

    uint32_t *return_sum_loc = ptr;

    for(int i = 0; i< 2; i++) {
        return_sum_loc++;
    }

    memcpy(return_sum_loc, (char*) &sum, sizeof(uint32_t));
    cout << "New sum for return: " << *(return_sum_loc) << endl;
}

unsigned int adder::transport_dbg(tlm::tlm_generic_payload& trans)
{
    return 0;
}

void adder::add()
{
    sum = addend1 + addend2;
}

然后我有一个 test_bench 模块将作为启动器

test_bench.cc

#define SC_INCLUDE_DYNAMIC_PROCESS

#include "tlm_utils/simple_initiator_socket.h"
#include "tlm_utils/simple_target_socket.h"

using namespace sc_core;
using namespace std;

#include "test_bench.h"
#include <fstream>
#include <iostream>

test_bench::test_bench(sc_module_name name):
    sc_module(name), socket("socket")
{
    SC_THREAD(run_tests);
}

void test_bench::run_tests()
{
    ifstream infile("./adder.golden.dat");
    ofstream ofs;
    ofs.open("./adder.dat");
    
    uint32_t theoretical_sum = 0;

    while(infile >> data[0] >> data[1] >> theoretical_sum)
    {   
        tlm::tlm_generic_payload *trans = new tlm::tlm_generic_payload;
        sc_time delay = sc_time(10, SC_NS);
    
        cout << "Sending" << endl;
        cout << "Data[0]: " << data[0] << endl;
        cout << "Data[1]: " << data[1] << endl; 

        trans->set_data_ptr((unsigned char*)data);

        socket->b_transport(*trans, delay);

        cout << "data[2]" << data[2] << endl;

        ofs << data[0] << "\t" << data[1] << "\t" << data[2] << "\n";       

        delete trans;
    }
    infile.close();
    ofs.close();

    printf ("Comparing against output data \n");
    if (system("diff -w adder.dat adder.golden.dat")) 
    {

        cout << "*******************************************" << endl;
        cout << "FAIL: Output DOES NOT match the golden output" << endl;
        cout << "*******************************************" << endl;
    } 
    else 
    {
        cout << "*******************************************" << endl;
        cout << "PASS: The output matches the golden output!" << endl;
        cout << "*******************************************" << endl;
    }

}

这里是实例化和连接它们的父模块。

main.cc

#include "systemc.h"
#include "tlm_utils/simple_initiator_socket.h"
#include "tlm_utils/simple_target_socket.h"
#include "tlm_utils/tlm_quantumkeeper.h"

using namespace sc_core;
using namespace sc_dt;
using namespace std;

#include "test_bench.h"
#include "adder.h"

SC_MODULE(Top)
{
    test_bench  *tb;
    adder       *ad;

    sc_signal<bool> rst;

    sc_signal<bool> tb_irq;
    sc_signal<bool> ad_irq;

    Top(sc_module_name name) :
        rst("rst")
    {
        tb = new test_bench("test_bench");
        ad = new adder("adder");

        tb->socket.bind(ad->socket);
        tb->irq(tb_irq);
        ad->irq(ad_irq);
    }

};

int sc_main(int argc, char *argv[])
{
    Top *top = new Top("Top");
    sc_start();

    
}

当我运行可执行文件时,这是我得到的输出。


1 0 1

1 1 2

2 1 3

2 2 4

2 3 5

3 3 6

4 3 7

4 4 8

5 4 9

5 5 10


失败:输出与黄金输出不匹配

所以我最初的想法是,您将此有效负载按值传递到绑定到目标的启动器的 b_transport 函数中。目标将接收并解码此有效载荷。这部分正在发生。我能够解析按值传递的 uint32_t 到数据 []。 我最终根据写入到传递的内存中的 0 返回值意识到,这实际上不是按值传递的。由于某种原因,它被创建为指针类型,然后它在通过时被取消引用。 这实质上破坏了目标操纵传递给发起者的响应的内存的能力。

所以,Aynsley 提到的整个双向沟通的事情让我有点困惑。双向是指两个模块都需要目标和启动器套接字来启用双向通信吗?

【问题讨论】:

  • adder.h 丢失

标签: c++ sockets systemc


【解决方案1】:

这是 b_transport 调用的签名:

void b_transport( tlm::tlm_generic_payload& trans, sc_time& delay )

Payload 是通过引用传递的,所以 target 可以修改它。 Initiator 可以从同一个 payload 对象中读取返回值。

所以艾恩斯利提到的这整个双向沟通的事情让我 有点困惑。通过双向确实,他的意思是两个模块都需要目标和 启动器套接字以启用双向通信?

b_transport 调用实现的阻塞传输协议是单向的。发起者模块是主动的,目标模块是被动的。事务在一次调用中完成。允许目标在 b_transport 实现中调用 wait()。

但 TLM2.0 也支持由两个调用组成的非阻塞协议:

  • nb_transport_fw 从启动器到目标
  • nb_transport_bw 从目标到启动器

此双向协议允许对总线时序进行更细粒度的建模。例如,您可以对 AMBA AXI 总线中的乱序事务处理进行建模。

但实际上几乎每个人都使用 b_transport。我见过的大多数模型甚至都不支持非阻塞接口。

【讨论】:

  • 我发现近似定时样式非常麻烦(因为它强制使用回调的样式而不是使用 SC_THREADS 的简单编程样式)。根据您的经验,是否有人使用 nb_transport 接口?如果没有,如果您想进行性能评估,如何使用 b_transport 准确建模资源争用?
  • @sheridp 根据我的经验,nb_transport 在实践中没有使用。大多数 SystemC 模型的时间松散,用于早期软件开发。
猜你喜欢
  • 2013-12-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多