1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MPFS-DISCO-KIT 向け Ubuntu 22.04 で Lチカ(UIO 編)

Last updated at Posted at 2024-12-27

はじめに

筆者は MPFS-DISCO-KIT(Microchip PolarFire SoC FPGA Discovery Kit) で動作する Ubuntu 22.04 を独自に構築しています。この記事では、この MPFS-DISCO-KIT 向け Ubuntu 22.04 で USER LED を UIO を使って制御する方法を説明します。

MPFS-DISCO-KIT とは

MPFS-DISCO-KIT(Microchip PolarFire SoC FPGA Discovery Kit) は、Microchip Technology 社の PolarFire SoC FPGA(MPFS095T_1FCSG325E) を搭載した開発用のキットです。

  • PolarFire SoC FPGA with 95k LEs
  • 1GByte main memory
  • 1x Gigabit Ethernet
  • 3x UART
  • 1x micro-SD interface
  • Form factor 4.1" x3.3"
  • 8x USER LED
  • 8x DIP SWITCH

詳細は次の URL を参照してください。

MPFS-DISCO-KIT 向け Ubuntu 22.04 とは

筆者は MPFS-DISCO-KIT(Microchip PolarFire SoC FPGA Discovery Kit) で動作する Ubuntu 22.04 を独自に構築しています。以下の URL で公開しています。

なお、これらはオフシャルなものではなく、筆者の魔改造がはいっています。ご使用の際はこの点にご留意してください。

準備

polarfire-soc-discovery-kit-reference-design

この記事では FPGA(Fabric) にプログラムするデザインとして、polarfire-soc-discovery-kit-reference-design を利用します。これは MPFS-DISCO-KIT 用に Fabric部(FPGA部)に SRAM、I2C、GPIO、DMAなどを実装したリファレンスデザインです。この記事では USER LED の制御に Fabric部(FPGA部) にある GPIO を使います。

このリファレンスデザインのダウンロード、ビルド、MPFS-DISCO-KIT へのプログラムに関しては、以下の記事を参照してください。

MPFS-DISCO-KIT 向け Ubuntu 22.04 の起動

SD-Card に MPFS-DISCO-KIT 向け Ubuntu 22.04 を書き込んで MPFS-DISCO-KIT を起動します。
詳細は以下の記事を参照してください。

デバイスツリーの用意

MPFS-DISCO-KIT 向け Ubuntu 22.04 では、FPGA(Fabric) にプログラムした IP を制御するためのデバイスドライバは Device Tree Overlay を使って Linux 起動後に動的にロードします。polarfire-soc-discovery-kit-reference-design では、USER LED は CoreGPIO という gpio コンポーネントに接続されています。そこで、次のような DTS(Device Tree Blob Source) を用意します。この DTS は gpio コンポーネントを UIO を使って制御するためのものです。

core-gpio-uio-0.dts
/dts-v1/; /plugin/;

/ {
	fragment@0 {
		target-path = "/fabric-bus@40000000";
		#address-cells = <2>;
		#size-cells = <2>;

		__overlay__ {
			#address-cells = <2>;
			#size-cells = <2>;
			core-gpio-uio-0 {
				compatible = "generic-uio";
				reg = <0x0 0x40000100 0x0 0x0100>;
			};
		};
	};
};

これを dtc(Device Tree Compiler) を使って DTB(Device Tree Blob) に変換します。

shell$ dtc -I dts -O dtb -o core-gpio-uio-0.dtb core-gpio-uio-0.dts

デバイスツリーのオーバーレイ

core-gpio-uio-0.dtb を Device Tree に Overlay します。

shell$ sudo mkdir /sys/kernel/config/device-tree/overlays/core-gpio-uio-0
shell$ sudo cp core-gpio-uio-0.dtb /sys/kernel/config/device-tree/overlays/core-gpio-uio-0/dtbo

これで /sys/class/uio に uioX (X は任意の数字) が見えるようなるはずです。以下の例では uio0 として見えています。

shell$ ls -la /sys/class/uio/
total 0
drwxr-xr-x  2 root root 0 Nov 22  2023 .
drwxr-xr-x 48 root root 0 Nov 22  2023 ..
lrwxrwxrwx  1 root root 0 Dec 27 09:18 uio0 -> ../../devices/platform/fabric-bus@40000000/40000100.core-gpio-uio-0/uio/uio0

動作

uio.py の紹介

ここでは、UIO の制御に python の numpy を使います。これについては以下の記事に説明しています。

上の記事で説明している uio.py は以下のようなものです。

uio.py (長いので折りたたみ)
uio.py
#!/usr/bin/env python3
# SPDX-License-Identifier: BSD-2-Clause
# Copyright (c) 2024 ikwzm

