python-periphery icon indicating copy to clipboard operation
python-periphery copied to clipboard

Add Userspace I/O class?

Open Livius90 opened this issue 1 year ago • 3 comments

Can you consider to implement generic uio in your python periphery? As i see your MMIO implementation can serve the reg mapping for a /dev/uioX, only the IRQ waiting which is needed to implemented for this new class.

See this examples:

  • https://github.com/ikwzm/FPGA-SoC-Linux-Example-1-ZYBO-Z7/blob/master/uio.py
  • https://tuxengineering.com/blog/2020/08/15/Linux-Userspace.html

Livius90 avatar Jul 31 '23 17:07 Livius90

@vsergeev What do you think, is it fine?

import os
from periphery import MMIO

def get_uio_dev(uio_name):
    for dev in os.listdir("/sys/class/uio"):
        with open("/sys/class/uio/" + dev + "/name", "r") as f:
            name = f.readline().strip()
        if name == uio_name:
            return "/dev/" + dev
    return None

def get_uio_size(uio_dev):
    size = None
    with open("/sys/class/uio/" + uio_dev + "/maps/map0/size", "r") as f:
        size = int(f.readline().strip(), 16)
    return size

class UIO(MMIO):
    def __init__(self, name):
        uio_dev = get_uio_dev(name)
        uio_size = get_uio_size(uio_dev)
        super().__init__(0x00, uio_size, uio_dev)
        # _fdesc should be opened in MMIO's __init__() instead
        self._fdesc = os.open(uio_dev, os.O_RDWR | os.O_SYNC)
        
    def enable_irq(self):
        os.write(self._fdesc, bytes([1, 0, 0, 0]))

    def disable_irq(self):
        os.write(self._fdesc, bytes([0, 0, 0, 0]))
        
    def wait_irq(self):
        os.read(self._fdesc, 4)
        
    def close(self):
        os.close(self._fdesc)
        super().close()

Livius90 avatar Aug 07 '23 20:08 Livius90

It's a good idea -- could use a few more getters with the UIO info. One thing to decide is whether or not to abstract away the interrupt counts -- e.g. should wait_irq() return the raw interrupt counter or the number of interrupts that occurred since the last wait.

vsergeev avatar Aug 16 '23 06:08 vsergeev

It's a good idea -- could use a few more getters with the UIO info. One thing to decide is whether or not to abstract away the interrupt counts -- e.g. should wait_irq() return the raw interrupt counter or the number of interrupts that occurred since the last wait.

What getter you think is need more?

Implementation for return missed interrupts: https://www.kernel.org/doc/html/v4.17/driver-api/uio-howto.html#how-uio-works

import os
from periphery import MMIO

def get_uio_dev(uio_name):
    for dev in os.listdir("/sys/class/uio"):
        with open("/sys/class/uio/" + dev + "/name", "r") as f:
            name = f.readline().strip()
        if name == uio_name:
            return "/dev/" + dev
    return None

def get_uio_size(uio_dev):
    size = None
    with open("/sys/class/uio/" + uio_dev + "/maps/map0/size", "r") as f:
        size = int(f.readline().strip(), 16)
    return size

class UIO(MMIO):
    def __init__(self, name):
        uio_dev = get_uio_dev(name)
        uio_size = get_uio_size(uio_dev)
        super().__init__(0x00, uio_size, uio_dev)
        # _fdesc should be opened in MMIO's __init__() instead
        self._fdesc = os.open(uio_dev, os.O_RDWR | os.O_SYNC)
        self.irq_total_count = 0
        self._irq_last_count = 0
        
    def enable_irq(self):
        os.write(self._fdesc, bytes([1, 0, 0, 0]))

    def disable_irq(self):
        os.write(self._fdesc, bytes([0, 0, 0, 0]))
        
    def wait_irq(self):
        self.irq_total_count = os.read(self._fdesc, 4)
        irq_count = self.irq_total_count - self._irq_last_count
        self._irq_last_count = self.irq_total_count
        
        # return 1 or number of missed interrupts
        return irq_count
        
    def close(self):
        os.close(self._fdesc)
        super().close()

Livius90 avatar Aug 17 '23 22:08 Livius90