John Frye
John Frye

Reputation: 255

Implementing SystemC TLM Testbench Build Fail

I am trying to implement a SystemC basic TLM test bench for an adder module I created using basic simple_initiator_socket and simple_target_socket.

Currently the build is failing and I am having trouble diagnosing why.

Here are the implementations for the three main modules, the adder, the test bench, and the main module that instantiates both and initiates dataflow.

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;

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

        tb->socket.bind(ad->socket);

    }
};

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


}

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;
using namespace sc_dt;

#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", ofstream::out | ofstream::app);

    while(infile >> data[0] >> data[1])
    {   
        tlm::tlm_generic_payload *trans = new tlm::tlm_generic_payload;
        sc_time delay = sc_time(10, SC_NS);

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

        socket->b_transport(*trans, delay);

        ofs << data[0] << data[1] << data[2];       

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

    printf ("Comparing against output data \n");
    if (system("diff -w sha1.dat sha1.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;
    }

}

adder.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 "adder.h"

adder::adder(sc_module_name name)
    : sc_module(name), socket("socket")
{
    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();

    memcpy(ptr + sizeof(uint32_t) * 2, (char*) &sum, sizeof(uint32_t));
}

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

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

Here is the error I am seeing upon compilation.

In file included from /home/epi/jfrye_xilinx/SystemC/systemc-2.3.2/include/sysc/kernel/sc_module.h:35:0, from /home/epi/jfrye_xilinx/SystemC/systemc-2.3.2/include/systemc:74, from /home/epi/jfrye_xilinx/SystemC/systemc-2.3.2/include/tlm:23, from /home/epi/jfrye_xilinx/SystemC/systemc-2.3.2/include/tlm_utils/simple_initiator_socket.h:23, from /home/test_benches/adder/test_bench.cc:3: /home/test_benches/adder/test_bench.cc:

In constructor ‘test_bench::test_bench(sc_core::sc_module_name)’: /home/epi/jfrye_xilinx/SystemC/systemc-2.3.2/include/sysc/kernel/sc_module.h:463:29: error: ‘SC_CURRENT_USER_MODULE’ has not been declared SC_CURRENT_USER_MODULE, \

/home/epi/jfrye_xilinx/SystemC/systemc-2.3.2/include/sysc/kernel/sc_process.h:151:46: note: in definition of macro ‘SC_MAKE_FUNC_PTR’ static_cast(&callback_tag::func)

/home/epi/jfrye_xilinx/SystemC/systemc-2.3.2/include/sysc/kernel/sc_module.h:461:5: note: in expansion of macro ‘declare_thread_process’ declare_thread_process( func ## _handle, \

/home/test_benches/adder/test_bench.cc:17:2: note: in expansion of

macro ‘SC_THREAD’ SC_THREAD(run_tests);

make: *** [/home//test_benches/adder/obj/test_bench.o] Error 1

My best guess is that I did not set up the sockets correctly. The test bench has a simple_initiator_socket and the adder has a simple_target_socket. Do I need to register the simple_target_socket with a b_transport method for the module? I did so in the initiator but in the tutorial below I did not see a requirement to do so for the target. My guess was the dataflow was like this:

  1. simple_initiator_socket (member of test_bench) registered to b_transport method of module and simple_target_socket of another module (in top module)
  2. Initiator module (test_bench) sets up tlm_generic_payload with data it needs to send to target (adder)
  3. b_transport method of simple_initiator_socket (member of test_bench) called with tlm_generic_payload being passed (with addends for adder)
  4. Target socket (target) receives and decodes tlm_generic_payload (addend values) that was passed.
  5. Target socket (adder) performs operations (adds decoded addends) and modifies the tlm_generic_payload (passed by value) (by writing the computed sum back to the payload memory)
  6. Initiator (test_bench) looks at modified tlm_generic_payload (now contains sum) and does some process (checks against theoretical sum)

I was trying to follow this example.

https://www.doulos.com/knowhow/systemc/tlm2/tutorial__1/

UPDATE

test_bench.h

class test_bench:
    public sc_core::sc_module
{
public: 
    tlm_utils::simple_initiator_socket<test_bench> socket;
    sc_out<bool> irq;

    test_bench(sc_core::sc_module_name name);
    void run_tests();

private:
    uint32_t data[3];
};

Upvotes: 0

Views: 749

Answers (1)

user1143634
user1143634

Reputation:

There are two ways to declare modules in SystemC.

The first one is through using SC_MODULE macro:

SC_MODULE(mymodule) {
    SC_CTOR(mymodule)
    {
    }
};

And the second one without it:

class mymodule : public sc_core::sc_module {
    SC_HAS_PROCESS(mymodule);
public:
    mymodule(sc_core::sc_module_name)
    { }
};

I would prefer the second one because:

  • It avoids those nasty macros as much as possible.
  • It allows you to inherit from another module.

Now why you need SC_MODULE or SC_HAS_PROCESS macros. The reason is that macros SC_METHOD and SC_THREAD need to know type of module they are being used from to do their job. Since SystemC is based on old revision of C++ language released in 1998, there was no way to do this automatically. So helper macro SC_HAS_PROCESS was defined as:

#define SC_HAS_PROCESS(user_module_name) typedef user_module_name SC_CURRENT_USER_MODULE

This allows SC_METHOD and SC_THREAD to use SC_CURRENT_USER_MODULE as synonym for module they are being used in. Macro SC_MODULE already uses SC_HAS_PROCESS behind the curtain.


Another advise - if you are using C++11 compatible compiler you can declare a helper macro for yourself:

#define DECL(name, ...) name{#name, __VA_ARGS__}

This can help you declare named objects without typing their name twice:

sc_in<bool> DECL(clk);

Any error message mentioning this port with contain proper name for it.

Can also be used in constructor to initialize member field:

mymodule(sc_core::sc_module_name)
    : DECL(clk)
{
}

Upvotes: 1

Related Questions