ZyboにUbuntu22.04のファイルシステムを入れて起動した。
PL部分にはAXI GPIOがつながっておりLEDが接続されている。
rotors.tar.goのファイルシステムにはdevmemがインストールされいた。
しかしUbuntuには入っていなかったのでdevmem2.cをどこからかダウンロードしてきてビルドする事で使える。
しかしこのままではBusyboxの時と何も変わらないのでRustで書いたアプリでdev/memを操作してみる。
環境構築
Zynq上でRustをビルドするわけにもいかないので
WSL2上でCrossコンパイルする
必要なものは
- WSL2
- Docker for WSL
- Rust
これらは解説記事がとても多いのでスキップして
- cross
cross で Rust のクロスコンパイル - Qiita
に沿ってやる
ターゲットのアーキテクチャは
~/d/r/z/devmem-rs (master)> rustup target list | grep arm arm-linux-androideabi arm-unknown-linux-gnueabi arm-unknown-linux-gnueabihf arm-unknown-linux-musleabi arm-unknown-linux-musleabihf armebv7r-none-eabi armebv7r-none-eabihf armv5te-unknown-linux-gnueabi armv5te-unknown-linux-musleabi armv7-linux-androideabi armv7-unknown-linux-gnueabi armv7-unknown-linux-gnueabihf armv7-unknown-linux-musleabi armv7-unknown-linux-musleabihf armv7a-none-eabi armv7r-none-eabi armv7r-none-eabihf
zynqはarmv7なのでarmv7-unknown-linux-gnueabihfを入れる
memmap2
cargo new devmem-rs --bin cd devmem-rs cargo add memmap2
ソースコード
こんな感じで
use clap::{arg, command}; use memmap2::{MmapMut, MmapOptions}; use std::env; use std::error::Error; use std::fs::OpenOptions; use std::path::PathBuf; use std::u64; fn mapping(addr: u64, len: usize) -> Result<MmapMut, Box<dyn Error>> { let file = OpenOptions::new() .read(true) .write(true) .create(true) .open(PathBuf::from("/dev/mem"))?; let m = unsafe { MmapOptions::new().offset(addr).len(len).map_mut(&file)? }; Ok(m) } fn main() { let matches = command!() .arg(arg!([ADDRESS])) .arg(arg!([TYPE]).default_value("b")) .arg(arg!([DATA])) .get_matches(); let adr = match matches.get_one::<String>("ADDRESS") { Some(adr) => { let adr = adr.trim_start_matches("0x"); u64::from_str_radix(&adr, 16).unwrap_or_default() } _ => { println!( "Usage: devmem {{ address }} [ type [ data ] ] address : memory address to act upon type : access operation type : [b]yte, [h]alfword, [w]ord data : data to be written\n" ); std::process::exit(0) } }; let width = match matches.get_one::<String>("TYPE") { Some(t) => match t.as_str() { "b" | "B" => 1, "h" | "H" => 2, "w" | "W" => 4, _ => { println!("illigal data type '{}'", t); std::process::exit(0) } }, _ => { unreachable!() } }; let mut m = mapping(adr, 4).unwrap(); let rdata = u32::from_ne_bytes(m[0..4].try_into().unwrap()); println!("Value at address 0x{:X}: 0x{:X}", adr, rdata); match matches.get_one::<String>("DATA") { Some(s) => { //Write Mode let data = if s.starts_with("0x") { let s = s.trim_start_matches("0x"); u32::from_str_radix(s, 16).unwrap_or_default() } else { u32::from_str_radix(s, 10).unwrap_or_default() }; let bytes = data.to_ne_bytes(); for i in 0..width { m[i] = bytes[i]; } let rbdata = u32::from_ne_bytes(m[0..4].try_into().unwrap()); println!("Written 0x{:X}: readback: 0x{:X}", data, rbdata); } None => {} }; }
使用
Clapによって簡単にヘルプ表示できる。
Vivadoのほうでbase addrを確認する。
AXI GPIOはbase + 0x0004に入出力方向を決めるレジスタがあり、デフォルトでは入力に設定されているので、そこに0x0を書きこむ。
LogiCORE IP AXI GPIO (v1.01b) Data Sheet (AXI) - 1.01b English
LED点灯
これでLEDが交互に光っているはず。
スイッチ入力
同様に、スイッチ入力を読み取ることもできる。