FC2カウンター FPGAの部屋 IP
fc2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

AXI4-Stream Data Width Converter のバイト・レーン変換を確認する4

AXI4-Stream Data Width Converter のバイト・レーン変換を確認する3”の続き。

Pythonの画像処理ライブラリPillow(PIL)の Image.open でオープンした画像ファイルのフォーマットを確認するということで、前回は、blue.bmp、 green.bmp、 red.bmp を用意して、ZUBoard 1CG の PYNQ Linux にアップロードし、Jupter Notebook を書き換えて実行したところ、AXI4-Stream Data Width Converter で変換された 3 バイトの AXI4-Stream は上のバイトから RGB でなく BGR であることがわかった。前回の作業は、AXI4-Stream Data Width Converter は 2 個搭載されているが、その内の 3 バイト幅の AXI4-Stream から 4 バイト幅に変換する AXI4-Stream Data Width Converter の動作を見た。今回は、4 バイト幅の AXI4-Stream から 3 バイト幅に変換する AXI4-Stream Data Width Converter の動作を見た。

前回は、Vivado 2023.1 の i3filtes プロジェクトの i3filters ブロック・デザインの axis_dwidth_converter_1 の動作を観察した。
今回は、axis_dwidth_converter_0 の動作を観察する。
axis_dwidth_converter_0 は 4 バイト幅の AXI4-Stream から 3 バイト幅に変換する AXI4-Stream Data Width Converter だ。
zub1cg_pynq_195_230826.png

最初に blue.bmp を読み込んだときの axis_dwidth_converter_0 の AXI4-Stream インターフェースを観察した。
slot0 が axis_dwidth_converter_0 の入力の 4 バイト幅の AXI4-Stream インターフェースで、slot 1 が 3 バイト幅の AXI4-Stream インターフェースだ。
zub1cg_pynq_223_230830.png

3 バイト幅の AXI4-Stream インターフェースでは、0xfd までしか出ていないが、これは後ろのフィルタのスタート信号がまだ入っていなくてデータが流れていないためだと推測している。

次に green.bmp を読み込んだときの axis_dwidth_converter_0 の AXI4-Stream インターフェースを観察した。
zub1cg_pynq_224_230831.png

最後に red.bmp を読み込んだときの axis_dwidth_converter_0 の AXI4-Stream インターフェースを観察した。
zub1cg_pynq_226_230831.png

今までの AXI4-Stream Data Width Converter のバイト・レーン変換結果を表にまとめた。
zub1cg_pynq_227_230831.png

見事に BGR だった。
AXI4-Stream Subset Converter IP を使用して、AXI4-Stream のバイト・レーンを入れ替えることにする。
  1. 2023年08月31日 04:50 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream Data Width Converter のバイト・レーン変換を確認する3

AXI4-Stream Data Width Converter のバイト・レーン変換を確認する2”の続き。

Pythonの画像処理ライブラリPillow(PIL)の Image.open でオープンした画像ファイルのフォーマットを確認するということで、前回は、PNG の画像ファイルを ZUBoard 1CG の PYNQ Linux にアップロードし、Jupter Notebook を書き換えて実行したところ、PNG の画像ファイルは 4 チャネルでエラーが出てしまった。今回は、blue.bmp、 green.bmp、 red.bmp を用意して、ZUBoard 1CG の PYNQ Linux にアップロードし、Jupter Notebook を書き換えて実行したところ、AXI4-Stream Data Width Converter で変換された 3 バイトの AXI4-Stream は上のバイトから RGB でなく BGR であることがわかった。

特殊な BMP ファイルを Pinta で作った。
青、緑、赤それぞれ、最初のピクセルから 4 個だけ、値を 0xff, 0xfe, 0xfd, 0xfc にした。他のピクセルは 0xff とした。
blue.bmp、 green.bmp、 red.bmp を作成した。
下の図は、red.bmp の 4 番めのピクセルを 0xfc (252) にしたところだ。
zub1cg_pynq_215_230830.png

プライマリー色を赤 252 にして、鉛筆ツールで 4 番めのピクセルを書き換えた。
zub1cg_pynq_216_230830.png

出来上がった blue.bmp、 green.bmp、 red.bmp の画像ファイルを ZUBoard 1CG の PYNQ Linux にアップロードした。
zub1cg_pynq_217_230830.png

blue.bmp、 green.bmp、 red.bmp の画像ファイルを ~/jupyter_notebooks/examples/i3filters ディレクトリにコピーした。
cd
sudo mv *.bmp ~/jupyter_notebooks/examples/i3filters/


