【问题标题】:Python: writing to memory in a single operationPython:在单个操作中写入内存
【发布时间】:2019-04-28 18:47:39
【问题描述】:

我正在编写一个用户空间驱动程序,用于在 Python 3.5 中访问 FPGA 寄存器,mmaps FPGA 的 PCI 地址空间,获得一个 memoryview 以提供对内存映射寄存器空间的直接访问,然后使用 @987654324 @ 将 32 位值写入选定的 32 位对齐地址。

def write_u32(address, data):
    assert address % 4 == 0, "Address must be 32-bit aligned"
    path = path.lib.Path("/dev/uio0")
    file_size = path.stat().st_size
    with path.open(mode='w+b') as f:
        mv = memoryview(mmap.mmap(f.fileno(), file_size))
        struct.pack_into("<I", mv, address, data)

不幸的是,appears struct.pack_into 做了一个 memset(buf, 0, ...) 在写入实际值之前清除寄存器。通过检查 FPGA 中的写操作,我可以看到寄存器在设置真值之前设置为 0x00000000,因此 PCI 总线上至少有两次写入(实际上对于 32 位访问,有 3 个,两个 0写入,然后是实际数据。64 位涉及六次写入)。这会对一些计算写入操作次数的寄存器产生副作用,或者一些“写入时清除”或在写入时触发某些事件。

我想使用另一种方法将寄存器数据一次性写入内存映射寄存器空间。我查看了ctypes.memmove,它看起来很有希望(尚未工作),但我想知道是否还有其他方法可以做到这一点。

请注意,使用struct.unpack_from 的寄存器读取 效果很好。

请注意,我还通过使用记录所有访问的 QEMU 驱动程序消除了 FPGA - 在写入数据之前,我看到了相同的双零写入访问。

【问题讨论】:

  • 我研究过使用ctypes,尤其是ctypes.from_bufferctypes.memmove。前者在一定程度上有效,但它会进行初步阅读。后者逐字节写入,因此不合适。我觉得我很接近 - 有没有办法,也许使用 ctypes 指针,对指针引用的地址进行原子写入?

标签: python struct fpga mmap memoryview


【解决方案1】:

也许这会根据需要起作用?

mv[address:address+4] = struct.pack("<I", data)

更新

从 cmets 看,上面的代码并没有解决问题。但是,它的以下变体可以:

mv_as_int = mv.cast('I')
mv_as_int[address/4] = data

不幸的是,准确了解幕后发生的事情以及 memoryview 究竟为何如此行为超出了现代技术的能力范围,因此将继续为未来的研究人员解决问题。

【讨论】:

  • 感谢您的回复。这确实显示了改进 - 而不是 PCI 总线上的三个写入(两个零 + 一个数据写入),它只执行 两个 写入,两者都是数据。我不确定这是否是由于memoryview 的工作方式。但是,我需要找到一种方法将其归结为一次写入。
  • 也许更有趣的是,尝试使用mv[address:address+1] = struct.pack("&lt;B", data) 写入单个字节(到对齐的地址)会导致在总线上写入三个相同的数据。跨度>
  • 我尝试过使用i = ctypes.c_int.from_buffer(mv); i.value = data,这会将正确的数据写入为原子 32 位写入,但由于某种原因,它会在写入之前对同一地址进行 32 位读取。这至少比多次写入要好,但它仍然不理想,因为某些寄存器具有由读取触发的行为,这在典型写入期间会出乎意料。不过越来越近了。
  • mv_as_int = mv.cast(int)mv_as_int[address/4] = data 怎么样?
  • 酷。很高兴知道它有效!很高兴了解这背后的原因,但我没有时间深入研究代码(即使发现它很重要)。也许面向字节的 memoryview 决定以某种方式将写入对齐到 16 位,而 int 大小的版本避免了这种情况。无论如何,我更新了答案。
【解决方案2】:

你可以试试这样的:

 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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-29
    • 2023-04-06
    • 2022-01-07
    • 1970-01-01
    • 2013-01-05
    • 2011-12-04
    • 1970-01-01
    相关资源
    最近更新 更多