はじめに
筆者は 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 の構築(SD-Card 作成編)』@Qiita
- 『MPFS-DISCO-KIT 向け Ubuntu 22.04 の構築(SD-Card 起動編)』@Qiita
デバイスツリーの用意
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 を使って制御するためのものです。
/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 (長いので折りたたみ)
#!/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 を用意します。
#!/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 で公開しています。
-
https://github.com/ikwzm/MPFS-FPGA-Example-1-DISCO-KIT
- python/
- examples/core-gpio-uio/
MPFS-DISCO-KIT 向け Ubuntu 22.04 に関する記事
- 『MPFS-DISCO-KIT 向け Ubuntu 22.04 の構築(イントロ編)』@Qiita
- 『MPFS-DISCO-KIT 向け Ubuntu 22.04 の構築(HSS編)』@Qiita
- 『MPFS-DISCO-KIT 向け Ubuntu 22.04 の構築(SD-Card 作成編)』@Qiita
- 『MPFS-DISCO-KIT 向け Ubuntu 22.04 の構築(SD-Card 起動編)』@Qiita
- https://github.com/ikwzm/MPFS-FPGA-Ubuntu22.04
MPFS-DISCO-KIT 関連
CoreGPIO 関連
- https://www.microchip.com/en-us/products/fpgas-and-plds/ip-core-tools/coregpio
- https://ww1.microchip.com/downloads/aemdocuments/documents/fpga/ProductDocuments/SupportingCollateral/CoreGPIO_HB.pdf