~/jupyter_notebooks/examples/i3filters ディレクトリに blue.bmp、 green.bmp、 red.bmp の画像ファイルがコピーされた。
zub1cg_pynq_218_230830.png

blue.bmp を Image.open() でオープンするように i3filters.ipynb を書き換えた。

#image_path = "./test2.jpg"
image_path = "./blue.bmp"
#image_path = "./green.bmp"
#image_path = "./red.bmp"
original_image = Image.open(image_path)


ガウシアン・フィルタ、メディアン・フィルタ、ソーベル・フィルタはすべてフィルタなしでスルーするように設定した。

gaussian.register_map.row_size = height
gaussian.register_map.col_size = width
gaussian.register_map.function_r = 2 # ORG_IMGwAxiDma
#gaussian.register_map.function_r = 3 # GAUSSIANwAxiDma

median.register_map.row_size = height
median.register_map.col_size = width
median.register_map.function_r = 2 # ORG_IMGwAxiDma
#median.register_map.function_r = 3 # MEDIANwAxiDma

sobel.register_map.row_size = height
sobel.register_map.col_size = width
sobel.register_map.function_r = 2 # ORG_IMGwAxiDma
#sobel.register_map.function_r = 3 # SOBELwAxiDma


i3filters.ipynb を実行して、bit ファイルをダウンロードする行を実行したら、Vivado で ILA ダッシュボードを表示する。
zub1cg_pynq_219_230830.png

Vivado 2023.1 で Project Navigator の PROGRAM AND DEBUG -> Open Hardware Manager -> Open Target をクリックして Auto Connect を選択すると、ILA ダッシュボードが表示された。
Trigger position in window を 512 から 100 に書き換えて、Run trigger for this ILA core の右向き三角ボタンをクリックして、トリガ待ちにした。

Jupyter Notebook で run_kernel() を実行すると、ILA ダッシュボードでトリガが掛かった。
zub1cg_pynq_220_230830.png

axis_dwidth_converter_1 (AXI4-Stream を 3 バイトから 4 バイトに変換する IP)の波形を示す。
slot2 が axis_dwidth_converter_1の出力(4 バイト)、slot3 が axis_dwidth_converter_1 の入力(3 バイト)となっている。
blue.bmp のときの波形を示す。
zub1cg_pynq_221_230830.png

green.bmp のときの波形を示す。
zub1cg_pynq_222_230830.png

red.bmp のときの波形を示す。
zub1cg_pynq_223_230830.png

見てお分かりの通りに、RGB だと思っていたのが、BGR だった。orz
  1. 2023年08月30日 05:05 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream Data Width Converter のバイト・レーン変換を確認する2

AXI4-Stream Data Width Converter のバイト・レーン変換を確認する1”の続き。

Pythonの画像処理ライブラリPillow(PIL)の Image.open でオープンした画像ファイルのフォーマットを確認するということで、前回は、blue.png、 green.png、 red.png の画像ファイルを作成した。今回は、PNG の画像ファイルを ZUBoard 1CG の PYNQ Linux にアップロードし、Jupter Notebook を書き換えて実行したところ、PNG の画像ファイルは 4 チャネルでエラーが出てしまった。

blue.png、 green.png、 red.png の画像ファイルを ZUBoard 1CG の PYNQ Linux にアップロードした。
zub1cg_pynq_208_230827.png

blue.png、 green.png、 red.png の画像ファイルを ~/jupyter_notebooks/examples/i3filters ディレクトリにコピーした。
cd
sudo mv blue.png ~/jupyter_notebooks/examples/i3filters
sudo mv green.png ~/jupyter_notebooks/examples/i3filters
sudo mv red.png ~/jupyter_notebooks/examples/i3filters


~/jupyter_notebooks/examples/i3filters ディレクトリに blue.png、 green.png、 red.png の画像ファイルがコピーされた。
zub1cg_pynq_209_230827.png

blue.png を Image.open() でオープンするように i3filters.ipynb を書き換えた。

#image_path = "./test2.jpg"
image_path = "./blue.png"
#image_path = "./green.png"
#image_path = "./red.png"
original_image = Image.open(image_path)


ガウシアン・フィルタ、メディアン・フィルタ、ソーベル・フィルタはすべてフィルタなしでスルーするように設定した。

gaussian.register_map.row_size = height
gaussian.register_map.col_size = width
gaussian.register_map.function_r = 2 # ORG_IMGwAxiDma
#gaussian.register_map.function_r = 3 # GAUSSIANwAxiDma


