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

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

FPGAの部屋

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

Vivado HLS の任意精度固定小数点データ型 ap_fixed 型にソフトウェアから直接データを入力するには?

Vivado HLS 2019.2 で合成した IP を Vivado で使ったが動作しなかった1(Vivado HLS 編 1)”の Vivado HLS 2019.2 でハードウェアにする ap_ufixed<8, 0, AP_TRN_ZERO, AP_SAT> in[784] の AXI4 Master のポートに入力するデータを”Vivado HLS 2019.2 で合成した IP を Vivado で使ったが動作しなかった8(動作しました)”で、uint8_t で定義された配列から入力した。
これは、任意精度固定小数点データ型の方が、2 進数の小数点位置を変更したものだったからだ。つまり、ap_ufixed<8, 0, AP_TRN_ZERO, AP_SAT> は uint8_t の小数点位置を 8 ビット左に移動したものだ。つまり、uint8_t の 0x01 は ap_ufixed<8, 0, AP_TRN_ZERO, AP_SAT> では、0.00390625 となる。そこで、 ap_ufixed<8, 0, AP_TRN_ZERO, AP_SAT> in[784] の AXI4 Master のポートに入力するデータを用意する場合は、uint8_t の配列を渡せばよい。uint8_t が 1/256 倍になっているのだが。。。
どうして、こういうことをするかというと、ソフトウェアには ap_ufixed<8, 0, AP_TRN_ZERO, AP_SAT> データ型が無いから、この型を使えないからだ。

それでは、符号なしの場合はこれで良いが、符号ありの場合の int8_t でデータを渡すための ap_fixed の型は何だろう?ということでやってみた。
それは、ap_fixed<8, 1, AP_TRN_ZERO, AP_SAT> だった。なお、ここでは、 AP_TRN_ZERO, AP_SAT は意味がない。 8, 1 が重要だ。
初めは、ap_fixed<8, 0, AP_TRN_ZERO, AP_SAT> かと思ったのだが、int8_t は 127 ~ -128 なので、正と負の値を取る。正の値の範囲は符号なしに比べて半分なので、整数値を符号として 1 ビット取る必要があるようだ。つまり、ap_fixed<8, 1, AP_TRN_ZERO, AP_SAT> のステップは 2 ^ -7 = 0.0078125 となる。
それでは、Vviado HLS で確かめてみよう。

AXI4 Master で 2 倍にする IP の DMA_multi2_ap_fixed プロジェクトを作る。

ソースコードの DMA_multi2_ap_fixed.cpp を示す。

// DMA_multi2_ap_fixed.cpp
// 2020/10/06 by marsee
//

#include <ap_fixed.h>

int DMA_multi2_ap_fixed(ap_fixed<8, 1, AP_TRN_ZERO, AP_SAT> *in, ap_fixed<8, 1, AP_TRN_ZERO, AP_SAT> *out){
#pragma HLS INTERFACE m_axi depth=10 port=out offset=slave
#pragma HLS INTERFACE m_axi depth=10 port=in offset=slave
#pragma HLS INTERFACE s_axilite port=return

    for (int i=0; i<10; i++){
#pragma HLS PIPELINE II=1
        ap_fixed<8, 1, AP_TRN_ZERO, AP_SAT> temp = in[i];
        out[i] = temp + temp;
    }

    return(0);
}


テストベンチの DMA_multi2_ap_fixed_tb.cpp を示す。

// DMA_multi2_ap_fixed_tb.cpp
// 2020/10/06 by marsee
//

#include <iostream>
#include <ap_fixed.h>

int DMA_multi2_ap_fixed(ap_fixed<8, 1, AP_TRN_ZERO, AP_SAT> *in, ap_fixed<8, 1, AP_TRN_ZERO, AP_SAT> *out);

int main(){
 ap_fixed<8, 1, AP_TRN_ZERO, AP_SAT> data[10] = {0, 0.0078125, 0.015625, 0.0234375, 0.03125, 0.0390625, 0.046875, 0.0546875, 0.5, -0.5};
 ap_fixed<8, 1, AP_TRN_ZERO, AP_SAT> result[10];

 DMA_multi2_ap_fixed(data, result);

    for(int i=0; i<10; i++){
        std::cout << "data[" << i << "]= " << data[i] <<
                ", result[" << i << "] = " <<
                result[i] << std::endl;
    }
}


Vivado HLS 2019.2 で DMA_multi2_ap_fixed プロジェクトを作成した。
DMA_multi2_ap_fixed_1_201007.png

C シミュレーションを行った。
DMA_multi2_ap_fixed_2_201007.png

ログを示す。

data[0]= 0, result[0] = 0
data[1]= 0.0078125, result[1] = 0.015625
data[2]= 0.015625, result[2] = 0.03125
data[3]= 0.0234375, result[3] = 0.046875
data[4]= 0.03125, result[4] = 0.0625
data[5]= 0.0390625, result[5] = 0.078125
data[6]= 0.046875, result[6] = 0.09375
data[7]= 0.0546875, result[7] = 0.109375
data[8]= 0.5, result[8] = 0.992188
data[9]= -0.5, result[9] = -1


-0.5 の 2 倍は -1 だが、0.5 の 2 倍は丸められて、0.992188 になっている。
-0.5 は 2 の補数で 0xC0、0.5 は 0x40 、-1 は 0x80、0.992188 は 0x7F のはずだ。

C コードの合成を行った。レポートを示す。
DMA_multi2_ap_fixed_3_201007.png

C/RTL協調シミュレーションの波形を示す。
DMA_multi2_ap_fixed_4_201007.png

最後の 2 倍の値が 80 と 7f になっているのが分かるだろうか?
ap_fixed<8, 1, AP_TRN_ZERO, AP_SAT> に入れるには、1/256 倍になった int8_t の値を入れれば良いようだ。

