lp6m’s blog

いろいろかきます

Ultra96v2のpl_resetnxをPSから制御

Qiitaとの記事の分け方が微妙だが、雑に書くのはこちらで書こうと思う。

Ultra96v2のPSコアからはpl_resetn0, pl_resetn1, pl_resetn2, pl_resetn3の4つのリセットがある。これをPSから制御したい。
以下のリンクを見るだけで理解できる人は理解できると思うが、よくわからなかったので実践した。

  • AR# 68962: Zynq MPSoC PS pl_resetnx ポートの制御アドレスの取得方法

https://support.xilinx.com/s/article/68962?language=ja

ブロックデザインの作成

リセットされているかを目で確認したかったのでLチカモジュールを作成した。
AXI BRAMとBRAM Generatorがブロックデザインには含まれているが今回は関係ないので不要。

  • ブロックデザイン

f:id:lp6m:20211230001124p:plain

  • Lチカモジュール

リセットがかかると一定時間2つのLEDが点灯し、その後2つのLEDは交互に点灯する。
gist.github.com

  • 制約ファイル

PL側に接続されているLEDを出力ピンに割り当てる。

set_property PACKAGE_PIN A9 [get_ports {LED_0[0]}]
set_property PACKAGE_PIN B9 [get_ports {LED_0[1]}]
set_property IOSTANDARD LVCMOS18 [get_ports {LED_0[0]}]
set_property IOSTANDARD LVCMOS18 [get_ports {LED_0[1]}]

Write Bitstream -> Export Hardware (Include Bitstream)でVivadoでの作業は完了

ベアメタルでの制御

VivadoからLaunch Vitis IDEでVitis IDEを起動、Application Projectを新規作成、VivadoでエクスポートしたXSAを選択してHello Worldテンプレートを選択
ここから先述のAR同様、pl_resetn0を制御するための制御アドレスを調べていく。

  • ZynqMP Ultrascale テクニカルリファレンスマニュアル

https://japan.xilinx.com/support/documentation/user_guides/j_ug1085-zynq-ultrascale-trm.pdf
このリファレンスマニュアルの607ページの図を見ると、pl_resetn0であるEMIO[95]はGPIO Bank 5に属していることがわかる。
f:id:lp6m:20211230002410p:plain
psu_init.c, psu_init.hファイルから、GPIO_DIRM_5_OFFSET, GPIO_OEN_5_OFFSET, GPIO_DATA_5_OFFSETの3つの物理アドレスを入手する。
f:id:lp6m:20211230002557p:plain
このアドレスを制御すればリセットがかけられるということになる。GPIOの制御はこちらの記事を参考にした。ありがとうございます。
qiita.com
記事ではXilinxから提供されるGPIO用ライブラリXGpioを使った例も紹介されているが、今回はレジスタを直接叩く。
テンプレートから作成されたhelloworld.cを以下のように書き換える。
gist.github.com
Reset Now!!が表示されるとともに2つのLEDが光って、きちんとリセット制御ができることを確認した。

Petalinuxでの制御

プロジェクトは、下記記事を参考に作ればOK。
RISC-VをUltra96上のpetalinuxから実行 - Qiita

system-user.dtsiは下記のようにした。

/include/ "system-conf.dtsi"
/ {
    chosen {
        bootargs = "earlycon console=ttyPS0,115200 clk_ignore_unused root=/dev/mmcblk0p2 rw rootwait cma=512M uio_pdrv_genirq.of_id=generic-uio";
    };
    xlnk {
        compatible = "xlnx,xlnk-1.0";
    };

};

&sdhci0 {
    disable-wp;
};

デフォルトでPetalinuxがXilinx製のGPIO/EMIO制御のドライバを使うようになっているので、これを使用する。GPIOの情報は下記コマンドで確認できる。

