6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python と Numpy で UIO を制御

Last updated at Posted at 2017-11-27

はじめに

UIO とは

UIO はユーザー空間でデバイスドライバを作成する仕組みです。

ユーザー空間で UIO を利用する際は、/dev/uio0 を open して mmap すると、デバイスのレジスタ空間が見えます。この記事では、Python と numpy を使って UIO のデバイスレジスタをアクセスする方法を示します。

Uio クラス

必要なライブラリをインポート

Uio クラスでは numpy、mmap、os、glob、re パッケージを使うのでインポートします。

uio.py
import numpy as np
import mmap
import os
import glob
import re

初期化メソッド

デバイス名による初期化

初期化時にデバイス名(例えば 'uio0''uio1' )を指定します。オプションでレジスタ空間の大きさをバイト数で指定できます。省略時は 0x1000(4KByte)です。初期化時に指定されたデバイス名のデバイスドライバを open し、指定された範囲を mmap.mmap でマッピングします。

uio.py
class Uio:
    """A simple uio class"""

    def __init__(self, name, length=0x1000):
        self.name        = name
        self.device_name = '/dev/%s' % self.name
        self.device_file = os.open(self.device_name, os.O_RDWR | os.O_SYNC)
        self.length      = length
        self.memmap      = mmap.mmap(self.device_file,
                                     self.length,
                                     mmap.MAP_SHARED,
                                     mmap.PROT_READ | mmap.PROT_WRITE,
                                     offset=0)

分かりやすい名前による初期化

uio は、通常、'uio0''uio1' などの uio + デバイス番号 みたいなのがデバイス名になります。しかし uio が多くなると、このようなデバイス名では使いたい uio デバイスを特定するのが困難になります。通常 uio には、'uio0' などのデバイス名の他に、デバイスツリーなどで指定した名前を持っています。
例えば次のデバイスツリーで uio を作成すると、

/dts-v1/; /plugin/;
/ {
	fragment@0 {
		target-path = "/amba_pl@0";
		#address-cells = <2>;
		#size-cells = <2>;

		__overlay__ {
			#address-cells = <2>;
			#size-cells = <2>;

			negative-uio {
				compatible = "generic-uio";
				reg = <0x0 0xA0010000 0x0 0x10000>;
				interrupt-parent = <&gic>;
				interrupts = <0 89 4>;
			};
                };
	} ;
} ;

/sys/class/uio/uioX/name (uioのデバイス番号Xは任意の整数)に デバイスツリーのノード名(この例では negative-uio)がセットされます。

shell$ cat /sys/class/uio/uio4/name
negative-uio
shell$

そこで、初期化時には名前を指定して、その名前を持つ uio を検索して初期するようにしましょう。

uio.py
class Uio:
    """A simple uio class"""

    @staticmethod
    def find_device_file(device_name):
        device_file = None
        r = re.compile("/sys/class/uio/(.*)/name")
        for uio_device_name_file in glob.glob("/sys/class/uio/uio*/name"):
            f = open(uio_device_name_file, "r")
            uio_device_name = f.readline().strip()
            if uio_device_name == device_name:
                m = r.match(uio_device_name_file)
                device_file = m.group(1)
            f.close()
        return device_file
        
    def __init__(self, name, length=0x1000):
        self.name        = name
        self.device_name = '/dev/%s' % Uio.find_device_file(self.name)
        self.device_file = os.open(self.device_name, os.O_RDWR | os.O_SYNC)
        self.length      = length
        self.memmap      = mmap.mmap(self.device_file,
                                     self.length,
                                     mmap.MAP_SHARED,
                                     mmap.PROT_READ | mmap.PROT_WRITE,
                                     offset=0)

割り込み関連のメソッド

UIO にはデバイスドライバに直接ライト/リードすることで割り込みを制御出来ます。詳しくは次の記事を参照してください。

uio.py
    def irq_on(self):
        os.write(self.device_file, bytes([1, 0, 0, 0]))

    def irq_off(self):
        os.write(self.device_file, bytes([0, 0, 0, 0]))
        
    def wait_irq(self):
        os.read(self.device_file, 4)

レジスタアクセス用のオブジェクトを生成

UIO のデバイスレジスタをアクセスするための Uio.Regs クラスのインスタンスを生成します。Uio.Regs に関しては次節を参照してください。
インスタンスを生成する際、レジスタの開始アドレスとレジスタ空間の大きさを指定することが出来ます。ここで指定するレジスタの開始アドレスは、UIO で設定された物理アドレスからのオフセット値です。

uio.py
    def regs(self, offset=0, length=None):
        if length == None:
            length = self.length
        if offset+length > self.length:
            raise ValueError("region range error")
        return Uio.Regs(self.memmap, offset, length)

Uio.Regs クラス

mmap.mmap() でマッピングしている空間を numpy の frombuffer() を使って numpy の配列にします。レジスタへのアクセスは、ここで作った numpy の配列にアクセスすることで行います。

uio.py
    class Regs:
        
        def __init__(self, memmap, offset, length):
            self.memmap     = memmap
            self.offset     = offset
            self.length     = length
            self.word_array = np.frombuffer(self.memmap, np.uint32, self.length>>2, self.offset)
            self.byte_array = np.frombuffer(self.memmap, np.uint8 , self.length>>0, self.offset)
            
        def read_word(self, offset):
            return int(self.word_array[offset>>2])

        def read_byte(self, offset):
            return int(self.byte_array[offset>>0])

        def write_word(self, offset, data):
            self.word_array[offset>>2] = np.uint32(data)

        def write_byte(self, offset, data):
            self.byte_array[offset>>0] = np.uint8(data)

補足(注意事項)

レジスタのアクセスの際は、この例のように numpy の配列を使ってください。
Python の mmap オブジェクトの read/write メソッドは使わないでください。
詳細は次の記事を参照してください。

*『Python の mmap.read メソッドを使って arm64 の非キャッシュ領域をアクセスするときの挙動について』@Qiita

使用例

sample.py

from uio     import Uio

class Sample:

    START_REGS  = 0x00
    ARG0_REGS   = 0x04
    ARG1_REGS   = 0x08
    RETURN_REGS = 0x0C

    def __init__(self)
        self.uio  = Uio('negative-uio')
        self.regs = self.uio.regs()
    
    def func(self, arg0, arg1)
        self.regs.write_word(Sample.ARG0_REGS , arg0)
        self.regs.write_word(Sample.ARG1_REGS , arg1)
        self.regs.write_byte(Sample.START_REGS, 1)
        self.uio.irq_on()
        self.uio.wait_irq()
        return self.regs.read_word(Sample.RETURN_REGS)

if __name__ == '__main__':
    sample = Sample()
    for x in range (0,9):
        for y in range (0,9):
            print ("func({0},{1}) => {2}".format(x, y, sample.func(x,y)))

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?