今回は、ap_ufixed の場合は 0 ~ 255 を 0 ~ 0.99609375 に割り当てた数(つまり正規化してある)について、ap_fixed の場合は -128 ~ +127 を -1 ~ +0.9921875 に割り当てた数についてやってみたが、8 ビット長の数については倍率が違っても、同じこととなる。符号なしで 0 ~ 1.9921875 や符号付で -2 ~ 1.984375 など。
また、uint16_t, int16_t, uint32_t, int32_t についてもレンジが違うが同じように扱うことができる。

(2020/12/25:追記)
ADXL355、3軸加速度センサーのリセット端子が無いか探していたが、0x2F番地に0x52を書くとリセットされる。
ただし、次の動作には待ち時間が必要、今のところ、1 msのWaitを入れてある。

acc_sensor_init(axi_iic_ad);

acc_sensor_write(axi_iic_ad, 0x3a, 0x2f, 0x52); // Reset
usleep(1000);
acc_sensor_write(axi_iic_ad, 0x3a, 0x2c, 0x82); // I2C speed is Hi speed, +-4g

  1. 2020年10月07日 05:00 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS で AXI4 Master を使用したときのビット幅2(いろいろなビット幅)

Vivado HLS で AXI4 Master を使用したときのビット幅1(128 ビットの例)”の続き。

Vivado HLS で AXI4 Master を使用したときに、32ビット幅以外の時はどのような実装になるかを調べてみたということで、前回は、128 ビット幅の例を試してみた。今回は、いろいろなビット幅を試してみよう。

まず、ビット幅がどこまで合成できるか?だが、まずは、AXI4 Master のビット幅が 512 ビットの場合をやってみよう。
DMA_pow2_XX_6_201005.png

これは、合成できたのだが、ZYBO Z7-10 では、DSP48E が 617 % と大幅にリソースをオーバーしている。
DMA_pow2_XX_7_201005.png

次に、、AXI4 Master のビット幅が 1024 ビットの場合をやってみた。
エラーになってしまったので、512 ビット幅までのようだ。
DMA_pow2_XX_8_201005.png

次に中途半端なビット幅ということで、127 ビット幅の場合をやってみた。
これは、

ERROR: [XFORM 203-801] Interface parameter bitwidth 'in.V' (DMA_pow2_XX/DMA_pow2_XX.cpp:9:1) must be a multiple of 8 for AXI4 master port.

でエラーになってしまう。
DMA_pow2_XX_9_201005.png

それじゃということで、120 ビット幅にしてみよう。これだと 8 の倍数だ。
これも、

ERROR: [XFORM 203-801] Bitwidth of (packed) data on axi master must be power of 2: Found 'in.V' (DMA_pow2_XX/DMA_pow2_XX.cpp:9:1) (packed) has a bitwidth of 120.

というエラーになった。
DMA_pow2_XX_10_201005.png

2 の n 乗ということで、それは、そうだよね。
それじゃ、ビット幅 8 は?ということで、8 ビット幅でやってみた。
DMA_pow2_XX_11_201005.png

これは合成できた。
DMA_pow2_XX_12_201005.png

合成された VHDL ファイルの entity 部分を引用する。

entity DMA_pow2_XX is
generic (
    C_M_AXI_GMEM_ADDR_WIDTH : INTEGER := 32;
    C_M_AXI_GMEM_ID_WIDTH : INTEGER := 1;
    C_M_AXI_GMEM_AWUSER_WIDTH : INTEGER := 1;
    C_M_AXI_GMEM_DATA_WIDTH : INTEGER := 32;
    C_M_AXI_GMEM_WUSER_WIDTH : INTEGER := 1;
    C_M_AXI_GMEM_ARUSER_WIDTH : INTEGER := 1;
    C_M_AXI_GMEM_RUSER_WIDTH : INTEGER := 1;
    C_M_AXI_GMEM_BUSER_WIDTH : INTEGER := 1;
    C_S_AXI_AXILITES_ADDR_WIDTH : INTEGER := 6;
    C_S_AXI_AXILITES_DATA_WIDTH : INTEGER := 32;
    C_M_AXI_GMEM_USER_VALUE : INTEGER := 0;
    C_M_AXI_GMEM_PROT_VALUE : INTEGER := 0;
    C_M_AXI_GMEM_CACHE_VALUE : INTEGER := 3 );
