LoginSignup
5
2

More than 5 years have passed since last update.

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

Posted at

はじめに

「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?)以上だとブートに失敗することがあります。

参考

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2