Vivado HLS でIP 化すると、チューニングもしやすいし、C/RTL 協調シミュレーションで波形も確認できるのでとても良い。そこで、IP 化したVerilog HDL コードをRTL カーネルとしてVitis で使いたいと考えたわけだ。。。
今回は、Vitis のRTL カーネルと実装するためのC++ のソースコードテストベンチを貼っておく。
テンプレートで書いた CNN の記事を下に示す。
これはテンプレートで書いた各層をHLSストリームで接続するフレームワークになっている。テンプレートのパラメータを設定すれば、いろいろな CNN として使用することができるように設計されている(はず)。現在は、白線走行用の CNN のパラメータがテンプレートに設定されている。
今回は、入力層ー畳み込み層ーReluーMaxpoolingー全結合層ーReluー全結合層ー出力層だが、途中のHLSストリームの幅を合わせれば、何層でも接続することができる。ただし、余り多いとVivado HLSで合成できなくなる。
その場合は、HLSストリームで接続なので、途中で切って合成し、IP インテグレータでつなぐこともできる。
”テンプレートで書いた畳み込 みニューラルネットワーク1(ソースコード)”
”テンプレートで書いた畳み込 みニューラルネットワーク2(C シミュレーションとC コードの合成)”
”テンプレートで書いた畳み込 みニューラルネットワーク2(C/RTL協調シミュレーションとExport RTL)”
このソースコードを、”Vitis のRTL カーネル”に沿って書き換えた。
ソースコードの all_layers_template_axim.cpp を示す。
// all_layers_template_axim.cpp
// 2018/05/10 by marsee
// 2019/12/28: VitisのRTLカーネルととして使用するためにall_layers_dnnを追加
//
#include <ap_int.h>
#include <hls_stream.h>
#include <ap_axi_sdata.h>
#include <hls_video.h>
#include <stdint.h>
#include "layer_general.h"
#include "all_layers_template.h"
int input_layer(hls::stream<ap_axiu<32,1,1,1> >&ins,
hls::stream<ap_fixed_axis<9,1,1,1> >&outs);
int conv_layer1(hls::stream<ap_fixed_axis<9,1,1,1> >& ins,
hls::stream<ap_fixed_axis<16,6,2,1> >& outs);
int relu_conv1(hls::stream<ap_fixed_axis<16,6,2,1> >& ins,
hls::stream<ap_fixed_axis<16,6,2,1> >& outs);
int max_pooling(hls::stream<ap_fixed_axis<16,6,2,1> >& ins,
hls::stream<ap_fixed_axis<16,6,2,1> >& outs);
int affine_layer1(hls::stream<ap_fixed_axis<16,6,2,1> >& ins,
hls::stream<ap_fixed_axis<19,7,1,1> >& outs);
int relu_affine1(hls::stream<ap_fixed_axis<19,7,1,1> >& ins,
hls::stream<ap_fixed_axis<19,7,1,1> >& outs);
int affine_layer2(hls::stream<ap_fixed_axis<19,7,1,1> >& ins,
hls::stream<ap_fixed_axis<12,7,1,1> >& outs);
int output_layer(hls::stream<ap_fixed_axis<12,7,1,1> >& ins, output_type& output,
out_affine_type dot2[NUMBER_OF_OUTPUT_LAYER]);
int all_layers(hls::stream<ap_axiu<32,1,1,1> >& ins, output_type& output,
out_affine_type dot2[NUMBER_OF_OUTPUT_LAYER]){
//#pragma HLS INTERFACE s_axilite port=output
//#pragma HLS INTERFACE s_axilite port=dot2
//#pragma HLS ARRAY_PARTITION variable=dot2 complete dim=1
//#pragma HLS INTERFACE s_axilite port=return
//#pragma HLS INTERFACE axis register both port=ins
#pragma HLS DATAFLOW
hls::stream<ap_fixed_axis<9,1,1,1> > outs_input_layer;
//#pragma HLS STREAM variable=outs_input_layer depth=560 dim=1
hls::stream<ap_fixed_axis<16,6,2,1> > outs_conv_layer;
//#pragma HLS STREAM variable=outs_conv_layer depth=312 dim=1
hls::stream<ap_fixed_axis<16,6,2,1> > outs_relu_conv1;
//#pragma HLS STREAM variable=outs_relu depth=312 dim=1
hls::stream<ap_fixed_axis<16,6,2,1> > outs_max_pooling;
//#pragma HLS STREAM variable=outs_max_pooling depth=78 dim=1
hls::stream<ap_fixed_axis<19,7,1,1> > outs_affine_layer1;
//#pragma HLS STREAM variable=outs_affine_layer1 depth=100 dim=1
hls::stream<ap_fixed_axis<19,7,1,1> > outs_relu_affine1;
//#pragma HLS STREAM variable=outs_relu_affine1 depth=100 dim=1
hls::stream<ap_fixed_axis<12,7,1,1> > outs_affine_layer2;
//#pragma HLS STREAM variable=outs_affine_layer2 depth=3 dim=1
input_layer(ins, outs_input_layer);
conv_layer1(outs_input_layer, outs_conv_layer);
relu_conv1(outs_conv_layer, outs_relu_conv1);
max_pooling(outs_relu_conv1, outs_max_pooling);
affine_layer1(outs_max_pooling, outs_affine_layer1);
relu_affine1(outs_affine_layer1, outs_relu_affine1);
affine_layer2(outs_relu_affine1, outs_affine_layer2);
output_layer(outs_affine_layer2, output, dot2);
return(0);
}
extern "C" {
void all_layers_dnn(volatile uint32_t *inm, volatile uint32_t *output,
volatile int32_t *dot2, int32_t x_size, int32_t y_size){
#pragma HLS INTERFACE m_axi port=inm offset = slave bundle = gmem
#pragma HLS INTERFACE m_axi port=output offset = slave bundle = gmem
#pragma HLS INTERFACE m_axi port=dot2 offset = slave bundle = gmem2
#pragma HLS INTERFACE s_axilite port = x_size bundle = control
#pragma HLS INTERFACE s_axilite port = y_size bundle = control
#pragma HLS INTERFACE s_axilite port = return bundle = control
#pragma HLS DATAFLOW
hls::stream<ap_axiu<32, 1, 1, 1> > ins;
ap_axiu<32,1,1,1> element;
out_affine_type dot2_o[NUMBER_OF_OUTPUT_LAYER];
output_type output_o;
Loop_y: for(int y=0; y<y_size; y++){
#pragma HLS LOOP_TRIPCOUNT min=10 max=10 avg=10
Loop_x: for(int x=0; x<x_size; x++){
#pragma HLS LOOP_TRIPCOUNT min=56 max=56 avg=56
#pragma HLS PIPELINE II=1
element.data = ap_uint<32>(inm[x_size*y+x]);
if(x==0 && y==0) // 最初のデータ
element.user = 1;
else
element.user = 0;
if(x==(x_size-1)) // 行の終了
element.last = 1;
else
element.last = 0;
ins << element;
}
}
all_layers(ins, output_o, dot2_o);
*output = uint32_t(output_o);
Loop_dot2: for(int i=0; i<NUMBER_OF_OUTPUT_LAYER; i++){
#pragma HLS PIPELINE II=1
dot2[i] = int32_t(dot2_o[i]);
}
}
}
テストベンチの all_layers_template_axim_tb.cpp を示す。
// all_layers_template_axim_tb.cpp
// 2018/05/12 by marsee
// 2019/12/28: VitisのRTLカーネルととして使用するためにall_layers_dnnを追加
//
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <ap_int.h>
#include <hls_stream.h>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <math.h>
#include <ap_axi_sdata.h>
#include <hls_video.h>
#include <vector>
#include <stdint.h>
#include "layer_general.h"
#include "all_layers_template.h"
#include "curve_data_0_100.h"
//#include "curve_data_2500_2600.h"
//#include "curve_data_5000_5100.h"
#define ALL_DATA_NUM 300
#define NUM_OF_KERNELS 2
#define COULMN_PIXELS 56
#define ROW_PIXELS 10
#define ALL_PIXELS 560
#define NUM_OF_OUTPUT 3
#define NUM_ITERATIONS 300 // C Simulation
//#define NUM_ITERATIONS 2 // C/RTL CoSimulation
void all_layers_dnn(volatile uint32_t *inm, volatile uint32_t *output,
volatile int32_t *dot2, int32_t x_size, int32_t y_size);
int all_layers_soft(hls::stream<ap_axiu<32,1,1,1> >& ins, output_type& output,
float dot2[NUMBER_OF_OUTPUT_LAYER]);
int main(){
using namespace std;
hls::stream<ap_axiu<32,1,1,1> > ins;
hls::stream<ap_axiu<32,1,1,1> > ins_soft;
output_type output_soft;
uint32_t output;
float dot2_soft[NUMBER_OF_OUTPUT_LAYER];
ap_axiu<32,1,1,1> pix;
int hw_err_cnt = 0;
int sw_err_cnt = 0;
vector<uint32_t> pixel(ROW_PIXELS * COULMN_PIXELS);
vector<int32_t> dot2(NUMBER_OF_OUTPUT_LAYER);
for(int i=0; i<NUM_ITERATIONS; i++){
// ins に入力データを用意する
for(int y=0; y<ROW_PIXELS; y++){
for(int x=0; x<COULMN_PIXELS; x++){
// 1 画面分のデータを ins、ins_soft に入力する
pix.data = ap_uint<32>(t_train_256[i][y*COULMN_PIXELS+x]);
pixel[y*COULMN_PIXELS+x] = uint32_t(t_train_256[i][y*COULMN_PIXELS+x]);
if (x==0 && y==0) // 最初のデータの時に TUSER を 1 にする
pix.user = 1;
else
pix.user = 0;
if (x == COULMN_PIXELS-1) // 行の最後でTLASTをアサートする
pix.last = 1;
else
pix.last = 0;
ins << pix;
ins_soft << pix;
}
}
all_layers_dnn(pixel.data(), &output, dot2.data(), COULMN_PIXELS, ROW_PIXELS);
all_layers_soft(ins_soft, output_soft, dot2_soft);
int t_test_num = 0;
for(int m=0; m<NUMBER_OF_OUTPUT_LAYER; m++){
if(t_test[i][m] == 1.0f){
t_test_num = m;
break;
}
}
// out と out_soft を比較する
/* cout << "output" << " = " << int(output) << " output_soft = " << int(output_soft) << endl;
for(int j=0; j<NUMBER_OF_OUTPUT_LAYER; j++){
cout << "dot2[" << j << "] = " << float(dot2[j]) << " dot2_soft[" << j << "] = " << dot2_soft[j] << endl;
} */
if(int(output) != t_test_num){
cout << "hw_error: i = " << i << " output = " << int(output) << " t_test_num = " << t_test_num << endl;
hw_err_cnt++;
//return(1);
}
if(int(output_soft) != t_test_num){
cout << "sw_error: i = "<< i << " output_soft = " << int(output_soft) << " t_test_num" " = " << t_test_num << endl;
sw_err_cnt++;
//return(1);
}
if(int(output) != t_test_num || int(output_soft) != t_test_num){
for(int j=0; j<NUMBER_OF_OUTPUT_LAYER; j++){
cout << "dot2[" << j << "] = " << fixed << setprecision(8) << float(dot2[j]) << " dot2_soft[" << j << "] = " << dot2_soft[j] << endl;
}
cout << endl;
}
}
cout << "hw_err_cnt = " << hw_err_cnt << " sw_err_cnt = " << sw_err_cnt << endl;
return(0);
}
コメント