sc_vector proposal

From: <john.aynsley@doulos.com>
Date: Tue Nov 09 2010 - 05:06:02 PST

Philipp, Everybody,

I have worked through Philipp's sc_vector proposal. I will share some
examples with you, starting with the most basic features, then showing the
clever cases. I have a few comments/questions, highlighted below.

Firstly, you can construct vectors of modules, ports, exports, and
channels, e.g.

        sc_vector<my_module> my_vec; // vector-of-modules
        sc_vector<sc_inout<int> > ports; // vector-of-ports
        sc_vector<sc_port<i_f> > ports; // vector-of-user-defined
ports
        sc_vector<sc_signal<int> > sigs; // vector-of primitive channels

Secondly, you can set the size of the port either at construction time or
deferred until later:
Either
        : ports("ports", N) // "ports" used as the prefix for the
string name,
or
        : ports("ports")
        {
                  ports.init(N); // N is the size of the vector - cannot
be modified once it has been set

Thirdly, you can access the vector elements using the size(), operator[],
and at() methods, e.g.

        for (unsigned int i = 0; i < ports.size(); i++)
                ports[i]->method();

        m1->ports[i].bind( sigs[i] ); // ports and sigs are both
sc_vectors

We are constraining the elements to be sc_objects. In each case, the
elements of the sc_vector are all children of the containing module or
process instance, so are picked up by

        std::vector<sc_object*> children = obj->get_child_objects();

This is all very natural and as-expected.

Then we add a few wrinkles. It is possible to bind a vector-of-ports to a
vector-of-channels directly, with no explicit loop, e.g.

        module_inst->ports.bind( sigs ); // ports is a vector-of-ports
and sigs is a vector-of-sigs

Very nice. It is also possible to bind just a subset of the
vector-of-ports, then come back for another pass later:

        typedef sc_vector<sc_inout<int> > port_type;
        port_type::iterator it;

        // Bind upper half of ports vector to hi_sigs
        it = module_inst->ports.bind( hi_sigs.begin(), hi_sigs.end() ); //
hi_sigs.size() < ports.size()
 
        // Bind lower half of ports vector to lo_sigs
        it = module_inst->ports.bind( lo_sigs.begin(), lo_sigs.end(), it);
  // Notice binding starts from position 'it' within ports vector

The above syntax - having bind return the position of the 1st unbound
element - seems a little arcane to me. Are we all okay with it?

But sometimes, rather than having a vector-of-ports, you might have a
vector-of-modules each containing a single port. In this case it is
possible to treat these ports distributed across multiple modules as one
vector, using sc_view. For example:

        struct Initiator1: sc_module
        {
                sc_port<i_f> port;
                ...
        struct Target: public sc_module, private i_f
        {
                sc_export<i_f> xp;
                ...

        sc_vector<Initiator1> initiator_vec;
        sc_vector<Target> target_vec;

        sc_view(initiator_vec, &Initiator1::port).bind( sc_view
(target_vec, &Target::xp) );

In the above
        sc_view(initiator_vec, &Initiator1::port) creates a
vector-of-ports
        sc_view(target_vec, &Target::xp) creates a vector-of-exports
        .bind() does a vector-to-vector bind

The semantics are very nice, but I am not particularly happy with the name
sc_view, because this is not suggestive of what is actually happening,
that is, gathering together a vector from a set of elements distributed
across another vector. Is there a precedent for the name sc_view, Phillip?
How about something like sc_gather or sc_make_vector or sc_vectorize
instead?

It is possible to extract a std::vector of elements from any sc_vector,
e.g.

        std::vector<sc_object*> elements;
        elements = ports.get_elements();
        elements = sc_view(initiator_vec,
&Initiator1::port).get_elements();

Finally, in the special case of a vector-of-modules, it is possible to
pass extra arguments to the module constructor, e.g.

        struct my_module: sc_module
        {
                 my_module(sc_module_name n, string weird_arg ) {...}
                ...
        ...
        sc_vector<my_module> my_vec;
 
        struct my_module_creator
        {
                my_module_creator( string arg ) : weird_arg(arg) {}
 
                my_module* operator() (const char* name, size_t)
                {
                        return new my_module(name, weird_arg );
                }
                string weird_arg;
        };
        ...
        my_vec.init(N, my_module_creator("The Creator"));

This works, but creating the function object feels like having to jump
through hoops. I think regular users are going to find it confusing. Can
we do any clever tricks to make it easier to use?

Thanks,

John A

 

-- 
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.
Received on Tue Nov 9 05:06:40 2010

This archive was generated by hypermail 2.1.8 : Tue Nov 09 2010 - 05:06:43 PST