2014-10-21 3 views
0

Я пытался реализовать этот подход доступа к регистрам: http://www.drdobbs.com/cpp/register-access-in-c/184401954?pgno=2доступа к регистрам в C++

Это пример кода: В этом примере, я пытаюсь создать два Регистр базы - UART и ШИМ. Оба имеют свой набор регистров (со смещением от базового адреса). Базовый адрес высмеивается для целей тестирования.

Вопрос 1: В статье подробно не говорится о способе обработки нескольких устройств, и является ли моя реализация правильным пониманием? если это так

Вопрос 2 Есть ли способ избежать дублирования кода - функции regWrite, regRead и regAddress? В идеале я хотел бы просто определить пространство имен с базовым адресом и перечислением регистров и повторно использовать функции доступа. Каков наилучший способ достижения этого?

Я получаю ошибку компиляции с помощью regWrite, regRead, regAddress за пределами пространств имен. Он не может распознавать регистры, baseAddress.

ПРИМЕЧАНИЕ: Я пишу код для контроллера 2k RAM с RTOS, работающим поверх него. Поэтому я также заинтересован в том, чтобы размер кода и время выполнения были относительно низкими, но все же улучшали удобочитаемость.

namespace UART{ 
enum Registers { 
    STATUS = 0, RESET = 1 
}; 

unsigned int fakeBase[2]; 
unsigned int baseAddress = (unsigned int)(&fakeBase); 

inline volatile unsigned int *regAddress(Registers Reg){ 
    return reinterpret_cast<volatile unsigned int *> (baseAddress + Reg); 
} 
inline void regWrite(Registers Reg, unsigned int value){ 
    *regAddress(Reg) = value; 
} 

inline volatile unsigned int regRead(Registers Reg){ 
    return *regAddress(Reg); 
} 
} 


namespace PWM{ 
enum Registers { 
    DCA = 0, DCB = 1, DCC = 2 
}; 

unsigned int fakeBase[3]; 
unsigned int baseAddress = (unsigned int)(&fakeBase); 

inline volatile unsigned int *regAddress(Registers Reg){ 
    return reinterpret_cast<volatile unsigned int *> (baseAddress + Reg); 
} 
inline void regWrite(Registers Reg, unsigned int value){ 
    *regAddress(Reg) = value; 
} 

inline volatile unsigned int regRead(Registers Reg){ 
    return *regAddress(Reg); 
} 
} 


int main() { 
    regWrite(UART::STATUS, 12); 
    std::cout << "UART 0 " << regRead(UART::STATUS)<< std::endl; 
    regWrite(PWM::DCA, 34); 
    std::cout << "PWM 0 " << regRead(PWM::DCA)<< std::endl; 
    regWrite(UART::RESET, 13); 
    std::cout << "UART 1 " << regRead(UART::RESET)<< std::endl; 
    return 0; 
} 
+0

Я получаю ошибку компиляции, потому что вы поставили пробел между ':' символами. Пространство имен называется с токеном '::' (без пробела). –

+0

@RichardHodges, я думаю, опрятный (при добавлении кода) вариант испортил пробелы. Я не собирался компилировать, просто хотел отобразить фрагмент кода. – Saaras

+1

@Saaras: Фрагменты для C++ не работают. Я думаю, что в настоящее время это только HTML, CSS и JavaScript. –

ответ

2

Реализация, как «игрушка», будет работать. Разумеется, любая современная ОС запретит приложениям пользовательского режима получать доступ к реальному оборудованию, и вам нужно написать код драйвера, и я думаю, что если вы не используете Windows CE или Windows Phone, вы не можете писать драйверы на C++ в Windows или Linux (хотя оба реализуют драйверы с интерфейсом «стиль наследования»).

Чтобы уменьшить дублирование кода, вы можете реализовать базовый класс для общего доступа к регистру, что-то вдоль этих строк: и использовать классы вместо пространств имен для реализации различия.

template<typename T> class RegisterBase 
{ 
public: 
    RegisterBase(T *location, size_t count) : 
     baseAddress(location), numRegisters(count) 
    {} 

    inline volatile T *regAddress(unsiged int Reg) { 
    assert(Reg < numRegisters); 
    return reinterpret_cast<volatile unsigned int *> (baseAddress + Reg); 
    } 
    inline void regWrite(unsigned int Reg, T value) { 
    *regAddress(Reg)=value; 
    } 
    inline volatile T regRead(unsigned int Reg) { 
    return *regAddress(Reg); 
    } 
private: 
    T* baseAddress; 
    unsigned int numRegisters; 
}; 


