У меня есть процессорный модуль NVIDIA Tegra TK1 на плате носителя с подключенным к нему слотом PCI-e. В этом слоте PCIe находится плата FPGA, которая предоставляет некоторые регистры и область памяти 64 КБ через PCIe.
На процессоре ARM платы Tegra выполняется минимальная установка Linux.
Я использую / dev / mem и функцию mmap для получения указателей пользовательского пространства на структуры регистров и область памяти 64 КБ.
Отдельные файлы регистров и блок памяти являются назначенными адресами, которые выровнены и не перекрываются по отношению к страницам памяти 4 КБ.
Я явно отображаю целые страницы с помощью mmap, используя результат getpagesize (), который также равен 4096.
Я могу читать / писать из / в эти открытые регистры просто отлично.
Я могу читать из области памяти (64 КБ), выполняя пословное чтение uint32 в цикле for, просто отлично. То есть прочитанное содержание правильное.
Но если я использую std :: memcpy в одном и том же диапазоне адресов, процессор Tegra всегда зависает. Я не вижу сообщения об ошибке, если GDB подключен, я также не вижу ничего в Eclipse, когда пытаюсь перешагнуть через строку memcpy, она просто останавливается. И я должен сбросить процессор, используя кнопку аппаратного сброса, поскольку удаленная консоль зависла.
Это отладочная сборка без оптимизации (-O0) с использованием gcc-linaro-6.3.1-2017.05-i686-mingw32_arm-linux-gnueabihf. Мне сказали, что область 64K доступна побайтно, я не пытался это явно.
Есть ли реальная (потенциальная) проблема, о которой мне нужно беспокоиться, или есть конкретная причина, по которой memcpy не работает и, возможно, не следует использовать в первую очередь в этом сценарии - и я могу просто продолжать использовать мои за петли и ничего об этом не думаешь?
EDIT : наблюдался другой эффект: во фрагменте исходного кода отсутствовал «жизненно важный» printf в цикле копирования for, который был до чтения из памяти. Это удалено, я не вернул действительные данные. Теперь я обновил фрагмент кода, чтобы иметь дополнительное чтение с того же адреса вместо printf, что также дает правильные данные. Путаница усиливается.
Здесь (я думаю) важные выдержки из того, что происходит. С небольшими изменениями, чтобы иметь смысл, как показано, в этой «распушенной» форме.
// void* physicalAddr: PCIe "BAR0" address as reported by dmesg, added to the physical address offset of FPGA memory region
// long size: size of the physical region to be mapped
//--------------------------------
// doing the memory mapping
//
const uint32_t pageSize = getpagesize();
assert( IsPowerOfTwo( pageSize ) );
const uint32_t physAddrNum = (uint32_t) physicalAddr;
const uint32_t offsetInPage = physAddrNum & (pageSize - 1);
const uint32_t firstMappedPageIdx = physAddrNum / pageSize;
const uint32_t lastMappedPageIdx = (physAddrNum + size - 1) / pageSize;
const uint32_t mappedPagesCount = 1 + lastMappedPageIdx - firstMappedPageIdx;
const uint32_t mappedSize = mappedPagesCount * pageSize;
const off_t targetOffset = physAddrNum & ~(off_t)(pageSize - 1);
m_fileID = open( "/dev/mem", O_RDWR | O_SYNC );
// addr passed as null means: we supply pages to map. Supplying non-null addr would mean, Linux takes it as a "hint" where to place.
void* mapAtPageStart = mmap( 0, mappedSize, PROT_READ | PROT_WRITE, MAP_SHARED, m_fileID, targetOffset );
if (MAP_FAILED != mapAtPageStart)
{
m_userSpaceMappedAddr = (volatile void*) ( uint32_t(mapAtPageStart) + offsetInPage );
}
//--------------------------------
// Accessing the mapped memory
//
//void* m_rawData: <== m_userSpaceMappedAddr
//uint32_t* destination: points to a stack object
//int length: size in 32bit words of the stack object (a struct with only U32's in it)
// this crashes:
std::memcpy( destination, m_rawData, length * sizeof(uint32_t) );
// this does not, AND does yield correct memory contents - but only with a preceding extra read
for (int i=0; i<length; ++i)
{
// This extra read makes the data gotten in the 2nd read below valid.
// Commented out, the data read into destination will not be valid.
uint32_t tmp = ((const volatile uint32_t*)m_rawData)[i];
(void)tmp; //pacify compiler
destination[i] = ((const volatile uint32_t*)m_rawData)[i];
}