median.register_map.row_size = height
median.register_map.col_size = width
median.register_map.function_r = 2 # ORG_IMGwAxiDma
#median.register_map.function_r = 3 # MEDIANwAxiDma


sobel.register_map.row_size = height
sobel.register_map.col_size = width
sobel.register_map.function_r = 2 # ORG_IMGwAxiDma
#sobel.register_map.function_r = 3 # SOBELwAxiDma


これで、i3filters.ipynb を実行していったところ、DMA 用の共有物理メモリに読み込む行でエラーが発生した。
どうやら作成した png には、アルファ・チャネルがあって、RGB と alpha があるようだ。
そこでエラーが出ている。
zub1cg_pynq_210_230827.png
  1. 2023年08月29日 04:17 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

AXI4-Stream Data Width Converter のバイト・レーン変換を確認する1

ZUBoard 1CG の PYNQ v3.0.1 で自作のガウシアン・フィルタ、メディアン・フィルタとソーベル・フィルタを動作させる3”でガウシアン・フィルタの動作を確認できたが、ファイルをロードした 4 バイトのデータは AXI4-Stream Data Width Converter で 4 バイトを 3 バイトに変換してガウシアン・フィルタに入力されている。
このバイト変換は”PYNQ の画像ファイル・フォーマットを調査するために choose_RGB IP を Vitis HLS 2021.2 で作成する8”で解析して、4 バイトのデータが OpenCV の MAT 形式であることは確認してある。(MAT 形式のフォーマットについては、”Vitis Vision Library の AXI4 Master インターフェース版 medianblur をZYBO Z7-20 で使ってみる1(準備編)”を参照)
しかし、”PYNQ の画像ファイル・フォーマットを調査するために choose_RGB IP を Vitis HLS 2021.2 で作成する8”では、4 バイトを 3 バイトに変換した後についての検証が足りなかったので、もう一度、”ZUBoard 1CG の PYNQ v3.0.1 で自作のガウシアン・フィルタ、メディアン・フィルタとソーベル・フィルタを動作させる3”のハードウェアで検証してみよう。

まずは、色が単一の画像ファイルを作成する。
JPEG ファイルが無難だが、画像圧縮されていて、完全にはもとに戻らないので、PNG ファイルを作成してみる。

Ubuntu 22.04 の Pinta アプリケーションで、800 x 600 ピクセルの画像を新規作成する。
パレットからプライマリー色を右クリックすると”プライマリ色の選択”ダイアログが表示される。
赤を 0、緑を 0、青を 255 に変更する。これで青一色の画像になる。
zub1cg_pynq_204_230827.png

プライマリー色をペイント缶で画像全体に適用すると青一色の画像になった。
zub1cg_pynq_205_230827.png

これを blue.png に保存した。

同様に、”プライマリ色の選択”ダイアログで、赤を 255、緑を 0、青を 0 にして、ペイント缶で画像に適用した。
この画像を red.png として、保存した。
zub1cg_pynq_206_230827.png

同様に、”プライマリ色の選択”ダイアログで、赤を 0、緑を 255、青を 0 にして、ペイント缶で画像に適用した。
この画像を green.png として、保存した。
zub1cg_pynq_207_230827.png

今日は寝坊してしまったので、ここまでとする。
  1. 2023年08月28日 05:23 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

Clocking Wizard の Dynamic Reconfiguration mode を使ってみよう2

Clocking Wizard の Dynamic Reconfiguration mode を使ってみよう1”の続き。

PLL の出力周波数をソフトウェアで動的に変更する機能の Clocking Wizard の Dynamic Reconfiguration mode を使ってみようということで、Adom Taylor さんの”MicroZed Chronicles: Dynamic Clocking”を参照して、やってみたが、ブロック・デザインに手を加えて、カウンタのカウント数で周波数をカウントしている。ただし、出力周波数は設定とは少し異なっていた。今回は、clk_wiz_0 の s_axi_lite に System ILA を挿入して、XClk_Wiz_SetRate(&ClkWiz_Dynamic, 10); のアクセスを調べて、設定周波数ピッタリになるように設定を行ってみよう。

ブロック・デザインの clk_wiz_0 の s_axi_lite の配線を右クリックし、右クリックメニューから Debug を選択して、System ILA を挿入した。
Dynamic_Clocking_10_230526.png

論理合成、インプリメンテーション、ビットストリームの生成を行った。
ハードウェアをエクスポートした。