import numpy as np
import mmap
import os
import glob
import re

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):
        self.name        = name
        self.device_name = Uio.find_device_file(self.name)
        self.device_file = os.open('/dev/%s' % self.device_name, os.O_RDWR | os.O_SYNC)
        self.memmap_dict = {}

    def sys_class_file_name(self, name):
        return '/sys/class/uio/%s/%s' % (self.device_name, name)

    def read_class_integer(self, name):
        file_name = self.sys_class_file_name(name)
        with open(file_name, "r") as file:
            value = file.readline().strip()
        if   value.startswith("0x") or value.startswith("0X"):
            return int(value, 16)
        elif value.isdigit():
            return int(value, 10)
        raise ValueError("Invalid value %s in %s " % (value, file_name))
        
    def get_map_addr(self, index=0):
        return self.read_class_integer('maps/map%d/addr'   % index)

    def get_map_size(self, index=0):
        return self.read_class_integer('maps/map%d/size'   % index)

    def get_map_offset(self, index=0):
        return self.read_class_integer('maps/map%d/offset' % index)

    def get_map_info(self, index=0):
        map_addr   = self.get_map_addr(index)
        map_size   = self.get_map_size(index)
        map_offset = self.get_map_offset(index)
        return dict(addr=map_addr, size=map_size, offset=map_offset)
        
    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)

    def regs(self, index=0, offset=0, length=None):
        if index in self.memmap_dict.keys():
            memmap_info = self.memmap_dict[index]
            memmap      = memmap_info['memmap']
            mmap_offset = memmap_info['offset']
            mmap_size   = memmap_info['size']
            mmap_addr   = memmap_info['addr']
        else:
            page_size   = os.sysconf("SC_PAGE_SIZE")
            map_info    = self.get_map_info(index)
            mmap_addr   = map_info['addr']
            mmap_offset = ((map_info['addr'] + map_info['offset'])        ) % page_size
            mmap_size   = ((map_info['size'] + page_size - 1) // page_size) * page_size
            memmap      = mmap.mmap(self.device_file,
                                    mmap_size,
                                    mmap.MAP_SHARED,
                                    mmap.PROT_READ | mmap.PROT_WRITE,
                                    index*page_size)
            memmap_info = {'memmap': memmap, 'size': mmap_size, 'addr': mmap_addr, 'offset': mmap_offset}
            self.memmap_dict[index] = memmap_info
        regs_offset = mmap_offset + offset
        if   length == None:
            regs_length = mmap_size - regs_offset
        elif regs_offset + length <= mmap_size:
            regs_length = length
        else:
            raise ValueError("region range error")
        return Uio.Regs(memmap, regs_offset, regs_length)

    class Regs:
        
        def __init__(self, memmap, offset, length):
            self.memmap = memmap
            self.offset = offset
            self.length = length
            self.alloc_word_array()
            self.alloc_byte_array()

        def alloc_word_array(self):
            self.word_array  = np.frombuffer(self.memmap, np.uint32, self.length>>2, self.offset)

        def read_word(self, offset):
            return int(self.word_array[offset>>2])

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

        def alloc_half_array(self):
            self.half_array  = np.frombuffer(self.memmap, np.uint16, self.length>> 1, self.offset)

        def read_half(self, offset):
            return int(self.half_array[offset>>1])

        def write_half(self, offset, data):
            self.half_array[offset>>1] = np.uint16(data)

        def alloc_byte_array(self):
            self.byte_array  = np.frombuffer(self.memmap, np.uint8 , self.length>>0, self.offset)

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

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

uio.py を使った例を示します。

shell$ sudo python3
Python 3.10.12 (main, Nov  6 2024, 20:22:13) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from uio import Uio
>>> core_gpio_uio  = Uio('core-gpio-uio-0')
>>> core_gpio_regs = core_gpio_uio.regs()
>>> core_gpio_regs.write_byte(0xA0, 0x00)
>>> core_gpio_regs.write_byte(0xA0, 0x7F)
>>> core_gpio_regs.write_byte(0xA0, 0x01)
>>> exit()

アドレス 0xA0 は CoreGPIO の出力データを書き込むレジスタです。
このレジスタに 0x00 を書き込むことで全ての USER LED が消灯します。
このレジスタに 0x7F を書き込むことで全ての USER LED が点灯します。
このレジスタに 0x01 を書き込むことで USER LED1 が点灯し、残りは消灯します。

sample.py

uio.py を使ったサンプルとして次のような sample.py を用意します。

sample.py
#!/usr/bin/env python3
# SPDX-License-Identifier: BSD-2-Clause
# Copyright (c) 2024 ikwzm

from uio import Uio
import time

if __name__ == '__main__':
    core_gpio_uio  = Uio('core-gpio-uio-0')
    core_gpio_regs = core_gpio_uio.regs()
    led_pattern_1  = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02]
    led_pattern_2  = [0x41, 0x22, 0x18, 0x22]

    for i in range(60):
        data = led_pattern_1[i % len(led_pattern_1)]
        core_gpio_regs.write_byte(0xA0, data)
        time.sleep(0.1)
    for i in range(60):
        data = led_pattern_2[i % len(led_pattern_2)]
        core_gpio_regs.write_byte(0xA0, data)
        time.sleep(0.1)
    core_gpio_regs.write_byte(0xA0, 0x7F)
        

この sample.py を走らせると、USER LED がイルミネーションのように点灯します。

shell$ sudo python3 sample.py

参考

MPFS-FPGA-Example-1-DISCO-KIT

ここで紹介した DTS(Device Tree Blob Source) と python スクリプトは以下の URL で公開しています。

MPFS-DISCO-KIT 向け Ubuntu 22.04 に関する記事

MPFS-DISCO-KIT 関連

CoreGPIO 関連

その他

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?