Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Registers as template parameter

Tags:

c++

gcc

embedded

I'm looking for a way to pass embedded device registers to C++ templates, using gcc 4.8.4. In data sheets describing the embedded devices, the addresses of the registers are usually given as raw memory locations (0x40008000 for example).

When I test the software, I want to use static integers as registers to see if register values are set correctly.

So basically a wrapper around some device peripheral boils down to a class with it's register addresses given as template parameter:

template < volatile std::uint32_t* Reg >
struct peripheral {};

Testing works fine:

std::uint32_t reg;
peripheral< &reg > mocked;

But when I want to instantiate the template with a fixed, data sheet given address:

peripheral< reinterpret_cast< std::uint32_t* >( 0x40008000 ) > mocked;

gcc complains: could not convert template argument '1073774592u' to 'volatile uint32_t* {aka volatile long unsigned int*}. clang doesn't complain about this.

If I use the address given as integer as template parameter, I have problems to instantiate the template during my tests with the address of the mocked registers:

template < std::intptr_t Reg >
struct peripheral {};

std::uint32_t reg;
peripheral< reinterpret_cast< std::intptr_t >( &reg ) > mocked;

This results in error: conversion from pointer type 'uint32_t* {aka long unsigned int*}' to arithmetic type 'intptr_t {aka int}' in a constant-expression.

I can think of two solutions to this:

1) Use pointers as template parameters, use global variables as registers and fix the address of the registers with some linker script magic.

2) Use special register types that have a common interface to the peripheral template but two very different implementations for testing and for the real application.

But I'm looking for an easier way to accomplish this. Any ideas, pointers or comments?

like image 901
Torsten Robitzki Avatar asked Feb 19 '15 10:02

Torsten Robitzki


1 Answers

Doing a reinterpret_cast<> is not allowed in a constant expression (that's what the compiler also tells you); see also Constant expressions .

I would suggest the following (see also C++11 constexpr function's argument passed in template argument):

#include <cstdint>
#include <cassert>

template<typename PtrT, PtrT Ptr>
struct peripheral
{
  static void* const value;

  template<typename T>
  static T* as() noexcept
  { return static_cast<T*>(value); }
};

#define PERIPHERAL(addr) peripheral<decltype((addr)), (addr)>

std::uint32_t reg = 0;

int main() {
   peripheral<std::uintptr_t, 0x42> mocked1;
   peripheral<volatile std::uint32_t*, &reg> mocked2;
   PERIPHERAL(0x42) mocked3;
   PERIPHERAL(&reg) mocked4;
   assert((mocked3.value == PERIPHERAL(0x42)::as<void*>()));
   return 0;
}
like image 89
ipapadop Avatar answered Oct 05 '22 06:10

ipapadop