Vitis で DC_wrapper プラットフォームを右クリックし、Update Hardware Specification を選択して、ハードウェアの仕様をアップデートした。
Vitis の Explorer の Dynamic_Clocking_system をクリックし、トンカチボタンをクリックして、ビルドを行って成功した。
Vitis の Explorer の Dynamic_Clocking_system を右クリックし、右クリックメニューから Dubug As -> 1 Launch Hardware を選択してデバック・モードで起動した。
Dynamic_Clocking_11_230526.png

Vivado で Flow Navigator で PROGRAM AND DEBUG -> Open Hardware Manager -> Open Target をクリックし、Auto Connect を選択した。
ILA ダッシュボードが表示された。
Trigger Setup で AWVALID を選択して、Value を R にして、トリガをかけた。
Dynamic_Clocking_12_230526.png

Vitis で Step Over 実行していって、XClk_Wiz_SetRate(&ClkWiz_Dynamic, 10); でトリガがかかった。(実際はその前の行でもトリガがかかったが、もう一度かけなおした)
Dynamic_Clocking_13_230526.png

ILA ダッシュボードの波形を示す。
Dynamic_Clocking_14_230526.png

最初に 0x200 番地に 0x801 を書いて、次に、0x208 番地に 0x49 を書いている。
Clocking Wizard LogiCORE IP Product Guide (PG065)”によると

0x200 番地 ― Clock Configuration Register 0
Bit[25:16] = CLKFBOUT_FRAC Multiply = 0
Bit[15:8] = CLKFBOUT_MULT = 8
Bit[7:0] = DIVCLK_DIVIDE = 1

0x208 番地 - Clock Configuration Register 2
Bit[7:0] = CLKOUT0_DIVIDE = 0x49 (10進で 73)
 Integer part of clkout0 divide value
 For example, for 2.250, this value is 2 = 0x2
Bit[17:8] = CLKOUT0_FRAC Divide = 0
 Fractional part of clkout0 divide value
 For example, for 2.250, this value is 250 = 0xFA


Dynamic Reconfiguration mode における VCO Frequency は以下の式で表せるということだ。(”Clocking Wizard LogiCORE IP Product Guide (PG065)”を参照)
VCO Frequency = (Input Clock Frequency) * (CLKFBOUT_MULT)/DIVCLK_DIVIDE
よって
VCO Frequency = 100 MHz * 8 *1 = 800 MHz
出力周波数 = VCO Frequency / CLKOUT0_DIVIDE.CLKOUT0_FRAC Divide
出力周波数 = 800 MHz / 73.0 ≒ 10.959 MHz となって、この数値はおおむね正しい。

次に、10 MHz にしようとすると CLKOUT0_DIVIDE.CLKOUT0_FRAC Divide = 80.0 とすれば良いので、
CLKOUT0_DIVIDE = 0x50 (10進数で 80)、CLKOUT0_FRAC Divide = 0 とすればよい。つまり、

XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, 0x200, 0x0801);
XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, 0x208, 0x50);


とすれば良いはずだ。

25 MHz は

XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, 0x200, 0x0801);
XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, 0x208, 0x20);


20 MHz は

XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, 0x200, 0x0801);
XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, 0x208, 0x28);


と書き換えて、実行すると、その周波数にほとんど一致した。
Dynamic_Clocking_16_230526.png

XClk_Wiz_SetRate() は周波数がぴったりとは行かないので、その場合は、自分で値を設定した方が良いようだ。

最後に現在の Dynamic_Clocking.c を貼っておく。

// Dynamic_Clocking.c
// 2023/05/18 by marsee
// Referred to "MicroZed Chronicles: Dynamic Clocking"
// https://www.adiuvoengineering.com/post/microzed-chronicles-dynamic-clocking
// 2023/05/27 : Modified by marsee

#include <stdio.h>
#include "xclk_wiz.h"
#include "xgpio.h"
#include "xparameters.h"

XClk_Wiz ClkWiz_Dynamic;
XClk_Wiz_Config *CfgPtr_Dynamic;
XGpio gpio_0, gpio_1;

#define XCLK_WIZARD_DEVICE_ID       XPAR_CLK_WIZ_0_DEVICE_ID
#define XCLK_US_WIZ_RECONFIG_OFFSET 0x0000025C
#define CLK_LOCK            1

