Edited at
HDLDay 19

[SystemC][HLS] ラプラシアンフィルタを SystemCで記述してみる。

More than 5 years have passed since last update.

この記事は HDL Advent Calendar 2013の 19日目の記事です。

まだまだ参加者募集しております!!!


概要

皆さん @marsee1さんが書かれているブログ

FPGAの部屋

をご存知でしょうか?


その中に、 Vivado HLS に関してのトライアルをした記事などがあります。

もう身近に 高位合成 が出来る時代になりましたね!

さて、今回のその記事の中からこちらを引用したいと思います。


SystemC版を作ってみよう

記事では C言語ですが、せっかくなので SystemC版を書いてみました。


※ただし、高位合成出来るかは試していません


  • laplacian_filter.h

#pragma once


#include <stdlib.h>
#include <systemc.h>

SC_MODULE( laplacian_filter ){
sc_in_clk clk;
sc_in<bool> xreset;
sc_in<int> x0y0;
sc_in<int> x1y0;
sc_in<int> x2y0;
sc_in<int> x0y1;
sc_in<int> x1y1;
sc_in<int> x2y1;
sc_in<int> x0y2;
sc_in<int> x1y2;
sc_in<int> x2y2;
sc_out<int> out;

void process();

int _x0y0;
int _x1y0;
int _x2y0;
int _x0y1;
int _x1y1;
int _x2y1;
int _x0y2;
int _x1y2;
int _x2y2;
int _exe;

SC_CTOR( laplacian_filter ):
clk("clk")
,xreset("xreset")
,x0y0("x0y0")
,x1y0("x1y0")
,x2y0("x2y0")
,x0y1("x0y1")
,x1y1("x1y1")
,x2y1("x2y1")
,x0y2("x0y2")
,x1y2("x1y2")
,x2y2("x2y2")
,out ("out")
{
SC_CTHREAD( process, clk.pos() );
async_reset_signal_is(xreset, false);
}
};


  • laplacian_filter.cpp

#include "laplacian_filter.h"


void laplacian_filter::process() {
out.write(0);
wait();

while (1) {
{ //input
_x0y0 = x0y0.read();
_x1y0 = x1y0.read();
_x2y0 = x2y0.read();
_x0y1 = x0y1.read();
_x1y1 = x1y1.read();
_x2y1 = x2y1.read();
_x0y2 = x0y2.read();
_x1y2 = x1y2.read();
_x2y2 = x2y2.read();
}

{ // execute
_exe = abs(-_x0y0 -_x1y0 -_x2y0 -_x0y1 +8*(_x1y1) -_x2y1 -_x0y2 -_x1y2 -_x2y2);
}

{ // output
out.write(_exe);
}
wait();
}
}

さて、ここまでが機能部分のところですが、


検証環境はというとこちらになります。


  • sc_main.cpp

#include <systemc.h>

#include <stdlib.h>

#include "laplacian_filter.h"
#include "testbench.h"

int sc_main(int argc, char* argv[])
{

sc_clock clk("clk", 10, SC_NS);
sc_signal<bool> xreset;
sc_signal<int> x0y0;
sc_signal<int> x1y0;
sc_signal<int> x2y0;
sc_signal<int> x0y1;
sc_signal<int> x1y1;
sc_signal<int> x2y1;
sc_signal<int> x0y2;
sc_signal<int> x1y2;
sc_signal<int> x2y2;
sc_signal<int> y;

laplacian_filter dut("dut");
testbench tb("tb");

dut.clk(clk);
dut.xreset(xreset);
dut.x0y0(x0y0);
dut.x1y0(x1y0);
dut.x2y0(x2y0);
dut.x0y1(x0y1);
dut.x1y1(x1y1);
dut.x2y1(x2y1);
dut.x0y2(x0y2);
dut.x1y2(x1y2);
dut.x2y2(x2y2);
dut.out(y);

tb.clk(clk);
tb.xreset(xreset);
tb.x0y0(x0y0);
tb.x1y0(x1y0);
tb.x2y0(x2y0);
tb.x0y1(x0y1);
tb.x1y1(x1y1);
tb.x2y1(x2y1);
tb.x0y2(x0y2);
tb.x1y2(x1y2);
tb.x2y2(x2y2);
tb.y(y);

xreset = 1;
sc_start(1, SC_NS);
xreset = 0;
sc_start(10, SC_NS);
xreset = 1;
sc_start();

}


  • testbench.h

#pragma once


#include <stdlib.h>
#include <systemc.h>

SC_MODULE( testbench ){
sc_in_clk clk;
sc_in<bool> xreset;
sc_in<int> y;
sc_out<int> x0y0;
sc_out<int> x1y0;
sc_out<int> x2y0;
sc_out<int> x0y1;
sc_out<int> x1y1;
sc_out<int> x2y1;
sc_out<int> x0y2;
sc_out<int> x1y2;
sc_out<int> x2y2;

sc_event _e_comp;
int _data[3][3];

void set_data();
void process();
void compere();

SC_CTOR( testbench ):
clk("clk")
,xreset("xreset")
,y ("y")
,x0y0("x0y0")
,x1y0("x1y0")
,x2y0("x2y0")
,x0y1("x0y1")
,x1y1("x1y1")
,x2y1("x2y1")
,x0y2("x0y2")
,x1y2("x1y2")
,x2y2("x2y2")
{
SC_THREAD( process );
sensitive << clk.pos();
SC_METHOD( compere );
sensitive << _e_comp ;
dont_initialize();
}
};


  • testbench.cpp

#include "testbench.h"


void testbench::set_data() {
_data[0][0] = 1;
_data[1][0] = 1;
_data[2][0] = 1;
_data[0][1] = 1;
_data[1][1] = 2;
_data[2][1] = 1;
_data[0][2] = 1;
_data[1][2] = 1;
_data[2][2] = 1;
}

void testbench::process() {
wait();

set_data();

x0y0.write(_data[0][0]);
x1y0.write(_data[1][0]);
x2y0.write(_data[2][0]);
x0y1.write(_data[0][1]);
x1y1.write(_data[1][1]);
x2y1.write(_data[2][1]);
x0y2.write(_data[0][2]);
x1y2.write(_data[1][2]);
x2y2.write(_data[2][2]);

wait(3);
_e_comp.notify();
wait(3);
sc_stop();
}

void testbench::compere() {

int hw_result, sw_result;

hw_result = y.read();
sw_result = abs(-_data[0][0] -_data[1][0] -_data[2][0]
-_data[0][1] + 8*(_data[1][1]) -_data[2][1]
-_data[0][2] -_data[1][2] -_data[2][2]);

std::cout << "simulation time = " << sc_time_stamp() << std::endl;
printf("HW result = %d\n", hw_result);
printf("SW result = %d\n", sw_result);

if (hw_result == sw_result){
printf("Success SW and HW results match\n");
// return(0);
} else {
printf("ERROR SW and HW results mismatch\n");
// return(1);
}
}



まとめ

今回は単純に書き換え(AXIインターフェースは抜きにして信号で表現) を行っただけですが、

設計対象および検証環境はまだまだ改善されるべきところがあります。

今後は、こちらをベースにして色々な設計スタイルや検証環境のTopicsについて

書いていきたいと思います。

※次回以降に乞うご期待。

書きたいと思っている内容については以下です。


  • Modular InterfaceやDesign Interface(p2p, AXI)について

  • 高位合成での合成

  • 汎用なデータ入力(検証環境)

  • ランダム検証(検証環境)

上記以外でリクエストあれば、ご連絡ください。