Posted at

U-Boot から Zynq の PLクロックとリセット信号を制御する

More than 3 years have passed since last update.


はじめに

「FPGA+SoC+Linuxのブートシーケンス(ZYNQ+U-Boot-SPL編)」で、U-Boot-spl(U-Boot Second Program Loader)をステージ1ブートローダーとして Linux をブートする方法を説明しました。

その際、ちょっと問題になったのが PL 周りの設定です。

PL周りの設定をPLのデザイン時に設定した値を使う場合、PL側のデザインを変更する度に boot.bin(U-Boot-SPL) を再ビルドする必要があります(PL周りの設定が変わらない場合は除く)。

また、PL周りの設定を U-Boot がすでに定義済みのものをそのまま使う場合は、boot.bin(U-Boot-SPL)を再ビルドする必要はありませんが、PL周りの設定を変更することができません。

そこで、U-Boot の mw (memory write) コマンドを使って直接 Zynq のレジスタに書き込むことによって PL周りの設定を変える方法を説明します。

なお、Zynq のレジスタを直接操作するので、けっこう危険です。ハードウェア や PLL に詳しい人向けです。

レジスタ等の詳細は「Zynq-7000 Technical Reference Manual」を参照してください。これは Xilinx 社から UG585(User Guide 585)として公開されています。


PL周りの設定レジスタ

Zynq で PL 周りの設定が必要なレジスタは、おそらく、クロックの設定とリセット出力制御だと思います。

これらを設定するレジスタは System Level Control Registers (slcr) のグループに属しています。このうち FPGA のクロックの設定とリセット出力の制御を行うレジスタは次の通りです。

Register Name
Address
Width
Type
Description

FPGA0_CLK_CTRL
0xF8000170
32
rw
PL Clock 0 Output control

FPGA1_CLK_CTRL
0xF8000180
32
rw
PL Clock 1 Output control

FPGA2_CLK_CTRL
0xF8000190
32
rw
PL Clock 2 Output control

FPGA3_CLK_CTRL
0xF80001A0
32
rw
PL Clock 3 Output control

FPGA_RST_CTRL
0xF8000240
32
rw
FPGA Software Reset Control


FPGA[0-3]_CLK_CTRL


  • Name : FPGA[0-3]_CLK_CTRL

  • Address : 0xF8000170, 0xF8000180, 0xF8000190, 0xF80001A0

  • Width : 32

  • Access Type : rw(Read and Write)

  • Description : PL Clock [0-3] Output control

Field Name
Bits
Type
Description

DIVISOR1
25:20
rw
Provides the divisor used to divide the source clock to generate the required generated clock frequency. Second cascade divide

DIVISOR0
13:08
rw
Provides the divisor used to divide the source clock to generate the required generated clock frequency. First cascade divide

SRCSEL
05:04
rw
Select the source used to generate the clock:
0x: Source for generated clock is IO PLL.
10: Source for generated clock is ARM PLL.
11: Source for generated clock is DDR PLL.


FPGA_RST_CTRL


  • Name : FPGA_RST_CTRL

  • Address : 0xF8000240

  • Width : 32

  • Access Type : rw(Read and Write)

  • Description : FPGA Software Reset Control

Field Name
Bits
Type
Description

FPGA3_OUT_RST
3
rw
PL Reset 3 (FCLKRESETN3 output signal). Refer to the PS7 wrapper in EDK for possible signal inversion. Logic level on the FCLKRESETN3 signal:
0: De-assert reset (High logic level).
1: Assert Reset (Low logic state)

FPGA2_OUT_RST
2
rw
PL Reset 2 (FCLKRESETN2 output signal). Refer to the PS7 wrapper in EDK for possible signal inversion. Logic level on the FCLKRESETN2 signal:
0: De-assert reset (High logic level).
1: Assert Reset (Low logic state)

FPGA1_OUT_RST
1
rw
PL Reset 1 (FCLKRESETN1 output signal). Refer to the PS7 wrapper in EDK for possible signal inversion. Logic level on the FCLKRESETN1 signal:
0: De-assert reset (High logic level).
1: Assert Reset (Low logic state)

FPGA0_OUT_RST
0
rw
PL Reset 0 (FCLKRESETN0 output signal). Refer to the PS7 wrapper in EDK for possible signal inversion. Logic level on the FCLKRESETN0 signal:
0: De-assert reset (High logic level).
1: Assert Reset (Low logic state)


どのような値を設定すれば良いか


U-Boot が定義済みの値(ZYBOの場合)