port (
    ap_clk : IN STD_LOGIC;
    ap_rst_n : IN STD_LOGIC;
    m_axi_gmem_AWVALID : OUT STD_LOGIC;
    m_axi_gmem_AWREADY : IN STD_LOGIC;
    m_axi_gmem_AWADDR : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_ADDR_WIDTH-1 downto 0);
    m_axi_gmem_AWID : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_ID_WIDTH-1 downto 0);
    m_axi_gmem_AWLEN : OUT STD_LOGIC_VECTOR (7 downto 0);
    m_axi_gmem_AWSIZE : OUT STD_LOGIC_VECTOR (2 downto 0);
    m_axi_gmem_AWBURST : OUT STD_LOGIC_VECTOR (1 downto 0);
    m_axi_gmem_AWLOCK : OUT STD_LOGIC_VECTOR (1 downto 0);
    m_axi_gmem_AWCACHE : OUT STD_LOGIC_VECTOR (3 downto 0);
    m_axi_gmem_AWPROT : OUT STD_LOGIC_VECTOR (2 downto 0);
    m_axi_gmem_AWQOS : OUT STD_LOGIC_VECTOR (3 downto 0);
    m_axi_gmem_AWREGION : OUT STD_LOGIC_VECTOR (3 downto 0);
    m_axi_gmem_AWUSER : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_AWUSER_WIDTH-1 downto 0);
    m_axi_gmem_WVALID : OUT STD_LOGIC;
    m_axi_gmem_WREADY : IN STD_LOGIC;
    m_axi_gmem_WDATA : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_DATA_WIDTH-1 downto 0);
    m_axi_gmem_WSTRB : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_DATA_WIDTH/8-1 downto 0);
    m_axi_gmem_WLAST : OUT STD_LOGIC;
    m_axi_gmem_WID : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_ID_WIDTH-1 downto 0);
    m_axi_gmem_WUSER : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_WUSER_WIDTH-1 downto 0);
    m_axi_gmem_ARVALID : OUT STD_LOGIC;
    m_axi_gmem_ARREADY : IN STD_LOGIC;
    m_axi_gmem_ARADDR : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_ADDR_WIDTH-1 downto 0);
    m_axi_gmem_ARID : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_ID_WIDTH-1 downto 0);
    m_axi_gmem_ARLEN : OUT STD_LOGIC_VECTOR (7 downto 0);
    m_axi_gmem_ARSIZE : OUT STD_LOGIC_VECTOR (2 downto 0);
    m_axi_gmem_ARBURST : OUT STD_LOGIC_VECTOR (1 downto 0);
    m_axi_gmem_ARLOCK : OUT STD_LOGIC_VECTOR (1 downto 0);
    m_axi_gmem_ARCACHE : OUT STD_LOGIC_VECTOR (3 downto 0);
    m_axi_gmem_ARPROT : OUT STD_LOGIC_VECTOR (2 downto 0);
    m_axi_gmem_ARQOS : OUT STD_LOGIC_VECTOR (3 downto 0);
    m_axi_gmem_ARREGION : OUT STD_LOGIC_VECTOR (3 downto 0);
    m_axi_gmem_ARUSER : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_ARUSER_WIDTH-1 downto 0);
    m_axi_gmem_RVALID : IN STD_LOGIC;
    m_axi_gmem_RREADY : OUT STD_LOGIC;
    m_axi_gmem_RDATA : IN STD_LOGIC_VECTOR (C_M_AXI_GMEM_DATA_WIDTH-1 downto 0);
    m_axi_gmem_RLAST : IN STD_LOGIC;
    m_axi_gmem_RID : IN STD_LOGIC_VECTOR (C_M_AXI_GMEM_ID_WIDTH-1 downto 0);
    m_axi_gmem_RUSER : IN STD_LOGIC_VECTOR (C_M_AXI_GMEM_RUSER_WIDTH-1 downto 0);
    m_axi_gmem_RRESP : IN STD_LOGIC_VECTOR (1 downto 0);
    m_axi_gmem_BVALID : IN STD_LOGIC;
    m_axi_gmem_BREADY : OUT STD_LOGIC;
    m_axi_gmem_BRESP : IN STD_LOGIC_VECTOR (1 downto 0);
    m_axi_gmem_BID : IN STD_LOGIC_VECTOR (C_M_AXI_GMEM_ID_WIDTH-1 downto 0);
    m_axi_gmem_BUSER : IN STD_LOGIC_VECTOR (C_M_AXI_GMEM_BUSER_WIDTH-1 downto 0);
    s_axi_AXILiteS_AWVALID : IN STD_LOGIC;
    s_axi_AXILiteS_AWREADY : OUT STD_LOGIC;
    s_axi_AXILiteS_AWADDR : IN STD_LOGIC_VECTOR (C_S_AXI_AXILITES_ADDR_WIDTH-1 downto 0);
    s_axi_AXILiteS_WVALID : IN STD_LOGIC;
    s_axi_AXILiteS_WREADY : OUT STD_LOGIC;
    s_axi_AXILiteS_WDATA : IN STD_LOGIC_VECTOR (C_S_AXI_AXILITES_DATA_WIDTH-1 downto 0);
    s_axi_AXILiteS_WSTRB : IN STD_LOGIC_VECTOR (C_S_AXI_AXILITES_DATA_WIDTH/8-1 downto 0);
    s_axi_AXILiteS_ARVALID : IN STD_LOGIC;
    s_axi_AXILiteS_ARREADY : OUT STD_LOGIC;
    s_axi_AXILiteS_ARADDR : IN STD_LOGIC_VECTOR (C_S_AXI_AXILITES_ADDR_WIDTH-1 downto 0);
    s_axi_AXILiteS_RVALID : OUT STD_LOGIC;
    s_axi_AXILiteS_RREADY : IN STD_LOGIC;
    s_axi_AXILiteS_RDATA : OUT STD_LOGIC_VECTOR (C_S_AXI_AXILITES_DATA_WIDTH-1 downto 0);
    s_axi_AXILiteS_RRESP : OUT STD_LOGIC_VECTOR (1 downto 0);
    s_axi_AXILiteS_BVALID : OUT STD_LOGIC;
    s_axi_AXILiteS_BREADY : IN STD_LOGIC;
    s_axi_AXILiteS_BRESP : OUT STD_LOGIC_VECTOR (1 downto 0);
    interrupt : OUT STD_LOGIC );
end;


C/RTL協調シミュレーションの波形を示す。
DMA_pow2_XX_13_201005.png

32 ビット幅の AXI4 インターフェースで 4 個の 8 ビット幅データを一度に転送している。
16ビット幅の場合も 32 ビット幅の AXI4 インターフェースで 2 個の 16 ビット幅データを一度に転送している。
AXI4 Master は 32 ビット幅が最小のようだ。
  1. 2020年10月06日 03:24 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS で AXI4 Master を使用したときのビット幅1(128 ビットの例)

Vivado HLS で AXI4 Master を使用したときに、32ビット幅以外の時はどのような実装になるかを調べてみた。
使用するプロジェクトは DMA_pow2_XX プロジェクトだ。
これは、AXI4 Master にする引数を ap_int で書いてあって、いろいろなデータ幅にすることができる。

初めに 128 ビットのデータ幅にしてみよう。
DMA_pow2_XX.h を示す。

// DMA_pow2_XX.h
// 2020/09/25 by marsee
//

#ifndef __DMA_POW2_XX_H__
#define __DMA_POW2_XX_H__

#define BIT_LEN 128

#endif


DMA_pow2_XX.cpp を示す。

// DMA_pow2_XX.cpp
// 2020/09/25 by marsee

#include <ap_int.h>
#include "DMA_pow2_XX.h"

int DMA_pow2_XX(volatile ap_int<BIT_LEN> *in, volatile ap_int<BIT_LEN> *out){
#pragma HLS INTERFACE m_axi depth=10 port=out offset=slave
#pragma HLS INTERFACE m_axi depth=10 port=in offset=slave
#pragma HLS INTERFACE s_axilite port=return

    for (int i=0; i<10; i++){
#pragma HLS PIPELINE II=1
        ap_int<BIT_LEN> temp = in[i];
        out[i] = temp * temp;
    }

    return(0);
}


