使用Python的mmap强制进行32位访问?

5

我在一台64位的ARM处理器上运行64位的Python。这个处理器上的AXI总线连接了一个FPGA(将总线和时钟域转换为32位宽)。但是,这个硬件不支持64位访问...

我正在尝试通过Python mmap方式(在类中)访问这个FPGA:

def __init__(self, base, len):
    self.base = base
    self.fd = open("/dev/mem", "r+")
    self.lw = mmap.mmap(self.fd.fileno(),
                        len,
                        mmap.MAP_SHARED,
                        mmap.PROT_READ | mmap.PROT_WRITE,
                        offset=base)

def get_U32(self, offset):
    s = self.lw[offset:offset+4]
    return struct.unpack("<I", s)[0]

想法是get_U32()从总线中读取32位的字(因此偏移量为offset到offset+4)。 不幸的是,似乎mmap无论如何都执行64位访问总线(我假设这是一种性能优化的缓存),然后执行32位"转换"。底层FPGA不高兴... 在C程序中,我只需编写:
data = *((uint32_t *) address);

目前CPU在其AXI总线上似乎轻松执行一个32位访问,这是底层硬件所偏爱的...(因此,我现在有一个(缓慢的)解决方法,Python需要一个C程序通过管道与硬件进行接口交互)

是否有一种方法强制64位Python执行32位访问,就像以前的C行显然成功了一样?

这里写的问题是关于读取32位,但当然,写入32位也是必需的...


你确定吗?在FPGA上,通过使用C程序查看最终的32位总线,我只看到一个 strobe,而使用Python时我看到了2个 strobe:因此我的假设是C程序只访问32位。据我所知,AXI上的一些信号允许比最大64位更短的访问。也许我错了,因为我看不到那个总线,但是C和Python的访问在远端明显不同。 - user1159290
如果这个C程序是64位的,它仍然使用64位来读取地址。假设它与总线大小有关并没有太多意义。此外,应用程序中进行的所有操作都在其虚拟内存空间中完成,您甚至不需要处理物理内存地址。 - Havenard
mmap 使用操作系统来访问内存。C 代码直接通过地址访问 32 位数量。你正在比较苹果和橙子。 - martineau
1
你可以尝试使用这个答案中展示的di()函数来实现类似的功能。你也可以尝试使用32位版本的Python解释器来运行你的脚本。 - martineau
这个C程序当然也使用了mmap: fd = open("/dev/mem", O_RDWR | O_SYNC) map_base_axi = mmap(0, map_sz[1], PROT_READ | PROT_WRITE, MAP_SHARED, fd, map_addr);然后,我引用的那一行。 - user1159290
显示剩余6条评论
2个回答

2

根据@martineau的想法,可以使用Python ctypes来固定双探针,如下:

Original Answer翻译成"最初的回答"

s = ctypes.c_uint32.from_buffer(self.lw, offset).value #read

或者

types.c_uint32.from_buffer(self.lw, offset).value = s #write

这确实会强制Python执行与C中相同的32位访问,并去除在32位总线上进行的双重读取或写入探测。

然而,遗憾的是,Python似乎在每次写入之前都要先读取一次。 因此,上述解决方案对于读取来说非常有效,但当进行写操作时,仍然会在写入之前发生读取。在C中,当写入时我可以只进行单次写入操作。

我发布这篇文章是希望能对其他可能感兴趣的人有所帮助。如果您有解决这个最后问题(写入之前的读取)的方法,请在下面发布。

最初的回答:这段代码确实能够使Python执行与C相同的32位访问,并消除在32位总线上的双重读取或写入检测。但是很遗憾,在写操作之前,Python似乎需要进行一次读取。因此,这个解决方案适用于读取操作,但在写入操作时,仍然需要进行读取操作。如果您有办法解决这个问题,请在下面留言告诉我们。

1
我们在解决同样的问题时,从这个答案中使用了memoryview解决方案,效果很好。 - Colm Ryan

0

关于这个问题的解决方案thread

你可以尝试像这样做:

 def __init__(self,offset,size=0x10000):
    self.offset = offset
    self.size = size
    
    mmap_file = os.open('/dev/mem', os.O_RDWR | os.O_SYNC)
    mem = mmap.mmap(mmap_file, self.size,
                    mmap.MAP_SHARED,
                    mmap.PROT_READ | mmap.PROT_WRITE,
                    offset=self.offset)
    os.close(mmap_file)
    self.array = np.frombuffer(mem, np.uint32, self.size >> 2)
    
def wread(self,address):
    idx = address >> 2
    return_val = int(self.array[idx])
    return return_val
    
def wwrite(self,address,data):
    idx = address >> 2
    self.array[idx] = np.uint32(data)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接