class UART : public RegisterBase<unsigned int> 
{ 
public: 
    enum Registers { 
    STATUS=0, RESET=1, 
    NUMREGS = 2 
    }; 

    UART() : RegisterBase(&fakeBase, NUMREGS) {} 

private: 
    unsigned int fakeBase[NUMREGS]; 
}; 


class PWM : public RegisterBase<unsigned short> 
{ 
    enum Registers { 
    DCA=0, DCB=1, DCC=2, 
    NUMREGS = 3 
    }; 

    PWM() : RegisterBase(&fakeBase, NUMREGS) {} 

private: 
    unsigned int fakeBase[NUMREGS]; 
}; 

Теперь вы должны «создать экземпляр» Ваш регистр устанавливает:

PWM pwm; 
UART uart1, uart2; 

uart1.RegWrite(UART::Status, 12); 
pwm.RegWrite(PWM::DCB, 42); 
+0

Слишком много накладных расходов для меня. ИМХО, если у вас будут методы, вы можете также абстрактно и иметь методы для функциональности и не записывать в регистры. Например, «get_status», а не «regRead (4)». –

+1

Я пишу код для встроенного устройства с 2K RAM и RTOS. – Saaras

+0

@ Томас: Где накладные расходы? Вы действительно скомпилировали код, чтобы узнать, что делает компилятор. Я делаю ставку на все, что вам нравится, что gcc или clang не выдадут ни одной инструкции в этом решении [если вы отключите утверждения], чем в исходном размещенном коде. (Это будет пара байтов дольше для 16-разрядного доступа, который я демонстрирую, используя шаблоны в PWM, но это потому, что для 16-разрядного доступа требуется дополнительный префикс, а не из-за C++). Конечно, если у вас есть система RAM 2K, вам, вероятно, нужно переосмыслить идею использования C++ и начать думать о сборщике. Если только нет OODLES ROM. –

1

Существует множество методов моделирования аппаратных устройств. Один из способов заключается в определении структуры со всеми регистрами, а затем поддерживать глобальный указатель на устройство:

struct UART_Registers 
{ 
    uint32_t status; 
    uint32_t transmit_register; 
    uint32_t receive_register; 
}; 

UART_Registers volatile * const p_UART0 = (UART_Registers *) 0xFFFFD000; // The address of the UART registers. 

//... 
    uint32_t transmit_value = (uint32_t)'X'; 
    p_UART0->transmit_register = transmit_value; 

Другой может быть структуры указателей на регистры:

struct UART_Pointers 
{ 
    uint32_t volatile * const status; 
    uint32_t volatile * const transmit; 
    uint32_t volatile * const receive; 
}; 
UART_Pointers UART_0 = 
{ 
    0xFFFFD000, // status 
    0xFFFFD004, // transmit 
    0xFFFFD008, // receive; 
}; 

//... 
    uint32_t receive = *UART_0.receive; 

Третья версия Я видел объявления указателей на каждый регистр как макросы:

#define UART_BASE_ADDR 0xFFFFD000 
#define REGISTER_UART_0_STATUS (UART_BASE_ADDR + 0U) 
#define REGISTER_UART_0_TRANSMIT (UART_BASE_ADDR + 4) 
#define REGISTER_UART_0_RECEIVE (UART_BASE_ADDR + 8) 
//... 
uint32_t status = *REGISTER_UART_0_STATUS; 

Это все мнение.

Мое предпочтение читаемости является первым.

Edit 1: Несколько устройств
Есть система на чипе (SOC), что там есть несколько «экземпляров» аппаратного устройства. Например, SOC может иметь 3 UARTS.

Для этого случая я создал класс UART_Base, который определяет всю общность UART. Для определенных адресов отвечают дочерние классы. Например, UART0 может быть на 0xFFFFD000 и UART1 на 0xFFFFD100 и UART2 на 0xFFFFD200. Различия между дочерними элементами - это адреса аппаратных регистров.

+0

Мне нравится структурный подход, хотя разве это не так, что прокладка может быть нестандартной? – Saaras

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