int main(){
    u32 count, locked;
    int Status;

    CfgPtr_Dynamic = XClk_Wiz_LookupConfig(XCLK_WIZARD_DEVICE_ID);
    XClk_Wiz_CfgInitialize(&ClkWiz_Dynamic, CfgPtr_Dynamic, CfgPtr_Dynamic->BaseAddr);
    XGpio_Initialize(&gpio_0, XPAR_AXI_GPIO_0_DEVICE_ID);
    XGpio_Initialize(&gpio_1, XPAR_AXI_GPIO_1_DEVICE_ID);

    printf("freq = 40 MHz\n");
    XGpio_DiscreteWrite(&gpio_1, 1, 1); // counter SCLR = 1, CE = 0
    usleep(1000);
    XGpio_DiscreteWrite(&gpio_1, 1, 2); // counter SCLR = 0, CE = 1
    sleep(1);
    XGpio_DiscreteWrite(&gpio_1, 1, 0); // counter SCLR = 0, CE = 0
    count = XGpio_DiscreteRead(&gpio_0, 1);
    locked = XGpio_DiscreteRead(&gpio_0, 2);
    printf("count = %d, locked = %d\n\n", count, locked);

    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, XCLK_WIZ_REG25_OFFSET, 0);
    //XClk_Wiz_SetRate(&ClkWiz_Dynamic, 10);
    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, 0x200, 0x0801);
    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, 0x208, 0x50);
    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr,
                       XCLK_US_WIZ_RECONFIG_OFFSET,
                       (XCLK_WIZ_RECONFIG_LOAD | XCLK_WIZ_RECONFIG_SADDR));
    Status = XClk_Wiz_WaitForLock(&ClkWiz_Dynamic);
    printf("freq = 10 MHz, XClk_Wiz_SetRate\n");
    XGpio_DiscreteWrite(&gpio_1, 1, 1); // counter SCLR = 1, CE = 0
    usleep(1000);
    XGpio_DiscreteWrite(&gpio_1, 1, 2); // counter SCLR = 0, CE = 1
    sleep(1);
    XGpio_DiscreteWrite(&gpio_1, 1, 0); // counter SCLR = 0, CE = 0
    count = XGpio_DiscreteRead(&gpio_0, 1);
    locked = XGpio_DiscreteRead(&gpio_0, 2);
    printf("count = %d, locked = %d\n\n", count, locked);

    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, XCLK_WIZ_REG25_OFFSET, 0);
    //XClk_Wiz_SetRate(&ClkWiz_Dynamic, 25);
    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, 0x200, 0x0801);
    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, 0x208, 0x20);
    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr,
                       XCLK_US_WIZ_RECONFIG_OFFSET,
                       (XCLK_WIZ_RECONFIG_LOAD | XCLK_WIZ_RECONFIG_SADDR));
    Status = XClk_Wiz_WaitForLock(&ClkWiz_Dynamic);
    printf("freq = 25 MHz\n");
    XGpio_DiscreteWrite(&gpio_1, 1, 1); // counter SCLR = 1, CE = 0
    usleep(1000);
    XGpio_DiscreteWrite(&gpio_1, 1, 2); // counter SCLR = 0, CE = 1
    sleep(1);
    XGpio_DiscreteWrite(&gpio_1, 1, 0); // counter SCLR = 0, CE = 0
    count = XGpio_DiscreteRead(&gpio_0, 1);
    locked = XGpio_DiscreteRead(&gpio_0, 2);
    printf("count = %d, locked = %d\n\n", count, locked);

    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, XCLK_WIZ_REG25_OFFSET, 0);
    //XClk_Wiz_SetRateHz(&ClkWiz_Dynamic, 20000000);
    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, 0x200, 0x0801);
    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, 0x208, 0x28);
    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr,
                       XCLK_US_WIZ_RECONFIG_OFFSET,
                       (XCLK_WIZ_RECONFIG_LOAD | XCLK_WIZ_RECONFIG_SADDR));
    Status = XClk_Wiz_WaitForLock(&ClkWiz_Dynamic);
    printf("freq = 20 MHz, XClk_Wiz_SetRateHz(&ClkWiz_Dynamic, 20000000);\n");
    XGpio_DiscreteWrite(&gpio_1, 1, 1); // counter SCLR = 1, CE = 0
    usleep(1000);
    XGpio_DiscreteWrite(&gpio_1, 1, 2); // counter SCLR = 0, CE = 1
    sleep(1);
    XGpio_DiscreteWrite(&gpio_1, 1, 0); // counter SCLR = 0, CE = 0
    count = XGpio_DiscreteRead(&gpio_0, 1);
    locked = XGpio_DiscreteRead(&gpio_0, 2);
    printf("count = %d, locked = %d\n\n", count, locked);

    return(0);
}

  1. 2023年05月27日 21:07 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

