---
title: 実践的！FPGA開発セミナー vol.26（2023/09/27）
tags:  #fpga #fpga開発 #fpga開発シリーズ  
author: [株式会社フィックスターズ](https://convert.docswell.com/user/fixstars)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/4JMV1893JW.jpg?width=480
description: フィックスターズならではの「FPGA」に関する高速化手法、 効率的な開発ノウハウ、苦労話などについてお話しいたします。   ＜講演内容＞  1、Vitis HLS で追加された新しい構文を試してみる  Vitis HLS (旧 Vivado HLS) は AMD Xilinx 社の出している高位合成ツールですが、現在もアップデートが続いています。 Vivado HLS の時代は、通常の C コードに対して #pragma を付与することで挙動をカスタマイズできていました。  一方、Vitis HLS だと Vivado HLS と同じような方式に加え、hls::stream_of_blocks, hls::task 等、より細かく挙動をカスタマイズできる記述方式が追加されています。  本セミナーではこれらの機能について紹介し、より効率の良い HDL を生成するための方法について解説します。   2、格安FPGAで始めるFPGA Ethernet: UDPオーディオ編  ※資料掲載先（格安FPGAで始めるFPGA Ethernet: UDPオーディオ編）： https://github.com/ciniml/fpga_seminar_slides/blob/main/20230927_udp_audio/fpga_seminar_udp_audio.pdf  弊社 FPGAセミナー vol.19（ https://news.fixstars.com/2990/ ） のLTなどで紹介した、 秋月電子通商などで購入可能な格安FPGAボード「Tang Primer 20K」を使った、 FPGAでのEthernetおよびUDP通信を使ったシステムの設計について解説します。  CPUを用いずにFPGAの論理回路のみでUDPのパケットの送受信を行い、 PCから送信された音声データをFPGA上で処理したのち、 Tang Primer 20Kに搭載されているI2S DAC経由でスピーカーに出力します。 FPGA上での音声データ処理の実装方法についても解説予定です。     ・当社技術ブログ 記事：　https://proc-cpuinfo.fixstars.com/  ・フィックスターズグループ／セミナー一覧：　https://www.fixstars.com/ja/seminar  ・フィックスターズのFPGAシステム開発：　https://www.fixstars.com/ja/services/fpga
published: January 26, 24
canonical: https://convert.docswell.com/s/fixstars/KP9PG8-20230927
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/4JMV1893JW.jpg)

実践的！FPGA開発セミナー
vol.26
2023/09/27 18:00～
Copyright© Fixstars Group
1


# Page. 2

