Search code examples
c++templatesvivado-hls

Template class with template method id


I am trying to create an identifier for my methods. This identifier is important for my work as it is in fact hardware synthesis.

I have the following template class:

        template <int FF_COUNT, int FB_COUNT, int NEXT_COUNT>
        class Core{
        public:
        ...
            template<int id> void consume_fb_events (hls::stream<event>  feedback_stream [FB_COUNT] [FB_COUNT], weight w_mem [128*128]);
        }
    


template <int FF_COUNT, int FB_COUNT, int NEXT_COUNT>
    template <int id>
    void Core<FF_COUNT, FB_COUNT, NEXT_COUNT>::consume_fb_events (hls::stream<event> feedback_stream [FB_COUNT] [FB_COUNT], weight w_mem [128*128]){
    #pragma HLS INLINE off
    event e;   
            for(int i = 0 ; i < FB_COUNT ; i++) {
                while (!feedback_stream[id][i].empty()) {
                    feedback_stream[id][i].read(e);
                    ap_uint<16> mem_offset = e << size_exp;
                    consume_event (e, mem_offset, w_mem);    
            }
    }
    }

and this is my function call

    #define sth 8
for int i = 0 ; i < sth; i++
    core[i].consume_fb_events<i>(....);

I get the compilation error:

ERROR: [HLS 200-70] Compilation errors found: In file included from c1/srnn.cpp:1:
c1/srnn.cpp:197:14: error: no matching member function for call to 'consume_fb_events'
   core_1[i].consume_fb_events<i>(buffer_layer1_1, w1[i]);
   ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
c1/core.h:52:24: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'id'
 template<int id> void consume_fb_events (hls::stream<event> feedback_stream [FB_COUNT] [FB_COUNT], weight w_mem [128*128]);
                   ^

Solution

  • What your looking for is a for loop at compile time. Because the template parameter must be a constexpr. I usually do it this way since you can't have for loops in constexpr functions:

    template<int i>
    struct MyFunc
    {
        MyFunc()
        {
            // do something with i
            core[i].consume_fb_events<i>(....);
        }
    };
    
    template<int end, template <int I> class func, int i = 0>
    struct ForLoop
    {
        ForLoop()
        {
            func<i>{};
            ForLoop<end, func, i+1>{};
        }
    };
    
    template<int end, template <int I> class func>
    struct ForLoop<end, func, end>
    {
        ForLoop()
        {
        }
    };
    

    You can run any code in the constructor of MyFunc.

    You can then execute it like that:

    ForLoop<8, MyFunc>{};
    

    where 8 is the number you would usually but at the i < ... part of the for loop

    You have to be careful with this, because this will only work for end up to about 900 (depending on the max template recursion depth). Or else you will get a compile time error.

    live example with std::cout

    EDIT:


    Since @SherifBadawy asked in a comment, you don't have to declare a struct/class MyFunc to do this, but I went with this approach because it makes the ForLoop more dynamic and you can reuse it multiple times.

    But if you'd like to this would also work:

    template<int i>
    void foo()
    {
        // code here
    }
    
    template<int end, int i = 0>
    struct ForLoop
    {
        ForLoop()
        {
            core[i].consume_fb_events<i>(....);
            // more code
    
            // or ...
    
            foo<i>();
    
            ForLoop<end, func, i+1>{};
        }
    };
    
    template<int end>
    struct ForLoop<end, end>
    {
        ForLoop()
        {
        }
    };
    

    To run ForLoop you would again do:

    ForLoop<8>{}; // without passing a class or function