Clocking Wizard の Dynamic Reconfiguration mode を使ってみよう1

Clocking Wizard の Dynamic Reconfiguration mode を使ってみよう。つまり PLL の出力周波数をソフトウェアで動的に変更する機能だ。

なお、この記事は、Adom Taylor さんの”MicroZed Chronicles: Dynamic Clocking”を参照している。

ZYBO Z7-10 用の Dynamic_Clocking プロジェクトを Vivado 2022.2 で作成した。
Dynamic_Clocking_1_230526.png

MicroZed Chronicles: Dynamic Clocking”を参考にしてブロック・デザインを作成し、それにカウンタを追加してカウント値から Clocking Wizard のクロック出力の周波数を推定する。
なお、CDC は考えていない。簡易的な用途だということでお目こぼし願いたい。
Dynamic Clocking ブロック・デザインを示す。
Dynamic_Clocking_2_230526.png

processing_system7_0 の FCLK_CLK0 の動作周波数は 100 MHz に設定した。
Dynamic_Clocking_3_230526.png

Clocking Wizard の設定を示す。
Clocing Options タブでは Dynamic Reconfig にチェックを入れてある。
Dynamic_Clocking_4_230526.png

Output Clocks タブでは、clk_out1 を 40 MHz に設定した。
Dynamic_Clocking_5_230526.png

Address Editor 画面を示す。
Dynamic_Clocking_6_230526.png

HDL Wrapper を作成して、論理合成、インプリメンテーション、ビットストリームの生成を行った。
Project Summary を示す。
Dynamic_Clocking_7_230526.png

ハードウェアをエクスポートして、Vitis 2022.2 を起動した。
DC_wrapper プラットフォームと Dynamic_Clocking アプリケーション・プロジェクトを作成した。
Dynamic_Clocking アプリケーション・プロジェクトをビルドして、成功した。
Dynamic_Clocking_8_230526.png

Dynamic_Clocking アプリケーション・プロジェクトを実行したところ、Tera Term に結果が表示された。
Dynamic_Clocking_9_230526.png

最初の 1 秒間のカウントは 40000019 だった。この時は、40 MHz の出力周波数だった。

次に XClk_Wiz_SetRate(&ClkWiz_Dynamic, 10); で 10 MHz にしたときの 1 秒間のカウントは 10958909 だった。10.96 MHz 位とすると、結構ずれている気がする。

3 番目に XClk_Wiz_SetRate(&ClkWiz_Dynamic, 25); で 25 MHz にしたときの 1 秒間のカウントは 25806462 だった。25.81 MHz 位とすると、こちらも結構ずれている気がする。

4 番目に細かい周波数の設定がしたくて XClk_Wiz_SetRateHz(&ClkWiz_Dynamic, 20000000); で 20 MHz に設定したつもりだったが、1 秒間のカウントは 25806465 だった。3 番目の設定値から変更されていないようだ。

アプリケーション・ソフトウェアの Dynamic_Clocking.c を貼っておく。

// Dynamic_Clocking.c
// 2023/05/18 by marsee
// Referred to "MicroZed Chronicles: Dynamic Clocking"
// https://www.adiuvoengineering.com/post/microzed-chronicles-dynamic-clocking

#include <stdio.h>
#include "xclk_wiz.h"
#include "xgpio.h"
#include "xparameters.h"

XClk_Wiz ClkWiz_Dynamic;
XClk_Wiz_Config *CfgPtr_Dynamic;
XGpio gpio_0, gpio_1;

#define XCLK_WIZARD_DEVICE_ID       XPAR_CLK_WIZ_0_DEVICE_ID
#define XCLK_US_WIZ_RECONFIG_OFFSET 0x0000025C
#define CLK_LOCK            1

