0

I'm working on modelling some hardware in SystemC (although that's not relevant to the question). The goal is to be able to declare a bunch of registers in a block (class) which are used directly in the class implementation. The external software/firmware will access the registers through a register map to decode based on address. So the HW block as part of its constructor will initialize the register map (not shown below). The issue arises from the fact that some registers need to trigger an action. To do this is seems appropriate to have method in the HW class that is called if the register is written.

The simplified class hierarchy is as follows

class regBase {
public:
    regBase(uint64_t _address, std::string _name) :
        m_address(_address),
        m_name(_name) { }
    regBase() {};
    void setAddress(uint64_t _address) { m_address = _address;}
    virtual void write(uint32_t newval) = 0;  //virtual methods to be overridden in template
    virtual uint32_t read(void) = 0;
    virtual int size(void) = 0;
private:
    uint64_t m_address;
    std::string m_name;
};

template <class REG>
class hwRegister : public regBase 
{
public:
    uint32_t read(void) override
    {
        return m_val.value();
    }
    void write(uint32_t newval) override
    {
        m_val.setValue(newval);
    }
    int size(void) override
    {
        return (m_val.size());
    }
private:
    REG m_val;
};

typedef std::function<void(uint64_t, uint32_t)> writeCallback_t;
struct reg_entry {
    reg_entry(int _size, regBase *_reg) :
       m_reg(_reg), m_size(_size) {}
    reg_entry() {}
    regBase *m_reg;
    int m_size;
    writeCallback_t m_callback;   
};

typedef boost::ptr_map<int, std::shared_ptr<reg_entry> > reg_map_t;
class RegMap {
public:
    void write(uint64_t address, uint32_t val) {

        auto it = m_register_map.find(address);
        if (it==m_register_map.end())
        {
            BOOST_ASSERT_MSG(false, "Invalid address");
        }
        auto entry = *it->second;
        entry->m_reg->write(val);
        if (entry->m_callback)
            entry->m_callback(address, val);
    };
    void setCallback(uint64_t address, writeCallback_t newcallback)
    {
        auto it = m_register_map.find(address);
        if (it==m_register_map.end())
        {
            BOOST_ASSERT_MSG(false, "Invalid address");
        }
        (*it->second)->m_callback = newcallback;
    };
    void addReg(uint64_t address, regBase *reg) {
        auto entry = std::make_shared<reg_entry>(reg->size(), reg);
        entry->m_callback = nullptr;
        entry->m_reg = reg;

        m_register_map[address] = entry;
    }
private:
    reg_map_t m_register_map;

};

I want to in the implementation of HW block, add myReg to myMap and be able to add a callback if necessary for a given register. The issue is around the callback. Does the declaration of the callback look right? Do I need to use std::bind and placeholders? Ok, updated with compiling code.

Andy Tomlin
  • 113
  • 1
  • 7
  • 1
    Drop the star in front of `newcallback` in `setCallback` declaration, and I expect it'd work (once you add all the missing semicolons). – Igor Tandetnik Feb 05 '22 at 19:43
  • `register` is a keyword in C++. You can't use it as name for a class. The compiler should be giving you some potentially weird errors for this. – user17732522 Feb 05 '22 at 19:58
  • 1
    @IgorTandetnik `myCallback` is a non-static member function. It wont work directly. – user17732522 Feb 05 '22 at 20:03
  • Does this answer your question? [Using generic std::function objects with member functions in one class](https://stackoverflow.com/questions/7582546/using-generic-stdfunction-objects-with-member-functions-in-one-class) Note that since C++11 the methods using lambdas are preferred over `std::bind`. Also read the less-upvoted answers and the upvoted comments under the answers. – user17732522 Feb 05 '22 at 20:04
  • 1
    @user17732522 Ah, right. Make it `myMap->setCallback(0, [this](uint64_t address, uint32_t val) { myCallback(address, val); });` – Igor Tandetnik Feb 05 '22 at 20:06
  • Sorry about the syntax errors (semicolons etc), Im trying to figure out direction on this and havent compiled this obviously. – Andy Tomlin Feb 05 '22 at 20:13
  • I'll try the suggestions and reply – Andy Tomlin Feb 05 '22 at 20:16
  • I updated the code above with compiling code – Andy Tomlin Feb 07 '22 at 13:46
  • @IgorTandetnik your suggestion worked, can you please explain it – Andy Tomlin Feb 07 '22 at 14:10
  • 1
    @AndyTomlin Are you at all familiar with [lambda expressions](https://en.cppreference.com/w/cpp/language/lambda)? – Igor Tandetnik Feb 07 '22 at 15:34
  • @IgorTandetnik not in C++, but that answers my question. Thanks for the help – Andy Tomlin Feb 07 '22 at 15:37
  • 1
    Anyway, `std::bind` should work, too. Something like `std::bind(&MyClass::myCallback, this, _1, _2)` in place of the lambda. You decide which way looks nicer to you. – Igor Tandetnik Feb 07 '22 at 15:39
  • I think the lambda function is easier to read than the bind with placeholders etc. Ill just document it better so others can follow the pattern in the code. – Andy Tomlin Feb 07 '22 at 15:41

1 Answers1

0

[this](uint64_t address, uint32_t val) { myCallback(address, val); } worked

struct my_reg_st {
    uint64_t data;
    uint64_t size(void) { return 8; };
    void setValue(uint32_t val) { data = val; };
    uint64_t value(void) {return data; };
};

class test {
public:
    test() {        
        myMap.addReg(0, &myReg);
        // use lambda function for callback. Note that use of intermediate f is only for readability
        writeCallback_t f = [this](uint64_t address, uint32_t val) { myRegCallback(address, val); };
        myMap.setCallback(0, f);
    }
    void myRegCallback(uint64_t address, uint32_t value);
    void write(uint64_t address, uint32_t val) { myMap.write(address, val); };
private:
    hwRegister<my_reg_st> myReg;
    RegMap myMap;
};
Andy Tomlin
  • 113
  • 1
  • 7