DMA_pow2_XX_tb.cpp を示す。

// DMA_pow2_XX_tb.cpp
// 2020/09/25 by marsee
//

#include <iostream>
#include <ap_int.h>
#include "DMA_pow2_XX.h"

int DMA_pow2_XX(volatile ap_int<BIT_LEN> *in, volatile ap_int<BIT_LEN> *out);

int main(){
    ap_int<BIT_LEN> data[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    ap_int<BIT_LEN> result[10];

    DMA_pow2_XX(data, result);

    for(int i=0; i<10; i++){
        std::cout << "data[" << i << "]= " << data[i] <<
                ", result[" << i << "] = " <<
                result[i] << std::endl;
    }
}


DMA_pow2_XX プロジェクトを示す。
DMA_pow2_XX_3_201005.png

C シミュレーションを行った。出力は問題ない。
DMA_pow2_XX_1_201005.png

C コードの合成を行った。
DMA_pow2_XX_2_201005.png

出力されたトップの VHDL コードの entity 部分を見てみよう。

entity DMA_pow2_XX is
generic (
    C_M_AXI_GMEM_ADDR_WIDTH : INTEGER := 32;
    C_M_AXI_GMEM_ID_WIDTH : INTEGER := 1;
    C_M_AXI_GMEM_AWUSER_WIDTH : INTEGER := 1;
    C_M_AXI_GMEM_DATA_WIDTH : INTEGER := 128;
    C_M_AXI_GMEM_WUSER_WIDTH : INTEGER := 1;
    C_M_AXI_GMEM_ARUSER_WIDTH : INTEGER := 1;
    C_M_AXI_GMEM_RUSER_WIDTH : INTEGER := 1;
    C_M_AXI_GMEM_BUSER_WIDTH : INTEGER := 1;
    C_S_AXI_AXILITES_ADDR_WIDTH : INTEGER := 6;
    C_S_AXI_AXILITES_DATA_WIDTH : INTEGER := 32;
    C_M_AXI_GMEM_USER_VALUE : INTEGER := 0;
    C_M_AXI_GMEM_PROT_VALUE : INTEGER := 0;
    C_M_AXI_GMEM_CACHE_VALUE : INTEGER := 3 );
port (
    ap_clk : IN STD_LOGIC;
    ap_rst_n : IN STD_LOGIC;
    m_axi_gmem_AWVALID : OUT STD_LOGIC;
    m_axi_gmem_AWREADY : IN STD_LOGIC;
    m_axi_gmem_AWADDR : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_ADDR_WIDTH-1 downto 0);
    m_axi_gmem_AWID : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_ID_WIDTH-1 downto 0);
    m_axi_gmem_AWLEN : OUT STD_LOGIC_VECTOR (7 downto 0);
    m_axi_gmem_AWSIZE : OUT STD_LOGIC_VECTOR (2 downto 0);
    m_axi_gmem_AWBURST : OUT STD_LOGIC_VECTOR (1 downto 0);
    m_axi_gmem_AWLOCK : OUT STD_LOGIC_VECTOR (1 downto 0);
    m_axi_gmem_AWCACHE : OUT STD_LOGIC_VECTOR (3 downto 0);
    m_axi_gmem_AWPROT : OUT STD_LOGIC_VECTOR (2 downto 0);
    m_axi_gmem_AWQOS : OUT STD_LOGIC_VECTOR (3 downto 0);
    m_axi_gmem_AWREGION : OUT STD_LOGIC_VECTOR (3 downto 0);
    m_axi_gmem_AWUSER : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_AWUSER_WIDTH-1 downto 0);
    m_axi_gmem_WVALID : OUT STD_LOGIC;
    m_axi_gmem_WREADY : IN STD_LOGIC;
    m_axi_gmem_WDATA : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_DATA_WIDTH-1 downto 0);
    m_axi_gmem_WSTRB : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_DATA_WIDTH/8-1 downto 0);
    m_axi_gmem_WLAST : OUT STD_LOGIC;
    m_axi_gmem_WID : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_ID_WIDTH-1 downto 0);
    m_axi_gmem_WUSER : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_WUSER_WIDTH-1 downto 0);
    m_axi_gmem_ARVALID : OUT STD_LOGIC;
    m_axi_gmem_ARREADY : IN STD_LOGIC;
    m_axi_gmem_ARADDR : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_ADDR_WIDTH-1 downto 0);
    m_axi_gmem_ARID : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_ID_WIDTH-1 downto 0);
    m_axi_gmem_ARLEN : OUT STD_LOGIC_VECTOR (7 downto 0);
    m_axi_gmem_ARSIZE : OUT STD_LOGIC_VECTOR (2 downto 0);
    m_axi_gmem_ARBURST : OUT STD_LOGIC_VECTOR (1 downto 0);
    m_axi_gmem_ARLOCK : OUT STD_LOGIC_VECTOR (1 downto 0);
    m_axi_gmem_ARCACHE : OUT STD_LOGIC_VECTOR (3 downto 0);
    m_axi_gmem_ARPROT : OUT STD_LOGIC_VECTOR (2 downto 0);
    m_axi_gmem_ARQOS : OUT STD_LOGIC_VECTOR (3 downto 0);
    m_axi_gmem_ARREGION : OUT STD_LOGIC_VECTOR (3 downto 0);
    m_axi_gmem_ARUSER : OUT STD_LOGIC_VECTOR (C_M_AXI_GMEM_ARUSER_WIDTH-1 downto 0);
    m_axi_gmem_RVALID : IN STD_LOGIC;
    m_axi_gmem_RREADY : OUT STD_LOGIC;
    m_axi_gmem_RDATA : IN STD_LOGIC_VECTOR (C_M_AXI_GMEM_DATA_WIDTH-1 downto 0);
    m_axi_gmem_RLAST : IN STD_LOGIC;
    m_axi_gmem_RID : IN STD_LOGIC_VECTOR (C_M_AXI_GMEM_ID_WIDTH-1 downto 0);
    m_axi_gmem_RUSER : IN STD_LOGIC_VECTOR (C_M_AXI_GMEM_RUSER_WIDTH-1 downto 0);
    m_axi_gmem_RRESP : IN STD_LOGIC_VECTOR (1 downto 0);
    m_axi_gmem_BVALID : IN STD_LOGIC;
    m_axi_gmem_BREADY : OUT STD_LOGIC;
    m_axi_gmem_BRESP : IN STD_LOGIC_VECTOR (1 downto 0);
    m_axi_gmem_BID : IN STD_LOGIC_VECTOR (C_M_AXI_GMEM_ID_WIDTH-1 downto 0);
    m_axi_gmem_BUSER : IN STD_LOGIC_VECTOR (C_M_AXI_GMEM_BUSER_WIDTH-1 downto 0);
    s_axi_AXILiteS_AWVALID : IN STD_LOGIC;
    s_axi_AXILiteS_AWREADY : OUT STD_LOGIC;
    s_axi_AXILiteS_AWADDR : IN STD_LOGIC_VECTOR (C_S_AXI_AXILITES_ADDR_WIDTH-1 downto 0);
    s_axi_AXILiteS_WVALID : IN STD_LOGIC;
    s_axi_AXILiteS_WREADY : OUT STD_LOGIC;
    s_axi_AXILiteS_WDATA : IN STD_LOGIC_VECTOR (C_S_AXI_AXILITES_DATA_WIDTH-1 downto 0);
    s_axi_AXILiteS_WSTRB : IN STD_LOGIC_VECTOR (C_S_AXI_AXILITES_DATA_WIDTH/8-1 downto 0);
    s_axi_AXILiteS_ARVALID : IN STD_LOGIC;
    s_axi_AXILiteS_ARREADY : OUT STD_LOGIC;
    s_axi_AXILiteS_ARADDR : IN STD_LOGIC_VECTOR (C_S_AXI_AXILITES_ADDR_WIDTH-1 downto 0);
    s_axi_AXILiteS_RVALID : OUT STD_LOGIC;
    s_axi_AXILiteS_RREADY : IN STD_LOGIC;
    s_axi_AXILiteS_RDATA : OUT STD_LOGIC_VECTOR (C_S_AXI_AXILITES_DATA_WIDTH-1 downto 0);
    s_axi_AXILiteS_RRESP : OUT STD_LOGIC_VECTOR (1 downto 0);
    s_axi_AXILiteS_BVALID : OUT STD_LOGIC;
    s_axi_AXILiteS_BREADY : IN STD_LOGIC;
    s_axi_AXILiteS_BRESP : OUT STD_LOGIC_VECTOR (1 downto 0);
    interrupt : OUT STD_LOGIC );