cat /sys/kernel/debug/gpio 
gpiochip0: GPIOs 338-511, parent: platform/ff0a0000.gpio, zynqmp_gpio:
 gpio-338 (UART1_TX            )
 gpio-339 (UART1_RX            )
 gpio-340 (UART0_RX            )
 gpio-341 (UART0_TX            )
 gpio-342 (I2C1_SCL            )
 gpio-343 (I2C1_SDA            )
...
 gpio-511 (                    )

gpio-338からgpio-511まで、511-338+1=174個ある。これは上記テクニカルリファレンスマニュアルのGPIOのピン情報(26x3+32*3=174)と一致する。すなわちpl_resetn0が繋がっているEMIO95を操作するには、gpio-511を操作すればよいということがわかった。
というわけで、petalinuxから下記コマンドでリセット操作が行える。

echo 511 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio511/direction
echo 0 > /sys/class/gpio/gpio511/value
echo 1 > /sys/class/gpio/gpio511/value

ちなみに、このGPIOのドライバがどのように適用されているかを確認してみた。petalinux-buildで生成された統合されたデバイスリーファイル(system.dtb)をデコンパイルして見ればわかる。

cd <petalinux_project_directory>
cd images/linux
dtc -I dtb -O dts -o system.dts sytem.dtb

system.dtbにGPIO/EMIO用のデバイスツリー記述を見つけた。

gpio@ff0a0000 {
			compatible = "xlnx,zynqmp-gpio-1.0";
			status = "okay";
			#gpio-cells = <0x2>;
			gpio-controller;
			interrupt-parent = <0x4>;
			interrupts = <0x0 0x10 0x4>;
			interrupt-controller;
			#interrupt-cells = <0x2>;
			reg = <0x0 0xff0a0000 0x0 0x1000>;
			power-domains = <0xc 0x2e>;
			clocks = <0x3 0x1f>;
			emio-gpio-width = <0x20>;
			gpio-mask-high = <0x0>;
			gpio-mask-low = <0x5600>;
			gpio-line-names = "UART1_TX", "UART1_RX", "UART0_RX", "UART0_TX", "I2C1_SCL", "I2C1_SDA", "SPI1_SCLK", "WLAN_EN", "BT_EN", "SPI1_CS", "SPI1_MISO", "SPI1_MOSI", "I2C_MUX_RESET", "SD0_DAT0", "SD0_DAT1", "SD0_DAT2", "SD0_DAT3", "PS_LED3", "PS_LED2", "PS_LED1", "PS_LED0", "SD0_CMD", "SD0_CLK", "GPIO_PB", "SD0_DETECT", "VBUS_DET", "POWER_INT", "DP_AUX", "DP_HPD", "DP_OE", "DP_AUX_IN", "INA226_ALERT", "PS_FP_PWR_EN", "PL_PWR_EN", "POWER_KILL", "", "GPIO-A", "GPIO-B", "SPI0_SCLK", "GPIO-C", "GPIO-D", "SPI0_CS", "SPI0_MISO", "SPI_MOSI", "GPIO-E", "GPIO-F", "SD1_D0", "SD1_D1", "SD1_D2", "SD1_D3", "SD1_CMD", "SD1_CLK", "USB0_CLK", "USB0_DIR", "USB0_DATA2", "USB0_NXT", "USB0_DATA0", "USB0_DATA1", "USB0_STP", "USB0_DATA3", "USB0_DATA4", "USB0_DATA5", "USB0_DATA6", "USB0_DATA7", "USB1_CLK", "USB1_DIR", "USB1_DATA2", "USB1_NXT", "USB1_DATA0", "USB1_DATA1", "USB1_STP", "USB1_DATA3", "USB1_DATA4", "USB1_DATA5", "USB1_DATA6", "USB_DATA7", "WLAN_IRQ", "PMIC_IRQ", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "";
			phandle = <0x10>;
		};

gpio-line-namesのところを上書きすれば/sys/kernel/debug/gpioを見たときにわかりやすいラベルが付けられるんだなと思った。
おしまい