Ryuz's tech blog

FPGAなどの技術ブログ

RustからUIOやu-dma-bufやベアメタルからのMMIOをやってみた

概要

以前、「uioやudmabufにアクセスするC++のクラスを作ってみる」という記事を書きました。

今回はその Rust 版になります。最大の違いは C++ 版では Linux しか考慮していなかったのに対して、Rust 版ではベアメタルプログラミングでのMMIOアクセスも考慮に入れてみました。

従って、同一のデバイス制御コードを用いて、UIO経由でLinuxから制御したり、no_std でベアメタルから制御したりできます。

ZynqMP にて Linux の動く APU(Cortex-A53) と RPU(Cortex-R5) の両方を駆使してプログラムを書くときにいろいろと便利そうです。

できること

  • UIO(Userspace I/O)を開いてマップしたアドレスを読み書きできる
  • ikwzm氏のu-dma-bufを開いてマップしたアドレスを読み書きできる
  • ベアメタルで物理アドレスと範囲を指定して上と同様の読み書きできる
  • レジスタワードアドレス単位でアドレス指定できるAPIも用意している
  • 定義した領域から子領域をクローンできる

などでしょうか。

作ったもの

Rustのクレートがこちらです。

  • uio_accessor UIO用のアクセサ
  • udmabuf_accessor u-dma-buf用のアクセサ
  • mmio_accessor ベアメタルでのMMIO用のアクセサ
  • phys_accessor ベアメタルでのMMIO用のアクセサでアドレスと範囲をgenerics指定としたもの

使い方

雰囲気は Ultra96V2 で u-dma-buf を試すサンプルmain.rsを見て頂くとだいたいわかるのではないかと思います。

下記のような書き方で、u-dma-buf や uio を開くことができます。

// mmap udmabuf
println!("\nudmabuf4 open");
let udmabuf_acc = UdmabufAccessor::<usize>::new("udmabuf4", false).unwrap();
println!("udmabuf4 phys addr : 0x{:x}", udmabuf_acc.phys_addr());
println!("udmabuf4 size      : 0x{:x}", udmabuf_acc.size());

// mmap uio
println!("\nuio open");
let mut uio_acc = UioAccessor::<usize>::new_with_name("uio_pl_peri").unwrap();
println!("uio_pl_peri phys addr : 0x{:x}", uio_acc.phys_addr());
println!("uio_pl_peri size      : 0x{:x}", uio_acc.size());

そこからさらに領域を分けて子クローンを作ってアクセスしたり

// UIOの中をさらにコアごとに割り当て
let dma0_acc = uio_acc.subclone(0x00000, 0x800);
let dma1_acc = uio_acc.subclone(0x00800, 0x800);
let led_acc = uio_acc.subclone(0x08000, 0x800);
let tim_acc = uio_acc.subclone(0x10000, 0x800);

// メモリアドレスでアクセス
println!("\n<test MemRead>");
println!("DMA0_CORE_ID : 0x{:x}", uio_acc.read_mem(0x0040));
println!("DMA1_CORE_ID : 0x{:x}", uio_acc.read_mem(0x0840));

// レジスタのワード単位のアドレスでアクセス
dma0_acc.write_reg(REG_DMA_ADDR, udmabuf_acc.phys_addr());
dma0_acc.write_reg(REG_DMA_RSTART, 1);

他にも、UIOに割り当ている割り込みを待ったりできます。

// 割り込み待ち
uio_acc.set_irq_enable(true).unwrap();
uio_acc.wait_irq().unwrap();
tim_acc.read_reg(REG_TIM_CONTROL); // clear interrupt

おわりに

あとは現在加速度センサを使うプログラムであれこれやろうとしているところですが、こちらはFPGARTOSと合わせていろいろやろうというものなのでもう少し整理出来たらまたの機会で取り上げたいと思います。

追記(2022/01/05)

ikwzm氏からご指摘を頂き、v0.1.4 以降 u-dma-buf をデバイス名でオープンするように修正いたしました。