Ultra96 向け Debian GNU/Linux でAXI I/F のデータのビット幅を変更する


# はじめに


Zynq UltraScale+ MPSoC(ZynqMP) の PS-PL Interface は Fig.1 のように12本あります。そのうちの10本はデータのビット幅を 32bit、64bit、128bit から選択出来ます。

![Fig.1 ZynqMP の PS-PL Interface](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24981/73ece8d8-4c30-ffaf-ede8-45247701dc19.jpeg "Fig.1 ZynqMP の PS-PL Interface")


Fig.1 ZynqMP の PS-PL Interface

<br />



これら AXI I/Fのデータのビット幅の変更は、通常、PL 側をビルドするときに指定して、それに対応した FSBL(First Stage Boot Loader) を使ってブートすることで行われます。しかし、[『Ultra96 向け Debian GNU/Linux (v2018.2版) ブートイメージの提供』(@Qiita)](https://qiita.com/ikwzm/items/975ab6997905700dd2e0)で紹介したような汎用の FSBL を使って Linux を起動してから Device Tree Overlay を使って PL 側のコンフィギュレーションを行うような場合には使えません。

この記事では、Device Tree を使って AXI I/F のデータのビット幅を変更する方法を紹介します。




# Xilinx の AFI デバイスドライバ


実は、Xilinx が提供している Linux のディストリビューション(linux-xlnx)には、ZynqMP の AXI I/F のデータ幅を変更するデバイスドライバがすでに存在します。名前は xilinx-afi です。

ソースコードは drivers/fpga/xilinx-afi.c です。menu config などで、CONFIG_XILINX_AFI=y にすることで、このドライバが Linux Kernel に組み込まれます。 [『Ultra96 向け Debian GNU/Linux (v2018.2版) ブートイメージの提供』(@Qiita)](https://qiita.com/ikwzm/items/975ab6997905700dd2e0)で紹介した Linux Kernel にはすでにこのデバイスドライバが組み込まれています。



xilinx-afi ドライバを使って AXI のデータ幅を設定するには、device tree で次のように指定します。


```devicetree
	afi0: afi0 {
		compatible = "xlnx,afi-fpga";
		config-afi = <0 0>, <1 0>;
	};
```


device tree にどのような記述をするかは、linux-xlnk の Documentation/devicetree/bindings/fpga/xlnx,afi-fpga.txt に説明があります。以下に引用します。


```devicetree
Xilinx ZynqMp AFI interface Manager
The Zynq UltraScale+ MPSoC Processing System core provides access from PL
masters to PS internal peripherals, and memory through AXI FIFO interface
(AFI) interfaces.
Required properties:
-compatible:		Should contain "xlnx,afi-fpga"
-config-afi:		Pairs of  <regid value >
The possible values of regid and values are
 regid:		Regids of the register to be written possible values
		0- AFIFM0_RDCTRL
		1- AFIFM0_WRCTRL
		2- AFIFM1_RDCTRL
		3- AFIFM1_WRCTRL
		4- AFIFM2_RDCTRL
		5- AFIFM2_WRCTRL
		6- AFIFM3_RDCTRL
		7- AFIFM3_WRCTRL
		8- AFIFM4_RDCTRL
		9- AFIFM4_WRCTRL
		10- AFIFM5_RDCTRL
		11- AFIFM5_WRCTRL
		12- AFIFM6_RDCTRL
		13- AFIFM6_WRCTRL
		14- AFIFS
		15- AFIFS_SS2
- value:	Array of values to be written.
		for FM0_RDCTRL(0) the valid values-fabric width   2: 32-bit,1 : 64-bit ,0: 128-bit enabled
		for FM0_WRCTRL(1) the valid values-fabric width   2: 32-bit,1 : 64-bit ,0: 128-bit enabled
		for FM1_RDCTRL(2) the valid values-fabric width   2: 32-bit,1 : 64-bit ,0: 128-bit enabled
		for FM1_WRCTRL(3) the valid values-fabric width   2: 32-bit,1 : 64-bit ,0: 128-bit enabled
		for FM2_RDCTRL(4) the valid values-fabric width   2: 32-bit,1 : 64-bit ,0: 128-bit enabled
		for FM2_WRCTRL(5) the valid values-fabric width   2: 32-bit,1 : 64-bit ,0: 128-bit enabled
		for FM3_RDCTRL(6) the valid values-fabric width   2: 32-bit,1 : 64-bit ,0: 128-bit enabled
		for FM3_WRCTRL(7) the valid values-fabric width   2: 32-bit,1 : 64-bit ,0: 128-bit enabled
		for FM4_RDCTRL(8) the valid values-fabric width   2: 32-bit,1 : 64-bit ,0: 128-bit enabled
		for FM4_WRCTRL(9) the valid values-fabric width   2: 32-bit,1 : 64-bit ,0: 128-bit enabled
		for FM5_RDCTRL(10) the valid values-fabric width   2: 32-bit,1 : 64-bit ,0: 128-bit enabled
		for FM5_WRCTRL(11) the valid values-fabric width   2: 32-bit,1 : 64-bit ,0: 128-bit enabled
		for FM6_RDCTRL(12) the valid values-fabric width   2: 32-bit,1 : 64-bit ,0: 128-bit enabled
		for FM6_WRCTRL(13) the valid values-fabric width   2: 32-bit,1 : 64-bit ,0: 128-bit enabled
		for AFI_FA(14)
			dw_ss1_sel	bits (11:10)
			dw_ss0_sel	bits (9:8)
				0x0: 32-bit AXI data width),
				0x1: 64-bit AXI data width,
				0x2: 128-bit AXI data
		All other bits are 0 write ignored.
		for AFI_FA(15)  selects for ss2AXI data width valid values
					0x000: 32-bit AXI data width),
					0x100: 64-bit AXI data width,
					0x200: 128-bit AXI data
Example:
afi0: afi0 {
	compatible = "xlnx,afi-fpga";
	config-afi = <0 2>, <1 1>, <2 1>;
};

```



# Xilinx の AFI デバイスツリーのregid とレジスタの対応


前節の Documentation/devicetree/bindings/fpga/xlnx,afi-fpga.txt が分かりづらいのは、regid の説明です。これでは regid が何を意味するのかさっぱり分かりません。そこで Linux Kernel や Firmware のソースコードを追って調べてみました。その際に判明した、regid に対応する ZynqMP Register Module と AXI I/F を次の表に示します。 



<table border="2">
  <tr>
    <td align="center" rowspan="2">regid</td>
    <td align="center" rowspan="2">Name</td>
    <td align="center" colspan="2">Register</td>
    <td align="center" rowspan="2">AXI I/F</td>
    <td align="center" rowspan="2">value </td>
  </tr>
  <tr>
    <td align="center">Module</td>
    <td align="center">Register</td>
  <tr>
    <td align="right">0</td>
    <td>AFIFM0_RDCTRL</td>
    <td rowspan="2">AFIFM0</td>
    <td>RDCTRL</td>
    <td rowspan="2">S_AXI_HPC0_FPD</td>
    <td rowspan="14">2&#39;b10: 32-bit <br />2&#39;b01: 64-bit <br />2&#39;b00: 128-bit </td>
  </tr>
  <tr>
    <td align="right">1</td>
    <td>AFIFM0_WRCTRL</td>
    <td>WRCTRL</td>
  <tr>
    <td align="right">2</td>
    <td>AFIFM1_RDCTRL</td>
    <td rowspan="2">AFIFM1</td>
    <td>RDCTRL</td>
    <td rowspan="2">S_AXI_HPC1_FPD</td>
  <tr>
    <td align="right">3</td>
    <td>AFIFM1_WRCTRL</td>
    <td>WRCTRL</td>
  <tr>
    <td align="right">4</td>
    <td>AFIFM2_RDCTRL</td>
    <td rowspan="2">AFIFM2</td>
    <td>RDCTRL</td>
    <td rowspan="2">S_AXI_HP0_FPD</td>
  <tr>
    <td align="right">5</td>
    <td>AFIFM2_WRCTRL</td>
    <td>WRCTRL</td>
  <tr>
    <td align="right">6</td>
    <td>AFIFM3_RDCTRL</td>
    <td rowspan="2">AFIFM3</td>
    <td>RDCTRL</td>
    <td rowspan="2">S_AXI_HP1_FPD</td>
  <tr>
    <td align="right">7</td>
    <td>AFIFM3_WRCTRL</td>
    <td>WRCTRL</td>
  <tr>
    <td align="right">8</td>
    <td>AFIFM4_RDCTRL</td>
    <td rowspan="2">AFIFM4</td>
    <td>RDCTRL</td>
    <td rowspan="2">S_AXI_HP2_FPD</td>
  <tr>
    <td align="right">9</td>
    <td>AFIFM4_WRCTRL</td>
    <td>WRCTRL</td>
  <tr>
    <td align="right">10</td>
    <td>AFIFM5_RDCTRL</td>
    <td rowspan="2">AFIFM5</td>
    <td>RDCTRL</td>
    <td rowspan="2">S_AXI_HP3_FPD</td>
  <tr>
    <td align="right">11</td>
    <td>AFIFM5_WRCTRL</td>
    <td>WRCTRL</td>
  <tr>
    <td align="right">12</td>
    <td>AFIFM6_RDCTRL</td>
    <td rowspan="2">AFIFM6</td>
    <td>RDCTRL</td>
    <td rowspan="2">S_AXI_LPD</td>
  <tr>
    <td align="right">13</td>
    <td>AFIFM6_WRCTRL</td>
    <td>WRCTRL</td>
  <tr>
    <td align="right">14</td>
    <td>AFIFS</td>
    <td>FPD_SLCR</td>
    <td>afi_fs</td>
    <td>SS0=M_AXI_HPM0_FPD<br />SS1=M_AXI_HPM1_FPD</td>
    <td>SS0_SEL bits[9:8]<br />SS1_SEL bits[11:10]<br /> 2&#39;b00: 32-bit <br /> 2&#39;b01: 64-bit <br /> 2&#39;b10: 128-bit </td>
  </tr>
  <tr>
    <td align="right">15</td>
    <td>AFIFS_SS2</td>
    <td>LPD_SLCR</td>
    <td>afi_fs</td>
    <td>SS2=M_AXI_HPM0_LPD</td>
    <td>SS2_SEL bits[9:8]<br /> 2&#39;b00: 32-bit <br /> 2&#39;b01: 64-bit <br /> 2&#39;b10: 128-bit </td>
  </tr>
</table>



各レジスタの詳細は『Zynq UltraScale+ Devices Register Reference』を参照してください。この資料は以下の URL で示すページが見やすくて良いと思います。

* https://www.xilinx.com/html_docs/registers/ug1087/ug1087-zynq-ultrascale-registers.html



次に device tree overlay を使ってPL のコンフィギュレーションと同時に S_AXI_HPC0_FPD と M_AXI_HPM0_FPD のビット幅を128bit に設定する例を示します。




```devicetree
/dts-v1/; /plugin/;
/ {
	fragment@0 {
		target-path = "/fpga-full";
		__overlay__ {
			firmware-name = "design_1_wrapper.bin";
		};
	};
	fragment@1 {
		target-path = "/amba_pl@0";
		__overlay__ {
			afi0 {
				compatible    = "xlnx,afi-fpga";
				config-afi    = <0 0>, <1 0>, <14 0x200>;
			};
		};
	};
};
```