int main(){
    u32 count, locked;
    int Status;

    CfgPtr_Dynamic = XClk_Wiz_LookupConfig(XCLK_WIZARD_DEVICE_ID);
    XClk_Wiz_CfgInitialize(&ClkWiz_Dynamic, CfgPtr_Dynamic, CfgPtr_Dynamic->BaseAddr);
    XGpio_Initialize(&gpio_0, XPAR_AXI_GPIO_0_DEVICE_ID);
    XGpio_Initialize(&gpio_1, XPAR_AXI_GPIO_1_DEVICE_ID);

    printf("freq = 40 MHz\n");
    XGpio_DiscreteWrite(&gpio_1, 1, 1); // counter SCLR = 1, CE = 0
    usleep(1000);
    XGpio_DiscreteWrite(&gpio_1, 1, 2); // counter SCLR = 0, CE = 1
    sleep(1);
    XGpio_DiscreteWrite(&gpio_1, 1, 0); // counter SCLR = 0, CE = 0
    count = XGpio_DiscreteRead(&gpio_0, 1);
    locked = XGpio_DiscreteRead(&gpio_0, 2);
    printf("count = %d, locked = %d\n\n", count, locked);

    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, XCLK_WIZ_REG25_OFFSET, 0);
    XClk_Wiz_SetRate(&ClkWiz_Dynamic, 10);
    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr,
                       XCLK_US_WIZ_RECONFIG_OFFSET,
                       (XCLK_WIZ_RECONFIG_LOAD | XCLK_WIZ_RECONFIG_SADDR));
    Status = XClk_Wiz_WaitForLock(&ClkWiz_Dynamic);
    printf("freq = 10 MHz, XClk_Wiz_SetRate\n");
    XGpio_DiscreteWrite(&gpio_1, 1, 1); // counter SCLR = 1, CE = 0
    usleep(1000);
    XGpio_DiscreteWrite(&gpio_1, 1, 2); // counter SCLR = 0, CE = 1
    sleep(1);
    XGpio_DiscreteWrite(&gpio_1, 1, 0); // counter SCLR = 0, CE = 0
    count = XGpio_DiscreteRead(&gpio_0, 1);
    locked = XGpio_DiscreteRead(&gpio_0, 2);
    printf("count = %d, locked = %d\n\n", count, locked);

    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, XCLK_WIZ_REG25_OFFSET, 0);
    XClk_Wiz_SetRate(&ClkWiz_Dynamic, 25);
    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr,
                       XCLK_US_WIZ_RECONFIG_OFFSET,
                       (XCLK_WIZ_RECONFIG_LOAD | XCLK_WIZ_RECONFIG_SADDR));
    Status = XClk_Wiz_WaitForLock(&ClkWiz_Dynamic);
    printf("freq = 25 MHz\n");
    XGpio_DiscreteWrite(&gpio_1, 1, 1); // counter SCLR = 1, CE = 0
    usleep(1000);
    XGpio_DiscreteWrite(&gpio_1, 1, 2); // counter SCLR = 0, CE = 1
    sleep(1);
    XGpio_DiscreteWrite(&gpio_1, 1, 0); // counter SCLR = 0, CE = 0
    count = XGpio_DiscreteRead(&gpio_0, 1);
    locked = XGpio_DiscreteRead(&gpio_0, 2);
    printf("count = %d, locked = %d\n\n", count, locked);

    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr, XCLK_WIZ_REG25_OFFSET, 0);
    XClk_Wiz_SetRateHz(&ClkWiz_Dynamic, 20000000);
    XClk_Wiz_WriteReg(CfgPtr_Dynamic->BaseAddr,
                       XCLK_US_WIZ_RECONFIG_OFFSET,
                       (XCLK_WIZ_RECONFIG_LOAD | XCLK_WIZ_RECONFIG_SADDR));
    Status = XClk_Wiz_WaitForLock(&ClkWiz_Dynamic);
    printf("freq = 20 MHz, XClk_Wiz_SetRateHz(&ClkWiz_Dynamic, 20000000);\n");
    XGpio_DiscreteWrite(&gpio_1, 1, 1); // counter SCLR = 1, CE = 0
    usleep(1000);
    XGpio_DiscreteWrite(&gpio_1, 1, 2); // counter SCLR = 0, CE = 1
    sleep(1);
    XGpio_DiscreteWrite(&gpio_1, 1, 0); // counter SCLR = 0, CE = 0
    count = XGpio_DiscreteRead(&gpio_0, 1);
    locked = XGpio_DiscreteRead(&gpio_0, 2);
    printf("count = %d, locked = %d\n\n", count, locked);

    return(0);
}

  1. 2023年05月26日 04:32 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0

axi_timer を使う 3 (割り込みを使用する)

ikwzm さんの FPGA-SoC-Linux を ZYBO Z7-20 で起動して、axi_timer を使用したビットストリームを使用して、axi_timer を割り込みで使用したが、”axi_timerを使う2(割り込みを使用した)”のコードを参考に Debian Linux 上でアプリケーションを作成してもうまく行かなかった。