end;


C_M_AXI_GMEM_DATA_WIDTH が 128 ビット幅になっている。

C/RTL協調シミュレーションを行った。
DMA_pow2_XX_4_201005.png

C/RTL協調シミュレーションの波形を示す。
RDATA と WDATA が 128 ビットになっているのが分かる。
DMA_pow2_XX_5_201005.png

データ幅が 128 ビットでも問題なく、Vivado HLS 2019.2 で実装できた。
次は、32 ビット幅以下の値を ap_int に書いた場合にどうなるのか?を調べてみたい。
  1. 2020年10月05日 05:17 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2019.2 で合成した IP を Vivado で使ったが動作しなかった8(動作しました)

Vivado HLS 2019.2 で合成した IP を Vivado で使ったが動作しなかった7(デバック編 3)”の続き。

Vivado HLS 2019.2 でニューラルネットワーク(NN)の IP を作成し、Vivado で IP を使用して回路を作成したが、動作しないという問題があった。これは、NN IP への AXI4-Lite インターフェースのトランザクションが出ていなかったが、”Vivado HLS 2019.2 で合成した IP を Vivado で使ったが動作しなかった7(デバック編 3)”で、main() 関数のソフトウェアによるニューラルネットワークを計算する部分を削除すると、NN IP への AXI4-Lite インターフェースのトランザクションが出るようになった。そこで、”Vitis のデフォルトの Stack size, Heap size は 8k バイトだった”で、スタックサイズを 128 k バイトに拡大すると、main() 関数のソフトウェアによるニューラルネットワークを計算する部分を削除しなくても AXI4-Lite インターフェースのトランザクションが出るようになった。Run すると正常な出力が出た。完成だ。結局 script.ld のスタック容量が足りないのが原因だったようだ。心配していたバイトレーンのエンディアンの影響も無かった。

まずは、Vitis の GUI 画面を示す。mnist_nn_test アプリケーション・プロジェクトを使用する。
mnist_nn_61_200929.png

、”Vitis のデフォルトの Stack size, Heap size は 8k バイトだった”で、スタックサイズを 128 k バイトに拡大した。
mnist_nn_60_200928.png

Vitis の Assistant ウインドウの mnist_nn_test_system -> mnist_nn_test -> Debug を右クリックし、右クリックメニューから Run -> Run Configuration を指定して Run する。
mnist_nn_62_200929.png

Tera Term に結果が表示された。これは正しい。
mnist_nn_68_200929.png

なお、実行時間も計測してみた。ハードウェアが約 2.39 ms 、ソフトウェアが約 2.59 ms で、ハードウェアの方がわずかに速いという結果になった。(なお、NN IP の動作周波数は 50 MHz で、当初の 100 MHz の 1/2 になっている。C コードの合成でのレイテンシは動作周波数 100 MHz で 1.18 ms だった。)

これで正常動作でめでたいのだが、この mnist_nn は Vivado HLS および Vitis HLS のバージョンによって IP の大きさにとっても差があるので、書いておきたい。なお、ソースコードは”Vivado HLS 2019.2 で合成した IP を Vivado で使ったが動作しなかった1(Vivado HLS 編 1)”に貼ってある。