最新の U-Boot を Zynq 用にビルドした場合、設定されているクロックの周波数を U-Boot の clk dump コマンドで見ることができます。

Zynq> clk dump

clk frequency
armpll 1300000000
ddrpll 1050000000
iopll 1000000000
cpu_6or4x 650000000
cpu_3or2x 325000000
cpu_2x 216666666
cpu_1x 108333333
ddr_2x 350000000
ddr_3x 525000000
dci 10096154
lqspi 200000000
smc 21666667
pcap 200000000
gem0 125000000
gem1 16666667
fclk0 100000000
fclk1 175000000
fclk2 12264151
fclk3 100000000
sdio0 50000000
sdio1 50000000
uart0 50000000
uart1 50000000
spi0 15873016
spi1 15873016
usb0_aper 108333333
usb1_aper 108333333
gem0_aper 108333333
gem1_aper 108333333
sdio0_aper 108333333
sdio1_aper 108333333
spi0_aper 108333333
spi1_aper 108333333
can0_aper 108333333
can1_aper 108333333
i2c0_aper 108333333
i2c1_aper 108333333
uart0_aper 108333333
uart1_aper 108333333
gpio_aper 108333333
lqspi_aper 108333333
smc_aper 108333333
dbg_trc 66666667
dbg_apb 66666667

これによるとクロックの周波数は次の様になっているようです。

clk
frequency

ARMPLL
1300MHz

DDRPLL
1050MHz

IOPLL
1000MHz

FPGA0
100MHz

FPGA1
175MHz

FPGA2
123MHz

FPGA3
100MHz

次に U-Boot がレジスタにどのような値を設定しているか見ると、次の様に設定しています。

Register Name
Address
U-Boot Def Value
Description

FPGA0_CLK_CTRL
0xF8000170
0x00100A00
PL Clock 0 Output control

FPGA1_CLK_CTRL
0xF8000180
0x00100630
PL Clock 1 Output control

FPGA2_CLK_CTRL
0xF8000190
0x00203520
PL Clock 2 Output control

FPGA3_CLK_CTRL
0xF80001A0
0x00100A00
PL Clock 3 Output control

FPGA_RST_CTRL
0xF8000240
0x00000000
FPGA Software Reset Control

FPGA0_CLK_CTRL では、ソースの PLL に IOPLL、DIVISOR0 に 10、DIVISOR1 に 1 を設定しています。

つまり、PL Clock 0 の周波数は (1GHz(=IOPLLの周波数)÷10(=DIVISOR0の値))÷(1(=DIVISOR1の値))で100MHzになります。


周波数に対応した設定値

例えば PL Clock 0 に 125MHz を設定したければ、FPGA0_CLK_CTRL に 0x00100800(SRCSEL=IOPLL、DIVISOR0=8、DIVISOR1=1)を書き込みます。

残念ながら、Zynq のこの PLL の仕様上、ソースの周波数の整数分の1の周波数しか設定できません。

例えば ZYBO の場合、PL CLock 0 に 133MHz を設定しようと思っても、無理です。一番近いのは、131.25MHz=((1050MHz(=DDRPLLの周波数)÷8)÷1)で、FPGA0_CLK_CTRL に 0x00100803(SRCSEL=DDRPLL、DIVISOR0=8、DIVISOR1=1)を書き込むことでしょう。


その他のボードの場合

ここでは ZYBO の例をあげましたが、ARMPLL、DDRPLL、IOPLLの周波数はボードによって異なります。

参考までに、判る範囲で調べて見たところ次の様になりました。

Board Name
Input Freq
ARMPLL Mult
ARMPLL Freq
DDRPLL Mult
DDRPLL Freq
IOPLL Mult
IOPLL Freq

ZC706
33.3333MHz
40
1333.333MHz
32
1066.667MHz
30
1000MHz

ZC702
33.3333MHz
40
1333.333MHz
32
1066.667MHz
30
1000MHz

ZedBoard
33.3333MHz
40
1333.333MHz
32
1066.667MHz
30
1000MHz

ZYBO
50MHz
26
1300.000MHz
21
1050.000MHz
20
1000MHz


レジスタへの書き込み

素直に mw(memory write) コマンドにアドレスとデータを指定して書き込みたいところですが、残念ながら Zynq の場合はそういうわけにはいきません。というのも、Zynq の System Level Control Registers(slcr) は、万が一プログラムが暴走してもレジスタに変な値が書き込まれないようにライトプロテクションがかけられているからです。

