C++ 在mmap和x27上使用memcpy;ed区域崩溃,for循环不存在

C++ 在mmap和x27上使用memcpy;ed区域崩溃,for循环不存在,c++,linux,fpga,pci-e,memory-mapping,C++,Linux,Fpga,Pci E,Memory Mapping,我有一个NVIDIA Tegra TK1处理器模块,在一个载体板上,有一个PCI-e插槽连接到它。在该PCIe插槽中有一块FPGA板,该板通过PCIe公开一些寄存器和一个64K内存区域 在Tegra板的ARM CPU上,正在运行最低限度的Linux安装 我使用/dev/mem和mmap函数获取指向寄存器结构和64K内存区域的用户空间指针。 不同的寄存器文件和内存块都是分配的地址,这些地址与4KB内存页对齐且不重叠。 我使用getpagesize()的结果,也就是4096,显式地使用mmap映射整

我有一个NVIDIA Tegra TK1处理器模块,在一个载体板上,有一个PCI-e插槽连接到它。在该PCIe插槽中有一块FPGA板,该板通过PCIe公开一些寄存器和一个64K内存区域

在Tegra板的ARM CPU上,正在运行最低限度的Linux安装

我使用/dev/mem和mmap函数获取指向寄存器结构和64K内存区域的用户空间指针。 不同的寄存器文件和内存块都是分配的地址,这些地址与4KB内存页对齐且不重叠。 我使用getpagesize()的结果,也就是4096,显式地使用mmap映射整个页面

我可以读取/写入暴露的寄存器。 我可以读取内存区域(64KB),在for循环中逐字读取uint32,很好。即阅读内容正确

但是,如果在同一地址范围内使用std::memcpy,Tegra CPU总是会冻结。我没有看到任何错误消息,如果连接了GDB,当我试图跨过memcpy行时,我在Eclipse中也没有看到任何东西,它只是很难停止。我必须使用硬件重置按钮重置CPU,因为远程控制台被冻结

这是使用gcc-linaro-6.3.1-2017.05-i686-mingw32_arm-linux-gnueabihf进行的无优化调试构建(-O0)。我被告知64K区域是可访问字节的,我没有明确尝试

是否有一个实际的(潜在的)问题我需要担心,或者是否有一个具体的原因为什么memcpy不工作,并且可能不应该在这个场景中首先使用它-我可以继续使用for循环而不去想它

编辑:观察到另一个影响:原始代码段在复制for循环中缺少一个“重要”printf,该printf出现在内存读取之前。删除后,我无法恢复有效数据。现在,我更新了代码片段,从同一地址读取了一个额外的数据,而不是printf,这也会生成正确的数据。混乱加剧

这里是(我认为)正在发生的事情的重要摘录。如图所示,只需稍作修改,就可以使用这种“去绒毛”形式

//void*PhysicalAddress:dmesg报告的PCIe“BAR0”地址,添加到FPGA内存区域的物理地址偏移量中
//长大小:要映射的物理区域的大小
//--------------------------------
//做内存映射
//
const uint32_t pageSize=getpagesize();
断言(IsPowerOfTwo(pageSize));
const uint32_t physadadrnum=(uint32_t)physicalAddr;
const uint32\u t offsetingpage=physAddrNum&(pageSize-1);
const uint32_t firstMappedPageIdx=物理量/页面大小;
constuint32\u t lastMappedPageIdx=(物理量+大小-1)/pageSize;
const uint32\u t MappedPageScont=1+lastMappedPageIdx-firstMappedPageIdx;
const uint32\u t mappedSize=MappedPageScont*页面大小;
const off_t targetOffset=physAddrNum&~(off_t)(页面大小-1);
m_fileID=open(“/dev/mem”,O_RDWR | O_SYNC);
//addr作为null传递意味着:我们提供要映射的页面。提供非空的addr意味着,Linux将其作为“提示”放置在何处。
void*mapAtPageStart=mmap(0,mappedSize,PROT_READ | PROT_WRITE,MAP_SHARED,m_fileID,targetOffset);
if(MAP_失败!=mapAtPageStart)
{
m_userSpaceMappedAddr=(volatile void*)(uint32_t(mapAtPageStart)+offsetingpage);
}
//--------------------------------
//访问映射内存
//

//void*m_rawData:根据描述,您的FPGA代码似乎没有正确响应从FPGA上的位置读取的加载指令,导致CPU锁定。它没有崩溃,它是永久性的停滞,因此需要硬复位。在FPGA上调试PCIE逻辑时,我也遇到了这个问题

另一个表明您的逻辑没有正确响应的迹象是您需要额外的读取才能获得正确的响应

您的循环正在进行32位加载,但memcpy正在进行至少64位加载,这会改变您的逻辑响应方式。例如,如果完成的前128位和第二个128位TLP中的下32位为完成,则需要使用两个具有32位响应的TLP

我发现超级有用的是添加逻辑以将所有PCIE事务记录到SRAM中,并能够转储SRAM以查看逻辑的行为或异常。我们有一个漂亮的实用程序,每行打印一个PCIE TLP。甚至有

当PCIE接口工作得不够好时,我将日志以十六进制的形式传输到UART,该UART可由pcieflat解码

这个工具对于调试性能问题也很有用——您可以查看DMA读写的流水线化程度


或者,如果您在FPGA上有集成逻辑分析仪或类似设备,则可以通过这种方式跟踪活动。但最好是根据PCIE协议解析TLP。

如果您能展示一些您正在做的代码,那就太好了。欢迎访问stackoverflow.com。请花些时间来阅读,采取,阅读有关,并阅读。最后,请学习如何创建断点。如果将断点放入
memcpy
,并逐个指令执行,会发生什么情况?它会崩溃吗?如果是,指令是什么,它崩溃的寄存器值是什么?您不一定能够使用任意汇编指令访问映射到外部硬件的内存。有时32位宽的访问是正常的,而字节宽的访问或64位宽的访问则不是。您需要了解您的硬件限制。我曾经使用过这样的设备,只有DWORD指令给出了正确的结果。geza:我现在在memcpy中执行指令10分钟,我的两个手指现在很累;)(一个用于按步骤,一个用于按打印)它做了很多事情,并在没有崩溃的情况下跳跃,所以我还不知道它最终会在哪个指令下崩溃
// 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];
}