2015-02-19 3 views
6

Я ищу способ передать регистры встроенных устройств в шаблоны C++, используя gcc 4.8.4. В листах данных, описывающих встроенные устройства, адреса регистров обычно указываются как необработанные ячейки памяти (например, 0x40008000).Регистры как параметр шаблона

Когда я тестирую программное обеспечение, я хочу использовать статические целые числа в качестве регистров, чтобы увидеть, правильно ли установлены значения регистров.

Так в основном обертка вокруг некоторого устройства периферии сводится к классу с его адреса регистров приведены в качестве параметра шаблона:

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

Тестирование работает отлично:

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

Но когда я хочу, чтобы создать экземпляр шаблон с фиксированным листом данных, адрес:

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

gcc жалуется: could not convert template argument '1073774592u' to 'volatile uint32_t* {aka volatile long unsigned int*}. Кланг не жалуется на это.

Если я использую адрес заданные в виде целого числа в качестве параметра шаблона, у меня есть проблемы создания экземпляров шаблона во время моих тестов с адресом из издевались регистры:

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

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

Это приводит к error: conversion from pointer type 'uint32_t* {aka long unsigned int*}' to arithmetic type 'intptr_t {aka int}' in a constant-expression.

Я могу думать о двух решений этого:

1) Использование указателей в качестве параметров шаблона, использовать глобальные переменные, регистры и зафиксировать адрес регистров с некоторым компоновщик сценария магии.

2) Используйте специальные типы регистров, которые имеют общий интерфейс для периферийного шаблона, но две очень разные реализации для тестирования и для реального приложения.

Но я ищу более простой способ выполнить это. Любые идеи, указатели или комментарии?

+1

Имейте в виду, что тип указателя всегда должен быть изменчивым. Это может быть причиной некоторых проблем. – Lundin

+0

Спасибо, что указали это. –

+0

Но это не проблема, которая вызывает ошибку компиляции. Каждый указатель на T должен быть конвертируемым в 'volatile T'. –

ответ

0

Мое решение выглядит следующим образом:

template < class Name = int, typename T = std::uint32_t, T* Value = nullptr > 
class mocked_register 
{ 
public: 
    static void set(T v) 
    { 
     *address() = v; 
    } 

    static T get() 
    { 
     return *address(); 
    } 

    static volatile T* address() 
    { 
     static T internal_; 
     return Value ? Value : &internal_; 
    } 
}; 

Где Имя должно быть любого типа, что делает создание экземпляра отличается от других конкретизации. При определении нескольких типов, можно использовать предыдущие определенные типы как имя:

typedef mocked_register<>    START; 
typedef mocked_register<START>  STOP; 
typedef mocked_register<STOP>  COUNT; 

Для тестирования, типа держит «пол» статические переменным, чтобы сохранить значение регистров. Для архитектуры arm у меня есть некоторые случаи, когда это полезно, использовать массив регистров.В этом случае параметр Value может быть использован для обеспечения внешнего массива:

std::uint32_t capture_regs[ 4 ]; 
typedef mocked_register< SHUTDOWN, std::uint32_t, capture_regs > CAPTURE; 

Для производственной части, шаблон намного проще:

template < std::uint32_t Register > 
struct addressed_register 
{ 
    static void set(std::uint32_t value) 
    { 
     *reinterpret_cast< volatile std::uint32_t* >(Register) = value; 
    } 

    static std::uint32_t get() 
    { 
     return *reinterpret_cast< volatile std::uint32_t* >(Register); 
    } 
}; 

В обоих случаях (испытания и производство) а устройство абстракции принимает набор параметров шаблона и использовать их как регистры:

template < 
    class OUTSET, 
    class OUTCLEAR, 
    class DIRSET, 
    class DIRCLR, 
    class PIN_CNF, 
    std::uint8_t Nr > 
struct pin 
{ 
    static void init_as_input() 
    { 
     DIRCLR::set(1 << Nr); 
    } 
}; 

Более зарегистрировать подобный синтаксис, могут быть добавлены, если назначение и неявное преобразование (но я не большой поклонник этой идеи):

START start; 
COUNT count; 
start = 1; 
std::uint32_t c = count; 
2

Выполнение reinterpret_cast<> не допускается в постоянном выражении (об этом также говорит вам компилятор); см. также Constant expressions .

Я хотел бы предложить следующее (см также 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; 
} 
+0

Можно даже добавить некоторую функцию-член 'std :: uint32_t volatile operator()() const {return reinterpret_cast (Ptr); } 'to' peripheral', чтобы обеспечить общий интерфейс (возможно, плюс некоторый 'static_assert' типа 'PtrT'). – dyp

+0

@ipadadop Спасибо за предложение. Как мне получить доступ к регистру (в процессе производства) и как я могу получить доступ к тестируемому регистру? TIA –

+1

@TorstenRobitzki Я добавил несколько помощников для этого: ':: value' даст вам указатель как' void * 'и статическую функцию' :: as() 'позволяет вам иметь удобный бросок через функцию. Я думаю, что, заставляя любой указатель проходить через void *, я должен позаботиться о любых проблемах с псевдонимом (если не поправьте меня). – ipapadop

Смежные вопросы