ライトプロテクションを制御するレジスタは次の通りです。

Register Name
Address
Width
Type
Description

SLCR_LOCK
0xF8000004
32
wo
SLCR Write Protection Lock

SLCR_UNLOCK
0xF8000008
32
wo
SLCR Write Protection UnLock


SLCR_LOCK


  • Name : SLCR_LOCK

  • Address : 0xF8000004

  • Width : 32

  • Access Type : wo(Write Only)

  • Description : SLCR Write Protection Lock

Field Name
Bits
Type
Description

LOCK_KEY
15:0
wo
Write the lock key, 0x767B, to write protect the slcr registers:
all slcr registers, 0xF800_0000 to 0xF800_0B74,
are write protected until the unlock key is written to the SLCR_UNLOCK register.
A read of this register returns zero.


SLCR_UNLOCK


  • Name : SLCR_UNLOCK

  • Address : 0xF8000008

  • Width : 32

  • Access Type : wo(Write Only)

  • Description : SLCR Write Protection Unlock

Field Name
Bits
Type
Description

UNLOCK_KEY
15:0
wo
Write the unlock key, 0xDF0D, to enable writes to the slcr registers.
All slcr registers, 0xF800_0000 to 0xF800_0B74,
are writeable until locked using the SLCR_LOCK register.
A read of this register returns zero.


書き込みの手順

つまり、FPGA0_CLK_CTRL や FPGA_RST_CTRL に値を書き込む前に SLCR_UNLOCK(0xF8000008) に 0xDF0D を書いてライトプロテクションを無効にする必要があります。またレジスタへの書き込みが終わったら SLCR_LOCK(0xF8000004) に 0x767B を書いてライトプロテクションを有効にします。


uEnv.txt の例

U-Boot がブート時に実行するスクリプトを記述したファイルを用意します。ここでは uEnv.txt を使いますが、U-Boot には他にも専用のスクリプトファイルがあるので、そちらを使ってもかまいません。


uEnv.txt

slcr_unlock_cmd=mw.l 0xF8000008 0xDF0D

slcr_lock_cmd=mw.l 0xF8000004 0x767B
fpga_set_cmd=run slcr_unlock_cmd && mw.l 0xF8000170 0x00101400 && run slcr_lock_cmd
fpga_load_cmd=fatload mmc 0 0x03000000 design.bit && fpga loadb 0 0x03000000 $filesize
linux_load_cmd=fatload mmc 0 0x03000000 zImage && fatload mmc 0 0x02A00000 devicetree.dtb
linux_boot_cmd=bootz 0x03000000 - 0x02A00000
uenvcmd=run fpga_load_cmd && run fpga_set_cmd && run linux_load_cmd && run linux_boot_cmd


  • slcr_unlock_cmd=mw.l 0xF8000008 0xDF0D

    System Levec Control Registers のライトプロテクションを解除するコマンドを定義。

  • slcr_lock_cmd=mw.l 0xF8000004 0x767B

    System Levec Control Registers のライトプロテクションをセットするコマンドを定義。

  • fpga_set_cmd=run slcr_unlock_cmd && mw.l 0xF8000170 0x00101400 && run slcr_lock_cmd

    PL Clock をセットするコマンドを定義。

    ここでは FPGA0_CLK_CTRL に 50MHz=(SRCSEL=IOPLL(1GHz),DIVISOR0=20,DIVISOR1=1)をセットしています。

  • fpga_load_cmd=fatload mmc 0 0x03000000 design.bit && fpga loadb 0 0x03000000 $filesize

    FPGAビットストリームファイル(ここではdesign.bit)を FPGA にロードするコマンドを定義。

  • linux_load_cmd=fatload mmc 0 0x03000000 zImage && fatload mmc 0 0x02A00000 devicetree.dtb

    Linux Kernelのイメージファイル(zImage)とDevice Tree Blob(devicetree.dtb)をメモリにロードするコマンドを定義。

  • linux_boot_cmd=bootz 0x03000000 - 0x02A00000

    メモリにロードした Linux Kernel と Device Tree Blob を使ってブートするコマンドを定義。

  • uenvcmd=run fpga_load_cmd && run fpga_set_cmd && run linux_load_cmd && run linux_boot_cmd

    FPGAのロード、FPGAの設定、Linux のロードとブートを行うコマンドを定義。

    ブートの際、uEnv.txt に uenvcmd があると自動的にこのコマンドが実行されます。

    なお、uenvcmd がある程度の文字数(256?)以上だとブートに失敗することがあります。


参考