この記事では、次のコードを理解するための基礎知識と、関連するハードウェア・ソフトウェアの概念を解説します。
axi_gpio_regset = np.dtype([
('gpio1_data' , 'uint32'),
('gpio1_control', 'uint32'),
('gpio2_data' , 'uint32'),
('gpio2_control', 'uint32')
])
memory_file_handle = os.open('/dev/mem', os.O_RDWR)
axi_mmap = mmap.mmap(fileno=memory_file_handle, length=mmap.PAGESIZE, offset=0x42000000)
axi_numpy_array = np.recarray(1, axi_gpio_regset, buf=axi_mmap)
axi_array_contents = axi_numpy_array[0]
出典:1.4.2.5. Frequency Counter — Red Pitaya 2.00-35 documentation
目次
ハードウェアの基礎知識
(1) メモリマップI/Oとは
- メモリマップI/O では、ハードウェアデバイスがメモリ空間に直接マップされ、CPUは通常のメモリアクセスと同様にデバイスを操作できます。
- ハードウェアの制御やデータ取得のために、特定のアドレスに読み書きする必要があります。
(2) AXI GPIO の役割
- AXI GPIO は、FPGA内部で構成された汎用入出力ポート(GPIO)を制御するIPコアです。
- 上記コードでは、
gpio1_data
やgpio2_control
などのレジスタにアクセスしています。
Pythonのmmapモジュール
(1) mmapとは
mmap
モジュールを使用すると、ファイルやデバイスをメモリにマップし、メモリアクセスのようにデータを操作できます。/dev/mem
はLinuxでデバイスメモリ全体を公開する特別なファイルで、mmap
を使うことで特定のメモリ領域にアクセス可能になります。
(2) 具体例: メモリマップの作成
以下のコードは、/dev/mem
を開き、特定のアドレス空間をマップします。
memory_file_handle = os.open('/dev/mem', os.O_RDWR)
axi_mmap = mmap.mmap(fileno=memory_file_handle, length=mmap.PAGESIZE, offset=0x42000000)
length=mmap.PAGESIZE
: メモリマッピングのサイズをページ単位(通常4KB)で指定します。offset=0x42000000
: マップするメモリアドレスの開始位置を指定します。
NumPyのrecarray構造
(1) NumPyの型定義
NumPyでは、dtype
を使用して複雑なデータ構造を定義できます。
axi_gpio_regset = np.dtype([
('gpio1_data' , 'uint32'),
('gpio1_control', 'uint32'),
('gpio2_data' , 'uint32'),
('gpio2_control', 'uint32')
])
このコードでは、次のフィールドを持つ構造体を定義しています。
gpio1_data
(32ビット符号なし整数)gpio1_control
(32ビット符号なし整数)gpio2_data
(32ビット符号なし整数)gpio2_control
(32ビット符号なし整数)
(2) recarrayによる構造化アクセス
np.recarray
を使うと、メモリマップされたデータを構造体のように扱えます。
axi_numpy_array = np.recarray(1, axi_gpio_regset, buf=axi_mmap)
axi_array_contents = axi_numpy_array[0]
1
: 配列の要素数(今回は1つだけ)。axi_gpio_regset
: 配列の各要素のデータ型。buf=axi_mmap
: メモリマップされたデータを指定。
これにより、次のようにフィールド名でアクセスが可能になります。
print(axi_array_contents.gpio1_data) # gpio1_data の値を取得
axi_array_contents.gpio1_control = 0xDEADBEEF # gpio1_control に値を書き込む
このコードの重要な部分は、np.recarray
を用いてハードウェアのレジスタマッピングを「Pythonオブジェクトとして」扱う仕組みです。
axi_gpio_regset
: NumPyのデータ型 (dtype
) で、メモリ内の構造を定義。axi_numpy_array
: メモリマップをデータ構造としてアクセス可能にした配列。axi_array_contents
: 配列の1つの要素にアクセスするオブジェクト。
以下は、このコードの具体的なメモリアドレスと recarray
のフィールドの対応を示す表です。
フィールド名 | オフセット | データ型 | 実際のメモリアドレス | 内容 |
---|---|---|---|---|
gpio1_data | 0x00 | uint32 | 0x42000000 | GPIO1のデータ |
gpio1_control | 0x04 | uint32 | 0x42000004 | GPIO1の制御 |
gpio2_data | 0x08 | uint32 | 0x42000008 | GPIO2のデータ |
gpio2_control | 0x0C | uint32 | 0x4200000C | GPIO2の制御 |
もし配列の要素数を2にしたときは、2つめのgpio1_data
のメモリアドレスは0x42000010
となります。
recarray
の使い方
1. フィールドの読み書き
axi_array_contents
を使うことで、簡単に特定のフィールドを操作できます。
読み出し
gpio1_value = axi_array_contents.gpio1_data
print("GPIO1 Data:", gpio1_value)
- メモリアドレス
0x42000000
の値を取得。
書き込み
axi_array_contents.gpio1_control = 0xDEADBEEF
- メモリアドレス
0x42000004
に0xDEADBEEF
を書き込む。
2. 配列全体の操作
要素数が複数の場合(例: np.recarray(10, ...)
)、各要素にアクセスできます。
複数要素の例
axi_numpy_array = np.recarray(10, axi_gpio_regset, buf=axi_mmap)
print(axi_numpy_array[1].gpio1_data) # 0x42000010 にアクセス
コードの動作の全体像
/dev/mem
をオープン: システム全体の物理メモリにアクセス可能なファイルを開きます。- メモリマップを作成:
mmap
を使い、FPGA上のAXI GPIOレジスタ領域をメモリにマップします。 - レジスタ定義を作成: NumPyの
dtype
を使用して、メモリ領域の構造を定義します。 - 構造体としてアクセス:
recarray
を用いて、ハードウェアレジスタをフィールド名で読み書きします。
まとめ
このコードでは、Pythonを用いてメモリマップされたハードウェアレジスタにアクセスしています。mmap
と np.recarray
を組み合わせることで、効率的かつ可読性の高い方法でハードウェア制御が可能になります。この手法は、組み込みシステム開発やFPGAデバッグにおいて非常に有用です。
これを参考に、実際のプロジェクトで応用してみてください!