diff --git a/src/dev/reg_bank.hh b/src/dev/reg_bank.hh index 32d9058a15..3d8dc576cb 100644 --- a/src/dev/reg_bank.hh +++ b/src/dev/reg_bank.hh @@ -270,6 +270,12 @@ * is an alternative form of update which also takes a custom bitmask, if you * need to update bits other than the normally writeable ones. * + * Similarly, you can set a "resetter" handler which is responsible for + * resetting the register. It takes a reference to the current Register, and + * no other parameters. The "initialValue" accessor can retrieve the value the + * register was constructed with. The register is simply set to this value + * in the default resetter implementation. + * * = Read only bits = * * Often registers have bits which are fixed and not affected by writes. To @@ -554,6 +560,7 @@ class RegisterBank : public RegisterBankBase using WriteFunc = std::function; using PartialWriteFunc = std::function< void (This ®, const Data &value, int first, int last)>; + using ResetFunc = std::function; private: Data _data = {}; @@ -564,6 +571,7 @@ class RegisterBank : public RegisterBankBase WriteFunc _writer = defaultWriter; PartialWriteFunc _partialWriter = defaultPartialWriter; PartialReadFunc _partialReader = defaultPartialReader; + ResetFunc _resetter = defaultResetter; protected: static Data defaultReader(This ®) { return reg.get(); } @@ -587,6 +595,12 @@ class RegisterBank : public RegisterBankBase mask(first, last))); } + static void + defaultResetter(This ®) + { + reg.get() = reg.initialValue(); + } + constexpr Data htoreg(Data data) { @@ -721,6 +735,30 @@ class RegisterBank : public RegisterBankBase return partialWriter(wrapper); } + // Set the callables which handle resetting. + // + // The default resetter restores the initial value used in the + // constructor. + constexpr This & + resetter(const ResetFunc &new_resetter) + { + _resetter = new_resetter; + return *this; + } + template + constexpr This & + resetter(Parent *parent, void (Parent::*nr)(Args... args)) + { + auto wrapper = [parent, nr](Args&&... args) { + return (parent->*nr)(std::forward(args)...); + }; + return resetter(wrapper); + } + + // An accessor which returns the initial value as set in the + // constructor. This is intended to be used in a resetter function. + const Data &initialValue() const { return _resetData; } + /* * Interface for accessing the register's state, for use by the @@ -817,7 +855,7 @@ class RegisterBank : public RegisterBankBase } // Reset our data to its initial value. - void reset() override { get() = _resetData; } + void reset() override { _resetter(*this); } }; private: diff --git a/src/dev/reg_bank.test.cc b/src/dev/reg_bank.test.cc index b4bc969724..4439526e35 100644 --- a/src/dev/reg_bank.test.cc +++ b/src/dev/reg_bank.test.cc @@ -868,6 +868,56 @@ TEST_F(TypedRegisterTest, PartialWriterReaderWriter) EXPECT_EQ(write_value, 0x0344); } +// Use the default resetter for a register. +TEST_F(TypedRegisterTest, DefaultResetter) +{ + BackingType initial_value = reg.get(); + + reg.get() = initial_value + 1; + EXPECT_EQ(reg.get(), initial_value + 1); + + reg.reset(); + + EXPECT_EQ(reg.get(), initial_value); +} + +// Set a custom resetter for a register. +TEST_F(TypedRegisterTest, Resetter) +{ + RegisterBankLE::Register *reg_ptr = nullptr; + + reg.resetter([®_ptr](auto &r) { + reg_ptr = &r; + }); + + reg.reset(); + + EXPECT_EQ(reg_ptr, ®); +} + +// Set a custom resetter for a register which is a class method. +TEST_F(TypedRegisterTest, ResetterMF) +{ + using Reg = RegisterBankLE::Register; + + struct ResetStruct + { + Reg *reg_ptr = nullptr; + + void + resetter(Reg &r) + { + reg_ptr = &r; + } + } reset_struct; + + reg.resetter(&reset_struct, &ResetStruct::resetter); + + reg.reset(); + + EXPECT_EQ(reset_struct.reg_ptr, ®); +} + TEST_F(TypedRegisterTest, Serialize) { std::ostringstream os;