Jump to content

Binding sc_out type of ports to sc_port<sc_signal_inout_if<T>,N> type of multiports failed


OMark

Recommended Posts

Hello,

I have an example of an initiator with large number of sc_out type of ports (let us say around 400). I need to model a target which should be bound to the different output signals of the initiator and handles their written values internally. To have a more generic and minimal code of such a target I thought that the best way is to have a multiport of type sc_port<sc_signal_inout_if<T>,N>, with number N is the number of outputs that the target should receive.

I tried the following code with N=2 as a probe:

struct test_producer : sc_module{
        sc_out<sc_dt::sc_logic> out1;
    sc_out<sc_dt::sc_logic> out2;
    SC_HAS_PROCESS(test_producer);
    test_producer(::sc_core::sc_module_name){
        SC_THREAD(test_prod);
        dont_initialize();
    }
    void test_prod(){
        while (1){
            out1.write(SC_LOGIC_0);
            out2.write(SC_LOGIC_0);
            wait(10, SC_NS);
        }

    }

};
struct test_module : sc_module {

    SC_HAS_PROCESS(test_module);

    test_module(::sc_core::sc_module_name) {
        SC_THREAD(test_thread);
    }

    sc_port<sc_signal_inout_if<sc_dt::sc_logic>, 2, SC_ZERO_OR_MORE_BOUND> prt_input;
    
    void test_thread() {

        // Create dynamic sensitivity list
        sc_event_or_list any_port;
        for (int i = 0; i<prt_input.size(); ++i) {
            any_port |= prt_input->value_changed_event();
        }
        sc_dt::sc_logic data;
        int id;
        for (;;) {
            wait(any_port);
            for (int i = 0; i < prt_input.size(); ++i) {
                if (prt_input->event()) {
                    data = prt_input->read();
                    id = i;
                    std::cout << std::dec
                        << "From port id " << id
                        << " port[" << i << "] received "
                        << "0x" << std::hex << data
                        << " at " << sc_time_stamp()
                        << std::endl;
                }
            }
        }
        
    }
    
};

int sc_main(int argc, char** argv)
{
    test_producer tprod{ "tprod" };
    test_module tmod{ "tmod" };
    tprod.out1(tmod.prt_input);
    tprod.out2(tmod.prt_input);
    sc_start();
    return 0;
}

 

The code builds successfully, but I got the following error when running it:

grafik.png.d6de322111cd5c02c0683760ca1da8d9.png

 

What I did wrong here and how can I model the interface of such a target efficiently?

Shall I insert for instance any kind of converters between the initiator and the target?

B.T.W. I thought as well about having and sc_vector type of inputs of my target module. But I am not sure how to bind an sc_vector type of port to each sc_out output port of my initiator and if that is even allowed. Any advise in this direction as well?

Thanks in advance for the help.

 

 

Link to comment
Share on other sites

You cannot bind an output port to an input port. Ultimately each port must bind to a signal either hierarchically (this is where port to port binding can is used) or directly.

So you need to define signal for each of the test_module output ports and bind the sc_out and sc_inout ports to it.

2 remarks:

  • you should use sc_in instead of sc_inout to indicate the purpose clearly sc_out is just a child of sc_inout
  • to reduce the number of output ports (and hence signal) you might want to group signals logically belonging together into structs. Those can be used as data typs of signals and ports. This reduces the number of signals and events and increases simulation speed.
Link to comment
Share on other sites

Hello,

Thanks @Eyck for your prompt response.

I corrected the code example as below and the binding works fine now.

#include "stdafx.h"
#include "systemc.h"

struct test_producer : sc_module{
    sc_out<sc_dt::sc_logic> out1;
    sc_out<sc_dt::sc_logic> out2;
    SC_HAS_PROCESS(test_producer);
    test_producer(::sc_core::sc_module_name){
        SC_THREAD(test_prod);
    }
    void test_prod(){
        while (1){
            out1.write(SC_LOGIC_0);
            out2.write(SC_LOGIC_0);
            wait(10, SC_NS);
            out1.write(SC_LOGIC_1);
            wait(30, SC_NS);
            out2.write(SC_LOGIC_1);
        }

    }

};
struct test_module : sc_module {

    SC_HAS_PROCESS(test_module);

    test_module(::sc_core::sc_module_name) {
        SC_THREAD(test_thread);
    }

    sc_port<sc_signal_in_if<sc_dt::sc_logic>, 2, SC_ZERO_OR_MORE_BOUND> prt_input;

    void test_thread() {

        // Create dynamic sensitivity list
        sc_event_or_list any_port;
        for (int i = 0; i<prt_input.size(); ++i) {
            any_port |= prt_input->value_changed_event();
        }
        sc_dt::sc_logic data;
        int id;
        for (;;) {
            wait(any_port);
            for (int i = 0; i < prt_input.size(); ++i) {
                if (prt_input->event()) {
                    data = prt_input->read();
                    id = i;
                    std::cout << std::dec
                        << "From port id " << id
                        << " port[" << i << "] received "
                        << "0x" << std::hex << data
                        << " at " << sc_time_stamp()
                        << std::endl;
                }
            }
        }

    }

};

int sc_main(int argc, char** argv)
{
    sc_signal<sc_dt::sc_logic> sig1;
    sc_signal<sc_dt::sc_logic> sig2;
    test_producer tprod{ "tprod" };
    test_module tmod{ "tmod" };
    tprod.out1(sig1);
    tmod.prt_input(sig1);
    tprod.out2(sig2);
    tmod.prt_input(sig2);
    sc_start();
    return 0;
}

 

But, I am not sure I understood your second remark "to reduce the number of output ports (and hence signal) you might want to group signals logically belonging together into structs. Those can be used as data typs of signals and ports. This reduces the number of signals and events and increases simulation speed. "

I am really interested in having a non-intrusive behaviour of that target module in terms of simulation speed mainly. It would be very helpful if can give an example of such logic groups of ports and signals on target side as you imagine(d) it. In my use case I have the pre-condition, that the initiator module is a 3rd party IP and I cannot restructure it using the logic groups as you proposed. Then, would it even make sense to have logic groups of input ports on target side only?

Thanks in advance.

Link to comment
Share on other sites

My point was more of a generic nature and more related to modeling guidelines. If your initiator/test_producer has many signals some of them might belong 'logically' together as they are written at the same time. E.g in AHB you have HADDR, HTRANS, HBURST, HWRITE, etc.

Instead of declaring them as sc_out<sc_dt::sc_logic> xxx or alike:

sc_out<sc_dt::sc_lv<32>> HADDR;
sc_out<sc_dt::sc_lv<4>> HTRANS;
sc_out<sc_dt::sc_logic> HWRITE;
  . . .

it would make sense to do it this way:

struct ahb_req {
  sc_dt::sc_lv<32> HADDR;
  sc_dt::sc_lv<4> HTRANS;
  sc_dt::sc_logic HWRITE;
    . . .
};

sc_out<ahb_req> ahb_out;

In the first case you have basically 3 signals/events to observe and deal with. In the latter one you only have 1 event.

If you cannot refactor the the producer than doing so just for the receiver is point less. But if you have the option to refactor the initiator then I would recommend to do it in this or a similar way.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...