![Page Image](https://bcdn.docswell.com/page/PJR2L5GRE9.jpg)

Vitis HLS で追加された
新しい構文を試してみる
Copyright© Fixstars Group
2


# Page. 3

![Page Image](https://bcdn.docswell.com/page/PEXDWKXY7X.jpg)

Who I am
Yuki MATSUDA
写真
松田 裕貴
ソリューション第四事業部
リードエンジニア
Copyright© Fixstars Group


# Page. 4

![Page Image](https://bcdn.docswell.com/page/3EKXV5W47D.jpg)

自己紹介
●
松田裕貴
○
2016 年 4 月に Fixstars に入社
○
ハードウェア開発をメインに、ソフトウェア開発なども担当
■
FPGA による画像処理・ネットワーク処理
■
CPU / GPU 上の処理の高速化
Copyright© Fixstars Group
4


# Page. 5

![Page Image](https://bcdn.docswell.com/page/L735MK1DE5.jpg)

本日のセミナーについて
●
Vitis HLS (旧 Vivado HLS) は現在も
機能追加の続いている AMD 製の高位合成ツール
●
○
更新情報: https://japan.xilinx.com/products/design-tools/vitis/vitis-whats-new.html
○
公式doc: https://docs.xilinx.com/r/ja-JP/ug1399-vitis-hls
Vivado HLS 時代の書き方でも機能を実現するには十分でも、
最近追加された構文を使用すると、効率の良い HW を作れる場合があります
●
最近追加された機能のうち、いくつかをピックアップして紹介します
Copyright© Fixstars Group
5


# Page. 6

![Page Image](https://bcdn.docswell.com/page/87DD63X27G.jpg)

本日紹介する機能
●
hls::stream_of_blocks
○
●
hls::task
○
●
タスク並列を行う際のタスク間のデータ交換の拡張
タスク並列を C コード上で明示的に指示できるようにする追加機能
hls::burst_maxi
○
HLS が行う AXI Memory-Mapped (AXI MM) のメモリアクセス方式を
明示的に指示できる構文
●
その他いくつか細かいもの
Copyright© Fixstars Group
6


# Page. 7

![Page Image](https://bcdn.docswell.com/page/VJPXY4PL78.jpg)

タスク並列とは
●
HLS カーネルの中で複数のタスク (処理) を並列に実行すること
○
1 つの処理を小さなタスクとして分割することで、
タスク間の並列実行・タスク内のスケジュールの簡易化など様々なメリット
●
以下は一番よく見るタスク並列の例
○
#pragma HLS dataﬂow で reader, proc, writer のタスク並列を指示する
top
reader
proc
writer
Copyright© Fixstars Group
void top(const int* x[N],
int* y[N]) {
int t0, t1[N];
#pragma HLS dataflow
reader(x, t0);
proc(t0, t1);
writer(t1, y);
}
7


# Page. 8

![Page Image](https://bcdn.docswell.com/page/2EVLGX26JQ.jpg)

タスク間のインターフェース
●
PIPO (Ping-Pong)
○
○
●
ある程度のサイズの
○
FIFO を用いてデータを渡す
ダブルバッファでデータを渡す
○
シーケンシャルな
バッファ領域に対して
アクセスパターンのみサポート
任意のアクセスパターンをサポート
○
FIFO (First-In First-Out)
■
ランダムアクセス
■
複数回のアクセス
○
producer の書き込み後、
データは即座に consumer に渡る
○
リソースは軽い
ブロック化されたアクセスになるので
■
レイテンシは長い
○
デッドロック回避・性能達成など
目的に合わせて深さは調整
バッファを複製するため
リソースは重い
buf0
producer
consumer
producer
consumer
buf1
Copyright© Fixstars Group
8


# Page. 9

![Page Image](https://bcdn.docswell.com/page/57GX8VR2EL.jpg)

タスク間のインターフェース (コード例, あくまで一例)
●
PIPO (Ping-Pong)
●
FIFO (First-In First-Out)
○
普通の配列アクセスで書ける
○
hls::stream&lt;T&gt; で明示的に使用できる
○
関数の終了タイミングで所有権が移動
○
read(), write() の呼び出しタイミングで
■
コーディングスタイルが制約される
void proc(const int t0[N],
int t1[N]) {
for (int i = 0; i &lt; N; i++) {
t1[i] = t0[i];
}
}
void top(const int* x[N],
int* y[N]) {
int t0[N], t1[N];
#pragma HLS dataflow
reader(x, t0);
proc(t0, t1);
writer(t1, y);
}
即座にデータが移動する
void proc(hls::stream&lt;int&gt;&amp; s0,
hls::stream&lt;int&gt;&amp; s1) {
for (int i = 0; i &lt; N; i++) {
s1.write(s0.read());
}
}
void top(const int* x[N],
int* y[N]) {
hls::stream&lt;int&gt; s0, s1;
#pragma HLS dataflow
reader(x, s0);
proc(s0, s1);
writer(s1, y);
}
Copyright© Fixstars Group
9


# Page. 10

![Page Image](https://bcdn.docswell.com/page/4EQ896V9JP.jpg)

hls::stream_of_blocks
●
PIPO 形式のバッファリングで、データの受け渡しタイミングを
明示的に制御できるようにしたもの (関数終了時に限らない)
●
C++ 標準のロックのような使い方をする
○
write_lock, read_lock オブジェクトのコンストラクタでロック取得、デストラクタで解放
○
ロックオブジェクトがそのまま配列として使える
using block_t = int[M];
void proc(hls::stream_of_blocks&lt;block_t&gt;&amp; s0,
hls::stream_of_blocks&lt;block_t&gt;&amp; s1) {
for (int i = 0; i &lt; N; i += M) {
hls::read_lock&lt;block_t&gt; b0(s0);
hls::write_lock&lt;block_t&gt; b1(s1);
for (int j = 0; j &lt; M; j++) {
b1[j] = b0[j];
}
}
}
Copyright© Fixstars Group
10


# Page. 11

![Page Image](https://bcdn.docswell.com/page/KJ42X4WRE1.jpg)

hls::stream_of_blocks: ユースケース
●
行列積
○
データ再利用が多いので PIPO だと嬉しい
○
DDR アクセスを減らすためにブロック単位の read/write 処理
○
ブロック内のデータに対して行列積が実行される
■
O(n^2) のブロック領域に対し、読み出しは O(n^3) 回
行列積のブロック化
Copyright© Fixstars Group
11


# Page. 12

![Page Image](https://bcdn.docswell.com/page/LE1MN4YV7G.jpg)

hls::stream_of_blocks: ユースケース (実際のコード)
●
●
実際のコード例
○
データ読み出し部 (右図)
○
処理部 (下図, 内部ループのみ抜粋)
関数内で PIPO 形式で渡せるから便利
Copyright© Fixstars Group
12


# Page. 13

![Page Image](https://bcdn.docswell.com/page/GEW34XG3E2.jpg)

タスク並列時のシミュレーションモデル
●
従来の #pragma HLS dataﬂow を使った形式だと、
Csim, Verilog HDL で挙動が異なっていた
○
C sim 時: reader, proc, writer の順に逐次実行。FIFO 深さは無限扱い
○
Cosim 時: reader, proc, writer は同時に動く
void top(const int* x[N],
int* y[N]) {
hls::stream&lt;int&gt; s0, s1;
#pragma HLS dataflow
reader(x, s0);
proc(s0, s1);
writer(s1, y);
}
Copyright© Fixstars Group
13


# Page. 14

![Page Image](https://bcdn.docswell.com/page/47ZV26LGE3.jpg)

hls::task
●
C コード上でタスク並列を明示的に使用するための新しい構文
●
hls::task t(func, …args); の形式でタスクを生成する
●
タスクは dataﬂow がなくても
タスク並列で実行される
●
右のコードを動かしたところ、
C simulation 時に inc, add が
同時に動いていた
○
マルチスレッドのような挙動
Copyright© Fixstars Group
14


# Page. 15

![Page Image](https://bcdn.docswell.com/page/YJ65V2WQ7V.jpg)

hls::task: 注意事項
●
hls::task で実行する関数は
自動的に無限ループで実行される
●
○
data driven な処理になる
○
多分タスクの制御のための HW リソースが減っていると思うが、こちらは未確認
また、以下の制約がある
○
タスクにできる関数の入出力は hls::stream または hls::stream_of_blocks のみ
■
スカラ変数などのパラメータもストリームで入れる必要がある
Copyright© Fixstars Group
15


# Page. 16

![Page Image](https://bcdn.docswell.com/page/GJ5YD2M2J4.jpg)

hls::task: ユースケース
●
dataﬂow で hls::stream, hls::stream_of_blocks を使っている
モジュール全般に利用可能
●
フィードバックがある回路を作るとき等、
C simulation では正しい結果が得られないような場合には
積極的に採用したい
Copyright© Fixstars Group
16


# Page. 17

![Page Image](https://bcdn.docswell.com/page/LE35MKW1J5.jpg)

AXI のバースト転送
●
●
AXI MM はバースト転送をサポートしている
○
バースト転送: 1つのアドレスリクエストに対し、複数のデータワードを送れること
○
シーケンシャルなアクセスのみがバースト転送にできる
バースト転送が可能かどうかの判断はツールが行っていたが、
burst_maxi を使用すると明示的にバースト転送を指示できる
https://docs.xilinx.com/r/ja-JP/ug1399-vitis-hls/AXI-バースト転送
Copyright© Fixstars Group
17


# Page. 18

![Page Image](https://bcdn.docswell.com/page/8EDD63KKEG.jpg)

通常のメモリアクセスコード
●
普通に C style でメモリアクセスを書くと、
ツールによりバースト推論が実行される
void reader(const int* x,
hls::stream&lt;int&gt;&amp; stm_x,
int size) {
for (int i = 0; i &lt; size; i++) {
stm_x.write(x[i]);
}
}
●
アクセスパターンが複雑な場合など、
うまくバースト推論が実行されないケースもある
Copyright© Fixstars Group
18


# Page. 19

![Page Image](https://bcdn.docswell.com/page/V7PXY4K3E8.jpg)

hls::burst_maxi
●
どこまでがバースト転送かを明示的に指示する API
●
読み出しの場合
○
read_request(oﬀset, length) で oﬀset から length 個の読み出しリクエスト
○
read() で値を取得
void reader(hls::burst_maxi&lt;int&gt;&amp; x,
hls::stream&lt;int&gt;&amp; stm_x,
int size) {
x.read_request(0, size);
for (int i = 0; i &lt; size; i++) {
stm_x.write(x.read());
}
}
Copyright© Fixstars Group
19


# Page. 20

![Page Image](https://bcdn.docswell.com/page/2JVLGXVN7Q.jpg)

hls::burst_maxi (write)
●
書き込みの場合
○
write_request(oﬀset, length) で oﬀset から length 個の書き込みリクエスト
○
write(val, byte_en_mask=-1) でデータ送信
■
○
通常のメモリアクセスだと byte_en_mask は設定できないと思うので便利そう
write_response でデータ送信の完了待ち
void writer(hls::burst_maxi&lt;int&gt;&amp; y,
hls::stream&lt;int&gt;&amp; stm_y,
int size) {
y.write_request(0, size);
for (int i = 0; i &lt; size; i++) {
y.write(stm_y.read());
}
y.write_response();
}
Copyright© Fixstars Group
20


# Page. 21

![Page Image](https://bcdn.docswell.com/page/5EGX8VL5JL.jpg)

hls::burst_maxi: ユースケース
●
画像の切り出し処理
○
二次元ループを使用する
○
通常のコードでもバースト転送自体はされるのだが、
ループ間にオーバーヘッドが載る
Copyright© Fixstars Group
21


# Page. 22

![Page Image](https://bcdn.docswell.com/page/4JQ896YL7P.jpg)

hls::burst_maxi: ユースケース
●
画像の切り出し処理
○
最初にリクエストをまとめて発行
○
後は read/write を連続して行うだけ
Copyright© Fixstars Group
22


# Page. 23

![Page Image](https://bcdn.docswell.com/page/K742X4W5J1.jpg)

おまけ1: hls::vector
●
hls::vector
○
std::vector のようなメモリ確保ではなく、 SIMD のためのベクトル命令の意
○
hls::vector&lt;int, 32&gt; とかやると 32 個の int が格納される
■
○
AXI MM のインターフェースにもそのまま使用可能
+, - 等基本的な element wise op のサポートがあるので、コードを楽に書きたければ
Copyright© Fixstars Group
23


# Page. 24

![Page Image](https://bcdn.docswell.com/page/LJ1MN4Y2EG.jpg)

おまけ2: hls::split, hls::merge
●
AXI Stream に対する分岐/合流処理
●
round robin, load balancing の 2つのアルゴリズムサポート
●
ワードが入力されるたびに、round robin or load balancing で分岐が走る
○
tlast までなどの柔軟な分割に期待
split
func0
func0
func
Copyright© Fixstars Group
merge
24


# Page. 25

![Page Image](https://bcdn.docswell.com/page/GJW34XG2J2.jpg)

まとめ
●
Vitis HLS の最近のアップデートで追加されている
いくつかの構文について紹介した
●
○
stream_of_blocks: PIPO 形式のデータ交換を明示的に行える構文
○
task: C コード上に明示的にタスク並列処理を記載できる構文
○
burst_maxi: C コード上から明示的にバースト転送を行える構文
便利な機能など多々あるので、良い HLS ライフを
Copyright© Fixstars Group
25


# Page. 26

![Page Image](https://bcdn.docswell.com/page/4EZV26L4J3.jpg)

Thank you!
お問い合わせ窓口 : hr-seminar@fixstars.com
Copyright © Fixstars Group
26


