Вопрос

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

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

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

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

Мы будем очень признательны за любой свет, который вы можете пролить на этот вопрос.Спасибо!

Это было полезно?

Решение

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

// 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++:

Другие советы

Каждый регистр в этом оборудовании представлен в виде двухбайтового массива, первый элемент выровнен по двухбайтовой границе (его адрес четен). 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 проверяет, правильно ли выровнены адреса, и копирует их в куски буксируемых байтов, если они есть.

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

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

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

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

void wordcpy(short *dest, const short *src, size_t bytecount)
{
    int i;
    for (i = 0;  i < bytecount/2;  ++i)
        *dest++ = *src++;
}

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

Конкретно;

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 битам.

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top