Richard <
legaliz...@mail.xmission.com> wrote:
> I've been tinkering around with how C++ can improve embedded
> development. My reference platform is the Nintendo Game Boy Advance.
> It has a bunch of coprocessing circuitry that is controlled through
> memory-mapped registers. I came up with this little header for
> modeling read-only, write-only and read-write registers located at a
> specific address.
> namespace RegisterModel
> {
> template <typename T, volatile T const *const address>
> struct ReadOnlyRegister
> {
> operator T() const { return *address; }
> T operator=(T) = delete;
> };
> template <typename T, volatile T *const address>
> struct WriteOnlyRegister
> {
> operator T() const = delete;
> void operator=(T value) { *address = value; }
> };
> template <typename T, volatile T *const address>
> struct ReadWriteRegister
> {
> operator T() const { return *address; }
> void operator=(T value) { *address = value; }
> };
> }
I've played around with this bit but get a problem when I
try to do e.g,
ReadWriteRegister<uint16_t, 0x1000> rwr;
The compiler complains that it can't convert the integer constant
to 'volatile T *const'. One way around may be to use 'intptr_t' in
the second template parameter and then do the appropriate casts on
it where needed.
Another thing is that the address may not always a compile
time constant, it might be e.g. obtained via a function call
and thus unsuitable as a template parameter.
Here's my take on this, trying to support both cases:
template<typename T>
class RegisterBase
{
protected:
RegisterBase(T * address) : m_address(address) {}
T read() const { return *m_address; }
T write(T value) { return *m_address = value; }
private:
volatile T * m_address;
};
template<typename T>
struct ReadOnlyRegister : public RegisterBase<T>
{
ReadOnlyRegister(intptr_t address)
: RegisterBase<T>(reinterpret_cast<T *>(address))
{}
ReadOnlyRegister(void const * address)
: RegisterBase<T>(const_cast<T *>(reinterpret_cast<T const *>(address)))
{}
operator T () const { return this->read(); }
T operator = (T) = delete;
};
template<typename T>
struct WriteOnlyRegister : public RegisterBase<T>
{
WriteOnlyRegister(intptr_t address)
: RegisterBase<T>(reinterpret_cast<T *>(address))
{}
WriteOnlyRegister(void * address)
: RegisterBase<T>(reinterpret_cast<T *>(address))
{}
operator T () const = delete;
T operator = (T value) { return this->write(value); }
};
template<typename T>
struct ReadWriteRegister : RegisterBase<T>
{
ReadWriteRegister(intptr_t address)
: RegisterBase<T>(reinterpret_cast<T *>(address))
{}
ReadWriteRegister(void * address)
: RegisterBase<T>(reinterpret_cast<T *>(address))
{}
operator T () const { return this->read(); }
T operator = (T value) { return this->write(value); }
};
I.e. the address is not a template parameter but is passed to the
constructor. You thus should be able to use it like this
#defined STAT_FLAGS_ADDR 0x10ea
ReadOnlyRegister<uint16_t> state_flags(STATE_FLAGS_ADDR)
as well as
extern unsigned char * reg_base(void);
#define STATE_FLAGS_OFFSET 0xea
ReadOnlyRegister<uint16_t> state_flags(reg_base() + STATE_FLAGS_OFFSET);
Another possibly interesting addition might be to be able to
set other than the default methods for reading and writing.
On some systems I've worked with certain registers where
"protected" in that you couldn't write directly to them but
this required that you first wrote certain values to some
other register with a certain timing, before a write to the
"real" register had an effect (to protect some important set-
tings fron getting overwritten inadvertently).
> I'm interested to hear if anyone else has done something similar
> and how you approached the problem.
Sorry, got no chance to use C++ in embedded programming yet, it
always had to be C...
Best regards, Jens
--
\ Jens Thoms Toerring ___
j...@toerring.de
\__________________________
http://toerring.de