まずは、今使用している Vivado HLS 2019.2 、Vivado HLS 2018.3、Vitis HLS 2020.1、Vivado HLS 2017.4 の Export RTL のレポートを示す。
mnist_nn_64_200929.pngmnist_nn_65_200929.pngmnist_nn_66_200929.pngmnist_nn_67_200929.png

Vivado HLS 2019.2 と 2018.3 の BRAM 使用量は 6 個だが、Vitis HLS 2020.1 の BRAM 使用量は 43 個、Vivado HLS 2017.4 の BRAM 使用量は 42 個だった。何でこんなに違うのだろうか?
Vivado HLS 2019.2 の IP は動作した。Vitis HLS 2020.1 で作った IP も Vivado 2019.2 のブロックデザインに入れてみたが動作した。Vitis HLS 2020.1 の IP のレイテンシは約 400 us なので、Vivado HLS 2019.2 よりも 2 倍以上速い。

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

// mnist_nn_test.c
// 2020/09/08 by marsee
//

#include <stdio.h>
#include <stdint.h>

#include "xtime_l.h"

#include "af1_weight_float.h"
#include "af1_bias_float.h"
#include "af2_weight_float.h"
#include "af2_bias_float.h"
#include "mnist_data_10.h"
#include "xmnist_nn.h"

int mnist_nn_float(float in[784], float out[10]);
int max_float(float out[10]);
int max_int32_t(int32_t out[10]);

void Xil_DCacheFlush(void);

#define NUM_ITERATIONS    10 // C Simulation
// #define NUM_ITERATIONS    2 // C/RTL CoSimulation

int main(){
    float t_tran_float[NUM_ITERATIONS][784];
    uint8_t t_tran_uint8_t[NUM_ITERATIONS][784];
    int32_t result_hard[NUM_ITERATIONS][10];
    float result_soft[NUM_ITERATIONS][10];
    int max_id_hw, max_id_sw, max_id_ref;
    XMnist_nn mnits_nn_ap;
    int32_t res;
    XTime hw_start_time, hw_end_time;
    XTime sw_start_time, sw_end_time;

    for(int i=0; i<NUM_ITERATIONS; i++){
        for(int j=0; j<784; j++){
            t_tran_float[i][j] = (float)(t_train_256[i][j])/256.0;
            t_tran_uint8_t[i][j] = (uint32_t)(t_train_256[i][j]);
        }
    }
    Xil_DCacheFlush();

    // Initialize tht Device
    int XMinst_status = XMnist_nn_Initialize(&mnits_nn_ap, 0);
    if (XMinst_status != XST_SUCCESS){
        fprintf(stderr, "Could not Initialize XMnist_nn\n");
        return(-1);
    }

    for(int i=0; i<NUM_ITERATIONS; i++){
        u32 char_num = (u32)(&t_tran_uint8_t[i][0]);
        XMnist_nn_Set_in_V(&mnits_nn_ap, char_num);

        XTime_GetTime(&hw_start_time);
        XMnist_nn_Start(&mnits_nn_ap);

        while(!XMnist_nn_IsDone(&mnits_nn_ap));
        XTime_GetTime(&hw_end_time);

        // minst nn result check
        for(int j=0; j<5; j++){
            XMnist_nn_Read_out_V_Words(&mnits_nn_ap, j, &res, 1);
            result_hard[i][j*2] = res & 0x1fff; // 13 bit
            if(result_hard[i][j*2] & 0x1000) // minus
                result_hard[i][j*2] = 0xffffe000 | result_hard[i][j*2]; // Sign extension

            result_hard[i][j*2+1] = (res & 0x1fff0000) >> 16;
            if(result_hard[i][j*2+1] & 0x1000) // minus
                result_hard[i][j*2+1] = 0xffffe000 | result_hard[i][j*2+1]; // Sign extension
        }
        XTime_GetTime(&sw_start_time);
        mnist_nn_float(&t_tran_float[i][0], &result_soft[i][0]);
        XTime_GetTime(&sw_end_time);
        printf("i = %d, HW Execution time = %lf ms, SW Execution time = %lf ms\n", i,
                (double)((long long int)hw_end_time-(long long int)hw_start_time)/333333.0,
                (double)((long long int)sw_end_time-(long long int)sw_start_time)/333333.0);
    }

    int errflag=0;
    for(int i=0; i<NUM_ITERATIONS; i++){
        max_id_hw = max_int32_t(&result_hard[i][0]);
        max_id_sw = max_float(&result_soft[i][0]);
        max_id_ref = max_float(&t_test[i][0]);

        if(max_id_ref != max_id_hw){
            printf("id = %d, max_id_ref = %d, max_id_hw = %d\n", i, max_id_ref, max_id_hw);
            errflag = 1;
        }
        if(max_id_ref != max_id_sw){
            printf("id = %d, max_id_ref = %d, max_id_sw = %d\n", i, max_id_ref, max_id_sw);
            errflag = 1;
        }
    }
    if(errflag == 0)
        printf("No Error\n");

    return(0);
}

int mnist_nn_float(float in[784], float out[10]){
    float dot1[50];
    float dot2[10];

    for(int col=0; col<50; col++){
        dot1[col] = 0;
        for(int row=0; row<784; row++){
            dot1[col] += in[row]*af1_fweight[row][col];
        }
        dot1[col] += af1_fbias[col];

        if(dot1[col] < 0)    // ReLU
            dot1[col] = 0;
    }

    for(int col=0; col<10; col++){
        dot2[col] = 0;
        for(int row=0; row<50; row++){
            dot2[col] += dot1[row]*af2_fweight[row][col];
        }
        dot2[col] += af2_fbias[col];

        if(dot2[col] < 0)    // ReLU
            dot2[col] = 0;
        out[col] = dot2[col];
    }

    return(0);
}

