SystemC传输级模型套接字:双向通信

时间:2017-12-22 16:21:53

标签: c++ sockets systemc

在Doulos的SystemC传输级模型文档中,编写了

  

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

我对此的理解是,当您创建启动器和目标时,启动器通过调用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();


}

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

  

&LT; 1 0 0

     

&LT; 1 1 0

     

&LT; 2 1 0

     

&LT; 2 2 0

     

&LT; 2 3 0

     

&LT; 3 3 0

     

&LT; 4 3 0

     

&LT; 4 4 0

     

&LT; 5 4 0

     

&LT; 5 5 0

  

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解析为data []。 我最终根据我的0返回值实现了什么,这些值被写入传递的内存中,实际上并没有按值传递。由于某种原因它被创建为指针类型,然后通过时它被解除引用。 这实质上破坏了目标操纵传递的内存以将响应交回发起者的能力。

所以Aynsley提到的整个双向沟通的事情让我有些困惑。通过双向方式,他的意思是两个模块都需要目标和启动器套接字才能实现双向通信?

1 个答案:

答案 0 :(得分:1)

这是b_transport电话的签名:

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

Payload通过引用传递,因此target可以修改它。 Initiator可以从同一个有效负载对象中读取返回值。

  

因此Aynsley提到的整个双向沟通的事情让我有了一个   有点困惑。通过双向方式,他的意思是两个模块都需要目标和   启动器套接字以实现双向通信?

阻止b_transport调用实现的传输协议是单向的。 Initiator模块处于活动状态,目标模块处于被动状态。交易在一次通话中结束。允许Target在b_transport实现中调用wait()。

但TLM2.0还支持包含两个调用的非阻塞协议:

  • nb_transport_fw从发起人到目标
  • nb_transport_bw从目标到发起人

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

在实践中,几乎每个人都使用b_transport。我见过的大多数模型甚至不支持非阻塞界面。