Чтение из 16-битных аппаратных регистров - PullRequest
2 голосов
/ 17 марта 2009

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

Однако для 16-разрядных аппаратных регистров memcpy() вызывает некоторые проблемы. Если я пытаюсь получить доступ к 16-битному аппаратному регистру, используя два 8-битных доступа, старший байт не читается правильно.

Кто-нибудь сталкивался с этой проблемой? Я парень высокого уровня (C # / Java / Python / Ruby), который приближается к аппаратному обеспечению, и это чужая территория.

Какой лучший способ справиться с этим? Я вижу некоторую информацию, в частности, несколько запутанный [для меня] пост здесь . У автора этого поста точно та же проблема, что и у меня, но я не хочу реализовывать решение, не понимая, что я делаю.

Любой свет, который вы можете пролить на этот вопрос, очень ценится. Спасибо!

Ответы [ 7 ]

4 голосов
/ 17 марта 2009

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

// using types from stdint.h to ensure particular size values
// most systems that access hardware registers will have typedefs
// for something similar (for 16-bit values might be uint16_t, INT16U,
// or something)

uint16_t volatile* pReg = (int16_t volatile*) 0x1234abcd;  // whatever the reg address is

uint16_t val = *pReg;  // read the 16-bit wide register

Вот серия статей Дэна Сакса, которые должны дать вам почти все, что вам нужно знать, чтобы иметь возможность эффективно использовать регистры с отображением памяти в C / C ++:

4 голосов
/ 17 марта 2009

Каждый регистр в этом оборудовании представлен как двухбайтовый массив, первый элемент выровнен по двухбайтовой границе (его адрес четный). memcpy () запускает цикл и копирует один байт на каждую итерацию, поэтому копирует из этих регистров следующим образом (все циклы развернуты, char - один байт):

*((char*)target) = *((char*)register);// evenly aligned - address is always even
*((char*)target + 1) = *((char*)register + 1);//oddly aligned - address is always odd

Однако вторая строка работает некорректно по некоторым аппаратным причинам. Если вы копируете два байта за раз вместо одного за раз, вместо этого это делается следующим образом (short int - два байта):

*((short int*)target) = *((short*)register;// evenly aligned

Здесь вы копируете два байта за одну операцию, и первый байт выравнивается равномерно. Поскольку нет отдельного копирования со странно выровненного адреса, это работает.

Модифицированный memcpy проверяет, правильно ли выровнены адреса, и копирует их в куски буксируемых байтов, если они есть.

3 голосов
/ 17 марта 2009

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

  • Поймите, как ваш компилятор C генерирует код, чтобы вы могли использовать соответствующий целочисленный тип для доступа к памяти, или
  • Встроить некоторую сборку, чтобы получить доступ с правильным размером байта или слова.

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

2 голосов
/ 17 марта 2009

Обычно достаточно использовать целочисленный тип того же размера, что и ваш регистр. На большинстве компиляторов короткое замыкание составляет 16 бит.

void wordcpy(short *dest, const short *src, size_t bytecount)
{
    int i;
    for (i = 0;  i < bytecount/2;  ++i)
        *dest++ = *src++;
}
2 голосов
/ 17 марта 2009

Я думаю, что все детали содержатся в той ветке, которую вы разместили, поэтому я постараюсь немного разбить ее;

В частности,

If you access a 16-bit hardware register using two 8-bit
accesses, the high-order byte doesn't read correctly (it
always read as 0xFF for me). This is fair enough since
TI's docs state that 16-bit hardware registers must be
read and written using 16-bit-wide instructions, and
normally would be, unless you're using memcpy() to
read them.

Таким образом, проблема в том, что аппаратные регистры сообщают правильное значение только в том случае, если их значения считываются за одно 16-битное чтение. Это было бы эквивалентно выполнению;

uint16 value = *(regAddress);

Это считывает из адреса в регистр значений, используя одно 16-байтовое чтение. С другой стороны, у вас есть memcpy, который копирует данные по одному байту за раз. Что-то вроде;

while (n--)
{
  *(uint8*)pDest++ = *(uint8*)pSource++;
}

Таким образом, регистры считываются 8 битами (1 байт) за раз, в результате чего значения становятся недействительными.

Решение, опубликованное в этом потоке, состоит в том, чтобы использовать версию memcpy, которая будет копировать данные с использованием 16-битного чтения, где источник и пункт назначения выровнены по 6-битному.

1 голос
/ 17 марта 2009

Не знаю точно, в чем вопрос - я думаю, что у этой статьи правильное решение. Как вы сказали, проблема в том, что стандартная подпрограмма memcpy () считывает байт за раз, что не работает корректно для аппаратных регистров с отображенной памятью. Это ограничение процессора - просто невозможно получить действительное значение, считывающее байт за раз.

Предлагаемое решение - написать свой собственный memcpy (), который работает только с выровненными по словам адресами и читает 16-битные слова за раз. Это довольно просто - ссылка дает версию c и сборку. Единственный недостаток - убедиться, что вы всегда делаете 16-битные копии с правильно выровненного адреса. Вы можете сделать это двумя способами: либо использовать команды компоновщика, либо прагмы, чтобы убедиться, что все выровнено, или добавить специальный случай для дополнительного байта в начале невыровненного буфера.

1 голос
/ 17 марта 2009

Что нужно знать? Вы уже нашли отдельный пост, объясняющий это. Очевидно, что для документации процессора требуется , чтобы 16-разрядные аппаратные регистры были доступны с помощью 16-разрядных операций чтения и записи, но ваша реализация memcpy использует 8-разрядные операции чтения / записи. Так что они не работают вместе.

Решение просто не использовать memcpy для доступа к этому регистру. Вместо этого напишите свою собственную подпрограмму, которая копирует 16-битные значения.

...