int max_float(float out[10]){
    int max_id;
    float max;

    for(int i=0; i<10; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    return(max_id);
}

int max_int32_t(int32_t out[10]){
    int max_id;
    int32_t max;

    for(int i=0; i<10; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    return(max_id);
}

  1. 2020年09月29日 05:09 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2019.2 で合成した IP を Vivado で使ったが動作しなかった7(デバック編 3)

Vivado HLS 2019.2 で合成した IP を Vivado で使ったが動作しなかった6(デバック編 2)”の続き。

Vivado HLS 2019.2 でニューラルネットワークの IP を作成し、Vivado で IP を使用して回路を作成したが、動作しなかった。その顛末を書くことにしたということで、Vivado Analyzer を挿入して AXI-Lite インターフェースのアクセスがあるかどうか?を調べたが、NN IP のアクセスは無かった。前回は、この構成でAXI-Lite インターフェースのアクセスがあるか?を調べるために DMA_pow2 IP (2乗 IP)を Add IP してアクセスを調べたところ、問題なくアクセスが来ていた。今回は、NN IP でもアクセスが来るようになったので、その顛末を書いておく。

もしかして、制御用のアプリケーション・ソフトウェアが多すぎるのか?と思い、ソフトウェアでの NN 計算を止めることにした。そうすると、重みやバイアスが無くなるので、大きくプログラムサイズが減ることになる。そこで、その部分を削除したソフトウェアを書いた。
新しい、mnist_nn_test.c を示す。

// mnist_nn_test.c
// 2020/09/08 by marsee
// 2020/09/24 : removed weights and biases.
//

#include <stdio.h>
#include <stdint.h>

#include "mnist_data_10.h"
#include "xmnist_nn.h"

int max_float(float out[10]);
int max_int32_t(int32_t out[10]);

#define NUM_ITERATIONS    10 // C Simulation
// #define NUM_ITERATIONS    2 // C/RTL CoSimulation

int main(){
    uint8_t t_tran_uint8_t[NUM_ITERATIONS][784];
    int32_t result_hard[NUM_ITERATIONS][10];
    int max_id_hw, max_id_ref;
    XMnist_nn mnits_nn_ap;
    int32_t res;

    for(int i=0; i<NUM_ITERATIONS; i++){
        for(int j=0; j<784; j++){
            t_tran_uint8_t[i][j] = (uint8_t)(t_train_256[i][j]);
        }
    }

    // Initialize tht Device
    int XMinst_status = XMnist_nn_Initialize(&mnits_nn_ap, 0);
    if (XMinst_status != XST_SUCCESS){
        fprintf(stderr, "Could not Initialize XMnist_nn\n");
        return(-1);
    }

    for(int i=0; i<NUM_ITERATIONS; i++){
        u32 char_num = (u32)(&t_tran_uint8_t[i][0]);
        XMnist_nn_Set_in_V(&mnits_nn_ap, char_num);
        XMnist_nn_Start(&mnits_nn_ap);

        while(!XMnist_nn_IsDone(&mnits_nn_ap));

        // minst nn result check
        for(int j=0; j<5; j++){
            XMnist_nn_Read_out_V_Words(&mnits_nn_ap, j, &res, 1);
            result_hard[i][j*2] = res & 0x1fff; // 13 bit
            if(result_hard[i][j*2] & 0x1000) // minus
                result_hard[i][j*2] = 0xffffe000 | result_hard[i][j*2]; // Sign extension

            result_hard[i][j*2+1] = (res & 0x1fff0000) >> 16;
            if(result_hard[i][j*2+1] & 0x1000) // minus
                result_hard[i][j*2+1] = 0xffffe000 | result_hard[i][j*2+1]; // Sign extension
        }
    }

    int errflag=0;
    for(int i=0; i<NUM_ITERATIONS; i++){
        max_id_hw = max_int32_t(&result_hard[i][0]);
        max_id_ref = max_float(&t_test[i][0]);

        if(max_id_ref != max_id_hw){
            printf("id = %d, max_id_ref = %d, max_id_hw = %d\n", i, max_id_ref, max_id_hw);
            errflag = 1;
        }
    }
    if(errflag == 0)
        printf("No Error\n");

    return(0);
}

int max_float(float out[10]){
    int max_id;
    float max;

    for(int i=0; i<10; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    return(max_id);
}

int max_int32_t(int32_t out[10]){
    int max_id;
    int32_t max;

    for(int i=0; i<10; i++){
        if(i == 0){
            max = out[0];
            max_id = 0;
        }else if(out[i]>max){
            max = out[i];
            max_id = i;
        }
    }
    return(max_id);
}


これを新規作成した mnist_nn_test2 アプリケーション・プログラムに入れた。
mnist_nn_50_200926.png

Debug を行って、

XMnist_nn_Set_in_V(&mnits_nn_ap, char_num);

を実行したところ、AXI4-Lite インターフェースに Write アクセスが発生した。
AXI-Lite インターフェースが動作している部分の拡大波形を示す。
mnist_nn_52_200925.png

processing_system7_0_M_AXI_GP0 と ps7_0_axi_periph_M01_AXI にトランザクションが見える。

processing_system7_0_M_AXI_GP0 のトランザクションを見てみよう。
mnist_nn_53_200925.png

0x43C10018 番地に 0x00122460 を書いている。

ps7_0_axi_periph_M00_AXI のトランザクションを見てみよう。
mnist_nn_54_200925.png

最後まで Run すると、結果が表示された。
すべての結果が 8 というのがおかしいけれど、AXI4-Lite インターフェースのアクセスは確認できた。
mnist_nn_55_200925.png

やはり、プログラムが多すぎるのが行けないのか?
配列もローカル変数で取っているが、スタックに配置されると思うので、大きな配列を取るのはまずいだろうか? malloc() でヒープに取ってみよう。
  1. 2020年09月26日 18:10 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2019.2 で合成した IP を Vivado で使ったが動作しなかった6(デバック編 2)

”Vivado HLS 2019.2 で合成した IP を Vivado で使ったが動作しなかった5(デバック編 1)”の続き。

Vivado HLS 2019.2 でニューラルネットワークの IP を作成し、Vivado で IP を使用して回路を作成したが、動作しなかった。その顛末を書くことにしたということで、前回は、Vivado Analyzer を挿入して AXI-Lite インターフェースのアクセスがあるかどうか?を調べたが、アクセスは無かった。今回は、この構成でAXI-Lite インターフェースのアクセスがあるか?を調べるために DMA_pow2 IP (2乗 IP)を Add IP してアクセスを調べてみる。

Vivado HLS 2019.2 の DMA_pow2 プロジェクトを示す。
DMA_pow2 は 10 個の配列を DMA Read で読んできて、それらを 2 乗して DMA Write する IP となっている。
mnist_nn_33_200925.png

これを Export RTL して IP にした。
mnist_nn_38_200925.png

これを Vivado 2019.2 の mnist_nn_test プロジェクトの IP Catalog に追加した。
mnist_nn_37_200925.png

これで使えるようになったので、ブロックデザインに DMA_pow2 を追加し、AXI-Lite インターフェースに Debug を設定した。
mnist_nn_34_200925.png

Address Editor を示す。
mnist_nn_35_200925.png

論理合成、インプリメンテーション、ビットストリームの生成を行った。
mnist_nn_36_200925.png

50 MHz なので余裕がある。

XSA ファイルを出力して、Vitis のプラットフォームをアップデートした。mnist_nn_test アプリケーション・プロジェクトも再ビルドした。
DMA_pow2 アプリケーション・プロジェクトを作成して、ビルド成功した。
mnist_nn_39_200925.png

DMA_pow2 アプリケーション・プロジェクトの dma_test.elf をデバックモードで起動した。
mnist_nn_40_200925.png

Vivado の Flow Navigator で PROGRAM AND DEBUG -> Open Hardware Manager -> Open Target をクリックし、メニューから Auto Connect を選択する。
自動的に Vivado Analyzer が起動する。
mnist_nn_41_200925.png

slot 0 ~ 2 の AXI4 インターフェースの AWVALID, ARVALID を OR でトリガーをかけた。

Vitis のデバックモードで

XDma_pow2_Set_in_r(&XDMA_pow2_ap, (u32)&data[0]);

を過ぎたときにトリガーがかかった。
mnist_nn_42_200925.png

AXI-Lite インターフェースが動作している部分の拡大波形を示す。
mnist_nn_43_200925.png

processing_system7_0_M_AXI_GP0 と ps7_0_axi_periph_M01_AXI にトランザクションが見える。

processing_system7_0_M_AXI_GP0 のトランザクションを見てみよう。
mnist_nn_44_200925.png

0x43C10018 番地に 0x0010b234 を書いている。

ps7_0_axi_periph_M01_AXI のトランザクションを見てみよう。
mnist_nn_45_200925.png

0x18 番地に 0x0010b234 を書いている。

次に、Vitis のデバックモードで

XDma_pow2_Set_out_r(&XDMA_pow2_ap, (u32)&result[0]);

を過ぎたときに Vivado Analyzer のトリガーがかかった。
mnist_nn_46_200925.png

processing_system7_0_M_AXI_GP0 のトランザクションを見た。
mnist_nn_47_200925.png

0x43C10020 番地に 0x00110318 を書いている。

ps7_0_axi_periph_M01_AXI のトランザクションを見てみよう。
mnist_nn_48_200925.png

0x43C10020 番地に 0x00110318 を書いている。

Vitis で最後までソフトウェアを走らせると Tera Term に計算結果が表示された。
mnist_nn_49_200925.png

DMA_pow2 は正常に動作している。
  1. 2020年09月25日 04:47 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0

Vivado HLS 2019.2 で合成した IP を Vivado で使ったが動作しなかった5(デバック編 1)

Vivado HLS 2019.2 で合成した IP を Vivado で使ったが動作しなかった4(Vitis 編)”の続き。

Vivado HLS 2019.2 でニューラルネットワークの IP を作成し、Vivado で IP を使用して回路を作成したが、動作しなかった。その顛末を書くことにしたということで、前回は、 XSA ファイルを使用して Vitis 2019.2 のプラットフォームを作成し、アプリケーション・プロジェクトを作成して、実機で検証したが動作しなかった。今回は Vivado Analyzer を挿入して AXI インターフェースのアクセスがあるかどうか?を調べてみよう。

Vivado のブロックデザインで、ZYNQ7 Processing System の M_AXI_GP0 ポートと mnist_nn_0 の s_axi_AXILiteS ポートに Debug を設定した。
mnist_nn_25_200924.png

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

問題ない。
XSA ファイルを出力してから、Vitis でプラットフォームを Update Hardware Specification を行って、更新された XSA ファイルを読み込んだ。
プラットフォームもアプリケーション・プロジェクトも再ビルドを行った。

Vitis をデバッカー・モードで起動した。
mnist_nn_27_200924.png

Vivado の Flow Navigator で PROGRAM AND DEBUG -> Open Hardware Manager -> Open Target をクリックし、メニューから Auto Connect を選択する。
自動的に Vivado Analyzer が起動する。
mnist_nn_28_200924.png

ps7_0_axi_periph_M00_AXI: AWVALID と processing_system7_0_M_AXI_GP0: AWVALID 立ち上がりの OR でトリガーがかかるように設定して、トリガー待ち状態にした。
mnist_nn_29_200924.png

Vitis でデバックを進めていって、XMnist_nn_Set_in_V() のところで止めた。ここでは mnist_nn IP の AXI4-Lite インターフェース経由で in が DMA Read するためにアドレスを ZYNQ7 Processing System から mnist_nn_0 に書き込まれるはずだ。
mnist_nn_30_200924.png

実際にデータを Write する画面になった。
mnist_nn_31_200924.png

ここを過ぎて DataAbortHandler の画面になった。
mnist_nn_32_200924.png

こうなっても、Vivado Analyzer はトリガを待ち続けている。
mnist_nn_29_200924.png

processing_system7_0_M_AXI_GP0 からのアクセスが来ないようだ?
なぜだろうか?なぜ AXI4-Lite のアクセスが来ない?

ここが間違っているとかの情報をお待ちしております。どこが悪いんでしょうか?
  1. 2020年09月24日 04:46 |
  2. Vivado HLS
  3. | トラックバック:0
  4. | コメント:0
»