なお、axi timer については、”PG079 - AXI Timer v2.0 Product Guide (PG079) (v2.0)”を参照した。

どのような現象だったかと言うと、どんどん割り込みが掛かってしまった。割り込みした後の axi timer での割り込み信号の解除ができていなかった。System ILA で見ると、最初の設定からTCSR0の”T0INT”を 1 にしておかないと、最初からタイマー割込みが入ってしまう。

解決策は、”PG079 - AXI Timer v2.0 Product Guide (PG079) (v2.0)”の 13 ページの”Control/Status Register 0 (TCSR0)”の”T0INT”を 1 にしていなかったためだった。
割り込みの度に”Control/Status Register 0 (TCSR0)”の”T0INT”を 1 にして割り込みを解除する必要があった。
GPIO を使用して 0.5 秒ことに LED の点灯/消灯を繰り返すソフトウェアを示す。

// timer_test.c
// 2023/02/08 by marsee
// Reference URL:https://github.com/ikwzm/ZYBO_UIO_IRQ_SAMPLE/blob/master/c-sample/sample1.c

#include        <stdio.h>
#include        <stdint.h>
#include        <stdlib.h>
#include        <fcntl.h>
#include        <string.h>
#include        <time.h>
#include        <sys/time.h>
#include        <poll.h>
#include        <sys/types.h>
#include        <sys/mman.h>

#define ENABLE_ALL_TIMERS                (0x1<<10)
#define ENABLE_PULSE_WIDTH_MODULATION    (0x1<<9)
#define TIMER_INTERRUPT                    (0x1<<8)
#define ENABLE_TIMER                    (0x1<<7)
#define ENABLE_INTERRUPT                (0x1<<6)
#define LOAD_TIMER                        (0x1<<5)
#define AUTO_RELOAD_HOLD_TIMER            (0x1<<4)
#define ENABLE_EXT_CAPTURE_TRIG            (0x1<<3)
#define ENABLE_EXT_GENERATE_SIG            (0x1<<2)
#define DOWN_UP_COUNT_TIMER                (0x1<<1)
#define TIMER_MODE_CAP_GENE                (0x1)

int uio_irq_on(int uio_fd)
{
    unsigned int  irq_on = 1;
    write(uio_fd, &irq_on, sizeof(irq_on));
}

int uio_wait_irq(int uio_fd)
{
    unsigned int  count = 0;
    return read(uio_fd, &count,  sizeof(count));
}

void main()
{
    int            uio0_fd, uio1_fd;
    uint32_t    *axi_gpio, *axi_timer;
    static uint32_t led_stat = 0;

    if((uio0_fd = open("/dev/uio0", O_RDWR)) == -1) {
        printf("Can not open /dev/uio0\n");
        exit(1);
    }
    axi_gpio = (uint32_t*)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, uio0_fd, 0);
    
    if((uio1_fd = open("/dev/uio1", O_RDWR)) == -1) {
        printf("Can not open /dev/uio1\n");
        exit(1);
    }
    axi_timer = (uint32_t*)mmap(NULL, 0x10000, PROT_READ|PROT_WRITE, MAP_SHARED, uio1_fd, 0);
    
    // axi_timer_0 -> Timer0
    axi_timer[1] = 0x02FAF080; // TLR0, Decimal = 50,000,000, 100 MHz = 0.5sec
    axi_timer[0] = LOAD_TIMER; // TCR0, ENALL and LOAD0 = 1
    axi_timer[0] = TIMER_INTERRUPT | ENABLE_TIMER | ENABLE_INTERRUPT | AUTO_RELOAD_HOLD_TIMER | DOWN_UP_COUNT_TIMER; // timer interrupt clear
    
    for(int i=0; i<100; i++){
        if(uio_irq_on(uio1_fd) == -1){
            fprintf(stderr, "uio_irq_on error\n");
            break;
        }
        if(uio_wait_irq(uio1_fd) == -1){
            fprintf(stderr, "uio_wait_irq error\n");
            break;
        }
        
        axi_timer[0] = TIMER_INTERRUPT | ENABLE_TIMER | ENABLE_INTERRUPT | AUTO_RELOAD_HOLD_TIMER | DOWN_UP_COUNT_TIMER; // timer interrupt clear
        
        led_stat ^= 0xf;
        axi_gpio[0] = led_stat;
    }
    close(uio0_fd);
    close(uio1_fd);
}

  1. 2023年02月14日 04:15 |
  2. IP
  3. | トラックバック:0
  4. | コメント:0
»