Skip to content

Commit

Permalink
regmap: Create a Python C extension module for low-level memory mappe…
Browse files Browse the repository at this point in the history
…d IO

This change implements a Python extension module in C to provide access to a
memory mapped region directly via pointers, ensuring that memcpy isn't used on
device memory where side effects can occur.
  • Loading branch information
jranger-es-net committed Jan 17, 2024
1 parent 585128e commit 6652887
Show file tree
Hide file tree
Showing 6 changed files with 422 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
build/
dist/
poetry.lock
setup.py
__pycache__
14 changes: 14 additions & 0 deletions build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from setuptools import Extension

#---------------------------------------------------------------------------------------------------
ext_modules = [
Extension(name='regio.regmap.io.mmap_ext',
sources=['src/regio/regmap/io/mmap_ext.c'],
extra_compile_args=['-Wall', '-Werror', '-Wextra', '-Wno-conversion']),
]

#---------------------------------------------------------------------------------------------------
def build(setup_kwargs):
setup_kwargs.update({
'ext_modules': ext_modules,
})
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ regio-flatten = "regio.tools.flatten:main"
regio-generate = "regio.tools.generate:main"
regio-info = "regio.tools.info:main"

[tool.poetry.build]
script = "build.py"
generate-setup-file = true

[build-system]
requires = ["poetry-core"]
requires = ["poetry-core", "setuptools"]
build-backend = "poetry.core.masonry.api"
1 change: 1 addition & 0 deletions src/regio/regmap/io/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mmap_ext.cpython-*.so
49 changes: 44 additions & 5 deletions src/regio/regmap/io/mmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
from . import io
from ..spec import info

try:
from . import mmap_ext
except ImportError:
mmap_ext = None

#---------------------------------------------------------------------------------------------------
class MmapIO(io.IO):
WORD_CTYPES = {
Expand Down Expand Up @@ -61,13 +66,13 @@ def __init__(self, path, data_width,
self.mmap_size = mmap_size - self.page_no * mmap.PAGESIZE

# Get the C type for accessing a single data word.
endian = endian.get()
self._word_ctype = self.WORD_CTYPES[octets][endian]
self.endian = endian.get()
self._word_ctype = self.WORD_CTYPES[octets][self.endian]

# Get the largest valid C type for accessing words in bulk.
psize = struct.calcsize('P') # Size in bytes of C pointer.
if psize > octets and psize in self.WORD_CTYPES:
self._bulk_ctype = self.WORD_CTYPES[psize][endian]
self._bulk_ctype = self.WORD_CTYPES[psize][self.endian]
else:
self._bulk_ctype = self._word_ctype

Expand Down Expand Up @@ -175,13 +180,47 @@ def update(self, offset, size, clr_mask, set_mask):
count -= 1

#---------------------------------------------------------------------------------------------------
class DevMmapIO(MmapIndirectIO): ...
# Use the C extension module if present. If not, fall back to using indirect IO via ctypes.
if mmap_ext is None:
import logging
logging.warning(
f'{__name__}: Falling back to indirect mmap IO. Build C extension for direct IO support.')

MmapDirectIO = MmapIndirectIO
else:
class MmapDirectIO(MmapIO):
def start(self):
if not self.started:
# Setup the mapping.
super().start()

# Instantiate a direct IO object from the C extension.
self._direct_io = mmap_ext.MmapDirectIO(
self._base_addr, self.word_width, self.bulk_width,
self.endian == io.Endian.LITTLE)

def stop(self):
if self.started:
del self._direct_io
super().stop()

def read(self, offset, size):
return self._direct_io.read(offset, size)

def write(self, offset, size, value):
self._direct_io.write(offset, size, value)

def update(self, offset, size, clr_mask, set_mask):
self._direct_io.update(offset, size, clr_mask, set_mask)

#---------------------------------------------------------------------------------------------------
class DevMmapIO(MmapDirectIO): ...
class DevMmapIOForSpec(DevMmapIO):
def __init__(self, spec, path, *pargs, **kargs):
super().__init__(path, info.data_width_of(spec), *pargs, **kargs)

#---------------------------------------------------------------------------------------------------
class FileMmapIO(MmapIndirectIO):
class FileMmapIO(MmapDirectIO):
def __init__(self, path, file_size, *pargs, **kargs):
super().__init__(path, *pargs, mmap_size=file_size, **kargs)
self.file_size = file_size
Expand Down
Loading

0 comments on commit 6652887

Please sign in to comment.